mirror of
https://github.com/tstack/lnav.git
synced 2024-08-16 16:30:25 +03:00
[regex101] add an integration with regex101
... and a pile of other changes
This commit is contained in:
parent
69b5cb1d58
commit
a27198e8ca
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,7 +2,7 @@
|
|||||||
.lnav
|
.lnav
|
||||||
*.dat
|
*.dat
|
||||||
*.diff
|
*.diff
|
||||||
*.err
|
test/*.err
|
||||||
*.index
|
*.index
|
||||||
*.log
|
*.log
|
||||||
*.o
|
*.o
|
||||||
|
6
NEWS
6
NEWS
@ -1,5 +1,10 @@
|
|||||||
lnav v0.10.2:
|
lnav v0.10.2:
|
||||||
Features:
|
Features:
|
||||||
|
* Added an integration with regex101.com to make it easier to edit
|
||||||
|
log message regular expressions. Using the new "management CLI"
|
||||||
|
(activated by the -m option), a log format can be created from
|
||||||
|
a regular expression entry on regex101.com and existing patterns
|
||||||
|
can be edited.
|
||||||
* Add initial support for pcap(3) files using tshark(1).
|
* Add initial support for pcap(3) files using tshark(1).
|
||||||
* Add format for UniFi gateway.
|
* Add format for UniFi gateway.
|
||||||
|
|
||||||
@ -13,6 +18,7 @@ lnav v0.10.2:
|
|||||||
Fixes:
|
Fixes:
|
||||||
* Toggling enabled/disabled filters when there is a SQL expression
|
* Toggling enabled/disabled filters when there is a SQL expression
|
||||||
no longer causes a crash.
|
no longer causes a crash.
|
||||||
|
* Fix a crash related to long lines that are word wrapped.
|
||||||
|
|
||||||
lnav v0.10.1:
|
lnav v0.10.1:
|
||||||
Features:
|
Features:
|
||||||
|
@ -42,6 +42,8 @@ test_file_base=`basename $1`
|
|||||||
# The current test number for shell based tests.
|
# The current test number for shell based tests.
|
||||||
test_num=0
|
test_num=0
|
||||||
|
|
||||||
|
test_hash=""
|
||||||
|
|
||||||
lnav_test="${top_builddir}/src/lnav-test"
|
lnav_test="${top_builddir}/src/lnav-test"
|
||||||
export lnav_test
|
export lnav_test
|
||||||
|
|
||||||
@ -61,6 +63,10 @@ export HAVE_SQLITE3_VALUE_SUBTYPE
|
|||||||
|
|
||||||
LAST_TEST=""
|
LAST_TEST=""
|
||||||
|
|
||||||
|
LAST_CAP_TEST=()
|
||||||
|
|
||||||
|
has_errors=""
|
||||||
|
|
||||||
#
|
#
|
||||||
# Run a test case and capture its standard out and standard err.
|
# Run a test case and capture its standard out and standard err.
|
||||||
#
|
#
|
||||||
@ -73,11 +79,68 @@ LAST_TEST=""
|
|||||||
# run_test rktimes -V
|
# run_test rktimes -V
|
||||||
#
|
#
|
||||||
run_test() {
|
run_test() {
|
||||||
LAST_TEST="test: $@"
|
printf "%s \033[0;35m=============================================================\033[0m\n" $(date -Iseconds)
|
||||||
|
LAST_TEST=("test: $@")
|
||||||
|
echo "${LAST_TEST[@]}"
|
||||||
export test_num=`expr ${test_num} \+ 1`
|
export test_num=`expr ${test_num} \+ 1`
|
||||||
"$@" > ${test_file_base}_${test_num}.tmp 2> ${test_file_base}_${test_num}.err
|
"$@" > ${test_file_base}_${test_num}.tmp 2> ${test_file_base}_${test_num}.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_cap_test() {
|
||||||
|
LAST_CAP_TEST=("test: $@")
|
||||||
|
local full_cmd=$(echo "${LAST_CAP_TEST[@]}" | sed -e "s;${test_dir};{test_dir};g")
|
||||||
|
export test_hash=$(echo "${full_cmd}" | shasum | cut -f 1 -d ' ')
|
||||||
|
echo "${full_cmd}" > ${test_file_base}_${test_hash}.cmd
|
||||||
|
"$@" > ${test_file_base}_${test_hash}.out 2> ${test_file_base}_${test_hash}.err
|
||||||
|
|
||||||
|
sed -ibak \
|
||||||
|
-e "s;${builddir};{builddir};g" \
|
||||||
|
-e "s;${test_dir};{test_dir};g" \
|
||||||
|
-e "s;${top_srcdir};{top_srcdir};g" \
|
||||||
|
${test_file_base}_${test_hash}.out
|
||||||
|
echo
|
||||||
|
printf "%s \033[0;35m=============================================================\033[0m\n" $(date -Iseconds)
|
||||||
|
printf '\033[0;35mCommand\033[0m: %s\n' "${full_cmd}"
|
||||||
|
printf '\033[0;32mBEGIN\033[0m %s\n' "${test_file_base}_${test_hash}.out"
|
||||||
|
cat "${test_file_base}_${test_hash}.out"
|
||||||
|
printf '\033[0;32mEND\033[0m %s\n' "${test_file_base}_${test_hash}.out"
|
||||||
|
if test -f ${srcdir}/expected/${test_file_base}_${test_hash}.out; then
|
||||||
|
diff -w -u \
|
||||||
|
${srcdir}/expected/${test_file_base}_${test_hash}.out \
|
||||||
|
${test_file_base}_${test_hash}.out \
|
||||||
|
> ${test_file_base}_${test_hash}.diff
|
||||||
|
if test $? -ne 0; then
|
||||||
|
echo OUT: "${full_cmd}"
|
||||||
|
cat ${test_file_base}_${test_hash}.diff
|
||||||
|
export has_errors="yes"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
export has_errors="yes"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sed -ibak -E \
|
||||||
|
-e "s;${builddir};{builddir};g" \
|
||||||
|
-e "s;${test_dir};{test_dir};g" \
|
||||||
|
-e "s;${top_srcdir};{top_srcdir};g" \
|
||||||
|
-e 's;"errorId":".+";;g' \
|
||||||
|
${test_file_base}_${test_hash}.err
|
||||||
|
printf '\033[0;31mBEGIN\033[0m %s\n' "${test_file_base}_${test_hash}.err"
|
||||||
|
cat "${test_file_base}_${test_hash}.err"
|
||||||
|
printf '\033[0;31mEND\033[0m %s\n' "${test_file_base}_${test_hash}.err"
|
||||||
|
if test -f ${srcdir}/expected/${test_file_base}_${test_hash}.err; then
|
||||||
|
diff -w -u ${srcdir}/expected/${test_file_base}_${test_hash}.err \
|
||||||
|
${test_file_base}_${test_hash}.err \
|
||||||
|
> ${test_file_base}_${test_hash}.err.diff
|
||||||
|
if test $? -ne 0; then
|
||||||
|
echo ERR: "${full_cmd}"
|
||||||
|
cat ${test_file_base}_${test_hash}.err.diff
|
||||||
|
export has_errors="yes"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
export has_errors="yes"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# Check the output generated by a run_test() call.
|
# Check the output generated by a run_test() call.
|
||||||
#
|
#
|
||||||
@ -100,7 +163,7 @@ check_output() {
|
|||||||
${test_file_base}_${test_num}.tmp
|
${test_file_base}_${test_num}.tmp
|
||||||
diff -w -u - ${test_file_base}_${test_num}.tmp > ${test_file_base}_${test_num}.diff
|
diff -w -u - ${test_file_base}_${test_num}.tmp > ${test_file_base}_${test_num}.diff
|
||||||
if test $? -ne 0; then
|
if test $? -ne 0; then
|
||||||
echo $LAST_TEST
|
echo "${LAST_TEST[@]}"
|
||||||
echo $1
|
echo $1
|
||||||
cat ${test_file_base}_${test_num}.diff
|
cat ${test_file_base}_${test_num}.diff
|
||||||
exit 1
|
exit 1
|
||||||
@ -110,7 +173,7 @@ check_output() {
|
|||||||
check_output_ws() {
|
check_output_ws() {
|
||||||
diff -u - ${test_file_base}_${test_num}.tmp > ${test_file_base}_${test_num}.diff
|
diff -u - ${test_file_base}_${test_num}.tmp > ${test_file_base}_${test_num}.diff
|
||||||
if test $? -ne 0; then
|
if test $? -ne 0; then
|
||||||
echo $LAST_TEST
|
echo "${LAST_TEST[@]}"
|
||||||
echo $1
|
echo $1
|
||||||
cat ${test_file_base}_${test_num}.diff
|
cat ${test_file_base}_${test_num}.diff
|
||||||
exit 1
|
exit 1
|
||||||
@ -133,7 +196,7 @@ check_error_output() {
|
|||||||
diff -w -u - ${test_file_base}_${test_num}.err \
|
diff -w -u - ${test_file_base}_${test_num}.err \
|
||||||
> ${test_file_base}_${test_num}.err.diff
|
> ${test_file_base}_${test_num}.err.diff
|
||||||
if test $? -ne 0; then
|
if test $? -ne 0; then
|
||||||
echo $LAST_TEST
|
echo "${LAST_TEST[@]}"
|
||||||
echo $1
|
echo $1
|
||||||
cat ${test_file_base}_${test_num}.err.diff
|
cat ${test_file_base}_${test_num}.err.diff
|
||||||
exit 1
|
exit 1
|
||||||
@ -191,3 +254,11 @@ else
|
|||||||
shift
|
shift
|
||||||
. ${test_file}
|
. ${test_file}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
if test "${has_errors}"x = "yes"x; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap "cleanup" EXIT
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
# aminclude_static.am generated automatically by Autoconf
|
# aminclude_static.am generated automatically by Autoconf
|
||||||
# from AX_AM_MACROS_STATIC on Thu Mar 31 15:28:54 PDT 2022
|
# from AX_AM_MACROS_STATIC on Fri Apr 22 09:39:38 PDT 2022
|
||||||
|
|
||||||
|
|
||||||
# Code coverage
|
# Code coverage
|
||||||
|
15
cleanup_expected.sh
Executable file
15
cleanup_expected.sh
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
srcdir="$1"
|
||||||
|
builddir="$2"
|
||||||
|
|
||||||
|
for fname in "${srcdir}"/expected/*.out; do
|
||||||
|
stem=$(basename "$fname" | sed -e 's/.out$//')
|
||||||
|
|
||||||
|
if ! test -f "${builddir}/$stem.cmd"; then
|
||||||
|
echo "removing $fname"
|
||||||
|
guilt rm "$fname"
|
||||||
|
echo "removing ${srcdir}/expected/${stem}.err"
|
||||||
|
guilt rm "${srcdir}/expected/${stem}.err"
|
||||||
|
fi
|
||||||
|
done
|
@ -347,6 +347,11 @@
|
|||||||
"description": "Styling for 6th-level headers",
|
"description": "Styling for 6th-level headers",
|
||||||
"title": "/ui/theme-defs/<theme_name>/styles/h6",
|
"title": "/ui/theme-defs/<theme_name>/styles/h6",
|
||||||
"$ref": "#/definitions/style"
|
"$ref": "#/definitions/style"
|
||||||
|
},
|
||||||
|
"list-glyph": {
|
||||||
|
"description": "Styling for glyphs that prefix a list item",
|
||||||
|
"title": "/ui/theme-defs/<theme_name>/styles/list-glyph",
|
||||||
|
"$ref": "#/definitions/style"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
@ -314,6 +314,11 @@
|
|||||||
"items": {
|
"items": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"title": "/<format_name>/sample/description",
|
||||||
|
"description": "A description of this sample.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"line": {
|
"line": {
|
||||||
"title": "/<format_name>/sample/line",
|
"title": "/<format_name>/sample/line",
|
||||||
"description": "A sample log line that should match a pattern in this format.",
|
"description": "A sample log line that should match a pattern in this format.",
|
||||||
|
@ -1,16 +1,24 @@
|
|||||||
|
|
||||||
.. _cli:
|
.. _cli:
|
||||||
|
|
||||||
Command Line Interface
|
Command Line Interface
|
||||||
======================
|
======================
|
||||||
|
|
||||||
|
There are two command-line interfaces provided by **lnav**, one for viewing
|
||||||
|
files and one for managing **lnav**'s configuration. The file viewing mode is
|
||||||
|
the default and is all that most people will need. The management mode can
|
||||||
|
be useful for those that are developing log file formats and is activated by
|
||||||
|
passing the :option:`-m` option as the first argument.
|
||||||
|
|
||||||
|
File Viewing Mode
|
||||||
|
-----------------
|
||||||
|
|
||||||
The following options can be used when starting **lnav**. There are not
|
The following options can be used when starting **lnav**. There are not
|
||||||
many flags because the majority of the functionality is accessed using
|
many flags because the majority of the functionality is accessed using
|
||||||
the :option:`-c` option to execute :ref:`commands<commands>` or
|
the :option:`-c` option to execute :ref:`commands<commands>` or
|
||||||
:ref:`SQL queries<sql-ext>`.
|
:ref:`SQL queries<sql-ext>`.
|
||||||
|
|
||||||
Options
|
Options
|
||||||
-------
|
^^^^^^^
|
||||||
|
|
||||||
.. option:: -h
|
.. option:: -h
|
||||||
|
|
||||||
@ -85,6 +93,32 @@ Options
|
|||||||
Do not print the log messages after executing all of the commands.
|
Do not print the log messages after executing all of the commands.
|
||||||
|
|
||||||
|
|
||||||
|
Management Mode (v0.10.2+)
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
Options
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
.. option:: -m
|
||||||
|
|
||||||
|
Switch to management mode. This must be the first option passed on the
|
||||||
|
command-line.
|
||||||
|
|
||||||
|
Subcommands
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. option:: regex101 import <regex101-url> <format-name> [<regex-name>]
|
||||||
|
|
||||||
|
Convert a regex101.com entry into a skeleton log format file.
|
||||||
|
|
||||||
|
.. option:: format <format-name> regex <regex-name> push
|
||||||
|
|
||||||
|
Push a log format regular expression to regex101.com .
|
||||||
|
|
||||||
|
.. option:: format <format-name> regex <regex-name> pull
|
||||||
|
|
||||||
|
Pull changes to a regex that was previously pushed to regex101.com .
|
||||||
|
|
||||||
Environment Variables
|
Environment Variables
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
@ -55,6 +55,35 @@ own formats or if you need to modify existing ones. Format directories can
|
|||||||
also contain '.sql' and '.lnav' script files that can be used automate log file
|
also contain '.sql' and '.lnav' script files that can be used automate log file
|
||||||
analysis.
|
analysis.
|
||||||
|
|
||||||
|
Creating a Format Using Regex101.com (v0.10.2+)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
For plain-text log files, the easiest way to create a log format definition is
|
||||||
|
to create the regular expression that recognizes log messages using
|
||||||
|
https://regex101.com . Simply copy a log line into the test string input box
|
||||||
|
on the site and then start editing the regular expression. When building the
|
||||||
|
regular expression, you'll want to use named captures for the structured parts
|
||||||
|
of the log message. Any raw message text should be matched by a captured named
|
||||||
|
"body". Once you have a regex that matches the whole log message, you can use
|
||||||
|
**lnav**'s "management CLI" to create a skeleton format file. The skeleton
|
||||||
|
will be populated with the regular expression from the site and the test
|
||||||
|
string, along with any unit tests, will be added to the "samples" list. The
|
||||||
|
"regex101 import" management command is used to create the skeleton and has
|
||||||
|
the following form:
|
||||||
|
|
||||||
|
.. prompt:: bash
|
||||||
|
|
||||||
|
lnav -m regex101 import <regex101-url> <format-name> [<regex-name>]
|
||||||
|
|
||||||
|
If the import was successful, the path to the new format file should be
|
||||||
|
printed out. The skeleton will most likely need some changes to make it
|
||||||
|
fully functional. For example, the :code:`kind` properties for captured values
|
||||||
|
default to :code:`string`, but you'll want to change them to the appropriate
|
||||||
|
type.
|
||||||
|
|
||||||
|
Format File Reference
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
An **lnav** format file must contain a single JSON object, preferably with a
|
An **lnav** format file must contain a single JSON object, preferably with a
|
||||||
:code:`$schema` property that refers to the
|
:code:`$schema` property that refers to the
|
||||||
`format-v1.schema <https://lnav.org/schemas/format-v1.schema.json>`_,
|
`format-v1.schema <https://lnav.org/schemas/format-v1.schema.json>`_,
|
||||||
@ -309,8 +338,8 @@ Example format:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Modifying an Existing Format
|
Patching an Existing Format
|
||||||
----------------------------
|
---------------------------
|
||||||
|
|
||||||
When loading log formats from files, **lnav** will overlay any new data over
|
When loading log formats from files, **lnav** will overlay any new data over
|
||||||
previously loaded data. This feature allows you to override existing value or
|
previously loaded data. This feature allows you to override existing value or
|
||||||
|
@ -68,8 +68,10 @@ set(TIME_FORMATS
|
|||||||
"%m/%d/%Y %l:%M:%S %p %Z"
|
"%m/%d/%Y %l:%M:%S %p %Z"
|
||||||
"%m/%e/%Y %I:%M:%S %p"
|
"%m/%e/%Y %I:%M:%S %p"
|
||||||
"%m/%e/%Y %l:%M:%S %p"
|
"%m/%e/%Y %l:%M:%S %p"
|
||||||
|
"%m/%d/%Y %H:%M:%S"
|
||||||
"%d/%b/%y %H:%M:%S"
|
"%d/%b/%y %H:%M:%S"
|
||||||
"%m%d %H:%M:%S"
|
"%m%d %H:%M:%S"
|
||||||
|
"%Y%m%d.%H%M%S"
|
||||||
"%H:%M:%S"
|
"%H:%M:%S"
|
||||||
"%M:%S"
|
"%M:%S"
|
||||||
"%m/%d %H:%M:%S"
|
"%m/%d %H:%M:%S"
|
||||||
@ -253,6 +255,7 @@ add_library(
|
|||||||
command_executor.cc
|
command_executor.cc
|
||||||
curl_looper.cc
|
curl_looper.cc
|
||||||
db_sub_source.cc
|
db_sub_source.cc
|
||||||
|
dump_internals.cc
|
||||||
elem_to_json.cc
|
elem_to_json.cc
|
||||||
environ_vtab.cc
|
environ_vtab.cc
|
||||||
extension-functions.cc
|
extension-functions.cc
|
||||||
@ -275,6 +278,8 @@ add_library(
|
|||||||
input_dispatcher.cc
|
input_dispatcher.cc
|
||||||
json-extension-functions.cc
|
json-extension-functions.cc
|
||||||
listview_curses.cc
|
listview_curses.cc
|
||||||
|
lnav.indexing.cc
|
||||||
|
lnav.management_cli.cc
|
||||||
lnav_commands.cc
|
lnav_commands.cc
|
||||||
lnav_config.cc
|
lnav_config.cc
|
||||||
lnav_util.cc
|
lnav_util.cc
|
||||||
@ -301,6 +306,8 @@ add_library(
|
|||||||
readline_highlighters.cc
|
readline_highlighters.cc
|
||||||
readline_possibilities.cc
|
readline_possibilities.cc
|
||||||
regexp_vtab.cc
|
regexp_vtab.cc
|
||||||
|
regex101.client.cc
|
||||||
|
regex101.import.cc
|
||||||
relative_time.cc
|
relative_time.cc
|
||||||
session_data.cc
|
session_data.cc
|
||||||
sequence_matcher.cc
|
sequence_matcher.cc
|
||||||
@ -347,6 +354,7 @@ add_library(
|
|||||||
column_namer.hh
|
column_namer.hh
|
||||||
curl_looper.hh
|
curl_looper.hh
|
||||||
doc_status_source.hh
|
doc_status_source.hh
|
||||||
|
dump_internals.hh
|
||||||
elem_to_json.hh
|
elem_to_json.hh
|
||||||
field_overlay_source.hh
|
field_overlay_source.hh
|
||||||
file_collection.hh
|
file_collection.hh
|
||||||
@ -364,6 +372,8 @@ add_library(
|
|||||||
hotkeys.hh
|
hotkeys.hh
|
||||||
input_dispatcher.hh
|
input_dispatcher.hh
|
||||||
k_merge_tree.h
|
k_merge_tree.h
|
||||||
|
lnav.indexing.hh
|
||||||
|
lnav.management_cli.hh
|
||||||
lnav_config.hh
|
lnav_config.hh
|
||||||
lnav_config_fwd.hh
|
lnav_config_fwd.hh
|
||||||
log_actions.hh
|
log_actions.hh
|
||||||
@ -390,6 +400,8 @@ add_library(
|
|||||||
readline_callbacks.hh
|
readline_callbacks.hh
|
||||||
readline_context.hh
|
readline_context.hh
|
||||||
readline_possibilities.hh
|
readline_possibilities.hh
|
||||||
|
regex101.client.hh
|
||||||
|
regex101.import.hh
|
||||||
regexp_vtab.hh
|
regexp_vtab.hh
|
||||||
relative_time.hh
|
relative_time.hh
|
||||||
styling.hh
|
styling.hh
|
||||||
@ -422,6 +434,7 @@ add_library(
|
|||||||
url_loader.hh
|
url_loader.hh
|
||||||
view_helpers.hh
|
view_helpers.hh
|
||||||
view_helpers.examples.hh
|
view_helpers.examples.hh
|
||||||
|
view_helpers.hist.hh
|
||||||
views_vtab.hh
|
views_vtab.hh
|
||||||
vis_line.hh
|
vis_line.hh
|
||||||
vtab_module.hh
|
vtab_module.hh
|
||||||
@ -439,7 +452,24 @@ add_library(
|
|||||||
ghc/fs_std_fwd.hpp
|
ghc/fs_std_fwd.hpp
|
||||||
ghc/fs_std_impl.hpp
|
ghc/fs_std_impl.hpp
|
||||||
ww898/cp_utf8.hpp
|
ww898/cp_utf8.hpp
|
||||||
log_level_re.cc)
|
log_level_re.cc
|
||||||
|
|
||||||
|
third-party/CLI/StringTools.hpp
|
||||||
|
third-party/CLI/App.hpp
|
||||||
|
third-party/CLI/Macros.hpp
|
||||||
|
third-party/CLI/Option.hpp
|
||||||
|
third-party/CLI/Config.hpp
|
||||||
|
third-party/CLI/CLI.hpp
|
||||||
|
third-party/CLI/Formatter.hpp
|
||||||
|
third-party/CLI/Error.hpp
|
||||||
|
third-party/CLI/Version.hpp
|
||||||
|
third-party/CLI/Timer.hpp
|
||||||
|
third-party/CLI/FormatterFwd.hpp
|
||||||
|
third-party/CLI/Validators.hpp
|
||||||
|
third-party/CLI/Split.hpp
|
||||||
|
third-party/CLI/TypeTools.hpp
|
||||||
|
third-party/CLI/ConfigFwd.hpp
|
||||||
|
)
|
||||||
|
|
||||||
set(lnav_SRCS lnav.cc)
|
set(lnav_SRCS lnav.cc)
|
||||||
|
|
||||||
|
@ -170,6 +170,7 @@ noinst_HEADERS = \
|
|||||||
data_parser.hh \
|
data_parser.hh \
|
||||||
db_sub_source.hh \
|
db_sub_source.hh \
|
||||||
doc_status_source.hh \
|
doc_status_source.hh \
|
||||||
|
dump_internals.hh \
|
||||||
elem_to_json.hh \
|
elem_to_json.hh \
|
||||||
environ_vtab.hh \
|
environ_vtab.hh \
|
||||||
field_overlay_source.hh \
|
field_overlay_source.hh \
|
||||||
@ -196,6 +197,8 @@ noinst_HEADERS = \
|
|||||||
line_buffer.hh \
|
line_buffer.hh \
|
||||||
listview_curses.hh \
|
listview_curses.hh \
|
||||||
lnav.hh \
|
lnav.hh \
|
||||||
|
lnav.indexing.hh \
|
||||||
|
lnav.management_cli.hh \
|
||||||
lnav_commands.hh \
|
lnav_commands.hh \
|
||||||
lnav_config.hh \
|
lnav_config.hh \
|
||||||
lnav_config_fwd.hh \
|
lnav_config_fwd.hh \
|
||||||
@ -233,6 +236,8 @@ noinst_HEADERS = \
|
|||||||
readline_curses.hh \
|
readline_curses.hh \
|
||||||
readline_highlighters.hh \
|
readline_highlighters.hh \
|
||||||
readline_possibilities.hh \
|
readline_possibilities.hh \
|
||||||
|
regex101.client.hh \
|
||||||
|
regex101.import.hh \
|
||||||
regexp_vtab.hh \
|
regexp_vtab.hh \
|
||||||
relative_time.hh \
|
relative_time.hh \
|
||||||
ring_span.hh \
|
ring_span.hh \
|
||||||
@ -271,6 +276,7 @@ noinst_HEADERS = \
|
|||||||
view_curses.hh \
|
view_curses.hh \
|
||||||
view_helpers.hh \
|
view_helpers.hh \
|
||||||
view_helpers.examples.hh \
|
view_helpers.examples.hh \
|
||||||
|
view_helpers.hist.hh \
|
||||||
views_vtab.hh \
|
views_vtab.hh \
|
||||||
vis_line.hh \
|
vis_line.hh \
|
||||||
vt52_curses.hh \
|
vt52_curses.hh \
|
||||||
@ -295,6 +301,21 @@ nodist_libdiag_a_SOURCES = \
|
|||||||
|
|
||||||
THIRD_PARTY_SRCS = \
|
THIRD_PARTY_SRCS = \
|
||||||
third-party/backward-cpp/backward.hpp \
|
third-party/backward-cpp/backward.hpp \
|
||||||
|
third-party/CLI/StringTools.hpp \
|
||||||
|
third-party/CLI/App.hpp \
|
||||||
|
third-party/CLI/Macros.hpp \
|
||||||
|
third-party/CLI/Option.hpp \
|
||||||
|
third-party/CLI/Config.hpp \
|
||||||
|
third-party/CLI/CLI.hpp \
|
||||||
|
third-party/CLI/Formatter.hpp \
|
||||||
|
third-party/CLI/Error.hpp \
|
||||||
|
third-party/CLI/Version.hpp \
|
||||||
|
third-party/CLI/Timer.hpp \
|
||||||
|
third-party/CLI/FormatterFwd.hpp \
|
||||||
|
third-party/CLI/Validators.hpp \
|
||||||
|
third-party/CLI/Split.hpp \
|
||||||
|
third-party/CLI/TypeTools.hpp \
|
||||||
|
third-party/CLI/ConfigFwd.hpp \
|
||||||
third-party/doctest-root/doctest/doctest.h \
|
third-party/doctest-root/doctest/doctest.h \
|
||||||
third-party/sqlite/ext/dbdump.c \
|
third-party/sqlite/ext/dbdump.c \
|
||||||
third-party/sqlite/ext/series.c
|
third-party/sqlite/ext/series.c
|
||||||
@ -312,6 +333,7 @@ libdiag_a_SOURCES = \
|
|||||||
data_scanner.cc \
|
data_scanner.cc \
|
||||||
data_scanner_re.cc \
|
data_scanner_re.cc \
|
||||||
db_sub_source.cc \
|
db_sub_source.cc \
|
||||||
|
dump_internals.cc \
|
||||||
elem_to_json.cc \
|
elem_to_json.cc \
|
||||||
environ_vtab.cc \
|
environ_vtab.cc \
|
||||||
extension-functions.cc \
|
extension-functions.cc \
|
||||||
@ -359,6 +381,8 @@ libdiag_a_SOURCES = \
|
|||||||
readline_curses.cc \
|
readline_curses.cc \
|
||||||
readline_highlighters.cc \
|
readline_highlighters.cc \
|
||||||
readline_possibilities.cc \
|
readline_possibilities.cc \
|
||||||
|
regex101.client.cc \
|
||||||
|
regex101.import.cc \
|
||||||
regexp_vtab.cc \
|
regexp_vtab.cc \
|
||||||
relative_time.cc \
|
relative_time.cc \
|
||||||
session_data.cc \
|
session_data.cc \
|
||||||
@ -398,9 +422,18 @@ PLUGIN_SRCS = \
|
|||||||
|
|
||||||
lnav.$(OBJEXT): help-txt.h init-sql.h
|
lnav.$(OBJEXT): help-txt.h init-sql.h
|
||||||
|
|
||||||
lnav_SOURCES = lnav.cc $(PLUGIN_SRCS)
|
lnav_SOURCES = \
|
||||||
|
lnav.cc \
|
||||||
|
lnav.indexing.cc \
|
||||||
|
lnav.management_cli.cc \
|
||||||
|
$(PLUGIN_SRCS)
|
||||||
|
|
||||||
lnav_test_SOURCES = lnav.cc test_override.c $(PLUGIN_SRCS)
|
lnav_test_SOURCES = \
|
||||||
|
lnav.cc \
|
||||||
|
lnav.indexing.cc \
|
||||||
|
lnav.management_cli.cc \
|
||||||
|
test_override.c \
|
||||||
|
$(PLUGIN_SRCS)
|
||||||
|
|
||||||
ptimec$(BUILD_EXEEXT): ptimec.c
|
ptimec$(BUILD_EXEEXT): ptimec.c
|
||||||
$(AM_V_CC) $(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) -g3 -o $@ $?
|
$(AM_V_CC) $(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) -g3 -o $@ $?
|
||||||
@ -419,6 +452,9 @@ DISTCLEANFILES = \
|
|||||||
$(LNAV_BUILT_FILES) \
|
$(LNAV_BUILT_FILES) \
|
||||||
$(RE2C_FILES)
|
$(RE2C_FILES)
|
||||||
|
|
||||||
|
distclean-local:
|
||||||
|
$(RM_V)rm -rf *.dSYM
|
||||||
|
|
||||||
uncrusty:
|
uncrusty:
|
||||||
(cd $(srcdir) && uncrustify -c ../lnav.cfg --replace $(SOURCES) \
|
(cd $(srcdir) && uncrustify -c ../lnav.cfg --replace $(SOURCES) \
|
||||||
$(HEADERS))
|
$(HEADERS))
|
||||||
|
@ -40,7 +40,9 @@ add_library(
|
|||||||
intern_string.hh
|
intern_string.hh
|
||||||
is_utf8.hh
|
is_utf8.hh
|
||||||
isc.hh
|
isc.hh
|
||||||
|
itertools.hh
|
||||||
lnav.console.hh
|
lnav.console.hh
|
||||||
|
log_level_enum.hh
|
||||||
lrucache.hpp
|
lrucache.hpp
|
||||||
math_util.hh
|
math_util.hh
|
||||||
network.tcp.hh
|
network.tcp.hh
|
||||||
@ -56,6 +58,7 @@ target_link_libraries(base cppfmt pcre::libpcre ncurses::libcurses pthread)
|
|||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
test_base
|
test_base
|
||||||
|
fs_util.tests.cc
|
||||||
humanize.file_size.tests.cc
|
humanize.file_size.tests.cc
|
||||||
humanize.network.tests.cc
|
humanize.network.tests.cc
|
||||||
humanize.time.tests.cc
|
humanize.time.tests.cc
|
||||||
|
@ -38,9 +38,11 @@ noinst_HEADERS = \
|
|||||||
intern_string.hh \
|
intern_string.hh \
|
||||||
is_utf8.hh \
|
is_utf8.hh \
|
||||||
isc.hh \
|
isc.hh \
|
||||||
|
itertools.hh \
|
||||||
lnav_log.hh \
|
lnav_log.hh \
|
||||||
lnav.console.hh \
|
lnav.console.hh \
|
||||||
lnav.gzip.hh \
|
lnav.gzip.hh \
|
||||||
|
log_level_enum.hh \
|
||||||
lrucache.hpp \
|
lrucache.hpp \
|
||||||
math_util.hh \
|
math_util.hh \
|
||||||
network.tcp.hh \
|
network.tcp.hh \
|
||||||
@ -78,6 +80,7 @@ check_PROGRAMS = \
|
|||||||
test_base
|
test_base
|
||||||
|
|
||||||
test_base_SOURCES = \
|
test_base_SOURCES = \
|
||||||
|
fs_util.tests.cc \
|
||||||
humanize.file_size.tests.cc \
|
humanize.file_size.tests.cc \
|
||||||
humanize.network.tests.cc \
|
humanize.network.tests.cc \
|
||||||
humanize.time.tests.cc \
|
humanize.time.tests.cc \
|
||||||
|
@ -190,7 +190,7 @@ attr_line_t::subline(size_t start, size_t len) const
|
|||||||
lr.intersection(sa.sa_range).shift(lr.lr_start, -lr.lr_start),
|
lr.intersection(sa.sa_range).shift(lr.lr_start, -lr.lr_start),
|
||||||
std::make_pair(sa.sa_type, sa.sa_value));
|
std::make_pair(sa.sa_type, sa.sa_value));
|
||||||
|
|
||||||
line_range& last_lr = retval.al_attrs.back().sa_range;
|
const auto& last_lr = retval.al_attrs.back().sa_range;
|
||||||
|
|
||||||
ensure(last_lr.lr_end <= (int) retval.al_string.length());
|
ensure(last_lr.lr_end <= (int) retval.al_string.length());
|
||||||
}
|
}
|
||||||
@ -300,6 +300,18 @@ attr_line_t::erase(size_t pos, size_t len)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attr_line_t&
|
||||||
|
attr_line_t::pad_to(size_t size)
|
||||||
|
{
|
||||||
|
const auto curr_len = this->length();
|
||||||
|
|
||||||
|
if (curr_len < size) {
|
||||||
|
this->append((size - curr_len), ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
line_range
|
line_range
|
||||||
line_range::intersection(const line_range& other) const
|
line_range::intersection(const line_range& other) const
|
||||||
{
|
{
|
||||||
|
@ -526,6 +526,23 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename C>
|
||||||
|
attr_line_t& join(const C& container,
|
||||||
|
const string_attr_pair& sap,
|
||||||
|
const char* fill)
|
||||||
|
{
|
||||||
|
bool init = true;
|
||||||
|
for (const auto& elem : container) {
|
||||||
|
if (!init) {
|
||||||
|
this->append(fill);
|
||||||
|
}
|
||||||
|
this->append(std::make_pair(elem, sap));
|
||||||
|
init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
attr_line_t& insert(size_t index,
|
attr_line_t& insert(size_t index,
|
||||||
const attr_line_t& al,
|
const attr_line_t& al,
|
||||||
text_wrap_settings* tws = nullptr);
|
text_wrap_settings* tws = nullptr);
|
||||||
@ -560,6 +577,26 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
attr_line_t& add_header(Args... args)
|
||||||
|
{
|
||||||
|
if (!this->blank()) {
|
||||||
|
this->insert(0, args...);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
attr_line_t& with_default(Args... args)
|
||||||
|
{
|
||||||
|
if (this->blank()) {
|
||||||
|
this->clear();
|
||||||
|
this->append(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
attr_line_t& erase(size_t pos, size_t len = std::string::npos);
|
attr_line_t& erase(size_t pos, size_t len = std::string::npos);
|
||||||
|
|
||||||
attr_line_t& rtrim();
|
attr_line_t& rtrim();
|
||||||
@ -574,6 +611,8 @@ public:
|
|||||||
|
|
||||||
attr_line_t& right_justify(unsigned long width);
|
attr_line_t& right_justify(unsigned long width);
|
||||||
|
|
||||||
|
attr_line_t& pad_to(size_t size);
|
||||||
|
|
||||||
ssize_t length() const
|
ssize_t length() const
|
||||||
{
|
{
|
||||||
size_t retval = this->al_string.length();
|
size_t retval = this->al_string.length();
|
||||||
|
@ -62,15 +62,17 @@ public:
|
|||||||
|
|
||||||
struct source_location {
|
struct source_location {
|
||||||
source_location()
|
source_location()
|
||||||
: sl_source(intern_string::lookup("unknown")), sl_line_number(-1)
|
: sl_source(intern_string::lookup("unknown")), sl_line_number(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
source_location(intern_string_t source, int line)
|
explicit source_location(intern_string_t source, int32_t line = 0)
|
||||||
: sl_source(source), sl_line_number(line){};
|
: sl_source(source), sl_line_number(line)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
intern_string_t sl_source;
|
intern_string_t sl_source;
|
||||||
int sl_line_number;
|
int32_t sl_line_number;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
#include "itertools.hh"
|
||||||
#include "opt_util.hh"
|
#include "opt_util.hh"
|
||||||
|
|
||||||
namespace lnav {
|
namespace lnav {
|
||||||
@ -87,25 +88,56 @@ read_file(const ghc::filesystem::path& path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<void, std::string>
|
||||||
|
write_file(const ghc::filesystem::path& path, const string_fragment& content)
|
||||||
|
{
|
||||||
|
auto tmp_pattern = path;
|
||||||
|
tmp_pattern += ".XXXXXX";
|
||||||
|
|
||||||
|
auto tmp_pair = TRY(open_temp_file(tmp_pattern));
|
||||||
|
auto bytes_written
|
||||||
|
= write(tmp_pair.second.get(), content.data(), content.length());
|
||||||
|
if (bytes_written < 0) {
|
||||||
|
return Err(
|
||||||
|
fmt::format(FMT_STRING("unable to write to temporary file {}: {}"),
|
||||||
|
tmp_pair.first.string(),
|
||||||
|
strerror(errno)));
|
||||||
|
}
|
||||||
|
if (bytes_written != content.length()) {
|
||||||
|
return Err(fmt::format(FMT_STRING("short write to file {}: {} < {}"),
|
||||||
|
tmp_pair.first.string(),
|
||||||
|
bytes_written,
|
||||||
|
content.length()));
|
||||||
|
}
|
||||||
|
std::error_code ec;
|
||||||
|
ghc::filesystem::rename(tmp_pair.first, path, ec);
|
||||||
|
if (ec) {
|
||||||
|
return Err(
|
||||||
|
fmt::format(FMT_STRING("unable to move temporary file {}: {}"),
|
||||||
|
tmp_pair.first.string(),
|
||||||
|
ec.message()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
build_path(const std::vector<ghc::filesystem::path>& paths)
|
build_path(const std::vector<ghc::filesystem::path>& paths)
|
||||||
{
|
{
|
||||||
std::string retval;
|
return paths
|
||||||
|
| lnav::itertools::map(
|
||||||
for (const auto& path : paths) {
|
static_cast<std::string (ghc::filesystem::path::*)() const>(
|
||||||
if (path.empty()) {
|
&ghc::filesystem::path::string))
|
||||||
continue;
|
| lnav::itertools::append(getenv_opt("PATH").value_or(""))
|
||||||
}
|
| lnav::itertools::filter_out(&std::string::empty)
|
||||||
if (!retval.empty()) {
|
| lnav::itertools::fold(
|
||||||
retval += ":";
|
[](const auto& elem, auto& accum) {
|
||||||
}
|
if (!accum.empty()) {
|
||||||
retval += path.string();
|
accum.push_back(':');
|
||||||
}
|
}
|
||||||
auto env_path = getenv_opt("PATH");
|
return accum.append(elem);
|
||||||
if (env_path) {
|
},
|
||||||
retval += ":" + std::string(*env_path);
|
std::string());
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<struct stat, std::string>
|
Result<struct stat, std::string>
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#include "auto_fd.hh"
|
#include "auto_fd.hh"
|
||||||
#include "ghc/filesystem.hpp"
|
#include "ghc/filesystem.hpp"
|
||||||
|
#include "intern_string.hh"
|
||||||
#include "result.h"
|
#include "result.h"
|
||||||
|
|
||||||
namespace lnav {
|
namespace lnav {
|
||||||
@ -69,6 +70,9 @@ Result<std::pair<ghc::filesystem::path, auto_fd>, std::string> open_temp_file(
|
|||||||
|
|
||||||
Result<std::string, std::string> read_file(const ghc::filesystem::path& path);
|
Result<std::string, std::string> read_file(const ghc::filesystem::path& path);
|
||||||
|
|
||||||
|
Result<void, std::string> write_file(const ghc::filesystem::path& path,
|
||||||
|
const string_fragment& content);
|
||||||
|
|
||||||
std::string build_path(const std::vector<ghc::filesystem::path>& paths);
|
std::string build_path(const std::vector<ghc::filesystem::path>& paths);
|
||||||
|
|
||||||
} // namespace filesystem
|
} // namespace filesystem
|
||||||
|
56
src/base/fs_util.tests.cc
Normal file
56
src/base/fs_util.tests.cc
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022, 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 <iostream>
|
||||||
|
|
||||||
|
#include "base/fs_util.hh"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "doctest/doctest.h"
|
||||||
|
|
||||||
|
TEST_CASE("fs_util::build_path")
|
||||||
|
{
|
||||||
|
auto* old_path = getenv("PATH");
|
||||||
|
unsetenv("PATH");
|
||||||
|
|
||||||
|
CHECK("" == lnav::filesystem::build_path({}));
|
||||||
|
|
||||||
|
CHECK("/bin:/usr/bin"
|
||||||
|
== lnav::filesystem::build_path({"", "/bin", "/usr/bin", ""}));
|
||||||
|
setenv("PATH", "/usr/local/bin", 1);
|
||||||
|
CHECK("/bin:/usr/bin:/usr/local/bin"
|
||||||
|
== lnav::filesystem::build_path({"", "/bin", "/usr/bin", ""}));
|
||||||
|
setenv("PATH", "/usr/local/bin:/opt/bin", 1);
|
||||||
|
CHECK("/usr/local/bin:/opt/bin" == lnav::filesystem::build_path({}));
|
||||||
|
CHECK("/bin:/usr/bin:/usr/local/bin:/opt/bin"
|
||||||
|
== lnav::filesystem::build_path({"", "/bin", "/usr/bin", ""}));
|
||||||
|
if (old_path != nullptr) {
|
||||||
|
setenv("PATH", old_path, 1);
|
||||||
|
}
|
||||||
|
}
|
@ -64,4 +64,30 @@ struct noop_func {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace lnav {
|
||||||
|
namespace func {
|
||||||
|
|
||||||
|
template<typename Fn,
|
||||||
|
typename... Args,
|
||||||
|
std::enable_if_t<std::is_member_pointer<std::decay_t<Fn>>{}, int> = 0>
|
||||||
|
constexpr decltype(auto)
|
||||||
|
invoke(Fn&& f, Args&&... args) noexcept(
|
||||||
|
noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
|
||||||
|
{
|
||||||
|
return std::mem_fn(f)(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Fn,
|
||||||
|
typename... Args,
|
||||||
|
std::enable_if_t<!std::is_member_pointer<std::decay_t<Fn>>{}, int> = 0>
|
||||||
|
constexpr decltype(auto)
|
||||||
|
invoke(Fn&& f, Args&&... args) noexcept(
|
||||||
|
noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
|
||||||
|
{
|
||||||
|
return std::forward<Fn>(f)(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace func
|
||||||
|
} // namespace lnav
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -185,3 +185,20 @@ string_fragment::consume_n(int amount) const
|
|||||||
this->sf_end,
|
this->sf_end,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<string_fragment>
|
||||||
|
string_fragment::split_lines() const
|
||||||
|
{
|
||||||
|
std::vector<string_fragment> retval;
|
||||||
|
int start = this->sf_begin;
|
||||||
|
|
||||||
|
for (auto index = start; index < this->sf_end; index++) {
|
||||||
|
if ((*this)[index] == '\n') {
|
||||||
|
retval.emplace_back(this->sf_string, start, index + 1);
|
||||||
|
start = index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval.emplace_back(this->sf_string, start, this->sf_end);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#define intern_string_hh
|
#define intern_string_hh
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -239,13 +240,12 @@ struct string_fragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<string_fragment> split_lines() const;
|
||||||
|
|
||||||
struct tag1 {
|
struct tag1 {
|
||||||
const char t_value;
|
const char t_value;
|
||||||
|
|
||||||
bool operator()(char ch) const
|
bool operator()(char ch) const { return this->t_value == ch; }
|
||||||
{
|
|
||||||
return this->t_value == ch;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct quoted_string_body {
|
struct quoted_string_body {
|
||||||
@ -367,20 +367,11 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const intern_string* unwrap() const
|
const intern_string* unwrap() const { return this->ist_interned_string; }
|
||||||
{
|
|
||||||
return this->ist_interned_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear()
|
void clear() { this->ist_interned_string = nullptr; };
|
||||||
{
|
|
||||||
this->ist_interned_string = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const { return this->ist_interned_string == nullptr; }
|
||||||
{
|
|
||||||
return this->ist_interned_string == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* get() const
|
const char* get() const
|
||||||
{
|
{
|
||||||
@ -390,15 +381,11 @@ public:
|
|||||||
return this->ist_interned_string->get();
|
return this->ist_interned_string->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator begin() const
|
const char* c_str() const { return this->get(); }
|
||||||
{
|
|
||||||
return this->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator end() const
|
iterator begin() const { return this->get(); }
|
||||||
{
|
|
||||||
return this->get() + this->size();
|
iterator end() const { return this->get() + this->size(); }
|
||||||
}
|
|
||||||
|
|
||||||
size_t size() const
|
size_t size() const
|
||||||
{
|
{
|
||||||
@ -456,6 +443,11 @@ public:
|
|||||||
return strcmp(this->get(), rhs) != 0;
|
return strcmp(this->get(), rhs) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool case_lt(const intern_string_t& lhs, const intern_string_t& rhs)
|
||||||
|
{
|
||||||
|
return strnatcasecmp(lhs.size(), lhs.get(), rhs.size(), rhs.get()) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const intern_string* ist_interned_string;
|
const intern_string* ist_interned_string;
|
||||||
};
|
};
|
||||||
|
@ -35,6 +35,29 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "doctest/doctest.h"
|
#include "doctest/doctest.h"
|
||||||
|
|
||||||
|
TEST_CASE("split_lines")
|
||||||
|
{
|
||||||
|
std::string in1 = "Hello, World!";
|
||||||
|
std::string in2 = "Hello, World!\nGoodbye, World!";
|
||||||
|
|
||||||
|
{
|
||||||
|
auto sf = string_fragment(in1);
|
||||||
|
auto split = sf.split_lines();
|
||||||
|
|
||||||
|
CHECK(1 == split.size());
|
||||||
|
CHECK(in1 == split[0].to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto sf = string_fragment(in2);
|
||||||
|
auto split = sf.split_lines();
|
||||||
|
|
||||||
|
CHECK(2 == split.size());
|
||||||
|
CHECK("Hello, World!\n" == split[0].to_string());
|
||||||
|
CHECK("Goodbye, World!" == split[1].to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("consume")
|
TEST_CASE("consume")
|
||||||
{
|
{
|
||||||
auto is_eq = string_fragment::tag1{'='};
|
auto is_eq = string_fragment::tag1{'='};
|
||||||
|
400
src/base/itertools.hh
Normal file
400
src/base/itertools.hh
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
|
||||||
|
#ifndef lnav_itertools_hh
|
||||||
|
#define lnav_itertools_hh
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "func_util.hh"
|
||||||
|
#include "optional.hpp"
|
||||||
|
|
||||||
|
namespace lnav {
|
||||||
|
namespace itertools {
|
||||||
|
|
||||||
|
struct empty {};
|
||||||
|
|
||||||
|
struct not_empty {};
|
||||||
|
|
||||||
|
struct full {
|
||||||
|
size_t f_max_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct unwrap_or {
|
||||||
|
T uo_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename P>
|
||||||
|
struct find_if {
|
||||||
|
P fi_predicate;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct find {
|
||||||
|
T f_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
struct filter_in {
|
||||||
|
F f_func;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
struct filter_out {
|
||||||
|
F f_func;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename C>
|
||||||
|
struct sort_by {
|
||||||
|
C sb_cmp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sorted {};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
struct mapper {
|
||||||
|
F m_func;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename R, typename T>
|
||||||
|
struct folder {
|
||||||
|
R f_func;
|
||||||
|
T f_init;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct prepend {
|
||||||
|
T p_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct append {
|
||||||
|
T p_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline details::unwrap_or<T>
|
||||||
|
unwrap_or(T value)
|
||||||
|
{
|
||||||
|
return details::unwrap_or<T>{
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename P>
|
||||||
|
inline details::find_if<P>
|
||||||
|
find_if(P predicate)
|
||||||
|
{
|
||||||
|
return details::find_if<P>{
|
||||||
|
predicate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline details::find<T>
|
||||||
|
find(T value)
|
||||||
|
{
|
||||||
|
return details::find<T>{
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
inline details::filter_in<F>
|
||||||
|
filter_in(F func)
|
||||||
|
{
|
||||||
|
return details::filter_in<F>{
|
||||||
|
func,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
inline details::filter_out<F>
|
||||||
|
filter_out(F func)
|
||||||
|
{
|
||||||
|
return details::filter_out<F>{
|
||||||
|
func,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline details::prepend<T>
|
||||||
|
prepend(T value)
|
||||||
|
{
|
||||||
|
return details::prepend<T>{
|
||||||
|
std::move(value),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline details::append<T>
|
||||||
|
append(T value)
|
||||||
|
{
|
||||||
|
return details::append<T>{
|
||||||
|
std::move(value),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C>
|
||||||
|
inline details::sort_by<C>
|
||||||
|
sort_with(C cmp)
|
||||||
|
{
|
||||||
|
return details::sort_by<C>{cmp};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C, typename T>
|
||||||
|
inline auto
|
||||||
|
sort_by(T C::*m)
|
||||||
|
{
|
||||||
|
return sort_with(
|
||||||
|
[m](const C& lhs, const C& rhs) { return lhs.*m < rhs.*m; });
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
inline details::mapper<F>
|
||||||
|
map(F func)
|
||||||
|
{
|
||||||
|
return details::mapper<F>{func};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename T>
|
||||||
|
inline details::folder<R, T>
|
||||||
|
fold(R func, T init)
|
||||||
|
{
|
||||||
|
return details::folder<R, T>{func, init};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline details::sorted
|
||||||
|
sorted()
|
||||||
|
{
|
||||||
|
return details::sorted{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
T
|
||||||
|
chain(const T& value1, const Args&... args)
|
||||||
|
{
|
||||||
|
T retval;
|
||||||
|
|
||||||
|
for (const auto& arg : {value1, args...}) {
|
||||||
|
for (const auto& elem : arg) {
|
||||||
|
retval.emplace_back(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace itertools
|
||||||
|
} // namespace lnav
|
||||||
|
|
||||||
|
template<typename C, typename P>
|
||||||
|
nonstd::optional<typename C::value_type>
|
||||||
|
operator|(const C& in, const lnav::itertools::details::find_if<P>& finder)
|
||||||
|
{
|
||||||
|
for (const auto& elem : in) {
|
||||||
|
if (lnav::func::invoke(finder.fi_predicate, elem)) {
|
||||||
|
return nonstd::make_optional(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonstd::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C, typename T>
|
||||||
|
nonstd::optional<size_t>
|
||||||
|
operator|(const C& in, const lnav::itertools::details::find<T>& finder)
|
||||||
|
{
|
||||||
|
size_t retval = 0;
|
||||||
|
for (const auto& elem : in) {
|
||||||
|
if (elem == finder.f_value) {
|
||||||
|
return nonstd::make_optional(retval);
|
||||||
|
}
|
||||||
|
retval += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonstd::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C, typename F>
|
||||||
|
C
|
||||||
|
operator|(const C& in, const lnav::itertools::details::filter_in<F>& filterer)
|
||||||
|
{
|
||||||
|
C retval;
|
||||||
|
|
||||||
|
for (const auto& elem : in) {
|
||||||
|
if (lnav::func::invoke(filterer.f_func, elem)) {
|
||||||
|
retval.emplace_back(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C, typename F>
|
||||||
|
C
|
||||||
|
operator|(const C& in, const lnav::itertools::details::filter_out<F>& filterer)
|
||||||
|
{
|
||||||
|
C retval;
|
||||||
|
|
||||||
|
for (const auto& elem : in) {
|
||||||
|
if (!lnav::func::invoke(filterer.f_func, elem)) {
|
||||||
|
retval.emplace_back(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C, typename T>
|
||||||
|
C
|
||||||
|
operator|(C in, const lnav::itertools::details::prepend<T>& prepender)
|
||||||
|
{
|
||||||
|
in.emplace(in.begin(), prepender.p_value);
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C, typename T>
|
||||||
|
C
|
||||||
|
operator|(C in, const lnav::itertools::details::append<T>& appender)
|
||||||
|
{
|
||||||
|
in.emplace_back(appender.p_value);
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename C, typename R, typename T>
|
||||||
|
T
|
||||||
|
operator|(const C& in, const lnav::itertools::details::folder<R, T>& folder)
|
||||||
|
{
|
||||||
|
auto accum = folder.f_init;
|
||||||
|
|
||||||
|
for (const auto& elem : in) {
|
||||||
|
accum = folder.f_func(elem, accum);
|
||||||
|
}
|
||||||
|
|
||||||
|
return accum;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename C>
|
||||||
|
T
|
||||||
|
operator|(T in, const lnav::itertools::details::sort_by<C>& sorter)
|
||||||
|
{
|
||||||
|
std::sort(in.begin(), in.end(), sorter.sb_cmp);
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T
|
||||||
|
operator|(T in, const lnav::itertools::details::sorted& sorter)
|
||||||
|
{
|
||||||
|
std::sort(in.begin(), in.end());
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename F>
|
||||||
|
auto
|
||||||
|
operator|(const T& in, const lnav::itertools::details::mapper<F>& mapper)
|
||||||
|
-> std::vector<decltype(mapper.m_func(typename T::value_type{}))>
|
||||||
|
{
|
||||||
|
using return_type
|
||||||
|
= std::vector<decltype(mapper.m_func(typename T::value_type{}))>;
|
||||||
|
return_type retval;
|
||||||
|
|
||||||
|
retval.reserve(in.size());
|
||||||
|
std::transform(
|
||||||
|
in.begin(), in.end(), std::back_inserter(retval), mapper.m_func);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename F>
|
||||||
|
auto
|
||||||
|
operator|(const std::vector<std::shared_ptr<T>>& in,
|
||||||
|
const lnav::itertools::details::mapper<F>& mapper)
|
||||||
|
-> std::vector<typename std::remove_const_t<decltype(((*in.front())
|
||||||
|
.*mapper.m_func)())>>
|
||||||
|
{
|
||||||
|
using return_type = std::vector<typename std::remove_const_t<decltype((
|
||||||
|
(*in.front()).*mapper.m_func)())>>;
|
||||||
|
return_type retval;
|
||||||
|
|
||||||
|
retval.reserve(in.size());
|
||||||
|
std::transform(
|
||||||
|
in.begin(),
|
||||||
|
in.end(),
|
||||||
|
std::back_inserter(retval),
|
||||||
|
[&mapper](const auto& elem) { return ((*elem).*mapper.m_func)(); });
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename F>
|
||||||
|
auto
|
||||||
|
operator|(const std::vector<std::shared_ptr<T>>& in,
|
||||||
|
const lnav::itertools::details::mapper<F>& mapper)
|
||||||
|
-> std::vector<typename std::remove_reference_t<
|
||||||
|
typename std::remove_const_t<decltype(((*in.front()).*mapper.m_func))>>>
|
||||||
|
{
|
||||||
|
using return_type = std::vector<
|
||||||
|
typename std::remove_reference_t<typename std::remove_const_t<decltype((
|
||||||
|
(*in.front()).*mapper.m_func))>>>;
|
||||||
|
return_type retval;
|
||||||
|
|
||||||
|
retval.reserve(in.size());
|
||||||
|
for (const auto& elem : in) {
|
||||||
|
retval.template emplace_back(((*elem).*mapper.m_func));
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename F>
|
||||||
|
auto
|
||||||
|
operator|(nonstd::optional<T> in,
|
||||||
|
const lnav::itertools::details::mapper<F>& mapper)
|
||||||
|
-> nonstd::optional<typename std::remove_reference_t<
|
||||||
|
typename std::remove_const_t<decltype(((in.value()).*mapper.m_func))>>>
|
||||||
|
{
|
||||||
|
if (!in) {
|
||||||
|
return nonstd::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonstd::make_optional((in.value()).*mapper.m_func);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T
|
||||||
|
operator|(nonstd::optional<T> in,
|
||||||
|
const lnav::itertools::details::unwrap_or<T>& unwrapper)
|
||||||
|
{
|
||||||
|
return in.value_or(unwrapper.uo_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename F>
|
||||||
|
auto
|
||||||
|
operator|(const T& in, const lnav::itertools::details::mapper<F>& mapper)
|
||||||
|
-> std::vector<std::remove_const_t<decltype(((typename T::value_type{})
|
||||||
|
.*mapper.m_func)())>>
|
||||||
|
{
|
||||||
|
using return_type = std::vector<std::remove_const_t<decltype((
|
||||||
|
(typename T::value_type{}).*mapper.m_func)())>>;
|
||||||
|
return_type retval;
|
||||||
|
|
||||||
|
retval.reserve(in.size());
|
||||||
|
for (const auto& elem : in) {
|
||||||
|
retval.template emplace_back((elem.*mapper.m_func)());
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -33,11 +33,25 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "fmt/color.h"
|
#include "fmt/color.h"
|
||||||
|
#include "itertools.hh"
|
||||||
|
#include "log_level_enum.hh"
|
||||||
#include "view_curses.hh"
|
#include "view_curses.hh"
|
||||||
|
|
||||||
|
using namespace lnav::roles::literals;
|
||||||
|
|
||||||
namespace lnav {
|
namespace lnav {
|
||||||
namespace console {
|
namespace console {
|
||||||
|
|
||||||
|
user_message
|
||||||
|
user_message::raw(const attr_line_t& al)
|
||||||
|
{
|
||||||
|
user_message retval;
|
||||||
|
|
||||||
|
retval.um_level = level::raw;
|
||||||
|
retval.um_message.append(al);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
user_message
|
user_message
|
||||||
user_message::error(const attr_line_t& al)
|
user_message::error(const attr_line_t& al)
|
||||||
{
|
{
|
||||||
@ -81,10 +95,17 @@ user_message::warning(const attr_line_t& al)
|
|||||||
attr_line_t
|
attr_line_t
|
||||||
user_message::to_attr_line(std::set<render_flags> flags) const
|
user_message::to_attr_line(std::set<render_flags> flags) const
|
||||||
{
|
{
|
||||||
|
auto indent = 1;
|
||||||
attr_line_t retval;
|
attr_line_t retval;
|
||||||
|
|
||||||
|
if (this->um_level == level::warning) {
|
||||||
|
indent = 3;
|
||||||
|
}
|
||||||
|
|
||||||
if (flags.count(render_flags::prefix)) {
|
if (flags.count(render_flags::prefix)) {
|
||||||
switch (this->um_level) {
|
switch (this->um_level) {
|
||||||
|
case level::raw:
|
||||||
|
break;
|
||||||
case level::ok:
|
case level::ok:
|
||||||
retval.append(lnav::roles::ok("\u2714 "));
|
retval.append(lnav::roles::ok("\u2714 "));
|
||||||
break;
|
break;
|
||||||
@ -105,13 +126,18 @@ user_message::to_attr_line(std::set<render_flags> flags) const
|
|||||||
if (!this->um_reason.empty()) {
|
if (!this->um_reason.empty()) {
|
||||||
bool first_line = true;
|
bool first_line = true;
|
||||||
for (const auto& line : this->um_reason.split_lines()) {
|
for (const auto& line : this->um_reason.split_lines()) {
|
||||||
|
auto role = this->um_level == level::error ? role_t::VCR_ERROR
|
||||||
|
: role_t::VCR_WARNING;
|
||||||
attr_line_t prefix;
|
attr_line_t prefix;
|
||||||
|
|
||||||
if (first_line) {
|
if (first_line) {
|
||||||
prefix.append(lnav::roles::error(" reason")).append(": ");
|
prefix.append(indent, ' ')
|
||||||
|
.append("reason", VC_ROLE.value(role))
|
||||||
|
.append(": ");
|
||||||
first_line = false;
|
first_line = false;
|
||||||
} else {
|
} else {
|
||||||
prefix.append(lnav::roles::error(" | "));
|
prefix.append(" | ", VC_ROLE.value(role))
|
||||||
|
.append(indent, ' ');
|
||||||
}
|
}
|
||||||
retval.append(prefix).append(line).append("\n");
|
retval.append(prefix).append(line).append("\n");
|
||||||
}
|
}
|
||||||
@ -120,35 +146,34 @@ user_message::to_attr_line(std::set<render_flags> flags) const
|
|||||||
for (const auto& snip : this->um_snippets) {
|
for (const auto& snip : this->um_snippets) {
|
||||||
attr_line_t header;
|
attr_line_t header;
|
||||||
|
|
||||||
header.append(lnav::roles::comment(" --> "))
|
header.append(" --> "_comment)
|
||||||
.append(lnav::roles::file(snip.s_source));
|
.append(lnav::roles::file(snip.s_location.sl_source.get()));
|
||||||
if (snip.s_line > 0) {
|
if (snip.s_location.sl_line_number > 0) {
|
||||||
header.append(":").append(FMT_STRING("{}"), snip.s_line);
|
header.append(":").append(FMT_STRING("{}"),
|
||||||
if (snip.s_column > 0) {
|
snip.s_location.sl_line_number);
|
||||||
header.append(":").append(FMT_STRING("{}"), snip.s_column);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
retval.append(header).append("\n");
|
retval.append(header).append("\n");
|
||||||
if (!snip.s_content.blank()) {
|
if (!snip.s_content.blank()) {
|
||||||
for (const auto& line : snip.s_content.split_lines()) {
|
for (const auto& line : snip.s_content.split_lines()) {
|
||||||
retval.append(lnav::roles::comment(" | "))
|
retval.append(" | "_comment).append(line).append("\n");
|
||||||
.append(line)
|
|
||||||
.append("\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this->um_notes.empty()) {
|
if (!this->um_notes.empty()) {
|
||||||
bool first_line = true;
|
|
||||||
for (const auto& note : this->um_notes) {
|
for (const auto& note : this->um_notes) {
|
||||||
|
bool first_line = true;
|
||||||
for (const auto& line : note.split_lines()) {
|
for (const auto& line : note.split_lines()) {
|
||||||
attr_line_t prefix;
|
attr_line_t prefix;
|
||||||
|
|
||||||
if (first_line) {
|
if (first_line) {
|
||||||
prefix.append(lnav::roles::comment(" = note")).append(": ");
|
prefix.append(" ="_comment)
|
||||||
|
.append(indent, ' ')
|
||||||
|
.append("note"_comment)
|
||||||
|
.append(": ");
|
||||||
first_line = false;
|
first_line = false;
|
||||||
} else {
|
} else {
|
||||||
prefix.append(" ");
|
prefix.append(" ").append(indent, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
retval.append(prefix).append(line).append("\n");
|
retval.append(prefix).append(line).append("\n");
|
||||||
@ -161,7 +186,10 @@ user_message::to_attr_line(std::set<render_flags> flags) const
|
|||||||
attr_line_t prefix;
|
attr_line_t prefix;
|
||||||
|
|
||||||
if (first_line) {
|
if (first_line) {
|
||||||
prefix.append(lnav::roles::comment(" = help")).append(": ");
|
prefix.append(" ="_comment)
|
||||||
|
.append(indent, ' ')
|
||||||
|
.append("help"_comment)
|
||||||
|
.append(": ");
|
||||||
first_line = false;
|
first_line = false;
|
||||||
} else {
|
} else {
|
||||||
prefix.append(" ");
|
prefix.append(" ");
|
||||||
@ -174,85 +202,152 @@ user_message::to_attr_line(std::set<render_flags> flags) const
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt::terminal_color
|
||||||
|
curses_color_to_terminal_color(int curses_color)
|
||||||
|
{
|
||||||
|
switch (curses_color) {
|
||||||
|
case COLOR_BLACK:
|
||||||
|
return fmt::terminal_color::black;
|
||||||
|
case COLOR_CYAN:
|
||||||
|
return fmt::terminal_color::cyan;
|
||||||
|
case COLOR_WHITE:
|
||||||
|
return fmt::terminal_color::white;
|
||||||
|
case COLOR_MAGENTA:
|
||||||
|
return fmt::terminal_color::magenta;
|
||||||
|
case COLOR_BLUE:
|
||||||
|
return fmt::terminal_color::blue;
|
||||||
|
case COLOR_YELLOW:
|
||||||
|
return fmt::terminal_color::yellow;
|
||||||
|
case COLOR_GREEN:
|
||||||
|
return fmt::terminal_color::green;
|
||||||
|
case COLOR_RED:
|
||||||
|
return fmt::terminal_color::red;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure(false);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
println(FILE* file, const attr_line_t& al)
|
println(FILE* file, const attr_line_t& al)
|
||||||
{
|
{
|
||||||
const auto& str = al.get_string();
|
const auto& str = al.get_string();
|
||||||
|
|
||||||
if (!isatty(fileno(file))) {
|
if (getenv("NO_COLOR") != nullptr
|
||||||
|
|| (!isatty(fileno(file)) && getenv("YES_COLOR") == nullptr))
|
||||||
|
{
|
||||||
fmt::print(file, "{}\n", str);
|
fmt::print(file, "{}\n", str);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string_attrs_t style_attrs;
|
std::set<int> points = {0, (int) al.length()};
|
||||||
|
|
||||||
for (const auto& sa : al.get_attrs()) {
|
for (const auto& attr : al.get_attrs()) {
|
||||||
if (sa.sa_type != &VC_ROLE) {
|
if (!attr.sa_range.is_valid()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
points.insert(attr.sa_range.lr_start);
|
||||||
style_attrs.emplace_back(sa);
|
if (attr.sa_range.lr_end > 0) {
|
||||||
}
|
points.insert(attr.sa_range.lr_end);
|
||||||
|
|
||||||
std::sort(style_attrs.begin(), style_attrs.end(), [](auto lhs, auto rhs) {
|
|
||||||
return lhs.sa_range < rhs.sa_range;
|
|
||||||
});
|
|
||||||
|
|
||||||
auto start = size_t{0};
|
|
||||||
for (const auto& attr : style_attrs) {
|
|
||||||
fmt::print(
|
|
||||||
file, "{}", str.substr(start, attr.sa_range.lr_start - start));
|
|
||||||
if (attr.sa_type == &VC_ROLE) {
|
|
||||||
auto saw = string_attr_wrapper<role_t>(&attr);
|
|
||||||
auto role = saw.get();
|
|
||||||
auto line_style = fmt::text_style();
|
|
||||||
|
|
||||||
switch (role) {
|
|
||||||
case role_t::VCR_ERROR:
|
|
||||||
line_style = fmt::fg(fmt::terminal_color::red);
|
|
||||||
break;
|
|
||||||
case role_t::VCR_WARNING:
|
|
||||||
line_style = fmt::fg(fmt::terminal_color::yellow);
|
|
||||||
break;
|
|
||||||
case role_t::VCR_COMMENT:
|
|
||||||
line_style = fmt::fg(fmt::terminal_color::cyan);
|
|
||||||
break;
|
|
||||||
case role_t::VCR_OK:
|
|
||||||
line_style = fmt::emphasis::bold
|
|
||||||
| fmt::fg(fmt::terminal_color::red);
|
|
||||||
break;
|
|
||||||
case role_t::VCR_STATUS:
|
|
||||||
line_style = fmt::emphasis::bold
|
|
||||||
| fmt::fg(fmt::terminal_color::magenta);
|
|
||||||
break;
|
|
||||||
case role_t::VCR_VARIABLE:
|
|
||||||
line_style = fmt::emphasis::underline;
|
|
||||||
break;
|
|
||||||
case role_t::VCR_SYMBOL:
|
|
||||||
case role_t::VCR_NUMBER:
|
|
||||||
case role_t::VCR_FILE:
|
|
||||||
line_style = fmt::emphasis::bold;
|
|
||||||
break;
|
|
||||||
case role_t::VCR_H1:
|
|
||||||
case role_t::VCR_H2:
|
|
||||||
case role_t::VCR_H3:
|
|
||||||
case role_t::VCR_H4:
|
|
||||||
case role_t::VCR_H5:
|
|
||||||
case role_t::VCR_H6:
|
|
||||||
line_style = fmt::emphasis::underline;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fmt::print(
|
|
||||||
file,
|
|
||||||
line_style,
|
|
||||||
"{}",
|
|
||||||
str.substr(attr.sa_range.lr_start, attr.sa_range.length()));
|
|
||||||
}
|
}
|
||||||
start = attr.sa_range.lr_end;
|
|
||||||
}
|
}
|
||||||
fmt::print(file, "{}\n", str.substr(start));
|
|
||||||
|
nonstd::optional<int> last_point;
|
||||||
|
for (const auto& point : points) {
|
||||||
|
if (last_point) {
|
||||||
|
auto line_style = fmt::text_style{};
|
||||||
|
auto fg_style = fmt::text_style{};
|
||||||
|
auto start = last_point.value();
|
||||||
|
|
||||||
|
for (const auto& attr : al.get_attrs()) {
|
||||||
|
if (!attr.sa_range.contains(start)
|
||||||
|
&& !attr.sa_range.contains(point - 1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attr.sa_type == &VC_BACKGROUND) {
|
||||||
|
auto saw = string_attr_wrapper<int64_t>(&attr);
|
||||||
|
auto color = saw.get();
|
||||||
|
|
||||||
|
if (color >= 0) {
|
||||||
|
line_style
|
||||||
|
|= fmt::bg(curses_color_to_terminal_color(color));
|
||||||
|
}
|
||||||
|
} else if (attr.sa_type == &VC_FOREGROUND) {
|
||||||
|
auto saw = string_attr_wrapper<int64_t>(&attr);
|
||||||
|
auto color = saw.get();
|
||||||
|
|
||||||
|
if (color >= 0) {
|
||||||
|
fg_style
|
||||||
|
= fmt::fg(curses_color_to_terminal_color(color));
|
||||||
|
}
|
||||||
|
} else if (attr.sa_type == &VC_ROLE) {
|
||||||
|
auto saw = string_attr_wrapper<role_t>(&attr);
|
||||||
|
auto role = saw.get();
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case role_t::VCR_ERROR:
|
||||||
|
line_style |= fmt::fg(fmt::terminal_color::red);
|
||||||
|
break;
|
||||||
|
case role_t::VCR_WARNING:
|
||||||
|
line_style |= fmt::fg(fmt::terminal_color::yellow);
|
||||||
|
break;
|
||||||
|
case role_t::VCR_COMMENT:
|
||||||
|
line_style |= fmt::fg(fmt::terminal_color::cyan);
|
||||||
|
break;
|
||||||
|
case role_t::VCR_OK:
|
||||||
|
line_style |= fmt::emphasis::bold
|
||||||
|
| fmt::fg(fmt::terminal_color::green);
|
||||||
|
break;
|
||||||
|
case role_t::VCR_STATUS:
|
||||||
|
line_style |= fmt::emphasis::bold
|
||||||
|
| fmt::fg(fmt::terminal_color::magenta);
|
||||||
|
break;
|
||||||
|
case role_t::VCR_KEYWORD:
|
||||||
|
line_style |= fmt::emphasis::bold
|
||||||
|
| fmt::fg(fmt::terminal_color::blue);
|
||||||
|
break;
|
||||||
|
case role_t::VCR_VARIABLE:
|
||||||
|
line_style |= fmt::emphasis::underline;
|
||||||
|
break;
|
||||||
|
case role_t::VCR_SYMBOL:
|
||||||
|
case role_t::VCR_NUMBER:
|
||||||
|
case role_t::VCR_FILE:
|
||||||
|
line_style |= fmt::emphasis::bold;
|
||||||
|
break;
|
||||||
|
case role_t::VCR_H1:
|
||||||
|
line_style |= fmt::emphasis::bold
|
||||||
|
| fmt::fg(fmt::terminal_color::magenta);
|
||||||
|
break;
|
||||||
|
case role_t::VCR_H2:
|
||||||
|
line_style |= fmt::emphasis::bold;
|
||||||
|
break;
|
||||||
|
case role_t::VCR_H3:
|
||||||
|
case role_t::VCR_H4:
|
||||||
|
case role_t::VCR_H5:
|
||||||
|
case role_t::VCR_H6:
|
||||||
|
line_style |= fmt::emphasis::underline;
|
||||||
|
break;
|
||||||
|
case role_t::VCR_LIST_GLYPH:
|
||||||
|
line_style |= fmt::fg(fmt::terminal_color::yellow);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line_style.has_foreground() && fg_style.has_foreground()) {
|
||||||
|
line_style |= fg_style;
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print(file,
|
||||||
|
line_style,
|
||||||
|
FMT_STRING("{}"),
|
||||||
|
str.substr(start, point - start));
|
||||||
|
}
|
||||||
|
last_point = point;
|
||||||
|
}
|
||||||
|
fmt::print(file, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/attr_line.hh"
|
#include "base/attr_line.hh"
|
||||||
|
#include "base/file_range.hh"
|
||||||
|
|
||||||
namespace lnav {
|
namespace lnav {
|
||||||
namespace console {
|
namespace console {
|
||||||
@ -41,41 +42,45 @@ namespace console {
|
|||||||
void println(FILE* file, const attr_line_t& al);
|
void println(FILE* file, const attr_line_t& al);
|
||||||
|
|
||||||
struct snippet {
|
struct snippet {
|
||||||
static snippet from(std::string src, const attr_line_t& content)
|
static snippet from(intern_string_t src, const attr_line_t& content)
|
||||||
{
|
{
|
||||||
snippet retval;
|
snippet retval;
|
||||||
|
|
||||||
retval.s_source = std::move(src);
|
retval.s_location.sl_source = src;
|
||||||
|
retval.s_content = content;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static snippet from(source_location loc, const attr_line_t& content)
|
||||||
|
{
|
||||||
|
snippet retval;
|
||||||
|
|
||||||
|
retval.s_location = loc;
|
||||||
retval.s_content = content;
|
retval.s_content = content;
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
snippet& with_line(int32_t line)
|
snippet& with_line(int32_t line)
|
||||||
{
|
{
|
||||||
this->s_line = line;
|
this->s_location.sl_line_number = line;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
snippet& with_column(int32_t column)
|
source_location s_location;
|
||||||
{
|
|
||||||
this->s_column = column;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string s_source;
|
|
||||||
int32_t s_line{0};
|
|
||||||
int32_t s_column{0};
|
|
||||||
attr_line_t s_content;
|
attr_line_t s_content;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct user_message {
|
struct user_message {
|
||||||
enum class level {
|
enum class level {
|
||||||
|
raw,
|
||||||
ok,
|
ok,
|
||||||
info,
|
info,
|
||||||
warning,
|
warning,
|
||||||
error,
|
error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static user_message raw(const attr_line_t& al);
|
||||||
|
|
||||||
static user_message error(const attr_line_t& al);
|
static user_message error(const attr_line_t& al);
|
||||||
|
|
||||||
static user_message warning(const attr_line_t& al);
|
static user_message warning(const attr_line_t& al);
|
||||||
@ -91,6 +96,11 @@ struct user_message {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user_message& with_reason(const user_message& um)
|
||||||
|
{
|
||||||
|
return this->with_reason(um.to_attr_line({}));
|
||||||
|
}
|
||||||
|
|
||||||
user_message& with_errno_reason()
|
user_message& with_errno_reason()
|
||||||
{
|
{
|
||||||
this->um_reason = strerror(errno);
|
this->um_reason = strerror(errno);
|
||||||
@ -113,15 +123,21 @@ struct user_message {
|
|||||||
|
|
||||||
user_message& with_note(const attr_line_t& al)
|
user_message& with_note(const attr_line_t& al)
|
||||||
{
|
{
|
||||||
this->um_notes.emplace_back(al);
|
if (!al.blank()) {
|
||||||
|
this->um_notes.emplace_back(al);
|
||||||
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
user_message& with_help(const attr_line_t& al)
|
user_message& with_help(const attr_line_t& al)
|
||||||
{
|
{
|
||||||
this->um_help = al;
|
if (al.blank()) {
|
||||||
this->um_help.rtrim();
|
this->um_help.clear();
|
||||||
|
} else {
|
||||||
|
this->um_help = al;
|
||||||
|
this->um_help.rtrim();
|
||||||
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
65
src/base/log_level_enum.hh
Normal file
65
src/base/log_level_enum.hh
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2020, Timothy Stack
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef lnav_log_level_enum_hh
|
||||||
|
#define lnav_log_level_enum_hh
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The logging level identifiers for a line(s).
|
||||||
|
*/
|
||||||
|
enum log_level_t : int {
|
||||||
|
LEVEL_UNKNOWN,
|
||||||
|
LEVEL_TRACE,
|
||||||
|
LEVEL_DEBUG5,
|
||||||
|
LEVEL_DEBUG4,
|
||||||
|
LEVEL_DEBUG3,
|
||||||
|
LEVEL_DEBUG2,
|
||||||
|
LEVEL_DEBUG,
|
||||||
|
LEVEL_INFO,
|
||||||
|
LEVEL_STATS,
|
||||||
|
LEVEL_NOTICE,
|
||||||
|
LEVEL_WARNING,
|
||||||
|
LEVEL_ERROR,
|
||||||
|
LEVEL_CRITICAL,
|
||||||
|
LEVEL_FATAL,
|
||||||
|
LEVEL_INVALID,
|
||||||
|
|
||||||
|
LEVEL__MAX,
|
||||||
|
|
||||||
|
LEVEL_IGNORE = 0x10, /*< Ignore */
|
||||||
|
LEVEL_TIME_SKEW = 0x20, /*< Received after timestamp. */
|
||||||
|
LEVEL_MARK = 0x40, /*< Bookmarked line. */
|
||||||
|
LEVEL_CONTINUED = 0x80, /*< Continuation of multiline entry. */
|
||||||
|
|
||||||
|
/** Mask of flags for the level field. */
|
||||||
|
LEVEL__FLAGS
|
||||||
|
= (LEVEL_IGNORE | LEVEL_TIME_SKEW | LEVEL_MARK | LEVEL_CONTINUED)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -38,6 +38,7 @@ string_attr_type<const intern_string_t> SA_FORMAT("format");
|
|||||||
string_attr_type<void> SA_REMOVED("removed");
|
string_attr_type<void> SA_REMOVED("removed");
|
||||||
string_attr_type<std::string> SA_INVALID("invalid");
|
string_attr_type<std::string> SA_INVALID("invalid");
|
||||||
string_attr_type<std::string> SA_ERROR("error");
|
string_attr_type<std::string> SA_ERROR("error");
|
||||||
|
string_attr_type<int64_t> SA_LEVEL("level");
|
||||||
|
|
||||||
string_attr_type<role_t> VC_ROLE("role");
|
string_attr_type<role_t> VC_ROLE("role");
|
||||||
string_attr_type<role_t> VC_ROLE_FG("role-fg");
|
string_attr_type<role_t> VC_ROLE_FG("role-fg");
|
||||||
|
@ -109,6 +109,8 @@ enum class role_t : int32_t {
|
|||||||
VCR_H5,
|
VCR_H5,
|
||||||
VCR_H6,
|
VCR_H6,
|
||||||
|
|
||||||
|
VCR_LIST_GLYPH,
|
||||||
|
|
||||||
VCR__MAX
|
VCR__MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -162,6 +164,7 @@ extern string_attr_type<const intern_string_t> SA_FORMAT;
|
|||||||
extern string_attr_type<void> SA_REMOVED;
|
extern string_attr_type<void> SA_REMOVED;
|
||||||
extern string_attr_type<std::string> SA_INVALID;
|
extern string_attr_type<std::string> SA_INVALID;
|
||||||
extern string_attr_type<std::string> SA_ERROR;
|
extern string_attr_type<std::string> SA_ERROR;
|
||||||
|
extern string_attr_type<int64_t> SA_LEVEL;
|
||||||
|
|
||||||
extern string_attr_type<role_t> VC_ROLE;
|
extern string_attr_type<role_t> VC_ROLE;
|
||||||
extern string_attr_type<role_t> VC_ROLE_FG;
|
extern string_attr_type<role_t> VC_ROLE_FG;
|
||||||
@ -310,6 +313,13 @@ inline std::pair<std::string, string_attr_pair> operator"" _symbol(
|
|||||||
VC_ROLE.template value(role_t::VCR_SYMBOL));
|
VC_ROLE.template value(role_t::VCR_SYMBOL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::pair<std::string, string_attr_pair> operator"" _keyword(
|
||||||
|
const char* str, std::size_t len)
|
||||||
|
{
|
||||||
|
return std::make_pair(std::string(str, len),
|
||||||
|
VC_ROLE.template value(role_t::VCR_KEYWORD));
|
||||||
|
}
|
||||||
|
|
||||||
inline std::pair<std::string, string_attr_pair> operator"" _variable(
|
inline std::pair<std::string, string_attr_pair> operator"" _variable(
|
||||||
const char* str, std::size_t len)
|
const char* str, std::size_t len)
|
||||||
{
|
{
|
||||||
@ -317,6 +327,13 @@ inline std::pair<std::string, string_attr_pair> operator"" _variable(
|
|||||||
VC_ROLE.template value(role_t::VCR_VARIABLE));
|
VC_ROLE.template value(role_t::VCR_VARIABLE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::pair<std::string, string_attr_pair> operator"" _comment(
|
||||||
|
const char* str, std::size_t len)
|
||||||
|
{
|
||||||
|
return std::make_pair(std::string(str, len),
|
||||||
|
VC_ROLE.template value(role_t::VCR_COMMENT));
|
||||||
|
}
|
||||||
|
|
||||||
inline std::pair<std::string, string_attr_pair> operator"" _h1(const char* str,
|
inline std::pair<std::string, string_attr_pair> operator"" _h1(const char* str,
|
||||||
std::size_t len)
|
std::size_t len)
|
||||||
{
|
{
|
||||||
@ -338,6 +355,13 @@ inline std::pair<std::string, string_attr_pair> operator"" _h3(const char* str,
|
|||||||
VC_ROLE.template value(role_t::VCR_H3));
|
VC_ROLE.template value(role_t::VCR_H3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::pair<std::string, string_attr_pair> operator"" _list_glyph(
|
||||||
|
const char* str, std::size_t len)
|
||||||
|
{
|
||||||
|
return std::make_pair(std::string(str, len),
|
||||||
|
VC_ROLE.template value(role_t::VCR_LIST_GLYPH));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace literals
|
} // namespace literals
|
||||||
|
|
||||||
} // namespace roles
|
} // namespace roles
|
||||||
|
@ -153,7 +153,7 @@ truncate_to(std::string& str, size_t max_char_len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
is_url(const char* fn)
|
is_url(const std::string& fn)
|
||||||
{
|
{
|
||||||
static const auto url_re = std::regex("^(file|https?|ftps?|scp|sftp):.*");
|
static const auto url_re = std::regex("^(file|https?|ftps?|scp|sftp):.*");
|
||||||
|
|
||||||
|
@ -105,6 +105,17 @@ trim(const std::string& str)
|
|||||||
return str.substr(start, end - start);
|
return str.substr(start, end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string
|
||||||
|
rtrim(const std::string& str)
|
||||||
|
{
|
||||||
|
std::string::size_type end;
|
||||||
|
|
||||||
|
for (end = str.size(); end > 0 && isspace(str[end - 1]); end--)
|
||||||
|
;
|
||||||
|
|
||||||
|
return str.substr(0, end);
|
||||||
|
}
|
||||||
|
|
||||||
inline std::string
|
inline std::string
|
||||||
tolower(const char* str)
|
tolower(const char* str)
|
||||||
{
|
{
|
||||||
@ -186,7 +197,7 @@ utf8_string_length(const std::string& str)
|
|||||||
return utf8_string_length(str.c_str(), str.length());
|
return utf8_string_length(str.c_str(), str.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_url(const char* fn);
|
bool is_url(const std::string& fn);
|
||||||
|
|
||||||
bool is_blank(const std::string& str);
|
bool is_blank(const std::string& str);
|
||||||
|
|
||||||
@ -201,4 +212,14 @@ std::string center_str(const std::string& subject, size_t width);
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
size_t strtonum(T& num_out, const char* data, size_t len);
|
size_t strtonum(T& num_out, const char* data, size_t len);
|
||||||
|
|
||||||
|
inline std::string
|
||||||
|
on_blank(const std::string& str, const std::string& def)
|
||||||
|
{
|
||||||
|
if (is_blank(str)) {
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "bookmarks.hh"
|
#include "bookmarks.hh"
|
||||||
|
|
||||||
|
#include "base/itertools.hh"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
std::unordered_set<std::string> bookmark_metadata::KNOWN_TAGS;
|
std::unordered_set<std::string> bookmark_metadata::KNOWN_TAGS;
|
||||||
@ -38,10 +39,8 @@ std::unordered_set<std::string> bookmark_metadata::KNOWN_TAGS;
|
|||||||
void
|
void
|
||||||
bookmark_metadata::add_tag(const std::string& tag)
|
bookmark_metadata::add_tag(const std::string& tag)
|
||||||
{
|
{
|
||||||
if (std::find(this->bm_tags.begin(), this->bm_tags.end(), tag)
|
if (!(this->bm_tags | lnav::itertools::find(tag))) {
|
||||||
== this->bm_tags.end())
|
this->bm_tags.emplace_back(tag);
|
||||||
{
|
|
||||||
this->bm_tags.push_back(tag);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,14 +71,18 @@ bookmark_metadata::clear()
|
|||||||
this->bm_tags.clear();
|
this->bm_tags.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bookmark_type_t*
|
nonstd::optional<bookmark_type_t*>
|
||||||
bookmark_type_t::find_type(const std::string& name)
|
bookmark_type_t::find_type(const std::string& name)
|
||||||
{
|
{
|
||||||
auto iter = std::find_if(type_begin(), type_end(), mark_eq(name));
|
return get_all_types()
|
||||||
bookmark_type_t* retval = nullptr;
|
| lnav::itertools::find_if(
|
||||||
|
[&name](const auto& elem) { return elem->bt_name == name; });
|
||||||
if (iter != type_end()) {
|
}
|
||||||
retval = (*iter);
|
|
||||||
}
|
std::vector<bookmark_type_t*>&
|
||||||
return retval;
|
bookmark_type_t::get_all_types()
|
||||||
|
{
|
||||||
|
static std::vector<bookmark_type_t*> all_types;
|
||||||
|
|
||||||
|
return all_types;
|
||||||
}
|
}
|
||||||
|
@ -141,47 +141,23 @@ class bookmark_type_t {
|
|||||||
public:
|
public:
|
||||||
using type_iterator = std::vector<bookmark_type_t*>::iterator;
|
using type_iterator = std::vector<bookmark_type_t*>::iterator;
|
||||||
|
|
||||||
static type_iterator type_begin()
|
static type_iterator type_begin() { return get_all_types().begin(); }
|
||||||
{
|
|
||||||
return get_all_types().begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
static type_iterator type_end()
|
static type_iterator type_end() { return get_all_types().end(); }
|
||||||
{
|
|
||||||
return get_all_types().end();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bookmark_type_t* find_type(const std::string& name);
|
static nonstd::optional<bookmark_type_t*> find_type(
|
||||||
|
const std::string& name);
|
||||||
|
|
||||||
static std::vector<bookmark_type_t*>& get_all_types()
|
static std::vector<bookmark_type_t*>& get_all_types();
|
||||||
{
|
|
||||||
static std::vector<bookmark_type_t*> all_types;
|
|
||||||
|
|
||||||
return all_types;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit bookmark_type_t(std::string name) : bt_name(std::move(name))
|
explicit bookmark_type_t(std::string name) : bt_name(std::move(name))
|
||||||
{
|
{
|
||||||
get_all_types().push_back(this);
|
get_all_types().push_back(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& get_name() const
|
const std::string& get_name() const { return this->bt_name; }
|
||||||
{
|
|
||||||
return this->bt_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct mark_eq {
|
|
||||||
explicit mark_eq(const std::string& name) : me_name(name){};
|
|
||||||
|
|
||||||
bool operator()(bookmark_type_t* bt)
|
|
||||||
{
|
|
||||||
return bt->bt_name == this->me_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::string& me_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::string bt_name;
|
const std::string bt_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,20 +53,17 @@ public:
|
|||||||
|
|
||||||
bottom_status_source();
|
bottom_status_source();
|
||||||
|
|
||||||
status_field& get_field(field_t id)
|
status_field& get_field(field_t id) { return this->bss_fields[id]; }
|
||||||
{
|
|
||||||
return this->bss_fields[id];
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_prompt(const std::string& prompt)
|
void set_prompt(const std::string& prompt)
|
||||||
{
|
{
|
||||||
this->bss_prompt.set_value(prompt);
|
this->bss_prompt.set_value(prompt);
|
||||||
};
|
}
|
||||||
|
|
||||||
void grep_error(const std::string& msg) override
|
void grep_error(const std::string& msg) override
|
||||||
{
|
{
|
||||||
this->bss_error.set_value(msg);
|
this->bss_error.set_value(msg);
|
||||||
};
|
}
|
||||||
|
|
||||||
size_t statusview_fields() override;
|
size_t statusview_fields() override;
|
||||||
|
|
||||||
|
@ -44,32 +44,29 @@ struct byte_array {
|
|||||||
static constexpr size_t BYTE_COUNT = COUNT * sizeof(T);
|
static constexpr size_t BYTE_COUNT = COUNT * sizeof(T);
|
||||||
static constexpr size_t STRING_SIZE = BYTE_COUNT * 2 + 1;
|
static constexpr size_t STRING_SIZE = BYTE_COUNT * 2 + 1;
|
||||||
|
|
||||||
byte_array(){};
|
byte_array() {}
|
||||||
|
|
||||||
byte_array(const byte_array& other)
|
byte_array(const byte_array& other)
|
||||||
{
|
{
|
||||||
memcpy(this->ba_data, other.ba_data, BYTE_COUNT);
|
memcpy(this->ba_data, other.ba_data, BYTE_COUNT);
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator<(const byte_array& other) const
|
bool operator<(const byte_array& other) const
|
||||||
{
|
{
|
||||||
return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) < 0;
|
return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) < 0;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator!=(const byte_array& other) const
|
bool operator!=(const byte_array& other) const
|
||||||
{
|
{
|
||||||
return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) != 0;
|
return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) != 0;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator==(const byte_array& other) const
|
bool operator==(const byte_array& other) const
|
||||||
{
|
{
|
||||||
return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) == 0;
|
return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) == 0;
|
||||||
};
|
}
|
||||||
|
|
||||||
void clear()
|
void clear() { memset(this->ba_data, 0, BYTE_COUNT); }
|
||||||
{
|
|
||||||
memset(this->ba_data, 0, BYTE_COUNT);
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename OutputIt>
|
template<typename OutputIt>
|
||||||
void to_string(OutputIt out) const
|
void to_string(OutputIt out) const
|
||||||
@ -88,17 +85,14 @@ struct byte_array {
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned char* in() const
|
const unsigned char* in() const { return this->ba_data; }
|
||||||
{
|
|
||||||
return this->ba_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
T* out(int offset = 0)
|
T* out(int offset = 0)
|
||||||
{
|
{
|
||||||
T* ptr = (T*) this->ba_data;
|
T* ptr = (T*) this->ba_data;
|
||||||
|
|
||||||
return &ptr[offset];
|
return &ptr[offset];
|
||||||
};
|
}
|
||||||
|
|
||||||
unsigned char ba_data[BYTE_COUNT];
|
unsigned char ba_data[BYTE_COUNT];
|
||||||
};
|
};
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
#include "column_namer.hh"
|
#include "column_namer.hh"
|
||||||
|
|
||||||
|
#include "base/itertools.hh"
|
||||||
#include "base/lnav_log.hh"
|
#include "base/lnav_log.hh"
|
||||||
#include "base/string_util.hh"
|
#include "base/string_util.hh"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -47,17 +48,11 @@ column_namer::existing_name(const std::string& in_name) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::find(this->cn_builtin_names.begin(),
|
if (this->cn_builtin_names | lnav::itertools::find(in_name)) {
|
||||||
this->cn_builtin_names.end(),
|
|
||||||
in_name)
|
|
||||||
!= this->cn_builtin_names.end())
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std::find(this->cn_names.begin(), this->cn_names.end(), in_name)
|
if (this->cn_names | lnav::itertools::find(in_name)) {
|
||||||
!= this->cn_names.end())
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
#include "db_sub_source.hh"
|
#include "db_sub_source.hh"
|
||||||
#include "help_text_formatter.hh"
|
#include "help_text_formatter.hh"
|
||||||
#include "lnav.hh"
|
#include "lnav.hh"
|
||||||
|
#include "lnav.indexing.hh"
|
||||||
#include "lnav_config.hh"
|
#include "lnav_config.hh"
|
||||||
#include "lnav_util.hh"
|
#include "lnav_util.hh"
|
||||||
#include "log_format_loader.hh"
|
#include "log_format_loader.hh"
|
||||||
@ -175,8 +176,7 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
|
|||||||
auto source = ec.ec_source.top();
|
auto source = ec.ec_source.top();
|
||||||
sql_progress_guard progress_guard(sql_progress,
|
sql_progress_guard progress_guard(sql_progress,
|
||||||
sql_progress_finished,
|
sql_progress_finished,
|
||||||
source.s_source,
|
source.s_location,
|
||||||
source.s_line,
|
|
||||||
source.s_content);
|
source.s_content);
|
||||||
gettimeofday(&start_tv, nullptr);
|
gettimeofday(&start_tv, nullptr);
|
||||||
retcode = sqlite3_prepare_v2(
|
retcode = sqlite3_prepare_v2(
|
||||||
@ -315,8 +315,17 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
|
|||||||
log_error("sqlite3_step error code: %d", retcode);
|
log_error("sqlite3_step error code: %d", retcode);
|
||||||
errmsg = sqlite3_errmsg(lnav_data.ld_db);
|
errmsg = sqlite3_errmsg(lnav_data.ld_db);
|
||||||
if (startswith(errmsg, "lnav-error:")) {
|
if (startswith(errmsg, "lnav-error:")) {
|
||||||
return Err(lnav::from_json<lnav::console::user_message>(
|
auto from_res
|
||||||
&errmsg[11]));
|
= lnav::from_json<lnav::console::user_message>(
|
||||||
|
&errmsg[11]);
|
||||||
|
|
||||||
|
if (from_res.isOk()) {
|
||||||
|
return Err(from_res.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ec.make_error(
|
||||||
|
"internal error: {}",
|
||||||
|
from_res.unwrapErr()[0].um_message.get_string());
|
||||||
}
|
}
|
||||||
return ec.make_error("{}", errmsg);
|
return ec.make_error("{}", errmsg);
|
||||||
}
|
}
|
||||||
@ -344,7 +353,7 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lnav_data.ld_rl_view != nullptr) {
|
if (lnav_data.ld_rl_view != nullptr) {
|
||||||
lnav_data.ld_rl_view->set_value("");
|
lnav_data.ld_rl_view->clear_value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,7 +616,8 @@ execute_from_file(exec_context& ec,
|
|||||||
const std::string& cmdline)
|
const std::string& cmdline)
|
||||||
{
|
{
|
||||||
std::string retval, alt_msg;
|
std::string retval, alt_msg;
|
||||||
auto _sg = ec.enter_source(path.string(), line_number, cmdline);
|
auto _sg = ec.enter_source(
|
||||||
|
intern_string::lookup(path.string()), line_number, cmdline);
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case ':':
|
case ':':
|
||||||
@ -691,12 +701,15 @@ execute_init_commands(
|
|||||||
|
|
||||||
log_info("Executing initial commands");
|
log_info("Executing initial commands");
|
||||||
for (auto& cmd : lnav_data.ld_commands) {
|
for (auto& cmd : lnav_data.ld_commands) {
|
||||||
|
static const auto COMMAND_OPTION_SRC
|
||||||
|
= intern_string::lookup("command-option");
|
||||||
|
|
||||||
std::string alt_msg;
|
std::string alt_msg;
|
||||||
|
|
||||||
wait_for_children();
|
wait_for_children();
|
||||||
|
|
||||||
{
|
{
|
||||||
auto _sg = ec.enter_source("command-option", option_index++, cmd);
|
auto _sg = ec.enter_source(COMMAND_OPTION_SRC, option_index++, cmd);
|
||||||
switch (cmd.at(0)) {
|
switch (cmd.at(0)) {
|
||||||
case ':':
|
case ':':
|
||||||
msgs.emplace_back(execute_command(ec, cmd.substr(1)),
|
msgs.emplace_back(execute_command(ec, cmd.substr(1)),
|
||||||
@ -906,10 +919,12 @@ exec_context::exec_context(std::vector<logline_value>* line_values,
|
|||||||
ec_accumulator(std::make_unique<attr_line_t>()),
|
ec_accumulator(std::make_unique<attr_line_t>()),
|
||||||
ec_sql_callback(sql_callback), ec_pipe_callback(pipe_callback)
|
ec_sql_callback(sql_callback), ec_pipe_callback(pipe_callback)
|
||||||
{
|
{
|
||||||
|
static const auto COMMAND_SRC = intern_string::lookup("command");
|
||||||
|
|
||||||
this->ec_local_vars.push(std::map<std::string, std::string>());
|
this->ec_local_vars.push(std::map<std::string, std::string>());
|
||||||
this->ec_path_stack.emplace_back(".");
|
this->ec_path_stack.emplace_back(".");
|
||||||
this->ec_source.emplace(
|
this->ec_source.emplace(
|
||||||
lnav::console::snippet::from("command", "").with_line(1));
|
lnav::console::snippet::from(COMMAND_SRC, "").with_line(1));
|
||||||
this->ec_output_stack.emplace_back("screen", nonstd::nullopt);
|
this->ec_output_stack.emplace_back("screen", nonstd::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ struct exec_context {
|
|||||||
exec_context& sg_context;
|
exec_context& sg_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
source_guard enter_source(const std::string& path,
|
source_guard enter_source(intern_string_t path,
|
||||||
int line_number,
|
int line_number,
|
||||||
const std::string& content)
|
const std::string& content)
|
||||||
{
|
{
|
||||||
|
@ -91,6 +91,17 @@ curl_request::debug_cb(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
curl_request::string_cb(void* data, size_t size, size_t nmemb, void* userp)
|
||||||
|
{
|
||||||
|
auto realsize = size * nmemb;
|
||||||
|
auto& vec = *static_cast<std::string*>(userp);
|
||||||
|
|
||||||
|
vec.append((char*) data, ((char*) data) + realsize);
|
||||||
|
|
||||||
|
return realsize;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
curl_looper::loop_body()
|
curl_looper::loop_body()
|
||||||
{
|
{
|
||||||
@ -245,4 +256,18 @@ curl_looper::compute_timeout(mstime_t current_time) const
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
curl_looper::process_all()
|
||||||
|
{
|
||||||
|
this->check_for_new_requests();
|
||||||
|
|
||||||
|
this->requeue_requests(LONG_MAX);
|
||||||
|
|
||||||
|
while (!this->cl_handle_to_request.empty()) {
|
||||||
|
this->perform_io();
|
||||||
|
|
||||||
|
this->check_for_finished_requests();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -39,6 +39,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/isc.hh"
|
#include "base/isc.hh"
|
||||||
|
#include "base/lnav.console.hh"
|
||||||
|
#include "base/result.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#if !defined(HAVE_LIBCURL)
|
#if !defined(HAVE_LIBCURL)
|
||||||
@ -73,8 +75,7 @@ public:
|
|||||||
class curl_request {
|
class curl_request {
|
||||||
public:
|
public:
|
||||||
curl_request(std::string name)
|
curl_request(std::string name)
|
||||||
: cr_name(std::move(name)), cr_open(true), cr_handle(curl_easy_cleanup),
|
: cr_name(std::move(name)), cr_handle(curl_easy_cleanup)
|
||||||
cr_completions(0)
|
|
||||||
{
|
{
|
||||||
this->cr_handle.reset(curl_easy_init());
|
this->cr_handle.reset(curl_easy_init());
|
||||||
curl_easy_setopt(this->cr_handle, CURLOPT_NOSIGNAL, 1);
|
curl_easy_setopt(this->cr_handle, CURLOPT_NOSIGNAL, 1);
|
||||||
@ -91,34 +92,39 @@ public:
|
|||||||
# endif
|
# endif
|
||||||
CURLSSH_AUTH_PASSWORD);
|
CURLSSH_AUTH_PASSWORD);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
virtual ~curl_request() = default;
|
virtual ~curl_request() = default;
|
||||||
|
|
||||||
const std::string& get_name() const
|
const std::string& get_name() const
|
||||||
{
|
{
|
||||||
return this->cr_name;
|
return this->cr_name;
|
||||||
};
|
}
|
||||||
|
|
||||||
virtual void close()
|
virtual void close()
|
||||||
{
|
{
|
||||||
this->cr_open = false;
|
this->cr_open = false;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_open() const
|
bool is_open() const
|
||||||
{
|
{
|
||||||
return this->cr_open;
|
return this->cr_open;
|
||||||
};
|
}
|
||||||
|
|
||||||
CURL* get_handle() const
|
CURL* get_handle() const
|
||||||
{
|
{
|
||||||
return this->cr_handle;
|
return this->cr_handle;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
operator CURL*() const
|
||||||
|
{
|
||||||
|
return this->cr_handle;
|
||||||
|
}
|
||||||
|
|
||||||
int get_completions() const
|
int get_completions() const
|
||||||
{
|
{
|
||||||
return this->cr_completions;
|
return this->cr_completions;
|
||||||
};
|
}
|
||||||
|
|
||||||
virtual long complete(CURLcode result)
|
virtual long complete(CURLcode result)
|
||||||
{
|
{
|
||||||
@ -136,17 +142,42 @@ public:
|
|||||||
"%s: download_speed=%f", this->cr_name.c_str(), download_speed);
|
"%s: download_speed=%f", this->cr_name.c_str(), download_speed);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
Result<std::string, CURLcode> perform()
|
||||||
|
{
|
||||||
|
std::string response;
|
||||||
|
|
||||||
|
curl_easy_setopt(this->get_handle(), CURLOPT_WRITEFUNCTION, string_cb);
|
||||||
|
curl_easy_setopt(this->get_handle(), CURLOPT_WRITEDATA, &response);
|
||||||
|
|
||||||
|
auto rc = curl_easy_perform(this->get_handle());
|
||||||
|
if (rc == CURLE_OK) {
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
long get_response_code() const
|
||||||
|
{
|
||||||
|
long retval;
|
||||||
|
|
||||||
|
curl_easy_getinfo(this->get_handle(), CURLINFO_RESPONSE_CODE, &retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static int debug_cb(
|
static int debug_cb(
|
||||||
CURL* handle, curl_infotype type, char* data, size_t size, void* userp);
|
CURL* handle, curl_infotype type, char* data, size_t size, void* userp);
|
||||||
|
|
||||||
|
static size_t string_cb(void* data, size_t size, size_t nmemb, void* userp);
|
||||||
|
|
||||||
const std::string cr_name;
|
const std::string cr_name;
|
||||||
bool cr_open;
|
bool cr_open{true};
|
||||||
auto_mem<CURL> cr_handle;
|
auto_mem<CURL> cr_handle;
|
||||||
char cr_error_buffer[CURL_ERROR_SIZE];
|
char cr_error_buffer[CURL_ERROR_SIZE];
|
||||||
int cr_completions;
|
int cr_completions{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
class curl_looper : public isc::service<curl_looper> {
|
class curl_looper : public isc::service<curl_looper> {
|
||||||
@ -154,20 +185,9 @@ public:
|
|||||||
curl_looper() : cl_curl_multi(curl_multi_cleanup)
|
curl_looper() : cl_curl_multi(curl_multi_cleanup)
|
||||||
{
|
{
|
||||||
this->cl_curl_multi.reset(curl_multi_init());
|
this->cl_curl_multi.reset(curl_multi_init());
|
||||||
};
|
}
|
||||||
|
|
||||||
void process_all()
|
void process_all();
|
||||||
{
|
|
||||||
this->check_for_new_requests();
|
|
||||||
|
|
||||||
this->requeue_requests(LONG_MAX);
|
|
||||||
|
|
||||||
while (!this->cl_handle_to_request.empty()) {
|
|
||||||
this->perform_io();
|
|
||||||
|
|
||||||
this->check_for_finished_requests();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void add_request(const std::shared_ptr<curl_request>& cr)
|
void add_request(const std::shared_ptr<curl_request>& cr)
|
||||||
{
|
{
|
||||||
@ -175,12 +195,12 @@ public:
|
|||||||
|
|
||||||
this->cl_all_requests.emplace_back(cr);
|
this->cl_all_requests.emplace_back(cr);
|
||||||
this->cl_new_requests.emplace_back(cr);
|
this->cl_new_requests.emplace_back(cr);
|
||||||
};
|
}
|
||||||
|
|
||||||
void close_request(const std::string& name)
|
void close_request(const std::string& name)
|
||||||
{
|
{
|
||||||
this->cl_close_requests.emplace_back(name);
|
this->cl_close_requests.emplace_back(name);
|
||||||
};
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void loop_body() override;
|
void loop_body() override;
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "db_sub_source.hh"
|
#include "db_sub_source.hh"
|
||||||
|
|
||||||
#include "base/date_time_scanner.hh"
|
#include "base/date_time_scanner.hh"
|
||||||
|
#include "base/itertools.hh"
|
||||||
#include "base/time_util.hh"
|
#include "base/time_util.hh"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "yajlpp/json_ptr.hh"
|
#include "yajlpp/json_ptr.hh"
|
||||||
@ -261,17 +262,10 @@ db_label_source::clear()
|
|||||||
this->dls_cell_width.clear();
|
this->dls_cell_width.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
long
|
nonstd::optional<size_t>
|
||||||
db_label_source::column_name_to_index(const std::string& name) const
|
db_label_source::column_name_to_index(const std::string& name) const
|
||||||
{
|
{
|
||||||
std::vector<header_meta>::const_iterator iter;
|
return this->dls_headers | lnav::itertools::find(name);
|
||||||
|
|
||||||
iter = std::find(this->dls_headers.begin(), this->dls_headers.end(), name);
|
|
||||||
if (iter == this->dls_headers.end()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::distance(this->dls_headers.begin(), iter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nonstd::optional<vis_line_t>
|
nonstd::optional<vis_line_t>
|
||||||
|
@ -48,20 +48,14 @@ public:
|
|||||||
this->clear();
|
this->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_log_time_column() const
|
bool has_log_time_column() const { return !this->dls_time_column.empty(); }
|
||||||
{
|
|
||||||
return !this->dls_time_column.empty();
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t text_line_count()
|
size_t text_line_count() { return this->dls_rows.size(); }
|
||||||
{
|
|
||||||
return this->dls_rows.size();
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t text_size_for_line(textview_curses& tc, int line, line_flags_t flags)
|
size_t text_size_for_line(textview_curses& tc, int line, line_flags_t flags)
|
||||||
{
|
{
|
||||||
return this->text_line_width(tc);
|
return this->text_line_width(tc);
|
||||||
};
|
}
|
||||||
|
|
||||||
size_t text_line_width(textview_curses& curses)
|
size_t text_line_width(textview_curses& curses)
|
||||||
{
|
{
|
||||||
@ -71,7 +65,7 @@ public:
|
|||||||
retval += dls_header.hm_column_size + 1;
|
retval += dls_header.hm_column_size + 1;
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
void text_value_for_line(textview_curses& tc,
|
void text_value_for_line(textview_curses& tc,
|
||||||
int row,
|
int row,
|
||||||
@ -86,7 +80,8 @@ public:
|
|||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
long column_name_to_index(const std::string& name) const;
|
nonstd::optional<size_t> column_name_to_index(
|
||||||
|
const std::string& name) const;
|
||||||
|
|
||||||
nonstd::optional<vis_line_t> row_for_time(struct timeval time_bucket);
|
nonstd::optional<vis_line_t> row_for_time(struct timeval time_bucket);
|
||||||
|
|
||||||
@ -97,26 +92,22 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this->dls_time_column[row];
|
return this->dls_time_column[row];
|
||||||
};
|
}
|
||||||
|
|
||||||
struct header_meta {
|
struct header_meta {
|
||||||
explicit header_meta(std::string name)
|
explicit header_meta(std::string name) : hm_name(std::move(name)) {}
|
||||||
: hm_name(std::move(name)), hm_column_type(SQLITE3_TEXT),
|
|
||||||
hm_graphable(false), hm_log_time(false), hm_column_size(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const std::string& name) const
|
bool operator==(const std::string& name) const
|
||||||
{
|
{
|
||||||
return this->hm_name == name;
|
return this->hm_name == name;
|
||||||
};
|
}
|
||||||
|
|
||||||
std::string hm_name;
|
std::string hm_name;
|
||||||
int hm_column_type;
|
int hm_column_type{SQLITE3_TEXT};
|
||||||
unsigned int hm_sub_type{0};
|
unsigned int hm_sub_type{0};
|
||||||
bool hm_graphable;
|
bool hm_graphable{false};
|
||||||
bool hm_log_time;
|
bool hm_log_time{false};
|
||||||
size_t hm_column_size;
|
size_t hm_column_size{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
stacked_bar_chart<std::string> dls_chart;
|
stacked_bar_chart<std::string> dls_chart;
|
||||||
|
@ -55,17 +55,14 @@ public:
|
|||||||
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
|
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
|
||||||
this->tss_fields[TSF_DESCRIPTION].set_share(1);
|
this->tss_fields[TSF_DESCRIPTION].set_share(1);
|
||||||
this->tss_fields[TSF_DESCRIPTION].set_role(role_t::VCR_STATUS);
|
this->tss_fields[TSF_DESCRIPTION].set_role(role_t::VCR_STATUS);
|
||||||
};
|
}
|
||||||
|
|
||||||
size_t statusview_fields() override
|
size_t statusview_fields() override { return TSF__MAX; }
|
||||||
{
|
|
||||||
return TSF__MAX;
|
|
||||||
};
|
|
||||||
|
|
||||||
status_field& statusview_value_for_field(int field) override
|
status_field& statusview_value_for_field(int field) override
|
||||||
{
|
{
|
||||||
return this->tss_fields[field];
|
return this->tss_fields[field];
|
||||||
};
|
}
|
||||||
|
|
||||||
void set_title(const std::string& title)
|
void set_title(const std::string& title)
|
||||||
{
|
{
|
||||||
|
83
src/dump_internals.cc
Normal file
83
src/dump_internals.cc
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022, 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 "dump_internals.hh"
|
||||||
|
|
||||||
|
#include "lnav.hh"
|
||||||
|
#include "lnav_config.hh"
|
||||||
|
#include "log_format_loader.hh"
|
||||||
|
#include "sql_help.hh"
|
||||||
|
#include "view_helpers.examples.hh"
|
||||||
|
#include "yajlpp/yajlpp.hh"
|
||||||
|
|
||||||
|
namespace lnav {
|
||||||
|
|
||||||
|
void
|
||||||
|
dump_internals(const char* internals_dir)
|
||||||
|
{
|
||||||
|
dump_schema_to(
|
||||||
|
lnav_config_handlers, internals_dir, "config-v1.schema.json");
|
||||||
|
dump_schema_to(root_format_handler, internals_dir, "format-v1.schema.json");
|
||||||
|
|
||||||
|
execute_examples();
|
||||||
|
|
||||||
|
auto cmd_ref_path = ghc::filesystem::path(internals_dir) / "cmd-ref.rst";
|
||||||
|
auto cmd_file = std::unique_ptr<FILE, decltype(&fclose)>(
|
||||||
|
fopen(cmd_ref_path.c_str(), "w+"), fclose);
|
||||||
|
|
||||||
|
if (cmd_file != nullptr) {
|
||||||
|
std::set<readline_context::command_t*> unique_cmds;
|
||||||
|
|
||||||
|
for (auto& cmd : lnav_commands) {
|
||||||
|
if (unique_cmds.find(cmd.second) != unique_cmds.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unique_cmds.insert(cmd.second);
|
||||||
|
format_help_text_for_rst(
|
||||||
|
cmd.second->c_help, eval_example, cmd_file.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sql_ref_path = ghc::filesystem::path(internals_dir) / "sql-ref.rst";
|
||||||
|
auto sql_file = std::unique_ptr<FILE, decltype(&fclose)>(
|
||||||
|
fopen(sql_ref_path.c_str(), "w+"), fclose);
|
||||||
|
std::set<help_text*> unique_sql_help;
|
||||||
|
|
||||||
|
if (sql_file != nullptr) {
|
||||||
|
for (auto& sql : sqlite_function_help) {
|
||||||
|
if (unique_sql_help.find(sql.second) != unique_sql_help.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unique_sql_help.insert(sql.second);
|
||||||
|
format_help_text_for_rst(*sql.second, eval_example, sql_file.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lnav
|
41
src/dump_internals.hh
Normal file
41
src/dump_internals.hh
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022, Timothy Stack
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef dump_internals_hh
|
||||||
|
#define dump_internals_hh
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace lnav {
|
||||||
|
|
||||||
|
void dump_internals(const char* dir);
|
||||||
|
|
||||||
|
} // namespace lnav
|
||||||
|
|
||||||
|
#endif
|
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "elem_to_json.hh"
|
#include "elem_to_json.hh"
|
||||||
|
|
||||||
|
#include "base/itertools.hh"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "yajlpp/yajlpp.hh"
|
#include "yajlpp/yajlpp.hh"
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ map_elements_to_json(yajl_gen gen,
|
|||||||
if (key_str.empty()) {
|
if (key_str.empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (find(names.begin(), names.end(), key_str) != names.end()) {
|
if (names | lnav::itertools::find(key_str)) {
|
||||||
unique_names = false;
|
unique_names = false;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -579,22 +579,19 @@ field_overlay_source::build_meta_line(const listview_curses& lv,
|
|||||||
size_t filename_width = this->fos_lss.get_filename_offset();
|
size_t filename_width = this->fos_lss.get_filename_offset();
|
||||||
|
|
||||||
if (!line_meta.bm_comment.empty()) {
|
if (!line_meta.bm_comment.empty()) {
|
||||||
|
const auto* lead = line_meta.bm_tags.empty() ? " \u2514 "
|
||||||
|
: " \u251c ";
|
||||||
attr_line_t al;
|
attr_line_t al;
|
||||||
|
|
||||||
al.with_string(" + ")
|
al.with_string(lead).append(
|
||||||
.with_attr(string_attr(
|
lnav::roles::comment(line_meta.bm_comment));
|
||||||
line_range(1, 2),
|
|
||||||
VC_GRAPHIC.value(
|
|
||||||
line_meta.bm_tags.empty() ? ACS_LLCORNER : ACS_LTEE)))
|
|
||||||
.append(line_meta.bm_comment);
|
|
||||||
al.insert(0, filename_width, ' ');
|
al.insert(0, filename_width, ' ');
|
||||||
dst.emplace_back(al);
|
dst.emplace_back(al);
|
||||||
}
|
}
|
||||||
if (!line_meta.bm_tags.empty()) {
|
if (!line_meta.bm_tags.empty()) {
|
||||||
attr_line_t al;
|
attr_line_t al;
|
||||||
|
|
||||||
al.with_string(" +").with_attr(string_attr(
|
al.with_string(" \u2514");
|
||||||
line_range(1, 2), VC_GRAPHIC.value(ACS_LLCORNER)));
|
|
||||||
for (const auto& str : line_meta.bm_tags) {
|
for (const auto& str : line_meta.bm_tags) {
|
||||||
al.append(1, ' ').append(
|
al.append(1, ' ').append(
|
||||||
str, VC_STYLE.value(vc.attrs_for_ident(str)));
|
str, VC_STYLE.value(vc.attrs_for_ident(str)));
|
||||||
@ -614,3 +611,53 @@ field_overlay_source::build_meta_line(const listview_curses& lv,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
field_overlay_source::add_key_line_attrs(int key_size, bool last_line)
|
||||||
|
{
|
||||||
|
string_attrs_t& sa = this->fos_lines.back().get_attrs();
|
||||||
|
struct line_range lr(1, 2);
|
||||||
|
int64_t graphic = (int64_t) (last_line ? ACS_LLCORNER : ACS_LTEE);
|
||||||
|
sa.emplace_back(lr, VC_GRAPHIC.value(graphic));
|
||||||
|
|
||||||
|
lr.lr_start = 3 + key_size + 3;
|
||||||
|
lr.lr_end = -1;
|
||||||
|
sa.emplace_back(lr, VC_STYLE.value(A_BOLD));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
field_overlay_source::list_value_for_overlay(const listview_curses& lv,
|
||||||
|
int y,
|
||||||
|
int bottom,
|
||||||
|
vis_line_t row,
|
||||||
|
attr_line_t& value_out)
|
||||||
|
{
|
||||||
|
if (y == 0) {
|
||||||
|
this->build_field_lines(lv);
|
||||||
|
this->build_summary_lines(lv);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (1 <= y && y <= (int) this->fos_lines.size()) {
|
||||||
|
value_out = this->fos_lines[y - 1];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->fos_summary_lines.empty() && y == (bottom - 1)) {
|
||||||
|
value_out = this->fos_summary_lines[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->fos_meta_lines.empty()) {
|
||||||
|
value_out = this->fos_meta_lines.front();
|
||||||
|
this->fos_meta_lines.erase(this->fos_meta_lines.begin());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row < lv.get_inner_height()) {
|
||||||
|
this->build_meta_line(lv, this->fos_meta_lines, row);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -42,57 +42,17 @@ class field_overlay_source : public list_overlay_source {
|
|||||||
public:
|
public:
|
||||||
explicit field_overlay_source(logfile_sub_source& lss,
|
explicit field_overlay_source(logfile_sub_source& lss,
|
||||||
textfile_sub_source& tss)
|
textfile_sub_source& tss)
|
||||||
: fos_lss(lss), fos_tss(tss), fos_log_helper(lss){
|
: fos_lss(lss), fos_tss(tss), fos_log_helper(lss)
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void add_key_line_attrs(int key_size, bool last_line = false)
|
|
||||||
{
|
{
|
||||||
string_attrs_t& sa = this->fos_lines.back().get_attrs();
|
}
|
||||||
struct line_range lr(1, 2);
|
|
||||||
int64_t graphic = (int64_t) (last_line ? ACS_LLCORNER : ACS_LTEE);
|
|
||||||
sa.emplace_back(lr, VC_GRAPHIC.value(graphic));
|
|
||||||
|
|
||||||
lr.lr_start = 3 + key_size + 3;
|
void add_key_line_attrs(int key_size, bool last_line = false);
|
||||||
lr.lr_end = -1;
|
|
||||||
sa.emplace_back(lr, VC_STYLE.value(A_BOLD));
|
|
||||||
};
|
|
||||||
|
|
||||||
bool list_value_for_overlay(const listview_curses& lv,
|
bool list_value_for_overlay(const listview_curses& lv,
|
||||||
int y,
|
int y,
|
||||||
int bottom,
|
int bottom,
|
||||||
vis_line_t row,
|
vis_line_t row,
|
||||||
attr_line_t& value_out) override
|
attr_line_t& value_out) override;
|
||||||
{
|
|
||||||
if (y == 0) {
|
|
||||||
this->build_field_lines(lv);
|
|
||||||
this->build_summary_lines(lv);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1 <= y && y <= (int) this->fos_lines.size()) {
|
|
||||||
value_out = this->fos_lines[y - 1];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->fos_summary_lines.empty() && y == (bottom - 1)) {
|
|
||||||
value_out = this->fos_summary_lines[0];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->fos_meta_lines.empty()) {
|
|
||||||
value_out = this->fos_meta_lines.front();
|
|
||||||
this->fos_meta_lines.erase(this->fos_meta_lines.begin());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row < lv.get_inner_height()) {
|
|
||||||
this->build_meta_line(lv, this->fos_meta_lines, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
void build_field_lines(const listview_curses& lv);
|
void build_field_lines(const listview_curses& lv);
|
||||||
void build_summary_lines(const listview_curses& lv);
|
void build_summary_lines(const listview_curses& lv);
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
#include "base/humanize.network.hh"
|
#include "base/humanize.network.hh"
|
||||||
#include "base/isc.hh"
|
#include "base/isc.hh"
|
||||||
|
#include "base/itertools.hh"
|
||||||
#include "base/opt_util.hh"
|
#include "base/opt_util.hh"
|
||||||
#include "base/string_util.hh"
|
#include "base/string_util.hh"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -280,21 +281,19 @@ file_collection::watch_logfile(const std::string& filename,
|
|||||||
return lnav::futures::make_ready_future(std::move(retval));
|
return lnav::futures::make_ready_future(std::move(retval));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto stat_iter = find_if(this->fc_new_stats.begin(),
|
if (this->fc_new_stats | lnav::itertools::find_if([&st](const auto& elem) {
|
||||||
this->fc_new_stats.end(),
|
return st.st_ino == elem.st_ino && st.st_dev == elem.st_dev;
|
||||||
[&st](auto& elem) {
|
}))
|
||||||
return st.st_ino == elem.st_ino
|
{
|
||||||
&& st.st_dev == elem.st_dev;
|
|
||||||
});
|
|
||||||
if (stat_iter != this->fc_new_stats.end()) {
|
|
||||||
// this file is probably a link that we have already scanned in this
|
// this file is probably a link that we have already scanned in this
|
||||||
// pass.
|
// pass.
|
||||||
return lnav::futures::make_ready_future(std::move(retval));
|
return lnav::futures::make_ready_future(std::move(retval));
|
||||||
}
|
}
|
||||||
|
|
||||||
this->fc_new_stats.emplace_back(st);
|
this->fc_new_stats.emplace_back(st);
|
||||||
auto file_iter
|
|
||||||
= find_if(this->fc_files.begin(), this->fc_files.end(), same_file(st));
|
auto file_iter = std::find_if(
|
||||||
|
this->fc_files.begin(), this->fc_files.end(), same_file(st));
|
||||||
|
|
||||||
if (file_iter == this->fc_files.end()) {
|
if (file_iter == this->fc_files.end()) {
|
||||||
if (this->fc_other_files.find(filename) != this->fc_other_files.end()) {
|
if (this->fc_other_files.find(filename) != this->fc_other_files.end()) {
|
||||||
@ -313,7 +312,8 @@ file_collection::watch_logfile(const std::string& filename,
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ff = detect_file_format(filename);
|
auto ff = loo2.loo_temp_file ? file_format_t::UNKNOWN
|
||||||
|
: detect_file_format(filename);
|
||||||
|
|
||||||
loo2.loo_file_format = ff;
|
loo2.loo_file_format = ff;
|
||||||
switch (ff) {
|
switch (ff) {
|
||||||
@ -619,7 +619,7 @@ file_collection::rescan_files(bool required)
|
|||||||
|
|
||||||
this->expand_filename(fq, path, pair.second, false);
|
this->expand_filename(fq, path, pair.second, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (pair.second.loo_fd.get() != -1) {
|
||||||
fq.push_back(watch_logfile(pair.first, pair.second, required));
|
fq.push_back(watch_logfile(pair.first, pair.second, required));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ files_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
|||||||
}
|
}
|
||||||
|
|
||||||
lv.reload_data();
|
lv.reload_data();
|
||||||
lnav_data.ld_mode = LNM_PAGING;
|
lnav_data.ld_mode = ln_mode_t::PAGING;
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -205,7 +205,7 @@ files_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
|||||||
void
|
void
|
||||||
files_sub_source::list_input_handle_scroll_out(listview_curses& lv)
|
files_sub_source::list_input_handle_scroll_out(listview_curses& lv)
|
||||||
{
|
{
|
||||||
lnav_data.ld_mode = LNM_PAGING;
|
lnav_data.ld_mode = ln_mode_t::PAGING;
|
||||||
lnav_data.ld_filter_view.reload_data();
|
lnav_data.ld_filter_view.reload_data();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +300,7 @@ files_sub_source::text_attrs_for_line(textview_curses& tc,
|
|||||||
string_attrs_t& value_out)
|
string_attrs_t& value_out)
|
||||||
{
|
{
|
||||||
bool selected
|
bool selected
|
||||||
= lnav_data.ld_mode == LNM_FILES && line == tc.get_selection();
|
= lnav_data.ld_mode == ln_mode_t::FILES && line == tc.get_selection();
|
||||||
const auto& fc = lnav_data.ld_active_files;
|
const auto& fc = lnav_data.ld_active_files;
|
||||||
auto& vcolors = view_colors::singleton();
|
auto& vcolors = view_colors::singleton();
|
||||||
const auto dim = tc.get_dimensions();
|
const auto dim = tc.get_dimensions();
|
||||||
|
@ -91,12 +91,12 @@ size_t
|
|||||||
filter_status_source::statusview_fields()
|
filter_status_source::statusview_fields()
|
||||||
{
|
{
|
||||||
switch (lnav_data.ld_mode) {
|
switch (lnav_data.ld_mode) {
|
||||||
case LNM_SEARCH_FILTERS:
|
case ln_mode_t::SEARCH_FILTERS:
|
||||||
case LNM_SEARCH_FILES:
|
case ln_mode_t::SEARCH_FILES:
|
||||||
this->tss_fields[TSF_HELP].set_value("");
|
this->tss_fields[TSF_HELP].set_value("");
|
||||||
break;
|
break;
|
||||||
case LNM_FILTER:
|
case ln_mode_t::FILTER:
|
||||||
case LNM_FILES:
|
case ln_mode_t::FILES:
|
||||||
this->tss_fields[TSF_HELP].set_value(EXIT_MSG);
|
this->tss_fields[TSF_HELP].set_value(EXIT_MSG);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -104,12 +104,12 @@ filter_status_source::statusview_fields()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lnav_data.ld_mode == LNM_FILES || lnav_data.ld_mode == LNM_SEARCH_FILES)
|
if (lnav_data.ld_mode == ln_mode_t::FILES
|
||||||
|
|| lnav_data.ld_mode == ln_mode_t::SEARCH_FILES)
|
||||||
{
|
{
|
||||||
this->tss_fields[TSF_FILES_TITLE].set_value(
|
this->tss_fields[TSF_FILES_TITLE].set_value(
|
||||||
" " ANSI_ROLE("F") "iles ", role_t::VCR_STATUS_TITLE_HOTKEY);
|
" " ANSI_ROLE("F") "iles ", role_t::VCR_STATUS_TITLE_HOTKEY);
|
||||||
this->tss_fields[TSF_FILES_TITLE].set_role(
|
this->tss_fields[TSF_FILES_TITLE].set_role(role_t::VCR_STATUS_TITLE);
|
||||||
role_t::VCR_STATUS_TITLE);
|
|
||||||
this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value(
|
this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value(
|
||||||
role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
|
role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
|
||||||
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
|
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
|
||||||
@ -245,7 +245,7 @@ filter_help_status_source::statusview_fields()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lnav_data.ld_mode == LNM_FILTER) {
|
if (lnav_data.ld_mode == ln_mode_t::FILTER) {
|
||||||
auto& editor = lnav_data.ld_filter_source;
|
auto& editor = lnav_data.ld_filter_source;
|
||||||
auto& lv = lnav_data.ld_filter_view;
|
auto& lv = lnav_data.ld_filter_view;
|
||||||
auto& fs = tss->get_filters();
|
auto& fs = tss->get_filters();
|
||||||
@ -285,7 +285,7 @@ filter_help_status_source::statusview_fields()
|
|||||||
tss->tss_apply_filters ? "Disable Filtering"
|
tss->tss_apply_filters ? "Disable Filtering"
|
||||||
: "Enable Filtering");
|
: "Enable Filtering");
|
||||||
}
|
}
|
||||||
} else if (lnav_data.ld_mode == LNM_FILES
|
} else if (lnav_data.ld_mode == ln_mode_t::FILES
|
||||||
&& lnav_data.ld_session_loaded) {
|
&& lnav_data.ld_session_loaded) {
|
||||||
auto& lv = lnav_data.ld_files_view;
|
auto& lv = lnav_data.ld_files_view;
|
||||||
auto sel = files_model::from_selection(lv.get_selection());
|
auto sel = files_model::from_selection(lv.get_selection());
|
||||||
|
@ -41,13 +41,10 @@ filter_sub_source::filter_sub_source()
|
|||||||
{
|
{
|
||||||
this->fss_regex_context.set_highlighter(readline_regex_highlighter)
|
this->fss_regex_context.set_highlighter(readline_regex_highlighter)
|
||||||
.set_append_character(0);
|
.set_append_character(0);
|
||||||
this->fss_editor.add_context(
|
this->fss_editor.add_context(filter_lang_t::REGEX, this->fss_regex_context);
|
||||||
lnav::enums::to_underlying(filter_lang_t::REGEX),
|
|
||||||
this->fss_regex_context);
|
|
||||||
this->fss_sql_context.set_highlighter(readline_sqlite_highlighter)
|
this->fss_sql_context.set_highlighter(readline_sqlite_highlighter)
|
||||||
.set_append_character(0);
|
.set_append_character(0);
|
||||||
this->fss_editor.add_context(lnav::enums::to_underlying(filter_lang_t::SQL),
|
this->fss_editor.add_context(filter_lang_t::SQL, this->fss_sql_context);
|
||||||
this->fss_sql_context);
|
|
||||||
this->fss_editor.set_change_action(
|
this->fss_editor.set_change_action(
|
||||||
bind_mem(&filter_sub_source::rl_change, this));
|
bind_mem(&filter_sub_source::rl_change, this));
|
||||||
this->fss_editor.set_perform_action(
|
this->fss_editor.set_perform_action(
|
||||||
@ -80,8 +77,8 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
|||||||
|
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'f': {
|
case 'f': {
|
||||||
auto top_view = *lnav_data.ld_view_stack.top();
|
auto* top_view = *lnav_data.ld_view_stack.top();
|
||||||
auto tss = top_view->get_sub_source();
|
auto* tss = top_view->get_sub_source();
|
||||||
|
|
||||||
tss->toggle_apply_filters();
|
tss->toggle_apply_filters();
|
||||||
break;
|
break;
|
||||||
@ -159,19 +156,18 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
|||||||
|
|
||||||
this->fss_editing = true;
|
this->fss_editing = true;
|
||||||
|
|
||||||
add_view_text_possibilities(
|
add_view_text_possibilities(&this->fss_editor,
|
||||||
&this->fss_editor,
|
filter_lang_t::REGEX,
|
||||||
lnav::enums::to_underlying(filter_lang_t::REGEX),
|
"*",
|
||||||
"*",
|
top_view,
|
||||||
top_view);
|
text_quoting::regex);
|
||||||
this->fss_editor.set_window(lv.get_window());
|
this->fss_editor.set_window(lv.get_window());
|
||||||
this->fss_editor.set_visible(true);
|
this->fss_editor.set_visible(true);
|
||||||
this->fss_editor.set_y(lv.get_y()
|
this->fss_editor.set_y(lv.get_y()
|
||||||
+ (int) (lv.get_selection() - lv.get_top()));
|
+ (int) (lv.get_selection() - lv.get_top()));
|
||||||
this->fss_editor.set_left(25);
|
this->fss_editor.set_left(25);
|
||||||
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
||||||
this->fss_editor.focus(
|
this->fss_editor.focus(filter_lang_t::REGEX, "", "");
|
||||||
lnav::enums::to_underlying(filter_lang_t::REGEX), "", "");
|
|
||||||
this->fss_filter_state = true;
|
this->fss_filter_state = true;
|
||||||
ef->disable();
|
ef->disable();
|
||||||
return true;
|
return true;
|
||||||
@ -196,19 +192,18 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
|||||||
|
|
||||||
this->fss_editing = true;
|
this->fss_editing = true;
|
||||||
|
|
||||||
add_view_text_possibilities(
|
add_view_text_possibilities(&this->fss_editor,
|
||||||
&this->fss_editor,
|
filter_lang_t::REGEX,
|
||||||
lnav::enums::to_underlying(filter_lang_t::REGEX),
|
"*",
|
||||||
"*",
|
top_view,
|
||||||
top_view);
|
text_quoting::regex);
|
||||||
this->fss_editor.set_window(lv.get_window());
|
this->fss_editor.set_window(lv.get_window());
|
||||||
this->fss_editor.set_visible(true);
|
this->fss_editor.set_visible(true);
|
||||||
this->fss_editor.set_y(lv.get_y()
|
this->fss_editor.set_y(lv.get_y()
|
||||||
+ (int) (lv.get_selection() - lv.get_top()));
|
+ (int) (lv.get_selection() - lv.get_top()));
|
||||||
this->fss_editor.set_left(25);
|
this->fss_editor.set_left(25);
|
||||||
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
||||||
this->fss_editor.focus(
|
this->fss_editor.focus(filter_lang_t::REGEX, "", "");
|
||||||
lnav::enums::to_underlying(filter_lang_t::REGEX), "", "");
|
|
||||||
this->fss_filter_state = true;
|
this->fss_filter_state = true;
|
||||||
ef->disable();
|
ef->disable();
|
||||||
return true;
|
return true;
|
||||||
@ -227,23 +222,20 @@ filter_sub_source::list_input_handle_key(listview_curses& lv, int ch)
|
|||||||
|
|
||||||
this->fss_editing = true;
|
this->fss_editing = true;
|
||||||
|
|
||||||
|
auto tq = tf->get_lang() == filter_lang_t::SQL
|
||||||
|
? text_quoting::sql
|
||||||
|
: text_quoting::regex;
|
||||||
add_view_text_possibilities(
|
add_view_text_possibilities(
|
||||||
&this->fss_editor,
|
&this->fss_editor, tf->get_lang(), "*", top_view, tq);
|
||||||
lnav::enums::to_underlying(filter_lang_t::REGEX),
|
|
||||||
"*",
|
|
||||||
top_view);
|
|
||||||
add_filter_expr_possibilities(
|
add_filter_expr_possibilities(
|
||||||
&this->fss_editor,
|
&this->fss_editor, filter_lang_t::SQL, "*");
|
||||||
lnav::enums::to_underlying(filter_lang_t::SQL),
|
|
||||||
"*");
|
|
||||||
this->fss_editor.set_window(lv.get_window());
|
this->fss_editor.set_window(lv.get_window());
|
||||||
this->fss_editor.set_visible(true);
|
this->fss_editor.set_visible(true);
|
||||||
this->fss_editor.set_y(lv.get_y()
|
this->fss_editor.set_y(lv.get_y()
|
||||||
+ (int) (lv.get_selection() - lv.get_top()));
|
+ (int) (lv.get_selection() - lv.get_top()));
|
||||||
this->fss_editor.set_left(25);
|
this->fss_editor.set_left(25);
|
||||||
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
this->fss_editor.set_width(this->tss_view->get_width() - 8 - 1);
|
||||||
this->fss_editor.focus(lnav::enums::to_underlying(tf->get_lang()),
|
this->fss_editor.focus(tf->get_lang(), "");
|
||||||
"");
|
|
||||||
this->fss_editor.rewrite_line(0, tf->get_id().c_str());
|
this->fss_editor.rewrite_line(0, tf->get_id().c_str());
|
||||||
this->fss_filter_state = tf->is_enabled();
|
this->fss_filter_state = tf->is_enabled();
|
||||||
tf->disable();
|
tf->disable();
|
||||||
@ -349,7 +341,7 @@ filter_sub_source::text_attrs_for_line(textview_curses& tc,
|
|||||||
filter_stack& fs = tss->get_filters();
|
filter_stack& fs = tss->get_filters();
|
||||||
auto tf = *(fs.begin() + line);
|
auto tf = *(fs.begin() + line);
|
||||||
bool selected
|
bool selected
|
||||||
= lnav_data.ld_mode == LNM_FILTER && line == tc.get_selection();
|
= lnav_data.ld_mode == ln_mode_t::FILTER && line == tc.get_selection();
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
value_out.emplace_back(line_range{0, 1},
|
value_out.emplace_back(line_range{0, 1},
|
||||||
@ -509,7 +501,7 @@ filter_sub_source::rl_perform(readline_curses* rc)
|
|||||||
filter_stack& fs = tss->get_filters();
|
filter_stack& fs = tss->get_filters();
|
||||||
auto iter = fs.begin() + this->tss_view->get_selection();
|
auto iter = fs.begin() + this->tss_view->get_selection();
|
||||||
auto tf = *iter;
|
auto tf = *iter;
|
||||||
auto new_value = rc->get_value();
|
auto new_value = rc->get_value().get_string();
|
||||||
|
|
||||||
if (new_value.empty()) {
|
if (new_value.empty()) {
|
||||||
this->rl_abort(rc);
|
this->rl_abort(rc);
|
||||||
@ -663,6 +655,6 @@ filter_sub_source::rl_display_next(readline_curses* rc)
|
|||||||
void
|
void
|
||||||
filter_sub_source::list_input_handle_scroll_out(listview_curses& lv)
|
filter_sub_source::list_input_handle_scroll_out(listview_curses& lv)
|
||||||
{
|
{
|
||||||
lnav_data.ld_mode = LNM_PAGING;
|
lnav_data.ld_mode = ln_mode_t::PAGING;
|
||||||
lnav_data.ld_filter_view.reload_data();
|
lnav_data.ld_filter_view.reload_data();
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,15 @@
|
|||||||
"description": "A generic format for logs, like cron, that have a date at the start of a block.",
|
"description": "A generic format for logs, like cron, that have a date at the start of a block.",
|
||||||
"regex": {
|
"regex": {
|
||||||
"std": {
|
"std": {
|
||||||
"pattern": "^(?<timestamp>\\S{3,8} \\w{3}\\s+\\d{1,2} \\d{2}:\\d{2}:\\d{2} \\w+ \\d{4})(?<body>.*)$"
|
"pattern": "^(?<timestamp>\\S{3,8} \\w{3}\\s+\\d{1,2} \\d{2}:\\d{2}:\\d{2} \\w+ \\d{4})\\s*(?<body>.*)$"
|
||||||
},
|
},
|
||||||
"sq-brackets": {
|
"sq-brackets": {
|
||||||
"pattern": "^\\[(?<timestamp>\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3,6})?)Z?\\](?<body>.*)$"
|
"pattern": "^\\[(?<timestamp>\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{3,6})?)Z?\\]\\s*(?<body>.*)$"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sample": [
|
"sample": [
|
||||||
{
|
{
|
||||||
"line": "Sat Apr 27 03:33:07 PDT 2013"
|
"line": "Sat Apr 27 03:33:07 PDT 2013\nHello, World"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"line": "[2021-05-21T21:58:57.022497Z]"
|
"line": "[2021-05-21T21:58:57.022497Z]"
|
||||||
|
@ -4,8 +4,12 @@
|
|||||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
// publish, and distribute this file as you see fit.
|
// publish, and distribute this file as you see fit.
|
||||||
|
|
||||||
|
#include <cstring> // memcpy
|
||||||
|
|
||||||
#include "fts_fuzzy_match.hh"
|
#include "fts_fuzzy_match.hh"
|
||||||
|
|
||||||
|
#include <ctype.h> // ::tolower, ::toupper
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
namespace fts {
|
namespace fts {
|
||||||
|
@ -35,10 +35,6 @@
|
|||||||
#define FTS_FUZZY_MATCH_H
|
#define FTS_FUZZY_MATCH_H
|
||||||
|
|
||||||
#include <cstdint> // uint8_t
|
#include <cstdint> // uint8_t
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring> // memcpy
|
|
||||||
|
|
||||||
#include <ctype.h> // ::tolower, ::toupper
|
|
||||||
|
|
||||||
// Public interface
|
// Public interface
|
||||||
namespace fts {
|
namespace fts {
|
||||||
|
@ -79,12 +79,9 @@ public:
|
|||||||
return highest;
|
return highest;
|
||||||
}
|
}
|
||||||
return start;
|
return start;
|
||||||
};
|
}
|
||||||
|
|
||||||
virtual void grep_next_line(LineType& line)
|
virtual void grep_next_line(LineType& line) { line = line + LineType(1); }
|
||||||
{
|
|
||||||
line = line + LineType(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
grep_proc<LineType>* gps_proc;
|
grep_proc<LineType>* gps_proc;
|
||||||
};
|
};
|
||||||
@ -97,7 +94,7 @@ public:
|
|||||||
virtual ~grep_proc_control() = default;
|
virtual ~grep_proc_control() = default;
|
||||||
|
|
||||||
/** @param msg The error encountered while attempting the grep. */
|
/** @param msg The error encountered while attempting the grep. */
|
||||||
virtual void grep_error(const std::string& msg){};
|
virtual void grep_error(const std::string& msg) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,10 +111,10 @@ public:
|
|||||||
LineType stop){};
|
LineType stop){};
|
||||||
|
|
||||||
/** Called periodically between grep_begin and grep_end. */
|
/** Called periodically between grep_begin and grep_end. */
|
||||||
virtual void grep_end_batch(grep_proc<LineType>& gp){};
|
virtual void grep_end_batch(grep_proc<LineType>& gp) {}
|
||||||
|
|
||||||
/** Called at the end of a grep run. */
|
/** Called at the end of a grep run. */
|
||||||
virtual void grep_end(grep_proc<LineType>& gp){};
|
virtual void grep_end(grep_proc<LineType>& gp) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a match is found on 'line' and between [start, end).
|
* Called when a match is found on 'line' and between [start, end).
|
||||||
@ -183,24 +180,15 @@ public:
|
|||||||
virtual ~grep_proc();
|
virtual ~grep_proc();
|
||||||
|
|
||||||
/** @param gpd The sink to send resuls to. */
|
/** @param gpd The sink to send resuls to. */
|
||||||
void set_sink(grep_proc_sink<LineType>* gpd)
|
void set_sink(grep_proc_sink<LineType>* gpd) { this->gp_sink = gpd; }
|
||||||
{
|
|
||||||
this->gp_sink = gpd;
|
|
||||||
};
|
|
||||||
|
|
||||||
grep_proc& invalidate();
|
grep_proc& invalidate();
|
||||||
|
|
||||||
/** @param gpd The sink to send results to. */
|
/** @param gpd The sink to send results to. */
|
||||||
void set_control(grep_proc_control* gpc)
|
void set_control(grep_proc_control* gpc) { this->gp_control = gpc; }
|
||||||
{
|
|
||||||
this->gp_control = gpc;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @return The sink to send results to. */
|
/** @return The sink to send results to. */
|
||||||
grep_proc_sink<LineType>* get_sink()
|
grep_proc_sink<LineType>* get_sink() { return this->gp_sink; }
|
||||||
{
|
|
||||||
return this->gp_sink;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue a request to search the input between the given line numbers.
|
* Queue a request to search the input between the given line numbers.
|
||||||
@ -221,7 +209,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the search requests that have been queued up with queue_request.
|
* Start the search requests that have been queued up with queue_request.
|
||||||
@ -237,7 +225,7 @@ public:
|
|||||||
if (this->gp_err_pipe != -1) {
|
if (this->gp_err_pipe != -1) {
|
||||||
pollfds.push_back((struct pollfd){this->gp_err_pipe, POLLIN, 0});
|
pollfds.push_back((struct pollfd){this->gp_err_pipe, POLLIN, 0});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the fd_set to see if there is any new data to be processed.
|
* Check the fd_set to see if there is any new data to be processed.
|
||||||
@ -259,7 +247,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
@ -277,15 +265,9 @@ protected:
|
|||||||
|
|
||||||
virtual void child_init(){};
|
virtual void child_init(){};
|
||||||
|
|
||||||
virtual void child_batch()
|
virtual void child_batch() { fflush(stdout); }
|
||||||
{
|
|
||||||
fflush(stdout);
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void child_term()
|
virtual void child_term() { fflush(stdout); }
|
||||||
{
|
|
||||||
fflush(stdout);
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void handle_match(
|
virtual void handle_match(
|
||||||
int line, std::string& line_value, int off, int* matches, int count);
|
int line, std::string& line_value, int off, int* matches, int count);
|
||||||
@ -319,4 +301,5 @@ protected:
|
|||||||
grep_proc_sink<LineType>* gp_sink{nullptr}; /*< The sink delegate. */
|
grep_proc_sink<LineType>* gp_sink{nullptr}; /*< The sink delegate. */
|
||||||
grep_proc_control* gp_control{nullptr}; /*< The control delegate. */
|
grep_proc_control* gp_control{nullptr}; /*< The control delegate. */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -100,56 +100,56 @@ struct help_text {
|
|||||||
this->ht_context = help_context_t::HC_COMMAND;
|
this->ht_context = help_context_t::HC_COMMAND;
|
||||||
this->ht_name = &name[1];
|
this->ht_name = &name[1];
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& command() noexcept
|
help_text& command() noexcept
|
||||||
{
|
{
|
||||||
this->ht_context = help_context_t::HC_COMMAND;
|
this->ht_context = help_context_t::HC_COMMAND;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& sql_function() noexcept
|
help_text& sql_function() noexcept
|
||||||
{
|
{
|
||||||
this->ht_context = help_context_t::HC_SQL_FUNCTION;
|
this->ht_context = help_context_t::HC_SQL_FUNCTION;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& sql_agg_function() noexcept
|
help_text& sql_agg_function() noexcept
|
||||||
{
|
{
|
||||||
this->ht_context = help_context_t::HC_SQL_FUNCTION;
|
this->ht_context = help_context_t::HC_SQL_FUNCTION;
|
||||||
this->ht_function_type = help_function_type_t::HFT_AGGREGATE;
|
this->ht_function_type = help_function_type_t::HFT_AGGREGATE;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& sql_table_valued_function() noexcept
|
help_text& sql_table_valued_function() noexcept
|
||||||
{
|
{
|
||||||
this->ht_context = help_context_t::HC_SQL_TABLE_VALUED_FUNCTION;
|
this->ht_context = help_context_t::HC_SQL_TABLE_VALUED_FUNCTION;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& sql_command() noexcept
|
help_text& sql_command() noexcept
|
||||||
{
|
{
|
||||||
this->ht_context = help_context_t::HC_SQL_COMMAND;
|
this->ht_context = help_context_t::HC_SQL_COMMAND;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& sql_keyword() noexcept
|
help_text& sql_keyword() noexcept
|
||||||
{
|
{
|
||||||
this->ht_context = help_context_t::HC_SQL_KEYWORD;
|
this->ht_context = help_context_t::HC_SQL_KEYWORD;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& sql_infix() noexcept
|
help_text& sql_infix() noexcept
|
||||||
{
|
{
|
||||||
this->ht_context = help_context_t::HC_SQL_INFIX;
|
this->ht_context = help_context_t::HC_SQL_INFIX;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& with_summary(const char* summary) noexcept
|
help_text& with_summary(const char* summary) noexcept
|
||||||
{
|
{
|
||||||
this->ht_summary = summary;
|
this->ht_summary = summary;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& with_flag_name(const char* flag) noexcept
|
help_text& with_flag_name(const char* flag) noexcept
|
||||||
{
|
{
|
||||||
@ -181,19 +181,19 @@ struct help_text {
|
|||||||
{
|
{
|
||||||
this->ht_nargs = help_nargs_t::HN_OPTIONAL;
|
this->ht_nargs = help_nargs_t::HN_OPTIONAL;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& zero_or_more() noexcept
|
help_text& zero_or_more() noexcept
|
||||||
{
|
{
|
||||||
this->ht_nargs = help_nargs_t::HN_ZERO_OR_MORE;
|
this->ht_nargs = help_nargs_t::HN_ZERO_OR_MORE;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& one_or_more() noexcept
|
help_text& one_or_more() noexcept
|
||||||
{
|
{
|
||||||
this->ht_nargs = help_nargs_t::HN_ONE_OR_MORE;
|
this->ht_nargs = help_nargs_t::HN_ONE_OR_MORE;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
help_text& with_format(help_parameter_format_t format) noexcept
|
help_text& with_format(help_parameter_format_t format) noexcept
|
||||||
{
|
{
|
||||||
|
@ -46,7 +46,7 @@ struct highlighter {
|
|||||||
{
|
{
|
||||||
pcre_refcount(this->h_code, 1);
|
pcre_refcount(this->h_code, 1);
|
||||||
this->study();
|
this->study();
|
||||||
};
|
}
|
||||||
|
|
||||||
highlighter(const highlighter& other);
|
highlighter(const highlighter& other);
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ struct highlighter {
|
|||||||
this->h_code = nullptr;
|
this->h_code = nullptr;
|
||||||
}
|
}
|
||||||
free(this->h_code_extra);
|
free(this->h_code_extra);
|
||||||
};
|
}
|
||||||
|
|
||||||
void study();
|
void study();
|
||||||
|
|
||||||
@ -75,14 +75,14 @@ struct highlighter {
|
|||||||
this->h_role = role;
|
this->h_role = role;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
highlighter& with_attrs(int attrs)
|
highlighter& with_attrs(int attrs)
|
||||||
{
|
{
|
||||||
this->h_attrs = attrs;
|
this->h_attrs = attrs;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
highlighter& with_text_format(text_format_t tf)
|
highlighter& with_text_format(text_format_t tf)
|
||||||
{
|
{
|
||||||
@ -96,7 +96,7 @@ struct highlighter {
|
|||||||
this->h_format_name = name;
|
this->h_format_name = name;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
highlighter& with_color(const styling::color_unit& fg,
|
highlighter& with_color(const styling::color_unit& fg,
|
||||||
const styling::color_unit& bg)
|
const styling::color_unit& bg)
|
||||||
@ -105,7 +105,7 @@ struct highlighter {
|
|||||||
this->h_bg = bg;
|
this->h_bg = bg;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
highlighter& with_nestable(bool val)
|
highlighter& with_nestable(bool val)
|
||||||
{
|
{
|
||||||
@ -118,7 +118,7 @@ struct highlighter {
|
|||||||
ensure(this->h_attrs != -1);
|
ensure(this->h_attrs != -1);
|
||||||
|
|
||||||
return this->h_attrs;
|
return this->h_attrs;
|
||||||
};
|
}
|
||||||
|
|
||||||
void annotate(attr_line_t& al, int start) const;
|
void annotate(attr_line_t& al, int start) const;
|
||||||
|
|
||||||
|
@ -73,31 +73,25 @@ struct stacked_bar_chart_base {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
class stacked_bar_chart : public stacked_bar_chart_base {
|
class stacked_bar_chart : public stacked_bar_chart_base {
|
||||||
public:
|
public:
|
||||||
stacked_bar_chart()
|
|
||||||
: sbc_do_stacking(true), sbc_left(0), sbc_right(0),
|
|
||||||
sbc_show_state(show_all()){
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
stacked_bar_chart& with_stacking_enabled(bool enabled)
|
stacked_bar_chart& with_stacking_enabled(bool enabled)
|
||||||
{
|
{
|
||||||
this->sbc_do_stacking = enabled;
|
this->sbc_do_stacking = enabled;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
stacked_bar_chart& with_attrs_for_ident(const T& ident, int attrs)
|
stacked_bar_chart& with_attrs_for_ident(const T& ident, int attrs)
|
||||||
{
|
{
|
||||||
struct chart_ident& ci = this->find_ident(ident);
|
struct chart_ident& ci = this->find_ident(ident);
|
||||||
ci.ci_attrs = attrs;
|
ci.ci_attrs = attrs;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
stacked_bar_chart& with_margins(unsigned long left, unsigned long right)
|
stacked_bar_chart& with_margins(unsigned long left, unsigned long right)
|
||||||
{
|
{
|
||||||
this->sbc_left = left;
|
this->sbc_left = left;
|
||||||
this->sbc_right = right;
|
this->sbc_right = right;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
show_state show_next_ident(direction dir)
|
show_state show_next_ident(direction dir)
|
||||||
{
|
{
|
||||||
@ -152,7 +146,7 @@ public:
|
|||||||
});
|
});
|
||||||
|
|
||||||
return this->sbc_show_state;
|
return this->sbc_show_state;
|
||||||
};
|
}
|
||||||
|
|
||||||
void get_ident_to_show(T& ident_out) const
|
void get_ident_to_show(T& ident_out) const
|
||||||
{
|
{
|
||||||
@ -162,7 +156,7 @@ public:
|
|||||||
[&](const show_one& one) {
|
[&](const show_one& one) {
|
||||||
ident_out = this->sbc_idents[one.so_index].ci_ident;
|
ident_out = this->sbc_idents[one.so_index].ci_ident;
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
void chart_attrs_for_value(const listview_curses& lc,
|
void chart_attrs_for_value(const listview_curses& lc,
|
||||||
int& left,
|
int& left,
|
||||||
@ -230,20 +224,20 @@ public:
|
|||||||
value_out.emplace_back(
|
value_out.emplace_back(
|
||||||
lr, VC_STYLE.value(ci.ci_attrs | A_REVERSE));
|
lr, VC_STYLE.value(ci.ci_attrs | A_REVERSE));
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
this->sbc_idents.clear();
|
this->sbc_idents.clear();
|
||||||
this->sbc_ident_lookup.clear();
|
this->sbc_ident_lookup.clear();
|
||||||
this->sbc_show_state = show_all();
|
this->sbc_show_state = show_all();
|
||||||
};
|
}
|
||||||
|
|
||||||
void add_value(const T& ident, double amount = 1.0)
|
void add_value(const T& ident, double amount = 1.0)
|
||||||
{
|
{
|
||||||
struct chart_ident& ci = this->find_ident(ident);
|
struct chart_ident& ci = this->find_ident(ident);
|
||||||
ci.ci_stats.update(amount);
|
ci.ci_stats.update(amount);
|
||||||
};
|
}
|
||||||
|
|
||||||
struct bucket_stats_t {
|
struct bucket_stats_t {
|
||||||
bucket_stats_t()
|
bucket_stats_t()
|
||||||
@ -281,11 +275,11 @@ public:
|
|||||||
const chart_ident& ci = this->find_ident(ident);
|
const chart_ident& ci = this->find_ident(ident);
|
||||||
|
|
||||||
return ci.ci_stats;
|
return ci.ci_stats;
|
||||||
};
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct chart_ident {
|
struct chart_ident {
|
||||||
chart_ident(const T& ident) : ci_ident(ident){};
|
chart_ident(const T& ident) : ci_ident(ident) {}
|
||||||
|
|
||||||
T ci_ident;
|
T ci_ident;
|
||||||
int ci_attrs{0};
|
int ci_attrs{0};
|
||||||
@ -303,13 +297,13 @@ protected:
|
|||||||
return this->sbc_idents.back();
|
return this->sbc_idents.back();
|
||||||
}
|
}
|
||||||
return this->sbc_idents[iter->second];
|
return this->sbc_idents[iter->second];
|
||||||
};
|
}
|
||||||
|
|
||||||
bool sbc_do_stacking;
|
bool sbc_do_stacking{true};
|
||||||
unsigned long sbc_left, sbc_right;
|
unsigned long sbc_left{0}, sbc_right{0};
|
||||||
std::vector<struct chart_ident> sbc_idents;
|
std::vector<struct chart_ident> sbc_idents;
|
||||||
std::map<T, unsigned int> sbc_ident_lookup;
|
std::map<T, unsigned int> sbc_ident_lookup;
|
||||||
show_state sbc_show_state;
|
show_state sbc_show_state{show_all()};
|
||||||
};
|
};
|
||||||
|
|
||||||
class hist_source2
|
class hist_source2
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "base/ansi_scrubber.hh"
|
#include "base/ansi_scrubber.hh"
|
||||||
#include "base/injector.hh"
|
#include "base/injector.hh"
|
||||||
|
#include "base/itertools.hh"
|
||||||
#include "base/math_util.hh"
|
#include "base/math_util.hh"
|
||||||
#include "base/opt_util.hh"
|
#include "base/opt_util.hh"
|
||||||
#include "bookmarks.hh"
|
#include "bookmarks.hh"
|
||||||
@ -160,8 +161,17 @@ handle_keyseq(const char* keyseq)
|
|||||||
|
|
||||||
log_debug("executing key sequence %s: %s", keyseq, kc.kc_cmd.c_str());
|
log_debug("executing key sequence %s: %s", keyseq, kc.kc_cmd.c_str());
|
||||||
auto result = execute_any(ec, kc.kc_cmd);
|
auto result = execute_any(ec, kc.kc_cmd);
|
||||||
lnav_data.ld_rl_view->set_value(
|
if (result.isOk()) {
|
||||||
result.map(ok_prefix).orElse(err_to_ok).unwrap());
|
lnav_data.ld_rl_view->set_value(result.unwrap());
|
||||||
|
} else {
|
||||||
|
auto um = result.unwrapErr();
|
||||||
|
|
||||||
|
um.um_snippets.clear();
|
||||||
|
um.um_reason.clear();
|
||||||
|
um.um_notes.clear();
|
||||||
|
um.um_help.clear();
|
||||||
|
lnav_data.ld_rl_view->set_attr_value(um.to_attr_line());
|
||||||
|
}
|
||||||
|
|
||||||
if (!kc.kc_alt_msg.empty()) {
|
if (!kc.kc_alt_msg.empty()) {
|
||||||
shlex lexer(kc.kc_alt_msg);
|
shlex lexer(kc.kc_alt_msg);
|
||||||
@ -731,13 +741,13 @@ handle_paging_key(int ch)
|
|||||||
db_label_source& dls = lnav_data.ld_db_row_source;
|
db_label_source& dls = lnav_data.ld_db_row_source;
|
||||||
|
|
||||||
if (toggle_view(db_tc)) {
|
if (toggle_view(db_tc)) {
|
||||||
long log_line_index = dls.column_name_to_index("log_line");
|
auto log_line_index = dls.column_name_to_index("log_line");
|
||||||
|
|
||||||
if (log_line_index == -1) {
|
if (!log_line_index) {
|
||||||
log_line_index = dls.column_name_to_index("min(log_line)");
|
log_line_index = dls.column_name_to_index("min(log_line)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log_line_index != -1) {
|
if (log_line_index) {
|
||||||
fmt::memory_buffer linestr;
|
fmt::memory_buffer linestr;
|
||||||
int line_number = (int) tc->get_top();
|
int line_number = (int) tc->get_top();
|
||||||
unsigned int row;
|
unsigned int row;
|
||||||
@ -747,7 +757,7 @@ handle_paging_key(int ch)
|
|||||||
line_number);
|
line_number);
|
||||||
linestr.push_back('\0');
|
linestr.push_back('\0');
|
||||||
for (row = 0; row < dls.dls_rows.size(); row++) {
|
for (row = 0; row < dls.dls_rows.size(); row++) {
|
||||||
if (strcmp(dls.dls_rows[row][log_line_index],
|
if (strcmp(dls.dls_rows[row][log_line_index.value()],
|
||||||
linestr.data())
|
linestr.data())
|
||||||
== 0) {
|
== 0) {
|
||||||
vis_line_t db_line(row);
|
vis_line_t db_line(row);
|
||||||
@ -761,16 +771,16 @@ handle_paging_key(int ch)
|
|||||||
} else if (db_tc->get_inner_height() > 0) {
|
} else if (db_tc->get_inner_height() > 0) {
|
||||||
int db_row = db_tc->get_top();
|
int db_row = db_tc->get_top();
|
||||||
tc = &lnav_data.ld_views[LNV_LOG];
|
tc = &lnav_data.ld_views[LNV_LOG];
|
||||||
long log_line_index = dls.column_name_to_index("log_line");
|
auto log_line_index = dls.column_name_to_index("log_line");
|
||||||
|
|
||||||
if (log_line_index == -1) {
|
if (!log_line_index) {
|
||||||
log_line_index = dls.column_name_to_index("min(log_line)");
|
log_line_index = dls.column_name_to_index("min(log_line)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log_line_index != -1) {
|
if (log_line_index) {
|
||||||
unsigned int line_number;
|
unsigned int line_number;
|
||||||
|
|
||||||
if (sscanf(dls.dls_rows[db_row][log_line_index],
|
if (sscanf(dls.dls_rows[db_row][log_line_index.value()],
|
||||||
"%d",
|
"%d",
|
||||||
&line_number)
|
&line_number)
|
||||||
&& line_number < tc->listview_rows(*tc))
|
&& line_number < tc->listview_rows(*tc))
|
||||||
@ -839,11 +849,10 @@ handle_paging_key(int ch)
|
|||||||
if (line_attr_opt) {
|
if (line_attr_opt) {
|
||||||
const auto& fc = lnav_data.ld_active_files;
|
const auto& fc = lnav_data.ld_active_files;
|
||||||
auto lf = line_attr_opt.value().get();
|
auto lf = line_attr_opt.value().get();
|
||||||
auto iter
|
auto index_opt
|
||||||
= find(fc.fc_files.begin(), fc.fc_files.end(), lf);
|
= fc.fc_files | lnav::itertools::find(lf);
|
||||||
if (iter != fc.fc_files.end()) {
|
if (index_opt) {
|
||||||
auto index = distance(fc.fc_files.begin(), iter);
|
auto index_vl = vis_line_t(index_opt.value());
|
||||||
auto index_vl = vis_line_t(index);
|
|
||||||
|
|
||||||
lnav_data.ld_files_view.set_top(index_vl);
|
lnav_data.ld_files_view.set_top(index_vl);
|
||||||
lnav_data.ld_files_view.set_selection(index_vl);
|
lnav_data.ld_files_view.set_selection(index_vl);
|
||||||
|
@ -185,7 +185,7 @@ gen_handle_null(void* ctx)
|
|||||||
sql_json_op* sjo = (sql_json_op*) ctx;
|
sql_json_op* sjo = (sql_json_op*) ctx;
|
||||||
yajl_gen gen = (yajl_gen) sjo->jo_ptr_data;
|
yajl_gen gen = (yajl_gen) sjo->jo_ptr_data;
|
||||||
|
|
||||||
if (sjo->jo_ptr.jp_state == json_ptr::MS_DONE) {
|
if (sjo->jo_ptr.jp_state == json_ptr::match_state_t::DONE) {
|
||||||
sjo->sjo_type = SQLITE_NULL;
|
sjo->sjo_type = SQLITE_NULL;
|
||||||
} else {
|
} else {
|
||||||
sjo->jo_ptr_error_code = yajl_gen_null(gen);
|
sjo->jo_ptr_error_code = yajl_gen_null(gen);
|
||||||
@ -200,7 +200,7 @@ gen_handle_boolean(void* ctx, int boolVal)
|
|||||||
sql_json_op* sjo = (sql_json_op*) ctx;
|
sql_json_op* sjo = (sql_json_op*) ctx;
|
||||||
yajl_gen gen = (yajl_gen) sjo->jo_ptr_data;
|
yajl_gen gen = (yajl_gen) sjo->jo_ptr_data;
|
||||||
|
|
||||||
if (sjo->jo_ptr.jp_state == json_ptr::MS_DONE) {
|
if (sjo->jo_ptr.jp_state == json_ptr::match_state_t::DONE) {
|
||||||
sjo->sjo_type = SQLITE_INTEGER;
|
sjo->sjo_type = SQLITE_INTEGER;
|
||||||
sjo->sjo_int = boolVal;
|
sjo->sjo_int = boolVal;
|
||||||
} else {
|
} else {
|
||||||
@ -216,7 +216,7 @@ gen_handle_string(void* ctx, const unsigned char* stringVal, size_t len)
|
|||||||
sql_json_op* sjo = (sql_json_op*) ctx;
|
sql_json_op* sjo = (sql_json_op*) ctx;
|
||||||
yajl_gen gen = (yajl_gen) sjo->jo_ptr_data;
|
yajl_gen gen = (yajl_gen) sjo->jo_ptr_data;
|
||||||
|
|
||||||
if (sjo->jo_ptr.jp_state == json_ptr::MS_DONE) {
|
if (sjo->jo_ptr.jp_state == json_ptr::match_state_t::DONE) {
|
||||||
sjo->sjo_type = SQLITE3_TEXT;
|
sjo->sjo_type = SQLITE3_TEXT;
|
||||||
sjo->sjo_str = std::string((char*) stringVal, len);
|
sjo->sjo_str = std::string((char*) stringVal, len);
|
||||||
} else {
|
} else {
|
||||||
@ -232,7 +232,7 @@ gen_handle_number(void* ctx, const char* numval, size_t numlen)
|
|||||||
sql_json_op* sjo = (sql_json_op*) ctx;
|
sql_json_op* sjo = (sql_json_op*) ctx;
|
||||||
yajl_gen gen = (yajl_gen) sjo->jo_ptr_data;
|
yajl_gen gen = (yajl_gen) sjo->jo_ptr_data;
|
||||||
|
|
||||||
if (sjo->jo_ptr.jp_state == json_ptr::MS_DONE) {
|
if (sjo->jo_ptr.jp_state == json_ptr::match_state_t::DONE) {
|
||||||
if (strtonum(sjo->sjo_int, numval, numlen) == numlen) {
|
if (strtonum(sjo->sjo_int, numval, numlen) == numlen) {
|
||||||
sjo->sjo_type = SQLITE_INTEGER;
|
sjo->sjo_type = SQLITE_INTEGER;
|
||||||
} else {
|
} else {
|
||||||
@ -298,7 +298,8 @@ sql_jget(sqlite3_context* context, int argc, sqlite3_value** argv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case yajl_status_client_canceled:
|
case yajl_status_client_canceled:
|
||||||
if (jo.jo_ptr.jp_state == json_ptr::MS_ERR_INVALID_ESCAPE) {
|
if (jo.jo_ptr.jp_state
|
||||||
|
== json_ptr::match_state_t::ERR_INVALID_ESCAPE) {
|
||||||
sqlite3_result_error(
|
sqlite3_result_error(
|
||||||
context, jo.jo_ptr.error_msg().c_str(), -1);
|
context, jo.jo_ptr.error_msg().c_str(), -1);
|
||||||
} else {
|
} else {
|
||||||
@ -320,7 +321,8 @@ sql_jget(sqlite3_context* context, int argc, sqlite3_value** argv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case yajl_status_client_canceled:
|
case yajl_status_client_canceled:
|
||||||
if (jo.jo_ptr.jp_state == json_ptr::MS_ERR_INVALID_ESCAPE) {
|
if (jo.jo_ptr.jp_state
|
||||||
|
== json_ptr::match_state_t::ERR_INVALID_ESCAPE) {
|
||||||
sqlite3_result_error(
|
sqlite3_result_error(
|
||||||
context, jo.jo_ptr.error_msg().c_str(), -1);
|
context, jo.jo_ptr.error_msg().c_str(), -1);
|
||||||
} else {
|
} else {
|
||||||
|
@ -800,3 +800,40 @@ line_buffer::get_available()
|
|||||||
{
|
{
|
||||||
return {this->lb_file_offset, this->lb_buffer_size};
|
return {this->lb_file_offset, this->lb_buffer_size};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
line_buffer::gz_indexed::indexDict::indexDict(const z_stream& s,
|
||||||
|
const file_size_t size)
|
||||||
|
{
|
||||||
|
assert((s.data_type & GZ_END_OF_BLOCK_MASK));
|
||||||
|
assert(!(s.data_type & GZ_END_OF_FILE_MASK));
|
||||||
|
assert(size >= s.avail_out + GZ_WINSIZE);
|
||||||
|
this->bits = s.data_type & GZ_BORROW_BITS_MASK;
|
||||||
|
this->in = s.total_in;
|
||||||
|
this->out = s.total_out;
|
||||||
|
auto last_byte_in = s.next_in[-1];
|
||||||
|
this->in_bits = last_byte_in >> (8 - this->bits);
|
||||||
|
// Copy the last 32k uncompressed data (sliding window) to our
|
||||||
|
// index
|
||||||
|
memcpy(this->index, s.next_out - GZ_WINSIZE, GZ_WINSIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
line_buffer::gz_indexed::indexDict::apply(z_streamp s)
|
||||||
|
{
|
||||||
|
s->zalloc = Z_NULL;
|
||||||
|
s->zfree = Z_NULL;
|
||||||
|
s->opaque = Z_NULL;
|
||||||
|
s->avail_in = 0;
|
||||||
|
s->next_in = Z_NULL;
|
||||||
|
auto ret = inflateInit2(s, GZ_RAW_MODE);
|
||||||
|
if (ret != Z_OK) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (this->bits) {
|
||||||
|
inflatePrime(s, this->bits, this->in_bits);
|
||||||
|
}
|
||||||
|
s->total_in = this->in;
|
||||||
|
s->total_out = this->out;
|
||||||
|
inflateSetDictionary(s, this->index, GZ_WINSIZE);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -99,7 +99,7 @@ public:
|
|||||||
return this->gz_fd != -1;
|
return this->gz_fd != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
uLong get_source_offset()
|
uLong get_source_offset() const
|
||||||
{
|
{
|
||||||
return !!*this ? this->strm.total_in + this->strm.avail_in : 0;
|
return !!*this ? this->strm.total_in + this->strm.avail_in : 0;
|
||||||
}
|
}
|
||||||
@ -123,40 +123,9 @@ public:
|
|||||||
unsigned char bits = 0;
|
unsigned char bits = 0;
|
||||||
unsigned char in_bits = 0;
|
unsigned char in_bits = 0;
|
||||||
Bytef index[GZ_WINSIZE];
|
Bytef index[GZ_WINSIZE];
|
||||||
indexDict(z_stream const& s, const file_size_t size)
|
indexDict(z_stream const& s, const file_size_t size);
|
||||||
{
|
|
||||||
assert((s.data_type & GZ_END_OF_BLOCK_MASK));
|
|
||||||
assert(!(s.data_type & GZ_END_OF_FILE_MASK));
|
|
||||||
assert(size >= s.avail_out + GZ_WINSIZE);
|
|
||||||
this->bits = s.data_type & GZ_BORROW_BITS_MASK;
|
|
||||||
this->in = s.total_in;
|
|
||||||
this->out = s.total_out;
|
|
||||||
auto last_byte_in = s.next_in[-1];
|
|
||||||
this->in_bits = last_byte_in >> (8 - this->bits);
|
|
||||||
// Copy the last 32k uncompressed data (sliding window) to our
|
|
||||||
// index
|
|
||||||
memcpy(this->index, s.next_out - GZ_WINSIZE, GZ_WINSIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int apply(z_streamp s)
|
int apply(z_streamp s);
|
||||||
{
|
|
||||||
s->zalloc = Z_NULL;
|
|
||||||
s->zfree = Z_NULL;
|
|
||||||
s->opaque = Z_NULL;
|
|
||||||
s->avail_in = 0;
|
|
||||||
s->next_in = Z_NULL;
|
|
||||||
auto ret = inflateInit2(s, GZ_RAW_MODE);
|
|
||||||
if (ret != Z_OK) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (this->bits) {
|
|
||||||
inflatePrime(s, this->bits, this->in_bits);
|
|
||||||
}
|
|
||||||
s->total_in = this->in;
|
|
||||||
s->total_out = this->out;
|
|
||||||
inflateSetDictionary(s, this->index, GZ_WINSIZE);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -181,12 +150,12 @@ public:
|
|||||||
int get_fd() const
|
int get_fd() const
|
||||||
{
|
{
|
||||||
return this->lb_fd;
|
return this->lb_fd;
|
||||||
};
|
}
|
||||||
|
|
||||||
time_t get_file_time() const
|
time_t get_file_time() const
|
||||||
{
|
{
|
||||||
return this->lb_file_time;
|
return this->lb_file_time;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The size of the file or the amount of data pulled from a pipe.
|
* @return The size of the file or the amount of data pulled from a pipe.
|
||||||
@ -194,22 +163,22 @@ public:
|
|||||||
file_ssize_t get_file_size() const
|
file_ssize_t get_file_size() const
|
||||||
{
|
{
|
||||||
return this->lb_file_size;
|
return this->lb_file_size;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_pipe() const
|
bool is_pipe() const
|
||||||
{
|
{
|
||||||
return !this->lb_seekable;
|
return !this->lb_seekable;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_pipe_closed() const
|
bool is_pipe_closed() const
|
||||||
{
|
{
|
||||||
return !this->lb_seekable && (this->lb_file_size != -1);
|
return !this->lb_seekable && (this->lb_file_size != -1);
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_compressed() const
|
bool is_compressed() const
|
||||||
{
|
{
|
||||||
return this->lb_gz_file || this->lb_bz_file;
|
return this->lb_gz_file || this->lb_bz_file;
|
||||||
};
|
}
|
||||||
|
|
||||||
file_off_t get_read_offset(file_off_t off) const
|
file_off_t get_read_offset(file_off_t off) const
|
||||||
{
|
{
|
||||||
@ -218,7 +187,7 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
return off;
|
return off;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_data_available(file_off_t off, file_off_t stat_size) const
|
bool is_data_available(file_off_t off, file_off_t stat_size) const
|
||||||
{
|
{
|
||||||
@ -226,7 +195,7 @@ public:
|
|||||||
return (this->lb_file_size == -1 || off < this->lb_file_size);
|
return (this->lb_file_size == -1 || off < this->lb_file_size);
|
||||||
}
|
}
|
||||||
return off < stat_size;
|
return off < stat_size;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to load the next line into the buffer.
|
* Attempt to load the next line into the buffer.
|
||||||
@ -244,7 +213,7 @@ public:
|
|||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
this->lb_buffer_size = 0;
|
this->lb_buffer_size = 0;
|
||||||
};
|
}
|
||||||
|
|
||||||
/** Release any resources held by this object. */
|
/** Release any resources held by this object. */
|
||||||
void reset()
|
void reset()
|
||||||
@ -255,16 +224,16 @@ public:
|
|||||||
this->lb_file_size = (ssize_t) -1;
|
this->lb_file_size = (ssize_t) -1;
|
||||||
this->lb_buffer_size = 0;
|
this->lb_buffer_size = 0;
|
||||||
this->lb_last_line_offset = -1;
|
this->lb_last_line_offset = -1;
|
||||||
};
|
}
|
||||||
|
|
||||||
/** Check the invariants for this object. */
|
/** Check the invariants for this object. */
|
||||||
bool invariant()
|
bool invariant() const
|
||||||
{
|
{
|
||||||
require(this->lb_buffer != nullptr);
|
require(this->lb_buffer != nullptr);
|
||||||
require(this->lb_buffer_size <= this->lb_buffer_max);
|
require(this->lb_buffer_size <= this->lb_buffer_max);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
@ -275,7 +244,7 @@ private:
|
|||||||
{
|
{
|
||||||
return this->lb_file_offset <= off
|
return this->lb_file_offset <= off
|
||||||
&& off < (this->lb_file_offset + this->lb_buffer_size);
|
&& off < (this->lb_file_offset + this->lb_buffer_size);
|
||||||
};
|
}
|
||||||
|
|
||||||
void resize_buffer(size_t new_max);
|
void resize_buffer(size_t new_max);
|
||||||
|
|
||||||
@ -325,7 +294,7 @@ private:
|
|||||||
avail_out = this->lb_buffer_size - buffer_offset;
|
avail_out = this->lb_buffer_size - buffer_offset;
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
shared_buffer lb_share_manager;
|
shared_buffer lb_share_manager;
|
||||||
|
|
||||||
|
@ -397,14 +397,15 @@ listview_curses::handle_mouse(mouse_event& me)
|
|||||||
this->lv_mouse_time = me.me_time;
|
this->lv_mouse_time = me.me_time;
|
||||||
|
|
||||||
if (me.me_button != mouse_button_t::BUTTON_LEFT || inner_height == 0
|
if (me.me_button != mouse_button_t::BUTTON_LEFT || inner_height == 0
|
||||||
|| (this->lv_mouse_mode != LV_MODE_DRAG && me.me_x < (int) (width - 2)))
|
|| (this->lv_mouse_mode != lv_mode_t::DRAG
|
||||||
|
&& me.me_x < (int) (width - 2)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (me.me_state == mouse_button_state_t::BUTTON_STATE_RELEASED) {
|
if (me.me_state == mouse_button_state_t::BUTTON_STATE_RELEASED) {
|
||||||
this->lv_mouse_y = -1;
|
this->lv_mouse_y = -1;
|
||||||
this->lv_mouse_mode = LV_MODE_NONE;
|
this->lv_mouse_mode = lv_mode_t::NONE;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,35 +417,35 @@ listview_curses::handle_mouse(mouse_event& me)
|
|||||||
scroll_top = (this->get_y() + (int) (top_pct * (double) height));
|
scroll_top = (this->get_y() + (int) (top_pct * (double) height));
|
||||||
scroll_bottom = (this->get_y() + (int) (bot_pct * (double) height));
|
scroll_bottom = (this->get_y() + (int) (bot_pct * (double) height));
|
||||||
|
|
||||||
if (this->lv_mouse_mode == LV_MODE_NONE) {
|
if (this->lv_mouse_mode == lv_mode_t::NONE) {
|
||||||
if ((scroll_top - 1) <= me.me_y && me.me_y <= (scroll_bottom + 1)) {
|
if ((scroll_top - 1) <= me.me_y && me.me_y <= (scroll_bottom + 1)) {
|
||||||
this->lv_mouse_mode = LV_MODE_DRAG;
|
this->lv_mouse_mode = lv_mode_t::DRAG;
|
||||||
this->lv_mouse_y = me.me_y - scroll_top;
|
this->lv_mouse_y = me.me_y - scroll_top;
|
||||||
} else if (me.me_y < scroll_top) {
|
} else if (me.me_y < scroll_top) {
|
||||||
this->lv_mouse_mode = LV_MODE_UP;
|
this->lv_mouse_mode = lv_mode_t::UP;
|
||||||
} else {
|
} else {
|
||||||
this->lv_mouse_mode = LV_MODE_DOWN;
|
this->lv_mouse_mode = lv_mode_t::DOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this->lv_mouse_mode) {
|
switch (this->lv_mouse_mode) {
|
||||||
case LV_MODE_NONE:
|
case lv_mode_t::NONE:
|
||||||
require(0);
|
require(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LV_MODE_UP:
|
case lv_mode_t::UP:
|
||||||
if (me.me_y < scroll_top) {
|
if (me.me_y < scroll_top) {
|
||||||
shift_amount = -1 * height;
|
shift_amount = -1 * height;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LV_MODE_DOWN:
|
case lv_mode_t::DOWN:
|
||||||
if (me.me_y > scroll_bottom) {
|
if (me.me_y > scroll_bottom) {
|
||||||
shift_amount = height;
|
shift_amount = height;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LV_MODE_DRAG:
|
case lv_mode_t::DRAG:
|
||||||
pct = (double) inner_height / (double) height;
|
pct = (double) inner_height / (double) height;
|
||||||
new_top = me.me_y - this->get_y() - this->lv_mouse_y;
|
new_top = me.me_y - this->get_y() - this->lv_mouse_y;
|
||||||
new_top = (int) floor(((double) new_top * pct) + 0.5);
|
new_top = (int) floor(((double) new_top * pct) + 0.5);
|
||||||
@ -490,3 +491,57 @@ listview_curses::set_top(vis_line_t top, bool suppress_flash)
|
|||||||
this->set_needs_update();
|
this->set_needs_update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vis_line_t
|
||||||
|
listview_curses::get_bottom() const
|
||||||
|
{
|
||||||
|
auto retval = this->lv_top;
|
||||||
|
auto avail = this->rows_available(retval, RD_DOWN);
|
||||||
|
|
||||||
|
if (avail > 0) {
|
||||||
|
retval += vis_line_t(avail - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
vis_line_t
|
||||||
|
listview_curses::rows_available(vis_line_t line,
|
||||||
|
listview_curses::row_direction_t dir) const
|
||||||
|
{
|
||||||
|
unsigned long width;
|
||||||
|
vis_line_t height;
|
||||||
|
vis_line_t retval(0);
|
||||||
|
|
||||||
|
this->get_dimensions(height, width);
|
||||||
|
if (this->lv_word_wrap) {
|
||||||
|
size_t row_count = this->lv_source->listview_rows(*this);
|
||||||
|
|
||||||
|
width -= 1;
|
||||||
|
while ((height > 0) && (line >= 0) && ((size_t) line < row_count)) {
|
||||||
|
size_t len = this->lv_source->listview_size_for_row(*this, line);
|
||||||
|
|
||||||
|
do {
|
||||||
|
len -= std::min((size_t) width, len);
|
||||||
|
--height;
|
||||||
|
} while (len > 0);
|
||||||
|
line += vis_line_t(dir);
|
||||||
|
if (height >= 0) {
|
||||||
|
++retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (dir) {
|
||||||
|
case RD_UP:
|
||||||
|
retval = std::min(height, line + 1_vl);
|
||||||
|
break;
|
||||||
|
case RD_DOWN:
|
||||||
|
retval = std::min(
|
||||||
|
height,
|
||||||
|
vis_line_t(this->lv_source->listview_rows(*this) - line));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
@ -55,10 +55,7 @@ public:
|
|||||||
/** @return The number of rows in the list. */
|
/** @return The number of rows in the list. */
|
||||||
virtual size_t listview_rows(const listview_curses& lv) = 0;
|
virtual size_t listview_rows(const listview_curses& lv) = 0;
|
||||||
|
|
||||||
virtual size_t listview_width(const listview_curses& lv)
|
virtual size_t listview_width(const listview_curses& lv) { return INT_MAX; }
|
||||||
{
|
|
||||||
return INT_MAX;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the string value for a row in the list.
|
* Get the string value for a row in the list.
|
||||||
@ -78,7 +75,7 @@ public:
|
|||||||
virtual std::string listview_source_name(const listview_curses& lv)
|
virtual std::string listview_source_name(const listview_curses& lv)
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class list_gutter_source {
|
class list_gutter_source {
|
||||||
@ -94,7 +91,7 @@ public:
|
|||||||
role_t& bar_role_out)
|
role_t& bar_role_out)
|
||||||
{
|
{
|
||||||
ch_out = ACS_VLINE;
|
ch_out = ACS_VLINE;
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class list_overlay_source {
|
class list_overlay_source {
|
||||||
@ -115,7 +112,7 @@ public:
|
|||||||
|
|
||||||
virtual bool list_input_handle_key(listview_curses& lv, int ch) = 0;
|
virtual bool list_input_handle_key(listview_curses& lv, int ch) = 0;
|
||||||
|
|
||||||
virtual void list_input_handle_scroll_out(listview_curses& lv){};
|
virtual void list_input_handle_scroll_out(listview_curses& lv) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,15 +126,9 @@ public:
|
|||||||
|
|
||||||
listview_curses();
|
listview_curses();
|
||||||
|
|
||||||
void set_title(const std::string& title)
|
void set_title(const std::string& title) { this->lv_title = title; }
|
||||||
{
|
|
||||||
this->lv_title = title;
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::string& get_title() const
|
const std::string& get_title() const { return this->lv_title; }
|
||||||
{
|
|
||||||
return this->lv_title;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @param src The data source delegate. */
|
/** @param src The data source delegate. */
|
||||||
void set_data_source(list_data_source* src)
|
void set_data_source(list_data_source* src)
|
||||||
@ -145,18 +136,15 @@ public:
|
|||||||
this->lv_source = src;
|
this->lv_source = src;
|
||||||
this->invoke_scroll();
|
this->invoke_scroll();
|
||||||
this->reload_data();
|
this->reload_data();
|
||||||
};
|
}
|
||||||
|
|
||||||
/** @return The data source delegate. */
|
/** @return The data source delegate. */
|
||||||
list_data_source* get_data_source() const
|
list_data_source* get_data_source() const { return this->lv_source; }
|
||||||
{
|
|
||||||
return this->lv_source;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_gutter_source(list_gutter_source* src)
|
void set_gutter_source(list_gutter_source* src)
|
||||||
{
|
{
|
||||||
this->lv_gutter_source = src;
|
this->lv_gutter_source = src;
|
||||||
};
|
}
|
||||||
|
|
||||||
/** @param src The data source delegate. */
|
/** @param src The data source delegate. */
|
||||||
listview_curses& set_overlay_source(list_overlay_source* src)
|
listview_curses& set_overlay_source(list_overlay_source* src)
|
||||||
@ -165,38 +153,30 @@ public:
|
|||||||
this->reload_data();
|
this->reload_data();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
/** @return The overlay source delegate. */
|
/** @return The overlay source delegate. */
|
||||||
list_overlay_source* get_overlay_source()
|
list_overlay_source* get_overlay_source()
|
||||||
{
|
{
|
||||||
return this->lv_overlay_source;
|
return this->lv_overlay_source;
|
||||||
};
|
}
|
||||||
|
|
||||||
listview_curses& add_input_delegate(list_input_delegate& lid)
|
listview_curses& add_input_delegate(list_input_delegate& lid)
|
||||||
{
|
{
|
||||||
this->lv_input_delegates.push_back(&lid);
|
this->lv_input_delegates.push_back(&lid);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param va The action to invoke when the view is scrolled.
|
* @param va The action to invoke when the view is scrolled.
|
||||||
* @todo Allow multiple observers.
|
* @todo Allow multiple observers.
|
||||||
*/
|
*/
|
||||||
void set_scroll_action(action va)
|
void set_scroll_action(action va) { this->lv_scroll = std::move(va); }
|
||||||
{
|
|
||||||
this->lv_scroll = std::move(va);
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_show_scrollbar(bool ss)
|
void set_show_scrollbar(bool ss) { this->lv_show_scrollbar = ss; }
|
||||||
{
|
|
||||||
this->lv_show_scrollbar = ss;
|
bool get_show_scrollbar() const { return this->lv_show_scrollbar; }
|
||||||
};
|
|
||||||
bool get_show_scrollbar() const
|
|
||||||
{
|
|
||||||
return this->lv_show_scrollbar;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_show_bottom_border(bool val)
|
void set_show_bottom_border(bool val)
|
||||||
{
|
{
|
||||||
@ -204,21 +184,12 @@ public:
|
|||||||
this->lv_show_bottom_border = val;
|
this->lv_show_bottom_border = val;
|
||||||
this->set_needs_update();
|
this->set_needs_update();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
bool get_show_bottom_border() const
|
bool get_show_bottom_border() const { return this->lv_show_bottom_border; }
|
||||||
{
|
|
||||||
return this->lv_show_bottom_border;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_selectable(bool sel)
|
void set_selectable(bool sel) { this->lv_selectable = sel; }
|
||||||
{
|
|
||||||
this->lv_selectable = sel;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool is_selectable() const
|
bool is_selectable() const { return this->lv_selectable; }
|
||||||
{
|
|
||||||
return this->lv_selectable;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_selection(vis_line_t sel)
|
void set_selection(vis_line_t sel)
|
||||||
{
|
{
|
||||||
@ -266,58 +237,16 @@ public:
|
|||||||
this->set_needs_update();
|
this->set_needs_update();
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool get_word_wrap() const
|
bool get_word_wrap() const { return this->lv_word_wrap; }
|
||||||
{
|
|
||||||
return this->lv_word_wrap;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum row_direction_t {
|
enum row_direction_t {
|
||||||
RD_UP = -1,
|
RD_UP = -1,
|
||||||
RD_DOWN = 1,
|
RD_DOWN = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
vis_line_t rows_available(vis_line_t line, row_direction_t dir) const
|
vis_line_t rows_available(vis_line_t line, row_direction_t dir) const;
|
||||||
{
|
|
||||||
unsigned long width;
|
|
||||||
vis_line_t height;
|
|
||||||
vis_line_t retval(0);
|
|
||||||
|
|
||||||
this->get_dimensions(height, width);
|
|
||||||
if (this->lv_word_wrap) {
|
|
||||||
size_t row_count = this->lv_source->listview_rows(*this);
|
|
||||||
|
|
||||||
width -= 1;
|
|
||||||
while ((height > 0) && (line >= 0) && ((size_t) line < row_count)) {
|
|
||||||
size_t len
|
|
||||||
= this->lv_source->listview_size_for_row(*this, line);
|
|
||||||
|
|
||||||
do {
|
|
||||||
len -= std::min((size_t) width, len);
|
|
||||||
--height;
|
|
||||||
} while (len > 0);
|
|
||||||
line += vis_line_t(dir);
|
|
||||||
if (height >= 0) {
|
|
||||||
++retval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (dir) {
|
|
||||||
case RD_UP:
|
|
||||||
retval = std::min(height, line + 1_vl);
|
|
||||||
break;
|
|
||||||
case RD_DOWN:
|
|
||||||
retval = std::min(
|
|
||||||
height,
|
|
||||||
vis_line_t(this->lv_source->listview_rows(*this)
|
|
||||||
- line));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
auto map_top_row(F func) ->
|
auto map_top_row(F func) ->
|
||||||
@ -334,16 +263,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @param win The curses window this view is attached to. */
|
/** @param win The curses window this view is attached to. */
|
||||||
void set_window(WINDOW* win)
|
void set_window(WINDOW* win) { this->lv_window = win; }
|
||||||
{
|
|
||||||
this->lv_window = win;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @return The curses window this view is attached to. */
|
/** @return The curses window this view is attached to. */
|
||||||
WINDOW* get_window() const
|
WINDOW* get_window() const { return this->lv_window; }
|
||||||
{
|
|
||||||
return this->lv_window;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_y(unsigned int y)
|
void set_y(unsigned int y)
|
||||||
{
|
{
|
||||||
@ -351,11 +274,9 @@ public:
|
|||||||
this->lv_y = y;
|
this->lv_y = y;
|
||||||
this->set_needs_update();
|
this->set_needs_update();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
unsigned int get_y() const
|
|
||||||
{
|
unsigned int get_y() const { return this->lv_y; }
|
||||||
return this->lv_y;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_x(unsigned int x)
|
void set_x(unsigned int x)
|
||||||
{
|
{
|
||||||
@ -363,11 +284,9 @@ public:
|
|||||||
this->lv_x = x;
|
this->lv_x = x;
|
||||||
this->set_needs_update();
|
this->set_needs_update();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
unsigned int get_x() const
|
|
||||||
{
|
unsigned int get_x() const { return this->lv_x; }
|
||||||
return this->lv_x;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the line number to be displayed at the top of the view. If the
|
* Set the line number to be displayed at the top of the view. If the
|
||||||
@ -380,10 +299,7 @@ public:
|
|||||||
void set_top(vis_line_t top, bool suppress_flash = false);
|
void set_top(vis_line_t top, bool suppress_flash = false);
|
||||||
|
|
||||||
/** @return The line number that is displayed at the top. */
|
/** @return The line number that is displayed at the top. */
|
||||||
vis_line_t get_top() const
|
vis_line_t get_top() const { return this->lv_top; }
|
||||||
{
|
|
||||||
return this->lv_top;
|
|
||||||
};
|
|
||||||
|
|
||||||
nonstd::optional<vis_line_t> get_top_opt() const
|
nonstd::optional<vis_line_t> get_top_opt() const
|
||||||
{
|
{
|
||||||
@ -395,19 +311,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @return The line number that is displayed at the bottom. */
|
/** @return The line number that is displayed at the bottom. */
|
||||||
vis_line_t get_bottom() const
|
vis_line_t get_bottom() const;
|
||||||
{
|
|
||||||
auto retval = this->lv_top;
|
|
||||||
auto avail = this->rows_available(retval, RD_DOWN);
|
|
||||||
|
|
||||||
if (avail > 0) {
|
|
||||||
retval += vis_line_t(avail - 1);
|
|
||||||
} else {
|
|
||||||
retval = -1_vl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
};
|
|
||||||
|
|
||||||
vis_line_t get_top_for_last_row()
|
vis_line_t get_top_for_last_row()
|
||||||
{
|
{
|
||||||
@ -424,13 +328,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
/** @return True if the given line is visible. */
|
/** @return True if the given line is visible. */
|
||||||
bool is_line_visible(vis_line_t line) const
|
bool is_line_visible(vis_line_t line) const
|
||||||
{
|
{
|
||||||
return this->get_top() <= line && line <= this->get_bottom();
|
return this->get_top() <= line && line <= this->get_bottom();
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shift the value of top by the given value.
|
* Shift the value of top by the given value.
|
||||||
@ -451,7 +355,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this->lv_top;
|
return this->lv_top;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the column number to be displayed at the left of the view. If the
|
* Set the column number to be displayed at the left of the view. If the
|
||||||
@ -480,13 +384,10 @@ public:
|
|||||||
this->lv_left = left;
|
this->lv_left = left;
|
||||||
this->invoke_scroll();
|
this->invoke_scroll();
|
||||||
this->set_needs_update();
|
this->set_needs_update();
|
||||||
};
|
}
|
||||||
|
|
||||||
/** @return The column number that is displayed at the left. */
|
/** @return The column number that is displayed at the left. */
|
||||||
unsigned int get_left() const
|
unsigned int get_left() const { return this->lv_left; }
|
||||||
{
|
|
||||||
return this->lv_left;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shift the value of left by the given value.
|
* Shift the value of left by the given value.
|
||||||
@ -505,7 +406,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this->lv_left;
|
return this->lv_left;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the height of the view. A value greater than one is considered to
|
* Set the height of the view. A value greater than one is considered to
|
||||||
@ -520,13 +421,10 @@ public:
|
|||||||
this->lv_height = height;
|
this->lv_height = height;
|
||||||
this->set_needs_update();
|
this->set_needs_update();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/** @return The absolute or relative height of the window. */
|
/** @return The absolute or relative height of the window. */
|
||||||
vis_line_t get_height() const
|
vis_line_t get_height() const { return this->lv_height; }
|
||||||
{
|
|
||||||
return this->lv_height;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @return The number of rows of data in this view's source data. */
|
/** @return The number of rows of data in this view's source data. */
|
||||||
vis_line_t get_inner_height() const
|
vis_line_t get_inner_height() const
|
||||||
@ -534,19 +432,16 @@ public:
|
|||||||
return vis_line_t(this->lv_source == nullptr
|
return vis_line_t(this->lv_source == nullptr
|
||||||
? 0
|
? 0
|
||||||
: this->lv_source->listview_rows(*this));
|
: this->lv_source->listview_rows(*this));
|
||||||
};
|
}
|
||||||
|
|
||||||
size_t get_inner_width() const
|
size_t get_inner_width() const
|
||||||
{
|
{
|
||||||
return this->lv_source == nullptr
|
return this->lv_source == nullptr
|
||||||
? 0
|
? 0
|
||||||
: this->lv_source->listview_width(*this);
|
: this->lv_source->listview_width(*this);
|
||||||
};
|
}
|
||||||
|
|
||||||
void set_overlay_needs_update()
|
void set_overlay_needs_update() { this->lv_overlay_needs_update = true; }
|
||||||
{
|
|
||||||
this->lv_overlay_needs_update = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the actual dimensions of the view.
|
* Get the actual dimensions of the view.
|
||||||
@ -579,7 +474,7 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
width_out = 0;
|
width_out = 0;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
std::pair<vis_line_t, unsigned long> get_dimensions() const
|
std::pair<vis_line_t, unsigned long> get_dimensions() const
|
||||||
{
|
{
|
||||||
@ -619,13 +514,10 @@ public:
|
|||||||
log_debug(" lv_title=%s", this->lv_title.c_str());
|
log_debug(" lv_title=%s", this->lv_title.c_str());
|
||||||
log_debug(" lv_y=%u", this->lv_y);
|
log_debug(" lv_y=%u", this->lv_y);
|
||||||
log_debug(" lv_top=%d", (int) this->lv_top);
|
log_debug(" lv_top=%d", (int) this->lv_top);
|
||||||
};
|
|
||||||
|
|
||||||
virtual void invoke_scroll()
|
|
||||||
{
|
|
||||||
this->lv_scroll(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void invoke_scroll() { this->lv_scroll(this); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void delegate_scroll_out()
|
void delegate_scroll_out()
|
||||||
{
|
{
|
||||||
@ -634,11 +526,11 @@ protected:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum lv_mode_t {
|
enum class lv_mode_t {
|
||||||
LV_MODE_NONE,
|
NONE,
|
||||||
LV_MODE_DOWN,
|
DOWN,
|
||||||
LV_MODE_UP,
|
UP,
|
||||||
LV_MODE_DRAG
|
DRAG
|
||||||
};
|
};
|
||||||
|
|
||||||
static list_gutter_source DEFAULT_GUTTER_SOURCE;
|
static list_gutter_source DEFAULT_GUTTER_SOURCE;
|
||||||
@ -667,7 +559,7 @@ protected:
|
|||||||
int lv_scroll_accel{1};
|
int lv_scroll_accel{1};
|
||||||
int lv_scroll_velo;
|
int lv_scroll_velo;
|
||||||
int lv_mouse_y{-1};
|
int lv_mouse_y{-1};
|
||||||
lv_mode_t lv_mouse_mode{LV_MODE_NONE};
|
lv_mode_t lv_mouse_mode{lv_mode_t::NONE};
|
||||||
vis_line_t lv_tail_space{1};
|
vis_line_t lv_tail_space{1};
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
1604
src/lnav.cc
1604
src/lnav.cc
File diff suppressed because it is too large
Load Diff
72
src/lnav.hh
72
src/lnav.hh
@ -75,53 +75,17 @@
|
|||||||
#include "top_status_source.hh"
|
#include "top_status_source.hh"
|
||||||
#include "view_helpers.hh"
|
#include "view_helpers.hh"
|
||||||
|
|
||||||
/** The command modes that are available while viewing a file. */
|
|
||||||
typedef enum {
|
|
||||||
LNM_PAGING,
|
|
||||||
LNM_FILTER,
|
|
||||||
LNM_FILES,
|
|
||||||
LNM_COMMAND,
|
|
||||||
LNM_SEARCH,
|
|
||||||
LNM_SEARCH_FILTERS,
|
|
||||||
LNM_SEARCH_FILES,
|
|
||||||
LNM_CAPTURE,
|
|
||||||
LNM_SQL,
|
|
||||||
LNM_EXEC,
|
|
||||||
LNM_USER,
|
|
||||||
} ln_mode_t;
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
LNB_SYSLOG,
|
|
||||||
LNB__MAX,
|
|
||||||
|
|
||||||
LNB_TIMESTAMP,
|
|
||||||
LNB_HELP,
|
|
||||||
LNB_HEADLESS,
|
LNB_HEADLESS,
|
||||||
LNB_QUIET,
|
LNB_MANAGEMENT,
|
||||||
LNB_CHECK_CONFIG,
|
|
||||||
LNB_INSTALL,
|
|
||||||
LNB_UPDATE_FORMATS,
|
|
||||||
LNB_VERBOSE,
|
|
||||||
LNB_SECURE_MODE,
|
LNB_SECURE_MODE,
|
||||||
LNB_NO_DEFAULT,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Flags set on the lnav command-line. */
|
/** Flags set on the lnav command-line. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
LNF_SYSLOG = (1L << LNB_SYSLOG),
|
|
||||||
|
|
||||||
LNF_TIMESTAMP = (1L << LNB_TIMESTAMP),
|
|
||||||
LNF_HELP = (1L << LNB_HELP),
|
|
||||||
LNF_HEADLESS = (1L << LNB_HEADLESS),
|
LNF_HEADLESS = (1L << LNB_HEADLESS),
|
||||||
LNF_QUIET = (1L << LNB_QUIET),
|
LNF_MANAGEMENT = (1L << LNB_MANAGEMENT),
|
||||||
LNF_CHECK_CONFIG = (1L << LNB_CHECK_CONFIG),
|
|
||||||
LNF_INSTALL = (1L << LNB_INSTALL),
|
|
||||||
LNF_UPDATE_FORMATS = (1L << LNB_UPDATE_FORMATS),
|
|
||||||
LNF_VERBOSE = (1L << LNB_VERBOSE),
|
|
||||||
LNF_SECURE_MODE = (1L << LNB_SECURE_MODE),
|
LNF_SECURE_MODE = (1L << LNB_SECURE_MODE),
|
||||||
LNF_NO_DEFAULT = (1L << LNB_NO_DEFAULT),
|
|
||||||
|
|
||||||
LNF__ALL = (LNF_SYSLOG | LNF_HELP),
|
|
||||||
} lnav_flags_t;
|
} lnav_flags_t;
|
||||||
|
|
||||||
extern const char* lnav_zoom_strings[];
|
extern const char* lnav_zoom_strings[];
|
||||||
@ -138,17 +102,17 @@ typedef enum {
|
|||||||
LNS__MAX
|
LNS__MAX
|
||||||
} lnav_status_t;
|
} lnav_status_t;
|
||||||
|
|
||||||
typedef std::pair<int, int> ppid_time_pair_t;
|
using ppid_time_pair_t = std::pair<int, int>;
|
||||||
typedef std::pair<ppid_time_pair_t, ghc::filesystem::path> session_pair_t;
|
using session_pair_t = std::pair<ppid_time_pair_t, ghc::filesystem::path>;
|
||||||
|
|
||||||
class input_state_tracker : public log_state_dumper {
|
class input_state_tracker : public log_state_dumper {
|
||||||
public:
|
public:
|
||||||
input_state_tracker() : ist_index(0)
|
input_state_tracker()
|
||||||
{
|
{
|
||||||
memset(this->ist_recent_key_presses,
|
memset(this->ist_recent_key_presses,
|
||||||
0,
|
0,
|
||||||
sizeof(this->ist_recent_key_presses));
|
sizeof(this->ist_recent_key_presses));
|
||||||
};
|
}
|
||||||
|
|
||||||
void log_state() override
|
void log_state() override
|
||||||
{
|
{
|
||||||
@ -159,19 +123,19 @@ public:
|
|||||||
this->ist_recent_key_presses[lpc]);
|
this->ist_recent_key_presses[lpc]);
|
||||||
}
|
}
|
||||||
log_msg_extra_complete();
|
log_msg_extra_complete();
|
||||||
};
|
}
|
||||||
|
|
||||||
void push_back(int ch)
|
void push_back(int ch)
|
||||||
{
|
{
|
||||||
this->ist_recent_key_presses[this->ist_index % COUNT] = ch;
|
this->ist_recent_key_presses[this->ist_index % COUNT] = ch;
|
||||||
this->ist_index = (this->ist_index + 1) % COUNT;
|
this->ist_index = (this->ist_index + 1) % COUNT;
|
||||||
};
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int COUNT = 10;
|
static const int COUNT = 10;
|
||||||
|
|
||||||
int ist_recent_key_presses[COUNT];
|
int ist_recent_key_presses[COUNT];
|
||||||
size_t ist_index;
|
size_t ist_index{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct key_repeat_history {
|
struct key_repeat_history {
|
||||||
@ -209,7 +173,7 @@ struct lnav_data_t {
|
|||||||
time_t ld_session_time;
|
time_t ld_session_time;
|
||||||
time_t ld_session_load_time;
|
time_t ld_session_load_time;
|
||||||
const char* ld_program_name;
|
const char* ld_program_name;
|
||||||
const char* ld_debug_log_name;
|
std::string ld_debug_log_name;
|
||||||
|
|
||||||
std::list<std::string> ld_commands;
|
std::list<std::string> ld_commands;
|
||||||
bool ld_cmd_init_done;
|
bool ld_cmd_init_done;
|
||||||
@ -228,7 +192,7 @@ struct lnav_data_t {
|
|||||||
unsigned long ld_flags;
|
unsigned long ld_flags;
|
||||||
WINDOW* ld_window;
|
WINDOW* ld_window;
|
||||||
ln_mode_t ld_mode;
|
ln_mode_t ld_mode;
|
||||||
ln_mode_t ld_last_config_mode{LNM_FILTER};
|
ln_mode_t ld_last_config_mode{ln_mode_t::FILTER};
|
||||||
|
|
||||||
statusview_curses ld_status[LNS__MAX];
|
statusview_curses ld_status[LNS__MAX];
|
||||||
top_status_source ld_top_source;
|
top_status_source ld_top_source;
|
||||||
@ -301,6 +265,9 @@ struct lnav_data_t {
|
|||||||
int ld_fifo_counter;
|
int ld_fifo_counter;
|
||||||
|
|
||||||
struct key_repeat_history ld_key_repeat_history;
|
struct key_repeat_history ld_key_repeat_history;
|
||||||
|
|
||||||
|
bool ld_initial_build{false};
|
||||||
|
bool ld_show_help_view{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct static_service {
|
struct static_service {
|
||||||
@ -322,18 +289,7 @@ extern const ssize_t ZOOM_COUNT;
|
|||||||
|
|
||||||
#define HELP_MSG_2(x, y, msg) "Press " ANSI_BOLD(#x) "/" ANSI_BOLD(#y) " " msg
|
#define HELP_MSG_2(x, y, msg) "Press " ANSI_BOLD(#x) "/" ANSI_BOLD(#y) " " msg
|
||||||
|
|
||||||
void rebuild_hist();
|
|
||||||
size_t rebuild_indexes(nonstd::optional<ui_clock::time_point> deadline
|
|
||||||
= nonstd::nullopt);
|
|
||||||
void rebuild_indexes_repeatedly();
|
|
||||||
|
|
||||||
bool setup_logline_table(exec_context& ec);
|
bool setup_logline_table(exec_context& ec);
|
||||||
|
|
||||||
bool rescan_files(bool required = false);
|
|
||||||
bool update_active_files(file_collection& new_files);
|
|
||||||
|
|
||||||
void wait_for_children();
|
void wait_for_children();
|
||||||
|
|
||||||
textview_curses* get_textview_for_mode(ln_mode_t mode);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
385
src/lnav.indexing.cc
Normal file
385
src/lnav.indexing.cc
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022, 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 "lnav.indexing.hh"
|
||||||
|
|
||||||
|
#include "lnav.hh"
|
||||||
|
#include "service_tags.hh"
|
||||||
|
#include "session_data.hh"
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observer for loading progress that updates the bottom status bar.
|
||||||
|
*/
|
||||||
|
class loading_observer : public logfile_observer {
|
||||||
|
public:
|
||||||
|
loading_observer() : lo_last_offset(0){};
|
||||||
|
|
||||||
|
indexing_result logfile_indexing(const std::shared_ptr<logfile>& lf,
|
||||||
|
file_off_t off,
|
||||||
|
file_size_t total) override
|
||||||
|
{
|
||||||
|
static sig_atomic_t index_counter = 0;
|
||||||
|
|
||||||
|
if (lnav_data.ld_window == nullptr) {
|
||||||
|
return indexing_result::CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX require(off <= total); */
|
||||||
|
if (off > (off_t) total) {
|
||||||
|
off = total;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((((size_t) off == total) && (this->lo_last_offset != off))
|
||||||
|
|| ui_periodic_timer::singleton().time_to_update(index_counter))
|
||||||
|
{
|
||||||
|
lnav_data.ld_bottom_source.update_loading(off, total);
|
||||||
|
do_observer_update(lf);
|
||||||
|
this->lo_last_offset = off;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lnav_data.ld_looping) {
|
||||||
|
return indexing_result::BREAK;
|
||||||
|
}
|
||||||
|
return indexing_result::CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t lo_last_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
do_observer_update(const std::shared_ptr<logfile>& lf)
|
||||||
|
{
|
||||||
|
if (isendwin()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lnav_data.ld_top_source.update_time();
|
||||||
|
for (auto& sc : lnav_data.ld_status) {
|
||||||
|
sc.do_update();
|
||||||
|
}
|
||||||
|
if (lf && lnav_data.ld_mode == ln_mode_t::FILES
|
||||||
|
&& !lnav_data.ld_initial_build) {
|
||||||
|
auto& fc = lnav_data.ld_active_files;
|
||||||
|
auto iter = std::find(fc.fc_files.begin(), fc.fc_files.end(), lf);
|
||||||
|
|
||||||
|
if (iter != fc.fc_files.end()) {
|
||||||
|
auto index = std::distance(fc.fc_files.begin(), iter);
|
||||||
|
lnav_data.ld_files_view.set_selection(
|
||||||
|
vis_line_t(fc.fc_other_files.size() + index));
|
||||||
|
lnav_data.ld_files_view.reload_data();
|
||||||
|
lnav_data.ld_files_view.do_update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rebuild_hist()
|
||||||
|
{
|
||||||
|
logfile_sub_source& lss = lnav_data.ld_log_source;
|
||||||
|
hist_source2& hs = lnav_data.ld_hist_source2;
|
||||||
|
int zoom = lnav_data.ld_zoom_level;
|
||||||
|
|
||||||
|
hs.set_time_slice(ZOOM_LEVELS[zoom]);
|
||||||
|
lss.reload_index_delegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
class textfile_callback {
|
||||||
|
public:
|
||||||
|
void closed_files(const std::vector<std::shared_ptr<logfile>>& files)
|
||||||
|
{
|
||||||
|
for (const auto& lf : files) {
|
||||||
|
log_info("closed text files: %s", lf->get_filename().c_str());
|
||||||
|
}
|
||||||
|
lnav_data.ld_active_files.close_files(files);
|
||||||
|
}
|
||||||
|
|
||||||
|
void promote_file(const std::shared_ptr<logfile>& lf)
|
||||||
|
{
|
||||||
|
if (lnav_data.ld_log_source.insert_file(lf)) {
|
||||||
|
this->did_promotion = true;
|
||||||
|
log_info("promoting text file to log file: %s (%s)",
|
||||||
|
lf->get_filename().c_str(),
|
||||||
|
lf->get_content_id().c_str());
|
||||||
|
auto format = lf->get_format();
|
||||||
|
if (format->lf_is_self_describing) {
|
||||||
|
auto vt = format->get_vtab_impl();
|
||||||
|
|
||||||
|
if (vt != nullptr) {
|
||||||
|
lnav_data.ld_vtab_manager->register_vtab(vt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter = session_data.sd_file_states.find(lf->get_filename());
|
||||||
|
if (iter != session_data.sd_file_states.end()) {
|
||||||
|
log_debug("found state for log file %d",
|
||||||
|
iter->second.fs_is_visible);
|
||||||
|
|
||||||
|
lnav_data.ld_log_source.find_data(lf) | [&iter](auto ld) {
|
||||||
|
ld->set_visibility(iter->second.fs_is_visible);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this->closed_files({lf});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scanned_file(const std::shared_ptr<logfile>& lf)
|
||||||
|
{
|
||||||
|
if (!lnav_data.ld_files_to_front.empty()
|
||||||
|
&& lnav_data.ld_files_to_front.front().first == lf->get_filename())
|
||||||
|
{
|
||||||
|
this->front_file = lf;
|
||||||
|
this->front_top = lnav_data.ld_files_to_front.front().second;
|
||||||
|
|
||||||
|
lnav_data.ld_files_to_front.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<logfile> front_file;
|
||||||
|
int front_top{-1};
|
||||||
|
bool did_promotion{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t
|
||||||
|
rebuild_indexes(nonstd::optional<ui_clock::time_point> deadline)
|
||||||
|
{
|
||||||
|
logfile_sub_source& lss = lnav_data.ld_log_source;
|
||||||
|
textview_curses& log_view = lnav_data.ld_views[LNV_LOG];
|
||||||
|
textview_curses& text_view = lnav_data.ld_views[LNV_TEXT];
|
||||||
|
vis_line_t old_bottoms[LNV__MAX];
|
||||||
|
bool scroll_downs[LNV__MAX];
|
||||||
|
size_t retval = 0;
|
||||||
|
|
||||||
|
for (int lpc = 0; lpc < LNV__MAX; lpc++) {
|
||||||
|
old_bottoms[lpc] = lnav_data.ld_views[lpc].get_top_for_last_row();
|
||||||
|
scroll_downs[lpc]
|
||||||
|
= (lnav_data.ld_views[lpc].get_top() >= old_bottoms[lpc])
|
||||||
|
&& !(lnav_data.ld_flags & LNF_HEADLESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
textfile_sub_source* tss = &lnav_data.ld_text_source;
|
||||||
|
textfile_callback cb;
|
||||||
|
|
||||||
|
if (tss->rescan_files(cb, deadline)) {
|
||||||
|
text_view.reload_data();
|
||||||
|
retval += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb.front_file != nullptr) {
|
||||||
|
ensure_view(&text_view);
|
||||||
|
|
||||||
|
if (tss->current_file() != cb.front_file) {
|
||||||
|
tss->to_front(cb.front_file);
|
||||||
|
old_bottoms[LNV_TEXT] = -1_vl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb.front_top < 0) {
|
||||||
|
cb.front_top += text_view.get_inner_height();
|
||||||
|
}
|
||||||
|
if (cb.front_top < text_view.get_inner_height()) {
|
||||||
|
text_view.set_top(vis_line_t(cb.front_top));
|
||||||
|
scroll_downs[LNV_TEXT] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cb.did_promotion && deadline) {
|
||||||
|
// If there's a new log file, extend the deadline so it can be
|
||||||
|
// indexed quickly.
|
||||||
|
deadline = deadline.value() + 500ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<logfile>> closed_files;
|
||||||
|
for (auto& lf : lnav_data.ld_active_files.fc_files) {
|
||||||
|
if ((!lf->exists() || lf->is_closed())) {
|
||||||
|
log_info("closed log file: %s", lf->get_filename().c_str());
|
||||||
|
lnav_data.ld_text_source.remove(lf);
|
||||||
|
lnav_data.ld_log_source.remove_file(lf);
|
||||||
|
closed_files.emplace_back(lf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!closed_files.empty()) {
|
||||||
|
lnav_data.ld_active_files.close_files(closed_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = lss.rebuild_index(deadline);
|
||||||
|
if (result != logfile_sub_source::rebuild_result::rr_no_change) {
|
||||||
|
size_t new_count = lss.text_line_count();
|
||||||
|
bool force
|
||||||
|
= result == logfile_sub_source::rebuild_result::rr_full_rebuild;
|
||||||
|
|
||||||
|
if ((!scroll_downs[LNV_LOG]
|
||||||
|
|| log_view.get_top() > vis_line_t(new_count))
|
||||||
|
&& force)
|
||||||
|
{
|
||||||
|
scroll_downs[LNV_LOG] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_view.reload_data();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, std::list<std::shared_ptr<logfile>>>
|
||||||
|
id_to_files;
|
||||||
|
bool reload = false;
|
||||||
|
|
||||||
|
for (const auto& lf : lnav_data.ld_active_files.fc_files) {
|
||||||
|
id_to_files[lf->get_content_id()].push_back(lf);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& pair : id_to_files) {
|
||||||
|
if (pair.second.size() == 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pair.second.sort([](const auto& left, const auto& right) {
|
||||||
|
return right->get_stat().st_size < left->get_stat().st_size;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto dupe_name = pair.second.front()->get_unique_path();
|
||||||
|
pair.second.pop_front();
|
||||||
|
for_each(pair.second.begin(),
|
||||||
|
pair.second.end(),
|
||||||
|
[&dupe_name](auto& lf) {
|
||||||
|
log_info("Hiding duplicate file: %s",
|
||||||
|
lf->get_filename().c_str());
|
||||||
|
lf->mark_as_duplicate(dupe_name);
|
||||||
|
lnav_data.ld_log_source.find_data(lf) |
|
||||||
|
[](auto ld) { ld->set_visibility(false); };
|
||||||
|
});
|
||||||
|
reload = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reload) {
|
||||||
|
lss.text_filters_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retval += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int lpc = 0; lpc < LNV__MAX; lpc++) {
|
||||||
|
textview_curses& scroll_view = lnav_data.ld_views[lpc];
|
||||||
|
|
||||||
|
if (scroll_downs[lpc]
|
||||||
|
&& scroll_view.get_top_for_last_row() > scroll_view.get_top())
|
||||||
|
{
|
||||||
|
scroll_view.set_top(scroll_view.get_top_for_last_row());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lnav_data.ld_view_stack.top() | [](auto tc) {
|
||||||
|
lnav_data.ld_filter_status_source.update_filtered(tc->get_sub_source());
|
||||||
|
lnav_data.ld_scroll_broadcaster(tc);
|
||||||
|
};
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rebuild_indexes_repeatedly()
|
||||||
|
{
|
||||||
|
for (size_t attempt = 0; attempt < 10 && rebuild_indexes() > 0; attempt++) {
|
||||||
|
log_info("continuing to rebuild indexes...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
update_active_files(file_collection& new_files)
|
||||||
|
{
|
||||||
|
static loading_observer obs;
|
||||||
|
|
||||||
|
if (lnav_data.ld_active_files.fc_invalidate_merge) {
|
||||||
|
lnav_data.ld_active_files.fc_invalidate_merge = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& lf : new_files.fc_files) {
|
||||||
|
lf->set_logfile_observer(&obs);
|
||||||
|
lnav_data.ld_text_source.push_back(lf);
|
||||||
|
}
|
||||||
|
for (const auto& other_pair : new_files.fc_other_files) {
|
||||||
|
switch (other_pair.second.ofd_format) {
|
||||||
|
case file_format_t::SQLITE_DB:
|
||||||
|
attach_sqlite_db(lnav_data.ld_db.in(), other_pair.first);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lnav_data.ld_active_files.merge(new_files);
|
||||||
|
if (!new_files.fc_files.empty() || !new_files.fc_other_files.empty()
|
||||||
|
|| !new_files.fc_name_to_errors.empty())
|
||||||
|
{
|
||||||
|
lnav_data.ld_active_files.regenerate_unique_file_names();
|
||||||
|
}
|
||||||
|
lnav_data.ld_child_pollers.insert(
|
||||||
|
lnav_data.ld_child_pollers.begin(),
|
||||||
|
std::make_move_iterator(
|
||||||
|
lnav_data.ld_active_files.fc_child_pollers.begin()),
|
||||||
|
std::make_move_iterator(
|
||||||
|
lnav_data.ld_active_files.fc_child_pollers.end()));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
rescan_files(bool req)
|
||||||
|
{
|
||||||
|
auto& mlooper = injector::get<main_looper&, services::main_t>();
|
||||||
|
bool done = false;
|
||||||
|
auto delay = 0ms;
|
||||||
|
|
||||||
|
do {
|
||||||
|
auto fc = lnav_data.ld_active_files.rescan_files(req);
|
||||||
|
bool all_synced = true;
|
||||||
|
|
||||||
|
update_active_files(fc);
|
||||||
|
mlooper.get_port().process_for(delay);
|
||||||
|
if (lnav_data.ld_flags & LNF_HEADLESS) {
|
||||||
|
for (const auto& pair : lnav_data.ld_active_files.fc_other_files) {
|
||||||
|
if (pair.second.ofd_format != file_format_t::REMOTE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lnav_data.ld_active_files.fc_synced_files.count(pair.first)
|
||||||
|
== 0) {
|
||||||
|
all_synced = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!all_synced) {
|
||||||
|
delay = 30ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done = fc.fc_file_names.empty() && all_synced;
|
||||||
|
} while (!done);
|
||||||
|
return true;
|
||||||
|
}
|
45
src/lnav.indexing.hh
Normal file
45
src/lnav.indexing.hh
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022, Timothy Stack
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef lnav_indexing_hh
|
||||||
|
#define lnav_indexing_hh
|
||||||
|
|
||||||
|
#include "file_collection.hh"
|
||||||
|
#include "logfile_fwd.hh"
|
||||||
|
#include "optional.hpp"
|
||||||
|
|
||||||
|
void rebuild_hist();
|
||||||
|
size_t rebuild_indexes(nonstd::optional<ui_clock::time_point> deadline
|
||||||
|
= nonstd::nullopt);
|
||||||
|
void rebuild_indexes_repeatedly();
|
||||||
|
bool rescan_files(bool required = false);
|
||||||
|
bool update_active_files(file_collection& new_files);
|
||||||
|
void do_observer_update(const std::shared_ptr<logfile>& lf);
|
||||||
|
|
||||||
|
#endif
|
970
src/lnav.management_cli.cc
Normal file
970
src/lnav.management_cli.cc
Normal file
@ -0,0 +1,970 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022, 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 <queue>
|
||||||
|
|
||||||
|
#include "lnav.management_cli.hh"
|
||||||
|
|
||||||
|
#include "base/itertools.hh"
|
||||||
|
#include "base/result.h"
|
||||||
|
#include "base/string_util.hh"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
#include "fts_fuzzy_match.hh"
|
||||||
|
#include "log_format.hh"
|
||||||
|
#include "log_format_ext.hh"
|
||||||
|
#include "mapbox/variant.hpp"
|
||||||
|
#include "regex101.import.hh"
|
||||||
|
#include "session_data.hh"
|
||||||
|
|
||||||
|
using namespace lnav::roles::literals;
|
||||||
|
|
||||||
|
namespace lnav {
|
||||||
|
namespace itertools {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
struct similar_to {
|
||||||
|
std::string st_pattern;
|
||||||
|
size_t st_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
details::similar_to
|
||||||
|
similar_to(std::string pattern, size_t count = 5)
|
||||||
|
{
|
||||||
|
return lnav::itertools::details::similar_to{std::move(pattern), count};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace itertools
|
||||||
|
} // namespace lnav
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::vector<typename T::value_type>
|
||||||
|
operator|(const T& in, const lnav::itertools::details::similar_to& st)
|
||||||
|
{
|
||||||
|
using score_pair = std::pair<int, typename T::value_type>;
|
||||||
|
|
||||||
|
struct score_cmp {
|
||||||
|
bool operator()(const score_pair& lhs, const score_pair& rhs)
|
||||||
|
{
|
||||||
|
return lhs.first > rhs.first;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::priority_queue<score_pair, std::vector<score_pair>, score_cmp> pq;
|
||||||
|
|
||||||
|
for (const auto& elem : in) {
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
if (!fts::fuzzy_match(st.st_pattern.c_str(), elem.c_str(), score)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (score <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pq.push(std::make_pair(score, elem));
|
||||||
|
|
||||||
|
if (pq.size() > st.st_count) {
|
||||||
|
pq.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::remove_const_t<typename T::value_type>> retval;
|
||||||
|
|
||||||
|
while (!pq.empty()) {
|
||||||
|
retval.template emplace_back(pq.top().second);
|
||||||
|
pq.pop();
|
||||||
|
}
|
||||||
|
std::reverse(retval.begin(), retval.end());
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace lnav {
|
||||||
|
|
||||||
|
namespace management {
|
||||||
|
|
||||||
|
struct no_subcmd_t {
|
||||||
|
CLI::App* ns_root_app{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline attr_line_t&
|
||||||
|
symbol_reducer(const std::string& elem, attr_line_t& accum)
|
||||||
|
{
|
||||||
|
return accum.append("\n ").append(lnav::roles::symbol(elem));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline attr_line_t&
|
||||||
|
subcmd_reducer(const CLI::App* app, attr_line_t& accum)
|
||||||
|
{
|
||||||
|
return accum.append("\n \u2022 ")
|
||||||
|
.append(lnav::roles::keyword(app->get_name()))
|
||||||
|
.append(": ")
|
||||||
|
.append(app->get_description());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct subcmd_format_t {
|
||||||
|
using action_t = std::function<perform_result_t(const subcmd_format_t&)>;
|
||||||
|
|
||||||
|
CLI::App* sf_format_app{nullptr};
|
||||||
|
std::string sf_name;
|
||||||
|
CLI::App* sf_regex_app{nullptr};
|
||||||
|
std::string sf_regex_name;
|
||||||
|
CLI::App* sf_regex101_app{nullptr};
|
||||||
|
action_t sf_action;
|
||||||
|
|
||||||
|
subcmd_format_t& set_action(action_t act)
|
||||||
|
{
|
||||||
|
if (!this->sf_action) {
|
||||||
|
this->sf_action = std::move(act);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<std::shared_ptr<log_format>, console::user_message> validate_format()
|
||||||
|
const
|
||||||
|
{
|
||||||
|
if (this->sf_name.empty()) {
|
||||||
|
auto um = console::user_message::error(
|
||||||
|
"expecting a format name to operate on");
|
||||||
|
um.with_note(
|
||||||
|
(log_format::get_root_formats()
|
||||||
|
| lnav::itertools::map(&log_format::get_name)
|
||||||
|
| lnav::itertools::sort_with(intern_string_t::case_lt)
|
||||||
|
| lnav::itertools::map(&intern_string_t::to_string)
|
||||||
|
| lnav::itertools::fold(symbol_reducer, attr_line_t{}))
|
||||||
|
.add_header("the available formats are:"));
|
||||||
|
|
||||||
|
return Err(um);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lformat = log_format::find_root_format(this->sf_name.c_str());
|
||||||
|
if (lformat == nullptr) {
|
||||||
|
auto um = console::user_message::error(
|
||||||
|
attr_line_t("unknown format: ")
|
||||||
|
.append(lnav::roles::symbol(this->sf_name)));
|
||||||
|
um.with_note(
|
||||||
|
(log_format::get_root_formats()
|
||||||
|
| lnav::itertools::map(&log_format::get_name)
|
||||||
|
| lnav::itertools::similar_to(this->sf_name)
|
||||||
|
| lnav::itertools::map(&intern_string_t::to_string)
|
||||||
|
| lnav::itertools::fold(symbol_reducer, attr_line_t{}))
|
||||||
|
.add_header("did you mean one of the following?"));
|
||||||
|
|
||||||
|
return Err(um);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(lformat);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<external_log_format*, console::user_message>
|
||||||
|
validate_external_format() const
|
||||||
|
{
|
||||||
|
auto lformat = TRY(this->validate_format());
|
||||||
|
auto* ext_lformat = dynamic_cast<external_log_format*>(lformat.get());
|
||||||
|
|
||||||
|
if (ext_lformat == nullptr) {
|
||||||
|
return Err(console::user_message::error(
|
||||||
|
attr_line_t()
|
||||||
|
.append_quoted(lnav::roles::symbol(this->sf_name))
|
||||||
|
.append(" is an internal format that is not defined in a "
|
||||||
|
"configuration file")));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(ext_lformat);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<std::pair<external_log_format*,
|
||||||
|
std::shared_ptr<external_log_format::pattern>>,
|
||||||
|
console::user_message>
|
||||||
|
validate_regex() const
|
||||||
|
{
|
||||||
|
auto* ext_lformat = TRY(this->validate_external_format());
|
||||||
|
|
||||||
|
if (this->sf_regex_name.empty()) {
|
||||||
|
auto um = console::user_message::error(
|
||||||
|
"expecting a regex name to operate on");
|
||||||
|
um.with_note(
|
||||||
|
ext_lformat->elf_pattern_order
|
||||||
|
| lnav::itertools::map(&external_log_format::pattern::p_name)
|
||||||
|
| lnav::itertools::fold(
|
||||||
|
symbol_reducer, attr_line_t{"the available regexes are:"}));
|
||||||
|
|
||||||
|
return Err(um);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& pat : ext_lformat->elf_pattern_order) {
|
||||||
|
if (pat->p_name == this->sf_regex_name) {
|
||||||
|
return Ok(std::make_pair(ext_lformat, pat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto um = console::user_message::error(
|
||||||
|
attr_line_t("unknown regex: ")
|
||||||
|
.append(lnav::roles::symbol(this->sf_regex_name)));
|
||||||
|
um.with_note(
|
||||||
|
(ext_lformat->elf_pattern_order
|
||||||
|
| lnav::itertools::map(&external_log_format::pattern::p_name)
|
||||||
|
| lnav::itertools::similar_to(this->sf_regex_name)
|
||||||
|
| lnav::itertools::fold(symbol_reducer, attr_line_t{}))
|
||||||
|
.add_header("did you mean one of the following?"));
|
||||||
|
|
||||||
|
return Err(um);
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t default_action(const subcmd_format_t& sf)
|
||||||
|
{
|
||||||
|
auto validate_res = sf.validate_format();
|
||||||
|
if (validate_res.isErr()) {
|
||||||
|
return {validate_res.unwrapErr()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lformat = validate_res.unwrap();
|
||||||
|
auto* ext_format = dynamic_cast<external_log_format*>(lformat.get());
|
||||||
|
|
||||||
|
attr_line_t ext_details;
|
||||||
|
if (ext_format != nullptr) {
|
||||||
|
ext_details.append("\n ")
|
||||||
|
.append("Regexes"_h3)
|
||||||
|
.append(": ")
|
||||||
|
.join(ext_format->elf_pattern_order
|
||||||
|
| lnav::itertools::map(
|
||||||
|
&external_log_format::pattern::p_name),
|
||||||
|
VC_ROLE.value(role_t::VCR_SYMBOL),
|
||||||
|
", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto um = console::user_message::error(
|
||||||
|
attr_line_t("expecting an operation to perform on the ")
|
||||||
|
.append(lnav::roles::symbol(sf.sf_name))
|
||||||
|
.append(" format"));
|
||||||
|
um.with_note(attr_line_t()
|
||||||
|
.append(lnav::roles::symbol(sf.sf_name))
|
||||||
|
.append(": ")
|
||||||
|
.append(lformat->lf_description)
|
||||||
|
.append(ext_details));
|
||||||
|
um.with_help(
|
||||||
|
sf.sf_format_app->get_subcommands({})
|
||||||
|
| lnav::itertools::fold(
|
||||||
|
subcmd_reducer, attr_line_t{"the available operations are:"}));
|
||||||
|
|
||||||
|
return {um};
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t default_regex_action(const subcmd_format_t& sf)
|
||||||
|
{
|
||||||
|
auto validate_res = sf.validate_regex();
|
||||||
|
|
||||||
|
if (validate_res.isErr()) {
|
||||||
|
return {validate_res.unwrapErr()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto um = console::user_message::error(
|
||||||
|
attr_line_t("expecting an operation to perform on the ")
|
||||||
|
.append(lnav::roles::symbol(sf.sf_regex_name))
|
||||||
|
.append(" regular expression using regex101.com"));
|
||||||
|
|
||||||
|
um.with_help(attr_line_t{"the available subcommands are:"}.append(
|
||||||
|
sf.sf_regex101_app->get_subcommands({})
|
||||||
|
| lnav::itertools::fold(subcmd_reducer, attr_line_t{})));
|
||||||
|
|
||||||
|
return {um};
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t get_action(const subcmd_format_t& sf)
|
||||||
|
{
|
||||||
|
auto validate_res = sf.validate_format();
|
||||||
|
|
||||||
|
if (validate_res.isErr()) {
|
||||||
|
return {validate_res.unwrapErr()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto format = validate_res.unwrap();
|
||||||
|
|
||||||
|
auto um = console::user_message::raw(
|
||||||
|
attr_line_t()
|
||||||
|
.append(lnav::roles::symbol(sf.sf_name))
|
||||||
|
.append(": ")
|
||||||
|
.append(on_blank(format->lf_description, "<no description>")));
|
||||||
|
|
||||||
|
return {um};
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t source_action(const subcmd_format_t& sf)
|
||||||
|
{
|
||||||
|
auto validate_res = sf.validate_external_format();
|
||||||
|
|
||||||
|
if (validate_res.isErr()) {
|
||||||
|
return {validate_res.unwrapErr()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* format = validate_res.unwrap();
|
||||||
|
|
||||||
|
if (format->elf_format_source_order.empty()) {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
"format is builtin, there is no source file"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto um = console::user_message::raw(
|
||||||
|
format->elf_format_source_order[0].string());
|
||||||
|
|
||||||
|
return {um};
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t sources_action(const subcmd_format_t& sf)
|
||||||
|
{
|
||||||
|
auto validate_res = sf.validate_external_format();
|
||||||
|
|
||||||
|
if (validate_res.isErr()) {
|
||||||
|
return {validate_res.unwrapErr()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* format = validate_res.unwrap();
|
||||||
|
|
||||||
|
if (format->elf_format_source_order.empty()) {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
"format is builtin, there is no source file"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto um = console::user_message::raw(
|
||||||
|
attr_line_t().join(format->elf_format_source_order,
|
||||||
|
VC_ROLE.value(role_t::VCR_TEXT),
|
||||||
|
"\n"));
|
||||||
|
|
||||||
|
return {um};
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t regex101_pull_action(const subcmd_format_t& sf)
|
||||||
|
{
|
||||||
|
auto validate_res = sf.validate_regex();
|
||||||
|
if (validate_res.isErr()) {
|
||||||
|
return {validate_res.unwrapErr()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto format_regex_pair = validate_res.unwrap();
|
||||||
|
auto get_meta_res
|
||||||
|
= lnav::session::regex101::get_entry(sf.sf_name, sf.sf_regex_name);
|
||||||
|
|
||||||
|
return get_meta_res.match(
|
||||||
|
[&sf](
|
||||||
|
const lnav::session::regex101::error& err) -> perform_result_t {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
attr_line_t("unable to get DB entry for: ")
|
||||||
|
.append(lnav::roles::symbol(sf.sf_name))
|
||||||
|
.append("/")
|
||||||
|
.append(lnav::roles::symbol(sf.sf_regex_name)))
|
||||||
|
.with_reason(err.e_msg),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[&sf](
|
||||||
|
const lnav::session::regex101::no_entry&) -> perform_result_t {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
attr_line_t("regex ")
|
||||||
|
.append_quoted(
|
||||||
|
lnav::roles::symbol(sf.sf_regex_name))
|
||||||
|
.append(" of format ")
|
||||||
|
.append_quoted(lnav::roles::symbol(sf.sf_name))
|
||||||
|
.append(" has not been pushed to regex101.com"))
|
||||||
|
.with_help(
|
||||||
|
attr_line_t("use the ")
|
||||||
|
.append_quoted("push"_keyword)
|
||||||
|
.append(" subcommand to create the regex on "
|
||||||
|
"regex101.com for easy editing")),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[&](const lnav::session::regex101::entry& en) -> perform_result_t {
|
||||||
|
auto retrieve_res = regex101::client::retrieve(en.re_permalink);
|
||||||
|
|
||||||
|
return retrieve_res.match(
|
||||||
|
[&](const console::user_message& um) -> perform_result_t {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
attr_line_t("unable to retrieve entry ")
|
||||||
|
.append_quoted(
|
||||||
|
lnav::roles::symbol(en.re_permalink))
|
||||||
|
.append(" from regex101.com"))
|
||||||
|
.with_reason(um),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[&](const regex101::client::no_entry&) -> perform_result_t {
|
||||||
|
lnav::session::regex101::delete_entry(sf.sf_name,
|
||||||
|
sf.sf_regex_name);
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
attr_line_t("entry ")
|
||||||
|
.append_quoted(
|
||||||
|
lnav::roles::symbol(en.re_permalink))
|
||||||
|
.append(
|
||||||
|
" no longer exists on regex101.com"))
|
||||||
|
.with_help(attr_line_t("use the ")
|
||||||
|
.append_quoted("delete"_keyword)
|
||||||
|
.append(" subcommand to delete "
|
||||||
|
"the association")),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[&](const regex101::client::entry& remote_entry)
|
||||||
|
-> perform_result_t {
|
||||||
|
auto curr_entry = regex101::convert_format_pattern(
|
||||||
|
format_regex_pair.first, format_regex_pair.second);
|
||||||
|
|
||||||
|
if (curr_entry.e_regex == remote_entry.e_regex) {
|
||||||
|
return {
|
||||||
|
console::user_message::ok(
|
||||||
|
attr_line_t("local regex is in sync "
|
||||||
|
"with entry ")
|
||||||
|
.append_quoted(lnav::roles::symbol(
|
||||||
|
en.re_permalink))
|
||||||
|
.append(" on regex101.com"))
|
||||||
|
.with_help(
|
||||||
|
attr_line_t("make edits on ")
|
||||||
|
.append_quoted(lnav::roles::file(
|
||||||
|
regex101::client::to_edit_url(
|
||||||
|
en.re_permalink)))
|
||||||
|
.append(" and then run this "
|
||||||
|
"command again to update "
|
||||||
|
"the local values")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto patch_res
|
||||||
|
= regex101::patch(format_regex_pair.first,
|
||||||
|
sf.sf_regex_name,
|
||||||
|
remote_entry);
|
||||||
|
|
||||||
|
if (patch_res.isErr()) {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
attr_line_t(
|
||||||
|
"unable to patch format regex: ")
|
||||||
|
.append(lnav::roles::symbol(sf.sf_name))
|
||||||
|
.append("/")
|
||||||
|
.append(lnav::roles::symbol(
|
||||||
|
sf.sf_regex_name)))
|
||||||
|
.with_reason(patch_res.unwrapErr()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto um = console::user_message::ok(
|
||||||
|
attr_line_t("format patch file written to: ")
|
||||||
|
.append(lnav::roles::file(
|
||||||
|
patch_res.unwrap().string())));
|
||||||
|
if (!format_regex_pair.first->elf_builtin_format) {
|
||||||
|
um.with_help(
|
||||||
|
attr_line_t("once the regex has been found "
|
||||||
|
"to be working correctly, move the "
|
||||||
|
"contents of the patch file to the "
|
||||||
|
"original file at:\n ")
|
||||||
|
.append(lnav::roles::file(
|
||||||
|
format_regex_pair.first
|
||||||
|
->elf_format_source_order.front()
|
||||||
|
.string())));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {um};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t regex101_default_action(const subcmd_format_t& sf)
|
||||||
|
{
|
||||||
|
auto validate_res = sf.validate_regex();
|
||||||
|
|
||||||
|
if (validate_res.isErr()) {
|
||||||
|
return {validate_res.unwrapErr()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto um = console::user_message::error(
|
||||||
|
attr_line_t("expecting an operation to perform on the ")
|
||||||
|
.append(lnav::roles::symbol(sf.sf_regex_name))
|
||||||
|
.append(" regex"));
|
||||||
|
|
||||||
|
auto get_res
|
||||||
|
= lnav::session::regex101::get_entry(sf.sf_name, sf.sf_regex_name);
|
||||||
|
if (get_res.is<lnav::session::regex101::entry>()) {
|
||||||
|
auto local_entry = get_res.get<lnav::session::regex101::entry>();
|
||||||
|
um.with_note(
|
||||||
|
attr_line_t("this regex is currently associated with the "
|
||||||
|
"following regex101.com entry:\n ")
|
||||||
|
.append(lnav::roles::file(regex101::client::to_edit_url(
|
||||||
|
local_entry.re_permalink))));
|
||||||
|
}
|
||||||
|
|
||||||
|
um.with_help(attr_line_t{"the available subcommands are:"}.append(
|
||||||
|
sf.sf_regex_app->get_subcommands({})
|
||||||
|
| lnav::itertools::fold(subcmd_reducer, attr_line_t{})));
|
||||||
|
|
||||||
|
return {um};
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t regex101_push_action(const subcmd_format_t& sf)
|
||||||
|
{
|
||||||
|
auto validate_res = sf.validate_regex();
|
||||||
|
if (validate_res.isErr()) {
|
||||||
|
return {validate_res.unwrapErr()};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto format_regex_pair = validate_res.unwrap();
|
||||||
|
auto entry = regex101::convert_format_pattern(format_regex_pair.first,
|
||||||
|
format_regex_pair.second);
|
||||||
|
auto get_meta_res
|
||||||
|
= lnav::session::regex101::get_entry(sf.sf_name, sf.sf_regex_name);
|
||||||
|
|
||||||
|
if (get_meta_res.is<lnav::session::regex101::entry>()) {
|
||||||
|
auto entry_meta
|
||||||
|
= get_meta_res.get<lnav::session::regex101::entry>();
|
||||||
|
auto retrieve_res
|
||||||
|
= regex101::client::retrieve(entry_meta.re_permalink);
|
||||||
|
|
||||||
|
if (retrieve_res.is<regex101::client::entry>()) {
|
||||||
|
auto remote_entry = retrieve_res.get<regex101::client::entry>();
|
||||||
|
|
||||||
|
if (remote_entry == entry) {
|
||||||
|
return {
|
||||||
|
console::user_message::ok(
|
||||||
|
attr_line_t("regex101 entry ")
|
||||||
|
.append(lnav::roles::symbol(
|
||||||
|
entry_meta.re_permalink))
|
||||||
|
.append(" is already up-to-date")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (retrieve_res.is<console::user_message>()) {
|
||||||
|
return {
|
||||||
|
retrieve_res.get<console::user_message>(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.e_permalink_fragment = entry_meta.re_permalink;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto upsert_res = regex101::client::upsert(entry);
|
||||||
|
auto upsert_info = upsert_res.unwrap();
|
||||||
|
|
||||||
|
if (get_meta_res.is<lnav::session::regex101::no_entry>()) {
|
||||||
|
lnav::session::regex101::insert_entry({
|
||||||
|
format_regex_pair.first->get_name().to_string(),
|
||||||
|
format_regex_pair.second->p_name,
|
||||||
|
upsert_info.cr_permalink_fragment,
|
||||||
|
upsert_info.cr_delete_code,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
console::user_message::ok(
|
||||||
|
attr_line_t("pushed regex to -- ")
|
||||||
|
.append(lnav::roles::file(regex101::client::to_edit_url(
|
||||||
|
upsert_info.cr_permalink_fragment))))
|
||||||
|
.with_help(attr_line_t("use the ")
|
||||||
|
.append_quoted("pull"_keyword)
|
||||||
|
.append(" subcommand to update the format after "
|
||||||
|
"you make changes on regex101.com")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t regex101_delete_action(const subcmd_format_t& sf)
|
||||||
|
{
|
||||||
|
auto get_res
|
||||||
|
= lnav::session::regex101::get_entry(sf.sf_name, sf.sf_regex_name);
|
||||||
|
|
||||||
|
return get_res.match(
|
||||||
|
[&sf](
|
||||||
|
const lnav::session::regex101::entry& en) -> perform_result_t {
|
||||||
|
{
|
||||||
|
auto validate_res = sf.validate_external_format();
|
||||||
|
|
||||||
|
if (validate_res.isOk()) {
|
||||||
|
auto ppath = regex101::patch_path(validate_res.unwrap(),
|
||||||
|
en.re_permalink);
|
||||||
|
|
||||||
|
if (ghc::filesystem::exists(ppath)) {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
attr_line_t("cannot delete regex101 entry "
|
||||||
|
"while patch file exists"))
|
||||||
|
.with_note(attr_line_t(" ").append(
|
||||||
|
lnav::roles::file(ppath.string())))
|
||||||
|
.with_help(attr_line_t(
|
||||||
|
"move the contents of the patch file "
|
||||||
|
"to the main log format and then "
|
||||||
|
"delete the file to continue")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
perform_result_t retval;
|
||||||
|
if (en.re_delete_code.empty()) {
|
||||||
|
retval.emplace_back(
|
||||||
|
console::user_message::warning(
|
||||||
|
attr_line_t("not deleting regex101 entry ")
|
||||||
|
.append_quoted(
|
||||||
|
lnav::roles::symbol(en.re_permalink)))
|
||||||
|
.with_reason(
|
||||||
|
"delete code is not known for this entry")
|
||||||
|
.with_note(
|
||||||
|
"formats created by importing a regex101.com "
|
||||||
|
"entry will not have a delete code"));
|
||||||
|
} else {
|
||||||
|
auto delete_res
|
||||||
|
= regex101::client::delete_entry(en.re_delete_code);
|
||||||
|
|
||||||
|
if (delete_res.isErr()) {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
"unable to delete regex101 entry")
|
||||||
|
.with_reason(delete_res.unwrapErr()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lnav::session::regex101::delete_entry(sf.sf_name,
|
||||||
|
sf.sf_regex_name);
|
||||||
|
|
||||||
|
retval.emplace_back(console::user_message::ok(
|
||||||
|
attr_line_t("deleted regex101 entry: ")
|
||||||
|
.append(lnav::roles::symbol(en.re_permalink))));
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
},
|
||||||
|
[&sf](
|
||||||
|
const lnav::session::regex101::no_entry&) -> perform_result_t {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
attr_line_t("no regex101 entry for ")
|
||||||
|
.append(lnav::roles::symbol(sf.sf_name))
|
||||||
|
.append("/")
|
||||||
|
.append(lnav::roles::symbol(sf.sf_regex_name))),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[&sf](
|
||||||
|
const lnav::session::regex101::error& err) -> perform_result_t {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
attr_line_t("unable to get regex101 entry for ")
|
||||||
|
.append(lnav::roles::symbol(sf.sf_name))
|
||||||
|
.append("/")
|
||||||
|
.append(lnav::roles::symbol(sf.sf_regex_name)))
|
||||||
|
.with_reason(err.e_msg),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct subcmd_regex101_t {
|
||||||
|
using action_t = std::function<perform_result_t(const subcmd_regex101_t&)>;
|
||||||
|
|
||||||
|
CLI::App* sr_app{nullptr};
|
||||||
|
action_t sr_action;
|
||||||
|
std::string sr_import_url;
|
||||||
|
std::string sr_import_name;
|
||||||
|
std::string sr_import_regex_name{"std"};
|
||||||
|
|
||||||
|
subcmd_regex101_t& set_action(action_t act)
|
||||||
|
{
|
||||||
|
if (!this->sr_action) {
|
||||||
|
this->sr_action = std::move(act);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t default_action(const subcmd_regex101_t& sr)
|
||||||
|
{
|
||||||
|
auto um = console::user_message::error(
|
||||||
|
"expecting an operation related to the regex101.com integration");
|
||||||
|
um.with_help(
|
||||||
|
sr.sr_app->get_subcommands({})
|
||||||
|
| lnav::itertools::fold(
|
||||||
|
subcmd_reducer, attr_line_t{"the available operations are:"}));
|
||||||
|
|
||||||
|
return {um};
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t list_action(const subcmd_regex101_t&)
|
||||||
|
{
|
||||||
|
auto get_res = lnav::session::regex101::get_entries();
|
||||||
|
|
||||||
|
if (get_res.isErr()) {
|
||||||
|
return {
|
||||||
|
console::user_message::error(
|
||||||
|
"unable to read regex101 entries from DB")
|
||||||
|
.with_reason(get_res.unwrapErr()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entries
|
||||||
|
= get_res.unwrap() | lnav::itertools::map([](const auto& elem) {
|
||||||
|
return fmt::format(
|
||||||
|
FMT_STRING(" format {} regex {} regex101\n"),
|
||||||
|
elem.re_format_name,
|
||||||
|
elem.re_regex_name);
|
||||||
|
})
|
||||||
|
| lnav::itertools::fold(
|
||||||
|
[](const auto& elem, auto& accum) {
|
||||||
|
return accum.append(elem);
|
||||||
|
},
|
||||||
|
attr_line_t{});
|
||||||
|
|
||||||
|
auto um = console::user_message::ok(
|
||||||
|
entries.add_header("the following regex101 entries were found:\n")
|
||||||
|
.with_default("no regex101 entries found"));
|
||||||
|
|
||||||
|
return {um};
|
||||||
|
}
|
||||||
|
|
||||||
|
static perform_result_t import_action(const subcmd_regex101_t& sr)
|
||||||
|
{
|
||||||
|
auto import_res = regex101::import(
|
||||||
|
sr.sr_import_url, sr.sr_import_name, sr.sr_import_regex_name);
|
||||||
|
|
||||||
|
if (import_res.isOk()) {
|
||||||
|
return {
|
||||||
|
lnav::console::user_message::ok(
|
||||||
|
attr_line_t("converted regex101 entry to format file: ")
|
||||||
|
.append(lnav::roles::file(import_res.unwrap())))
|
||||||
|
.with_note("the converted format may still have errors")
|
||||||
|
.with_help(
|
||||||
|
attr_line_t(
|
||||||
|
"use the following command to patch the regex as "
|
||||||
|
"more changes are made on regex101.com:\n")
|
||||||
|
.append(FMT_STRING(" lnav -m format {} regex {} "
|
||||||
|
"regex101 pull"),
|
||||||
|
sr.sr_import_name,
|
||||||
|
sr.sr_import_regex_name)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
import_res.unwrapErr(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using operations_v
|
||||||
|
= mapbox::util::variant<no_subcmd_t, subcmd_format_t, subcmd_regex101_t>;
|
||||||
|
|
||||||
|
class operations {
|
||||||
|
public:
|
||||||
|
operations_v o_ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<operations>
|
||||||
|
describe_cli(CLI::App& app, int argc, char* argv[])
|
||||||
|
{
|
||||||
|
auto retval = std::make_shared<operations>();
|
||||||
|
|
||||||
|
retval->o_ops = no_subcmd_t{
|
||||||
|
&app,
|
||||||
|
};
|
||||||
|
|
||||||
|
app.add_flag("-m", "Switch to the management CLI mode.");
|
||||||
|
|
||||||
|
subcmd_format_t format_args;
|
||||||
|
subcmd_regex101_t regex101_args;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* subcmd_format
|
||||||
|
= app.add_subcommand("format",
|
||||||
|
"perform operations on log file formats")
|
||||||
|
->callback([&]() {
|
||||||
|
format_args.set_action(subcmd_format_t::default_action);
|
||||||
|
retval->o_ops = format_args;
|
||||||
|
});
|
||||||
|
format_args.sf_format_app = subcmd_format;
|
||||||
|
subcmd_format
|
||||||
|
->add_option(
|
||||||
|
"format_name", format_args.sf_name, "the name of the format")
|
||||||
|
->expected(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
subcmd_format
|
||||||
|
->add_subcommand("get", "print information about a format")
|
||||||
|
->callback([&]() {
|
||||||
|
format_args.set_action(subcmd_format_t::get_action);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
subcmd_format
|
||||||
|
->add_subcommand("source",
|
||||||
|
"print the path of the first source file "
|
||||||
|
"containing this format")
|
||||||
|
->callback([&]() {
|
||||||
|
format_args.set_action(subcmd_format_t::source_action);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
subcmd_format
|
||||||
|
->add_subcommand("sources",
|
||||||
|
"print the paths of all source files "
|
||||||
|
"containing this format")
|
||||||
|
->callback([&]() {
|
||||||
|
format_args.set_action(subcmd_format_t::sources_action);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* subcmd_format_regex
|
||||||
|
= subcmd_format
|
||||||
|
->add_subcommand(
|
||||||
|
"regex",
|
||||||
|
"operate on the format's regular expressions")
|
||||||
|
->callback([&]() {
|
||||||
|
format_args.set_action(
|
||||||
|
subcmd_format_t::default_regex_action);
|
||||||
|
});
|
||||||
|
format_args.sf_regex_app = subcmd_format_regex;
|
||||||
|
subcmd_format_regex->add_option(
|
||||||
|
"regex-name",
|
||||||
|
format_args.sf_regex_name,
|
||||||
|
"the name of the regular expression to operate on");
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* subcmd_format_regex_regex101
|
||||||
|
= subcmd_format_regex
|
||||||
|
->add_subcommand("regex101",
|
||||||
|
"use regex101.com to edit this "
|
||||||
|
"regular expression")
|
||||||
|
->callback([&]() {
|
||||||
|
format_args.set_action(
|
||||||
|
subcmd_format_t::regex101_default_action);
|
||||||
|
});
|
||||||
|
format_args.sf_regex101_app = subcmd_format_regex_regex101;
|
||||||
|
|
||||||
|
{
|
||||||
|
subcmd_format_regex_regex101
|
||||||
|
->add_subcommand("push",
|
||||||
|
"create/update an entry for "
|
||||||
|
"this regex on regex101.com")
|
||||||
|
->callback([&]() {
|
||||||
|
format_args.set_action(
|
||||||
|
subcmd_format_t::regex101_push_action);
|
||||||
|
});
|
||||||
|
subcmd_format_regex_regex101
|
||||||
|
->add_subcommand(
|
||||||
|
"pull",
|
||||||
|
"create a patch format file for this "
|
||||||
|
"regular expression based on the entry in "
|
||||||
|
"regex101.com")
|
||||||
|
->callback([&]() {
|
||||||
|
format_args.set_action(
|
||||||
|
subcmd_format_t::regex101_pull_action);
|
||||||
|
});
|
||||||
|
subcmd_format_regex_regex101
|
||||||
|
->add_subcommand(
|
||||||
|
"delete",
|
||||||
|
"delete the entry regex101.com that was "
|
||||||
|
"created by a push operation")
|
||||||
|
->callback([&]() {
|
||||||
|
format_args.set_action(
|
||||||
|
subcmd_format_t::regex101_delete_action);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto* subcmd_regex101
|
||||||
|
= app.add_subcommand("regex101",
|
||||||
|
"create and edit log message regular "
|
||||||
|
"expressions using regex101.com")
|
||||||
|
->callback([&]() {
|
||||||
|
regex101_args.set_action(
|
||||||
|
subcmd_regex101_t::default_action);
|
||||||
|
retval->o_ops = regex101_args;
|
||||||
|
});
|
||||||
|
regex101_args.sr_app = subcmd_regex101;
|
||||||
|
|
||||||
|
{
|
||||||
|
subcmd_regex101
|
||||||
|
->add_subcommand("list",
|
||||||
|
"list the log format regular expression "
|
||||||
|
"linked to entries on regex101.com")
|
||||||
|
->callback([&]() {
|
||||||
|
regex101_args.set_action(subcmd_regex101_t::list_action);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto* subcmd_regex101_import
|
||||||
|
= subcmd_regex101
|
||||||
|
->add_subcommand("import",
|
||||||
|
"create a new format from a regular "
|
||||||
|
"expression on regex101.com")
|
||||||
|
->callback([&]() {
|
||||||
|
regex101_args.set_action(
|
||||||
|
subcmd_regex101_t::import_action);
|
||||||
|
});
|
||||||
|
|
||||||
|
subcmd_regex101_import->add_option(
|
||||||
|
"url",
|
||||||
|
regex101_args.sr_import_url,
|
||||||
|
"The regex101.com url to construct a log format from");
|
||||||
|
subcmd_regex101_import->add_option("name",
|
||||||
|
regex101_args.sr_import_name,
|
||||||
|
"The name for the log format");
|
||||||
|
subcmd_regex101_import
|
||||||
|
->add_option("regex-name",
|
||||||
|
regex101_args.sr_import_regex_name,
|
||||||
|
"The name for the new regex")
|
||||||
|
->always_capture_default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.parse(argc, argv);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
perform_result_t
|
||||||
|
perform(std::shared_ptr<operations> opts)
|
||||||
|
{
|
||||||
|
return opts->o_ops.match(
|
||||||
|
[](const no_subcmd_t& ns) -> perform_result_t {
|
||||||
|
auto um = console::user_message::error(
|
||||||
|
attr_line_t("expecting an operation to perform"));
|
||||||
|
um.with_help(ns.ns_root_app->get_subcommands({})
|
||||||
|
| lnav::itertools::fold(
|
||||||
|
subcmd_reducer,
|
||||||
|
attr_line_t{"the available operations are:"}));
|
||||||
|
|
||||||
|
return {um};
|
||||||
|
},
|
||||||
|
[](const subcmd_format_t& sf) { return sf.sf_action(sf); },
|
||||||
|
[](const subcmd_regex101_t& sr) { return sr.sr_action(sr); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace management
|
||||||
|
} // namespace lnav
|
53
src/lnav.management_cli.hh
Normal file
53
src/lnav.management_cli.hh
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2022, Timothy Stack
|
||||||
|
*
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of Timothy Stack nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef lnav_management_cli_hh
|
||||||
|
#define lnav_management_cli_hh
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "base/lnav.console.hh"
|
||||||
|
#include "CLI/CLI.hpp"
|
||||||
|
|
||||||
|
namespace lnav {
|
||||||
|
namespace management {
|
||||||
|
|
||||||
|
class operations;
|
||||||
|
|
||||||
|
std::shared_ptr<operations> describe_cli(CLI::App& app, int argc, char* argv[]);
|
||||||
|
|
||||||
|
using perform_result_t = std::vector<lnav::console::user_message>;
|
||||||
|
|
||||||
|
perform_result_t perform(std::shared_ptr<operations> opts);
|
||||||
|
|
||||||
|
} // namespace management
|
||||||
|
} // namespace lnav
|
||||||
|
|
||||||
|
#endif
|
@ -55,6 +55,7 @@
|
|||||||
#include "db_sub_source.hh"
|
#include "db_sub_source.hh"
|
||||||
#include "field_overlay_source.hh"
|
#include "field_overlay_source.hh"
|
||||||
#include "fmt/printf.h"
|
#include "fmt/printf.h"
|
||||||
|
#include "lnav.indexing.hh"
|
||||||
#include "lnav_commands.hh"
|
#include "lnav_commands.hh"
|
||||||
#include "lnav_config.hh"
|
#include "lnav_config.hh"
|
||||||
#include "lnav_util.hh"
|
#include "lnav_util.hh"
|
||||||
@ -613,11 +614,12 @@ com_goto_mark(exec_context& ec,
|
|||||||
|
|
||||||
if (args.size() > 1) {
|
if (args.size() > 1) {
|
||||||
for (size_t lpc = 1; lpc < args.size(); lpc++) {
|
for (size_t lpc = 1; lpc < args.size(); lpc++) {
|
||||||
auto bt = bookmark_type_t::find_type(args[lpc]);
|
auto bt_opt = bookmark_type_t::find_type(args[lpc]);
|
||||||
if (bt == nullptr) {
|
if (!bt_opt) {
|
||||||
return ec.make_error("unknown bookmark type");
|
return ec.make_error("unknown bookmark type: {}",
|
||||||
|
args[lpc]);
|
||||||
}
|
}
|
||||||
mark_types.insert(bt);
|
mark_types.insert(bt_opt.value());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mark_types = DEFAULT_TYPES;
|
mark_types = DEFAULT_TYPES;
|
||||||
@ -826,34 +828,6 @@ json_write_row(yajl_gen handle, int row)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
write_line_to(FILE* outfile, const attr_line_t& al)
|
|
||||||
{
|
|
||||||
const auto& al_attrs = al.get_attrs();
|
|
||||||
auto lr = find_string_attr_range(al_attrs, &SA_ORIGINAL_LINE);
|
|
||||||
const auto line_meta_opt = get_string_attr(al_attrs, logline::L_META);
|
|
||||||
|
|
||||||
if (lr.lr_start > 1) {
|
|
||||||
// If the line is prefixed with some extra information, include that
|
|
||||||
// in the output. For example, the log file name or time offset.
|
|
||||||
lr = line_range{0, -1};
|
|
||||||
}
|
|
||||||
|
|
||||||
fwrite(lr.substr(al.get_string()), 1, lr.sublen(al.get_string()), outfile);
|
|
||||||
fwrite("\n", 1, 1, outfile);
|
|
||||||
|
|
||||||
if (line_meta_opt) {
|
|
||||||
auto bm = line_meta_opt.value().get();
|
|
||||||
|
|
||||||
if (!bm->bm_comment.empty()) {
|
|
||||||
fprintf(outfile, " // %s\n", bm->bm_comment.c_str());
|
|
||||||
}
|
|
||||||
if (!bm->bm_tags.empty()) {
|
|
||||||
fmt::print(outfile, " -- {}\n", fmt::join(bm->bm_tags, " "));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result<std::string, lnav::console::user_message>
|
static Result<std::string, lnav::console::user_message>
|
||||||
com_save_to(exec_context& ec,
|
com_save_to(exec_context& ec,
|
||||||
std::string cmdline,
|
std::string cmdline,
|
||||||
@ -1129,13 +1103,33 @@ com_save_to(exec_context& ec,
|
|||||||
|
|
||||||
vis_line_t top = tc->get_top();
|
vis_line_t top = tc->get_top();
|
||||||
vis_line_t bottom = tc->get_bottom();
|
vis_line_t bottom = tc->get_bottom();
|
||||||
|
auto y = 0_vl;
|
||||||
std::vector<attr_line_t> rows(bottom - top + 1);
|
std::vector<attr_line_t> rows(bottom - top + 1);
|
||||||
|
attr_line_t ov_al;
|
||||||
|
|
||||||
|
auto* los = tc->get_overlay_source();
|
||||||
tc->listview_value_for_rows(*tc, top, rows);
|
tc->listview_value_for_rows(*tc, top, rows);
|
||||||
for (auto& al : rows) {
|
for (const auto& al : rows) {
|
||||||
|
while (los != nullptr
|
||||||
|
&& los->list_value_for_overlay(
|
||||||
|
*tc, y, tc->get_inner_height(), top, ov_al))
|
||||||
|
{
|
||||||
|
write_line_to(outfile, ov_al);
|
||||||
|
++y;
|
||||||
|
}
|
||||||
write_line_to(outfile, al);
|
write_line_to(outfile, al);
|
||||||
|
|
||||||
line_count += 1;
|
line_count += 1;
|
||||||
|
++top;
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
while (los != nullptr
|
||||||
|
&& los->list_value_for_overlay(
|
||||||
|
*tc, y, tc->get_inner_height(), top, ov_al)
|
||||||
|
&& !ov_al.empty())
|
||||||
|
{
|
||||||
|
write_line_to(outfile, ov_al);
|
||||||
|
++y;
|
||||||
}
|
}
|
||||||
|
|
||||||
tc->set_word_wrap(wrapped);
|
tc->set_word_wrap(wrapped);
|
||||||
@ -1225,9 +1219,10 @@ com_save_to(exec_context& ec,
|
|||||||
|
|
||||||
tc->set_word_wrap(wrapped);
|
tc->set_word_wrap(wrapped);
|
||||||
} else {
|
} else {
|
||||||
|
auto* los = tc->get_overlay_source();
|
||||||
std::vector<attr_line_t> rows(1);
|
std::vector<attr_line_t> rows(1);
|
||||||
|
attr_line_t ov_al;
|
||||||
size_t count = 0;
|
size_t count = 0;
|
||||||
std::string line;
|
|
||||||
|
|
||||||
for (auto iter = all_user_marks.begin(); iter != all_user_marks.end();
|
for (auto iter = all_user_marks.begin(); iter != all_user_marks.end();
|
||||||
iter++, count++)
|
iter++, count++)
|
||||||
@ -1238,6 +1233,15 @@ com_save_to(exec_context& ec,
|
|||||||
tc->listview_value_for_rows(*tc, *iter, rows);
|
tc->listview_value_for_rows(*tc, *iter, rows);
|
||||||
write_line_to(outfile, rows[0]);
|
write_line_to(outfile, rows[0]);
|
||||||
|
|
||||||
|
auto y = 1_vl;
|
||||||
|
while (los != nullptr
|
||||||
|
&& los->list_value_for_overlay(
|
||||||
|
*tc, y, tc->get_inner_height(), *iter, ov_al))
|
||||||
|
{
|
||||||
|
write_line_to(outfile, ov_al);
|
||||||
|
++y;
|
||||||
|
}
|
||||||
|
|
||||||
line_count += 1;
|
line_count += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1551,7 +1555,7 @@ com_highlight(exec_context& ec,
|
|||||||
|
|
||||||
if (lnav_data.ld_rl_view != nullptr) {
|
if (lnav_data.ld_rl_view != nullptr) {
|
||||||
lnav_data.ld_rl_view->add_possibility(
|
lnav_data.ld_rl_view->add_possibility(
|
||||||
LNM_COMMAND, "highlight", args[1]);
|
ln_mode_t::COMMAND, "highlight", args[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = "info: highlight pattern now active";
|
retval = "info: highlight pattern now active";
|
||||||
@ -1591,7 +1595,7 @@ com_clear_highlight(exec_context& ec,
|
|||||||
|
|
||||||
if (lnav_data.ld_rl_view != NULL) {
|
if (lnav_data.ld_rl_view != NULL) {
|
||||||
lnav_data.ld_rl_view->rem_possibility(
|
lnav_data.ld_rl_view->rem_possibility(
|
||||||
LNM_COMMAND, "highlight", args[1]);
|
ln_mode_t::COMMAND, "highlight", args[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1991,7 +1995,7 @@ com_create_logline_table(exec_context& ec,
|
|||||||
custom_logline_tables.insert(args[1]);
|
custom_logline_tables.insert(args[1]);
|
||||||
if (lnav_data.ld_rl_view != NULL) {
|
if (lnav_data.ld_rl_view != NULL) {
|
||||||
lnav_data.ld_rl_view->add_possibility(
|
lnav_data.ld_rl_view->add_possibility(
|
||||||
LNM_COMMAND, "custom-table", args[1]);
|
ln_mode_t::COMMAND, "custom-table", args[1]);
|
||||||
}
|
}
|
||||||
retval = "info: created new log table -- " + args[1];
|
retval = "info: created new log table -- " + args[1];
|
||||||
} else {
|
} else {
|
||||||
@ -2032,7 +2036,7 @@ com_delete_logline_table(exec_context& ec,
|
|||||||
if (rc.empty()) {
|
if (rc.empty()) {
|
||||||
if (lnav_data.ld_rl_view != NULL) {
|
if (lnav_data.ld_rl_view != NULL) {
|
||||||
lnav_data.ld_rl_view->rem_possibility(
|
lnav_data.ld_rl_view->rem_possibility(
|
||||||
LNM_COMMAND, "custom-table", args[1]);
|
ln_mode_t::COMMAND, "custom-table", args[1]);
|
||||||
}
|
}
|
||||||
retval = "info: deleted logline table";
|
retval = "info: deleted logline table";
|
||||||
} else {
|
} else {
|
||||||
@ -2103,7 +2107,7 @@ com_create_search_table(exec_context& ec,
|
|||||||
custom_search_tables.insert(args[1]);
|
custom_search_tables.insert(args[1]);
|
||||||
if (lnav_data.ld_rl_view != NULL) {
|
if (lnav_data.ld_rl_view != NULL) {
|
||||||
lnav_data.ld_rl_view->add_possibility(
|
lnav_data.ld_rl_view->add_possibility(
|
||||||
LNM_COMMAND, "search-table", args[1]);
|
ln_mode_t::COMMAND, "search-table", args[1]);
|
||||||
}
|
}
|
||||||
retval = "info: created new search table -- " + args[1];
|
retval = "info: created new search table -- " + args[1];
|
||||||
} else {
|
} else {
|
||||||
@ -2140,7 +2144,7 @@ com_delete_search_table(exec_context& ec,
|
|||||||
if (rc.empty()) {
|
if (rc.empty()) {
|
||||||
if (lnav_data.ld_rl_view != NULL) {
|
if (lnav_data.ld_rl_view != NULL) {
|
||||||
lnav_data.ld_rl_view->rem_possibility(
|
lnav_data.ld_rl_view->rem_possibility(
|
||||||
LNM_COMMAND, "search-table", args[1]);
|
ln_mode_t::COMMAND, "search-table", args[1]);
|
||||||
}
|
}
|
||||||
retval = "info: deleted search table";
|
retval = "info: deleted search table";
|
||||||
} else {
|
} else {
|
||||||
@ -3156,8 +3160,7 @@ com_summarize(exec_context& ec,
|
|||||||
const auto& top_source = ec.ec_source.top();
|
const auto& top_source = ec.ec_source.top();
|
||||||
sql_progress_guard progress_guard(sql_progress,
|
sql_progress_guard progress_guard(sql_progress,
|
||||||
sql_progress_finished,
|
sql_progress_finished,
|
||||||
top_source.s_source,
|
top_source.s_location,
|
||||||
top_source.s_line,
|
|
||||||
top_source.s_content);
|
top_source.s_content);
|
||||||
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
|
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
|
||||||
int retcode;
|
int retcode;
|
||||||
@ -3848,6 +3851,14 @@ com_poll_now(exec_context& ec,
|
|||||||
return Ok(std::string());
|
return Ok(std::string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Result<std::string, lnav::console::user_message>
|
||||||
|
com_test_comment(exec_context& ec,
|
||||||
|
std::string cmdline,
|
||||||
|
std::vector<std::string>& args)
|
||||||
|
{
|
||||||
|
return Ok(std::string());
|
||||||
|
}
|
||||||
|
|
||||||
static Result<std::string, lnav::console::user_message>
|
static Result<std::string, lnav::console::user_message>
|
||||||
com_redraw(exec_context& ec,
|
com_redraw(exec_context& ec,
|
||||||
std::string cmdline,
|
std::string cmdline,
|
||||||
@ -4014,7 +4025,9 @@ com_config(exec_context& ec,
|
|||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
args.emplace_back("config-option");
|
args.emplace_back("config-option");
|
||||||
} else if (args.size() > 1) {
|
} else if (args.size() > 1) {
|
||||||
yajlpp_parse_context ypc("input", &lnav_config_handlers);
|
static const auto INPUT_SRC = intern_string::lookup("input");
|
||||||
|
|
||||||
|
yajlpp_parse_context ypc(INPUT_SRC, &lnav_config_handlers);
|
||||||
std::vector<lnav::console::user_message> errors;
|
std::vector<lnav::console::user_message> errors;
|
||||||
std::string option = args[1];
|
std::string option = args[1];
|
||||||
|
|
||||||
@ -4129,10 +4142,7 @@ com_config(exec_context& ec,
|
|||||||
if (changed) {
|
if (changed) {
|
||||||
intern_string_t path = intern_string::lookup(option);
|
intern_string_t path = intern_string::lookup(option);
|
||||||
|
|
||||||
lnav_config_locations[path] = {
|
lnav_config_locations[path] = ec.ec_source.top().s_location;
|
||||||
intern_string::lookup(ec.ec_source.top().s_source),
|
|
||||||
ec.ec_source.top().s_line,
|
|
||||||
};
|
|
||||||
reload_config(errors);
|
reload_config(errors);
|
||||||
|
|
||||||
if (!errors.empty()) {
|
if (!errors.empty()) {
|
||||||
@ -4170,7 +4180,9 @@ com_reset_config(exec_context& ec,
|
|||||||
} else if (args.size() == 1) {
|
} else if (args.size() == 1) {
|
||||||
return ec.make_error("expecting a configuration option to reset");
|
return ec.make_error("expecting a configuration option to reset");
|
||||||
} else {
|
} else {
|
||||||
yajlpp_parse_context ypc("input", &lnav_config_handlers);
|
static const auto INPUT_SRC = intern_string::lookup("input");
|
||||||
|
|
||||||
|
yajlpp_parse_context ypc(INPUT_SRC, &lnav_config_handlers);
|
||||||
std::string option = args[1];
|
std::string option = args[1];
|
||||||
|
|
||||||
lnav_config = rollback_lnav_config;
|
lnav_config = rollback_lnav_config;
|
||||||
@ -4401,10 +4413,10 @@ public:
|
|||||||
class db_spectro_value_source : public spectrogram_value_source {
|
class db_spectro_value_source : public spectrogram_value_source {
|
||||||
public:
|
public:
|
||||||
db_spectro_value_source(std::string colname)
|
db_spectro_value_source(std::string colname)
|
||||||
: dsvs_colname(std::move(colname)), dsvs_begin_time(0), dsvs_end_time(0)
|
: dsvs_colname(std::move(colname))
|
||||||
{
|
{
|
||||||
this->update_stats();
|
this->update_stats();
|
||||||
};
|
}
|
||||||
|
|
||||||
void update_stats()
|
void update_stats()
|
||||||
{
|
{
|
||||||
@ -4425,12 +4437,12 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->dsvs_column_index == -1) {
|
if (!this->dsvs_column_index) {
|
||||||
this->dsvs_error_msg = "unknown column -- " + this->dsvs_colname;
|
this->dsvs_error_msg = "unknown column -- " + this->dsvs_colname;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dls.dls_headers[this->dsvs_column_index].hm_graphable) {
|
if (!dls.dls_headers[this->dsvs_column_index.value()].hm_graphable) {
|
||||||
this->dsvs_error_msg
|
this->dsvs_error_msg
|
||||||
= "column is not numeric -- " + this->dsvs_colname;
|
= "column is not numeric -- " + this->dsvs_colname;
|
||||||
return;
|
return;
|
||||||
@ -4449,9 +4461,9 @@ public:
|
|||||||
this->dsvs_stats.lvs_min_value = bs.bs_min_value;
|
this->dsvs_stats.lvs_min_value = bs.bs_min_value;
|
||||||
this->dsvs_stats.lvs_max_value = bs.bs_max_value;
|
this->dsvs_stats.lvs_max_value = bs.bs_max_value;
|
||||||
this->dsvs_stats.lvs_count = dls.dls_rows.size();
|
this->dsvs_stats.lvs_count = dls.dls_rows.size();
|
||||||
};
|
}
|
||||||
|
|
||||||
void spectro_bounds(spectrogram_bounds& sb_out)
|
void spectro_bounds(spectrogram_bounds& sb_out) override
|
||||||
{
|
{
|
||||||
db_label_source& dls = lnav_data.ld_db_row_source;
|
db_label_source& dls = lnav_data.ld_db_row_source;
|
||||||
|
|
||||||
@ -4466,9 +4478,9 @@ public:
|
|||||||
sb_out.sb_min_value_out = this->dsvs_stats.lvs_min_value;
|
sb_out.sb_min_value_out = this->dsvs_stats.lvs_min_value;
|
||||||
sb_out.sb_max_value_out = this->dsvs_stats.lvs_max_value;
|
sb_out.sb_max_value_out = this->dsvs_stats.lvs_max_value;
|
||||||
sb_out.sb_count = this->dsvs_stats.lvs_count;
|
sb_out.sb_count = this->dsvs_stats.lvs_count;
|
||||||
};
|
}
|
||||||
|
|
||||||
void spectro_row(spectrogram_request& sr, spectrogram_row& row_out)
|
void spectro_row(spectrogram_request& sr, spectrogram_row& row_out) override
|
||||||
{
|
{
|
||||||
db_label_source& dls = lnav_data.ld_db_row_source;
|
db_label_source& dls = lnav_data.ld_db_row_source;
|
||||||
auto begin_row = dls.row_for_time({sr.sr_begin_time, 0}).value_or(0_vl);
|
auto begin_row = dls.row_for_time({sr.sr_begin_time, 0}).value_or(0_vl);
|
||||||
@ -4478,23 +4490,25 @@ public:
|
|||||||
for (auto lpc = begin_row; lpc < end_row; ++lpc) {
|
for (auto lpc = begin_row; lpc < end_row; ++lpc) {
|
||||||
double value = 0.0;
|
double value = 0.0;
|
||||||
|
|
||||||
sscanf(dls.dls_rows[lpc][this->dsvs_column_index], "%lf", &value);
|
sscanf(dls.dls_rows[lpc][this->dsvs_column_index.value()],
|
||||||
|
"%lf",
|
||||||
|
&value);
|
||||||
|
|
||||||
row_out.add_value(sr, value, false);
|
row_out.add_value(sr, value, false);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
void spectro_mark(textview_curses& tc,
|
void spectro_mark(textview_curses& tc,
|
||||||
time_t begin_time,
|
time_t begin_time,
|
||||||
time_t end_time,
|
time_t end_time,
|
||||||
double range_min,
|
double range_min,
|
||||||
double range_max){};
|
double range_max) override{};
|
||||||
|
|
||||||
std::string dsvs_colname;
|
std::string dsvs_colname;
|
||||||
logline_value_stats dsvs_stats;
|
logline_value_stats dsvs_stats;
|
||||||
time_t dsvs_begin_time;
|
time_t dsvs_begin_time{0};
|
||||||
time_t dsvs_end_time;
|
time_t dsvs_end_time{0};
|
||||||
int dsvs_column_index;
|
nonstd::optional<size_t> dsvs_column_index;
|
||||||
std::string dsvs_error_msg;
|
std::string dsvs_error_msg;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4589,9 +4603,10 @@ command_prompt(std::vector<std::string>& args)
|
|||||||
|
|
||||||
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
|
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
|
||||||
|
|
||||||
lnav_data.ld_rl_view->clear_possibilities(LNM_COMMAND,
|
lnav_data.ld_rl_view->clear_possibilities(ln_mode_t::COMMAND,
|
||||||
"numeric-colname");
|
"numeric-colname");
|
||||||
lnav_data.ld_rl_view->clear_possibilities(LNM_COMMAND, "colname");
|
lnav_data.ld_rl_view->clear_possibilities(ln_mode_t::COMMAND,
|
||||||
|
"colname");
|
||||||
|
|
||||||
ldh.parse_line(log_view.get_top(), true);
|
ldh.parse_line(log_view.get_top(), true);
|
||||||
|
|
||||||
@ -4604,7 +4619,7 @@ command_prompt(std::vector<std::string>& args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
lnav_data.ld_rl_view->add_possibility(
|
lnav_data.ld_rl_view->add_possibility(
|
||||||
LNM_COMMAND, "numeric-colname", dls_header.hm_name);
|
ln_mode_t::COMMAND, "numeric-colname", dls_header.hm_name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (auto& ldh_line_value : ldh.ldh_line_values) {
|
for (auto& ldh_line_value : ldh.ldh_line_values) {
|
||||||
@ -4622,67 +4637,74 @@ command_prompt(std::vector<std::string>& args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
lnav_data.ld_rl_view->add_possibility(
|
lnav_data.ld_rl_view->add_possibility(
|
||||||
LNM_COMMAND, "numeric-colname", meta.lvm_name.to_string());
|
ln_mode_t::COMMAND,
|
||||||
|
"numeric-colname",
|
||||||
|
meta.lvm_name.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& cn_name : ldh.ldh_namer->cn_names) {
|
for (auto& cn_name : ldh.ldh_namer->cn_names) {
|
||||||
lnav_data.ld_rl_view->add_possibility(
|
lnav_data.ld_rl_view->add_possibility(
|
||||||
LNM_COMMAND, "colname", cn_name);
|
ln_mode_t::COMMAND, "colname", cn_name);
|
||||||
}
|
}
|
||||||
for (const auto& iter : ldh.ldh_namer->cn_builtin_names) {
|
for (const auto& iter : ldh.ldh_namer->cn_builtin_names) {
|
||||||
if (iter == "col") {
|
if (iter == "col") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND, "colname", iter);
|
lnav_data.ld_rl_view->add_possibility(
|
||||||
|
ln_mode_t::COMMAND, "colname", iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
ldh.clear();
|
ldh.clear();
|
||||||
|
|
||||||
readline_curses* rlc = lnav_data.ld_rl_view;
|
readline_curses* rlc = lnav_data.ld_rl_view;
|
||||||
|
|
||||||
rlc->clear_possibilities(LNM_COMMAND, "move-time");
|
rlc->clear_possibilities(ln_mode_t::COMMAND, "move-time");
|
||||||
rlc->add_possibility(LNM_COMMAND, "move-time", MOVE_TIMES);
|
rlc->add_possibility(ln_mode_t::COMMAND, "move-time", MOVE_TIMES);
|
||||||
rlc->clear_possibilities(LNM_COMMAND, "line-time");
|
rlc->clear_possibilities(ln_mode_t::COMMAND, "line-time");
|
||||||
{
|
{
|
||||||
struct timeval tv = lf->get_time_offset();
|
struct timeval tv = lf->get_time_offset();
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
|
|
||||||
sql_strftime(
|
sql_strftime(
|
||||||
buffer, sizeof(buffer), ll->get_time(), ll->get_millis(), 'T');
|
buffer, sizeof(buffer), ll->get_time(), ll->get_millis(), 'T');
|
||||||
rlc->add_possibility(LNM_COMMAND, "line-time", buffer);
|
rlc->add_possibility(ln_mode_t::COMMAND, "line-time", buffer);
|
||||||
rlc->add_possibility(LNM_COMMAND, "move-time", buffer);
|
rlc->add_possibility(ln_mode_t::COMMAND, "move-time", buffer);
|
||||||
sql_strftime(buffer,
|
sql_strftime(buffer,
|
||||||
sizeof(buffer),
|
sizeof(buffer),
|
||||||
ll->get_time() - tv.tv_sec,
|
ll->get_time() - tv.tv_sec,
|
||||||
ll->get_millis() - (tv.tv_usec / 1000),
|
ll->get_millis() - (tv.tv_usec / 1000),
|
||||||
'T');
|
'T');
|
||||||
rlc->add_possibility(LNM_COMMAND, "line-time", buffer);
|
rlc->add_possibility(ln_mode_t::COMMAND, "line-time", buffer);
|
||||||
rlc->add_possibility(LNM_COMMAND, "move-time", buffer);
|
rlc->add_possibility(ln_mode_t::COMMAND, "move-time", buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rollback_lnav_config = lnav_config;
|
rollback_lnav_config = lnav_config;
|
||||||
lnav_data.ld_doc_status_source.set_title("Command Help");
|
lnav_data.ld_doc_status_source.set_title("Command Help");
|
||||||
add_view_text_possibilities(
|
add_view_text_possibilities(lnav_data.ld_rl_view,
|
||||||
lnav_data.ld_rl_view, LNM_COMMAND, "filter", tc);
|
ln_mode_t::COMMAND,
|
||||||
|
"filter",
|
||||||
|
tc,
|
||||||
|
text_quoting::none);
|
||||||
lnav_data.ld_rl_view->add_possibility(
|
lnav_data.ld_rl_view->add_possibility(
|
||||||
LNM_COMMAND, "filter", tc->get_current_search());
|
ln_mode_t::COMMAND, "filter", tc->get_current_search());
|
||||||
add_filter_possibilities(tc);
|
add_filter_possibilities(tc);
|
||||||
add_mark_possibilities();
|
add_mark_possibilities();
|
||||||
add_config_possibilities();
|
add_config_possibilities();
|
||||||
add_env_possibilities(LNM_COMMAND);
|
add_env_possibilities(ln_mode_t::COMMAND);
|
||||||
add_tag_possibilities();
|
add_tag_possibilities();
|
||||||
add_file_possibilities();
|
add_file_possibilities();
|
||||||
add_recent_netlocs_possibilities();
|
add_recent_netlocs_possibilities();
|
||||||
|
|
||||||
if (tc == &lnav_data.ld_views[LNV_LOG]) {
|
if (tc == &lnav_data.ld_views[LNV_LOG]) {
|
||||||
add_filter_expr_possibilities(
|
add_filter_expr_possibilities(
|
||||||
lnav_data.ld_rl_view, LNM_COMMAND, "filter-expr-syms");
|
lnav_data.ld_rl_view, ln_mode_t::COMMAND, "filter-expr-syms");
|
||||||
}
|
}
|
||||||
lnav_data.ld_mode = LNM_COMMAND;
|
lnav_data.ld_mode = ln_mode_t::COMMAND;
|
||||||
lnav_data.ld_rl_view->focus(
|
lnav_data.ld_rl_view->focus(ln_mode_t::COMMAND,
|
||||||
LNM_COMMAND, cget(args, 2).value_or(":"), cget(args, 3).value_or(""));
|
cget(args, 2).value_or(":"),
|
||||||
|
cget(args, 3).value_or(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -4691,19 +4713,21 @@ script_prompt(std::vector<std::string>& args)
|
|||||||
textview_curses* tc = *lnav_data.ld_view_stack.top();
|
textview_curses* tc = *lnav_data.ld_view_stack.top();
|
||||||
auto& scripts = injector::get<available_scripts&>();
|
auto& scripts = injector::get<available_scripts&>();
|
||||||
|
|
||||||
lnav_data.ld_mode = LNM_EXEC;
|
lnav_data.ld_mode = ln_mode_t::EXEC;
|
||||||
|
|
||||||
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
|
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
|
||||||
lnav_data.ld_rl_view->clear_possibilities(LNM_EXEC, "__command");
|
lnav_data.ld_rl_view->clear_possibilities(ln_mode_t::EXEC, "__command");
|
||||||
find_format_scripts(lnav_data.ld_config_paths, scripts);
|
find_format_scripts(lnav_data.ld_config_paths, scripts);
|
||||||
for (const auto& iter : scripts.as_scripts) {
|
for (const auto& iter : scripts.as_scripts) {
|
||||||
lnav_data.ld_rl_view->add_possibility(
|
lnav_data.ld_rl_view->add_possibility(
|
||||||
LNM_EXEC, "__command", iter.first);
|
ln_mode_t::EXEC, "__command", iter.first);
|
||||||
}
|
}
|
||||||
add_view_text_possibilities(lnav_data.ld_rl_view, LNM_EXEC, "*", tc);
|
add_view_text_possibilities(
|
||||||
add_env_possibilities(LNM_EXEC);
|
lnav_data.ld_rl_view, ln_mode_t::EXEC, "*", tc, text_quoting::regex);
|
||||||
lnav_data.ld_rl_view->focus(
|
add_env_possibilities(ln_mode_t::EXEC);
|
||||||
LNM_EXEC, cget(args, 2).value_or("|"), cget(args, 3).value_or(""));
|
lnav_data.ld_rl_view->focus(ln_mode_t::EXEC,
|
||||||
|
cget(args, 2).value_or("|"),
|
||||||
|
cget(args, 3).value_or(""));
|
||||||
lnav_data.ld_bottom_source.set_prompt(
|
lnav_data.ld_bottom_source.set_prompt(
|
||||||
"Enter a script to execute: (Press " ANSI_BOLD("CTRL+]") " to abort)");
|
"Enter a script to execute: (Press " ANSI_BOLD("CTRL+]") " to abort)");
|
||||||
}
|
}
|
||||||
@ -4713,11 +4737,13 @@ search_prompt(std::vector<std::string>& args)
|
|||||||
{
|
{
|
||||||
textview_curses* tc = *lnav_data.ld_view_stack.top();
|
textview_curses* tc = *lnav_data.ld_view_stack.top();
|
||||||
|
|
||||||
lnav_data.ld_mode = LNM_SEARCH;
|
lnav_data.ld_mode = ln_mode_t::SEARCH;
|
||||||
lnav_data.ld_search_start_line = tc->get_top();
|
lnav_data.ld_search_start_line = tc->get_top();
|
||||||
add_view_text_possibilities(lnav_data.ld_rl_view, LNM_SEARCH, "*", tc);
|
add_view_text_possibilities(
|
||||||
lnav_data.ld_rl_view->focus(
|
lnav_data.ld_rl_view, ln_mode_t::SEARCH, "*", tc, text_quoting::regex);
|
||||||
LNM_SEARCH, cget(args, 2).value_or("/"), cget(args, 3).value_or(""));
|
lnav_data.ld_rl_view->focus(ln_mode_t::SEARCH,
|
||||||
|
cget(args, 2).value_or("/"),
|
||||||
|
cget(args, 3).value_or(""));
|
||||||
lnav_data.ld_doc_status_source.set_title("Syntax Help");
|
lnav_data.ld_doc_status_source.set_title("Syntax Help");
|
||||||
rl_set_help();
|
rl_set_help();
|
||||||
lnav_data.ld_bottom_source.set_prompt(
|
lnav_data.ld_bottom_source.set_prompt(
|
||||||
@ -4729,13 +4755,14 @@ search_prompt(std::vector<std::string>& args)
|
|||||||
static void
|
static void
|
||||||
search_filters_prompt(std::vector<std::string>& args)
|
search_filters_prompt(std::vector<std::string>& args)
|
||||||
{
|
{
|
||||||
lnav_data.ld_mode = LNM_SEARCH_FILTERS;
|
lnav_data.ld_mode = ln_mode_t::SEARCH_FILTERS;
|
||||||
lnav_data.ld_filter_view.reload_data();
|
lnav_data.ld_filter_view.reload_data();
|
||||||
add_view_text_possibilities(lnav_data.ld_rl_view,
|
add_view_text_possibilities(lnav_data.ld_rl_view,
|
||||||
LNM_SEARCH_FILTERS,
|
ln_mode_t::SEARCH_FILTERS,
|
||||||
"*",
|
"*",
|
||||||
&lnav_data.ld_filter_view);
|
&lnav_data.ld_filter_view,
|
||||||
lnav_data.ld_rl_view->focus(LNM_SEARCH_FILTERS,
|
text_quoting::regex);
|
||||||
|
lnav_data.ld_rl_view->focus(ln_mode_t::SEARCH_FILTERS,
|
||||||
cget(args, 2).value_or("/"),
|
cget(args, 2).value_or("/"),
|
||||||
cget(args, 3).value_or(""));
|
cget(args, 3).value_or(""));
|
||||||
lnav_data.ld_bottom_source.set_prompt(
|
lnav_data.ld_bottom_source.set_prompt(
|
||||||
@ -4749,12 +4776,13 @@ search_files_prompt(std::vector<std::string>& args)
|
|||||||
{
|
{
|
||||||
static const std::regex re_escape(R"(([.\^$*+?()\[\]{}\\|]))");
|
static const std::regex re_escape(R"(([.\^$*+?()\[\]{}\\|]))");
|
||||||
|
|
||||||
lnav_data.ld_mode = LNM_SEARCH_FILES;
|
lnav_data.ld_mode = ln_mode_t::SEARCH_FILES;
|
||||||
for (const auto& lf : lnav_data.ld_active_files.fc_files) {
|
for (const auto& lf : lnav_data.ld_active_files.fc_files) {
|
||||||
auto path = pcrepp::quote(lf->get_unique_path());
|
auto path = pcrepp::quote(lf->get_unique_path());
|
||||||
lnav_data.ld_rl_view->add_possibility(LNM_SEARCH_FILES, "*", path);
|
lnav_data.ld_rl_view->add_possibility(
|
||||||
|
ln_mode_t::SEARCH_FILES, "*", path);
|
||||||
}
|
}
|
||||||
lnav_data.ld_rl_view->focus(LNM_SEARCH_FILES,
|
lnav_data.ld_rl_view->focus(ln_mode_t::SEARCH_FILES,
|
||||||
cget(args, 2).value_or("/"),
|
cget(args, 2).value_or("/"),
|
||||||
cget(args, 3).value_or(""));
|
cget(args, 3).value_or(""));
|
||||||
lnav_data.ld_bottom_source.set_prompt(
|
lnav_data.ld_bottom_source.set_prompt(
|
||||||
@ -4771,10 +4799,11 @@ sql_prompt(std::vector<std::string>& args)
|
|||||||
|
|
||||||
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
|
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
|
||||||
|
|
||||||
lnav_data.ld_mode = LNM_SQL;
|
lnav_data.ld_mode = ln_mode_t::SQL;
|
||||||
setup_logline_table(lnav_data.ld_exec_context);
|
setup_logline_table(lnav_data.ld_exec_context);
|
||||||
lnav_data.ld_rl_view->focus(
|
lnav_data.ld_rl_view->focus(ln_mode_t::SQL,
|
||||||
LNM_SQL, cget(args, 2).value_or(";"), cget(args, 3).value_or(""));
|
cget(args, 2).value_or(";"),
|
||||||
|
cget(args, 3).value_or(""));
|
||||||
|
|
||||||
lnav_data.ld_doc_status_source.set_title("Query Help");
|
lnav_data.ld_doc_status_source.set_title("Query Help");
|
||||||
rl_set_help();
|
rl_set_help();
|
||||||
@ -4794,10 +4823,11 @@ user_prompt(std::vector<std::string>& args)
|
|||||||
textview_curses* tc = *lnav_data.ld_view_stack.top();
|
textview_curses* tc = *lnav_data.ld_view_stack.top();
|
||||||
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
|
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
|
||||||
|
|
||||||
lnav_data.ld_mode = LNM_USER;
|
lnav_data.ld_mode = ln_mode_t::USER;
|
||||||
setup_logline_table(lnav_data.ld_exec_context);
|
setup_logline_table(lnav_data.ld_exec_context);
|
||||||
lnav_data.ld_rl_view->focus(
|
lnav_data.ld_rl_view->focus(ln_mode_t::USER,
|
||||||
LNM_USER, cget(args, 2).value_or("? "), cget(args, 3).value_or(""));
|
cget(args, 2).value_or("? "),
|
||||||
|
cget(args, 3).value_or(""));
|
||||||
|
|
||||||
lnav_data.ld_bottom_source.update_loading(0, 0);
|
lnav_data.ld_bottom_source.update_loading(0, 0);
|
||||||
lnav_data.ld_status[LNS_BOTTOM].do_update();
|
lnav_data.ld_status[LNS_BOTTOM].do_update();
|
||||||
@ -5700,10 +5730,12 @@ init_lnav_commands(readline_context::command_map_t& cmd_map)
|
|||||||
}
|
}
|
||||||
if (getenv("lnav_test") != nullptr) {
|
if (getenv("lnav_test") != nullptr) {
|
||||||
static readline_context::command_t rebuild(com_rebuild),
|
static readline_context::command_t rebuild(com_rebuild),
|
||||||
shexec(com_shexec), poll_now(com_poll_now);
|
shexec(com_shexec), poll_now(com_poll_now),
|
||||||
|
test_comment(com_test_comment);
|
||||||
|
|
||||||
cmd_map["rebuild"] = &rebuild;
|
cmd_map["rebuild"] = &rebuild;
|
||||||
cmd_map["shexec"] = &shexec;
|
cmd_map["shexec"] = &shexec;
|
||||||
cmd_map["poll-now"] = &poll_now;
|
cmd_map["poll-now"] = &poll_now;
|
||||||
|
cmd_map["test-comment"] = &test_comment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ ensure_dotlnav()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
install_from_git(const char* repo)
|
install_from_git(const std::string& repo)
|
||||||
{
|
{
|
||||||
static const std::regex repo_name_converter("[^\\w]");
|
static const std::regex repo_name_converter("[^\\w]");
|
||||||
|
|
||||||
@ -197,18 +197,18 @@ install_from_git(const char* repo)
|
|||||||
auto git_cmd = fork_res.unwrap();
|
auto git_cmd = fork_res.unwrap();
|
||||||
if (git_cmd.in_child()) {
|
if (git_cmd.in_child()) {
|
||||||
if (ghc::filesystem::is_directory(local_formats_path)) {
|
if (ghc::filesystem::is_directory(local_formats_path)) {
|
||||||
printf("Updating format repo: %s\n", repo);
|
fmt::print("Updating format repo: {}\n", repo);
|
||||||
log_perror(chdir(local_formats_path.c_str()));
|
log_perror(chdir(local_formats_path.c_str()));
|
||||||
execlp("git", "git", "pull", nullptr);
|
execlp("git", "git", "pull", nullptr);
|
||||||
} else if (ghc::filesystem::is_directory(local_configs_path)) {
|
} else if (ghc::filesystem::is_directory(local_configs_path)) {
|
||||||
printf("Updating config repo: %s\n", repo);
|
fmt::print("Updating config repo: {}\n", repo);
|
||||||
log_perror(chdir(local_configs_path.c_str()));
|
log_perror(chdir(local_configs_path.c_str()));
|
||||||
execlp("git", "git", "pull", nullptr);
|
execlp("git", "git", "pull", nullptr);
|
||||||
} else {
|
} else {
|
||||||
execlp("git",
|
execlp("git",
|
||||||
"git",
|
"git",
|
||||||
"clone",
|
"clone",
|
||||||
repo,
|
repo.c_str(),
|
||||||
local_staging_path.c_str(),
|
local_staging_path.c_str(),
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
@ -344,7 +344,8 @@ install_extra_formats()
|
|||||||
if ((fd = lnav::filesystem::openp(config_json, O_RDONLY)) == -1) {
|
if ((fd = lnav::filesystem::openp(config_json, O_RDONLY)) == -1) {
|
||||||
perror("Unable to open remote-config.json");
|
perror("Unable to open remote-config.json");
|
||||||
} else {
|
} else {
|
||||||
yajlpp_parse_context ypc_config(config_root.string(), &format_handlers);
|
yajlpp_parse_context ypc_config(
|
||||||
|
intern_string::lookup(config_root.string()), &format_handlers);
|
||||||
auto_mem<yajl_handle_t> jhandle(yajl_free);
|
auto_mem<yajl_handle_t> jhandle(yajl_free);
|
||||||
unsigned char buffer[4096];
|
unsigned char buffer[4096];
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
@ -639,6 +640,13 @@ static const struct json_path_container theme_styles_handlers = {
|
|||||||
return &root->lt_style_header[5];
|
return &root->lt_style_header[5];
|
||||||
})
|
})
|
||||||
.with_children(style_config_handlers),
|
.with_children(style_config_handlers),
|
||||||
|
yajlpp::property_handler("list-glyph")
|
||||||
|
.with_description("Styling for glyphs that prefix a list item")
|
||||||
|
.with_obj_provider<style_config, lnav_theme>(
|
||||||
|
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
|
||||||
|
return &root->lt_style_list_glyph;
|
||||||
|
})
|
||||||
|
.with_children(style_config_handlers),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct json_path_container theme_syntax_styles_handlers = {
|
static const struct json_path_container theme_syntax_styles_handlers = {
|
||||||
@ -1271,7 +1279,8 @@ load_config_from(_lnav_config& lconfig,
|
|||||||
const ghc::filesystem::path& path,
|
const ghc::filesystem::path& path,
|
||||||
std::vector<lnav::console::user_message>& errors)
|
std::vector<lnav::console::user_message>& errors)
|
||||||
{
|
{
|
||||||
yajlpp_parse_context ypc(path.string(), &lnav_config_handlers);
|
yajlpp_parse_context ypc(intern_string::lookup(path.string()),
|
||||||
|
&lnav_config_handlers);
|
||||||
struct userdata ud(errors);
|
struct userdata ud(errors);
|
||||||
auto_fd fd;
|
auto_fd fd;
|
||||||
|
|
||||||
@ -1326,7 +1335,8 @@ load_default_config(struct _lnav_config& config_obj,
|
|||||||
const bin_src_file& bsf,
|
const bin_src_file& bsf,
|
||||||
std::vector<lnav::console::user_message>& errors)
|
std::vector<lnav::console::user_message>& errors)
|
||||||
{
|
{
|
||||||
yajlpp_parse_context ypc_builtin(bsf.get_name(), &lnav_config_handlers);
|
yajlpp_parse_context ypc_builtin(intern_string::lookup(bsf.get_name()),
|
||||||
|
&lnav_config_handlers);
|
||||||
auto_mem<yajl_handle_t> handle(yajl_free);
|
auto_mem<yajl_handle_t> handle(yajl_free);
|
||||||
struct userdata ud(errors);
|
struct userdata ud(errors);
|
||||||
|
|
||||||
@ -1501,7 +1511,7 @@ reload_config(std::vector<lnav::console::user_message>& errors)
|
|||||||
.with_reason(errmsg)
|
.with_reason(errmsg)
|
||||||
.with_snippet(
|
.with_snippet(
|
||||||
lnav::console::snippet::from(
|
lnav::console::snippet::from(
|
||||||
loc_iter->second.sl_source.to_string(), "")
|
loc_iter->second.sl_source, "")
|
||||||
.with_line(loc_iter->second.sl_line_number)));
|
.with_line(loc_iter->second.sl_line_number)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ bool check_experimental(const char* feature_name);
|
|||||||
*/
|
*/
|
||||||
void ensure_dotlnav();
|
void ensure_dotlnav();
|
||||||
|
|
||||||
bool install_from_git(const char* repo);
|
bool install_from_git(const std::string& repo);
|
||||||
bool update_installs_from_git();
|
bool update_installs_from_git();
|
||||||
|
|
||||||
void install_extra_formats();
|
void install_extra_formats();
|
||||||
|
152
src/lnav_util.cc
152
src/lnav_util.cc
@ -37,9 +37,12 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "base/ansi_scrubber.hh"
|
#include "base/ansi_scrubber.hh"
|
||||||
|
#include "base/itertools.hh"
|
||||||
#include "base/result.h"
|
#include "base/result.h"
|
||||||
|
#include "bookmarks.hh"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
#include "log_format_fwd.hh"
|
||||||
#include "view_curses.hh"
|
#include "view_curses.hh"
|
||||||
#include "yajlpp/yajlpp.hh"
|
#include "yajlpp/yajlpp.hh"
|
||||||
#include "yajlpp/yajlpp_def.hh"
|
#include "yajlpp/yajlpp_def.hh"
|
||||||
@ -109,8 +112,44 @@ err_to_ok(const lnav::console::user_message msg)
|
|||||||
return Ok(msg.to_attr_line().get_string());
|
return Ok(msg.to_attr_line().get_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
short
|
||||||
|
pollfd_revents(const std::vector<struct pollfd>& pollfds, int fd)
|
||||||
|
{
|
||||||
|
return pollfds | lnav::itertools::find_if([fd](const auto& entry) {
|
||||||
|
return entry.fd == fd;
|
||||||
|
})
|
||||||
|
| lnav::itertools::map(&pollfd::revents)
|
||||||
|
| lnav::itertools::unwrap_or((short) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write_line_to(FILE* outfile, const attr_line_t& al)
|
||||||
|
{
|
||||||
|
const auto& al_attrs = al.get_attrs();
|
||||||
|
auto lr = find_string_attr_range(al_attrs, &SA_ORIGINAL_LINE);
|
||||||
|
|
||||||
|
if (!lr.is_valid() || lr.lr_start > 1) {
|
||||||
|
// If the line is prefixed with some extra information, include that
|
||||||
|
// in the output. For example, the log file name or time offset.
|
||||||
|
lnav::console::println(outfile, al);
|
||||||
|
} else {
|
||||||
|
lnav::console::println(outfile, al.subline(lr.lr_start, lr.length()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace lnav {
|
namespace lnav {
|
||||||
|
|
||||||
|
std::string
|
||||||
|
to_json(const std::string& str)
|
||||||
|
{
|
||||||
|
yajlpp_gen gen;
|
||||||
|
|
||||||
|
yajl_gen_config(gen, yajl_gen_beautify, false);
|
||||||
|
yajl_gen_string(gen, str);
|
||||||
|
|
||||||
|
return gen.to_string_fragment().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
to_json(yajlpp_gen& gen, const attr_line_t& al)
|
to_json(yajlpp_gen& gen, const attr_line_t& al)
|
||||||
{
|
{
|
||||||
@ -173,6 +212,9 @@ to_json(const lnav::console::user_message& um)
|
|||||||
|
|
||||||
root_map.gen("level");
|
root_map.gen("level");
|
||||||
switch (um.um_level) {
|
switch (um.um_level) {
|
||||||
|
case console::user_message::level::raw:
|
||||||
|
root_map.gen("raw");
|
||||||
|
break;
|
||||||
case console::user_message::level::ok:
|
case console::user_message::level::ok:
|
||||||
root_map.gen("ok");
|
root_map.gen("ok");
|
||||||
break;
|
break;
|
||||||
@ -199,11 +241,9 @@ to_json(const lnav::console::user_message& um)
|
|||||||
yajlpp_map snip_map(gen);
|
yajlpp_map snip_map(gen);
|
||||||
|
|
||||||
snip_map.gen("source");
|
snip_map.gen("source");
|
||||||
snip_map.gen(snip.s_source);
|
snip_map.gen(snip.s_location.sl_source);
|
||||||
snip_map.gen("line");
|
snip_map.gen("line");
|
||||||
snip_map.gen(snip.s_line);
|
snip_map.gen(snip.s_location.sl_line_number);
|
||||||
snip_map.gen("column");
|
|
||||||
snip_map.gen(snip.s_column);
|
|
||||||
snip_map.gen("content");
|
snip_map.gen("content");
|
||||||
to_json(gen, snip.s_content);
|
to_json(gen, snip.s_content);
|
||||||
}
|
}
|
||||||
@ -234,10 +274,10 @@ read_string_attr_type(yajlpp_parse_context* ypc,
|
|||||||
static int
|
static int
|
||||||
read_string_attr_int_value(yajlpp_parse_context* ypc, long long in)
|
read_string_attr_int_value(yajlpp_parse_context* ypc, long long in)
|
||||||
{
|
{
|
||||||
auto sa = (string_attr*) ypc->ypc_obj_stack.top();
|
auto* sa = (string_attr*) ypc->ypc_obj_stack.top();
|
||||||
|
|
||||||
if (sa->sa_type == &VC_ROLE) {
|
if (sa->sa_type == &VC_ROLE) {
|
||||||
sa->sa_value = (role_t) in;
|
sa->sa_value = static_cast<role_t>(in);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -251,39 +291,27 @@ static const struct json_path_container string_attr_handlers = {
|
|||||||
yajlpp::property_handler("value").add_cb(read_string_attr_int_value),
|
yajlpp::property_handler("value").add_cb(read_string_attr_int_value),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct json_path_container attr_line_handlers = {
|
static const typed_json_path_container<attr_line_t> attr_line_handlers = {
|
||||||
yajlpp::property_handler("str").for_field(&attr_line_t::al_string),
|
yajlpp::property_handler("str").for_field(&attr_line_t::al_string),
|
||||||
yajlpp::property_handler("attrs#")
|
yajlpp::property_handler("attrs#")
|
||||||
.with_obj_provider<string_attr, attr_line_t>(
|
.for_field(&attr_line_t::al_attrs)
|
||||||
[](const yajlpp_provider_context& ypc, attr_line_t* root) {
|
|
||||||
root->al_attrs.resize(ypc.ypc_index + 1);
|
|
||||||
|
|
||||||
return &root->al_attrs[ypc.ypc_index];
|
|
||||||
})
|
|
||||||
.with_children(string_attr_handlers),
|
.with_children(string_attr_handlers),
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
attr_line_t
|
Result<attr_line_t, std::vector<console::user_message>>
|
||||||
from_json(const std::string& json)
|
from_json(const std::string& json)
|
||||||
{
|
{
|
||||||
yajlpp_parse_context ypc("string", &attr_line_handlers);
|
static const auto STRING_SRC = intern_string::lookup("string");
|
||||||
auto_mem<yajl_handle_t> handle(yajl_free);
|
|
||||||
attr_line_t retval;
|
|
||||||
|
|
||||||
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
|
return attr_line_handlers.parser_for(STRING_SRC).of(json);
|
||||||
ypc.with_handle(handle);
|
|
||||||
ypc.with_obj(retval);
|
|
||||||
ypc.parse(json);
|
|
||||||
ypc.complete_parse();
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const json_path_container snippet_handlers = {
|
static const json_path_container snippet_handlers = {
|
||||||
yajlpp::property_handler("source").for_field(&console::snippet::s_source),
|
yajlpp::property_handler("source").for_field(&console::snippet::s_location,
|
||||||
yajlpp::property_handler("line").for_field(&console::snippet::s_line),
|
&source_location::sl_source),
|
||||||
yajlpp::property_handler("column").for_field(&console::snippet::s_column),
|
yajlpp::property_handler("line").for_field(
|
||||||
|
&console::snippet::s_location, &source_location::sl_line_number),
|
||||||
yajlpp::property_handler("content")
|
yajlpp::property_handler("content")
|
||||||
.with_obj_provider<attr_line_t, console::snippet>(
|
.with_obj_provider<attr_line_t, console::snippet>(
|
||||||
[](const yajlpp_provider_context& ypc, console::snippet* snip) {
|
[](const yajlpp_provider_context& ypc, console::snippet* snip) {
|
||||||
@ -293,6 +321,7 @@ static const json_path_container snippet_handlers = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const json_path_handler_base::enum_value_t LEVEL_ENUM[] = {
|
static const json_path_handler_base::enum_value_t LEVEL_ENUM[] = {
|
||||||
|
{"raw", lnav::console::user_message::level::raw},
|
||||||
{"ok", lnav::console::user_message::level::ok},
|
{"ok", lnav::console::user_message::level::ok},
|
||||||
{"info", lnav::console::user_message::level::info},
|
{"info", lnav::console::user_message::level::info},
|
||||||
{"warning", lnav::console::user_message::level::warning},
|
{"warning", lnav::console::user_message::level::warning},
|
||||||
@ -301,51 +330,44 @@ static const json_path_handler_base::enum_value_t LEVEL_ENUM[] = {
|
|||||||
json_path_handler_base::ENUM_TERMINATOR,
|
json_path_handler_base::ENUM_TERMINATOR,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct json_path_container user_message_handlers = {
|
static const typed_json_path_container<console::user_message>
|
||||||
yajlpp::property_handler("level")
|
user_message_handlers = {
|
||||||
.with_enum_values(LEVEL_ENUM)
|
yajlpp::property_handler("level")
|
||||||
.for_field(&console::user_message::um_level),
|
.with_enum_values(LEVEL_ENUM)
|
||||||
yajlpp::property_handler("message")
|
.for_field(&console::user_message::um_level),
|
||||||
.with_obj_provider<attr_line_t, console::user_message>(
|
yajlpp::property_handler("message")
|
||||||
[](const yajlpp_provider_context& ypc,
|
.with_obj_provider<attr_line_t, console::user_message>(
|
||||||
console::user_message* root) { return &root->um_message; })
|
[](const yajlpp_provider_context& ypc,
|
||||||
.with_children(attr_line_handlers),
|
console::user_message* root) { return &root->um_message; })
|
||||||
yajlpp::property_handler("reason")
|
.with_children(attr_line_handlers),
|
||||||
.with_obj_provider<attr_line_t, console::user_message>(
|
yajlpp::property_handler("reason")
|
||||||
[](const yajlpp_provider_context& ypc,
|
.with_obj_provider<attr_line_t, console::user_message>(
|
||||||
console::user_message* root) { return &root->um_reason; })
|
[](const yajlpp_provider_context& ypc,
|
||||||
.with_children(attr_line_handlers),
|
console::user_message* root) { return &root->um_reason; })
|
||||||
yajlpp::property_handler("snippets#")
|
.with_children(attr_line_handlers),
|
||||||
.with_obj_provider<console::snippet, console::user_message>(
|
yajlpp::property_handler("snippets#")
|
||||||
[](const yajlpp_provider_context& ypc,
|
.with_obj_provider<console::snippet, console::user_message>(
|
||||||
console::user_message* root) {
|
[](const yajlpp_provider_context& ypc,
|
||||||
root->um_snippets.resize(ypc.ypc_index + 1);
|
console::user_message* root) {
|
||||||
|
root->um_snippets.resize(ypc.ypc_index + 1);
|
||||||
|
|
||||||
return &root->um_snippets[ypc.ypc_index];
|
return &root->um_snippets[ypc.ypc_index];
|
||||||
})
|
})
|
||||||
.with_children(snippet_handlers),
|
.with_children(snippet_handlers),
|
||||||
yajlpp::property_handler("help")
|
yajlpp::property_handler("help")
|
||||||
.with_obj_provider<attr_line_t, console::user_message>(
|
.with_obj_provider<attr_line_t, console::user_message>(
|
||||||
[](const yajlpp_provider_context& ypc,
|
[](const yajlpp_provider_context& ypc,
|
||||||
console::user_message* root) { return &root->um_help; })
|
console::user_message* root) { return &root->um_help; })
|
||||||
.with_children(attr_line_handlers),
|
.with_children(attr_line_handlers),
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
lnav::console::user_message
|
Result<lnav::console::user_message, std::vector<console::user_message>>
|
||||||
from_json(const std::string& json)
|
from_json(const std::string& json)
|
||||||
{
|
{
|
||||||
yajlpp_parse_context ypc("string", &user_message_handlers);
|
static const auto STRING_SRC = intern_string::lookup("string");
|
||||||
auto_mem<yajl_handle_t> handle(yajl_free);
|
|
||||||
lnav::console::user_message retval;
|
|
||||||
|
|
||||||
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
|
return user_message_handlers.parser_for(STRING_SRC).of(json);
|
||||||
ypc.with_handle(handle);
|
|
||||||
ypc.with_obj(retval);
|
|
||||||
ypc.parse(json);
|
|
||||||
ypc.complete_parse();
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace lnav
|
} // namespace lnav
|
||||||
|
@ -132,26 +132,14 @@ to_string(const char* s)
|
|||||||
} // namespace std
|
} // namespace std
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
is_glob(const char* fn)
|
is_glob(const std::string& fn)
|
||||||
{
|
{
|
||||||
return (strchr(fn, '*') != nullptr || strchr(fn, '?') != nullptr
|
return (fn.find('*') != std::string::npos
|
||||||
|| strchr(fn, '[') != nullptr);
|
|| fn.find('?') != std::string::npos
|
||||||
|
|| fn.find('[') != std::string::npos);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline short
|
short pollfd_revents(const std::vector<struct pollfd>& pollfds, int fd);
|
||||||
pollfd_revents(const std::vector<struct pollfd>& pollfds, int fd)
|
|
||||||
{
|
|
||||||
auto iter
|
|
||||||
= std::find_if(pollfds.begin(), pollfds.end(), [fd](const auto& entry) {
|
|
||||||
return entry.fd == fd;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (iter == pollfds.end()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return iter->revents;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
pollfd_ready(const std::vector<struct pollfd>& pollfds,
|
pollfd_ready(const std::vector<struct pollfd>& pollfds,
|
||||||
@ -233,13 +221,17 @@ std::string err_prefix(std::string msg);
|
|||||||
Result<std::string, lnav::console::user_message> err_to_ok(
|
Result<std::string, lnav::console::user_message> err_to_ok(
|
||||||
lnav::console::user_message msg);
|
lnav::console::user_message msg);
|
||||||
|
|
||||||
|
void write_line_to(FILE* outfile, const attr_line_t& al);
|
||||||
|
|
||||||
namespace lnav {
|
namespace lnav {
|
||||||
|
|
||||||
|
std::string to_json(const std::string& str);
|
||||||
std::string to_json(const lnav::console::user_message& um);
|
std::string to_json(const lnav::console::user_message& um);
|
||||||
std::string to_json(const attr_line_t& al);
|
std::string to_json(const attr_line_t& al);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T from_json(const std::string& str);
|
Result<T, std::vector<lnav::console::user_message>> from_json(
|
||||||
|
const std::string& json);
|
||||||
|
|
||||||
} // namespace lnav
|
} // namespace lnav
|
||||||
|
|
||||||
|
@ -46,9 +46,9 @@ public:
|
|||||||
std::function<void(const std::string&, std::shared_ptr<piper_proc>)>
|
std::function<void(const std::string&, std::shared_ptr<piper_proc>)>
|
||||||
piper_cb)
|
piper_cb)
|
||||||
: ad_log_helper(lss), ad_child_cb(std::move(child_cb)),
|
: ad_log_helper(lss), ad_child_cb(std::move(child_cb)),
|
||||||
ad_piper_cb(std::move(piper_cb)){
|
ad_piper_cb(std::move(piper_cb))
|
||||||
|
{
|
||||||
};
|
}
|
||||||
|
|
||||||
bool text_handle_mouse(textview_curses& tc, mouse_event& me) override;
|
bool text_handle_mouse(textview_curses& tc, mouse_event& me) override;
|
||||||
|
|
||||||
|
@ -48,10 +48,7 @@
|
|||||||
|
|
||||||
class log_data_helper {
|
class log_data_helper {
|
||||||
public:
|
public:
|
||||||
explicit log_data_helper(logfile_sub_source& lss)
|
explicit log_data_helper(logfile_sub_source& lss) : ldh_log_source(lss) {}
|
||||||
: ldh_log_source(lss){
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
@ -70,7 +67,7 @@ public:
|
|||||||
return std::count(this->ldh_msg.get_data(),
|
return std::count(this->ldh_msg.get_data(),
|
||||||
this->ldh_msg.get_data() + lv.lv_origin.lr_start,
|
this->ldh_msg.get_data() + lv.lv_origin.lr_start,
|
||||||
'\n');
|
'\n');
|
||||||
};
|
}
|
||||||
|
|
||||||
std::string format_json_getter(const intern_string_t field, int index);
|
std::string format_json_getter(const intern_string_t field, int index);
|
||||||
|
|
||||||
|
@ -53,13 +53,13 @@ public:
|
|||||||
void get_columns(std::vector<vtab_column>& cols) const override
|
void get_columns(std::vector<vtab_column>& cols) const override
|
||||||
{
|
{
|
||||||
cols = this->ldt_cols;
|
cols = this->ldt_cols;
|
||||||
};
|
}
|
||||||
|
|
||||||
void get_foreign_keys(std::vector<std::string>& keys_inout) const override
|
void get_foreign_keys(std::vector<std::string>& keys_inout) const override
|
||||||
{
|
{
|
||||||
log_vtab_impl::get_foreign_keys(keys_inout);
|
log_vtab_impl::get_foreign_keys(keys_inout);
|
||||||
keys_inout.emplace_back("log_msg_instance");
|
keys_inout.emplace_back("log_msg_instance");
|
||||||
};
|
}
|
||||||
|
|
||||||
bool next(log_cursor& lc, logfile_sub_source& lss) override;
|
bool next(log_cursor& lc, logfile_sub_source& lss) override;
|
||||||
|
|
||||||
|
@ -46,6 +46,8 @@
|
|||||||
#include "yajlpp/yajlpp.hh"
|
#include "yajlpp/yajlpp.hh"
|
||||||
#include "yajlpp/yajlpp_def.hh"
|
#include "yajlpp/yajlpp_def.hh"
|
||||||
|
|
||||||
|
using namespace lnav::roles::literals;
|
||||||
|
|
||||||
static auto intern_lifetime = intern_string::get_table_lifetime();
|
static auto intern_lifetime = intern_string::get_table_lifetime();
|
||||||
|
|
||||||
string_attr_type<void> logline::L_PREFIX("prefix");
|
string_attr_type<void> logline::L_PREFIX("prefix");
|
||||||
@ -1165,9 +1167,7 @@ external_log_format::rewrite(exec_context& ec,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto _sg = ec.enter_source(
|
auto _sg = ec.enter_source(
|
||||||
this->elf_name.to_string() + ":" + vd_iter->first.to_string(),
|
vd_iter->second->vd_rewrite_src_name, 1, vd.vd_rewriter);
|
||||||
1,
|
|
||||||
vd.vd_rewriter);
|
|
||||||
auto field_value
|
auto field_value
|
||||||
= execute_any(ec, vd.vd_rewriter).orElse(err_to_ok).unwrap();
|
= execute_any(ec, vd.vd_rewriter).orElse(err_to_ok).unwrap();
|
||||||
struct line_range adj_origin
|
struct line_range adj_origin
|
||||||
@ -1391,12 +1391,12 @@ external_log_format::get_subline(const logline& ll,
|
|||||||
size_t begin_size = this->jlf_cached_line.size();
|
size_t begin_size = this->jlf_cached_line.size();
|
||||||
|
|
||||||
switch (jfe.jfe_type) {
|
switch (jfe.jfe_type) {
|
||||||
case JLF_CONSTANT:
|
case json_log_field::CONSTANT:
|
||||||
this->json_append_to_cache(
|
this->json_append_to_cache(
|
||||||
jfe.jfe_default_value.c_str(),
|
jfe.jfe_default_value.c_str(),
|
||||||
jfe.jfe_default_value.size());
|
jfe.jfe_default_value.size());
|
||||||
break;
|
break;
|
||||||
case JLF_VARIABLE:
|
case json_log_field::VARIABLE:
|
||||||
lv_iter = find_if(
|
lv_iter = find_if(
|
||||||
this->jlf_line_values.begin(),
|
this->jlf_line_values.begin(),
|
||||||
this->jlf_line_values.end(),
|
this->jlf_line_values.end(),
|
||||||
@ -1792,8 +1792,8 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
.with_snippets(this->get_snippets()));
|
.with_snippets(this->get_snippets()));
|
||||||
}
|
}
|
||||||
if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
|
if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
|
||||||
this->jlf_parse_context = std::make_shared<yajlpp_parse_context>(
|
this->jlf_parse_context
|
||||||
this->elf_name.to_string());
|
= std::make_shared<yajlpp_parse_context>(this->elf_name);
|
||||||
this->jlf_yajl_handle.reset(
|
this->jlf_yajl_handle.reset(
|
||||||
yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
|
yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
|
||||||
nullptr,
|
nullptr,
|
||||||
@ -1820,6 +1820,7 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
for (auto& vd : this->elf_value_def_order) {
|
for (auto& vd : this->elf_value_def_order) {
|
||||||
std::vector<std::string>::iterator act_iter;
|
std::vector<std::string>::iterator act_iter;
|
||||||
|
|
||||||
|
vd->vd_meta.lvm_format = this;
|
||||||
if (!vd->vd_internal && vd->vd_meta.lvm_column == -1) {
|
if (!vd->vd_internal && vd->vd_meta.lvm_column == -1) {
|
||||||
vd->vd_meta.lvm_column = this->elf_column_count++;
|
vd->vd_meta.lvm_column = this->elf_column_count++;
|
||||||
}
|
}
|
||||||
@ -1841,6 +1842,8 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vd->set_rewrite_src_name();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
|
if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
|
||||||
@ -1857,8 +1860,10 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto& elf_sample : this->elf_samples) {
|
for (auto& elf_sample : this->elf_samples) {
|
||||||
|
auto sample_lines
|
||||||
|
= string_fragment(elf_sample.s_line.pp_value).split_lines();
|
||||||
pcre_context_static<128> pc;
|
pcre_context_static<128> pc;
|
||||||
pcre_input pi(elf_sample.s_line.pp_value);
|
pcre_input pi(sample_lines[0]);
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
for (auto pat_iter = this->elf_pattern_order.begin();
|
for (auto pat_iter = this->elf_pattern_order.begin();
|
||||||
@ -1930,8 +1935,8 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
|| dts.scan(ts, ts_len, custom_formats, &tm, tv) == nullptr) {
|
|| dts.scan(ts, ts_len, custom_formats, &tm, tv) == nullptr) {
|
||||||
attr_line_t notes;
|
attr_line_t notes;
|
||||||
|
|
||||||
notes.append("the following formats were tried:");
|
|
||||||
if (custom_formats == nullptr) {
|
if (custom_formats == nullptr) {
|
||||||
|
notes.append("the following built-in formats were tried:");
|
||||||
for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr;
|
for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr;
|
||||||
lpc++) {
|
lpc++) {
|
||||||
off_t off = 0;
|
off_t off = 0;
|
||||||
@ -1941,13 +1946,13 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
.append(ts, (size_t) ts_len)
|
.append(ts, (size_t) ts_len)
|
||||||
.append("\n")
|
.append("\n")
|
||||||
.append(2 + off, ' ')
|
.append(2 + off, ' ')
|
||||||
.append(lnav::roles::comment("^ "))
|
.append("^ "_comment)
|
||||||
.append_quoted(
|
.append_quoted(
|
||||||
lnav::roles::symbol(PTIMEC_FORMATS[lpc].pf_fmt))
|
lnav::roles::symbol(PTIMEC_FORMATS[lpc].pf_fmt))
|
||||||
.append(
|
.append(" matched up to here"_comment);
|
||||||
lnav::roles::comment(" matched up to here"));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
notes.append("the following custom formats were tried:");
|
||||||
for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++) {
|
for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++) {
|
||||||
off_t off = 0;
|
off_t off = 0;
|
||||||
|
|
||||||
@ -1956,22 +1961,26 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
.append(ts, (size_t) ts_len)
|
.append(ts, (size_t) ts_len)
|
||||||
.append("\n")
|
.append("\n")
|
||||||
.append(2 + off, ' ')
|
.append(2 + off, ' ')
|
||||||
.append(lnav::roles::comment("^ "))
|
.append("^ "_comment)
|
||||||
.append_quoted(
|
.append_quoted(
|
||||||
lnav::roles::symbol(custom_formats[lpc]))
|
lnav::roles::symbol(custom_formats[lpc]))
|
||||||
.append(
|
.append(" matched up to here"_comment);
|
||||||
lnav::roles::comment(" matched up to here"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errors.emplace_back(
|
errors.emplace_back(
|
||||||
lnav::console::user_message::error(
|
lnav::console::user_message::error(
|
||||||
attr_line_t("invalid sample log message ")
|
attr_line_t("invalid sample log message: ")
|
||||||
.append_quoted(elf_sample.s_line.pp_value))
|
.append(lnav::to_json(elf_sample.s_line.pp_value)))
|
||||||
.with_reason(attr_line_t("unrecognized timestamp -- ")
|
.with_reason(attr_line_t("unrecognized timestamp -- ")
|
||||||
.append(ts, (size_t) ts_len))
|
.append(ts, (size_t) ts_len))
|
||||||
.with_snippet(elf_sample.s_line.to_snippet())
|
.with_snippet(elf_sample.s_line.to_snippet())
|
||||||
.with_note(notes));
|
.with_note(notes)
|
||||||
|
.with_help(attr_line_t("If the timestamp format is not "
|
||||||
|
"supported by default, you can "
|
||||||
|
"add a custom format with the ")
|
||||||
|
.append_quoted("timestamp-format"_symbol)
|
||||||
|
.append(" property")));
|
||||||
}
|
}
|
||||||
|
|
||||||
log_level_t level = this->convert_level(pi, level_cap);
|
log_level_t level = this->convert_level(pi, level_cap);
|
||||||
@ -1980,8 +1989,8 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
&& elf_sample.s_level != level) {
|
&& elf_sample.s_level != level) {
|
||||||
errors.emplace_back(
|
errors.emplace_back(
|
||||||
lnav::console::user_message::error(
|
lnav::console::user_message::error(
|
||||||
attr_line_t("invalid sample log message ")
|
attr_line_t("invalid sample log message: ")
|
||||||
.append_quoted(elf_sample.s_line.pp_value))
|
.append(lnav::to_json(elf_sample.s_line.pp_value)))
|
||||||
.with_reason(attr_line_t()
|
.with_reason(attr_line_t()
|
||||||
.append_quoted(lnav::roles::symbol(
|
.append_quoted(lnav::roles::symbol(
|
||||||
level_names[level]))
|
level_names[level]))
|
||||||
@ -1991,14 +2000,35 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
level_names[elf_sample.s_level])))
|
level_names[elf_sample.s_level])))
|
||||||
.with_snippet(elf_sample.s_line.to_snippet()));
|
.with_snippet(elf_sample.s_line.to_snippet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
pcre_context_static<128> pc_full;
|
||||||
|
pcre_input pi_full(elf_sample.s_line.pp_value);
|
||||||
|
|
||||||
|
if (!pat.p_pcre->match(pc_full, pi_full)
|
||||||
|
|| pc_full.all()->length()
|
||||||
|
!= elf_sample.s_line.pp_value.length())
|
||||||
|
{
|
||||||
|
errors.emplace_back(
|
||||||
|
lnav::console::user_message::error(
|
||||||
|
attr_line_t("invalid pattern: ")
|
||||||
|
.append_quoted(lnav::roles::symbol(pat.p_name)))
|
||||||
|
.with_reason("pattern does not match entire "
|
||||||
|
"multiline message")
|
||||||
|
.with_snippet(elf_sample.s_line.to_snippet())
|
||||||
|
.with_help(attr_line_t("using ")
|
||||||
|
.append_quoted(".*")
|
||||||
|
.append(" when capturing the body "
|
||||||
|
"will match new-lines")));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found && !this->elf_pattern_order.empty()) {
|
if (!found && !this->elf_pattern_order.empty()) {
|
||||||
attr_line_t notes(
|
std::vector<std::pair<ssize_t, std::string>> partial_indexes;
|
||||||
"the following shows how each pattern matched this sample:\n");
|
attr_line_t notes;
|
||||||
size_t max_name_width = 0;
|
size_t max_name_width = 0;
|
||||||
|
|
||||||
notes.append(" ").append_quoted(elf_sample.s_line.pp_value);
|
|
||||||
for (const auto& pat_iter : this->elf_pattern_order) {
|
for (const auto& pat_iter : this->elf_pattern_order) {
|
||||||
pattern& pat = *pat_iter;
|
pattern& pat = *pat_iter;
|
||||||
|
|
||||||
@ -2006,21 +2036,37 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t partial_len = pat.p_pcre->match_partial(pi);
|
partial_indexes.emplace_back(pat.p_pcre->match_partial(pi),
|
||||||
|
pat.p_name);
|
||||||
notes.append("\n ")
|
|
||||||
.append(partial_len, ' ')
|
|
||||||
.append(lnav::roles::comment("^ "))
|
|
||||||
.append(lnav::roles::symbol(pat.p_name))
|
|
||||||
.append(lnav::roles::comment(" matched up to here"));
|
|
||||||
|
|
||||||
max_name_width = std::max(max_name_width, pat.p_name.size());
|
max_name_width = std::max(max_name_width, pat.p_name.size());
|
||||||
}
|
}
|
||||||
|
for (const auto& line_frag : sample_lines) {
|
||||||
|
auto src_line = line_frag.to_string();
|
||||||
|
if (!endswith(src_line, "\n")) {
|
||||||
|
src_line.append("\n");
|
||||||
|
}
|
||||||
|
notes.append(" ").append(src_line);
|
||||||
|
for (auto& part_pair : partial_indexes) {
|
||||||
|
if (part_pair.first >= 0
|
||||||
|
&& part_pair.first < line_frag.length()) {
|
||||||
|
notes.append(" ")
|
||||||
|
.append(part_pair.first, ' ')
|
||||||
|
.append("^ "_comment)
|
||||||
|
.append(lnav::roles::symbol(part_pair.second))
|
||||||
|
.append(" matched up to here"_comment)
|
||||||
|
.append("\n");
|
||||||
|
}
|
||||||
|
part_pair.first -= line_frag.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notes.add_header(
|
||||||
|
"the following shows how each pattern matched this sample:\n");
|
||||||
|
|
||||||
attr_line_t help;
|
attr_line_t regex_note;
|
||||||
for (const auto& pat_iter : this->elf_pattern_order) {
|
for (const auto& pat_iter : this->elf_pattern_order) {
|
||||||
if (!pat_iter->p_pcre) {
|
if (!pat_iter->p_pcre) {
|
||||||
help.append(
|
regex_note
|
||||||
|
.append(
|
||||||
lnav::roles::symbol(fmt::format(FMT_STRING("{:{}}"),
|
lnav::roles::symbol(fmt::format(FMT_STRING("{:{}}"),
|
||||||
pat_iter->p_name,
|
pat_iter->p_name,
|
||||||
max_name_width)))
|
max_name_width)))
|
||||||
@ -2028,22 +2074,22 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
help
|
regex_note
|
||||||
.append(lnav::roles::symbol(fmt::format(
|
.append(lnav::roles::symbol(fmt::format(
|
||||||
FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
|
FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
|
||||||
.append(" = ")
|
.append(" = ")
|
||||||
.append(pat_iter->p_pcre->get_pattern())
|
.append_quoted(pat_iter->p_pcre->get_pattern())
|
||||||
.append("\n");
|
.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
errors.emplace_back(
|
errors.emplace_back(
|
||||||
lnav::console::user_message::error(
|
lnav::console::user_message::error(
|
||||||
attr_line_t("invalid sample log message ")
|
attr_line_t("invalid sample log message: ")
|
||||||
.append_quoted(elf_sample.s_line.pp_value))
|
.append(lnav::to_json(elf_sample.s_line.pp_value)))
|
||||||
.with_reason("sample does not match any patterns")
|
.with_reason("sample does not match any patterns")
|
||||||
.with_snippet(elf_sample.s_line.to_snippet())
|
.with_snippet(elf_sample.s_line.to_snippet())
|
||||||
.with_note(notes)
|
.with_note(notes.rtrim())
|
||||||
.with_help(help));
|
.with_note(regex_note));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2097,7 +2143,7 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (jfe.jfe_type) {
|
switch (jfe.jfe_type) {
|
||||||
case JLF_VARIABLE: {
|
case json_log_field::VARIABLE: {
|
||||||
auto vd_iter
|
auto vd_iter
|
||||||
= this->elf_value_defs.find(jfe.jfe_value.pp_value);
|
= this->elf_value_defs.find(jfe.jfe_value.pp_value);
|
||||||
if (jfe.jfe_value.pp_value == ts) {
|
if (jfe.jfe_value.pp_value == ts) {
|
||||||
@ -2124,7 +2170,7 @@ external_log_format::build(std::vector<lnav::console::user_message>& errors)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case JLF_CONSTANT:
|
case json_log_field::CONSTANT:
|
||||||
this->jlf_line_format_init_count
|
this->jlf_line_format_init_count
|
||||||
+= std::count(jfe.jfe_default_value.begin(),
|
+= std::count(jfe.jfe_default_value.begin(),
|
||||||
jfe.jfe_default_value.end(),
|
jfe.jfe_default_value.end(),
|
||||||
@ -2404,8 +2450,8 @@ external_log_format::specialized(int fmt_lock)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
|
if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
|
||||||
this->jlf_parse_context = std::make_shared<yajlpp_parse_context>(
|
this->jlf_parse_context
|
||||||
this->elf_name.to_string());
|
= std::make_shared<yajlpp_parse_context>(this->elf_name);
|
||||||
this->jlf_yajl_handle.reset(
|
this->jlf_yajl_handle.reset(
|
||||||
yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
|
yajl_alloc(&this->jlf_parse_context->ypc_callbacks,
|
||||||
nullptr,
|
nullptr,
|
||||||
@ -2444,6 +2490,103 @@ external_log_format::match_mime_type(const file_format_t ff) const
|
|||||||
return this->elf_mime_types.count(ff) == 1;
|
return this->elf_mime_types.count(ff) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long
|
||||||
|
external_log_format::value_line_count(const intern_string_t ist,
|
||||||
|
bool top_level,
|
||||||
|
const unsigned char* str,
|
||||||
|
ssize_t len) const
|
||||||
|
{
|
||||||
|
const auto iter = this->elf_value_defs.find(ist);
|
||||||
|
long line_count
|
||||||
|
= (str != nullptr) ? std::count(&str[0], &str[len], '\n') + 1 : 1;
|
||||||
|
|
||||||
|
if (iter == this->elf_value_defs.end()) {
|
||||||
|
return (this->jlf_hide_extra || !top_level) ? 0 : line_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iter->second->vd_meta.lvm_hidden) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::find_if(this->jlf_line_format.begin(),
|
||||||
|
this->jlf_line_format.end(),
|
||||||
|
json_field_cmp(json_log_field::VARIABLE, ist))
|
||||||
|
!= this->jlf_line_format.end())
|
||||||
|
{
|
||||||
|
return line_count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return line_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_level_t
|
||||||
|
external_log_format::convert_level(
|
||||||
|
const pcre_input& pi, const pcre_context::capture_t* level_cap) const
|
||||||
|
{
|
||||||
|
log_level_t retval = LEVEL_INFO;
|
||||||
|
|
||||||
|
if (level_cap != nullptr && level_cap->is_valid()) {
|
||||||
|
pcre_context_static<128> pc_level;
|
||||||
|
pcre_input pi_level(
|
||||||
|
pi.get_substr_start(level_cap), 0, level_cap->length());
|
||||||
|
|
||||||
|
if (this->elf_level_patterns.empty()) {
|
||||||
|
retval = string2level(pi_level.get_string(), level_cap->length());
|
||||||
|
} else {
|
||||||
|
for (const auto& elf_level_pattern : this->elf_level_patterns) {
|
||||||
|
if (elf_level_pattern.second.lp_pcre->match(pc_level, pi_level))
|
||||||
|
{
|
||||||
|
retval = elf_level_pattern.first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
logline_value_meta
|
||||||
|
external_log_format::get_value_meta(intern_string_t field_name,
|
||||||
|
value_kind_t kind)
|
||||||
|
{
|
||||||
|
auto iter = this->elf_value_defs.find(field_name);
|
||||||
|
|
||||||
|
if (iter == this->elf_value_defs.end()) {
|
||||||
|
auto retval = logline_value_meta(field_name, kind, -1, this);
|
||||||
|
|
||||||
|
retval.lvm_hidden = this->jlf_hide_extra;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lvm = iter->second->vd_meta;
|
||||||
|
|
||||||
|
lvm.lvm_kind = kind;
|
||||||
|
return lvm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
external_log_format::json_append(
|
||||||
|
const external_log_format::json_format_element& jfe,
|
||||||
|
const char* value,
|
||||||
|
ssize_t len)
|
||||||
|
{
|
||||||
|
if (len == -1) {
|
||||||
|
len = strlen(value);
|
||||||
|
}
|
||||||
|
if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
|
||||||
|
if (len < jfe.jfe_min_width) {
|
||||||
|
this->json_append_to_cache(jfe.jfe_min_width - len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->json_append_to_cache(value, len);
|
||||||
|
if (jfe.jfe_align == json_format_element::align_t::LEFT) {
|
||||||
|
if (len < jfe.jfe_min_width) {
|
||||||
|
this->json_append_to_cache(jfe.jfe_min_width - len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
log_format::pattern_index_for_line(uint64_t line_number) const
|
log_format::pattern_index_for_line(uint64_t line_number) const
|
||||||
{
|
{
|
||||||
@ -2468,11 +2611,58 @@ log_format::get_pattern_name(uint64_t line_number) const
|
|||||||
return fmt::format(FMT_STRING("builtin ({})"), pat_index);
|
return fmt::format(FMT_STRING("builtin ({})"), pat_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<log_format>
|
||||||
|
log_format::find_root_format(const char* name)
|
||||||
|
{
|
||||||
|
auto& fmts = get_root_formats();
|
||||||
|
for (auto& lf : fmts) {
|
||||||
|
if (lf->get_name() == name) {
|
||||||
|
return lf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
log_format::pattern_for_lines::pattern_for_lines(uint32_t pfl_line,
|
log_format::pattern_for_lines::pattern_for_lines(uint32_t pfl_line,
|
||||||
uint32_t pfl_pat_index)
|
uint32_t pfl_pat_index)
|
||||||
: pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
|
: pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
logline_value_stats::merge(const logline_value_stats& other)
|
||||||
|
{
|
||||||
|
if (other.lvs_count == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
require(other.lvs_min_value <= other.lvs_max_value);
|
||||||
|
|
||||||
|
if (other.lvs_min_value < this->lvs_min_value) {
|
||||||
|
this->lvs_min_value = other.lvs_min_value;
|
||||||
|
}
|
||||||
|
if (other.lvs_max_value > this->lvs_max_value) {
|
||||||
|
this->lvs_max_value = other.lvs_max_value;
|
||||||
|
}
|
||||||
|
this->lvs_count += other.lvs_count;
|
||||||
|
this->lvs_total += other.lvs_total;
|
||||||
|
|
||||||
|
ensure(this->lvs_count >= 0);
|
||||||
|
ensure(this->lvs_min_value <= this->lvs_max_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
logline_value_stats::add_value(double value)
|
||||||
|
{
|
||||||
|
if (value < this->lvs_min_value) {
|
||||||
|
this->lvs_min_value = value;
|
||||||
|
}
|
||||||
|
if (value > this->lvs_max_value) {
|
||||||
|
this->lvs_max_value = value;
|
||||||
|
}
|
||||||
|
this->lvs_count += 1;
|
||||||
|
this->lvs_total += value;
|
||||||
|
}
|
||||||
|
|
||||||
/* XXX */
|
/* XXX */
|
||||||
#include "log_format_impls.cc"
|
#include "log_format_impls.cc"
|
||||||
|
@ -51,7 +51,6 @@
|
|||||||
#include "base/date_time_scanner.hh"
|
#include "base/date_time_scanner.hh"
|
||||||
#include "base/intern_string.hh"
|
#include "base/intern_string.hh"
|
||||||
#include "base/lnav_log.hh"
|
#include "base/lnav_log.hh"
|
||||||
#include "byte_array.hh"
|
|
||||||
#include "file_format.hh"
|
#include "file_format.hh"
|
||||||
#include "highlighter.hh"
|
#include "highlighter.hh"
|
||||||
#include "line_buffer.hh"
|
#include "line_buffer.hh"
|
||||||
@ -73,8 +72,6 @@ enum class scale_op_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct scaling_factor {
|
struct scaling_factor {
|
||||||
scaling_factor() : sf_op(scale_op_t::SO_IDENTITY), sf_value(1){};
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void scale(T& val) const
|
void scale(T& val) const
|
||||||
{
|
{
|
||||||
@ -90,8 +87,8 @@ struct scaling_factor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scale_op_t sf_op;
|
scale_op_t sf_op{scale_op_t::SO_IDENTITY};
|
||||||
double sf_value;
|
double sf_value{1};
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class value_kind_t : int {
|
enum class value_kind_t : int {
|
||||||
@ -227,10 +224,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct logline_value_stats {
|
struct logline_value_stats {
|
||||||
logline_value_stats()
|
logline_value_stats() { this->clear(); }
|
||||||
{
|
|
||||||
this->clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
@ -238,40 +232,11 @@ struct logline_value_stats {
|
|||||||
this->lvs_total = 0;
|
this->lvs_total = 0;
|
||||||
this->lvs_min_value = std::numeric_limits<double>::max();
|
this->lvs_min_value = std::numeric_limits<double>::max();
|
||||||
this->lvs_max_value = -std::numeric_limits<double>::max();
|
this->lvs_max_value = -std::numeric_limits<double>::max();
|
||||||
};
|
}
|
||||||
|
|
||||||
void merge(const logline_value_stats& other)
|
void merge(const logline_value_stats& other);
|
||||||
{
|
|
||||||
if (other.lvs_count == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
require(other.lvs_min_value <= other.lvs_max_value);
|
void add_value(double value);
|
||||||
|
|
||||||
if (other.lvs_min_value < this->lvs_min_value) {
|
|
||||||
this->lvs_min_value = other.lvs_min_value;
|
|
||||||
}
|
|
||||||
if (other.lvs_max_value > this->lvs_max_value) {
|
|
||||||
this->lvs_max_value = other.lvs_max_value;
|
|
||||||
}
|
|
||||||
this->lvs_count += other.lvs_count;
|
|
||||||
this->lvs_total += other.lvs_total;
|
|
||||||
|
|
||||||
ensure(this->lvs_count >= 0);
|
|
||||||
ensure(this->lvs_min_value <= this->lvs_max_value);
|
|
||||||
};
|
|
||||||
|
|
||||||
void add_value(double value)
|
|
||||||
{
|
|
||||||
if (value < this->lvs_min_value) {
|
|
||||||
this->lvs_min_value = value;
|
|
||||||
}
|
|
||||||
if (value > this->lvs_max_value) {
|
|
||||||
this->lvs_max_value = value;
|
|
||||||
}
|
|
||||||
this->lvs_count += 1;
|
|
||||||
this->lvs_total += value;
|
|
||||||
};
|
|
||||||
|
|
||||||
int64_t lvs_count;
|
int64_t lvs_count;
|
||||||
double lvs_total;
|
double lvs_total;
|
||||||
@ -282,9 +247,9 @@ struct logline_value_stats {
|
|||||||
struct logline_value_cmp {
|
struct logline_value_cmp {
|
||||||
explicit logline_value_cmp(const intern_string_t* name = nullptr,
|
explicit logline_value_cmp(const intern_string_t* name = nullptr,
|
||||||
int col = -1)
|
int col = -1)
|
||||||
: lvc_name(name), lvc_column(col){
|
: lvc_name(name), lvc_column(col)
|
||||||
|
{
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator()(const logline_value& lv) const
|
bool operator()(const logline_value& lv) const
|
||||||
{
|
{
|
||||||
@ -298,7 +263,7 @@ struct logline_value_cmp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
const intern_string_t* lvc_name;
|
const intern_string_t* lvc_name;
|
||||||
int lvc_column;
|
int lvc_column;
|
||||||
@ -316,29 +281,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
static std::vector<std::shared_ptr<log_format>>& get_root_formats();
|
static std::vector<std::shared_ptr<log_format>>& get_root_formats();
|
||||||
|
|
||||||
static std::shared_ptr<log_format> find_root_format(const char* name)
|
static std::shared_ptr<log_format> find_root_format(const char* name);
|
||||||
{
|
|
||||||
auto& fmts = get_root_formats();
|
|
||||||
for (auto& lf : fmts) {
|
|
||||||
if (lf->get_name() == name) {
|
|
||||||
return lf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct action_def {
|
struct action_def {
|
||||||
std::string ad_name;
|
std::string ad_name;
|
||||||
std::string ad_label;
|
std::string ad_label;
|
||||||
std::vector<std::string> ad_cmdline;
|
std::vector<std::string> ad_cmdline;
|
||||||
bool ad_capture_output;
|
bool ad_capture_output{false};
|
||||||
|
|
||||||
action_def() : ad_capture_output(false){};
|
|
||||||
|
|
||||||
bool operator<(const action_def& rhs) const
|
bool operator<(const action_def& rhs) const
|
||||||
{
|
{
|
||||||
return this->ad_name < rhs.ad_name;
|
return this->ad_name < rhs.ad_name;
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~log_format() = default;
|
virtual ~log_format() = default;
|
||||||
@ -347,7 +301,7 @@ public:
|
|||||||
{
|
{
|
||||||
this->lf_pattern_locks.clear();
|
this->lf_pattern_locks.clear();
|
||||||
this->lf_date_time.clear();
|
this->lf_date_time.clear();
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of this log format.
|
* Get the name of this log format.
|
||||||
@ -356,10 +310,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual const intern_string_t get_name() const = 0;
|
virtual const intern_string_t get_name() const = 0;
|
||||||
|
|
||||||
virtual bool match_name(const std::string& filename)
|
virtual bool match_name(const std::string& filename) { return true; }
|
||||||
{
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual bool match_mime_type(const file_format_t ff) const
|
virtual bool match_mime_type(const file_format_t ff) const
|
||||||
{
|
{
|
||||||
@ -367,7 +318,7 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
enum scan_result_t {
|
enum scan_result_t {
|
||||||
SCAN_MATCH,
|
SCAN_MATCH,
|
||||||
@ -393,7 +344,7 @@ public:
|
|||||||
virtual bool scan_for_partial(shared_buffer_ref& sbr, size_t& len_out) const
|
virtual bool scan_for_partial(shared_buffer_ref& sbr, size_t& len_out) const
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove redundant data from the log line string.
|
* Remove redundant data from the log line string.
|
||||||
@ -409,7 +360,9 @@ public:
|
|||||||
shared_buffer_ref& sbr,
|
shared_buffer_ref& sbr,
|
||||||
string_attrs_t& sa,
|
string_attrs_t& sa,
|
||||||
std::vector<logline_value>& values,
|
std::vector<logline_value>& values,
|
||||||
bool annotate_module = true) const {};
|
bool annotate_module = true) const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual void rewrite(exec_context& ec,
|
virtual void rewrite(exec_context& ec,
|
||||||
shared_buffer_ref& line,
|
shared_buffer_ref& line,
|
||||||
@ -417,20 +370,20 @@ public:
|
|||||||
std::string& value_out)
|
std::string& value_out)
|
||||||
{
|
{
|
||||||
value_out.assign(line.get_data(), line.length());
|
value_out.assign(line.get_data(), line.length());
|
||||||
};
|
}
|
||||||
|
|
||||||
virtual const logline_value_stats* stats_for_value(
|
virtual const logline_value_stats* stats_for_value(
|
||||||
const intern_string_t& name) const
|
const intern_string_t& name) const
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
}
|
||||||
|
|
||||||
virtual std::shared_ptr<log_format> specialized(int fmt_lock = -1) = 0;
|
virtual std::shared_ptr<log_format> specialized(int fmt_lock = -1) = 0;
|
||||||
|
|
||||||
virtual std::shared_ptr<log_vtab_impl> get_vtab_impl() const
|
virtual std::shared_ptr<log_vtab_impl> get_vtab_impl() const
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
}
|
||||||
|
|
||||||
virtual void get_subline(const logline& ll,
|
virtual void get_subline(const logline& ll,
|
||||||
shared_buffer_ref& sbr,
|
shared_buffer_ref& sbr,
|
||||||
@ -440,7 +393,7 @@ public:
|
|||||||
const logline_value& lv) const
|
const logline_value& lv) const
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
}
|
||||||
|
|
||||||
virtual std::set<std::string> get_source_path() const
|
virtual std::set<std::string> get_source_path() const
|
||||||
{
|
{
|
||||||
@ -449,12 +402,12 @@ public:
|
|||||||
retval.insert("default");
|
retval.insert("default");
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
virtual bool hide_field(const intern_string_t field_name, bool val)
|
virtual bool hide_field(const intern_string_t field_name, bool val)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
const char* const* get_timestamp_formats() const
|
const char* const* get_timestamp_formats() const
|
||||||
{
|
{
|
||||||
@ -463,7 +416,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &this->lf_timestamp_format[0];
|
return &this->lf_timestamp_format[0];
|
||||||
};
|
}
|
||||||
|
|
||||||
void check_for_new_year(std::vector<logline>& dst,
|
void check_for_new_year(std::vector<logline>& dst,
|
||||||
exttm log_tv,
|
exttm log_tv,
|
||||||
@ -474,7 +427,7 @@ public:
|
|||||||
virtual std::string get_pattern_regex(uint64_t line_number) const
|
virtual std::string get_pattern_regex(uint64_t line_number) const
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
};
|
}
|
||||||
|
|
||||||
struct pattern_for_lines {
|
struct pattern_for_lines {
|
||||||
pattern_for_lines(uint32_t pfl_line, uint32_t pfl_pat_index);
|
pattern_for_lines(uint32_t pfl_line, uint32_t pfl_pat_index);
|
||||||
@ -494,6 +447,18 @@ public:
|
|||||||
|
|
||||||
int pattern_index_for_line(uint64_t line_number) const;
|
int pattern_index_for_line(uint64_t line_number) const;
|
||||||
|
|
||||||
|
bool operator<(const log_format& rhs) const
|
||||||
|
{
|
||||||
|
return this->get_name() < rhs.get_name();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool name_lt(const std::shared_ptr<const log_format>& lhs,
|
||||||
|
const std::shared_ptr<const log_format>& rhs)
|
||||||
|
{
|
||||||
|
return intern_string_t::case_lt(lhs->get_name(), rhs->get_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string lf_description;
|
||||||
uint8_t lf_mod_index{0};
|
uint8_t lf_mod_index{0};
|
||||||
bool lf_multiline{true};
|
bool lf_multiline{true};
|
||||||
date_time_scanner lf_date_time;
|
date_time_scanner lf_date_time;
|
||||||
@ -515,9 +480,9 @@ protected:
|
|||||||
pcre_format(const char* regex) : name(regex), pcre(regex)
|
pcre_format(const char* regex) : name(regex), pcre(regex)
|
||||||
{
|
{
|
||||||
this->pf_timestamp_index = this->pcre.name_index("timestamp");
|
this->pf_timestamp_index = this->pcre.name_index("timestamp");
|
||||||
};
|
}
|
||||||
|
|
||||||
pcre_format() : name(nullptr), pcre(""){};
|
pcre_format() : name(nullptr), pcre("") {}
|
||||||
|
|
||||||
const char* name;
|
const char* name;
|
||||||
pcrepp pcre;
|
pcrepp pcre;
|
||||||
|
@ -42,10 +42,9 @@ class module_format;
|
|||||||
class external_log_format : public log_format {
|
class external_log_format : public log_format {
|
||||||
public:
|
public:
|
||||||
struct sample {
|
struct sample {
|
||||||
sample() : s_level(LEVEL_UNKNOWN) {}
|
|
||||||
|
|
||||||
positioned_property<std::string> s_line;
|
positioned_property<std::string> s_line;
|
||||||
log_level_t s_level;
|
std::string s_description;
|
||||||
|
log_level_t s_level{LEVEL_UNKNOWN};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct value_def {
|
struct value_def {
|
||||||
@ -53,7 +52,17 @@ public:
|
|||||||
value_kind_t kind,
|
value_kind_t kind,
|
||||||
int col,
|
int col,
|
||||||
log_format* format)
|
log_format* format)
|
||||||
: vd_meta(name, kind, col, format){};
|
: vd_meta(name, kind, col, format)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_rewrite_src_name()
|
||||||
|
{
|
||||||
|
this->vd_rewrite_src_name = intern_string::lookup(
|
||||||
|
fmt::format(FMT_STRING("{}:{}"),
|
||||||
|
this->vd_meta.lvm_format.value()->get_name(),
|
||||||
|
this->vd_meta.lvm_name));
|
||||||
|
}
|
||||||
|
|
||||||
logline_value_meta vd_meta;
|
logline_value_meta vd_meta;
|
||||||
std::string vd_collate;
|
std::string vd_collate;
|
||||||
@ -65,6 +74,7 @@ public:
|
|||||||
std::vector<std::string> vd_action_list;
|
std::vector<std::string> vd_action_list;
|
||||||
std::string vd_rewriter;
|
std::string vd_rewriter;
|
||||||
std::string vd_description;
|
std::string vd_description;
|
||||||
|
intern_string_t vd_rewrite_src_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct indexed_value_def {
|
struct indexed_value_def {
|
||||||
@ -89,7 +99,7 @@ public:
|
|||||||
struct pattern {
|
struct pattern {
|
||||||
std::string p_name;
|
std::string p_name;
|
||||||
std::string p_config_path;
|
std::string p_config_path;
|
||||||
std::shared_ptr<pcrepp> p_pcre;
|
std::shared_ptr<pcrepp_with_options<PCRE_DOTALL>> p_pcre;
|
||||||
std::vector<indexed_value_def> p_value_by_index;
|
std::vector<indexed_value_def> p_value_by_index;
|
||||||
std::vector<int> p_numeric_value_indexes;
|
std::vector<int> p_numeric_value_indexes;
|
||||||
int p_timestamp_field_index{-1};
|
int p_timestamp_field_index{-1};
|
||||||
@ -204,16 +214,16 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
std::set<std::string> get_source_path() const
|
std::set<std::string> get_source_path() const
|
||||||
{
|
{
|
||||||
return this->elf_source_path;
|
return this->elf_source_path;
|
||||||
};
|
}
|
||||||
|
|
||||||
enum json_log_field {
|
enum class json_log_field {
|
||||||
JLF_CONSTANT,
|
CONSTANT,
|
||||||
JLF_VARIABLE
|
VARIABLE
|
||||||
};
|
};
|
||||||
|
|
||||||
struct json_format_element {
|
struct json_format_element {
|
||||||
@ -235,81 +245,52 @@ public:
|
|||||||
CAPITALIZE,
|
CAPITALIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
json_format_element()
|
json_log_field jfe_type{json_log_field::CONSTANT};
|
||||||
: jfe_type(JLF_CONSTANT), jfe_default_value("-"), jfe_min_width(0),
|
|
||||||
jfe_max_width(LLONG_MAX), jfe_align(align_t::LEFT),
|
|
||||||
jfe_overflow(overflow_t::ABBREV),
|
|
||||||
jfe_text_transform(transform_t::NONE){};
|
|
||||||
|
|
||||||
json_log_field jfe_type;
|
|
||||||
positioned_property<intern_string_t> jfe_value;
|
positioned_property<intern_string_t> jfe_value;
|
||||||
std::string jfe_default_value;
|
std::string jfe_default_value{"-"};
|
||||||
long long jfe_min_width;
|
long long jfe_min_width{0};
|
||||||
long long jfe_max_width;
|
long long jfe_max_width{LLONG_MAX};
|
||||||
align_t jfe_align;
|
align_t jfe_align{align_t::LEFT};
|
||||||
overflow_t jfe_overflow;
|
overflow_t jfe_overflow{overflow_t::ABBREV};
|
||||||
transform_t jfe_text_transform;
|
transform_t jfe_text_transform{transform_t::NONE};
|
||||||
std::string jfe_ts_format;
|
std::string jfe_ts_format;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct json_field_cmp {
|
struct json_field_cmp {
|
||||||
json_field_cmp(json_log_field type, const intern_string_t name)
|
json_field_cmp(json_log_field type, const intern_string_t name)
|
||||||
: jfc_type(type), jfc_field_name(name){};
|
: jfc_type(type), jfc_field_name(name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
bool operator()(const json_format_element& jfe) const
|
bool operator()(const json_format_element& jfe) const
|
||||||
{
|
{
|
||||||
return (this->jfc_type == jfe.jfe_type
|
return (this->jfc_type == jfe.jfe_type
|
||||||
&& this->jfc_field_name == jfe.jfe_value.pp_value);
|
&& this->jfc_field_name == jfe.jfe_value.pp_value);
|
||||||
};
|
}
|
||||||
|
|
||||||
json_log_field jfc_type;
|
json_log_field jfc_type;
|
||||||
const intern_string_t jfc_field_name;
|
const intern_string_t jfc_field_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct highlighter_def {
|
struct highlighter_def {
|
||||||
highlighter_def() : hd_underline(false), hd_blink(false) {}
|
|
||||||
|
|
||||||
std::shared_ptr<pcrepp> hd_pattern;
|
std::shared_ptr<pcrepp> hd_pattern;
|
||||||
positioned_property<std::string> hd_color;
|
positioned_property<std::string> hd_color;
|
||||||
positioned_property<std::string> hd_background_color;
|
positioned_property<std::string> hd_background_color;
|
||||||
bool hd_underline;
|
bool hd_underline{false};
|
||||||
bool hd_blink;
|
bool hd_blink{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
long value_line_count(const intern_string_t ist,
|
long value_line_count(const intern_string_t ist,
|
||||||
bool top_level,
|
bool top_level,
|
||||||
const unsigned char* str = nullptr,
|
const unsigned char* str = nullptr,
|
||||||
ssize_t len = -1) const
|
ssize_t len = -1) const;
|
||||||
{
|
|
||||||
const auto iter = this->elf_value_defs.find(ist);
|
|
||||||
long line_count
|
|
||||||
= (str != NULL) ? std::count(&str[0], &str[len], '\n') + 1 : 1;
|
|
||||||
|
|
||||||
if (iter == this->elf_value_defs.end()) {
|
|
||||||
return (this->jlf_hide_extra || !top_level) ? 0 : line_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iter->second->vd_meta.lvm_hidden) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::find_if(this->jlf_line_format.begin(),
|
|
||||||
this->jlf_line_format.end(),
|
|
||||||
json_field_cmp(JLF_VARIABLE, ist))
|
|
||||||
!= this->jlf_line_format.end())
|
|
||||||
{
|
|
||||||
return line_count - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return line_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool has_value_def(const intern_string_t ist) const
|
bool has_value_def(const intern_string_t ist) const
|
||||||
{
|
{
|
||||||
const auto iter = this->elf_value_defs.find(ist);
|
const auto iter = this->elf_value_defs.find(ist);
|
||||||
|
|
||||||
return iter != this->elf_value_defs.end();
|
return iter != this->elf_value_defs.end();
|
||||||
};
|
}
|
||||||
|
|
||||||
std::string get_pattern_name(uint64_t line_number) const
|
std::string get_pattern_name(uint64_t line_number) const
|
||||||
{
|
{
|
||||||
@ -330,31 +311,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
log_level_t convert_level(const pcre_input& pi,
|
log_level_t convert_level(const pcre_input& pi,
|
||||||
const pcre_context::capture_t* level_cap) const
|
const pcre_context::capture_t* level_cap) const;
|
||||||
{
|
|
||||||
log_level_t retval = LEVEL_INFO;
|
|
||||||
|
|
||||||
if (level_cap != nullptr && level_cap->is_valid()) {
|
|
||||||
pcre_context_static<128> pc_level;
|
|
||||||
pcre_input pi_level(
|
|
||||||
pi.get_substr_start(level_cap), 0, level_cap->length());
|
|
||||||
|
|
||||||
if (this->elf_level_patterns.empty()) {
|
|
||||||
retval
|
|
||||||
= string2level(pi_level.get_string(), level_cap->length());
|
|
||||||
} else {
|
|
||||||
for (const auto& elf_level_pattern : this->elf_level_patterns) {
|
|
||||||
if (elf_level_pattern.second.lp_pcre->match(pc_level,
|
|
||||||
pi_level)) {
|
|
||||||
retval = elf_level_pattern.first;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
using mod_map_t = std::map<intern_string_t, module_format>;
|
using mod_map_t = std::map<intern_string_t, module_format>;
|
||||||
static mod_map_t MODULE_FORMATS;
|
static mod_map_t MODULE_FORMATS;
|
||||||
@ -362,7 +319,8 @@ public:
|
|||||||
GRAPH_ORDERED_FORMATS;
|
GRAPH_ORDERED_FORMATS;
|
||||||
|
|
||||||
std::set<std::string> elf_source_path;
|
std::set<std::string> elf_source_path;
|
||||||
std::unordered_map<std::string, int> elf_format_sources;
|
std::vector<ghc::filesystem::path> elf_format_source_order;
|
||||||
|
std::map<intern_string_t, int> elf_format_sources;
|
||||||
std::list<intern_string_t> elf_collision;
|
std::list<intern_string_t> elf_collision;
|
||||||
std::string elf_file_pattern;
|
std::string elf_file_pattern;
|
||||||
std::set<file_format_t> elf_mime_types;
|
std::set<file_format_t> elf_mime_types;
|
||||||
@ -410,52 +368,21 @@ public:
|
|||||||
}
|
}
|
||||||
this->jlf_cached_line.resize(old_size + len);
|
this->jlf_cached_line.resize(old_size + len);
|
||||||
memcpy(&(this->jlf_cached_line[old_size]), value, len);
|
memcpy(&(this->jlf_cached_line[old_size]), value, len);
|
||||||
};
|
}
|
||||||
|
|
||||||
void json_append_to_cache(ssize_t len)
|
void json_append_to_cache(ssize_t len)
|
||||||
{
|
{
|
||||||
size_t old_size = this->jlf_cached_line.size();
|
size_t old_size = this->jlf_cached_line.size();
|
||||||
this->jlf_cached_line.resize(old_size + len);
|
this->jlf_cached_line.resize(old_size + len);
|
||||||
memset(&this->jlf_cached_line[old_size], ' ', len);
|
memset(&this->jlf_cached_line[old_size], ' ', len);
|
||||||
};
|
}
|
||||||
|
|
||||||
void json_append(const json_format_element& jfe,
|
void json_append(const json_format_element& jfe,
|
||||||
const char* value,
|
const char* value,
|
||||||
ssize_t len)
|
ssize_t len);
|
||||||
{
|
|
||||||
if (len == -1) {
|
|
||||||
len = strlen(value);
|
|
||||||
}
|
|
||||||
if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
|
|
||||||
if (len < jfe.jfe_min_width) {
|
|
||||||
this->json_append_to_cache(jfe.jfe_min_width - len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this->json_append_to_cache(value, len);
|
|
||||||
if (jfe.jfe_align == json_format_element::align_t::LEFT) {
|
|
||||||
if (len < jfe.jfe_min_width) {
|
|
||||||
this->json_append_to_cache(jfe.jfe_min_width - len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
logline_value_meta get_value_meta(intern_string_t field_name,
|
logline_value_meta get_value_meta(intern_string_t field_name,
|
||||||
value_kind_t kind)
|
value_kind_t kind);
|
||||||
{
|
|
||||||
auto iter = this->elf_value_defs.find(field_name);
|
|
||||||
|
|
||||||
if (iter == this->elf_value_defs.end()) {
|
|
||||||
auto retval = logline_value_meta(field_name, kind, -1, this);
|
|
||||||
|
|
||||||
retval.lvm_hidden = this->jlf_hide_extra;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lvm = iter->second->vd_meta;
|
|
||||||
|
|
||||||
lvm.lvm_kind = kind;
|
|
||||||
return lvm;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<lnav::console::snippet> get_snippets() const;
|
std::vector<lnav::console::snippet> get_snippets() const;
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ public:
|
|||||||
ll_expr_mark(0)
|
ll_expr_mark(0)
|
||||||
{
|
{
|
||||||
memset(this->ll_schema, 0, sizeof(this->ll_schema));
|
memset(this->ll_schema, 0, sizeof(this->ll_schema));
|
||||||
};
|
}
|
||||||
|
|
||||||
logline(file_off_t off,
|
logline(file_off_t off,
|
||||||
const struct timeval& tv,
|
const struct timeval& tv,
|
||||||
@ -85,69 +85,48 @@ public:
|
|||||||
{
|
{
|
||||||
this->set_time(tv);
|
this->set_time(tv);
|
||||||
memset(this->ll_schema, 0, sizeof(this->ll_schema));
|
memset(this->ll_schema, 0, sizeof(this->ll_schema));
|
||||||
};
|
}
|
||||||
|
|
||||||
/** @return The offset of the line in the file. */
|
/** @return The offset of the line in the file. */
|
||||||
file_off_t get_offset() const
|
file_off_t get_offset() const { return this->ll_offset; }
|
||||||
{
|
|
||||||
return this->ll_offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint16_t get_sub_offset() const
|
uint16_t get_sub_offset() const { return this->ll_sub_offset; }
|
||||||
{
|
|
||||||
return this->ll_sub_offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_sub_offset(uint16_t suboff)
|
void set_sub_offset(uint16_t suboff) { this->ll_sub_offset = suboff; }
|
||||||
{
|
|
||||||
this->ll_sub_offset = suboff;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @return The timestamp for the line. */
|
/** @return The timestamp for the line. */
|
||||||
time_t get_time() const
|
time_t get_time() const { return this->ll_time; }
|
||||||
{
|
|
||||||
return this->ll_time;
|
|
||||||
};
|
|
||||||
|
|
||||||
void to_exttm(struct exttm& tm_out) const
|
void to_exttm(struct exttm& tm_out) const
|
||||||
{
|
{
|
||||||
tm_out.et_tm = *gmtime(&this->ll_time);
|
tm_out.et_tm = *gmtime(&this->ll_time);
|
||||||
tm_out.et_nsec = this->ll_millis * 1000 * 1000;
|
tm_out.et_nsec = this->ll_millis * 1000 * 1000;
|
||||||
};
|
}
|
||||||
|
|
||||||
void set_time(time_t t)
|
void set_time(time_t t) { this->ll_time = t; }
|
||||||
{
|
|
||||||
this->ll_time = t;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @return The millisecond timestamp for the line. */
|
/** @return The millisecond timestamp for the line. */
|
||||||
uint16_t get_millis() const
|
uint16_t get_millis() const { return this->ll_millis; }
|
||||||
{
|
|
||||||
return this->ll_millis;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_millis(uint16_t m)
|
void set_millis(uint16_t m) { this->ll_millis = m; }
|
||||||
{
|
|
||||||
this->ll_millis = m;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint64_t get_time_in_millis() const
|
uint64_t get_time_in_millis() const
|
||||||
{
|
{
|
||||||
return (this->ll_time * 1000ULL + (uint64_t) this->ll_millis);
|
return (this->ll_time * 1000ULL + (uint64_t) this->ll_millis);
|
||||||
};
|
}
|
||||||
|
|
||||||
struct timeval get_timeval() const
|
struct timeval get_timeval() const
|
||||||
{
|
{
|
||||||
struct timeval retval = {this->ll_time, this->ll_millis * 1000};
|
struct timeval retval = {this->ll_time, this->ll_millis * 1000};
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
void set_time(const struct timeval& tv)
|
void set_time(const struct timeval& tv)
|
||||||
{
|
{
|
||||||
this->ll_time = tv.tv_sec;
|
this->ll_time = tv.tv_sec;
|
||||||
this->ll_millis = tv.tv_usec / 1000;
|
this->ll_millis = tv.tv_usec / 1000;
|
||||||
};
|
}
|
||||||
|
|
||||||
void set_ignore(bool val)
|
void set_ignore(bool val)
|
||||||
{
|
{
|
||||||
@ -156,7 +135,7 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
this->ll_level &= ~LEVEL_IGNORE;
|
this->ll_level &= ~LEVEL_IGNORE;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_ignored() const
|
bool is_ignored() const
|
||||||
{
|
{
|
||||||
@ -170,22 +149,13 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
this->ll_level &= ~LEVEL_MARK;
|
this->ll_level &= ~LEVEL_MARK;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_marked() const
|
bool is_marked() const { return this->ll_level & LEVEL_MARK; }
|
||||||
{
|
|
||||||
return this->ll_level & LEVEL_MARK;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_expr_mark(bool val)
|
void set_expr_mark(bool val) { this->ll_expr_mark = val; }
|
||||||
{
|
|
||||||
this->ll_expr_mark = val;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool is_expr_marked() const
|
bool is_expr_marked() const { return this->ll_expr_mark; }
|
||||||
{
|
|
||||||
return this->ll_expr_mark;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_time_skew(bool val)
|
void set_time_skew(bool val)
|
||||||
{
|
{
|
||||||
@ -194,22 +164,13 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
this->ll_level &= ~LEVEL_TIME_SKEW;
|
this->ll_level &= ~LEVEL_TIME_SKEW;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
bool is_time_skewed() const
|
|
||||||
{
|
|
||||||
return this->ll_level & LEVEL_TIME_SKEW;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_valid_utf(bool v)
|
|
||||||
{
|
|
||||||
this->ll_valid_utf = v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_valid_utf() const
|
bool is_time_skewed() const { return this->ll_level & LEVEL_TIME_SKEW; }
|
||||||
{
|
|
||||||
return this->ll_valid_utf;
|
void set_valid_utf(bool v) { this->ll_valid_utf = v; }
|
||||||
}
|
|
||||||
|
bool is_valid_utf() const { return this->ll_valid_utf; }
|
||||||
|
|
||||||
/** @param l The logging level. */
|
/** @param l The logging level. */
|
||||||
void set_level(log_level_t l)
|
void set_level(log_level_t l)
|
||||||
@ -221,42 +182,30 @@ public:
|
|||||||
log_level_t get_level_and_flags() const
|
log_level_t get_level_and_flags() const
|
||||||
{
|
{
|
||||||
return (log_level_t) this->ll_level;
|
return (log_level_t) this->ll_level;
|
||||||
};
|
}
|
||||||
|
|
||||||
log_level_t get_msg_level() const
|
log_level_t get_msg_level() const
|
||||||
{
|
{
|
||||||
return (log_level_t) (this->ll_level & ~LEVEL__FLAGS);
|
return (log_level_t) (this->ll_level & ~LEVEL__FLAGS);
|
||||||
};
|
}
|
||||||
|
|
||||||
const char* get_level_name() const
|
const char* get_level_name() const
|
||||||
{
|
{
|
||||||
return level_names[this->ll_level & ~LEVEL__FLAGS];
|
return level_names[this->ll_level & ~LEVEL__FLAGS];
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_message() const
|
bool is_message() const
|
||||||
{
|
{
|
||||||
return (this->ll_level & (LEVEL_IGNORE | LEVEL_CONTINUED)) == 0;
|
return (this->ll_level & (LEVEL_IGNORE | LEVEL_CONTINUED)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_continued() const
|
bool is_continued() const { return this->ll_level & LEVEL_CONTINUED; }
|
||||||
{
|
|
||||||
return this->ll_level & LEVEL_CONTINUED;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint8_t get_module_id() const
|
uint8_t get_module_id() const { return this->ll_module_id; }
|
||||||
{
|
|
||||||
return this->ll_module_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_opid(uint8_t opid)
|
void set_opid(uint8_t opid) { this->ll_opid = opid; }
|
||||||
{
|
|
||||||
this->ll_opid = opid;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint8_t get_opid() const
|
uint8_t get_opid() const { return this->ll_opid; }
|
||||||
{
|
|
||||||
return this->ll_opid;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if there is a schema value set for this log line.
|
* @return True if there is a schema value set for this log line.
|
||||||
@ -264,7 +213,7 @@ public:
|
|||||||
bool has_schema() const
|
bool has_schema() const
|
||||||
{
|
{
|
||||||
return (this->ll_schema[0] != 0 || this->ll_schema[1] != 0);
|
return (this->ll_schema[0] != 0 || this->ll_schema[1] != 0);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the "schema" for this log line. The schema ID is used to match log
|
* Set the "schema" for this log line. The schema ID is used to match log
|
||||||
@ -276,12 +225,9 @@ public:
|
|||||||
void set_schema(const byte_array<2, uint64_t>& ba)
|
void set_schema(const byte_array<2, uint64_t>& ba)
|
||||||
{
|
{
|
||||||
memcpy(this->ll_schema, ba.in(), sizeof(this->ll_schema));
|
memcpy(this->ll_schema, ba.in(), sizeof(this->ll_schema));
|
||||||
};
|
}
|
||||||
|
|
||||||
char get_schema() const
|
char get_schema() const { return this->ll_schema[0]; }
|
||||||
{
|
|
||||||
return this->ll_schema[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a partial match of the given schema against this log line.
|
* Perform a partial match of the given schema against this log line.
|
||||||
@ -309,26 +255,23 @@ public:
|
|||||||
|| (this->ll_time == rhs.ll_time && this->ll_millis == rhs.ll_millis
|
|| (this->ll_time == rhs.ll_time && this->ll_millis == rhs.ll_millis
|
||||||
&& this->ll_offset == rhs.ll_offset
|
&& this->ll_offset == rhs.ll_offset
|
||||||
&& this->ll_sub_offset < rhs.ll_sub_offset);
|
&& this->ll_sub_offset < rhs.ll_sub_offset);
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator<(const time_t& rhs) const
|
bool operator<(const time_t& rhs) const { return this->ll_time < rhs; }
|
||||||
{
|
|
||||||
return this->ll_time < rhs;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool operator<(const struct timeval& rhs) const
|
bool operator<(const struct timeval& rhs) const
|
||||||
{
|
{
|
||||||
return ((this->ll_time < rhs.tv_sec)
|
return ((this->ll_time < rhs.tv_sec)
|
||||||
|| ((this->ll_time == rhs.tv_sec)
|
|| ((this->ll_time == rhs.tv_sec)
|
||||||
&& (this->ll_millis < (rhs.tv_usec / 1000))));
|
&& (this->ll_millis < (rhs.tv_usec / 1000))));
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator<=(const struct timeval& rhs) const
|
bool operator<=(const struct timeval& rhs) const
|
||||||
{
|
{
|
||||||
return ((this->ll_time < rhs.tv_sec)
|
return ((this->ll_time < rhs.tv_sec)
|
||||||
|| ((this->ll_time == rhs.tv_sec)
|
|| ((this->ll_time == rhs.tv_sec)
|
||||||
&& (this->ll_millis <= (rhs.tv_usec / 1000))));
|
&& (this->ll_millis <= (rhs.tv_usec / 1000))));
|
||||||
};
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
file_off_t ll_offset;
|
file_off_t ll_offset;
|
||||||
|
@ -93,11 +93,14 @@ ensure_format(const yajlpp_provider_context& ypc, userdata* ud)
|
|||||||
formats->push_back(name);
|
formats->push_back(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto srcs_iter
|
if (!ud->ud_format_path.empty()) {
|
||||||
= retval->elf_format_sources.find(ud->ud_format_path.string());
|
auto i_src_path = intern_string::lookup(ud->ud_format_path.string());
|
||||||
if (srcs_iter == retval->elf_format_sources.end()) {
|
auto srcs_iter = retval->elf_format_sources.find(i_src_path);
|
||||||
retval->elf_format_sources[ud->ud_format_path.string()]
|
if (srcs_iter == retval->elf_format_sources.end()) {
|
||||||
= ud->ud_parse_context->get_line_number();
|
retval->elf_format_source_order.emplace_back(ud->ud_format_path);
|
||||||
|
retval->elf_format_sources[i_src_path]
|
||||||
|
= ud->ud_parse_context->get_line_number();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ud->ud_format_path.empty()) {
|
if (ud->ud_format_path.empty()) {
|
||||||
@ -170,7 +173,7 @@ line_format_provider(const yajlpp_provider_context& ypc,
|
|||||||
{
|
{
|
||||||
auto& jfe = ensure_json_format_element(elf, ypc.ypc_index);
|
auto& jfe = ensure_json_format_element(elf, ypc.ypc_index);
|
||||||
|
|
||||||
jfe.jfe_type = external_log_format::JLF_VARIABLE;
|
jfe.jfe_type = external_log_format::json_log_field::VARIABLE;
|
||||||
|
|
||||||
return &jfe;
|
return &jfe;
|
||||||
}
|
}
|
||||||
@ -394,7 +397,7 @@ read_json_constant(yajlpp_parse_context* ypc,
|
|||||||
|
|
||||||
ypc->ypc_array_index.back() += 1;
|
ypc->ypc_array_index.back() += 1;
|
||||||
auto& jfe = ensure_json_format_element(elf, ypc->ypc_array_index.back());
|
auto& jfe = ensure_json_format_element(elf, ypc->ypc_array_index.back());
|
||||||
jfe.jfe_type = external_log_format::JLF_CONSTANT;
|
jfe.jfe_type = external_log_format::json_log_field::CONSTANT;
|
||||||
jfe.jfe_default_value = val;
|
jfe.jfe_default_value = val;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -647,6 +650,10 @@ static const json_path_handler_base::enum_value_t LEVEL_ENUM[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct json_path_container sample_handlers = {
|
static struct json_path_container sample_handlers = {
|
||||||
|
yajlpp::property_handler("description")
|
||||||
|
.with_synopsis("<text>")
|
||||||
|
.with_description("A description of this sample.")
|
||||||
|
.for_field(&external_log_format::sample::s_description),
|
||||||
yajlpp::property_handler("line")
|
yajlpp::property_handler("line")
|
||||||
.with_synopsis("<log-line>")
|
.with_synopsis("<log-line>")
|
||||||
.with_description(
|
.with_description(
|
||||||
@ -800,7 +807,8 @@ struct json_path_container format_handlers = {
|
|||||||
json_path_handler("title", read_format_field)
|
json_path_handler("title", read_format_field)
|
||||||
.with_description("The human-readable name for this log format"),
|
.with_description("The human-readable name for this log format"),
|
||||||
json_path_handler("description", read_format_field)
|
json_path_handler("description", read_format_field)
|
||||||
.with_description("A longer description of this log format"),
|
.with_description("A longer description of this log format")
|
||||||
|
.for_field(&external_log_format::lf_description),
|
||||||
json_path_handler("timestamp-format#", read_format_field)
|
json_path_handler("timestamp-format#", read_format_field)
|
||||||
.with_description("An array of strptime(3)-like timestamp formats"),
|
.with_description("An array of strptime(3)-like timestamp formats"),
|
||||||
json_path_handler("module-field", read_format_field)
|
json_path_handler("module-field", read_format_field)
|
||||||
@ -983,7 +991,8 @@ load_format_file(const ghc::filesystem::path& filename,
|
|||||||
auto_fd fd;
|
auto_fd fd;
|
||||||
|
|
||||||
log_info("loading formats from file: %s", filename.c_str());
|
log_info("loading formats from file: %s", filename.c_str());
|
||||||
yajlpp_parse_context ypc(filename, &root_format_handler);
|
yajlpp_parse_context ypc(intern_string::lookup(filename.string()),
|
||||||
|
&root_format_handler);
|
||||||
ud.ud_parse_context = &ypc;
|
ud.ud_parse_context = &ypc;
|
||||||
ud.ud_format_path = filename;
|
ud.ud_format_path = filename;
|
||||||
ud.ud_format_names = &retval;
|
ud.ud_format_names = &retval;
|
||||||
@ -1082,7 +1091,8 @@ load_formats(const std::vector<ghc::filesystem::path>& extra_paths,
|
|||||||
|
|
||||||
log_debug("Loading default formats");
|
log_debug("Loading default formats");
|
||||||
for (const auto& bsf : lnav_format_json) {
|
for (const auto& bsf : lnav_format_json) {
|
||||||
yajlpp_parse_context ypc_builtin(bsf.get_name(), &root_format_handler);
|
yajlpp_parse_context ypc_builtin(intern_string::lookup(bsf.get_name()),
|
||||||
|
&root_format_handler);
|
||||||
handle = yajl_alloc(&ypc_builtin.ypc_callbacks, nullptr, &ypc_builtin);
|
handle = yajl_alloc(&ypc_builtin.ypc_callbacks, nullptr, &ypc_builtin);
|
||||||
ud.ud_parse_context = &ypc_builtin;
|
ud.ud_parse_context = &ypc_builtin;
|
||||||
ud.ud_format_names = &retval;
|
ud.ud_format_names = &retval;
|
||||||
@ -1140,13 +1150,7 @@ load_formats(const std::vector<ghc::filesystem::path>& extra_paths,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errors.empty()) {
|
alpha_ordered_formats.push_back(elf);
|
||||||
alpha_ordered_formats.push_back(elf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errors.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& graph_ordered_formats = external_log_format::GRAPH_ORDERED_FORMATS;
|
auto& graph_ordered_formats = external_log_format::GRAPH_ORDERED_FORMATS;
|
||||||
|
@ -72,7 +72,7 @@ public:
|
|||||||
bar_role_out = role_t::VCR_SCROLLBAR_WARNING;
|
bar_role_out = role_t::VCR_SCROLLBAR_WARNING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -34,37 +34,7 @@
|
|||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
/**
|
#include "base/log_level_enum.hh"
|
||||||
* The logging level identifiers for a line(s).
|
|
||||||
*/
|
|
||||||
enum log_level_t : int {
|
|
||||||
LEVEL_UNKNOWN,
|
|
||||||
LEVEL_TRACE,
|
|
||||||
LEVEL_DEBUG5,
|
|
||||||
LEVEL_DEBUG4,
|
|
||||||
LEVEL_DEBUG3,
|
|
||||||
LEVEL_DEBUG2,
|
|
||||||
LEVEL_DEBUG,
|
|
||||||
LEVEL_INFO,
|
|
||||||
LEVEL_STATS,
|
|
||||||
LEVEL_NOTICE,
|
|
||||||
LEVEL_WARNING,
|
|
||||||
LEVEL_ERROR,
|
|
||||||
LEVEL_CRITICAL,
|
|
||||||
LEVEL_FATAL,
|
|
||||||
LEVEL_INVALID,
|
|
||||||
|
|
||||||
LEVEL__MAX,
|
|
||||||
|
|
||||||
LEVEL_IGNORE = 0x10, /*< Ignore */
|
|
||||||
LEVEL_TIME_SKEW = 0x20, /*< Received after timestamp. */
|
|
||||||
LEVEL_MARK = 0x40, /*< Bookmarked line. */
|
|
||||||
LEVEL_CONTINUED = 0x80, /*< Continuation of multiline entry. */
|
|
||||||
|
|
||||||
/** Mask of flags for the level field. */
|
|
||||||
LEVEL__FLAGS
|
|
||||||
= (LEVEL_IGNORE | LEVEL_TIME_SKEW | LEVEL_MARK | LEVEL_CONTINUED)
|
|
||||||
};
|
|
||||||
|
|
||||||
extern const char* level_names[LEVEL__MAX + 1];
|
extern const char* level_names[LEVEL__MAX + 1];
|
||||||
|
|
||||||
|
@ -168,6 +168,41 @@ log_vtab_impl::logline_value_to_sqlite_type(value_kind_t kind)
|
|||||||
return std::make_pair(type, subtype);
|
return std::make_pair(type, subtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
log_vtab_impl::get_foreign_keys(std::vector<std::string>& keys_inout) const
|
||||||
|
{
|
||||||
|
keys_inout.emplace_back("log_line");
|
||||||
|
keys_inout.emplace_back("min(log_line)");
|
||||||
|
keys_inout.emplace_back("log_mark");
|
||||||
|
keys_inout.emplace_back("log_time_msecs");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
log_vtab_impl::extract(std::shared_ptr<logfile> lf,
|
||||||
|
uint64_t line_number,
|
||||||
|
shared_buffer_ref& line,
|
||||||
|
std::vector<logline_value>& values)
|
||||||
|
{
|
||||||
|
auto format = lf->get_format();
|
||||||
|
|
||||||
|
this->vi_attrs.clear();
|
||||||
|
format->annotate(line_number, line, this->vi_attrs, values, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
log_vtab_impl::is_valid(log_cursor& lc, logfile_sub_source& lss)
|
||||||
|
{
|
||||||
|
content_line_t cl(lss.at(lc.lc_curr_line));
|
||||||
|
std::shared_ptr<logfile> lf = lss.find(cl);
|
||||||
|
auto lf_iter = lf->begin() + cl;
|
||||||
|
|
||||||
|
if (!lf_iter->is_message()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct vtab {
|
struct vtab {
|
||||||
sqlite3_vtab base;
|
sqlite3_vtab base;
|
||||||
sqlite3* db;
|
sqlite3* db;
|
||||||
@ -984,14 +1019,12 @@ vt_update(sqlite3_vtab* tab,
|
|||||||
|
|
||||||
if (log_tags) {
|
if (log_tags) {
|
||||||
std::vector<lnav::console::user_message> errors;
|
std::vector<lnav::console::user_message> errors;
|
||||||
yajlpp_parse_context ypc(
|
yajlpp_parse_context ypc(vt->vi->get_tags_name(), &tags_handler);
|
||||||
fmt::format(FMT_STRING("{}.log_tags"), vt->vi->get_name()),
|
|
||||||
&tags_handler);
|
|
||||||
auto_mem<yajl_handle_t> handle(yajl_free);
|
auto_mem<yajl_handle_t> handle(yajl_free);
|
||||||
|
|
||||||
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
|
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
|
||||||
ypc.ypc_userdata = &errors;
|
ypc.ypc_userdata = &errors;
|
||||||
ypc.ypc_line_number = log_vtab_data.lvd_line_number;
|
ypc.ypc_line_number = log_vtab_data.lvd_location.sl_line_number;
|
||||||
ypc.with_handle(handle)
|
ypc.with_handle(handle)
|
||||||
.with_error_reporter([](const yajlpp_parse_context& ypc,
|
.with_error_reporter([](const yajlpp_parse_context& ypc,
|
||||||
auto msg) {
|
auto msg) {
|
||||||
@ -1002,19 +1035,16 @@ vt_update(sqlite3_vtab* tab,
|
|||||||
.with_obj(tmp_bm);
|
.with_obj(tmp_bm);
|
||||||
ypc.parse_doc(string_fragment{log_tags});
|
ypc.parse_doc(string_fragment{log_tags});
|
||||||
if (!errors.empty()) {
|
if (!errors.empty()) {
|
||||||
auto top_error
|
auto top_error = lnav::console::user_message::error(
|
||||||
= lnav::console::user_message::error(
|
attr_line_t("invalid value for ")
|
||||||
attr_line_t("invalid value for ")
|
.append_quoted("log_tags"_symbol)
|
||||||
.append_quoted("log_tags"_symbol)
|
.append(" column of table ")
|
||||||
.append(" column of table ")
|
.append_quoted(lnav::roles::symbol(
|
||||||
.append_quoted(lnav::roles::symbol(
|
vt->vi->get_name().to_string())))
|
||||||
vt->vi->get_name().to_string())))
|
.with_reason(errors[0].to_attr_line({}))
|
||||||
.with_reason(errors[0].to_attr_line({}))
|
.with_snippet(lnav::console::snippet::from(
|
||||||
.with_snippet(
|
log_vtab_data.lvd_location,
|
||||||
lnav::console::snippet::from(
|
log_vtab_data.lvd_content));
|
||||||
log_vtab_data.lvd_source,
|
|
||||||
log_vtab_data.lvd_content)
|
|
||||||
.with_line(log_vtab_data.lvd_line_number));
|
|
||||||
auto json_error = lnav::to_json(top_error);
|
auto json_error = lnav::to_json(top_error);
|
||||||
tab->zErrMsg
|
tab->zErrMsg
|
||||||
= sqlite3_mprintf("lnav-error:%s", json_error.c_str());
|
= sqlite3_mprintf("lnav-error:%s", json_error.c_str());
|
||||||
@ -1185,3 +1215,33 @@ log_vtab_manager::unregister_vtab(intern_string_t name)
|
|||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
log_format_vtab_impl::next(log_cursor& lc, logfile_sub_source& lss)
|
||||||
|
{
|
||||||
|
lc.lc_curr_line = lc.lc_curr_line + vis_line_t(1);
|
||||||
|
lc.lc_sub_index = 0;
|
||||||
|
|
||||||
|
if (lc.is_eof()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cl = content_line_t(lss.at(lc.lc_curr_line));
|
||||||
|
auto lf = lss.find(cl);
|
||||||
|
auto lf_iter = lf->begin() + cl;
|
||||||
|
uint8_t mod_id = lf_iter->get_module_id();
|
||||||
|
|
||||||
|
if (!lf_iter->is_message()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto format = lf->get_format();
|
||||||
|
if (format->get_name() == this->lfvi_format.get_name()) {
|
||||||
|
return true;
|
||||||
|
} else if (mod_id && mod_id == this->lfvi_format.lf_mod_index) {
|
||||||
|
// XXX
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -107,61 +107,40 @@ public:
|
|||||||
value_kind_t kind);
|
value_kind_t kind);
|
||||||
|
|
||||||
log_vtab_impl(const intern_string_t name)
|
log_vtab_impl(const intern_string_t name)
|
||||||
: vi_supports_indexes(true), vi_name(name)
|
: vi_name(name), vi_tags_name(intern_string::lookup(
|
||||||
|
fmt::format(FMT_STRING("{}.log_tags"), name)))
|
||||||
{
|
{
|
||||||
this->vi_attrs.resize(128);
|
this->vi_attrs.resize(128);
|
||||||
};
|
}
|
||||||
|
|
||||||
virtual ~log_vtab_impl() = default;
|
virtual ~log_vtab_impl() = default;
|
||||||
|
|
||||||
const intern_string_t get_name() const
|
const intern_string_t get_name() const { return this->vi_name; }
|
||||||
{
|
|
||||||
return this->vi_name;
|
intern_string_t get_tags_name() const { return this->vi_tags_name; }
|
||||||
};
|
|
||||||
|
|
||||||
std::string get_table_statement();
|
std::string get_table_statement();
|
||||||
|
|
||||||
virtual bool is_valid(log_cursor& lc, logfile_sub_source& lss)
|
virtual bool is_valid(log_cursor& lc, logfile_sub_source& lss);
|
||||||
{
|
|
||||||
content_line_t cl(lss.at(lc.lc_curr_line));
|
|
||||||
std::shared_ptr<logfile> lf = lss.find(cl);
|
|
||||||
auto lf_iter = lf->begin() + cl;
|
|
||||||
|
|
||||||
if (!lf_iter->is_message()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual bool next(log_cursor& lc, logfile_sub_source& lss) = 0;
|
virtual bool next(log_cursor& lc, logfile_sub_source& lss) = 0;
|
||||||
|
|
||||||
virtual void get_columns(std::vector<vtab_column>& cols) const {};
|
virtual void get_columns(std::vector<vtab_column>& cols) const {};
|
||||||
|
|
||||||
virtual void get_foreign_keys(std::vector<std::string>& keys_inout) const
|
virtual void get_foreign_keys(std::vector<std::string>& keys_inout) const;
|
||||||
{
|
|
||||||
keys_inout.emplace_back("log_line");
|
|
||||||
keys_inout.emplace_back("min(log_line)");
|
|
||||||
keys_inout.emplace_back("log_mark");
|
|
||||||
keys_inout.emplace_back("log_time_msecs");
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void extract(std::shared_ptr<logfile> lf,
|
virtual void extract(std::shared_ptr<logfile> lf,
|
||||||
uint64_t line_number,
|
uint64_t line_number,
|
||||||
shared_buffer_ref& line,
|
shared_buffer_ref& line,
|
||||||
std::vector<logline_value>& values)
|
std::vector<logline_value>& values);
|
||||||
{
|
|
||||||
auto format = lf->get_format();
|
|
||||||
|
|
||||||
this->vi_attrs.clear();
|
bool vi_supports_indexes{true};
|
||||||
format->annotate(line_number, line, this->vi_attrs, values, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
bool vi_supports_indexes;
|
|
||||||
int vi_column_count;
|
int vi_column_count;
|
||||||
string_attrs_t vi_attrs;
|
string_attrs_t vi_attrs;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const intern_string_t vi_name;
|
const intern_string_t vi_name;
|
||||||
|
const intern_string_t vi_tags_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
class log_format_vtab_impl : public log_vtab_impl {
|
class log_format_vtab_impl : public log_vtab_impl {
|
||||||
@ -171,34 +150,7 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool next(log_cursor& lc, logfile_sub_source& lss)
|
virtual bool next(log_cursor& lc, logfile_sub_source& lss);
|
||||||
{
|
|
||||||
lc.lc_curr_line = lc.lc_curr_line + vis_line_t(1);
|
|
||||||
lc.lc_sub_index = 0;
|
|
||||||
|
|
||||||
if (lc.is_eof()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto cl = content_line_t(lss.at(lc.lc_curr_line));
|
|
||||||
auto lf = lss.find(cl);
|
|
||||||
auto lf_iter = lf->begin() + cl;
|
|
||||||
uint8_t mod_id = lf_iter->get_module_id();
|
|
||||||
|
|
||||||
if (!lf_iter->is_message()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto format = lf->get_format();
|
|
||||||
if (format->get_name() == this->lfvi_format.get_name()) {
|
|
||||||
return true;
|
|
||||||
} else if (mod_id && mod_id == this->lfvi_format.lf_mod_index) {
|
|
||||||
// XXX
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const log_format& lfvi_format;
|
const log_format& lfvi_format;
|
||||||
@ -210,8 +162,7 @@ typedef void (*sql_progress_finished_callback_t)();
|
|||||||
struct _log_vtab_data {
|
struct _log_vtab_data {
|
||||||
sql_progress_callback_t lvd_progress;
|
sql_progress_callback_t lvd_progress;
|
||||||
sql_progress_finished_callback_t lvd_finished;
|
sql_progress_finished_callback_t lvd_finished;
|
||||||
std::string lvd_source;
|
source_location lvd_location;
|
||||||
int lvd_line_number{0};
|
|
||||||
attr_line_t lvd_content;
|
attr_line_t lvd_content;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -221,14 +172,12 @@ class sql_progress_guard {
|
|||||||
public:
|
public:
|
||||||
sql_progress_guard(sql_progress_callback_t cb,
|
sql_progress_guard(sql_progress_callback_t cb,
|
||||||
sql_progress_finished_callback_t fcb,
|
sql_progress_finished_callback_t fcb,
|
||||||
const std::string& source,
|
source_location loc,
|
||||||
int line_number,
|
|
||||||
const attr_line_t& content)
|
const attr_line_t& content)
|
||||||
{
|
{
|
||||||
log_vtab_data.lvd_progress = cb;
|
log_vtab_data.lvd_progress = cb;
|
||||||
log_vtab_data.lvd_finished = fcb;
|
log_vtab_data.lvd_finished = fcb;
|
||||||
log_vtab_data.lvd_source = source;
|
log_vtab_data.lvd_location = loc;
|
||||||
log_vtab_data.lvd_line_number = line_number;
|
|
||||||
log_vtab_data.lvd_content = content;
|
log_vtab_data.lvd_content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,8 +188,7 @@ public:
|
|||||||
}
|
}
|
||||||
log_vtab_data.lvd_progress = nullptr;
|
log_vtab_data.lvd_progress = nullptr;
|
||||||
log_vtab_data.lvd_finished = nullptr;
|
log_vtab_data.lvd_finished = nullptr;
|
||||||
log_vtab_data.lvd_source.clear();
|
log_vtab_data.lvd_location = source_location{};
|
||||||
log_vtab_data.lvd_line_number = 0;
|
|
||||||
log_vtab_data.lvd_content.clear();
|
log_vtab_data.lvd_content.clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -253,15 +201,9 @@ public:
|
|||||||
log_vtab_manager(sqlite3* db, textview_curses& tc, logfile_sub_source& lss);
|
log_vtab_manager(sqlite3* db, textview_curses& tc, logfile_sub_source& lss);
|
||||||
~log_vtab_manager();
|
~log_vtab_manager();
|
||||||
|
|
||||||
textview_curses* get_view() const
|
textview_curses* get_view() const { return &this->vm_textview; }
|
||||||
{
|
|
||||||
return &this->vm_textview;
|
|
||||||
};
|
|
||||||
|
|
||||||
logfile_sub_source* get_source()
|
logfile_sub_source* get_source() { return &this->vm_source; }
|
||||||
{
|
|
||||||
return &this->vm_source;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string register_vtab(std::shared_ptr<log_vtab_impl> vi);
|
std::string register_vtab(std::shared_ptr<log_vtab_impl> vi);
|
||||||
std::string unregister_vtab(intern_string_t name);
|
std::string unregister_vtab(intern_string_t name);
|
||||||
@ -274,17 +216,11 @@ public:
|
|||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
}
|
||||||
|
|
||||||
iterator begin() const
|
iterator begin() const { return this->vm_impls.begin(); }
|
||||||
{
|
|
||||||
return this->vm_impls.begin();
|
|
||||||
};
|
|
||||||
|
|
||||||
iterator end() const
|
iterator end() const { return this->vm_impls.end(); }
|
||||||
{
|
|
||||||
return this->vm_impls.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sqlite3* vm_db;
|
sqlite3* vm_db;
|
||||||
|
@ -796,3 +796,49 @@ logfile::mark_as_duplicate(const std::string& name)
|
|||||||
note_type::duplicate,
|
note_type::duplicate,
|
||||||
fmt::format(FMT_STRING("hiding duplicate of {}"), name));
|
fmt::format(FMT_STRING("hiding duplicate of {}"), name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
logfile::adjust_content_time(int line, const timeval& tv, bool abs_offset)
|
||||||
|
{
|
||||||
|
struct timeval old_time = this->lf_time_offset;
|
||||||
|
|
||||||
|
this->lf_time_offset_line = line;
|
||||||
|
if (abs_offset) {
|
||||||
|
this->lf_time_offset = tv;
|
||||||
|
} else {
|
||||||
|
timeradd(&old_time, &tv, &this->lf_time_offset);
|
||||||
|
}
|
||||||
|
for (auto& iter : *this) {
|
||||||
|
struct timeval curr, diff, new_time;
|
||||||
|
|
||||||
|
curr = iter.get_timeval();
|
||||||
|
timersub(&curr, &old_time, &diff);
|
||||||
|
timeradd(&diff, &this->lf_time_offset, &new_time);
|
||||||
|
iter.set_time(new_time);
|
||||||
|
}
|
||||||
|
this->lf_sort_needed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
logfile::set_filename(const std::string& filename)
|
||||||
|
{
|
||||||
|
if (this->lf_filename != filename) {
|
||||||
|
this->lf_filename = filename;
|
||||||
|
ghc::filesystem::path p(filename);
|
||||||
|
this->lf_basename = p.filename();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timeval
|
||||||
|
logfile::original_line_time(logfile::iterator ll)
|
||||||
|
{
|
||||||
|
if (this->is_time_adjusted()) {
|
||||||
|
struct timeval line_time = ll->get_timeval();
|
||||||
|
struct timeval retval;
|
||||||
|
|
||||||
|
timersub(&line_time, &this->lf_time_offset, &retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ll->get_timeval();
|
||||||
|
}
|
||||||
|
156
src/logfile.hh
156
src/logfile.hh
@ -111,10 +111,7 @@ public:
|
|||||||
|
|
||||||
~logfile() override;
|
~logfile() override;
|
||||||
|
|
||||||
const logfile_activity& get_activity() const
|
const logfile_activity& get_activity() const { return this->lf_activity; }
|
||||||
{
|
|
||||||
return this->lf_activity;
|
|
||||||
};
|
|
||||||
|
|
||||||
nonstd::optional<ghc::filesystem::path> get_actual_path() const
|
nonstd::optional<ghc::filesystem::path> get_actual_path() const
|
||||||
{
|
{
|
||||||
@ -122,128 +119,63 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @return The filename as given in the constructor. */
|
/** @return The filename as given in the constructor. */
|
||||||
const std::string& get_filename() const
|
const std::string& get_filename() const { return this->lf_filename; }
|
||||||
{
|
|
||||||
return this->lf_filename;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @return The filename as given in the constructor, excluding the path
|
/** @return The filename as given in the constructor, excluding the path
|
||||||
* prefix. */
|
* prefix. */
|
||||||
const std::string& get_basename() const
|
const std::string& get_basename() const { return this->lf_basename; }
|
||||||
{
|
|
||||||
return this->lf_basename;
|
|
||||||
};
|
|
||||||
|
|
||||||
int get_fd() const
|
int get_fd() const { return this->lf_line_buffer.get_fd(); }
|
||||||
{
|
|
||||||
return this->lf_line_buffer.get_fd();
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @param filename The new filename for this log file. */
|
/** @param filename The new filename for this log file. */
|
||||||
void set_filename(const std::string& filename)
|
void set_filename(const std::string& filename);
|
||||||
{
|
|
||||||
if (this->lf_filename != filename) {
|
|
||||||
this->lf_filename = filename;
|
|
||||||
ghc::filesystem::path p(filename);
|
|
||||||
this->lf_basename = p.filename();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::string& get_content_id() const
|
const std::string& get_content_id() const { return this->lf_content_id; }
|
||||||
{
|
|
||||||
return this->lf_content_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @return The inode for this log file. */
|
/** @return The inode for this log file. */
|
||||||
const struct stat& get_stat() const
|
const struct stat& get_stat() const { return this->lf_stat; }
|
||||||
{
|
|
||||||
return this->lf_stat;
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t get_longest_line_length() const
|
size_t get_longest_line_length() const { return this->lf_longest_line; }
|
||||||
{
|
|
||||||
return this->lf_longest_line;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_compressed() const
|
bool is_compressed() const { return this->lf_line_buffer.is_compressed(); }
|
||||||
{
|
|
||||||
return this->lf_line_buffer.is_compressed();
|
|
||||||
};
|
|
||||||
|
|
||||||
bool is_valid_filename() const
|
bool is_valid_filename() const { return this->lf_valid_filename; }
|
||||||
{
|
|
||||||
return this->lf_valid_filename;
|
|
||||||
};
|
|
||||||
|
|
||||||
file_off_t get_index_size() const
|
file_off_t get_index_size() const { return this->lf_index_size; }
|
||||||
{
|
|
||||||
return this->lf_index_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The detected format, rebuild_index() must be called before this
|
* @return The detected format, rebuild_index() must be called before this
|
||||||
* will return a value other than NULL.
|
* will return a value other than NULL.
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<log_format> get_format() const
|
std::shared_ptr<log_format> get_format() const { return this->lf_format; }
|
||||||
{
|
|
||||||
return this->lf_format;
|
|
||||||
};
|
|
||||||
|
|
||||||
intern_string_t get_format_name() const;
|
intern_string_t get_format_name() const;
|
||||||
|
|
||||||
text_format_t get_text_format() const
|
text_format_t get_text_format() const { return this->lf_text_format; }
|
||||||
{
|
|
||||||
return this->lf_text_format;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The last modified time of the file when the file was last
|
* @return The last modified time of the file when the file was last
|
||||||
* indexed.
|
* indexed.
|
||||||
*/
|
*/
|
||||||
time_t get_modified_time() const
|
time_t get_modified_time() const { return this->lf_index_time; }
|
||||||
{
|
|
||||||
return this->lf_index_time;
|
|
||||||
};
|
|
||||||
|
|
||||||
int get_time_offset_line() const
|
int get_time_offset_line() const { return this->lf_time_offset_line; }
|
||||||
{
|
|
||||||
return this->lf_time_offset_line;
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct timeval& get_time_offset() const
|
const struct timeval& get_time_offset() const
|
||||||
{
|
{
|
||||||
return this->lf_time_offset;
|
return this->lf_time_offset;
|
||||||
};
|
}
|
||||||
|
|
||||||
void adjust_content_time(int line,
|
void adjust_content_time(int line,
|
||||||
const struct timeval& tv,
|
const struct timeval& tv,
|
||||||
bool abs_offset = true)
|
bool abs_offset = true);
|
||||||
{
|
|
||||||
struct timeval old_time = this->lf_time_offset;
|
|
||||||
|
|
||||||
this->lf_time_offset_line = line;
|
|
||||||
if (abs_offset) {
|
|
||||||
this->lf_time_offset = tv;
|
|
||||||
} else {
|
|
||||||
timeradd(&old_time, &tv, &this->lf_time_offset);
|
|
||||||
}
|
|
||||||
for (auto& iter : *this) {
|
|
||||||
struct timeval curr, diff, new_time;
|
|
||||||
|
|
||||||
curr = iter.get_timeval();
|
|
||||||
timersub(&curr, &old_time, &diff);
|
|
||||||
timeradd(&diff, &this->lf_time_offset, &new_time);
|
|
||||||
iter.set_time(new_time);
|
|
||||||
}
|
|
||||||
this->lf_sort_needed = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
void clear_time_offset()
|
void clear_time_offset()
|
||||||
{
|
{
|
||||||
struct timeval tv = {0, 0};
|
struct timeval tv = {0, 0};
|
||||||
|
|
||||||
this->adjust_content_time(-1, tv);
|
this->adjust_content_time(-1, tv);
|
||||||
};
|
}
|
||||||
|
|
||||||
void mark_as_duplicate(const std::string& name);
|
void mark_as_duplicate(const std::string& name);
|
||||||
|
|
||||||
@ -299,46 +231,20 @@ public:
|
|||||||
nonstd::optional<const_iterator> find_from_time(
|
nonstd::optional<const_iterator> find_from_time(
|
||||||
const struct timeval& tv) const;
|
const struct timeval& tv) const;
|
||||||
|
|
||||||
logline& operator[](int index)
|
logline& operator[](int index) { return this->lf_index[index]; }
|
||||||
{
|
|
||||||
return this->lf_index[index];
|
|
||||||
};
|
|
||||||
|
|
||||||
logline& front()
|
logline& front() { return this->lf_index.front(); }
|
||||||
{
|
|
||||||
return this->lf_index.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
logline& back()
|
logline& back() { return this->lf_index.back(); }
|
||||||
{
|
|
||||||
return this->lf_index.back();
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @return True if this log file still exists. */
|
/** @return True if this log file still exists. */
|
||||||
bool exists() const;
|
bool exists() const;
|
||||||
|
|
||||||
void close()
|
void close() { this->lf_is_closed = true; }
|
||||||
{
|
|
||||||
this->lf_is_closed = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool is_closed() const
|
bool is_closed() const { return this->lf_is_closed; }
|
||||||
{
|
|
||||||
return this->lf_is_closed;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct timeval original_line_time(iterator ll)
|
struct timeval original_line_time(iterator ll);
|
||||||
{
|
|
||||||
if (this->is_time_adjusted()) {
|
|
||||||
struct timeval line_time = ll->get_timeval();
|
|
||||||
struct timeval retval;
|
|
||||||
|
|
||||||
timersub(&line_time, &this->lf_time_offset, &retval);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ll->get_timeval();
|
|
||||||
};
|
|
||||||
|
|
||||||
Result<shared_buffer_ref, std::string> read_line(iterator ll);
|
Result<shared_buffer_ref, std::string> read_line(iterator ll);
|
||||||
|
|
||||||
@ -351,7 +257,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
iterator message_start(iterator ll)
|
iterator message_start(iterator ll)
|
||||||
{
|
{
|
||||||
@ -370,8 +276,10 @@ public:
|
|||||||
|
|
||||||
file_range get_file_range(const_iterator ll, bool include_continues = true)
|
file_range get_file_range(const_iterator ll, bool include_continues = true)
|
||||||
{
|
{
|
||||||
return {ll->get_offset(),
|
return {
|
||||||
(file_ssize_t) this->line_length(ll, include_continues)};
|
ll->get_offset(),
|
||||||
|
(file_ssize_t) this->line_length(ll, include_continues),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_full_message(const_iterator ll,
|
void read_full_message(const_iterator ll,
|
||||||
@ -402,14 +310,14 @@ public:
|
|||||||
void set_logfile_observer(logfile_observer* lo)
|
void set_logfile_observer(logfile_observer* lo)
|
||||||
{
|
{
|
||||||
this->lf_logfile_observer = lo;
|
this->lf_logfile_observer = lo;
|
||||||
};
|
}
|
||||||
|
|
||||||
void set_logline_observer(logline_observer* llo);
|
void set_logline_observer(logline_observer* llo);
|
||||||
|
|
||||||
logline_observer* get_logline_observer() const
|
logline_observer* get_logline_observer() const
|
||||||
{
|
{
|
||||||
return this->lf_logline_observer;
|
return this->lf_logline_observer;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator<(const logfile& rhs) const
|
bool operator<(const logfile& rhs) const
|
||||||
{
|
{
|
||||||
@ -424,7 +332,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_indexing() const
|
bool is_indexing() const
|
||||||
{
|
{
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
#include "base/ansi_scrubber.hh"
|
#include "base/ansi_scrubber.hh"
|
||||||
#include "base/humanize.time.hh"
|
#include "base/humanize.time.hh"
|
||||||
|
#include "base/itertools.hh"
|
||||||
#include "base/string_util.hh"
|
#include "base/string_util.hh"
|
||||||
#include "command_executor.hh"
|
#include "command_executor.hh"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -349,8 +350,6 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
|
|||||||
|
|
||||||
value_out = this->lss_token_attrs;
|
value_out = this->lss_token_attrs;
|
||||||
|
|
||||||
attrs = vc.vc_level_attrs[this->lss_token_line->get_msg_level()].first;
|
|
||||||
|
|
||||||
if ((row + 1) < (int) this->lss_filtered_index.size()) {
|
if ((row + 1) < (int) this->lss_filtered_index.size()) {
|
||||||
next_line = this->find_line(this->at(vis_line_t(row + 1)));
|
next_line = this->find_line(this->at(vis_line_t(row + 1)));
|
||||||
}
|
}
|
||||||
@ -367,6 +366,8 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
|
|||||||
lr.lr_start = 0;
|
lr.lr_start = 0;
|
||||||
lr.lr_end = this->lss_token_value.length();
|
lr.lr_end = this->lss_token_value.length();
|
||||||
value_out.emplace_back(lr, SA_ORIGINAL_LINE.value());
|
value_out.emplace_back(lr, SA_ORIGINAL_LINE.value());
|
||||||
|
value_out.emplace_back(
|
||||||
|
lr, SA_LEVEL.value(this->lss_token_line->get_msg_level()));
|
||||||
|
|
||||||
lr.lr_start = time_offset_end;
|
lr.lr_start = time_offset_end;
|
||||||
lr.lr_end = -1;
|
lr.lr_end = -1;
|
||||||
@ -1731,15 +1732,9 @@ logfile_sub_source::reload_index_delegate()
|
|||||||
nonstd::optional<std::shared_ptr<text_filter>>
|
nonstd::optional<std::shared_ptr<text_filter>>
|
||||||
logfile_sub_source::get_sql_filter()
|
logfile_sub_source::get_sql_filter()
|
||||||
{
|
{
|
||||||
auto iter
|
return this->tss_filters | lnav::itertools::find_if([](const auto& filt) {
|
||||||
= std::find_if(this->tss_filters.begin(),
|
return filt->get_index() == 0;
|
||||||
this->tss_filters.end(),
|
});
|
||||||
[](const auto& filt) { return filt->get_index() == 0; });
|
|
||||||
|
|
||||||
if (iter != this->tss_filters.end()) {
|
|
||||||
return *iter;
|
|
||||||
}
|
|
||||||
return nonstd::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -65,19 +65,15 @@ class index_delegate {
|
|||||||
public:
|
public:
|
||||||
virtual ~index_delegate() = default;
|
virtual ~index_delegate() = default;
|
||||||
|
|
||||||
virtual void index_start(logfile_sub_source& lss){
|
virtual void index_start(logfile_sub_source& lss) {}
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
virtual void index_line(logfile_sub_source& lss,
|
virtual void index_line(logfile_sub_source& lss,
|
||||||
logfile* lf,
|
logfile* lf,
|
||||||
logfile::iterator ll){
|
logfile::iterator ll)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
};
|
virtual void index_complete(logfile_sub_source& lss) {}
|
||||||
|
|
||||||
virtual void index_complete(logfile_sub_source& lss){
|
|
||||||
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class pcre_filter : public text_filter {
|
class pcre_filter : public text_filter {
|
||||||
@ -95,14 +91,14 @@ public:
|
|||||||
pcre_input pi(line.get_data(), 0, line.length());
|
pcre_input pi(line.get_data(), 0, line.length());
|
||||||
|
|
||||||
return this->pf_pcre.match(pc, pi);
|
return this->pf_pcre.match(pc, pi);
|
||||||
};
|
}
|
||||||
|
|
||||||
std::string to_command() const override
|
std::string to_command() const override
|
||||||
{
|
{
|
||||||
return (this->lf_type == text_filter::INCLUDE ? "filter-in "
|
return (this->lf_type == text_filter::INCLUDE ? "filter-in "
|
||||||
: "filter-out ")
|
: "filter-out ")
|
||||||
+ this->lf_id;
|
+ this->lf_id;
|
||||||
};
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
pcrepp pf_pcre;
|
pcrepp pf_pcre;
|
||||||
@ -177,7 +173,7 @@ public:
|
|||||||
{
|
{
|
||||||
this->lss_flags ^= F_TIME_OFFSET;
|
this->lss_flags ^= F_TIME_OFFSET;
|
||||||
this->clear_line_size_cache();
|
this->clear_line_size_cache();
|
||||||
};
|
}
|
||||||
|
|
||||||
void increase_line_context()
|
void increase_line_context()
|
||||||
{
|
{
|
||||||
@ -194,7 +190,7 @@ public:
|
|||||||
if (old_flags != this->lss_flags) {
|
if (old_flags != this->lss_flags) {
|
||||||
this->clear_line_size_cache();
|
this->clear_line_size_cache();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
bool decrease_line_context()
|
bool decrease_line_context()
|
||||||
{
|
{
|
||||||
@ -213,7 +209,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
}
|
||||||
|
|
||||||
size_t get_filename_offset() const
|
size_t get_filename_offset() const
|
||||||
{
|
{
|
||||||
@ -233,27 +229,24 @@ public:
|
|||||||
else
|
else
|
||||||
this->lss_flags &= ~F_TIME_OFFSET;
|
this->lss_flags &= ~F_TIME_OFFSET;
|
||||||
this->clear_line_size_cache();
|
this->clear_line_size_cache();
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_time_offset_enabled() const
|
bool is_time_offset_enabled() const
|
||||||
{
|
{
|
||||||
return (bool) (this->lss_flags & F_TIME_OFFSET);
|
return (bool) (this->lss_flags & F_TIME_OFFSET);
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_filename_enabled() const
|
bool is_filename_enabled() const
|
||||||
{
|
{
|
||||||
return (bool) (this->lss_flags & F_FILENAME);
|
return (bool) (this->lss_flags & F_FILENAME);
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_basename_enabled() const
|
bool is_basename_enabled() const
|
||||||
{
|
{
|
||||||
return (bool) (this->lss_flags & F_BASENAME);
|
return (bool) (this->lss_flags & F_BASENAME);
|
||||||
};
|
}
|
||||||
|
|
||||||
log_level_t get_min_log_level() const
|
log_level_t get_min_log_level() const { return this->lss_min_log_level; }
|
||||||
{
|
|
||||||
return this->lss_min_log_level;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_force_rebuild()
|
void set_force_rebuild()
|
||||||
{
|
{
|
||||||
@ -266,14 +259,14 @@ public:
|
|||||||
this->lss_min_log_level = level;
|
this->lss_min_log_level = level;
|
||||||
this->text_filters_changed();
|
this->text_filters_changed();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
bool get_min_log_time(struct timeval& tv_out) const
|
bool get_min_log_time(struct timeval& tv_out) const
|
||||||
{
|
{
|
||||||
tv_out = this->lss_min_log_time;
|
tv_out = this->lss_min_log_time;
|
||||||
return (this->lss_min_log_time.tv_sec != 0
|
return (this->lss_min_log_time.tv_sec != 0
|
||||||
|| this->lss_min_log_time.tv_usec != 0);
|
|| this->lss_min_log_time.tv_usec != 0);
|
||||||
};
|
}
|
||||||
|
|
||||||
void set_min_log_time(const struct timeval& tv)
|
void set_min_log_time(const struct timeval& tv)
|
||||||
{
|
{
|
||||||
@ -281,7 +274,7 @@ public:
|
|||||||
this->lss_min_log_time = tv;
|
this->lss_min_log_time = tv;
|
||||||
this->text_filters_changed();
|
this->text_filters_changed();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
bool get_max_log_time(struct timeval& tv_out) const
|
bool get_max_log_time(struct timeval& tv_out) const
|
||||||
{
|
{
|
||||||
@ -289,7 +282,7 @@ public:
|
|||||||
return (this->lss_max_log_time.tv_sec
|
return (this->lss_max_log_time.tv_sec
|
||||||
!= std::numeric_limits<time_t>::max()
|
!= std::numeric_limits<time_t>::max()
|
||||||
|| this->lss_max_log_time.tv_usec != 0);
|
|| this->lss_max_log_time.tv_usec != 0);
|
||||||
};
|
}
|
||||||
|
|
||||||
void set_max_log_time(struct timeval& tv)
|
void set_max_log_time(struct timeval& tv)
|
||||||
{
|
{
|
||||||
@ -297,7 +290,7 @@ public:
|
|||||||
this->lss_max_log_time = tv;
|
this->lss_max_log_time = tv;
|
||||||
this->text_filters_changed();
|
this->text_filters_changed();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
void clear_min_max_log_times()
|
void clear_min_max_log_times()
|
||||||
{
|
{
|
||||||
@ -312,7 +305,7 @@ public:
|
|||||||
this->lss_max_log_time.tv_usec = 0;
|
this->lss_max_log_time.tv_usec = 0;
|
||||||
this->text_filters_changed();
|
this->text_filters_changed();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
bool list_input_handle_key(listview_curses& lv, int ch);
|
bool list_input_handle_key(listview_curses& lv, int ch);
|
||||||
|
|
||||||
@ -322,22 +315,16 @@ public:
|
|||||||
this->lss_marked_only = val;
|
this->lss_marked_only = val;
|
||||||
this->text_filters_changed();
|
this->text_filters_changed();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
bool get_marked_only()
|
bool get_marked_only() { return this->lss_marked_only; }
|
||||||
{
|
|
||||||
return this->lss_marked_only;
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t text_line_count()
|
size_t text_line_count() { return this->lss_filtered_index.size(); }
|
||||||
{
|
|
||||||
return this->lss_filtered_index.size();
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t text_line_width(textview_curses& curses)
|
size_t text_line_width(textview_curses& curses)
|
||||||
{
|
{
|
||||||
return this->lss_longest_line;
|
return this->lss_longest_line;
|
||||||
};
|
}
|
||||||
|
|
||||||
size_t file_count() const
|
size_t file_count() const
|
||||||
{
|
{
|
||||||
@ -353,10 +340,7 @@ public:
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const { return this->lss_filtered_index.empty(); }
|
||||||
{
|
|
||||||
return this->lss_filtered_index.empty();
|
|
||||||
};
|
|
||||||
|
|
||||||
void text_value_for_line(textview_curses& tc,
|
void text_value_for_line(textview_curses& tc,
|
||||||
int row,
|
int row,
|
||||||
@ -404,22 +388,22 @@ public:
|
|||||||
void set_user_mark(const bookmark_type_t* bm, content_line_t cl)
|
void set_user_mark(const bookmark_type_t* bm, content_line_t cl)
|
||||||
{
|
{
|
||||||
this->lss_user_marks[bm].insert_once(cl);
|
this->lss_user_marks[bm].insert_once(cl);
|
||||||
};
|
}
|
||||||
|
|
||||||
bookmarks<content_line_t>::type& get_user_bookmarks()
|
bookmarks<content_line_t>::type& get_user_bookmarks()
|
||||||
{
|
{
|
||||||
return this->lss_user_marks;
|
return this->lss_user_marks;
|
||||||
};
|
}
|
||||||
|
|
||||||
std::map<content_line_t, bookmark_metadata>& get_user_bookmark_metadata()
|
std::map<content_line_t, bookmark_metadata>& get_user_bookmark_metadata()
|
||||||
{
|
{
|
||||||
return this->lss_user_mark_metadata;
|
return this->lss_user_mark_metadata;
|
||||||
};
|
}
|
||||||
|
|
||||||
int get_filtered_count() const
|
int get_filtered_count() const
|
||||||
{
|
{
|
||||||
return this->lss_index.size() - this->lss_filtered_index.size();
|
return this->lss_index.size() - this->lss_filtered_index.size();
|
||||||
};
|
}
|
||||||
|
|
||||||
int get_filtered_count_for(size_t filter_index) const
|
int get_filtered_count_for(size_t filter_index) const
|
||||||
{
|
{
|
||||||
@ -468,7 +452,7 @@ public:
|
|||||||
line = content_line_t(line % MAX_LINES_PER_FILE);
|
line = content_line_t(line % MAX_LINES_PER_FILE);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
logfile* find_file_ptr(content_line_t& line)
|
logfile* find_file_ptr(content_line_t& line)
|
||||||
{
|
{
|
||||||
@ -477,7 +461,7 @@ public:
|
|||||||
line = content_line_t(line % MAX_LINES_PER_FILE);
|
line = content_line_t(line % MAX_LINES_PER_FILE);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
logline* find_line(content_line_t line) const
|
logline* find_line(content_line_t line) const
|
||||||
{
|
{
|
||||||
@ -491,7 +475,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
nonstd::optional<vis_line_t> find_from_time(
|
nonstd::optional<vis_line_t> find_from_time(
|
||||||
const struct timeval& start) const;
|
const struct timeval& start) const;
|
||||||
@ -501,12 +485,12 @@ public:
|
|||||||
struct timeval tv = {start, 0};
|
struct timeval tv = {start, 0};
|
||||||
|
|
||||||
return this->find_from_time(tv);
|
return this->find_from_time(tv);
|
||||||
};
|
}
|
||||||
|
|
||||||
nonstd::optional<vis_line_t> find_from_time(const exttm& etm) const
|
nonstd::optional<vis_line_t> find_from_time(const exttm& etm) const
|
||||||
{
|
{
|
||||||
return this->find_from_time(etm.to_timeval());
|
return this->find_from_time(etm.to_timeval());
|
||||||
};
|
}
|
||||||
|
|
||||||
nonstd::optional<vis_line_t> find_from_content(content_line_t cl);
|
nonstd::optional<vis_line_t> find_from_content(content_line_t cl);
|
||||||
|
|
||||||
@ -516,17 +500,17 @@ public:
|
|||||||
return this->find_line(this->at(row))->get_timeval();
|
return this->find_line(this->at(row))->get_timeval();
|
||||||
}
|
}
|
||||||
return nonstd::nullopt;
|
return nonstd::nullopt;
|
||||||
};
|
}
|
||||||
|
|
||||||
nonstd::optional<vis_line_t> row_for_time(struct timeval time_bucket)
|
nonstd::optional<vis_line_t> row_for_time(struct timeval time_bucket)
|
||||||
{
|
{
|
||||||
return this->find_from_time(time_bucket);
|
return this->find_from_time(time_bucket);
|
||||||
};
|
}
|
||||||
|
|
||||||
content_line_t at(vis_line_t vl)
|
content_line_t at(vis_line_t vl)
|
||||||
{
|
{
|
||||||
return this->lss_index[this->lss_filtered_index[vl]];
|
return this->lss_index[this->lss_filtered_index[vl]];
|
||||||
};
|
}
|
||||||
|
|
||||||
content_line_t at_base(vis_line_t vl)
|
content_line_t at_base(vis_line_t vl)
|
||||||
{
|
{
|
||||||
@ -535,7 +519,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
return this->at(vl);
|
return this->at(vl);
|
||||||
};
|
}
|
||||||
|
|
||||||
log_accel::direction_t get_line_accel_direction(vis_line_t vl);
|
log_accel::direction_t get_line_accel_direction(vis_line_t vl);
|
||||||
|
|
||||||
@ -551,28 +535,25 @@ public:
|
|||||||
ld_visible(lf->is_indexing())
|
ld_visible(lf->is_indexing())
|
||||||
{
|
{
|
||||||
lf->set_logline_observer(&this->ld_filter_state);
|
lf->set_logline_observer(&this->ld_filter_state);
|
||||||
};
|
}
|
||||||
|
|
||||||
void clear()
|
void clear() { this->ld_filter_state.lfo_filter_state.clear(); }
|
||||||
{
|
|
||||||
this->ld_filter_state.lfo_filter_state.clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_file(const std::shared_ptr<logfile>& lf)
|
void set_file(const std::shared_ptr<logfile>& lf)
|
||||||
{
|
{
|
||||||
this->ld_filter_state.lfo_filter_state.tfs_logfile = lf;
|
this->ld_filter_state.lfo_filter_state.tfs_logfile = lf;
|
||||||
lf->set_logline_observer(&this->ld_filter_state);
|
lf->set_logline_observer(&this->ld_filter_state);
|
||||||
};
|
}
|
||||||
|
|
||||||
std::shared_ptr<logfile> get_file() const
|
std::shared_ptr<logfile> get_file() const
|
||||||
{
|
{
|
||||||
return this->ld_filter_state.lfo_filter_state.tfs_logfile;
|
return this->ld_filter_state.lfo_filter_state.tfs_logfile;
|
||||||
};
|
}
|
||||||
|
|
||||||
logfile* get_file_ptr() const
|
logfile* get_file_ptr() const
|
||||||
{
|
{
|
||||||
return this->ld_filter_state.lfo_filter_state.tfs_logfile.get();
|
return this->ld_filter_state.lfo_filter_state.tfs_logfile.get();
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_visible() const
|
bool is_visible() const
|
||||||
{
|
{
|
||||||
@ -594,25 +575,13 @@ public:
|
|||||||
using const_iterator
|
using const_iterator
|
||||||
= std::vector<std::unique_ptr<logfile_data>>::const_iterator;
|
= std::vector<std::unique_ptr<logfile_data>>::const_iterator;
|
||||||
|
|
||||||
iterator begin()
|
iterator begin() { return this->lss_files.begin(); }
|
||||||
{
|
|
||||||
return this->lss_files.begin();
|
|
||||||
};
|
|
||||||
|
|
||||||
iterator end()
|
iterator end() { return this->lss_files.end(); }
|
||||||
{
|
|
||||||
return this->lss_files.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
const_iterator cbegin() const
|
const_iterator cbegin() const { return this->lss_files.begin(); }
|
||||||
{
|
|
||||||
return this->lss_files.begin();
|
|
||||||
};
|
|
||||||
|
|
||||||
const_iterator cend() const
|
const_iterator cend() const { return this->lss_files.end(); }
|
||||||
{
|
|
||||||
return this->lss_files.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
iterator find_data(content_line_t& line)
|
iterator find_data(content_line_t& line)
|
||||||
{
|
{
|
||||||
@ -621,7 +590,7 @@ public:
|
|||||||
line = content_line_t(line % MAX_LINES_PER_FILE);
|
line = content_line_t(line % MAX_LINES_PER_FILE);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
iterator find_data(content_line_t line, uint64_t& offset_out)
|
iterator find_data(content_line_t line, uint64_t& offset_out)
|
||||||
{
|
{
|
||||||
@ -630,7 +599,7 @@ public:
|
|||||||
offset_out = line % MAX_LINES_PER_FILE;
|
offset_out = line % MAX_LINES_PER_FILE;
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
}
|
||||||
|
|
||||||
nonstd::optional<logfile_data*> find_data(
|
nonstd::optional<logfile_data*> find_data(
|
||||||
const std::shared_ptr<logfile>& lf)
|
const std::shared_ptr<logfile>& lf)
|
||||||
@ -659,7 +628,7 @@ public:
|
|||||||
ssize_t index = std::distance(this->begin(), iter);
|
ssize_t index = std::distance(this->begin(), iter);
|
||||||
|
|
||||||
return content_line_t(index * MAX_LINES_PER_FILE);
|
return content_line_t(index * MAX_LINES_PER_FILE);
|
||||||
};
|
}
|
||||||
|
|
||||||
void set_index_delegate(index_delegate* id)
|
void set_index_delegate(index_delegate* id)
|
||||||
{
|
{
|
||||||
@ -667,12 +636,12 @@ public:
|
|||||||
this->lss_index_delegate = id;
|
this->lss_index_delegate = id;
|
||||||
this->reload_index_delegate();
|
this->reload_index_delegate();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
index_delegate* get_index_delegate() const
|
index_delegate* get_index_delegate() const
|
||||||
{
|
{
|
||||||
return this->lss_index_delegate;
|
return this->lss_index_delegate;
|
||||||
};
|
}
|
||||||
|
|
||||||
void reload_index_delegate();
|
void reload_index_delegate();
|
||||||
|
|
||||||
@ -712,7 +681,7 @@ public:
|
|||||||
nonstd::optional<location_history*> get_location_history()
|
nonstd::optional<location_history*> get_location_history()
|
||||||
{
|
{
|
||||||
return &this->lss_location_history;
|
return &this->lss_location_history;
|
||||||
};
|
}
|
||||||
|
|
||||||
Result<bool, std::string> eval_sql_filter(sqlite3_stmt* stmt,
|
Result<bool, std::string> eval_sql_filter(sqlite3_stmt* stmt,
|
||||||
iterator ld,
|
iterator ld,
|
||||||
@ -757,19 +726,14 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct __attribute__((__packed__)) indexed_content {
|
struct __attribute__((__packed__)) indexed_content {
|
||||||
indexed_content(){
|
indexed_content() {}
|
||||||
|
|
||||||
};
|
indexed_content(content_line_t cl) : ic_value(cl) {}
|
||||||
|
|
||||||
indexed_content(content_line_t cl)
|
|
||||||
: ic_value(cl){
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
operator content_line_t() const
|
operator content_line_t() const
|
||||||
{
|
{
|
||||||
return content_line_t(this->ic_value);
|
return content_line_t(this->ic_value);
|
||||||
};
|
}
|
||||||
|
|
||||||
uint64_t ic_value : 40;
|
uint64_t ic_value : 40;
|
||||||
};
|
};
|
||||||
@ -783,7 +747,7 @@ private:
|
|||||||
logline* ll_rhs = this->llss_controller.find_line(rhs);
|
logline* ll_rhs = this->llss_controller.find_line(rhs);
|
||||||
|
|
||||||
return (*ll_lhs) < (*ll_rhs);
|
return (*ll_lhs) < (*ll_rhs);
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator()(const uint32_t& lhs, const uint32_t& rhs) const
|
bool operator()(const uint32_t& lhs, const uint32_t& rhs) const
|
||||||
{
|
{
|
||||||
@ -795,7 +759,7 @@ private:
|
|||||||
logline* ll_rhs = this->llss_controller.find_line(cl_rhs);
|
logline* ll_rhs = this->llss_controller.find_line(cl_rhs);
|
||||||
|
|
||||||
return (*ll_lhs) < (*ll_rhs);
|
return (*ll_lhs) < (*ll_rhs);
|
||||||
};
|
}
|
||||||
#if 0
|
#if 0
|
||||||
bool operator()(const indexed_content &lhs, const indexed_content &rhs)
|
bool operator()(const indexed_content &lhs, const indexed_content &rhs)
|
||||||
{
|
{
|
||||||
@ -803,21 +767,23 @@ private:
|
|||||||
logline *ll_rhs = this->llss_controller.find_line(rhs.ic_value);
|
logline *ll_rhs = this->llss_controller.find_line(rhs.ic_value);
|
||||||
|
|
||||||
return (*ll_lhs) < (*ll_rhs);
|
return (*ll_lhs) < (*ll_rhs);
|
||||||
};
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool operator()(const content_line_t& lhs, const time_t& rhs) const
|
bool operator()(const content_line_t& lhs, const time_t& rhs) const
|
||||||
{
|
{
|
||||||
logline* ll_lhs = this->llss_controller.find_line(lhs);
|
logline* ll_lhs = this->llss_controller.find_line(lhs);
|
||||||
|
|
||||||
return *ll_lhs < rhs;
|
return *ll_lhs < rhs;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator()(const content_line_t& lhs,
|
bool operator()(const content_line_t& lhs,
|
||||||
const struct timeval& rhs) const
|
const struct timeval& rhs) const
|
||||||
{
|
{
|
||||||
logline* ll_lhs = this->llss_controller.find_line(lhs);
|
logline* ll_lhs = this->llss_controller.find_line(lhs);
|
||||||
|
|
||||||
return *ll_lhs < rhs;
|
return *ll_lhs < rhs;
|
||||||
};
|
}
|
||||||
|
|
||||||
logfile_sub_source& llss_controller;
|
logfile_sub_source& llss_controller;
|
||||||
};
|
};
|
||||||
@ -835,7 +801,7 @@ private:
|
|||||||
logline* ll_rhs = this->llss_controller.find_line(cl_rhs);
|
logline* ll_rhs = this->llss_controller.find_line(cl_rhs);
|
||||||
|
|
||||||
return (*ll_lhs) < (*ll_rhs);
|
return (*ll_lhs) < (*ll_rhs);
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator()(const uint32_t& lhs, const struct timeval& rhs) const
|
bool operator()(const uint32_t& lhs, const struct timeval& rhs) const
|
||||||
{
|
{
|
||||||
@ -844,7 +810,7 @@ private:
|
|||||||
logline* ll_lhs = this->llss_controller.find_line(cl_lhs);
|
logline* ll_lhs = this->llss_controller.find_line(cl_lhs);
|
||||||
|
|
||||||
return (*ll_lhs) < rhs;
|
return (*ll_lhs) < rhs;
|
||||||
};
|
}
|
||||||
|
|
||||||
const logfile_sub_source& llss_controller;
|
const logfile_sub_source& llss_controller;
|
||||||
};
|
};
|
||||||
|
@ -55,11 +55,8 @@ class papertrail_proc : public curl_request {
|
|||||||
public:
|
public:
|
||||||
papertrail_proc(const std::string& search, time_t min_time, time_t max_time)
|
papertrail_proc(const std::string& search, time_t min_time, time_t max_time)
|
||||||
: curl_request("papertrailapp.com"),
|
: curl_request("papertrailapp.com"),
|
||||||
ptp_jcontext(this->cr_name, &FORMAT_HANDLERS), ptp_jhandle(yajl_free),
|
ptp_jcontext(intern_string::lookup(this->cr_name), &FORMAT_HANDLERS),
|
||||||
ptp_gen(yajl_gen_free), ptp_search(search),
|
ptp_search(search), ptp_min_time(min_time), ptp_max_time(max_time)
|
||||||
ptp_quoted_search(curl_free), ptp_header_list(curl_slist_free_all),
|
|
||||||
ptp_partial_read(false), ptp_min_time(min_time),
|
|
||||||
ptp_max_time(max_time)
|
|
||||||
{
|
{
|
||||||
char piper_tmpname[PATH_MAX];
|
char piper_tmpname[PATH_MAX];
|
||||||
const char* tmpdir;
|
const char* tmpdir;
|
||||||
@ -111,14 +108,11 @@ public:
|
|||||||
this->cr_handle, CURLOPT_HTTPHEADER, this->ptp_header_list.in());
|
this->cr_handle, CURLOPT_HTTPHEADER, this->ptp_header_list.in());
|
||||||
|
|
||||||
this->set_url();
|
this->set_url();
|
||||||
};
|
}
|
||||||
|
|
||||||
~papertrail_proc() {}
|
~papertrail_proc() {}
|
||||||
|
|
||||||
auto_fd copy_fd() const
|
auto_fd copy_fd() const { return this->ptp_fd.dup(); }
|
||||||
{
|
|
||||||
return this->ptp_fd.dup();
|
|
||||||
};
|
|
||||||
|
|
||||||
long complete(CURLcode result);
|
long complete(CURLcode result);
|
||||||
|
|
||||||
@ -149,7 +143,7 @@ public:
|
|||||||
base_url,
|
base_url,
|
||||||
this->ptp_quoted_search.in()));
|
this->ptp_quoted_search.in()));
|
||||||
curl_easy_setopt(this->cr_handle, CURLOPT_URL, this->ptp_url.in());
|
curl_easy_setopt(this->cr_handle, CURLOPT_URL, this->ptp_url.in());
|
||||||
};
|
}
|
||||||
|
|
||||||
static size_t write_cb(void* contents,
|
static size_t write_cb(void* contents,
|
||||||
size_t size,
|
size_t size,
|
||||||
@ -164,17 +158,17 @@ public:
|
|||||||
static const char* PT_SEARCH_URL;
|
static const char* PT_SEARCH_URL;
|
||||||
|
|
||||||
yajlpp_parse_context ptp_jcontext;
|
yajlpp_parse_context ptp_jcontext;
|
||||||
auto_mem<yajl_handle_t> ptp_jhandle;
|
auto_mem<yajl_handle_t> ptp_jhandle{yajl_free};
|
||||||
auto_mem<yajl_gen_t> ptp_gen;
|
auto_mem<yajl_gen_t> ptp_gen{yajl_gen_free};
|
||||||
const char* ptp_api_key;
|
const char* ptp_api_key;
|
||||||
const std::string ptp_search;
|
const std::string ptp_search;
|
||||||
auto_mem<const char> ptp_quoted_search;
|
auto_mem<const char> ptp_quoted_search{curl_free};
|
||||||
auto_mem<char> ptp_url;
|
auto_mem<char> ptp_url;
|
||||||
auto_mem<char> ptp_token_header;
|
auto_mem<char> ptp_token_header;
|
||||||
auto_mem<struct curl_slist> ptp_header_list;
|
auto_mem<struct curl_slist> ptp_header_list{curl_slist_free_all};
|
||||||
auto_fd ptp_fd;
|
auto_fd ptp_fd;
|
||||||
std::string ptp_last_max_id;
|
std::string ptp_last_max_id;
|
||||||
bool ptp_partial_read;
|
bool ptp_partial_read{false};
|
||||||
std::string ptp_error;
|
std::string ptp_error;
|
||||||
time_t ptp_min_time;
|
time_t ptp_min_time;
|
||||||
time_t ptp_max_time;
|
time_t ptp_max_time;
|
||||||
|
@ -38,9 +38,7 @@ pcre_context::capture_t*
|
|||||||
pcre_context::operator[](const char* name) const
|
pcre_context::operator[](const char* name) const
|
||||||
{
|
{
|
||||||
capture_t* retval = nullptr;
|
capture_t* retval = nullptr;
|
||||||
int index;
|
auto index = this->pc_pcre->name_index(name);
|
||||||
|
|
||||||
index = this->pc_pcre->name_index(name);
|
|
||||||
if (index != PCRE_ERROR_NOSUBSTRING) {
|
if (index != PCRE_ERROR_NOSUBSTRING) {
|
||||||
retval = &this->pc_captures[index + 1];
|
retval = &this->pc_captures[index + 1];
|
||||||
}
|
}
|
||||||
@ -48,6 +46,18 @@ pcre_context::operator[](const char* name) const
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pcre_context::capture_t*
|
||||||
|
pcre_context::first_valid() const
|
||||||
|
{
|
||||||
|
for (int lpc = 1; lpc < this->pc_count; lpc++) {
|
||||||
|
if (this->pc_captures[lpc].is_valid()) {
|
||||||
|
return &this->pc_captures[lpc];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
pcrepp::quote(const char* unquoted)
|
pcrepp::quote(const char* unquoted)
|
||||||
{
|
{
|
||||||
@ -85,7 +95,7 @@ void
|
|||||||
pcrepp::find_captures(const char* pattern)
|
pcrepp::find_captures(const char* pattern)
|
||||||
{
|
{
|
||||||
bool in_class = false, in_escape = false, in_literal = false;
|
bool in_class = false, in_escape = false, in_literal = false;
|
||||||
std::vector<pcre_context::capture> cap_in_progress;
|
std::vector<pcre_context::capture_t> cap_in_progress;
|
||||||
|
|
||||||
for (int lpc = 0; pattern[lpc]; lpc++) {
|
for (int lpc = 0; pattern[lpc]; lpc++) {
|
||||||
if (in_escape) {
|
if (in_escape) {
|
||||||
@ -118,7 +128,7 @@ pcrepp::find_captures(const char* pattern)
|
|||||||
break;
|
break;
|
||||||
case ')': {
|
case ')': {
|
||||||
if (!cap_in_progress.empty()) {
|
if (!cap_in_progress.empty()) {
|
||||||
pcre_context::capture& cap = cap_in_progress.back();
|
auto& cap = cap_in_progress.back();
|
||||||
char first = '\0', second = '\0', third = '\0';
|
char first = '\0', second = '\0', third = '\0';
|
||||||
bool is_cap = false;
|
bool is_cap = false;
|
||||||
|
|
||||||
@ -342,6 +352,60 @@ pcrepp::jit_stack()
|
|||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
pcrepp::match_partial(pcre_input& pi) const
|
||||||
|
{
|
||||||
|
size_t length = pi.pi_length;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = pcre_exec(this->p_code,
|
||||||
|
this->p_code_extra.in(),
|
||||||
|
pi.get_string(),
|
||||||
|
length,
|
||||||
|
pi.pi_offset,
|
||||||
|
PCRE_PARTIAL,
|
||||||
|
nullptr,
|
||||||
|
0);
|
||||||
|
switch (rc) {
|
||||||
|
case 0:
|
||||||
|
case PCRE_ERROR_PARTIAL:
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
if (length > 0) {
|
||||||
|
length -= 1;
|
||||||
|
}
|
||||||
|
} while (length > 0);
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char*
|
||||||
|
pcrepp::name_for_capture(int index) const
|
||||||
|
{
|
||||||
|
for (pcre_named_capture::iterator iter = this->named_begin();
|
||||||
|
iter != this->named_end();
|
||||||
|
++iter)
|
||||||
|
{
|
||||||
|
if (iter->index() == index) {
|
||||||
|
return iter->pnc_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pcrepp::name_index(const char* name) const
|
||||||
|
{
|
||||||
|
int retval = pcre_get_stringnumber(this->p_code, name);
|
||||||
|
|
||||||
|
if (retval == PCRE_ERROR_NOSUBSTRING) {
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval - 1;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
# warning "pcrejit is not available, search performance will be degraded"
|
# warning "pcrejit is not available, search performance will be degraded"
|
||||||
|
|
||||||
@ -351,3 +415,11 @@ pcrepp::pcre_free_study(pcre_extra* extra)
|
|||||||
free(extra);
|
free(extra);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
pcre_context::capture_t::ltrim(const char* str)
|
||||||
|
{
|
||||||
|
while (this->c_begin < this->c_end && isspace(str[this->c_begin])) {
|
||||||
|
this->c_begin += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -72,88 +72,53 @@ class pcrepp;
|
|||||||
*/
|
*/
|
||||||
class pcre_context {
|
class pcre_context {
|
||||||
public:
|
public:
|
||||||
typedef struct capture {
|
struct capture_t {
|
||||||
capture(){
|
capture_t()
|
||||||
/* We don't initialize anything since it's a perf hit. */
|
{ /* We don't initialize anything since it's a perf hit. */
|
||||||
};
|
}
|
||||||
|
|
||||||
capture(int begin, int end) : c_begin(begin), c_end(end)
|
capture_t(int begin, int end) : c_begin(begin), c_end(end)
|
||||||
{
|
{
|
||||||
assert(begin <= end);
|
assert(begin <= end);
|
||||||
};
|
}
|
||||||
|
|
||||||
int c_begin;
|
int c_begin;
|
||||||
int c_end;
|
int c_end;
|
||||||
|
|
||||||
void ltrim(const char* str)
|
void ltrim(const char* str);
|
||||||
{
|
|
||||||
while (this->c_begin < this->c_end && isspace(str[this->c_begin])) {
|
|
||||||
this->c_begin += 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool contains(int pos) const
|
bool contains(int pos) const
|
||||||
{
|
{
|
||||||
return this->c_begin <= pos && pos < this->c_end;
|
return this->c_begin <= pos && pos < this->c_end;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_valid() const
|
bool is_valid() const { return this->c_begin != -1; }
|
||||||
{
|
|
||||||
return this->c_begin != -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
int length() const
|
int length() const { return this->c_end - this->c_begin; }
|
||||||
{
|
|
||||||
return this->c_end - this->c_begin;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const { return this->c_begin == this->c_end; }
|
||||||
{
|
};
|
||||||
return this->c_begin == this->c_end;
|
using iterator = capture_t*;
|
||||||
};
|
using const_iterator = const capture_t*;
|
||||||
} capture_t;
|
|
||||||
typedef capture_t* iterator;
|
|
||||||
typedef const capture_t* const_iterator;
|
|
||||||
|
|
||||||
/** @return The maximum number of strings this context can capture. */
|
/** @return The maximum number of strings this context can capture. */
|
||||||
int get_max_count() const
|
int get_max_count() const { return this->pc_max_count; }
|
||||||
{
|
|
||||||
return this->pc_max_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_count(int count)
|
void set_count(int count) { this->pc_count = count; }
|
||||||
{
|
|
||||||
this->pc_count = count;
|
|
||||||
};
|
|
||||||
|
|
||||||
int get_count() const
|
int get_count() const { return this->pc_count; }
|
||||||
{
|
|
||||||
return this->pc_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_pcrepp(const pcrepp* src)
|
void set_pcrepp(const pcrepp* src) { this->pc_pcre = src; }
|
||||||
{
|
|
||||||
this->pc_pcre = src;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return a capture_t that covers all of the text that was matched.
|
* @return a capture_t that covers all of the text that was matched.
|
||||||
*/
|
*/
|
||||||
capture_t* all() const
|
capture_t* all() const { return pc_captures; }
|
||||||
{
|
|
||||||
return pc_captures;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** @return An iterator to the first capture. */
|
/** @return An iterator to the first capture. */
|
||||||
iterator begin()
|
iterator begin() { return pc_captures + 1; }
|
||||||
{
|
|
||||||
return pc_captures + 1;
|
|
||||||
};
|
|
||||||
/** @return An iterator that refers to the end of the capture array. */
|
/** @return An iterator that refers to the end of the capture array. */
|
||||||
iterator end()
|
iterator end() { return pc_captures + pc_count; };
|
||||||
{
|
|
||||||
return pc_captures + pc_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
capture_t* operator[](int offset) const
|
capture_t* operator[](int offset) const
|
||||||
{
|
{
|
||||||
@ -161,39 +126,31 @@ public:
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return &this->pc_captures[offset + 1];
|
return &this->pc_captures[offset + 1];
|
||||||
};
|
}
|
||||||
|
|
||||||
capture_t* operator[](const char* name) const;
|
capture_t* operator[](const char* name) const;
|
||||||
|
|
||||||
capture_t* operator[](const std::string& name) const
|
capture_t* operator[](const std::string& name) const
|
||||||
{
|
{
|
||||||
return (*this)[name.c_str()];
|
return (*this)[name.c_str()];
|
||||||
};
|
}
|
||||||
|
|
||||||
capture_t* first_valid() const
|
capture_t* first_valid() const;
|
||||||
{
|
|
||||||
for (int lpc = 1; lpc < this->pc_count; lpc++) {
|
|
||||||
if (this->pc_captures[lpc].is_valid()) {
|
|
||||||
return &this->pc_captures[lpc];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
pcre_context(capture_t* captures, int max_count)
|
pcre_context(capture_t* captures, int max_count)
|
||||||
: pc_pcre(nullptr), pc_captures(captures), pc_max_count(max_count),
|
: pc_captures(captures), pc_max_count(max_count)
|
||||||
pc_count(0){};
|
{
|
||||||
|
}
|
||||||
|
|
||||||
const pcrepp* pc_pcre;
|
const pcrepp* pc_pcre{nullptr};
|
||||||
capture_t* pc_captures;
|
capture_t* pc_captures;
|
||||||
int pc_max_count;
|
int pc_max_count;
|
||||||
int pc_count;
|
int pc_count{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct capture_if_not {
|
struct capture_if_not {
|
||||||
capture_if_not(int begin) : cin_begin(begin){};
|
capture_if_not(int begin) : cin_begin(begin) {}
|
||||||
|
|
||||||
bool operator()(const pcre_context::capture_t& cap) const
|
bool operator()(const pcre_context::capture_t& cap) const
|
||||||
{
|
{
|
||||||
@ -203,19 +160,6 @@ struct capture_if_not {
|
|||||||
int cin_begin;
|
int cin_begin;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline pcre_context::iterator
|
|
||||||
skip_invalid_captures(pcre_context::iterator iter,
|
|
||||||
pcre_context::iterator pc_end)
|
|
||||||
{
|
|
||||||
for (; iter != pc_end; ++iter) {
|
|
||||||
if (iter->c_begin == -1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A pcre_context that allocates storage for the capture array within the object
|
* A pcre_context that allocates storage for the capture array within the object
|
||||||
* itself.
|
* itself.
|
||||||
@ -241,7 +185,7 @@ public:
|
|||||||
if (this->pi_length == (size_t) -1) {
|
if (this->pi_length == (size_t) -1) {
|
||||||
this->pi_length = strlen(str);
|
this->pi_length = strlen(str);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
pcre_input(const string_fragment& s)
|
pcre_input(const string_fragment& s)
|
||||||
: pi_offset(0), pi_next_offset(0), pi_length(s.length()),
|
: pi_offset(0), pi_next_offset(0), pi_length(s.length()),
|
||||||
@ -259,20 +203,17 @@ public:
|
|||||||
|
|
||||||
pcre_input(const std::string&&, size_t off = 0) = delete;
|
pcre_input(const std::string&&, size_t off = 0) = delete;
|
||||||
|
|
||||||
const char* get_string() const
|
const char* get_string() const { return this->pi_string; }
|
||||||
{
|
|
||||||
return this->pi_string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* get_substr_start(pcre_context::const_iterator iter) const
|
const char* get_substr_start(pcre_context::const_iterator iter) const
|
||||||
{
|
{
|
||||||
return &this->pi_string[iter->c_begin];
|
return &this->pi_string[iter->c_begin];
|
||||||
};
|
}
|
||||||
|
|
||||||
size_t get_substr_len(pcre_context::const_iterator iter) const
|
size_t get_substr_len(pcre_context::const_iterator iter) const
|
||||||
{
|
{
|
||||||
return iter->length();
|
return iter->length();
|
||||||
};
|
}
|
||||||
|
|
||||||
std::string get_substr(pcre_context::const_iterator iter) const
|
std::string get_substr(pcre_context::const_iterator iter) const
|
||||||
{
|
{
|
||||||
@ -280,13 +221,13 @@ public:
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return std::string(&this->pi_string[iter->c_begin], iter->length());
|
return std::string(&this->pi_string[iter->c_begin], iter->length());
|
||||||
};
|
}
|
||||||
|
|
||||||
intern_string_t get_substr_i(pcre_context::const_iterator iter) const
|
intern_string_t get_substr_i(pcre_context::const_iterator iter) const
|
||||||
{
|
{
|
||||||
return intern_string::lookup(&this->pi_string[iter->c_begin],
|
return intern_string::lookup(&this->pi_string[iter->c_begin],
|
||||||
iter->length());
|
iter->length());
|
||||||
};
|
}
|
||||||
|
|
||||||
nonstd::optional<std::string> get_substr_opt(
|
nonstd::optional<std::string> get_substr_opt(
|
||||||
pcre_context::const_iterator iter) const
|
pcre_context::const_iterator iter) const
|
||||||
@ -302,12 +243,9 @@ public:
|
|||||||
{
|
{
|
||||||
memcpy(dst, &this->pi_string[iter->c_begin], iter->length());
|
memcpy(dst, &this->pi_string[iter->c_begin], iter->length());
|
||||||
dst[iter->length()] = '\0';
|
dst[iter->length()] = '\0';
|
||||||
};
|
}
|
||||||
|
|
||||||
void reset_next_offset()
|
void reset_next_offset() { this->pi_next_offset = this->pi_offset; }
|
||||||
{
|
|
||||||
this->pi_next_offset = this->pi_offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
void reset(const char* str, size_t off = 0, size_t len = -1)
|
void reset(const char* str, size_t off = 0, size_t len = -1)
|
||||||
{
|
{
|
||||||
@ -324,7 +262,7 @@ public:
|
|||||||
void reset(const std::string& str, size_t off = 0)
|
void reset(const std::string& str, size_t off = 0)
|
||||||
{
|
{
|
||||||
this->reset(str.c_str(), off, str.length());
|
this->reset(str.c_str(), off, str.length());
|
||||||
};
|
}
|
||||||
|
|
||||||
size_t pi_offset;
|
size_t pi_offset;
|
||||||
size_t pi_next_offset;
|
size_t pi_next_offset;
|
||||||
@ -345,17 +283,17 @@ struct pcre_named_capture {
|
|||||||
const pcre_named_capture& operator*() const
|
const pcre_named_capture& operator*() const
|
||||||
{
|
{
|
||||||
return *this->i_named_capture;
|
return *this->i_named_capture;
|
||||||
};
|
}
|
||||||
|
|
||||||
const pcre_named_capture* operator->() const
|
const pcre_named_capture* operator->() const
|
||||||
{
|
{
|
||||||
return this->i_named_capture;
|
return this->i_named_capture;
|
||||||
};
|
}
|
||||||
|
|
||||||
bool operator!=(const iterator& rhs) const
|
bool operator!=(const iterator& rhs) const
|
||||||
{
|
{
|
||||||
return this->i_named_capture != rhs.i_named_capture;
|
return this->i_named_capture != rhs.i_named_capture;
|
||||||
};
|
}
|
||||||
|
|
||||||
iterator& operator++()
|
iterator& operator++()
|
||||||
{
|
{
|
||||||
@ -364,7 +302,7 @@ struct pcre_named_capture {
|
|||||||
ptr += this->i_name_len;
|
ptr += this->i_name_len;
|
||||||
this->i_named_capture = (pcre_named_capture*) ptr;
|
this->i_named_capture = (pcre_named_capture*) ptr;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pcre_named_capture* i_named_capture;
|
pcre_named_capture* i_named_capture;
|
||||||
@ -374,7 +312,7 @@ struct pcre_named_capture {
|
|||||||
int index() const
|
int index() const
|
||||||
{
|
{
|
||||||
return (this->pnc_index_msb << 8 | this->pnc_index_lsb) - 1;
|
return (this->pnc_index_msb << 8 | this->pnc_index_lsb) - 1;
|
||||||
};
|
}
|
||||||
|
|
||||||
char pnc_index_msb;
|
char pnc_index_msb;
|
||||||
char pnc_index_lsb;
|
char pnc_index_lsb;
|
||||||
@ -389,13 +327,13 @@ struct pcre_extractor {
|
|||||||
intern_string_t get_substr_i(T name) const
|
intern_string_t get_substr_i(T name) const
|
||||||
{
|
{
|
||||||
return this->pe_input.get_substr_i(this->pe_context[name]);
|
return this->pe_input.get_substr_i(this->pe_context[name]);
|
||||||
};
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
std::string get_substr(T name) const
|
std::string get_substr(T name) const
|
||||||
{
|
{
|
||||||
return this->pe_input.get_substr(this->pe_context[name]);
|
return this->pe_input.get_substr(this->pe_context[name]);
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class pcrepp {
|
class pcrepp {
|
||||||
@ -422,8 +360,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct compile_error {
|
struct compile_error {
|
||||||
const char* ce_msg;
|
const char* ce_msg{nullptr};
|
||||||
int ce_offset;
|
int ce_offset{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
static Result<pcrepp, compile_error> from_str(std::string pattern,
|
static Result<pcrepp, compile_error> from_str(std::string pattern,
|
||||||
@ -433,7 +371,7 @@ public:
|
|||||||
{
|
{
|
||||||
pcre_refcount(this->p_code, 1);
|
pcre_refcount(this->p_code, 1);
|
||||||
this->study();
|
this->study();
|
||||||
};
|
}
|
||||||
|
|
||||||
pcrepp(std::string pattern, pcre* code)
|
pcrepp(std::string pattern, pcre* code)
|
||||||
: p_code(code), p_pattern(std::move(pattern)),
|
: p_code(code), p_pattern(std::move(pattern)),
|
||||||
@ -442,7 +380,7 @@ public:
|
|||||||
pcre_refcount(this->p_code, 1);
|
pcre_refcount(this->p_code, 1);
|
||||||
this->study();
|
this->study();
|
||||||
this->find_captures(this->p_pattern.c_str());
|
this->find_captures(this->p_pattern.c_str());
|
||||||
};
|
}
|
||||||
|
|
||||||
explicit pcrepp(const char* pattern, int options = 0)
|
explicit pcrepp(const char* pattern, int options = 0)
|
||||||
: p_pattern(pattern), p_code_extra(pcre_free_study)
|
: p_pattern(pattern), p_code_extra(pcre_free_study)
|
||||||
@ -460,7 +398,7 @@ public:
|
|||||||
pcre_refcount(this->p_code, 1);
|
pcre_refcount(this->p_code, 1);
|
||||||
this->study();
|
this->study();
|
||||||
this->find_captures(pattern);
|
this->find_captures(pattern);
|
||||||
};
|
}
|
||||||
|
|
||||||
explicit pcrepp(const std::string& pattern, int options = 0)
|
explicit pcrepp(const std::string& pattern, int options = 0)
|
||||||
: p_pattern(pattern), p_code_extra(pcre_free_study)
|
: p_pattern(pattern), p_code_extra(pcre_free_study)
|
||||||
@ -478,7 +416,7 @@ public:
|
|||||||
pcre_refcount(this->p_code, 1);
|
pcre_refcount(this->p_code, 1);
|
||||||
this->study();
|
this->study();
|
||||||
this->find_captures(pattern.c_str());
|
this->find_captures(pattern.c_str());
|
||||||
};
|
}
|
||||||
|
|
||||||
pcrepp() {}
|
pcrepp() {}
|
||||||
|
|
||||||
@ -488,7 +426,7 @@ public:
|
|||||||
{
|
{
|
||||||
pcre_refcount(this->p_code, 1);
|
pcre_refcount(this->p_code, 1);
|
||||||
this->study();
|
this->study();
|
||||||
};
|
}
|
||||||
|
|
||||||
pcrepp(pcrepp&& other)
|
pcrepp(pcrepp&& other)
|
||||||
: p_code(other.p_code), p_pattern(std::move(other.p_pattern)),
|
: p_code(other.p_code), p_pattern(std::move(other.p_pattern)),
|
||||||
@ -501,10 +439,7 @@ public:
|
|||||||
this->p_code_extra = std::move(other.p_code_extra);
|
this->p_code_extra = std::move(other.p_code_extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~pcrepp()
|
virtual ~pcrepp() { this->clear(); }
|
||||||
{
|
|
||||||
this->clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
pcrepp& operator=(pcrepp&& other) noexcept
|
pcrepp& operator=(pcrepp&& other) noexcept
|
||||||
{
|
{
|
||||||
@ -555,7 +490,7 @@ public:
|
|||||||
pcre_named_capture::iterator named_begin() const
|
pcre_named_capture::iterator named_begin() const
|
||||||
{
|
{
|
||||||
return {this->p_named_entries, static_cast<size_t>(this->p_name_len)};
|
return {this->p_named_entries, static_cast<size_t>(this->p_name_len)};
|
||||||
};
|
}
|
||||||
|
|
||||||
pcre_named_capture::iterator named_end() const
|
pcre_named_capture::iterator named_end() const
|
||||||
{
|
{
|
||||||
@ -564,56 +499,33 @@ public:
|
|||||||
ptr += this->p_named_count * this->p_name_len;
|
ptr += this->p_named_count * this->p_name_len;
|
||||||
return {(pcre_named_capture*) ptr,
|
return {(pcre_named_capture*) ptr,
|
||||||
static_cast<size_t>(this->p_name_len)};
|
static_cast<size_t>(this->p_name_len)};
|
||||||
};
|
}
|
||||||
|
|
||||||
const std::vector<pcre_context::capture>& captures() const
|
const std::vector<pcre_context::capture_t>& captures() const
|
||||||
{
|
{
|
||||||
return this->p_captures;
|
return this->p_captures;
|
||||||
};
|
}
|
||||||
|
|
||||||
std::vector<pcre_context::capture>::const_iterator cap_begin() const
|
std::vector<pcre_context::capture_t>::const_iterator cap_begin() const
|
||||||
{
|
{
|
||||||
return this->p_captures.begin();
|
return this->p_captures.begin();
|
||||||
};
|
}
|
||||||
|
|
||||||
std::vector<pcre_context::capture>::const_iterator cap_end() const
|
std::vector<pcre_context::capture_t>::const_iterator cap_end() const
|
||||||
{
|
{
|
||||||
return this->p_captures.end();
|
return this->p_captures.end();
|
||||||
};
|
}
|
||||||
|
|
||||||
int name_index(const std::string& name) const
|
int name_index(const std::string& name) const
|
||||||
{
|
{
|
||||||
return this->name_index(name.c_str());
|
return this->name_index(name.c_str());
|
||||||
};
|
}
|
||||||
|
|
||||||
int name_index(const char* name) const
|
int name_index(const char* name) const;
|
||||||
{
|
|
||||||
int retval = pcre_get_stringnumber(this->p_code, name);
|
|
||||||
|
|
||||||
if (retval == PCRE_ERROR_NOSUBSTRING) {
|
const char* name_for_capture(int index) const;
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval - 1;
|
int get_capture_count() const { return this->p_capture_count; }
|
||||||
};
|
|
||||||
|
|
||||||
const char* name_for_capture(int index) const
|
|
||||||
{
|
|
||||||
for (pcre_named_capture::iterator iter = this->named_begin();
|
|
||||||
iter != this->named_end();
|
|
||||||
++iter)
|
|
||||||
{
|
|
||||||
if (iter->index() == index) {
|
|
||||||
return iter->pnc_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
int get_capture_count() const
|
|
||||||
{
|
|
||||||
return this->p_capture_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool match(pcre_context& pc, pcre_input& pi, int options = 0) const;
|
bool match(pcre_context& pc, pcre_input& pi, int options = 0) const;
|
||||||
|
|
||||||
@ -633,30 +545,7 @@ public:
|
|||||||
|
|
||||||
std::string replace(const char* str, const char* repl) const;
|
std::string replace(const char* str, const char* repl) const;
|
||||||
|
|
||||||
size_t match_partial(pcre_input& pi) const
|
size_t match_partial(pcre_input& pi) const;
|
||||||
{
|
|
||||||
size_t length = pi.pi_length;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
do {
|
|
||||||
rc = pcre_exec(this->p_code,
|
|
||||||
this->p_code_extra.in(),
|
|
||||||
pi.get_string(),
|
|
||||||
length,
|
|
||||||
pi.pi_offset,
|
|
||||||
PCRE_PARTIAL,
|
|
||||||
nullptr,
|
|
||||||
0);
|
|
||||||
switch (rc) {
|
|
||||||
case 0:
|
|
||||||
case PCRE_ERROR_PARTIAL:
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
length -= 1;
|
|
||||||
} while (length > 0);
|
|
||||||
|
|
||||||
return length;
|
|
||||||
};
|
|
||||||
|
|
||||||
// #undef PCRE_STUDY_JIT_COMPILE
|
// #undef PCRE_STUDY_JIT_COMPILE
|
||||||
#ifdef PCRE_STUDY_JIT_COMPILE
|
#ifdef PCRE_STUDY_JIT_COMPILE
|
||||||
@ -678,7 +567,16 @@ public:
|
|||||||
int p_name_len{0};
|
int p_name_len{0};
|
||||||
unsigned long p_options{0};
|
unsigned long p_options{0};
|
||||||
pcre_named_capture* p_named_entries{nullptr};
|
pcre_named_capture* p_named_entries{nullptr};
|
||||||
std::vector<pcre_context::capture> p_captures;
|
std::vector<pcre_context::capture_t> p_captures;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<int options = 0>
|
||||||
|
class pcrepp_with_options : public pcrepp {
|
||||||
|
public:
|
||||||
|
template<typename... Args>
|
||||||
|
pcrepp_with_options(Args... args) : pcrepp(args..., options)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -98,18 +98,20 @@ main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
pcre_context::capture cap(1, 4);
|
pcre_context::capture_t cap(1, 4);
|
||||||
pcre_input pi("\0foo", 0, 4);
|
pcre_input pi("\0foo", 0, 4);
|
||||||
|
|
||||||
assert("foo" == pi.get_substr(&cap));
|
assert("foo" == pi.get_substr(&cap));
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* empty_cap_regexes[] = {"foo (?:bar)",
|
const char* empty_cap_regexes[] = {
|
||||||
"foo [(]",
|
"foo (?:bar)",
|
||||||
"foo \\Q(bar)\\E",
|
"foo [(]",
|
||||||
"(?i)",
|
"foo \\Q(bar)\\E",
|
||||||
|
"(?i)",
|
||||||
|
|
||||||
NULL};
|
nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
for (int lpc = 0; empty_cap_regexes[lpc]; lpc++) {
|
for (int lpc = 0; empty_cap_regexes[lpc]; lpc++) {
|
||||||
pcrepp re(empty_cap_regexes[lpc]);
|
pcrepp re(empty_cap_regexes[lpc]);
|
||||||
|
@ -48,7 +48,7 @@ class piper_proc {
|
|||||||
public:
|
public:
|
||||||
class error : public std::exception {
|
class error : public std::exception {
|
||||||
public:
|
public:
|
||||||
error(int err) : e_err(err){};
|
error(int err) : e_err(err) {}
|
||||||
|
|
||||||
int e_err;
|
int e_err;
|
||||||
};
|
};
|
||||||
@ -72,15 +72,9 @@ public:
|
|||||||
virtual ~piper_proc();
|
virtual ~piper_proc();
|
||||||
|
|
||||||
/** @return The file descriptor for the temporary file. */
|
/** @return The file descriptor for the temporary file. */
|
||||||
auto_fd get_fd()
|
auto_fd get_fd() { return this->pp_fd.dup(); }
|
||||||
{
|
|
||||||
return this->pp_fd.dup();
|
|
||||||
};
|
|
||||||
|
|
||||||
pid_t get_child_pid() const
|
pid_t get_child_pid() const { return this->pp_child; }
|
||||||
{
|
|
||||||
return this->pp_child;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** A file descriptor that refers to the temporary file. */
|
/** A file descriptor that refers to the temporary file. */
|
||||||
|
@ -121,12 +121,12 @@ void
|
|||||||
rl_set_help()
|
rl_set_help()
|
||||||
{
|
{
|
||||||
switch (lnav_data.ld_mode) {
|
switch (lnav_data.ld_mode) {
|
||||||
case LNM_SEARCH: {
|
case ln_mode_t::SEARCH: {
|
||||||
lnav_data.ld_doc_source.replace_with(RE_HELP);
|
lnav_data.ld_doc_source.replace_with(RE_HELP);
|
||||||
lnav_data.ld_example_source.replace_with(RE_EXAMPLE);
|
lnav_data.ld_example_source.replace_with(RE_EXAMPLE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LNM_SQL: {
|
case ln_mode_t::SQL: {
|
||||||
textview_curses& log_view = lnav_data.ld_views[LNV_LOG];
|
textview_curses& log_view = lnav_data.ld_views[LNV_LOG];
|
||||||
auto* lss = (logfile_sub_source*) log_view.get_sub_source();
|
auto* lss = (logfile_sub_source*) log_view.get_sub_source();
|
||||||
attr_line_t example_al;
|
attr_line_t example_al;
|
||||||
@ -250,7 +250,7 @@ rl_change(readline_curses* rc)
|
|||||||
.clear();
|
.clear();
|
||||||
|
|
||||||
switch (lnav_data.ld_mode) {
|
switch (lnav_data.ld_mode) {
|
||||||
case LNM_COMMAND: {
|
case ln_mode_t::COMMAND: {
|
||||||
static std::string last_command;
|
static std::string last_command;
|
||||||
static int generation = 0;
|
static int generation = 0;
|
||||||
|
|
||||||
@ -302,7 +302,8 @@ rl_change(readline_curses* rc)
|
|||||||
lnav_data.ld_bottom_source.set_prompt(LNAV_CMD_PROMPT);
|
lnav_data.ld_bottom_source.set_prompt(LNAV_CMD_PROMPT);
|
||||||
lnav_data.ld_bottom_source.grep_error("");
|
lnav_data.ld_bottom_source.grep_error("");
|
||||||
} else if (args[0] == "config" && args.size() > 1) {
|
} else if (args[0] == "config" && args.size() > 1) {
|
||||||
yajlpp_parse_context ypc("input", &lnav_config_handlers);
|
static const auto INPUT_SRC = intern_string::lookup("input");
|
||||||
|
yajlpp_parse_context ypc(INPUT_SRC, &lnav_config_handlers);
|
||||||
|
|
||||||
ypc.set_path(args[1]).with_obj(lnav_config);
|
ypc.set_path(args[1]).with_obj(lnav_config);
|
||||||
ypc.update_callbacks();
|
ypc.update_callbacks();
|
||||||
@ -363,7 +364,7 @@ rl_change(readline_curses* rc)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LNM_EXEC: {
|
case ln_mode_t::EXEC: {
|
||||||
const auto line = rc->get_line_buffer();
|
const auto line = rc->get_line_buffer();
|
||||||
size_t name_end = line.find(' ');
|
size_t name_end = line.find(' ');
|
||||||
const auto script_name = line.substr(0, name_end);
|
const auto script_name = line.substr(0, name_end);
|
||||||
@ -406,18 +407,18 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
|
|||||||
lnav_data.ld_user_message_source.clear();
|
lnav_data.ld_user_message_source.clear();
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case LNM_SEARCH:
|
case ln_mode_t::SEARCH:
|
||||||
case LNM_SEARCH_FILTERS:
|
case ln_mode_t::SEARCH_FILTERS:
|
||||||
case LNM_SEARCH_FILES:
|
case ln_mode_t::SEARCH_FILES:
|
||||||
name = "$search";
|
name = "$search";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LNM_CAPTURE:
|
case ln_mode_t::CAPTURE:
|
||||||
require(0);
|
require(0);
|
||||||
name = "$capture";
|
name = "$capture";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LNM_COMMAND: {
|
case ln_mode_t::COMMAND: {
|
||||||
lnav_data.ld_exec_context.ec_dry_run = true;
|
lnav_data.ld_exec_context.ec_dry_run = true;
|
||||||
|
|
||||||
lnav_data.ld_preview_generation += 1;
|
lnav_data.ld_preview_generation += 1;
|
||||||
@ -425,8 +426,8 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
|
|||||||
.set_cylon(false)
|
.set_cylon(false)
|
||||||
.clear();
|
.clear();
|
||||||
lnav_data.ld_preview_source.clear();
|
lnav_data.ld_preview_source.clear();
|
||||||
auto result
|
auto result = execute_command(lnav_data.ld_exec_context,
|
||||||
= execute_command(lnav_data.ld_exec_context, rc->get_value());
|
rc->get_value().get_string());
|
||||||
|
|
||||||
if (result.isOk()) {
|
if (result.isOk()) {
|
||||||
auto msg = result.unwrap();
|
auto msg = result.unwrap();
|
||||||
@ -450,8 +451,8 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LNM_SQL: {
|
case ln_mode_t::SQL: {
|
||||||
term_val = trim(rc->get_value() + ";");
|
term_val = trim(rc->get_value().get_string() + ";");
|
||||||
|
|
||||||
if (!term_val.empty() && term_val[0] == '.') {
|
if (!term_val.empty() && term_val[0] == '.') {
|
||||||
lnav_data.ld_bottom_source.grep_error("");
|
lnav_data.ld_bottom_source.grep_error("");
|
||||||
@ -462,11 +463,12 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
|
|||||||
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
|
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
|
||||||
int retcode;
|
int retcode;
|
||||||
|
|
||||||
retcode = sqlite3_prepare_v2(lnav_data.ld_db,
|
retcode
|
||||||
rc->get_value().c_str(),
|
= sqlite3_prepare_v2(lnav_data.ld_db,
|
||||||
-1,
|
rc->get_value().get_string().c_str(),
|
||||||
stmt.out(),
|
-1,
|
||||||
nullptr);
|
stmt.out(),
|
||||||
|
nullptr);
|
||||||
if (retcode != SQLITE_OK) {
|
if (retcode != SQLITE_OK) {
|
||||||
const char* errmsg = sqlite3_errmsg(lnav_data.ld_db);
|
const char* errmsg = sqlite3_errmsg(lnav_data.ld_db);
|
||||||
|
|
||||||
@ -484,18 +486,18 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LNM_PAGING:
|
case ln_mode_t::PAGING:
|
||||||
case LNM_FILTER:
|
case ln_mode_t::FILTER:
|
||||||
case LNM_FILES:
|
case ln_mode_t::FILES:
|
||||||
case LNM_EXEC:
|
case ln_mode_t::EXEC:
|
||||||
case LNM_USER:
|
case ln_mode_t::USER:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!complete) {
|
if (!complete) {
|
||||||
tc->set_top(lnav_data.ld_search_start_line);
|
tc->set_top(lnav_data.ld_search_start_line);
|
||||||
}
|
}
|
||||||
tc->execute_search(rc->get_value());
|
tc->execute_search(rc->get_value().get_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -529,18 +531,18 @@ lnav_rl_abort(readline_curses* rc)
|
|||||||
|
|
||||||
lnav_data.ld_bottom_source.grep_error("");
|
lnav_data.ld_bottom_source.grep_error("");
|
||||||
switch (lnav_data.ld_mode) {
|
switch (lnav_data.ld_mode) {
|
||||||
case LNM_SEARCH:
|
case ln_mode_t::SEARCH:
|
||||||
tc->set_top(lnav_data.ld_search_start_line);
|
tc->set_top(lnav_data.ld_search_start_line);
|
||||||
tc->revert_search();
|
tc->revert_search();
|
||||||
break;
|
break;
|
||||||
case LNM_SQL:
|
case ln_mode_t::SQL:
|
||||||
tc->reload_data();
|
tc->reload_data();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
lnav_data.ld_rl_view->set_value("");
|
lnav_data.ld_rl_view->set_value("");
|
||||||
lnav_data.ld_mode = LNM_PAGING;
|
lnav_data.ld_mode = ln_mode_t::PAGING;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -561,14 +563,14 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
|||||||
tc->get_highlights().erase({highlight_source_t::PREVIEW, "bodypreview"});
|
tc->get_highlights().erase({highlight_source_t::PREVIEW, "bodypreview"});
|
||||||
lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
|
lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
|
||||||
|
|
||||||
auto new_mode = LNM_PAGING;
|
auto new_mode = ln_mode_t::PAGING;
|
||||||
|
|
||||||
switch (lnav_data.ld_mode) {
|
switch (lnav_data.ld_mode) {
|
||||||
case LNM_SEARCH_FILTERS:
|
case ln_mode_t::SEARCH_FILTERS:
|
||||||
new_mode = LNM_FILTER;
|
new_mode = ln_mode_t::FILTER;
|
||||||
break;
|
break;
|
||||||
case LNM_SEARCH_FILES:
|
case ln_mode_t::SEARCH_FILES:
|
||||||
new_mode = LNM_FILES;
|
new_mode = ln_mode_t::FILES;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -576,17 +578,17 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
|||||||
|
|
||||||
auto old_mode = std::exchange(lnav_data.ld_mode, new_mode);
|
auto old_mode = std::exchange(lnav_data.ld_mode, new_mode);
|
||||||
switch (old_mode) {
|
switch (old_mode) {
|
||||||
case LNM_PAGING:
|
case ln_mode_t::PAGING:
|
||||||
case LNM_FILTER:
|
case ln_mode_t::FILTER:
|
||||||
case LNM_FILES:
|
case ln_mode_t::FILES:
|
||||||
require(0);
|
require(0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LNM_COMMAND: {
|
case ln_mode_t::COMMAND: {
|
||||||
rc->set_alt_value("");
|
rc->set_alt_value("");
|
||||||
ec.ec_source.top().s_content
|
ec.ec_source.top().s_content
|
||||||
= fmt::format(FMT_STRING(":{}"), rc->get_value());
|
= fmt::format(FMT_STRING(":{}"), rc->get_value().get_string());
|
||||||
auto exec_res = execute_command(ec, rc->get_value());
|
auto exec_res = execute_command(ec, rc->get_value().get_string());
|
||||||
if (exec_res.isOk()) {
|
if (exec_res.isOk()) {
|
||||||
rc->set_value(exec_res.unwrap());
|
rc->set_value(exec_res.unwrap());
|
||||||
} else {
|
} else {
|
||||||
@ -607,16 +609,16 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LNM_USER:
|
case ln_mode_t::USER:
|
||||||
rc->set_alt_value("");
|
rc->set_alt_value("");
|
||||||
ec.ec_local_vars.top()["value"] = rc->get_value();
|
ec.ec_local_vars.top()["value"] = rc->get_value().get_string();
|
||||||
rc->set_value("");
|
rc->set_value("");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LNM_SEARCH:
|
case ln_mode_t::SEARCH:
|
||||||
case LNM_SEARCH_FILTERS:
|
case ln_mode_t::SEARCH_FILTERS:
|
||||||
case LNM_SEARCH_FILES:
|
case ln_mode_t::SEARCH_FILES:
|
||||||
case LNM_CAPTURE:
|
case ln_mode_t::CAPTURE:
|
||||||
rl_search_internal(rc, old_mode, true);
|
rl_search_internal(rc, old_mode, true);
|
||||||
if (!rc->get_value().empty()) {
|
if (!rc->get_value().empty()) {
|
||||||
auto_mem<FILE> pfile(pclose);
|
auto_mem<FILE> pfile(pclose);
|
||||||
@ -627,7 +629,8 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
|||||||
|
|
||||||
pfile = sysclip::open(sysclip::type_t::FIND);
|
pfile = sysclip::open(sysclip::type_t::FIND);
|
||||||
if (pfile.in() != nullptr) {
|
if (pfile.in() != nullptr) {
|
||||||
fprintf(pfile, "%s", rc->get_value().c_str());
|
fmt::print(
|
||||||
|
pfile, FMT_STRING("{}"), rc->get_value().get_string());
|
||||||
}
|
}
|
||||||
if (vl != -1_vl) {
|
if (vl != -1_vl) {
|
||||||
tc->set_top(vl);
|
tc->set_top(vl);
|
||||||
@ -660,15 +663,17 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
rc->set_value("search: " + rc->get_value());
|
rc->set_attr_value(
|
||||||
|
attr_line_t("search: ").append(rc->get_value()));
|
||||||
rc->set_alt_value(HELP_MSG_2(
|
rc->set_alt_value(HELP_MSG_2(
|
||||||
n, N, "to move forward/backward through search results"));
|
n, N, "to move forward/backward through search results"));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LNM_SQL: {
|
case ln_mode_t::SQL: {
|
||||||
ec.ec_source.top().s_content = rc->get_value();
|
ec.ec_source.top().s_content = rc->get_value();
|
||||||
auto result = execute_sql(ec, rc->get_value(), alt_msg);
|
auto result
|
||||||
|
= execute_sql(ec, rc->get_value().get_string(), alt_msg);
|
||||||
db_label_source& dls = lnav_data.ld_db_row_source;
|
db_label_source& dls = lnav_data.ld_db_row_source;
|
||||||
std::string prompt;
|
std::string prompt;
|
||||||
|
|
||||||
@ -696,7 +701,7 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LNM_EXEC: {
|
case ln_mode_t::EXEC: {
|
||||||
auto_mem<FILE> tmpout(fclose);
|
auto_mem<FILE> tmpout(fclose);
|
||||||
|
|
||||||
tmpout = std::tmpfile();
|
tmpout = std::tmpfile();
|
||||||
@ -715,7 +720,7 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
|||||||
exec_context::output_guard og(
|
exec_context::output_guard og(
|
||||||
ec, "tmp", std::make_pair(tmpout.release(), fclose));
|
ec, "tmp", std::make_pair(tmpout.release(), fclose));
|
||||||
|
|
||||||
auto result = execute_file(ec, path_and_args)
|
auto result = execute_file(ec, path_and_args.get_string())
|
||||||
.map(ok_prefix)
|
.map(ok_prefix)
|
||||||
.orElse(err_to_ok)
|
.orElse(err_to_ok)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -736,7 +741,7 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
|||||||
snprintf(desc,
|
snprintf(desc,
|
||||||
sizeof(desc),
|
sizeof(desc),
|
||||||
"Output of %s (%s)",
|
"Output of %s (%s)",
|
||||||
path_and_args.c_str(),
|
path_and_args.get_string().c_str(),
|
||||||
timestamp);
|
timestamp);
|
||||||
lnav_data.ld_active_files.fc_file_names[desc]
|
lnav_data.ld_active_files.fc_file_names[desc]
|
||||||
.with_fd(std::move(fd_copy))
|
.with_fd(std::move(fd_copy))
|
||||||
@ -839,3 +844,22 @@ rl_completion_request(readline_curses* rc)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rl_focus(readline_curses* rc)
|
||||||
|
{
|
||||||
|
auto fos = (field_overlay_source*) lnav_data.ld_views[LNV_LOG]
|
||||||
|
.get_overlay_source();
|
||||||
|
|
||||||
|
fos->fos_contexts.emplace("", false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rl_blur(readline_curses* rc)
|
||||||
|
{
|
||||||
|
auto fos = (field_overlay_source*) lnav_data.ld_views[LNV_LOG]
|
||||||
|
.get_overlay_source();
|
||||||
|
|
||||||
|
fos->fos_contexts.pop();
|
||||||
|
lnav_data.ld_preview_generation += 1;
|
||||||
|
}
|
||||||
|
@ -39,6 +39,8 @@ void rl_alt_callback(readline_curses* rc);
|
|||||||
void rl_display_matches(readline_curses* rc);
|
void rl_display_matches(readline_curses* rc);
|
||||||
void rl_display_next(readline_curses* rc);
|
void rl_display_next(readline_curses* rc);
|
||||||
void rl_completion_request(readline_curses* rc);
|
void rl_completion_request(readline_curses* rc);
|
||||||
|
void rl_focus(readline_curses* rc);
|
||||||
|
void rl_blur(readline_curses* rc);
|
||||||
|
|
||||||
extern const char* RE_HELP;
|
extern const char* RE_HELP;
|
||||||
extern const char* RE_EXAMPLE;
|
extern const char* RE_EXAMPLE;
|
||||||
|
@ -69,10 +69,13 @@ public:
|
|||||||
help_text help = {},
|
help_text help = {},
|
||||||
prompt_func_t prompt = nullptr) noexcept
|
prompt_func_t prompt = nullptr) noexcept
|
||||||
: c_name(name), c_func(func), c_help(std::move(help)),
|
: c_name(name), c_func(func), c_help(std::move(help)),
|
||||||
c_prompt(prompt){};
|
c_prompt(prompt)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
_command_t(command_func_t func) noexcept
|
_command_t(command_func_t func) noexcept : c_name("anon"), c_func(func)
|
||||||
: c_name("anon"), c_func(func){};
|
{
|
||||||
|
}
|
||||||
} command_t;
|
} command_t;
|
||||||
typedef std::map<std::string, command_t*> command_map_t;
|
typedef std::map<std::string, command_t*> command_map_t;
|
||||||
|
|
||||||
@ -80,10 +83,7 @@ public:
|
|||||||
command_map_t* commands = nullptr,
|
command_map_t* commands = nullptr,
|
||||||
bool case_sensitive = true);
|
bool case_sensitive = true);
|
||||||
|
|
||||||
const std::string& get_name() const
|
const std::string& get_name() const { return this->rc_name; }
|
||||||
{
|
|
||||||
return this->rc_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
void load();
|
void load();
|
||||||
|
|
||||||
@ -92,29 +92,26 @@ public:
|
|||||||
void add_possibility(const std::string& type, const std::string& value)
|
void add_possibility(const std::string& type, const std::string& value)
|
||||||
{
|
{
|
||||||
this->rc_possibilities[type].insert(value);
|
this->rc_possibilities[type].insert(value);
|
||||||
};
|
}
|
||||||
|
|
||||||
void rem_possibility(const std::string& type, const std::string& value)
|
void rem_possibility(const std::string& type, const std::string& value)
|
||||||
{
|
{
|
||||||
this->rc_possibilities[type].erase(value);
|
this->rc_possibilities[type].erase(value);
|
||||||
};
|
}
|
||||||
|
|
||||||
void clear_possibilities(const std::string& type)
|
void clear_possibilities(const std::string& type)
|
||||||
{
|
{
|
||||||
this->rc_possibilities[type].clear();
|
this->rc_possibilities[type].clear();
|
||||||
};
|
}
|
||||||
|
|
||||||
bool is_case_sensitive() const
|
bool is_case_sensitive() const { return this->rc_case_sensitive; }
|
||||||
{
|
|
||||||
return this->rc_case_sensitive;
|
|
||||||
};
|
|
||||||
|
|
||||||
readline_context& set_append_character(int ch)
|
readline_context& set_append_character(int ch)
|
||||||
{
|
{
|
||||||
this->rc_append_character = ch;
|
this->rc_append_character = ch;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
int get_append_character() const
|
int get_append_character() const
|
||||||
{
|
{
|
||||||
@ -125,26 +122,26 @@ public:
|
|||||||
{
|
{
|
||||||
this->rc_highlighter = hl;
|
this->rc_highlighter = hl;
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
readline_context& set_quote_chars(const char* qc)
|
readline_context& set_quote_chars(const char* qc)
|
||||||
{
|
{
|
||||||
this->rc_quote_chars = qc;
|
this->rc_quote_chars = qc;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
readline_context& with_readline_var(char** var, const char* val)
|
readline_context& with_readline_var(char** var, const char* val)
|
||||||
{
|
{
|
||||||
this->rc_vars.emplace_back(var, val);
|
this->rc_vars.emplace_back(var, val);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
}
|
||||||
|
|
||||||
readline_highlighter_t get_highlighter() const
|
readline_highlighter_t get_highlighter() const
|
||||||
{
|
{
|
||||||
return this->rc_highlighter;
|
return this->rc_highlighter;
|
||||||
};
|
}
|
||||||
|
|
||||||
static int command_complete(int, int);
|
static int command_complete(int, int);
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user