mirror of
https://github.com/tstack/lnav.git
synced 2024-09-11 13:05:51 +03:00
[exec] add a prompt to execute lnav scripts
This commit is contained in:
parent
7386ea3864
commit
e07b4e448f
6
NEWS
6
NEWS
@ -14,6 +14,12 @@ lnav v0.8.1:
|
||||
* Added ":hide-lines-before", ":hide-lines-after", and
|
||||
":show-lines-before-and-after" commands so that you can filter out
|
||||
log lines based on time.
|
||||
* Scripts containing lnav commands/queries can now be executed using
|
||||
the pipe ('|') hotkey. See the documentation for more information.
|
||||
* Added an ":eval" command that can be used to execute a command or
|
||||
query after performing environment variable substitution.
|
||||
* Added an ":echo" command that can be useful for scripts to message
|
||||
the user.
|
||||
|
||||
Interface Changes:
|
||||
* The 'o/O' hotkeys have been reassigned to navigate through log
|
||||
|
@ -100,3 +100,15 @@ Output
|
||||
shell command and open the output in lnav.
|
||||
* pipe-line-to <shell-cmd> - Pipe the top line in the current view to a shell
|
||||
command and open the output in lnav.
|
||||
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
|
||||
* echo [-n] <msg> - Display the given message in the command prompt. Useful
|
||||
for scripts to display messages to the user. The '-n' option leaves out the
|
||||
new line at the end of the message.
|
||||
* eval <cmd> - Evaluate the given command or SQL query after performing
|
||||
environment variable substitution. The argument to *eval* must start with a
|
||||
colon, semi-colon, or pipe character to signify whether the argument is a
|
||||
command, SQL query, or a script to be executed, respectively.
|
||||
|
@ -117,6 +117,7 @@ Spatial Navigation
|
||||
- Next/previous bookmark
|
||||
* - |ks| o |ke|
|
||||
- |ks| Shift |ke| + |ks| o |ke|
|
||||
-
|
||||
- Forward/backward through log messages with a matching "opid" field
|
||||
* - |ks| y |ke|
|
||||
- |ks| Shift |ke| + |ks| y |ke|
|
||||
@ -223,5 +224,7 @@ Query
|
||||
- Execute an SQL query
|
||||
* - |ks| : |ke|
|
||||
- Execute an internal command, see :ref:`commands` for more information
|
||||
* - |ks| \| |ke|
|
||||
- Execute an lnav script located in a format directory.
|
||||
* - |ks| Ctrl |ke| + |ks| ] |ke|
|
||||
- Abort a
|
||||
|
@ -42,6 +42,7 @@ set(diag_STAT_SRCS
|
||||
session_data.cc
|
||||
sequence_matcher.cc
|
||||
shared_buffer.cc
|
||||
shlex.cc
|
||||
sqlite-extension-func.c
|
||||
statusview_curses.cc
|
||||
string-extension-functions.cc
|
||||
@ -111,6 +112,7 @@ set(diag_STAT_SRCS
|
||||
readline_possibilities.hh
|
||||
relative_time.hh
|
||||
sequence_sink.hh
|
||||
shlex.hh
|
||||
status_controllers.hh
|
||||
strong_int.hh
|
||||
sysclip.hh
|
||||
|
@ -160,6 +160,7 @@ noinst_HEADERS = \
|
||||
sequence_sink.hh \
|
||||
session_data.hh \
|
||||
shared_buffer.hh \
|
||||
shlex.hh \
|
||||
sql_util.hh \
|
||||
sqlite-extension-func.h \
|
||||
status_controllers.hh \
|
||||
@ -244,6 +245,7 @@ libdiag_a_SOURCES = \
|
||||
session_data.cc \
|
||||
sequence_matcher.cc \
|
||||
shared_buffer.cc \
|
||||
shlex.cc \
|
||||
sqlite-extension-func.c \
|
||||
statusview_curses.cc \
|
||||
string-extension-functions.cc \
|
||||
|
@ -29,11 +29,14 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <wordexp.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "json_ptr.hh"
|
||||
#include "pcrecpp.h"
|
||||
#include "lnav.hh"
|
||||
#include "log_format_loader.hh"
|
||||
|
||||
#include "command_executor.hh"
|
||||
|
||||
@ -71,7 +74,7 @@ static int sql_progress(const struct log_cursor &lc)
|
||||
|
||||
string execute_from_file(const string &path, int line_number, char mode, const string &cmdline);
|
||||
|
||||
string execute_command(string cmdline)
|
||||
string execute_command(const string &cmdline)
|
||||
{
|
||||
stringstream ss(cmdline);
|
||||
|
||||
@ -99,7 +102,7 @@ string execute_command(string cmdline)
|
||||
return msg;
|
||||
}
|
||||
|
||||
string execute_sql(string sql, string &alt_msg)
|
||||
string execute_sql(const string &sql, string &alt_msg)
|
||||
{
|
||||
db_label_source &dls = lnav_data.ld_db_row_source;
|
||||
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
|
||||
@ -150,9 +153,16 @@ string execute_sql(string sql, string &alt_msg)
|
||||
|
||||
name = sqlite3_bind_parameter_name(stmt.in(), lpc + 1);
|
||||
if (name[0] == '$') {
|
||||
map<string, string> &vars = lnav_data.ld_local_vars.top();
|
||||
map<string, string>::iterator local_var;
|
||||
const char *env_value;
|
||||
|
||||
if ((env_value = getenv(&name[1])) != NULL) {
|
||||
if ((local_var = vars.find(&name[1])) != vars.end()) {
|
||||
sqlite3_bind_text(stmt.in(), lpc + 1,
|
||||
local_var->second.c_str(), -1,
|
||||
SQLITE_TRANSIENT);
|
||||
}
|
||||
else if ((env_value = getenv(&name[1])) != NULL) {
|
||||
sqlite3_bind_text(stmt.in(), lpc + 1, env_value, -1, SQLITE_STATIC);
|
||||
}
|
||||
}
|
||||
@ -246,15 +256,16 @@ string execute_sql(string sql, string &alt_msg)
|
||||
return retval;
|
||||
}
|
||||
|
||||
void execute_file(string path, bool multiline)
|
||||
static string execute_file_contents(const string &path, bool multiline)
|
||||
{
|
||||
string retval;
|
||||
FILE *file;
|
||||
|
||||
if (path == "-") {
|
||||
file = stdin;
|
||||
}
|
||||
else if ((file = fopen(path.c_str(), "r")) == NULL) {
|
||||
return;
|
||||
return "error: unable to open file";
|
||||
}
|
||||
|
||||
int line_number = 0, starting_line_number = 0;
|
||||
@ -263,7 +274,9 @@ void execute_file(string path, bool multiline)
|
||||
ssize_t line_size;
|
||||
string cmdline;
|
||||
char mode = '\0';
|
||||
pair<string, string> dir_and_base = split_path(path);
|
||||
|
||||
lnav_data.ld_path_stack.push(dir_and_base.first);
|
||||
while ((line_size = getline(&line, &line_max_size, file)) != -1) {
|
||||
line_number += 1;
|
||||
|
||||
@ -280,7 +293,7 @@ void execute_file(string path, bool multiline)
|
||||
case ';':
|
||||
case '|':
|
||||
if (mode) {
|
||||
execute_from_file(path, starting_line_number, mode, trim(cmdline));
|
||||
retval = execute_from_file(path, starting_line_number, mode, trim(cmdline));
|
||||
}
|
||||
|
||||
starting_line_number = line_number;
|
||||
@ -292,7 +305,7 @@ void execute_file(string path, bool multiline)
|
||||
cmdline += line;
|
||||
}
|
||||
else {
|
||||
execute_from_file(path, line_number, ':', line);
|
||||
retval = execute_from_file(path, line_number, ':', line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -300,12 +313,84 @@ void execute_file(string path, bool multiline)
|
||||
}
|
||||
|
||||
if (mode) {
|
||||
execute_from_file(path, starting_line_number, mode, trim(cmdline));
|
||||
retval = execute_from_file(path, starting_line_number, mode, trim(cmdline));
|
||||
}
|
||||
|
||||
if (file != stdin) {
|
||||
fclose(file);
|
||||
}
|
||||
lnav_data.ld_path_stack.pop();
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
string execute_file(const string &path_and_args, bool multiline)
|
||||
{
|
||||
map<string, vector<string> > scripts;
|
||||
map<string, vector<string> >::iterator iter;
|
||||
static_root_mem<wordexp_t, wordfree> wordmem;
|
||||
string msg, retval;
|
||||
|
||||
log_info("Executing file: %s", path_and_args.c_str());
|
||||
|
||||
int exp_rc = wordexp(path_and_args.c_str(),
|
||||
wordmem.inout(),
|
||||
WRDE_NOCMD | WRDE_UNDEF);
|
||||
|
||||
if (!wordexperr(exp_rc, msg)) {
|
||||
retval = msg;
|
||||
}
|
||||
else if (wordmem->we_wordc == 0) {
|
||||
retval = "error: no script specified";
|
||||
}
|
||||
else {
|
||||
lnav_data.ld_local_vars.push(map<string, string>());
|
||||
|
||||
string script_name = wordmem->we_wordv[0];
|
||||
map<string, string> &vars = lnav_data.ld_local_vars.top();
|
||||
char env_arg_name[32];
|
||||
string result;
|
||||
|
||||
snprintf(env_arg_name, sizeof(env_arg_name), "%d", (int) wordmem->we_wordc - 1);
|
||||
|
||||
vars["#"] = env_arg_name;
|
||||
for (int lpc = 0; lpc < wordmem->we_wordc; lpc++) {
|
||||
snprintf(env_arg_name, sizeof(env_arg_name), "%d", lpc);
|
||||
vars[env_arg_name] = wordmem->we_wordv[lpc];
|
||||
}
|
||||
|
||||
vector<string> paths_to_exec;
|
||||
|
||||
find_format_scripts(lnav_data.ld_config_paths, scripts);
|
||||
if ((iter = scripts.find(script_name)) != scripts.end()) {
|
||||
paths_to_exec = iter->second;
|
||||
}
|
||||
if (access(script_name.c_str(), R_OK) == 0) {
|
||||
paths_to_exec.push_back(script_name);
|
||||
}
|
||||
else {
|
||||
string local_path = lnav_data.ld_path_stack.top() + "/" + script_name;
|
||||
|
||||
if (access(local_path.c_str(), R_OK) == 0) {
|
||||
paths_to_exec.push_back(local_path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!paths_to_exec.empty()) {
|
||||
for (vector<string>::iterator path_iter = paths_to_exec.begin();
|
||||
path_iter != paths_to_exec.end();
|
||||
++path_iter) {
|
||||
result = execute_file_contents(*path_iter, multiline);
|
||||
}
|
||||
retval = "Executed: " + script_name + " -- " + result;
|
||||
}
|
||||
else {
|
||||
retval = "error: unknown script -- " + script_name;
|
||||
}
|
||||
lnav_data.ld_local_vars.pop();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
string execute_from_file(const string &path, int line_number, char mode, const string &cmdline)
|
||||
@ -322,7 +407,7 @@ string execute_from_file(const string &path, int line_number, char mode, const s
|
||||
retval = execute_sql(cmdline, alt_msg);
|
||||
break;
|
||||
case '|':
|
||||
execute_file(cmdline);
|
||||
retval = execute_file(cmdline);
|
||||
break;
|
||||
default:
|
||||
retval = execute_command(cmdline);
|
||||
@ -362,7 +447,7 @@ void execute_init_commands(vector<pair<string, string> > &msgs)
|
||||
msg = execute_sql(iter->substr(1), alt_msg);
|
||||
break;
|
||||
case '|':
|
||||
execute_file(iter->substr(1));
|
||||
msg = execute_file(iter->substr(1));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -34,10 +34,10 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
std::string execute_command(std::string cmdline);
|
||||
std::string execute_command(const std::string &cmdline);
|
||||
|
||||
std::string execute_sql(std::string sql, std::string &alt_msg);
|
||||
void execute_file(std::string path, bool multiline = true);
|
||||
std::string execute_sql(const std::string &sql, std::string &alt_msg);
|
||||
std::string execute_file(const std::string &path_and_args, bool multiline = true);
|
||||
void execute_init_commands(std::vector<std::pair<std::string, std::string> > &msgs);
|
||||
|
||||
int sql_callback(sqlite3_stmt *stmt);
|
||||
|
@ -115,12 +115,9 @@ static void sql_dirname(sqlite3_context *context,
|
||||
text_end -= 1;
|
||||
}
|
||||
|
||||
if (text_end == -1) {
|
||||
sqlite3_result_text(context,
|
||||
path_in[0] == '/' ? "/" : ".", 1,
|
||||
SQLITE_STATIC);
|
||||
return;
|
||||
}
|
||||
sqlite3_result_text(context,
|
||||
path_in[0] == '/' ? "/" : ".", 1,
|
||||
SQLITE_STATIC);
|
||||
}
|
||||
|
||||
static void sql_joinpath(sqlite3_context *context,
|
||||
|
22
src/help.txt
22
src/help.txt
@ -316,7 +316,16 @@ through the file.
|
||||
that can be used in queries. See the SQL section
|
||||
below for more information.
|
||||
|
||||
CTRL+] Abort command-line entry started with '/', ':', or ';'.
|
||||
|<script> [arg1 .. argN]
|
||||
Execute an lnav script contained in a format directory
|
||||
(e.g. ~/.lnav/formats/default). The script can contain
|
||||
lines starting with ':', ';', or '|' to execute commands,
|
||||
SQL queries or execute other files in lnav. Any values
|
||||
after the script name are treated as arguments can be
|
||||
referenced in the script using '$1', '$2', and so on, like
|
||||
in a shell script.
|
||||
|
||||
CTRL+] Abort command-line entry started with '/', ':', ';', or '|'.
|
||||
|
||||
y/Y Move forward/backward through the log view based on the
|
||||
"log_line" column in the SQL result view.
|
||||
@ -569,6 +578,17 @@ COMMANDS
|
||||
clear-partition
|
||||
Clear the partition the top line is a part of.
|
||||
|
||||
echo [-n] <msg> Display the given message. Useful for scripts to message
|
||||
the user. The '-n' option leaves out the new line at the
|
||||
end of the message.
|
||||
|
||||
eval <cmd|query|file>
|
||||
Execute the given command, query, or file after doing
|
||||
environment variable substitution. The argument to this
|
||||
command should start with a ':', ';', or '|' signify the
|
||||
type of action to perform (command, SQL query, execute
|
||||
script).
|
||||
|
||||
pt-min-time [<date>|<relative-time>]
|
||||
Set/get the minimum time range for any papertrail queries.
|
||||
Absolute or relative time values can be specified.
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "readline_possibilities.hh"
|
||||
#include "field_overlay_source.hh"
|
||||
#include "hotkeys.hh"
|
||||
#include "log_format_loader.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -940,6 +941,25 @@ void handle_paging_key(int ch)
|
||||
}
|
||||
break;
|
||||
|
||||
case '|': {
|
||||
map<string, vector<string> > scripts;
|
||||
|
||||
lnav_data.ld_mode = LNM_EXEC;
|
||||
|
||||
lnav_data.ld_rl_view->clear_possibilities(LNM_EXEC, "__command");
|
||||
find_format_scripts(lnav_data.ld_config_paths, scripts);
|
||||
for (map<string, vector<string> >::iterator iter = scripts.begin();
|
||||
iter != scripts.end();
|
||||
++iter) {
|
||||
lnav_data.ld_rl_view->add_possibility(LNM_EXEC, "__command", iter->first);
|
||||
}
|
||||
lnav_data.ld_rl_view->focus(LNM_EXEC, "|");
|
||||
lnav_data.ld_bottom_source.set_prompt(
|
||||
"Enter a script to execute: (Press "
|
||||
ANSI_BOLD("CTRL+]") " to abort)");
|
||||
break;
|
||||
}
|
||||
|
||||
case 'p':
|
||||
if (tc == &lnav_data.ld_views[LNV_LOG]) {
|
||||
field_overlay_source *fos;
|
||||
|
@ -1576,6 +1576,7 @@ static void handle_key(int ch)
|
||||
case LNM_SEARCH:
|
||||
case LNM_CAPTURE:
|
||||
case LNM_SQL:
|
||||
case LNM_EXEC:
|
||||
handle_rl_key(ch);
|
||||
break;
|
||||
|
||||
@ -1635,6 +1636,7 @@ static void looper(void)
|
||||
readline_context search_context("search");
|
||||
readline_context index_context("capture");
|
||||
readline_context sql_context("sql", NULL, false);
|
||||
readline_context exec_context("exec");
|
||||
readline_curses rlc;
|
||||
int lpc;
|
||||
|
||||
@ -1653,6 +1655,7 @@ static void looper(void)
|
||||
rlc.add_context(LNM_SEARCH, search_context);
|
||||
rlc.add_context(LNM_CAPTURE, index_context);
|
||||
rlc.add_context(LNM_SQL, sql_context);
|
||||
rlc.add_context(LNM_EXEC, exec_context);
|
||||
rlc.start();
|
||||
|
||||
lnav_data.ld_rl_view = &rlc;
|
||||
@ -2174,6 +2177,8 @@ int main(int argc, char *argv[])
|
||||
umask(077);
|
||||
|
||||
lnav_data.ld_program_name = argv[0];
|
||||
lnav_data.ld_local_vars.push(map<string, string>());
|
||||
lnav_data.ld_path_stack.push(".");
|
||||
|
||||
rl_readline_name = "lnav";
|
||||
|
||||
|
@ -70,6 +70,7 @@ typedef enum {
|
||||
LNM_SEARCH,
|
||||
LNM_CAPTURE,
|
||||
LNM_SQL,
|
||||
LNM_EXEC,
|
||||
} ln_mode_t;
|
||||
|
||||
enum {
|
||||
@ -251,6 +252,9 @@ struct _lnav_data {
|
||||
curl_looper ld_curl_looper;
|
||||
|
||||
relative_time ld_last_relative_time;
|
||||
|
||||
std::stack<std::map<std::string, std::string> > ld_local_vars;
|
||||
std::stack<std::string> ld_path_stack;
|
||||
};
|
||||
|
||||
extern struct _lnav_data lnav_data;
|
||||
|
@ -51,39 +51,10 @@
|
||||
#include "readline_curses.hh"
|
||||
#include "relative_time.hh"
|
||||
#include "log_search_table.hh"
|
||||
#include "shlex.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
static bool wordexperr(int rc, string &msg)
|
||||
{
|
||||
switch (rc) {
|
||||
case WRDE_BADCHAR:
|
||||
msg = "error: invalid filename character";
|
||||
return false;
|
||||
|
||||
case WRDE_CMDSUB:
|
||||
msg = "error: command substitution is not allowed";
|
||||
return false;
|
||||
|
||||
case WRDE_BADVAL:
|
||||
msg = "error: unknown environment variable in file name";
|
||||
return false;
|
||||
|
||||
case WRDE_NOSPACE:
|
||||
msg = "error: out of memory";
|
||||
return false;
|
||||
|
||||
case WRDE_SYNTAX:
|
||||
msg = "error: invalid syntax";
|
||||
return false;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static string remaining_args(const string &cmdline,
|
||||
const vector<string> &args,
|
||||
size_t index = 1)
|
||||
@ -2137,6 +2108,76 @@ static string com_redraw(string cmdline, vector<string> &args)
|
||||
return "";
|
||||
}
|
||||
|
||||
static string com_echo(string cmdline, vector<string> &args)
|
||||
{
|
||||
string retval = "error: expecting a message";
|
||||
|
||||
if (args.empty()) {
|
||||
|
||||
}
|
||||
else if (args.size() > 1) {
|
||||
bool lf = true;
|
||||
|
||||
if (args.size() > 2 && args[1] == "-n") {
|
||||
lf = false;
|
||||
}
|
||||
retval = remaining_args(cmdline, args, lf ? 1 : 2);
|
||||
if (lnav_data.ld_flags & LNF_HEADLESS) {
|
||||
printf("%s", retval.c_str());
|
||||
if (lf) {
|
||||
putc('\n', stdout);
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static string com_eval(string cmdline, vector<string> &args)
|
||||
{
|
||||
string retval = "error: expecting a command or query to evaluate";
|
||||
|
||||
if (args.empty()) {
|
||||
|
||||
}
|
||||
else if (args.size() > 1) {
|
||||
string all_args = remaining_args(cmdline, args);
|
||||
string expanded_cmd;
|
||||
shlex lexer(all_args.c_str(), all_args.size());
|
||||
|
||||
log_debug("Evaluating: %s", all_args.c_str());
|
||||
if (!lexer.eval(expanded_cmd, lnav_data.ld_local_vars.top())) {
|
||||
return "error: invalid arguments";
|
||||
}
|
||||
log_debug("Expanded command to evaluate: %s", expanded_cmd.c_str());
|
||||
|
||||
if (expanded_cmd.empty()) {
|
||||
return "error: empty result after evaluation";
|
||||
}
|
||||
|
||||
string alt_msg;
|
||||
switch (expanded_cmd[0]) {
|
||||
case ':':
|
||||
retval = execute_command(expanded_cmd.substr(1));
|
||||
break;
|
||||
case ';':
|
||||
retval = execute_sql(expanded_cmd.substr(1), alt_msg);
|
||||
break;
|
||||
case '|':
|
||||
retval = "info: executed file -- " + expanded_cmd.substr(1) +
|
||||
" -- " + execute_file(expanded_cmd.substr(1));
|
||||
break;
|
||||
default:
|
||||
retval = "error: expecting argument to start with ':', ';', "
|
||||
"or '|' to signify a command, SQL query, or script to execute";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
readline_context::command_t STD_COMMANDS[] = {
|
||||
{
|
||||
"adjust-log-time",
|
||||
@ -2413,6 +2454,19 @@ readline_context::command_t STD_COMMANDS[] = {
|
||||
"Zoom the histogram view to the given level",
|
||||
com_zoom_to,
|
||||
},
|
||||
{
|
||||
"echo",
|
||||
"[-n] <msg>",
|
||||
"Echo the given message",
|
||||
com_echo,
|
||||
},
|
||||
{
|
||||
"eval",
|
||||
"<msg>",
|
||||
"Evaluate the given command/query after doing environment variable substitution",
|
||||
com_eval,
|
||||
},
|
||||
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <wordexp.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
@ -198,6 +199,27 @@ bool change_to_parent_dir(void)
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> split_path(const char *path, ssize_t len)
|
||||
{
|
||||
ssize_t dir_len = len;
|
||||
|
||||
while (dir_len >= 0 && (path[dir_len] == '/' || path[dir_len] == '\\')) {
|
||||
dir_len -= 1;
|
||||
}
|
||||
|
||||
while (dir_len >= 0) {
|
||||
if (path[dir_len] == '/' || path[dir_len] == '\\') {
|
||||
return make_pair(string(path, dir_len),
|
||||
string(&path[dir_len + 1], len - dir_len));
|
||||
}
|
||||
|
||||
dir_len -= 1;
|
||||
}
|
||||
|
||||
return make_pair(path[0] == '/' ? "/" : ".",
|
||||
path[0] == '/' ? string(&path[1], len - 1) : string(path, len));
|
||||
}
|
||||
|
||||
file_format_t detect_file_format(const std::string &filename)
|
||||
{
|
||||
file_format_t retval = FF_UNKNOWN;
|
||||
@ -587,3 +609,33 @@ bool read_file(const char *filename, string &out)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool wordexperr(int rc, string &msg)
|
||||
{
|
||||
switch (rc) {
|
||||
case WRDE_BADCHAR:
|
||||
msg = "error: invalid filename character";
|
||||
return false;
|
||||
|
||||
case WRDE_CMDSUB:
|
||||
msg = "error: command substitution is not allowed";
|
||||
return false;
|
||||
|
||||
case WRDE_BADVAL:
|
||||
msg = "error: unknown environment variable in file name";
|
||||
return false;
|
||||
|
||||
case WRDE_NOSPACE:
|
||||
msg = "error: out of memory";
|
||||
return false;
|
||||
|
||||
case WRDE_SYNTAX:
|
||||
msg = "error: invalid syntax";
|
||||
return false;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -169,6 +169,13 @@ std::string get_current_dir(void);
|
||||
|
||||
bool change_to_parent_dir(void);
|
||||
|
||||
std::pair<std::string, std::string> split_path(const char *path, ssize_t len);
|
||||
|
||||
inline
|
||||
std::pair<std::string, std::string> split_path(const std::string &path) {
|
||||
return split_path(path.c_str(), path.size());
|
||||
};
|
||||
|
||||
enum file_format_t {
|
||||
FF_UNKNOWN,
|
||||
FF_SQLITE_DB,
|
||||
@ -321,4 +328,6 @@ inline bool pollfd_ready(const std::vector<struct pollfd> &pollfds, int fd, shor
|
||||
return false;
|
||||
};
|
||||
|
||||
bool wordexperr(int rc, std::string &msg);
|
||||
|
||||
#endif
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <glob.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
@ -732,3 +733,33 @@ void load_format_extra(sqlite3 *db,
|
||||
exec_sql_in_path(db, *path_iter, errors);
|
||||
}
|
||||
}
|
||||
|
||||
static void find_format_in_path(const string &path,
|
||||
std::map<std::string, std::vector<std::string> > &scripts)
|
||||
{
|
||||
string format_path = path + "/formats/*/*.lnav";
|
||||
static_root_mem<glob_t, globfree> gl;
|
||||
|
||||
if (glob(format_path.c_str(), 0, NULL, gl.inout()) == 0) {
|
||||
for (int lpc = 0; lpc < (int)gl->gl_pathc; lpc++) {
|
||||
const char *filename = basename(gl->gl_pathv[lpc]);
|
||||
string script_name = string(filename, strlen(filename) - 5);
|
||||
|
||||
scripts[script_name].push_back(gl->gl_pathv[lpc]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void find_format_scripts(const std::vector<std::string> &extra_paths,
|
||||
std::map<std::string, std::vector<std::string> > &scripts)
|
||||
{
|
||||
find_format_in_path("/etc/lnav", scripts);
|
||||
find_format_in_path(SYSCONFDIR "/lnav", scripts);
|
||||
find_format_in_path(dotlnav_path(""), scripts);
|
||||
|
||||
for (vector<string>::const_iterator path_iter = extra_paths.begin();
|
||||
path_iter != extra_paths.end();
|
||||
++path_iter) {
|
||||
find_format_in_path(*path_iter, scripts);
|
||||
}
|
||||
}
|
||||
|
@ -47,4 +47,7 @@ void load_format_extra(sqlite3 *db,
|
||||
const std::vector<std::string> &extra_paths,
|
||||
std::vector<std::string> &errors);
|
||||
|
||||
void find_format_scripts(const std::vector<std::string> &extra_paths,
|
||||
std::map<std::string, std::vector<std::string> > &scripts);
|
||||
|
||||
#endif
|
||||
|
@ -29,12 +29,16 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <wordexp.h>
|
||||
|
||||
#include "lnav.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "sysclip.hh"
|
||||
#include "plain_text_source.hh"
|
||||
#include "command_executor.hh"
|
||||
#include "readline_curses.hh"
|
||||
#include "log_search_table.hh"
|
||||
#include "log_format_loader.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -129,6 +133,9 @@ static void rl_search_internal(void *dummy, readline_curses *rc, bool complete =
|
||||
}
|
||||
return;
|
||||
|
||||
case LNM_EXEC:
|
||||
return;
|
||||
|
||||
default:
|
||||
require(0);
|
||||
break;
|
||||
@ -181,7 +188,6 @@ void rl_callback(void *dummy, readline_curses *rc)
|
||||
break;
|
||||
|
||||
case LNM_COMMAND:
|
||||
lnav_data.ld_mode = LNM_PAGING;
|
||||
rc->set_alt_value("");
|
||||
rc->set_value(execute_command(rc->get_value()));
|
||||
break;
|
||||
@ -202,15 +208,19 @@ void rl_callback(void *dummy, readline_curses *rc)
|
||||
n, N,
|
||||
"to move forward/backward through search results"));
|
||||
}
|
||||
lnav_data.ld_mode = LNM_PAGING;
|
||||
break;
|
||||
|
||||
case LNM_SQL:
|
||||
rc->set_value(execute_sql(rc->get_value(), alt_msg));
|
||||
rc->set_alt_value(alt_msg);
|
||||
lnav_data.ld_mode = LNM_PAGING;
|
||||
break;
|
||||
|
||||
case LNM_EXEC:
|
||||
rc->set_value(execute_file(rc->get_value()));
|
||||
break;
|
||||
}
|
||||
|
||||
lnav_data.ld_mode = LNM_PAGING;
|
||||
}
|
||||
|
||||
void rl_display_matches(void *dummy, readline_curses *rc)
|
||||
|
@ -322,6 +322,17 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void add_possibility(int context,
|
||||
const std::string &type,
|
||||
const std::vector<std::string> &values)
|
||||
{
|
||||
for (std::vector<std::string>::const_iterator iter = values.begin();
|
||||
iter != values.end();
|
||||
++iter) {
|
||||
this->add_possibility(context, type, *iter);
|
||||
}
|
||||
};
|
||||
|
||||
void rem_possibility(int context,
|
||||
const std::string &type,
|
||||
const std::string &value);
|
||||
|
34
src/shlex.cc
Normal file
34
src/shlex.cc
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2015, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @file shlex.cc
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "shlex.hh"
|
191
src/shlex.hh
Normal file
191
src/shlex.hh
Normal file
@ -0,0 +1,191 @@
|
||||
/**
|
||||
* Copyright (c) 2015, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @file shlex.hh
|
||||
*/
|
||||
|
||||
#ifndef LNAV_SHLEX_HH_H
|
||||
#define LNAV_SHLEX_HH_H
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "pcrepp.hh"
|
||||
|
||||
enum shlex_token_t {
|
||||
ST_ERROR,
|
||||
ST_ESCAPE,
|
||||
ST_DOUBLE_QUOTE_START,
|
||||
ST_DOUBLE_QUOTE_END,
|
||||
ST_SINGLE_QUOTE_START,
|
||||
ST_SINGLE_QUOTE_END,
|
||||
ST_VARIABLE_REF,
|
||||
};
|
||||
|
||||
class shlex {
|
||||
public:
|
||||
shlex(const char *str, size_t len)
|
||||
: s_str(str), s_len(len), s_index(0), s_state(STATE_NORMAL) {
|
||||
|
||||
};
|
||||
|
||||
bool tokenize(pcre_context::capture_t &cap_out, shlex_token_t &token_out) {
|
||||
while (this->s_index < this->s_len) {
|
||||
switch (this->s_str[this->s_index]) {
|
||||
case '\\':
|
||||
cap_out.c_begin = this->s_index;
|
||||
if (this->s_index + 1 < this->s_len) {
|
||||
token_out = ST_ESCAPE;
|
||||
this->s_index += 2;
|
||||
cap_out.c_end = this->s_index;
|
||||
}
|
||||
else {
|
||||
this->s_index += 1;
|
||||
cap_out.c_end = this->s_index;
|
||||
token_out = ST_ERROR;
|
||||
}
|
||||
return true;
|
||||
case '\"':
|
||||
cap_out.c_begin = this->s_index;
|
||||
this->s_index += 1;
|
||||
cap_out.c_end = this->s_index;
|
||||
switch (this->s_state) {
|
||||
case STATE_NORMAL:
|
||||
token_out = ST_DOUBLE_QUOTE_START;
|
||||
this->s_state = STATE_IN_DOUBLE_QUOTE;
|
||||
return true;
|
||||
case STATE_IN_DOUBLE_QUOTE:
|
||||
token_out = ST_DOUBLE_QUOTE_END;
|
||||
this->s_state = STATE_NORMAL;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '\'':
|
||||
cap_out.c_begin = this->s_index;
|
||||
this->s_index += 1;
|
||||
cap_out.c_end = this->s_index;
|
||||
switch (this->s_state) {
|
||||
case STATE_NORMAL:
|
||||
token_out = ST_SINGLE_QUOTE_START;
|
||||
this->s_state = STATE_IN_SINGLE_QUOTE;
|
||||
return true;
|
||||
case STATE_IN_SINGLE_QUOTE:
|
||||
token_out = ST_SINGLE_QUOTE_END;
|
||||
this->s_state = STATE_NORMAL;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '$':
|
||||
switch (this->s_state) {
|
||||
case STATE_NORMAL:
|
||||
case STATE_IN_DOUBLE_QUOTE:
|
||||
cap_out.c_begin = this->s_index;
|
||||
this->s_index += 1;
|
||||
while (this->s_index < this->s_len &&
|
||||
(isalnum(this->s_str[this->s_index]) ||
|
||||
this->s_str[this->s_index] == '#' ||
|
||||
this->s_str[this->s_index] == '_')) {
|
||||
this->s_index += 1;
|
||||
}
|
||||
cap_out.c_end = this->s_index;
|
||||
token_out = ST_VARIABLE_REF;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this->s_index += 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
bool eval(std::string &result, const std::map<std::string, std::string> &vars) {
|
||||
result.clear();
|
||||
|
||||
pcre_context::capture_t cap;
|
||||
shlex_token_t token;
|
||||
int last_index = 0;
|
||||
|
||||
while (this->tokenize(cap, token)) {
|
||||
result.append(&this->s_str[last_index], cap.c_begin - last_index);
|
||||
switch (token) {
|
||||
case ST_ERROR:
|
||||
return false;
|
||||
case ST_ESCAPE:
|
||||
result.append(1, this->s_str[cap.c_begin + 1]);
|
||||
break;
|
||||
case ST_VARIABLE_REF: {
|
||||
std::string var_name(&this->s_str[cap.c_begin + 1], cap.length() - 1);
|
||||
std::map<std::string, std::string>::const_iterator local_var;
|
||||
const char *var_value = getenv(var_name.c_str());
|
||||
|
||||
if ((local_var = vars.find(var_name)) != vars.end()) {
|
||||
result.append(local_var->second);
|
||||
}
|
||||
else if (var_value != NULL) {
|
||||
result.append(var_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
last_index = cap.c_end;
|
||||
}
|
||||
|
||||
result.append(&this->s_str[last_index], this->s_len - last_index);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
void reset() {
|
||||
this->s_index = 0;
|
||||
this->s_state = STATE_NORMAL;
|
||||
};
|
||||
|
||||
enum state_t {
|
||||
STATE_NORMAL,
|
||||
STATE_IN_DOUBLE_QUOTE,
|
||||
STATE_IN_SINGLE_QUOTE,
|
||||
};
|
||||
|
||||
const char *s_str;
|
||||
size_t s_len;
|
||||
size_t s_index;
|
||||
state_t s_state;
|
||||
};
|
||||
|
||||
#endif //LNAV_SHLEX_HH_H
|
@ -25,6 +25,7 @@ check_PROGRAMS = \
|
||||
drive_logfile \
|
||||
drive_mvwattrline \
|
||||
drive_sequencer \
|
||||
drive_shlexer \
|
||||
drive_sql \
|
||||
drive_view_colors \
|
||||
drive_vt52_curses \
|
||||
@ -131,6 +132,9 @@ drive_logfile_LDADD = \
|
||||
drive_sequencer_SOURCES = drive_sequencer.cc
|
||||
drive_sequencer_LDADD = ../src/libdiag.a $(CURSES_LIB) $(SQLITE3_LIBS)
|
||||
|
||||
drive_shlexer_SOURCES = drive_shlexer.cc
|
||||
drive_shlexer_LDADD = ../src/libdiag.a
|
||||
|
||||
drive_data_scanner_SOURCES = \
|
||||
drive_data_scanner.cc
|
||||
drive_data_scanner_LDADD = \
|
||||
@ -188,6 +192,7 @@ dist_noinst_SCRIPTS = \
|
||||
test_logfile.sh \
|
||||
test_mvwattrline.sh \
|
||||
test_sessions.sh \
|
||||
test_shlexer.sh \
|
||||
test_sql.sh \
|
||||
test_sql_coll_func.sh \
|
||||
test_sql_json_func.sh \
|
||||
@ -303,6 +308,7 @@ TESTS = \
|
||||
test_pcrepp \
|
||||
test_reltime \
|
||||
test_sessions.sh \
|
||||
test_shlexer.sh \
|
||||
test_sql.sh \
|
||||
test_sql_coll_func.sh \
|
||||
test_sql_json_func.sh \
|
||||
|
88
test/drive_shlexer.cc
Normal file
88
test/drive_shlexer.cc
Normal file
@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) 2015, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "shlex.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
const char *ST_TOKEN_NAMES[] = {
|
||||
"err",
|
||||
"esc",
|
||||
"dst",
|
||||
"den",
|
||||
"sst",
|
||||
"sen",
|
||||
"ref",
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "error: expecting an argument to parse\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
shlex lexer(argv[1], strlen(argv[1]));
|
||||
pcre_context::capture_t cap;
|
||||
shlex_token_t token;
|
||||
|
||||
printf(" %s\n", argv[1]);
|
||||
while (lexer.tokenize(cap, token)) {
|
||||
int lpc;
|
||||
|
||||
printf("%s ", ST_TOKEN_NAMES[token]);
|
||||
for (lpc = 0; lpc < cap.c_end; lpc++) {
|
||||
if (lpc == cap.c_begin) {
|
||||
fputc('^', stdout);
|
||||
}
|
||||
else if (lpc == (cap.c_end - 1)) {
|
||||
fputc('^', stdout);
|
||||
}
|
||||
else if (lpc > cap.c_begin) {
|
||||
fputc('-', stdout);
|
||||
}
|
||||
else{
|
||||
fputc(' ', stdout);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
lexer.reset();
|
||||
std::string result;
|
||||
if (lexer.eval(result, map<string, string>())) {
|
||||
printf("eval -- %s\n", result.c_str());
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
2
test/nested.lnav
Normal file
2
test/nested.lnav
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
:eval :echo nested here $0 $1 $2
|
@ -1,5 +1,30 @@
|
||||
#! /bin/bash
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c "|${test_dir}/toplevel.lnav 123 456" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_error_output "include toplevel.lnav" <<EOF
|
||||
EOF
|
||||
|
||||
check_output "include toplevel.lnav" <<EOF
|
||||
toplevel here 123 456
|
||||
nested here nested.lnav abc
|
||||
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 \
|
||||
-f "nonexistent.lnav" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_error_output "include nonexistent" <<EOF
|
||||
invalid command file: No such file or directory
|
||||
EOF
|
||||
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ":adjust-log-time 2010-01-01T00:00:00" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
@ -653,3 +678,42 @@ check_output "hide-lines-after does not work?" <<EOF
|
||||
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
|
||||
|
||||
export XYZ="World"
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ':echo Hello, $XYZ!' \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_output "echo hello" <<EOF
|
||||
Hello, \$XYZ!
|
||||
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
|
||||
|
||||
export XYZ="World"
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
-c ':echo -n Hello, ' \
|
||||
-c ':echo World!' \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_output "echo hello" <<EOF
|
||||
Hello, World!
|
||||
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 ':eval :echo Hello, $XYZ!' \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
||||
check_output "eval echo hello" <<EOF
|
||||
Hello, World!
|
||||
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
|
||||
|
65
test/test_shlexer.sh
Normal file
65
test/test_shlexer.sh
Normal file
@ -0,0 +1,65 @@
|
||||
#! /bin/bash
|
||||
|
||||
export FOO='bar'
|
||||
export DEF='xyz'
|
||||
|
||||
run_test drive_shlexer '$FOO'
|
||||
|
||||
check_output "var ref" <<EOF
|
||||
\$FOO
|
||||
ref ^--^
|
||||
eval -- bar
|
||||
EOF
|
||||
|
||||
run_test drive_shlexer '\a'
|
||||
|
||||
check_output "escape" <<EOF
|
||||
\a
|
||||
esc ^^
|
||||
eval -- a
|
||||
EOF
|
||||
|
||||
run_test drive_shlexer "'abc'"
|
||||
|
||||
check_output "single" <<EOF
|
||||
'abc'
|
||||
sst ^
|
||||
sen ^
|
||||
eval -- abc
|
||||
EOF
|
||||
|
||||
run_test drive_shlexer '"def"'
|
||||
|
||||
check_output "double" <<EOF
|
||||
"def"
|
||||
dst ^
|
||||
den ^
|
||||
eval -- def
|
||||
EOF
|
||||
|
||||
run_test drive_shlexer '"abc $DEF 123"'
|
||||
|
||||
check_output "double w/ref" <<EOF
|
||||
"abc \$DEF 123"
|
||||
dst ^
|
||||
ref ^--^
|
||||
den ^
|
||||
eval -- abc xyz 123
|
||||
EOF
|
||||
|
||||
run_test drive_shlexer "'abc \$DEF 123'"
|
||||
|
||||
check_output "single w/ref" <<EOF
|
||||
'abc \$DEF 123'
|
||||
sst ^
|
||||
sen ^
|
||||
eval -- abc \$DEF 123
|
||||
EOF
|
||||
|
||||
run_test drive_shlexer 'abc $DEF 123'
|
||||
|
||||
check_output "unquoted" <<EOF
|
||||
abc \$DEF 123
|
||||
ref ^--^
|
||||
eval -- abc xyz 123
|
||||
EOF
|
4
test/toplevel.lnav
Normal file
4
test/toplevel.lnav
Normal file
@ -0,0 +1,4 @@
|
||||
|
||||
:eval :echo toplevel here $1 $2
|
||||
|
||||
|nested.lnav abc
|
Loading…
Reference in New Issue
Block a user