[session] allow the name

This commit is contained in:
Timothy Stack 2020-04-25 07:32:05 -07:00
parent 138a506b1a
commit 3da3ec799a
32 changed files with 851 additions and 612 deletions

10
NEWS
View File

@ -9,8 +9,18 @@ lnav v0.8.6:
and unpaused by pressing it again. The bottom status bar will display
'Paused' in the right corner while paused.
* CMake is now a supported way to build.
* When viewing data from the standard-input, a symbolic name can now be
assigned to the view by doing an UPDATE to the filepath column of the
lnav_file table. For example, to assign the name "journald", the
following SQL statement can be executed in lnav:
;UPDATE lnav_file SET filepath='journald' WHERE filepath='stdin'
The symbolic name will be used when saving and loading session
information. A "rename-stdin" lnav script has also been added as a
convenience to perform this operation.
* The size of the terminal can be accessed in SQL using the $LINES and
$COLS variables.
* The raise_error(msg) SQL function has been added to make it easier to
raise an error in an lnav script to stop execution and notify the user.
Interface Changes:
* Data piped into lnav is no longer dumped to the console after exit.

View File

@ -194,6 +194,15 @@ The following functions can be used to access **lnav**'s internal state:
* log_top_datetime() - Return the timestamp of the line at the top of the log
view.
Miscellaneous
---------------
Miscellaneous functions:
* raise_error(msg) - Raises an error with the given message when executed.
This function can be useful to raise errors in **lnav** scripts, for example,
when checking arguments.
.. _collators:
Collators

View File

@ -55,7 +55,9 @@ following columns are available in this table:
:device: The device the file is stored on.
:inode: The inode for the file on the device.
:filepath: The absolute path to the file.
:filepath: If this is a real file, it will be the absolute path. Otherwise,
it is a symbolic name. If it is a symbolic name, it can be UPDATEd so that
this file will be considered when saving and loading session information.
:format: The log file format for the file.
:lines: The number of lines in the file.
:time_offset: The millisecond offset for timestamps. This column can be

View File

@ -37,6 +37,7 @@ BUILTIN_LNAVSCRIPTS = \
$(srcdir)/scripts/dhclient-summary.lnav \
$(srcdir)/scripts/lnav-pop-view.lnav \
$(srcdir)/scripts/partition-by-boot.lnav \
$(srcdir)/scripts/rename-stdin.lnav \
$(srcdir)/scripts/search-for.lnav \
$()
@ -165,6 +166,7 @@ dist_noinst_DATA = \
scripts/dump-pid.sh \
scripts/lnav-pop-view.lnav \
scripts/partition-by-boot.lnav \
scripts/rename-stdin.lnav \
scripts/search-for.lnav \
themes/default-theme.json \
themes/eldar.json \

View File

@ -62,4 +62,14 @@ auto operator|(T&& t, F f) -> decltype(detail::void_or_nullopt<decltype(f(std::f
else return detail::void_or_nullopt<return_type>();
}
template< class T >
optional_constexpr nonstd::optional< typename std::decay<T>::type > make_optional_from_nullable( T && v )
{
if (v != nullptr) {
return nonstd::optional<typename std::decay<T>::type>(
std::forward<T>(v));
}
return nonstd::nullopt;
}
#endif

View File

@ -59,7 +59,7 @@ int sql_progress(const struct log_cursor &lc)
size_t total = lnav_data.ld_log_source.text_line_count();
off_t off = lc.lc_curr_line;
if (lnav_data.ld_window == NULL) {
if (lnav_data.ld_window == nullptr) {
return 0;
}
@ -78,33 +78,44 @@ int sql_progress(const struct log_cursor &lc)
return 0;
}
string execute_from_file(exec_context &ec, const filesystem::path &path, int line_number, char mode, const string &cmdline);
void sql_progress_finished()
{
if (lnav_data.ld_window == nullptr) {
return;
}
string execute_command(exec_context &ec, const string &cmdline)
lnav_data.ld_bottom_source.update_loading(0, 0);
lnav_data.ld_top_source.update_time();
lnav_data.ld_status[LNS_TOP].do_update();
lnav_data.ld_status[LNS_BOTTOM].do_update();
lnav_data.ld_views[LNV_DB].redo_search();
}
Result<string, string> execute_from_file(exec_context &ec, const filesystem::path &path, int line_number, char mode, const string &cmdline);
Result<string, string> execute_command(exec_context &ec, const string &cmdline)
{
vector<string> args;
string msg;
log_info("Executing: %s", cmdline.c_str());
split_ws(cmdline, args);
if (args.size() > 0) {
if (!args.empty()) {
readline_context::command_map_t::iterator iter;
if ((iter = lnav_commands.find(args[0])) ==
lnav_commands.end()) {
msg = ec.get_error_prefix() + "unknown command - " + args[0];
if ((iter = lnav_commands.find(args[0])) == lnav_commands.end()) {
return ec.make_error("unknown command - {}", args[0]);
}
else {
msg = iter->second->c_func(ec, cmdline, args);
return iter->second->c_func(ec, cmdline, args);
}
}
return msg;
return ec.make_error("no command to execute");
}
string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
Result<string, string> execute_sql(exec_context &ec, const string &sql, string &alt_msg)
{
db_label_source &dls = lnav_data.ld_db_row_source;
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
@ -123,7 +134,7 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
ensure_view(&lnav_data.ld_views[LNV_SCHEMA]);
lnav_data.ld_mode = LNM_PAGING;
return "";
return Ok(string());
}
else if (stmt_str == ".msgformats") {
stmt_str = MSG_FORMAT_STMT;
@ -133,6 +144,7 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
pair<string, int> source = ec.ec_source.top();
sql_progress_guard progress_guard(sql_progress,
sql_progress_finished,
source.first,
source.second);
gettimeofday(&start_tv, NULL);
@ -144,12 +156,12 @@ 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 = ec.get_error_prefix() + string(errmsg);
alt_msg = "";
return ec.make_error("{}", errmsg);
}
else if (stmt == NULL) {
retval = "";
alt_msg = "";
return ec.make_error("No statement given");
}
else {
bool done = false;
@ -253,24 +265,23 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
retcode = sqlite3_step(stmt.in());
switch (retcode) {
case SQLITE_OK:
case SQLITE_DONE:
done = true;
break;
case SQLITE_OK:
case SQLITE_DONE:
done = true;
break;
case SQLITE_ROW:
ec.ec_sql_callback(ec, stmt.in());
break;
case SQLITE_ROW:
ec.ec_sql_callback(ec, stmt.in());
break;
default: {
const char *errmsg;
default: {
const char *errmsg;
log_error("sqlite3_step error code: %d", retcode);
errmsg = sqlite3_errmsg(lnav_data.ld_db);
retval = ec.get_error_prefix() + string(errmsg);
done = true;
}
break;
log_error("sqlite3_step error code: %d", retcode);
errmsg = sqlite3_errmsg(lnav_data.ld_db);
return ec.make_error("{}", errmsg);
break;
}
}
}
@ -371,16 +382,10 @@ string execute_sql(exec_context &ec, const string &sql, string &alt_msg)
#endif
}
if (!(lnav_data.ld_flags & LNF_HEADLESS)) {
lnav_data.ld_bottom_source.update_loading(0, 0);
lnav_data.ld_status[LNS_BOTTOM].do_update();
lnav_data.ld_views[LNV_DB].redo_search();
}
return retval;
return Ok(retval);
}
static string execute_file_contents(exec_context &ec, const filesystem::path &path, bool multiline)
static Result<string, string> execute_file_contents(exec_context &ec, const filesystem::path &path, bool multiline)
{
static filesystem::path stdin_path("-");
static filesystem::path dev_stdin_path("/dev/stdin");
@ -390,12 +395,12 @@ static string execute_file_contents(exec_context &ec, const filesystem::path &pa
if (path == stdin_path || path == dev_stdin_path) {
if (isatty(STDIN_FILENO)) {
return "error: stdin has already been consumed";
return ec.make_error("stdin has already been consumed");
}
file = stdin;
}
else if ((file = fopen(path.str().c_str(), "r")) == nullptr) {
return ec.get_error_prefix() + "unable to open file";
return ec.make_error("unable to open file");
}
int line_number = 0, starting_line_number = 0;
@ -423,7 +428,7 @@ static string execute_file_contents(exec_context &ec, const filesystem::path &pa
case ';':
case '|':
if (mode) {
retval = execute_from_file(ec, path, starting_line_number, mode, trim(cmdline));
retval = TRY(execute_from_file(ec, path, starting_line_number, mode, trim(cmdline)));
}
starting_line_number = line_number;
@ -435,7 +440,7 @@ static string execute_file_contents(exec_context &ec, const filesystem::path &pa
cmdline += line;
}
else {
retval = execute_from_file(ec, path, line_number, ':', line);
retval = TRY(execute_from_file(ec, path, line_number, ':', line));
}
break;
}
@ -443,7 +448,7 @@ static string execute_file_contents(exec_context &ec, const filesystem::path &pa
}
if (mode) {
retval = execute_from_file(ec, path, starting_line_number, mode, trim(cmdline));
retval = TRY(execute_from_file(ec, path, starting_line_number, mode, trim(cmdline)));
}
if (file == stdin) {
@ -456,113 +461,110 @@ static string execute_file_contents(exec_context &ec, const filesystem::path &pa
ec.ec_output_stack.pop_back();
ec.ec_path_stack.pop_back();
return retval;
return Ok(retval);
}
string execute_file(exec_context &ec, const string &path_and_args, bool multiline)
Result<string, string> execute_file(exec_context &ec, const string &path_and_args, bool multiline)
{
map<string, vector<script_metadata> > scripts;
map<string, vector<script_metadata> >::iterator iter;
vector<string> split_args;
string msg, retval;
string retval, msg;
shlex lexer(path_and_args);
log_info("Executing file: %s", path_and_args.c_str());
if (!lexer.split(split_args, ec.ec_local_vars.top())) {
retval = ec.get_error_prefix() + "unable to parse path";
return ec.make_error("unable to parse path");
}
else if (split_args.empty()) {
retval = ec.get_error_prefix() + "no script specified";
if (split_args.empty()) {
return ec.make_error("no script specified");
}
else {
ec.ec_local_vars.push({});
auto script_name = split_args[0];
map<string, string> &vars = ec.ec_local_vars.top();
char env_arg_name[32];
string star, result, open_error = "file not found";
ec.ec_local_vars.push({});
add_ansi_vars(vars);
auto script_name = split_args[0];
map<string, string> &vars = ec.ec_local_vars.top();
char env_arg_name[32];
string star, open_error = "file not found";
snprintf(env_arg_name, sizeof(env_arg_name), "%d", (int) split_args.size() - 1);
add_ansi_vars(vars);
vars["#"] = env_arg_name;
for (size_t lpc = 0; lpc < split_args.size(); lpc++) {
snprintf(env_arg_name, sizeof(env_arg_name), "%lu", lpc);
vars[env_arg_name] = split_args[lpc];
snprintf(env_arg_name, sizeof(env_arg_name), "%d",
(int) split_args.size() - 1);
vars["#"] = env_arg_name;
for (size_t lpc = 0; lpc < split_args.size(); lpc++) {
snprintf(env_arg_name, sizeof(env_arg_name), "%lu", lpc);
vars[env_arg_name] = split_args[lpc];
}
for (size_t lpc = 1; lpc < split_args.size(); lpc++) {
if (lpc > 1) {
star.append(" ");
}
for (size_t lpc = 1; lpc < split_args.size(); lpc++) {
if (lpc > 1) {
star.append(" ");
}
star.append(split_args[lpc]);
}
vars["__all__"] = star;
star.append(split_args[lpc]);
}
vars["__all__"] = star;
vector<script_metadata> paths_to_exec;
map<string, const char *>::iterator internal_iter;
vector<script_metadata> paths_to_exec;
map<string, const char *>::iterator internal_iter;
find_format_scripts(lnav_data.ld_config_paths, scripts);
if ((iter = scripts.find(script_name)) != scripts.end()) {
paths_to_exec = iter->second;
find_format_scripts(lnav_data.ld_config_paths, scripts);
if ((iter = scripts.find(script_name)) != scripts.end()) {
paths_to_exec = iter->second;
}
if (script_name == "-" || script_name == "/dev/stdin") {
paths_to_exec.push_back({script_name});
} else if (access(script_name.c_str(), R_OK) == 0) {
struct script_metadata meta;
meta.sm_path = script_name;
extract_metadata_from_file(meta);
paths_to_exec.push_back(meta);
} else if (errno != ENOENT) {
open_error = strerror(errno);
} else {
auto script_path = filesystem::path(script_name);
if (!script_path.is_absolute()) {
script_path = ec.ec_path_stack.back() / script_path;
}
if (script_name == "-" || script_name == "/dev/stdin") {
paths_to_exec.push_back({script_name});
}
else if (access(script_name.c_str(), R_OK) == 0) {
if (script_path.is_file()) {
struct script_metadata meta;
meta.sm_path = script_name;
meta.sm_path = script_path;
extract_metadata_from_file(meta);
paths_to_exec.push_back(meta);
}
else if (errno != ENOENT) {
} else if (errno != ENOENT) {
open_error = strerror(errno);
}
else {
auto script_path = filesystem::path(script_name);
if (!script_path.is_absolute()) {
script_path = ec.ec_path_stack.back() / script_path;
}
if (script_path.is_file()) {
struct script_metadata meta;
meta.sm_path = script_path;
extract_metadata_from_file(meta);
paths_to_exec.push_back(meta);
}
else if (errno != ENOENT) {
open_error = strerror(errno);
}
}
if (!paths_to_exec.empty()) {
for (auto &path_iter : paths_to_exec) {
result = execute_file_contents(ec, path_iter.sm_path, multiline);
}
retval = result;
}
else {
retval = ec.get_error_prefix()
+ "unknown script -- " + script_name + " -- " + open_error;
}
ec.ec_local_vars.pop();
}
return retval;
if (!paths_to_exec.empty()) {
for (auto &path_iter : paths_to_exec) {
retval = TRY(
execute_file_contents(ec, path_iter.sm_path, multiline));
}
}
ec.ec_local_vars.pop();
if (paths_to_exec.empty()) {
return ec.make_error("unknown script -- {} -- {}",
script_name, open_error);
}
return Ok(retval);
}
string execute_from_file(exec_context &ec, const filesystem::path &path, int line_number, char mode, const string &cmdline)
Result<string, string> execute_from_file(exec_context &ec, const filesystem::path &path, int line_number, char mode, const string &cmdline)
{
string retval, alt_msg;
auto _sg = ec.enter_source(path.str(), line_number);
ec.ec_source.emplace(path.str(), line_number);
switch (mode) {
case ':':
retval = execute_command(ec, cmdline);
retval = TRY(execute_command(ec, cmdline));
break;
case '/':
lnav_data.ld_view_stack.top() | [cmdline] (auto tc) {
@ -571,36 +573,35 @@ string execute_from_file(exec_context &ec, const filesystem::path &path, int lin
break;
case ';':
setup_logline_table(ec);
retval = execute_sql(ec, cmdline, alt_msg);
retval = TRY(execute_sql(ec, cmdline, alt_msg));
break;
case '|':
retval = execute_file(ec, cmdline);
retval = TRY(execute_file(ec, cmdline));
break;
default:
retval = execute_command(ec, cmdline);
retval = TRY(execute_command(ec, cmdline));
break;
}
rescan_files();
rebuild_indexes();
log_info("%s:%d:execute result -- %s",
path.str().c_str(),
line_number,
retval.c_str());
ec.ec_source.pop();
return retval;
return Ok(retval);
}
string execute_any(exec_context &ec, const string &cmdline_with_mode)
Result<string, string> execute_any(exec_context &ec, const string &cmdline_with_mode)
{
string retval, alt_msg, cmdline = cmdline_with_mode.substr(1);
auto _cleanup = finally([] {
rescan_files();
rebuild_indexes();
});
switch (cmdline_with_mode[0]) {
case ':':
retval = execute_command(ec, cmdline);
retval = TRY(execute_command(ec, cmdline));
break;
case '/':
lnav_data.ld_view_stack.top() | [cmdline] (auto tc) {
@ -609,24 +610,21 @@ string execute_any(exec_context &ec, const string &cmdline_with_mode)
break;
case ';':
setup_logline_table(ec);
retval = execute_sql(ec, cmdline, alt_msg);
retval = TRY(execute_sql(ec, cmdline, alt_msg));
break;
case '|': {
retval = execute_file(ec, cmdline);
retval = TRY(execute_file(ec, cmdline));
break;
}
default:
retval = execute_command(ec, cmdline);
retval = TRY(execute_command(ec, cmdline));
break;
}
rescan_files();
rebuild_indexes();
return retval;
return Ok(retval);
}
void execute_init_commands(exec_context &ec, vector<pair<string, string> > &msgs)
void execute_init_commands(exec_context &ec, vector<pair<Result<string, string>, string> > &msgs)
{
if (lnav_data.ld_cmd_init_done) {
return;
@ -637,14 +635,14 @@ void execute_init_commands(exec_context &ec, vector<pair<string, string> > &msgs
log_info("Executing initial commands");
for (auto &cmd : lnav_data.ld_commands) {
string msg, alt_msg;
string alt_msg;
wait_for_children();
ec.ec_source.emplace("command-option", option_index++);
switch (cmd.at(0)) {
case ':':
msg = execute_command(ec, cmd.substr(1));
msgs.emplace_back(execute_command(ec, cmd.substr(1)), alt_msg);
break;
case '/':
lnav_data.ld_view_stack.top() | [cmd] (auto tc) {
@ -653,15 +651,13 @@ void execute_init_commands(exec_context &ec, vector<pair<string, string> > &msgs
break;
case ';':
setup_logline_table(ec);
msg = execute_sql(ec, cmd.substr(1), alt_msg);
msgs.emplace_back(execute_sql(ec, cmd.substr(1), alt_msg), alt_msg);
break;
case '|':
msg = execute_file(ec, cmd.substr(1));
msgs.emplace_back(execute_file(ec, cmd.substr(1)), alt_msg);
break;
}
msgs.emplace_back(msg, alt_msg);
rescan_files();
rebuild_indexes();
@ -794,6 +790,7 @@ future<string> pipe_callback(exec_context &ec, const string &cmdline, auto_fd &f
cmdline.c_str());
lnav_data.ld_file_names[desc]
.with_fd(pp->get_fd())
.with_include_in_session(false)
.with_detect_format(false);
lnav_data.ld_files_to_front.emplace_back(desc, 0);
if (lnav_data.ld_rl_view != nullptr) {

View File

@ -35,6 +35,7 @@
#include <future>
#include <string>
#include "fmt/format.h"
#include "optional.hpp"
#include "auto_fd.hh"
#include "attr_line.hh"
@ -69,7 +70,14 @@ struct exec_context {
std::pair<std::string, int> source = this->ec_source.top();
return "error:" + source.first + ":" + std::to_string(source.second) + ":";
return fmt::format("{}:{}: error: ", source.first, source.second);
}
template<typename ...Args>
Result<std::string, std::string> make_error(
fmt::string_view format_str, const Args& ...args) {
return Err(this->get_error_prefix() +
fmt::vformat(format_str, fmt::make_format_args(args...)));
}
nonstd::optional<FILE *> get_output() {
@ -84,6 +92,23 @@ struct exec_context {
return nonstd::nullopt;
}
struct source_guard {
source_guard(exec_context &context) : sg_context(context) {
}
~source_guard() {
this->sg_context.ec_source.pop();
}
exec_context &sg_context;
};
source_guard enter_source(const std::string path, int line_number) {
this->ec_source.emplace(path, line_number);
return source_guard(*this);
}
vis_line_t ec_top_line;
bool ec_dry_run;
@ -101,18 +126,23 @@ struct exec_context {
pipe_callback_t ec_pipe_callback;
};
std::string execute_command(exec_context &ec, const std::string &cmdline);
Result<std::string, std::string> execute_command(exec_context &ec, const std::string &cmdline);
std::string execute_sql(exec_context &ec, const std::string &sql, std::string &alt_msg);
std::string execute_file(exec_context &ec, const std::string &path_and_args, bool multiline = true);
std::string execute_any(exec_context &ec, const std::string &cmdline);
void execute_init_commands(exec_context &ec, std::vector<std::pair<std::string, std::string> > &msgs);
Result<std::string, std::string> execute_sql(exec_context &ec, const std::string &sql, std::string &alt_msg);
Result<std::string, std::string> execute_file(exec_context &ec, const std::string &path_and_args, bool multiline = true);
Result<std::string, std::string> execute_any(exec_context &ec, const std::string &cmdline);
void execute_init_commands(exec_context &ec, std::vector<std::pair<Result<std::string, std::string>, std::string> > &msgs);
int sql_callback(exec_context &ec, sqlite3_stmt *stmt);
std::future<std::string> pipe_callback(
exec_context &ec, const std::string &cmdline, auto_fd &fd);
inline Result<std::string, std::string> err_to_ok(const std::string msg) {
return Ok(msg);
}
int sql_progress(const struct log_cursor &lc);
void sql_progress_finished();
void add_global_vars(exec_context &ec);

View File

@ -38,6 +38,7 @@
#include "base/lnav_log.hh"
#include "sql_util.hh"
#include "file_vtab.hh"
#include "session_data.hh"
#include "vtab_module.hh"
using namespace std;
@ -128,7 +129,7 @@ CREATE TABLE lnav_file (
sqlite3_int64 &rowid,
int64_t device,
int64_t inode,
const char *path,
std::string path,
const char *format,
int64_t lines,
int64_t time_offset) {
@ -140,6 +141,29 @@ CREATE TABLE lnav_file (
lf->adjust_content_time(0, tv, true);
if (path != lf->get_filename()) {
if (lf->is_valid_filename()) {
throw sqlite_func_error(
"real file paths cannot be updated, only symbolic ones");
}
auto iter = lnav_data.ld_file_names.find(lf->get_filename());
if (iter != lnav_data.ld_file_names.end()) {
auto loo = iter->second;
lnav_data.ld_file_names.erase(iter);
loo.loo_include_in_session = true;
lnav_data.ld_file_names[path] = loo;
lf->set_filename(path);
init_session();
load_session();
}
}
return SQLITE_OK;
};
};

View File

@ -136,7 +136,7 @@ string sql_readlink(const char *path)
struct stat st;
if (lstat(path, &st) == -1) {
throw sqlite_func_error("unable to stat path: %s -- %s", path, strerror(errno));
throw sqlite_func_error("unable to stat path: {} -- {}", path, strerror(errno));
}
char buf[st.st_size];
@ -147,7 +147,7 @@ string sql_readlink(const char *path)
if (errno == EINVAL) {
return path;
}
throw sqlite_func_error("unable to read link: %s -- %s", path, strerror(errno));
throw sqlite_func_error("unable to read link: {} -- {}", path, strerror(errno));
}
return string(buf, rc);
@ -159,7 +159,7 @@ string sql_realpath(const char *path)
char resolved_path[PATH_MAX];
if (realpath(path, resolved_path) == NULL) {
throw sqlite_func_error("Could not get real path for %s -- %s",
throw sqlite_func_error("Could not get real path for {} -- {}",
path, strerror(errno));
}

View File

@ -140,7 +140,6 @@ bool handle_keyseq(const char *keyseq)
vector<logline_value> values;
exec_context ec(&values, key_sql_callback, pipe_callback);
auto &var_stack = ec.ec_local_vars;
string result;
ec.ec_global_vars = lnav_data.ld_exec_context.ec_global_vars;
var_stack.push(map<string, string>());
@ -149,8 +148,8 @@ bool handle_keyseq(const char *keyseq)
const auto &kc = iter->second;
log_debug("executing key sequence x%02x: %s", keyseq, kc.kc_cmd.c_str());
result = execute_any(ec, kc.kc_cmd);
lnav_data.ld_rl_view->set_value(result);
auto result = execute_any(ec, kc.kc_cmd);
lnav_data.ld_rl_view->set_value(result.orElse(err_to_ok).unwrap());
if (!kc.kc_alt_msg.empty()) {
shlex lexer(kc.kc_alt_msg);

View File

@ -132,7 +132,6 @@
#include <curl/curl.h>
#endif
#include "papertrail_proc.hh"
#include "yajlpp/yajlpp.hh"
#include "readline_callbacks.hh"
#include "command_executor.hh"
@ -1675,7 +1674,7 @@ static void looper()
if (!session_loaded) {
load_session();
if (!lnav_data.ld_session_file_names.empty()) {
if (lnav_data.ld_session_save_time) {
std::string ago;
ago = time_ago(lnav_data.ld_session_save_time);
@ -1685,15 +1684,16 @@ static void looper()
(ANSI_NORM "; press Ctrl-R to reset session"));
}
vector<pair<string, string> > msgs;
vector<pair<Result<string, string>, string>> cmd_results;
execute_init_commands(ec, msgs);
execute_init_commands(ec, cmd_results);
if (!msgs.empty()) {
pair<string, string> last_msg = msgs.back();
if (!cmd_results.empty()) {
auto last_cmd_result = cmd_results.back();
lnav_data.ld_rl_view->set_value(last_msg.first);
lnav_data.ld_rl_view->set_alt_value(last_msg.second);
lnav_data.ld_rl_view->set_value(
last_cmd_result.first.orElse(err_to_ok).unwrap());
lnav_data.ld_rl_view->set_alt_value(last_cmd_result.second);
}
session_loaded = true;
@ -2403,7 +2403,8 @@ int main(int argc, char *argv[])
stdin_reader = make_shared<piper_proc>(
STDIN_FILENO, lnav_data.ld_flags & LNF_TIMESTAMP, stdin_out_fd);
lnav_data.ld_file_names["stdin"]
.with_fd(stdin_out_fd);
.with_fd(stdin_out_fd)
.with_include_in_session(false);
if (dup2(STDOUT_FILENO, STDIN_FILENO) == -1) {
perror("cannot dup stdout to stdin");
}
@ -2457,8 +2458,7 @@ int main(int argc, char *argv[])
}
if (lnav_data.ld_flags & LNF_HEADLESS) {
std::vector<pair<string, string> > msgs;
std::vector<pair<string, string> >::iterator msg_iter;
std::vector<pair<Result<string, string>, string>> cmd_results;
textview_curses *log_tc, *text_tc, *tc;
bool found_error = false;
@ -2483,21 +2483,19 @@ int main(int argc, char *argv[])
}
log_info("Executing initial commands");
execute_init_commands(lnav_data.ld_exec_context, msgs);
execute_init_commands(lnav_data.ld_exec_context, cmd_results);
wait_for_pipers();
lnav_data.ld_curl_looper.process_all();
rebuild_indexes();
for (msg_iter = msgs.begin();
msg_iter != msgs.end();
++msg_iter) {
if (strncmp("error:", msg_iter->first.c_str(), 6) == 0) {
fprintf(stderr, "%s\n", msg_iter->first.c_str());
for (auto &pair : cmd_results) {
if (pair.first.isErr()) {
fprintf(stderr, "%s\n", pair.first.unwrapErr().c_str());
found_error = true;
}
else if (startswith(msg_iter->first, "info:") &&
else if (startswith(pair.first.unwrap(), "info:") &&
lnav_data.ld_flags & LNF_VERBOSE) {
printf("%s\n", msg_iter->first.c_str());
printf("%s\n", pair.first.unwrap().c_str());
}
}
@ -2578,10 +2576,6 @@ int main(int argc, char *argv[])
init_session();
log_info(" session_id=%s", lnav_data.ld_session_id.c_str());
scan_sessions();
guard_termios gt(STDIN_FILENO);
auto_fd::pipe(errpipe);

View File

@ -214,12 +214,10 @@ struct key_repeat_history {
};
struct _lnav_data {
std::string ld_session_id;
std::map<std::string, std::list<session_pair_t>> ld_session_id;
time_t ld_session_time;
time_t ld_session_load_time;
time_t ld_session_save_time;
std::list<session_pair_t> ld_session_file_names;
int ld_session_file_index;
const char * ld_program_name;
const char * ld_debug_log_name;

File diff suppressed because it is too large Load Diff

View File

@ -513,4 +513,17 @@ inline int openp(const filesystem::path &path, int flags, mode_t mode) {
Result<std::pair<filesystem::path, int>, std::string>
open_temp_file(const filesystem::path &pattern);
template<typename A>
struct final_action { // slightly simplified
A act;
final_action(A a) :act{a} {}
~final_action() { act(); }
};
template<typename A>
final_action<A> finally(A act) // deduce action type
{
return final_action<A>{act};
}
#endif

View File

@ -898,11 +898,12 @@ 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);
auto _sg = ec.enter_source(this->elf_name.to_string() +
":" +
vd_iter->first.to_string(),
1);
string field_value = execute_any(ec, vd.vd_rewriter)
.orElse(err_to_ok).unwrap();
struct line_range adj_origin = iter->origin_in_full_msg(
value_out.c_str(), value_out.length());

View File

@ -194,9 +194,11 @@ protected:
};
typedef int (*sql_progress_callback_t)(const log_cursor &lc);
typedef void (*sql_progress_finished_callback_t)();
extern struct _log_vtab_data {
sql_progress_callback_t lvd_progress;
sql_progress_finished_callback_t lvd_finished;
std::string lvd_source;
int lvd_line_number{0};
} log_vtab_data;
@ -204,15 +206,21 @@ extern struct _log_vtab_data {
class sql_progress_guard {
public:
sql_progress_guard(sql_progress_callback_t cb,
sql_progress_finished_callback_t fcb,
const std::string &source,
int line_number) {
log_vtab_data.lvd_progress = cb;
log_vtab_data.lvd_finished = fcb;
log_vtab_data.lvd_source = source;
log_vtab_data.lvd_line_number = line_number;
};
~sql_progress_guard() {
log_vtab_data.lvd_progress = NULL;
if (log_vtab_data.lvd_finished) {
log_vtab_data.lvd_finished();
}
log_vtab_data.lvd_progress = nullptr;
log_vtab_data.lvd_finished = nullptr;
log_vtab_data.lvd_source.clear();
log_vtab_data.lvd_line_number = 0;
};

View File

@ -90,8 +90,15 @@ struct logfile_open_options {
return *this;
};
logfile_open_options &with_include_in_session(bool val) {
this->loo_include_in_session = val;
return *this;
};
auto_fd loo_fd;
bool loo_detect_format;
bool loo_include_in_session{true};
};
struct logfile_activity {

View File

@ -294,7 +294,8 @@ static void rl_search_internal(void *dummy, readline_curses *rc, bool complete =
lnav_data.ld_preview_status_source.get_description().clear();
lnav_data.ld_preview_source.clear();
string result = execute_command(lnav_data.ld_exec_context, rc->get_value());
string result = execute_command(lnav_data.ld_exec_context, rc->get_value())
.orElse(err_to_ok).unwrap();
if (result.empty()) {
lnav_data.ld_bottom_source.set_prompt(LNAV_CMD_PROMPT);
@ -548,7 +549,8 @@ void rl_callback(void *dummy, readline_curses *rc)
case LNM_COMMAND:
rc->set_alt_value("");
rc->set_value(execute_command(ec, rc->get_value()));
rc->set_value(execute_command(ec, rc->get_value())
.orElse(err_to_ok).unwrap());
break;
case LNM_SEARCH:
@ -577,7 +579,8 @@ void rl_callback(void *dummy, readline_curses *rc)
break;
case LNM_SQL: {
string result = execute_sql(ec, rc->get_value(), alt_msg);
string result = execute_sql(ec, rc->get_value(), alt_msg)
.orElse(err_to_ok).unwrap();
db_label_source &dls = lnav_data.ld_db_row_source;
if (!result.empty()) {
@ -608,7 +611,8 @@ void rl_callback(void *dummy, readline_curses *rc)
string path_and_args = rc->get_value();
ec.ec_output_stack.back() = tmpout.in();
string result = execute_file(ec, path_and_args);
string result = execute_file(ec, path_and_args)
.orElse(err_to_ok).unwrap();
string::size_type lf_index = result.find('\n');
if (lf_index != string::npos) {
result = result.substr(0, lf_index);
@ -628,6 +632,7 @@ void rl_callback(void *dummy, readline_curses *rc)
timestamp);
lnav_data.ld_file_names[desc]
.with_fd(fd_copy)
.with_include_in_session(false)
.with_detect_format(false);
lnav_data.ld_files_to_front.emplace_back(desc, 0);

View File

@ -51,6 +51,7 @@
#include <readline/readline.h>
#include <readline/history.h>
#include "base/result.h"
#include "auto_fd.hh"
#include "vt52_curses.hh"
#include "log_format.hh"
@ -69,7 +70,7 @@ extern exec_context INIT_EXEC_CONTEXT;
*/
class readline_context {
public:
typedef std::string (*command_func_t)(exec_context &ec,
typedef Result<std::string, std::string> (*command_func_t)(exec_context &ec,
std::string cmdline, std::vector<std::string> &args);
typedef std::string (*prompt_func_t)(exec_context &ec,
const std::string &cmdline);

View File

@ -0,0 +1,12 @@
#
# @synopsis: rename-stdin <name>
# @description: Give a symbolic name to the standard-input file
#
;SELECT raise_error('expecting the new name for stdin as the first argument') WHERE $1 IS NULL
;SELECT raise_error('no data was redirected to lnav''s standard-input')
WHERE (SELECT count(1) FROM lnav_file WHERE filepath='stdin') = 0
;UPDATE lnav_file SET filepath=$1 WHERE filepath='stdin'
;SELECT 'info: renamed stdin to ' || $1 AS msg

View File

@ -289,57 +289,59 @@ static void cleanup_session_data()
}
void init_session()
{
lnav_data.ld_session_time = time(nullptr);
lnav_data.ld_session_id.clear();
}
static nonstd::optional<std::string> compute_session_id()
{
byte_array<2, uint64> hash;
SpookyHash context;
lnav_data.ld_session_time = time(nullptr);
bool has_files = false;
context.Init(0, 0);
hash_updater updater(&context);
for (auto &ld_file_name : lnav_data.ld_file_names) {
if (!ld_file_name.second.loo_include_in_session) {
continue;
}
has_files = true;
updater(ld_file_name.first);
}
if (!has_files) {
return nonstd::nullopt;
}
context.Final(hash.out(0), hash.out(1));
lnav_data.ld_session_id = hash.to_string();
log_info("init_session: time=%d; id=%s", lnav_data.ld_session_time,
lnav_data.ld_session_id.c_str());
return hash.to_string();
}
void scan_sessions()
nonstd::optional<session_pair_t> scan_sessions()
{
std::list<session_pair_t> &session_file_names =
lnav_data.ld_session_file_names;
static_root_mem<glob_t, globfree> view_info_list;
std::list<session_pair_t>::iterator iter;
char view_info_pattern_base[128];
string old_session_name;
int index;
static_root_mem<glob_t, globfree> view_info_list;
char view_info_pattern_base[128];
cleanup_session_data();
if (lnav_data.ld_session_file_index >= 0 &&
lnav_data.ld_session_file_index < (int)session_file_names.size()) {
iter = session_file_names.begin();
advance(iter, lnav_data.ld_session_file_index);
old_session_name = iter->second;
const auto session_id = compute_session_id();
if (!session_id) {
return nonstd::nullopt;
}
std::list<session_pair_t> &session_file_names =
lnav_data.ld_session_id[session_id.value()];
session_file_names.clear();
snprintf(view_info_pattern_base, sizeof(view_info_pattern_base),
"view-info-%s.*.json",
lnav_data.ld_session_id.c_str());
session_id.value().c_str());
auto view_info_pattern = dotlnav_path() / view_info_pattern_base;
if (glob(view_info_pattern.str().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];
int timestamp, ppid, rc;
int timestamp, ppid, rc;
const char *base;
base = strrchr(path, '/');
@ -374,16 +376,11 @@ void scan_sessions()
session_file_names.pop_front();
}
lnav_data.ld_session_file_index = ((int)session_file_names.size()) - 1;
for (index = 0, iter = session_file_names.begin();
iter != session_file_names.end();
index++, ++iter) {
if (iter->second == old_session_name) {
lnav_data.ld_session_file_index = index;
break;
}
if (session_file_names.empty()) {
return nonstd::nullopt;
}
return nonstd::make_optional(session_file_names.back());
}
static void load_time_bookmarks()
@ -832,40 +829,35 @@ static struct json_path_handler view_info_handlers[] = {
void load_session()
{
std::list<session_pair_t>::iterator sess_iter;
yajl_handle handle;
auto_fd fd;
if (lnav_data.ld_session_file_names.empty()) {
load_time_bookmarks();
return;
}
sess_iter = lnav_data.ld_session_file_names.begin();
advance(sess_iter, lnav_data.ld_session_file_index);
lnav_data.ld_session_load_time = sess_iter->first.second;
lnav_data.ld_session_save_time = sess_iter->first.second;
string &view_info_name = sess_iter->second;
yajlpp_parse_context ypc(view_info_name, view_info_handlers);
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
load_time_bookmarks();
scan_sessions() | [](const auto pair) {
yajl_handle handle;
auto_fd fd;
if ((fd = open(view_info_name.c_str(), O_RDONLY)) < 0) {
perror("cannot open session file");
}
else {
unsigned char buffer[1024];
ssize_t rc;
lnav_data.ld_session_load_time = pair.first.second;
lnav_data.ld_session_save_time = pair.first.second;
const string &view_info_name = pair.second;
log_info("loading session file: %s", view_info_name.c_str());
while ((rc = read(fd, buffer, sizeof(buffer))) > 0) {
yajl_parse(handle, buffer, rc);
yajlpp_parse_context ypc(view_info_name, view_info_handlers);
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
load_time_bookmarks();
if ((fd = open(view_info_name.c_str(), O_RDONLY)) < 0) {
perror("cannot open session file");
}
yajl_complete_parse(handle);
}
yajl_free(handle);
else {
unsigned char buffer[1024];
ssize_t rc;
log_info("loading session file: %s", view_info_name.c_str());
while ((rc = read(fd, buffer, sizeof(buffer))) > 0) {
yajl_parse(handle, buffer, rc);
}
yajl_complete_parse(handle);
}
yajl_free(handle);
};
}
static void yajl_writer(void *context, const char *str, size_t len)
@ -1215,19 +1207,19 @@ static void save_time_bookmarks()
}
}
void save_session()
static void save_session_with_id(const std::string session_id)
{
auto_mem<FILE> file(fclose);
yajl_gen handle = nullptr;
save_time_bookmarks();
yajl_gen handle = nullptr;
/* TODO: save the last search query */
log_info("saving session with id: %s", session_id.c_str());
char view_base_name[256];
snprintf(view_base_name, sizeof(view_base_name),
"view-info-%s.ts%ld.ppid%d.json",
lnav_data.ld_session_id.c_str(),
session_id.c_str(),
lnav_data.ld_session_time,
getppid());
@ -1384,12 +1376,27 @@ void save_session()
}
}
void save_session()
{
save_time_bookmarks();
const auto opt_session_id = compute_session_id();
opt_session_id | [](auto &session_id) {
save_session_with_id(session_id);
};
for (const auto pair : lnav_data.ld_session_id) {
if (opt_session_id && pair.first == opt_session_id.value()) {
continue;
}
save_session_with_id(pair.first);
}
}
void reset_session()
{
log_info("reset session: time=%d", lnav_data.ld_session_time);
save_session();
scan_sessions();
lnav_data.ld_session_time = time(nullptr);

View File

@ -32,10 +32,9 @@
#ifndef _session_data_hh
#define _session_data_hh
void init_session(void);
void load_session(void);
void save_session(void);
void reset_session(void);
void scan_sessions(void);
void init_session();
void load_session();
void save_session();
void reset_session();
#endif

View File

@ -47,6 +47,11 @@ struct FuncDef {
uint8_t needCollSeq;
void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
help_text fd_help;
FuncDef &with_flags(int flags) {
this->eTextRep = flags;
return *this;
};
};
struct FuncDefAgg {

View File

@ -36,6 +36,7 @@
#include <stdint.h>
#include <string>
#include <exception>
#include "sqlite3.h"
@ -56,10 +57,15 @@ static std::string sql_log_top_datetime()
return buffer;
}
static int64_t sql_error(const std::string str)
{
throw sqlite_func_error("{}", str);
}
int state_extension_functions(struct FuncDef **basic_funcs,
struct FuncDefAgg **agg_funcs)
{
static struct FuncDef datetime_funcs[] = {
static struct FuncDef state_funcs[] = {
sqlite_func_adapter<decltype(&sql_log_top_line), sql_log_top_line>::builder(
help_text("log_top_line",
"Return the line number at the top of the log view.")
@ -72,10 +78,17 @@ int state_extension_functions(struct FuncDef **basic_funcs,
.sql_function()
),
sqlite_func_adapter<decltype(&sql_error), sql_error>::builder(
help_text("raise_error",
"Raises an error with the given message when executed")
.sql_function()
.with_parameter({"msg", "The error message"})
).with_flags(SQLITE_UTF8),
{ NULL }
};
*basic_funcs = datetime_funcs;
*basic_funcs = state_funcs;
return SQLITE_OK;
}

View File

@ -58,7 +58,7 @@ static string timeslice(const char *time_in, nonstd::optional<const char *> slic
dts.set_base_time(now);
if (!rt.parse(slice_in, strlen(slice_in), pe)) {
throw sqlite_func_error("unable to parse time slice value");
throw sqlite_func_error("unable to parse time slice value -- {}", slice_in);
}
if (rt.empty()) {
@ -73,7 +73,7 @@ static string timeslice(const char *time_in, nonstd::optional<const char *> slic
struct timeval tv;
if (dts.scan(time_in, strlen(time_in), NULL, &tm, tv) == NULL) {
throw sqlite_func_error("unable to parse time value");
throw sqlite_func_error("unable to parse time value -- {}", time_in);
}
int64_t us = tv.tv_sec * 1000000LL + tv.tv_usec, remainder;

View File

@ -42,6 +42,7 @@
#include "auto_mem.hh"
#include "yajl/api/yajl_gen.h"
#include "mapbox/variant.hpp"
#include "fmt/format.h"
#include "sqlite-extension-func.hh"
@ -56,22 +57,17 @@ struct from_sqlite_conversion_error : std::exception {
};
struct sqlite_func_error : std::exception {
sqlite_func_error(const char *fmt, ...) {
char buffer[1024];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
this->e_what = buffer;
};
template<typename ...Args>
sqlite_func_error(
fmt::string_view format_str, const Args& ...args) :
e_what(fmt::vformat(format_str, fmt::make_format_args(args...))) {
}
const char *what() const noexcept {
return this->e_what.c_str();
}
std::string e_what;
const std::string e_what;
};
template<typename T>
@ -129,8 +125,8 @@ struct from_sqlite<const char *> {
};
template<>
struct from_sqlite<const std::string &> {
inline const std::string operator()(int argc, sqlite3_value **val, int argi) {
struct from_sqlite<std::string> {
inline std::string operator()(int argc, sqlite3_value **val, int argi) {
return std::string((const char *) sqlite3_value_text(val[argi]));
}
};

View File

@ -9,6 +9,7 @@ RM_V_0 = @echo " RM " $@;
AM_CPPFLAGS = \
-Wall \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/fmtlib \
$(READLINE_CFLAGS) \
$(SQLITE3_CFLAGS)

View File

@ -1601,6 +1601,12 @@ Examples
Synopsis
raise_error(msg) -- Raises an error with the given message when executed
Parameter
msg The error message
Synopsis
random() -- Returns a pseudo-random integer between -9223372036854775808 and
+9223372036854775807.

View File

@ -48,7 +48,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "hide-fields with unknown" <<EOF
error: unknown field(s) -- foobar
command-option:1: error: unknown field(s) -- foobar
EOF
run_test ${lnav_test} -n \
@ -83,7 +83,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "config bad option" <<EOF
error: unknown configuration option -- /bad/option
command-option:1: error: unknown configuration option -- /bad/option
EOF
check_output "config bad option" <<EOF
@ -149,7 +149,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "include nonexistent" <<EOF
error:command-option:1:unknown script -- nonexistent.lnav -- file not found
command-option:1: error: unknown script -- nonexistent.lnav -- file not found
EOF
@ -231,7 +231,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "goto invalid is working" <<EOF
error: expecting line number/percentage, timestamp, or relative time
command-option:1: error: expecting line number/percentage, timestamp, or relative time
EOF
check_output "goto invalid is not working" <<EOF
@ -276,7 +276,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "goto invalid is not working" <<EOF
error: unknown bookmark type
command-option:2: error: unknown bookmark type
EOF
check_output "invalid mark-type is working" <<EOF
@ -426,7 +426,7 @@ run_test eval ${lnav_test} -d /tmp/lnav.err -n \
$TOO_MANY_FILTERS \
${test_dir}/logfile_filter.0
check_error_output "able to create too many filters?" <<EOF
error: filter limit reached, try combining filters with a pipe symbol (e.g. foo|bar)
command-option:33: error: filter limit reached, try combining filters with a pipe symbol (e.g. foo|bar)
EOF
@ -444,7 +444,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "double close works" <<EOF
error: no log files loaded
command-option:2: error: no log files loaded
EOF
check_output "double close is working" <<EOF
@ -469,7 +469,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "open non-existent is working" <<EOF
error: cannot stat file: /non-existent -- No such file or directory
command-option:2: error: cannot stat file: /non-existent -- No such file or directory
EOF
check_output "open non-existent is not working" <<EOF
@ -584,7 +584,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "We managed to bypass LNAVSECURE mode" <<EOF
error: write-json-to -- unavailable in secure mode
command-option:2: error: write-json-to -- unavailable in secure mode
EOF
unset LNAVSECURE
@ -783,7 +783,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "clear-highlight did not report an error?" <<EOF
error: highlight does not exist
command-option:1: error: highlight does not exist -- foobar
EOF
run_test ${lnav_test} -n \
@ -878,7 +878,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "bad zoom level is not rejected?" <<EOF
error: invalid zoom level -- bad
command-option:1: error: invalid zoom level -- bad
EOF

View File

@ -122,7 +122,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "updating log_tags is not working?" <<EOF
error:command-option:1:command-option:line 1
command-option:1: error: command-option:line 1
unexpected JSON value
accepted paths --
# <tag> -- A tag for the log line
@ -134,7 +134,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "updating log_tags is not working?" <<EOF
error:command-option:1:Value does not match pattern: ^#[^\s]+$
command-option:1: error: Value does not match pattern: ^#[^\s]+$
EOF
run_test ${lnav_test} -n \

View File

@ -3,6 +3,7 @@
lnav_test="${top_builddir}/src/lnav-test"
export HOME="./sessions"
rm -rf "./sessions"
mkdir -p $HOME
run_test ${lnav_test} -nq \
@ -71,7 +72,7 @@ EOF
rm -rf ./sessions
mkdir -p $HOME
run_test ${lnav_test} -nq \
run_test ${lnav_test} -nq -d /tmp/lnav.err \
-c ":hide-fields c_ip" \
-c ":save-session" \
${test_dir}/logfile_access_log.0

View File

@ -2,6 +2,39 @@
lnav_test="${top_builddir}/src/lnav-test"
run_test ${lnav_test} -n \
-c ";UPDATE lnav_file SET filepath='foo' WHERE endswith(filepath, '_log.0')" \
${test_dir}/logfile_access_log.0
check_error_output "able to change a real file's path?" <<EOF
command-option:1: error: real file paths cannot be updated, only symbolic ones
EOF
run_test ${lnav_test} -n \
-c "|rename-stdin" \
${test_dir}/logfile_access_log.0
check_error_output "rename-stdin works without an argument?" <<EOF
../test/.lnav/formats/default/rename-stdin.lnav:6: error: expecting the new name for stdin as the first argument
EOF
run_test ${lnav_test} -n \
-c "|rename-stdin foo" \
${test_dir}/logfile_access_log.0
check_error_output "rename-stdin when there is no stdin file?" <<EOF
../test/.lnav/formats/default/rename-stdin.lnav:7: error: no data was redirected to lnav's standard-input
EOF
echo 'Hello, World!' | run_test ${lnav_test} -n \
-c "|rename-stdin foo" \
-c ";SELECT filepath FROM lnav_file"
check_output "rename of stdin did not work?" <<EOF
filepath
foo
EOF
run_test ${lnav_test} -n \
-c ";SELECT basename(filepath),format,lines,time_offset FROM lnav_file LIMIT 2" \
-c ":write-csv-to -" \
@ -31,7 +64,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "inserted filter with an empty pattern?" <<EOF
error:command-option:1:Expecting an non-empty pattern for column number 4
command-option:1: error: Expecting an non-empty pattern for column number 4
EOF
run_test ${lnav_test} -n \
@ -39,7 +72,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "inserted filter with an empty pattern?" <<EOF
error:command-option:1:Expecting an lnav view name for column number 0
command-option:1: error: Expecting an lnav view name for column number 0
EOF
run_test ${lnav_test} -n \
@ -47,7 +80,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "inserted filter with an invalid filter type?" <<EOF
error:command-option:1:Expecting an filter type for column number 3
command-option:1: error: Expecting an filter type for column number 3
EOF
run_test ${lnav_test} -n \
@ -216,7 +249,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "spectrogram worked without log_time?" <<EOF
error: no 'log_time' column found or not in ascending order, unable to create spectrogram
command-option:2: error: no 'log_time' column found or not in ascending order, unable to create spectrogram
EOF
run_test ${lnav_test} -n \
@ -225,7 +258,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "spectrogram worked with bad column?" <<EOF
error: unknown column -- sc_byes
command-option:2: error: unknown column -- sc_byes
EOF
run_test ${lnav_test} -n \
@ -234,7 +267,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "spectrogram worked with non-numeric column?" <<EOF
error: column is not numeric -- c_ip
command-option:2: error: column is not numeric -- c_ip
EOF
run_test ${lnav_test} -n \
@ -243,7 +276,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "spectrogram worked with unordered log_time?" <<EOF
error: no 'log_time' column found or not in ascending order, unable to create spectrogram
command-option:2: error: no 'log_time' column found or not in ascending order, unable to create spectrogram
EOF
cp ${srcdir}/logfile_syslog_with_mixed_times.0 logfile_syslog_with_mixed_times.0
@ -485,7 +518,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "insert into environ table works" <<EOF
error:command-option:1:A non-empty name and value must be provided when inserting an environment variable
command-option:1: error: A non-empty name and value must be provided when inserting an environment variable
EOF
check_output "insert into environ table works" <<EOF
@ -497,7 +530,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "insert into environ table works" <<EOF
error:command-option:1:A non-empty name and value must be provided when inserting an environment variable
command-option:1: error: A non-empty name and value must be provided when inserting an environment variable
EOF
check_output "insert into environ table works" <<EOF
@ -509,7 +542,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "insert into environ table works" <<EOF
error:command-option:1:A non-empty name and value must be provided when inserting an environment variable
command-option:1: error: A non-empty name and value must be provided when inserting an environment variable
EOF
check_output "insert into environ table works" <<EOF
@ -521,7 +554,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "insert into environ table works" <<EOF
error:command-option:1:Environment variable names cannot contain an equals sign (=)
command-option:1: error: Environment variable names cannot contain an equals sign (=)
EOF
check_output "insert into environ table works" <<EOF
@ -533,7 +566,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "insert into environ table works" <<EOF
error:command-option:1:An environment variable with the name 'SQL_ENV_VALUE' already exists
command-option:1: error: An environment variable with the name 'SQL_ENV_VALUE' already exists
EOF
check_output "insert into environ table works" <<EOF
@ -656,7 +689,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:command-option:1:Invalid time: bad-time
command-option:1: error: Invalid time: bad-time
EOF
@ -721,7 +754,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "errors are not reported" <<EOF
error:command-option:1:no such table: nonexistent_table
command-option:1: error: no such table: nonexistent_table
EOF
check_output "errors are not reported" <<EOF
@ -733,7 +766,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_access_log.0
check_error_output "errors are not reported" <<EOF
error:command-option:1:attempt to write a readonly database
command-option:1: error: attempt to write a readonly database
EOF
check_output "errors are not reported" <<EOF
@ -945,7 +978,7 @@ run_test ${lnav_test} -n \
empty
check_error_output "LNAVSECURE mode bypassed" <<EOF
error:command-option:1:not authorized
command-option:1: error: not authorized
EOF
run_test ${lnav_test} -n \
@ -953,7 +986,7 @@ run_test ${lnav_test} -n \
empty
check_error_output "LNAVSECURE mode bypassed (':' adorned)" <<EOF
error:command-option:1:not authorized
command-option:1: error: not authorized
EOF
run_test ${lnav_test} -n \
@ -961,7 +994,7 @@ run_test ${lnav_test} -n \
empty
check_error_output "LNAVSECURE mode bypassed (filepath)" <<EOF
error:command-option:1:not authorized
command-option:1: error: not authorized
EOF
run_test ${lnav_test} -n \
@ -969,7 +1002,7 @@ run_test ${lnav_test} -n \
empty
check_error_output "LNAVSECURE mode bypassed (URI)" <<EOF
error:command-option:1:not authorized
command-option:1: error: not authorized
EOF
unset LNAVSECURE
@ -1065,7 +1098,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_multiline.0
check_error_output "able to delete unknown table?" <<EOF
error: unknown search table -- search_test1
command-option:1: error: unknown search table -- search_test1
EOF
run_test ${lnav_test} -n \
@ -1074,7 +1107,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_multiline.0
check_error_output "able to delete logline table?" <<EOF
error: unknown search table -- search_test1
command-option:2: error: unknown search table -- search_test1
EOF
run_test ${lnav_test} -n \
@ -1082,7 +1115,7 @@ run_test ${lnav_test} -n \
${test_dir}/logfile_multiline.0
check_error_output "able to create table with a bad regex?" <<EOF
error: missing )
command-option:1: error: missing )
EOF
NULL_GRAPH_SELECT_1=$(cat <<EOF