[bookmarks] add support for tags and comments for log lines

Initial work for #446 and #447
  TODO: filtering on tags/comments

Also did a bunch of clang-tidy cleanups and improvements to
the online help.
This commit is contained in:
Timothy Stack 2018-05-17 07:06:50 -07:00
parent b8fc956677
commit 4ccae48aea
74 changed files with 2842 additions and 1084 deletions

17
NEWS
View File

@ -1,8 +1,23 @@
lnav v0.8.4:
Features:
* Added the ':comment' command that can be used to attach a comment to a
log line. The comment will be displayed below the line, like so:
2017-01-01T15:30:00 error: computer is on fire
+ This is where it all went wrong
The ':clear-comment' command will remove the attached comment. Comments
are searchable with the standard search mechanism and they are available
in SQL through the "log_comment" column.
* Added the ':tag', ':untag', and ':delete-tags' commands that can be used
to attach/detach tags on the top log line and delete all instances of
a tag. Tags are also searchable and are available in SQL as a JSON
array in the "log_tags" column.
* Pressing left-arrow while viewing log messages will reveal the source
file name for each line. Pressing again will reveal the full path.
* Added the ":hide-unmarked-lines" and ":show-unmarked-lines" commands
that hide/show lines based on whether they are bookmarked.
* Added the "json_contains()" SQL function to check if a JSON value
contains a number of a string.
Interface Changes:
* When typing in a search, instead of moving the view to the first match
@ -10,6 +25,8 @@ lnav v0.8.4:
window.
* The pretty-print view maintains highlighting from the log view.
* The pretty-print view no longer tries to reverse lookup IP addresses.
* The online help for commands and SQL functions now includes a 'See Also'
section that lists related commands/functions.
Fixes:
* The HOME key should now work in the command-prompt and move the cursor

View File

@ -29,6 +29,24 @@ with the following commands:
* hide-lines-after <abs-time|rel-time> - Hide lines after the given time.
* show-lines-before-and-after - Show lines that were hidden by the "hide-lines" commands.
Bookmarks
---------
* mark - Bookmark the top line in the view.
* partition-name <name> - Partition the log file around the top line in the
log view and assign the given name. The top line and all that follow, up to
the start of the next partition, will be included in the partition. The name
of the partition for a log line is visible in the top status bar to the right
of the time stamp. The partition name for a log line can be retrieved via
the *log_part* field in any log table.
* comment <text> - Attach a comment to the top line in the log view and
bookmark that line.
* clear-comment - Clear the comment attached to the top line in the view.
* tag <tag1> [<tag2> ... [<tagN>]] - Attach one or more tags to a log line.
A '#' will automatically be prepended to the tag name if it is not already there.
* untag <tag1> [<tag2> ... [<tagN>]] - Detach one or more tags from a log line.
* delete-tags <tag1> [<tag2> ... [<tagN>]] - Detach the given tags from all log lines.
Navigation
----------
@ -37,7 +55,6 @@ Navigation
relative time (e.g. 'a minute ago').
* relative-goto <line#|N%> - Move the current view up or down by the given
amount.
* mark - Bookmark the top line in the view.
* next-mark error|warning|search|user|file|partition - Move to the next
bookmark of the given type in the current view.
* prev-mark error|warning|search|user|file|partition - Move to the previous

View File

@ -163,11 +163,11 @@ set(diag_STAT_SRCS
filesystem/path.h
filesystem/resolver.h
../../lbuild/src/config.h
../../lbuild-debug/src/config.h
)
set(lnav_SRCS lnav.cc)
include_directories(../../lbuild/src /opt/local/include)
include_directories(../../lbuild-debug/src /opt/local/include)
include_directories(SYSTEM .)
add_executable(lnav ${lnav_SRCS} ${diag_STAT_SRCS})

View File

@ -35,6 +35,8 @@
using namespace std;
set<string> bookmark_metadata::KNOWN_TAGS;
template<typename LineType>
LineType bookmark_vector<LineType>::next(LineType start) const
{

View File

@ -33,6 +33,7 @@
#define __bookmarks_hh
#include <map>
#include <set>
#include <string>
#include <vector>
#include <algorithm>
@ -41,7 +42,43 @@
#include "listview_curses.hh"
struct bookmark_metadata {
static std::set<std::string> KNOWN_TAGS;
std::string bm_name;
std::string bm_comment;
std::vector<std::string> bm_tags;
void add_tag(const std::string &tag) {
if (std::find(this->bm_tags.begin(),
this->bm_tags.end(),
tag) == this->bm_tags.end()) {
this->bm_tags.push_back(tag);
}
};
bool remove_tag(const std::string &tag) {
auto iter = std::find(this->bm_tags.begin(),
this->bm_tags.end(),
tag);
bool retval = false;
if (iter != this->bm_tags.end()) {
this->bm_tags.erase(iter);
retval = true;
}
return retval;
};
bool empty() {
return this->bm_name.empty() &&
this->bm_comment.empty() &&
this->bm_tags.empty();
};
void clear() {
this->bm_comment.clear();
this->bm_tags.clear();
};
};
/**
@ -59,7 +96,12 @@ struct bookmark_metadata {
*/
template<typename LineType>
class bookmark_vector : public std::vector<LineType> {
typedef std::vector<LineType> base_vector;
public:
typedef typename base_vector::size_type size_type;
typedef typename base_vector::iterator iterator;
typedef typename base_vector::const_iterator const_iterator;
/**
* Insert a bookmark into this vector, but only if it is not already in the
@ -67,9 +109,9 @@ public:
*
* @param vl The line to bookmark.
*/
typename bookmark_vector::iterator insert_once(LineType vl)
iterator insert_once(LineType vl)
{
typename bookmark_vector::iterator lb, retval;
iterator lb, retval;
require(vl >= 0);
@ -85,6 +127,18 @@ public:
return retval;
};
std::pair<iterator, iterator> equal_range(LineType start, LineType stop) {
auto lb = std::lower_bound(this->begin(), this->end(), start);
if (stop == LineType(-1)) {
return std::make_pair(lb, this->end());
} else {
auto up = std::upper_bound(this->begin(), this->end(), stop);
return std::make_pair(lb, up);
}
};
/**
* @param start The value to start the search for the next bookmark.
* @return The next bookmark value in the vector or -1 if there are

View File

@ -93,10 +93,10 @@ string execute_command(exec_context &ec, const string &cmdline)
if ((iter = lnav_commands.find(args[0])) ==
lnav_commands.end()) {
msg = "error: unknown command - " + args[0];
msg = ec.get_error_prefix() + "unknown command - " + args[0];
}
else {
msg = iter->second.c_func(ec, cmdline, args);
msg = iter->second->c_func(ec, cmdline, args);
}
}
@ -129,7 +129,11 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
}
ec.ec_accumulator.clear();
sql_progress_guard progress_guard(sql_progress);
pair<string, int> source = ec.ec_source.top();
sql_progress_guard progress_guard(sql_progress,
source.first,
source.second);
gettimeofday(&start_tv, NULL);
retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
stmt_str.c_str(),
@ -139,7 +143,7 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
if (retcode != SQLITE_OK) {
const char *errmsg = sqlite3_errmsg(lnav_data.ld_db);
retval = "error: " + string(errmsg);
retval = ec.get_error_prefix() + string(errmsg);
alt_msg = "";
}
else if (stmt == NULL) {
@ -244,7 +248,7 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
log_error("sqlite3_step error code: %d", retcode);
errmsg = sqlite3_errmsg(lnav_data.ld_db);
retval = "error: " + string(errmsg);
retval = ec.get_error_prefix() + string(errmsg);
done = true;
}
break;
@ -367,12 +371,12 @@ static string execute_file_contents(exec_context &ec, const string &path, bool m
}
file = stdin;
}
else if ((file = fopen(path.c_str(), "r")) == NULL) {
return "error: unable to open file";
else if ((file = fopen(path.c_str(), "r")) == nullptr) {
return ec.get_error_prefix() + "unable to open file";
}
int line_number = 0, starting_line_number = 0;
char *line = NULL;
char *line = nullptr;
size_t line_max_size;
ssize_t line_size;
string cmdline;
@ -442,10 +446,10 @@ string execute_file(exec_context &ec, const string &path_and_args, bool multilin
log_info("Executing file: %s", path_and_args.c_str());
if (!lexer.split(split_args, ec.ec_local_vars.top())) {
retval = "error: unable to parse path";
retval = ec.get_error_prefix() + "unable to parse path";
}
else if (split_args.empty()) {
retval = "error: no script specified";
retval = ec.get_error_prefix() + "no script specified";
}
else {
ec.ec_local_vars.push(map<string, string>());
@ -516,7 +520,8 @@ string execute_file(exec_context &ec, const string &path_and_args, bool multilin
retval = result;
}
else {
retval = "error: unknown script -- " + script_name + " -- " + open_error;
retval = ec.get_error_prefix()
+ "unknown script -- " + script_name + " -- " + open_error;
}
ec.ec_local_vars.pop();
}
@ -528,6 +533,7 @@ string execute_from_file(exec_context &ec, const string &path, int line_number,
{
string retval, alt_msg;
ec.ec_source.emplace(path, line_number);
switch (mode) {
case ':':
retval = execute_command(ec, cmdline);
@ -561,6 +567,8 @@ string execute_from_file(exec_context &ec, const string &path, int line_number,
line_number,
retval.c_str());
ec.ec_source.pop();
return retval;
}
@ -607,6 +615,7 @@ void execute_init_commands(exec_context &ec, vector<pair<string, string> > &msgs
}
db_label_source &dls = lnav_data.ld_db_row_source;
int option_index = 1;
log_info("Executing initial commands");
for (auto &cmd : lnav_data.ld_commands) {
@ -614,6 +623,7 @@ void execute_init_commands(exec_context &ec, vector<pair<string, string> > &msgs
wait_for_children();
ec.ec_source.emplace("command-option", option_index++);
switch (cmd.at(0)) {
case ':':
msg = execute_command(ec, cmd.substr(1));
@ -639,6 +649,8 @@ void execute_init_commands(exec_context &ec, vector<pair<string, string> > &msgs
if (rescan_files()) {
rebuild_indexes(true);
}
ec.ec_source.pop();
}
lnav_data.ld_commands.clear();
@ -732,7 +744,7 @@ future<string> pipe_callback(exec_context &ec, const string &cmdline, auto_fd &f
lnav_data.ld_file_names[desc]
.with_fd(pp->get_fd())
.with_detect_format(false);
lnav_data.ld_files_to_front.push_back(make_pair(desc, 0));
lnav_data.ld_files_to_front.emplace_back(desc, 0);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
X, "to close the file"));

View File

@ -54,7 +54,18 @@ struct exec_context {
ec_sql_callback(sql_callback),
ec_pipe_callback(pipe_callback) {
this->ec_local_vars.push(std::map<std::string, std::string>());
this->ec_path_stack.push_back(".");
this->ec_path_stack.emplace_back(".");
this->ec_source.emplace("unknown", 0);
}
std::string get_error_prefix() {
if (this->ec_source.size() <= 1) {
return "error: ";
}
std::pair<std::string, int> source = this->ec_source.top();
return "error:" + source.first + ":" + std::to_string(source.second) + ":";
}
vis_line_t ec_top_line;
@ -65,6 +76,7 @@ struct exec_context {
std::stack<std::map<std::string, std::string> > ec_local_vars;
std::map<std::string, std::string> ec_global_vars;
std::vector<std::string> ec_path_stack;
std::stack<std::pair<std::string, int>> ec_source;
attr_line_t ec_accumulator;

View File

@ -432,12 +432,14 @@ public:
};
bool list_value_for_overlay(const listview_curses &lv,
vis_line_t y,
int y, int bottom,
vis_line_t row,
attr_line_t &value_out)
{
view_colors &vc = view_colors::singleton();
if (y == 0) {
this->list_overlay_count(lv);
std::string &line = value_out.get_string();
db_label_source *dls = this->dos_labels;
string_attrs_t &sa = value_out.get_attrs();

View File

@ -1756,6 +1756,7 @@ int common_extension_functions(struct FuncDef **basic_funcs,
.with_summary("Returns the given string concatenated N times.")
.with_parameter({"str", "The string to replicate."})
.with_parameter({"N", "The number of times to replicate the string."})
.with_tags({"string"})
.with_example({"SELECT replicate('abc', 3)"})
},
{ "charindex", 2, SQLITE_UTF8, 0, charindexFunc },
@ -1766,6 +1767,7 @@ int common_extension_functions(struct FuncDef **basic_funcs,
.with_summary("Returns the N leftmost (UTF-8) characters in the given string.")
.with_parameter({"str", "The string to return subset."})
.with_parameter({"N", "The number of characters from the left side of the string to return."})
.with_tags({"string"})
.with_example({"SELECT leftstr('abc', 1)"})
.with_example({"SELECT leftstr('abc', 10)"})
},
@ -1775,6 +1777,7 @@ int common_extension_functions(struct FuncDef **basic_funcs,
.with_summary("Returns the N rightmost (UTF-8) characters in the given string.")
.with_parameter({"str", "The string to return subset."})
.with_parameter({"N", "The number of characters from the right side of the string to return."})
.with_tags({"string"})
.with_example({"SELECT rightstr('abc', 1)"})
.with_example({"SELECT rightstr('abc', 10)"})
},
@ -1789,6 +1792,7 @@ int common_extension_functions(struct FuncDef **basic_funcs,
.sql_function()
.with_summary("Returns the reverse of the given string.")
.with_parameter({"str", "The string to reverse."})
.with_tags({"string"})
.with_example({"SELECT reverse('abc')"})
},
{ "proper", 1, SQLITE_UTF8, 0, properFunc },

View File

@ -42,7 +42,7 @@ json_string extract(const char *str);
void field_overlay_source::build_summary_lines(const listview_curses &lv)
{
textfile_sub_source &tss = lnav_data.ld_text_source;
logfile_sub_source &lss = lnav_data.ld_log_source;
logfile_sub_source &lss = this->fos_lss;
this->fos_summary_lines.clear();
@ -55,7 +55,7 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv)
lv.get_dimensions(height, width);
free_rows = height - filled_rows - vis_line_t(this->fos_lines.size());
if (free_rows < 2) {
if (free_rows < 2 || lnav_data.ld_flags & LNF_HEADLESS) {
this->fos_summary_lines.clear();
}
else {
@ -82,10 +82,8 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv)
time_t five_minutes_ago = local_now - (5 * 60 * 60);
time_t ten_secs_ago = local_now - 10;
vis_line_t from_five_min_ago = lnav_data.ld_log_source.
find_from_time(five_minutes_ago);
vis_line_t from_ten_secs_ago = lnav_data.ld_log_source.
find_from_time(ten_secs_ago);
vis_line_t from_five_min_ago = lss.find_from_time(five_minutes_ago);
vis_line_t from_ten_secs_ago = lss.find_from_time(ten_secs_ago);
vis_bookmarks &bm = lnav_data.ld_views[LNV_LOG].get_bookmarks();
bookmark_vector<vis_line_t> &error_bookmarks =
bm[&logfile_sub_source::BM_ERRORS];
@ -196,7 +194,7 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv)
void field_overlay_source::build_field_lines(const listview_curses &lv)
{
logfile_sub_source &lss = lnav_data.ld_log_source;
logfile_sub_source &lss = this->fos_lss;
view_colors &vc = view_colors::singleton();
this->fos_lines.clear();
@ -220,6 +218,8 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
display = true;
}
this->build_meta_line(lv, this->fos_lines, lv.get_top());
if (!display) {
return;
}
@ -316,7 +316,7 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
}
if (this->fos_active || diff_tv.tv_sec > 0) {
this->fos_lines.push_back(time_line);
this->fos_lines.emplace_back(time_line);
}
if (!this->fos_active) {
@ -359,12 +359,12 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
int skip = pattern_str.length();
pattern_str += lf->get_pattern_regex();
readline_regex_highlighter(pattern_al, skip);
this->fos_lines.push_back(pattern_al);
this->fos_lines.emplace_back(pattern_al);
}
if (this->fos_log_helper.ldh_line_values.empty()) {
this->fos_lines.push_back(" No known message fields");
this->fos_lines.emplace_back(" No known message fields");
}
const log_format *last_format = NULL;
@ -376,9 +376,9 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
string str, value_str = lv.to_string();
if (lv.lv_format != last_format) {
this->fos_lines.push_back(" Known message fields for table " +
format_name +
":");
this->fos_lines.emplace_back(" Known message fields for table " +
format_name +
":");
this->fos_lines.back().with_attr(string_attr(
line_range(32, 32 + format_name.length()),
&view_curses::VC_STYLE,
@ -396,7 +396,7 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
&view_curses::VC_STYLE,
vc.attrs_for_ident(lv.lv_name.to_string())));
this->fos_lines.push_back(al);
this->fos_lines.emplace_back(al);
this->add_key_line_attrs(this->fos_known_key_size);
if (lv.lv_kind == logline_value::VALUE_STRUCT) {
@ -414,7 +414,7 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
.append(this->fos_known_key_size - lv.lv_name.size() - 9 + 3, ' ')
.append(" = ")
.append((const char *) js.js_content, js.js_len);
this->fos_lines.push_back(al);
this->fos_lines.emplace_back(al);
this->add_key_line_attrs(this->fos_known_key_size);
}
@ -423,7 +423,7 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
std::map<const intern_string_t, json_ptr_walk::walk_list_t>::iterator json_iter;
if (!this->fos_log_helper.ldh_json_pairs.empty()) {
this->fos_lines.push_back(" JSON fields:");
this->fos_lines.emplace_back(" JSON fields:");
}
for (json_iter = this->fos_log_helper.ldh_json_pairs.begin();
@ -432,18 +432,19 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
json_ptr_walk::walk_list_t &jpairs = json_iter->second;
for (size_t lpc = 0; lpc < jpairs.size(); lpc++) {
this->fos_lines.push_back(" " +
this->fos_log_helper.format_json_getter(json_iter->first, lpc) + " = " +
jpairs[lpc].wt_value);
this->fos_lines.emplace_back(
" " +
this->fos_log_helper.format_json_getter(json_iter->first, lpc) + " = " +
jpairs[lpc].wt_value);
this->add_key_line_attrs(0);
}
}
if (this->fos_log_helper.ldh_parser->dp_pairs.empty()) {
this->fos_lines.push_back(" No discovered message fields");
this->fos_lines.emplace_back(" No discovered message fields");
}
else {
this->fos_lines.push_back(" Discovered fields for logline table from message format: ");
this->fos_lines.emplace_back(" Discovered fields for logline table from message format: ");
this->fos_lines.back().with_attr(string_attr(
line_range(23, 23 + 7),
&view_curses::VC_STYLE,
@ -474,8 +475,60 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
&view_curses::VC_STYLE,
vc.attrs_for_ident(name)));
this->fos_lines.push_back(al);
this->fos_lines.emplace_back(al);
this->add_key_line_attrs(this->fos_unknown_key_size,
lpc == (this->fos_log_helper.ldh_parser->dp_pairs.size() - 1));
}
};
}
void field_overlay_source::build_meta_line(const listview_curses &lv,
std::vector<attr_line_t> &dst,
vis_line_t row)
{
content_line_t cl = this->fos_lss.at(row);
auto const &bm = this->fos_lss.get_user_bookmark_metadata();
view_colors &vc = view_colors::singleton();
auto iter = bm.find(cl);
if (iter != bm.end()) {
const bookmark_metadata &line_meta = iter->second;
if (!line_meta.bm_comment.empty()) {
attr_line_t al;
al.with_string(" + ")
.with_attr(string_attr(
line_range(1, 2),
&view_curses::VC_GRAPHIC,
line_meta.bm_tags.empty() ? ACS_LLCORNER : ACS_LTEE
))
.append(line_meta.bm_comment);
dst.emplace_back(al);
}
if (!line_meta.bm_tags.empty()) {
attr_line_t al;
al.with_string(" +")
.with_attr(string_attr(
line_range(1, 2),
&view_curses::VC_GRAPHIC,
ACS_LLCORNER
));
for (const auto &str : line_meta.bm_tags) {
al.append(1, ' ')
.append(str, &view_curses::VC_STYLE, vc.attrs_for_ident(str));
}
const auto *tc = dynamic_cast<const textview_curses *>(&lv);
if (tc) {
const textview_curses::highlight_map_t &hm = tc->get_highlights();
auto hl_iter = hm.find("$search");
if (hl_iter != hm.end()) {
hl_iter->second.annotate(al, 2);
}
}
dst.emplace_back(al);
}
}
}

View File

@ -39,21 +39,10 @@
class field_overlay_source : public list_overlay_source {
public:
field_overlay_source(logfile_sub_source &lss)
: fos_active(false),
fos_active_prev(false),
fos_log_helper(lss),
fos_known_key_size(0),
fos_unknown_key_size(0) {
: fos_lss(lss), fos_log_helper(lss) {
};
size_t list_overlay_count(const listview_curses &lv) {
this->build_field_lines(lv);
this->build_summary_lines(lv);
return this->fos_lines.size() + this->fos_summary_lines.size();
};
void add_key_line_attrs(int key_size, bool last_line = false) {
string_attrs_t &sa = this->fos_lines.back().get_attrs();
struct line_range lr(1, 2);
@ -61,28 +50,38 @@ public:
lr.lr_start = 3 + key_size + 3;
lr.lr_end = -1;
sa.push_back(string_attr(lr, &view_curses::VC_STYLE, A_BOLD));
sa.emplace_back(lr, &view_curses::VC_STYLE, A_BOLD);
};
bool list_value_for_overlay(const listview_curses &lv,
vis_line_t y,
attr_line_t &value_out)
{
int y, int bottom,
vis_line_t row,
attr_line_t &value_out) override {
if (y == 0) {
this->build_field_lines(lv);
this->build_summary_lines(lv);
return false;
}
if (1 <= y && y <= (int)this->fos_lines.size()) {
value_out = this->fos_lines[y - 1];
return true;
}
if (!this->fos_summary_lines.empty()) {
unsigned long width;
vis_line_t height;
if (!this->fos_summary_lines.empty() && y == (bottom - 1)) {
value_out = this->fos_summary_lines[0];
return true;
}
lv.get_dimensions(height, width);
if (!this->fos_meta_lines.empty()) {
value_out = this->fos_meta_lines.front();
this->fos_meta_lines.erase(this->fos_meta_lines.begin());
if (y == height - 1) {
value_out = this->fos_summary_lines[0];
return true;
}
return true;
}
if (row < lv.get_inner_height()) {
this->build_meta_line(lv, this->fos_meta_lines, row);
}
return false;
@ -90,14 +89,19 @@ public:
void build_field_lines(const listview_curses &lv);
void build_summary_lines(const listview_curses &lv);
void build_meta_line(const listview_curses &lv,
std::vector<attr_line_t> &dst,
vis_line_t row);
bool fos_active;
bool fos_active_prev;
bool fos_active{false};
bool fos_active_prev{false};
logfile_sub_source &fos_lss;
log_data_helper fos_log_helper;
int fos_known_key_size;
int fos_unknown_key_size;
int fos_known_key_size{0};
int fos_unknown_key_size{0};
std::vector<attr_line_t> fos_lines;
std::vector<attr_line_t> fos_summary_lines;
std::vector<attr_line_t> fos_meta_lines;
};
#endif //LNAV_FIELD_OVERLAY_SOURCE_H

View File

@ -43,10 +43,8 @@ public:
};
void logline_restart(const logfile &lf) {
for (filter_stack::iterator iter = this->lfo_filter_stack.begin();
iter != this->lfo_filter_stack.end();
++iter) {
(*iter)->revert_to_last(this->lfo_filter_state);
for (auto &filter : this->lfo_filter_stack) {
filter->revert_to_last(this->lfo_filter_state);
}
};
@ -57,24 +55,20 @@ public:
this->lfo_filter_state.resize(lf.size());
if (!this->lfo_filter_stack.empty()) {
if (lf.get_format() != NULL) {
if (lf.get_format() != nullptr) {
lf.get_format()->get_subline(*ll, sbr);
}
for (filter_stack::iterator iter = this->lfo_filter_stack.begin();
iter != this->lfo_filter_stack.end();
++iter) {
if (offset >= this->lfo_filter_state.tfs_filter_count[(*iter)->get_index()]) {
(*iter)->add_line(this->lfo_filter_state, ll, sbr);
for (auto &filter : this->lfo_filter_stack) {
if (offset >= this->lfo_filter_state.tfs_filter_count[filter->get_index()]) {
filter->add_line(this->lfo_filter_state, ll, sbr);
}
}
}
};
void logline_eof(const logfile &lf) {
for (filter_stack::iterator iter = this->lfo_filter_stack.begin();
iter != this->lfo_filter_stack.end();
++iter) {
(*iter)->end_of_message(this->lfo_filter_state);
for (auto &iter : this->lfo_filter_stack) {
iter->end_of_message(this->lfo_filter_state);
}
};
@ -90,10 +84,8 @@ public:
size_t get_min_count(size_t max) const {
size_t retval = max;
for (filter_stack::iterator iter = this->lfo_filter_stack.begin();
iter != this->lfo_filter_stack.end();
++iter) {
retval = std::min(retval, this->lfo_filter_state.tfs_filter_count[(*iter)->get_index()]);
for (auto &filter : this->lfo_filter_stack) {
retval = std::min(retval, this->lfo_filter_state.tfs_filter_count[filter->get_index()]);
}
return retval;
@ -102,10 +94,8 @@ public:
void clear_deleted_filter_state() {
uint32_t used_mask = 0;
for (filter_stack::iterator iter = this->lfo_filter_stack.begin();
iter != this->lfo_filter_stack.end();
++iter) {
used_mask |= (1L << (*iter)->get_index());
for (auto &filter : this->lfo_filter_stack) {
used_mask |= (1UL << filter->get_index());
}
this->lfo_filter_state.clear_deleted_filter_state(used_mask);
};

View File

@ -177,6 +177,7 @@ int fs_extension_functions(struct FuncDef **basic_funcs,
"Extract the base portion of a pathname.")
.sql_function()
.with_parameter({"path", "The path"})
.with_tags({"filename"})
.with_example({"SELECT basename('foobar')"})
.with_example({"SELECT basename('foo/bar')"})
.with_example({"SELECT basename('foo/bar/')"})
@ -191,6 +192,7 @@ int fs_extension_functions(struct FuncDef **basic_funcs,
"Extract the directory portion of a pathname.")
.sql_function()
.with_parameter({"path", "The path"})
.with_tags({"filename"})
.with_example({"SELECT dirname('foo/bar')"})
.with_example({"SELECT dirname('/foo/bar')"})
.with_example({"SELECT dirname('/bar')"})
@ -206,6 +208,7 @@ int fs_extension_functions(struct FuncDef **basic_funcs,
"If an argument starts with a forward or backward slash, it will be considered "
"an absolute path and any preceding elements will be ignored.")
.one_or_more())
.with_tags({"filename"})
.with_example({"SELECT joinpath('foo', 'bar')"})
.with_example({"SELECT joinpath('', 'foo', 'bar')"})
.with_example({"SELECT joinpath('/', 'foo', 'bar')"})
@ -217,6 +220,7 @@ int fs_extension_functions(struct FuncDef **basic_funcs,
"Read the target of a symbolic link.")
.sql_function()
.with_parameter({"path", "The path to the symbolic link."})
.with_tags({"filename"})
),
sqlite_func_adapter<decltype(&sql_realpath), sql_realpath>::builder(
@ -225,6 +229,7 @@ int fs_extension_functions(struct FuncDef **basic_funcs,
"resolving '.' and '..' references.")
.sql_function()
.with_parameter({"path", "The path to resolve."})
.with_tags({"filename"})
),
/*

View File

@ -31,17 +31,17 @@
#include <string>
#include <memory>
#include <utility>
#include "grep_proc.hh"
#include "textview_curses.hh"
class grep_highlighter {
public:
grep_highlighter(std::unique_ptr<grep_proc> &gp,
grep_highlighter(std::unique_ptr<grep_proc<vis_line_t>> &gp,
std::string hl_name,
textview_curses::highlight_map_t &hl_map)
: gh_grep_proc(std::move(gp)),
gh_hl_name(hl_name),
gh_hl_name(std::move(hl_name)),
gh_hl_map(hl_map) { };
~grep_highlighter()
@ -49,10 +49,10 @@ public:
this->gh_hl_map.erase(this->gh_hl_map.find(this->gh_hl_name));
};
grep_proc *get_grep_proc() { return this->gh_grep_proc.get(); };
grep_proc<vis_line_t> *get_grep_proc() { return this->gh_grep_proc.get(); };
private:
std::unique_ptr<grep_proc> gh_grep_proc;
std::unique_ptr<grep_proc<vis_line_t>> gh_grep_proc;
std::string gh_hl_name;
textview_curses::highlight_map_t &gh_hl_map;
};

View File

@ -43,36 +43,36 @@
#include "lnav_log.hh"
#include "lnav_util.hh"
#include "grep_proc.hh"
#include "listview_curses.hh"
#include "time_T.hh"
using namespace std;
grep_proc::grep_proc(pcre *code, grep_proc_source &gps)
template<typename LineType>
grep_proc<LineType>::grep_proc(pcre *code, grep_proc_source<LineType> &gps)
: gp_pcre(code),
gp_code(code),
gp_source(gps),
gp_pipe_offset(0),
gp_child(-1),
gp_child_started(false),
gp_last_line(0),
gp_sink(NULL),
gp_control(NULL)
gp_source(gps)
{
require(this->invariant());
gps.register_proc(this);
}
grep_proc::~grep_proc()
template<typename LineType>
grep_proc<LineType>::~grep_proc()
{
this->gp_queue.clear();
this->cleanup();
}
void grep_proc::handle_match(int line,
string &line_value,
int off,
int *matches,
int count)
template<typename LineType>
void grep_proc<LineType>::handle_match(int line,
string &line_value,
int off,
int *matches,
int count)
{
int lpc;
@ -94,7 +94,8 @@ void grep_proc::handle_match(int line,
}
}
void grep_proc::start(void)
template<typename LineType>
void grep_proc<LineType>::start()
{
require(this->invariant());
@ -133,6 +134,7 @@ void grep_proc::start(void)
require(this->gp_err_pipe.get() == -1);
this->gp_err_pipe = err_pipe.read_end();
this->gp_child_started = true;
this->gp_child_queue_size = this->gp_queue.size();
this->gp_queue.clear();
return;
@ -154,7 +156,8 @@ void grep_proc::start(void)
_exit(0);
}
void grep_proc::child_loop(void)
template<typename LineType>
void grep_proc<LineType>::child_loop()
{
char outbuf[BUFSIZ * 2];
string line_value;
@ -163,21 +166,18 @@ void grep_proc::child_loop(void)
if (setvbuf(stdout, outbuf, _IOFBF, BUFSIZ * 2) < 0) {
perror("setvbuf");
}
lnav_log_file = fopen("/tmp/lnav.grep.err", "a");
line_value.reserve(BUFSIZ * 2);
while (!this->gp_queue.empty()) {
grep_line_t start_line = this->gp_queue.front().first;
grep_line_t stop_line = this->gp_queue.front().second;
LineType start_line = this->gp_queue.front().first;
LineType stop_line = this->gp_queue.front().second;
bool done = false;
int line;
LineType line;
this->gp_queue.pop_front();
if (start_line == -1) {
start_line = this->gp_highest_line;
log_debug("highest %d", start_line);
}
for (line = start_line;
(stop_line == -1 || line < stop_line) && !done;
line++) {
for (line = this->gp_source.grep_initial_line(start_line, this->gp_highest_line);
line != -1 && (stop_line == -1 || line < stop_line) && !done;
this->gp_source.grep_next_line(line)) {
line_value.clear();
done = !this->gp_source.grep_value_for_line(line, line_value);
if (!done) {
@ -189,7 +189,7 @@ void grep_proc::child_loop(void)
pcre_context::capture_t *m;
if (pi.pi_offset == 0) {
fprintf(stdout, "%d\n", line);
fprintf(stdout, "%d\n", (int) line);
}
m = pc.all();
fprintf(stdout, "[%d:%d]\n", m->c_begin, m->c_end);
@ -234,7 +234,8 @@ void grep_proc::child_loop(void)
}
}
void grep_proc::cleanup(void)
template<typename LineType>
void grep_proc<LineType>::cleanup()
{
if (this->gp_child != -1 && this->gp_child != 0) {
int status = 0;
@ -248,7 +249,9 @@ void grep_proc::cleanup(void)
this->gp_child_started = false;
if (this->gp_sink) {
this->gp_sink->grep_end(*this);
for (int lpc = 0; lpc < this->gp_child_queue_size; lpc++) {
this->gp_sink->grep_end(*this);
}
}
}
@ -266,14 +269,18 @@ void grep_proc::cleanup(void)
}
}
void grep_proc::dispatch_line(char *line)
template<typename LineType>
void grep_proc<LineType>::dispatch_line(char *line)
{
int start, end, capture_start;
require(line != NULL);
if (sscanf(line, "h%d", this->gp_highest_line.out()) == 1) {
if (this->gp_sink) {
this->gp_sink->grep_end(*this);
}
this->gp_child_queue_size -= 1;
} else if (sscanf(line, "%d", this->gp_last_line.out()) == 1) {
/* Starting a new line with matches. */
ensure(this->gp_last_line >= 0);
@ -311,7 +318,8 @@ void grep_proc::dispatch_line(char *line)
}
}
void grep_proc::check_poll_set(const std::vector<struct pollfd> &pollfds)
template<typename LineType>
void grep_proc<LineType>::check_poll_set(const std::vector<struct pollfd> &pollfds)
{
require(this->invariant());
@ -327,10 +335,10 @@ void grep_proc::check_poll_set(const std::vector<struct pollfd> &pollfds)
if (strncmp(buffer, PREFIX, strlen(PREFIX)) == 0) {
char *lf;
if ((lf = strchr(buffer, '\n')) != NULL) {
if ((lf = strchr(buffer, '\n')) != nullptr) {
*lf = '\0';
}
if (this->gp_control != NULL) {
if (this->gp_control != nullptr) {
this->gp_control->grep_error(&buffer[strlen(PREFIX)]);
}
}
@ -355,7 +363,7 @@ void grep_proc::check_poll_set(const std::vector<struct pollfd> &pollfds)
loop_count += 1;
}
if (this->gp_sink != NULL) {
if (this->gp_sink != nullptr) {
this->gp_sink->grep_end_batch(*this);
}
@ -371,3 +379,5 @@ void grep_proc::check_poll_set(const std::vector<struct pollfd> &pollfds)
ensure(this->invariant());
}
template class grep_proc<vis_line_t>;

View File

@ -57,25 +57,41 @@
#include "strong_int.hh"
#include "line_buffer.hh"
/** Strongly-typed integer for matched line numbers. */
STRONG_INT_TYPE(int, grep_line);
template<typename LineType>
class grep_proc;
/**
* Data source for lines to be searched using a grep_proc.
*/
template<typename LineType>
class grep_proc_source {
public:
virtual ~grep_proc_source() { };
virtual void register_proc(grep_proc<LineType> *proc) {
this->gps_proc = proc;
}
/**
* Get the value for a particular line in the source.
*
* @param line The line to retrieve.
* @param value_out The destination for the line value.
*/
virtual bool grep_value_for_line(int line, std::string &value_out) = 0;
virtual bool grep_value_for_line(LineType line, std::string &value_out) = 0;
virtual LineType grep_initial_line(LineType start, LineType highest) {
if (start == -1) {
return highest;
}
return start;
};
virtual void grep_next_line(LineType &line) {
line = line + LineType(1);
};
grep_proc<LineType> *gps_proc;
};
/**
@ -93,18 +109,19 @@ public:
/**
* Sink for matches produced by a grep_proc instance.
*/
template<typename LineType>
class grep_proc_sink {
public:
virtual ~grep_proc_sink() { };
virtual ~grep_proc_sink() = default;
/** Called at the start of a new grep run. */
virtual void grep_begin(grep_proc &gp) { };
virtual void grep_begin(grep_proc<LineType> &gp, LineType start, LineType stop) { };
/** Called periodically between grep_begin and grep_end. */
virtual void grep_end_batch(grep_proc &gp) { };
virtual void grep_end_batch(grep_proc<LineType> &gp) { };
/** Called at the end of a grep run. */
virtual void grep_end(grep_proc &gp) { };
virtual void grep_end(grep_proc<LineType> &gp) { };
/**
* Called when a match is found on 'line' and between [start, end).
@ -114,8 +131,8 @@ public:
* @param end The offset of the character after the last character in the
* match.
*/
virtual void grep_match(grep_proc &gp,
grep_line_t line,
virtual void grep_match(grep_proc<LineType> &gp,
LineType line,
int start,
int end) = 0;
@ -128,13 +145,13 @@ public:
* capture.
* @param capture The captured substring itself.
*/
virtual void grep_capture(grep_proc &gp,
grep_line_t line,
virtual void grep_capture(grep_proc<LineType> &gp,
LineType line,
int start,
int end,
char *capture) { };
virtual void grep_match_end(grep_proc &gp, grep_line_t line) { };
virtual void grep_match_end(grep_proc<LineType> &gp, LineType line) { };
};
/**
@ -147,6 +164,7 @@ public:
* Note: The "grep" executable is not actually used, instead we use the pcre(3)
* library directly.
*/
template<typename LineType>
class grep_proc {
public:
class error
@ -165,7 +183,7 @@ public:
* @param code The pcre code to run over the lines of input.
* @param gps The source of the data to match.
*/
grep_proc(pcre *code, grep_proc_source &gps);
grep_proc(pcre *code, grep_proc_source<LineType> &gps);
virtual ~grep_proc();
@ -173,21 +191,14 @@ public:
pcre *get_code() { return this->gp_code; };
/** @param gpd The sink to send resuls to. */
void set_sink(grep_proc_sink *gpd)
void set_sink(grep_proc_sink<LineType> *gpd)
{
this->gp_sink = gpd;
this->reset();
};
void reset()
{
if (this->gp_sink != NULL) {
this->gp_sink->grep_begin(*this);
}
};
void invalidate() {
grep_proc &invalidate() {
this->cleanup();
return *this;
};
/** @param gpd The sink to send results to. */
@ -197,7 +208,7 @@ public:
};
/** @return The sink to send resuls to. */
grep_proc_sink *get_sink() { return this->gp_sink; };
grep_proc_sink<LineType> *get_sink() { return this->gp_sink; };
/**
* Queue a request to search the input between the given line numbers.
@ -206,19 +217,24 @@ public:
* @param stop The line number to stop the search at or -1 to read until
* the end-of-file.
*/
void queue_request(grep_line_t start = grep_line_t(0),
grep_line_t stop = grep_line_t(-1))
grep_proc &queue_request(LineType start = LineType(0),
LineType stop = LineType(-1))
{
require(start != -1 || stop == -1);
require(stop == -1 || start < stop);
this->gp_queue.push_back(std::make_pair(start, stop));
this->gp_queue.emplace_back(start, stop);
if (this->gp_sink) {
this->gp_sink->grep_begin(*this, start, stop);
}
return *this;
};
/**
* Start the search requests that have been queued up with queue_request.
*/
void start(void);
void start();
void update_poll_set(std::vector<struct pollfd> &pollfds)
{
@ -273,15 +289,15 @@ protected:
* Free any resources used by the object and make sure the child has been
* terminated.
*/
void cleanup(void);
void cleanup();
void child_loop(void);
void child_loop();
virtual void child_init(void) { };
virtual void child_init() { };
virtual void child_batch(void) { fflush(stdout); };
virtual void child_batch() { fflush(stdout); };
virtual void child_term(void) { fflush(stdout); };
virtual void child_term() { fflush(stdout); };
virtual void handle_match(int line,
std::string &line_value,
@ -291,31 +307,32 @@ protected:
pcrepp gp_pcre;
pcre * gp_code; /*< The compiled pattern. */
grep_proc_source & gp_source; /*< The data source delegate. */
grep_proc_source<LineType> &gp_source; /*< The data source delegate. */
auto_fd gp_err_pipe; /*< Standard error from the child. */
line_buffer gp_line_buffer; /*< Standard out from the child. */
off_t gp_pipe_offset;
off_t gp_pipe_offset{0};
pid_t gp_child; /*<
pid_t gp_child{-1}; /*<
* The child's pid or zero in the
* child.
*/
bool gp_child_started; /*< True if the child was start()'d. */
bool gp_child_started{false}; /*< True if the child was start()'d. */
size_t gp_child_queue_size{0};
/** The queue of search requests. */
std::deque<std::pair<grep_line_t, grep_line_t> > gp_queue;
grep_line_t gp_last_line; /*<
std::deque<std::pair<LineType, LineType> > gp_queue;
LineType gp_last_line{0}; /*<
* The last line number received from
* the child. For multiple matches,
* the line number is only sent once.
*/
grep_line_t gp_highest_line; /*< The highest numbered line processed
LineType gp_highest_line; /*< The highest numbered line processed
* by the grep child process. This
* value is used when the start line
* for a queued request is -1.
*/
grep_proc_sink * gp_sink; /*< The sink delegate. */
grep_proc_control *gp_control; /*< The control delegate. */
grep_proc_sink<LineType> *gp_sink{nullptr}; /*< The sink delegate. */
grep_proc_control *gp_control{nullptr}; /*< The control delegate. */
};
#endif

View File

@ -472,11 +472,32 @@ COMMANDS
relative-goto <line#|N%>
Move the current view up or down by the given amount.
next-mark error|warning|search|user|file|partition
comment <message>
Add a comment to the top line in the log view. The
comment will be saved in the session and will be available
the next time the file is loaded. Searches will also scan
the comment for any matches.
clear-comment Clear the comment that is attached to the top line in the
log view.
tag <tag1> [<tag2> [... <tagN>]]
Attach a tag to the top line in the log view. The tags are
prefixed with a '#', if they don't have one already. And,
like comments, they are saved in the session and
searchable.
untag <tag1> [<tag2> [... <tagN>]]
Detach a tag from the top line in the log view.
delete-tags <tag1> [<tag2> [... <tagN>]]
Detach the tags from all log lines.
next-mark error|warning|search|user|file|meta
Move to the next bookmark of the given type in the
current view.
prev-mark error|warning|search|user|file|partition
prev-mark error|warning|search|user|file|meta
Move to the previous bookmark of the given type in the
current view.

View File

@ -39,7 +39,10 @@
using namespace std;
void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out) {
std::multimap<std::string, help_text *> help_text::TAGGED;
void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out, bool synopsis_only)
{
static size_t body_indent = 2;
view_colors &vc = view_colors::singleton();
@ -114,10 +117,12 @@ void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out)
size_t line_start = body_indent;
bool break_all = false;
out.append("Synopsis", &view_curses::VC_STYLE, A_UNDERLINE)
.append("\n")
.append(body_indent, ' ')
.append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD);
if (!synopsis_only) {
out.append("Synopsis", &view_curses::VC_STYLE, A_UNDERLINE)
.append("\n");
}
out.append(body_indent, ' ')
.append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD);
for (auto &param : ht.ht_parameters) {
if (break_all ||
(int)(out.get_string().length() - start_index - line_start + 10) >=
@ -198,17 +203,21 @@ void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out)
out.append("]");
}
}
out.append("\n\n")
.append(body_indent, ' ')
.append(ht.ht_summary, &tws)
.append("\n");
if (!synopsis_only) {
out.append("\n\n")
.append(body_indent, ' ')
.append(ht.ht_summary, &tws)
.append("\n");
} else {
out.append("\n");
}
break;
}
default:
break;
}
if (!ht.ht_parameters.empty()) {
if (!synopsis_only && !ht.ht_parameters.empty()) {
size_t max_param_name_width = 0;
for (auto &param : ht.ht_parameters) {
@ -237,6 +246,66 @@ void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out)
.append("\n");
}
}
if (!synopsis_only && !ht.ht_tags.empty()) {
vector<string> tags;
for (const auto &tag : ht.ht_tags) {
auto tagged = help_text::TAGGED.equal_range(tag);
for (auto tag_iter = tagged.first;
tag_iter != tagged.second;
++tag_iter) {
if (tag_iter->second == &ht) {
continue;
}
help_text &related = *tag_iter->second;
if (!related.ht_opposites.empty() &&
find_if(related.ht_opposites.begin(),
related.ht_opposites.end(),
[&ht](const char *x) {
return strcmp(x, ht.ht_name) == 0;
}) == related.ht_opposites.end()) {
continue;
}
string name = related.ht_name;
switch (related.ht_context) {
case HC_COMMAND:
name = ":" + name;
break;
case HC_SQL_FUNCTION:
name = name + "()";
break;
default:
break;
}
tags.push_back(name);
}
}
stable_sort(tags.begin(), tags.end());
out.append("See Also", &view_curses::VC_STYLE, A_UNDERLINE)
.append("\n")
.append(body_indent, ' ');
bool first = true;
size_t line_start = out.length();
for (const auto &tag : tags) {
if (!first) {
out.append(", ");
}
if ((out.length() - line_start + tag.length()) > width) {
out.append("\n")
.append(body_indent, ' ');
line_start = out.length();
}
out.append(tag, &view_curses::VC_STYLE, A_BOLD);
first = false;
}
}
}
void format_example_text_for_term(const help_text &ht, int width, attr_line_t &out)

View File

@ -30,6 +30,7 @@
#ifndef LNAV_HELP_TEXT_FORMATTER_HH
#define LNAV_HELP_TEXT_FORMATTER_HH
#include <map>
#include <string>
#include <vector>
@ -65,34 +66,31 @@ struct help_example {
};
struct help_text {
help_context_t ht_context;
help_context_t ht_context{HC_NONE};
const char *ht_name;
const char *ht_summary;
const char *ht_flag_name;
const char *ht_description;
const char *ht_flag_name{nullptr};
const char *ht_description{nullptr};
std::vector<struct help_text> ht_parameters;
std::vector<struct help_example> ht_example;
help_nargs_t ht_nargs;
help_parameter_format_t ht_format;
help_nargs_t ht_nargs{HN_REQUIRED};
help_parameter_format_t ht_format{HPF_STRING};
std::vector<const char *> ht_enum_values;
std::vector<const char *> ht_tags;
std::vector<const char *> ht_opposites;
help_text() : ht_context(HC_NONE) {
help_text() {
};
help_text(const char *name, const char *summary = nullptr)
: ht_context(HC_NONE),
ht_name(name),
ht_summary(summary),
ht_flag_name(nullptr),
ht_description(nullptr),
ht_nargs(HN_REQUIRED),
ht_format(HPF_STRING) {
: ht_name(name),
ht_summary(summary) {
if (name[0] == ':') {
this->ht_context = HC_COMMAND;
this->ht_name = &name[1];
}
}
};
help_text &command() {
this->ht_context = HC_COMMAND;
@ -167,9 +165,27 @@ struct help_text {
this->ht_enum_values = enum_values;
return *this;
};
help_text &with_tags(const std::initializer_list<const char*> &tags) {
this->ht_tags = tags;
return *this;
};
help_text &with_opposites(const std::initializer_list<const char*> &opps) {
this->ht_opposites = opps;
return *this;
};
void index_tags() {
for (const auto &tag: this->ht_tags) {
TAGGED.insert(std::make_pair(tag, this));
}
};
static std::multimap<std::string, help_text *> TAGGED;
};
void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out);
void format_help_text_for_term(const help_text &ht, int width, attr_line_t &out, bool synopsis_only = false);
void format_example_text_for_term(const help_text &ht, int width, attr_line_t &out);
#endif //LNAV_HELP_TEXT_FORMATTER_HH

View File

@ -148,6 +148,52 @@ struct highlighter {
return this->h_attrs;
};
void annotate(attr_line_t &al, int start) const {
const std::string &str = al.get_string();
string_attrs_t &sa = al.get_attrs();
size_t re_end;
if (str.length() > 8192)
re_end = 8192;
else
re_end = str.length();
for (int off = start; off < (int)str.size(); ) {
int rc, matches[60];
rc = pcre_exec(this->h_code,
this->h_code_extra,
str.c_str(),
re_end,
off,
0,
matches,
60);
if (rc > 0) {
struct line_range lr;
if (rc == 2) {
lr.lr_start = matches[2];
lr.lr_end = matches[3];
}
else {
lr.lr_start = matches[0];
lr.lr_end = matches[1];
}
if (lr.lr_end > lr.lr_start) {
sa.emplace_back(lr, &view_curses::VC_STYLE, this->h_attrs);
off = matches[1];
}
else {
off += 1;
}
}
else {
off = str.size();
}
}
};
std::string h_pattern;
rgb_color h_fg;
rgb_color h_bg;

View File

@ -364,7 +364,7 @@ void handle_paging_key(int ch)
break;
case 'u': {
vis_line_t user_top, part_top;
vis_line_t user_top, meta_top;
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_1(c, "to copy marked lines to the clipboard; ")
@ -373,44 +373,39 @@ void handle_paging_key(int ch)
user_top = next_cluster(&bookmark_vector<vis_line_t>::next,
&textview_curses::BM_USER,
tc->get_top());
part_top = next_cluster(&bookmark_vector<vis_line_t>::next,
&textview_curses::BM_PARTITION,
meta_top = next_cluster(&bookmark_vector<vis_line_t>::next,
&textview_curses::BM_META,
tc->get_top());
if (part_top == -1 && user_top == -1) {
if (user_top == -1 && meta_top == -1) {
alerter::singleton().chime();
}
else if (part_top == -1) {
tc->set_top(user_top);
}
else if (user_top == -1) {
tc->set_top(part_top);
}
else {
tc->set_top(min(user_top, part_top));
if (user_top == -1) {
user_top = vis_line_t(INT_MAX);
}
if (meta_top == -1) {
meta_top = vis_line_t(INT_MAX);
}
tc->set_top(min(user_top, meta_top));
}
break;
}
case 'U': {
vis_line_t user_top, part_top;
vis_line_t user_top, meta_top;
user_top = next_cluster(&bookmark_vector<vis_line_t>::prev,
&textview_curses::BM_USER,
tc->get_top());
part_top = next_cluster(&bookmark_vector<vis_line_t>::prev,
&textview_curses::BM_PARTITION,
meta_top = next_cluster(&bookmark_vector<vis_line_t>::prev,
&textview_curses::BM_META,
tc->get_top());
if (part_top == -1 && user_top == -1) {
if (user_top == -1 && meta_top == -1) {
alerter::singleton().chime();
}
else if (part_top == -1) {
tc->set_top(user_top);
}
else if (user_top == -1) {
tc->set_top(part_top);
}
else {
tc->set_top(max(user_top, part_top));
tc->set_top(max(user_top, meta_top));
}
break;
}
@ -882,6 +877,7 @@ void handle_paging_key(int ch)
add_mark_possibilities();
add_config_possibilities();
add_env_possibilities(LNM_COMMAND);
add_tag_possibilities();
lnav_data.ld_mode = LNM_COMMAND;
lnav_data.ld_rl_view->focus(LNM_COMMAND, ":");
break;

View File

@ -100,6 +100,10 @@ struct string_fragment {
return std::string(this->data(), this->length());
}
std::string to_string() {
return std::string(&this->sf_string[this->sf_begin], this->length());
};
void clear() {
this->sf_begin = 0;
this->sf_end = 0;

View File

@ -40,12 +40,16 @@
#include "sqlite3.h"
#include "yajlpp.hh"
#include "json_op.hh"
#include "mapbox/variant.hpp"
#include "vtab_module.hh"
#include "yajl/api/yajl_gen.h"
#include "sqlite-extension-func.hh"
using namespace std;
using namespace mapbox;
#define JSON_SUBTYPE 74 /* Ascii for "J" */
@ -68,6 +72,61 @@ static void null_or_default(sqlite3_context *context, int argc, sqlite3_value **
}
}
struct contains_userdata {
util::variant<const char *, int64_t, bool> cu_match_value{false};
bool cu_result{false};
};
static int contains_string(void *ctx, const unsigned char *str, size_t len)
{
auto &cu = *((contains_userdata *) ctx);
if (strncmp((const char *) str, cu.cu_match_value.get<const char *>(), len) == 0) {
cu.cu_result = true;
}
return 1;
}
static int contains_integer(void *ctx, int64_t value)
{
auto &cu = *((contains_userdata *) ctx);
if (cu.cu_match_value.get<int64_t>() == value) {
cu.cu_result = true;
}
return 1;
}
static bool json_contains(const char *json_in, sqlite3_value *value)
{
auto_mem<yajl_handle_t> handle(yajl_free);
yajl_callbacks cb;
contains_userdata cu;
memset(&cb, 0, sizeof(cb));
handle = yajl_alloc(&cb, nullptr, &cu);
switch (sqlite3_value_type(value)) {
case SQLITE3_TEXT:
cb.yajl_string = contains_string;
cu.cu_match_value = (const char *) sqlite3_value_text(value);
break;
case SQLITE_INTEGER:
cb.yajl_integer = contains_integer;
cu.cu_match_value = sqlite3_value_int64(value);
break;
}
if (yajl_parse(handle.in(), (const unsigned char *) json_in, strlen(json_in)) != yajl_status_ok ||
yajl_complete_parse(handle.in()) != yajl_status_ok) {
throw yajlpp_error(handle.in(), json_in, strlen(json_in));
}
return cu.cu_result;
}
static int gen_handle_null(void *ctx)
{
sql_json_op *sjo = (sql_json_op *)ctx;
@ -142,7 +201,7 @@ static void sql_jget(sqlite3_context *context,
auto_mem<yajl_handle_t> handle(yajl_free);
const unsigned char *err;
gen = yajl_gen_alloc(NULL);
gen = yajl_gen_alloc(nullptr);
yajl_gen_config(gen.in(), yajl_gen_beautify, false);
jo.jo_ptr_callbacks = json_op::gen_callbacks;
@ -151,7 +210,7 @@ static void sql_jget(sqlite3_context *context,
jo.jo_ptr_callbacks.yajl_string = gen_handle_string;
jo.jo_ptr_data = gen.in();
handle.reset(yajl_alloc(&json_op::ptr_callbacks, NULL, &jo));
handle.reset(yajl_alloc(&json_op::ptr_callbacks, nullptr, &jo));
switch (yajl_parse(handle.in(), (const unsigned char *)json_in, strlen(json_in))) {
case yajl_status_error:
err = yajl_get_error(handle.in(), 0, (const unsigned char *)json_in, strlen(json_in));
@ -397,6 +456,16 @@ int json_extension_functions(struct FuncDef **basic_funcs,
struct FuncDefAgg **agg_funcs)
{
static struct FuncDef json_funcs[] = {
sqlite_func_adapter<decltype(&json_contains), json_contains>::builder(
help_text("json_contains", "")
.sql_function()
.with_parameter({"json", "The JSON value to query."})
.with_parameter({"value", "The value to look for in the first argument"})
.with_tags({"json"})
.with_example({"SELECT json_contains('[1, 2, 3]', 4)"})
.with_example({"SELECT json_contains('[\"abc\", \"def\"]', 'def')"})
),
{
"jget", -1, SQLITE_UTF8, 0, sql_jget,
help_text("jget",
@ -404,6 +473,7 @@ int json_extension_functions(struct FuncDef **basic_funcs,
.sql_function()
.with_parameter({"json", "The JSON object to query."})
.with_parameter({"ptr", "The JSON-Pointer to lookup in the object."})
.with_tags({"json"})
.with_example({"SELECT jget('1', '')"})
.with_example({"SELECT jget('{ \"a\": 1, \"b\": 2 }', '/b')"})
},

View File

@ -187,17 +187,13 @@ void listview_curses::do_update(void)
}
if (this->lv_needs_update) {
vis_line_t y(this->lv_y), height, bottom, row;
vis_line_t height, row;
attr_line_t overlay_line;
vis_line_t overlay_height(0);
struct line_range lr;
unsigned long width, wrap_width;
size_t row_count;
if (this->lv_overlay_source != NULL) {
overlay_height = vis_line_t(
this->lv_overlay_source->list_overlay_count(*this));
}
int y = this->lv_y, bottom;
this->get_dimensions(height, width);
@ -214,7 +210,8 @@ void listview_curses::do_update(void)
if (this->lv_overlay_source != NULL &&
this->lv_overlay_source->list_value_for_overlay(
*this,
y - vis_line_t(this->lv_y),
y - this->lv_y, bottom - this->lv_y,
row,
overlay_line)) {
this->mvwattrline(this->lv_window, y, this->lv_x, overlay_line, lr);
overlay_line.clear();
@ -254,10 +251,8 @@ void listview_curses::do_update(void)
coverage = (double)height / (double)row_count;
}
y = vis_line_t(this->lv_y) +
vis_line_t((int)(progress * (double)height));
lines = y + min(height, vis_line_t(
(int)(coverage * (double)height)));
y = this->lv_y + (int)(progress * (double)height);
lines = vis_line_t(y + min((int) height, (int)(coverage * (double)height)));
for (unsigned int gutter_y = this->lv_y;
gutter_y < (this->lv_y + height);
@ -293,6 +288,7 @@ void listview_curses::do_update(void)
this->lv_needs_update = false;
}
#if 0
else if (this->lv_overlay_needs_update && this->lv_overlay_source != NULL) {
vis_line_t y(this->lv_y), height, bottom;
attr_line_t overlay_line;
@ -318,6 +314,7 @@ void listview_curses::do_update(void)
++y;
}
}
#endif
}
static int scroll_polarity(mouse_button_t button)

View File

@ -101,30 +101,13 @@ public:
};
};
struct listview_overlay {
listview_overlay(int y, const attr_line_t &al) : lo_y(y), lo_line(al) { };
int get_absolute_y(int height) const
{
if (this->lo_y >= 0) {
return this->lo_y;
}
return height + this->lo_y;
};
int lo_y;
attr_line_t lo_line;
};
class list_overlay_source {
public:
virtual ~list_overlay_source() { };
virtual size_t list_overlay_count(const listview_curses &lv) = 0;
virtual bool list_value_for_overlay(const listview_curses &lv,
vis_line_t y,
int y, int bottom,
vis_line_t line,
attr_line_t &value_out) = 0;
};

View File

@ -64,6 +64,7 @@
#define _WCHAR_H_CPLUSPLUS_98_CONFORMANCE_
#endif
#include <map>
#include <memory>
#include <set>
#include <stack>
#include <vector>
@ -227,7 +228,7 @@ public:
next = bm[&textview_curses::BM_USER].next(vis_line_t(start));
if (next == -1) {
next = bm[&textview_curses::BM_PARTITION].next(vis_line_t(start));
next = bm[&textview_curses::BM_META].next(vis_line_t(start));
}
if (next != -1 && next <= end) {
ch = search_hit ? ACS_PLUS : ACS_LTEE;
@ -588,11 +589,10 @@ void rebuild_indexes(bool force)
}
}
if (new_data && lnav_data.ld_search_child[LNV_TEXT].get() != NULL) {
lnav_data.ld_search_child[LNV_TEXT]->get_grep_proc()->reset();
if (new_data && lnav_data.ld_search_child[LNV_TEXT]) {
lnav_data.ld_search_child[LNV_TEXT]->get_grep_proc()->
queue_request(grep_line_t(-1));
lnav_data.ld_search_child[LNV_TEXT]->get_grep_proc()->start();
queue_request(-1_vl)
.start();
}
text_view.reload_data();
}
@ -626,7 +626,7 @@ void rebuild_indexes(bool force)
if (lss.rebuild_index(force)) {
size_t new_count = lss.text_line_count();
grep_line_t start_line;
vis_line_t start_line;
if (!scroll_downs[LNV_LOG] && force) {
content_line_t new_top_content = content_line_t(-1);
@ -640,20 +640,22 @@ void rebuild_indexes(bool force)
}
}
start_line = force ? grep_line_t(0) : grep_line_t(-1);
start_line = force ? 0_vl : -1_vl;
if (force) {
if (lnav_data.ld_search_child[LNV_LOG].get() != NULL) {
if (lnav_data.ld_search_child[LNV_LOG]) {
lnav_data.ld_search_child[LNV_LOG]->get_grep_proc()->invalidate();
}
if (lnav_data.ld_meta_search) {
lnav_data.ld_meta_search->invalidate();
}
log_view.match_reset();
}
if (lnav_data.ld_search_child[LNV_LOG].get() != NULL) {
lnav_data.ld_search_child[LNV_LOG]->get_grep_proc()->reset();
if (lnav_data.ld_search_child[LNV_LOG]) {
lnav_data.ld_search_child[LNV_LOG]->get_grep_proc()->
queue_request(start_line);
lnav_data.ld_search_child[LNV_LOG]->get_grep_proc()->start();
queue_request(start_line)
.start();
}
log_view.reload_data();
@ -951,7 +953,7 @@ bool toggle_view(textview_curses *toggle_tc)
else if (toggle_tc == &lnav_data.ld_views[LNV_HELP]) {
build_all_help_text();
}
lnav_data.ld_last_view = NULL;
lnav_data.ld_last_view = nullptr;
lnav_data.ld_view_stack.push_back(toggle_tc);
retval = true;
}
@ -967,15 +969,19 @@ void redo_search(lnav_view_t view_index)
textview_curses *tc = &lnav_data.ld_views[view_index];
tc->reload_data();
if (lnav_data.ld_search_child[view_index].get() != NULL) {
grep_proc *gp = lnav_data.ld_search_child[view_index]->get_grep_proc();
if (lnav_data.ld_search_child[view_index] != NULL) {
grep_proc<vis_line_t> *gp = lnav_data.ld_search_child[view_index]->get_grep_proc();
gp->invalidate();
tc->match_reset();
gp->reset();
gp->queue_request(grep_line_t(0));
gp->queue_request(0_vl);
gp->start();
}
if (view_index == LNV_LOG && lnav_data.ld_meta_search) {
lnav_data.ld_meta_search->invalidate()
.queue_request(0_vl)
.start();
}
if (!lnav_data.ld_view_stack.empty() && tc == lnav_data.ld_view_stack.back()) {
lnav_data.ld_scroll_broadcaster.invoke(tc);
}
@ -989,7 +995,7 @@ void redo_search(lnav_view_t view_index)
*/
bool ensure_view(textview_curses *expected_tc)
{
textview_curses *tc = lnav_data.ld_view_stack.empty() ? NULL : lnav_data.ld_view_stack.back();
textview_curses *tc = lnav_data.ld_view_stack.empty() ? nullptr : lnav_data.ld_view_stack.back();
bool retval = true;
if (tc != expected_tc) {
@ -1140,19 +1146,19 @@ readline_context::command_map_t lnav_commands;
void execute_search(lnav_view_t view, const std::string &regex_orig)
{
unique_ptr<grep_highlighter> &gc = lnav_data.ld_search_child[view];
textview_curses & tc = lnav_data.ld_views[view];
textview_curses &tc = lnav_data.ld_views[view];
std::string regex = regex_orig;
pcre * code = NULL;
pcre *code = nullptr;
if ((gc.get() == NULL) || (regex != lnav_data.ld_last_search[view])) {
if ((gc.get() == nullptr) || (regex != lnav_data.ld_last_search[view])) {
const char *errptr;
int eoff;
bool quoted = false;
tc.match_reset();
if (regex.empty() && gc.get() != NULL) {
tc.grep_begin(*(gc->get_grep_proc()));
if (regex.empty() && gc != nullptr) {
tc.grep_begin(*(gc->get_grep_proc()), 0_vl, -1_vl);
tc.grep_end(*(gc->get_grep_proc()));
}
gc.reset();
@ -1166,7 +1172,7 @@ void execute_search(lnav_view_t view, const std::string &regex_orig)
PCRE_CASELESS,
&errptr,
&eoff,
NULL)) == NULL) {
nullptr)) == nullptr) {
string errmsg = string(errptr);
quoted = true;
@ -1177,7 +1183,7 @@ void execute_search(lnav_view_t view, const std::string &regex_orig)
PCRE_CASELESS,
&errptr,
&eoff,
NULL)) == NULL) {
nullptr)) == nullptr) {
log_error("Unable to compile quoted regex: %s", regex.c_str());
} else {
lnav_data.ld_bottom_source.grep_error(
@ -1187,7 +1193,7 @@ void execute_search(lnav_view_t view, const std::string &regex_orig)
}
}
if (code != NULL) {
if (code != nullptr) {
highlighter hl(code);
hl.with_role(view_colors::VCR_SEARCH);
@ -1200,23 +1206,34 @@ void execute_search(lnav_view_t view, const std::string &regex_orig)
textview_curses::highlight_map_t &hm = tc.get_highlights();
hm["$search"] = hl;
unique_ptr<grep_proc> gp(new grep_proc(code, tc));
unique_ptr<grep_proc<vis_line_t>> gp = make_unique<grep_proc<vis_line_t>>(code, tc);
gp->queue_request(grep_line_t(tc.get_top()));
gp->set_sink(&tc);
gp->queue_request(tc.get_top());
if (tc.get_top() > 0) {
gp->queue_request(grep_line_t(0), grep_line_t(tc.get_top()));
gp->queue_request(0_vl, tc.get_top());
}
gp->start();
gp->set_sink(&tc);
gc.reset(new grep_highlighter(gp, "$search", hm));
gc = std::make_unique<grep_highlighter>(gp, "$search", hm);
if (view == LNV_LOG) {
logfile_sub_source::meta_grepper &mg = lnav_data.ld_log_source.get_meta_grepper();
shared_ptr<grep_proc<vis_line_t>> mgp = make_shared<grep_proc<vis_line_t>>(code, mg);
mgp->set_sink(&mg);
mgp->queue_request(0_vl);
mgp->start();
lnav_data.ld_meta_search = mgp;
}
}
if (view == LNV_LOG) {
static intern_string_t log_search_name = intern_string::lookup("log_search");
lnav_data.ld_vtab_manager->unregister_vtab(log_search_name);
if (code != NULL) {
if (code != nullptr) {
lnav_data.ld_vtab_manager->register_vtab(new log_search_table(
regex.c_str(), log_search_name));
}
@ -1226,7 +1243,7 @@ void execute_search(lnav_view_t view, const std::string &regex_orig)
lnav_data.ld_last_search[view] = regex;
}
static void usage(void)
static void usage()
{
const char *usage_msg =
"usage: %s [options] [logfile1 logfile2 ...]\n"
@ -1486,7 +1503,7 @@ static void expand_filename(string path, bool required)
for (lpc = 0; lpc < (int)gl->gl_pathc; lpc++) {
auto_mem<char> abspath;
if ((abspath = realpath(gl->gl_pathv[lpc], NULL)) == NULL) {
if ((abspath = realpath(gl->gl_pathv[lpc], nullptr)) == NULL) {
if (required) {
fprintf(stderr, "Cannot find file: %s -- %s",
gl->gl_pathv[lpc], strerror(errno));
@ -1980,7 +1997,9 @@ static void execute_examples()
ex.he_result.append(dls.dls_rows[0][0]);
} else {
attr_line_t al;
dos.list_value_for_overlay(db_tc, vis_line_t(0),
dos.list_value_for_overlay(db_tc,
0, 1,
vis_line_t(0),
al);
ex.he_result.append(al);
for (int lpc = 0;
@ -2321,6 +2340,9 @@ static void looper(void)
gc->get_grep_proc()->update_poll_set(pollfds);
}
}
if (lnav_data.ld_meta_search) {
lnav_data.ld_meta_search->update_poll_set(pollfds);
}
rc = poll(&pollfds[0], pollfds.size(), to.tv_usec / 1000);
@ -2414,6 +2436,9 @@ static void looper(void)
}
}
}
if (lnav_data.ld_meta_search) {
lnav_data.ld_meta_search->check_poll_set(pollfds);
}
rlc.check_poll_set(pollfds);
}
@ -2531,6 +2556,10 @@ static void looper(void)
gather_pipers();
}
if (lnav_data.ld_meta_search) {
lnav_data.ld_meta_search->start();
}
if (lnav_data.ld_view_stack.empty() ||
(lnav_data.ld_view_stack.size() == 1 &&
starting_view_stack_size == 2 &&
@ -2550,6 +2579,10 @@ void wait_for_children()
vector<struct pollfd> pollfds;
struct timeval to = { 0, 333000 };
if (lnav_data.ld_meta_search) {
lnav_data.ld_meta_search->start();
}
do {
pollfds.clear();
@ -2558,6 +2591,9 @@ void wait_for_children()
gc->get_grep_proc()->update_poll_set(pollfds);
}
}
if (lnav_data.ld_meta_search) {
lnav_data.ld_meta_search->update_poll_set(pollfds);
}
if (pollfds.empty()) {
return;
@ -2585,6 +2621,9 @@ void wait_for_children()
}
}
}
if (lnav_data.ld_meta_search) {
lnav_data.ld_meta_search->check_poll_set(pollfds);
}
} while (true);
}
@ -3628,13 +3667,14 @@ int main(int argc, char *argv[])
los = tc->get_overlay_source();
for (vis_line_t vl = tc->get_top();
vis_line_t vl;
for (vl = tc->get_top();
vl < tc->get_inner_height();
++vl, ++y) {
attr_line_t al;
string &line = al.get_string();
while (los != NULL &&
los->list_value_for_overlay(*tc, y, al)) {
while (los != nullptr &&
los->list_value_for_overlay(*tc, y, tc->get_inner_height(), vl, al)) {
if (write(STDOUT_FILENO, line.c_str(),
line.length()) == -1 ||
write(STDOUT_FILENO, "\n", 1) == -1) {
@ -3656,6 +3696,22 @@ int main(int argc, char *argv[])
write(STDOUT_FILENO, "\n", 1) == -1) {
perror("2 write to STDOUT");
}
}
{
attr_line_t al;
string &line = al.get_string();
while (los != nullptr &&
los->list_value_for_overlay(*tc, y, tc->get_inner_height(), vl, al) &&
!al.empty()) {
if (write(STDOUT_FILENO, line.c_str(),
line.length()) == -1 ||
write(STDOUT_FILENO, "\n", 1) == -1) {
perror("1 write to STDOUT");
}
++y;
}
}
}
}

View File

@ -266,6 +266,7 @@ struct _lnav_data {
textview_curses *ld_last_view;
textview_curses ld_views[LNV__MAX];
std::unique_ptr<grep_highlighter> ld_search_child[LNV__MAX];
std::shared_ptr<grep_proc<vis_line_t>> ld_meta_search;
vis_line_t ld_search_start_line;
readline_curses * ld_rl_view;

File diff suppressed because it is too large Load Diff

View File

@ -264,7 +264,7 @@ void log_msg(lnav_log_level_t level, const char *src_file, int line_number,
}
line[prefix_size + rc] = '\n';
log_ring.lr_length += prefix_size + rc + 1;
if (lnav_log_file != NULL) {
if (lnav_log_file != nullptr) {
fwrite(line, 1, prefix_size + rc + 1, lnav_log_file);
fflush(lnav_log_file);
}
@ -283,7 +283,7 @@ void log_msg_extra(const char *fmt, ...)
line = log_alloc();
rc = vsnprintf(line, MAX_LOG_LINE_SIZE - 1, fmt, args);
log_ring.lr_length += rc;
if (lnav_log_file != NULL) {
if (lnav_log_file != nullptr) {
fwrite(line, 1, rc, lnav_log_file);
fflush(lnav_log_file);
}
@ -299,7 +299,7 @@ void log_msg_extra_complete()
line = log_alloc();
line[0] = '\n';
log_ring.lr_length += 1;
if (lnav_log_file != NULL) {
if (lnav_log_file != nullptr) {
fwrite(line, 1, 1, lnav_log_file);
fflush(lnav_log_file);
}
@ -315,7 +315,7 @@ static void sigabrt(int sig)
struct tm localtm;
time_t curr_time;
if (lnav_log_crash_dir == NULL) {
if (lnav_log_crash_dir == nullptr) {
printf("%*s", (int) log_ring.lr_length, log_ring.lr_data);
return;
}
@ -325,7 +325,7 @@ static void sigabrt(int sig)
#ifdef HAVE_EXECINFO_H
frame_count = backtrace(frames, 128);
#endif
curr_time = time(NULL);
curr_time = time(nullptr);
localtime_r(&curr_time, &localtm);
snprintf(crash_path, sizeof(crash_path),
"%s/crash-%4d-%02d-%02d-%02d-%02d-%02d.%d.log",

View File

@ -78,6 +78,22 @@ inline std::string tolower(const std::string &str)
return tolower(str.c_str());
}
inline std::string toupper(const char *str)
{
std::string retval;
for (int lpc = 0; str[lpc]; lpc++) {
retval.push_back(::toupper(str[lpc]));
}
return retval;
}
inline std::string toupper(const std::string &str)
{
return toupper(str.c_str());
}
size_t unquote(char *dst, const char *str, size_t len);
#undef rounddown

View File

@ -1005,6 +1005,10 @@ void external_log_format::rewrite(exec_context &ec,
continue;
}
ec.ec_source.emplace(this->elf_name.to_string() +
":" +
vd_iter->first.to_string(),
1);
string field_value = execute_any(ec, vd.vd_rewriter);
struct line_range adj_origin = iter->origin_in_full_msg(
value_out.c_str(), value_out.length());

View File

@ -692,11 +692,17 @@ static void write_sample_file(void)
}
}
static void format_error_reporter(const yajlpp_parse_context &ypc, const char *msg)
static void format_error_reporter(const yajlpp_parse_context &ypc,
lnav_log_level_t level,
const char *msg)
{
struct userdata *ud = (userdata *) ypc.ypc_userdata;
if (level >= LOG_LEVEL_ERROR) {
struct userdata *ud = (userdata *) ypc.ypc_userdata;
ud->ud_errors->push_back(msg);
ud->ud_errors->push_back(msg);
} else {
fprintf(stderr, "warning:%s\n", msg);
}
}
std::vector<intern_string_t> load_format_file(const string &filename, std::vector<string> &errors)

View File

@ -29,6 +29,7 @@
#include "config.h"
#include "lnav.hh"
#include "lnav_log.hh"
#include "sql_util.hh"
#include "log_vtab_impl.hh"
@ -39,7 +40,7 @@ using namespace std;
static struct log_cursor log_cursor_latest;
sql_progress_callback_t log_vtab_progress_callback;
struct _log_vtab_data log_vtab_data;
static const char *type_to_string(int type)
{
@ -59,7 +60,7 @@ static const char *type_to_string(int type)
return NULL;
}
std::string log_vtab_impl::get_table_statement(void)
std::string log_vtab_impl::get_table_statement()
{
std::vector<log_vtab_impl::vtab_column> cols;
std::vector<log_vtab_impl::vtab_column>::const_iterator iter;
@ -74,6 +75,8 @@ std::string log_vtab_impl::get_table_statement(void)
<< " log_idle_msecs INTEGER,\n"
<< " log_level TEXT COLLATE loglevel,\n"
<< " log_mark BOOLEAN,\n"
<< " log_comment TEXT,\n"
<< " log_tags TEXT,\n"
<< " -- BEGIN Format-specific fields:\n";
this->get_columns(cols);
this->vi_column_count = cols.size();
@ -272,8 +275,8 @@ static int vt_next(sqlite3_vtab_cursor *cur)
do {
log_cursor_latest = vc->log_cursor;
if (((log_cursor_latest.lc_curr_line % 1024) == 0) &&
(log_vtab_progress_callback != NULL &&
log_vtab_progress_callback(log_cursor_latest))) {
(log_vtab_data.lvd_progress != NULL &&
log_vtab_data.lvd_progress(log_cursor_latest))) {
break;
}
done = vt->vi->next(vc->log_cursor, *vt->lss);
@ -304,7 +307,7 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col)
case VT_COL_PARTITION:
{
vis_bookmarks &vb = vt->tc->get_bookmarks();
bookmark_vector<vis_line_t> &bv = vb[&textview_curses::BM_PARTITION];
bookmark_vector<vis_line_t> &bv = vb[&textview_curses::BM_META];
bookmark_vector<vis_line_t>::iterator iter;
vis_line_t curr_line;
@ -323,7 +326,7 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col)
std::map<content_line_t, bookmark_metadata>::iterator meta_iter;
meta_iter = bm_meta.find(part_line);
if (meta_iter != bm_meta.end()) {
if (meta_iter != bm_meta.end() && !meta_iter->second.bm_name.empty()) {
sqlite3_result_text(ctx,
meta_iter->second.bm_name.c_str(),
meta_iter->second.bm_name.size(),
@ -416,6 +419,54 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col)
}
break;
case VT_COL_LOG_COMMENT: {
const map<content_line_t, bookmark_metadata> &bm = vt->lss->get_user_bookmark_metadata();
auto bm_iter = bm.find(vt->lss->at(vc->log_cursor.lc_curr_line));
if (bm_iter == bm.end() || bm_iter->second.bm_comment.empty()) {
sqlite3_result_null(ctx);
} else {
const bookmark_metadata &meta = bm_iter->second;
sqlite3_result_text(ctx,
meta.bm_comment.c_str(),
meta.bm_comment.length(),
SQLITE_TRANSIENT);
}
break;
}
case VT_COL_LOG_TAGS: {
const map<content_line_t, bookmark_metadata> &bm = vt->lss->get_user_bookmark_metadata();
auto bm_iter = bm.find(vt->lss->at(vc->log_cursor.lc_curr_line));
if (bm_iter == bm.end() || bm_iter->second.bm_tags.empty()) {
sqlite3_result_null(ctx);
} else {
const bookmark_metadata &meta = bm_iter->second;
yajlpp_gen gen;
yajl_gen_config(gen, yajl_gen_beautify, false);
{
yajlpp_array arr(gen);
for (auto str : meta.bm_tags) {
arr.gen(str);
}
}
string_fragment sf = gen.to_string_fragment();
sqlite3_result_text(ctx,
sf.data(),
sf.length(),
SQLITE_TRANSIENT);
sqlite3_result_subtype(ctx, 'J');
}
break;
}
default:
if (col > (VT_COL_MAX + vt->vi->vi_column_count - 1)) {
int post_col_number = col -
@ -708,6 +759,16 @@ static int vt_best_index(sqlite3_vtab *tab, sqlite3_index_info *p_info)
return SQLITE_OK;
}
static struct json_path_handler tags_handler[] = {
json_path_handler("#")
.with_synopsis("<tag>")
.with_description("A tag for the log line")
.with_pattern(R"(^#[^\s]+$)")
.for_field(&nullobj<bookmark_metadata>()->bm_tags),
json_path_handler()
};
static int vt_update(sqlite3_vtab *tab,
int argc,
sqlite3_value **argv,
@ -724,33 +785,66 @@ static int vt_update(sqlite3_vtab *tab,
std::map<content_line_t, bookmark_metadata> &bm = vt->lss->get_user_bookmark_metadata();
const unsigned char *part_name = sqlite3_value_text(argv[2 + VT_COL_PARTITION]);
const unsigned char *log_comment = sqlite3_value_text(argv[2 + VT_COL_LOG_COMMENT]);
const unsigned char *log_tags = sqlite3_value_text(argv[2 + VT_COL_LOG_TAGS]);
bookmark_vector<vis_line_t> &bv = vt->tc->get_bookmarks()[
&textview_curses::BM_PARTITION];
bookmark_vector<vis_line_t>::iterator part_iter;
bool set_name = false;
&textview_curses::BM_META];
bool has_meta = part_name != nullptr || log_comment != nullptr ||
log_tags != nullptr;
if ((part_iter = find(bv.begin(), bv.end(), vrowid)) != bv.end()) {
if (part_name == NULL) {
vt->tc->set_user_mark(&textview_curses::BM_PARTITION, vrowid, false);
bm.erase(vt->lss->at(vrowid));
}
else {
set_name = true;
}
}
else if (part_name != NULL) {
vt->tc->set_user_mark(&textview_curses::BM_PARTITION, vrowid, true);
set_name = true;
if (binary_search(bv.begin(), bv.end(), vrowid) && !has_meta) {
vt->tc->set_user_mark(&textview_curses::BM_META, vrowid, false);
bm.erase(vt->lss->at(vrowid));
}
if (set_name) {
if (has_meta) {
bookmark_metadata &line_meta = bm[vt->lss->at(vrowid)];
line_meta.bm_name = string((const char *) part_name);
vt->tc->set_user_mark(&textview_curses::BM_META, vrowid, true);
if (part_name) {
line_meta.bm_name = string((const char *) part_name);
} else {
line_meta.bm_name.clear();
}
if (log_comment) {
line_meta.bm_comment = string((const char *) log_comment);
} else {
line_meta.bm_comment.clear();
}
if (log_tags) {
vector<string> errors;
yajlpp_parse_context ypc(log_vtab_data.lvd_source, tags_handler);
auto_mem<yajl_handle_t> handle(yajl_free);
line_meta.bm_tags.clear();
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
ypc.ypc_userdata = &errors;
ypc.ypc_line_number = log_vtab_data.lvd_line_number;
ypc.with_handle(handle)
.with_error_reporter([](const yajlpp_parse_context &ypc,
lnav_log_level_t level,
const char *msg) {
vector<string> &errors = *((vector<string> *) ypc.ypc_userdata);
errors.emplace_back(msg);
})
.with_obj(line_meta);
ypc.parse(log_tags, strlen((const char *) log_tags));
ypc.complete_parse();
if (!errors.empty()) {
tab->zErrMsg = sqlite3_mprintf("%s",
join(errors.begin(), errors.end(), "\n").c_str());
retval = SQLITE_ERROR;
}
for (const auto &tag : line_meta.bm_tags) {
bookmark_metadata::KNOWN_TAGS.insert(tag);
}
} else {
line_meta.bm_tags.clear();
}
}
vt->tc->set_user_mark(&textview_curses::BM_USER, vis_line_t(rowid), val);
vt->tc->set_user_mark(&textview_curses::BM_USER, vrowid, val);
rowid += 1;
while ((size_t)rowid < vt->lss->text_line_count()) {
vis_line_t vl(rowid);
@ -763,7 +857,9 @@ static int vt_update(sqlite3_vtab *tab,
rowid += 1;
}
retval = SQLITE_OK;
if (retval != SQLITE_ERROR) {
retval = SQLITE_OK;
}
}
return retval;
@ -795,8 +891,8 @@ static int progress_callback(void *ptr)
{
int retval = 0;
if (log_vtab_progress_callback != NULL) {
retval = log_vtab_progress_callback(log_cursor_latest);
if (log_vtab_data.lvd_progress != NULL) {
retval = log_vtab_data.lvd_progress(log_cursor_latest);
}
return retval;

View File

@ -47,6 +47,8 @@ enum {
VT_COL_IDLE_MSECS,
VT_COL_LEVEL,
VT_COL_MARK,
VT_COL_LOG_COMMENT,
VT_COL_LOG_TAGS,
VT_COL_MAX
};
@ -188,16 +190,26 @@ protected:
typedef int (*sql_progress_callback_t)(const log_cursor &lc);
extern sql_progress_callback_t log_vtab_progress_callback;
extern struct _log_vtab_data {
sql_progress_callback_t lvd_progress;
std::string lvd_source;
int lvd_line_number{0};
} log_vtab_data;
class sql_progress_guard {
public:
sql_progress_guard(sql_progress_callback_t cb) {
log_vtab_progress_callback = cb;
sql_progress_guard(sql_progress_callback_t cb,
const std::string &source,
int line_number) {
log_vtab_data.lvd_progress = cb;
log_vtab_data.lvd_source = source;
log_vtab_data.lvd_line_number = line_number;
};
~sql_progress_guard() {
log_vtab_progress_callback = NULL;
log_vtab_data.lvd_progress = NULL;
log_vtab_data.lvd_source.clear();
log_vtab_data.lvd_line_number = 0;
};
};

View File

@ -54,21 +54,10 @@ static const size_t MAX_UNRECOGNIZED_LINES = 1000;
static const size_t INDEX_RESERVE_INCREMENT = 1024;
logfile::logfile(const string &filename, logfile_open_options &loo)
: lf_filename(filename),
lf_index_time(0),
lf_index_size(0),
lf_sort_needed(false),
lf_is_closed(false),
lf_logline_observer(NULL),
lf_logfile_observer(NULL),
lf_longest_line(0),
lf_text_format(TF_UNKNOWN)
: lf_filename(filename)
{
require(filename.size() > 0);
this->lf_time_offset.tv_sec = 0;
this->lf_time_offset.tv_usec = 0;
memset(&this->lf_stat, 0, sizeof(this->lf_stat));
if (loo.loo_fd == -1) {
char resolved_path[PATH_MAX];
@ -372,7 +361,7 @@ logfile::rebuild_result_t logfile::rebuild_index()
old_size = 0;
}
for (logfile::iterator iter = this->begin() + old_size;
for (auto iter = this->begin() + old_size;
iter != this->end(); ++iter) {
if (this->lf_logline_observer != NULL) {
this->lf_logline_observer->logline_new_line(*this, iter, sbr);
@ -386,7 +375,7 @@ logfile::rebuild_result_t logfile::rebuild_index()
st.st_size);
}
if (!has_format && this->lf_format.get() != NULL) {
if (!has_format && this->lf_format != NULL) {
break;
}
}
@ -438,7 +427,7 @@ void logfile::read_line(logfile::iterator ll, string &line_out)
line_out.clear();
if (this->lf_line_buffer.read_line(off, sbr)) {
if (this->lf_format.get() != NULL) {
if (this->lf_format != NULL) {
this->lf_format->get_subline(*ll, sbr);
}
line_out.append(sbr.get_data(), sbr.length());
@ -458,7 +447,7 @@ bool logfile::read_line(logfile::iterator ll, shared_buffer_ref &sbr)
off_t off = ll->get_offset();
if (this->lf_line_buffer.read_line(off, sbr)) {
if (this->lf_format.get() != NULL) {
if (this->lf_format != NULL) {
this->lf_format->get_subline(*ll, sbr);
}
return true;

View File

@ -423,18 +423,18 @@ protected:
struct stat lf_stat;
std::unique_ptr<log_format> lf_format;
std::vector<logline> lf_index;
time_t lf_index_time;
off_t lf_index_size;
bool lf_sort_needed;
time_t lf_index_time{0};
off_t lf_index_size{0};
bool lf_sort_needed{false};
line_buffer lf_line_buffer;
int lf_time_offset_line;
struct timeval lf_time_offset;
bool lf_is_closed;
bool lf_partial_line;
logline_observer *lf_logline_observer;
logfile_observer *lf_logfile_observer;
size_t lf_longest_line;
text_format_t lf_text_format;
int lf_time_offset_line{0};
struct timeval lf_time_offset{0, 0};
bool lf_is_closed{false};
bool lf_partial_line{false};
logline_observer *lf_logline_observer{nullptr};
logfile_observer *lf_logfile_observer{nullptr};
size_t lf_longest_line{0};
text_format_t lf_text_format{TF_UNKNOWN};
};
class logline_observer {

View File

@ -101,8 +101,10 @@ logfile_sub_source::logfile_sub_source()
lss_force_rebuild(false),
lss_token_file(NULL),
lss_min_log_level(logline::LEVEL_UNKNOWN),
lss_marked_only(false),
lss_index_delegate(NULL),
lss_longest_line(0)
lss_longest_line(0),
lss_meta_grepper(*this)
{
this->clear_line_size_cache();
this->clear_min_max_log_times();
@ -485,7 +487,7 @@ void logfile_sub_source::text_attrs_for_line(textview_curses &lv,
this->lss_token_file->get_format()->get_name());
{
bookmark_vector<vis_line_t> &bv = lv.get_bookmarks()[&textview_curses::BM_PARTITION];
bookmark_vector<vis_line_t> &bv = lv.get_bookmarks()[&textview_curses::BM_META];
bookmark_vector<vis_line_t>::iterator bv_iter;
bv_iter = lower_bound(bv.begin(), bv.end(), vis_line_t(row + 1));
@ -495,7 +497,8 @@ void logfile_sub_source::text_attrs_for_line(textview_curses &lv,
std::map<content_line_t, bookmark_metadata>::iterator bm_iter;
if ((bm_iter = this->lss_user_mark_metadata.find(part_start_line))
!= this->lss_user_mark_metadata.end()) {
!= this->lss_user_mark_metadata.end() &&
!bm_iter->second.bm_name.empty()) {
lr.lr_start = 0;
lr.lr_end = -1;
value_out.emplace_back(lr, &logline::L_PARTITION, &bm_iter->second);
@ -706,7 +709,7 @@ bool logfile_sub_source::rebuild_index(bool force)
content_line_t cl = (content_line_t) this->lss_index[index_index];
uint64_t line_number;
logfile_data *ld = this->find_data(cl, line_number);
logfile::iterator line_iter = ld->get_file()->begin() + line_number;
auto line_iter = ld->get_file()->begin() + line_number;
if (!ld->ld_filter_state.excluded(filter_in_mask, filter_out_mask,
line_number) && this->check_extra_filters(*line_iter)) {
@ -719,7 +722,7 @@ bool logfile_sub_source::rebuild_index(bool force)
}
}
if (this->lss_index_delegate != NULL) {
if (this->lss_index_delegate != nullptr) {
this->lss_index_delegate->index_complete(*this);
}
}
@ -729,37 +732,31 @@ bool logfile_sub_source::rebuild_index(bool force)
void logfile_sub_source::text_update_marks(vis_bookmarks &bm)
{
shared_ptr<logfile> last_file = NULL;
shared_ptr<logfile> last_file = nullptr;
vis_line_t vl;
bm[&BM_WARNINGS].clear();
bm[&BM_ERRORS].clear();
bm[&BM_FILES].clear();
for (bookmarks<content_line_t>::type::iterator iter =
this->lss_user_marks.begin();
iter != this->lss_user_marks.end();
++iter) {
bm[iter->first].clear();
for (auto &lss_user_mark : this->lss_user_marks) {
bm[lss_user_mark.first].clear();
}
for (; vl < (int)this->lss_filtered_index.size(); ++vl) {
const content_line_t orig_cl = this->at(vl);
content_line_t cl = orig_cl;
shared_ptr<logfile> lf;
shared_ptr<logfile> lf;
lf = this->find(cl);
for (bookmarks<content_line_t>::type::iterator iter =
this->lss_user_marks.begin();
iter != this->lss_user_marks.end();
++iter) {
if (binary_search(iter->second.begin(),
iter->second.end(),
for (auto &lss_user_mark : this->lss_user_marks) {
if (binary_search(lss_user_mark.second.begin(),
lss_user_mark.second.end(),
orig_cl)) {
bm[iter->first].insert_once(vl);
bm[lss_user_mark.first].insert_once(vl);
if (iter->first == &textview_curses::BM_USER) {
if (lss_user_mark.first == &textview_curses::BM_USER) {
logfile::iterator ll = lf->begin() + cl;
ll->set_mark(true);
@ -771,7 +768,7 @@ void logfile_sub_source::text_update_marks(vis_bookmarks &bm)
bm[&BM_FILES].insert_once(vl);
}
logfile::iterator line_iter = lf->begin() + cl;
auto line_iter = lf->begin() + cl;
if (!line_iter->is_continued()) {
switch (line_iter->get_msg_level()) {
case logline::LEVEL_WARNING:

View File

@ -37,6 +37,7 @@
#include <map>
#include <list>
#include <sstream>
#include <utility>
#include <vector>
#include <algorithm>
@ -89,17 +90,12 @@ public:
logfile_sub_source();
virtual ~logfile_sub_source();
void toggle_scrub(void) {
this->lss_flags ^= F_SCRUB;
this->clear_line_size_cache();
};
void toggle_time_offset(void) {
void toggle_time_offset() {
this->lss_flags ^= F_TIME_OFFSET;
this->clear_line_size_cache();
};
void increase_line_context(void) {
void increase_line_context() {
auto old_flags = this->lss_flags;
if (this->lss_flags & F_FILENAME) {
@ -115,7 +111,7 @@ public:
}
};
bool decrease_line_context(void) {
bool decrease_line_context() {
auto old_flags = this->lss_flags;
if (this->lss_flags & F_FILENAME) {
@ -141,19 +137,19 @@ public:
this->clear_line_size_cache();
};
bool is_time_offset_enabled(void) const {
bool is_time_offset_enabled() const {
return (bool) (this->lss_flags & F_TIME_OFFSET);
};
bool is_filename_enabled(void) const {
bool is_filename_enabled() const {
return (bool) (this->lss_flags & F_FILENAME);
};
bool is_basename_enabled(void) const {
bool is_basename_enabled() const {
return (bool) (this->lss_flags & F_BASENAME);
};
logline::level_t get_min_log_level(void) const {
logline::level_t get_min_log_level() const {
return this->lss_min_log_level;
};
@ -199,6 +195,17 @@ public:
bool list_input_handle_key(listview_curses &lv, int ch);
void set_marked_only(bool val) {
if (this->lss_marked_only != val) {
this->lss_marked_only = val;
this->lss_force_rebuild = true;
}
};
bool get_marked_only() {
return this->lss_marked_only;
};
size_t text_line_count()
{
return this->lss_filtered_index.size();
@ -245,13 +252,13 @@ public:
return this->lss_line_size_cache[index].second;
};
void text_mark(bookmark_type_t *bm, int line, bool added)
void text_mark(bookmark_type_t *bm, vis_line_t line, bool added)
{
if (line >= (int) this->lss_index.size()) {
return;
}
content_line_t cl = this->at(vis_line_t(line));
content_line_t cl = this->at(line);
std::vector<content_line_t>::iterator lb;
if (bm == &textview_curses::BM_USER) {
@ -272,6 +279,10 @@ public:
this->lss_user_marks[bm].erase(lb);
}
if (bm == &textview_curses::BM_META &&
this->lss_meta_grepper.gps_proc != nullptr) {
this->lss_meta_grepper.gps_proc->queue_request(line, line + 1_vl);
}
};
void text_clear_marks(bookmark_type_t *bm)
@ -280,12 +291,18 @@ public:
if (bm == &textview_curses::BM_USER) {
for (iter = this->lss_user_marks[bm].begin();
iter != this->lss_user_marks[bm].end();
++iter) {
iter != this->lss_user_marks[bm].end();) {
auto bm_iter = this->lss_user_mark_metadata.find(*iter);
if (bm_iter != this->lss_user_mark_metadata.end()) {
++iter;
continue;
}
this->find_line(*iter)->set_mark(false);
iter = this->lss_user_marks[bm].erase(iter);
}
} else {
this->lss_user_marks[bm].clear();
}
this->lss_user_marks[bm].clear();
};
bool insert_file(std::shared_ptr<logfile> lf)
@ -355,12 +372,12 @@ public:
this->lss_user_marks[bm].insert_once(cl);
};
bookmarks<content_line_t>::type &get_user_bookmarks(void)
bookmarks<content_line_t>::type &get_user_bookmarks()
{
return this->lss_user_marks;
};
std::map<content_line_t, bookmark_metadata> &get_user_bookmark_metadata(void) {
std::map<content_line_t, bookmark_metadata> &get_user_bookmark_metadata() {
return this->lss_user_mark_metadata;
};
@ -382,11 +399,11 @@ public:
logline *find_line(content_line_t line)
{
logline *retval = NULL;
logline *retval = nullptr;
std::shared_ptr<logfile> lf = this->find(line);
if (lf != NULL) {
logfile::iterator ll_iter = lf->begin() + line;
if (lf != nullptr) {
auto ll_iter = lf->begin() + line;
retval = &(*ll_iter);
}
@ -437,7 +454,7 @@ public:
lf->set_logline_observer(&this->ld_filter_state);
};
void clear(void)
void clear()
{
this->ld_filter_state.lfo_filter_state.clear();
};
@ -446,7 +463,7 @@ public:
this->ld_enabled = enabled;
}
void set_file(std::shared_ptr<logfile> lf) {
void set_file(const std::shared_ptr<logfile> &lf) {
this->ld_filter_state.lfo_filter_state.tfs_logfile = lf;
lf->set_logline_observer(&this->ld_filter_state);
};
@ -495,7 +512,7 @@ public:
};
content_line_t get_file_base_content_line(iterator iter) {
int index = std::distance(this->begin(), iter);
ssize_t index = std::distance(this->begin(), iter);
return content_line_t(index * MAX_LINES_PER_FILE);
};
@ -517,8 +534,8 @@ public:
}
this->lss_index_delegate->index_start(*this);
for (size_t index = 0; index < this->lss_filtered_index.size(); index++) {
content_line_t cl = (content_line_t) this->lss_index[this->lss_filtered_index[index]];
for (unsigned int index : this->lss_filtered_index) {
content_line_t cl = (content_line_t) this->lss_index[index];
uint64_t line_number;
logfile_data *ld = this->find_data(cl, line_number);
std::shared_ptr<logfile> lf = ld->get_file();
@ -528,6 +545,77 @@ public:
this->lss_index_delegate->index_complete(*this);
};
class meta_grepper
: public grep_proc_source<vis_line_t>,
public grep_proc_sink<vis_line_t> {
public:
meta_grepper(logfile_sub_source &source)
: lmg_source(source) {
};
bool grep_value_for_line(vis_line_t line, std::string &value_out) override {
content_line_t cl = this->lmg_source.at(vis_line_t(line));
std::map<content_line_t, bookmark_metadata> &user_mark_meta =
lmg_source.get_user_bookmark_metadata();
auto meta_iter = user_mark_meta.find(cl);
if (meta_iter == user_mark_meta.end()) {
value_out.clear();
} else {
bookmark_metadata &bm = meta_iter->second;
value_out.append(bm.bm_comment);
for (const auto &tag : bm.bm_tags) {
value_out.append(tag);
}
}
return !this->lmg_done;
};
vis_line_t grep_initial_line(vis_line_t start, vis_line_t highest) override {
vis_bookmarks &bm = this->lmg_source.tss_view->get_bookmarks();
bookmark_vector<vis_line_t> &bv = bm[&textview_curses::BM_META];
if (bv.empty()) {
return -1_vl;
}
return *bv.begin();
};
void grep_next_line(vis_line_t &line) override {
vis_bookmarks &bm = this->lmg_source.tss_view->get_bookmarks();
bookmark_vector<vis_line_t> &bv = bm[&textview_curses::BM_META];
line = bv.next(vis_line_t(line));
if (line == -1) {
this->lmg_done = true;
}
};
void grep_begin(grep_proc<vis_line_t> &gp, vis_line_t start, vis_line_t stop) override {
this->lmg_source.tss_view->grep_begin(gp, start, stop);
};
void grep_end(grep_proc<vis_line_t> &gp) override {
this->lmg_source.tss_view->grep_end(gp);
};
void grep_match(grep_proc<vis_line_t> &gp,
vis_line_t line,
int start,
int end) override {
this->lmg_source.tss_view->grep_match(gp, line, start, end);
};
logfile_sub_source &lmg_source;
bool lmg_done{false};
};
meta_grepper &get_meta_grepper() {
return this->lss_meta_grepper;
}
static const uint64_t MAX_CONTENT_LINES = (1ULL << 40) - 1;
static const uint64_t MAX_LINES_PER_FILE = 256 * 1024 * 1024;
static const uint64_t MAX_FILES = (
@ -544,10 +632,10 @@ private:
};
enum {
F_SCRUB = (1L << B_SCRUB),
F_TIME_OFFSET = (1L << B_TIME_OFFSET),
F_FILENAME = (1L << B_FILENAME),
F_BASENAME = (1L << B_BASENAME),
F_SCRUB = (1UL << B_SCRUB),
F_TIME_OFFSET = (1UL << B_TIME_OFFSET),
F_FILENAME = (1UL << B_FILENAME),
F_BASENAME = (1UL << B_BASENAME),
F_NAME_MASK = (F_FILENAME | F_BASENAME),
};
@ -646,7 +734,7 @@ private:
* Functor for comparing the ld_file field of the logfile_data struct.
*/
struct logfile_data_eq {
logfile_data_eq(std::shared_ptr<logfile> lf) : lde_file(lf) { };
explicit logfile_data_eq(std::shared_ptr<logfile> lf) : lde_file(std::move(lf)) { };
bool operator()(const logfile_data *ld)
{
@ -656,12 +744,16 @@ private:
std::shared_ptr<logfile> lde_file;
};
void clear_line_size_cache(void) {
void clear_line_size_cache() {
memset(this->lss_line_size_cache, 0, sizeof(this->lss_line_size_cache));
this->lss_line_size_cache[0].first = -1;
};
bool check_extra_filters(const logline &ll) {
if (this->lss_marked_only && !ll.is_marked()) {
return false;
}
return (
ll.get_msg_level() >= this->lss_min_log_level &&
!(ll < this->lss_min_log_time) &&
@ -693,8 +785,10 @@ private:
logline::level_t lss_min_log_level;
struct timeval lss_min_log_time;
struct timeval lss_max_log_time;
bool lss_marked_only;
index_delegate *lss_index_delegate;
size_t lss_longest_line;
meta_grepper lss_meta_grepper;
};
#endif

View File

@ -133,6 +133,7 @@ int network_extension_functions(struct FuncDef **basic_funcs,
"Get the IP address for the given hostname")
.sql_function()
.with_parameter({"hostname", "The DNS hostname to lookup."})
.with_tags({"net"})
.with_example({"SELECT gethostbyname('localhost')"})
),
@ -141,6 +142,7 @@ int network_extension_functions(struct FuncDef **basic_funcs,
"Get the IP address for the given hostname")
.sql_function()
.with_parameter({"hostname", "The DNS hostname to lookup."})
.with_tags({"net"})
.with_example({"SELECT gethostbyaddr('127.0.0.1')"})
),

View File

@ -51,6 +51,7 @@
#include <string>
#include <memory>
#include <utility>
#include <vector>
#include <exception>
@ -343,10 +344,10 @@ public:
class error : public std::exception {
public:
error(std::string msg, int offset = 0)
: e_msg(msg), e_offset(offset) { };
: e_msg(std::move(msg)), e_offset(offset) { };
virtual ~error() { };
virtual const char *what() const throw() {
virtual const char *what() const noexcept {
return this->e_msg.c_str();
};

View File

@ -99,27 +99,23 @@ void rl_change(void *dummy, readline_curses *rc)
}
}
else {
readline_context::command_t &cmd = iter->second;
readline_context::command_t &cmd = *iter->second;
const help_text &ht = cmd.c_help;
if (ht.ht_name) {
textview_curses &dtc = lnav_data.ld_doc_view;
textview_curses &etc = lnav_data.ld_example_view;
vector<attr_line_t> lines;
unsigned long width;
vis_line_t height;
attr_line_t al;
dtc.get_dimensions(height, width);
format_help_text_for_term(ht, min(70UL, width), al);
al.split_lines(lines);
lnav_data.ld_doc_source.replace_with(al);
al.clear();
lines.clear();
etc.get_dimensions(height, width);
format_example_text_for_term(ht, width, al);
al.split_lines(lines);
lnav_data.ld_example_source.replace_with(al);
}
@ -243,14 +239,15 @@ static void rl_search_internal(void *dummy, readline_curses *rc, bool complete =
x -= 1;
}
auto iter = rfind_string_attr_if(sa, x, [](auto sa) {
return (sa.sa_type == &SQL_FUNCTION_ATTR ||
sa.sa_type == &SQL_KEYWORD_ATTR);
});
vector<string> kw;
auto iter = rfind_string_attr_if(sa, x, [&al, &name, &kw](auto sa) {
if (sa.sa_type != &SQL_FUNCTION_ATTR &&
sa.sa_type != &SQL_KEYWORD_ATTR) {
return false;
}
if (iter != sa.end()) {
const line_range &lr = iter->sa_range;
const string &str = al.get_string();
const line_range &lr = sa.sa_range;
int lpc;
for (lpc = lr.lr_start; lpc < lr.lr_end; lpc++) {
@ -259,32 +256,58 @@ static void rl_search_internal(void *dummy, readline_curses *rc, bool complete =
}
}
name = str.substr(lr.lr_start, lpc - lr.lr_start);
string tmp_name = str.substr(lr.lr_start, lpc - lr.lr_start);
if (sa.sa_type == &SQL_KEYWORD_ATTR) {
tmp_name = toupper(tmp_name);
}
bool retval = sqlite_function_help.count(tmp_name) > 0;
const auto &func_iter = sqlite_function_help.find(tolower(name));
if (retval) {
kw.push_back(tmp_name);
name = tmp_name;
}
return retval;
});
if (func_iter != sqlite_function_help.end()) {
textview_curses &dtc = lnav_data.ld_doc_view;
textview_curses &etc = lnav_data.ld_example_view;
if (iter != sa.end()) {
auto func_pair = sqlite_function_help.equal_range(name);
size_t help_count = distance(func_pair.first, func_pair.second);
textview_curses &dtc = lnav_data.ld_doc_view;
textview_curses &etc = lnav_data.ld_example_view;
unsigned long doc_width, ex_width;
vis_line_t doc_height, ex_height;
attr_line_t doc_al, ex_al;
dtc.get_dimensions(doc_height, doc_width);
etc.get_dimensions(ex_height, ex_width);
if (help_count > 1 && name != func_pair.first->second->ht_name) {
while (find(kw.begin(), kw.end(),
func_pair.first->second->ht_name) == kw.end()) {
++func_pair.first;
}
func_pair.second = next(func_pair.first);
help_count = 1;
}
for (auto func_iter = func_pair.first;
func_iter != func_pair.second;
++func_iter) {
const help_text &ht = *func_iter->second;
vector<attr_line_t> lines;
unsigned long width;
vis_line_t height;
attr_line_t al;
dtc.get_dimensions(height, width);
format_help_text_for_term(ht, min(70UL, width), al);
al.split_lines(lines);
lnav_data.ld_doc_source.replace_with(al);
format_help_text_for_term(ht, min(70UL, doc_width), doc_al,
help_count > 1);
if (help_count == 1) {
format_example_text_for_term(ht, ex_width, ex_al);
}
}
if (!doc_al.empty()) {
lnav_data.ld_doc_source.replace_with(doc_al);
dtc.reload_data();
al.clear();
lines.clear();
etc.get_dimensions(height, width);
format_example_text_for_term(ht, width, al);
al.split_lines(lines);
lnav_data.ld_example_source.replace_with(al);
etc.reload_data();
if (!ex_al.empty()) {
lnav_data.ld_example_source.replace_with(ex_al);
etc.reload_data();
}
has_doc = true;
}

View File

@ -71,7 +71,7 @@ class readline_context {
public:
typedef std::string (*command_func_t)(exec_context &ec,
std::string cmdline, std::vector<std::string> &args);
typedef struct {
typedef struct _command_t {
const char *c_name;
command_func_t c_func;
@ -82,7 +82,7 @@ public:
this->c_func = func;
}
} command_t;
typedef std::map<std::string, command_t> command_map_t;
typedef std::map<std::string, command_t *> command_map_t;
readline_context(const std::string &name,
command_map_t *commands = NULL,
@ -101,7 +101,7 @@ public:
std::string cmd = iter->first;
this->rc_possibilities["__command"].insert(cmd);
iter->second.c_func(INIT_EXEC_CONTEXT, cmd, this->rc_prototypes[cmd]);
iter->second->c_func(INIT_EXEC_CONTEXT, cmd, this->rc_prototypes[cmd]);
}
}
@ -354,14 +354,13 @@ public:
}
};
template<template<typename ...> class Container>
void add_possibility(int context,
const std::string &type,
const std::vector<std::string> &values)
const Container<std::string> &values)
{
for (std::vector<std::string>::const_iterator iter = values.begin();
iter != values.end();
++iter) {
this->add_possibility(context, type, *iter);
for (const auto &str : values) {
this->add_possibility(context, type, str);
}
};

View File

@ -329,10 +329,10 @@ static void readline_regex_highlighter_int(attr_line_t &al, int x, int skip)
break;
default:
if (isdigit(line[lpc])) {
al.get_attrs().push_back(string_attr(
al.get_attrs().emplace_back(
line_range(lpc - 1, lpc + 1),
&view_curses::VC_STYLE,
special_char));
special_char);
}
break;
}
@ -354,6 +354,7 @@ void readline_command_highlighter(attr_line_t &al, int x)
static const pcrepp RE_PREFIXES(
R"(^:(filter-in|filter-out|delete-filter|enable-filter|disable-filter|highlight|clear-highlight|create-search-table\s+[^\s]+\s+))");
static const pcrepp SH_PREFIXES("^:(eval|open|append-to|write-to|write-csv-to|write-json-to)");
static const pcrepp IDENT_PREFIXES("^:(tag|untag|delete-tags)");
view_colors &vc = view_colors::singleton();
int keyword_attrs = (
@ -365,6 +366,7 @@ void readline_command_highlighter(attr_line_t &al, int x)
size_t ws_index;
ws_index = line.find(' ');
string command = line.substr(0, ws_index);
if (ws_index != string::npos) {
al.get_attrs().push_back(string_attr(
line_range(1, ws_index),
@ -378,6 +380,32 @@ void readline_command_highlighter(attr_line_t &al, int x)
if (SH_PREFIXES.match(pc, pi)) {
readline_shlex_highlighter(al, x);
}
pi.reset(line);
if (IDENT_PREFIXES.match(pc, pi)) {
size_t start = ws_index, last;
do {
for (; start < line.length() && isspace(line[start]); start++);
for (last = start; last < line.length() && !isspace(line[last]); last++);
struct line_range lr{(int) start, (int) last};
if (lr.length() > 0 && !lr.contains(x) && !lr.contains(x - 1)) {
string value(lr.substr(line), lr.sublen(line));
if ((command == ":tag" ||
command == ":untag" ||
command == ":delete-tags") &&
!startswith(value, "#")) {
value = "#" + value;
}
al.get_attrs().emplace_back(lr,
&view_curses::VC_STYLE,
vc.attrs_for_ident(value));
}
start = last;
} while (start < line.length());
}
}
static string sql_keyword_re(void)

View File

@ -48,7 +48,7 @@ static int handle_collation_list(void *ptr,
char **colvalues,
char **colnames)
{
if (lnav_data.ld_rl_view != NULL) {
if (lnav_data.ld_rl_view != nullptr) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
}
@ -60,7 +60,7 @@ static int handle_db_list(void *ptr,
char **colvalues,
char **colnames)
{
if (lnav_data.ld_rl_view != NULL) {
if (lnav_data.ld_rl_view != nullptr) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
}
@ -72,7 +72,7 @@ static int handle_table_list(void *ptr,
char **colvalues,
char **colnames)
{
if (lnav_data.ld_rl_view != NULL) {
if (lnav_data.ld_rl_view != nullptr) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[0]);
lnav_data.ld_table_ddl[colvalues[0]] = colvalues[1];
@ -86,7 +86,7 @@ static int handle_table_info(void *ptr,
char **colvalues,
char **colnames)
{
if (lnav_data.ld_rl_view != NULL) {
if (lnav_data.ld_rl_view != nullptr) {
auto_mem<char, sqlite3_free> quoted_name;
quoted_name = sql_quote_ident(colvalues[1]);
@ -94,7 +94,7 @@ static int handle_table_info(void *ptr,
string(quoted_name));
}
if (strcmp(colvalues[5], "1") == 0) {
lnav_data.ld_db_key_names.push_back(colvalues[1]);
lnav_data.ld_db_key_names.emplace_back(colvalues[1]);
}
return 0;
}
@ -104,8 +104,8 @@ static int handle_foreign_key_list(void *ptr,
char **colvalues,
char **colnames)
{
lnav_data.ld_db_key_names.push_back(colvalues[3]);
lnav_data.ld_db_key_names.push_back(colvalues[4]);
lnav_data.ld_db_key_names.emplace_back(colvalues[3]);
lnav_data.ld_db_key_names.emplace_back(colvalues[4]);
return 0;
}
@ -189,14 +189,14 @@ void add_view_text_possibilities(int context, const string &type, textview_curse
auto_mem<FILE> pfile(pclose);
pfile = open_clipboard(CT_FIND, CO_READ);
if (pfile.in() != NULL) {
if (pfile.in() != nullptr) {
char buffer[64];
if (fgets(buffer, sizeof(buffer), pfile) != NULL) {
if (fgets(buffer, sizeof(buffer), pfile) != nullptr) {
char *nl;
buffer[sizeof(buffer) - 1] = '\0';
if ((nl = strchr(buffer, '\n')) != NULL) {
if ((nl = strchr(buffer, '\n')) != nullptr) {
*nl = '\0';
}
rlc->add_possibility(context, type, std::string(buffer));
@ -213,6 +213,8 @@ void add_view_text_possibilities(int context, const string &type, textview_curse
add_text_possibilities(context, type, line);
}
rlc->add_possibility(context, type, bookmark_metadata::KNOWN_TAGS);
}
void add_env_possibilities(int context)
@ -220,19 +222,19 @@ void add_env_possibilities(int context)
extern char **environ;
readline_curses *rlc = lnav_data.ld_rl_view;
for (char **var = environ; *var != NULL; var++) {
for (char **var = environ; *var != nullptr; var++) {
rlc->add_possibility(context, "*", "$" + string(*var, strchr(*var, '=')));
}
exec_context &ec = lnav_data.ld_exec_context;
if (!ec.ec_local_vars.empty()) {
for (const auto iter : ec.ec_local_vars.top()) {
for (const auto &iter : ec.ec_local_vars.top()) {
rlc->add_possibility(context, "*", "$" + iter.first);
}
}
for (const auto iter : ec.ec_global_vars) {
for (const auto &iter : ec.ec_global_vars) {
rlc->add_possibility(context, "*", "$" + iter.first);
}
}
@ -246,9 +248,7 @@ void add_filter_possibilities(textview_curses *tc)
rc->clear_possibilities(LNM_COMMAND, "all-filters");
rc->clear_possibilities(LNM_COMMAND, "disabled-filter");
rc->clear_possibilities(LNM_COMMAND, "enabled-filter");
for (auto iter = fs.begin(); iter != fs.end(); ++iter) {
shared_ptr<text_filter> tf = *iter;
for (const auto &tf : fs) {
rc->add_possibility(LNM_COMMAND, "all-filters", tf->get_id());
if (tf->is_enabled()) {
rc->add_possibility(LNM_COMMAND, "enabled-filter", tf->get_id());
@ -264,7 +264,7 @@ void add_mark_possibilities()
readline_curses *rc = lnav_data.ld_rl_view;
rc->clear_possibilities(LNM_COMMAND, "mark-type");
for (bookmark_type_t::type_iterator iter = bookmark_type_t::type_begin();
for (auto iter = bookmark_type_t::type_begin();
iter != bookmark_type_t::type_end();
++iter) {
bookmark_type_t *bt = (*iter);
@ -288,3 +288,25 @@ void add_config_possibilities()
}
rc->add_possibility(LNM_COMMAND, "config-option", config_options);
}
void add_tag_possibilities()
{
readline_curses *rc = lnav_data.ld_rl_view;
rc->clear_possibilities(LNM_COMMAND, "tag");
rc->clear_possibilities(LNM_COMMAND, "line-tags");
rc->add_possibility(LNM_COMMAND, "tag", bookmark_metadata::KNOWN_TAGS);
if (lnav_data.ld_view_stack.back() == &lnav_data.ld_views[LNV_LOG]) {
logfile_sub_source &lss = lnav_data.ld_log_source;
content_line_t cl = lss.at(lnav_data.ld_views[LNV_LOG].get_top());
const map<content_line_t, bookmark_metadata> &user_meta =
lss.get_user_bookmark_metadata();
auto meta_iter = user_meta.find(cl);
if (meta_iter != user_meta.end()) {
rc->add_possibility(LNM_COMMAND,
"line-tags",
meta_iter->second.bm_tags);
}
}
}

View File

@ -40,6 +40,7 @@ void add_env_possibilities(int context);
void add_filter_possibilities(textview_curses *tc);
void add_mark_possibilities();
void add_config_possibilities();
void add_tag_possibilities();
extern struct sqlite_metadata_callbacks lnav_sql_meta_callbacks;

View File

@ -36,22 +36,22 @@
#include "grep_proc.hh"
#include "sequence_matcher.hh"
class sequence_sink : public grep_proc_sink {
class sequence_sink : public grep_proc_sink<vis_line_t> {
public:
sequence_sink(sequence_matcher &sm, bookmark_vector<vis_line_t> &bv)
: ss_matcher(sm),
ss_bookmarks(bv) {};
void grep_match(grep_proc &gp,
grep_line_t line,
void grep_match(grep_proc<vis_line_t> &gp,
vis_line_t line,
int start,
int end)
{
this->ss_line_values.clear();
};
void grep_capture(grep_proc &gp,
grep_line_t line,
void grep_capture(grep_proc<vis_line_t> &gp,
vis_line_t line,
int start,
int end,
char *capture)
@ -64,17 +64,17 @@ public:
}
};
void grep_match_end(grep_proc &gp, grep_line_t line)
void grep_match_end(grep_proc<vis_line_t> &gp, vis_line_t line)
{
sequence_matcher::id_t line_id;
this->ss_matcher.identity(this->ss_line_values, line_id);
std::vector<grep_line_t> &line_state = this->ss_state[line_id];
std::vector<vis_line_t> &line_state = this->ss_state[line_id];
if (this->ss_matcher.match(this->ss_line_values,
line_state,
line)) {
std::vector<grep_line_t>::iterator iter;
std::vector<vis_line_t>::iterator iter;
for (iter = line_state.begin();
iter != line_state.end();
@ -89,6 +89,6 @@ private:
sequence_matcher & ss_matcher;
bookmark_vector<vis_line_t> &ss_bookmarks;
std::vector<std::string> ss_line_values;
std::map<sequence_matcher::id_t, std::vector<grep_line_t> > ss_state;
std::map<sequence_matcher::id_t, std::vector<vis_line_t> > ss_state;
};
#endif

View File

@ -40,6 +40,8 @@
#include "spookyhash/SpookyV2.h"
#include <algorithm>
#include <utility>
#include <yajl/api/yajl_tree.h>
#include "yajlpp.hh"
#include "lnav.hh"
@ -62,6 +64,8 @@ static const char *BOOKMARK_TABLE_DEF =
" session_time integer,\n"
" part_name text,\n"
" access_time datetime DEFAULT CURRENT_TIMESTAMP,\n"
" comment text DEFAULT '',\n"
" tags text DEFAULT '',\n"
"\n"
" PRIMARY KEY (log_time, log_format, log_hash, session_time)\n"
");\n"
@ -82,11 +86,14 @@ static const char *BOOKMARK_LRU_STMT =
" (SELECT access_time FROM bookmarks "
" ORDER BY access_time DESC LIMIT 1 OFFSET 50000)";
static const char *UPGRADE_STMTS[] = {
R"(ALTER TABLE bookmarks ADD COLUMN comment text DEFAULT '';)",
R"(ALTER TABLE bookmarks ADD COLUMN tags text DEFAULT '';)",
};
static const size_t MAX_SESSIONS = 8;
static const size_t MAX_SESSION_FILE_COUNT = 256;
typedef std::vector<std::pair<int, string> > timestamped_list_t;
static std::vector<content_line_t> marked_session_lines;
static std::vector<content_line_t> offset_session_lines;
@ -154,9 +161,12 @@ static bool bind_line(sqlite3 *db,
struct session_file_info {
session_file_info(int timestamp,
const string &id,
const string &path)
: sfi_timestamp(timestamp), sfi_id(id), sfi_path(path) {};
string id,
string path)
: sfi_timestamp(timestamp),
sfi_id(std::move(id)),
sfi_path(std::move(path)) {
};
bool operator<(const session_file_info &other) const
{
@ -209,8 +219,7 @@ static void cleanup_session_data(void)
hash_id,
&timestamp) == 2) {
session_count[hash_id] += 1;
session_info_list.push_back(session_file_info(
timestamp, hash_id, path));
session_info_list.emplace_back(timestamp, hash_id, path);
}
}
}
@ -266,7 +275,7 @@ void init_session(void)
context.Init(0, 0);
hash_updater updater(&context);
for (map<string, logfile_open_options>::iterator iter = lnav_data.ld_file_names.begin();
for (auto iter = lnav_data.ld_file_names.begin();
iter != lnav_data.ld_file_names.end();
++iter) {
updater(iter->first);
@ -307,7 +316,7 @@ void scan_sessions(void)
"view-info-%s.*.json",
lnav_data.ld_session_id.c_str());
view_info_pattern = dotlnav_path(view_info_pattern_base);
if (glob(view_info_pattern.c_str(), 0, NULL,
if (glob(view_info_pattern.c_str(), 0, nullptr,
view_info_list.inout()) == 0) {
for (size_t lpc = 0; lpc < view_info_list->gl_pathc; lpc++) {
const char *path = view_info_list->gl_pathv[lpc];
@ -327,7 +336,7 @@ void scan_sessions(void)
ptp.first = (ppid == getppid()) ? 1 : 0;
ptp.second = timestamp;
session_file_names.push_back(make_pair(ptp, path));
session_file_names.emplace_back(ptp, path);
}
}
}
@ -366,6 +375,8 @@ static void load_time_bookmarks(void)
string db_path = dotlnav_path(LOG_METADATA_NAME);
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
logfile_sub_source::iterator file_iter;
bool reload_needed = false;
auto_mem<char, sqlite3_free> errmsg;
log_info("loading bookmark db: %s", db_path.c_str());
@ -373,15 +384,22 @@ static void load_time_bookmarks(void)
return;
}
for (const char *stmt : UPGRADE_STMTS) {
if (sqlite3_exec(db.in(), stmt, nullptr, nullptr, errmsg.out()) != SQLITE_OK) {
log_error("unable to upgrade bookmark table -- %s\n", errmsg.in());
}
}
if (sqlite3_prepare_v2(db.in(),
"SELECT *,session_time=? as same_session FROM bookmarks WHERE "
"SELECT log_time, log_format, log_hash, session_time, part_name, access_time, comment,"
" tags, session_time=? as same_session FROM bookmarks WHERE "
" log_time between ? and ? and log_format = ? "
" ORDER BY same_session DESC, session_time DESC",
-1,
stmt.out(),
NULL) != SQLITE_OK) {
nullptr) != SQLITE_OK) {
log_error(
"could not prepare bookmark select statemnt -- %s\n",
"could not prepare bookmark select statement -- %s\n",
sqlite3_errmsg(db));
return;
}
@ -398,7 +416,7 @@ static void load_time_bookmarks(void)
base_content_line = lss.get_file_base_content_line(file_iter);
logfile::iterator line_iter = lf->begin();
auto line_iter = lf->begin();
sql_strftime(low_timestamp, sizeof(low_timestamp),
lf->original_line_time(line_iter), 'T');
@ -458,6 +476,8 @@ static void load_time_bookmarks(void)
const char *log_time = (const char *)sqlite3_column_text(stmt.in(), 0);
const char *log_hash = (const char *)sqlite3_column_text(stmt.in(), 2);
const char *part_name = (const char *)sqlite3_column_text(stmt.in(), 4);
const char *comment = (const char *)sqlite3_column_text(stmt.in(), 6);
const char *tags = (const char *)sqlite3_column_text(stmt.in(), 7);
int64_t mark_time = sqlite3_column_int64(stmt.in(), 3);
struct timeval log_tv;
struct exttm log_tm;
@ -500,18 +520,47 @@ static void load_time_bookmarks(void)
if (line_hash == log_hash) {
content_line_t line_cl = content_line_t(
base_content_line + std::distance(lf->begin(), line_iter));
bool meta = false;
if (part_name != NULL && part_name[0] != '\0') {
lss.set_user_mark(&textview_curses::BM_PARTITION,
line_cl);
lss.set_user_mark(&textview_curses::BM_META, line_cl);
bm_meta[line_cl].bm_name = part_name;
meta = true;
}
else {
if (comment != NULL && comment[0] != '\0') {
lss.set_user_mark(&textview_curses::BM_META,
line_cl);
bm_meta[line_cl].bm_comment = comment;
meta = true;
}
if (tags != nullptr && tags[0] != '\0') {
auto_mem<yajl_val_s> tag_list(yajl_tree_free);
char error_buffer[1024];
tag_list = yajl_tree_parse(tags, error_buffer, sizeof(error_buffer));
if (!YAJL_IS_ARRAY(tag_list.in())) {
log_error("invalid tags column: %s", tags);
} else {
lss.set_user_mark(&textview_curses::BM_META,
line_cl);
for (int lpc = 0; lpc < tag_list.in()->u.array.len; lpc++) {
yajl_val elem = tag_list.in()->u.array.values[lpc];
if (!YAJL_IS_STRING(elem)) {
continue;
}
bookmark_metadata::KNOWN_TAGS.insert(elem->u.string);
bm_meta[line_cl].add_tag(elem->u.string);
}
}
meta = true;
}
if (!meta) {
marked_session_lines.push_back(line_cl);
lss.set_user_mark(&textview_curses::BM_USER,
line_cl);
line_cl);
}
reload_needed = true;
}
++line_iter;
@ -543,9 +592,9 @@ static void load_time_bookmarks(void)
" ORDER BY same_session DESC, session_time DESC",
-1,
stmt.out(),
NULL) != SQLITE_OK) {
nullptr) != SQLITE_OK) {
log_error(
"could not prepare time_offset select statemnt -- %s\n",
"could not prepare time_offset select statement -- %s\n",
sqlite3_errmsg(db));
return;
}
@ -557,8 +606,9 @@ static void load_time_bookmarks(void)
shared_ptr<logfile> lf = (*file_iter)->get_file();
content_line_t base_content_line;
if (lf == NULL)
if (lf == NULL) {
continue;
}
lss.find(lf->get_filename().c_str(), base_content_line);
@ -665,6 +715,8 @@ static void load_time_bookmarks(void)
offset.tv_sec = sqlite3_column_int64(stmt.in(), 4);
offset.tv_usec = sqlite3_column_int64(stmt.in(), 5);
lf->adjust_content_time(file_line, offset);
reload_needed = true;
}
++line_iter;
@ -689,6 +741,10 @@ static void load_time_bookmarks(void)
sqlite3_reset(stmt.in());
}
if (reload_needed) {
lnav_data.ld_views[LNV_LOG].reload_data();
}
}
static int read_save_time(yajlpp_parse_context *ypc, long long value)
@ -823,7 +879,7 @@ void load_session(void)
string &view_info_name = sess_iter->second;
yajlpp_parse_context ypc(view_info_name, view_info_handlers);
handle = yajl_alloc(&ypc.ypc_callbacks, NULL, &ypc);
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
load_time_bookmarks();
@ -860,13 +916,10 @@ static void save_user_bookmarks(
for (iter = user_marks.begin(); iter != user_marks.end(); ++iter) {
std::map<content_line_t, bookmark_metadata>::iterator meta_iter;
logfile::iterator line_iter;
content_line_t cl = *iter;
meta_iter = bm_meta.find(cl);
marked_session_lines.push_back(cl);
if (!bind_line(db, stmt, cl, lnav_data.ld_session_time)) {
continue;
}
@ -879,14 +932,55 @@ static void save_user_bookmarks(
}
}
else {
if (meta_iter->second.empty()) {
continue;
}
if (sqlite3_bind_text(stmt, 5,
meta_iter->second.bm_name.c_str(),
meta_iter->second.bm_name.length(),
SQLITE_TRANSIENT) != SQLITE_OK) {
log_error("could not bind log hash -- %s\n",
log_error("could not bind part name -- %s\n",
sqlite3_errmsg(db));
return;
}
bookmark_metadata &line_meta = meta_iter->second;
if (sqlite3_bind_text(stmt, 6,
meta_iter->second.bm_comment.c_str(),
meta_iter->second.bm_comment.length(),
SQLITE_TRANSIENT) != SQLITE_OK) {
log_error("could not bind comment -- %s\n",
sqlite3_errmsg(db));
return;
}
string tags;
if (!line_meta.bm_tags.empty()) {
yajlpp_gen gen;
yajl_gen_config(gen, yajl_gen_beautify, false);
{
yajlpp_array arr(gen);
for (const auto &str : line_meta.bm_tags) {
arr.gen(str);
}
}
tags = gen.to_string_fragment().to_string();
}
if (sqlite3_bind_text(stmt, 7,
tags.c_str(),
tags.length(),
SQLITE_TRANSIENT) != SQLITE_OK) {
log_error("could not bind tags -- %s\n",
sqlite3_errmsg(db));
return;
}
}
if (sqlite3_step(stmt) != SQLITE_DONE) {
@ -896,6 +990,8 @@ static void save_user_bookmarks(
return;
}
marked_session_lines.push_back(cl);
sqlite3_reset(stmt);
}
@ -932,18 +1028,16 @@ static void save_time_bookmarks(void)
" and session_time = ?",
-1,
stmt.out(),
NULL) != SQLITE_OK) {
nullptr) != SQLITE_OK) {
log_error(
"could not prepare bookmark delete statemnt -- %s\n",
"could not prepare bookmark delete statement -- %s\n",
sqlite3_errmsg(db));
return;
}
for (std::vector<content_line_t>::iterator cl_iter = marked_session_lines.begin();
cl_iter != marked_session_lines.end();
++cl_iter) {
for (auto &marked_session_line : marked_session_lines) {
if (!bind_line(
db.in(), stmt.in(), *cl_iter, lnav_data.ld_session_time)) {
db.in(), stmt.in(), marked_session_line, lnav_data.ld_session_time)) {
continue;
}
@ -961,13 +1055,13 @@ static void save_time_bookmarks(void)
if (sqlite3_prepare_v2(db.in(),
"REPLACE INTO bookmarks"
" (log_time, log_format, log_hash, session_time, part_name)"
" VALUES (?, ?, ?, ?, ?)",
" (log_time, log_format, log_hash, session_time, part_name, comment, tags)"
" VALUES (?, ?, ?, ?, ?, ?, ?)",
-1,
stmt.out(),
NULL) != SQLITE_OK) {
nullptr) != SQLITE_OK) {
log_error(
"could not prepare bookmark replace statemnt -- %s\n",
"could not prepare bookmark replace statement -- %s\n",
sqlite3_errmsg(db));
return;
}
@ -1011,7 +1105,7 @@ static void save_time_bookmarks(void)
}
save_user_bookmarks(db.in(), stmt.in(), bm[&textview_curses::BM_USER]);
save_user_bookmarks(db.in(), stmt.in(), bm[&textview_curses::BM_PARTITION]);
save_user_bookmarks(db.in(), stmt.in(), bm[&textview_curses::BM_META]);
if (sqlite3_prepare_v2(db.in(),
"DELETE FROM time_offset WHERE "
@ -1021,16 +1115,14 @@ static void save_time_bookmarks(void)
stmt.out(),
NULL) != SQLITE_OK) {
log_error(
"could not prepare time_offset delete statemnt -- %s\n",
"could not prepare time_offset delete statement -- %s\n",
sqlite3_errmsg(db));
return;
}
for (std::vector<content_line_t>::iterator cl_iter = offset_session_lines.begin();
cl_iter != offset_session_lines.end();
++cl_iter) {
for (auto &offset_session_line : offset_session_lines) {
if (!bind_line(
db.in(), stmt.in(), *cl_iter, lnav_data.ld_session_time)) {
db.in(), stmt.in(), offset_session_line, lnav_data.ld_session_time)) {
continue;
}
@ -1054,7 +1146,7 @@ static void save_time_bookmarks(void)
stmt.out(),
NULL) != SQLITE_OK) {
log_error(
"could not prepare time_offset replace statemnt -- %s\n",
"could not prepare time_offset replace statement -- %s\n",
sqlite3_errmsg(db));
return;
}
@ -1101,15 +1193,13 @@ static void save_time_bookmarks(void)
}
}
for (logfile_sub_source::iterator file_iter = lss.begin();
file_iter != lss.end();
++file_iter) {
for (auto &ls : lss) {
logfile::iterator line_iter;
if ((*file_iter)->get_file() == NULL)
if (ls->get_file() == NULL)
continue;
shared_ptr<logfile> lf = (*file_iter)->get_file();
shared_ptr<logfile> lf = ls->get_file();
if (!lf->is_time_adjusted())
continue;
@ -1234,10 +1324,8 @@ void save_session(void)
{
yajlpp_array file_list(handle);
for (map<string, logfile_open_options>::iterator iter = lnav_data.ld_file_names.begin();
iter != lnav_data.ld_file_names.end();
++iter) {
file_list.gen(iter->first);
for (auto &ld_file_name : lnav_data.ld_file_names) {
file_list.gen(ld_file_name.first);
}
}
@ -1358,18 +1446,18 @@ void save_session(void)
}
}
void reset_session(void)
void reset_session()
{
textview_curses::highlight_map_t &hmap =
lnav_data.ld_views[LNV_LOG].get_highlights();
textview_curses::highlight_map_t::iterator hl_iter = hmap.begin();
auto hl_iter = hmap.begin();
log_info("reset session: time=%d", lnav_data.ld_session_time);
save_session();
scan_sessions();
lnav_data.ld_session_time = time(NULL);
lnav_data.ld_session_time = time(nullptr);
while (hl_iter != hmap.end()) {
if (hl_iter->first[0] == '$') {
@ -1390,23 +1478,23 @@ void reset_session(void)
lf->clear_time_offset();
}
lnav_data.ld_log_source.set_marked_only(false);
lnav_data.ld_log_source.clear_min_max_log_times();
lnav_data.ld_log_source.get_user_bookmark_metadata().clear();
for (int lpc = 0; lpc < LNV__MAX; lpc++) {
textview_curses &tc = lnav_data.ld_views[lpc];
for (auto &tc : lnav_data.ld_views) {
text_sub_source *tss = tc.get_sub_source();
if (tss == NULL) {
if (tss == nullptr) {
continue;
}
tss->get_filters().clear_filters();
tss->text_filters_changed();
tss->text_clear_marks(&textview_curses::BM_USER);
tc.get_bookmarks()[&textview_curses::BM_USER].clear();
tss->text_clear_marks(&textview_curses::BM_PARTITION);
tc.get_bookmarks()[&textview_curses::BM_PARTITION].clear();
tss->text_clear_marks(&textview_curses::BM_META);
tc.get_bookmarks()[&textview_curses::BM_META].clear();
tc.reload_data();
}

View File

@ -227,12 +227,9 @@ public:
}
};
size_t list_overlay_count(const listview_curses &lv) {
return 1;
};
bool list_value_for_overlay(const listview_curses &lv,
vis_line_t y,
int y, int bottom,
vis_line_t row,
attr_line_t &value_out) {
if (y != 0) {
return false;

View File

@ -786,8 +786,6 @@ int guess_type_from_pcre(const string &pattern, const char **collator)
vector<int> matches;
int retval = SQLITE3_TEXT;
log_debug("guess pattern %s", pattern.c_str());
*collator = NULL;
for (int lpc = 0; TYPE_TEST_VALUE[lpc].sqlite_type != SQLITE_NULL; lpc++) {
pcre_context_static<30> pc;
@ -799,7 +797,6 @@ int guess_type_from_pcre(const string &pattern, const char **collator)
}
}
log_debug("match size %d", matches.size());
if (matches.size() == 1) {
retval = TYPE_TEST_VALUE[matches.front()].sqlite_type;
*collator = TYPE_TEST_VALUE[matches.front()].collator;

View File

@ -53,7 +53,7 @@ sqlite_registration_func_t sqlite_registration_funcs[] = {
NULL
};
std::unordered_map<std::string, help_text *> sqlite_function_help;
multimap<std::string, help_text *> sqlite_function_help;
int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
{
@ -80,13 +80,14 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
basic_funcs[i].eTextRep,
(void *) &fd,
basic_funcs[i].xFunc,
0,
0);
nullptr,
nullptr);
if (fd.fd_help.ht_context != HC_NONE) {
help_text &ht = fd.fd_help;
sqlite_function_help[ht.ht_name] = &ht;
sqlite_function_help.insert(make_pair(ht.ht_name, &ht));
ht.index_tags();
}
}
@ -108,6 +109,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
"Return the absolute value of the argument")
.sql_function()
.with_parameter({"x", "The number to convert"})
.with_tags({"math"})
.with_example({"SELECT abs(-1)"}),
help_text("changes",
@ -119,6 +121,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.sql_function()
.with_parameter(help_text("X", "The unicode code point values")
.zero_or_more())
.with_tags({"string"})
.with_example({"SELECT char(0x48, 0x49)"}),
help_text("coalesce",
@ -154,6 +157,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.sql_function()
.with_parameter({"haystack", "The string to search within"})
.with_parameter({"needle", "The string to look for in the haystack"})
.with_tags({"string"})
.with_example({"SELECT instr('abc', 'b')"}),
help_text("last_insert_rowid",
@ -164,6 +168,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
"Returns the number of characters (not bytes) in the given string prior to the first NUL character")
.sql_function()
.with_parameter({"str", "The string to determine the length of"})
.with_tags({"string"})
.with_example({"SELECT length('abc')"}),
help_text("like",
@ -202,6 +207,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
"Returns a copy of the given string with all ASCII characters converted to lower case.")
.sql_function()
.with_parameter({"str", "The string to convert."})
.with_tags({"string"})
.with_example({"SELECT lower('AbC')"}),
help_text("ltrim",
@ -210,6 +216,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"str", "The string to trim characters from the left side"})
.with_parameter(help_text("chars", "The characters to trim. Defaults to spaces.")
.optional())
.with_tags({"string"})
.with_example({"SELECT ltrim(' abc')"})
.with_example({"SELECT ltrim('aaaabbbc', 'ab')"}),
@ -219,6 +226,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter(help_text("X", "The numbers to find the maximum of. "
"If only one argument is given, this function operates as an aggregate.")
.one_or_more())
.with_tags({"math"})
.with_example({"SELECT max(2, 1, 3)"})
.with_example({"SELECT max(status) FROM http_status_codes"}),
@ -228,6 +236,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter(help_text("X", "The numbers to find the minimum of. "
"If only one argument is given, this function operates as an aggregate.")
.one_or_more())
.with_tags({"math"})
.with_example({"SELECT min(2, 1, 3)"})
.with_example({"SELECT min(status) FROM http_status_codes"}),
@ -245,6 +254,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.sql_function()
.with_parameter({"format", "The format of the string to return."})
.with_parameter(help_text("X", "The argument to substitute at a given position in the format."))
.with_tags({"string"})
.with_example({"SELECT printf('Hello, %s!', 'World')"})
.with_example({"SELECT printf('align: % 10s', 'small')"})
.with_example({"SELECT printf('value: %05d', 11)"}),
@ -271,6 +281,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"str", "The string to perform substitutions on."})
.with_parameter({"old", "The string to be replaced."})
.with_parameter({"replacement", "The string to replace any occurrences of the old string with."})
.with_tags({"string"})
.with_example({"SELECT replace('abc', 'x', 'z')"})
.with_example({"SELECT replace('abc', 'a', 'z')"}),
@ -280,6 +291,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"num", "The value to round."})
.with_parameter(help_text("digits", "The number of digits to the right of the decimal to round to.")
.optional())
.with_tags({"math"})
.with_example({"SELECT round(123.456)"})
.with_example({"SELECT round(123.456, 1)"})
.with_example({"SELECT round(123.456, 5)"}),
@ -290,6 +302,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"str", "The string to trim characters from the right side"})
.with_parameter(help_text("chars", "The characters to trim. Defaults to spaces.")
.optional())
.with_tags({"string"})
.with_example({"SELECT ltrim('abc ')"})
.with_example({"SELECT ltrim('abbbbcccc', 'bc')"}),
@ -323,6 +336,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
"If not given, then all characters through the end of the string are returned. "
"If the value is negative, then the characters before the start are returned.")
.optional())
.with_tags({"string"})
.with_example({"SELECT substr('abc', 2)"})
.with_example({"SELECT substr('abc', 2, 1)"})
.with_example({"SELECT substr('abc', -1)"})
@ -338,6 +352,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"str", "The string to trim characters from the left and right sides."})
.with_parameter(help_text("chars", "The characters to trim. Defaults to spaces.")
.optional())
.with_tags({"string"})
.with_example({"SELECT trim(' abc ')"})
.with_example({"SELECT trim('-+abc+-', '-+')"}),
@ -363,6 +378,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
"Returns a copy of the given string with all ASCII characters converted to upper case.")
.sql_function()
.with_parameter({"str", "The string to convert."})
.with_tags({"string"})
.with_example({"SELECT upper('aBc')"}),
help_text("zeroblob",
@ -376,6 +392,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"timestring", "The string to convert to a date."})
.with_parameter(help_text("modifier", "A transformation that is applied to the value to the left.")
.zero_or_more())
.with_tags({"datetime"})
.with_example({"SELECT date('2017-01-02T03:04:05')"})
.with_example({"SELECT date('2017-01-02T03:04:05', '+1 day')"})
.with_example({"SELECT date(1491341842, 'unixepoch')"}),
@ -386,6 +403,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"timestring", "The string to convert to a time."})
.with_parameter(help_text("modifier", "A transformation that is applied to the value to the left.")
.zero_or_more())
.with_tags({"datetime"})
.with_example({"SELECT time('2017-01-02T03:04:05')"})
.with_example({"SELECT time('2017-01-02T03:04:05', '+1 minute')"})
.with_example({"SELECT time(1491341842, 'unixepoch')"}),
@ -396,6 +414,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"timestring", "The string to convert to a date with time."})
.with_parameter(help_text("modifier", "A transformation that is applied to the value to the left.")
.zero_or_more())
.with_tags({"datetime"})
.with_example({"SELECT datetime('2017-01-02T03:04:05')"})
.with_example({"SELECT datetime('2017-01-02T03:04:05', '+1 minute')"})
.with_example({"SELECT datetime(1491341842, 'unixepoch')"}),
@ -406,6 +425,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"timestring", "The string to convert to a date with time."})
.with_parameter(help_text("modifier", "A transformation that is applied to the value to the left.")
.zero_or_more())
.with_tags({"datetime"})
.with_example({"SELECT julianday('2017-01-02T03:04:05')"})
.with_example({"SELECT julianday('2017-01-02T03:04:05', '+1 minute')"})
.with_example({"SELECT julianday(1491341842, 'unixepoch')"}),
@ -417,6 +437,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"timestring", "The string to convert to a date with time."})
.with_parameter(help_text("modifier", "A transformation that is applied to the value to the left.")
.zero_or_more())
.with_tags({"datetime"})
.with_example({"SELECT strftime('%Y', '2017-01-02T03:04:05')"})
.with_example({"SELECT strftime('The time is: %H%M%S', '2017-01-02T03:04:05', '+1 minute')"})
.with_example({"SELECT strftime('Julian day: %J', 1491341842, 'unixepoch')"}),
@ -425,6 +446,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
"Returns the average value of all non-NULL numbers within a group.")
.sql_function()
.with_parameter({"X", "The value to compute the average of."})
.with_tags({"math"})
.with_example({"SELECT avg(ex_duration) FROM lnav_example_log"})
.with_example({"SELECT ex_procname, avg(ex_duration) FROM lnav_example_log GROUP BY ex_procname"}),
@ -442,6 +464,7 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_parameter({"X", "The value to concatenate."})
.with_parameter(help_text("sep", "The separator to place between the values.")
.optional())
.with_tags({"string"})
.with_example({"SELECT group_concat(ex_procname) FROM lnav_example_log"})
.with_example({"SELECT group_concat(ex_procname, ', ') FROM lnav_example_log"})
.with_example({"SELECT group_concat(DISTINCT ex_procname) FROM lnav_example_log"}),
@ -450,18 +473,21 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
"Returns the sum of the values in the group as an integer.")
.sql_function()
.with_parameter({"X", "The values to add."})
.with_tags({"math"})
.with_example({"SELECT sum(ex_duration) FROM lnav_example_log"}),
help_text("total",
"Returns the sum of the values in the group as a floating-point.")
.sql_function()
.with_parameter({"X", "The values to add."})
.with_tags({"math"})
.with_example({"SELECT total(ex_duration) FROM lnav_example_log"}),
};
for (auto &ht : builtin_funcs) {
sqlite_function_help[ht.ht_name] = &ht;
sqlite_function_help.insert(make_pair(ht.ht_name, &ht));
ht.index_tags();
}
static help_text idents[] = {
@ -481,6 +507,82 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
.with_flag_name("DATABASE"))
.with_example({"DETACH DATABASE customers"}),
help_text("CREATE", "Assign a name to a SELECT statement")
.sql_keyword()
.with_parameter(help_text("TEMP")
.optional())
.with_parameter(help_text("")
.with_flag_name("VIEW"))
.with_parameter(help_text("IF NOT EXISTS", "Do not create the view if it already exists")
.optional())
.with_parameter(help_text("schema-name.", "The database to create the view in")
.optional())
.with_parameter(help_text("view-name", "The name of the view"))
.with_parameter(help_text("select-stmt", "The SELECT statement the view represents")
.with_flag_name("AS")),
help_text("CREATE", "Create a table")
.sql_keyword()
.with_parameter(help_text("TEMP").optional())
.with_parameter(help_text("")
.with_flag_name("TABLE"))
.with_parameter(help_text("IF NOT EXISTS")
.optional())
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("table-name"))
.with_parameter(help_text("select-stmt")
.with_flag_name("AS")),
help_text("DELETE", "Delete rows from a table")
.sql_keyword()
.with_parameter(help_text("table-name", "The name of the table")
.with_flag_name("FROM"))
.with_parameter(help_text("cond", "The conditions used to delete the rows.")
.with_flag_name("WHERE")
.optional())
.with_example({"SELECT * FROM syslog_log"}),
help_text("DROP", "Drop an index")
.sql_keyword()
.with_parameter(help_text("")
.with_flag_name("INDEX"))
.with_parameter(help_text("IF EXISTS")
.optional())
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("index-name")),
help_text("DROP", "Drop a table")
.sql_keyword()
.with_parameter(help_text("")
.with_flag_name("TABLE"))
.with_parameter(help_text("IF EXISTS")
.optional())
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("table-name")),
help_text("DROP", "Drop a view")
.sql_keyword()
.with_parameter(help_text("")
.with_flag_name("VIEW"))
.with_parameter(help_text("IF EXISTS")
.optional())
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("view-name")),
help_text("DROP", "Drop a trigger")
.sql_keyword()
.with_parameter(help_text("")
.with_flag_name("TRIGGER"))
.with_parameter(help_text("IF EXISTS")
.optional())
.with_parameter(help_text("schema-name.")
.optional())
.with_parameter(help_text("trigger-name")),
help_text("SELECT",
"Query the database and return zero or more rows of data.")
.sql_keyword()
@ -556,12 +658,12 @@ int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs)
};
for (auto &ht : idents) {
sqlite_function_help[tolower(ht.ht_name)] = &ht;
sqlite_function_help.insert(make_pair(toupper(ht.ht_name), &ht));
for (const auto &param : ht.ht_parameters) {
if (!param.ht_flag_name) {
continue;
}
sqlite_function_help[tolower(param.ht_flag_name)] = &ht;
sqlite_function_help.insert(make_pair(toupper(param.ht_flag_name), &ht));
}
}

View File

@ -36,7 +36,7 @@
#include <sqlite3.h>
#include <string>
#include <unordered_map>
#include <map>
#include "help_text_formatter.hh"
@ -83,7 +83,7 @@ int time_extension_functions(struct FuncDef **basic_funcs,
extern sqlite_registration_func_t sqlite_registration_funcs[];
extern std::unordered_map<std::string, help_text *> sqlite_function_help;
extern std::multimap<std::string, help_text *> sqlite_function_help;
int register_sqlite_funcs(sqlite3 *db, sqlite_registration_func_t *reg_funcs);

View File

@ -55,7 +55,7 @@ static cache_entry *find_re(const char *re)
auto_mem<char> e2(sqlite3_free);
e2 = sqlite3_mprintf("%s: %s", re, c.re->error().c_str());
throw new pcrepp::error(e2.in(), 0);
throw pcrepp::error(e2.in(), 0);
}
CACHE[re_str] = c;
@ -91,7 +91,7 @@ regexp_match(const char *re, const char *str)
auto_mem<yajl_gen_t> gen(yajl_gen_free);
gen = yajl_gen_alloc(NULL);
gen = yajl_gen_alloc(nullptr);
yajl_gen_config(gen.in(), yajl_gen_beautify, false);
if (extractor.get_capture_count() == 1) {
@ -216,6 +216,7 @@ int string_extension_functions(struct FuncDef **basic_funcs,
.sql_function()
.with_parameter({"re", "The regular expression to use"})
.with_parameter({"str", "The string to test against the regular expression"})
.with_tags({"string", "regex"})
.with_example({"SELECT regexp_match('(\\d+)', '123')"})
.with_example({"SELECT regexp_match('(\\d+) (\\w+)', '123 four')"})
.with_example({"SELECT regexp_match('(?<num>\\d+) (?<str>\\w+)', '123 four')"})
@ -230,6 +231,7 @@ int string_extension_functions(struct FuncDef **basic_funcs,
.with_parameter({"repl", "The replacement string. "
"You can reference capture groups with a backslash followed by the number of the "
"group, starting with 1."})
.with_tags({"string", "regex"})
.with_example({"SELECT regexp_replace('Hello, World!', '^(\\w+)', 'Goodbye')"})
.with_example({"SELECT regexp_replace('123 abc', '(\\w+)', '<\\1>')"})
),
@ -239,6 +241,7 @@ int string_extension_functions(struct FuncDef **basic_funcs,
"Automatically Parse and extract data from a string")
.sql_function()
.with_parameter({"str", "The string to parse"})
.with_tags({"string"})
.with_example({"SELECT extract('foo=1 bar=2 name=\"Rolo Tomassi\"')"})
.with_example({"SELECT extract('1.0 abc 2.0')"})
),
@ -251,6 +254,7 @@ int string_extension_functions(struct FuncDef **basic_funcs,
.sql_function()
.with_parameter({"str", "The string to test"})
.with_parameter({"prefix", "The prefix to check in the string"})
.with_tags({"string"})
.with_example({"SELECT startswith('foobar', 'foo')"})
.with_example({"SELECT startswith('foobar', 'bar')"})
),
@ -261,6 +265,7 @@ int string_extension_functions(struct FuncDef **basic_funcs,
.sql_function()
.with_parameter({"str", "The string to test"})
.with_parameter({"suffix", "The suffix to check in the string"})
.with_tags({"string"})
.with_example({"SELECT endswith('notbad.jpg', '.jpg')"})
.with_example({"SELECT endswith('notbad.png', '.jpg')"})
),

View File

@ -55,8 +55,8 @@ void text_filter::add_line(
}
bookmark_type_t textview_curses::BM_USER("user");
bookmark_type_t textview_curses::BM_PARTITION("partition");
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");
@ -66,7 +66,6 @@ string_attr_type textview_curses::SA_FORMAT("format");
textview_curses::textview_curses()
: tc_sub_source(NULL),
tc_delegate(NULL),
tc_searching(false),
tc_selection_start(-1),
tc_selection_last(-1),
tc_selection_cleared(false),
@ -82,33 +81,49 @@ textview_curses::~textview_curses()
void textview_curses::reload_data(void)
{
if (this->tc_sub_source != NULL) {
if (this->tc_sub_source != nullptr) {
this->tc_sub_source->text_update_marks(this->tc_bookmarks);
}
this->listview_curses::reload_data();
}
void textview_curses::grep_begin(grep_proc &gp)
void textview_curses::grep_begin(grep_proc<vis_line_t> &gp, vis_line_t start, vis_line_t stop)
{
this->tc_searching = true;
require(this->tc_searching >= 0);
this->tc_searching += 1;
this->tc_search_action.invoke(this);
bookmark_vector<vis_line_t> &search_bv = this->tc_bookmarks[&BM_SEARCH];
if (start != -1) {
auto pair = search_bv.equal_range(vis_line_t(start), vis_line_t(stop));
for (auto mark_iter = pair.first;
mark_iter != pair.second;
++mark_iter) {
this->set_user_mark(&BM_SEARCH, *mark_iter, false);
}
}
listview_curses::reload_data();
}
void textview_curses::grep_end(grep_proc &gp)
void textview_curses::grep_end(grep_proc<vis_line_t> &gp)
{
this->tc_searching = false;
this->tc_searching -= 1;
this->tc_search_action.invoke(this);
ensure(this->tc_searching >= 0);
}
void textview_curses::grep_match(grep_proc &gp,
grep_line_t line,
void textview_curses::grep_match(grep_proc<vis_line_t> &gp,
vis_line_t line,
int start,
int end)
{
this->tc_bookmarks[&BM_SEARCH].insert_once(vis_line_t(line));
if (this->tc_sub_source != NULL) {
if (this->tc_sub_source != nullptr) {
this->tc_sub_source->text_mark(&BM_SEARCH, line, true);
}
@ -137,7 +152,7 @@ bool textview_curses::handle_mouse(mouse_event &me)
return true;
}
if (this->tc_delegate != NULL &&
if (this->tc_delegate != nullptr &&
this->tc_delegate->text_handle_mouse(*this, me)) {
return true;
}
@ -214,10 +229,8 @@ void textview_curses::textview_value_for_row(vis_line_t row,
{
view_colors &vc = view_colors::singleton();
bookmark_vector<vis_line_t> &user_marks = this->tc_bookmarks[&BM_USER];
bookmark_vector<vis_line_t> &part_marks = this->tc_bookmarks[&BM_PARTITION];
string_attrs_t & sa = value_out.get_attrs();
string & str = value_out.get_string();
highlight_map_t::iterator iter;
string_attrs_t &sa = value_out.get_attrs();
string &str = value_out.get_string();
text_format_t source_format = this->tc_sub_source->get_text_format();
intern_string_t format_name;
@ -245,20 +258,13 @@ void textview_curses::textview_value_for_row(vis_line_t row,
format_name = sa_iter->to_string();
}
for (iter = this->tc_highlights.begin();
for (auto iter = this->tc_highlights.begin();
iter != this->tc_highlights.end();
iter++) {
// XXX testing for '$search' here sucks
bool internal_hl = iter->first[0] == '$'
&& iter->first != "$search"
&& iter->first != "$preview";
int off;
size_t re_end;
if (body.lr_end > 8192)
re_end = 8192;
else
re_end = body.lr_end;
if (iter->second.h_text_format != TF_UNKNOWN &&
source_format != iter->second.h_text_format) {
@ -270,42 +276,7 @@ void textview_curses::textview_value_for_row(vis_line_t row,
continue;
}
for (off = internal_hl ? body.lr_start : 0; off < (int)str.size(); ) {
int rc, matches[60];
rc = pcre_exec(iter->second.h_code,
iter->second.h_code_extra,
str.c_str(),
re_end,
off,
0,
matches,
60);
if (rc > 0) {
struct line_range lr;
if (rc == 2) {
lr.lr_start = matches[2];
lr.lr_end = matches[3];
}
else {
lr.lr_start = matches[0];
lr.lr_end = matches[1];
}
if (lr.lr_end > lr.lr_start) {
sa.push_back(string_attr(
lr, &view_curses::VC_STYLE, iter->second.get_attrs()));
off = matches[1];
}
else {
off += 1;
}
}
else {
off = str.size();
}
}
iter->second.annotate(value_out, internal_hl ? body.lr_start : 0);
}
if (this->tc_hide_fields) {
@ -398,8 +369,4 @@ void textview_curses::textview_value_for_row(vis_line_t row,
sa.emplace_back(orig_line, &view_curses::VC_STYLE, A_REVERSE);
}
else if (binary_search(part_marks.begin(), part_marks.end(), row + 1)) {
sa.push_back(string_attr(
line_range(0), &view_curses::VC_STYLE, A_UNDERLINE));
}
}

View File

@ -32,6 +32,7 @@
#ifndef __textview_curses_hh
#define __textview_curses_hh
#include <utility>
#include <vector>
#include "grep_proc.hh"
@ -48,13 +49,14 @@ class textview_curses;
class logfile_filter_state {
public:
logfile_filter_state(std::shared_ptr<logfile> lf = nullptr) : tfs_logfile(lf) {
logfile_filter_state(std::shared_ptr<logfile> lf = nullptr) : tfs_logfile(
std::move(lf)) {
memset(this->tfs_filter_count, 0, sizeof(this->tfs_filter_count));
this->tfs_mask.reserve(64 * 1024);
};
void clear(void) {
this->tfs_logfile = NULL;
void clear() {
this->tfs_logfile = nullptr;
memset(this->tfs_filter_count, 0, sizeof(this->tfs_filter_count));
this->tfs_mask.clear();
this->tfs_index.clear();
@ -113,13 +115,13 @@ public:
lf_index(index) { };
virtual ~text_filter() { };
type_t get_type(void) const { return this->lf_type; };
std::string get_id(void) const { return this->lf_id; };
size_t get_index(void) const { return this->lf_index; };
type_t get_type() const { return this->lf_type; };
std::string get_id() const { return this->lf_id; };
size_t get_index() const { return this->lf_index; };
bool is_enabled(void) { return this->lf_enabled; };
void enable(void) { this->lf_enabled = true; };
void disable(void) { this->lf_enabled = false; };
bool is_enabled() { return this->lf_enabled; };
void enable() { this->lf_enabled = true; };
void disable() { this->lf_enabled = false; };
void revert_to_last(logfile_filter_state &lfs) {
this->lf_message_matched = this->lf_last_message_matched;
@ -160,7 +162,7 @@ public:
virtual bool matches(const logfile &lf, const logline &ll, shared_buffer_ref &line) = 0;
virtual std::string to_command(void) = 0;
virtual std::string to_command() = 0;
bool operator==(const std::string &rhs) {
return this->lf_id == rhs;
@ -206,14 +208,14 @@ public:
bool used[32];
memset(used, 0, sizeof(used));
for (iterator iter = this->begin(); iter != this->end(); ++iter) {
size_t index = (*iter)->get_index();
for (auto &iter : *this) {
size_t index = iter->get_index();
require(used[index] == false);
used[index] = true;
}
for (int lpc = 0; lpc < logfile_filter_state::MAX_FILTERS; lpc++) {
for (size_t lpc = 0; lpc < logfile_filter_state::MAX_FILTERS; lpc++) {
if (!used[lpc]) {
return lpc;
}
@ -221,17 +223,17 @@ public:
throw "No more filters";
};
void add_filter(std::shared_ptr<text_filter> filter) {
void add_filter(const std::shared_ptr<text_filter> &filter) {
this->fs_filters.push_back(filter);
};
void clear_filters(void) {
void clear_filters() {
while (!this->fs_filters.empty()) {
this->fs_filters.pop_back();
}
};
void set_filter_enabled(std::shared_ptr<text_filter> filter, bool enabled) {
void set_filter_enabled(const std::shared_ptr<text_filter> &filter, bool enabled) {
if (enabled) {
filter->enable();
}
@ -240,7 +242,7 @@ public:
}
}
std::shared_ptr<text_filter> get_filter(std::string id)
std::shared_ptr<text_filter> get_filter(const std::string &id)
{
auto iter = this->fs_filters.begin();
std::shared_ptr<text_filter> retval;
@ -255,7 +257,7 @@ public:
return retval;
};
bool delete_filter(std::string id) {
bool delete_filter(const std::string &id) {
auto iter = this->fs_filters.begin();
for (;
@ -273,7 +275,7 @@ public:
void get_enabled_mask(uint32_t &filter_in_mask, uint32_t &filter_out_mask) {
filter_in_mask = filter_out_mask = 0;
for (iterator iter = this->begin(); iter != this->end(); ++iter) {
for (auto iter = this->begin(); iter != this->end(); ++iter) {
std::shared_ptr<text_filter> tf = (*iter);
if (tf->is_enabled()) {
uint32_t bit = (1UL << tf->get_index());
@ -320,14 +322,16 @@ public:
};
enum {
RF_RAW = (1L << RB_RAW),
RF_FULL = (1L << RB_FULL),
RF_REWRITE = (1L << RB_REWRITE),
RF_RAW = (1UL << RB_RAW),
RF_FULL = (1UL << RB_FULL),
RF_REWRITE = (1UL << RB_REWRITE),
};
typedef long line_flags_t;
virtual void toggle_scrub(void) { };
void register_view(textview_curses *tc) {
this->tss_view = tc;
};
/**
* @return The total number of lines available from the source.
@ -367,7 +371,7 @@ public:
* @param added True if the line was bookmarked and false if it was
* unmarked.
*/
virtual void text_mark(bookmark_type_t *bm, int line, bool added) {};
virtual void text_mark(bookmark_type_t *bm, vis_line_t line, bool added) {};
/**
* Clear the bookmarks for a particular type in the text source.
@ -416,7 +420,8 @@ public:
return TF_UNKNOWN;
};
private:
protected:
textview_curses *tss_view;
filter_stack tss_filters;
};
@ -438,15 +443,15 @@ public:
class textview_curses
: public listview_curses,
public list_data_source,
public grep_proc_source,
public grep_proc_sink {
public grep_proc_source<vis_line_t>,
public grep_proc_sink<vis_line_t> {
public:
typedef view_action<textview_curses> action;
static bookmark_type_t BM_USER;
static bookmark_type_t BM_PARTITION;
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;
@ -477,8 +482,7 @@ public:
}
for (vis_line_t curr_line = start_line; curr_line <= end_line;
++curr_line) {
bookmark_vector<vis_line_t> &bv =
this->tc_bookmarks[bm];
bookmark_vector<vis_line_t> &bv = this->tc_bookmarks[bm];
bookmark_vector<vis_line_t>::iterator iter;
bool added;
@ -490,8 +494,8 @@ public:
bv.erase(iter);
added = false;
}
if (this->tc_sub_source != NULL) {
this->tc_sub_source->text_mark(bm, (int)curr_line, added);
if (this->tc_sub_source) {
this->tc_sub_source->text_mark(bm, curr_line, added);
}
}
};
@ -509,13 +513,14 @@ public:
bv.erase(iter);
}
}
if (this->tc_sub_source != NULL) {
this->tc_sub_source->text_mark(bm, (int)vl, marked);
if (this->tc_sub_source) {
this->tc_sub_source->text_mark(bm, vl, marked);
}
};
textview_curses &set_sub_source(text_sub_source *src) {
this->tc_sub_source = src;
src->register_view(this);
this->reload_data();
return *this;
};
@ -532,7 +537,7 @@ public:
void horiz_shift(vis_line_t start, vis_line_t end,
int off_start,
std::string highlight_name,
const std::string &highlight_name,
std::pair<int, int> &range_out)
{
highlighter &hl = this->tc_highlights[highlight_name];
@ -598,7 +603,7 @@ public:
this->tc_search_action = action(mf);
};
void grep_end_batch(grep_proc &gp)
void grep_end_batch(grep_proc<vis_line_t> &gp)
{
if (this->tc_follow_deadline.tv_sec) {
struct timeval now;
@ -624,16 +629,16 @@ public:
}
this->tc_search_action.invoke(this);
};
void grep_end(grep_proc &gp);
void grep_end(grep_proc<vis_line_t> &gp);
size_t listview_rows(const listview_curses &lv)
{
return this->tc_sub_source == NULL ? 0 :
return this->tc_sub_source == nullptr ? 0 :
this->tc_sub_source->text_line_count();
};
size_t listview_width(const listview_curses &lv) {
return this->tc_sub_source == NULL ? 0 :
return this->tc_sub_source == nullptr ? 0 :
this->tc_sub_source->text_line_width(*this);
};
@ -648,15 +653,15 @@ public:
};
std::string listview_source_name(const listview_curses &lv) {
return this->tc_sub_source == NULL ? "" :
return this->tc_sub_source == nullptr ? "" :
this->tc_sub_source->text_source_name(*this);
};
bool grep_value_for_line(int line, std::string &value_out)
bool grep_value_for_line(vis_line_t line, std::string &value_out)
{
bool retval = false;
if (this->tc_sub_source != NULL &&
if (this->tc_sub_source &&
line < (int)this->tc_sub_source->text_line_count()) {
this->tc_sub_source->text_value_for_line(*this,
line,
@ -668,20 +673,20 @@ public:
return retval;
};
void grep_begin(grep_proc &gp);
void grep_match(grep_proc &gp,
grep_line_t line,
void grep_begin(grep_proc<vis_line_t> &gp, vis_line_t start, vis_line_t stop);
void grep_match(grep_proc<vis_line_t> &gp,
vis_line_t line,
int start,
int end);
bool is_searching(void) { return this->tc_searching; };
bool is_searching(void) { return this->tc_searching > 0; };
void set_follow_search_for(int64_t ms_to_deadline) {
struct timeval now, tv;
tv.tv_sec = ms_to_deadline / 1000;
tv.tv_usec = (ms_to_deadline % 1000) * 1000;
gettimeofday(&now, NULL);
gettimeofday(&now, nullptr);
timeradd(&now, &tv, &this->tc_follow_deadline);
};
@ -702,6 +707,8 @@ public:
highlight_map_t &get_highlights() { return this->tc_highlights; };
const highlight_map_t &get_highlights() const { return this->tc_highlights; };
bool handle_mouse(mouse_event &me);
void reload_data(void);
@ -727,12 +734,11 @@ protected:
vis_bookmarks tc_bookmarks;
bool tc_searching;
int tc_searching{0};
struct timeval tc_follow_deadline;
action tc_search_action;
highlight_map_t tc_highlights;
highlight_map_t::iterator tc_current_highlight;
vis_line_t tc_selection_start;
vis_line_t tc_selection_last;

View File

@ -127,6 +127,7 @@ int time_extension_functions(struct FuncDef **basic_funcs,
.sql_function()
.with_parameter({"time", "The timestamp to get the time slice for."})
.with_parameter({"slice", "The size of the time slices"})
.with_tags({"datetime"})
.with_example({"SELECT timeslice('2017-01-01T05:05:00', '10m')"})
.with_example({"SELECT timeslice(log_time, '5m') AS slice, count(*) FROM lnav_example_log GROUP BY slice"})
),
@ -137,6 +138,7 @@ int time_extension_functions(struct FuncDef **basic_funcs,
.sql_function()
.with_parameter({"time1", "The first timestamp"})
.with_parameter({"time2", "The timestamp to subtract from the first"})
.with_tags({"datetime"})
.with_example({"SELECT timediff('2017-02-03T04:05:06', '2017-02-03T04:05:00')"})
.with_example({"SELECT timediff('today', 'yesterday')"})
),

View File

@ -61,6 +61,7 @@ public:
{
this->tss_fields[TSF_TIME].set_width(24);
this->tss_fields[TSF_PARTITION_NAME].set_width(34);
this->tss_fields[TSF_PARTITION_NAME].set_left_pad(1);
this->tss_fields[TSF_VIEW_NAME].set_width(8);
this->tss_fields[TSF_VIEW_NAME].set_role(view_colors::VCR_VIEW_STATUS);
this->tss_fields[TSF_VIEW_NAME].right_justify(true);

View File

@ -83,7 +83,7 @@ static struct _xterm_colors {
yajlpp_parse_context ypc_xterm("xterm-palette.json", root_color_handler);
yajl_handle handle;
handle = yajl_alloc(&ypc_xterm.ypc_callbacks, NULL, &ypc_xterm);
handle = yajl_alloc(&ypc_xterm.ypc_callbacks, nullptr, &ypc_xterm);
ypc_xterm
.with_ignore_unused(true)
.with_obj(this->xc_palette)
@ -175,8 +175,8 @@ ui_periodic_timer::ui_periodic_timer()
sa.sa_handler = ui_periodic_timer::sigalrm;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
sigaction(SIGALRM, &sa, NULL);
if (setitimer(ITIMER_REAL, &INTERVAL, NULL) == -1) {
sigaction(SIGALRM, &sa, nullptr);
if (setitimer(ITIMER_REAL, &INTERVAL, nullptr) == -1) {
perror("setitimer");
}
}
@ -208,7 +208,7 @@ attr_line_t &attr_line_t::with_ansi_string(const char *str, ...)
auto ret = vasprintf(formatted_str.out(), str, args);
va_end(args);
if (ret >= 0 && formatted_str != NULL) {
if (ret >= 0 && formatted_str != nullptr) {
this->al_string = formatted_str;
scrub_ansi_string(this->al_string, this->al_attrs);
}
@ -382,7 +382,8 @@ void view_curses::mvwattrline(WINDOW *window,
const struct line_range &lr,
view_colors::role_t base_role)
{
int text_attrs, attrs, line_width;
attr_t text_attrs, attrs;
int line_width;
string_attrs_t & sa = al.get_attrs();
string & line = al.get_string();
string_attrs_t::const_iterator iter;
@ -544,7 +545,7 @@ class color_listener : public lnav_config_listener {
static color_listener _COLOR_LISTENER;
int view_colors::BASIC_HL_PAIRS[view_colors::BASIC_COLOR_COUNT] = {
attr_t view_colors::BASIC_HL_PAIRS[view_colors::BASIC_COLOR_COUNT] = {
ansi_color_pair(COLOR_BLUE, COLOR_BLACK),
ansi_color_pair(COLOR_CYAN, COLOR_BLACK),
ansi_color_pair(COLOR_GREEN, COLOR_BLACK),
@ -620,7 +621,7 @@ void view_colors::init(void)
initialized = true;
}
inline int attr_for_colors(int &pair_base, short fg, short bg)
inline attr_t attr_for_colors(int &pair_base, short fg, short bg)
{
int pair = ++pair_base;

View File

@ -206,7 +206,7 @@ public:
broadcaster()
: b_functor(*this, &broadcaster::invoke) { };
virtual ~broadcaster() { };
virtual ~broadcaster() = default;
void invoke(_Sender *sender)
{
@ -233,8 +233,8 @@ private:
* parameters, the first being the value of the receiver pointer and the
* second being the sender pointer as passed to invoke().
*/
view_action(void(*invoker)(void *, _Sender *) = NULL)
: va_functor(NULL),
view_action(void(*invoker)(void *, _Sender *) = nullptr)
: va_functor(nullptr),
va_invoker(invoker) { };
template<class _Receiver>
@ -350,10 +350,12 @@ struct lab_color {
return i < 0.0 ? 0.0 : sqrt(i);
}
void operator=(const lab_color &other) {
lab_color& operator=(const lab_color &other) {
this->lc_l = other.lc_l;
this->lc_a = other.lc_a;
this->lc_b = other.lc_b;
return *this;
};
double lc_l;
@ -369,7 +371,7 @@ public:
static const unsigned long BASIC_COLOR_COUNT = 8;
static const unsigned long HI_COLOR_COUNT = 6 * 3 * 3;
static int BASIC_HL_PAIRS[BASIC_COLOR_COUNT];
static attr_t BASIC_HL_PAIRS[BASIC_COLOR_COUNT];
/** Roles that can be mapped to curses attributes using attrs_for_role() */
typedef enum {
@ -429,7 +431,7 @@ public:
* @param role The role to retrieve character attributes for.
* @return The attributes to use for the given role.
*/
int attrs_for_role(role_t role) const
attr_t attrs_for_role(role_t role) const
{
require(role >= 0);
require(role < VCR__MAX);
@ -437,7 +439,7 @@ public:
return this->vc_role_colors[role];
};
int reverse_attrs_for_role(role_t role) const
attr_t reverse_attrs_for_role(role_t role) const
{
require(role >= 0);
require(role < VCR__MAX);
@ -445,9 +447,9 @@ public:
return this->vc_role_reverse_colors[role];
};
int attrs_for_ident(const char *str, size_t len) const {
attr_t attrs_for_ident(const char *str, size_t len) const {
unsigned long index = crc32(1, (const Bytef*)str, len);
int retval;
attr_t retval;
if (COLORS >= 256) {
unsigned long offset = index % HI_COLOR_COUNT;
@ -460,7 +462,7 @@ public:
return retval;
};
int attrs_for_ident(const std::string &str) const {
attr_t attrs_for_ident(const std::string &str) const {
return this->attrs_for_ident(str.c_str(), str.length());
};
@ -471,7 +473,7 @@ public:
return VC_ANSI_START + ((fg * 8) + bg);
};
static inline int ansi_color_pair(int fg, int bg)
static inline attr_t ansi_color_pair(int fg, int bg)
{
return COLOR_PAIR(ansi_color_pair_index(fg, bg));
};
@ -489,9 +491,9 @@ private:
static bool initialized;
/** Map of role IDs to attribute values. */
int vc_role_colors[VCR__MAX];
attr_t vc_role_colors[VCR__MAX];
/** Map of role IDs to reverse-video attribute values. */
int vc_role_reverse_colors[VCR__MAX];
attr_t vc_role_reverse_colors[VCR__MAX];
int vc_color_pair_end;
};

View File

@ -91,6 +91,13 @@ struct from_sqlite<int64_t> {
}
};
template<>
struct from_sqlite<sqlite3_value *> {
inline sqlite3_value *operator()(int argc, sqlite3_value **val, int argi) {
return val[argi];
}
};
template<>
struct from_sqlite<int> {
inline int operator()(int argc, sqlite3_value **val, int argi) {
@ -175,6 +182,11 @@ inline void to_sqlite(sqlite3_context *ctx, int64_t val)
sqlite3_result_int64(ctx, val);
}
inline void to_sqlite(sqlite3_context *ctx, int val)
{
sqlite3_result_int64(ctx, val);
}
inline void to_sqlite(sqlite3_context *ctx, double val)
{
sqlite3_result_double(ctx, val);

View File

@ -91,18 +91,50 @@ int yajlpp_static_string(yajlpp_parse_context *ypc, const unsigned char *str, si
return 1;
}
bool yajlpp_string_validator(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
const json_path_handler_base *jph = ypc->ypc_current_handler;
bool retval = true;
if (jph->jph_pattern) {
pcre_input pi((const char *) str, 0, len);
pcre_context_static<30> pc;
if (!jph->jph_pattern->match(pc, pi)) {
ypc->report_error(LOG_LEVEL_ERROR,
"Value (%.*s) does not match pattern: %s",
len, str,
jph->jph_pattern_re);
retval = false;
}
}
if (len == 0 && jph->jph_min_length > 0) {
ypc->report_error(LOG_LEVEL_ERROR, "value must not be empty");
retval = false;
} else if (len < jph->jph_min_length) {
ypc->report_error(LOG_LEVEL_ERROR,
"value must be at least %lu characters long",
jph->jph_min_length);
retval = false;
}
return retval;
}
int yajlpp_static_string_vector(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
const json_path_handler_base *jph = ypc->ypc_current_handler;
if (jph->jph_kv_pair) {
map<string, vector<string>> &field_ptr = resolve_root<map<string, vector<string>>>(ypc);
auto &field_ptr = resolve_root<map<string, vector<string>>>(ypc);
field_ptr[ypc->get_path_fragment(-2)].push_back(string((const char *) str, len));
} else {
vector<string> &field_ptr = resolve_root<vector<string>>(ypc);
auto &field_ptr = resolve_root<vector<string>>(ypc);
field_ptr.push_back(string((const char *) str, len));
if (yajlpp_string_validator(ypc, str, len)) {
field_ptr.emplace_back((const char *) str, len);
}
}
yajlpp_validator_for_string_vector(*ypc, *ypc->ypc_current_handler);
@ -112,7 +144,7 @@ int yajlpp_static_string_vector(yajlpp_parse_context *ypc, const unsigned char *
int yajlpp_static_intern_string(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
intern_string_t &field_ptr = resolve_root<intern_string_t>(ypc);
auto &field_ptr = resolve_root<intern_string_t>(ypc);
field_ptr = intern_string::lookup((const char *) str, len);
@ -138,22 +170,25 @@ int yajlpp_static_enum(yajlpp_parse_context *ypc, const unsigned char *str, size
}
if (!found) {
ypc->report_error("error:%s:line %d\n "
ypc->report_error(LOG_LEVEL_ERROR,
"error:%s:line %d\n "
"Invalid value, '%.*s', for option:",
ypc->ypc_source.c_str(),
ypc->get_line_number(),
len,
str);
ypc->report_error(" %s %s -- %s\n",
ypc->report_error(LOG_LEVEL_ERROR,
" %s %s -- %s\n",
&ypc->ypc_path[0],
jph.jph_synopsis,
jph.jph_description);
ypc->report_error(" Allowed values: ");
ypc->report_error(LOG_LEVEL_ERROR,
" Allowed values: ");
for (int lpc = 0; jph.jph_enum_values[lpc].first; lpc++) {
const json_path_handler::enum_value_t &ev = jph.jph_enum_values[lpc];
ypc->report_error(" %s\n", ev.first);
ypc->report_error(LOG_LEVEL_ERROR, " %s\n", ev.first);
}
}
@ -202,9 +237,9 @@ yajl_gen_status yajlpp_static_gen_string_vector(yajlpp_gen_context &ygc,
yajl_gen handle)
{
if (jph.jph_kv_pair) {
map<string, vector<string>> *default_field_ptr = resolve_root<map<string, vector<string>>>(
auto *default_field_ptr = resolve_root<map<string, vector<string>>>(
ygc.ygc_default_stack, jph);
map<string, vector<string>> *field_ptr = resolve_root<map<string, vector<string>>>(
auto *field_ptr = resolve_root<map<string, vector<string>>>(
ygc.ygc_obj_stack, jph);
const string &base_name = ygc.ygc_base_name;
@ -241,12 +276,12 @@ void yajlpp_validator_for_string(yajlpp_parse_context &ypc,
return; // XXX
}
string &field_ptr = resolve_root<string>(&ypc);
auto &field_ptr = resolve_root<string>(&ypc);
if (field_ptr.empty() && jph.jph_min_length > 0) {
ypc.report_error("value must not be empty");
ypc.report_error(LOG_LEVEL_ERROR, "value must not be empty");
} else if (field_ptr.size() < jph.jph_min_length) {
ypc.report_error("value must be at least %lu characters long",
ypc.report_error(LOG_LEVEL_ERROR, "value must be at least %lu characters long",
jph.jph_min_length);
}
}
@ -258,13 +293,13 @@ void yajlpp_validator_for_string_vector(yajlpp_parse_context &ypc,
return; // XXX
}
vector<string> &field_ptr = resolve_root<vector<string>>(&ypc);
auto &field_ptr = resolve_root<vector<string>>(&ypc);
for (string str : field_ptr) {
for (const string &str : field_ptr) {
if (str.empty() && jph.jph_min_length > 0) {
ypc.report_error("value must not be empty");
ypc.report_error(LOG_LEVEL_ERROR, "value must not be empty");
} else if (str.size() < jph.jph_min_length) {
ypc.report_error("value must be at least %lu characters long",
ypc.report_error(LOG_LEVEL_ERROR, "value must be at least %lu characters long",
jph.jph_min_length);
}
}
@ -273,17 +308,17 @@ void yajlpp_validator_for_string_vector(yajlpp_parse_context &ypc,
void yajlpp_validator_for_intern_string(yajlpp_parse_context &ypc,
const json_path_handler_base &jph)
{
intern_string_t &field_ptr = resolve_root<intern_string_t>(&ypc);
auto &field_ptr = resolve_root<intern_string_t>(&ypc);
char buffer[1024];
if (field_ptr.empty() && jph.jph_min_length > 0) {
ypc.report_error("value must not be empty");
ypc.report_error(LOG_LEVEL_ERROR, "value must not be empty");
}
else if (field_ptr.size() < jph.jph_min_length) {
snprintf(buffer, sizeof(buffer),
"value must be at least %lu characters long",
jph.jph_min_length);
ypc.report_error(buffer);
ypc.report_error(LOG_LEVEL_ERROR, buffer);
}
}
@ -297,7 +332,7 @@ void yajlpp_validator_for_int(yajlpp_parse_context &ypc,
snprintf(buffer, sizeof(buffer),
"value must be greater than %lld",
jph.jph_min_value);
ypc.report_error(buffer);
ypc.report_error(LOG_LEVEL_ERROR, buffer);
}
}
@ -311,7 +346,7 @@ void yajlpp_validator_for_double(yajlpp_parse_context &ypc,
snprintf(buffer, sizeof(buffer),
"value must be greater than %lld",
jph.jph_min_value);
ypc.report_error(buffer);
ypc.report_error(LOG_LEVEL_ERROR, buffer);
}
}
@ -703,50 +738,58 @@ int yajlpp_parse_context::handle_unused(void *ctx)
int line_number = ypc->get_line_number();
if (handler != NULL && strlen(handler->jph_synopsis) > 0 &&
if (handler != nullptr && strlen(handler->jph_synopsis) > 0 &&
strlen(handler->jph_description) > 0) {
fprintf(stderr,
"warning:%s:line %d\n unexpected data for path -- \n",
ypc->ypc_source.c_str(),
line_number);
ypc->report_error(
LOG_LEVEL_WARNING,
"%s:line %d",
ypc->ypc_source.c_str(),
line_number);
ypc->report_error(LOG_LEVEL_WARNING, " unexpected data for path");
fprintf(stderr, " %s %s -- %s\n",
&ypc->ypc_path[0],
handler->jph_synopsis,
handler->jph_description
);
ypc->report_error(LOG_LEVEL_WARNING,
" %s %s -- %s",
&ypc->ypc_path[0],
handler->jph_synopsis,
handler->jph_description);
}
else {
fprintf(stderr,
"warning:%s:line %d\n unexpected path -- \n",
ypc->ypc_source.c_str(),
line_number);
else if (ypc->ypc_path[0]) {
ypc->report_error(LOG_LEVEL_WARNING,
"%s:line %d",
ypc->ypc_source.c_str(),
line_number);
ypc->report_error(LOG_LEVEL_WARNING, " unexpected path --");
fprintf(stderr, " %s\n", &ypc->ypc_path[0]);
ypc->report_error(LOG_LEVEL_WARNING, " %s", &ypc->ypc_path[0]);
} else {
ypc->report_error(LOG_LEVEL_WARNING,
"%s:line %d\n unexpected JSON value",
ypc->ypc_source.c_str(),
line_number);
}
if (ypc->ypc_callbacks.yajl_boolean != (int (*)(void *, int))yajlpp_parse_context::handle_unused ||
ypc->ypc_callbacks.yajl_integer != (int (*)(void *, long long))yajlpp_parse_context::handle_unused ||
ypc->ypc_callbacks.yajl_double != (int (*)(void *, double))yajlpp_parse_context::handle_unused ||
ypc->ypc_callbacks.yajl_string != (int (*)(void *, const unsigned char *, size_t))yajlpp_parse_context::handle_unused) {
fprintf(stderr, " expecting one of the following data types --\n");
ypc->report_error(LOG_LEVEL_WARNING, " expecting one of the following data types --");
}
if (ypc->ypc_callbacks.yajl_boolean != (int (*)(void *, int))yajlpp_parse_context::handle_unused) {
fprintf(stderr, " boolean\n");
ypc->report_error(LOG_LEVEL_WARNING, " boolean");
}
if (ypc->ypc_callbacks.yajl_integer != (int (*)(void *, long long))yajlpp_parse_context::handle_unused) {
fprintf(stderr, " integer\n");
ypc->report_error(LOG_LEVEL_WARNING, " integer");
}
if (ypc->ypc_callbacks.yajl_double != (int (*)(void *, double))yajlpp_parse_context::handle_unused) {
fprintf(stderr, " float\n");
ypc->report_error(LOG_LEVEL_WARNING, " float");
}
if (ypc->ypc_callbacks.yajl_string != (int (*)(void *, const unsigned char *, size_t))yajlpp_parse_context::handle_unused) {
fprintf(stderr, " string\n");
ypc->report_error(LOG_LEVEL_WARNING, " string");
}
if (handler == NULL) {
if (handler == nullptr) {
const json_path_handler_base *accepted_handlers;
if (ypc->ypc_sibling_handlers) {
@ -755,9 +798,9 @@ int yajlpp_parse_context::handle_unused(void *ctx)
accepted_handlers = ypc->ypc_handlers;
}
fprintf(stderr, " accepted paths --\n");
ypc->report_error(LOG_LEVEL_WARNING, " accepted paths --");
for (int lpc = 0; accepted_handlers[lpc].jph_path[0]; lpc++) {
fprintf(stderr, " %s %s -- %s\n",
ypc->report_error(LOG_LEVEL_WARNING, " %s %s -- %s",
accepted_handlers[lpc].jph_path,
accepted_handlers[lpc].jph_synopsis,
accepted_handlers[lpc].jph_description);

View File

@ -37,8 +37,10 @@
#include <limits.h>
#include <map>
#include <memory>
#include <set>
#include <stack>
#include <utility>
#include <vector>
#include <string>
#include <algorithm>
@ -103,6 +105,27 @@ struct yajlpp_provider_context {
};
};
class yajlpp_error : public std::exception {
public:
yajlpp_error(yajl_handle handle, const char *json, size_t len) {
auto_mem<unsigned char> yajl_msg;
yajl_msg = yajl_get_error(handle, 1, (const unsigned char *) json, len);
this->msg = (const char *) yajl_msg.in();
}
~yajlpp_error() override {
}
const char *what() const noexcept override {
return this->msg.c_str();
}
private:
std::string msg;
};
struct json_path_handler_base {
typedef std::pair<const char *, int> enum_value_t;
@ -147,6 +170,7 @@ struct json_path_handler_base {
json_path_handler_base *jph_children;
bool jph_kv_pair;
std::shared_ptr<pcrepp> jph_pattern;
const char * jph_pattern_re{nullptr};
size_t jph_min_length;
size_t jph_max_length;
const enum_value_t *jph_enum_values;
@ -275,7 +299,8 @@ struct json_path_handler : public json_path_handler_base {
}
json_path_handler &with_pattern(const char *re) {
this->jph_pattern.reset(new pcrepp(re));
this->jph_pattern_re = re;
this->jph_pattern = std::make_shared<pcrepp>(re);
return *this;
};
@ -387,20 +412,13 @@ struct json_path_handler : public json_path_handler_base {
class yajlpp_parse_context {
public:
typedef void (*error_reporter_t)(const yajlpp_parse_context &ypc,
lnav_log_level_t level,
const char *msg);
yajlpp_parse_context(const std::string &source,
struct json_path_handler *handlers = NULL)
: ypc_source(source),
ypc_line_number(1),
ypc_handlers(handlers),
ypc_userdata(NULL),
ypc_handle(NULL),
ypc_json_text(NULL),
ypc_ignore_unused(false),
ypc_sibling_handlers(nullptr),
ypc_current_handler(nullptr),
ypc_error_reporter(nullptr)
yajlpp_parse_context(std::string source,
struct json_path_handler *handlers = nullptr)
: ypc_source(std::move(source)),
ypc_handlers(handlers)
{
this->ypc_path.reserve(4096);
this->ypc_path.push_back('\0');
@ -422,7 +440,7 @@ public:
else {
end = this->ypc_path.size() - 1;
}
if (this->ypc_handlers != NULL) {
if (this->ypc_handlers) {
len_out = json_ptr::decode(frag_in, &this->ypc_path[start], end - start);
retval = frag_in;
}
@ -495,15 +513,15 @@ public:
this->ypc_path.push_back('\0');
this->ypc_path_index_stack.clear();
this->ypc_array_index.clear();
if (jph.jph_callbacks.yajl_null != NULL)
if (jph.jph_callbacks.yajl_null != nullptr)
this->ypc_callbacks.yajl_null = jph.jph_callbacks.yajl_null;
if (jph.jph_callbacks.yajl_boolean != NULL)
if (jph.jph_callbacks.yajl_boolean != nullptr)
this->ypc_callbacks.yajl_boolean = jph.jph_callbacks.yajl_boolean;
if (jph.jph_callbacks.yajl_integer != NULL)
if (jph.jph_callbacks.yajl_integer != nullptr)
this->ypc_callbacks.yajl_integer = jph.jph_callbacks.yajl_integer;
if (jph.jph_callbacks.yajl_double != NULL)
if (jph.jph_callbacks.yajl_double != nullptr)
this->ypc_callbacks.yajl_double = jph.jph_callbacks.yajl_double;
if (jph.jph_callbacks.yajl_string != NULL)
if (jph.jph_callbacks.yajl_string != nullptr)
this->ypc_callbacks.yajl_string = jph.jph_callbacks.yajl_string;
}
@ -559,38 +577,38 @@ public:
return yajl_complete_parse(this->ypc_handle);
};
void report_error(const char *format, ...) {
void report_error(lnav_log_level_t level, const char *format, ...) {
va_list args;
va_start(args, format);
if (this->ypc_error_reporter != NULL) {
if (this->ypc_error_reporter) {
char buffer[1024];
vsnprintf(buffer, sizeof(buffer), format, args);
this->ypc_error_reporter(*this, buffer);
this->ypc_error_reporter(*this, level, buffer);
}
va_end(args);
}
const std::string ypc_source;
int ypc_line_number;
int ypc_line_number{1};
struct json_path_handler *ypc_handlers;
void * ypc_userdata;
void * ypc_userdata{nullptr};
std::stack<void *> ypc_obj_stack;
yajl_handle ypc_handle;
const unsigned char * ypc_json_text;
yajl_handle ypc_handle{nullptr};
const unsigned char * ypc_json_text{nullptr};
yajl_callbacks ypc_callbacks;
yajl_callbacks ypc_alt_callbacks;
std::vector<char> ypc_path;
std::vector<size_t> ypc_path_index_stack;
std::vector<int> ypc_array_index;
pcre_context_static<30> ypc_pcre_context;
bool ypc_ignore_unused;
const struct json_path_handler_base *ypc_sibling_handlers;
const struct json_path_handler_base *ypc_current_handler;
bool ypc_ignore_unused{false};
const struct json_path_handler_base *ypc_sibling_handlers{nullptr};
const struct json_path_handler_base *ypc_current_handler{nullptr};
std::set<std::string> ypc_active_paths;
error_reporter_t ypc_error_reporter;
error_reporter_t ypc_error_reporter{nullptr};
void update_callbacks(const json_path_handler_base *handlers = NULL,
int child_start = 0);
@ -738,4 +756,27 @@ struct json_string {
size_t js_len;
};
class yajlpp_gen {
public:
yajlpp_gen() : yg_handle(yajl_gen_free) {
this->yg_handle = yajl_gen_alloc(NULL);
};
operator yajl_gen_t *() {
return this->yg_handle.in();
};
string_fragment to_string_fragment() {
const unsigned char *buf;
size_t len;
yajl_gen_get_buf(this->yg_handle.in(), &buf, &len);
return string_fragment((const char *) buf, 0, len);
};
private:
auto_mem<yajl_gen_t> yg_handle;
};
#endif

View File

@ -1,5 +1,5 @@
include_directories(../../lbuild/src ../src/ /opt/local/include)
include_directories(../../lbuild-debug/src ../src/ /opt/local/include)
add_executable(lnav_doctests
lnav_doctests.cc
@ -13,14 +13,14 @@ add_executable(test_reltime test_reltime.cc
../src/lnav_log.cc)
add_executable(test_date_time_scanner test_date_time_scanner.cc
../src/lnav_util.cc
../../lbuild/src/time_fmts.cc
../../lbuild-debug/src/time_fmts.cc
../src/ptimec_rt.cc
../src/pcrepp.cc
../src/lnav_log.cc
../src/spookyhash/SpookyV2.cpp)
add_executable(test_abbrev test_abbrev.cc
../src/lnav_util.cc
../../lbuild/src/time_fmts.cc
../../lbuild-debug/src/time_fmts.cc
../src/ptimec_rt.cc
../src/pcrepp.cc
../src/lnav_log.cc

View File

@ -239,6 +239,7 @@ dist_noinst_SCRIPTS = \
test_line_buffer.sh \
test_listview.sh \
test_logfile.sh \
test_meta.sh \
test_mvwattrline.sh \
test_scripts.sh \
test_sessions.sh \
@ -376,6 +377,7 @@ TESTS = \
test_line_buffer2 \
test_line_buffer.sh \
test_listview.sh \
test_meta.sh \
test_grep_proc.sh \
test_grep_proc2 \
test_hist_source \

View File

@ -40,17 +40,18 @@
#include "grep_proc.hh"
#include "line_buffer.hh"
#include "listview_curses.hh"
using namespace std;
class my_source : public grep_proc_source {
class my_source : public grep_proc_source<vis_line_t> {
public:
my_source(auto_fd &fd) : ms_offset(0) {
this->ms_buffer.set_fd(fd);
};
bool grep_value_for_line(int line_number, string &value_out) {
bool grep_value_for_line(vis_line_t line_number, string &value_out) {
bool retval = false;
try {
@ -77,27 +78,27 @@ private:
};
class my_sink : public grep_proc_sink {
class my_sink : public grep_proc_sink<vis_line_t> {
public:
my_sink() : ms_finished(false) { };
void grep_match(grep_proc &gp,
grep_line_t line,
void grep_match(grep_proc<vis_line_t> &gp,
vis_line_t line,
int start,
int end) {
printf("%d:%d:%d\n", (int)line, start, end);
};
void grep_capture(grep_proc &gp,
grep_line_t line,
void grep_capture(grep_proc<vis_line_t> &gp,
vis_line_t line,
int start,
int end,
char *capture) {
fprintf(stderr, "%d(%d:%d)%s\n", (int)line, start, end, capture);
};
void grep_end(grep_proc &gp) {
void grep_end(grep_proc<vis_line_t> &gp) {
this->ms_finished = true;
};
@ -131,11 +132,11 @@ int main(int argc, char *argv[])
my_source ms(fd);
my_sink msink;
grep_proc gp(code, ms);
grep_proc<vis_line_t> gp(code, ms);
gp.set_sink(&msink);
gp.queue_request();
gp.start();
gp.set_sink(&msink);
while (!msink.ms_finished) {
vector<struct pollfd> pollfds;

View File

@ -50,14 +50,14 @@
using namespace std;
class my_source : public grep_proc_source {
class my_source : public grep_proc_source<vis_line_t> {
public:
my_source(auto_fd &fd) : ms_offset(0) {
this->ms_buffer.set_fd(fd);
};
bool grep_value_for_line(int line_number, string &value_out) {
bool grep_value_for_line(vis_line_t line_number, string &value_out) {
bool retval = false;
try {
@ -131,11 +131,11 @@ int main(int argc, char *argv[])
vis_bookmarks bm;
sequence_sink ss(sm, bm[&SEQUENCE]);
grep_proc gp(code, ms);
grep_proc<vis_line_t> gp(code, ms);
gp.set_sink(&ss);
gp.queue_request();
gp.start();
gp.set_sink(&ss);
while (bm[&SEQUENCE].size() == 0) {
vector<struct pollfd> pollfds;

View File

@ -5,6 +5,8 @@
#include <sqlite3.h>
#include <iostream>
#include "lnav.hh"
#include "auto_mem.hh"
#include "sqlite-extension-func.hh"
@ -46,14 +48,17 @@ int main(int argc, char *argv[])
{
int retval = EXIT_SUCCESS;
auto_mem<sqlite3> db(sqlite3_close);
std::string stmt;
log_argv(argc, argv);
if (argc != 2) {
fprintf(stderr, "error: expecting an SQL statement\n");
retval = EXIT_FAILURE;
if (argc == 2) {
stmt = argv[1];
} else {
std::getline(std::cin, stmt, '\0');
}
else if (sqlite3_open(":memory:", db.out()) != SQLITE_OK) {
if (sqlite3_open(":memory:", db.out()) != SQLITE_OK) {
fprintf(stderr, "error: unable to make sqlite memory database\n");
retval = EXIT_FAILURE;
}
@ -73,7 +78,7 @@ int main(int argc, char *argv[])
register_regexp_vtab(db.in());
if (sqlite3_exec(db.in(),
argv[1],
stmt.c_str(),
sql_callback,
&state,
errmsg.out()) != SQLITE_OK) {

View File

@ -472,11 +472,32 @@ COMMANDS
relative-goto <line#|N%>
Move the current view up or down by the given amount.
next-mark error|warning|search|user|file|partition
comment <message>
Add a comment to the top line in the log view. The
comment will be saved in the session and will be available
the next time the file is loaded. Searches will also scan
the comment for any matches.
clear-comment Clear the comment that is attached to the top line in the
log view.
tag <tag1> [<tag2> [... <tagN>]]
Attach a tag to the top line in the log view. The tags are
prefixed with a '#', if they don't have one already. And,
like comments, they are saved in the session and
searchable.
untag <tag1> [<tag2> [... <tagN>]]
Detach a tag from the top line in the log view.
delete-tags <tag1> [<tag2> [... <tagN>]]
Detach the tags from all log lines.
next-mark error|warning|search|user|file|meta
Move to the next bookmark of the given type in the
current view.
prev-mark error|warning|search|user|file|partition
prev-mark error|warning|search|user|file|meta
Move to the previous bookmark of the given type in the
current view.
@ -937,7 +958,8 @@ Synopsis
abs(x) -- Return the absolute value of the argument
Parameter
x The number to convert
See Also
avg(), max(), min(), round(), sum(), total()
Example
#1 ;SELECT abs(-1)
@ -947,7 +969,8 @@ Synopsis
avg(X) -- Returns the average value of all non-NULL numbers within a group.
Parameter
X The value to compute the average of.
See Also
abs(), max(), min(), round(), sum(), total()
Examples
#1 ;SELECT avg(ex_duration) FROM lnav_example_log
@ -961,7 +984,8 @@ Synopsis
basename(path) -- Extract the base portion of a pathname.
Parameter
path The path
See Also
dirname(), joinpath(), readlink(), realpath()
Examples
#1 ;SELECT basename('foobar')
@ -995,7 +1019,10 @@ Synopsis
unicode code point values
Parameter
X The unicode code point values
See Also
endswith(), extract(), group_concat(), instr(), leftstr(), length(), lower(),
ltrim(), printf(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Example
#1 ;SELECT char(0x48, 0x49)
@ -1033,7 +1060,8 @@ Synopsis
Parameters
timestring The string to convert to a date.
modifier A transformation that is applied to the value to the left.
See Also
datetime(), julianday(), strftime(), time(), timediff(), timeslice()
Examples
#1 ;SELECT date('2017-01-02T03:04:05')
@ -1051,7 +1079,8 @@ Synopsis
Parameters
timestring The string to convert to a date with time.
modifier A transformation that is applied to the value to the left.
See Also
date(), julianday(), strftime(), time(), timediff(), timeslice()
Examples
#1 ;SELECT datetime('2017-01-02T03:04:05')
@ -1067,7 +1096,8 @@ Synopsis
dirname(path) -- Extract the directory portion of a pathname.
Parameter
path The path
See Also
basename(), joinpath(), readlink(), realpath()
Examples
#1 ;SELECT dirname('foo/bar')
@ -1090,7 +1120,10 @@ Synopsis
Parameters
str The string to test
suffix The suffix to check in the string
See Also
char(), extract(), group_concat(), instr(), leftstr(), length(), lower(),
ltrim(), printf(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Examples
#1 ;SELECT endswith('notbad.jpg', '.jpg')
@ -1103,7 +1136,10 @@ Synopsis
extract(str) -- Automatically Parse and extract data from a string
Parameter
str The string to parse
See Also
char(), endswith(), group_concat(), instr(), leftstr(), length(), lower(),
ltrim(), printf(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Examples
#1 ;SELECT extract('foo=1 bar=2 name="Rolo Tomassi"')
@ -1116,7 +1152,8 @@ Synopsis
gethostbyaddr(hostname) -- Get the IP address for the given hostname
Parameter
hostname The DNS hostname to lookup.
See Also
gethostbyname()
Example
#1 ;SELECT gethostbyaddr('127.0.0.1')
@ -1126,7 +1163,8 @@ Synopsis
gethostbyname(hostname) -- Get the IP address for the given hostname
Parameter
hostname The DNS hostname to lookup.
See Also
gethostbyaddr()
Example
#1 ;SELECT gethostbyname('localhost')
@ -1149,7 +1187,10 @@ Synopsis
Parameters
X The value to concatenate.
sep The separator to place between the values.
See Also
char(), endswith(), extract(), instr(), leftstr(), length(), lower(), ltrim(),
printf(), regexp_match(), regexp_replace(), replace(), replicate(), reverse(),
rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Examples
#1 ;SELECT group_concat(ex_procname) FROM lnav_example_log
@ -1191,7 +1232,10 @@ Synopsis
Parameters
haystack The string to search within
needle The string to look for in the haystack
See Also
char(), endswith(), extract(), group_concat(), leftstr(), length(), lower(),
ltrim(), printf(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Example
#1 ;SELECT instr('abc', 'b')
@ -1202,7 +1246,8 @@ Synopsis
Parameters
json The JSON object to query.
ptr The JSON-Pointer to lookup in the object.
See Also
json_contains()
Examples
#1 ;SELECT jget('1', '')
@ -1217,7 +1262,8 @@ Parameter
path One or more path components to join together. If an argument starts
with a forward or backward slash, it will be considered an absolute
path and any preceding elements will be ignored.
See Also
basename(), dirname(), readlink(), realpath()
Examples
#1 ;SELECT joinpath('foo', 'bar')
@ -1232,13 +1278,29 @@ Examples
Synopsis
json_contains(json, value) --
Parameters
json The JSON value to query.
value The value to look for in the first argument
See Also
jget()
Examples
#1 ;SELECT json_contains('[1, 2, 3]', 4)
#2 ;SELECT json_contains('["abc", "def"]', 'def')
Synopsis
julianday(timestring, modifier, ...) -- Returns the number of days since noon
in Greenwich on November 24, 4714 B.C.
Parameters
timestring The string to convert to a date with time.
modifier A transformation that is applied to the value to the left.
See Also
date(), datetime(), strftime(), time(), timediff(), timeslice()
Examples
#1 ;SELECT julianday('2017-01-02T03:04:05')
@ -1261,7 +1323,10 @@ Synopsis
Parameters
str The string to return subset.
N The number of characters from the left side of the string to return.
See Also
char(), endswith(), extract(), group_concat(), instr(), length(), lower(),
ltrim(), printf(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Examples
#1 ;SELECT leftstr('abc', 1)
@ -1275,7 +1340,10 @@ Synopsis
string prior to the first NUL character
Parameter
str The string to determine the length of
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), lower(),
ltrim(), printf(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Example
#1 ;SELECT length('abc')
@ -1334,7 +1402,10 @@ Synopsis
converted to lower case.
Parameter
str The string to convert.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
ltrim(), printf(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Example
#1 ;SELECT lower('AbC')
@ -1347,7 +1418,10 @@ Synopsis
Parameters
str The string to trim characters from the left side
chars The characters to trim. Defaults to spaces.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), printf(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Examples
#1 ;SELECT ltrim(' abc')
@ -1362,7 +1436,8 @@ Synopsis
Parameter
X The numbers to find the maximum of. If only one argument is given, this
function operates as an aggregate.
See Also
abs(), avg(), min(), round(), sum(), total()
Examples
#1 ;SELECT max(2, 1, 3)
@ -1377,7 +1452,8 @@ Synopsis
Parameter
X The numbers to find the minimum of. If only one argument is given, this
function operates as an aggregate.
See Also
abs(), avg(), max(), round(), sum(), total()
Examples
#1 ;SELECT min(2, 1, 3)
@ -1408,7 +1484,10 @@ Synopsis
Parameters
format The format of the string to return.
X The argument to substitute at a given position in the format.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), regexp_match(), regexp_replace(), replace(), replicate(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Examples
#1 ;SELECT printf('Hello, %s!', 'World')
@ -1449,14 +1528,16 @@ Synopsis
readlink(path) -- Read the target of a symbolic link.
Parameter
path The path to the symbolic link.
See Also
basename(), dirname(), joinpath(), realpath()
Synopsis
realpath(path) -- Returns the resolved version of the given path, expanding
symbolic links and resolving '.' and '..' references.
Parameter
path The path to resolve.
See Also
basename(), dirname(), joinpath(), readlink()
Synopsis
regexp(re, str) -- Test if a string matches a regular expression
@ -1471,7 +1552,11 @@ Synopsis
Parameters
re The regular expression to use
str The string to test against the regular expression
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_replace(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), startswith(), substr(), trim(),
upper()
Examples
#1 ;SELECT regexp_match('(\d+)', '123')
@ -1491,7 +1576,11 @@ Parameters
re The regular expression to match
repl The replacement string. You can reference capture groups with a
backslash followed by the number of the group, starting with 1.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_match(), regexp_match(), replace(),
replicate(), reverse(), rightstr(), rtrim(), startswith(), substr(), trim(),
upper()
Examples
#1 ;SELECT regexp_replace('Hello, World!', '^(\w+)', 'Goodbye')
@ -1508,7 +1597,10 @@ Parameters
str The string to perform substitutions on.
old The string to be replaced.
replacement The string to replace any occurrences of the old string with.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_match(), regexp_replace(), replicate(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Examples
#1 ;SELECT replace('abc', 'x', 'z')
@ -1522,7 +1614,10 @@ Synopsis
Parameters
str The string to replicate.
N The number of times to replicate the string.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_match(), regexp_replace(), replace(),
reverse(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Example
#1 ;SELECT replicate('abc', 3)
@ -1532,7 +1627,10 @@ Synopsis
reverse(str) -- Returns the reverse of the given string.
Parameter
str The string to reverse.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_match(), regexp_replace(), replace(),
replicate(), rightstr(), rtrim(), startswith(), substr(), trim(), upper()
Example
#1 ;SELECT reverse('abc')
@ -1544,7 +1642,10 @@ Synopsis
Parameters
str The string to return subset.
N The number of characters from the right side of the string to return.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rtrim(), startswith(), substr(), trim(), upper()
Examples
#1 ;SELECT rightstr('abc', 1)
@ -1559,7 +1660,8 @@ Synopsis
Parameters
num The value to round.
digits The number of digits to the right of the decimal to round to.
See Also
abs(), avg(), max(), min(), sum(), total()
Examples
#1 ;SELECT round(123.456)
@ -1578,7 +1680,10 @@ Synopsis
Parameters
str The string to trim characters from the right side
chars The characters to trim. Defaults to spaces.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), startswith(), substr(), trim(), upper()
Examples
#1 ;SELECT ltrim('abc ')
@ -1620,7 +1725,10 @@ Synopsis
Parameters
str The string to test
prefix The prefix to check in the string
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), substr(), trim(), upper()
Examples
#1 ;SELECT startswith('foobar', 'foo')
@ -1637,7 +1745,8 @@ Parameters
strftime() standard C library.
timestring The string to convert to a date with time.
modifier A transformation that is applied to the value to the left.
See Also
date(), datetime(), julianday(), time(), timediff(), timeslice()
Examples
#1 ;SELECT strftime('%Y', '2017-01-02T03:04:05')
@ -1660,7 +1769,10 @@ Parameters
size The size of the substring. If not given, then all characters through
the end of the string are returned. If the value is negative, then
the characters before the start are returned.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), startswith(), trim(), upper()
Examples
#1 ;SELECT substr('abc', 2)
@ -1679,7 +1791,8 @@ Synopsis
sum(X) -- Returns the sum of the values in the group as an integer.
Parameter
X The values to add.
See Also
abs(), avg(), max(), min(), round(), total()
Example
#1 ;SELECT sum(ex_duration) FROM lnav_example_log
@ -1691,7 +1804,8 @@ Synopsis
Parameters
timestring The string to convert to a time.
modifier A transformation that is applied to the value to the left.
See Also
date(), datetime(), julianday(), strftime(), timediff(), timeslice()
Examples
#1 ;SELECT time('2017-01-02T03:04:05')
@ -1708,7 +1822,8 @@ Synopsis
Parameters
time1 The first timestamp
time2 The timestamp to subtract from the first
See Also
date(), datetime(), julianday(), strftime(), time(), timeslice()
Examples
#1 ;SELECT timediff('2017-02-03T04:05:06', '2017-02-03T04:05:00')
@ -1723,7 +1838,8 @@ Synopsis
Parameters
time The timestamp to get the time slice for.
slice The size of the time slices
See Also
date(), datetime(), julianday(), strftime(), time(), timediff()
Examples
#1 ;SELECT timeslice('2017-01-01T05:05:00', '10m')
@ -1737,7 +1853,8 @@ Synopsis
total(X) -- Returns the sum of the values in the group as a floating-point.
Parameter
X The values to add.
See Also
abs(), avg(), max(), min(), round(), sum()
Example
#1 ;SELECT total(ex_duration) FROM lnav_example_log
@ -1755,7 +1872,10 @@ Synopsis
Parameters
str The string to trim characters from the left and right sides.
chars The characters to trim. Defaults to spaces.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), startswith(), substr(), upper()
Examples
#1 ;SELECT trim(' abc ')
@ -1800,7 +1920,10 @@ Synopsis
converted to upper case.
Parameter
str The string to convert.
See Also
char(), endswith(), extract(), group_concat(), instr(), leftstr(), length(),
lower(), ltrim(), printf(), regexp_match(), regexp_replace(), replace(),
replicate(), reverse(), rightstr(), rtrim(), startswith(), substr(), trim()
Example
#1 ;SELECT upper('aBc')
@ -1843,6 +1966,30 @@ Example
Synopsis
CREATE [TEMP] VIEW [IF NOT EXISTS] [schema-name.] view-name AS select-stmt
Assign a name to a SELECT statement
Parameters
IF NOT EXISTS Do not create the view if it already exists
schema-name. The database to create the view in
view-name The name of the view
select-stmt The SELECT statement the view represents
Synopsis
DELETE FROM table-name [WHERE cond]
Delete rows from a table
Parameters
table-name The name of the table
cond The conditions used to delete the rows.
Example
#1 ;SELECT * FROM syslog_log
Synopsis
DETACH DATABASE schema-name
@ -1855,6 +2002,13 @@ Example
Synopsis
DROP VIEW [IF EXISTS] [schema-name.] view-name
Drop a view
Parameters
Synopsis
SELECT result-column1 [, ... result-columnN] [FROM table1 [, ... tableN]]
[WHERE cond]

View File

@ -113,7 +113,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "include nonexistent" <<EOF
error: unknown script -- nonexistent.lnav -- file not found
error:command-option:1:unknown script -- nonexistent.lnav -- file not found
EOF
@ -457,6 +457,8 @@ check_output "write-json-to is not working" <<EOF
"log_idle_msecs": 0,
"log_level": "info",
"log_mark": 0,
"log_comment": null,
"log_tags": null,
"c_ip": "192.168.202.254",
"cs_method": "GET",
"cs_referer": "-",
@ -475,6 +477,8 @@ check_output "write-json-to is not working" <<EOF
"log_idle_msecs": 3000,
"log_level": "error",
"log_mark": 0,
"log_comment": null,
"log_tags": null,
"c_ip": "192.168.202.254",
"cs_method": "GET",
"cs_referer": "-",
@ -493,6 +497,8 @@ check_output "write-json-to is not working" <<EOF
"log_idle_msecs": 0,
"log_level": "info",
"log_mark": 0,
"log_comment": null,
"log_tags": null,
"c_ip": "192.168.202.254",
"cs_method": "GET",
"cs_referer": "-",

View File

@ -10,18 +10,18 @@ sed -i "" -e "s|/.*/format|format|g" `test_err_filename`
check_error_output "invalid format not detected?" <<EOF
warning:format.json:line 5
unexpected path --
/invalid_key_log/value/test/identifiers
accepted paths --
kind string|integer|float|boolean|json|quoted -- The type of data in the field
collate <function> -- The collating function to use for this column
unit/ -- Unit definitions for this field
identifier <bool> -- Indicates whether or not this field contains an identifier that should be highlighted
foreign-key <bool> -- Indicates whether or not this field should be treated as a foreign key for row in another table
hidden <bool> -- Indicates whether or not this field should be hidden
action-list# <string> -- Actions to execute when this field is clicked on
rewriter <command> -- A command that will rewrite this field when pretty-printing
description <string> -- A description of the field
warning: unexpected path --
warning: /invalid_key_log/value/test/identifiers
warning: accepted paths --
warning: kind string|integer|float|boolean|json|quoted -- The type of data in the field
warning: collate <function> -- The collating function to use for this column
warning: unit/ -- Unit definitions for this field
warning: identifier <bool> -- Indicates whether or not this field contains an identifier that should be highlighted
warning: foreign-key <bool> -- Indicates whether or not this field should be treated as a foreign key for row in another table
warning: hidden <bool> -- Indicates whether or not this field should be hidden
warning: action-list# <string> -- Actions to execute when this field is clicked on
warning: rewriter <command> -- A command that will rewrite this field when pretty-printing
warning: description <string> -- A description of the field
error:format.json:4:invalid json -- parse error: object key and value must be separated by a colon (':')
ar_log": { "abc" } }
(right here) ------^
@ -57,13 +57,13 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_leveltest.0
check_output "levels are not correct?" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark
0,<NULL>,2016-06-30 12:00:01.000,0,trace,0
1,<NULL>,2016-06-30 12:00:02.000,1000,debug,0
2,<NULL>,2016-06-30 12:00:03.000,1000,debug2,0
3,<NULL>,2016-06-30 12:00:04.000,1000,debug3,0
4,<NULL>,2016-06-30 12:00:05.000,1000,info,0
5,<NULL>,2016-06-30 12:00:06.000,1000,warning,0
6,<NULL>,2016-06-30 12:00:07.000,1000,fatal,0
7,<NULL>,2016-06-30 12:00:08.000,1000,info,0
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags
0,<NULL>,2016-06-30 12:00:01.000,0,trace,0,<NULL>,<NULL>
1,<NULL>,2016-06-30 12:00:02.000,1000,debug,0,<NULL>,<NULL>
2,<NULL>,2016-06-30 12:00:03.000,1000,debug2,0,<NULL>,<NULL>
3,<NULL>,2016-06-30 12:00:04.000,1000,debug3,0,<NULL>,<NULL>
4,<NULL>,2016-06-30 12:00:05.000,1000,info,0,<NULL>,<NULL>
5,<NULL>,2016-06-30 12:00:06.000,1000,warning,0,<NULL>,<NULL>
6,<NULL>,2016-06-30 12:00:07.000,1000,fatal,0,<NULL>,<NULL>
7,<NULL>,2016-06-30 12:00:08.000,1000,info,0,<NULL>,<NULL>
EOF

View File

@ -37,6 +37,7 @@
#include <sys/wait.h>
#include "grep_proc.hh"
#include "listview_curses.hh"
using namespace std;
@ -53,12 +54,12 @@ static struct {
{ 2, "" },
};
class my_source : public grep_proc_source {
class my_source : public grep_proc_source<vis_line_t> {
public:
my_source() : ms_current_line(0) { };
bool grep_value_for_line(int line_number, string &value_out) {
bool grep_value_for_line(vis_line_t line_number, string &value_out) {
bool retval = true;
assert(line_number == MS_LINES[this->ms_current_line].l_number);
@ -72,32 +73,32 @@ public:
int ms_current_line;
};
class my_sleeper_source : public grep_proc_source {
bool grep_value_for_line(int line_number, string &value_out) {
class my_sleeper_source : public grep_proc_source<vis_line_t> {
bool grep_value_for_line(vis_line_t line_number, string &value_out) {
sleep(1000);
return true;
};
};
class my_sink : public grep_proc_sink {
class my_sink : public grep_proc_sink<vis_line_t> {
public:
my_sink() : ms_finished(false) { };
void grep_match(grep_proc &gp,
grep_line_t line,
void grep_match(grep_proc<vis_line_t> &gp,
vis_line_t line,
int start,
int end) {
};
void grep_end(grep_proc &gp) {
void grep_end(grep_proc<vis_line_t> &gp) {
this->ms_finished = true;
};
bool ms_finished;
};
static void looper(grep_proc &gp)
static void looper(grep_proc<vis_line_t> &gp)
{
my_sink msink;
@ -129,17 +130,17 @@ int main(int argc, char *argv[])
{
my_source ms;
grep_proc gp(code, ms);
grep_proc<vis_line_t> gp(code, ms);
gp.queue_request(grep_line_t(10), grep_line_t(14));
gp.queue_request(grep_line_t(0), grep_line_t(3));
gp.queue_request(10_vl, 14_vl);
gp.queue_request(0_vl, 3_vl);
gp.start();
looper(gp);
}
{
my_sleeper_source mss;
grep_proc *gp = new grep_proc(code, mss);
grep_proc<vis_line_t> *gp = new grep_proc<vis_line_t>(code, mss);
int status;
gp->queue_request();

View File

@ -122,20 +122,20 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_json.json
check_output "log levels not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,user
0,<NULL>,2013-09-06 20:00:48.124,0,trace,0,<NULL>
1,<NULL>,2013-09-06 20:00:49.124,1000,info,0,<NULL>
2,<NULL>,2013-09-06 22:00:49.124,7200000,info,0,steve@example.com
4,<NULL>,2013-09-06 22:00:59.124,10000,debug5,0,<NULL>
5,<NULL>,2013-09-06 22:00:59.124,0,debug4,0,<NULL>
6,<NULL>,2013-09-06 22:00:59.124,0,debug3,0,<NULL>
7,<NULL>,2013-09-06 22:00:59.124,0,debug2,0,<NULL>
8,<NULL>,2013-09-06 22:00:59.124,0,debug,0,<NULL>
9,<NULL>,2013-09-06 22:01:49.124,50000,stats,0,<NULL>
10,<NULL>,2013-09-06 22:01:49.124,0,warning,0,<NULL>
11,<NULL>,2013-09-06 22:01:49.124,0,error,0,<NULL>
12,<NULL>,2013-09-06 22:01:49.124,0,critical,0,<NULL>
13,<NULL>,2013-09-06 22:01:49.124,0,fatal,0,<NULL>
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,user
0,<NULL>,2013-09-06 20:00:48.124,0,trace,0,<NULL>,<NULL>,<NULL>
1,<NULL>,2013-09-06 20:00:49.124,1000,info,0,<NULL>,<NULL>,<NULL>
2,<NULL>,2013-09-06 22:00:49.124,7200000,info,0,<NULL>,<NULL>,steve@example.com
4,<NULL>,2013-09-06 22:00:59.124,10000,debug5,0,<NULL>,<NULL>,<NULL>
5,<NULL>,2013-09-06 22:00:59.124,0,debug4,0,<NULL>,<NULL>,<NULL>
6,<NULL>,2013-09-06 22:00:59.124,0,debug3,0,<NULL>,<NULL>,<NULL>
7,<NULL>,2013-09-06 22:00:59.124,0,debug2,0,<NULL>,<NULL>,<NULL>
8,<NULL>,2013-09-06 22:00:59.124,0,debug,0,<NULL>,<NULL>,<NULL>
9,<NULL>,2013-09-06 22:01:49.124,50000,stats,0,<NULL>,<NULL>,<NULL>
10,<NULL>,2013-09-06 22:01:49.124,0,warning,0,<NULL>,<NULL>,<NULL>
11,<NULL>,2013-09-06 22:01:49.124,0,error,0,<NULL>,<NULL>,<NULL>
12,<NULL>,2013-09-06 22:01:49.124,0,critical,0,<NULL>,<NULL>,<NULL>
13,<NULL>,2013-09-06 22:01:49.124,0,fatal,0,<NULL>,<NULL>,<NULL>
EOF
@ -157,10 +157,10 @@ run_test ${lnav_test} -n -d /tmp/lnav.err \
${test_dir}/logfile_json2.json
check_output "log levels not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,cl,user
0,<NULL>,2013-09-06 20:00:49.124,0,info,0,com.exmaple.foo,<NULL>
1,<NULL>,2013-09-06 22:00:49.124,7200000,info,0,com.exmaple.foo,steve@example.com
3,<NULL>,2013-09-06 22:01:49.124,60000,error,0,com.exmaple.foo,<NULL>
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,cl,user
0,<NULL>,2013-09-06 20:00:49.124,0,info,0,<NULL>,<NULL>,com.exmaple.foo,<NULL>
1,<NULL>,2013-09-06 22:00:49.124,7200000,info,0,<NULL>,<NULL>,com.exmaple.foo,steve@example.com
3,<NULL>,2013-09-06 22:01:49.124,60000,error,0,<NULL>,<NULL>,com.exmaple.foo,<NULL>
EOF
@ -219,20 +219,20 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_nested_json.json
check_output "log levels not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,@fields/user
0,<NULL>,2013-09-06 20:00:48.124,0,trace,0,<NULL>
2,<NULL>,2013-09-06 20:00:49.124,1000,info,0,<NULL>
4,<NULL>,2013-09-06 22:00:49.124,7200000,info,0,steve@example.com
7,<NULL>,2013-09-06 22:00:59.124,10000,debug5,0,<NULL>
9,<NULL>,2013-09-06 22:00:59.124,0,debug4,0,<NULL>
11,<NULL>,2013-09-06 22:00:59.124,0,debug3,0,<NULL>
13,<NULL>,2013-09-06 22:00:59.124,0,debug2,0,<NULL>
15,<NULL>,2013-09-06 22:00:59.124,0,debug,0,<NULL>
17,<NULL>,2013-09-06 22:01:49.124,50000,stats,0,<NULL>
19,<NULL>,2013-09-06 22:01:49.124,0,warning,0,<NULL>
21,<NULL>,2013-09-06 22:01:49.124,0,error,0,<NULL>
23,<NULL>,2013-09-06 22:01:49.124,0,critical,0,<NULL>
25,<NULL>,2013-09-06 22:01:49.124,0,fatal,0,<NULL>
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,@fields/user
0,<NULL>,2013-09-06 20:00:48.124,0,trace,0,<NULL>,<NULL>,<NULL>
2,<NULL>,2013-09-06 20:00:49.124,1000,info,0,<NULL>,<NULL>,<NULL>
4,<NULL>,2013-09-06 22:00:49.124,7200000,info,0,<NULL>,<NULL>,steve@example.com
7,<NULL>,2013-09-06 22:00:59.124,10000,debug5,0,<NULL>,<NULL>,<NULL>
9,<NULL>,2013-09-06 22:00:59.124,0,debug4,0,<NULL>,<NULL>,<NULL>
11,<NULL>,2013-09-06 22:00:59.124,0,debug3,0,<NULL>,<NULL>,<NULL>
13,<NULL>,2013-09-06 22:00:59.124,0,debug2,0,<NULL>,<NULL>,<NULL>
15,<NULL>,2013-09-06 22:00:59.124,0,debug,0,<NULL>,<NULL>,<NULL>
17,<NULL>,2013-09-06 22:01:49.124,50000,stats,0,<NULL>,<NULL>,<NULL>
19,<NULL>,2013-09-06 22:01:49.124,0,warning,0,<NULL>,<NULL>,<NULL>
21,<NULL>,2013-09-06 22:01:49.124,0,error,0,<NULL>,<NULL>,<NULL>
23,<NULL>,2013-09-06 22:01:49.124,0,critical,0,<NULL>,<NULL>,<NULL>
25,<NULL>,2013-09-06 22:01:49.124,0,fatal,0,<NULL>,<NULL>,<NULL>
EOF
@ -266,8 +266,8 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_json3.json
check_output "json log3 format is not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,client_ip,request/method,request/size,request/uri,response/status
0,<NULL>,2017-03-24 20:06:26.240,0,info,0,1.1.1.1,GET,166,/example/uri/5,200
1,<NULL>,2017-03-24 20:12:47.764,381524,critical,0,1.1.1.1,GET,166,/example/uri/5,500
2,<NULL>,2017-03-24 20:15:31.694,163930,warning,0,1.1.1.1,GET,166,/example/uri/5,400
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,client_ip,request/method,request/size,request/uri,response/status
0,<NULL>,2017-03-24 20:06:26.240,0,info,0,<NULL>,<NULL>,1.1.1.1,GET,166,/example/uri/5,200
1,<NULL>,2017-03-24 20:12:47.764,381524,critical,0,<NULL>,<NULL>,1.1.1.1,GET,166,/example/uri/5,500
2,<NULL>,2017-03-24 20:15:31.694,163930,warning,0,<NULL>,<NULL>,1.1.1.1,GET,166,/example/uri/5,400
EOF

137
test/test_meta.sh Normal file
View File

@ -0,0 +1,137 @@
#! /bin/bash
lnav_test="${top_builddir}/src/lnav-test"
export HOME="./meta-sessions"
rm -rf "./meta-sessions"
mkdir -p $HOME
run_test ${lnav_test} -n \
-c ":comment Hello, World!" \
-c ":tag foo" \
-c ":save-session" \
${test_dir}/logfile_access_log.0
check_output ":tag did not work?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
+ Hello, World!
+ #foo
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -n \
-c ":load-session" \
${test_dir}/logfile_access_log.0
check_output "tag was not saved in session?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
+ Hello, World!
+ #foo
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -n \
-c ":load-session" \
-c ":untag #foo" \
${test_dir}/logfile_access_log.0
check_output ":untag did not work?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
+ Hello, World!
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -n \
-c ":load-session" \
-c ":clear-comment" \
${test_dir}/logfile_access_log.0
check_output ":clear-comment did not work?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
+ #foo
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -n \
-c ":goto 2" \
-c "/foo" \
-c ":tag #foo" \
-c ":goto 0" \
-c ":next-mark search" \
${test_dir}/logfile_access_log.0
check_output "searching for a tag did not work?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
+ #foo
EOF
run_test ${lnav_test} -n \
-c ":load-session" \
-c ";SELECT log_line, log_comment, log_tags FROM access_log" \
${test_dir}/logfile_access_log.0
check_output "metadata columns are not working?" <<EOF
log_line log_comment log_tags
0 Hello, World! ["#foo"]
1 <NULL> <NULL>
2 <NULL> <NULL>
EOF
run_test ${lnav_test} -n \
-c ";UPDATE access_log SET log_tags = json_array('#foo') WHERE log_line = 1" \
${test_dir}/logfile_access_log.0
check_output "updating log_tags is not working?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
+ #foo
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -n \
-c ";UPDATE access_log SET log_comment = 'Hello, World!' WHERE log_line = 1" \
${test_dir}/logfile_access_log.0
check_output "updating log_comment is not working?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
+ Hello, World!
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -n \
-c ";UPDATE access_log SET log_tags = 1 WHERE log_line = 1" \
${test_dir}/logfile_access_log.0
check_error_output "updating log_tags is not working?" <<EOF
error:command-option:1:command-option:line 1
unexpected JSON value
accepted paths --
# <tag> -- A tag for the log line
EOF
run_test ${lnav_test} -n \
-c ":tag foo" \
-c ";UPDATE access_log SET log_tags = null" \
${test_dir}/logfile_access_log.0
check_output "clearing log_tags is not working?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -n \
-c ":comment foo" \
-c ";UPDATE access_log SET log_comment = null" \
${test_dir}/logfile_access_log.0
check_output "clearing log_tags is not working?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkboot.gz HTTP/1.0" 404 46210 "-" "gPXE/0.9.7"
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
EOF

View File

@ -34,12 +34,12 @@ run_test env TZ=UTC ${lnav_test} -n \
${test_dir}/logfile_bro_http.log.0
check_output "bro logs are not recognized?" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,bro_ts,bro_uid,bro_id_orig_h,bro_id_orig_p,bro_id_resp_h,bro_id_resp_p,bro_trans_depth,bro_method,bro_host,bro_uri,bro_referrer,bro_version,bro_user_agent,bro_request_body_len,bro_response_body_len,bro_status_code,bro_status_msg,bro_info_code,bro_info_msg,bro_tags,bro_username,bro_password,bro_proxied,bro_orig_fuids,bro_orig_filenames,bro_orig_mime_types,bro_resp_fuids,bro_resp_filenames,bro_resp_mime_types
0,<NULL>,2011-11-03 00:19:26.452,0,info,0,1320279566.452687,CwFs1P2UcUdlSxD2La,192.168.2.76,52026,132.235.215.119,80,1,GET,www.reddit.com,/,<NULL>,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,109978,200,OK,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,Ftw3fJ2JJF3ntMTL2,<NULL>,text/html
1,<NULL>,2011-11-03 00:19:26.831,379,info,0,1320279566.831619,CJxSUgkInyKSHiju1,192.168.2.76,52030,72.21.211.173,80,1,GET,e.thumbs.redditmedia.com,/E-pbDbmiBclPkDaX.jpg,http://www.reddit.com/,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,2300,200,OK,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,FFTf9Zdgk3YkfCKo3,<NULL>,image/jpeg
2,<NULL>,2011-11-03 00:19:26.831,0,info,0,1320279566.831563,CJwUi9bdB9c1lLW44,192.168.2.76,52029,72.21.211.173,80,1,GET,f.thumbs.redditmedia.com,/BP5bQfy4o-C7cF6A.jpg,http://www.reddit.com/,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,2272,200,OK,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,FfXtOj3o7aub4vbs2j,<NULL>,image/jpeg
3,<NULL>,2011-11-03 00:19:26.831,0,info,0,1320279566.831473,CoX7zA3OJKGUOSCBY2,192.168.2.76,52027,72.21.211.173,80,1,GET,e.thumbs.redditmedia.com,/SVUtep3Rhg5FTRn4.jpg,http://www.reddit.com/,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,2562,200,OK,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,F21Ybs3PTqS6O4Q2Zh,<NULL>,image/jpeg
4,<NULL>,2011-11-03 00:19:26.831,0,info,0,1320279566.831643,CT0JIh479jXIGt0Po1,192.168.2.76,52031,72.21.211.173,80,1,GET,f.thumbs.redditmedia.com,/uuy31444rLSyKdHS.jpg,http://www.reddit.com/,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,1595,200,OK,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,Fdk0MZ1wQmKWAJ4WH4,<NULL>,image/jpeg
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,bro_ts,bro_uid,bro_id_orig_h,bro_id_orig_p,bro_id_resp_h,bro_id_resp_p,bro_trans_depth,bro_method,bro_host,bro_uri,bro_referrer,bro_version,bro_user_agent,bro_request_body_len,bro_response_body_len,bro_status_code,bro_status_msg,bro_info_code,bro_info_msg,bro_tags,bro_username,bro_password,bro_proxied,bro_orig_fuids,bro_orig_filenames,bro_orig_mime_types,bro_resp_fuids,bro_resp_filenames,bro_resp_mime_types
0,<NULL>,2011-11-03 00:19:26.452,0,info,0,<NULL>,<NULL>,1320279566.452687,CwFs1P2UcUdlSxD2La,192.168.2.76,52026,132.235.215.119,80,1,GET,www.reddit.com,/,<NULL>,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,109978,200,OK,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,Ftw3fJ2JJF3ntMTL2,<NULL>,text/html
1,<NULL>,2011-11-03 00:19:26.831,379,info,0,<NULL>,<NULL>,1320279566.831619,CJxSUgkInyKSHiju1,192.168.2.76,52030,72.21.211.173,80,1,GET,e.thumbs.redditmedia.com,/E-pbDbmiBclPkDaX.jpg,http://www.reddit.com/,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,2300,200,OK,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,FFTf9Zdgk3YkfCKo3,<NULL>,image/jpeg
2,<NULL>,2011-11-03 00:19:26.831,0,info,0,<NULL>,<NULL>,1320279566.831563,CJwUi9bdB9c1lLW44,192.168.2.76,52029,72.21.211.173,80,1,GET,f.thumbs.redditmedia.com,/BP5bQfy4o-C7cF6A.jpg,http://www.reddit.com/,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,2272,200,OK,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,FfXtOj3o7aub4vbs2j,<NULL>,image/jpeg
3,<NULL>,2011-11-03 00:19:26.831,0,info,0,<NULL>,<NULL>,1320279566.831473,CoX7zA3OJKGUOSCBY2,192.168.2.76,52027,72.21.211.173,80,1,GET,e.thumbs.redditmedia.com,/SVUtep3Rhg5FTRn4.jpg,http://www.reddit.com/,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,2562,200,OK,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,F21Ybs3PTqS6O4Q2Zh,<NULL>,image/jpeg
4,<NULL>,2011-11-03 00:19:26.831,0,info,0,<NULL>,<NULL>,1320279566.831643,CT0JIh479jXIGt0Po1,192.168.2.76,52031,72.21.211.173,80,1,GET,f.thumbs.redditmedia.com,/uuy31444rLSyKdHS.jpg,http://www.reddit.com/,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,1595,200,OK,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,Fdk0MZ1wQmKWAJ4WH4,<NULL>,image/jpeg
EOF
run_test env TZ=UTC ${lnav_test} -n \
@ -48,8 +48,8 @@ run_test env TZ=UTC ${lnav_test} -n \
${test_dir}/logfile_bro_http.log.0
check_output "bro logs are not recognized?" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,bro_ts,bro_uid,bro_id_orig_h,bro_id_orig_p,bro_id_resp_h,bro_id_resp_p,bro_trans_depth,bro_method,bro_host,bro_uri,bro_referrer,bro_version,bro_user_agent,bro_request_body_len,bro_response_body_len,bro_status_code,bro_status_msg,bro_info_code,bro_info_msg,bro_tags,bro_username,bro_password,bro_proxied,bro_orig_fuids,bro_orig_filenames,bro_orig_mime_types,bro_resp_fuids,bro_resp_filenames,bro_resp_mime_types
118,<NULL>,2011-11-03 00:19:49.337,18,error,0,1320279589.337053,CBHHuR1xFnm5C5CQBc,192.168.2.76,52074,74.125.225.76,80,1,GET,i4.ytimg.com,/vi/gDbg_GeuiSY/hqdefault.jpg,<NULL>,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,893,404,Not Found,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,F2GiAw3j1m22R2yIg2,<NULL>,image/jpeg
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,bro_ts,bro_uid,bro_id_orig_h,bro_id_orig_p,bro_id_resp_h,bro_id_resp_p,bro_trans_depth,bro_method,bro_host,bro_uri,bro_referrer,bro_version,bro_user_agent,bro_request_body_len,bro_response_body_len,bro_status_code,bro_status_msg,bro_info_code,bro_info_msg,bro_tags,bro_username,bro_password,bro_proxied,bro_orig_fuids,bro_orig_filenames,bro_orig_mime_types,bro_resp_fuids,bro_resp_filenames,bro_resp_mime_types
118,<NULL>,2011-11-03 00:19:49.337,18,error,0,<NULL>,<NULL>,1320279589.337053,CBHHuR1xFnm5C5CQBc,192.168.2.76,52074,74.125.225.76,80,1,GET,i4.ytimg.com,/vi/gDbg_GeuiSY/hqdefault.jpg,<NULL>,1.1,Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:7.0.1) Gecko/20100101 Firefox/7.0.1,0,893,404,Not Found,<NULL>,<NULL>,,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,<NULL>,F2GiAw3j1m22R2yIg2,<NULL>,image/jpeg
EOF
run_test ${lnav_test} -n \
@ -188,10 +188,10 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "setting log_part is not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,middle,2009-07-20 22:59:29.000,3000,error,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,middle,2009-07-20 22:59:29.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,middle,2009-07-20 22:59:29.000,3000,error,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,middle,2009-07-20 22:59:29.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
EOF
run_test ${lnav_test} -n \
@ -202,10 +202,10 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "setting log_part is not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,<NULL>,2009-07-20 22:59:29.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,<NULL>,2009-07-20 22:59:29.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
EOF
run_test ${lnav_test} -n \
@ -216,10 +216,10 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "setting log_part is not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,middle,2009-07-20 22:59:29.000,3000,error,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,middle,2009-07-20 22:59:29.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,middle,2009-07-20 22:59:29.000,3000,error,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,middle,2009-07-20 22:59:29.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
EOF
@ -242,10 +242,10 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "access_log table is not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,<NULL>,2009-07-20 22:59:29.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,<NULL>,2009-07-20 22:59:29.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
EOF
@ -255,8 +255,8 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "loglevel collator is not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
EOF
run_test ${lnav_test} -n \
@ -280,11 +280,11 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_syslog.0
check_output "syslog_log table is not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_hostname,log_msgid,log_pid,log_pri,log_procname,log_struct,syslog_version
0,<NULL>,2013-11-03 09:23:38.000,0,error,0,veridian,<NULL>,7998,<NULL>,automount,<NULL>,<NULL>
1,<NULL>,2013-11-03 09:23:38.000,0,info,0,veridian,<NULL>,16442,<NULL>,automount,<NULL>,<NULL>
2,<NULL>,2013-11-03 09:23:38.000,0,error,0,veridian,<NULL>,7999,<NULL>,automount,<NULL>,<NULL>
3,<NULL>,2013-11-03 09:47:02.000,1404000,info,0,veridian,<NULL>,<NULL>,<NULL>,sudo,<NULL>,<NULL>
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,log_hostname,log_msgid,log_pid,log_pri,log_procname,log_struct,syslog_version
0,<NULL>,2013-11-03 09:23:38.000,0,error,0,<NULL>,<NULL>,veridian,<NULL>,7998,<NULL>,automount,<NULL>,<NULL>
1,<NULL>,2013-11-03 09:23:38.000,0,info,0,<NULL>,<NULL>,veridian,<NULL>,16442,<NULL>,automount,<NULL>,<NULL>
2,<NULL>,2013-11-03 09:23:38.000,0,error,0,<NULL>,<NULL>,veridian,<NULL>,7999,<NULL>,automount,<NULL>,<NULL>
3,<NULL>,2013-11-03 09:47:02.000,1404000,info,0,<NULL>,<NULL>,veridian,<NULL>,<NULL>,<NULL>,sudo,<NULL>,<NULL>
EOF
@ -294,8 +294,8 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_syslog.0
check_output "log_time collation is wrong" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_hostname,log_msgid,log_pid,log_pri,log_procname,log_struct,syslog_version
3,<NULL>,2013-11-03 09:47:02.000,1404000,info,0,veridian,<NULL>,<NULL>,<NULL>,sudo,<NULL>,<NULL>
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,log_hostname,log_msgid,log_pid,log_pri,log_procname,log_struct,syslog_version
3,<NULL>,2013-11-03 09:47:02.000,1404000,info,0,<NULL>,<NULL>,veridian,<NULL>,<NULL>,<NULL>,sudo,<NULL>,<NULL>
EOF
@ -306,8 +306,8 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_syslog.0
check_output "logline table is not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_hostname,log_msgid,log_pid,log_pri,log_procname,log_struct,syslog_version,log_msg_instance,col_0,TTY,PWD,USER,COMMAND
0,<NULL>,2013-11-03 09:47:02.000,0,info,0,veridian,<NULL>,<NULL>,<NULL>,sudo,<NULL>,<NULL>,0,timstack,pts/6,/auto/wstimstack/rpms/lbuild/test,root,/usr/bin/tail /var/log/messages
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,log_hostname,log_msgid,log_pid,log_pri,log_procname,log_struct,syslog_version,log_msg_instance,col_0,TTY,PWD,USER,COMMAND
0,<NULL>,2013-11-03 09:47:02.000,0,info,0,<NULL>,<NULL>,veridian,<NULL>,<NULL>,<NULL>,sudo,<NULL>,<NULL>,0,timstack,pts/6,/auto/wstimstack/rpms/lbuild/test,root,/usr/bin/tail /var/log/messages
EOF
@ -318,8 +318,8 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_syslog.1
check_output "logline table is not working" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_hostname,log_msgid,log_pid,log_pri,log_procname,log_struct,syslog_version,log_msg_instance,col_0
1,<NULL>,2006-12-03 09:23:38.000,0,info,0,veridian,<NULL>,16442,<NULL>,automount,<NULL>,<NULL>,0,/auto/opt
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,log_hostname,log_msgid,log_pid,log_pri,log_procname,log_struct,syslog_version,log_msg_instance,col_0
1,<NULL>,2006-12-03 09:23:38.000,0,info,0,<NULL>,<NULL>,veridian,<NULL>,16442,<NULL>,automount,<NULL>,<NULL>,0,/auto/opt
EOF
run_test ${lnav_test} -n \
@ -386,7 +386,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "insert into environ table works" <<EOF
error: A non-empty name and value must be provided when inserting an environment variable
error:command-option:1:A non-empty name and value must be provided when inserting an environment variable
EOF
check_output "insert into environ table works" <<EOF
@ -398,7 +398,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "insert into environ table works" <<EOF
error: A non-empty name and value must be provided when inserting an environment variable
error:command-option:1:A non-empty name and value must be provided when inserting an environment variable
EOF
check_output "insert into environ table works" <<EOF
@ -410,7 +410,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "insert into environ table works" <<EOF
error: A non-empty name and value must be provided when inserting an environment variable
error:command-option:1:A non-empty name and value must be provided when inserting an environment variable
EOF
check_output "insert into environ table works" <<EOF
@ -422,7 +422,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "insert into environ table works" <<EOF
error: Environment variable names cannot contain an equals sign (=)
error:command-option:1:Environment variable names cannot contain an equals sign (=)
EOF
check_output "insert into environ table works" <<EOF
@ -434,7 +434,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "insert into environ table works" <<EOF
error: An environment variable with the name 'SQL_ENV_VALUE' already exists
error:command-option:1:An environment variable with the name 'SQL_ENV_VALUE' already exists
EOF
check_output "insert into environ table works" <<EOF
@ -557,7 +557,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "updating lnav_views.top_time with a bad time works?" <<EOF
error: Invalid time: bad-time
error:command-option:1:Invalid time: bad-time
EOF
@ -618,7 +618,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "errors are not reported" <<EOF
error: no such table: nonexistent_table
error:command-option:1:no such table: nonexistent_table
EOF
check_output "errors are not reported" <<EOF
@ -630,7 +630,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "errors are not reported" <<EOF
error: attempt to write a readonly database
error:command-option:1:attempt to write a readonly database
EOF
check_output "errors are not reported" <<EOF
@ -645,10 +645,10 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "partition-name does not work" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,middle,2009-07-20 22:59:29.000,3000,error,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,middle,2009-07-20 22:59:29.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,middle,2009-07-20 22:59:29.000,3000,error,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,middle,2009-07-20 22:59:29.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
EOF
@ -661,10 +661,10 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "clear-partition does not work" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,<NULL>,2009-07-20 22:59:29.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,<NULL>,2009-07-20 22:59:29.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
EOF
run_test ${lnav_test} -n \
@ -677,10 +677,10 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_output "clear-partition does not work when in the middle of a part" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,<NULL>,2009-07-20 22:59:29.000,0,info,0,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
0,<NULL>,2009-07-20 22:59:26.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/cgi/tramp,gPXE/0.9.7,-,HTTP/1.0,134,200
1,<NULL>,2009-07-20 22:59:29.000,3000,error,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkboot.gz,gPXE/0.9.7,-,HTTP/1.0,46210,404
2,<NULL>,2009-07-20 22:59:29.000,0,info,0,<NULL>,<NULL>,192.168.202.254,GET,-,<NULL>,/vmw/vSphere/default/vmkernel.gz,gPXE/0.9.7,-,HTTP/1.0,78929,200
EOF
@ -698,6 +698,8 @@ check_output "" <<EOF
"log_idle_msecs": 0,
"log_level": "info",
"log_mark": 0,
"log_comment": null,
"log_tags": null,
"contextid": "82e87195d704585501",
"data": "http://localhost:8086|/|<samlp:Response xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" ID=\"s2daac0735bf476f4560aab81104b623bedfb0cbc0\" InResponseTo=\"84cbf2be33f6410bbe55877545a93f02\" Version=\"2.0\" IssueInstant=\"2014-06-15T01:04:52Z\" Destination=\"http://localhost:8086/api/1/rest/admin/org/530e42ccd6f45fd16d0d0717/saml/consume\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://openam.vagrant.dev/openam</saml:Issuer><samlp:Status xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\">\\\\n<samlp:StatusCode xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"\\\\nValue=\"urn:oasis:names:tc:SAML:2.0:status:Success\">\\\\n</samlp:StatusCode>\\\\n</samlp:Status><saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"s2a0bee0da937e236167e99b209802056033816ac2\" IssueInstant=\"2014-06-15T01:04:52Z\" Version=\"2.0\">\\\\n<saml:Issuer>http://openam.vagrant.dev/openam</saml:Issuer><ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\\\\n<ds:SignedInfo>\\\\n<ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\\\\n<ds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha1\"/>\\\\n<ds:Reference URI=\"#s2a0bee0da937e236167e99b209802056033816ac2\">\\\\n<ds:Transforms>\\\\n<ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/>\\\\n<ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\\\\n</ds:Transforms>\\\\n<ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"/>\\\\n<ds:DigestValue>4uSmVzjovUdQd3px/RcnoxQBsqE=</ds:DigestValue>\\\\n</ds:Reference>\\\\n</ds:SignedInfo>\\\\n<ds:SignatureValue>\\\\nhm/grge36uA6j1OWif2bTcvVTwESjmuJa27NxepW0AiV5YlcsHDl7RAIk6k/CjsSero3bxGbm56m\\\\nYncOEi9F1Tu7dS0bfx+vhm/kKTPgwZctf4GWn4qQwP+KeoZywbNj9ShsYJ+zPKzXwN4xBSuPjMxP\\\\nNf5szzjEWpOndQO/uDs=\\\\n</ds:SignatureValue>\\\\n<ds:KeyInfo>\\\\n<ds:X509Data>\\\\n<ds:X509Certificate>\\\\nMIICQDCCAakCBEeNB0swDQYJKoZIhvcNAQEEBQAwZzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNh\\\\nbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMQwwCgYDVQQKEwNTdW4xEDAOBgNVBAsTB09w\\\\nZW5TU08xDTALBgNVBAMTBHRlc3QwHhcNMDgwMTE1MTkxOTM5WhcNMTgwMTEyMTkxOTM5WjBnMQsw\\\\nCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExDDAK\\\\nBgNVBAoTA1N1bjEQMA4GA1UECxMHT3BlblNTTzENMAsGA1UEAxMEdGVzdDCBnzANBgkqhkiG9w0B\\\\nAQEFAAOBjQAwgYkCgYEArSQc/U75GB2AtKhbGS5piiLkmJzqEsp64rDxbMJ+xDrye0EN/q1U5Of+\\\\nRkDsaN/igkAvV1cuXEgTL6RlafFPcUX7QxDhZBhsYF9pbwtMzi4A4su9hnxIhURebGEmxKW9qJNY\\\\nJs0Vo5+IgjxuEWnjnnVgHTs1+mq5QYTA7E6ZyL8CAwEAATANBgkqhkiG9w0BAQQFAAOBgQB3Pw/U\\\\nQzPKTPTYi9upbFXlrAKMwtFf2OW4yvGWWvlcwcNSZJmTJ8ARvVYOMEVNbsT4OFcfu2/PeYoAdiDA\\\\ncGy/F2Zuj8XJJpuQRSE6PtQqBuDEHjjmOQJ0rV/r8mO1ZCtHRhpZ5zYRjhRC9eCbjx9VrFax0JDC\\\\n/FfwWigmrW0Y0Q==\\\\n</ds:X509Certificate>\\\\n</ds:X509Data>\\\\n</ds:KeyInfo>\\\\n</ds:Signature><saml:Subject>\\\\n<saml:NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\" NameQualifier=\"http://openam.vagrant.dev/openam\">user@example.com</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\">\\\\n<saml:SubjectConfirmationData InResponseTo=\"84cbf2be33f6410bbe55877545a93f02\" NotOnOrAfter=\"2014-06-15T01:14:52Z\" Recipient=\"http://localhost:8086/api/1/rest/admin/org/530e42ccd6f45fd16d0d0717/saml/consume\"/></saml:SubjectConfirmation>\\\\n</saml:Subject><saml:Conditions NotBefore=\"2014-06-15T00:54:52Z\" NotOnOrAfter=\"2014-06-15T01:14:52Z\">\\\\n<saml:AudienceRestriction>\\\\n<saml:Audience>http://localhost:8086</saml:Audience>\\\\n</saml:AudienceRestriction>\\\\n</saml:Conditions>\\\\n<saml:AuthnStatement AuthnInstant=\"2014-06-15T01:00:25Z\" SessionIndex=\"s2f9b4d4b453d12b40ef3905cc959cdb40579c2301\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion></samlp:Response>",
"domain": "dc=openam",
@ -716,6 +718,8 @@ check_output "" <<EOF
"log_idle_msecs": 0,
"log_level": "trace",
"log_mark": 0,
"log_comment": null,
"log_tags": null,
"contextid": "ec5708a7f199678a01",
"data": "vagrant|/",
"domain": "dc=openam",
@ -767,22 +771,22 @@ EOF
cat ${test_dir}/logfile_syslog.0 | run_test ${lnav_test} -n \
-c ";select * from syslog_log where log_procname = 'automount'"
-c ";select log_time from syslog_log where log_procname = 'automount'"
check_output "querying against stdin is not working?" <<EOF
log_line log_part log_time log_idle_msecs log_level log_mark log_hostname log_msgid log_pid log_pri log_procname log_struct syslog_version
0 <NULL> 2018-11-03 09:23:38.000 0 error 0 veridian <NULL> 7998 <NULL> automount <NULL> <NULL>
1 <NULL> 2018-11-03 09:23:38.000 0 info 0 veridian <NULL> 16442 <NULL> automount <NULL> <NULL>
2 <NULL> 2018-11-03 09:23:38.000 0 error 0 veridian <NULL> 7999 <NULL> automount <NULL> <NULL>
log_time
2018-11-03 09:23:38.000
2018-11-03 09:23:38.000
2018-11-03 09:23:38.000
EOF
cat ${test_dir}/logfile_syslog.0 | run_test ${lnav_test} -n \
-c ";select * from syslog_log where log_procname = 'sudo'"
-c ";select log_time from syslog_log where log_procname = 'sudo'"
check_output "single result is not working?" <<EOF
log_line log_part log_time log_idle_msecs log_level log_mark log_hostname log_msgid log_pid log_pri log_procname log_struct syslog_version
3 <NULL> 2018-11-03 09:47:02.000 1404000 info 0 veridian <NULL> <NULL> <NULL> sudo <NULL> <NULL>
log_time
2018-11-03 09:47:02.000
EOF
# Create a dummy database for the next couple of tests to consume.
@ -831,7 +835,7 @@ run_test ${lnav_test} -n \
empty
check_error_output "LNAVSECURE mode bypassed" <<EOF
error: not authorized
error:command-option:1:not authorized
EOF
run_test ${lnav_test} -n \
@ -839,7 +843,7 @@ run_test ${lnav_test} -n \
empty
check_error_output "LNAVSECURE mode bypassed (':' adorned)" <<EOF
error: not authorized
error:command-option:1:not authorized
EOF
run_test ${lnav_test} -n \
@ -847,7 +851,7 @@ run_test ${lnav_test} -n \
empty
check_error_output "LNAVSECURE mode bypassed (filepath)" <<EOF
error: not authorized
error:command-option:1:not authorized
EOF
run_test ${lnav_test} -n \
@ -855,7 +859,7 @@ run_test ${lnav_test} -n \
empty
check_error_output "LNAVSECURE mode bypassed (URI)" <<EOF
error: not authorized
error:command-option:1:not authorized
EOF
unset LNAVSECURE
@ -868,8 +872,8 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_syslog_with_access_log.0
check_output "access_log not found within syslog file" <<EOF
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
1,<NULL>,2015-03-24 14:02:50.000,6927348000,info,0,127.0.0.1,GET,<NULL>,<NULL>,/includes/js/combined-javascript.js,<NULL>,-,HTTP/1.1,65508,200
log_line,log_part,log_time,log_idle_msecs,log_level,log_mark,log_comment,log_tags,c_ip,cs_method,cs_referer,cs_uri_query,cs_uri_stem,cs_user_agent,cs_username,cs_version,sc_bytes,sc_status
1,<NULL>,2015-03-24 14:02:50.000,6927348000,info,0,<NULL>,<NULL>,127.0.0.1,GET,<NULL>,<NULL>,/includes/js/combined-javascript.js,<NULL>,-,HTTP/1.1,65508,200
EOF

View File

@ -1,5 +1,57 @@
#! /bin/bash
run_test ./drive_sql "select json_contains('4', 4)"
check_output "json_contains does not work" <<EOF
Row 0:
Column json_contains('4', 4): 1
EOF
run_test ./drive_sql "select json_contains('4', 2)"
check_output "json_contains does not work" <<EOF
Row 0:
Column json_contains('4', 2): 0
EOF
run_test ./drive_sql <<EOF
select json_contains('"hi"', 'hi')
EOF
check_output "json_contains does not work" <<EOF
Row 0:
Column json_contains('"hi"', 'hi'): 1
EOF
run_test ./drive_sql <<EOF
select json_contains('["hi", "bye"]', 'hola') as res
EOF
check_output "json_contains does not work" <<EOF
Row 0:
Column res: 0
EOF
run_test ./drive_sql <<EOF
select json_contains('["hi", "bye", "solong"]', 'bye') as res
EOF
check_output "json_contains does not work" <<EOF
Row 0:
Column res: 1
EOF
run_test ./drive_sql <<EOF
select json_contains('["hi", "bye", "solong]', 'bye') as res
EOF
check_error_output "json_contains does not work" <<EOF
error: sqlite3_exec failed -- parse error: premature EOF
["hi", "bye", "solong]
(right here) ------^
EOF
run_test ./drive_sql "select jget('4', '')"
check_output "jget root does not work" <<EOF