[pretty-print] allow formats to do transforms before pretty-printing

Also started upgrading to C++11.

Fixes #353
pull/398/head
Timothy Stack 8 years ago
parent 02bfd5846b
commit 44d93dddc3

@ -1,5 +1,6 @@
cmake_minimum_required (VERSION 2.6)
SET(CMAKE_CXX_STANDARD 11)
project (lnav)
add_subdirectory(src)
add_subdirectory(test)

@ -8,6 +8,10 @@ lnav v0.8.2:
give you more control over how the displayed line looks.
* Added a "hidden" option to log format values so that you can hide JSON
log fields from being displayed if they are not in the line format.
* Added a "rewriter" field to log format value definitions that is a
command used to rewrite the field in the pretty-printed version of a
log message. For example, the HTTP access log format will rewrite the
status code field to include the textual version (e.g. 200 (OK)).
Interface Changes:
* The color used for text colored via ":highlight" is now based on the

@ -8,6 +8,8 @@ AC_PREFIX_DEFAULT(/usr)
AC_CANONICAL_HOST
AX_CXX_COMPILE_STDCXX_11([noext], [mandatory])
for defdir in /opt/local /usr/local /usr /; do
if test -d "$defdir/include"; then
CPPFLAGS="$CPPFLAGS -I$defdir/include"

@ -158,6 +158,16 @@ fields:
not be graphed. This should only need to be set for integer fields.
:hidden: A boolean for JSON log fields that indicates whether they should
be displayed if they are not present in the line-format.
:rewriter: A command to rewrite this field when pretty-printing log
messages containing this value. The command must start with ':', ';',
or '|' to signify whether it is a regular command, SQL query, or a script
to be executed. The other fields in the line are accessible in SQL by
using the ':' prefix. The text value of this field will then be replaced
with the result of the command when pretty-printing. For example, the
HTTP access log format will rewrite the status code field to include the
textual version (e.g. 200 (OK)) using the following SQL query::
;SELECT :sc_status || ' (' || (SELECT message FROM http_status_codes WHERE status = :sc_status) || ') '
:sample: A list of objects that contain sample log messages. All formats
must include at least one sample and it must be matched by one of the

@ -0,0 +1,562 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the specified
# version of the C++ standard. If necessary, add switches to CXX and
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
# or '14' (for the C++14 standard).
#
# The second argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
# -std=c++11). If neither is specified, you get whatever works, with
# preference for an extended mode.
#
# The third argument, if specified 'mandatory' or if left unspecified,
# indicates that baseline support for the specified C++ standard is
# required and that the macro should error out if no mode with that
# support is found. If specified 'optional', then configuration proceeds
# regardless, after defining HAVE_CXX${VERSION} if and only if a
# supporting mode is found.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 4
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
dnl (serial version number 13).
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
m4_if([$1], [11], [],
[$1], [14], [],
[$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$2], [], [],
[$2], [ext], [],
[$2], [noext], [],
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
AC_LANG_PUSH([C++])dnl
ac_success=no
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
ax_cv_cxx_compile_cxx$1,
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[ax_cv_cxx_compile_cxx$1=yes],
[ax_cv_cxx_compile_cxx$1=no])])
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
ac_success=yes
fi
m4_if([$2], [noext], [], [dnl
if test x$ac_success = xno; then
for switch in -std=gnu++$1 -std=gnu++0x; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
fi])
m4_if([$2], [ext], [], [dnl
if test x$ac_success = xno; then
dnl HP's aCC needs +std=c++11 according to:
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
dnl Cray's crayCC needs "-h std=c++11"
for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
fi])
AC_LANG_POP([C++])
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
if test x$ac_success = xno; then
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
fi
fi
if test x$ac_success = xno; then
HAVE_CXX$1=0
AC_MSG_NOTICE([No compiler with C++$1 support was found])
else
HAVE_CXX$1=1
AC_DEFINE(HAVE_CXX$1,1,
[define if the compiler supports basic C++$1 syntax])
fi
AC_SUBST(HAVE_CXX$1)
])
dnl Test body for checking C++11 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
)
dnl Test body for checking C++14 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
)
dnl Tests for new features in C++11
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
// If the compiler admits that it is not ready for C++11, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
#else
namespace cxx11
{
namespace test_static_assert
{
template <typename T>
struct check
{
static_assert(sizeof(int) <= sizeof(T), "not big enough");
};
}
namespace test_final_override
{
struct Base
{
virtual void f() {}
};
struct Derived : public Base
{
virtual void f() override {}
};
}
namespace test_double_right_angle_brackets
{
template < typename T >
struct check {};
typedef check<void> single_type;
typedef check<check<void>> double_type;
typedef check<check<check<void>>> triple_type;
typedef check<check<check<check<void>>>> quadruple_type;
}
namespace test_decltype
{
int
f()
{
int a = 1;
decltype(a) b = 2;
return a + b;
}
}
namespace test_type_deduction
{
template < typename T1, typename T2 >
struct is_same
{
static const bool value = false;
};
template < typename T >
struct is_same<T, T>
{
static const bool value = true;
};
template < typename T1, typename T2 >
auto
add(T1 a1, T2 a2) -> decltype(a1 + a2)
{
return a1 + a2;
}
int
test(const int c, volatile int v)
{
static_assert(is_same<int, decltype(0)>::value == true, "");
static_assert(is_same<int, decltype(c)>::value == false, "");
static_assert(is_same<int, decltype(v)>::value == false, "");
auto ac = c;
auto av = v;
auto sumi = ac + av + 'x';
auto sumf = ac + av + 1.0;
static_assert(is_same<int, decltype(ac)>::value == true, "");
static_assert(is_same<int, decltype(av)>::value == true, "");
static_assert(is_same<int, decltype(sumi)>::value == true, "");
static_assert(is_same<int, decltype(sumf)>::value == false, "");
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
return (sumf > 0.0) ? sumi : add(c, v);
}
}
namespace test_noexcept
{
int f() { return 0; }
int g() noexcept { return 0; }
static_assert(noexcept(f()) == false, "");
static_assert(noexcept(g()) == true, "");
}
namespace test_constexpr
{
template < typename CharT >
unsigned long constexpr
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
{
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
}
template < typename CharT >
unsigned long constexpr
strlen_c(const CharT *const s) noexcept
{
return strlen_c_r(s, 0UL);
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("1") == 1UL, "");
static_assert(strlen_c("example") == 7UL, "");
static_assert(strlen_c("another\0example") == 7UL, "");
}
namespace test_rvalue_references
{
template < int N >
struct answer
{
static constexpr int value = N;
};
answer<1> f(int&) { return answer<1>(); }
answer<2> f(const int&) { return answer<2>(); }
answer<3> f(int&&) { return answer<3>(); }
void
test()
{
int i = 0;
const int c = 0;
static_assert(decltype(f(i))::value == 1, "");
static_assert(decltype(f(c))::value == 2, "");
static_assert(decltype(f(0))::value == 3, "");
}
}
namespace test_uniform_initialization
{
struct test
{
static const int zero {};
static const int one {1};
};
static_assert(test::zero == 0, "");
static_assert(test::one == 1, "");
}
namespace test_lambdas
{
void
test1()
{
auto lambda1 = [](){};
auto lambda2 = lambda1;
lambda1();
lambda2();
}
int
test2()
{
auto a = [](int i, int j){ return i + j; }(1, 2);
auto b = []() -> int { return '0'; }();
auto c = [=](){ return a + b; }();
auto d = [&](){ return c; }();
auto e = [a, &b](int x) mutable {
const auto identity = [](int y){ return y; };
for (auto i = 0; i < a; ++i)
a += b--;
return x + identity(a + b);
}(0);
return a + b + c + d + e;
}
int
test3()
{
const auto nullary = [](){ return 0; };
const auto unary = [](int x){ return x; };
using nullary_t = decltype(nullary);
using unary_t = decltype(unary);
const auto higher1st = [](nullary_t f){ return f(); };
const auto higher2nd = [unary](nullary_t f1){
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
};
return higher1st(nullary) + higher2nd(nullary)(unary);
}
}
namespace test_variadic_templates
{
template <int...>
struct sum;
template <int N0, int... N1toN>
struct sum<N0, N1toN...>
{
static constexpr auto value = N0 + sum<N1toN...>::value;
};
template <>
struct sum<>
{
static constexpr auto value = 0;
};
static_assert(sum<>::value == 0, "");
static_assert(sum<1>::value == 1, "");
static_assert(sum<23>::value == 23, "");
static_assert(sum<1, 2>::value == 3, "");
static_assert(sum<5, 5, 11>::value == 21, "");
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
}
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
// because of this.
namespace test_template_alias_sfinae
{
struct foo {};
template<typename T>
using member = typename T::member_type;
template<typename T>
void func(...) {}
template<typename T>
void func(member<T>*) {}
void test();
void test() { func<foo>(0); }
}
} // namespace cxx11
#endif // __cplusplus >= 201103L
]])
dnl Tests for new features in C++14
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
// If the compiler admits that it is not ready for C++14, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201402L
#error "This is not a C++14 compiler"
#else
namespace cxx14
{
namespace test_polymorphic_lambdas
{
int
test()
{
const auto lambda = [](auto&&... args){
const auto istiny = [](auto x){
return (sizeof(x) == 1UL) ? 1 : 0;
};
const int aretiny[] = { istiny(args)... };
return aretiny[0];
};
return lambda(1, 1L, 1.0f, '1');
}
}
namespace test_binary_literals
{
constexpr auto ivii = 0b0000000000101010;
static_assert(ivii == 42, "wrong value");
}
namespace test_generalized_constexpr
{
template < typename CharT >
constexpr unsigned long
strlen_c(const CharT *const s) noexcept
{
auto length = 0UL;
for (auto p = s; *p; ++p)
++length;
return length;
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("x") == 1UL, "");
static_assert(strlen_c("test") == 4UL, "");
static_assert(strlen_c("another\0test") == 7UL, "");
}
namespace test_lambda_init_capture
{
int
test()
{
auto x = 0;
const auto lambda1 = [a = x](int b){ return a + b; };
const auto lambda2 = [a = lambda1(x)](){ return a; };
return lambda2();
}
}
namespace test_digit_seperators
{
constexpr auto ten_million = 100'000'000;
static_assert(ten_million == 100000000, "");
}
namespace test_return_type_deduction
{
auto f(int& x) { return x; }
decltype(auto) g(int& x) { return x; }
template < typename T1, typename T2 >
struct is_same
{
static constexpr auto value = false;
};
template < typename T >
struct is_same<T, T>
{
static constexpr auto value = true;
};
int
test()
{
auto x = 0;
static_assert(is_same<int, decltype(f(x))>::value, "");
static_assert(is_same<int&, decltype(g(x))>::value, "");
return x;
}
}
} // namespace cxx14
#endif // __cplusplus >= 201402L
]])

@ -0,0 +1,39 @@
# ============================================================================
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
# ============================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX_11([ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the C++11
# standard; if necessary, add switches to CXX and CXXCPP to enable
# support.
#
# This macro is a convenience alias for calling the AX_CXX_COMPILE_STDCXX
# macro with the version set to C++11. The two optional arguments are
# forwarded literally as the second and third argument respectively.
# Please see the documentation for the AX_CXX_COMPILE_STDCXX macro for
# more information. If you want to use this macro, you also need to
# download the ax_cxx_compile_stdcxx.m4 file.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 17
AX_REQUIRE_DEFINED([AX_CXX_COMPILE_STDCXX])
AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [AX_CXX_COMPILE_STDCXX([11], [$1], [$2])])

@ -44,6 +44,8 @@
using namespace std;
exec_context INIT_EXEC_CONTEXT;
static const string MSG_FORMAT_STMT =
"SELECT count(*) as total, min(log_line) as log_line, log_msg_format "
"FROM all_logs GROUP BY log_msg_format ORDER BY total desc";
@ -74,9 +76,9 @@ static int sql_progress(const struct log_cursor &lc)
return 0;
}
string execute_from_file(const string &path, int line_number, char mode, const string &cmdline);
string execute_from_file(exec_context &ec, const string &path, int line_number, char mode, const string &cmdline);
string execute_command(const string &cmdline)
string execute_command(exec_context &ec, const string &cmdline)
{
vector<string> args;
string msg;
@ -93,14 +95,14 @@ string execute_command(const string &cmdline)
msg = "error: unknown command - " + args[0];
}
else {
msg = iter->second.c_func(cmdline, args);
msg = iter->second.c_func(ec, cmdline, args);
}
}
return msg;
}
string execute_sql(const string &sql, string &alt_msg)
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);
@ -147,11 +149,20 @@ string execute_sql(const string &sql, string &alt_msg)
param_count = sqlite3_bind_parameter_count(stmt.in());
for (int lpc = 0; lpc < param_count; lpc++) {
map<string, string>::iterator ov_iter;
const char *name;
name = sqlite3_bind_parameter_name(stmt.in(), lpc + 1);
if (name[0] == '$') {
map<string, string> &vars = lnav_data.ld_local_vars.top();
ov_iter = ec.ec_override.find(name);
if (ov_iter != ec.ec_override.end()) {
sqlite3_bind_text(stmt.in(),
lpc,
ov_iter->second.c_str(),
ov_iter->second.length(),
SQLITE_TRANSIENT);
}
else if (name[0] == '$') {
map<string, string> &vars = ec.ec_local_vars.top();
map<string, string>::iterator local_var;
const char *env_value;
@ -164,6 +175,40 @@ string execute_sql(const string &sql, string &alt_msg)
sqlite3_bind_text(stmt.in(), lpc + 1, env_value, -1, SQLITE_STATIC);
}
}
else if (name[0] == ':' && ec.ec_line_values != NULL) {
vector<logline_value> &lvalues = *ec.ec_line_values;
vector<logline_value>::iterator iter;
for (iter = lvalues.begin(); iter != lvalues.end(); ++iter) {
if (strcmp(&name[1], iter->lv_name.get()) != 0) {
continue;
}
switch (iter->lv_kind) {
case logline_value::VALUE_BOOLEAN:
sqlite3_bind_int64(stmt.in(), lpc + 1, iter->lv_value.i);
break;
case logline_value::VALUE_FLOAT:
sqlite3_bind_double(stmt.in(), lpc + 1, iter->lv_value.d);
break;
case logline_value::VALUE_INTEGER:
sqlite3_bind_int64(stmt.in(), lpc + 1, iter->lv_value.i);
break;
case logline_value::VALUE_NULL:
sqlite3_bind_null(stmt.in(), lpc + 1);
break;
default:
sqlite3_bind_text(stmt.in(),
lpc + 1,
iter->text_value(),
iter->text_length(),
SQLITE_TRANSIENT);
break;
}
}
}
else {
log_warning("Could not bind variable: %s", name);
}
}
if (lnav_data.ld_rl_view != NULL) {
@ -182,7 +227,7 @@ string execute_sql(const string &sql, string &alt_msg)
break;
case SQLITE_ROW:
sql_callback(stmt.in());
ec.ec_sql_callback(ec, stmt.in());
break;
default: {
@ -203,29 +248,38 @@ string execute_sql(const string &sql, string &alt_msg)
lnav_data.ld_views[LNV_DB].reload_data();
lnav_data.ld_views[LNV_DB].set_left(0);
if (dls.dls_rows.size() > 0) {
vis_bookmarks &bm =
lnav_data.ld_views[LNV_LOG].get_bookmarks();
if (!ec.ec_accumulator.empty()) {
retval = ec.ec_accumulator;
ec.ec_accumulator.clear();
}
else if (dls.dls_rows.size() > 0) {
vis_bookmarks &bm = lnav_data.ld_views[LNV_LOG].get_bookmarks();
if (lnav_data.ld_flags & LNF_HEADLESS) {
if (ec.ec_local_vars.size() == 1) {
ensure_view(&lnav_data.ld_views[LNV_DB]);
}
if (!(lnav_data.ld_flags & LNF_HEADLESS) &&
dls.dls_headers.size() == 1 && !bm[&BM_QUERY].empty()) {
retval = "";
alt_msg = "";
}
else if (dls.dls_headers.size() == 1 && !bm[&BM_QUERY].empty()) {
retval = "";
alt_msg = HELP_MSG_2(
y, Y,
"to move forward/backward through query results "
"in the log view");
}
else if (!(lnav_data.ld_flags & LNF_HEADLESS) &&
dls.dls_rows.size() == 1) {
else if (dls.dls_rows.size() == 1) {
string row;
dls.text_value_for_line(lnav_data.ld_views[LNV_DB], 0, row, true);
retval = "SQL Result: " + row;
retval = row;
}
else {
char row_count[32];
if (lnav_data.ld_local_vars.size() == 1) {
if (ec.ec_local_vars.size() == 1) {
ensure_view(&lnav_data.ld_views[LNV_DB]);
}
snprintf(row_count, sizeof(row_count),
@ -256,7 +310,7 @@ string execute_sql(const string &sql, string &alt_msg)
return retval;
}
static string execute_file_contents(const string &path, bool multiline)
static string execute_file_contents(exec_context &ec, const string &path, bool multiline)
{
string retval;
FILE *file;
@ -276,7 +330,7 @@ static string execute_file_contents(const string &path, bool multiline)
char mode = '\0';
pair<string, string> dir_and_base = split_path(path);
lnav_data.ld_path_stack.push(dir_and_base.first);
ec.ec_path_stack.push(dir_and_base.first);
while ((line_size = getline(&line, &line_max_size, file)) != -1) {
line_number += 1;
@ -293,7 +347,7 @@ static string execute_file_contents(const string &path, bool multiline)
case ';':
case '|':
if (mode) {
retval = execute_from_file(path, starting_line_number, mode, trim(cmdline));
retval = execute_from_file(ec, path, starting_line_number, mode, trim(cmdline));
}
starting_line_number = line_number;
@ -305,7 +359,7 @@ static string execute_file_contents(const string &path, bool multiline)
cmdline += line;
}
else {
retval = execute_from_file(path, line_number, ':', line);
retval = execute_from_file(ec, path, line_number, ':', line);
}
break;
}
@ -313,18 +367,18 @@ static string execute_file_contents(const string &path, bool multiline)
}
if (mode) {
retval = execute_from_file(path, starting_line_number, mode, trim(cmdline));
retval = execute_from_file(ec, path, starting_line_number, mode, trim(cmdline));
}
if (file != stdin) {
fclose(file);
}
lnav_data.ld_path_stack.pop();
ec.ec_path_stack.pop();
return retval;
}
string execute_file(const string &path_and_args, bool multiline)
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;
@ -334,17 +388,17 @@ string execute_file(const string &path_and_args, bool multiline)
log_info("Executing file: %s", path_and_args.c_str());
if (!lexer.split(split_args, lnav_data.ld_local_vars.top())) {
if (!lexer.split(split_args, ec.ec_local_vars.top())) {
retval = "error: unable to parse path";
}
else if (split_args.empty()) {
retval = "error: no script specified";
}
else {
lnav_data.ld_local_vars.push(map<string, string>());
ec.ec_local_vars.push(map<string, string>());
string script_name = split_args[0];
map<string, string> &vars = lnav_data.ld_local_vars.top();
map<string, string> &vars = ec.ec_local_vars.top();
char env_arg_name[32];
string result, open_error = "file not found";
@ -375,7 +429,7 @@ string execute_file(const string &path_and_args, bool multiline)
open_error = strerror(errno);
}
else {
string local_path = lnav_data.ld_path_stack.top() + "/" + script_name;
string local_path = ec.ec_path_stack.top() + "/" + script_name;
if (access(local_path.c_str(), R_OK) == 0) {
struct script_metadata meta;
@ -393,37 +447,37 @@ string execute_file(const string &path_and_args, bool multiline)
for (vector<script_metadata>::iterator path_iter = paths_to_exec.begin();
path_iter != paths_to_exec.end();
++path_iter) {
result = execute_file_contents(path_iter->sm_path, multiline);
result = execute_file_contents(ec, path_iter->sm_path, multiline);
}
retval = "Executed: " + script_name + " -- " + result;
retval = result;
}
else {
retval = "error: unknown script -- " + script_name + " -- " + open_error;
}
lnav_data.ld_local_vars.pop();
ec.ec_local_vars.pop();
}
return retval;
}
string execute_from_file(const string &path, int line_number, char mode, const string &cmdline)
string execute_from_file(exec_context &ec, const string &path, int line_number, char mode, const string &cmdline)
{
string retval, alt_msg;
switch (mode) {
case ':':
retval = execute_command(cmdline);
retval = execute_command(ec, cmdline);
break;
case '/':
case ';':
setup_logline_table();
retval = execute_sql(cmdline, alt_msg);
retval = execute_sql(ec, cmdline, alt_msg);
break;
case '|':
retval = execute_file(cmdline);
retval = execute_file(ec, cmdline);
break;
default:
retval = execute_command(cmdline);
retval = execute_command(ec, cmdline);
break;
}
@ -439,28 +493,55 @@ string execute_from_file(const string &path, int line_number, char mode, const s
return retval;
}
void execute_init_commands(vector<pair<string, string> > &msgs)
string execute_any(exec_context &ec, const string &cmdline_with_mode)
{
string retval, alt_msg, cmdline = cmdline_with_mode.substr(1);
switch (cmdline_with_mode[0]) {
case ':':
retval = execute_command(ec, cmdline);
break;
case '/':
case ';':
setup_logline_table();
retval = execute_sql(ec, cmdline, alt_msg);
break;
case '|': {
retval = execute_file(ec, cmdline);
break;
}
default:
retval = execute_command(ec, cmdline);
break;
}
if (rescan_files()) {
rebuild_indexes(true);
}
return retval;
}
void execute_init_commands(exec_context &ec, vector<pair<string, string> > &msgs)
{
if (lnav_data.ld_cmd_init_done) {
return;
}
for (list<std::string>::iterator iter = lnav_data.ld_commands.begin();
iter != lnav_data.ld_commands.end();
++iter) {
for (auto &cmd : lnav_data.ld_commands) {
string msg, alt_msg;
switch (iter->at(0)) {
switch (cmd.at(0)) {
case ':':
msg = execute_command(iter->substr(1));
msg = execute_command(ec, cmd.substr(1));
break;
case '/':
case ';':
setup_logline_table();
msg = execute_sql(iter->substr(1), alt_msg);
msg = execute_sql(ec, cmd.substr(1), alt_msg);
break;
case '|':
msg = execute_file(iter->substr(1));
msg = execute_file(ec, cmd.substr(1));
break;
}
@ -487,7 +568,7 @@ void execute_init_commands(vector<pair<string, string> > &msgs)
lnav_data.ld_cmd_init_done = true;
}
int sql_callback(sqlite3_stmt *stmt)
int sql_callback(exec_context &ec, sqlite3_stmt *stmt)
{
logfile_sub_source &lss = lnav_data.ld_log_source;
db_label_source &dls = lnav_data.ld_db_row_source;
@ -534,3 +615,30 @@ int sql_callback(sqlite3_stmt *stmt)
return retval;
}
future<string> pipe_callback(exec_context &ec, const string &cmdline, auto_fd &fd)
{
piper_proc *pp = new piper_proc(fd, false);
static int exec_count = 0;
char desc[128];
lnav_data.ld_pipers.push_back(pp);
snprintf(desc,
sizeof(desc), "[%d] Output of %s",
exec_count++,
cmdline.c_str());
lnav_data.ld_file_names[desc]
.with_fd(pp->get_fd())
.with_detect_format(false);
lnav_data.ld_files_to_front.push_back(make_pair(desc, 0));
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
X, "to close the file"));
}
packaged_task<string()> task([]() { return ""; });
task();
return task.get_future();
}

@ -32,14 +32,49 @@
#include <sqlite3.h>
#include <future>
#include <string>
std::string execute_command(const std::string &cmdline);
struct exec_context;
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);
typedef int (*sql_callback_t)(exec_context &ec, sqlite3_stmt *stmt);
int sql_callback(sqlite3_stmt *stmt);
typedef std::future<std::string> (*pipe_callback_t)(
exec_context &ec, const std::string &cmdline, auto_fd &fd);
struct exec_context {
exec_context(std::vector<logline_value> *line_values = NULL,
sql_callback_t sql_callback = NULL,
pipe_callback_t pipe_callback = NULL)
: ec_line_values(line_values),
ec_sql_callback(sql_callback),
ec_pipe_callback(pipe_callback) {
this->ec_local_vars.push(std::map<std::string, std::string>());
this->ec_path_stack.push(".");
}
vis_line_t ec_top_line;
std::map<std::string, std::string> ec_override;
std::vector<logline_value> *ec_line_values;
std::stack<std::map<std::string, std::string> > ec_local_vars;
std::stack<std::string> ec_path_stack;
std::string ec_accumulator;
sql_callback_t ec_sql_callback;
pipe_callback_t ec_pipe_callback;
};
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);
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);
#endif //LNAV_COMMAND_EXECUTOR_H

@ -51,7 +51,8 @@
},
"sc_status" : {
"kind" : "integer",
"foreign-key" : true
"foreign-key" : true,
"rewriter" : ";SELECT :sc_status || ' (' || (SELECT message FROM http_status_codes WHERE status = :sc_status) || ') '"
},
"sc_bytes" : {
"kind" : "integer"

@ -171,6 +171,7 @@ void update_view_name(void)
void handle_paging_key(int ch)
{
textview_curses * tc = lnav_data.ld_view_stack.top();
exec_context &ec = lnav_data.ld_exec_context;
logfile_sub_source *lss = NULL;
bookmarks<vis_line_t>::type & bm = tc->get_bookmarks();
@ -431,7 +432,7 @@ void handle_paging_key(int ch)
alerter::singleton().chime();
}
else {
execute_command("zoom-to " + string(lnav_zoom_strings[lnav_data.ld_zoom_level - 1]));
execute_command(ec, "zoom-to " + string(lnav_zoom_strings[lnav_data.ld_zoom_level - 1]));
}
break;
@ -440,7 +441,7 @@ void handle_paging_key(int ch)
alerter::singleton().chime();
}
else {
execute_command("zoom-to " + string(lnav_zoom_strings[lnav_data.ld_zoom_level + 1]));
execute_command(ec, "zoom-to " + string(lnav_zoom_strings[lnav_data.ld_zoom_level + 1]));
}
break;
@ -883,6 +884,8 @@ void handle_paging_key(int ch)
logfile::iterator ll = lf->begin() + cl;
log_data_helper ldh(lss);
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
lnav_data.ld_rl_view->clear_possibilities(LNM_COMMAND, "numeric-colname");
lnav_data.ld_rl_view->clear_possibilities(LNM_COMMAND, "colname");
@ -985,6 +988,7 @@ void handle_paging_key(int ch)
tc == &lnav_data.ld_views[LNV_DB] ||
tc == &lnav_data.ld_views[LNV_SCHEMA]) {
textview_curses &log_view = lnav_data.ld_views[LNV_LOG];
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
lnav_data.ld_mode = LNM_SQL;
setup_logline_table();
@ -1011,12 +1015,11 @@ void handle_paging_key(int ch)
lnav_data.ld_mode = LNM_EXEC;
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
lnav_data.ld_rl_view->clear_possibilities(LNM_EXEC, "__command");
find_format_scripts(lnav_data.ld_config_paths, scripts);
for (map<string, vector<script_metadata> >::iterator iter = scripts.begin();
iter != scripts.end();
++iter) {
lnav_data.ld_rl_view->add_possibility(LNM_EXEC, "__command", iter->first);
for (const auto &iter : scripts) {
lnav_data.ld_rl_view->add_possibility(LNM_EXEC, "__command", iter.first);
}
add_view_text_possibilities(LNM_EXEC, "*", tc);
add_env_possibilities(LNM_EXEC);
@ -1204,7 +1207,7 @@ void handle_paging_key(int ch)
break;
case 'X':
lnav_data.ld_rl_view->set_value(execute_command("close"));
lnav_data.ld_rl_view->set_value(execute_command(ec, "close"));
break;
case '\\':
@ -1269,7 +1272,7 @@ void handle_paging_key(int ch)
break;
case KEY_CTRL_W:
execute_command(lnav_data.ld_views[LNV_LOG].get_word_wrap() ?
execute_command(ec, lnav_data.ld_views[LNV_LOG].get_word_wrap() ?
"disable-word-wrap" : "enable-word-wrap");
break;

@ -40,8 +40,10 @@
#include <string>
#include <vector>
#include "yajlpp.hh"
#include "auto_mem.hh"
#include "yajl/api/yajl_parse.h"
#include "yajl/api/yajl_tree.h"
#include "yajl/api/yajl_gen.h"
#include "lnav_log.hh"
class json_ptr_walk {
@ -183,6 +185,42 @@ public:
return retval;
};
static size_t decode(char *dst, const char *src, ssize_t src_len = -1) {
size_t retval = 0;
if (src_len == -1) {
src_len = strlen(src);
}
for (int lpc = 0; lpc < src_len; lpc++) {
switch (src[lpc]) {
case '~':
if ((lpc + 1) < src_len) {
switch (src[lpc + 1]) {
case '0':
dst[retval++] = '~';
lpc += 1;
break;
case '1':
dst[retval++] = '/';
lpc += 1;
break;
default:
break;
}
}
break;
default:
dst[retval++] = src[lpc];
break;
}
}
dst[retval] = '\0';
return retval;
}
json_ptr(const char *value)
: jp_value(value), jp_pos(value), jp_depth(0), jp_array_index(-1),
jp_state(MS_VALUE) {

@ -717,6 +717,45 @@ static void open_schema_view(void)
schema_tc->set_sub_source(new plain_text_source(schema));
}
static int pretty_sql_callback(exec_context &ec, sqlite3_stmt *stmt)
{
int ncols = sqlite3_column_count(stmt);
for (int lpc = 0; lpc < ncols; lpc++) {
if (lpc > 0) {
ec.ec_accumulator += ", ";
}
ec.ec_accumulator.append((const char *)sqlite3_column_text(stmt, lpc));
}
return 0;
}
static future<string> pretty_pipe_callback(exec_context &ec,
const string &cmdline,
auto_fd &fd)
{
auto retval = std::async(std::launch::async, [&]() {
char buffer[1024];
ostringstream ss;
ssize_t rc;
while ((rc = read(fd, buffer, sizeof(buffer))) > 0) {
ss.write(buffer, rc);
}
string retval = ss.str();
if (endswith(retval.c_str(), "\n")) {
retval.resize(retval.length() - 1);
}
return retval;
});
return retval;
}
static void open_pretty_view(void)
{
static const char *NOTHING_MSG =
@ -741,6 +780,7 @@ static void open_pretty_view(void)
for (vis_line_t vl = log_tc->get_top(); vl <= log_tc->get_bottom(); ++vl) {
content_line_t cl = lss.at(vl);
logfile *lf = lss.find(cl);
log_format *format = lf->get_format();
logfile::iterator ll = lf->begin() + cl;
shared_buffer_ref sbr;
@ -750,7 +790,17 @@ static void open_pretty_view(void)
ll = lf->message_start(ll);
lf->read_full_message(ll, sbr);
data_scanner ds(sbr);
string_attrs_t sa;
vector<logline_value> values;
string rewritten_line;
format->annotate(sbr, sa, values);
exec_context ec(&values, pretty_sql_callback, pretty_pipe_callback);
add_ansi_vars(ec.ec_local_vars.top());
format->rewrite(ec, sbr, sa, rewritten_line);
data_scanner ds(rewritten_line);
pretty_printer pp(&ds);
// TODO: dump more details of the line in the output.
@ -1715,6 +1765,8 @@ static void wait_for_pipers(void)
static void looper(void)
{
try {
exec_context &ec = lnav_data.ld_exec_context;
readline_context command_context("cmd", &lnav_commands);
readline_context search_context("search");
@ -1842,7 +1894,7 @@ static void looper(void)
lnav_data.ld_status[0].window_change();
lnav_data.ld_status[1].window_change();
execute_file(dotlnav_path("session"));
execute_file(ec, dotlnav_path("session"));
lnav_data.ld_scroll_broadcaster.invoke(lnav_data.ld_view_stack.top());
@ -2058,7 +2110,7 @@ static void looper(void)
vector<pair<string, string> > msgs;
execute_init_commands(msgs);
execute_init_commands(ec, msgs);
if (!msgs.empty()) {
pair<string, string> last_msg = msgs.back();
@ -2285,6 +2337,7 @@ redraw_listener REDRAW_LISTENER;
int main(int argc, char *argv[])
{
std::vector<std::string> config_errors, loader_errors;
exec_context &ec = lnav_data.ld_exec_context;
int lpc, c, retval = EXIT_SUCCESS;
auto_ptr<piper_proc> stdin_reader;
@ -2302,10 +2355,11 @@ int main(int argc, char *argv[])
lnav_data.ld_flags |= LNF_SECURE_MODE;
}
lnav_data.ld_exec_context.ec_sql_callback = sql_callback;
lnav_data.ld_exec_context.ec_pipe_callback = pipe_callback;
lnav_data.ld_program_name = argv[0];
lnav_data.ld_local_vars.push(map<string, string>());
add_ansi_vars(lnav_data.ld_local_vars.top());
lnav_data.ld_path_stack.push(".");
add_ansi_vars(ec.ec_local_vars.top());
rl_readline_name = "lnav";
@ -2913,7 +2967,7 @@ int main(int argc, char *argv[])
}
log_info("Executing initial commands");
execute_init_commands(msgs);
execute_init_commands(lnav_data.ld_exec_context, msgs);
wait_for_pipers();
lnav_data.ld_curl_looper.process_all();
rebuild_indexes(false);

@ -63,6 +63,7 @@
#include "relative_time.hh"
#include "log_format_loader.hh"
#include "spectro_source.hh"
#include "command_executor.hh"
/** The command modes that are available while viewing a file. */
typedef enum {
@ -291,12 +292,12 @@ struct _lnav_data {
relative_time ld_last_relative_time;
std::stack<std::map<std::string, std::string> > ld_local_vars;
std::stack<std::string> ld_path_stack;
std::stack<FILE *> ld_output_stack;
std::map<std::string, std::vector<script_metadata> > ld_scripts;
exec_context ld_exec_context;
int ld_fifo_counter;
struct key_repeat_history ld_key_repeat_history;

@ -117,7 +117,7 @@ static string refresh_pt_search()
return retval;
}
static string com_adjust_log_time(string cmdline, vector<string> &args)
static string com_adjust_log_time(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting new time value";
@ -161,7 +161,7 @@ static string com_adjust_log_time(string cmdline, vector<string> &args)
return retval;
}
static string com_unix_time(string cmdline, vector<string> &args)
static string com_unix_time(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a unix time value";
@ -217,7 +217,7 @@ static string com_unix_time(string cmdline, vector<string> &args)
return retval;
}
static string com_current_time(string cmdline, vector<string> &args)
static string com_current_time(exec_context &ec, string cmdline, vector<string> &args)
{
char ftime[128];
struct tm localtm;
@ -239,7 +239,7 @@ static string com_current_time(string cmdline, vector<string> &args)
return retval;
}
static string com_goto(string cmdline, vector<string> &args)
static string com_goto(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting line number/percentage, timestamp, or relative time";
@ -319,7 +319,7 @@ static string com_goto(string cmdline, vector<string> &args)
return retval;
}
static string com_relative_goto(string cmdline, vector<string> &args)
static string com_relative_goto(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting line number/percentage";
@ -348,7 +348,7 @@ static string com_relative_goto(string cmdline, vector<string> &args)
return retval;
}
static string com_goto_mark(string cmdline, vector<string> &args)
static string com_goto_mark(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
@ -442,7 +442,7 @@ static void json_write_row(yajl_gen handle, int row)
}
}
static string com_save_to(string cmdline, vector<string> &args)
static string com_save_to(exec_context &ec, string cmdline, vector<string> &args)
{
FILE *outfile = NULL, *toclose = NULL;
const char *mode = "";
@ -467,7 +467,7 @@ static string com_save_to(string cmdline, vector<string> &args)
vector<string> split_args;
shlex lexer(fn);
if (!lexer.split(split_args, lnav_data.ld_local_vars.top())) {
if (!lexer.split(split_args, ec.ec_local_vars.top())) {
return "error: unable to parse arguments";
}
if (split_args.size() > 1) {
@ -643,7 +643,7 @@ static string com_save_to(string cmdline, vector<string> &args)
return "";
}
static string com_pipe_to(string cmdline, vector<string> &args)
static string com_pipe_to(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting command to execute";
@ -697,9 +697,9 @@ static string com_pipe_to(string cmdline, vector<string> &args)
log_data_helper ldh(lss);
char tmp_str[64];
ldh.parse_line(tc->get_top(), true);
ldh.parse_line(ec.ec_top_line, true);
snprintf(tmp_str, sizeof(tmp_str), "%d", (int) tc->get_top());
snprintf(tmp_str, sizeof(tmp_str), "%d", (int) ec.ec_top_line);
setenv("log_line", tmp_str, 1);
sql_strftime(tmp_str, sizeof(tmp_str), ldh.ldh_line->get_timeval());
setenv("log_time", tmp_str, 1);
@ -728,30 +728,17 @@ static string com_pipe_to(string cmdline, vector<string> &args)
default:
bookmark_vector<vis_line_t>::iterator iter;
static int exec_count = 0;
string line;
in_pipe.read_end().close_on_exec();
in_pipe.write_end().close_on_exec();
lnav_data.ld_children.push_back(child_pid);
future<string> reader;
if (out_pipe.read_end() != -1) {
piper_proc *pp = new piper_proc(out_pipe.read_end(), false);
char desc[128];
lnav_data.ld_pipers.push_back(pp);
snprintf(desc,
sizeof(desc), "[%d] Output of %s",
exec_count++,
cmdline.c_str());
lnav_data.ld_file_names[desc]
.with_fd(pp->get_fd())
.with_detect_format(false);
lnav_data.ld_files_to_front.push_back(make_pair(desc, 0));
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
X, "to close the file"));
}
reader = ec.ec_pipe_callback(ec, cmdline, out_pipe.read_end());
}
if (pipe_line_to) {
@ -787,14 +774,19 @@ static string com_pipe_to(string cmdline, vector<string> &args)
}
}
retval = "";
if (reader.valid()) {
retval = reader.get();
}
else {
retval = "";
}
break;
}
return retval;
}
static string com_highlight(string cmdline, vector<string> &args)
static string com_highlight(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting regular expression to highlight";
@ -839,7 +831,7 @@ static string com_highlight(string cmdline, vector<string> &args)
return retval;
}
static string com_clear_highlight(string cmdline, vector<string> &args)
static string com_clear_highlight(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting highlight expression to clear";
@ -866,7 +858,7 @@ static string com_clear_highlight(string cmdline, vector<string> &args)
return retval;
}
static string com_help(string cmdline, vector<string> &args)
static string com_help(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
@ -905,9 +897,9 @@ protected:
pcrepp pf_pcre;
};
static string com_enable_filter(string cmdline, vector<string> &args);
static string com_enable_filter(exec_context &ec, string cmdline, vector<string> &args);
static string com_filter(string cmdline, vector<string> &args)
static string com_filter(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting regular expression to filter out";
@ -924,7 +916,7 @@ static string com_filter(string cmdline, vector<string> &args)
args[1] = remaining_args(cmdline, args);
if (fs.get_filter(args[1]) != NULL) {
retval = com_enable_filter(cmdline, args);
retval = com_enable_filter(ec, cmdline, args);
}
else if (fs.full()) {
retval = "error: filter limit reached, try combining "
@ -959,7 +951,7 @@ static string com_filter(string cmdline, vector<string> &args)
return retval;
}
static string com_delete_filter(string cmdline, vector<string> &args)
static string com_delete_filter(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a filter to delete";
@ -985,7 +977,7 @@ static string com_delete_filter(string cmdline, vector<string> &args)
return retval;
}
static string com_enable_filter(string cmdline, vector<string> &args)
static string com_enable_filter(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting disabled filter to enable";
@ -1017,7 +1009,7 @@ static string com_enable_filter(string cmdline, vector<string> &args)
return retval;
}
static string com_disable_filter(string cmdline, vector<string> &args)
static string com_disable_filter(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting enabled filter to disable";
@ -1049,7 +1041,7 @@ static string com_disable_filter(string cmdline, vector<string> &args)
return retval;
}
static string com_enable_word_wrap(string cmdline, vector<string> &args)
static string com_enable_word_wrap(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
@ -1065,7 +1057,7 @@ static string com_enable_word_wrap(string cmdline, vector<string> &args)
return retval;
}
static string com_disable_word_wrap(string cmdline, vector<string> &args)
static string com_disable_word_wrap(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
@ -1083,7 +1075,7 @@ static string com_disable_word_wrap(string cmdline, vector<string> &args)
static std::set<string> custom_logline_tables;
static string com_create_logline_table(string cmdline, vector<string> &args)
static string com_create_logline_table(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a table name";
@ -1120,7 +1112,7 @@ static string com_create_logline_table(string cmdline, vector<string> &args)
return retval;
}
static string com_delete_logline_table(string cmdline, vector<string> &args)
static string com_delete_logline_table(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a table name";
@ -1153,7 +1145,7 @@ static string com_delete_logline_table(string cmdline, vector<string> &args)
static std::set<string> custom_search_tables;
static string com_create_search_table(string cmdline, vector<string> &args)
static string com_create_search_table(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a table name";
@ -1199,7 +1191,7 @@ static string com_create_search_table(string cmdline, vector<string> &args)
return retval;
}
static string com_delete_search_table(string cmdline, vector<string> &args)
static string com_delete_search_table(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a table name";
@ -1230,7 +1222,7 @@ static string com_delete_search_table(string cmdline, vector<string> &args)
return retval;
}
static string com_session(string cmdline, vector<string> &args)
static string com_session(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a command to save to the session file";
@ -1296,7 +1288,7 @@ static string com_session(string cmdline, vector<string> &args)
return retval;
}
static string com_open(string cmdline, vector<string> &args)
static string com_open(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting file name to open";
@ -1322,7 +1314,7 @@ static string com_open(string cmdline, vector<string> &args)
vector<string> split_args;
shlex lexer(pat);
if (!lexer.split(split_args, lnav_data.ld_local_vars.top())) {
if (!lexer.split(split_args, ec.ec_local_vars.top())) {
return "error: unable to parse arguments";
}
@ -1444,7 +1436,7 @@ static string com_open(string cmdline, vector<string> &args)
return retval;
}
static string com_close(string cmdline, vector<string> &args)
static string com_close(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: close must be run in the log or text file views";
@ -1497,7 +1489,7 @@ static string com_close(string cmdline, vector<string> &args)
return retval;
}
static string com_partition_name(string cmdline, vector<string> &args)
static string com_partition_name(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting partition name";
@ -1522,7 +1514,7 @@ static string com_partition_name(string cmdline, vector<string> &args)
return retval;
}
static string com_clear_partition(string cmdline, vector<string> &args)
static string com_clear_partition(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
@ -1558,7 +1550,7 @@ static string com_clear_partition(string cmdline, vector<string> &args)
return retval;
}
static string com_pt_time(string cmdline, vector<string> &args)
static string com_pt_time(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a time value";
@ -1623,7 +1615,7 @@ static string com_pt_time(string cmdline, vector<string> &args)
return retval;
}
static string com_summarize(string cmdline, vector<string> &args)
static string com_summarize(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
@ -1794,7 +1786,7 @@ static string com_summarize(string cmdline, vector<string> &args)
break;
case SQLITE_ROW:
sql_callback(stmt.in());
ec.ec_sql_callback(ec, stmt.in());
break;
default:
@ -1827,7 +1819,7 @@ static string com_summarize(string cmdline, vector<string> &args)
return retval;
}
static string com_add_test(string cmdline, vector<string> &args)
static string com_add_test(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
@ -1868,7 +1860,7 @@ static string com_add_test(string cmdline, vector<string> &args)
return retval;
}
static string com_switch_to_view(string cmdline, vector<string> &args)
static string com_switch_to_view(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
@ -1893,7 +1885,7 @@ static string com_switch_to_view(string cmdline, vector<string> &args)
return retval;
}
static string com_zoom_to(string cmdline, vector<string> &args)
static string com_zoom_to(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "";
@ -1949,7 +1941,7 @@ static string com_zoom_to(string cmdline, vector<string> &args)
return retval;
}
static string com_reset_session(string cmdline, vector<string> &args)
static string com_reset_session(exec_context &ec, string cmdline, vector<string> &args)
{
if (args.empty()) {
@ -1962,7 +1954,7 @@ static string com_reset_session(string cmdline, vector<string> &args)
return "";
}
static string com_load_session(string cmdline, vector<string> &args)
static string com_load_session(exec_context &ec, string cmdline, vector<string> &args)
{
if (args.empty()) {
@ -1976,7 +1968,7 @@ static string com_load_session(string cmdline, vector<string> &args)
return "";
}
static string com_save_session(string cmdline, vector<string> &args)
static string com_save_session(exec_context &ec, string cmdline, vector<string> &args)
{
if (args.empty()) {
@ -1988,7 +1980,7 @@ static string com_save_session(string cmdline, vector<string> &args)
return "";
}
static string com_set_min_log_level(string cmdline, vector<string> &args)
static string com_set_min_log_level(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting log level name";
@ -2011,7 +2003,7 @@ static string com_set_min_log_level(string cmdline, vector<string> &args)
return retval;
}
static string com_hide_line(string cmdline, vector<string> &args)
static string com_hide_line(exec_context &ec, string cmdline, vector<string> &args)
{
string retval;
@ -2113,7 +2105,7 @@ static string com_hide_line(string cmdline, vector<string> &args)
return retval;
}
static string com_show_lines(string cmdline, vector<string> &args)
static string com_show_lines(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "info: showing lines";
@ -2131,7 +2123,7 @@ static string com_show_lines(string cmdline, vector<string> &args)
return retval;
}
static string com_rebuild(string cmdline, vector<string> &args)
static string com_rebuild(exec_context &ec, string cmdline, vector<string> &args)
{
if (args.empty()) {
@ -2143,7 +2135,7 @@ static string com_rebuild(string cmdline, vector<string> &args)
return "";
}
static string com_shexec(string cmdline, vector<string> &args)
static string com_shexec(exec_context &ec, string cmdline, vector<string> &args)
{
if (args.empty()) {
@ -2155,7 +2147,7 @@ static string com_shexec(string cmdline, vector<string> &args)
return "";
}
static string com_poll_now(string cmdline, vector<string> &args)
static string com_poll_now(exec_context &ec, string cmdline, vector<string> &args)
{
if (args.empty()) {
@ -2167,7 +2159,7 @@ static string com_poll_now(string cmdline, vector<string> &args)
return "";
}
static string com_redraw(string cmdline, vector<string> &args)
static string com_redraw(exec_context &ec, string cmdline, vector<string> &args)
{
if (args.empty()) {
@ -2179,7 +2171,7 @@ static string com_redraw(string cmdline, vector<string> &args)
return "";
}
static string com_echo(string cmdline, vector<string> &args)
static string com_echo(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a message";
@ -2219,7 +2211,7 @@ static string com_echo(string cmdline, vector<string> &args)
return retval;
}
static string com_eval(string cmdline, vector<string> &args)
static string com_eval(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a command or query to evaluate";
@ -2232,7 +2224,7 @@ static string com_eval(string cmdline, vector<string> &args)
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())) {
if (!lexer.eval(expanded_cmd, ec.ec_local_vars.top())) {
return "error: invalid arguments";
}
log_debug("Expanded command to evaluate: %s", expanded_cmd.c_str());
@ -2244,14 +2236,14 @@ static string com_eval(string cmdline, vector<string> &args)
string alt_msg;
switch (expanded_cmd[0]) {
case ':':
retval = execute_command(expanded_cmd.substr(1));
retval = execute_command(ec, expanded_cmd.substr(1));
break;
case ';':
retval = execute_sql(expanded_cmd.substr(1), alt_msg);
retval = execute_sql(ec, expanded_cmd.substr(1), alt_msg);
break;
case '|':
retval = "info: executed file -- " + expanded_cmd.substr(1) +
" -- " + execute_file(expanded_cmd.substr(1));
" -- " + execute_file(ec, expanded_cmd.substr(1));
break;
default:
retval = "error: expecting argument to start with ':', ';', "
@ -2263,7 +2255,7 @@ static string com_eval(string cmdline, vector<string> &args)
return retval;
}
static string com_config(string cmdline, vector<string> &args)
static string com_config(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a configuration option to read or write";
@ -2331,7 +2323,7 @@ static string com_config(string cmdline, vector<string> &args)
return retval;
}
static string com_save_config(string cmdline, vector<string> &args)
static string com_save_config(exec_context &ec, string cmdline, vector<string> &args)
{
string retval;
@ -2344,7 +2336,7 @@ static string com_save_config(string cmdline, vector<string> &args)
return retval;
}
static string com_reset_config(string cmdline, vector<string> &args)
static string com_reset_config(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a configuration option to reset";
@ -2668,7 +2660,7 @@ public:
string dsvs_error_msg;
};
static string com_spectrogram(string cmdline, vector<string> &args)
static string com_spectrogram(exec_context &ec, string cmdline, vector<string> &args)
{
string retval = "error: expecting a message field name";

@ -41,6 +41,7 @@
#include "log_vtab_impl.hh"
#include "ptimec.hh"
#include "log_search_table.hh"
#include "command_executor.hh"
using namespace std;
@ -923,6 +924,56 @@ void external_log_format::annotate(shared_buffer_ref &line,
}
}
void external_log_format::rewrite(exec_context &ec,
shared_buffer_ref &line,
string_attrs_t &sa,
string &value_out)
{
vector<logline_value>::iterator iter, bind_iter, shift_iter;
vector<logline_value> &values = *ec.ec_line_values;
value_out.assign(line.get_data(), line.length());
for (iter = values.begin(); iter != values.end(); ++iter) {
map<const intern_string_t, value_def>::iterator vd_iter;
if (!iter->lv_origin.is_valid()) {
log_debug("not rewriting value with invalid origin -- %s", iter->lv_name.get());
continue;
}
vd_iter = this->elf_value_defs.find(iter->lv_name);
if (vd_iter == this->elf_value_defs.end()) {
log_debug("not rewriting undefined value -- %s", iter->lv_name.get());
continue;
}
value_def &vd = vd_iter->second;
if (!vd.vd_rewriter.empty()) {
string field_value = iter->to_string();
field_value = execute_any(ec, vd.vd_rewriter);
struct line_range adj_origin = iter->origin_in_full_msg(
value_out.c_str(), value_out.length());
value_out.erase(adj_origin.lr_start, adj_origin.length());
int32_t shift_amount = field_value.length() - adj_origin.length();
value_out.insert(adj_origin.lr_start, field_value);
for (shift_iter = values.begin();
shift_iter != values.end(); ++shift_iter) {
if (shift_iter->lv_name == iter->lv_name) {
continue;
}
shift_iter->lv_origin.shift(adj_origin.lr_start, shift_amount);
}
}
}
}
static int read_json_field(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
json_log_userdata *jlu = (json_log_userdata *)ypc->ypc_userdata;

@ -56,8 +56,10 @@
#include "intern_string.hh"
#include "shared_buffer.hh"
struct sqlite3;
class log_format;
class log_vtab_manager;
struct exec_context;
/**
* Metadata for a single line in a log file.
@ -523,6 +525,38 @@ public:
return this->lv_sbr.length();
}
struct line_range origin_in_full_msg(const char *msg, size_t len) {
if (this->lv_sub_offset == 0) {
return this->lv_origin;
}
struct line_range retval = this->lv_origin;
const char *last = msg;
for (int lpc = 0; lpc < this->lv_sub_offset; lpc++) {
const char *next = strchr(last, '\n');
require(next != NULL);
next += 1;
int amount = (next - last);
retval.lr_start += amount;
if (retval.lr_end != -1) {
retval.lr_end += amount;
}
last = next + 1;
}
if (retval.lr_end == -1) {
const char *eol = strchr(last, '\n');
retval.lr_end = eol - msg;
}
return retval;
};
intern_string_t lv_name;
kind_t lv_kind;
union value_u {
@ -732,6 +766,13 @@ public:
bool annotate_module = true) const
{ };
virtual void rewrite(exec_context &ec,
shared_buffer_ref &line,
string_attrs_t &sa,
std::string &value_out) {
value_out.assign(line.get_data(), line.length());
};
virtual const logline_value_stats *stats_for_value(const intern_string_t &name) const {
return NULL;
};
@ -847,6 +888,7 @@ public:
bool vd_hidden;
bool vd_internal;
std::vector<std::string> vd_action_list;
std::string vd_rewriter;
bool operator<(const value_def &rhs) const {
return this->vd_index < rhs.vd_index;
@ -926,6 +968,11 @@ public:
std::vector<logline_value> &values,
bool annotate_module = true) const;
void rewrite(exec_context &ec,
shared_buffer_ref &line,
string_attrs_t &sa,
std::string &value_out);
void build(std::vector<std::string> &errors);
void register_vtabs(log_vtab_manager *vtab_manager,

@ -107,6 +107,41 @@ static external_log_format::pattern *pattern_provider(yajlpp_parse_context &ypc,
return &pat;
}
static external_log_format::value_def *value_def_provider(yajlpp_parse_context &ypc, void *root)
{
external_log_format *elf = ensure_format(&ypc);
const intern_string_t value_name = ypc.get_path_fragment_i(2);
external_log_format::value_def &retval = elf->elf_value_defs[value_name];
retval.vd_name = value_name;
return &retval;
}
static scaling_factor *scaling_factor_provider(yajlpp_parse_context &ypc, void *root)
{
external_log_format *elf = ensure_format(&ypc);
const intern_string_t value_name = ypc.get_path_fragment_i(2);
string scale_spec = ypc.get_path_fragment(5);
const intern_string_t scale_name = intern_string::lookup(scale_spec.substr(1));
external_log_format::value_def &value_def = elf->elf_value_defs[value_name];
value_def.vd_name = value_name;
scaling_factor &retval = value_def.vd_unit_scaling[scale_name];
if (scale_spec[0] == '/') {
retval.sf_op = SO_DIVIDE;
}
else if (scale_spec[0] == '*') {
retval.sf_op = SO_MULTIPLY;
}
return &retval;
}
static external_log_format::json_format_element &
ensure_json_format_element(external_log_format *elf, int index)
{
@ -229,62 +264,6 @@ static int read_level_int(yajlpp_parse_context *ypc, long long val)
return 1;
}
static int read_value_def(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
external_log_format *elf = ensure_format(ypc);
const intern_string_t value_name = ypc->get_path_fragment_i(2);
string field_name = ypc->get_path_fragment(3);
string val = string((const char *)str, len);
elf->elf_value_defs[value_name].vd_name = value_name;
if (field_name == "kind") {
logline_value::kind_t kind;
if ((kind = logline_value::string2kind(val.c_str())) ==
logline_value::VALUE_UNKNOWN) {
fprintf(stderr, "error: unknown value kind %s\n", val.c_str());
return 0;
}
elf->elf_value_defs[value_name].vd_kind = kind;
}
else if (field_name == "unit" && ypc->get_path_fragment(4) == "field") {
elf->elf_value_defs[value_name].vd_unit_field = intern_string::lookup(val);
}
else if (field_name == "collate") {
elf->elf_value_defs[value_name].vd_collate = val;
}
return 1;
}
static int read_value_action(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
external_log_format *elf = ensure_format(ypc);
const intern_string_t value_name = ypc->get_path_fragment_i(2);
string field_name = ypc->get_path_fragment(3);
string val = string((const char *)str, len);
elf->elf_value_defs[value_name].vd_action_list.push_back(val);
return 1;
}
static int read_value_bool(yajlpp_parse_context *ypc, int val)
{
external_log_format *elf = ensure_format(ypc);
const intern_string_t value_name = ypc->get_path_fragment_i(2);
string key_name = ypc->get_path_fragment(3);
if (key_name == "identifier")
elf->elf_value_defs[value_name].vd_identifier = val;
else if (key_name == "foreign-key")
elf->elf_value_defs[value_name].vd_foreign_key = val;
else if (key_name == "hidden")
elf->elf_value_defs[value_name].vd_hidden = val;
return 1;
}
static int read_action_def(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
external_log_format *elf = ensure_format(ypc);
@ -331,42 +310,6 @@ static external_log_format::sample &ensure_sample(external_log_format *elf,
return elf->elf_samples[index];
}
static int read_scaling(yajlpp_parse_context *ypc, double val)
{
external_log_format *elf = ensure_format(ypc);
const intern_string_t value_name = ypc->get_path_fragment_i(2);
string scale_spec = ypc->get_path_fragment(5);
if (scale_spec.empty()) {
fprintf(stderr,
"error:%s:%s: scaling factor field cannot be empty\n",
ypc->get_path_fragment(0).c_str(),
value_name.get());
return 0;
}
const intern_string_t scale_name = intern_string::lookup(scale_spec.substr(1));
struct scaling_factor &sf = elf->elf_value_defs[value_name].vd_unit_scaling[scale_name];
if (scale_spec[0] == '/') {
sf.sf_op = SO_DIVIDE;
}
else if (scale_spec[0] == '*') {
sf.sf_op = SO_MULTIPLY;
}
else {
fprintf(stderr,
"error:%s:%s: scaling factor field must start with '/' or '*'\n",
ypc->get_path_fragment(0).c_str(),
value_name.get());
return 0;
}
sf.sf_value = val;
return 1;
}
static int read_sample_line(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
external_log_format *elf = ensure_format(ypc);
@ -476,7 +419,7 @@ static struct json_path_handler line_format_handlers[] = {
.for_enum(&nullobj<external_log_format::json_format_element>()->jfe_align),
json_path_handler("overflow")
.with_synopsis("abbrev")
.with_synopsis("abbrev|truncate|dot-dot")
.with_description("Overflow style")
.with_enum_values(OVERFLOW_ENUM)
.for_enum(&nullobj<external_log_format::json_format_element>()->jfe_overflow),
@ -484,6 +427,75 @@ static struct json_path_handler line_format_handlers[] = {
json_path_handler()
};
static const json_path_handler_base::enum_value_t KIND_ENUM[] = {
{"string", logline_value::VALUE_TEXT},
{"integer", logline_value::VALUE_INTEGER},
{"float", logline_value::VALUE_FLOAT},
{"boolean", logline_value::VALUE_BOOLEAN},
{"json", logline_value::VALUE_JSON},
{"quoted", logline_value::VALUE_QUOTED},
json_path_handler_base::ENUM_TERMINATOR
};
static struct json_path_handler unit_handlers[] = {
json_path_handler("field")
.with_synopsis("<field-name>")
.with_description("The name of the field that contains the units for this field")
.for_field(&nullobj<external_log_format::value_def>()->vd_unit_field),
json_path_handler("scaling-factor/.*$")
.with_synopsis("[*,/]<unit>")
.with_obj_provider(scaling_factor_provider)
.for_field(&nullobj<scaling_factor>()->sf_value),
json_path_handler()
};
static struct json_path_handler value_def_handlers[] = {
json_path_handler("kind")
.with_synopsis("string|integer|float|boolean|json|quoted")
.with_description("The type of data in the field")
.with_enum_values(KIND_ENUM)
.for_enum(&nullobj<external_log_format::value_def>()->vd_kind),
json_path_handler("collate")
.with_synopsis("<function>")
.with_description("The collating function to use for this column")
.for_field(&nullobj<external_log_format::value_def>()->vd_collate),
json_path_handler("unit/")
.with_obj_provider(value_def_provider)
.with_children(unit_handlers),
json_path_handler("identifier")
.with_synopsis("<bool>")
.with_description("Indicates whether or not this field contains an identifier that should be highlighted")
.for_field(&nullobj<external_log_format::value_def>()->vd_identifier),
json_path_handler("foreign-key")
.with_synopsis("<bool>")
.with_description("Indicates whether or not this field should be treated as a foreign key for row in another table")
.for_field(&nullobj<external_log_format::value_def>()->vd_foreign_key),
json_path_handler("hidden")
.with_synopsis("<bool>")
.with_description("Indicates whether or not this JSON field should be hidden")
.for_field(&nullobj<external_log_format::value_def>()->vd_hidden),
json_path_handler("action-list#")
.with_synopsis("<string>")
.with_description("Actions to execute when this field is clicked on")
.for_field(&nullobj<external_log_format::value_def>()->vd_action_list),
json_path_handler("rewriter")
.with_synopsis("<command>")
.with_description("A command that will rewrite this field when pretty-printing")
.for_field(&nullobj<external_log_format::value_def>()->vd_rewriter),
json_path_handler()
};
struct json_path_handler format_handlers[] = {
json_path_handler("/\\w+/regex/[^/]+/")
.with_obj_provider(pattern_provider)
@ -502,11 +514,11 @@ struct json_path_handler format_handlers[] = {
"(trace|debug\\d*|info|stats|warning|error|critical|fatal)")
.add_cb(read_levels)
.add_cb(read_level_int),
json_path_handler("/\\w+/value/.+/(kind|collate|unit/field)$", read_value_def),
json_path_handler("/\\w+/value/.+/(identifier|foreign-key|hidden)$", read_value_bool),
json_path_handler("/\\w+/value/.+/unit/scaling-factor/.*$",
read_scaling),
json_path_handler("/\\w+/value/.+/action-list#", read_value_action),
json_path_handler("/\\w+/value/[^/]+/")
.with_obj_provider(value_def_provider)
.with_children(value_def_handlers),
json_path_handler("/\\w+/action/[^/]+/label", read_action_def),
json_path_handler("/\\w+/action/[^/]+/capture-output", read_action_bool),
json_path_handler("/\\w+/action/[^/]+/cmd#", read_action_cmd),
@ -909,6 +921,8 @@ static void find_format_in_path(const string &path,
meta.sm_name = script_name;
extract_metadata_from_file(meta);
scripts[script_name].push_back(meta);
log_debug(" found script: %s", meta.sm_path.c_str());
}
}
}

@ -94,6 +94,8 @@ std::string log_vtab_impl::get_table_statement(void)
<< " log_body text hidden\n"
<< ");\n";
log_debug("log_vtab_impl.get_table_statement() -> %s", oss.str().c_str());
return oss.str();
}

@ -233,6 +233,7 @@ void rl_abort(void *dummy, readline_curses *rc)
void rl_callback(void *dummy, readline_curses *rc)
{
exec_context &ec = lnav_data.ld_exec_context;
string alt_msg;
lnav_data.ld_bottom_source.set_prompt("");
@ -243,7 +244,7 @@ void rl_callback(void *dummy, readline_curses *rc)
case LNM_COMMAND:
rc->set_alt_value("");
rc->set_value(execute_command(rc->get_value()));
rc->set_value(execute_command(ec, rc->get_value()));
break;
case LNM_SEARCH:
@ -264,10 +265,17 @@ void rl_callback(void *dummy, readline_curses *rc)
}
break;
case LNM_SQL:
rc->set_value(execute_sql(rc->get_value(), alt_msg));
case LNM_SQL: {
string result = execute_sql(ec, rc->get_value(), alt_msg);
if (!result.empty()) {
result = "SQL Result: " + result;
}
rc->set_value(result);
rc->set_alt_value(alt_msg);
break;
}
case LNM_EXEC: {
char fn_template[PATH_MAX];
@ -294,7 +302,7 @@ void rl_callback(void *dummy, readline_curses *rc)
if ((tmpout = fdopen(fd, "w+")) != NULL) {
lnav_data.ld_output_stack.push(tmpout);
string result = execute_file(path_and_args);
string result = execute_file(ec, path_and_args);
string::size_type lf_index = result.find('\n');
if (lf_index != string::npos) {
result = result.substr(0, lf_index);

@ -43,6 +43,7 @@
#include <map>
#include <set>
#include <stack>
#include <string>
#include <vector>
#include <exception>
@ -52,9 +53,14 @@
#include "auto_fd.hh"
#include "vt52_curses.hh"
#include "log_format.hh"
struct exec_context;
typedef void (*readline_highlighter_t)(attr_line_t &line, int x);
extern exec_context INIT_EXEC_CONTEXT;
/**
* Container for information related to different readline contexts. Since
* lnav uses readline for different inputs, we need a way to keep things like
@ -62,7 +68,7 @@ typedef void (*readline_highlighter_t)(attr_line_t &line, int x);
*/
class readline_context {
public:
typedef std::string (*command_func_t)(
typedef std::string (*command_func_t)(exec_context &ec,
std::string cmdline, std::vector<std::string> &args);
typedef struct {
const char *c_name;
@ -96,7 +102,7 @@ public:
std::string cmd = iter->first;
this->rc_possibilities["__command"].insert(cmd);
iter->second.c_func(cmd, this->rc_prototypes[cmd]);
iter->second.c_func(INIT_EXEC_CONTEXT, cmd, this->rc_prototypes[cmd]);
}
}

@ -771,7 +771,7 @@ static int read_commands(yajlpp_parse_context *ypc, const unsigned char *str, si
ypc->get_path_fragment(-3));
view_index = view_name - lnav_view_strings;
bool active = ensure_view(&lnav_data.ld_views[view_index]);
execute_command(cmdline);
execute_command(lnav_data.ld_exec_context, cmdline);
if (!active) {
lnav_data.ld_view_stack.pop();
}

@ -186,6 +186,15 @@ struct line_range {
return this->contains(other.lr_start) || this->contains(other.lr_end);
};
void shift(int32_t start, int32_t amount) {
if (this->lr_start >= start) {
this->lr_start += amount;
}
if (this->lr_end != -1 && start < this->lr_end) {
this->lr_end += amount;
}
};
void ltrim(const char *str) {
while (this->lr_start < this->lr_end && isspace(str[this->lr_start])) {
this->lr_start += 1;
@ -315,14 +324,7 @@ inline void remove_string_attr(string_attrs_t &sa, const struct line_range &lr)
inline void shift_string_attrs(string_attrs_t &sa, int32_t start, int32_t amount)
{
for (string_attrs_t::iterator iter = sa.begin(); iter != sa.end(); ++iter) {
struct line_range *existing_lr = &iter->sa_range;
if (existing_lr->lr_start >= start) {
existing_lr->lr_start += amount;
}
if (existing_lr->lr_end != -1 && start < existing_lr->lr_end) {
existing_lr->lr_end += amount;
}
iter->sa_range.shift(start, amount);
}
}

@ -65,6 +65,17 @@ int yajlpp_static_string(yajlpp_parse_context *ypc, const unsigned char *str, si
return 1;
}
int yajlpp_static_string_vector(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
char *root_ptr = resolve_root(ypc);
vector<string> *field_ptr = (vector<string> *) root_ptr;
field_ptr->push_back(string((const char *) str, len));
yajlpp_validator_for_string(*ypc, *ypc->ypc_current_handler);
return 1;
}
int yajlpp_static_intern_string(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
char *root_ptr = resolve_root(ypc);
@ -182,6 +193,20 @@ void yajlpp_validator_for_int(yajlpp_parse_context &ypc,
}
}
void yajlpp_validator_for_double(yajlpp_parse_context &ypc,
const json_path_handler_base &jph)
{
double *field_ptr = (double *) resolve_root(&ypc);
char buffer[1024];
if (*field_ptr < jph.jph_min_value) {
snprintf(buffer, sizeof(buffer),
"value must be greater than %lld",
jph.jph_min_value);
ypc.report_error(buffer);
}
}
int yajlpp_static_number(yajlpp_parse_context *ypc, long long num)
{
char *root_ptr = resolve_root(ypc);
@ -192,6 +217,16 @@ int yajlpp_static_number(yajlpp_parse_context *ypc, long long num)
return 1;
}
int yajlpp_static_decimal(yajlpp_parse_context *ypc, double num)
{
char *root_ptr = resolve_root(ypc);
double *field_ptr = (double *) root_ptr;
*field_ptr = num;
return 1;
}
int yajlpp_static_bool(yajlpp_parse_context *ypc, int val)
{
char *root_ptr = resolve_root(ypc);
@ -283,13 +318,32 @@ int yajlpp_parse_context::map_key(void *ctx,
size_t len)
{
yajlpp_parse_context *ypc = (yajlpp_parse_context *)ctx;
int start, retval = 1;
int retval = 1;
ypc->ypc_path.resize(ypc->ypc_path_index_stack.back());
ypc->ypc_path.push_back('/');
start = ypc->ypc_path.size();
ypc->ypc_path.resize(ypc->ypc_path.size() + len);
memcpy(&ypc->ypc_path[start], key, len);
if (ypc->ypc_handlers != NULL) {
for (size_t lpc = 0; lpc < len; lpc++) {
switch (key[lpc]) {
case '~':
ypc->ypc_path.push_back('~');
ypc->ypc_path.push_back('0');
break;
case '/':
ypc->ypc_path.push_back('~');
ypc->ypc_path.push_back('1');
break;
default:
ypc->ypc_path.push_back(key[lpc]);
break;
}
}
}
else {
size_t start = ypc->ypc_path.size();
ypc->ypc_path.resize(ypc->ypc_path.size() + len);
memcpy(&ypc->ypc_path[start], key, len);
}
ypc->ypc_path.push_back('\0');
if (ypc->ypc_alt_callbacks.yajl_map_key != NULL) {
@ -339,11 +393,11 @@ void yajlpp_parse_context::update_callbacks(const json_path_handler_base *orig_h
pcre_context::capture_t *cap = this->ypc_pcre_context.all();
if (jph.jph_children) {
if (this->ypc_path[cap->c_end - 1] != '/') {
if (this->ypc_path[child_start + cap->c_end - 1] != '/') {
continue;
}
this->update_callbacks(jph.jph_children, cap->c_end);
this->update_callbacks(jph.jph_children, child_start + cap->c_end);
}
else {
if (child_start + cap->c_end != this->ypc_path.size() - 1) {

@ -43,6 +43,7 @@
#include <algorithm>
#include "pcrepp.hh"
#include "json_ptr.hh"
#include "intern_string.hh"
#include "yajl/api/yajl_parse.h"
@ -129,6 +130,7 @@ struct json_path_handler_base {
};
int yajlpp_static_string(yajlpp_parse_context *, const unsigned char *, size_t);
int yajlpp_static_string_vector(yajlpp_parse_context *, const unsigned char *, size_t);
int yajlpp_static_intern_string(yajlpp_parse_context *, const unsigned char *, size_t);
int yajlpp_static_enum(yajlpp_parse_context *, const unsigned char *, size_t);
yajl_gen_status yajlpp_static_gen_string(yajlpp_gen_context &ygc,
@ -140,8 +142,11 @@ void yajlpp_validator_for_intern_string(yajlpp_parse_context &ypc,
const json_path_handler_base &jph);
void yajlpp_validator_for_int(yajlpp_parse_context &ypc,
const json_path_handler_base &jph);
void yajlpp_validator_for_double(yajlpp_parse_context &ypc,
const json_path_handler_base &jph);
int yajlpp_static_number(yajlpp_parse_context *, long long);
int yajlpp_static_decimal(yajlpp_parse_context *, double);
int yajlpp_static_bool(yajlpp_parse_context *, int);
yajl_gen_status yajlpp_static_gen_bool(yajlpp_gen_context &ygc,
@ -257,6 +262,12 @@ struct json_path_handler : public json_path_handler_base {
return *this;
};
json_path_handler &for_field(std::vector<std::string> *field) {
this->add_cb(yajlpp_static_string_vector);
this->jph_simple_offset = field;
return *this;
};
json_path_handler &for_field(intern_string_t *field) {
this->add_cb(yajlpp_static_intern_string);
this->jph_simple_offset = field;
@ -280,6 +291,13 @@ struct json_path_handler : public json_path_handler_base {
return *this;
};
json_path_handler &for_field(double *field) {
this->add_cb(yajlpp_static_decimal);
this->jph_simple_offset = field;
this->jph_validator = yajlpp_validator_for_double;
return *this;
};
json_path_handler &for_field(bool *field) {
this->add_cb(yajlpp_static_bool);
this->jph_simple_offset = field;
@ -323,7 +341,8 @@ public:
memset(&this->ypc_alt_callbacks, 0, sizeof(this->ypc_alt_callbacks));
};
void get_path_fragment(int offset, const char **frag, size_t &len_out) const {
const char *get_path_fragment(int offset, char *frag_in, size_t &len_out) const {
const char *retval;
size_t start, end;
if (offset < 0) {
@ -333,27 +352,36 @@ public:
if ((offset + 1) < (int)this->ypc_path_index_stack.size()) {
end = this->ypc_path_index_stack[offset + 1];
}
else{
else {
end = this->ypc_path.size() - 1;
}
*frag = &this->ypc_path[start];
len_out = end - start;
if (this->ypc_handlers != NULL) {
len_out = json_ptr::decode(frag_in, &this->ypc_path[start], end - start);
retval = frag_in;
}
else {
retval = &this->ypc_path[start];
len_out = end - start;
}
return retval;
}
const intern_string_t get_path_fragment_i(int offset) const {
char fragbuf[this->ypc_path.size()];
const char *frag;
size_t len;
this->get_path_fragment(offset, &frag, len);
frag = this->get_path_fragment(offset, fragbuf, len);
return intern_string::lookup(frag, len);
};
std::string get_path_fragment(int offset) const
{
std::string get_path_fragment(int offset) const {
char fragbuf[this->ypc_path.size()];
const char *frag;
size_t len;
this->get_path_fragment(offset, &frag, len);
frag = this->get_path_fragment(offset, fragbuf, len);
return std::string(frag, len);
};

@ -161,6 +161,7 @@ drive_data_scanner_LDADD = \
../src/dhclient-summary.o \
../src/dump-pid-sh.o \
../src/partition-by-boot.o \
$(LIBCURL) \
$(PCRE_LIBS) \
$(SQLITE3_LIBS) \
-lpcrecpp \

@ -51,6 +51,11 @@ using namespace std;
const char *TMP_NAME = "scanned.tmp";
string execute_any(exec_context &ec, const string &cmdline_with_mode)
{
return "";
}
int main(int argc, char *argv[])
{
int c, retval = EXIT_SUCCESS;

@ -57,6 +57,11 @@ time_t time(time_t *_unused) {
return 1194107018;
}
string execute_any(exec_context &ec, const string &cmdline_with_mode)
{
return "";
}
int main(int argc, char *argv[]) {
int c, retval = EXIT_SUCCESS;
dl_mode_t mode = MODE_NONE;

@ -56,6 +56,17 @@ static struct {
volatile sig_atomic_t dd_looping;
} drive_data;
string execute_any(exec_context &ec, const string &cmdline_with_mode)
{
return "";
}
struct exec_context {
};
exec_context INIT_EXEC_CONTEXT;
static void rl_callback(void *dummy, readline_curses *rc)
{
string line = rc->get_value();

@ -32,6 +32,11 @@ static int sql_callback(void *ptr,
return 0;
}
std::string execute_any(exec_context &ec, const std::string &cmdline_with_mode)
{
return "";
}
int main(int argc, char *argv[])
{
int retval = EXIT_SUCCESS;

@ -18,6 +18,12 @@
"user" : {
"kind" : "string",
"identifier" : true
},
"msg" : {
"rewriter" : ";SELECT :msg || 'bork bork bork'"
},
"user" : {
"rewriter" : "|rewrite-user"
}
}
}

@ -1,6 +1,6 @@
[pid: 88185|app: 0|req: 1/1] 127.0.0.1 () {38 vars in 696 bytes} [Sun Mar 13 22:49:12 2016] POST /update_metrics => generated 47 bytes in 129 msecs (HTTP/1.1 200) 9 headers in 378 bytes (1 switches on core 3)
[pid: 88185|app: 0|req: 3/2] 127.0.0.1 () {38 vars in 696 bytes} [Sun Mar 13 22:49:15 2016] POST /update_metrics => generated 47 bytes in 35 msecs (HTTP/1.1 200) 9 headers in 378 bytes (1 switches on core 30)
[pid: 88185|app: 0|req: 3/3] 127.0.0.1 () {34 vars in 617 bytes} [Sun Mar 13 22:49:15 2016] POST /endpoint2 => generated 215 bytes in 68 msecs (HTTP/1.1 200) 9 headers in 373 bytes (1 switches on core 8)
[pid: 88185|app: 0|req: 3/3] 127.0.0.1 () {34 vars in 617 bytes} [Sun Mar 13 22:49:15 2016] POST /endpoint2 => generated 215 bytes in 68 micros (HTTP/1.1 200) 9 headers in 373 bytes (1 switches on core 8)
[pid: 88185|app: 0|req: 4/4] 127.0.0.1 () {34 vars in 617 bytes} [Sun Mar 13 22:49:15 2016] POST /endpoint2 => generated 215 bytes in 16 msecs (HTTP/1.1 200) 9 headers in 373 bytes (1 switches on core 22)
[pid: 88185|app: 0|req: 5/5] 127.0.0.1 () {38 vars in 696 bytes} [Sun Mar 13 22:50:12 2016] POST /update_metrics => generated 47 bytes in 10 msecs (HTTP/1.1 200) 9 headers in 378 bytes (1 switches on core 0)
[pid: 88186|app: 0|req: 1/6] 127.0.0.1 () {38 vars in 696 bytes} [Sun Mar 13 22:50:15 2016] POST /update_metrics => generated 47 bytes in 65 msecs (HTTP/1.1 200) 9 headers in 378 bytes (1 switches on core 16)

@ -27,6 +27,36 @@ check_output "json log format is not working" <<EOF
EOF
run_test ${lnav_test} -n \
-I ${test_dir} -c ':switch-to-view pretty' \
${test_dir}/logfile_json.json
check_output "json log format is not working" <<EOF
2013-09-06T20:00:48.124 TRACE trace testbork bork bork
2013-09-06T20:00:49.124 INFO Starting up servicebork bork bork
2013-09-06T22:00:49.124 INFO Shutting down servicebork bork bork
user: mailto:steve@example.com
2013-09-06T22:00:59.124 DEBUG5 Details...bork bork bork
2013-09-06T22:00:59.124 DEBUG4 Details...bork bork bork
2013-09-06T22:00:59.124 DEBUG3 Details...bork bork bork
2013-09-06T22:00:59.124 DEBUG2 Details...bork bork bork
2013-09-06T22:00:59.124 DEBUG Details...bork bork bork
2013-09-06T22:01:49.124 STATS 1 beat per secondbork bork bork
2013-09-06T22:01:49.124 WARNING not looking goodbork bork bork
2013-09-06T22:01:49.124 ERROR looking badbork bork bork
2013-09-06T22:01:49.124 CRITICAL sooo badbork bork bork
2013-09-06T22:01:49.124 FATAL shootbork bork bork
obj: {
"field1" : "hi",
"field2": 2
}
arr: [
"hi",
{"sub1": true}
]
EOF
run_test ${lnav_test} -n \
-I ${test_dir} \
${test_dir}/log.clog

@ -47,6 +47,11 @@ time_t time(time_t *arg)
return current_time;
}
string execute_any(exec_context &ec, const string &cmdline_with_mode)
{
return "";
}
int main(int argc, char *argv[])
{
int retval = EXIT_SUCCESS;

Loading…
Cancel
Save