mirror of https://github.com/tstack/lnav.git
[docs] add ARCHITECTURE.md and various other things
This commit is contained in:
parent
fd40b55e0a
commit
f5e88b7158
|
@ -0,0 +1,10 @@
|
|||
name: Check Markdown links
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
markdown-link-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@v1
|
|
@ -0,0 +1,80 @@
|
|||
# Architecture
|
||||
|
||||
This document covers the internal architecture of the Logfile Navigator (lnav),
|
||||
a terminal-based tool for viewing and analyzing log files.
|
||||
|
||||
## Goals
|
||||
|
||||
The following goals drive the design and implementation of lnav:
|
||||
|
||||
- Don't make the user do something that can be done automatically.
|
||||
|
||||
Example: Automatically detect log formats for files instead of making them
|
||||
specify the format for each file.
|
||||
|
||||
- Be performant on low-spec hardware.
|
||||
|
||||
Example: Prefer single-threaded optimizations over trying to parallelize
|
||||
|
||||
- Operations should be "live" and not block the user from continuing to work.
|
||||
|
||||
Example: Searches are run in the background.
|
||||
|
||||
- Provide context-sensitive help.
|
||||
|
||||
Example: When the cursor is over a SQL keyword/function, the help text for
|
||||
that is shown above.
|
||||
|
||||
- Show a preview of operations so the user knows what is going to happen.
|
||||
|
||||
Example: When entering a `:filter-out` command, the matched parts of the
|
||||
lines are highlighted in red.
|
||||
|
||||
## Overview
|
||||
|
||||
The whole of lnav consists of a
|
||||
[log file parser](https://lnav.readthedocs.io/en/latest/formats.html),
|
||||
[text UI](https://lnav.readthedocs.io/en/latest/ui.html),
|
||||
[integrations with SQLite](https://lnav.readthedocs.io/en/latest/sqlext.html),
|
||||
[command-line interface](https://lnav.readthedocs.io/en/latest/cli.html),
|
||||
and
|
||||
[commands for operating on logs](https://lnav.readthedocs.io/en/latest/commands.html).
|
||||
Since the majority of lnav's operations center around logs, the core
|
||||
data-structure is the combined log message index. The message index
|
||||
is populated when new messages are read from log files. The text UI
|
||||
displays a subset of messages from the index. The SQLite virtual-tables
|
||||
allow for programmatic access to the messages and lnav's internal state.
|
||||
|
||||
[![lnav architecture](docs/lnav-architecture.png)](https://whimsical.com/lnav-architecture-UM594Qo4G3nt2XWaSZA1mh)
|
||||
|
||||
## File Monitoring
|
||||
|
||||
Each file being monitored by lnav has an associated [`logfile`](src/logfile.hh)
|
||||
object, be they plaintext files or files with a recognized format. These
|
||||
objects are periodically polled by the main event loop to check if the file
|
||||
was deleted, truncated, or new lines added. While reading new lines, if no
|
||||
log format has matched yet, each line will be passed through the log format
|
||||
regular expressions to try and find a match. Each line that is read is added
|
||||
to an index
|
||||
|
||||
### Why is `mmap()` not used?
|
||||
|
||||
Note that file contents are consumed using `pread(2)`/`read(2)` and not
|
||||
`mmap(2)` since `mmap(2)` does not react well to files changing out from
|
||||
underneath it. For example, a truncated file would likely result in a
|
||||
`SIGBUS`.
|
||||
|
||||
## Log Messages
|
||||
|
||||
As files are being indexed, if a matching format is found, the file is
|
||||
"promoted" from a plaintext file to a log file. When the file is promoted,
|
||||
it is added to the [logfile_sub_source](src/logfile_sub_source.hh), which
|
||||
collates all log messages together into a single index.
|
||||
|
||||
## User Interface
|
||||
|
||||
[![lnav TUI](docs/lnav-tui.png)](https://whimsical.com/lnav-tui-MQjXc7Vx23BxQTHrnuNp5F)
|
||||
|
||||
The lnav text-user-interface is built on top of the basic drawing functionality
|
||||
of [ncurses](https://invisible-island.net/ncurses/announce.html). However,
|
||||
|
1
NEWS
1
NEWS
|
@ -32,6 +32,7 @@ lnav v0.9.1:
|
|||
"lnav_views" SQLite table.
|
||||
* Themes can now include definitions for text highlights under:
|
||||
/ui/theme-defs/<theme_name>/highlights
|
||||
* Added a "grayscale" theme that isn't so colorful.
|
||||
|
||||
Interface Changes:
|
||||
* When copying log lines, the file name and time offset will be included
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
![Build](https://github.com/tstack/lnav/workflows/ci-build/badge.svg)
|
||||
[![Build](https://github.com/tstack/lnav/workflows/ci-build/badge.svg)](https://github.com/tstack/lnav/actions?query=workflow%3Aci-build)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/tstack/lnav/badge.svg?branch=master)](https://coveralls.io/github/tstack/lnav?branch=master)
|
||||
[![lnav](https://snapcraft.io//lnav/badge.svg)](https://snapcraft.io/lnav)
|
||||
|
||||
|
@ -15,6 +15,7 @@ no setup.
|
|||
|
||||
- [Main Site](https://lnav.org)
|
||||
- [**Documentation**](https://lnav.readthedocs.io) on Read the Docs
|
||||
- [Architecture](ARCHITECTURE.md)
|
||||
|
||||
## Contributing
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
# aminclude_static.am generated automatically by Autoconf
|
||||
# from AX_AM_MACROS_STATIC on Tue Feb 2 19:06:51 PST 2021
|
||||
# from AX_AM_MACROS_STATIC on Tue Feb 9 14:05:27 PST 2021
|
||||
|
||||
|
||||
# Code coverage
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Docs
|
||||
|
||||
This directory contains the ReST documentation that is published to
|
||||
[lnav.readthedocs.io](https://lnav.readthedocs.io)
|
Binary file not shown.
After Width: | Height: | Size: 208 KiB |
Binary file not shown.
After Width: | Height: | Size: 153 KiB |
|
@ -0,0 +1,26 @@
|
|||
|
||||
__all__ = ['LnavCommandLexer']
|
||||
|
||||
import re
|
||||
|
||||
from pygments.token import Whitespace, Text, Keyword, Literal
|
||||
from pygments.lexers._mapping import LEXERS
|
||||
from pygments.lexers.python import RegexLexer
|
||||
|
||||
class LnavCommandLexer(RegexLexer):
|
||||
name = 'lnav'
|
||||
|
||||
flags = re.IGNORECASE
|
||||
tokens = {
|
||||
'root': [
|
||||
(r'\s+', Whitespace),
|
||||
(r':[\w\-]+', Keyword),
|
||||
(r'\<[\w\-]+\>', Literal.String.Doc),
|
||||
(r'.', Text),
|
||||
]
|
||||
}
|
||||
|
||||
def setup(app):
|
||||
LEXERS['LnavCommandLexer'] = (
|
||||
'_ext.lnavlexer', 'lnav', ('lnav',), ('*.lnav',), ('text/lnav',))
|
||||
app.add_lexer('lnav', LnavCommandLexer)
|
|
@ -20,3 +20,37 @@ th p {
|
|||
table.query-results p {
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
|
||||
DL DT:target, :target > H2, :target > H3, span:target + H2, span:target + H3 {
|
||||
border: 0 !important;
|
||||
border-bottom: 1px solid #d3d381 !important;
|
||||
background: #ffc !important;
|
||||
/* padding: 1em !important;*/
|
||||
}
|
||||
|
||||
DL DT {
|
||||
border: 0 !important;
|
||||
background: inherit !important;
|
||||
border-bottom: 1px solid #c3c0ee !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
kbd {
|
||||
padding: 0.1em 0.6em;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 11px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
background-color: #f7f7f7;
|
||||
color: #333;
|
||||
-moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset;
|
||||
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset;
|
||||
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
display: inline-block;
|
||||
margin: 0 0.1em 0.1em 0.1em;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
line-height: 1.4;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
|
|
@ -6,33 +6,92 @@ Command Line Interface
|
|||
|
||||
The following options can be used when starting **lnav**. There are not
|
||||
many flags because the majority of the functionality is accessed using
|
||||
the :code:`-c` option to execute :ref:`commands<commands>` or
|
||||
the :option:`-c` option to execute :ref:`commands<commands>` or
|
||||
:ref:`SQL queries<sql-ext>`.
|
||||
|
||||
-h Print these command-line options and exit.
|
||||
-H Start lnav and switch to the help view.
|
||||
-C Check the given files against the configuration, report any errors, and
|
||||
exit. This option can be helpful for validating that a log format is
|
||||
well-formed.
|
||||
-c cmd Execute the given lnav command, SQL query, or lnav script. The
|
||||
Options
|
||||
-------
|
||||
|
||||
.. option:: -h
|
||||
|
||||
Print these command-line options and exit.
|
||||
|
||||
.. option:: -H
|
||||
|
||||
Start lnav and switch to the help view.
|
||||
|
||||
.. option:: -C
|
||||
|
||||
Check the given files against the configuration, report any errors, and
|
||||
exit. This option can be helpful for validating that a log format is
|
||||
well-formed.
|
||||
|
||||
.. option:: -c <command>
|
||||
|
||||
Execute the given lnav command, SQL query, or lnav script. The
|
||||
argument must be prefixed with the character used to enter the prompt
|
||||
to distinguish between the different types (i.e. ':', ';', '|').
|
||||
This option can be given multiple times.
|
||||
-f path Execute the given command file. This option can be given multiple times.
|
||||
-I path Add a configuration directory.
|
||||
-i Install the format files in the :file:`.lnav/formats/` directory.
|
||||
Individual files will be installed in the :file:`installed`
|
||||
directory and git repositories will be cloned with a directory
|
||||
name based on their repository URI.
|
||||
-u Update formats installed from git repositories.
|
||||
-d path Write debug messages to the given file.
|
||||
-n Run without the curses UI (headless mode).
|
||||
-r Recursively load files from the given base directories.
|
||||
-t Prepend timestamps to the lines of data being read in on the standard
|
||||
input.
|
||||
-w path Write the contents of the standard input to this file.
|
||||
-V Print the version of lnav.
|
||||
-q Do not print the log messages after executing all of the commands.
|
||||
|
||||
.. option:: -f <path>
|
||||
|
||||
Execute the given command file. This option can be given multiple times.
|
||||
|
||||
.. option:: -I <path>
|
||||
|
||||
Add a configuration directory.
|
||||
|
||||
.. option:: -i
|
||||
|
||||
Install the format files in the :file:`.lnav/formats/` directory.
|
||||
Individual files will be installed in the :file:`installed`
|
||||
directory and git repositories will be cloned with a directory
|
||||
name based on their repository URI.
|
||||
|
||||
.. option:: -u
|
||||
|
||||
Update formats installed from git repositories.
|
||||
|
||||
.. option:: -d <path>
|
||||
|
||||
Write debug messages to the given file.
|
||||
|
||||
.. option:: -n
|
||||
|
||||
Run without the curses UI (headless mode).
|
||||
|
||||
.. option:: -r
|
||||
|
||||
Recursively load files from the given base directories.
|
||||
|
||||
.. option:: -t
|
||||
|
||||
Prepend timestamps to the lines of data being read in on the standard input.
|
||||
|
||||
.. option:: -w <path>
|
||||
|
||||
Write the contents of the standard input to this file.
|
||||
|
||||
.. option:: -V
|
||||
|
||||
Print the version of lnav.
|
||||
|
||||
.. option:: -q
|
||||
|
||||
Do not print the log messages after executing all of the commands.
|
||||
|
||||
|
||||
Environment Variables
|
||||
---------------------
|
||||
|
||||
.. envvar:: XDG_CONFIG_HOME
|
||||
|
||||
If this variable is set,
|
||||
|
||||
.. envvar:: HOME
|
||||
|
||||
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
.. include:: kbd.rst
|
||||
.. role:: lnavcmd(code)
|
||||
:language: lnav
|
||||
:class: highlight
|
||||
|
||||
.. _commands:
|
||||
|
||||
|
@ -7,13 +9,14 @@ Commands
|
|||
========
|
||||
|
||||
Commands provide access to some of the more advanced features in **lnav**, like
|
||||
filtering and "search tables". You can activate the command prompt by
|
||||
pressing the |ks| : |ke| key. At the prompt, you can start typing in the
|
||||
desired command and/or double-tap |ks| TAB |ke| to activate auto-completion
|
||||
and show the available commands. To guide you in the usage of the commands,
|
||||
a help window will appear above the command prompt with an explanation of the
|
||||
command and its parameters (if it has any). For example, the screenshot below
|
||||
shows the help for the :code:`:open` command:
|
||||
:ref:`filtering<filtering>` and
|
||||
:ref:`"search tables"<search_tables>`. You can activate the command
|
||||
prompt by pressing the :kbd:`:` key. At the prompt, you can start typing
|
||||
in the desired command and/or double-tap :kbd:`TAB` to activate
|
||||
auto-completion and show the available commands. To guide you in the usage of
|
||||
the commands, a help window will appear above the command prompt with an
|
||||
explanation of the command and its parameters (if it has any). For example,
|
||||
the screenshot below shows the help for the :code:`:open` command:
|
||||
|
||||
.. figure:: open-help.png
|
||||
:align: center
|
||||
|
@ -22,7 +25,7 @@ shows the help for the :code:`:open` command:
|
|||
|
||||
In addition to online help, many commands provide a preview of the effects that
|
||||
the command will have. This preview will activate shortly after you have
|
||||
finished typing, but before you have pressed |ks| Enter |ke| to execute the
|
||||
finished typing, but before you have pressed :kbd:`Enter` to execute the
|
||||
command. For example, the :code:`:open` command will show a preview of the
|
||||
first few lines of the file given as its argument:
|
||||
|
||||
|
@ -31,7 +34,7 @@ first few lines of the file given as its argument:
|
|||
|
||||
Screenshot of the preview shown for the :code:`:open` command.
|
||||
|
||||
The :code:`:filter-out` command is another instance where the preview behavior
|
||||
The :lnavcmd:`:filter-out pattern` command is another instance where the preview behavior
|
||||
can help you craft the correct command-line. This command takes a PCRE regular
|
||||
expression that specifies the log messages that should be filtered out of the
|
||||
view. The preview for this command will highlight the portion of the log
|
||||
|
@ -58,7 +61,7 @@ an error message in the status bar, like so:
|
|||
|
||||
Note that almost all commands support TAB-completion for their arguments.
|
||||
So, if you are in doubt as to what to type for an argument, you can double-
|
||||
tap the |ks| TAB |ke| key to get suggestions. For example, the
|
||||
tap the :kbd:`TAB` key to get suggestions. For example, the
|
||||
TAB-completion for the :code:`filter-in` command will suggest words that are
|
||||
currently displayed in the view.
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import sys, os
|
|||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
this_dir = os.path.abspath('.')
|
||||
src_dir = os.path.join(this_dir, "..", "..", "src")
|
||||
sys.path.insert(0, this_dir)
|
||||
sys.path.insert(0, src_dir)
|
||||
|
||||
import format2csv
|
||||
|
@ -216,21 +217,6 @@ class CustSqliteLexer(RegexLexer):
|
|||
|
||||
lexers['custsqlite'] = CustSqliteLexer(startinline=True)
|
||||
|
||||
class LnavCommandLexer(RegexLexer):
|
||||
name = 'lnav'
|
||||
|
||||
flags = re.IGNORECASE
|
||||
tokens = {
|
||||
'root': [
|
||||
(r'\s+', Whitespace),
|
||||
(r':[\w\-]+', Keyword),
|
||||
(r'\<[\w\-]+\>', Literal.String.Doc),
|
||||
(r'.', Text),
|
||||
]
|
||||
}
|
||||
|
||||
lexers['lnav'] = LnavCommandLexer()
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
|
@ -243,7 +229,9 @@ extensions = [
|
|||
"sphinx_rtd_theme",
|
||||
'sphinx-jsonschema',
|
||||
'sphinx-prompt',
|
||||
'_ext.lnavlexer',
|
||||
]
|
||||
import sphinx_rtd_theme
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
@ -259,7 +247,7 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'lnav'
|
||||
copyright = u'2020, Tim Stack'
|
||||
copyright = u'2021, Tim Stack'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
|
|
@ -14,7 +14,7 @@ The configuration for **lnav** is stored in the following JSON files in
|
|||
Removing the :file:`.sample` extension and editing the file will allow you to
|
||||
do basic customizations.
|
||||
* :file:`configs/installed/*.json` -- Contains configuration files installed
|
||||
using the :code:`-i` flag (e.g. :code:`$ lnav -i /path/to/config.json`).
|
||||
using the :option:`-i` flag (e.g. :code:`$ lnav -i /path/to/config.json`).
|
||||
* :file:`configs/*/*.json` -- Other directories that contain :file:`*.json`
|
||||
files will be loaded on startup. This structure is convenient for installing
|
||||
**lnav** configurations, like from a git repository.
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
.. include:: kbd.rst
|
||||
|
||||
.. _Cookbook:
|
||||
|
||||
Cookbook
|
||||
|
@ -11,6 +9,16 @@ These recipes can be used as a starting point for your own needs after some
|
|||
adaptation.
|
||||
|
||||
|
||||
Log Formats
|
||||
-----------
|
||||
|
||||
TBD
|
||||
|
||||
Defining a New Format
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TBD
|
||||
|
||||
Log Analysis
|
||||
------------
|
||||
|
||||
|
@ -19,11 +27,26 @@ following examples should give you some ideas to start leveraging this
|
|||
functionality. One thing to keep in mind is that if a query gets to be too
|
||||
large or multiple statements need to be executed, you can create a
|
||||
:code:`.lnav` script that contains the statements and execute it using the
|
||||
|ks| | |ke| command prompt.
|
||||
:kbd:`\|` command prompt.
|
||||
|
||||
* To count the number of times a client IP shows up in the loaded web access
|
||||
logs:
|
||||
Count client IPs in web access logs
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To count the occurrences of an IP in web access logs and order the results
|
||||
from highest to lowest:
|
||||
|
||||
.. code-block:: custsqlite
|
||||
|
||||
;SELECT c_ip, count(*) as hits FROM access_log GROUP BY c_ip ORDER BY hits DESC
|
||||
|
||||
|
||||
Show only lines where a numeric field is in a range
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The :ref:`:filter-expr<filter_expr>` command can be used to filter web access
|
||||
logs to only show lines where the number of bytes transferred to the client is
|
||||
between 10,000 and 40,000 bytes like so:
|
||||
|
||||
.. code-block:: custsqlite
|
||||
|
||||
:filter-expr :sc_bytes BETWEEN 10000 AND 40000
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[restructuredtext parser]
|
||||
syntax_highlight = short
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
.. _faq:
|
||||
|
||||
Frequently Asked Questions
|
||||
==========================
|
||||
|
||||
Q: How can I copy & paste without decorations?
|
||||
----------------------------------------------
|
||||
|
||||
:Answer: There are a few ways to do this:
|
||||
|
||||
* Use the :ref:`bookmark<hotkeys_bookmarks>` hotkeys to mark lines and then
|
||||
press :kbd:`c` to copy to the local system keyboard.
|
||||
|
||||
* Press :kbd:`CTRL` + :kbd:`l` to temporarily switch to "lo-fi"
|
||||
mode where the contents of the current view are printed to the terminal.
|
||||
This option is useful when you are logged into a remote host.
|
||||
|
||||
|
||||
Q: How can I force a format for a file?
|
||||
---------------------------------------
|
||||
|
||||
:Answer: The log format for a file is automatically detected and cannot be
|
||||
forced.
|
||||
|
||||
:Solution: Add some of the log file lines to the :ref:`sample<format_sample>`
|
||||
array and then startup lnav to get a detailed explanation of where the format
|
||||
patterns are not matching the sample lines.
|
||||
|
||||
:Details: The first lines of the file are matched against the
|
||||
:ref:`regular expressions defined in the format definitions<format_regex>`.
|
||||
The order of the formats is automatically determined so that more specific
|
||||
formats are tried before more generic ones. Therefore, if the expected
|
||||
format is not being chosen for a file, then it means the regular expressions
|
||||
defined by that format are not matching the first few lines of the file.
|
||||
|
||||
See :ref:`format_order` for more information.
|
||||
|
||||
Q: Why isn't my log file highlighted correctly?
|
||||
-----------------------------------------------
|
||||
|
||||
TBD
|
||||
|
||||
Q: Why isn't a file being displayed?
|
||||
------------------------------------
|
||||
|
||||
:Answer: Plaintext files are displayed separately from log files in the TEXT
|
||||
view.
|
||||
|
||||
:Solution: Press the :kbd:`t` key to switch to the text view. Or, open the
|
||||
files configuration panel by pressing :kbd:`TAB` to cycle through the
|
||||
panels, and then press :kbd:`/` to search for the file you're interested in.
|
||||
If the file is a log, a new :ref:`log format<log_formats>` will need to be
|
||||
created or an existing one modified.
|
||||
|
||||
:Details: If a file being monitored by lnav does not match a known log file
|
||||
format, it is treated as plaintext and will be displayed in the TEXT view.
|
|
@ -62,6 +62,8 @@ fields:
|
|||
can be used to limit which files a format is applied to in case there is
|
||||
a potential for conflicts.
|
||||
|
||||
.. _format_regex:
|
||||
|
||||
:regex: This object contains sub-objects that describe the message formats
|
||||
to match in a plain log file. Log files that contain JSON messages should
|
||||
not specify this field.
|
||||
|
@ -218,6 +220,8 @@ fields:
|
|||
SELECT message FROM http_status_codes
|
||||
WHERE status = :sc_status) || ') '
|
||||
|
||||
.. _format_sample:
|
||||
|
||||
:sample: A list of objects that contain sample log messages. All formats
|
||||
must include at least one sample and it must be matched by one of the
|
||||
included regexes. Each object must contain the following field:
|
||||
|
@ -377,6 +381,7 @@ Executing the format file should then install it automatically:
|
|||
$ ./myformat.json
|
||||
info: installed: /home/example/.lnav/formats/installed/myformat_log.json
|
||||
|
||||
.. _format_order:
|
||||
|
||||
Format Order When Scanning a File
|
||||
---------------------------------
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
.. include:: kbd.rst
|
||||
|
||||
.. _hotkeys:
|
||||
|
||||
Hotkey Reference
|
||||
|
@ -21,86 +19,86 @@ Spatial Navigation
|
|||
-
|
||||
-
|
||||
- Command
|
||||
* - |ks| Space |ke|
|
||||
- |ks| PgDn |ke|
|
||||
* - :kbd:`Space`
|
||||
- :kbd:`PgDn`
|
||||
-
|
||||
- Down a page
|
||||
* - |ks| b |ke|
|
||||
- |ks| Backspace |ke|
|
||||
- |ks| PgUp |ke|
|
||||
* - :kbd:`b`
|
||||
- :kbd:`Backspace`
|
||||
- :kbd:`PgUp`
|
||||
- Up a page
|
||||
* - |ks| j |ke|
|
||||
- |ks| Return |ke|
|
||||
- |ks| ↓ |ke|
|
||||
* - :kbd:`j`
|
||||
- :kbd:`Return`
|
||||
- :kbd:`↓`
|
||||
- Down a line
|
||||
* - |ks| k |ke|
|
||||
- |ks| ↑ |ke|
|
||||
* - :kbd:`k`
|
||||
- :kbd:`↑`
|
||||
-
|
||||
- Up a line
|
||||
* - |ks| h |ke|
|
||||
- |ks| ← |ke|
|
||||
* - :kbd:`h`
|
||||
- :kbd:`←`
|
||||
-
|
||||
- Left half a page. In the log view, pressing left while at the start of
|
||||
the message text will reveal the source file name for each line.
|
||||
Pressing again will reveal the full path.
|
||||
* - |ks| Shift |ke| + |ks| h |ke|
|
||||
- |ks| Shift |ke| + |ks| ← |ke|
|
||||
* - :kbd:`Shift` + :kbd:`h`
|
||||
- :kbd:`Shift` + :kbd:`←`
|
||||
-
|
||||
- Left ten columns
|
||||
* - |ks| l |ke|
|
||||
- |ks| → |ke|
|
||||
* - :kbd:`l`
|
||||
- :kbd:`→`
|
||||
-
|
||||
- Right half a page
|
||||
* - |ks| Shift |ke| + |ks| l |ke|
|
||||
- |ks| Shift |ke| + |ks| → |ke|
|
||||
* - :kbd:`Shift` + :kbd:`l`
|
||||
- :kbd:`Shift` + :kbd:`→`
|
||||
-
|
||||
- Right ten columns
|
||||
* - |ks| Home |ke|
|
||||
- |ks| g |ke|
|
||||
* - :kbd:`Home`
|
||||
- :kbd:`g`
|
||||
-
|
||||
- Top of the view
|
||||
* - |ks| End |ke|
|
||||
- |ks| G |ke|
|
||||
* - :kbd:`End`
|
||||
- :kbd:`G`
|
||||
-
|
||||
- Bottom of the view
|
||||
* - |ks| e |ke|
|
||||
- |ks| Shift |ke| + |ks| e |ke|
|
||||
* - :kbd:`e`
|
||||
- :kbd:`Shift` + :kbd:`e`
|
||||
-
|
||||
- Next/previous error
|
||||
* - |ks| w |ke|
|
||||
- |ks| Shift |ke| + |ks| w |ke|
|
||||
* - :kbd:`w`
|
||||
- :kbd:`Shift` + :kbd:`w`
|
||||
-
|
||||
- Next/previous warning
|
||||
* - |ks| n |ke|
|
||||
- |ks| Shift |ke| + |ks| n |ke|
|
||||
* - :kbd:`n`
|
||||
- :kbd:`Shift` + :kbd:`n`
|
||||
-
|
||||
- Next/previous search hit
|
||||
* - |ks| > |ke|
|
||||
- |ks| < |ke|
|
||||
* - :kbd:`>`
|
||||
- :kbd:`<`
|
||||
-
|
||||
- Next/previous search hit (horizontal)
|
||||
* - |ks| f |ke|
|
||||
- |ks| Shift |ke| + |ks| f |ke|
|
||||
* - :kbd:`f`
|
||||
- :kbd:`Shift` + :kbd:`f`
|
||||
-
|
||||
- Next/previous file
|
||||
* - |ks| u |ke|
|
||||
- |ks| Shift |ke| + |ks| u |ke|
|
||||
* - :kbd:`u`
|
||||
- :kbd:`Shift` + :kbd:`u`
|
||||
-
|
||||
- Next/previous bookmark
|
||||
* - |ks| o |ke|
|
||||
- |ks| Shift |ke| + |ks| o |ke|
|
||||
* - :kbd:`o`
|
||||
- :kbd:`Shift` + :kbd:`o`
|
||||
-
|
||||
- Forward/backward through log messages with a matching "opid" field
|
||||
* - |ks| y |ke|
|
||||
- |ks| Shift |ke| + |ks| y |ke|
|
||||
* - :kbd:`y`
|
||||
- :kbd:`Shift` + :kbd:`y`
|
||||
-
|
||||
- Next/prevous SQL result
|
||||
* - |ks| s |ke|
|
||||
- |ks| Shift |ke| + |ks| s |ke|
|
||||
- Next/previous SQL result
|
||||
* - :kbd:`s`
|
||||
- :kbd:`Shift` + :kbd:`s`
|
||||
-
|
||||
- Next/prevous slow down in the log message rate
|
||||
* - |ks| { |ke|
|
||||
- |ks| } |ke|
|
||||
- Next/previous slow down in the log message rate
|
||||
* - :kbd:`{`
|
||||
- :kbd:`}`
|
||||
-
|
||||
- Previous/next location in history
|
||||
|
||||
|
@ -114,22 +112,25 @@ Chronological Navigation
|
|||
* - Keypress
|
||||
-
|
||||
- Command
|
||||
* - |ks| d |ke|
|
||||
- |ks| Shift |ke| + |ks| d |ke|
|
||||
* - :kbd:`d`
|
||||
- :kbd:`Shift` + :kbd:`d`
|
||||
- Forward/backward 24 hours
|
||||
* - |ks| 1 |ke| - |ks| 6 |ke|
|
||||
- |ks| Shift |ke| + |ks| 1 |ke| - |ks| 6 |ke|
|
||||
* - :kbd:`1` - :kbd:`6`
|
||||
- :kbd:`Shift` + :kbd:`1` - :kbd:`6`
|
||||
- Next/previous n'th ten minute of the hour
|
||||
* - |ks| 7 |ke|
|
||||
- |ks| 8 |ke|
|
||||
* - :kbd:`7`
|
||||
- :kbd:`8`
|
||||
- Previous/next minute
|
||||
* - |ks| 0 |ke|
|
||||
- |ks| Shift |ke| + |ks| 0 |ke|
|
||||
* - :kbd:`0`
|
||||
- :kbd:`Shift` + :kbd:`0`
|
||||
- Next/previous day
|
||||
* - |ks| r |ke|
|
||||
- |ks| Shift |ke| + |ks| r |ke|
|
||||
* - :kbd:`r`
|
||||
- :kbd:`Shift` + :kbd:`r`
|
||||
- Forward/backward by the relative time that was last used with the goto command.
|
||||
|
||||
|
||||
.. _hotkeys_bookmarks:
|
||||
|
||||
Bookmarks
|
||||
---------
|
||||
|
||||
|
@ -139,17 +140,17 @@ Bookmarks
|
|||
|
||||
* - Keypress
|
||||
- Command
|
||||
* - |ks| m |ke|
|
||||
* - :kbd:`m`
|
||||
- Mark/unmark the top line
|
||||
* - |ks| Shift |ke| + |ks| m |ke|
|
||||
* - :kbd:`Shift` + :kbd:`m`
|
||||
- Mark/unmark the range of lines from the last marked to the top
|
||||
* - |ks| Shift |ke| + |ks| j |ke|
|
||||
* - :kbd:`Shift` + :kbd:`j`
|
||||
- Mark/unmark the next line after the previously marked
|
||||
* - |ks| Shift |ke| + |ks| k |ke|
|
||||
* - :kbd:`Shift` + :kbd:`k`
|
||||
- Mark/unmark the previous line
|
||||
* - |ks| c |ke|
|
||||
* - :kbd:`c`
|
||||
- Copy marked lines to the clipboard
|
||||
* - |ks| Shift |ke| + |ks| c |ke|
|
||||
* - :kbd:`Shift` + :kbd:`c`
|
||||
- Clear marked lines
|
||||
|
||||
Display
|
||||
|
@ -161,51 +162,51 @@ Display
|
|||
|
||||
* - Keypress
|
||||
- Command
|
||||
* - |ks| ? |ke|
|
||||
* - :kbd:`?`
|
||||
- View/leave builtin help
|
||||
* - |ks| q |ke|
|
||||
* - :kbd:`q`
|
||||
- Return to the previous view/quit
|
||||
* - |ks| Shift |ke| + |ks| q |ke|
|
||||
* - :kbd:`Shift` + :kbd:`q`
|
||||
- Return to the previous view/quit while matching the top times of the two views
|
||||
* - |ks| a |ke|
|
||||
* - :kbd:`a`
|
||||
- Restore the view that was previously popped with 'q/Q'
|
||||
* - |ks| Shift |ke| + |ks| a |ke|
|
||||
* - :kbd:`Shift` + :kbd:`a`
|
||||
- Restore the view that was previously popped with 'q/Q' and match the top times of the views
|
||||
* - |ks| Shift |ke| + |ks| p |ke|
|
||||
* - :kbd:`Shift` + :kbd:`p`
|
||||
- Switch to/from the pretty-printed view of the displayed log or text files
|
||||
* - |ks| Shift |ke| + |ks| t |ke|
|
||||
* - :kbd:`Shift` + :kbd:`t`
|
||||
- Display elapsed time between lines
|
||||
* - |ks| t |ke|
|
||||
* - :kbd:`t`
|
||||
- Switch to/from the text file view
|
||||
* - |ks| i |ke|
|
||||
* - :kbd:`i`
|
||||
- Switch to/from the histogram view
|
||||
* - |ks| Shift |ke| + |ks| i |ke|
|
||||
* - :kbd:`Shift` + :kbd:`i`
|
||||
- Switch to/from the histogram view
|
||||
* - |ks| v |ke|
|
||||
* - :kbd:`v`
|
||||
- Switch to/from the SQL result view
|
||||
* - |ks| Shift |ke| + |ks| v |ke|
|
||||
* - :kbd:`Shift` + :kbd:`v`
|
||||
- Switch to/from the SQL result view and move to the corresponding in the
|
||||
log_line column
|
||||
* - |ks| p |ke|
|
||||
* - :kbd:`p`
|
||||
- Toggle the display of the log parser results
|
||||
* - |ks| Tab |ke|
|
||||
* - :kbd:`Tab`
|
||||
- In the log/text views, focus on the configuration panel for editing
|
||||
filters and examining the list of loaded files. In the SQL result view,
|
||||
cycle through columns to display as bar graphs
|
||||
* - |ks| Ctrl |ke| + |ks| l |ke|
|
||||
* - :kbd:`Ctrl` + :kbd:`l`
|
||||
- Switch to lo-fi mode. The displayed log lines will be dumped to the
|
||||
terminal without any decorations so they can be copied easily.
|
||||
* - |ks| Ctrl |ke| + |ks| w |ke|
|
||||
* - :kbd:`Ctrl` + :kbd:`w`
|
||||
- Toggle word-wrap.
|
||||
* - |ks| Ctrl |ke| + |ks| p |ke|
|
||||
* - :kbd:`Ctrl` + :kbd:`p`
|
||||
- Show/hide the data preview panel that may be opened when entering
|
||||
commands or SQL queries.
|
||||
* - |ks| Ctrl |ke| + |ks| f |ke|
|
||||
* - :kbd:`Ctrl` + :kbd:`f`
|
||||
- Toggle the enabled/disabled state of all filters in the current view.
|
||||
* - |ks| x |ke|
|
||||
* - :kbd:`x`
|
||||
- Toggle the hiding of log message fields. The hidden fields will be
|
||||
replaced with three bullets and highlighted in yellow.
|
||||
* - |ks| = |ke|
|
||||
* - :kbd:`=`
|
||||
- Pause/unpause loading of new file data.
|
||||
|
||||
Session
|
||||
|
@ -217,11 +218,11 @@ Session
|
|||
|
||||
* - Keypress
|
||||
- Command
|
||||
* - |ks| Ctrl |ke| + |ks| R |ke|
|
||||
* - :kbd:`Ctrl` + :kbd:`R`
|
||||
- Reset current session.
|
||||
|
||||
Query
|
||||
-----
|
||||
Query Prompts
|
||||
-------------
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
@ -229,15 +230,15 @@ Query
|
|||
|
||||
* - Keypress
|
||||
- Command
|
||||
* - |ks| / |ke|
|
||||
* - :kbd:`/`
|
||||
- Search for lines matching a regular expression
|
||||
* - |ks| ; |ke|
|
||||
- Execute an SQL query
|
||||
* - |ks| : |ke|
|
||||
* - :kbd:`;`
|
||||
- Open the :ref:`sql-ext` to execute SQL statements/queries
|
||||
* - :kbd:`:`
|
||||
- Execute an internal command, see :ref:`commands` for more information
|
||||
* - |ks| \| |ke|
|
||||
- Execute an lnav script located in a format directory.
|
||||
* - |ks| Ctrl |ke| + |ks| ] |ke|
|
||||
* - :kbd:`\|`
|
||||
- Execute an lnav script located in a format directory
|
||||
* - :kbd:`Ctrl` + :kbd:`]`
|
||||
- Abort the prompt
|
||||
|
||||
Customizing
|
||||
|
|
|
@ -18,16 +18,17 @@ Contents:
|
|||
usage
|
||||
cookbook
|
||||
howitworks
|
||||
config
|
||||
cli
|
||||
ui
|
||||
hotkeys
|
||||
config
|
||||
formats
|
||||
sessions
|
||||
commands
|
||||
sqlext
|
||||
sqltab
|
||||
data
|
||||
faq
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
.. include:: kbd.rst
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
|
@ -27,13 +25,14 @@ When compiling from source, the following dependencies are required:
|
|||
* `Bzip2 <http://www.bzip.org>`_
|
||||
* `Readline <http://www.gnu.org/s/readline>`_
|
||||
* `libcurl <https://curl.haxx.se>`_
|
||||
* `libarchive <https://libarchive.org>`_
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Check the `downloads page <http://lnav.org/downloads>`_ to see if there are
|
||||
packages for your operating system. Compiling from source is just a matter of
|
||||
doing:
|
||||
packages for your operating system. To compile from source, use the following
|
||||
commands:
|
||||
|
||||
.. prompt:: bash
|
||||
|
||||
|
@ -70,7 +69,7 @@ Setup
|
|||
After starting **lnav**, you might want to set the
|
||||
:ref:`configuration options<Configuration>` mentioned below. Configuration in
|
||||
**lnav** is done using the :code:`:config` command. To change a configuration
|
||||
option, start by pressing |ks|:|ke| to enter the command prompt. Then,
|
||||
option, start by pressing :kbd:`:` to enter the command prompt. Then,
|
||||
type "config" followed by the option name and value.
|
||||
|
||||
.. note::
|
||||
|
@ -113,6 +112,7 @@ command can be used to the change the theme:
|
|||
The builtin themes are:
|
||||
`default <https://github.com/tstack/lnav/blob/master/src/themes/default-theme.json>`_,
|
||||
`eldar <https://github.com/tstack/lnav/blob/master/src/themes/eldar.json>`_,
|
||||
`grayscale <https://github.com/tstack/lnav/blob/master/src/themes/grayscale.json>`_,
|
||||
`monocai <https://github.com/tstack/lnav/blob/master/src/themes/monocai.json>`_,
|
||||
`night-owl <https://github.com/tstack/lnav/blob/master/src/themes/night-owl.json>`_,
|
||||
`solarized-dark <https://github.com/tstack/lnav/blob/master/src/themes/solarized-dark.json>`_,
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
|
||||
.. |ks| raw:: html
|
||||
|
||||
<kbd>
|
||||
|
||||
.. |ke| raw:: html
|
||||
|
||||
</kbd>
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<style>
|
||||
kbd {
|
||||
padding: 0.1em 0.6em;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 11px;
|
||||
font-family: Arial,Helvetica,sans-serif;
|
||||
background-color: #f7f7f7;
|
||||
color: #333;
|
||||
-moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
|
||||
-webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
|
||||
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset;
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
display: inline-block;
|
||||
margin: 0 0.1em 0.1em 0.1em;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
line-height: 1.4;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,4 @@
|
|||
|
||||
.. include:: kbd.rst
|
||||
|
||||
.. _sql-ext:
|
||||
|
||||
SQLite Interface
|
||||
|
@ -27,9 +25,9 @@ These columns would be available for its row in the :code:`access_log` table:
|
|||
results, but they can still be accessed when explicitly used. The hidden
|
||||
columns are: :code:`log_path`, :code:`log_text`, and :code:`log_body`.
|
||||
|
||||
You can activate the SQL prompt by pressing the |ks| ; |ke| key. At the
|
||||
You can activate the SQL prompt by pressing the :kbd:`;` key. At the
|
||||
prompt, you can start typing in the desired SQL statement and/or double-tap
|
||||
|ks| TAB |ke| to activate auto-completion. A help window will appear above
|
||||
:kbd:`TAB` to activate auto-completion. A help window will appear above
|
||||
the prompt to guide you in the usage of SQL keywords and functions.
|
||||
|
||||
.. figure:: sql-help.png
|
||||
|
@ -49,12 +47,12 @@ and maximum number of bytes returned by the server, grouped by IP address:
|
|||
|
||||
;SELECT c_ip, avg(sc_bytes), max(sc_bytes) FROM access_log GROUP BY c_ip
|
||||
|
||||
After pressing |ks| Enter |ke|, SQLite will execute the query using **lnav**'s
|
||||
After pressing :kbd:`Enter`, SQLite will execute the query using **lnav**'s
|
||||
virtual table implementation to extract the data directly from the log files.
|
||||
Once the query has finished, the main window will switch to the DB view to
|
||||
show the results. Press |ks| q |ke| to return to the log view and press |ks|
|
||||
v |ke| to return to the log view. If the SQL results contain a
|
||||
:code:`log_line` column, you can press to |ks| Shift |ke| + |ks| V |ke| to
|
||||
show the results. Press :kbd:`q` to return to the log view and press :kbd:`v`
|
||||
to return to the log view. If the SQL results contain a
|
||||
:code:`log_line` column, you can press to :kbd:`Shift` + :kbd:`V` to
|
||||
switch between the DB view and the log
|
||||
|
||||
.. figure:: query-results.png
|
||||
|
@ -66,9 +64,9 @@ The DB view has the following display features:
|
|||
|
||||
* Column headers stick to the top of the view when scrolling.
|
||||
* A stacked bar chart of the numeric column values is displayed underneath the
|
||||
rows. Pressing |ks| TAB |ke| will cycle through displaying no columns, each
|
||||
rows. Pressing :kbd:`TAB` will cycle through displaying no columns, each
|
||||
individual column, or all columns.
|
||||
* JSON columns in the top row can be pretty-printed by pressing |ks| p |ke|.
|
||||
* JSON columns in the top row can be pretty-printed by pressing :kbd:`p`.
|
||||
The display will show the value and JSON-Pointer path that can be passed to
|
||||
the `jget`_ function.
|
||||
|
||||
|
@ -144,7 +142,7 @@ the current IP is different from the previous IP:
|
|||
|
||||
Since the above can be a lot to type out interactively, you can put these
|
||||
commands into a :ref:`script<scripts>` and execute that script with the
|
||||
|ks| \| |ke| hotkey.
|
||||
:kbd:`\|` hotkey.
|
||||
|
||||
.. [#] The expression :code:`regexp_match('bound to ([^ ]+)', log_body) as ip`
|
||||
can be used to extract the IP address from the log message body.
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
.. include:: kbd.rst
|
||||
|
||||
.. _usage:
|
||||
|
||||
|
@ -13,8 +12,8 @@ Basic Controls
|
|||
|
||||
Like most file viewers, scrolling through files can be done with the usual
|
||||
:ref:`hotkeys<hotkeys>`. For non-trivial operations, you can enter the
|
||||
:ref:`command<commands>` prompt by pressing |ks| : |ke|. To analyze data in a
|
||||
log file, you can enter the :ref:`SQL prompt<sql-ext>` by pressing |ks| ; |ke|.
|
||||
:ref:`command<commands>` prompt by pressing :kbd:`:`. To analyze data in a
|
||||
log file, you can enter the :ref:`SQL prompt<sql-ext>` by pressing :kbd:`;`.
|
||||
|
||||
.. tip::
|
||||
|
||||
|
@ -24,8 +23,8 @@ log file, you can enter the :ref:`SQL prompt<sql-ext>` by pressing |ks| ; |ke|.
|
|||
.. figure:: hotkey-tips.png
|
||||
:align: center
|
||||
|
||||
When **lnav** is first open, it suggests using |ks| e |ke| and
|
||||
|ks| Shift |ke| + |ks| e |ke| to jump to error messages.
|
||||
When **lnav** is first open, it suggests using :kbd:`e` and
|
||||
:kbd:`Shift` + :kbd:`e` to jump to error messages.
|
||||
|
||||
|
||||
Viewing Files
|
||||
|
@ -42,7 +41,7 @@ extracted to a temporary location and the files within will be loaded. The
|
|||
files that are found will be scanned to identify their file format. Files
|
||||
that match a log format will be collated by time and displayed in the LOG
|
||||
view. Plain text files can be viewed in the TEXT view, which can be accessed
|
||||
by pressing |ks| t |ke|.
|
||||
by pressing :kbd:`t`.
|
||||
|
||||
|
||||
Archive Support
|
||||
|
@ -71,13 +70,15 @@ Searching
|
|||
|
||||
Any log messages that are loaded into **lnav** are indexed by time and log
|
||||
level (e.g. error, warning) to make searching quick and easy with
|
||||
:ref:`hotkeys<hotkeys>`. For example, pressing |ks| e |ke| will jump to the
|
||||
next error in the file and pressing |ks| Shift |ke| + |ks| e |ke| will jump to
|
||||
the previous error. Plain text searches can be done by pressing |ks| / |ke|
|
||||
:ref:`hotkeys<hotkeys>`. For example, pressing :kbd:`e` will jump to the
|
||||
next error in the file and pressing :kbd:`Shift` + :kbd:`e` will jump to
|
||||
the previous error. Plain text searches can be done by pressing :kbd:`/`
|
||||
to enter the search prompt. A regular expression can be entered into the
|
||||
prompt to start a search through the current view.
|
||||
|
||||
|
||||
.. _filtering:
|
||||
|
||||
Filtering
|
||||
---------
|
||||
|
||||
|
@ -126,6 +127,8 @@ Log level
|
|||
To hide messages below a certain log level, you can use the
|
||||
:ref:`:set-min-log-level<set_min_log_level>`.
|
||||
|
||||
.. _search_tables:
|
||||
|
||||
Search Tables
|
||||
-------------
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Release
|
||||
|
||||
This directory contains the [Makefile](Makefile) and scripts used to build the
|
||||
binaries for a release.
|
|
@ -171,6 +171,7 @@ set(CONFIG_FILES
|
|||
keymaps/uk-keymap.json
|
||||
keymaps/us-keymap.json
|
||||
themes/default-theme.json
|
||||
themes/grayscale.json
|
||||
themes/eldar.json
|
||||
themes/monocai.json
|
||||
themes/night-owl.json
|
||||
|
|
|
@ -21,80 +21,28 @@ RE2C_V = $(RE2C_V_@AM_V@)
|
|||
RE2C_V_ = $(RE2C_V_@AM_DEFAULT_V@)
|
||||
RE2C_V_0 = @echo " RE2C " $@;
|
||||
|
||||
FORMAT_FILES = \
|
||||
$(srcdir)/formats/access_log.json \
|
||||
$(srcdir)/formats/alb_log.json \
|
||||
$(srcdir)/formats/autodeploy_log.json \
|
||||
$(srcdir)/formats/block_log.json \
|
||||
$(srcdir)/formats/candlepin_log.json \
|
||||
$(srcdir)/formats/choose_repo_log.json \
|
||||
$(srcdir)/formats/cups_log.json \
|
||||
$(srcdir)/formats/dpkg_log.json \
|
||||
$(srcdir)/formats/elb_log.json \
|
||||
$(srcdir)/formats/engine_log.json \
|
||||
$(srcdir)/formats/error_log.json \
|
||||
$(srcdir)/formats/fsck_hfs_log.json \
|
||||
$(srcdir)/formats/glog_log.json \
|
||||
$(srcdir)/formats/haproxy_log.json \
|
||||
$(srcdir)/formats/java_log.json \
|
||||
$(srcdir)/formats/journald_json_log.json \
|
||||
$(srcdir)/formats/katello_log.json \
|
||||
$(srcdir)/formats/openam_log.json \
|
||||
$(srcdir)/formats/openamdb_log.json \
|
||||
$(srcdir)/formats/openstack_log.json \
|
||||
$(srcdir)/formats/page_log.json \
|
||||
$(srcdir)/formats/papertrail_log.json \
|
||||
$(srcdir)/formats/snaplogic_log.json \
|
||||
$(srcdir)/formats/sssd_log.json \
|
||||
$(srcdir)/formats/strace_log.json \
|
||||
$(srcdir)/formats/sudo_log.json \
|
||||
$(srcdir)/formats/syslog_log.json \
|
||||
$(srcdir)/formats/s3_log.json \
|
||||
$(srcdir)/formats/tcf_log.json \
|
||||
$(srcdir)/formats/tcsh_history.json \
|
||||
$(srcdir)/formats/uwsgi_log.json \
|
||||
$(srcdir)/formats/vdsm_log.json \
|
||||
$(srcdir)/formats/vmk_log.json \
|
||||
$(srcdir)/formats/vmw_log.json \
|
||||
$(srcdir)/formats/xmlrpc_log.json \
|
||||
$()
|
||||
include formats/formats.am
|
||||
|
||||
default-formats.h default-formats.cc: bin2c$(BUILD_EXEEXT) $(FORMAT_FILES)
|
||||
$(BIN2C_V)./bin2c$(BUILD_EXEEXT) -n lnav_format_json default-formats $(FORMAT_FILES)
|
||||
|
||||
include keymaps/keymaps.am
|
||||
include themes/themes.am
|
||||
|
||||
CONFIG_FILES = \
|
||||
$(srcdir)/root-config.json \
|
||||
$(srcdir)/keymaps/de-keymap.json \
|
||||
$(srcdir)/keymaps/default-keymap.json \
|
||||
$(srcdir)/keymaps/fr-keymap.json \
|
||||
$(srcdir)/keymaps/uk-keymap.json \
|
||||
$(srcdir)/keymaps/us-keymap.json \
|
||||
$(srcdir)/themes/default-theme.json \
|
||||
$(srcdir)/themes/eldar.json \
|
||||
$(srcdir)/themes/monocai.json \
|
||||
$(srcdir)/themes/night-owl.json \
|
||||
$(srcdir)/themes/solarized-dark.json \
|
||||
$(srcdir)/themes/solarized-light.json \
|
||||
$(KEYMAP_FILES) \
|
||||
$(THEME_FILES) \
|
||||
$()
|
||||
|
||||
default-config.h default-config.cc: bin2c$(BUILD_EXEEXT) $(CONFIG_FILES)
|
||||
$(BIN2C_V)./bin2c$(BUILD_EXEEXT) -n lnav_config_json default-config $(CONFIG_FILES)
|
||||
|
||||
BUILTIN_LNAVSCRIPTS = \
|
||||
$(srcdir)/scripts/dhclient-summary.lnav \
|
||||
$(srcdir)/scripts/lnav-pop-view.lnav \
|
||||
$(srcdir)/scripts/partition-by-boot.lnav \
|
||||
$(srcdir)/scripts/rename-stdin.lnav \
|
||||
$(srcdir)/scripts/search-for.lnav \
|
||||
$()
|
||||
include scripts/scripts.am
|
||||
|
||||
builtin-scripts.h builtin-scripts.cc: bin2c$(BUILD_EXEEXT) $(BUILTIN_LNAVSCRIPTS)
|
||||
$(BIN2C_V)./bin2c$(BUILD_EXEEXT) -n lnav_scripts builtin-scripts $(BUILTIN_LNAVSCRIPTS)
|
||||
|
||||
BUILTIN_SHSCRIPTS = \
|
||||
$(srcdir)/scripts/dump-pid.sh \
|
||||
$()
|
||||
|
||||
builtin-sh-scripts.h builtin-sh-scripts.cc: bin2c$(BUILD_EXEEXT) $(BUILTIN_SHSCRIPTS)
|
||||
$(BIN2C_V)./bin2c$(BUILD_EXEEXT) -n lnav_sh_scripts builtin-sh-scripts $(BUILTIN_SHSCRIPTS)
|
||||
|
||||
|
|
|
@ -99,15 +99,21 @@ bool is_archive(const fs::path& filename)
|
|||
archive_read_support_filter_all(arc);
|
||||
archive_read_support_format_all(arc);
|
||||
archive_read_support_format_raw(arc);
|
||||
auto r = archive_read_open_filename(arc, filename.c_str(), 16384);
|
||||
log_debug("read open %s", filename.c_str());
|
||||
auto r = archive_read_open_filename(arc, filename.c_str(), 128 * 1024);
|
||||
if (r == ARCHIVE_OK) {
|
||||
struct archive_entry *entry;
|
||||
|
||||
auto format_name = archive_format_name(arc);
|
||||
|
||||
log_debug("read next header %s %s", format_name, filename.c_str());
|
||||
if (archive_read_next_header(arc, &entry) == ARCHIVE_OK) {
|
||||
log_debug("read next done %s", filename.c_str());
|
||||
|
||||
static const auto RAW_FORMAT_NAME = string_fragment("raw");
|
||||
static const auto GZ_FILTER_NAME = string_fragment("gzip");
|
||||
|
||||
auto format_name = archive_format_name(arc);
|
||||
format_name = archive_format_name(arc);
|
||||
|
||||
if (RAW_FORMAT_NAME == format_name) {
|
||||
auto filter_count = archive_filter_count(arc);
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
#include "date_time_scanner.hh"
|
||||
#include "ptimec.hh"
|
||||
|
||||
size_t date_time_scanner::ftime(char *dst, size_t len, const exttm &tm)
|
||||
size_t date_time_scanner::ftime(char *dst, size_t len, const exttm &tm) const
|
||||
{
|
||||
off_t off = 0;
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ struct date_time_scanner {
|
|||
struct timeval &tv_out,
|
||||
bool convert_local = true);
|
||||
|
||||
size_t ftime(char *dst, size_t len, const struct exttm &tm);;
|
||||
size_t ftime(char *dst, size_t len, const struct exttm &tm) const;
|
||||
|
||||
bool convert_to_timeval(const char *time_src,
|
||||
ssize_t time_len,
|
||||
|
|
|
@ -34,10 +34,14 @@
|
|||
|
||||
#include "intern_string.hh"
|
||||
|
||||
using file_off_t = int64_t;
|
||||
using file_size_t = uint64_t;
|
||||
using file_ssize_t = int64_t;
|
||||
|
||||
class file_range {
|
||||
public:
|
||||
off_t fr_offset{0};
|
||||
ssize_t fr_size{0};
|
||||
file_off_t fr_offset{0};
|
||||
file_ssize_t fr_size{0};
|
||||
|
||||
void clear() {
|
||||
this->fr_offset = 0;
|
||||
|
|
|
@ -159,9 +159,9 @@ void bottom_status_source::update_hits(textview_curses *tc)
|
|||
this->update_marks(tc);
|
||||
}
|
||||
|
||||
void bottom_status_source::update_loading(off_t off, size_t total)
|
||||
void bottom_status_source::update_loading(file_off_t off, file_size_t total)
|
||||
{
|
||||
status_field &sf = this->bss_fields[BSF_LOADING];
|
||||
auto &sf = this->bss_fields[BSF_LOADING];
|
||||
|
||||
require(off >= 0);
|
||||
|
||||
|
|
|
@ -103,7 +103,7 @@ public:
|
|||
|
||||
void update_hits(textview_curses *tc);
|
||||
|
||||
void update_loading(off_t off, size_t total);
|
||||
void update_loading(file_off_t off, file_size_t total);
|
||||
|
||||
private:
|
||||
status_field bss_prompt{1024, view_colors::VCR_STATUS};
|
||||
|
|
|
@ -283,9 +283,6 @@ void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
|
|||
(int) filename_width + 3 + 4,
|
||||
(int) filename_width + 3 + 10,
|
||||
};
|
||||
value_out.emplace_back(lr, &view_curses::VC_FOREGROUND, COLOR_WHITE);
|
||||
lr.lr_start = lr.lr_end;
|
||||
lr.lr_end += 2;
|
||||
value_out.emplace_back(lr, &view_curses::VC_STYLE, A_BOLD);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
|
||||
};
|
||||
|
||||
void logline_restart(const logfile &lf, size_t rollback_size) {
|
||||
void logline_restart(const logfile &lf, file_size_t rollback_size) {
|
||||
for (auto &filter : this->lfo_filter_stack) {
|
||||
filter->revert_to_last(this->lfo_filter_state, rollback_size);
|
||||
}
|
||||
|
|
|
@ -305,7 +305,11 @@ void filter_sub_source::text_value_for_line(textview_curses &tc, int line,
|
|||
value_out.append(" IN ");
|
||||
break;
|
||||
case text_filter::EXCLUDE:
|
||||
value_out.append("OUT ");
|
||||
if (tf->get_lang() == filter_lang_t::REGEX) {
|
||||
value_out.append("OUT ");
|
||||
} else {
|
||||
value_out.append(" ");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ensure(0);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Formats
|
||||
|
||||
This directory contains the built-in log file format definitions. These files
|
||||
are converted to C by `bin2c` and compiled into the executable.
|
||||
are converted to C by `bin2c` and compiled into the executable. New formats
|
||||
need to be added to the [formats.am](formats.am) file.
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
|
||||
FORMAT_FILES = \
|
||||
$(srcdir)/%reldir%/access_log.json \
|
||||
$(srcdir)/%reldir%/alb_log.json \
|
||||
$(srcdir)/%reldir%/autodeploy_log.json \
|
||||
$(srcdir)/%reldir%/block_log.json \
|
||||
$(srcdir)/%reldir%/candlepin_log.json \
|
||||
$(srcdir)/%reldir%/choose_repo_log.json \
|
||||
$(srcdir)/%reldir%/cups_log.json \
|
||||
$(srcdir)/%reldir%/dpkg_log.json \
|
||||
$(srcdir)/%reldir%/elb_log.json \
|
||||
$(srcdir)/%reldir%/engine_log.json \
|
||||
$(srcdir)/%reldir%/error_log.json \
|
||||
$(srcdir)/%reldir%/fsck_hfs_log.json \
|
||||
$(srcdir)/%reldir%/glog_log.json \
|
||||
$(srcdir)/%reldir%/haproxy_log.json \
|
||||
$(srcdir)/%reldir%/java_log.json \
|
||||
$(srcdir)/%reldir%/journald_json_log.json \
|
||||
$(srcdir)/%reldir%/katello_log.json \
|
||||
$(srcdir)/%reldir%/openam_log.json \
|
||||
$(srcdir)/%reldir%/openamdb_log.json \
|
||||
$(srcdir)/%reldir%/openstack_log.json \
|
||||
$(srcdir)/%reldir%/page_log.json \
|
||||
$(srcdir)/%reldir%/papertrail_log.json \
|
||||
$(srcdir)/%reldir%/snaplogic_log.json \
|
||||
$(srcdir)/%reldir%/sssd_log.json \
|
||||
$(srcdir)/%reldir%/strace_log.json \
|
||||
$(srcdir)/%reldir%/sudo_log.json \
|
||||
$(srcdir)/%reldir%/syslog_log.json \
|
||||
$(srcdir)/%reldir%/s3_log.json \
|
||||
$(srcdir)/%reldir%/tcf_log.json \
|
||||
$(srcdir)/%reldir%/tcsh_history.json \
|
||||
$(srcdir)/%reldir%/uwsgi_log.json \
|
||||
$(srcdir)/%reldir%/vdsm_log.json \
|
||||
$(srcdir)/%reldir%/vmk_log.json \
|
||||
$(srcdir)/%reldir%/vmw_log.json \
|
||||
$(srcdir)/%reldir%/xmlrpc_log.json \
|
||||
$()
|
|
@ -569,7 +569,7 @@ void format_help_text_for_rst(const help_text &ht,
|
|||
}
|
||||
|
||||
if (param_count > 0) {
|
||||
fprintf(rst_file, " **Parameters:**\n\n");
|
||||
fprintf(rst_file, " **Parameters**\n");
|
||||
for (auto ¶m: ht.ht_parameters) {
|
||||
if (param.ht_summary && param.ht_summary[0]) {
|
||||
fprintf(rst_file, " * **%s%s** --- %s\n",
|
||||
|
@ -584,7 +584,7 @@ void format_help_text_for_rst(const help_text &ht,
|
|||
prefix = ";";
|
||||
}
|
||||
if (!ht.ht_example.empty()) {
|
||||
fprintf(rst_file, " **Examples:**\n\n");
|
||||
fprintf(rst_file, " **Examples**\n");
|
||||
for (auto &example: ht.ht_example) {
|
||||
fprintf(rst_file, " %s:\n\n", example.he_description);
|
||||
fprintf(rst_file, " .. code-block:: %s\n\n",
|
||||
|
@ -616,7 +616,7 @@ void format_help_text_for_rst(const help_text &ht,
|
|||
}
|
||||
stable_sort(related_refs.begin(), related_refs.end());
|
||||
|
||||
fmt::print(rst_file, " **See Also:**\n\n {}\n",
|
||||
fmt::print(rst_file, " **See Also**\n {}\n",
|
||||
fmt::join(related_refs, ", "));
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,6 @@ highlighter::highlighter(const highlighter &other)
|
|||
this->h_text_format = other.h_text_format;
|
||||
this->h_format_name = other.h_format_name;
|
||||
this->h_nestable = other.h_nestable;
|
||||
this->h_semantic = other.h_semantic;
|
||||
}
|
||||
|
||||
highlighter &highlighter::operator=(const highlighter &other)
|
||||
|
@ -70,7 +69,6 @@ highlighter &highlighter::operator=(const highlighter &other)
|
|||
this->h_attrs = other.h_attrs;
|
||||
this->h_text_format = other.h_text_format;
|
||||
this->h_nestable = other.h_nestable;
|
||||
this->h_semantic = other.h_semantic;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
@ -138,25 +136,6 @@ void highlighter::annotate(attr_line_t &al, int start) const
|
|||
if (this->h_attrs != -1) {
|
||||
attrs = this->h_attrs;
|
||||
}
|
||||
if (this->h_semantic) {
|
||||
bool found_color = false;
|
||||
|
||||
if (str[lr.lr_start] == '#' &&
|
||||
(lr.length() == 4 || lr.length() == 7)) {
|
||||
auto fg_res = rgb_color::from_str(
|
||||
string_fragment(str.data(), lr.lr_start, lr.lr_end));
|
||||
if (fg_res.isOk()) {
|
||||
sa.emplace_back(lr,
|
||||
&view_curses::VC_FOREGROUND,
|
||||
vc.match_color(fg_res.unwrap()));
|
||||
found_color = true;
|
||||
}
|
||||
}
|
||||
if (!found_color) {
|
||||
attrs |= vc.attrs_for_ident(&str[lr.lr_start],
|
||||
lr.length());
|
||||
}
|
||||
}
|
||||
if (!this->h_fg.empty()) {
|
||||
sa.emplace_back(lr,
|
||||
&view_curses::VC_FOREGROUND,
|
||||
|
@ -172,7 +151,9 @@ void highlighter::annotate(attr_line_t &al, int start) const
|
|||
&view_curses::VC_ROLE,
|
||||
this->h_role);
|
||||
}
|
||||
sa.emplace_back(lr, &view_curses::VC_STYLE, attrs);
|
||||
if (attrs) {
|
||||
sa.emplace_back(lr, &view_curses::VC_STYLE, attrs);
|
||||
}
|
||||
|
||||
off = matches[1];
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ struct highlighter {
|
|||
return *this;
|
||||
};
|
||||
|
||||
highlighter &with_color(const rgb_color &fg, const rgb_color &bg) {
|
||||
highlighter &with_color(const styling::color_unit &fg, const styling::color_unit &bg) {
|
||||
this->h_fg = fg;
|
||||
this->h_bg = bg;
|
||||
|
||||
|
@ -105,11 +105,6 @@ struct highlighter {
|
|||
return *this;
|
||||
}
|
||||
|
||||
highlighter &with_semantic(bool val) {
|
||||
this->h_semantic = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int get_attrs() const
|
||||
{
|
||||
ensure(this->h_attrs != -1);
|
||||
|
@ -121,15 +116,14 @@ struct highlighter {
|
|||
|
||||
std::string h_pattern;
|
||||
view_colors::role_t h_role{view_colors::VCR_NONE};
|
||||
rgb_color h_fg;
|
||||
rgb_color h_bg;
|
||||
styling::color_unit h_fg{styling::color_unit::make_empty()};
|
||||
styling::color_unit h_bg{styling::color_unit::make_empty()};
|
||||
pcre *h_code;
|
||||
pcre_extra *h_code_extra;
|
||||
int h_attrs{-1};
|
||||
text_format_t h_text_format{text_format_t::TF_UNKNOWN};
|
||||
intern_string_t h_format_name;
|
||||
bool h_nestable{true};
|
||||
bool h_semantic{false};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -405,11 +405,6 @@
|
|||
"type": "object",
|
||||
"$$target": "#/definitions/style",
|
||||
"properties": {
|
||||
"semantic": {
|
||||
"title": "/semantic",
|
||||
"description": "Pick a color based on the text being highlighted",
|
||||
"type": "boolean"
|
||||
},
|
||||
"color": {
|
||||
"title": "/color",
|
||||
"description": "The foreground color value for this style. The value can be the name of an xterm color, the hexadecimal value, or a theme variable reference.",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,5 @@
|
|||
# Keymaps
|
||||
|
||||
This directory contains the built-in keymap definitions. The files are
|
||||
turned into C using `bin2c` and compiled into the executable.
|
||||
turned into C using `bin2c` and compiled into the executable. New keymaps
|
||||
need to be added to the [keymaps.am](keymaps.am) file.
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
KEYMAP_FILES = \
|
||||
$(srcdir)/%reldir%/de-keymap.json \
|
||||
$(srcdir)/%reldir%/default-keymap.json \
|
||||
$(srcdir)/%reldir%/fr-keymap.json \
|
||||
$(srcdir)/%reldir%/uk-keymap.json \
|
||||
$(srcdir)/%reldir%/us-keymap.json \
|
||||
$()
|
|
@ -319,7 +319,7 @@ line_buffer::~line_buffer()
|
|||
|
||||
void line_buffer::set_fd(auto_fd &fd)
|
||||
{
|
||||
off_t newoff = 0;
|
||||
file_off_t newoff = 0;
|
||||
|
||||
if (this->lb_gz_file) {
|
||||
this->lb_gz_file.close();
|
||||
|
@ -411,14 +411,14 @@ void line_buffer::resize_buffer(size_t new_max)
|
|||
}
|
||||
}
|
||||
|
||||
void line_buffer::ensure_available(off_t start, ssize_t max_length)
|
||||
void line_buffer::ensure_available(file_off_t start, ssize_t max_length)
|
||||
{
|
||||
ssize_t prefill, available;
|
||||
|
||||
require(max_length <= MAX_LINE_BUFFER_SIZE);
|
||||
|
||||
if (this->lb_file_size != -1) {
|
||||
if (start + (off_t)max_length > this->lb_file_size) {
|
||||
if (start + (file_off_t)max_length > this->lb_file_size) {
|
||||
max_length = (this->lb_file_size - start);
|
||||
}
|
||||
}
|
||||
|
@ -428,7 +428,7 @@ void line_buffer::ensure_available(off_t start, ssize_t max_length)
|
|||
* after.
|
||||
*/
|
||||
if (start < this->lb_file_offset ||
|
||||
start > (off_t)(this->lb_file_offset + this->lb_buffer_size)) {
|
||||
start > (file_off_t)(this->lb_file_offset + this->lb_buffer_size)) {
|
||||
/*
|
||||
* The request is outside the cached range, need to reload the
|
||||
* whole thing.
|
||||
|
@ -444,7 +444,7 @@ void line_buffer::ensure_available(off_t start, ssize_t max_length)
|
|||
*/
|
||||
this->lb_file_offset = this->lb_file_size -
|
||||
std::min(this->lb_file_size,
|
||||
this->lb_buffer_max);
|
||||
(file_ssize_t) this->lb_buffer_max);
|
||||
}
|
||||
else {
|
||||
this->lb_file_offset = start;
|
||||
|
@ -483,7 +483,7 @@ void line_buffer::ensure_available(off_t start, ssize_t max_length)
|
|||
}
|
||||
}
|
||||
|
||||
bool line_buffer::fill_range(off_t start, ssize_t max_length)
|
||||
bool line_buffer::fill_range(file_off_t start, ssize_t max_length)
|
||||
{
|
||||
bool retval = false;
|
||||
|
||||
|
@ -530,7 +530,7 @@ bool line_buffer::fill_range(off_t start, ssize_t max_length)
|
|||
lock_hack::guard guard;
|
||||
char scratch[32 * 1024];
|
||||
BZFILE * bz_file;
|
||||
off_t seek_to;
|
||||
file_off_t seek_to;
|
||||
int bzfd;
|
||||
|
||||
/*
|
||||
|
@ -602,7 +602,7 @@ bool line_buffer::fill_range(off_t start, ssize_t max_length)
|
|||
if (!this->lb_seekable) {
|
||||
this->lb_file_size = this->lb_file_offset + this->lb_buffer_size;
|
||||
}
|
||||
if (start < (off_t) this->lb_file_size) {
|
||||
if (start < (file_off_t) this->lb_file_size) {
|
||||
retval = true;
|
||||
}
|
||||
|
||||
|
@ -763,7 +763,7 @@ Result<shared_buffer_ref, std::string> line_buffer::read_range(const file_range
|
|||
{
|
||||
shared_buffer_ref retval;
|
||||
char *line_start;
|
||||
ssize_t avail;
|
||||
file_ssize_t avail;
|
||||
|
||||
if (this->lb_last_line_offset != -1 &&
|
||||
fr.fr_offset > this->lb_last_line_offset) {
|
||||
|
|
|
@ -178,7 +178,7 @@ public:
|
|||
/**
|
||||
* @return The size of the file or the amount of data pulled from a pipe.
|
||||
*/
|
||||
ssize_t get_file_size() const { return this->lb_file_size; };
|
||||
file_ssize_t get_file_size() const { return this->lb_file_size; };
|
||||
|
||||
bool is_pipe() const {
|
||||
return !this->lb_seekable;
|
||||
|
@ -192,7 +192,7 @@ public:
|
|||
return this->lb_gz_file || this->lb_bz_file;
|
||||
};
|
||||
|
||||
off_t get_read_offset(off_t off) const
|
||||
file_off_t get_read_offset(file_off_t off) const
|
||||
{
|
||||
if (this->is_compressed()) {
|
||||
return this->lb_compressed_offset;
|
||||
|
@ -202,7 +202,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
bool is_data_available(off_t off, off_t stat_size) const {
|
||||
bool is_data_available(file_off_t off, file_off_t stat_size) const {
|
||||
if (this->is_compressed()) {
|
||||
return (this->lb_file_size == -1 || off < this->lb_file_size);
|
||||
}
|
||||
|
@ -253,10 +253,10 @@ private:
|
|||
* @param off The file offset to check for in the buffer.
|
||||
* @return True if the given offset is cached in the buffer.
|
||||
*/
|
||||
bool in_range(off_t off) const
|
||||
bool in_range(file_off_t off) const
|
||||
{
|
||||
return this->lb_file_offset <= off &&
|
||||
off < (int)(this->lb_file_offset + this->lb_buffer_size);
|
||||
off < (this->lb_file_offset + this->lb_buffer_size);
|
||||
};
|
||||
|
||||
void resize_buffer(size_t new_max);
|
||||
|
@ -273,7 +273,7 @@ private:
|
|||
* @param start The file offset of the start of the line.
|
||||
* @param max_length The amount of data to be cached in the buffer.
|
||||
*/
|
||||
void ensure_available(off_t start, ssize_t max_length);
|
||||
void ensure_available(file_off_t start, ssize_t max_length);
|
||||
|
||||
/**
|
||||
* Fill the buffer with the given range of data from the file.
|
||||
|
@ -283,7 +283,7 @@ private:
|
|||
* @param max_length The maximum amount of data to read from the file.
|
||||
* @return True if any data was read from the file.
|
||||
*/
|
||||
bool fill_range(off_t start, ssize_t max_length);
|
||||
bool fill_range(file_off_t start, ssize_t max_length);
|
||||
|
||||
/**
|
||||
* After a successful fill, the cached data can be retrieved with this
|
||||
|
@ -295,9 +295,9 @@ private:
|
|||
* @return A pointer to the start of the cached data in the internal
|
||||
* buffer.
|
||||
*/
|
||||
char *get_range(off_t start, ssize_t &avail_out) const
|
||||
char *get_range(file_off_t start, file_ssize_t &avail_out) const
|
||||
{
|
||||
off_t buffer_offset = start - this->lb_file_offset;
|
||||
auto buffer_offset = start - this->lb_file_offset;
|
||||
char *retval;
|
||||
|
||||
require(buffer_offset >= 0);
|
||||
|
@ -314,16 +314,16 @@ private:
|
|||
auto_fd lb_fd; /*< The file to read data from. */
|
||||
gz_indexed lb_gz_file; /*< File reader for gzipped files. */
|
||||
bool lb_bz_file; /*< Flag set for bzip2 compressed files. */
|
||||
off_t lb_compressed_offset; /*< The offset into the compressed file. */
|
||||
file_off_t lb_compressed_offset; /*< The offset into the compressed file. */
|
||||
|
||||
auto_mem<char> lb_buffer; /*< The internal buffer where data is cached */
|
||||
|
||||
ssize_t lb_file_size; /*<
|
||||
file_ssize_t lb_file_size; /*<
|
||||
* The size of the file. When lb_fd refers to
|
||||
* a pipe, this is set to the amount of data
|
||||
* read from the pipe when EOF is reached.
|
||||
*/
|
||||
off_t lb_file_offset; /*<
|
||||
file_off_t lb_file_offset; /*<
|
||||
* Data cached in the buffer comes from this
|
||||
* offset in the file.
|
||||
*/
|
||||
|
@ -332,6 +332,6 @@ private:
|
|||
ssize_t lb_buffer_max; /*< The amount of allocated memory for the
|
||||
* buffer. */
|
||||
bool lb_seekable; /*< Flag set for seekable file descriptors. */
|
||||
off_t lb_last_line_offset; /*< */
|
||||
file_off_t lb_last_line_offset; /*< */
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -388,7 +388,9 @@ public:
|
|||
|
||||
};
|
||||
|
||||
void logfile_indexing(const shared_ptr<logfile>& lf, off_t off, size_t total)
|
||||
void logfile_indexing(const shared_ptr<logfile>& lf,
|
||||
file_off_t off,
|
||||
file_size_t total)
|
||||
{
|
||||
static sig_atomic_t index_counter = 0;
|
||||
|
||||
|
|
|
@ -459,10 +459,6 @@ static struct json_path_container global_var_handlers = {
|
|||
|
||||
static struct json_path_container style_config_handlers =
|
||||
json_path_container{
|
||||
yajlpp::property_handler("semantic")
|
||||
.with_description(
|
||||
"Pick a color based on the text being highlighted")
|
||||
.FOR_FIELD(style_config, sc_semantic),
|
||||
yajlpp::property_handler("color")
|
||||
.with_synopsis("#hex|color_name")
|
||||
.with_description(
|
||||
|
|
|
@ -1926,11 +1926,12 @@ void external_log_format::build(std::vector<std::string> &errors) {
|
|||
external_log_format::highlighter_def &hd = hd_pair.second;
|
||||
const std::string &pattern = hd.hd_pattern;
|
||||
const char *errptr;
|
||||
rgb_color fg, bg;
|
||||
auto fg = styling::color_unit::make_empty();
|
||||
auto bg = styling::color_unit::make_empty();
|
||||
int eoff, attrs = 0;
|
||||
|
||||
if (!hd.hd_color.empty()) {
|
||||
fg = rgb_color::from_str(hd.hd_color)
|
||||
fg = styling::color_unit::from_str(hd.hd_color)
|
||||
.unwrapOrElse([&](const auto& msg) {
|
||||
errors.push_back("error:"
|
||||
+ this->elf_name.to_string()
|
||||
|
@ -1938,12 +1939,12 @@ void external_log_format::build(std::vector<std::string> &errors) {
|
|||
+ hd_pair.first.to_string()
|
||||
+ "/color:"
|
||||
+ msg);
|
||||
return rgb_color{};
|
||||
return styling::color_unit::make_empty();
|
||||
});
|
||||
}
|
||||
|
||||
if (!hd.hd_background_color.empty()) {
|
||||
bg = rgb_color::from_str(hd.hd_background_color)
|
||||
bg = styling::color_unit::from_str(hd.hd_background_color)
|
||||
.unwrapOrElse([&](const auto& msg) {
|
||||
errors.push_back("error:"
|
||||
+ this->elf_name.to_string()
|
||||
|
@ -1951,7 +1952,7 @@ void external_log_format::build(std::vector<std::string> &errors) {
|
|||
+ hd_pair.first.to_string()
|
||||
+ "/color:"
|
||||
+ msg);
|
||||
return rgb_color{};
|
||||
return styling::color_unit::make_empty();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
* @param millis The millisecond timestamp for the line.
|
||||
* @param l The logging level.
|
||||
*/
|
||||
logline(off_t off,
|
||||
logline(file_off_t off,
|
||||
time_t t,
|
||||
uint16_t millis,
|
||||
log_level_t l,
|
||||
|
@ -80,7 +80,7 @@ public:
|
|||
memset(this->ll_schema, 0, sizeof(this->ll_schema));
|
||||
};
|
||||
|
||||
logline(off_t off,
|
||||
logline(file_off_t off,
|
||||
const struct timeval &tv,
|
||||
log_level_t l,
|
||||
uint8_t mod = 0,
|
||||
|
@ -97,7 +97,7 @@ public:
|
|||
};
|
||||
|
||||
/** @return The offset of the line in the file. */
|
||||
off_t get_offset() const { return this->ll_offset; };
|
||||
file_off_t get_offset() const { return this->ll_offset; };
|
||||
|
||||
uint16_t get_sub_offset() const { return this->ll_sub_offset; };
|
||||
|
||||
|
@ -283,8 +283,8 @@ public:
|
|||
(this->ll_millis <= (rhs.tv_usec / 1000))));
|
||||
};
|
||||
private:
|
||||
off_t ll_offset;
|
||||
time_t ll_time;
|
||||
file_off_t ll_offset;
|
||||
time_t ll_time;
|
||||
unsigned int ll_millis : 10;
|
||||
unsigned int ll_opid : 6;
|
||||
unsigned int ll_sub_offset : 15;
|
||||
|
|
|
@ -506,7 +506,7 @@ static struct json_path_container value_def_handlers = {
|
|||
yajlpp::property_handler("foreign-key")
|
||||
.with_synopsis("<bool>")
|
||||
.with_description("Indicates whether or not this field should be treated as a foreign key for row in another table")
|
||||
.FOR_FIELD(external_log_format::value_def, vd_foreign_key),
|
||||
.for_field(&external_log_format::value_def::vd_foreign_key),
|
||||
|
||||
yajlpp::property_handler("hidden")
|
||||
.with_synopsis("<bool>")
|
||||
|
|
|
@ -123,6 +123,9 @@ bool logfile::exists() const
|
|||
}
|
||||
|
||||
if (::stat(this->lf_filename.c_str(), &st) == -1) {
|
||||
log_error("%s: stat failed -- %s",
|
||||
this->lf_filename.c_str(),
|
||||
strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -318,7 +321,7 @@ logfile::rebuild_result_t logfile::rebuild_index()
|
|||
// line buffer's notion of the file size since it may be compressed.
|
||||
bool has_format = this->lf_format.get() != nullptr;
|
||||
struct rusage begin_rusage;
|
||||
off_t off;
|
||||
file_off_t off;
|
||||
size_t begin_size = this->lf_index.size();
|
||||
bool record_rusage = this->lf_index.size() == 1;
|
||||
off_t begin_index_size = this->lf_index_size;
|
||||
|
@ -350,7 +353,7 @@ logfile::rebuild_result_t logfile::rebuild_index()
|
|||
if (!this->lf_index.empty()) {
|
||||
auto last_line = this->lf_index.end();
|
||||
--last_line;
|
||||
off_t check_line_off = last_line->get_offset();
|
||||
auto check_line_off = last_line->get_offset();
|
||||
auto last_length = ssize_t(this->line_length(last_line, false));
|
||||
|
||||
auto read_result = this->lf_line_buffer.read_range({
|
||||
|
@ -384,6 +387,9 @@ logfile::rebuild_result_t logfile::rebuild_index()
|
|||
auto load_result = this->lf_line_buffer.load_next_line(prev_range);
|
||||
|
||||
if (load_result.isErr()) {
|
||||
log_error("%s: load next line failure -- %s",
|
||||
this->lf_filename.c_str(),
|
||||
load_result.unwrapErr().c_str());
|
||||
this->close();
|
||||
return RR_INVALID;
|
||||
}
|
||||
|
@ -418,6 +424,9 @@ logfile::rebuild_result_t logfile::rebuild_index()
|
|||
|
||||
auto read_result = this->lf_line_buffer.read_range(li.li_file_range);
|
||||
if (read_result.isErr()) {
|
||||
log_error("%s:read failure -- %s",
|
||||
this->lf_filename.c_str(),
|
||||
read_result.unwrapErr().c_str());
|
||||
this->close();
|
||||
return RR_INVALID;
|
||||
}
|
||||
|
|
|
@ -68,8 +68,8 @@ public:
|
|||
* @param total The total size of the file.
|
||||
*/
|
||||
virtual void logfile_indexing(const std::shared_ptr<logfile>& lf,
|
||||
off_t off,
|
||||
size_t total) = 0;
|
||||
file_off_t off,
|
||||
file_size_t total) = 0;
|
||||
};
|
||||
|
||||
struct logfile_activity {
|
||||
|
@ -154,7 +154,7 @@ public:
|
|||
return this->lf_valid_filename;
|
||||
};
|
||||
|
||||
off_t get_index_size() const {
|
||||
file_off_t get_index_size() const {
|
||||
return this->lf_index_size;
|
||||
}
|
||||
|
||||
|
@ -302,7 +302,7 @@ public:
|
|||
|
||||
file_range get_file_range(const_iterator ll, bool include_continues = true) {
|
||||
return {ll->get_offset(),
|
||||
(ssize_t) this->line_length(ll, include_continues)};
|
||||
(file_ssize_t) this->line_length(ll, include_continues)};
|
||||
}
|
||||
|
||||
void read_full_message(const_iterator ll, shared_buffer_ref &msg_out, int max_lines=50);
|
||||
|
@ -392,7 +392,7 @@ protected:
|
|||
std::shared_ptr<log_format> lf_format;
|
||||
std::vector<logline> lf_index;
|
||||
time_t lf_index_time{0};
|
||||
off_t lf_index_size{0};
|
||||
file_off_t lf_index_size{0};
|
||||
bool lf_sort_needed{false};
|
||||
line_buffer lf_line_buffer;
|
||||
int lf_time_offset_line{0};
|
||||
|
@ -406,14 +406,14 @@ protected:
|
|||
text_format_t lf_text_format{text_format_t::TF_UNKNOWN};
|
||||
uint32_t lf_out_of_time_order_count{0};
|
||||
|
||||
nonstd::optional<std::pair<off_t, size_t>> lf_next_line_cache;
|
||||
nonstd::optional<std::pair<file_off_t, size_t>> lf_next_line_cache;
|
||||
};
|
||||
|
||||
class logline_observer {
|
||||
public:
|
||||
virtual ~logline_observer() = default;
|
||||
|
||||
virtual void logline_restart(const logfile &lf, size_t rollback_size) = 0;
|
||||
virtual void logline_restart(const logfile &lf, file_size_t rollback_size) = 0;
|
||||
|
||||
virtual void logline_new_lines(
|
||||
const logfile &lf,
|
||||
|
|
|
@ -395,17 +395,15 @@ void logfile_sub_source::text_attrs_for_line(textview_curses &lv,
|
|||
continue;
|
||||
}
|
||||
|
||||
auto start = this->lss_token_value.c_str();
|
||||
int id_attrs = vc.attrs_for_ident(&start[line_value.lv_origin.lr_start],
|
||||
line_value.lv_origin.sublen(this->lss_token_value));
|
||||
|
||||
line_range ident_range = line_value.lv_origin;
|
||||
if (this->lss_token_flags & RF_FULL) {
|
||||
ident_range = line_value.origin_in_full_msg(
|
||||
this->lss_token_value.c_str(), this->lss_token_value.length());
|
||||
}
|
||||
|
||||
value_out.emplace_back(ident_range, &view_curses::VC_STYLE, id_attrs);
|
||||
value_out.emplace_back(ident_range,
|
||||
&view_curses::VC_ROLE,
|
||||
view_colors::VCR_IDENTIFIER);
|
||||
}
|
||||
|
||||
if (this->lss_token_shift_size) {
|
||||
|
|
|
@ -404,9 +404,9 @@ void readline_command_highlighter(attr_line_t &al, int x)
|
|||
attr_t color_hint_attrs = vc.attrs_for_role(view_colors::VCR_COLOR_HINT);
|
||||
int pnum = PAIR_NUMBER(color_hint_attrs);
|
||||
|
||||
rgb_color::from_str(hash_color).then([&](const auto& rgb_fg) {
|
||||
styling::color_unit::from_str(hash_color).then([&](const auto& rgb_fg) {
|
||||
pnum -= 1;
|
||||
vc.ensure_color_pair(pnum, rgb_fg, rgb_color{});
|
||||
vc.ensure_color_pair(pnum, rgb_fg, styling::color_unit::make_empty());
|
||||
|
||||
al.get_attrs().emplace_back(
|
||||
line_range{cap->c_begin, cap->c_begin + 1},
|
||||
|
|
|
@ -257,7 +257,7 @@ void add_filter_expr_possibilities(readline_curses *rlc, int context, const std:
|
|||
lf->read_full_message(ll, sbr);
|
||||
format->annotate(cl, sbr, sa, values);
|
||||
for (auto& lv : values) {
|
||||
if (!lv.lv_meta.lvm_identifier) {
|
||||
if (!lv.lv_meta.lvm_struct_name.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Scripts
|
||||
|
||||
This directory contains the built-in lnav scripts. The files are
|
||||
turned into C using `bin2c` and compiled into the executable.
|
||||
turned into C using `bin2c` and compiled into the executable. New scripts
|
||||
need to be added to the [scripts.am](scripts.am) file.
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
BUILTIN_LNAVSCRIPTS = \
|
||||
$(srcdir)/scripts/dhclient-summary.lnav \
|
||||
$(srcdir)/scripts/lnav-pop-view.lnav \
|
||||
$(srcdir)/scripts/partition-by-boot.lnav \
|
||||
$(srcdir)/scripts/rename-stdin.lnav \
|
||||
$(srcdir)/scripts/search-for.lnav \
|
||||
$()
|
||||
|
||||
BUILTIN_SHSCRIPTS = \
|
||||
$(srcdir)/scripts/dump-pid.sh \
|
||||
$()
|
|
@ -211,3 +211,18 @@ short term_color_palette::match_color(const lab_color &to_match)
|
|||
|
||||
return lowest_id;
|
||||
}
|
||||
|
||||
namespace styling {
|
||||
|
||||
Result<color_unit, std::string> color_unit::from_str(const string_fragment &sf)
|
||||
{
|
||||
if (sf == "semantic()") {
|
||||
return Ok(color_unit{ semantic{} });
|
||||
}
|
||||
|
||||
auto retval = TRY(rgb_color::from_str(sf));
|
||||
|
||||
return Ok(color_unit{ retval });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -32,21 +32,27 @@
|
|||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "log_level.hh"
|
||||
#include "base/result.h"
|
||||
#include "base/intern_string.hh"
|
||||
#include "mapbox/variant.hpp"
|
||||
|
||||
struct rgb_color {
|
||||
static Result<rgb_color, std::string> from_str(const string_fragment &sf);
|
||||
|
||||
explicit rgb_color(short r = -1, short g = -1, short b = -1)
|
||||
: rc_r(r), rc_g(g), rc_b(b) {
|
||||
: rc_r(r), rc_g(g), rc_b(b)
|
||||
{
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return this->rc_r == -1 && this->rc_g == -1 && this->rc_b == -1;
|
||||
bool empty() const
|
||||
{
|
||||
return this->rc_r == -1 &&
|
||||
this->rc_g == -1 &&
|
||||
this->rc_b == -1;
|
||||
}
|
||||
|
||||
bool operator==(const rgb_color &rhs) const;
|
||||
|
@ -115,8 +121,36 @@ struct term_color_palette {
|
|||
std::vector<term_color> tc_palette;
|
||||
};
|
||||
|
||||
namespace styling {
|
||||
|
||||
struct semantic {};
|
||||
|
||||
class color_unit {
|
||||
public:
|
||||
static Result<color_unit, std::string> from_str(const string_fragment& sf);
|
||||
|
||||
static color_unit make_empty() {
|
||||
return { rgb_color{} };
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return this->cu_value.match(
|
||||
[](semantic) { return false; },
|
||||
[](const rgb_color& rc) { return rc.empty(); }
|
||||
);
|
||||
}
|
||||
|
||||
using variants_t = mapbox::util::variant<semantic, rgb_color>;
|
||||
|
||||
variants_t cu_value;
|
||||
|
||||
private:
|
||||
color_unit(variants_t value) : cu_value(std::move(value)) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
struct style_config {
|
||||
bool sc_semantic{false};
|
||||
std::string sc_color;
|
||||
std::string sc_background_color;
|
||||
bool sc_underline{false};
|
||||
|
|
|
@ -45,7 +45,7 @@ static pcre *xpcre_compile(const char *pattern, int options = 0)
|
|||
options,
|
||||
&errptr,
|
||||
&eoff,
|
||||
NULL)) == NULL) {
|
||||
nullptr)) == nullptr) {
|
||||
fprintf(stderr, "internal error: failed to compile -- %s\n", pattern);
|
||||
fprintf(stderr, "internal error: %s\n", errptr);
|
||||
|
||||
|
@ -395,10 +395,10 @@ void setup_highlights(highlight_map_t &hm)
|
|||
"\\d+"))
|
||||
.with_role(view_colors::VCR_FILE);
|
||||
hm[{highlight_source_t::INTERNAL, "1.stringd"}] = highlighter(xpcre_compile(
|
||||
"\"(?:\\\\.|[^\"])*\""))
|
||||
R"("(?:\\.|[^"])*")"))
|
||||
.with_role(view_colors::VCR_STRING);
|
||||
hm[{highlight_source_t::INTERNAL, "1.strings"}] = highlighter(xpcre_compile(
|
||||
"(?<![A-WY-Za-qstv-z])\'(?:\\\\.|[^'])*\'"))
|
||||
R"((?<![A-WY-Za-qstv-z])'(?:\\.|[^'])*')"))
|
||||
.with_role(view_colors::VCR_STRING);
|
||||
hm[{highlight_source_t::INTERNAL, "1.stringb"}] = highlighter(xpcre_compile(
|
||||
"`(?:\\\\.|[^`])*`"))
|
||||
|
@ -413,7 +413,7 @@ void setup_highlights(highlight_map_t &hm)
|
|||
"^\\@@ .*"))
|
||||
.with_role(view_colors::VCR_DIFF_SECTION);
|
||||
hm[{highlight_source_t::INTERNAL, "0.comment"}] = highlighter(xpcre_compile(
|
||||
"(?<=[\\s;])//.*|/\\*.*\\*/|\\(\\*.*\\*\\)|^#.*|\\s+#.*|dnl.*"))
|
||||
R"((?<=[\s;])//.*|/\*.*\*/|\(\*.*\*\)|^#.*|\s+#.*|dnl.*)"))
|
||||
.with_role(view_colors::VCR_COMMENT);
|
||||
hm[{highlight_source_t::INTERNAL, "javadoc"}] = highlighter(xpcre_compile(
|
||||
"@(?:author|deprecated|exception|file|param|return|see|since|throws|todo|version)"))
|
||||
|
|
|
@ -137,6 +137,12 @@ void textview_curses::reload_config(error_reporter &reporter)
|
|||
iter = this->tc_highlights.erase(iter);
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> vars;
|
||||
auto curr_theme_iter = lnav_config.lc_ui_theme_defs.find(lnav_config.lc_ui_theme);
|
||||
if (curr_theme_iter != lnav_config.lc_ui_theme_defs.end()) {
|
||||
vars = curr_theme_iter->second.lt_vars;
|
||||
}
|
||||
|
||||
for (const auto& theme_name : {DEFAULT_THEME_NAME, lnav_config.lc_ui_theme}) {
|
||||
auto theme_iter = lnav_config.lc_ui_theme_defs.find(theme_name);
|
||||
|
||||
|
@ -171,20 +177,20 @@ void textview_curses::reload_config(error_reporter &reporter)
|
|||
|
||||
fg1 = sc.sc_color;
|
||||
bg1 = sc.sc_background_color;
|
||||
shlex(fg1).eval(fg_color, theme_iter->second.lt_vars);
|
||||
shlex(bg1).eval(bg_color, theme_iter->second.lt_vars);
|
||||
shlex(fg1).eval(fg_color, vars);
|
||||
shlex(bg1).eval(bg_color, vars);
|
||||
|
||||
auto fg = rgb_color::from_str(fg_color)
|
||||
auto fg = styling::color_unit::from_str(fg_color)
|
||||
.unwrapOrElse([&](const auto& msg) {
|
||||
reporter(&sc.sc_color, errmsg);
|
||||
invalid = true;
|
||||
return rgb_color{};
|
||||
return styling::color_unit::make_empty();
|
||||
});
|
||||
auto bg = rgb_color::from_str(bg_color)
|
||||
auto bg = styling::color_unit::from_str(bg_color)
|
||||
.unwrapOrElse([&](const auto& msg) {
|
||||
reporter(&sc.sc_background_color, errmsg);
|
||||
invalid = true;
|
||||
return rgb_color{};
|
||||
return styling::color_unit::make_empty();
|
||||
});
|
||||
if (invalid) {
|
||||
continue;
|
||||
|
@ -200,8 +206,7 @@ void textview_curses::reload_config(error_reporter &reporter)
|
|||
highlighter(code)
|
||||
.with_pattern(hl_pair.second.hc_regex)
|
||||
.with_attrs(attrs != 0 ? attrs : -1)
|
||||
.with_color(fg, bg)
|
||||
.with_semantic(sc.sc_semantic);
|
||||
.with_color(fg, bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Themes
|
||||
|
||||
This directory contains the built-in theme definitions. The files are
|
||||
turned into C using `bin2c` and compiled into the executable.
|
||||
turned into C using `bin2c` and compiled into the executable. New themes
|
||||
need to be added to the [themes.am](themes.am) file.
|
||||
|
|
|
@ -3,13 +3,17 @@
|
|||
"ui": {
|
||||
"theme-defs": {
|
||||
"default": {
|
||||
"vars": {
|
||||
"semantic_highlight_color": "semantic()"
|
||||
},
|
||||
"styles": {
|
||||
"text": {
|
||||
"color": "Silver",
|
||||
"background-color": "Black"
|
||||
},
|
||||
"identifier": {
|
||||
"background-color": ""
|
||||
"background-color": "",
|
||||
"color": "semantic()"
|
||||
},
|
||||
"alt-text": {
|
||||
"color": "Silver",
|
||||
|
@ -158,21 +162,21 @@
|
|||
},
|
||||
"highlights": {
|
||||
"colors": {
|
||||
"pattern": "(?:#[a-fA-F0-9]{6}|#[a-fA-F0-9]{3})",
|
||||
"pattern": "(?:#[a-fA-F0-9]{6}|#[a-fA-F0-9]{3}\\b)",
|
||||
"style": {
|
||||
"semantic": true
|
||||
"color": "${semantic_highlight_color}"
|
||||
}
|
||||
},
|
||||
"ipv4": {
|
||||
"pattern": "\\b(?<!\\d\\.)\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b(?!\\.\\d)",
|
||||
"style": {
|
||||
"semantic": true
|
||||
"color": "${semantic_highlight_color}"
|
||||
}
|
||||
},
|
||||
"xml": {
|
||||
"pattern": "</?([^ >=]+)[^>]*>",
|
||||
"style": {
|
||||
"semantic": true
|
||||
"color": "${semantic_highlight_color}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,13 @@
|
|||
"blue": "#729fcf",
|
||||
"cyan": "#34e2e2",
|
||||
"green": "#8ae234",
|
||||
"white": "#ffffff"
|
||||
"white": "#ffffff",
|
||||
"semantic_highlight_color": "semantic()"
|
||||
},
|
||||
"styles": {
|
||||
"identifier": {
|
||||
"background-color": ""
|
||||
"background-color": "",
|
||||
"color": "semantic()"
|
||||
},
|
||||
"text": {
|
||||
"color": "$white",
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
{
|
||||
"$schema": "https://lnav.org/schemas/config-v1.schema.json",
|
||||
"ui": {
|
||||
"theme-defs": {
|
||||
"grayscale": {
|
||||
"vars": {
|
||||
"black": "#2d2a2e",
|
||||
"red": "#f92772",
|
||||
"green": "#a7e22e",
|
||||
"yellow": "#fe9720",
|
||||
"blue": "#5394ec",
|
||||
"magenta": "#ae81ff",
|
||||
"cyan": "#66d9ee",
|
||||
"white": "#f6f6f6",
|
||||
"plaintext": "#ccc"
|
||||
},
|
||||
"styles": {
|
||||
"identifier": {
|
||||
"background-color": "",
|
||||
"color": "",
|
||||
"bold": true
|
||||
},
|
||||
"text": {
|
||||
"color": "",
|
||||
"background-color": ""
|
||||
},
|
||||
"alt-text": {
|
||||
"color": "",
|
||||
"background-color": "",
|
||||
"bold": true
|
||||
},
|
||||
"ok": {
|
||||
"color": "$green",
|
||||
"bold": true
|
||||
},
|
||||
"error": {
|
||||
"color": "$red",
|
||||
"bold": true
|
||||
},
|
||||
"warning": {
|
||||
"color": "$yellow",
|
||||
"bold": true
|
||||
},
|
||||
"hidden": {
|
||||
"color": "$yellow",
|
||||
"bold": true
|
||||
},
|
||||
"adjusted-time": {
|
||||
"color": "$magenta"
|
||||
},
|
||||
"skewed-time": {
|
||||
"color": "$yellow"
|
||||
},
|
||||
"offset-time": {
|
||||
"color": "$cyan"
|
||||
},
|
||||
"invalid-msg": {
|
||||
"color": "$yellow"
|
||||
},
|
||||
"focused": {
|
||||
"color": "$black",
|
||||
"background-color": "$plaintext"
|
||||
},
|
||||
"disabled-focused": {
|
||||
"color": "$plaintext",
|
||||
"background-color": "#333"
|
||||
},
|
||||
"popup": {
|
||||
"color": "$plaintext",
|
||||
"background-color": "#626262"
|
||||
},
|
||||
"scrollbar": {
|
||||
"color": "$black",
|
||||
"background-color": "#888"
|
||||
}
|
||||
},
|
||||
"status-styles": {
|
||||
"disabled-title": {
|
||||
"color": "#5394ec",
|
||||
"background-color": "#353535",
|
||||
"bold": true
|
||||
},
|
||||
"title": {
|
||||
"color": "#f6f6f6",
|
||||
"background-color": "#8a8a8a",
|
||||
"bold": true
|
||||
},
|
||||
"subtitle": {
|
||||
"color": "#e4e4e4",
|
||||
"background-color": "#626262",
|
||||
"bold": true
|
||||
},
|
||||
"title-hotkey": {
|
||||
"color": "$black",
|
||||
"background-color": "#5394ec",
|
||||
"underline": true
|
||||
},
|
||||
"hotkey": {
|
||||
"color": "#fff",
|
||||
"background-color": "#353535",
|
||||
"underline": true
|
||||
},
|
||||
"text": {
|
||||
"color": "#f6f6f6",
|
||||
"background-color": "#353535"
|
||||
},
|
||||
"warn": {
|
||||
"color": "$yellow",
|
||||
"background-color": "#353535"
|
||||
},
|
||||
"alert": {
|
||||
"color": "$red",
|
||||
"background-color": "#353535"
|
||||
},
|
||||
"active": {
|
||||
"color": "$green",
|
||||
"background-color": "#353535"
|
||||
},
|
||||
"inactive": {
|
||||
"color": "#555",
|
||||
"background-color": "#2f2f2f"
|
||||
}
|
||||
},
|
||||
"log-level-styles": {
|
||||
"warning": {
|
||||
"color": "$yellow"
|
||||
},
|
||||
"error": {
|
||||
"color": "$red"
|
||||
},
|
||||
"critical": {
|
||||
"color": "$red"
|
||||
},
|
||||
"fatal": {
|
||||
"color": "$red"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,11 +11,13 @@
|
|||
"blue": "#5394ec",
|
||||
"magenta": "#ae81ff",
|
||||
"cyan": "#66d9ee",
|
||||
"white": "#808080"
|
||||
"white": "#808080",
|
||||
"semantic_highlight_color": "semantic()"
|
||||
},
|
||||
"styles": {
|
||||
"identifier": {
|
||||
"background-color": "$black"
|
||||
"background-color": "$black",
|
||||
"color": "semantic()"
|
||||
},
|
||||
"text": {
|
||||
"color": "#f6f6f6",
|
||||
|
|
|
@ -11,11 +11,13 @@
|
|||
"blue": "#5394ec",
|
||||
"magenta": "#ff70ff",
|
||||
"cyan": "#33cccc",
|
||||
"white": "#d6deeb"
|
||||
"white": "#d6deeb",
|
||||
"semantic_highlight_color": "semantic()"
|
||||
},
|
||||
"styles": {
|
||||
"identifier": {
|
||||
"background-color": "#011627"
|
||||
"background-color": "#011627",
|
||||
"color": "semantic()"
|
||||
},
|
||||
"text": {
|
||||
"color": "#d6deeb",
|
||||
|
|
|
@ -20,11 +20,13 @@
|
|||
"violet": "#6c71c4",
|
||||
"blue": "#268bd2",
|
||||
"cyan": "#2aa198",
|
||||
"green": "#859900"
|
||||
"green": "#859900",
|
||||
"semantic_highlight_color": "semantic()"
|
||||
},
|
||||
"styles": {
|
||||
"identifier": {
|
||||
"background-color": "$base03"
|
||||
"background-color": "$base03",
|
||||
"color": "semantic()"
|
||||
},
|
||||
"text": {
|
||||
"color": "$base0",
|
||||
|
|
|
@ -20,11 +20,13 @@
|
|||
"violet": "#6c71c4",
|
||||
"blue": "#268bd2",
|
||||
"cyan": "#2aa198",
|
||||
"green": "#859900"
|
||||
"green": "#859900",
|
||||
"semantic_highlight_color": "semantic()"
|
||||
},
|
||||
"styles": {
|
||||
"identifier": {
|
||||
"background-color": "$base3"
|
||||
"background-color": "$base3",
|
||||
"color": "semantic()"
|
||||
},
|
||||
"text": {
|
||||
"color": "$base00",
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
THEME_FILES = \
|
||||
$(srcdir)/%reldir%/default-theme.json \
|
||||
$(srcdir)/%reldir%/eldar.json \
|
||||
$(srcdir)/%reldir%/grayscale.json \
|
||||
$(srcdir)/%reldir%/monocai.json \
|
||||
$(srcdir)/%reldir%/night-owl.json \
|
||||
$(srcdir)/%reldir%/solarized-dark.json \
|
||||
$(srcdir)/%reldir%/solarized-light.json \
|
||||
$()
|
|
@ -81,10 +81,9 @@ void top_status_source::update_time()
|
|||
|
||||
void top_status_source::update_filename(listview_curses *lc)
|
||||
{
|
||||
status_field & sf_partition = this->tss_fields[TSF_PARTITION_NAME];
|
||||
status_field & sf_format = this->tss_fields[TSF_FORMAT];
|
||||
status_field & sf_filename = this->tss_fields[TSF_FILENAME];
|
||||
struct line_range lr(0);
|
||||
auto &sf_partition = this->tss_fields[TSF_PARTITION_NAME];
|
||||
auto &sf_format = this->tss_fields[TSF_FORMAT];
|
||||
auto &sf_filename = this->tss_fields[TSF_FILENAME];
|
||||
|
||||
if (lc->get_inner_height() > 0) {
|
||||
string_attrs_t::const_iterator line_attr;
|
||||
|
|
|
@ -260,7 +260,13 @@ void view_curses::mvwattrline(WINDOW *window,
|
|||
if (!has_fg) {
|
||||
memset(fg_color, -1, line_width_chars * sizeof(short));
|
||||
}
|
||||
fill(&fg_color[attr_range.lr_start], &fg_color[attr_range.lr_end], (short) iter->sa_value.sav_int);
|
||||
short attr_fg = iter->sa_value.sav_int;
|
||||
if (attr_fg == view_colors::MATCH_COLOR_SEMANTIC) {
|
||||
attr_fg = vc.color_for_ident(
|
||||
&line[iter->sa_range.lr_start],
|
||||
iter->sa_range.length());
|
||||
}
|
||||
fill(&fg_color[attr_range.lr_start], &fg_color[attr_range.lr_end], attr_fg);
|
||||
has_fg = true;
|
||||
continue;
|
||||
}
|
||||
|
@ -269,7 +275,13 @@ void view_curses::mvwattrline(WINDOW *window,
|
|||
if (!has_bg) {
|
||||
memset(bg_color, -1, line_width_chars * sizeof(short));
|
||||
}
|
||||
fill(bg_color + attr_range.lr_start, bg_color + attr_range.lr_end, (short) iter->sa_value.sav_int);
|
||||
short attr_bg = iter->sa_value.sav_int;
|
||||
if (attr_bg == view_colors::MATCH_COLOR_SEMANTIC) {
|
||||
attr_bg = vc.color_for_ident(
|
||||
&line[iter->sa_range.lr_start],
|
||||
iter->sa_range.length());
|
||||
}
|
||||
fill(bg_color + attr_range.lr_start, bg_color + attr_range.lr_end, attr_bg);
|
||||
has_bg = true;
|
||||
continue;
|
||||
}
|
||||
|
@ -307,6 +319,24 @@ void view_curses::mvwattrline(WINDOW *window,
|
|||
int ch_width = min(awidth, (line_width_chars - attr_range.lr_start));
|
||||
cchar_t row_ch[ch_width + 1];
|
||||
|
||||
if (attrs & (A_LEFT|A_RIGHT)) {
|
||||
short pair_fg, pair_bg;
|
||||
|
||||
pair_content(color_pair, &pair_fg, &pair_bg);
|
||||
if (attrs & A_LEFT) {
|
||||
pair_fg = vc.color_for_ident(
|
||||
&line[iter->sa_range.lr_start],
|
||||
iter->sa_range.length());
|
||||
}
|
||||
if (attrs & A_RIGHT) {
|
||||
pair_bg = vc.color_for_ident(
|
||||
&line[iter->sa_range.lr_start],
|
||||
iter->sa_range.length());
|
||||
}
|
||||
color_pair = vc.ensure_color_pair(pair_fg, pair_bg);
|
||||
attrs &= ~(A_LEFT|A_RIGHT);
|
||||
}
|
||||
|
||||
mvwin_wchnstr(window, y, x_pos, row_ch, ch_width);
|
||||
for (int lpc = 0; lpc < ch_width; lpc++) {
|
||||
bool clear_rev = false;
|
||||
|
@ -535,7 +565,16 @@ inline attr_t attr_for_colors(int &pair_base, short fg, short bg)
|
|||
init_pair(pair, fg, bg);
|
||||
}
|
||||
|
||||
return COLOR_PAIR(pair);
|
||||
auto retval = COLOR_PAIR(pair);
|
||||
|
||||
if (fg == view_colors::MATCH_COLOR_SEMANTIC) {
|
||||
retval |= A_LEFT;
|
||||
}
|
||||
if (bg == view_colors::MATCH_COLOR_SEMANTIC) {
|
||||
retval |= A_RIGHT;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
pair<attr_t, attr_t> view_colors::to_attrs(
|
||||
|
@ -556,13 +595,13 @@ pair<attr_t, attr_t> view_colors::to_attrs(
|
|||
shlex(fg1).eval(fg_color, lt.lt_vars);
|
||||
shlex(bg1).eval(bg_color, lt.lt_vars);
|
||||
|
||||
auto fg = rgb_color::from_str(fg_color).unwrapOrElse([&](const auto& msg) {
|
||||
auto fg = styling::color_unit::from_str(fg_color).unwrapOrElse([&](const auto& msg) {
|
||||
reporter(&sc.sc_color, msg);
|
||||
return rgb_color{};
|
||||
return styling::color_unit::make_empty();
|
||||
});
|
||||
auto bg = rgb_color::from_str(bg_color).unwrapOrElse([&](const auto& msg) {
|
||||
auto bg = styling::color_unit::from_str(bg_color).unwrapOrElse([&](const auto& msg) {
|
||||
reporter(&sc.sc_background_color, msg);
|
||||
return rgb_color{};
|
||||
return styling::color_unit::make_empty();
|
||||
});
|
||||
|
||||
attr_t retval1 = attr_for_colors(pair_base,
|
||||
|
@ -590,7 +629,7 @@ void view_colors::init_roles(const lnav_theme <,
|
|||
string err;
|
||||
|
||||
if (COLORS == 256) {
|
||||
const style_config &ident_sc = lt.lt_style_identifier;
|
||||
const auto &ident_sc = lt.lt_style_identifier;
|
||||
int ident_bg = (lnav_config.lc_ui_default_colors ? -1 : COLOR_BLACK);
|
||||
|
||||
if (!ident_sc.sc_background_color.empty()) {
|
||||
|
@ -673,6 +712,8 @@ void view_colors::init_roles(const lnav_theme <,
|
|||
this->vc_role_colors[VCR_TEXT].second |= A_DIM;
|
||||
}
|
||||
this->vc_role_colors[VCR_SEARCH] = make_pair(A_REVERSE, A_REVERSE);
|
||||
this->vc_role_colors[VCR_IDENTIFIER] = this->to_attrs(
|
||||
color_pair_base, lt, lt.lt_style_identifier, lt.lt_style_text, reporter);
|
||||
this->vc_role_colors[VCR_OK] = this->to_attrs(color_pair_base,
|
||||
lt, lt.lt_style_ok,
|
||||
lt.lt_style_text,
|
||||
|
@ -853,7 +894,7 @@ void view_colors::init_roles(const lnav_theme <,
|
|||
this->vc_role_colors[VCR_HIGH_THRESHOLD] = this->to_attrs(
|
||||
color_pair_base, lt, lt.lt_style_high_threshold, lt.lt_style_text, reporter);
|
||||
|
||||
for (log_level_t level = static_cast<log_level_t>(LEVEL_UNKNOWN + 1);
|
||||
for (auto level = static_cast<log_level_t>(LEVEL_UNKNOWN + 1);
|
||||
level < LEVEL__MAX;
|
||||
level = static_cast<log_level_t>(level + 1)) {
|
||||
auto level_iter = lt.lt_level_styles.find(level);
|
||||
|
@ -875,8 +916,8 @@ void view_colors::init_roles(const lnav_theme <,
|
|||
|
||||
int view_colors::ensure_color_pair(int &pair_base, short fg, short bg)
|
||||
{
|
||||
require(fg >= -1);
|
||||
require(bg >= -1);
|
||||
require(fg >= -100);
|
||||
require(bg >= -100);
|
||||
|
||||
auto index_pair = make_pair(fg, bg);
|
||||
auto existing = this->vc_dyn_pairs.find(index_pair);
|
||||
|
@ -902,38 +943,78 @@ int view_colors::ensure_color_pair(int &pair_base, short fg, short bg)
|
|||
return retval;
|
||||
}
|
||||
|
||||
int view_colors::ensure_color_pair(int &pair_base, const rgb_color &rgb_fg, const rgb_color &rgb_bg)
|
||||
int view_colors::ensure_color_pair(int &pair_base,
|
||||
const styling::color_unit &rgb_fg,
|
||||
const styling::color_unit &rgb_bg)
|
||||
{
|
||||
int fg = this->match_color(rgb_fg);
|
||||
int bg = this->match_color(rgb_bg);
|
||||
auto fg = this->match_color(rgb_fg);
|
||||
auto bg = this->match_color(rgb_bg);
|
||||
|
||||
return this->ensure_color_pair(pair_base, fg, bg);
|
||||
}
|
||||
|
||||
int view_colors::match_color(const rgb_color &color)
|
||||
short view_colors::match_color(const styling::color_unit &color) const
|
||||
{
|
||||
if (color.empty()) {
|
||||
return -1;
|
||||
}
|
||||
return color.cu_value.match(
|
||||
[](styling::semantic) {
|
||||
return MATCH_COLOR_SEMANTIC;
|
||||
},
|
||||
[](const rgb_color& color) {
|
||||
if (color.empty()) {
|
||||
return MATCH_COLOR_DEFAULT;
|
||||
}
|
||||
|
||||
return vc_active_palette->match_color(lab_color(color));
|
||||
return vc_active_palette->match_color(lab_color(color));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
attr_t view_colors::attrs_for_ident(const char *str, size_t len) const
|
||||
int view_colors::color_for_ident(const char *str, size_t len) const
|
||||
{
|
||||
unsigned long index = crc32(1, (const Bytef*)str, len);
|
||||
attr_t retval;
|
||||
int retval;
|
||||
|
||||
if (COLORS >= 256) {
|
||||
if (str[0] == '#' && (len == 4 || len == 7)) {
|
||||
auto fg_res = styling::color_unit::from_str(string_fragment(str, 0, len));
|
||||
if (fg_res.isOk()) {
|
||||
return this->match_color(fg_res.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long offset = index % HI_COLOR_COUNT;
|
||||
retval = COLOR_PAIR(VC_ANSI_END + offset);
|
||||
auto cpair = COLOR_PAIR(VC_ANSI_END + offset);
|
||||
|
||||
short fg, bg;
|
||||
int pnum = PAIR_NUMBER(retval);
|
||||
auto pnum = PAIR_NUMBER(cpair);
|
||||
pair_content(pnum, &fg, &bg);
|
||||
retval = fg;
|
||||
}
|
||||
else {
|
||||
retval = A_BOLD;
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
attr_t view_colors::attrs_for_ident(const char *str, size_t len)
|
||||
{
|
||||
auto retval = this->attrs_for_role(VCR_IDENTIFIER);
|
||||
|
||||
if (retval & (A_LEFT|A_RIGHT)) {
|
||||
auto color_pair = PAIR_NUMBER(retval);
|
||||
short pair_fg, pair_bg;
|
||||
|
||||
pair_content(color_pair, &pair_fg, &pair_bg);
|
||||
if (retval & A_LEFT) {
|
||||
pair_fg = this->color_for_ident(str, len);
|
||||
}
|
||||
if (retval & A_RIGHT) {
|
||||
pair_bg = this->color_for_ident(str, len);
|
||||
}
|
||||
color_pair = this->ensure_color_pair(pair_fg, pair_bg);
|
||||
retval &= ~(A_COLOR|A_LEFT|A_RIGHT);
|
||||
retval |= COLOR_PAIR(color_pair);
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
|
|
@ -189,6 +189,7 @@ public:
|
|||
VCR_NONE = -1,
|
||||
|
||||
VCR_TEXT, /*< Raw text. */
|
||||
VCR_IDENTIFIER,
|
||||
VCR_SEARCH, /*< A search hit. */
|
||||
VCR_OK,
|
||||
VCR_ERROR, /*< An error message. */
|
||||
|
@ -283,13 +284,15 @@ public:
|
|||
return this->vc_role_reverse_colors[role];
|
||||
};
|
||||
|
||||
attr_t attrs_for_ident(const char *str, size_t len) const;
|
||||
int color_for_ident(const char *str, size_t len) const;
|
||||
|
||||
attr_t attrs_for_ident(intern_string_t str) const {
|
||||
attr_t attrs_for_ident(const char *str, size_t len);
|
||||
|
||||
attr_t attrs_for_ident(intern_string_t str) {
|
||||
return this->attrs_for_ident(str.get(), str.size());
|
||||
}
|
||||
|
||||
attr_t attrs_for_ident(const std::string &str) const {
|
||||
attr_t attrs_for_ident(const std::string &str) {
|
||||
return this->attrs_for_ident(str.c_str(), str.length());
|
||||
};
|
||||
|
||||
|
@ -299,13 +302,19 @@ public:
|
|||
return this->ensure_color_pair(this->vc_color_pair_end, fg, bg);
|
||||
}
|
||||
|
||||
int ensure_color_pair(int &pair_base, const rgb_color &fg, const rgb_color &bg);
|
||||
int ensure_color_pair(int &pair_base,
|
||||
const styling::color_unit &fg,
|
||||
const styling::color_unit &bg);
|
||||
|
||||
int ensure_color_pair(const rgb_color &fg, const rgb_color &bg) {
|
||||
int ensure_color_pair(const styling::color_unit &fg,
|
||||
const styling::color_unit &bg) {
|
||||
return this->ensure_color_pair(this->vc_color_pair_end, fg, bg);
|
||||
}
|
||||
|
||||
int match_color(const rgb_color &color);
|
||||
static constexpr short MATCH_COLOR_DEFAULT = -1;
|
||||
static constexpr short MATCH_COLOR_SEMANTIC = -10;
|
||||
|
||||
short match_color(const styling::color_unit &color) const;
|
||||
|
||||
static inline int ansi_color_pair_index(int fg, int bg)
|
||||
{
|
||||
|
|
|
@ -631,7 +631,7 @@ struct json_path_handler : public json_path_handler_base {
|
|||
yajlpp_generator gen(handle);
|
||||
relative_time rt;
|
||||
|
||||
rt.from_timeval({ field.count() });
|
||||
rt.from_timeval({ field.count(), 0 });
|
||||
return gen(rt.to_string());
|
||||
};
|
||||
this->jph_field_getter = [args...](void *root, nonstd::optional<std::string> name) {
|
||||
|
|
|
@ -62,6 +62,9 @@ add_executable(test_top_status test_top_status.cc)
|
|||
target_link_libraries(test_top_status diag PkgConfig::libpcre)
|
||||
add_test(NAME test_top_status COMMAND test_top_status)
|
||||
|
||||
add_executable(drive_view_colors drive_view_colors.cc)
|
||||
target_link_libraries(drive_view_colors diag PkgConfig::ncursesw)
|
||||
|
||||
add_executable(drive_vt52_curses drive_vt52_curses.cc)
|
||||
target_link_libraries(drive_vt52_curses diag PkgConfig::ncursesw)
|
||||
|
||||
|
|
|
@ -39,11 +39,10 @@ class test_colors : public view_curses {
|
|||
|
||||
public:
|
||||
test_colors()
|
||||
: tc_window(NULL) {
|
||||
|
||||
: tc_window(nullptr) {
|
||||
}
|
||||
|
||||
void do_update(void) {
|
||||
void do_update() override {
|
||||
view_colors &vc = view_colors::singleton();
|
||||
int lpc;
|
||||
|
||||
|
@ -54,21 +53,16 @@ public:
|
|||
line_range lr;
|
||||
|
||||
snprintf(label, sizeof(label), "This is line: %d", lpc);
|
||||
attrs = view_colors::singleton().attrs_for_ident(label);
|
||||
attrs = vc.attrs_for_ident(label);
|
||||
al = label;
|
||||
al.get_attrs().push_back(string_attr(
|
||||
al.get_attrs().emplace_back(
|
||||
line_range(0, -1),
|
||||
&view_curses::VC_STYLE,
|
||||
attrs
|
||||
));
|
||||
);
|
||||
lr.lr_start = 0;
|
||||
lr.lr_end = 40;
|
||||
this->mvwattrline(this->tc_window,
|
||||
lpc,
|
||||
0,
|
||||
al,
|
||||
lr,
|
||||
view_colors::VCR_TEXT);
|
||||
test_colors::mvwattrline(this->tc_window, lpc, 0, al, lr);
|
||||
}
|
||||
|
||||
attr_line_t al;
|
||||
|
@ -76,14 +70,9 @@ public:
|
|||
|
||||
al = "before <123> after";
|
||||
al.with_attr({line_range{8, 11}, &VC_STYLE,
|
||||
(int64_t) vc.ansi_color_pair(COLOR_CYAN, COLOR_BLACK)});
|
||||
(int64_t) view_colors::ansi_color_pair(COLOR_CYAN, COLOR_BLACK)});
|
||||
al.with_attr({line_range{8, 11}, &VC_STYLE, A_REVERSE});
|
||||
this->mvwattrline(this->tc_window,
|
||||
lpc,
|
||||
0,
|
||||
al,
|
||||
lr,
|
||||
view_colors::VCR_TEXT);
|
||||
test_colors::mvwattrline(this->tc_window, lpc, 0, al, lr);
|
||||
};
|
||||
|
||||
WINDOW *tc_window;
|
||||
|
@ -107,7 +96,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
view_colors::singleton().init();
|
||||
view_colors::init();
|
||||
curs_set(0);
|
||||
tc.tc_window = win;
|
||||
tc.do_update();
|
||||
|
|
|
@ -9,40 +9,38 @@ S -1 ┋
|
|||
A └ normal, normal, normal
|
||||
CSI Erase all
|
||||
S 1 ┋This is line: 0 ┋
|
||||
A └ bold │
|
||||
A ········································└ carriage-return
|
||||
A ···············└ carriage-return
|
||||
S 2 ┋This is line: 1 ┋
|
||||
A ········································└ carriage-return
|
||||
A ···············└ carriage-return
|
||||
S 3 ┋This is line: 2 ┋
|
||||
A ········································└ carriage-return
|
||||
A ···············└ carriage-return
|
||||
S 4 ┋This is line: 3 ┋
|
||||
A ········································└ carriage-return
|
||||
A ···············└ carriage-return
|
||||
S 5 ┋This is line: 4 ┋
|
||||
A ········································└ carriage-return
|
||||
A ···············└ carriage-return
|
||||
S 6 ┋This is line: 5 ┋
|
||||
A ········································└ carriage-return
|
||||
A ···············└ carriage-return
|
||||
S 7 ┋This is line: 6 ┋
|
||||
A ········································└ carriage-return
|
||||
A ···············└ carriage-return
|
||||
S 8 ┋This is line: 7 ┋
|
||||
A ········································└ carriage-return
|
||||
A ···············└ carriage-return
|
||||
S 9 ┋This is line: 8 ┋
|
||||
A ········································└ carriage-return
|
||||
A ···············└ carriage-return
|
||||
S 10 ┋This is line: 9 ┋
|
||||
A ········································└ carriage-return
|
||||
A ···············└ carriage-return
|
||||
S 11 ┋This is line: 10 ┋
|
||||
A ········································└ carriage-return
|
||||
A ················└ carriage-return
|
||||
S 12 ┋This is line: 11 ┋
|
||||
A ········································└ carriage-return
|
||||
A ················└ carriage-return
|
||||
S 13 ┋This is line: 12 ┋
|
||||
A ········································└ carriage-return
|
||||
A ················└ carriage-return
|
||||
S 14 ┋This is line: 13 ┋
|
||||
A ········································└ carriage-return
|
||||
A ················└ carriage-return
|
||||
S 15 ┋This is line: 14 ┋
|
||||
A ········································└ carriage-return
|
||||
A ················└ carriage-return
|
||||
S 16 ┋This is line: 15 ┋
|
||||
A ········································└ carriage-return
|
||||
A ················└ carriage-return
|
||||
S 17 ┋before <123> after ┋
|
||||
A └ normal│ │
|
||||
A ········└ fg(#008080), inverse
|
||||
A ···········└ normal
|
||||
S 17 ┋ ┋
|
||||
|
|
179
test/xpath_tui.0
179
test/xpath_tui.0
|
@ -88,12 +88,11 @@ A ····································
|
|||
A ·······································································└ normal, fg(#000000), bg(#c0c0c0)
|
||||
S 19 ┋ ┋
|
||||
S 20 ┋→ ` logfile_xml_msg.0 0.0 B — x┋
|
||||
A ··├ fg(#008000), bg(#c0c0c0) │ │ │
|
||||
A └┛ alt │ │ │ │
|
||||
A ···└ fg(#000000), bg(#c0c0c0) │ │ │
|
||||
A ························└ fg(#c0c0c0), bg(#c0c0c0) │
|
||||
A ······························└ fg(#000000), bg(#c0c0c0), bold │
|
||||
A ································└ normal, fg(#000000), bg(#c0c0c0) │
|
||||
A ··├ fg(#008000), bg(#c0c0c0) │ │
|
||||
A └┛ alt │ │ │
|
||||
A ···└ fg(#000000), bg(#c0c0c0) │ │
|
||||
A ························└ bold│ │
|
||||
A ······························└ normal, fg(#000000), bg(#c0c0c0) │
|
||||
A ················································································└ normal
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
|
@ -118,14 +117,13 @@ A ·············└ bold
|
|||
A ··················└ normal, fg(#000000), bg(#c0c0c0)
|
||||
S 20 ┋ 64.0 B 2020-12-10 06:56:41.061 — 2020-12-10 06:56:41. ┋
|
||||
A ···························└ backspace │
|
||||
A ··························└ fg(#c0c0c0), bg(#c0c0c0) │
|
||||
A ······························└ fg(#000000), bg(#c0c0c0), bold │
|
||||
A ································└ normal, fg(#000000), bg(#c0c0c0) │
|
||||
A ··························└ bold │
|
||||
A ······························└ normal, fg(#000000), bg(#c0c0c0) │
|
||||
A ···············································································└ carriage-return
|
||||
S 22 ┋ ┋
|
||||
A └ normal, normal
|
||||
S 20 ┋ 628 ┋
|
||||
A ·························└ fg(#c0c0c0), bg(#c0c0c0)
|
||||
A ·························└ fg(#000000), bg(#c0c0c0), bold
|
||||
A ····························└ carriage-return
|
||||
S 22 ┋ ┋
|
||||
A └ normal, normal
|
||||
|
@ -135,94 +133,66 @@ A ·······························└ fg(#c0c
|
|||
S 1 ┋ xml_msg_log ┋
|
||||
A ······································································└ carriage-return
|
||||
S 2 ┋x x ┋
|
||||
A ├ normal, bold
|
||||
A └┛ alt│
|
||||
A ·└ normal
|
||||
A ······└ carriage-return
|
||||
S 3 ┋x </head> ┋
|
||||
A ├ bold ││
|
||||
A └┛ alt ││
|
||||
A ·└ normal││
|
||||
A ·····└ bold
|
||||
A ·········└ normal
|
||||
A └┛ alt │
|
||||
A ·└ normal │
|
||||
A ··········└ carriage-return
|
||||
S 4 ┋x <reply id="2"> ┋
|
||||
A ├ bold ││ ││ ││
|
||||
A └┛ alt ││ ││ ││
|
||||
A ·└ normal││ ││ ││
|
||||
A ····└ bold│ ││ ││
|
||||
A ·········└ normal│
|
||||
A └┛ alt │ ││ ││
|
||||
A ·└ normal │ ││ ││
|
||||
A ··········└ fg(#008080)
|
||||
A ············└ normal
|
||||
A ·············└ fg(#008000), bold
|
||||
A ················└ normal
|
||||
A ·················└ carriage-return
|
||||
S 5 ┋x <status> ┋
|
||||
A ├ bold│ ││
|
||||
A └┛ alt│ ││
|
||||
A ·└ normal ││
|
||||
A ······└ bold││
|
||||
A ············└ normal
|
||||
A └┛ alt │
|
||||
A ·└ normal │
|
||||
A ·············└ carriage-return
|
||||
S 6 ┋x <result>OK</result> ┋
|
||||
A ├ bold ││ │ │ ││
|
||||
A └┛ alt ││ │ │ ││
|
||||
A ·└ normal │ │ ││
|
||||
A ·······└ bold, normal ││
|
||||
A ········└ bold│ │ ││
|
||||
A ··············└ normal ││
|
||||
A ···················└ bold││
|
||||
A ·························└ normal
|
||||
A ·└ normal │
|
||||
A └┛ alt │ │
|
||||
A ·└ normal │
|
||||
A ·······└ normal │
|
||||
A ··························└ carriage-return
|
||||
S 7 ┋x </status> ┋
|
||||
A ├ bold │ ││
|
||||
A └┛ alt │ ││
|
||||
A ·└ normal ││
|
||||
A ·······└ bold││
|
||||
A ·············└ normal
|
||||
A └┛ alt │
|
||||
A ·└ normal │
|
||||
A ··············└ carriage-return
|
||||
S 8 ┋x <name> ┋
|
||||
A ├ bold│ ││
|
||||
A └┛ alt│ ││
|
||||
A ·└ normal ││
|
||||
A ······└ bold
|
||||
A ··········└ normal
|
||||
A └┛ alt │
|
||||
A ·└ normal │
|
||||
A ···········└ carriage-return
|
||||
S 9 ┋x x ┋
|
||||
A ├ bold ││
|
||||
A ·└ normal
|
||||
A └┛ alt ││
|
||||
A ·└ normal
|
||||
A ·······└ bold, normal
|
||||
A ·······└ normal
|
||||
A ········└ carriage-return
|
||||
S 10 ┋x </name> ┋
|
||||
A ├ bold │ ││
|
||||
A └┛ alt │ ││
|
||||
A ·└ normal ││
|
||||
A ·······└ bold
|
||||
A ···········└ normal
|
||||
A └┛ alt │
|
||||
A ·└ normal │
|
||||
A ············└ carriage-return
|
||||
S 11 ┋x </reply> x┋
|
||||
A ├ bold │ ││
|
||||
A └┛ alt │ ││
|
||||
A ·└ normal │ ││
|
||||
A ·····└ bold ││
|
||||
A ··········└ normal ││
|
||||
A └┛ alt ││
|
||||
A ·└ normal ││
|
||||
A ···············································································└ fg(#000000), bg(#c0c0c0)
|
||||
A ················································································└ normal
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 12 ┋x <technical-track> x┋
|
||||
A ├ fg(#000000), bg(#c0c0c0), normal, bold ││
|
||||
A └┛ alt │ ││
|
||||
A ·└ normal │ ││
|
||||
A ····└ bold │ ││
|
||||
A ···················└ normal ││
|
||||
A ├ fg(#000000), bg(#c0c0c0) ││
|
||||
A └┛ alt ││
|
||||
A ·└ normal ││
|
||||
A ···············································································└ fg(#000000), bg(#c0c0c0)
|
||||
A ················································································└ normal
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 13 ┋x x x┋
|
||||
A ├ fg(#000000), bg(#c0c0c0), normal, bold ││
|
||||
A ├ fg(#000000), bg(#c0c0c0) ││
|
||||
A └┛ alt ││
|
||||
A ·└ normal ││
|
||||
A ···············································································└ fg(#000000), bg(#c0c0c0)
|
||||
|
@ -230,21 +200,17 @@ A ····································
|
|||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 14 ┋x </technical-track> x┋
|
||||
A ├ fg(#000000), bg(#c0c0c0), normal, bold ││
|
||||
A └┛ alt │ ││
|
||||
A ·└ normal │ ││
|
||||
A ·····└ bold │ ││
|
||||
A ····················└ normal ││
|
||||
A ├ fg(#000000), bg(#c0c0c0) ││
|
||||
A └┛ alt ││
|
||||
A ·└ normal ││
|
||||
A ···············································································└ fg(#000000), bg(#c0c0c0)
|
||||
A ················································································└ normal
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 15 ┋x</a-reply> x┋
|
||||
A ├ fg(#000000), bg(#c0c0c0), normal, bold ││
|
||||
A └┛ alt │ ││
|
||||
A ·└ normal │ ││
|
||||
A ···└ bold │ ││
|
||||
A ··········└ normal ││
|
||||
A ├ fg(#000000), bg(#c0c0c0) ││
|
||||
A └┛ alt ││
|
||||
A ·└ normal ││
|
||||
A ···············································································└ fg(#000000), bg(#c0c0c0)
|
||||
A ················································································└ normal
|
||||
A └┛ alt
|
||||
|
@ -283,12 +249,9 @@ A ····································
|
|||
A ·······································································└ fg(#008080), bg(#000080)
|
||||
A ········································································└ fg(#c0c0c0), bg(#000080), bold
|
||||
S 2 ┋ [2020-12-10 06:56:41,092] DEBUG [connect.client:69] Full request text: ┋
|
||||
A ·└ normal │ │
|
||||
A ··································└ bold │
|
||||
A ·└ normal │
|
||||
A ················································└ normal
|
||||
S 3 ┋ <?xml version='1.0' encoding='iso-8859-2'?> x┋
|
||||
A ··└ bold ││ ││ ││ │ ││
|
||||
A ······└ normal││ ││ ││ │ ││
|
||||
A ·······└ fg(#008080)││ ││ │ ││
|
||||
A ··············└ normal ││ │ ││
|
||||
A ···············└ fg(#008000), bold │ ││
|
||||
|
@ -303,16 +266,12 @@ A
|
|||
A ················································································└ normal
|
||||
S 4 ┋ <a-request> x┋
|
||||
A ·└ fg(#000000), bg(#c0c0c0), normal ││
|
||||
A ··└ bold │ ││
|
||||
A ···········└ normal ││
|
||||
A ···············································································└ fg(#000000), bg(#c0c0c0)
|
||||
A ················································································└ normal
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 5 ┋ <head> x┋
|
||||
A ···└ fg(#000000), bg(#c0c0c0), normal ││
|
||||
A ····└ bold ││
|
||||
A ········└ normal ││
|
||||
A ···············································································└ fg(#000000), bg(#c0c0c0)
|
||||
A ················································································└ normal
|
||||
A └┛ alt
|
||||
|
@ -325,16 +284,13 @@ A
|
|||
A ················································································└ normal
|
||||
S 7 ┋ </head> x┋
|
||||
A ···└ fg(#000000), bg(#c0c0c0), normal ││
|
||||
A ·····└ bold ││
|
||||
A ·········└ normal ││
|
||||
A ···············································································└ fg(#000000), bg(#c0c0c0)
|
||||
A ················································································└ normal
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 8 ┋ <sourc x┋
|
||||
A ···└ fg(#000000), bg(#c0c0c0), normal ││
|
||||
A ····└ bold ││
|
||||
A ···············································································└ normal, fg(#000000), bg(#c0c0c0)
|
||||
A ···············································································└ fg(#000000), bg(#c0c0c0)
|
||||
A ················································································└ normal
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
|
@ -346,58 +302,41 @@ A
|
|||
A ················································································└ normal
|
||||
S 10 ┋ </sourc x┋
|
||||
A ···└ fg(#000000), bg(#c0c0c0), normal ││
|
||||
A ·····└ bold ││
|
||||
A ···············································································└ normal, fg(#000000), bg(#c0c0c0)
|
||||
A ···············································································└ fg(#000000), bg(#c0c0c0)
|
||||
A ················································································└ normal
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 11 ┋ request id="1"> ┋
|
||||
A ····└ fg(#000000), bg(#c0c0c0), normal, bold
|
||||
A ···········└ normal
|
||||
A ····└ fg(#000000), bg(#c0c0c0), normal
|
||||
A ············└ fg(#008080)
|
||||
A ··············└ normal
|
||||
A ···············└ fg(#008000), bold
|
||||
A ··················└ normal
|
||||
S 12 ┋ <name> ┋
|
||||
A ······└ bold
|
||||
A ··········└ normal
|
||||
S 13 ┋ x ┋
|
||||
S 14 ┋ </name> ┋
|
||||
A ·······└ bold
|
||||
A ···········└ normal
|
||||
S 15 ┋ </request> x┋
|
||||
A ·····└ bold │ ││
|
||||
A ············└ normal ││
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 16 ┋x</a-request> x┋
|
||||
A ├ bold │ ││
|
||||
A └┛ alt │ ││
|
||||
A ·└ normal │ ││
|
||||
A ···└ bold │ ││
|
||||
A ············└ normal ││
|
||||
A └┛ alt ││
|
||||
A ·└ normal ││
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 17 ┋x x┋
|
||||
A ├ bold ││
|
||||
A └┛ alt ││
|
||||
A ·└ normal ││
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 18 ┋x[2020-12-10 06:56:41,099] DEBUG [m:85] Full reply text: x┋
|
||||
A ├ bold ││ ││
|
||||
A └┛ alt ││ ││
|
||||
A ·└ normal ││ ││
|
||||
A ··································└ bold ││
|
||||
A └┛ alt │ ││
|
||||
A ·└ normal │ ││
|
||||
A ···································└ normal ││
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 19 ┋x<?xml version='1.0' encoding='iso-8859-2'?> x┋
|
||||
A ├ bold││ ││ ││ ││ │ ││
|
||||
A └┛ alt││ ││ ││ ││ │ ││
|
||||
A └┛ alt │ ││ ││ ││ │ ││
|
||||
A ·└ normal ││ ││ ││ │ ││
|
||||
A ··└ bold ││ ││ ││ │ ││
|
||||
A ······└ normal││ ││ ││ │ ││
|
||||
A ·······└ fg(#008080)││ ││ │ ││
|
||||
A ··············└ normal ││ │ ││
|
||||
A ···············└ fg(#008000), bold │ ││
|
||||
|
@ -409,19 +348,13 @@ A ····································
|
|||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 20 ┋x<a-reply> x┋
|
||||
A ├ bold │ ││
|
||||
A └┛ alt │ ││
|
||||
A ·└ normal│ ││
|
||||
A ··└ bold │ ││
|
||||
A ·········└ normal ││
|
||||
A └┛ alt ││
|
||||
A ·└ normal ││
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 21 ┋x <head> x┋
|
||||
A ├ bold │ ││
|
||||
A └┛ alt │ ││
|
||||
A └┛ alt ││
|
||||
A ·└ normal ││
|
||||
A ····└ bold ││
|
||||
A ········└ normal ││
|
||||
A └┛ alt
|
||||
A ················································································└ normal
|
||||
S 22 ┋ Files :: Text Filters :: Press TAB to edit ┋
|
||||
|
@ -477,42 +410,36 @@ A ····································
|
|||
A ············································└ carriage-return
|
||||
S 6 ┋ t timestamp = 2020-12-10 06:56:41,092 ┋
|
||||
A └┛ alt │ │ │
|
||||
A ···└ bold │ │ │
|
||||
A ············└ normal │
|
||||
A ···············└ bold │
|
||||
A ···············································································└ carriage-return
|
||||
S 7 ┋ t level = DEBUG ┋
|
||||
A └ normal│ │ │
|
||||
A └┛ alt │ │ │
|
||||
A ···└ bold │ │
|
||||
A ········└ normal │
|
||||
A ···············└ bold │
|
||||
A ···············································································└ carriage-return
|
||||
S 8 ┋ t module = connect.client ┋
|
||||
A └ normal │ │ │
|
||||
A └┛ alt │ │ │
|
||||
A ···└ bold│ │ │
|
||||
A ·········└ normal │
|
||||
A ···············└ bold │
|
||||
A ···············································································└ carriage-return
|
||||
S 9 ┋ t line = 69 ┋
|
||||
A └ normal │ │ │
|
||||
A └┛ alt │ │ │
|
||||
A ···└ bold │ │ │
|
||||
A ·············└ normal │
|
||||
A ···············└ bold │
|
||||
A ···············································································└ carriage-return
|
||||
S 10 ┋ t body = Full request text: ┋
|
||||
A └ normal │ │
|
||||
A └┛ alt│ │ │
|
||||
A ···└ bold │ │
|
||||
A ·······└ normal│ │
|
||||
A ···············└ bold │
|
||||
A ···············································································└ carriage-return
|
||||
S 11 ┋ t msg_data = <?xml version='1.0' encoding='iso-8859-2'?> <a-request> <head> ┋
|
||||
A └ normal │ │ │
|
||||
A └┛ alt │ │ │
|
||||
A ···└ bold │ │ │
|
||||
A ···········└ normal │
|
||||
A ···············└ bold │
|
||||
A ···············································································└ carriage-return
|
||||
|
@ -541,8 +468,6 @@ A ····································
|
|||
S 17 ┋ No discovered message fields ┋
|
||||
A └ normal
|
||||
S 18 ┋ <?xml version='1.0' encoding='iso-8859-2'?> ┋
|
||||
A ··└ bold ││ ││ ││ │
|
||||
A ······└ normal││ ││ ││ │
|
||||
A ·······└ fg(#008080)││ ││ │
|
||||
A ··············└ normal ││ │
|
||||
A ···············└ fg(#008000), bold │
|
||||
|
@ -552,11 +477,7 @@ A ·····························└ normal
|
|||
A ······························└ fg(#008000), bold
|
||||
A ··········································└ normal
|
||||
S 19 ┋ a-request> ┋
|
||||
A ··└ bold │
|
||||
A ···········└ normal
|
||||
S 20 ┋ <head> ┋
|
||||
A ····└ bold
|
||||
A ········└ normal
|
||||
S 21 ┋ x ┋
|
||||
A ·········└ carriage-return
|
||||
S 24 ┋ ┋
|
||||
|
|
Loading…
Reference in New Issue