[clang-format] init

pull/968/head
Timothy Stack 2 years ago
parent d0ba84d9be
commit 66ef5fdae1

@ -1,12 +1,12 @@
--- ---
Language: Cpp Language: Cpp
# BasedOnStyle: Chromium # BasedOnStyle: Chromium
AccessModifierOffset: -2 AccessModifierOffset: -4
AlignAfterOpenBracket: Align AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false AlignConsecutiveMacros: Consecutive
AlignConsecutiveAssignments: false AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: false AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: false AlignConsecutiveDeclarations: None
AlignEscapedNewlines: DontAlign AlignEscapedNewlines: DontAlign
AlignOperands: DontAlign AlignOperands: DontAlign
AlignTrailingComments: false AlignTrailingComments: false
@ -20,38 +20,38 @@ AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None # AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: TopLevelDefinitions
AlwaysBreakBeforeMultilineStrings: true AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false BinPackArguments: false
BinPackParameters: false BinPackParameters: false
BraceWrapping: BraceWrapping:
AfterCaseLabel: false AfterCaseLabel: false
AfterClass: true AfterClass: false
AfterControlStatement: MultiLine AfterControlStatement: MultiLine
AfterEnum: true AfterEnum: false
AfterFunction: true AfterFunction: true
AfterNamespace: true AfterNamespace: false
AfterObjCDeclaration: false AfterObjCDeclaration: false
AfterStruct: true AfterStruct: false
AfterUnion: true AfterUnion: false
AfterExternBlock: true AfterExternBlock: true
BeforeCatch: false BeforeCatch: false
BeforeElse: false BeforeElse: false
BeforeLambdaBody: true BeforeLambdaBody: false
BeforeWhile: false BeforeWhile: false
IndentBraces: false IndentBraces: false
SplitEmptyFunction: true SplitEmptyFunction: true
SplitEmptyRecord: true SplitEmptyRecord: true
SplitEmptyNamespace: true SplitEmptyNamespace: true
BreakBeforeBinaryOperators: NonAssignment BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom BreakBeforeBraces: Custom
# BreakBeforeInheritanceComma: true # BreakBeforeInheritanceComma: true
BreakInheritanceList: BeforeComma BreakInheritanceList: BeforeComma
BreakBeforeTernaryOperators: true BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true # BreakConstructorInitializersBeforeComma: true
BreakConstructorInitializers: BeforeComma BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: true BreakAfterJavaFieldAnnotations: true
BreakStringLiterals: true BreakStringLiterals: true
ColumnLimit: 80 ColumnLimit: 80
@ -88,7 +88,7 @@ IndentCaseBlocks: false
IndentGotoLabels: true IndentGotoLabels: true
IndentPPDirectives: AfterHash IndentPPDirectives: AfterHash
IndentExternBlock: NoIndent IndentExternBlock: NoIndent
IndentWidth: 2 IndentWidth: 4
IndentWrappedFunctionNames: false IndentWrappedFunctionNames: false
InsertTrailingCommas: Wrapped InsertTrailingCommas: Wrapped
JavaScriptQuotes: Double JavaScriptQuotes: Double
@ -99,11 +99,11 @@ MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1 MaxEmptyLinesToKeep: 1
NamespaceIndentation: None NamespaceIndentation: None
ObjCBinPackProtocolList: Never ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2 ObjCBlockIndentWidth: 4
ObjCBreakBeforeNestedBlockParam: true ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2 PenaltyBreakAssignment: 4
PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300 PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120 PenaltyBreakFirstLessLess: 120
@ -143,13 +143,13 @@ RawStringFormats:
CanonicalDelimiter: '' CanonicalDelimiter: ''
BasedOnStyle: google BasedOnStyle: google
ReflowComments: true ReflowComments: true
SortIncludes: true SortIncludes: CaseInsensitive
SortUsingDeclarations: true SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: true SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatementsExceptForEachMacros SpaceBeforeParens: ControlStatementsExceptForEachMacros
@ -157,7 +157,7 @@ SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2 SpacesBeforeTrailingComments: 2
SpacesInAngles: false SpacesInAngles: Never
SpacesInConditionalStatement: false SpacesInConditionalStatement: false
SpacesInContainerLiterals: false SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false SpacesInCStyleCastParentheses: false

@ -35,7 +35,7 @@ set(lnav_LIBS
) )
add_subdirectory(src) add_subdirectory(src)
add_subdirectory(test) # add_subdirectory(test)
# ---- Install rules ---- # ---- Install rules ----

@ -35,6 +35,7 @@ class LnavConan(ConanFile):
"pcre:with_jit": True, "pcre:with_jit": True,
"sqlite3:enable_json1": True, "sqlite3:enable_json1": True,
"sqlite3:enable_soundex": True, "sqlite3:enable_soundex": True,
"readline:with_library": "curses",
} }
def generate(self): def generate(self):

@ -19,6 +19,8 @@ add_subdirectory(pcrepp)
add_subdirectory(remote) add_subdirectory(remote)
add_subdirectory(tailer) add_subdirectory(tailer)
add_subdirectory(formats/logfmt) add_subdirectory(formats/logfmt)
add_subdirectory(yajl)
add_subdirectory(yajlpp)
add_executable(bin2c bin2c.hh tools/bin2c.c) add_executable(bin2c bin2c.hh tools/bin2c.c)
target_link_libraries(bin2c ZLIB::ZLIB) target_link_libraries(bin2c ZLIB::ZLIB)
@ -26,465 +28,447 @@ target_link_libraries(bin2c ZLIB::ZLIB)
add_executable(ptimec ptimec.hh ptimec.c) add_executable(ptimec ptimec.hh ptimec.c)
set(TIME_FORMATS set(TIME_FORMATS
"@%@" "@%@"
"%Y-%m-%d %H:%M:%S" "%Y-%m-%d %H:%M:%S"
"%Y-%m-%d %H:%M:%S%z" "%Y-%m-%d %H:%M:%S%z"
"%Y-%m-%d %H:%M:%S %z" "%Y-%m-%d %H:%M:%S %z"
"%Y-%m-%d %H:%M" "%Y-%m-%d %H:%M"
"%Y-%m-%dT%H:%M:%S.%f%z" "%Y-%m-%dT%H:%M:%S.%f%z"
"%y-%m-%dT%H:%M:%S.%f%z" "%y-%m-%dT%H:%M:%S.%f%z"
"%Y-%m-%dT%H:%M:%SZ" "%Y-%m-%dT%H:%M:%SZ"
"%Y-%m-%dT%H:%M:%S%z" "%Y-%m-%dT%H:%M:%S%z"
"%Y-%m-%dT%H:%M:%S" "%Y-%m-%dT%H:%M:%S"
"%Y-%m-%dT%H:%M:%S%z" "%Y-%m-%dT%H:%M:%S%z"
"%Y/%m/%d %H:%M:%S" "%Y/%m/%d %H:%M:%S"
"%Y/%m/%d %H:%M:%S %z" "%Y/%m/%d %H:%M:%S %z"
"%Y/%m/%d %H:%M:%S%z" "%Y/%m/%d %H:%M:%S%z"
"%Y/%m/%d %H:%M" "%Y/%m/%d %H:%M"
"%Y %b %d %a %H:%M:%S.%L" "%Y %b %d %a %H:%M:%S.%L"
"%Y %b %d %H:%M:%S.%L" "%Y %b %d %H:%M:%S.%L"
"%Y %b %d %H:%M:%S" "%Y %b %d %H:%M:%S"
"%a %b %d %H:%M:%S %Y" "%a %b %d %H:%M:%S %Y"
"%a %b %d %H:%M:%S.%f %Y" "%a %b %d %H:%M:%S.%f %Y"
"%a %b %d %H:%M:%S %Z %Y" "%a %b %d %H:%M:%S %Z %Y"
"%a %b %d %H:%M:%S " "%a %b %d %H:%M:%S "
"%a %b %d %H:%M:%S.%L " "%a %b %d %H:%M:%S.%L "
"%d/%b/%Y:%H:%M:%S +0000" "%d/%b/%Y:%H:%M:%S +0000"
"%d/%b/%Y:%H:%M:%S %z" "%d/%b/%Y:%H:%M:%S %z"
"%d-%b-%Y %H:%M:%S %z" "%d-%b-%Y %H:%M:%S %z"
"%d-%b-%Y %H:%M:%S %Z" "%d-%b-%Y %H:%M:%S %Z"
"%d %b %Y %H:%M:%S" "%d %b %Y %H:%M:%S"
"%d %b %Y %H:%M:%S.%L" "%d %b %Y %H:%M:%S.%L"
"%d %b %Y %H:%M:%S,%L" "%d %b %Y %H:%M:%S,%L"
"%b %d %H:%M:%S" "%b %d %H:%M:%S"
"%b %d %k:%M:%S" "%b %d %k:%M:%S"
"%b %d %l:%M:%S" "%b %d %l:%M:%S"
"%b %e, %Y %l:%M:%S %p" "%b %e, %Y %l:%M:%S %p"
"%m/%d/%y %H:%M:%S" "%m/%d/%y %H:%M:%S"
"%m/%d/%Y %I:%M:%S:%L %p %Z" "%m/%d/%Y %I:%M:%S:%L %p %Z"
"%m/%d/%Y %I:%M:%S %p %Z" "%m/%d/%Y %I:%M:%S %p %Z"
"%m/%d/%Y %l:%M:%S %p %Z" "%m/%d/%Y %l:%M:%S %p %Z"
"%m/%e/%Y %I:%M:%S %p" "%m/%e/%Y %I:%M:%S %p"
"%m/%e/%Y %l:%M:%S %p" "%m/%e/%Y %l:%M:%S %p"
"%d/%b/%y %H:%M:%S" "%d/%b/%y %H:%M:%S"
"%m%d %H:%M:%S" "%m%d %H:%M:%S"
"%H:%M:%S" "%H:%M:%S"
"%M:%S" "%M:%S"
"%m/%d %H:%M:%S" "%m/%d %H:%M:%S"
"%Y-%m-%d" "%Y-%m-%d"
"%Y-%m" "%Y-%m"
"%Y/%m/%d" "%Y/%m/%d"
"%Y/%m" "%Y/%m"
"%s.%f") "%s.%f")
set(GEN_SRCS "") set(GEN_SRCS "")
add_custom_command(OUTPUT time_fmts.cc COMMAND ptimec ${TIME_FORMATS} > add_custom_command(OUTPUT time_fmts.cc COMMAND ptimec ${TIME_FORMATS} >
time_fmts.cc) time_fmts.cc)
list(APPEND GEN_SRCS time_fmts.cc)
add_library(lnavdt STATIC config.h.in ptimec.hh ptimec_rt.cc time_fmts.cc)
target_include_directories(lnavdt PUBLIC . ${CMAKE_CURRENT_BINARY_DIR})
function(bin2c) function(bin2c)
cmake_parse_arguments(BIN2C_ "" "VARNAME" "" ${ARGN}) cmake_parse_arguments(BIN2C_ "" "VARNAME" "" ${ARGN})
list(TRANSFORM BIN2C_UNPARSED_ARGUMENTS "\\." "-") list(TRANSFORM BIN2C_UNPARSED_ARGUMENTS "\\." "-")
add_custom_command( add_custom_command(
OUTPUT "${DST_FILE}.h" "${DST_FILE}.cc" OUTPUT "${DST_FILE}.h" "${DST_FILE}.cc"
COMMAND bin2c "${DST_FILE}" "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_TO_LINK}" COMMAND bin2c "${DST_FILE}" "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_TO_LINK}"
DEPENDS bin2c "${FILE_TO_LINK}") DEPENDS bin2c "${FILE_TO_LINK}")
endfunction(bin2c) endfunction(bin2c)
foreach(FILE_TO_LINK ansi-palette.json xterm-palette.json help.txt init.sql) foreach (FILE_TO_LINK ansi-palette.json xterm-palette.json help.txt init.sql)
string(REPLACE "." "-" DST_FILE "${FILE_TO_LINK}") string(REPLACE "." "-" DST_FILE "${FILE_TO_LINK}")
add_custom_command( add_custom_command(
OUTPUT "${DST_FILE}.h" "${DST_FILE}.cc" OUTPUT "${DST_FILE}.h" "${DST_FILE}.cc"
COMMAND bin2c "${DST_FILE}" "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_TO_LINK}" COMMAND bin2c "${DST_FILE}" "${CMAKE_CURRENT_SOURCE_DIR}/${FILE_TO_LINK}"
DEPENDS bin2c "${FILE_TO_LINK}") DEPENDS bin2c "${FILE_TO_LINK}")
list(APPEND GEN_SRCS "${CMAKE_CURRENT_BINARY_DIR}/${DST_FILE}.h" list(APPEND GEN_SRCS "${CMAKE_CURRENT_BINARY_DIR}/${DST_FILE}.h"
"${CMAKE_CURRENT_BINARY_DIR}/${DST_FILE}.cc") "${CMAKE_CURRENT_BINARY_DIR}/${DST_FILE}.cc")
endforeach(FILE_TO_LINK) endforeach (FILE_TO_LINK)
set(FORMAT_FILES set(FORMAT_FILES
formats/access_log.json formats/access_log.json
formats/alb_log.json formats/alb_log.json
formats/autodeploy_log.json formats/autodeploy_log.json
formats/block_log.json formats/block_log.json
formats/candlepin_log.json formats/candlepin_log.json
formats/choose_repo_log.json formats/choose_repo_log.json
formats/cups_log.json formats/cups_log.json
formats/dpkg_log.json formats/dpkg_log.json
formats/elb_log.json formats/elb_log.json
formats/engine_log.json formats/engine_log.json
formats/error_log.json formats/error_log.json
formats/fsck_hfs_log.json formats/fsck_hfs_log.json
formats/glog_log.json formats/glog_log.json
formats/haproxy_log.json formats/haproxy_log.json
formats/java_log.json formats/java_log.json
formats/journald_json_log.json formats/journald_json_log.json
formats/katello_log.json formats/katello_log.json
formats/openam_log.json formats/openam_log.json
formats/openamdb_log.json formats/openamdb_log.json
formats/openstack_log.json formats/openstack_log.json
formats/page_log.json formats/page_log.json
formats/papertrail_log.json formats/papertrail_log.json
formats/snaplogic_log.json formats/snaplogic_log.json
formats/sssd_log.json formats/sssd_log.json
formats/strace_log.json formats/strace_log.json
formats/sudo_log.json formats/sudo_log.json
formats/syslog_log.json formats/syslog_log.json
formats/s3_log.json formats/s3_log.json
formats/tcf_log.json formats/tcf_log.json
formats/tcsh_history.json formats/tcsh_history.json
formats/uwsgi_log.json formats/uwsgi_log.json
formats/vdsm_log.json formats/vdsm_log.json
formats/vmk_log.json formats/vmk_log.json
formats/vmw_log.json formats/vmw_log.json
formats/xmlrpc_log.json) formats/xmlrpc_log.json)
set(FORMAT_FILE_PATHS ${FORMAT_FILES}) set(FORMAT_FILE_PATHS ${FORMAT_FILES})
list(TRANSFORM FORMAT_FILE_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") list(TRANSFORM FORMAT_FILE_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
add_custom_command( add_custom_command(
OUTPUT default-formats.h default-formats.cc OUTPUT default-formats.h default-formats.cc
COMMAND bin2c -n lnav_format_json default-formats ${FORMAT_FILE_PATHS} COMMAND bin2c -n lnav_format_json default-formats ${FORMAT_FILE_PATHS}
DEPENDS bin2c ${FORMAT_FILES}) DEPENDS bin2c ${FORMAT_FILES})
list(APPEND GEN_SRCS default-formats.h default-formats.cc) list(APPEND GEN_SRCS default-formats.h default-formats.cc)
set(CONFIG_FILES set(CONFIG_FILES
root-config.json root-config.json
keymaps/de-keymap.json keymaps/de-keymap.json
keymaps/default-keymap.json keymaps/default-keymap.json
keymaps/fr-keymap.json keymaps/fr-keymap.json
keymaps/uk-keymap.json keymaps/uk-keymap.json
keymaps/us-keymap.json keymaps/us-keymap.json
themes/default-theme.json themes/default-theme.json
themes/grayscale.json themes/grayscale.json
themes/eldar.json themes/eldar.json
themes/monocai.json themes/monocai.json
themes/night-owl.json themes/night-owl.json
themes/solarized-dark.json themes/solarized-dark.json
themes/solarized-light.json) themes/solarized-light.json)
set(CONFIG_FILE_PATHS ${CONFIG_FILES}) set(CONFIG_FILE_PATHS ${CONFIG_FILES})
list(TRANSFORM CONFIG_FILE_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") list(TRANSFORM CONFIG_FILE_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
add_custom_command( add_custom_command(
OUTPUT default-config.h default-config.cc OUTPUT default-config.h default-config.cc
COMMAND bin2c -n lnav_config_json default-config ${CONFIG_FILE_PATHS} COMMAND bin2c -n lnav_config_json default-config ${CONFIG_FILE_PATHS}
DEPENDS bin2c ${CONFIG_FILES}) DEPENDS bin2c ${CONFIG_FILES})
list(APPEND GEN_SRCS default-config.h default-config.cc) list(APPEND GEN_SRCS default-config.h default-config.cc)
set(BUILTIN_LNAV_SCRIPTS set(BUILTIN_LNAV_SCRIPTS
scripts/dhclient-summary.lnav scripts/lnav-pop-view.lnav scripts/dhclient-summary.lnav scripts/lnav-pop-view.lnav
scripts/partition-by-boot.lnav scripts/rename-stdin.lnav scripts/partition-by-boot.lnav scripts/rename-stdin.lnav
scripts/search-for.lnav) scripts/search-for.lnav)
set(BUILTIN_LNAV_SCRIPT_PATHS ${BUILTIN_LNAV_SCRIPTS}) set(BUILTIN_LNAV_SCRIPT_PATHS ${BUILTIN_LNAV_SCRIPTS})
list(TRANSFORM BUILTIN_LNAV_SCRIPT_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") list(TRANSFORM BUILTIN_LNAV_SCRIPT_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
add_custom_command( add_custom_command(
OUTPUT builtin-scripts.h builtin-scripts.cc OUTPUT builtin-scripts.h builtin-scripts.cc
COMMAND bin2c -n lnav_scripts builtin-scripts ${BUILTIN_LNAV_SCRIPT_PATHS} COMMAND bin2c -n lnav_scripts builtin-scripts ${BUILTIN_LNAV_SCRIPT_PATHS}
DEPENDS bin2c ${BUILTIN_LNAV_SCRIPTS}) DEPENDS bin2c ${BUILTIN_LNAV_SCRIPTS})
list(APPEND GEN_SRCS builtin-scripts.h builtin-scripts.cc) list(APPEND GEN_SRCS builtin-scripts.h builtin-scripts.cc)
set(BUILTIN_SH_SCRIPTS scripts/dhclient-summary.lnav scripts/lnav-pop-view.lnav set(BUILTIN_SH_SCRIPTS scripts/dhclient-summary.lnav scripts/lnav-pop-view.lnav
scripts/partition-by-boot.lnav scripts/search-for.lnav) scripts/partition-by-boot.lnav scripts/search-for.lnav)
set(BUILTIN_SH_SCRIPT_PATHS ${BUILTIN_SH_SCRIPTS}) set(BUILTIN_SH_SCRIPT_PATHS ${BUILTIN_SH_SCRIPTS})
list(TRANSFORM BUILTIN_SH_SCRIPT_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") list(TRANSFORM BUILTIN_SH_SCRIPT_PATHS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/")
add_custom_command( add_custom_command(
OUTPUT builtin-sh-scripts.h builtin-sh-scripts.cc OUTPUT builtin-sh-scripts.h builtin-sh-scripts.cc
COMMAND bin2c -n lnav_sh_scripts builtin-sh-scripts ${BUILTIN_SH_SCRIPT_PATHS} COMMAND bin2c -n lnav_sh_scripts builtin-sh-scripts ${BUILTIN_SH_SCRIPT_PATHS}
DEPENDS bin2c ${BUILTIN_SH_SCRIPTS}) DEPENDS bin2c ${BUILTIN_SH_SCRIPTS})
list(APPEND GEN_SRCS builtin-sh-scripts.h builtin-sh-scripts.cc) list(APPEND GEN_SRCS builtin-sh-scripts.h builtin-sh-scripts.cc)
add_library( add_library(
cppfmt STATIC cppfmt STATIC
fmtlib/format.cc fmtlib/format.cc
fmtlib/os.cc fmtlib/os.cc
fmtlib/fmt/chrono.h fmtlib/fmt/chrono.h
fmtlib/fmt/color.h fmtlib/fmt/color.h
fmtlib/fmt/compile.h fmtlib/fmt/compile.h
fmtlib/fmt/core.h fmtlib/fmt/core.h
fmtlib/fmt/format-inl.h fmtlib/fmt/format-inl.h
fmtlib/fmt/format.h fmtlib/fmt/format.h
fmtlib/fmt/locale.h fmtlib/fmt/locale.h
fmtlib/fmt/os.h fmtlib/fmt/os.h
fmtlib/fmt/ostream.h fmtlib/fmt/ostream.h
fmtlib/fmt/posix.h fmtlib/fmt/posix.h
fmtlib/fmt/printf.h fmtlib/fmt/printf.h
fmtlib/fmt/ranges.h fmtlib/fmt/ranges.h
fmtlib/fmt/time.h) fmtlib/fmt/time.h)
target_include_directories(cppfmt PUBLIC fmtlib)
add_library(lnavfileio STATIC
grep_proc.hh
line_buffer.hh
shared_buffer.hh
grep_proc.cc
line_buffer.cc
shared_buffer.cc
)
target_include_directories(lnavfileio PRIVATE . ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(lnavfileio cppfmt pcrepp base BZip2::BZip2 ZLIB::ZLIB)
add_library( add_library(
diag STATIC diag STATIC
${GEN_SRCS} ${GEN_SRCS}
config.h.in config.h.in
all_logs_vtab.cc all_logs_vtab.cc
ansi_scrubber.cc ansi_scrubber.cc
archive_manager.cc archive_manager.cc
attr_line.cc attr_line.cc
bin2c.hh bin2c.hh
bookmarks.cc bookmarks.cc
bottom_status_source.cc bottom_status_source.cc
collation-functions.cc collation-functions.cc
column_namer.cc column_namer.cc
command_executor.cc command_executor.cc
curl_looper.cc curl_looper.cc
db_sub_source.cc db_sub_source.cc
elem_to_json.cc elem_to_json.cc
environ_vtab.cc environ_vtab.cc
extension-functions.cc extension-functions.cc
field_overlay_source.cc field_overlay_source.cc
file_collection.cc file_collection.cc
file_format.cc file_format.cc
file_vtab.cc file_vtab.cc
files_sub_source.cc files_sub_source.cc
filter_observer.cc filter_observer.cc
filter_status_source.cc filter_status_source.cc
filter_sub_source.cc filter_sub_source.cc
fs-extension-functions.cc fs-extension-functions.cc
fstat_vtab.cc fstat_vtab.cc
fts_fuzzy_match.cc fts_fuzzy_match.cc
grep_proc.cc help_text.cc
help_text.cc help_text_formatter.cc
help_text_formatter.cc highlighter.cc
highlighter.cc hist_source.cc
hist_source.cc hotkeys.cc
hotkeys.cc input_dispatcher.cc
input_dispatcher.cc json-extension-functions.cc
json-extension-functions.cc listview_curses.cc
yajlpp/json_op.cc lnav_commands.cc
yajlpp/json_ptr.cc lnav_config.cc
line_buffer.cc lnav_util.cc
listview_curses.cc log_accel.cc
lnav_commands.cc log_actions.cc
lnav_config.cc log_data_helper.cc
lnav_util.cc log_data_table.cc
log_accel.cc log_format.cc
log_actions.cc log_format_loader.cc
log_data_helper.cc log_level.cc
log_data_table.cc log_search_table.cc
log_format.cc logfile.cc
log_format_loader.cc logfile_sub_source.cc
log_level.cc network-extension-functions.cc
log_search_table.cc data_scanner.cc
logfile.cc data_scanner_re.cc
logfile_sub_source.cc data_parser.cc
network-extension-functions.cc papertrail_proc.cc
data_scanner.cc pcap_manager.cc
data_scanner_re.cc pretty_printer.cc
data_parser.cc pugixml/pugixml.cpp
papertrail_proc.cc readline_callbacks.cc
pcap_manager.cc readline_curses.cc
ptimec_rt.cc readline_highlighters.cc
pretty_printer.cc readline_possibilities.cc
pugixml/pugixml.cpp regexp_vtab.cc
readline_callbacks.cc relative_time.cc
readline_curses.cc session_data.cc
readline_highlighters.cc sequence_matcher.cc
readline_possibilities.cc shlex.cc
regexp_vtab.cc sqlite-extension-func.cc
relative_time.cc statusview_curses.cc
session_data.cc string-extension-functions.cc
sequence_matcher.cc sysclip.cc
shared_buffer.cc piper_proc.cc
shlex.cc spectro_source.cc
sqlite-extension-func.cc sql_commands.cc
statusview_curses.cc sql_util.cc
string-extension-functions.cc state-extension-functions.cc
sysclip.cc styling.cc
piper_proc.cc string_attr_type.cc
spectro_source.cc text_format.cc
sql_commands.cc textfile_highlighters.cc
sql_util.cc textfile_sub_source.cc
state-extension-functions.cc textview_curses.cc
styling.cc top_status_source.cc
string_attr_type.cc time-extension-functions.cc
text_format.cc timer.cc
textfile_highlighters.cc unique_path.cc
textfile_sub_source.cc unique_path.hh
textview_curses.cc view_curses.cc
top_status_source.cc view_helpers.cc
time-extension-functions.cc views_vtab.cc
timer.cc vt52_curses.cc
unique_path.cc vtab_module.cc
unique_path.hh log_vtab_impl.cc
view_curses.cc xml_util.cc
view_helpers.cc xpath_vtab.cc
views_vtab.cc xterm_mouse.cc
vt52_curses.cc spookyhash/SpookyV2.cpp
vtab_module.cc third-party/sqlite/ext/series.c
log_vtab_impl.cc third-party/sqlite/ext/dbdump.c
xml_util.cc all_logs_vtab.hh
xpath_vtab.cc archive_manager.hh
xterm_mouse.cc archive_manager.cfg.hh
yajlpp/yajlpp.cc attr_line.hh
yajl/yajl.c auto_fd.hh
yajl/yajl_alloc.c auto_mem.hh
yajl/yajl_alloc.h big_array.hh
yajl/yajl_buf.c bottom_status_source.hh
yajl/yajl_buf.h bound_tags.hh
yajl/yajl_bytestack.h byte_array.hh
yajl/yajl_encode.c command_executor.hh
yajl/yajl_encode.h column_namer.hh
yajl/yajl_gen.c curl_looper.hh
yajl/yajl_lex.c doc_status_source.hh
yajl/yajl_lex.h elem_to_json.hh
yajl/yajl_parser.c field_overlay_source.hh
yajl/yajl_parser.h file_collection.hh
yajl/yajl_tree.c file_format.hh
yajl/yajl_version.c files_sub_source.hh
spookyhash/SpookyV2.cpp filter_observer.hh
third-party/sqlite/ext/series.c filter_status_source.hh
third-party/sqlite/ext/dbdump.c filter_sub_source.hh
all_logs_vtab.hh fstat_vtab.hh
archive_manager.hh fts_fuzzy_match.hh
archive_manager.cfg.hh grep_highlighter.hh
attr_line.hh help_text.hh
auto_fd.hh help_text_formatter.hh
auto_mem.hh highlighter.hh
big_array.hh hotkeys.hh
bottom_status_source.hh input_dispatcher.hh
bound_tags.hh k_merge_tree.h
byte_array.hh log_actions.hh
command_executor.hh log_data_helper.hh
column_namer.hh log_data_table.hh
curl_looper.hh log_format.hh
doc_status_source.hh log_format_ext.hh
elem_to_json.hh log_format_fwd.hh
field_overlay_source.hh log_format_impls.cc
file_collection.hh log_gutter_source.hh
file_format.hh log_level.hh
files_sub_source.hh log_search_table.hh
filter_observer.hh logfile.hh
filter_status_source.hh logfile_fwd.hh
filter_sub_source.hh logfile_stats.hh
fstat_vtab.hh optional.hpp
fts_fuzzy_match.hh papertrail_proc.hh
grep_highlighter.hh pcap_manager.hh
help_text.hh plain_text_source.hh
help_text_formatter.hh pretty_printer.hh
highlighter.hh preview_status_source.hh
hotkeys.hh pugixml/pugiconfig.hpp
input_dispatcher.hh pugixml/pugixml.hpp
k_merge_tree.h readline_callbacks.hh
log_actions.hh readline_context.hh
log_data_helper.hh readline_possibilities.hh
log_data_table.hh regexp_vtab.hh
log_format.hh relative_time.hh
log_format_ext.hh styling.hh
log_format_fwd.hh ring_span.hh
log_format_impls.cc safe/accessmode.h
log_gutter_source.hh safe/defaulttypes.h
log_level.hh safe/mutableref.h
log_search_table.hh safe/safe.h
logfile.hh sequence_sink.hh
logfile_fwd.hh shlex.hh
logfile_stats.hh shlex.resolver.hh
optional.hpp simdutf8check.h
papertrail_proc.hh spectro_source.hh
pcap_manager.hh sqlitepp.hh
plain_text_source.hh sql_help.hh
pretty_printer.hh sql_util.hh
preview_status_source.hh strong_int.hh
ptimec.hh string_attr_type.hh
pugixml/pugiconfig.hpp sysclip.hh
pugixml/pugixml.hpp sysclip.cfg.hh
readline_callbacks.hh term_extra.hh
readline_context.hh termios_guard.hh
readline_possibilities.hh text_format.hh
regexp_vtab.hh textfile_highlighters.hh
relative_time.hh textfile_sub_source.hh
styling.hh textview_curses.hh
ring_span.hh textview_curses_fwd.hh
safe/accessmode.h time_T.hh
safe/defaulttypes.h timer.hh
safe/mutableref.h top_status_source.hh
safe/safe.h url_loader.hh
sequence_sink.hh view_helpers.hh
shlex.hh view_helpers.examples.hh
shlex.resolver.hh views_vtab.hh
simdutf8check.h vis_line.hh
spectro_source.hh vtab_module.hh
sqlitepp.hh vtab_module_json.hh
sql_help.hh xml_util.hh
sql_util.hh xpath_vtab.hh
strong_int.hh mapbox/recursive_wrapper.hpp
string_attr_type.hh mapbox/variant.hpp
sysclip.hh mapbox/variant_io.hpp
sysclip.cfg.hh mapbox/variant_visitor.hpp
term_extra.hh ghc/filesystem.hpp
termios_guard.hh ghc/fs_fwd.hpp
text_format.hh ghc/fs_impl.hpp
textfile_highlighters.hh ghc/fs_std.hpp
textfile_sub_source.hh ghc/fs_std_fwd.hpp
textview_curses.hh ghc/fs_std_impl.hpp
textview_curses_fwd.hh ww898/cp_utf8.hpp
time_T.hh log_level_re.cc)
timer.hh
top_status_source.hh
url_loader.hh
view_helpers.hh
view_helpers.examples.hh
views_vtab.hh
vis_line.hh
vtab_module.hh
vtab_module_json.hh
yajlpp/yajlpp.hh
yajlpp/yajlpp_def.hh
xml_util.hh
xpath_vtab.hh
mapbox/recursive_wrapper.hpp
mapbox/variant.hpp
mapbox/variant_io.hpp
mapbox/variant_visitor.hpp
yajl/api/yajl_common.h
yajl/api/yajl_gen.h
yajl/api/yajl_parse.h
yajl/api/yajl_tree.h
ghc/filesystem.hpp
ghc/fs_fwd.hpp
ghc/fs_impl.hpp
ghc/fs_std.hpp
ghc/fs_std_fwd.hpp
ghc/fs_std_impl.hpp
ww898/cp_utf8.hpp
log_level_re.cc)
set(lnav_SRCS lnav.cc) set(lnav_SRCS lnav.cc)
target_include_directories(diag PUBLIC . fmtlib ${CMAKE_CURRENT_BINARY_DIR} target_include_directories(diag PUBLIC . fmtlib ${CMAKE_CURRENT_BINARY_DIR}
third-party) third-party)
target_link_libraries( target_link_libraries(
diag diag
base base
pcrepp lnavdt
tailerservice lnavfileio
tailerpp pcrepp
tailercommon tailerservice
logfmt tailerpp
${lnav_LIBS}) tailercommon
logfmt
yajlpp
${lnav_LIBS})
target_compile_definitions(diag PRIVATE SQLITE_OMIT_LOAD_EXTENSION) target_compile_definitions(diag PRIVATE SQLITE_OMIT_LOAD_EXTENSION)
check_library_exists(util openpty "" HAVE_LIBUTIL) check_library_exists(util openpty "" HAVE_LIBUTIL)
if(HAVE_LIBUTIL) if (HAVE_LIBUTIL)
target_link_libraries(diag util) target_link_libraries(diag util)
endif() endif ()
add_executable(test_yajlpp yajlpp/test_yajlpp.cc)
target_link_libraries(test_yajlpp diag ${lnav_LIBS})
add_test(NAME test_yajlpp COMMAND test_yajlpp)
add_executable(drive_json_op yajlpp/drive_json_op.cc)
target_link_libraries(drive_json_op diag ${lnav_LIBS})
add_executable(lnav ${lnav_SRCS}) add_executable(lnav ${lnav_SRCS})
target_link_libraries(lnav diag) target_link_libraries(lnav diag)

@ -21,48 +21,54 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include "all_logs_vtab.hh" #include "all_logs_vtab.hh"
#include "config.h"
#include "string_attr_type.hh" #include "string_attr_type.hh"
static auto intern_lifetime = intern_string::get_table_lifetime(); static auto intern_lifetime = intern_string::get_table_lifetime();
all_logs_vtab::all_logs_vtab() all_logs_vtab::all_logs_vtab()
: log_vtab_impl(intern_string::lookup("all_logs")), : log_vtab_impl(intern_string::lookup("all_logs")),
alv_value_meta(intern_string::lookup("log_format"), alv_value_meta(
value_kind_t::VALUE_TEXT, intern_string::lookup("log_format"), value_kind_t::VALUE_TEXT, 0),
0), alv_msg_meta(
alv_msg_meta(intern_string::lookup("log_msg_format"), intern_string::lookup("log_msg_format"), value_kind_t::VALUE_TEXT, 1),
value_kind_t::VALUE_TEXT, alv_schema_meta(
1), intern_string::lookup("log_msg_schema"), value_kind_t::VALUE_TEXT, 2)
alv_schema_meta(intern_string::lookup("log_msg_schema"), {
value_kind_t::VALUE_TEXT,
2) {
this->alv_value_meta.lvm_identifier = true; this->alv_value_meta.lvm_identifier = true;
this->alv_msg_meta.lvm_identifier = true; this->alv_msg_meta.lvm_identifier = true;
this->alv_schema_meta.lvm_identifier = true; this->alv_schema_meta.lvm_identifier = true;
} }
void all_logs_vtab::get_columns(std::vector<vtab_column> &cols) const void
all_logs_vtab::get_columns(std::vector<vtab_column>& cols) const
{ {
cols.emplace_back(vtab_column(this->alv_value_meta.lvm_name.get()) cols.emplace_back(vtab_column(this->alv_value_meta.lvm_name.get())
.with_comment("The name of the log file format")); .with_comment("The name of the log file format"));
cols.emplace_back(vtab_column(this->alv_msg_meta.lvm_name.get()) cols.emplace_back(
.with_comment("The message format with variables replaced by hash marks")); vtab_column(this->alv_msg_meta.lvm_name.get())
cols.emplace_back(this->alv_schema_meta.lvm_name.get(), SQLITE3_TEXT, "", true, .with_comment(
"The message format with variables replaced by hash marks"));
cols.emplace_back(this->alv_schema_meta.lvm_name.get(),
SQLITE3_TEXT,
"",
true,
"The ID for the message schema"); "The ID for the message schema");
} }
void all_logs_vtab::extract(std::shared_ptr<logfile> lf, uint64_t line_number, void
shared_buffer_ref &line, all_logs_vtab::extract(std::shared_ptr<logfile> lf,
std::vector<logline_value> &values) uint64_t line_number,
shared_buffer_ref& line,
std::vector<logline_value>& values)
{ {
auto format = lf->get_format(); auto format = lf->get_format();
values.emplace_back(this->alv_value_meta, format->get_name()); values.emplace_back(this->alv_value_meta, format->get_name());
@ -98,7 +104,8 @@ void all_logs_vtab::extract(std::shared_ptr<logfile> lf, uint64_t line_number,
values.emplace_back(this->alv_schema_meta, schema_ref); values.emplace_back(this->alv_schema_meta, schema_ref);
} }
bool all_logs_vtab::is_valid(log_cursor &lc, logfile_sub_source &lss) bool
all_logs_vtab::is_valid(log_cursor& lc, logfile_sub_source& lss)
{ {
auto cl = lss.at(lc.lc_curr_line); auto cl = lss.at(lc.lc_curr_line);
auto lf = lss.find(cl); auto lf = lss.find(cl);
@ -111,7 +118,8 @@ bool all_logs_vtab::is_valid(log_cursor &lc, logfile_sub_source &lss)
return true; return true;
} }
bool all_logs_vtab::next(log_cursor &lc, logfile_sub_source &lss) bool
all_logs_vtab::next(log_cursor& lc, logfile_sub_source& lss)
{ {
lc.lc_curr_line = lc.lc_curr_line + vis_line_t(1); lc.lc_curr_line = lc.lc_curr_line + vis_line_t(1);
lc.lc_sub_index = 0; lc.lc_sub_index = 0;

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -32,27 +32,26 @@
#include <array> #include <array>
#include "log_vtab_impl.hh"
#include "data_parser.hh" #include "data_parser.hh"
#include "log_vtab_impl.hh"
/** /**
* A virtual table that provides access to all log messages from all formats. * A virtual table that provides access to all log messages from all formats.
*/ */
class all_logs_vtab : public log_vtab_impl { class all_logs_vtab : public log_vtab_impl {
public: public:
all_logs_vtab(); all_logs_vtab();
void get_columns(std::vector<vtab_column> &cols) const override; void get_columns(std::vector<vtab_column>& cols) const override;
void extract(std::shared_ptr<logfile> lf, void extract(std::shared_ptr<logfile> lf,
uint64_t line_number, uint64_t line_number,
shared_buffer_ref &line, shared_buffer_ref& line,
std::vector<logline_value> &values) override; std::vector<logline_value>& values) override;
bool is_valid(log_cursor &lc, logfile_sub_source &lss) override; bool is_valid(log_cursor& lc, logfile_sub_source& lss) override;
bool next(log_cursor &lc, logfile_sub_source &lss) override; bool next(log_cursor& lc, logfile_sub_source& lss) override;
private: private:
logline_value_meta alv_value_meta; logline_value_meta alv_value_meta;
@ -62,4 +61,4 @@ private:
std::array<char, data_parser::schema_id_t::STRING_SIZE> alv_schema_buffer{}; std::array<char, data_parser::schema_id_t::STRING_SIZE> alv_schema_buffer{};
}; };
#endif //LNAV_ALL_LOGS_VTAB_HH #endif // LNAV_ALL_LOGS_VTAB_HH

@ -21,44 +21,46 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file ansi_scrubber.cc * @file ansi_scrubber.cc
*/ */
#include "config.h"
#include <algorithm> #include <algorithm>
#include "ansi_scrubber.hh"
#include "base/opt_util.hh" #include "base/opt_util.hh"
#include "view_curses.hh" #include "config.h"
#include "pcrepp/pcrepp.hh" #include "pcrepp/pcrepp.hh"
#include "ansi_scrubber.hh" #include "view_curses.hh"
using namespace std; using namespace std;
static pcrepp &ansi_regex() static pcrepp&
ansi_regex()
{ {
static pcrepp retval("\x1b\\[([\\d=;\\?]*)([a-zA-Z])"); static pcrepp retval("\x1b\\[([\\d=;\\?]*)([a-zA-Z])");
return retval; return retval;
} }
void scrub_ansi_string(std::string &str, string_attrs_t &sa) void
scrub_ansi_string(std::string& str, string_attrs_t& sa)
{ {
pcre_context_static<60> context; pcre_context_static<60> context;
pcrepp & regex = ansi_regex(); pcrepp& regex = ansi_regex();
pcre_input pi(str); pcre_input pi(str);
replace(str.begin(), str.end(), '\0', ' '); replace(str.begin(), str.end(), '\0', ' ');
while (regex.match(context, pi)) { while (regex.match(context, pi)) {
pcre_context::capture_t *caps = context.all(); pcre_context::capture_t* caps = context.all();
struct line_range lr; struct line_range lr;
bool has_attrs = false; bool has_attrs = false;
attr_t attrs = 0; attr_t attrs = 0;
auto bg = nonstd::optional<int>(); auto bg = nonstd::optional<int>();
auto fg = nonstd::optional<int>(); auto fg = nonstd::optional<int>();
auto role = nonstd::optional<int>(); auto role = nonstd::optional<int>();
@ -67,7 +69,8 @@ void scrub_ansi_string(std::string &str, string_attrs_t &sa)
switch (pi.get_substr_start(&caps[2])[0]) { switch (pi.get_substr_start(&caps[2])[0]) {
case 'm': case 'm':
for (lpc = caps[1].c_begin; for (lpc = caps[1].c_begin;
lpc != string::npos && lpc < (size_t) caps[1].c_end;) { lpc != string::npos && lpc < (size_t) caps[1].c_end;)
{
int ansi_code = 0; int ansi_code = 0;
if (sscanf(&(str[lpc]), "%d", &ansi_code) == 1) { if (sscanf(&(str[lpc]), "%d", &ansi_code) == 1) {
@ -110,9 +113,10 @@ void scrub_ansi_string(std::string &str, string_attrs_t &sa)
case 'C': { case 'C': {
unsigned int spaces = 0; unsigned int spaces = 0;
if (sscanf(&(str[caps[1].c_begin]), "%u", &spaces) == 1 && if (sscanf(&(str[caps[1].c_begin]), "%u", &spaces) == 1
spaces > 0) { && spaces > 0) {
str.insert((unsigned long) caps[0].c_end, spaces, ' '); str.insert(
(std::string::size_type) caps[0].c_end, spaces, ' ');
} }
break; break;
} }
@ -120,11 +124,13 @@ void scrub_ansi_string(std::string &str, string_attrs_t &sa)
case 'H': { case 'H': {
unsigned int row = 0, spaces = 0; unsigned int row = 0, spaces = 0;
if (sscanf(&(str[caps[1].c_begin]), "%u;%u", &row, &spaces) == 2 && if (sscanf(&(str[caps[1].c_begin]), "%u;%u", &row, &spaces) == 2
spaces > 1) { && spaces > 1) {
int ispaces = spaces - 1; int ispaces = spaces - 1;
if (ispaces > caps[0].c_begin) { if (ispaces > caps[0].c_begin) {
str.insert((unsigned long) caps[0].c_end, ispaces - caps[0].c_begin, ' '); str.insert((unsigned long) caps[0].c_end,
ispaces - caps[0].c_begin,
' ');
} }
} }
break; break;
@ -142,8 +148,7 @@ void scrub_ansi_string(std::string &str, string_attrs_t &sa)
break; break;
} }
} }
str.erase(str.begin() + caps[0].c_begin, str.erase(str.begin() + caps[0].c_begin, str.begin() + caps[0].c_end);
str.begin() + caps[0].c_end);
if (has_attrs) { if (has_attrs) {
for (auto rit = sa.rbegin(); rit != sa.rend(); rit++) { for (auto rit = sa.rbegin(); rit != sa.rend(); rit++) {
@ -153,7 +158,7 @@ void scrub_ansi_string(std::string &str, string_attrs_t &sa)
rit->sa_range.lr_end = caps[0].c_begin; rit->sa_range.lr_end = caps[0].c_begin;
} }
lr.lr_start = caps[0].c_begin; lr.lr_start = caps[0].c_begin;
lr.lr_end = -1; lr.lr_end = -1;
if (attrs) { if (attrs) {
sa.emplace_back(lr, &view_curses::VC_STYLE, attrs); sa.emplace_back(lr, &view_curses::VC_STYLE, attrs);
} }
@ -172,7 +177,8 @@ void scrub_ansi_string(std::string &str, string_attrs_t &sa)
} }
} }
void add_ansi_vars(std::map<std::string, std::string> &vars) void
add_ansi_vars(std::map<std::string, std::string>& vars)
{ {
vars["ansi_csi"] = ANSI_CSI; vars["ansi_csi"] = ANSI_CSI;
vars["ansi_norm"] = ANSI_NORM; vars["ansi_norm"] = ANSI_NORM;

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -37,22 +37,22 @@
#include "attr_line.hh" #include "attr_line.hh"
#define ANSI_CSI "\x1b[" #define ANSI_CSI "\x1b["
#define ANSI_CHAR_ATTR "m" #define ANSI_CHAR_ATTR "m"
#define ANSI_BOLD_PARAM "1" #define ANSI_BOLD_PARAM "1"
#define ANSI_BOLD_START ANSI_CSI ANSI_BOLD_PARAM ANSI_CHAR_ATTR #define ANSI_BOLD_START ANSI_CSI ANSI_BOLD_PARAM ANSI_CHAR_ATTR
#define ANSI_UNDERLINE_START ANSI_CSI "4m" #define ANSI_UNDERLINE_START ANSI_CSI "4m"
#define ANSI_NORM ANSI_CSI "0m" #define ANSI_NORM ANSI_CSI "0m"
#define ANSI_STRIKE_PARAM "9" #define ANSI_STRIKE_PARAM "9"
#define ANSI_STRIKE_START ANSI_CSI ANSI_STRIKE_PARAM ANSI_CHAR_ATTR #define ANSI_STRIKE_START ANSI_CSI ANSI_STRIKE_PARAM ANSI_CHAR_ATTR
#define ANSI_BOLD(msg) ANSI_BOLD_START msg ANSI_NORM #define ANSI_BOLD(msg) ANSI_BOLD_START msg ANSI_NORM
#define ANSI_UNDERLINE(msg) ANSI_UNDERLINE_START msg ANSI_NORM #define ANSI_UNDERLINE(msg) ANSI_UNDERLINE_START msg ANSI_NORM
#define ANSI_ROLE(msg) ANSI_CSI "%dO" msg ANSI_NORM #define ANSI_ROLE(msg) ANSI_CSI "%dO" msg ANSI_NORM
#define XANSI_COLOR(col) "3" #col #define XANSI_COLOR(col) "3" #col
#define ANSI_COLOR_PARAM(col) XANSI_COLOR(col) #define ANSI_COLOR_PARAM(col) XANSI_COLOR(col)
#define ANSI_COLOR(col) ANSI_CSI XANSI_COLOR(col) "m" #define ANSI_COLOR(col) ANSI_CSI XANSI_COLOR(col) "m"
/** /**
* Check a string for ANSI escape sequences, process them, remove them, and add * Check a string for ANSI escape sequences, process them, remove them, and add
@ -61,12 +61,12 @@
* @param str The string to check for ANSI escape sequences. * @param str The string to check for ANSI escape sequences.
* @param sa The container for any style attributes. * @param sa The container for any style attributes.
*/ */
void scrub_ansi_string(std::string &str, string_attrs_t &sa); void scrub_ansi_string(std::string& str, string_attrs_t& sa);
/** /**
* Populate a variable map with strings that contain escape sequences that * Populate a variable map with strings that contain escape sequences that
* might be useful to script writers. * might be useful to script writers.
*/ */
void add_ansi_vars(std::map<std::string, std::string> &vars); void add_ansi_vars(std::map<std::string, std::string>& vars);
#endif #endif

@ -21,36 +21,35 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file archive_manager.cc * @file archive_manager.cc
*/ */
#include "config.h"
#include <unistd.h> #include <unistd.h>
#include "config.h"
#if HAVE_ARCHIVE_H #if HAVE_ARCHIVE_H
#include "archive.h" # include "archive.h"
#include "archive_entry.h" # include "archive_entry.h"
#endif #endif
#include "archive_manager.cfg.hh"
#include "archive_manager.hh"
#include "auto_fd.hh" #include "auto_fd.hh"
#include "auto_mem.hh" #include "auto_mem.hh"
#include "fmt/format.h"
#include "base/fs_util.hh" #include "base/fs_util.hh"
#include "base/humanize.hh"
#include "base/injector.hh" #include "base/injector.hh"
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "base/humanize.hh"
#include "base/paths.hh" #include "base/paths.hh"
#include "fmt/format.h"
#include "lnav_util.hh" #include "lnav_util.hh"
#include "archive_manager.hh"
#include "archive_manager.cfg.hh"
namespace fs = ghc::filesystem; namespace fs = ghc::filesystem;
namespace archive_manager { namespace archive_manager {
@ -59,32 +58,37 @@ class archive_lock {
public: public:
class guard { class guard {
public: public:
explicit guard(archive_lock& arc_lock) : g_lock(arc_lock)
explicit guard(archive_lock& arc_lock) : g_lock(arc_lock) { {
this->g_lock.lock(); this->g_lock.lock();
}; };
~guard() { ~guard()
{
this->g_lock.unlock(); this->g_lock.unlock();
}; };
private: private:
archive_lock &g_lock; archive_lock& g_lock;
}; };
void lock() const { void lock() const
{
lockf(this->lh_fd, F_LOCK, 0); lockf(this->lh_fd, F_LOCK, 0);
}; };
void unlock() const { void unlock() const
{
lockf(this->lh_fd, F_ULOCK, 0); lockf(this->lh_fd, F_ULOCK, 0);
}; };
explicit archive_lock(const fs::path& archive_path) { explicit archive_lock(const fs::path& archive_path)
{
auto lock_path = archive_path; auto lock_path = archive_path;
lock_path += ".lck"; lock_path += ".lck";
this->lh_fd = lnav::filesystem::openp(lock_path, O_CREAT | O_RDWR, 0600); this->lh_fd
= lnav::filesystem::openp(lock_path, O_CREAT | O_RDWR, 0600);
log_perror(fcntl(this->lh_fd, F_SETFD, FD_CLOEXEC)); log_perror(fcntl(this->lh_fd, F_SETFD, FD_CLOEXEC));
}; };
@ -96,7 +100,8 @@ public:
* Enables a subset of the supported archive formats to speed up detection, * Enables a subset of the supported archive formats to speed up detection,
* since some formats, like xar are unlikely to be used. * since some formats, like xar are unlikely to be used.
*/ */
static void enable_desired_archive_formats(archive *arc) static void
enable_desired_archive_formats(archive* arc)
{ {
archive_read_support_format_7zip(arc); archive_read_support_format_7zip(arc);
archive_read_support_format_cpio(arc); archive_read_support_format_cpio(arc);
@ -107,7 +112,8 @@ static void enable_desired_archive_formats(archive *arc)
} }
#endif #endif
bool is_archive(const fs::path& filename) bool
is_archive(const fs::path& filename)
{ {
#if HAVE_ARCHIVE_H #if HAVE_ARCHIVE_H
auto_mem<archive> arc(archive_read_free); auto_mem<archive> arc(archive_read_free);
@ -120,7 +126,7 @@ bool is_archive(const fs::path& filename)
log_debug("read open %s", filename.c_str()); log_debug("read open %s", filename.c_str());
auto r = archive_read_open_filename(arc, filename.c_str(), 128 * 1024); auto r = archive_read_open_filename(arc, filename.c_str(), 128 * 1024);
if (r == ARCHIVE_OK) { if (r == ARCHIVE_OK) {
struct archive_entry *entry; struct archive_entry* entry;
auto format_name = archive_format_name(arc); auto format_name = archive_format_name(arc);
@ -145,8 +151,8 @@ bool is_archive(const fs::path& filename)
return false; return false;
} }
} }
log_info("detected archive: %s -- %s", log_info(
filename.c_str(), format_name); "detected archive: %s -- %s", filename.c_str(), format_name);
return true; return true;
} else { } else {
log_info("archive read header failed: %s -- %s", log_info("archive read header failed: %s -- %s",
@ -163,14 +169,14 @@ bool is_archive(const fs::path& filename)
return false; return false;
} }
static static fs::path
fs::path archive_cache_path() archive_cache_path()
{ {
return lnav::paths::workdir() / "archives"; return lnav::paths::workdir() / "archives";
} }
fs::path fs::path
filename_to_tmp_path(const std::string &filename) filename_to_tmp_path(const std::string& filename)
{ {
auto fn_path = fs::path(filename); auto fn_path = fs::path(filename);
auto basename = fn_path.filename().string(); auto basename = fn_path.filename().string();
@ -195,14 +201,14 @@ filename_to_tmp_path(const std::string &filename)
#if HAVE_ARCHIVE_H #if HAVE_ARCHIVE_H
static walk_result_t static walk_result_t
copy_data(const std::string& filename, copy_data(const std::string& filename,
struct archive *ar, struct archive* ar,
struct archive_entry *entry, struct archive_entry* entry,
struct archive *aw, struct archive* aw,
const fs::path &entry_path, const fs::path& entry_path,
struct extract_progress *ep) struct extract_progress* ep)
{ {
int r; int r;
const void *buff; const void* buff;
size_t size, total = 0, next_space_check = 0; size_t size, total = 0, next_space_check = 0;
la_int64_t offset; la_int64_t offset;
@ -213,7 +219,9 @@ copy_data(const std::string& filename,
if (tmp_space.available < cfg.amc_min_free_space) { if (tmp_space.available < cfg.amc_min_free_space) {
return Err(fmt::format( return Err(fmt::format(
FMT_STRING("available space on disk ({}) is below the minimum-free threshold ({}). Unable to unpack '{}' to '{}'"), FMT_STRING("available space on disk ({}) is below the "
"minimum-free threshold ({}). Unable to unpack "
"'{}' to '{}'"),
humanize::file_size(tmp_space.available), humanize::file_size(tmp_space.available),
humanize::file_size(cfg.amc_min_free_space), humanize::file_size(cfg.amc_min_free_space),
entry_path.filename().string(), entry_path.filename().string(),
@ -244,12 +252,11 @@ copy_data(const std::string& filename,
} }
} }
static walk_result_t extract(const std::string &filename, const extract_cb &cb) static walk_result_t
extract(const std::string& filename, const extract_cb& cb)
{ {
static int FLAGS = ARCHIVE_EXTRACT_TIME static int FLAGS = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM
| ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS;
| ARCHIVE_EXTRACT_ACL
| ARCHIVE_EXTRACT_FFLAGS;
auto tmp_path = filename_to_tmp_path(filename); auto tmp_path = filename_to_tmp_path(filename);
auto arc_lock = archive_lock(tmp_path); auto arc_lock = archive_lock(tmp_path);
@ -267,9 +274,9 @@ static walk_result_t extract(const std::string &filename, const extract_cb &cb)
} }
} }
if (file_count > 0) { if (file_count > 0) {
fs::last_write_time( fs::last_write_time(done_path, std::chrono::system_clock::now());
done_path, std::chrono::system_clock::now()); log_info("%s: archive has already been extracted!",
log_info("%s: archive has already been extracted!", done_path.c_str()); done_path.c_str());
return Ok(); return Ok();
} else { } else {
log_warning("%s: archive cache has been damaged, re-extracting", log_warning("%s: archive cache has been damaged, re-extracting",
@ -289,17 +296,16 @@ static walk_result_t extract(const std::string &filename, const extract_cb &cb)
ext = archive_write_disk_new(); ext = archive_write_disk_new();
archive_write_disk_set_options(ext, FLAGS); archive_write_disk_set_options(ext, FLAGS);
archive_write_disk_set_standard_lookup(ext); archive_write_disk_set_standard_lookup(ext);
if (archive_read_open_filename(arc, filename.c_str(), 10240) != ARCHIVE_OK) { if (archive_read_open_filename(arc, filename.c_str(), 10240) != ARCHIVE_OK)
{
return Err(fmt::format("unable to open archive: {} -- {}", return Err(fmt::format("unable to open archive: {} -- {}",
filename, filename,
archive_error_string(arc))); archive_error_string(arc)));
} }
log_info("extracting %s to %s", log_info("extracting %s to %s", filename.c_str(), tmp_path.c_str());
filename.c_str(),
tmp_path.c_str());
while (true) { while (true) {
struct archive_entry *entry; struct archive_entry* entry;
auto r = archive_read_next_header(arc, &entry); auto r = archive_read_next_header(arc, &entry);
if (r == ARCHIVE_EOF) { if (r == ARCHIVE_EOF) {
log_info("all done"); log_info("all done");
@ -321,22 +327,21 @@ static walk_result_t extract(const std::string &filename, const extract_cb &cb)
desired_pathname = fs::path(filename).filename(); desired_pathname = fs::path(filename).filename();
} }
auto entry_path = tmp_path / desired_pathname; auto entry_path = tmp_path / desired_pathname;
auto prog = cb(entry_path, auto prog = cb(
archive_entry_size_is_set(entry) ? entry_path,
archive_entry_size(entry) : -1); archive_entry_size_is_set(entry) ? archive_entry_size(entry) : -1);
archive_entry_copy_pathname(wentry, entry_path.c_str()); archive_entry_copy_pathname(wentry, entry_path.c_str());
auto entry_mode = archive_entry_mode(wentry); auto entry_mode = archive_entry_mode(wentry);
archive_entry_set_perm( archive_entry_set_perm(
wentry, S_IRUSR | (S_ISDIR(entry_mode) ? S_IXUSR|S_IWUSR : 0)); wentry, S_IRUSR | (S_ISDIR(entry_mode) ? S_IXUSR | S_IWUSR : 0));
r = archive_write_header(ext, wentry); r = archive_write_header(ext, wentry);
if (r < ARCHIVE_OK) { if (r < ARCHIVE_OK) {
return Err(fmt::format("unable to write entry: {} -- {}", return Err(fmt::format("unable to write entry: {} -- {}",
entry_path.string(), entry_path.string(),
archive_error_string(ext))); archive_error_string(ext)));
} } else if (!archive_entry_size_is_set(entry)
else if (!archive_entry_size_is_set(entry) || || archive_entry_size(entry) > 0) {
archive_entry_size(entry) > 0) {
TRY(copy_data(filename, arc, entry, ext, entry_path, prog)); TRY(copy_data(filename, arc, entry, ext, entry_path, prog));
} }
r = archive_write_finish_entry(ext); r = archive_write_finish_entry(ext);
@ -355,12 +360,12 @@ static walk_result_t extract(const std::string &filename, const extract_cb &cb)
} }
#endif #endif
walk_result_t walk_archive_files( walk_result_t
const std::string &filename, walk_archive_files(
const extract_cb &cb, const std::string& filename,
const std::function<void( const extract_cb& cb,
const fs::path&, const std::function<void(const fs::path&, const fs::directory_entry&)>&
const fs::directory_entry &)>& callback) callback)
{ {
#if HAVE_ARCHIVE_H #if HAVE_ARCHIVE_H
auto tmp_path = filename_to_tmp_path(filename); auto tmp_path = filename_to_tmp_path(filename);
@ -385,7 +390,8 @@ walk_result_t walk_archive_files(
#endif #endif
} }
void cleanup_cache() void
cleanup_cache()
{ {
(void) std::async(std::launch::async, []() { (void) std::async(std::launch::async, []() {
auto now = std::chrono::system_clock::now(); auto now = std::chrono::system_clock::now();
@ -421,4 +427,4 @@ void cleanup_cache()
}); });
} }
} } // namespace archive_manager

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -41,6 +41,6 @@ struct config {
std::chrono::seconds amc_cache_ttl{std::chrono::hours(48)}; std::chrono::seconds amc_cache_ttl{std::chrono::hours(48)};
}; };
} } // namespace archive_manager
#endif #endif

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -33,8 +33,8 @@
#define lnav_archive_manager_hh #define lnav_archive_manager_hh
#include <atomic> #include <atomic>
#include <string>
#include <functional> #include <functional>
#include <string>
#include <utility> #include <utility>
#include "base/result.h" #include "base/result.h"
@ -43,34 +43,33 @@
namespace archive_manager { namespace archive_manager {
struct extract_progress { struct extract_progress {
extract_progress(ghc::filesystem::path path, extract_progress(ghc::filesystem::path path, ssize_t total)
ssize_t total) : ep_path(std::move(path)), : ep_path(std::move(path)), ep_total_size(total)
ep_total_size(total) {
{} }
const ghc::filesystem::path ep_path; const ghc::filesystem::path ep_path;
const ssize_t ep_total_size; const ssize_t ep_total_size;
std::atomic<size_t> ep_out_size{0}; std::atomic<size_t> ep_out_size{0};
}; };
using extract_cb = std::function<extract_progress *( using extract_cb
const ghc::filesystem::path &, ssize_t)>; = std::function<extract_progress*(const ghc::filesystem::path&, ssize_t)>;
bool is_archive(const ghc::filesystem::path& filename); bool is_archive(const ghc::filesystem::path& filename);
ghc::filesystem::path filename_to_tmp_path(const std::string &filename); ghc::filesystem::path filename_to_tmp_path(const std::string& filename);
using walk_result_t = Result<void, std::string>; using walk_result_t = Result<void, std::string>;
walk_result_t walk_archive_files( walk_result_t walk_archive_files(
const std::string &filename, const std::string& filename,
const extract_cb &cb, const extract_cb& cb,
const std::function<void( const std::function<void(const ghc::filesystem::path&,
const ghc::filesystem::path &, const ghc::filesystem::directory_entry&)>&);
const ghc::filesystem::directory_entry &)> &);
void cleanup_cache(); void cleanup_cache();
} } // namespace archive_manager
#endif #endif

@ -21,22 +21,23 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <algorithm> #include <algorithm>
#include "auto_mem.hh" #include "attr_line.hh"
#include "ansi_scrubber.hh" #include "ansi_scrubber.hh"
#include "auto_mem.hh"
#include "config.h"
#include "view_curses.hh" #include "view_curses.hh"
#include "attr_line.hh"
attr_line_t &attr_line_t::with_ansi_string(const char *str, ...) attr_line_t&
attr_line_t::with_ansi_string(const char* str, ...)
{ {
auto_mem<char> formatted_str; auto_mem<char> formatted_str;
va_list args; va_list args;
@ -53,7 +54,8 @@ attr_line_t &attr_line_t::with_ansi_string(const char *str, ...)
return *this; return *this;
} }
attr_line_t &attr_line_t::with_ansi_string(const std::string &str) attr_line_t&
attr_line_t::with_ansi_string(const std::string& str)
{ {
this->al_string = str; this->al_string = str;
scrub_ansi_string(this->al_string, this->al_attrs); scrub_ansi_string(this->al_string, this->al_attrs);
@ -61,7 +63,10 @@ attr_line_t &attr_line_t::with_ansi_string(const std::string &str)
return *this; return *this;
} }
attr_line_t &attr_line_t::insert(size_t index, const attr_line_t &al, text_wrap_settings *tws) attr_line_t&
attr_line_t::insert(size_t index,
const attr_line_t& al,
text_wrap_settings* tws)
{ {
if (index < this->al_string.length()) { if (index < this->al_string.length()) {
shift_string_attrs(this->al_attrs, index, al.al_string.length()); shift_string_attrs(this->al_attrs, index, al.al_string.length());
@ -69,10 +74,10 @@ attr_line_t &attr_line_t::insert(size_t index, const attr_line_t &al, text_wrap_
this->al_string.insert(index, al.al_string); this->al_string.insert(index, al.al_string);
for (auto &sa : al.al_attrs) { for (auto& sa : al.al_attrs) {
this->al_attrs.emplace_back(sa); this->al_attrs.emplace_back(sa);
line_range &lr = this->al_attrs.back().sa_range; line_range& lr = this->al_attrs.back().sa_range;
lr.shift(0, index); lr.shift(0, index);
if (lr.lr_end == -1) { if (lr.lr_end == -1) {
@ -80,7 +85,7 @@ attr_line_t &attr_line_t::insert(size_t index, const attr_line_t &al, text_wrap_
} }
} }
if (tws != nullptr && (int)this->al_string.length() > tws->tws_width) { if (tws != nullptr && (int) this->al_string.length() > tws->tws_width) {
ssize_t start_pos = index; ssize_t start_pos = index;
ssize_t line_start = this->al_string.rfind('\n', start_pos); ssize_t line_start = this->al_string.rfind('\n', start_pos);
@ -92,26 +97,27 @@ attr_line_t &attr_line_t::insert(size_t index, const attr_line_t &al, text_wrap_
ssize_t line_len = index - line_start; ssize_t line_len = index - line_start;
ssize_t usable_width = tws->tws_width - tws->tws_indent; ssize_t usable_width = tws->tws_width - tws->tws_indent;
ssize_t avail = std::max((ssize_t) 0, (ssize_t) tws->tws_width - line_len); ssize_t avail
= std::max((ssize_t) 0, (ssize_t) tws->tws_width - line_len);
if (avail == 0) { if (avail == 0) {
avail = INT_MAX; avail = INT_MAX;
} }
while (start_pos < (int)this->al_string.length()) { while (start_pos < (int) this->al_string.length()) {
ssize_t lpc; ssize_t lpc;
// Find the end of a word or a breakpoint. // Find the end of a word or a breakpoint.
for (lpc = start_pos; for (lpc = start_pos; lpc < (int) this->al_string.length()
lpc < (int)this->al_string.length() && && (isalnum(this->al_string[lpc])
(isalnum(this->al_string[lpc]) || || this->al_string[lpc] == ','
this->al_string[lpc] == ',' || || this->al_string[lpc] == '_'
this->al_string[lpc] == '_' || || this->al_string[lpc] == '.'
this->al_string[lpc] == '.' || || this->al_string[lpc] == ';');
this->al_string[lpc] == ';'); lpc++)
lpc++) { {
if (this->al_string[lpc] == '-' || if (this->al_string[lpc] == '-' || this->al_string[lpc] == '.')
this->al_string[lpc] == '.') { {
lpc += 1; lpc += 1;
break; break;
} }
@ -126,15 +132,15 @@ attr_line_t &attr_line_t::insert(size_t index, const attr_line_t &al, text_wrap_
} else { } else {
// There's still room to add stuff. // There's still room to add stuff.
avail -= (lpc - start_pos); avail -= (lpc - start_pos);
while (lpc < (int)this->al_string.length() && avail) { while (lpc < (int) this->al_string.length() && avail) {
if (this->al_string[lpc] == '\n') { if (this->al_string[lpc] == '\n') {
this->insert(lpc + 1, tws->tws_indent, ' '); this->insert(lpc + 1, tws->tws_indent, ' ');
avail = usable_width; avail = usable_width;
lpc += 1 + tws->tws_indent; lpc += 1 + tws->tws_indent;
break; break;
} }
if (isalnum(this->al_string[lpc]) || if (isalnum(this->al_string[lpc])
this->al_string[lpc] == '_') { || this->al_string[lpc] == '_') {
break; break;
} }
avail -= 1; avail -= 1;
@ -147,10 +153,10 @@ attr_line_t &attr_line_t::insert(size_t index, const attr_line_t &al, text_wrap_
start_pos += 1 + tws->tws_indent; start_pos += 1 + tws->tws_indent;
avail = usable_width; avail = usable_width;
for (lpc = start_pos; for (lpc = start_pos; lpc < (int) this->al_string.length()
lpc < (int)this->al_string.length() && && this->al_string[lpc] == ' ';
this->al_string[lpc] == ' '; lpc++)
lpc++) { {
} }
if (lpc != start_pos) { if (lpc != start_pos) {
@ -164,7 +170,8 @@ attr_line_t &attr_line_t::insert(size_t index, const attr_line_t &al, text_wrap_
return *this; return *this;
} }
attr_line_t attr_line_t::subline(size_t start, size_t len) const attr_line_t
attr_line_t::subline(size_t start, size_t len) const
{ {
if (len == std::string::npos) { if (len == std::string::npos) {
len = this->al_string.length() - start; len = this->al_string.length() - start;
@ -174,16 +181,17 @@ attr_line_t attr_line_t::subline(size_t start, size_t len) const
attr_line_t retval; attr_line_t retval;
retval.al_string = this->al_string.substr(start, len); retval.al_string = this->al_string.substr(start, len);
for (auto &sa : this->al_attrs) { for (auto& sa : this->al_attrs) {
if (!lr.intersects(sa.sa_range)) { if (!lr.intersects(sa.sa_range)) {
continue; continue;
} }
retval.al_attrs.emplace_back(lr.intersection(sa.sa_range) retval.al_attrs.emplace_back(
.shift(lr.lr_start, -lr.lr_start), lr.intersection(sa.sa_range).shift(lr.lr_start, -lr.lr_start),
sa.sa_type, sa.sa_value); sa.sa_type,
sa.sa_value);
line_range &last_lr = retval.al_attrs.back().sa_range; line_range& last_lr = retval.al_attrs.back().sa_range;
ensure(last_lr.lr_end <= (int) retval.al_string.length()); ensure(last_lr.lr_end <= (int) retval.al_string.length());
} }
@ -191,7 +199,8 @@ attr_line_t attr_line_t::subline(size_t start, size_t len) const
return retval; return retval;
} }
void attr_line_t::split_lines(std::vector<attr_line_t> &lines) const void
attr_line_t::split_lines(std::vector<attr_line_t>& lines) const
{ {
size_t pos = 0, next_line; size_t pos = 0, next_line;
@ -202,12 +211,13 @@ void attr_line_t::split_lines(std::vector<attr_line_t> &lines) const
lines.emplace_back(this->subline(pos)); lines.emplace_back(this->subline(pos));
} }
attr_line_t &attr_line_t::right_justify(unsigned long width) attr_line_t&
attr_line_t::right_justify(unsigned long width)
{ {
long padding = width - this->length(); long padding = width - this->length();
if (padding > 0) { if (padding > 0) {
this->al_string.insert(0, padding, ' '); this->al_string.insert(0, padding, ' ');
for (auto &al_attr : this->al_attrs) { for (auto& al_attr : this->al_attrs) {
if (al_attr.sa_range.lr_start > 0) { if (al_attr.sa_range.lr_start > 0) {
al_attr.sa_range.lr_start += padding; al_attr.sa_range.lr_start += padding;
} }
@ -220,7 +230,8 @@ attr_line_t &attr_line_t::right_justify(unsigned long width)
return *this; return *this;
} }
size_t attr_line_t::nearest_text(size_t x) const size_t
attr_line_t::nearest_text(size_t x) const
{ {
if (x > 0 && x >= (size_t) this->length()) { if (x > 0 && x >= (size_t) this->length()) {
if (this->empty()) { if (this->empty()) {
@ -237,18 +248,18 @@ size_t attr_line_t::nearest_text(size_t x) const
return x; return x;
} }
void attr_line_t::apply_hide() void
attr_line_t::apply_hide()
{ {
auto& sa = this->al_attrs; auto& sa = this->al_attrs;
for (auto &sattr : sa) { for (auto& sattr : sa) {
if (sattr.sa_type == &SA_HIDDEN && if (sattr.sa_type == &SA_HIDDEN && sattr.sa_range.length() > 3) {
sattr.sa_range.length() > 3) { struct line_range& lr = sattr.sa_range;
struct line_range &lr = sattr.sa_range;
std::for_each(sa.begin(), sa.end(), [&] (string_attr &attr) { std::for_each(sa.begin(), sa.end(), [&](string_attr& attr) {
if (attr.sa_type == &view_curses::VC_STYLE && if (attr.sa_type == &view_curses::VC_STYLE
lr.contains(attr.sa_range)) { && lr.contains(attr.sa_range)) {
attr.sa_type = &SA_REMOVED; attr.sa_type = &SA_REMOVED;
} }
}); });
@ -258,7 +269,6 @@ void attr_line_t::apply_hide()
sattr.sa_type = &view_curses::VC_ROLE; sattr.sa_type = &view_curses::VC_ROLE;
sattr.sa_value.sav_int = view_colors::VCR_HIDDEN; sattr.sa_value.sav_int = view_colors::VCR_HIDDEN;
lr.lr_end = lr.lr_start + 3; lr.lr_end = lr.lr_start + 3;
} }
} }
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,14 +32,14 @@
#ifndef attr_line_hh #ifndef attr_line_hh
#define attr_line_hh #define attr_line_hh
#include <limits.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <limits.h>
#include "base/intern_string.hh"
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "base/string_util.hh" #include "base/string_util.hh"
#include "base/intern_string.hh"
#include "string_attr_type.hh" #include "string_attr_type.hh"
/** /**
@ -49,9 +49,11 @@ struct line_range {
int lr_start; int lr_start;
int lr_end; int lr_end;
explicit line_range(int start = -1, int end = -1) : lr_start(start), lr_end(end) { }; explicit line_range(int start = -1, int end = -1)
: lr_start(start), lr_end(end){};
bool is_valid() const { bool is_valid() const
{
return this->lr_start != -1; return this->lr_start != -1;
} }
@ -60,19 +62,23 @@ struct line_range {
return this->lr_end == -1 ? INT_MAX : this->lr_end - this->lr_start; return this->lr_end == -1 ? INT_MAX : this->lr_end - this->lr_start;
}; };
bool contains(int pos) const { bool contains(int pos) const
{
return this->lr_start <= pos && pos < this->lr_end; return this->lr_start <= pos && pos < this->lr_end;
}; };
bool contains(const struct line_range &other) const { bool contains(const struct line_range& other) const
{
return this->contains(other.lr_start) && other.lr_end <= this->lr_end; return this->contains(other.lr_start) && other.lr_end <= this->lr_end;
}; };
bool intersects(const struct line_range &other) const { bool intersects(const struct line_range& other) const
{
return this->contains(other.lr_start) || this->contains(other.lr_end); return this->contains(other.lr_start) || this->contains(other.lr_end);
}; };
line_range intersection(const struct line_range &other) const { line_range intersection(const struct line_range& other) const
{
int actual_end; int actual_end;
if (this->lr_end == -1) { if (this->lr_end == -1) {
@ -85,7 +91,8 @@ struct line_range {
return line_range{std::max(this->lr_start, other.lr_start), actual_end}; return line_range{std::max(this->lr_start, other.lr_start), actual_end};
}; };
line_range &shift(int32_t start, int32_t amount) { line_range& shift(int32_t start, int32_t amount)
{
if (this->lr_start >= start) { if (this->lr_start >= start) {
this->lr_start = std::max(0, this->lr_start + amount); this->lr_start = std::max(0, this->lr_start + amount);
} }
@ -99,35 +106,46 @@ struct line_range {
return *this; return *this;
}; };
void ltrim(const char *str) { void ltrim(const char* str)
{
while (this->lr_start < this->lr_end && isspace(str[this->lr_start])) { while (this->lr_start < this->lr_end && isspace(str[this->lr_start])) {
this->lr_start += 1; this->lr_start += 1;
} }
}; };
bool operator<(const struct line_range &rhs) const bool operator<(const struct line_range& rhs) const
{ {
if (this->lr_start < rhs.lr_start) { return true; } if (this->lr_start < rhs.lr_start) {
else if (this->lr_start > rhs.lr_start) { return false; } return true;
} else if (this->lr_start > rhs.lr_start) {
return false;
}
if (this->lr_end == rhs.lr_end) { return false; } if (this->lr_end == rhs.lr_end) {
return false;
}
if (this->lr_end < rhs.lr_end) { return true; } if (this->lr_end < rhs.lr_end) {
return true;
}
return false; return false;
}; };
bool operator==(const struct line_range &rhs) const { bool operator==(const struct line_range& rhs) const
{
return (this->lr_start == rhs.lr_start && this->lr_end == rhs.lr_end); return (this->lr_start == rhs.lr_start && this->lr_end == rhs.lr_end);
}; };
const char *substr(const std::string &str) const { const char* substr(const std::string& str) const
{
if (this->lr_start == -1) { if (this->lr_start == -1) {
return str.c_str(); return str.c_str();
} }
return &(str.c_str()[this->lr_start]); return &(str.c_str()[this->lr_start]);
} }
size_t sublen(const std::string &str) const { size_t sublen(const std::string& str) const
{
if (this->lr_start == -1) { if (this->lr_start == -1) {
return str.length(); return str.length();
} }
@ -142,53 +160,67 @@ struct line_range {
* Container for attribute values for a substring. * Container for attribute values for a substring.
*/ */
typedef union { typedef union {
const void *sav_ptr; const void* sav_ptr;
int64_t sav_int; int64_t sav_int;
} string_attr_value_t; } string_attr_value_t;
struct string_attr { struct string_attr {
string_attr(const struct line_range &lr, string_attr_type_t type, void *val) string_attr(const struct line_range& lr, string_attr_type_t type, void* val)
: sa_range(lr), sa_type(type) { : sa_range(lr), sa_type(type)
{
require(lr.is_valid()); require(lr.is_valid());
require(type); require(type);
this->sa_value.sav_ptr = val; this->sa_value.sav_ptr = val;
}; };
string_attr(const struct line_range &lr, string_attr_type_t type, std::string val) string_attr(const struct line_range& lr,
: sa_range(lr), sa_type(type), sa_str_value(std::move(val)) { string_attr_type_t type,
std::string val)
: sa_range(lr), sa_type(type), sa_str_value(std::move(val))
{
require(lr.is_valid()); require(lr.is_valid());
require(type); require(type);
}; };
string_attr(const struct line_range &lr, string_attr_type_t type, intern_string_t val) string_attr(const struct line_range& lr,
: sa_range(lr), sa_type(type) { string_attr_type_t type,
intern_string_t val)
: sa_range(lr), sa_type(type)
{
require(lr.is_valid()); require(lr.is_valid());
require(type); require(type);
this->sa_value.sav_ptr = val.unwrap(); this->sa_value.sav_ptr = val.unwrap();
}; };
string_attr(const struct line_range &lr, string_attr_type_t type, int64_t val = 0) string_attr(const struct line_range& lr,
: sa_range(lr), sa_type(type) { string_attr_type_t type,
int64_t val = 0)
: sa_range(lr), sa_type(type)
{
require(lr.is_valid()); require(lr.is_valid());
require(type); require(type);
this->sa_value.sav_int = val; this->sa_value.sav_int = val;
}; };
string_attr(const struct line_range &lr, string_attr_type_t type, string_attr_value_t val) string_attr(const struct line_range& lr,
: sa_range(lr), sa_type(type), sa_value(val) { string_attr_type_t type,
string_attr_value_t val)
: sa_range(lr), sa_type(type), sa_value(val)
{
require(lr.is_valid()); require(lr.is_valid());
require(type); require(type);
}; };
string_attr() : sa_type(nullptr) { }; string_attr() : sa_type(nullptr){};
bool operator<(const struct string_attr &rhs) const bool operator<(const struct string_attr& rhs) const
{ {
return this->sa_range < rhs.sa_range; return this->sa_range < rhs.sa_range;
}; };
intern_string_t to_string() const { intern_string_t to_string() const
return intern_string_t((const intern_string *) this->sa_value.sav_ptr); {
return intern_string_t((const intern_string*) this->sa_value.sav_ptr);
}; };
struct line_range sa_range; struct line_range sa_range;
@ -201,7 +233,9 @@ struct string_attr {
typedef std::vector<string_attr> string_attrs_t; typedef std::vector<string_attr> string_attrs_t;
inline string_attrs_t::const_iterator inline string_attrs_t::const_iterator
find_string_attr(const string_attrs_t &sa, string_attr_type_t type, int start = 0) find_string_attr(const string_attrs_t& sa,
string_attr_type_t type,
int start = 0)
{ {
string_attrs_t::const_iterator iter; string_attrs_t::const_iterator iter;
@ -215,7 +249,9 @@ find_string_attr(const string_attrs_t &sa, string_attr_type_t type, int start =
} }
inline nonstd::optional<const string_attr*> inline nonstd::optional<const string_attr*>
get_string_attr(const string_attrs_t &sa, string_attr_type_t type, int start = 0) get_string_attr(const string_attrs_t& sa,
string_attr_type_t type,
int start = 0)
{ {
auto iter = find_string_attr(sa, type, start); auto iter = find_string_attr(sa, type, start);
@ -228,7 +264,9 @@ get_string_attr(const string_attrs_t &sa, string_attr_type_t type, int start = 0
template<typename T> template<typename T>
inline string_attrs_t::const_iterator inline string_attrs_t::const_iterator
find_string_attr_containing(const string_attrs_t &sa, string_attr_type_t type, T x) find_string_attr_containing(const string_attrs_t& sa,
string_attr_type_t type,
T x)
{ {
string_attrs_t::const_iterator iter; string_attrs_t::const_iterator iter;
@ -242,7 +280,7 @@ find_string_attr_containing(const string_attrs_t &sa, string_attr_type_t type, T
} }
inline string_attrs_t::iterator inline string_attrs_t::iterator
find_string_attr(string_attrs_t &sa, const struct line_range &lr) find_string_attr(string_attrs_t& sa, const struct line_range& lr)
{ {
string_attrs_t::iterator iter; string_attrs_t::iterator iter;
@ -256,13 +294,13 @@ find_string_attr(string_attrs_t &sa, const struct line_range &lr)
} }
inline string_attrs_t::const_iterator inline string_attrs_t::const_iterator
find_string_attr(const string_attrs_t &sa, size_t near) find_string_attr(const string_attrs_t& sa, size_t near)
{ {
string_attrs_t::const_iterator iter, nearest = sa.end(); string_attrs_t::const_iterator iter, nearest = sa.end();
ssize_t last_diff = INT_MAX; ssize_t last_diff = INT_MAX;
for (iter = sa.begin(); iter != sa.end(); ++iter) { for (iter = sa.begin(); iter != sa.end(); ++iter) {
auto &lr = iter->sa_range; auto& lr = iter->sa_range;
if (!lr.is_valid() || !lr.contains(near)) { if (!lr.is_valid() || !lr.contains(near)) {
continue; continue;
@ -280,13 +318,13 @@ find_string_attr(const string_attrs_t &sa, size_t near)
template<typename T> template<typename T>
inline string_attrs_t::const_iterator inline string_attrs_t::const_iterator
rfind_string_attr_if(const string_attrs_t &sa, ssize_t near, T predicate) rfind_string_attr_if(const string_attrs_t& sa, ssize_t near, T predicate)
{ {
string_attrs_t::const_iterator iter, nearest = sa.end(); string_attrs_t::const_iterator iter, nearest = sa.end();
ssize_t last_diff = INT_MAX; ssize_t last_diff = INT_MAX;
for (iter = sa.begin(); iter != sa.end(); ++iter) { for (iter = sa.begin(); iter != sa.end(); ++iter) {
auto &lr = iter->sa_range; auto& lr = iter->sa_range;
if (lr.lr_start > near) { if (lr.lr_start > near) {
continue; continue;
@ -307,7 +345,7 @@ rfind_string_attr_if(const string_attrs_t &sa, ssize_t near, T predicate)
} }
inline struct line_range inline struct line_range
find_string_attr_range(const string_attrs_t &sa, string_attr_type_t type) find_string_attr_range(const string_attrs_t& sa, string_attr_type_t type)
{ {
auto iter = find_string_attr(sa, type); auto iter = find_string_attr(sa, type);
@ -318,7 +356,8 @@ find_string_attr_range(const string_attrs_t &sa, string_attr_type_t type)
return line_range(); return line_range();
} }
inline void remove_string_attr(string_attrs_t &sa, const struct line_range &lr) inline void
remove_string_attr(string_attrs_t& sa, const struct line_range& lr)
{ {
string_attrs_t::iterator iter; string_attrs_t::iterator iter;
@ -327,7 +366,8 @@ inline void remove_string_attr(string_attrs_t &sa, const struct line_range &lr)
} }
} }
inline void remove_string_attr(string_attrs_t &sa, string_attr_type_t type) inline void
remove_string_attr(string_attrs_t& sa, string_attr_type_t type)
{ {
for (auto iter = sa.begin(); iter != sa.end();) { for (auto iter = sa.begin(); iter != sa.end();) {
if (iter->sa_type == type) { if (iter->sa_type == type) {
@ -338,22 +378,25 @@ inline void remove_string_attr(string_attrs_t &sa, string_attr_type_t type)
} }
} }
inline void shift_string_attrs(string_attrs_t &sa, int32_t start, int32_t amount) inline void
shift_string_attrs(string_attrs_t& sa, int32_t start, int32_t amount)
{ {
for (auto &iter : sa) { for (auto& iter : sa) {
iter.sa_range.shift(start, amount); iter.sa_range.shift(start, amount);
} }
} }
struct text_wrap_settings { struct text_wrap_settings {
text_wrap_settings() : tws_indent(2), tws_width(80) {}; text_wrap_settings() : tws_indent(2), tws_width(80){};
text_wrap_settings &with_indent(int indent) { text_wrap_settings& with_indent(int indent)
{
this->tws_indent = indent; this->tws_indent = indent;
return *this; return *this;
}; };
text_wrap_settings &with_width(int width) { text_wrap_settings& with_width(int width)
{
this->tws_width = width; this->tws_width = width;
return *this; return *this;
}; };
@ -367,62 +410,80 @@ struct text_wrap_settings {
*/ */
class attr_line_t { class attr_line_t {
public: public:
attr_line_t() { attr_line_t()
{
this->al_attrs.reserve(RESERVE_SIZE); this->al_attrs.reserve(RESERVE_SIZE);
}; };
attr_line_t(std::string str) : al_string(std::move(str)) { attr_line_t(std::string str) : al_string(std::move(str))
{
this->al_attrs.reserve(RESERVE_SIZE); this->al_attrs.reserve(RESERVE_SIZE);
}; };
attr_line_t(const char *str) : al_string(str) { attr_line_t(const char* str) : al_string(str)
{
this->al_attrs.reserve(RESERVE_SIZE); this->al_attrs.reserve(RESERVE_SIZE);
}; };
static inline attr_line_t from_ansi_str(const char *str) { static inline attr_line_t from_ansi_str(const char* str)
{
attr_line_t retval; attr_line_t retval;
return retval.with_ansi_string("%s", str); return retval.with_ansi_string("%s", str);
}; };
/** @return The string itself. */ /** @return The string itself. */
std::string &get_string() { return this->al_string; }; std::string& get_string()
{
return this->al_string;
};
const std::string &get_string() const { return this->al_string; }; const std::string& get_string() const
{
return this->al_string;
};
/** @return The attributes for the string. */ /** @return The attributes for the string. */
string_attrs_t &get_attrs() { return this->al_attrs; }; string_attrs_t& get_attrs()
{
return this->al_attrs;
};
const string_attrs_t &get_attrs() const { return this->al_attrs; }; const string_attrs_t& get_attrs() const
{
return this->al_attrs;
};
attr_line_t &with_string(const std::string &str) { attr_line_t& with_string(const std::string& str)
{
this->al_string = str; this->al_string = str;
return *this; return *this;
} }
attr_line_t &with_ansi_string(const char *str, ...); attr_line_t& with_ansi_string(const char* str, ...);
attr_line_t &with_ansi_string(const std::string &str); attr_line_t& with_ansi_string(const std::string& str);
attr_line_t &with_attr(const string_attr &sa) { attr_line_t& with_attr(const string_attr& sa)
{
this->al_attrs.push_back(sa); this->al_attrs.push_back(sa);
return *this; return *this;
}; };
attr_line_t &ensure_space() { attr_line_t& ensure_space()
if (!this->al_string.empty() && {
this->al_string.back() != ' ' && if (!this->al_string.empty() && this->al_string.back() != ' '
this->al_string.back() != '[') { && this->al_string.back() != '[')
{
this->append(1, ' '); this->append(1, ' ');
} }
return *this; return *this;
}; };
template<typename S, typename T = void *> template<typename S, typename T = void*>
attr_line_t &append(S str, attr_line_t& append(S str, string_attr_type_t type = nullptr, T val = T())
string_attr_type_t type = nullptr, {
T val = T()) {
size_t start_len = this->al_string.length(); size_t start_len = this->al_string.length();
this->al_string.append(str); this->al_string.append(str);
@ -434,24 +495,31 @@ public:
return *this; return *this;
}; };
attr_line_t &append(const char *str, size_t len) { attr_line_t& append(const char* str, size_t len)
{
this->al_string.append(str, len); this->al_string.append(str, len);
return *this; return *this;
}; };
attr_line_t &insert(size_t index, const attr_line_t &al, text_wrap_settings *tws = nullptr); attr_line_t& insert(size_t index,
const attr_line_t& al,
text_wrap_settings* tws = nullptr);
attr_line_t &append(const attr_line_t &al, text_wrap_settings *tws = nullptr) { attr_line_t& append(const attr_line_t& al,
text_wrap_settings* tws = nullptr)
{
return this->insert(this->al_string.length(), al, tws); return this->insert(this->al_string.length(), al, tws);
}; };
attr_line_t &append(size_t len, char c) { attr_line_t& append(size_t len, char c)
{
this->al_string.append(len, c); this->al_string.append(len, c);
return *this; return *this;
}; };
attr_line_t &insert(size_t index, size_t len, char c) { attr_line_t& insert(size_t index, size_t len, char c)
{
this->al_string.insert(index, len, c); this->al_string.insert(index, len, c);
shift_string_attrs(this->al_attrs, index, len); shift_string_attrs(this->al_attrs, index, len);
@ -459,7 +527,8 @@ public:
return *this; return *this;
} }
attr_line_t &insert(size_t index, const char *str) { attr_line_t& insert(size_t index, const char* str)
{
this->al_string.insert(index, str); this->al_string.insert(index, str);
shift_string_attrs(this->al_attrs, index, strlen(str)); shift_string_attrs(this->al_attrs, index, strlen(str));
@ -467,7 +536,8 @@ public:
return *this; return *this;
} }
attr_line_t &erase(size_t pos, size_t len = std::string::npos) { attr_line_t& erase(size_t pos, size_t len = std::string::npos)
{
this->al_string.erase(pos, len); this->al_string.erase(pos, len);
shift_string_attrs(this->al_attrs, pos, -((int32_t) len)); shift_string_attrs(this->al_attrs, pos, -((int32_t) len));
@ -475,19 +545,21 @@ public:
return *this; return *this;
}; };
attr_line_t &erase_utf8_chars(size_t start) { attr_line_t& erase_utf8_chars(size_t start)
{
auto byte_index = utf8_char_to_byte_index(this->al_string, start); auto byte_index = utf8_char_to_byte_index(this->al_string, start);
this->erase(byte_index); this->erase(byte_index);
return *this; return *this;
}; };
attr_line_t &right_justify(unsigned long width); attr_line_t& right_justify(unsigned long width);
ssize_t length() const { ssize_t length() const
{
size_t retval = this->al_string.length(); size_t retval = this->al_string.length();
for (const auto &al_attr : this->al_attrs) { for (const auto& al_attr : this->al_attrs) {
retval = std::max(retval, (size_t) al_attr.sa_range.lr_start); retval = std::max(retval, (size_t) al_attr.sa_range.lr_start);
if (al_attr.sa_range.lr_end != -1) { if (al_attr.sa_range.lr_end != -1) {
retval = std::max(retval, (size_t) al_attr.sa_range.lr_end); retval = std::max(retval, (size_t) al_attr.sa_range.lr_end);
@ -497,14 +569,16 @@ public:
return retval; return retval;
}; };
std::string get_substring(const line_range &lr) const { std::string get_substring(const line_range& lr) const
{
if (!lr.is_valid()) { if (!lr.is_valid()) {
return ""; return "";
} }
return this->al_string.substr(lr.lr_start, lr.length()); return this->al_string.substr(lr.lr_start, lr.length());
}; };
string_attrs_t::const_iterator find_attr(size_t near) const { string_attrs_t::const_iterator find_attr(size_t near) const
{
near = std::min(near, this->al_string.length() - 1); near = std::min(near, this->al_string.length() - 1);
while (near > 0 && isspace(this->al_string[near])) { while (near > 0 && isspace(this->al_string[near])) {
@ -514,12 +588,13 @@ public:
return find_string_attr(this->al_attrs, near); return find_string_attr(this->al_attrs, near);
}; };
bool empty() const { bool empty() const
{
return this->length() == 0; return this->length() == 0;
}; };
/** Clear the string and the attributes for the string. */ /** Clear the string and the attributes for the string. */
attr_line_t &clear() attr_line_t& clear()
{ {
this->al_string.clear(); this->al_string.clear();
this->al_attrs.clear(); this->al_attrs.clear();
@ -529,7 +604,7 @@ public:
attr_line_t subline(size_t start, size_t len = std::string::npos) const; attr_line_t subline(size_t start, size_t len = std::string::npos) const;
void split_lines(std::vector<attr_line_t> &lines) const; void split_lines(std::vector<attr_line_t>& lines) const;
size_t nearest_text(size_t x) const; size_t nearest_text(size_t x) const;
@ -538,7 +613,7 @@ public:
private: private:
const static size_t RESERVE_SIZE = 128; const static size_t RESERVE_SIZE = 128;
std::string al_string; std::string al_string;
string_attrs_t al_attrs; string_attrs_t al_attrs;
}; };

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,14 +32,14 @@
#ifndef auto_fd_hh #ifndef auto_fd_hh
#define auto_fd_hh #define auto_fd_hh
#include <exception>
#include <new>
#include <string>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#include <sys/select.h> #include <sys/select.h>
#include <unistd.h>
#include <new>
#include <string>
#include <exception>
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "base/result.h" #include "base/result.h"
@ -51,7 +51,6 @@
*/ */
class auto_fd { class auto_fd {
public: public:
/** /**
* Wrapper for the posix pipe(2) function that stores the file descriptor * Wrapper for the posix pipe(2) function that stores the file descriptor
* results in an auto_fd array. * results in an auto_fd array.
@ -60,7 +59,7 @@ public:
* contains the reader end of the pipe and the second contains the writer. * contains the reader end of the pipe and the second contains the writer.
* @return The result of the pipe(2) function. * @return The result of the pipe(2) function.
*/ */
static int pipe(auto_fd *af) static int pipe(auto_fd* af)
{ {
int retval, fd[2]; int retval, fd[2];
@ -80,7 +79,8 @@ public:
* @param fd The file descriptor to duplicate. * @param fd The file descriptor to duplicate.
* @return A new auto_fd that contains the duplicated file descriptor. * @return A new auto_fd that contains the duplicated file descriptor.
*/ */
static auto_fd dup_of(int fd) { static auto_fd dup_of(int fd)
{
if (fd == -1) { if (fd == -1) {
return auto_fd{}; return auto_fd{};
} }
@ -99,8 +99,7 @@ public:
* *
* @param fd The file descriptor to be managed. * @param fd The file descriptor to be managed.
*/ */
explicit auto_fd(int fd = -1) explicit auto_fd(int fd = -1) : af_fd(fd)
: af_fd(fd)
{ {
require(fd >= -1); require(fd >= -1);
}; };
@ -112,9 +111,7 @@ public:
* *
* @param af The source of the file descriptor. * @param af The source of the file descriptor.
*/ */
auto_fd(auto_fd && af) noexcept auto_fd(auto_fd&& af) noexcept : af_fd(af.release()){};
: af_fd(af.release()) {
};
/** /**
* Const copy constructor. The file descriptor from the source will be * Const copy constructor. The file descriptor from the source will be
@ -122,8 +119,7 @@ public:
* *
* @param af The source of the file descriptor. * @param af The source of the file descriptor.
*/ */
auto_fd(const auto_fd &af) auto_fd(const auto_fd& af) : af_fd(-1)
: af_fd(-1)
{ {
if (af.af_fd != -1 && (this->af_fd = dup(af.af_fd)) == -1) { if (af.af_fd != -1 && (this->af_fd = dup(af.af_fd)) == -1) {
throw std::bad_alloc(); throw std::bad_alloc();
@ -139,7 +135,10 @@ public:
}; };
/** @return The file descriptor as a plain integer. */ /** @return The file descriptor as a plain integer. */
operator int() const { return this->af_fd; }; operator int() const
{
return this->af_fd;
};
/** /**
* Replace the current descriptor with the given one. The current * Replace the current descriptor with the given one. The current
@ -148,7 +147,7 @@ public:
* @param fd The file descriptor to store in this object. * @param fd The file descriptor to store in this object.
* @return *this * @return *this
*/ */
auto_fd &operator=(int fd) auto_fd& operator=(int fd)
{ {
require(fd >= -1); require(fd >= -1);
@ -162,7 +161,8 @@ public:
* @param af The old manager of the file descriptor. * @param af The old manager of the file descriptor.
* @return *this * @return *this
*/ */
auto_fd &operator=(auto_fd && af) noexcept { auto_fd& operator=(auto_fd&& af) noexcept
{
this->reset(af.release()); this->reset(af.release());
return *this; return *this;
}; };
@ -173,7 +173,7 @@ public:
* *
* @return A pointer to the internal integer. * @return A pointer to the internal integer.
*/ */
int *out() int* out()
{ {
this->reset(); this->reset();
return &this->af_fd; return &this->af_fd;
@ -218,7 +218,8 @@ public:
} }
}; };
void close_on_exec() const { void close_on_exec() const
{
if (this->af_fd == -1) { if (this->af_fd == -1) {
return; return;
} }
@ -226,12 +227,13 @@ public:
} }
private: private:
int af_fd; /*< The managed file descriptor. */ int af_fd; /*< The managed file descriptor. */
}; };
class auto_pipe { class auto_pipe {
public: public:
static Result<auto_pipe, std::string> for_child_fd(int child_fd) { static Result<auto_pipe, std::string> for_child_fd(int child_fd)
{
auto_pipe retval(child_fd); auto_pipe retval(child_fd);
if (retval.open() == -1) { if (retval.open() == -1) {
@ -245,70 +247,73 @@ public:
: ap_child_flags(child_flags), ap_child_fd(child_fd) : ap_child_flags(child_flags), ap_child_fd(child_fd)
{ {
switch (child_fd) { switch (child_fd) {
case STDIN_FILENO: case STDIN_FILENO:
this->ap_child_flags = O_RDONLY; this->ap_child_flags = O_RDONLY;
break; break;
case STDOUT_FILENO: case STDOUT_FILENO:
case STDERR_FILENO: case STDERR_FILENO:
this->ap_child_flags = O_WRONLY; this->ap_child_flags = O_WRONLY;
break; break;
} }
}; };
int open() { int open()
{
return auto_fd::pipe(this->ap_fd); return auto_fd::pipe(this->ap_fd);
}; };
void close() { void close()
{
this->ap_fd[0].reset(); this->ap_fd[0].reset();
this->ap_fd[1].reset(); this->ap_fd[1].reset();
}; };
auto_fd &read_end() { auto_fd& read_end()
{
return this->ap_fd[0]; return this->ap_fd[0];
}; };
auto_fd &write_end() { auto_fd& write_end()
{
return this->ap_fd[1]; return this->ap_fd[1];
}; };
void after_fork(pid_t child_pid) { void after_fork(pid_t child_pid)
{
int new_fd; int new_fd;
switch (child_pid) { switch (child_pid) {
case -1: case -1:
this->close(); this->close();
break; break;
case 0: case 0:
if (this->ap_child_flags == O_RDONLY) { if (this->ap_child_flags == O_RDONLY) {
this->write_end().reset(); this->write_end().reset();
if (this->read_end().get() == -1) { if (this->read_end().get() == -1) {
this->read_end() = ::open("/dev/null", O_RDONLY); this->read_end() = ::open("/dev/null", O_RDONLY);
}
new_fd = this->read_end().get();
} else {
this->read_end().reset();
if (this->write_end().get() == -1) {
this->write_end() = ::open("/dev/null", O_WRONLY);
}
new_fd = this->write_end().get();
} }
new_fd = this->read_end().get(); if (this->ap_child_fd != -1) {
} if (new_fd != this->ap_child_fd) {
else { dup2(new_fd, this->ap_child_fd);
this->read_end().reset(); this->close();
if (this->write_end().get() == -1) { }
this->write_end() = ::open("/dev/null", O_WRONLY);
} }
new_fd = this->write_end().get(); break;
} default:
if (this->ap_child_fd != -1) { if (this->ap_child_flags == O_RDONLY) {
if (new_fd != this->ap_child_fd) { this->read_end().reset();
dup2(new_fd, this->ap_child_fd); } else {
this->close(); this->write_end().reset();
} }
} break;
break;
default:
if (this->ap_child_flags == O_RDONLY) {
this->read_end().reset();
}
else {
this->write_end().reset();
}
break;
} }
}; };

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,15 +32,15 @@
#ifndef lnav_auto_mem_hh #ifndef lnav_auto_mem_hh
#define lnav_auto_mem_hh #define lnav_auto_mem_hh
#include <exception>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h>
#include <exception>
#include "base/result.h" #include "base/result.h"
typedef void (*free_func_t)(void *); typedef void (*free_func_t)(void*);
/** /**
* Resource management class for memory allocated by a custom allocator. * Resource management class for memory allocated by a custom allocator.
@ -51,126 +51,149 @@ typedef void (*free_func_t)(void *);
template<class T, free_func_t default_free = free> template<class T, free_func_t default_free = free>
class auto_mem { class auto_mem {
public: public:
explicit auto_mem(T *ptr = nullptr) explicit auto_mem(T* ptr = nullptr)
: am_ptr(ptr), am_free_func(default_free) { : am_ptr(ptr), am_free_func(default_free){};
};
auto_mem(const auto_mem &am) = delete; auto_mem(const auto_mem& am) = delete;
template<typename F> template<typename F>
explicit auto_mem(F free_func) noexcept explicit auto_mem(F free_func) noexcept
: am_ptr(nullptr), am_free_func((free_func_t)free_func) { }; : am_ptr(nullptr), am_free_func((free_func_t) free_func){};
auto_mem(auto_mem &&other) noexcept auto_mem(auto_mem&& other) noexcept
: am_ptr(other.release()), : am_ptr(other.release()), am_free_func(other.am_free_func){};
am_free_func(other.am_free_func) {
};
~auto_mem() { ~auto_mem()
{
this->reset(); this->reset();
}; };
operator T *() const { return this->am_ptr; }; operator T*() const
{
return this->am_ptr;
};
auto_mem &operator =(T *ptr) auto_mem& operator=(T* ptr)
{ {
this->reset(ptr); this->reset(ptr);
return *this; return *this;
}; };
auto_mem &operator=(auto_mem &) = delete; auto_mem& operator=(auto_mem&) = delete;
auto_mem &operator =(auto_mem && am) noexcept auto_mem& operator=(auto_mem&& am) noexcept
{ {
this->reset(am.release()); this->reset(am.release());
this->am_free_func = am.am_free_func; this->am_free_func = am.am_free_func;
return *this; return *this;
}; };
T *release() T* release()
{ {
T *retval = this->am_ptr; T* retval = this->am_ptr;
this->am_ptr = nullptr; this->am_ptr = nullptr;
return retval; return retval;
}; };
T *in() const T* in() const
{ {
return this->am_ptr; return this->am_ptr;
}; };
T **out() T** out()
{ {
this->reset(); this->reset();
return &this->am_ptr; return &this->am_ptr;
}; };
void reset(T *ptr = nullptr) void reset(T* ptr = nullptr)
{ {
if (this->am_ptr != ptr) { if (this->am_ptr != ptr) {
if (this->am_ptr != nullptr) { if (this->am_ptr != nullptr) {
this->am_free_func((void *)this->am_ptr); this->am_free_func((void*) this->am_ptr);
} }
this->am_ptr = ptr; this->am_ptr = ptr;
} }
}; };
private: private:
T * am_ptr; T* am_ptr;
void (*am_free_func)(void *); void (*am_free_func)(void*);
}; };
template<typename T, void(*free_func) (T *)> template<typename T, void (*free_func)(T*)>
class static_root_mem { class static_root_mem {
public: public:
static_root_mem() { static_root_mem()
{
memset(&this->srm_value, 0, sizeof(T)); memset(&this->srm_value, 0, sizeof(T));
}; };
~static_root_mem() { free_func(&this->srm_value); }; ~static_root_mem()
{
free_func(&this->srm_value);
};
const T *operator->() const { return &this->srm_value; }; const T* operator->() const
{
return &this->srm_value;
};
const T &in() const { return this->srm_value; }; const T& in() const
{
return this->srm_value;
};
T *inout() { T* inout()
{
free_func(&this->srm_value); free_func(&this->srm_value);
memset(&this->srm_value, 0, sizeof(T)); memset(&this->srm_value, 0, sizeof(T));
return &this->srm_value; return &this->srm_value;
}; };
private: private:
static_root_mem &operator =(T &) { return *this; }; static_root_mem& operator=(T&)
{
return *this;
};
static_root_mem &operator =(static_root_mem &) { return *this; }; static_root_mem& operator=(static_root_mem&)
{
return *this;
};
T srm_value; T srm_value;
}; };
class auto_buffer { class auto_buffer {
public: public:
static auto_buffer alloc(size_t size) { static auto_buffer alloc(size_t size)
return auto_buffer{ (char *) malloc(size), size }; {
return auto_buffer{(char*) malloc(size), size};
} }
auto_buffer(auto_buffer&& other) noexcept auto_buffer(auto_buffer&& other) noexcept
: ab_buffer(other.ab_buffer), ab_size(other.ab_size) { : ab_buffer(other.ab_buffer), ab_size(other.ab_size)
{
other.ab_buffer = nullptr; other.ab_buffer = nullptr;
other.ab_size = 0; other.ab_size = 0;
} }
~auto_buffer() { ~auto_buffer()
{
free(this->ab_buffer); free(this->ab_buffer);
this->ab_buffer = nullptr; this->ab_buffer = nullptr;
this->ab_size = 0; this->ab_size = 0;
} }
char *in() { char* in()
{
return this->ab_buffer; return this->ab_buffer;
} }
std::pair<char *, size_t> release() { std::pair<char*, size_t> release()
{
auto retval = std::make_pair(this->ab_buffer, this->ab_size); auto retval = std::make_pair(this->ab_buffer, this->ab_size);
this->ab_buffer = nullptr; this->ab_buffer = nullptr;
@ -178,16 +201,18 @@ public:
return retval; return retval;
} }
size_t size() const { size_t size() const
{
return this->ab_size; return this->ab_size;
} }
void expand_by(size_t amount) { void expand_by(size_t amount)
{
if (amount == 0) { if (amount == 0) {
return; return;
} }
auto new_size = this->ab_size + amount; auto new_size = this->ab_size + amount;
auto new_buffer = (char *) realloc(this->ab_buffer, new_size); auto new_buffer = (char*) realloc(this->ab_buffer, new_size);
if (new_buffer == nullptr) { if (new_buffer == nullptr) {
throw std::bad_alloc(); throw std::bad_alloc();
@ -197,15 +222,16 @@ public:
this->ab_size = new_size; this->ab_size = new_size;
} }
auto_buffer& shrink_to(size_t new_size) { auto_buffer& shrink_to(size_t new_size)
{
this->ab_size = new_size; this->ab_size = new_size;
return *this; return *this;
} }
private: private:
auto_buffer(char *buffer, size_t size) : ab_buffer(buffer), ab_size(size) { auto_buffer(char* buffer, size_t size) : ab_buffer(buffer), ab_size(size) {}
}
char *ab_buffer; char* ab_buffer;
size_t ab_size; size_t ab_size;
}; };

@ -21,23 +21,24 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h" #include "auto_pid.hh"
#include <unistd.h> #include <unistd.h>
#include "lnav_log.hh" #include "config.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "auto_pid.hh" #include "lnav_log.hh"
namespace lnav { namespace lnav {
namespace pid { namespace pid {
Result<auto_pid<process_state::RUNNING>, std::string> from_fork() Result<auto_pid<process_state::running>, std::string>
from_fork()
{ {
auto pid = ::fork(); auto pid = ::fork();
@ -49,7 +50,7 @@ Result<auto_pid<process_state::RUNNING>, std::string> from_fork()
log_debug("started child: %d", pid); log_debug("started child: %d", pid);
} }
return Ok(auto_pid<process_state::RUNNING>(pid)); return Ok(auto_pid<process_state::running>(pid));
}
}
} }
} // namespace pid
} // namespace lnav

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,50 +32,58 @@
#ifndef auto_pid_hh #ifndef auto_pid_hh
#define auto_pid_hh #define auto_pid_hh
#include <errno.h> #include <cerrno>
#include <signal.h> #include <csignal>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include "base/result.h"
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "base/result.h"
#include "mapbox/variant.hpp" #include "mapbox/variant.hpp"
enum class process_state { enum class process_state {
RUNNING, running,
FINISHED, finished,
}; };
template<process_state ProcState> template<process_state ProcState>
class auto_pid { class auto_pid {
public: public:
explicit auto_pid(pid_t child, int status = 0) : ap_child(child), explicit auto_pid(pid_t child, int status = 0)
ap_status(status) : ap_child(child), ap_status(status)
{}; {
}
auto_pid(const auto_pid &other) = delete; auto_pid(const auto_pid& other) = delete;
auto_pid(auto_pid &&other) noexcept auto_pid(auto_pid&& other) noexcept
: ap_child(std::move(other).release()), : ap_child(std::move(other).release()), ap_status(other.ap_status)
ap_status(other.ap_status) {
{}; }
~auto_pid() noexcept ~auto_pid() noexcept
{ this->reset(); }; {
this->reset();
}
auto_pid &operator=(auto_pid &&other) noexcept auto_pid& operator=(auto_pid&& other) noexcept
{ {
this->reset(std::move(other).release()); this->reset(std::move(other).release());
this->ap_status = other.ap_status; this->ap_status = other.ap_status;
return *this; return *this;
}; }
auto_pid& operator=(const auto_pid& other) = delete;
pid_t in() const pid_t in() const
{ return this->ap_child; } {
return this->ap_child;
}
bool in_child() const bool in_child() const
{ {
static_assert(ProcState == process_state::RUNNING, static_assert(ProcState == process_state::running,
"this method is only available in the RUNNING state"); "this method is only available in the RUNNING state");
return this->ap_child == 0; return this->ap_child == 0;
}; };
@ -87,31 +95,31 @@ public:
int status() const int status() const
{ {
static_assert(ProcState == process_state::FINISHED, static_assert(ProcState == process_state::finished,
"wait_for_child() must be called first"); "wait_for_child() must be called first");
return this->ap_status; return this->ap_status;
}; };
bool was_normal_exit() const bool was_normal_exit() const
{ {
static_assert(ProcState == process_state::FINISHED, static_assert(ProcState == process_state::finished,
"wait_for_child() must be called first"); "wait_for_child() must be called first");
return WIFEXITED(this->ap_status); return WIFEXITED(this->ap_status);
} }
int exit_status() const int exit_status() const
{ {
static_assert(ProcState == process_state::FINISHED, static_assert(ProcState == process_state::finished,
"wait_for_child() must be called first"); "wait_for_child() must be called first");
return WEXITSTATUS(this->ap_status); return WEXITSTATUS(this->ap_status);
} }
using poll_result = mapbox::util::variant< using poll_result
auto_pid<process_state::RUNNING>, = mapbox::util::variant<auto_pid<process_state::running>,
auto_pid<process_state::FINISHED> auto_pid<process_state::finished>>;
>;
poll_result poll() && { poll_result poll() &&
{
if (this->ap_child != -1) { if (this->ap_child != -1) {
auto rc = waitpid(this->ap_child, &this->ap_status, WNOHANG); auto rc = waitpid(this->ap_child, &this->ap_status, WNOHANG);
@ -120,20 +128,21 @@ public:
} }
} }
return auto_pid<process_state::FINISHED>( return auto_pid<process_state::finished>(
std::exchange(this->ap_child, -1), this->ap_status); std::exchange(this->ap_child, -1), this->ap_status);
} }
auto_pid<process_state::FINISHED> wait_for_child(int options = 0) && auto_pid<process_state::finished> wait_for_child(int options = 0) &&
{ {
if (this->ap_child != -1) { if (this->ap_child != -1) {
while ((waitpid(this->ap_child, while ((waitpid(this->ap_child, &this->ap_status, options)) < 0
&this->ap_status, && (errno == EINTR))
options)) < 0 && (errno == EINTR)) { ; {
;
} }
} }
return auto_pid<process_state::FINISHED>( return auto_pid<process_state::finished>(
std::exchange(this->ap_child, -1), this->ap_status); std::exchange(this->ap_child, -1), this->ap_status);
} }
@ -141,7 +150,7 @@ public:
{ {
if (this->ap_child != child) { if (this->ap_child != child) {
this->ap_status = 0; this->ap_status = 0;
if (ProcState == process_state::RUNNING && this->ap_child != -1) { if (ProcState == process_state::running && this->ap_child != -1) {
log_debug("sending SIGTERM to child: %d", this->ap_child); log_debug("sending SIGTERM to child: %d", this->ap_child);
kill(this->ap_child, SIGTERM); kill(this->ap_child, SIGTERM);
} }
@ -156,8 +165,8 @@ private:
namespace lnav { namespace lnav {
namespace pid { namespace pid {
Result<auto_pid<process_state::RUNNING>, std::string> from_fork(); Result<auto_pid<process_state::running>, std::string> from_fork();
} } // namespace pid
} } // namespace lnav
#endif #endif

@ -21,20 +21,21 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file date_time_scanner.cc * @file date_time_scanner.cc
*/ */
#include "config.h"
#include "date_time_scanner.hh" #include "date_time_scanner.hh"
#include "config.h"
#include "ptimec.hh" #include "ptimec.hh"
size_t date_time_scanner::ftime(char *dst, size_t len, const exttm &tm) const size_t
date_time_scanner::ftime(char* dst, size_t len, const exttm& tm) const
{ {
off_t off = 0; off_t off = 0;
@ -43,7 +44,8 @@ size_t date_time_scanner::ftime(char *dst, size_t len, const exttm &tm) const
return (size_t) off; return (size_t) off;
} }
bool next_format(const char * const fmt[], int &index, int &locked_index) bool
next_format(const char* const fmt[], int& index, int& locked_index)
{ {
bool retval = true; bool retval = true;
@ -52,40 +54,35 @@ bool next_format(const char * const fmt[], int &index, int &locked_index)
if (fmt[index] == nullptr) { if (fmt[index] == nullptr) {
retval = false; retval = false;
} }
} } else if (index == locked_index) {
else if (index == locked_index) {
retval = false; retval = false;
} } else {
else {
index = locked_index; index = locked_index;
} }
return retval; return retval;
} }
const char *date_time_scanner::scan(const char *time_dest, const char*
size_t time_len, date_time_scanner::scan(const char* time_dest,
const char * const time_fmt[], size_t time_len,
struct exttm *tm_out, const char* const time_fmt[],
struct timeval &tv_out, struct exttm* tm_out,
bool convert_local) struct timeval& tv_out,
bool convert_local)
{ {
int curr_time_fmt = -1; int curr_time_fmt = -1;
bool found = false; bool found = false;
const char *retval = nullptr; const char* retval = nullptr;
if (!time_fmt) { if (!time_fmt) {
time_fmt = PTIMEC_FORMAT_STR; time_fmt = PTIMEC_FORMAT_STR;
} }
while (next_format(time_fmt, while (next_format(time_fmt, curr_time_fmt, this->dts_fmt_lock)) {
curr_time_fmt,
this->dts_fmt_lock)) {
*tm_out = this->dts_base_tm; *tm_out = this->dts_base_tm;
tm_out->et_flags = 0; tm_out->et_flags = 0;
if (time_len > 1 && if (time_len > 1 && time_dest[0] == '+' && isdigit(time_dest[1])) {
time_dest[0] == '+' &&
isdigit(time_dest[1])) {
char time_cp[time_len + 1]; char time_cp[time_len + 1];
int gmt_int, off; int gmt_int, off;
@ -105,7 +102,8 @@ const char *date_time_scanner::scan(const char *time_dest,
} }
tv_out.tv_sec = gmt; tv_out.tv_sec = gmt;
tv_out.tv_usec = 0; tv_out.tv_usec = 0;
tm_out->et_flags = ETF_DAY_SET|ETF_MONTH_SET|ETF_YEAR_SET|ETF_MACHINE_ORIENTED|ETF_EPOCH_TIME; tm_out->et_flags = ETF_DAY_SET | ETF_MONTH_SET | ETF_YEAR_SET
| ETF_MACHINE_ORIENTED | ETF_EPOCH_TIME;
this->dts_fmt_lock = curr_time_fmt; this->dts_fmt_lock = curr_time_fmt;
this->dts_fmt_len = off; this->dts_fmt_len = off;
@ -113,8 +111,7 @@ const char *date_time_scanner::scan(const char *time_dest,
found = true; found = true;
break; break;
} }
} } else if (time_fmt == PTIMEC_FORMAT_STR) {
else if (time_fmt == PTIMEC_FORMAT_STR) {
ptime_func func = PTIMEC_FORMATS[curr_time_fmt].pf_func; ptime_func func = PTIMEC_FORMATS[curr_time_fmt].pf_func;
off_t off = 0; off_t off = 0;
@ -129,8 +126,10 @@ const char *date_time_scanner::scan(const char *time_dest,
if (tm_out->et_tm.tm_year < 70) { if (tm_out->et_tm.tm_year < 70) {
tm_out->et_tm.tm_year = 80; tm_out->et_tm.tm_year = 80;
} }
if (convert_local && if (convert_local
(this->dts_local_time || tm_out->et_flags & ETF_EPOCH_TIME)) { && (this->dts_local_time
|| tm_out->et_flags & ETF_EPOCH_TIME))
{
time_t gmt = tm2sec(&tm_out->et_tm); time_t gmt = tm2sec(&tm_out->et_tm);
this->to_localtime(gmt, *tm_out); this->to_localtime(gmt, *tm_out);
@ -140,13 +139,12 @@ const char *date_time_scanner::scan(const char *time_dest,
secs2wday(tv_out, &tm_out->et_tm); secs2wday(tv_out, &tm_out->et_tm);
this->dts_fmt_lock = curr_time_fmt; this->dts_fmt_lock = curr_time_fmt;
this->dts_fmt_len = retval - time_dest; this->dts_fmt_len = retval - time_dest;
found = true; found = true;
break; break;
} }
} } else {
else {
off_t off = 0; off_t off = 0;
#ifdef HAVE_STRUCT_TM_TM_ZONE #ifdef HAVE_STRUCT_TM_TM_ZONE
@ -154,14 +152,19 @@ const char *date_time_scanner::scan(const char *time_dest,
tm_out->et_tm.tm_zone = nullptr; tm_out->et_tm.tm_zone = nullptr;
} }
#endif #endif
if (ptime_fmt(time_fmt[curr_time_fmt], tm_out, time_dest, off, time_len) && if (ptime_fmt(
(time_dest[off] == '.' || time_dest[off] == ',' || off == (off_t)time_len)) { time_fmt[curr_time_fmt], tm_out, time_dest, off, time_len)
&& (time_dest[off] == '.' || time_dest[off] == ','
|| off == (off_t) time_len))
{
retval = &time_dest[off]; retval = &time_dest[off];
if (tm_out->et_tm.tm_year < 70) { if (tm_out->et_tm.tm_year < 70) {
tm_out->et_tm.tm_year = 80; tm_out->et_tm.tm_year = 80;
} }
if (convert_local && if (convert_local
(this->dts_local_time || tm_out->et_flags & ETF_EPOCH_TIME)) { && (this->dts_local_time
|| tm_out->et_flags & ETF_EPOCH_TIME))
{
time_t gmt = tm2sec(&tm_out->et_tm); time_t gmt = tm2sec(&tm_out->et_tm);
this->to_localtime(gmt, *tm_out); this->to_localtime(gmt, *tm_out);
@ -176,7 +179,7 @@ const char *date_time_scanner::scan(const char *time_dest,
secs2wday(tv_out, &tm_out->et_tm); secs2wday(tv_out, &tm_out->et_tm);
this->dts_fmt_lock = curr_time_fmt; this->dts_fmt_lock = curr_time_fmt;
this->dts_fmt_len = retval - time_dest; this->dts_fmt_len = retval - time_dest;
found = true; found = true;
break; break;
@ -197,8 +200,7 @@ const char *date_time_scanner::scan(const char *time_dest,
tv_out.tv_usec = tm_out->et_nsec / 1000; tv_out.tv_usec = tm_out->et_nsec / 1000;
this->dts_fmt_len += 7; this->dts_fmt_len += 7;
retval += 7; retval += 7;
} } else if (ptime_L(tm_out, time_dest, off, time_len)) {
else if (ptime_L(tm_out, time_dest, off, time_len)) {
tv_out.tv_usec = tm_out->et_nsec / 1000; tv_out.tv_usec = tm_out->et_nsec / 1000;
this->dts_fmt_len += 4; this->dts_fmt_len += 4;
retval += 4; retval += 4;
@ -209,15 +211,16 @@ const char *date_time_scanner::scan(const char *time_dest,
return retval; return retval;
} }
void date_time_scanner::to_localtime(time_t t, exttm &tm_out) void
date_time_scanner::to_localtime(time_t t, exttm& tm_out)
{ {
if (t < (24 * 60 * 60)) { if (t < (24 * 60 * 60)) {
// Don't convert and risk going past the epoch. // Don't convert and risk going past the epoch.
return; return;
} }
if (t < this->dts_local_offset_valid || if (t < this->dts_local_offset_valid || t >= this->dts_local_offset_expiry)
t >= this->dts_local_offset_expiry) { {
time_t new_gmt; time_t new_gmt;
localtime_r(&t, &tm_out.et_tm); localtime_r(&t, &tm_out.et_tm);
@ -230,10 +233,9 @@ void date_time_scanner::to_localtime(time_t t, exttm &tm_out)
this->dts_local_offset_cache = t - new_gmt; this->dts_local_offset_cache = t - new_gmt;
this->dts_local_offset_valid = t; this->dts_local_offset_valid = t;
this->dts_local_offset_expiry = t + (EXPIRE_TIME - 1); this->dts_local_offset_expiry = t + (EXPIRE_TIME - 1);
this->dts_local_offset_expiry -= this->dts_local_offset_expiry
this->dts_local_offset_expiry % EXPIRE_TIME; -= this->dts_local_offset_expiry % EXPIRE_TIME;
} } else {
else {
time_t adjust_gmt = t - this->dts_local_offset_cache; time_t adjust_gmt = t - this->dts_local_offset_cache;
gmtime_r(&adjust_gmt, &tm_out.et_tm); gmtime_r(&adjust_gmt, &tm_out.et_tm);
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,11 +32,11 @@
#ifndef lnav_date_time_scanner_hh #ifndef lnav_date_time_scanner_hh
#define lnav_date_time_scanner_hh #define lnav_date_time_scanner_hh
#include <time.h>
#include <sys/types.h>
#include <string> #include <string>
#include <sys/types.h>
#include <time.h>
#include "time_util.hh" #include "time_util.hh"
/** /**
@ -47,11 +47,13 @@
* an exttm struct to a string using the ftime() method. * an exttm struct to a string using the ftime() method.
*/ */
struct date_time_scanner { struct date_time_scanner {
date_time_scanner() { date_time_scanner()
{
this->clear(); this->clear();
}; };
void clear() { void clear()
{
this->dts_base_time = 0; this->dts_base_time = 0;
memset(&this->dts_base_tm, 0, sizeof(this->dts_base_tm)); memset(&this->dts_base_tm, 0, sizeof(this->dts_base_tm));
this->dts_fmt_lock = -1; this->dts_fmt_lock = -1;
@ -61,12 +63,14 @@ struct date_time_scanner {
/** /**
* Unlock this scanner so that the format is rediscovered. * Unlock this scanner so that the format is rediscovered.
*/ */
void unlock() { void unlock()
{
this->dts_fmt_lock = -1; this->dts_fmt_lock = -1;
this->dts_fmt_len = -1; this->dts_fmt_len = -1;
} }
void set_base_time(time_t base_time) { void set_base_time(time_t base_time)
{
this->dts_base_time = base_time; this->dts_base_time = base_time;
localtime_r(&base_time, &this->dts_base_tm.et_tm); localtime_r(&base_time, &this->dts_base_tm.et_tm);
}; };
@ -78,7 +82,7 @@ struct date_time_scanner {
* every call, so we cache the result and only call it again if the * every call, so we cache the result and only call it again if the
* requested time falls outside of a fifteen minute range. * requested time falls outside of a fifteen minute range.
*/ */
void to_localtime(time_t t, struct exttm &tm_out); void to_localtime(time_t t, struct exttm& tm_out);
bool dts_keep_base_tz{false}; bool dts_keep_base_tz{false};
bool dts_local_time{false}; bool dts_local_time{false};
@ -92,19 +96,20 @@ struct date_time_scanner {
static const int EXPIRE_TIME = 15 * 60; static const int EXPIRE_TIME = 15 * 60;
const char *scan(const char *time_src, const char* scan(const char* time_src,
size_t time_len, size_t time_len,
const char * const time_fmt[], const char* const time_fmt[],
struct exttm *tm_out, struct exttm* tm_out,
struct timeval &tv_out, struct timeval& tv_out,
bool convert_local = true); bool convert_local = true);
size_t ftime(char *dst, size_t len, const struct exttm &tm) const; size_t ftime(char* dst, size_t len, const struct exttm& tm) const;
bool convert_to_timeval(const char *time_src, bool convert_to_timeval(const char* time_src,
ssize_t time_len, ssize_t time_len,
const char * const time_fmt[], const char* const time_fmt[],
struct timeval &tv_out) { struct timeval& tv_out)
{
struct exttm tm; struct exttm tm;
if (time_len == -1) { if (time_len == -1) {
@ -116,12 +121,13 @@ struct date_time_scanner {
return false; return false;
}; };
bool convert_to_timeval(const std::string &time_src, bool convert_to_timeval(const std::string& time_src, struct timeval& tv_out)
struct timeval &tv_out) { {
struct exttm tm; struct exttm tm;
if (this->scan(time_src.c_str(), time_src.size(), if (this->scan(time_src.c_str(), time_src.size(), nullptr, &tm, tv_out)
nullptr, &tm, tv_out) != nullptr) { != nullptr)
{
return true; return true;
} }
return false; return false;

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -35,13 +35,14 @@
namespace lnav { namespace lnav {
namespace enums { namespace enums {
template <typename E> template<typename E>
constexpr auto to_underlying(E e) noexcept constexpr auto
to_underlying(E e) noexcept
{ {
return static_cast<std::underlying_type_t<E>>(e); return static_cast<std::underlying_type_t<E>>(e);
} }
} } // namespace enums
} } // namespace lnav
#endif #endif

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -43,28 +43,31 @@ public:
file_off_t fr_offset{0}; file_off_t fr_offset{0};
file_ssize_t fr_size{0}; file_ssize_t fr_size{0};
void clear() { void clear()
{
this->fr_offset = 0; this->fr_offset = 0;
this->fr_size = 0; this->fr_size = 0;
} }
ssize_t next_offset() const { ssize_t next_offset() const
{
return this->fr_offset + this->fr_size; return this->fr_offset + this->fr_size;
} }
bool empty() const { bool empty() const
{
return fr_size == 0; return fr_size == 0;
} }
}; };
struct source_location { struct source_location {
source_location() source_location()
: sl_source(intern_string::lookup("unknown")), : sl_source(intern_string::lookup("unknown")), sl_line_number(-1)
sl_line_number(-1) { {
} }
source_location(intern_string_t source, int line) source_location(intern_string_t source, int line)
: sl_source(source), sl_line_number(line) {}; : sl_source(source), sl_line_number(line){};
intern_string_t sl_source; intern_string_t sl_source;
int sl_line_number; int sl_line_number;

@ -21,23 +21,23 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h" #include "fs_util.hh"
#include "config.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "fs_util.hh"
#include "opt_util.hh" #include "opt_util.hh"
namespace lnav { namespace lnav {
namespace filesystem { namespace filesystem {
Result<std::pair<ghc::filesystem::path, int>, std::string> Result<std::pair<ghc::filesystem::path, int>, std::string>
open_temp_file(const ghc::filesystem::path &pattern) open_temp_file(const ghc::filesystem::path& pattern)
{ {
auto pattern_str = pattern.string(); auto pattern_str = pattern.string();
char pattern_copy[pattern_str.size() + 1]; char pattern_copy[pattern_str.size() + 1];
@ -46,13 +46,15 @@ open_temp_file(const ghc::filesystem::path &pattern)
strcpy(pattern_copy, pattern_str.c_str()); strcpy(pattern_copy, pattern_str.c_str());
if ((fd = mkstemp(pattern_copy)) == -1) { if ((fd = mkstemp(pattern_copy)) == -1) {
return Err(fmt::format("unable to create temporary file: {} -- {}", return Err(fmt::format("unable to create temporary file: {} -- {}",
pattern.string(), strerror(errno))); pattern.string(),
strerror(errno)));
} }
return Ok(std::make_pair(ghc::filesystem::path(pattern_copy), fd)); return Ok(std::make_pair(ghc::filesystem::path(pattern_copy), fd));
} }
Result<std::string, std::string> read_file(const ghc::filesystem::path &path) Result<std::string, std::string>
read_file(const ghc::filesystem::path& path)
{ {
try { try {
ghc::filesystem::ifstream file_stream(path); ghc::filesystem::ifstream file_stream(path);
@ -70,11 +72,12 @@ Result<std::string, std::string> read_file(const ghc::filesystem::path &path)
} }
} }
std::string build_path(const std::vector<ghc::filesystem::path> &paths) std::string
build_path(const std::vector<ghc::filesystem::path>& paths)
{ {
std::string retval; std::string retval;
for (const auto &path : paths) { for (const auto& path : paths) {
if (path.empty()) { if (path.empty()) {
continue; continue;
} }
@ -90,5 +93,5 @@ std::string build_path(const std::vector<ghc::filesystem::path> &paths)
return retval; return retval;
} }
} } // namespace filesystem
} } // namespace lnav

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -33,32 +33,38 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "result.h"
#include "ghc/filesystem.hpp" #include "ghc/filesystem.hpp"
#include "result.h"
namespace lnav { namespace lnav {
namespace filesystem { namespace filesystem {
inline int statp(const ghc::filesystem::path &path, struct stat *buf) { inline int
statp(const ghc::filesystem::path& path, struct stat* buf)
{
return stat(path.c_str(), buf); return stat(path.c_str(), buf);
} }
inline int openp(const ghc::filesystem::path &path, int flags) { inline int
openp(const ghc::filesystem::path& path, int flags)
{
return open(path.c_str(), flags); return open(path.c_str(), flags);
} }
inline int openp(const ghc::filesystem::path &path, int flags, mode_t mode) { inline int
openp(const ghc::filesystem::path& path, int flags, mode_t mode)
{
return open(path.c_str(), flags, mode); return open(path.c_str(), flags, mode);
} }
Result<std::pair<ghc::filesystem::path, int>, std::string> Result<std::pair<ghc::filesystem::path, int>, std::string> open_temp_file(
open_temp_file(const ghc::filesystem::path &pattern); const ghc::filesystem::path& pattern);
Result<std::string, std::string> read_file(const ghc::filesystem::path &path); Result<std::string, std::string> read_file(const ghc::filesystem::path& path);
std::string build_path(const std::vector<ghc::filesystem::path> &paths); std::string build_path(const std::vector<ghc::filesystem::path>& paths);
} } // namespace filesystem
} } // namespace lnav
#endif #endif

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -33,12 +33,11 @@
#include <utility> #include <utility>
template<typename F, typename FrontArg> template<typename F, typename FrontArg>
decltype(auto) bind_mem(F&& f, FrontArg&& frontArg) decltype(auto)
bind_mem(F&& f, FrontArg&& frontArg)
{ {
return [f=std::forward<F>(f), return [f = std::forward<F>(f),
frontArg = std::forward<FrontArg>(frontArg)] frontArg = std::forward<FrontArg>(frontArg)](auto&&... backArgs) {
(auto&&...backArgs)
{
return (frontArg->*f)(std::forward<decltype(backArgs)>(backArgs)...); return (frontArg->*f)(std::forward<decltype(backArgs)>(backArgs)...);
}; };
} }
@ -46,13 +45,23 @@ decltype(auto) bind_mem(F&& f, FrontArg&& frontArg)
struct noop_func { struct noop_func {
struct anything { struct anything {
template<class T> template<class T>
operator T(){ return {}; } operator T()
{
return {};
}
// optional reference support. Somewhat evil. // optional reference support. Somewhat evil.
template<class T> template<class T>
operator T&()const{ static T t{}; return t; } operator T&() const
{
static T t{};
return t;
}
}; };
template<class...Args> template<class... Args>
anything operator()(Args&&...)const{return {};} anything operator()(Args&&...) const
{
return {};
}
}; };
#endif #endif

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -44,7 +44,9 @@ namespace futures {
* @return The new future. * @return The new future.
*/ */
template<class T> template<class T>
std::future<std::decay_t<T>> make_ready_future( T&& t ) { std::future<std::decay_t<T>>
make_ready_future(T&& t)
{
std::promise<std::decay_t<T>> pr; std::promise<std::decay_t<T>> pr;
auto r = pr.get_future(); auto r = pr.get_future();
pr.set_value(std::forward<T>(t)); pr.set_value(std::forward<T>(t));
@ -64,9 +66,10 @@ public:
* @param processor The function to execute with the result of a future. * @param processor The function to execute with the result of a future.
*/ */
explicit future_queue(std::function<void(T&)> processor) explicit future_queue(std::function<void(T&)> processor)
: fq_processor(processor) {}; : fq_processor(processor){};
~future_queue() { ~future_queue()
{
this->pop_to(); this->pop_to();
} }
@ -77,7 +80,8 @@ public:
* *
* @param f The future to add to the queue. * @param f The future to add to the queue.
*/ */
void push_back(std::future<T>&& f) { void push_back(std::future<T>&& f)
{
this->fq_deque.emplace_back(std::move(f)); this->fq_deque.emplace_back(std::move(f));
this->pop_to(MAX_QUEUE_SIZE); this->pop_to(MAX_QUEUE_SIZE);
} }
@ -88,7 +92,8 @@ public:
* *
* @param size The new desired size of the queue. * @param size The new desired size of the queue.
*/ */
void pop_to(size_t size = 0) { void pop_to(size_t size = 0)
{
while (this->fq_deque.size() > size) { while (this->fq_deque.size() > size) {
auto v = this->fq_deque.front().get(); auto v = this->fq_deque.front().get();
this->fq_processor(v); this->fq_processor(v);
@ -100,7 +105,7 @@ public:
std::deque<std::future<T>> fq_deque; std::deque<std::future<T>> fq_deque;
}; };
} } // namespace futures
} } // namespace lnav
#endif #endif

@ -21,27 +21,34 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <cmath> #include <cmath>
#include <vector> #include <vector>
#include "fmt/format.h"
#include "humanize.hh" #include "humanize.hh"
#include "config.h"
#include "fmt/format.h"
namespace humanize { namespace humanize {
std::string file_size(file_ssize_t value) std::string
file_size(file_ssize_t value)
{ {
static const double LN1024 = log(1024.0); static const double LN1024 = log(1024.0);
static const std::vector<const char *> UNITS = { static const std::vector<const char*> UNITS = {
" ", "K", "M", "G", "T", "P", "E", " ",
"K",
"M",
"G",
"T",
"P",
"E",
}; };
if (value < 0) { if (value < 0) {
@ -52,8 +59,8 @@ std::string file_size(file_ssize_t value)
return "0.0 B"; return "0.0 B";
} }
auto exp = floor(std::min(log(value) / LN1024, auto exp
(double) (UNITS.size() - 1))); = floor(std::min(log(value) / LN1024, (double) (UNITS.size() - 1)));
auto divisor = pow(1024, exp); auto divisor = pow(1024, exp);
return fmt::format(FMT_STRING("{:.1f}{}B"), return fmt::format(FMT_STRING("{:.1f}{}B"),
@ -61,7 +68,8 @@ std::string file_size(file_ssize_t value)
UNITS[exp]); UNITS[exp]);
} }
const std::string& sparkline(double value, nonstd::optional<double> upper_opt) const std::string&
sparkline(double value, nonstd::optional<double> upper_opt)
{ {
static const std::string ZERO = " "; static const std::string ZERO = " ";
static const std::string BARS[] = { static const std::string BARS[] = {
@ -91,4 +99,4 @@ const std::string& sparkline(double value, nonstd::optional<double> upper_opt)
return BARS[index]; return BARS[index];
} }
} } // namespace humanize

@ -21,21 +21,21 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <iostream> #include <iostream>
#include "doctest/doctest.h"
#include "base/humanize.hh" #include "base/humanize.hh"
TEST_CASE("humanize::file_size") { #include "config.h"
#include "doctest/doctest.h"
TEST_CASE("humanize::file_size")
{
CHECK(humanize::file_size(0) == "0.0 B"); CHECK(humanize::file_size(0) == "0.0 B");
CHECK(humanize::file_size(1) == "1.0 B"); CHECK(humanize::file_size(1) == "1.0 B");
CHECK(humanize::file_size(1024) == "1.0KB"); CHECK(humanize::file_size(1024) == "1.0KB");

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -48,6 +48,6 @@ std::string file_size(file_ssize_t value);
const std::string& sparkline(double value, nonstd::optional<double> upper); const std::string& sparkline(double value, nonstd::optional<double> upper);
} } // namespace humanize
#endif #endif

@ -21,22 +21,23 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include "humanize.network.hh" #include "humanize.network.hh"
#include "config.h"
#include "pcrepp/pcrepp.hh" #include "pcrepp/pcrepp.hh"
namespace humanize { namespace humanize {
namespace network { namespace network {
namespace path { namespace path {
nonstd::optional<::network::path> from_str(const char *str) nonstd::optional<::network::path>
from_str(const char* str)
{ {
static const pcrepp REMOTE_PATTERN( static const pcrepp REMOTE_PATTERN(
"(?:(?<username>[\\w\\._\\-]+)@)?" "(?:(?<username>[\\w\\._\\-]+)@)?"
@ -60,11 +61,11 @@ nonstd::optional<::network::path> from_str(const char *str)
path = "."; path = ".";
} }
return ::network::path{ return ::network::path{
{ username, locality_hostname, nonstd::nullopt }, {username, locality_hostname, nonstd::nullopt},
path, path,
}; };
} }
} } // namespace path
} } // namespace network
} } // namespace humanize

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -32,15 +32,16 @@
#include <string> #include <string>
#include "optional.hpp"
#include "network.tcp.hh"
#include "fmt/format.h" #include "fmt/format.h"
#include "network.tcp.hh"
#include "optional.hpp"
namespace fmt { namespace fmt {
template<> template<>
struct formatter<network::locality> { struct formatter<network::locality> {
constexpr auto parse(format_parse_context& ctx) { constexpr auto parse(format_parse_context& ctx)
{
auto it = ctx.begin(), end = ctx.end(); auto it = ctx.begin(), end = ctx.end();
// Check if reached the end of the range: // Check if reached the end of the range:
@ -51,24 +52,25 @@ struct formatter<network::locality> {
return it; return it;
} }
template <typename FormatContext> template<typename FormatContext>
auto format(const network::locality& l, FormatContext& ctx) { auto format(const network::locality& l, FormatContext& ctx)
{
bool is_ipv6 = l.l_hostname.find(':') != std::string::npos; bool is_ipv6 = l.l_hostname.find(':') != std::string::npos;
return format_to( return format_to(ctx.out(),
ctx.out(), "{}{}{}{}{}",
"{}{}{}{}{}", l.l_username.value_or(std::string()),
l.l_username.value_or(std::string()), l.l_username ? "@" : "",
l.l_username ? "@" : "", is_ipv6 ? "[" : "",
is_ipv6 ? "[" : "", l.l_hostname,
l.l_hostname, is_ipv6 ? "]" : "");
is_ipv6 ? "]" : "");
} }
}; };
template<> template<>
struct formatter<network::path> { struct formatter<network::path> {
constexpr auto parse(format_parse_context& ctx) { constexpr auto parse(format_parse_context& ctx)
{
auto it = ctx.begin(), end = ctx.end(); auto it = ctx.begin(), end = ctx.end();
// Check if reached the end of the range: // Check if reached the end of the range:
@ -79,32 +81,30 @@ struct formatter<network::path> {
return it; return it;
} }
template <typename FormatContext> template<typename FormatContext>
auto format(const network::path& p, FormatContext& ctx) { auto format(const network::path& p, FormatContext& ctx)
{
return format_to( return format_to(
ctx.out(), ctx.out(), "{}:{}", p.p_locality, p.p_path == "." ? "" : p.p_path);
"{}:{}",
p.p_locality,
p.p_path == "." ? "" : p.p_path);
} }
}; };
} } // namespace fmt
namespace humanize { namespace humanize {
namespace network { namespace network {
namespace path { namespace path {
nonstd::optional<::network::path> from_str(const char *str); nonstd::optional<::network::path> from_str(const char* str);
inline nonstd::optional<::network::path> from_str(const std::string &str) inline nonstd::optional<::network::path>
from_str(const std::string& str)
{ {
return from_str(str.c_str()); return from_str(str.c_str());
} }
} } // namespace path
} } // namespace network
} } // namespace humanize
#endif #endif

@ -21,21 +21,20 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <iostream> #include <iostream>
#include "doctest/doctest.h"
#include "base/humanize.network.hh" #include "base/humanize.network.hh"
#include "config.h"
#include "doctest/doctest.h"
TEST_CASE("humanize::network::path") { TEST_CASE("humanize::network::path")
{
{ {
auto rp_opt = humanize::network::path::from_str("foobar"); auto rp_opt = humanize::network::path::from_str("foobar");
CHECK(!rp_opt); CHECK(!rp_opt);
@ -70,8 +69,8 @@ TEST_CASE("humanize::network::path") {
CHECK(!rp.p_locality.l_service.has_value()); CHECK(!rp.p_locality.l_service.has_value());
CHECK(rp.p_path == "/var/log"); CHECK(rp.p_path == "/var/log");
CHECK(fmt::format("{}", rp.p_locality) == CHECK(fmt::format("{}", rp.p_locality)
"dean@[fe80::184f:c67:baf1:fe02%en0]"); == "dean@[fe80::184f:c67:baf1:fe02%en0]");
} }
{ {
@ -85,13 +84,13 @@ TEST_CASE("humanize::network::path") {
CHECK(!rp.p_locality.l_service.has_value()); CHECK(!rp.p_locality.l_service.has_value());
CHECK(rp.p_path == "/var/log"); CHECK(rp.p_path == "/var/log");
CHECK(fmt::format("{}", rp.p_locality) == CHECK(fmt::format("{}", rp.p_locality)
"[fe80::184f:c67:baf1:fe02%en0]"); == "[fe80::184f:c67:baf1:fe02%en0]");
} }
{ {
auto rp_opt = humanize::network::path::from_str( auto rp_opt
"host1.example.com:/var/log"); = humanize::network::path::from_str("host1.example.com:/var/log");
CHECK(rp_opt.has_value()); CHECK(rp_opt.has_value());
auto rp = *rp_opt; auto rp = *rp_opt;

@ -21,35 +21,37 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <chrono> #include <chrono>
#include "humanize.time.hh"
#include "config.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "time_util.hh" #include "time_util.hh"
#include "humanize.time.hh"
namespace humanize { namespace humanize {
namespace time { namespace time {
using namespace std::chrono_literals; using namespace std::chrono_literals;
point point::from_tv(const timeval &tv) point
point::from_tv(const timeval& tv)
{ {
return point(tv); return point(tv);
} }
std::string point::as_time_ago() const std::string
point::as_time_ago() const
{ {
struct timeval current_time = this->p_recent_point struct timeval current_time
.value_or(current_timeval()); = this->p_recent_point.value_or(current_timeval());
const char *fmt; const char* fmt;
char buffer[64]; char buffer[64];
int amount; int amount;
@ -57,7 +59,8 @@ std::string point::as_time_ago() const
current_time.tv_sec = convert_log_time_to_local(current_time.tv_sec); current_time.tv_sec = convert_log_time_to_local(current_time.tv_sec);
} }
auto delta = std::chrono::seconds(current_time.tv_sec - this->p_past_point.tv_sec); auto delta
= std::chrono::seconds(current_time.tv_sec - this->p_past_point.tv_sec);
if (delta < 0s) { if (delta < 0s) {
return "in the future"; return "in the future";
} else if (delta < 1min) { } else if (delta < 1min) {
@ -66,7 +69,8 @@ std::string point::as_time_ago() const
return "one minute ago"; return "one minute ago";
} else if (delta < 1h) { } else if (delta < 1h) {
fmt = "%d minutes ago"; fmt = "%d minutes ago";
amount = std::chrono::duration_cast<std::chrono::minutes>(delta).count(); amount
= std::chrono::duration_cast<std::chrono::minutes>(delta).count();
} else if (delta < 2h) { } else if (delta < 2h) {
return "one hour ago"; return "one hour ago";
} else if (delta < 24h) { } else if (delta < 24h) {
@ -89,7 +93,8 @@ std::string point::as_time_ago() const
return std::string(buffer); return std::string(buffer);
} }
std::string point::as_precise_time_ago() const std::string
point::as_precise_time_ago() const
{ {
struct timeval now, diff; struct timeval now, diff;
@ -121,31 +126,33 @@ std::string point::as_precise_time_ago() const
} }
} }
duration duration::from_tv(const struct timeval &tv) duration
duration::from_tv(const struct timeval& tv)
{ {
return duration{tv}; return duration{tv};
} }
std::string duration::to_string() const std::string
duration::to_string() const
{ {
/* 24h22m33s111 */ /* 24h22m33s111 */
static const struct rel_interval { static const struct rel_interval {
uint64_t length; uint64_t length;
const char *format; const char* format;
const char *symbol; const char* symbol;
} intervals[] = { } intervals[] = {
{ 1000, "%03lld%s", "" }, {1000, "%03lld%s", ""},
{ 60, "%lld%s", "s" }, {60, "%lld%s", "s"},
{ 60, "%lld%s", "m" }, {60, "%lld%s", "m"},
{ 24, "%lld%s", "h" }, {24, "%lld%s", "h"},
{ 0, "%lld%s", "d" }, {0, "%lld%s", "d"},
}; };
auto *curr_interval = intervals; auto* curr_interval = intervals;
auto usecs = std::chrono::duration_cast<std::chrono::microseconds>( auto usecs = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::seconds(this->d_timeval.tv_sec)) + std::chrono::seconds(this->d_timeval.tv_sec))
std::chrono::microseconds(this->d_timeval.tv_usec); + std::chrono::microseconds(this->d_timeval.tv_usec);
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(usecs); auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(usecs);
std::string retval; std::string retval;
bool neg = false; bool neg = false;
@ -157,8 +164,8 @@ std::string duration::to_string() const
uint64_t remaining; uint64_t remaining;
if (millis >= 10min) { if (millis >= 10min) {
remaining = std::chrono::duration_cast<std::chrono::seconds>(millis) remaining
.count(); = std::chrono::duration_cast<std::chrono::seconds>(millis).count();
curr_interval += 1; curr_interval += 1;
} else { } else {
remaining = millis.count(); remaining = millis.count();
@ -171,8 +178,7 @@ std::string duration::to_string() const
if (curr_interval->length) { if (curr_interval->length) {
amount = remaining % curr_interval->length; amount = remaining % curr_interval->length;
remaining = remaining / curr_interval->length; remaining = remaining / curr_interval->length;
} } else {
else {
amount = remaining; amount = remaining;
remaining = 0; remaining = 0;
} }
@ -181,7 +187,10 @@ std::string duration::to_string() const
break; break;
} }
snprintf(segment, sizeof(segment), curr_interval->format, amount, snprintf(segment,
sizeof(segment),
curr_interval->format,
amount,
curr_interval->symbol); curr_interval->symbol);
retval.insert(0, segment); retval.insert(0, segment);
} }
@ -193,5 +202,5 @@ std::string duration::to_string() const
return retval; return retval;
} }
} } // namespace time
} } // namespace humanize

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -30,10 +30,10 @@
#ifndef lnav_humanize_time_hh #ifndef lnav_humanize_time_hh
#define lnav_humanize_time_hh #define lnav_humanize_time_hh
#include <sys/time.h>
#include <string> #include <string>
#include <sys/time.h>
#include "optional.hpp" #include "optional.hpp"
namespace humanize { namespace humanize {
@ -41,15 +41,15 @@ namespace time {
class point { class point {
public: public:
static point from_tv(const struct timeval &tv); static point from_tv(const struct timeval& tv);
point &with_recent_point(const struct timeval &tv) point& with_recent_point(const struct timeval& tv)
{ {
this->p_recent_point = tv; this->p_recent_point = tv;
return *this; return *this;
} }
point &with_convert_to_local(bool convert_to_local) point& with_convert_to_local(bool convert_to_local)
{ {
this->p_convert_to_local = convert_to_local; this->p_convert_to_local = convert_to_local;
return *this; return *this;
@ -60,9 +60,10 @@ public:
std::string as_precise_time_ago() const; std::string as_precise_time_ago() const;
private: private:
explicit point(const struct timeval &tv) : p_past_point{tv.tv_sec, explicit point(const struct timeval& tv)
tv.tv_usec} : p_past_point{tv.tv_sec, tv.tv_usec}
{} {
}
struct timeval p_past_point; struct timeval p_past_point;
nonstd::optional<struct timeval> p_recent_point; nonstd::optional<struct timeval> p_recent_point;
@ -74,13 +75,14 @@ public:
static duration from_tv(const struct timeval& tv); static duration from_tv(const struct timeval& tv);
std::string to_string() const; std::string to_string() const;
private: private:
explicit duration(const struct timeval& tv) : d_timeval(tv) {} explicit duration(const struct timeval& tv) : d_timeval(tv) {}
struct timeval d_timeval; struct timeval d_timeval;
}; };
} } // namespace time
} } // namespace humanize
#endif #endif

@ -21,18 +21,17 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include "doctest/doctest.h"
#include "config.h"
#include "doctest/doctest.h"
#include "humanize.time.hh" #include "humanize.time.hh"
TEST_CASE("time ago") TEST_CASE("time ago")
@ -42,87 +41,82 @@ TEST_CASE("time ago")
time_t t1 = 1610000000; time_t t1 = 1610000000;
auto t1_chrono = std::chrono::seconds(t1); auto t1_chrono = std::chrono::seconds(t1);
auto p1 = humanize::time::point::from_tv({t1, 0}) auto p1 = humanize::time::point::from_tv({t1, 0}).with_recent_point(
.with_recent_point({(time_t) t1 + 5, 0}); {(time_t) t1 + 5, 0});
CHECK(p1.as_time_ago() == "just now"); CHECK(p1.as_time_ago() == "just now");
CHECK(p1.as_precise_time_ago() == " 5 seconds ago"); CHECK(p1.as_precise_time_ago() == " 5 seconds ago");
auto p2 = humanize::time::point::from_tv({t1, 0}) auto p2 = humanize::time::point::from_tv({t1, 0}).with_recent_point(
.with_recent_point({(time_t) t1 + 65, 0}); {(time_t) t1 + 65, 0});
CHECK(p2.as_time_ago() == "one minute ago"); CHECK(p2.as_time_ago() == "one minute ago");
CHECK(p2.as_precise_time_ago() == " 1 minute and 5 seconds ago"); CHECK(p2.as_precise_time_ago() == " 1 minute and 5 seconds ago");
auto p3 = humanize::time::point::from_tv({t1, 0}) auto p3 = humanize::time::point::from_tv({t1, 0}).with_recent_point(
.with_recent_point({(time_t) t1 + (3 * 60 + 5), 0}); {(time_t) t1 + (3 * 60 + 5), 0});
CHECK(p3.as_time_ago() == "3 minutes ago"); CHECK(p3.as_time_ago() == "3 minutes ago");
CHECK(p3.as_precise_time_ago() == " 3 minutes and 5 seconds ago"); CHECK(p3.as_precise_time_ago() == " 3 minutes and 5 seconds ago");
auto p4 = humanize::time::point::from_tv({t1, 0}) auto p4 = humanize::time::point::from_tv({t1, 0}).with_recent_point(
.with_recent_point({(time_t) (t1_chrono + 65min).count(), 0}); {(time_t) (t1_chrono + 65min).count(), 0});
CHECK(p4.as_time_ago() == "one hour ago"); CHECK(p4.as_time_ago() == "one hour ago");
CHECK(p4.as_precise_time_ago() == "one hour ago"); CHECK(p4.as_precise_time_ago() == "one hour ago");
auto p5 = humanize::time::point::from_tv({t1, 0}) auto p5 = humanize::time::point::from_tv({t1, 0}).with_recent_point(
.with_recent_point({(time_t) (t1_chrono + 3h).count(), 0}); {(time_t) (t1_chrono + 3h).count(), 0});
CHECK(p5.as_time_ago() == "3 hours ago"); CHECK(p5.as_time_ago() == "3 hours ago");
CHECK(p5.as_precise_time_ago() == "3 hours ago"); CHECK(p5.as_precise_time_ago() == "3 hours ago");
auto p6 = humanize::time::point::from_tv({t1, 0}) auto p6 = humanize::time::point::from_tv({t1, 0}).with_recent_point(
.with_recent_point({(time_t) (t1_chrono + 25h).count(), 0}); {(time_t) (t1_chrono + 25h).count(), 0});
CHECK(p6.as_time_ago() == "one day ago"); CHECK(p6.as_time_ago() == "one day ago");
CHECK(p6.as_precise_time_ago() == "one day ago"); CHECK(p6.as_precise_time_ago() == "one day ago");
auto p7 = humanize::time::point::from_tv({t1, 0}) auto p7 = humanize::time::point::from_tv({t1, 0}).with_recent_point(
.with_recent_point({(time_t) (t1_chrono + 50h).count(), 0}); {(time_t) (t1_chrono + 50h).count(), 0});
CHECK(p7.as_time_ago() == "2 days ago"); CHECK(p7.as_time_ago() == "2 days ago");
CHECK(p7.as_precise_time_ago() == "2 days ago"); CHECK(p7.as_precise_time_ago() == "2 days ago");
auto p8 = humanize::time::point::from_tv({t1, 0}) auto p8 = humanize::time::point::from_tv({t1, 0}).with_recent_point(
.with_recent_point({(time_t) (t1_chrono + 370 * 24h).count(), 0}); {(time_t) (t1_chrono + 370 * 24h).count(), 0});
CHECK(p8.as_time_ago() == "over a year ago"); CHECK(p8.as_time_ago() == "over a year ago");
CHECK(p8.as_precise_time_ago() == "over a year ago"); CHECK(p8.as_precise_time_ago() == "over a year ago");
auto p9 = humanize::time::point::from_tv({t1, 0}) auto p9 = humanize::time::point::from_tv({t1, 0}).with_recent_point(
.with_recent_point({(time_t) (t1_chrono + 800 * 24h).count(), 0}); {(time_t) (t1_chrono + 800 * 24h).count(), 0});
CHECK(p9.as_time_ago() == "over 2 years ago"); CHECK(p9.as_time_ago() == "over 2 years ago");
CHECK(p9.as_precise_time_ago() == "over 2 years ago"); CHECK(p9.as_precise_time_ago() == "over 2 years ago");
CHECK(humanize::time::point::from_tv({1610000000, 0}) CHECK(humanize::time::point::from_tv({1610000000, 0})
.with_recent_point({(time_t) 1612000000, 0}) .with_recent_point({(time_t) 1612000000, 0})
.as_time_ago() == "23 days ago"); .as_time_ago()
== "23 days ago");
} }
TEST_CASE("duration to_string") { TEST_CASE("duration to_string")
{
std::string val; std::string val;
val = humanize::time::duration::from_tv({25 * 60 * 60, 123000}) val = humanize::time::duration::from_tv({25 * 60 * 60, 123000}).to_string();
.to_string();
CHECK(val == "1d1h0m0s"); CHECK(val == "1d1h0m0s");
val = humanize::time::duration::from_tv({10, 123000}) val = humanize::time::duration::from_tv({10, 123000}).to_string();
.to_string();
CHECK(val == "10s123"); CHECK(val == "10s123");
val = humanize::time::duration::from_tv({10, 0}) val = humanize::time::duration::from_tv({10, 0}).to_string();
.to_string();
CHECK(val == "10s000"); CHECK(val == "10s000");
val = humanize::time::duration::from_tv({0, 100000}) val = humanize::time::duration::from_tv({0, 100000}).to_string();
.to_string();
CHECK(val == "100"); CHECK(val == "100");
val = humanize::time::duration::from_tv({0, 0}) val = humanize::time::duration::from_tv({0, 0}).to_string();
.to_string();
CHECK(val == ""); CHECK(val == "");
val = humanize::time::duration::from_tv({0, -10000}) val = humanize::time::duration::from_tv({0, -10000}).to_string();
.to_string();
CHECK(val == "-010"); CHECK(val == "-010");
val = humanize::time::duration::from_tv({-10, 0}) val = humanize::time::duration::from_tv({-10, 0}).to_string();
.to_string();
CHECK(val == "-10s000"); CHECK(val == "-10s000");
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -38,55 +38,60 @@ namespace injector {
namespace details { namespace details {
template<typename I, typename R, typename ...Args> template<typename I, typename R, typename... Args>
std::function<std::shared_ptr<I>()> create_factory(R (*)(Args...)) { std::function<std::shared_ptr<I>()>
return []() { create_factory(R (*)(Args...))
return std::make_shared<I>(::injector::get<Args>()...); {
}; return []() { return std::make_shared<I>(::injector::get<Args>()...); };
} }
template<typename I, template<typename I, std::enable_if_t<has_injectable<I>::value, bool> = true>
std::enable_if_t<has_injectable<I>::value, bool> = true> std::function<std::shared_ptr<I>()>
std::function<std::shared_ptr<I>()> create_factory() { create_factory()
typename I::injectable *i = nullptr; {
typename I::injectable* i = nullptr;
return create_factory<I>(i); return create_factory<I>(i);
} }
template<typename I, template<typename I, std::enable_if_t<!has_injectable<I>::value, bool> = true>
std::enable_if_t<!has_injectable<I>::value, bool> = true> std::function<std::shared_ptr<I>()>
std::function<std::shared_ptr<I>()> create_factory() noexcept { create_factory() noexcept
return []() { {
return std::make_shared<I>(); return []() { return std::make_shared<I>(); };
};
} }
} } // namespace details
template<typename T, typename...Annotations> template<typename T, typename... Annotations>
struct bind : singleton_storage<T, Annotations...> { struct bind : singleton_storage<T, Annotations...> {
static bool to_singleton() noexcept { static bool to_singleton() noexcept
{
static T storage; static T storage;
singleton_storage<T, Annotations...>::ss_data = &storage; singleton_storage<T, Annotations...>::ss_data = &storage;
return true; return true;
} }
template<typename...Args> template<typename... Args>
static bool to_instance(T* (*f)(Args...)) noexcept { static bool to_instance(T* (*f)(Args...)) noexcept
singleton_storage<T, Annotations...>::ss_data = f(::injector::get<Args>()...); {
singleton_storage<T, Annotations...>::ss_data
= f(::injector::get<Args>()...);
return true; return true;
} }
static bool to_instance(T* data) noexcept { static bool to_instance(T* data) noexcept
{
singleton_storage<T, Annotations...>::ss_data = data; singleton_storage<T, Annotations...>::ss_data = data;
return true; return true;
} }
template<typename I> template<typename I>
static bool to() noexcept { static bool to() noexcept
singleton_storage<T, Annotations...>::ss_factory = {
details::create_factory<I>(); singleton_storage<T, Annotations...>::ss_factory
= details::create_factory<I>();
return true; return true;
} }
}; };
@ -96,15 +101,17 @@ struct bind_multiple : multiple_storage<T> {
bind_multiple() noexcept = default; bind_multiple() noexcept = default;
template<typename I> template<typename I>
bind_multiple& add() noexcept { bind_multiple& add() noexcept
multiple_storage<T>::get_factories()[typeid(I).name()] = {
details::create_factory<I>(); multiple_storage<T>::get_factories()[typeid(I).name()]
= details::create_factory<I>();
return *this; return *this;
} }
template<typename I, typename...Annotations> template<typename I, typename... Annotations>
bind_multiple& add_singleton() noexcept { bind_multiple& add_singleton() noexcept
{
auto factory = details::create_factory<I>(); auto factory = details::create_factory<I>();
auto single = factory(); auto single = factory();
@ -112,14 +119,13 @@ struct bind_multiple : multiple_storage<T> {
bind<T, Annotations...>::to_instance(single.get()); bind<T, Annotations...>::to_instance(single.get());
} }
bind<I, Annotations...>::to_instance(single.get()); bind<I, Annotations...>::to_instance(single.get());
multiple_storage<T>::get_factories()[typeid(I).name()] = [single]() { multiple_storage<T>::get_factories()[typeid(I).name()]
return single; = [single]() { return single; };
};
return *this; return *this;
} }
}; };
} } // namespace injector
#endif #endif

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,12 +32,12 @@
#ifndef lnav_injector_hh #ifndef lnav_injector_hh
#define lnav_injector_hh #define lnav_injector_hh
#include <assert.h>
#include <map> #include <map>
#include <memory> #include <memory>
#include <vector>
#include <type_traits> #include <type_traits>
#include <vector>
#include <assert.h>
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
@ -46,40 +46,45 @@ namespace injector {
template<typename Annotation> template<typename Annotation>
void force_linking(Annotation anno); void force_linking(Annotation anno);
template<class ...> template<class...>
using void_t = void; using void_t = void;
template <class, class = void> template<class, class = void>
struct has_injectable : std::false_type {}; struct has_injectable : std::false_type {
};
template <class T> template<class T>
struct has_injectable<T, void_t<typename T::injectable>> : std::true_type struct has_injectable<T, void_t<typename T::injectable>> : std::true_type {
{}; };
template<typename T, typename...Annotations> template<typename T, typename... Annotations>
struct singleton_storage { struct singleton_storage {
static T *get() { static T* get()
{
static int _[] = {0, (force_linking(Annotations{}), 0)...}; static int _[] = {0, (force_linking(Annotations{}), 0)...};
(void)_; (void) _;
assert(ss_data != nullptr); assert(ss_data != nullptr);
return ss_data; return ss_data;
} }
static std::shared_ptr<T> create() { static std::shared_ptr<T> create()
{
static int _[] = {0, (force_linking(Annotations{}), 0)...}; static int _[] = {0, (force_linking(Annotations{}), 0)...};
(void)_; (void) _;
return ss_factory(); return ss_factory();
} }
protected: protected:
static T *ss_data; static T* ss_data;
static std::function<std::shared_ptr<T>()> ss_factory; static std::function<std::shared_ptr<T>()> ss_factory;
}; };
template<typename T, typename...Annotations> template<typename T, typename... Annotations>
T *singleton_storage<T, Annotations...>::ss_data = nullptr; T* singleton_storage<T, Annotations...>::ss_data = nullptr;
template<typename T, typename...Annotations> template<typename T, typename... Annotations>
std::function<std::shared_ptr<T>()> singleton_storage<T, Annotations...>::ss_factory; std::function<std::shared_ptr<T>()>
singleton_storage<T, Annotations...>::ss_factory;
template<typename T> template<typename T>
struct Impl { struct Impl {
@ -88,7 +93,8 @@ struct Impl {
template<typename T> template<typename T>
struct multiple_storage { struct multiple_storage {
static std::vector<std::shared_ptr<T>> create() { static std::vector<std::shared_ptr<T>> create()
{
std::vector<std::shared_ptr<T>> retval; std::vector<std::shared_ptr<T>> retval;
for (const auto& pair : get_factories()) { for (const auto& pair : get_factories()) {
@ -96,28 +102,35 @@ struct multiple_storage {
} }
return retval; return retval;
} }
protected: protected:
using factory_map_t = std::map<std::string, std::function<std::shared_ptr<T>()>>; using factory_map_t
= std::map<std::string, std::function<std::shared_ptr<T>()>>;
static factory_map_t& get_factories() { static factory_map_t& get_factories()
{
static factory_map_t retval; static factory_map_t retval;
return retval; return retval;
} }
}; };
template<typename T, typename...Annotations, template<typename T,
std::enable_if_t<std::is_reference<T>::value, bool> = true> typename... Annotations,
T get() std::enable_if_t<std::is_reference<T>::value, bool> = true>
T
get()
{ {
using plain_t = std::remove_const_t<std::remove_reference_t<T>>; using plain_t = std::remove_const_t<std::remove_reference_t<T>>;
return *singleton_storage<plain_t, Annotations...>::get(); return *singleton_storage<plain_t, Annotations...>::get();
} }
template<typename T, typename...Annotations, template<typename T,
std::enable_if_t<std::is_pointer<T>::value, bool> = true> typename... Annotations,
T get() std::enable_if_t<std::is_pointer<T>::value, bool> = true>
T
get()
{ {
using plain_t = std::remove_const_t<std::remove_pointer_t<T>>; using plain_t = std::remove_const_t<std::remove_pointer_t<T>>;
@ -125,31 +138,38 @@ T get()
} }
template<class T> template<class T>
struct is_shared_ptr : std::false_type {}; struct is_shared_ptr : std::false_type {
};
template<class T> template<class T>
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {}; struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {
};
template<class T> template<class T>
struct is_vector : std::false_type {}; struct is_vector : std::false_type {
};
template<class T> template<class T>
struct is_vector<std::vector<T>> : std::true_type {}; struct is_vector<std::vector<T>> : std::true_type {
};
template<typename T, typename...Annotations, template<typename T,
std::enable_if_t<is_shared_ptr<T>::value, bool> = true> typename... Annotations,
T get() std::enable_if_t<is_shared_ptr<T>::value, bool> = true>
T
get()
{ {
return singleton_storage<typename T::element_type, Annotations...>::create(); return singleton_storage<typename T::element_type,
Annotations...>::create();
} }
template<typename T, template<typename T, std::enable_if_t<is_vector<T>::value, bool> = true>
std::enable_if_t<is_vector<T>::value, bool> = true> T
T get() get()
{ {
return multiple_storage<typename T::value_type::element_type>::create(); return multiple_storage<typename T::value_type::element_type>::create();
} }
} } // namespace injector
#endif #endif

@ -21,26 +21,27 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file intern_string.cc * @file intern_string.cc
*/ */
#include "config.h"
#include <string.h>
#include <mutex> #include <mutex>
#include "intern_string.hh" #include "intern_string.hh"
#include <string.h>
#include "config.h"
const static int TABLE_SIZE = 4095; const static int TABLE_SIZE = 4095;
struct intern_string::intern_table { struct intern_string::intern_table {
~intern_table() { ~intern_table()
{
for (auto is : this->it_table) { for (auto is : this->it_table) {
auto curr = is; auto curr = is;
@ -53,10 +54,11 @@ struct intern_string::intern_table {
} }
} }
intern_string *it_table[TABLE_SIZE]; intern_string* it_table[TABLE_SIZE];
}; };
intern_table_lifetime intern_string::get_table_lifetime() intern_table_lifetime
intern_string::get_table_lifetime()
{ {
static intern_table_lifetime retval = std::make_shared<intern_table>(); static intern_table_lifetime retval = std::make_shared<intern_table>();
@ -64,22 +66,23 @@ intern_table_lifetime intern_string::get_table_lifetime()
} }
unsigned long unsigned long
hash_str(const char *str, size_t len) hash_str(const char* str, size_t len)
{ {
unsigned long retval = 5381; unsigned long retval = 5381;
for (size_t lpc = 0; lpc < len; lpc++) { for (size_t lpc = 0; lpc < len; lpc++) {
/* retval * 33 + c */ /* retval * 33 + c */
retval = ((retval << 5) + retval) + (unsigned char)str[lpc]; retval = ((retval << 5) + retval) + (unsigned char) str[lpc];
} }
return retval; return retval;
} }
const intern_string *intern_string::lookup(const char *str, ssize_t len) noexcept const intern_string*
intern_string::lookup(const char* str, ssize_t len) noexcept
{ {
unsigned long h; unsigned long h;
intern_string *curr; intern_string* curr;
if (len == -1) { if (len == -1) {
len = strlen(str); len = strlen(str);
@ -94,7 +97,8 @@ const intern_string *intern_string::lookup(const char *str, ssize_t len) noexcep
curr = tab->it_table[h]; curr = tab->it_table[h];
while (curr != nullptr) { while (curr != nullptr) {
if (curr->is_str.size() == len && strncmp(curr->is_str.c_str(), str, len) == 0) { if (curr->is_str.size() == len
&& strncmp(curr->is_str.c_str(), str, len) == 0) {
return curr; return curr;
} }
curr = curr->is_next; curr = curr->is_next;
@ -106,22 +110,24 @@ const intern_string *intern_string::lookup(const char *str, ssize_t len) noexcep
return curr; return curr;
} }
} }
const intern_string *intern_string::lookup(const string_fragment &sf) noexcept const intern_string*
intern_string::lookup(const string_fragment& sf) noexcept
{ {
return lookup(sf.data(), sf.length()); return lookup(sf.data(), sf.length());
} }
const intern_string *intern_string::lookup(const std::string &str) noexcept const intern_string*
intern_string::lookup(const std::string& str) noexcept
{ {
return lookup(str.c_str(), str.size()); return lookup(str.c_str(), str.size());
} }
bool intern_string::startswith(const char *prefix) const bool
intern_string::startswith(const char* prefix) const
{ {
const char *curr = this->is_str.data(); const char* curr = this->is_str.data();
while (*prefix != '\0' && *prefix == *curr) { while (*prefix != '\0' && *prefix == *curr) {
prefix += 1; prefix += 1;
@ -131,7 +137,8 @@ bool intern_string::startswith(const char *prefix) const
return *prefix == '\0'; return *prefix == '\0';
} }
void string_fragment::trim(const char *tokens) void
string_fragment::trim(const char* tokens)
{ {
while (this->sf_begin < this->sf_end) { while (this->sf_begin < this->sf_end) {
bool found = false; bool found = false;
@ -165,7 +172,8 @@ void string_fragment::trim(const char *tokens)
} }
} }
nonstd::optional<string_fragment> string_fragment::consume_n(int amount) const nonstd::optional<string_fragment>
string_fragment::consume_n(int amount) const
{ {
if (amount > this->length()) { if (amount > this->length()) {
return nonstd::nullopt; return nonstd::nullopt;

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,72 +32,81 @@
#ifndef intern_string_hh #ifndef intern_string_hh
#define intern_string_hh #define intern_string_hh
#include <string>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <string> #include "fmt/format.h"
#include "optional.hpp" #include "optional.hpp"
#include "strnatcmp.h" #include "strnatcmp.h"
#include "fmt/format.h"
struct string_fragment { struct string_fragment {
using iterator = const char *; using iterator = const char*;
explicit string_fragment(const char *str = "", int begin = 0, int end = -1)
: sf_string(str), sf_begin(begin), sf_end(end == -1 ? strlen(str) : end) {
};
explicit string_fragment(const unsigned char *str, int begin = 0, int end = -1) explicit string_fragment(const char* str = "", int begin = 0, int end = -1)
: sf_string((const char *) str), : sf_string(str), sf_begin(begin),
sf_begin(begin), sf_end(end == -1 ? strlen(str) : end){};
sf_end(end == -1 ? strlen((const char *) str) : end) {
};
string_fragment(const std::string &str) explicit string_fragment(const unsigned char* str,
: sf_string(str.c_str()), sf_begin(0), sf_end(str.length()) { int begin = 0,
int end = -1)
: sf_string((const char*) str), sf_begin(begin),
sf_end(end == -1 ? strlen((const char*) str) : end){};
string_fragment(const std::string& str)
: sf_string(str.c_str()), sf_begin(0), sf_end(str.length())
{
} }
bool is_valid() const { bool is_valid() const
{
return this->sf_begin != -1; return this->sf_begin != -1;
}; };
int length() const { int length() const
{
return this->sf_end - this->sf_begin; return this->sf_end - this->sf_begin;
}; };
const char *data() const { const char* data() const
{
return &this->sf_string[this->sf_begin]; return &this->sf_string[this->sf_begin];
} }
iterator begin() const { iterator begin() const
{
return &this->sf_string[this->sf_begin]; return &this->sf_string[this->sf_begin];
} }
iterator end() const { iterator end() const
{
return &this->sf_string[this->sf_end]; return &this->sf_string[this->sf_end];
} }
bool empty() const { bool empty() const
{
return !this->is_valid() || length() == 0; return !this->is_valid() || length() == 0;
}; };
char operator[](int index) const { char operator[](int index) const
{
return this->sf_string[sf_begin + index]; return this->sf_string[sf_begin + index];
}; };
bool operator==(const std::string &str) const { bool operator==(const std::string& str) const
{
if (this->length() != (int) str.length()) { if (this->length() != (int) str.length()) {
return false; return false;
} }
return memcmp(&this->sf_string[this->sf_begin], return memcmp(
str.c_str(), &this->sf_string[this->sf_begin], str.c_str(), str.length())
str.length()) == 0; == 0;
}; };
bool operator==(const string_fragment &sf) const { bool operator==(const string_fragment& sf) const
{
if (this->length() != sf.length()) { if (this->length() != sf.length()) {
return false; return false;
} }
@ -105,27 +114,32 @@ struct string_fragment {
return memcmp(this->data(), sf.data(), sf.length()) == 0; return memcmp(this->data(), sf.data(), sf.length()) == 0;
}; };
bool iequal(const string_fragment &sf) const { bool iequal(const string_fragment& sf) const
{
if (this->length() != sf.length()) { if (this->length() != sf.length()) {
return false; return false;
} }
return strnatcasecmp(this->length(), this->data(), return strnatcasecmp(
sf.length(), sf.data()) == 0; this->length(), this->data(), sf.length(), sf.data())
== 0;
}; };
bool operator==(const char *str) const { bool operator==(const char* str) const
{
size_t len = strlen(str); size_t len = strlen(str);
return len == (size_t) this->length() && return len == (size_t) this->length()
strncmp(this->data(), str, this->length()) == 0; && strncmp(this->data(), str, this->length()) == 0;
}; };
bool operator!=(const char *str) const { bool operator!=(const char* str) const
{
return !(*this == str); return !(*this == str);
} }
bool startswith(const char *prefix) const { bool startswith(const char* prefix) const
{
auto iter = this->begin(); auto iter = this->begin();
while (*prefix != '\0' && *prefix == *iter && iter < this->end()) { while (*prefix != '\0' && *prefix == *iter && iter < this->end()) {
@ -136,15 +150,14 @@ struct string_fragment {
return *prefix == '\0'; return *prefix == '\0';
} }
string_fragment substr(int begin) const { string_fragment substr(int begin) const
{
return string_fragment{ return string_fragment{
this->sf_string, this->sf_string, this->sf_begin + begin, this->sf_end};
this->sf_begin + begin,
this->sf_end
};
} }
nonstd::optional<size_t> find(char ch) const { nonstd::optional<size_t> find(char ch) const
{
for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) { for (int lpc = this->sf_begin; lpc < this->sf_end; lpc++) {
if (this->sf_string[lpc] == ch) { if (this->sf_string[lpc] == ch) {
return lpc; return lpc;
@ -155,7 +168,8 @@ struct string_fragment {
} }
template<typename P> template<typename P>
nonstd::optional<string_fragment> consume(P predicate) const { nonstd::optional<string_fragment> consume(P predicate) const
{
int consumed = 0; int consumed = 0;
while (consumed < this->length()) { while (consumed < this->length()) {
if (!predicate(this->data()[consumed])) { if (!predicate(this->data()[consumed])) {
@ -179,7 +193,8 @@ struct string_fragment {
nonstd::optional<string_fragment> consume_n(int amount) const; nonstd::optional<string_fragment> consume_n(int amount) const;
template<typename P> template<typename P>
string_fragment skip(P predicate) const { string_fragment skip(P predicate) const
{
int offset = 0; int offset = 0;
while (offset < this->length() && predicate(this->data()[offset])) { while (offset < this->length() && predicate(this->data()[offset])) {
offset += 1; offset += 1;
@ -192,10 +207,12 @@ struct string_fragment {
}; };
} }
using split_result = nonstd::optional<std::pair<string_fragment, string_fragment>>; using split_result
= nonstd::optional<std::pair<string_fragment, string_fragment>>;
template<typename P> template<typename P>
split_result split_while(P& predicate) const { split_result split_while(P& predicate) const
{
int consumed = 0; int consumed = 0;
while (consumed < this->length()) { while (consumed < this->length()) {
if (!predicate(this->data()[consumed])) { if (!predicate(this->data()[consumed])) {
@ -219,14 +236,14 @@ struct string_fragment {
this->sf_string, this->sf_string,
this->sf_begin + consumed, this->sf_begin + consumed,
this->sf_end, this->sf_end,
} });
);
} }
struct tag1 { struct tag1 {
const char t_value; const char t_value;
bool operator()(char ch) const { bool operator()(char ch) const
{
return this->t_value == ch; return this->t_value == ch;
} }
}; };
@ -234,7 +251,8 @@ struct string_fragment {
struct quoted_string_body { struct quoted_string_body {
bool qs_in_escape{false}; bool qs_in_escape{false};
bool operator()(char ch) { bool operator()(char ch)
{
if (this->qs_in_escape) { if (this->qs_in_escape) {
this->qs_in_escape = false; this->qs_in_escape = false;
return true; return true;
@ -249,80 +267,93 @@ struct string_fragment {
} }
}; };
const char *to_string(char *buf) const { const char* to_string(char* buf) const
{
memcpy(buf, this->data(), this->length()); memcpy(buf, this->data(), this->length());
buf[this->length()] = '\0'; buf[this->length()] = '\0';
return buf; return buf;
}; };
std::string to_string() const { std::string to_string() const
{
return {this->data(), (size_t) this->length()}; return {this->data(), (size_t) this->length()};
} }
void clear() { void clear()
{
this->sf_begin = 0; this->sf_begin = 0;
this->sf_end = 0; this->sf_end = 0;
}; };
void invalidate() { void invalidate()
{
this->sf_begin = -1; this->sf_begin = -1;
this->sf_end = -1; this->sf_end = -1;
}; };
void trim(const char *tokens); void trim(const char* tokens);
const char *sf_string; const char* sf_string;
int sf_begin; int sf_begin;
int sf_end; int sf_end;
}; };
inline bool operator<(const char *left, const string_fragment &right) { inline bool
operator<(const char* left, const string_fragment& right)
{
int rc = strncmp(left, right.data(), right.length()); int rc = strncmp(left, right.data(), right.length());
return rc < 0; return rc < 0;
} }
inline bool operator<(const string_fragment &left, const char *right) { inline bool
operator<(const string_fragment& left, const char* right)
{
return strncmp(left.data(), right, left.length()) < 0; return strncmp(left.data(), right, left.length()) < 0;
} }
class intern_string { class intern_string {
public: public:
static const intern_string *lookup(const char *str, ssize_t len) noexcept; static const intern_string* lookup(const char* str, ssize_t len) noexcept;
static const intern_string *lookup(const string_fragment &sf) noexcept; static const intern_string* lookup(const string_fragment& sf) noexcept;
static const intern_string *lookup(const std::string &str) noexcept; static const intern_string* lookup(const std::string& str) noexcept;
const char *get() const { const char* get() const
{
return this->is_str.c_str(); return this->is_str.c_str();
}; };
size_t size() const { size_t size() const
{
return this->is_str.size(); return this->is_str.size();
} }
std::string to_string() const { std::string to_string() const
{
return this->is_str; return this->is_str;
} }
string_fragment to_string_fragment() const { string_fragment to_string_fragment() const
{
return string_fragment{this->is_str}; return string_fragment{this->is_str};
} }
bool startswith(const char *prefix) const; bool startswith(const char* prefix) const;
struct intern_table; struct intern_table;
static std::shared_ptr<intern_table> get_table_lifetime(); static std::shared_ptr<intern_table> get_table_lifetime();
private: private:
friend intern_table; friend intern_table;
intern_string(const char *str, ssize_t len) intern_string(const char* str, ssize_t len)
: is_next(nullptr), is_str(str, (size_t) len) { : is_next(nullptr), is_str(str, (size_t) len)
{
} }
intern_string *is_next; intern_string* is_next;
std::string is_str; std::string is_str;
}; };
@ -330,97 +361,112 @@ using intern_table_lifetime = std::shared_ptr<intern_string::intern_table>;
class intern_string_t { class intern_string_t {
public: public:
using iterator = const char *; using iterator = const char*;
intern_string_t(const intern_string *is = nullptr) : ist_interned_string(is) {
intern_string_t(const intern_string* is = nullptr) : ist_interned_string(is)
{
} }
const intern_string *unwrap() const { const intern_string* unwrap() const
{
return this->ist_interned_string; return this->ist_interned_string;
} }
void clear() { void clear()
{
this->ist_interned_string = nullptr; this->ist_interned_string = nullptr;
}; };
bool empty() const { bool empty() const
{
return this->ist_interned_string == nullptr; return this->ist_interned_string == nullptr;
} }
const char *get() const { const char* get() const
{
if (this->empty()) { if (this->empty()) {
return ""; return "";
} }
return this->ist_interned_string->get(); return this->ist_interned_string->get();
} }
iterator begin() const { iterator begin() const
{
return this->get(); return this->get();
} }
iterator end() const { iterator end() const
{
return this->get() + this->size(); return this->get() + this->size();
} }
size_t size() const { size_t size() const
{
if (this->ist_interned_string == nullptr) { if (this->ist_interned_string == nullptr) {
return 0; return 0;
} }
return this->ist_interned_string->size(); return this->ist_interned_string->size();
} }
size_t hash() const { size_t hash() const
{
auto ptr = (uintptr_t) this->ist_interned_string; auto ptr = (uintptr_t) this->ist_interned_string;
return ptr; return ptr;
} }
std::string to_string() const { std::string to_string() const
{
if (this->ist_interned_string == nullptr) { if (this->ist_interned_string == nullptr) {
return ""; return "";
} }
return this->ist_interned_string->to_string(); return this->ist_interned_string->to_string();
} }
string_fragment to_string_fragment() const { string_fragment to_string_fragment() const
{
if (this->ist_interned_string == nullptr) { if (this->ist_interned_string == nullptr) {
return string_fragment{"", 0, 0}; return string_fragment{"", 0, 0};
} }
return this->ist_interned_string->to_string_fragment(); return this->ist_interned_string->to_string_fragment();
} }
bool operator<(const intern_string_t &rhs) const { bool operator<(const intern_string_t& rhs) const
{
return strcmp(this->get(), rhs.get()) < 0; return strcmp(this->get(), rhs.get()) < 0;
} }
bool operator==(const intern_string_t &rhs) const { bool operator==(const intern_string_t& rhs) const
{
return this->ist_interned_string == rhs.ist_interned_string; return this->ist_interned_string == rhs.ist_interned_string;
} }
bool operator!=(const intern_string_t &rhs) const { bool operator!=(const intern_string_t& rhs) const
{
return !(*this == rhs); return !(*this == rhs);
} }
bool operator==(const char *rhs) const { bool operator==(const char* rhs) const
{
return strcmp(this->get(), rhs) == 0; return strcmp(this->get(), rhs) == 0;
} }
bool operator!=(const char *rhs) const { bool operator!=(const char* rhs) const
{
return strcmp(this->get(), rhs) != 0; return strcmp(this->get(), rhs) != 0;
} }
private: private:
const intern_string *ist_interned_string; const intern_string* ist_interned_string;
}; };
unsigned long hash_str(const char *str, size_t len); unsigned long hash_str(const char* str, size_t len);
namespace fmt { namespace fmt {
template<> template<>
struct formatter<string_fragment> : formatter<string_view> { struct formatter<string_fragment> : formatter<string_view> {
template<typename FormatContext> template<typename FormatContext>
auto format(const string_fragment &sf, FormatContext &ctx) auto format(const string_fragment& sf, FormatContext& ctx)
{ {
return formatter<string_view>::format( return formatter<string_view>::format(
string_view{sf.data(), (size_t) sf.length()}, ctx); string_view{sf.data(), (size_t) sf.length()}, ctx);
@ -430,61 +476,80 @@ struct formatter<string_fragment> : formatter<string_view> {
template<> template<>
struct formatter<intern_string_t> : formatter<string_view> { struct formatter<intern_string_t> : formatter<string_view> {
template<typename FormatContext> template<typename FormatContext>
auto format(const intern_string_t& is, FormatContext &ctx) auto format(const intern_string_t& is, FormatContext& ctx)
{ {
return formatter<string_view>::format( return formatter<string_view>::format(
string_view{is.get(), (size_t) is.size()}, ctx); string_view{is.get(), (size_t) is.size()}, ctx);
} }
}; };
} } // namespace fmt
namespace std { namespace std {
template <> template<>
struct hash<const intern_string_t> { struct hash<const intern_string_t> {
std::size_t operator()(const intern_string_t &ist) const { std::size_t operator()(const intern_string_t& ist) const
return ist.hash(); {
} return ist.hash();
}; }
} };
} // namespace std
inline bool operator<(const char *left, const intern_string_t &right) { inline bool
operator<(const char* left, const intern_string_t& right)
{
int rc = strncmp(left, right.get(), right.size()); int rc = strncmp(left, right.get(), right.size());
return rc < 0; return rc < 0;
} }
inline bool operator<(const intern_string_t &left, const char *right) { inline bool
operator<(const intern_string_t& left, const char* right)
{
return strncmp(left.get(), right, left.size()) < 0; return strncmp(left.get(), right, left.size()) < 0;
} }
inline bool operator==(const intern_string_t &left, const string_fragment &sf) { inline bool
return ((int) left.size() == sf.length()) && operator==(const intern_string_t& left, const string_fragment& sf)
(memcmp(left.get(), sf.data(), left.size()) == 0); {
return ((int) left.size() == sf.length())
&& (memcmp(left.get(), sf.data(), left.size()) == 0);
} }
inline bool operator==(const string_fragment &left, const intern_string_t &right) { inline bool
return (left.length() == (int) right.size()) && operator==(const string_fragment& left, const intern_string_t& right)
(memcmp(left.data(), right.get(), left.length()) == 0); {
return (left.length() == (int) right.size())
&& (memcmp(left.data(), right.get(), left.length()) == 0);
} }
namespace std { namespace std {
inline string to_string(const string_fragment &s) { inline string
return {s.data(), (size_t) s.length()}; to_string(const string_fragment& s)
} {
return {s.data(), (size_t) s.length()};
}
inline string to_string(const intern_string_t &s) { inline string
return s.to_string(); to_string(const intern_string_t& s)
} {
return s.to_string();
} }
} // namespace std
inline string_fragment to_string_fragment(const string_fragment &s) { inline string_fragment
to_string_fragment(const string_fragment& s)
{
return s; return s;
} }
inline string_fragment to_string_fragment(const intern_string_t &s) { inline string_fragment
to_string_fragment(const intern_string_t& s)
{
return string_fragment(s.get(), 0, s.size()); return string_fragment(s.get(), 0, s.size());
} }
inline string_fragment to_string_fragment(const std::string &s) { inline string_fragment
to_string_fragment(const std::string& s)
{
return string_fragment(s.c_str(), 0, s.length()); return string_fragment(s.c_str(), 0, s.length());
} }

@ -21,28 +21,27 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <cctype> #include <cctype>
#include <iostream> #include <iostream>
#include "doctest/doctest.h"
#include "intern_string.hh" #include "intern_string.hh"
#include "config.h"
#include "doctest/doctest.h"
TEST_CASE("consume") TEST_CASE("consume")
{ {
auto is_eq = string_fragment::tag1{'='}; auto is_eq = string_fragment::tag1{'='};
auto is_dq = string_fragment::tag1{'"'}; auto is_dq = string_fragment::tag1{'"'};
auto is_colon = string_fragment::tag1{':'}; auto is_colon = string_fragment::tag1{':'};
const char *pair = "foo = bar"; const char* pair = "foo = bar";
auto sf = string_fragment(pair); auto sf = string_fragment(pair);
auto split_sf = sf.split_while(isalnum); auto split_sf = sf.split_while(isalnum);
@ -64,7 +63,7 @@ TEST_CASE("consume")
auto no_value = sf.consume(is_colon); auto no_value = sf.consume(is_colon);
CHECK(!no_value.has_value()); CHECK(!no_value.has_value());
const char *qs = R"("foo \" bar")"; const char* qs = R"("foo \" bar")";
auto qs_sf = string_fragment{qs}; auto qs_sf = string_fragment{qs};
auto qs_body = qs_sf.consume(is_dq); auto qs_body = qs_sf.consume(is_dq);

@ -25,10 +25,10 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#include "config.h"
#include "is_utf8.hh" #include "is_utf8.hh"
#include "config.h"
/* /*
Check if the given unsigned char * is a valid utf-8 sequence. Check if the given unsigned char * is a valid utf-8 sequence.
@ -56,240 +56,236 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
Returns the first erroneous byte position, and give in Returns the first erroneous byte position, and give in
`faulty_bytes` the number of actually existing bytes taking part in this error. `faulty_bytes` the number of actually existing bytes taking part in this
error.
*/ */
ssize_t is_utf8(unsigned char *str, size_t len, const char **message, int *faulty_bytes) ssize_t
is_utf8(unsigned char* str, size_t len, const char** message, int* faulty_bytes)
{ {
size_t i = 0; size_t i = 0;
*message = nullptr; *message = nullptr;
*faulty_bytes = 0; *faulty_bytes = 0;
while (i < len) while (i < len) {
{
if (str[i] == '\n') { if (str[i] == '\n') {
*message = nullptr; *message = nullptr;
return i; return i;
} }
if (str[i] <= 0x7F) /* 00..7F */ if (str[i] <= 0x7F) /* 00..7F */ {
{
i += 1; i += 1;
} } else if (str[i] >= 0xC2 && str[i] <= 0xDF) /* C2..DF 80..BF */ {
else if (str[i] >= 0xC2 && str[i] <= 0xDF) /* C2..DF 80..BF */ if (i + 1 < len) /* Expect a 2nd byte */ {
{ if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) {
if (i + 1 < len) /* Expect a 2nd byte */ *message
{ = "After a first byte between C2 and DF, expecting a "
if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) "2nd byte between 80 and BF";
{
*message = "After a first byte between C2 and DF, expecting a 2nd byte between 80 and BF";
*faulty_bytes = 2; *faulty_bytes = 2;
return i; return i;
} }
} } else {
else *message
{ = "After a first byte between C2 and DF, expecting a 2nd "
*message = "After a first byte between C2 and DF, expecting a 2nd byte."; "byte.";
*faulty_bytes = 1; *faulty_bytes = 1;
return i; return i;
} }
i += 2; i += 2;
} } else if (str[i] == 0xE0) /* E0 A0..BF 80..BF */ {
else if (str[i] == 0xE0) /* E0 A0..BF 80..BF */ if (i + 2 < len) /* Expect a 2nd and 3rd byte */ {
{ if (str[i + 1] < 0xA0 || str[i + 1] > 0xBF) {
if (i + 2 < len) /* Expect a 2nd and 3rd byte */ *message
{ = "After a first byte of E0, expecting a 2nd byte "
if (str[i + 1] < 0xA0 || str[i + 1] > 0xBF) "between A0 and BF.";
{
*message = "After a first byte of E0, expecting a 2nd byte between A0 and BF.";
*faulty_bytes = 2; *faulty_bytes = 2;
return i; return i;
} }
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
{ *message
*message = "After a first byte of E0, expecting a 3nd byte between 80 and BF."; = "After a first byte of E0, expecting a 3nd byte "
"between 80 and BF.";
*faulty_bytes = 3; *faulty_bytes = 3;
return i; return i;
} }
} } else {
else *message
{ = "After a first byte of E0, expecting two following "
*message = "After a first byte of E0, expecting two following bytes."; "bytes.";
*faulty_bytes = 1; *faulty_bytes = 1;
return i; return i;
} }
i += 3; i += 3;
} } else if (str[i] >= 0xE1 && str[i] <= 0xEC) /* E1..EC 80..BF 80..BF */
else if (str[i] >= 0xE1 && str[i] <= 0xEC) /* E1..EC 80..BF 80..BF */
{ {
if (i + 2 < len) /* Expect a 2nd and 3rd byte */ if (i + 2 < len) /* Expect a 2nd and 3rd byte */ {
{ if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) {
if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) *message
{ = "After a first byte between E1 and EC, expecting the "
*message = "After a first byte between E1 and EC, expecting the 2nd byte between 80 and BF."; "2nd byte between 80 and BF.";
*faulty_bytes = 2; *faulty_bytes = 2;
return i; return i;
} }
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
{ *message
*message = "After a first byte between E1 and EC, expecting the 3rd byte between 80 and BF."; = "After a first byte between E1 and EC, expecting the "
"3rd byte between 80 and BF.";
*faulty_bytes = 3; *faulty_bytes = 3;
return i; return i;
} }
} } else {
else *message
{ = "After a first byte between E1 and EC, expecting two "
*message = "After a first byte between E1 and EC, expecting two following bytes."; "following bytes.";
*faulty_bytes = 1; *faulty_bytes = 1;
return i; return i;
} }
i += 3; i += 3;
} } else if (str[i] == 0xED) /* ED 80..9F 80..BF */ {
else if (str[i] == 0xED) /* ED 80..9F 80..BF */ if (i + 2 < len) /* Expect a 2nd and 3rd byte */ {
{ if (str[i + 1] < 0x80 || str[i + 1] > 0x9F) {
if (i + 2 < len) /* Expect a 2nd and 3rd byte */ *message
{ = "After a first byte of ED, expecting 2nd byte "
if (str[i + 1] < 0x80 || str[i + 1] > 0x9F) "between 80 and 9F.";
{
*message = "After a first byte of ED, expecting 2nd byte between 80 and 9F.";
*faulty_bytes = 2; *faulty_bytes = 2;
return i; return i;
} }
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
{ *message
*message = "After a first byte of ED, expecting 3rd byte between 80 and BF."; = "After a first byte of ED, expecting 3rd byte "
"between 80 and BF.";
*faulty_bytes = 3; *faulty_bytes = 3;
return i; return i;
} }
} } else {
else *message
{ = "After a first byte of ED, expecting two following "
*message = "After a first byte of ED, expecting two following bytes."; "bytes.";
*faulty_bytes = 1; *faulty_bytes = 1;
return i; return i;
} }
i += 3; i += 3;
} } else if (str[i] >= 0xEE && str[i] <= 0xEF) /* EE..EF 80..BF 80..BF */
else if (str[i] >= 0xEE && str[i] <= 0xEF) /* EE..EF 80..BF 80..BF */
{ {
if (i + 2 < len) /* Expect a 2nd and 3rd byte */ if (i + 2 < len) /* Expect a 2nd and 3rd byte */ {
{ if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) {
if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) *message
{ = "After a first byte between EE and EF, expecting 2nd "
*message = "After a first byte between EE and EF, expecting 2nd byte between 80 and BF."; "byte between 80 and BF.";
*faulty_bytes = 2; *faulty_bytes = 2;
return i; return i;
} }
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
{ *message
*message = "After a first byte between EE and EF, expecting 3rd byte between 80 and BF."; = "After a first byte between EE and EF, expecting 3rd "
"byte between 80 and BF.";
*faulty_bytes = 3; *faulty_bytes = 3;
return i; return i;
} }
} } else {
else *message
{ = "After a first byte between EE and EF, two following "
*message = "After a first byte between EE and EF, two following bytes."; "bytes.";
*faulty_bytes = 1; *faulty_bytes = 1;
return i; return i;
} }
i += 3; i += 3;
} } else if (str[i] == 0xF0) /* F0 90..BF 80..BF 80..BF */ {
else if (str[i] == 0xF0) /* F0 90..BF 80..BF 80..BF */ if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ {
{ if (str[i + 1] < 0x90 || str[i + 1] > 0xBF) {
if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ *message
{ = "After a first byte of F0, expecting 2nd byte "
if (str[i + 1] < 0x90 || str[i + 1] > 0xBF) "between 90 and BF.";
{
*message = "After a first byte of F0, expecting 2nd byte between 90 and BF.";
*faulty_bytes = 2; *faulty_bytes = 2;
return i; return i;
} }
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
{ *message
*message = "After a first byte of F0, expecting 3rd byte between 80 and BF."; = "After a first byte of F0, expecting 3rd byte "
"between 80 and BF.";
*faulty_bytes = 3; *faulty_bytes = 3;
return i; return i;
} }
if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) {
{ *message
*message = "After a first byte of F0, expecting 4th byte between 80 and BF."; = "After a first byte of F0, expecting 4th byte "
"between 80 and BF.";
*faulty_bytes = 4; *faulty_bytes = 4;
return i; return i;
} }
} } else {
else *message
{ = "After a first byte of F0, expecting three following "
*message = "After a first byte of F0, expecting three following bytes."; "bytes.";
*faulty_bytes = 1; *faulty_bytes = 1;
return i; return i;
} }
i += 4; i += 4;
} } else if (str[i] >= 0xF1
else if (str[i] >= 0xF1 && str[i] <= 0xF3) /* F1..F3 80..BF 80..BF 80..BF */ && str[i] <= 0xF3) /* F1..F3 80..BF 80..BF 80..BF */ {
{ if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ {
if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) {
{ *message
if (str[i + 1] < 0x80 || str[i + 1] > 0xBF) = "After a first byte of F1, F2, or F3, expecting a "
{ "2nd byte between 80 and BF.";
*message = "After a first byte of F1, F2, or F3, expecting a 2nd byte between 80 and BF.";
*faulty_bytes = 2; *faulty_bytes = 2;
return i; return i;
} }
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
{ *message
*message = "After a first byte of F1, F2, or F3, expecting a 3rd byte between 80 and BF."; = "After a first byte of F1, F2, or F3, expecting a "
"3rd byte between 80 and BF.";
*faulty_bytes = 3; *faulty_bytes = 3;
return i; return i;
} }
if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) {
{ *message
*message = "After a first byte of F1, F2, or F3, expecting a 4th byte between 80 and BF."; = "After a first byte of F1, F2, or F3, expecting a "
"4th byte between 80 and BF.";
*faulty_bytes = 4; *faulty_bytes = 4;
return i; return i;
} }
} } else {
else *message
{ = "After a first byte of F1, F2, or F3, expecting three "
*message = "After a first byte of F1, F2, or F3, expecting three following bytes."; "following bytes.";
*faulty_bytes = 1; *faulty_bytes = 1;
return i; return i;
} }
i += 4; i += 4;
} } else if (str[i] == 0xF4) /* F4 80..8F 80..BF 80..BF */ {
else if (str[i] == 0xF4) /* F4 80..8F 80..BF 80..BF */ if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ {
{ if (str[i + 1] < 0x80 || str[i + 1] > 0x8F) {
if (i + 3 < len) /* Expect a 2nd, 3rd 3th byte */ *message
{ = "After a first byte of F4, expecting 2nd byte "
if (str[i + 1] < 0x80 || str[i + 1] > 0x8F) "between 80 and 8F.";
{
*message = "After a first byte of F4, expecting 2nd byte between 80 and 8F.";
*faulty_bytes = 2; *faulty_bytes = 2;
return i; return i;
} }
if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) if (str[i + 2] < 0x80 || str[i + 2] > 0xBF) {
{ *message
*message = "After a first byte of F4, expecting 3rd byte between 80 and BF."; = "After a first byte of F4, expecting 3rd byte "
"between 80 and BF.";
*faulty_bytes = 3; *faulty_bytes = 3;
return i; return i;
} }
if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) if (str[i + 3] < 0x80 || str[i + 3] > 0xBF) {
{ *message
*message = "After a first byte of F4, expecting 4th byte between 80 and BF."; = "After a first byte of F4, expecting 4th byte "
"between 80 and BF.";
*faulty_bytes = 4; *faulty_bytes = 4;
return i; return i;
} }
} } else {
else *message
{ = "After a first byte of F4, expecting three following "
*message = "After a first byte of F4, expecting three following bytes."; "bytes.";
*faulty_bytes = 1; *faulty_bytes = 1;
return i; return i;
} }
i += 4; i += 4;
} } else {
else *message
{ = "Expecting bytes in the following ranges: 00..7F C2..F4.";
*message = "Expecting bytes in the following ranges: 00..7F C2..F4.";
*faulty_bytes = 1; *faulty_bytes = 1;
return i; return i;
} }

@ -28,9 +28,12 @@
#ifndef _IS_UTF8_H #ifndef _IS_UTF8_H
#define _IS_UTF8_H #define _IS_UTF8_H
#include <sys/types.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h>
ssize_t is_utf8(unsigned char *str, size_t len, const char **message, int *faulty_bytes); ssize_t is_utf8(unsigned char* str,
size_t len,
const char** message,
int* faulty_bytes);
#endif /* _IS_UTF8_H */ #endif /* _IS_UTF8_H */

@ -21,30 +21,32 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file isc.cc * @file isc.cc
*/ */
#include "config.h"
#include <algorithm> #include <algorithm>
#include "isc.hh" #include "isc.hh"
#include "config.h"
namespace isc { namespace isc {
void service_base::start() void
service_base::start()
{ {
log_debug("starting service thread for: %s", this->s_name.c_str()); log_debug("starting service thread for: %s", this->s_name.c_str());
this->s_thread = std::thread(&service_base::run, this); this->s_thread = std::thread(&service_base::run, this);
this->s_started = true; this->s_started = true;
} }
void *service_base::run() void*
service_base::run()
{ {
log_info("BEGIN isc thread: %s", this->s_name.c_str()); log_info("BEGIN isc thread: %s", this->s_name.c_str());
while (this->s_looping) { while (this->s_looping) {
@ -66,7 +68,8 @@ void *service_base::run()
return nullptr; return nullptr;
} }
void service_base::stop() void
service_base::stop()
{ {
if (this->s_started) { if (this->s_started) {
log_debug("stopping service thread: %s", this->s_name.c_str()); log_debug("stopping service thread: %s", this->s_name.c_str());
@ -81,8 +84,9 @@ void service_base::stop()
} }
} }
supervisor::supervisor(service_list servs, service_base *parent) supervisor::supervisor(service_list servs, service_base* parent)
: s_service_list(std::move(servs)), s_parent(parent) { : s_service_list(std::move(servs)), s_parent(parent)
{
for (auto& serv : this->s_service_list) { for (auto& serv : this->s_service_list) {
serv->start(); serv->start();
} }
@ -93,7 +97,8 @@ supervisor::~supervisor()
this->stop_children(); this->stop_children();
} }
void supervisor::stop_children() void
supervisor::stop_children()
{ {
for (auto& serv : this->s_service_list) { for (auto& serv : this->s_service_list) {
serv->stop(); serv->stop();
@ -101,29 +106,31 @@ void supervisor::stop_children()
this->cleanup_children(); this->cleanup_children();
} }
void supervisor::cleanup_children() void
supervisor::cleanup_children()
{ {
this->s_service_list.erase( this->s_service_list.erase(
std::remove_if( std::remove_if(this->s_service_list.begin(),
this->s_service_list.begin(), this->s_service_list.end(), this->s_service_list.end(),
[this](auto &child) { [this](auto& child) {
if (child->is_looping()) { if (child->is_looping()) {
return false; return false;
} }
child->stop(); child->stop();
if (this->s_parent != nullptr) { if (this->s_parent != nullptr) {
this->s_parent->child_finished(child); this->s_parent->child_finished(child);
} }
return true; return true;
}), }),
this->s_service_list.end()); this->s_service_list.end());
} }
void supervisor::add_child_service(std::shared_ptr<service_base> new_service) void
supervisor::add_child_service(std::shared_ptr<service_base> new_service)
{ {
this->s_service_list.emplace_back(new_service); this->s_service_list.emplace_back(new_service);
new_service->start(); new_service->start();
} }
} } // namespace isc

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -30,19 +30,19 @@
*/ */
#include <atomic> #include <atomic>
#include <deque>
#include <chrono> #include <chrono>
#include <condition_variable>
#include <deque>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include <condition_variable>
#include <utility> #include <utility>
#include "injector.hh" #include "injector.hh"
#include "time_util.hh"
#include "safe/safe.h" #include "safe/safe.h"
#include "time_util.hh"
#ifndef lnav_isc_hh #ifndef lnav_isc_hh
#define lnav_isc_hh # define lnav_isc_hh
namespace isc { namespace isc {
@ -50,28 +50,33 @@ struct msg {
std::function<void()> m_callback; std::function<void()> m_callback;
}; };
inline msg empty_msg() { inline msg
return { []() { } }; empty_msg()
{
return {[]() {}};
} }
class msg_port { class msg_port {
public: public:
msg_port() = default; msg_port() = default;
void send(msg&& m) { void send(msg&& m)
safe::WriteAccess<safe_message_list, std::unique_lock> writable_msgs(this->mp_messages); {
safe::WriteAccess<safe_message_list, std::unique_lock> writable_msgs(
this->mp_messages);
writable_msgs->emplace_back(m); writable_msgs->emplace_back(m);
this->sp_cond.notify_all(); this->sp_cond.notify_all();
} }
template<class Rep, class Period> template<class Rep, class Period>
void process_for(const std::chrono::duration<Rep, Period>& rel_time) { void process_for(const std::chrono::duration<Rep, Period>& rel_time)
{
std::deque<msg> tmp_msgs; std::deque<msg> tmp_msgs;
{ {
safe::WriteAccess<safe_message_list, std::unique_lock> writable_msgs( safe::WriteAccess<safe_message_list, std::unique_lock>
this->mp_messages); writable_msgs(this->mp_messages);
if (writable_msgs->empty()) { if (writable_msgs->empty()) {
this->sp_cond.template wait_for(writable_msgs.lock, rel_time); this->sp_cond.template wait_for(writable_msgs.lock, rel_time);
@ -80,12 +85,13 @@ public:
tmp_msgs.swap(*writable_msgs); tmp_msgs.swap(*writable_msgs);
} }
while (!tmp_msgs.empty()) { while (!tmp_msgs.empty()) {
auto &m = tmp_msgs.front(); auto& m = tmp_msgs.front();
m.m_callback(); m.m_callback();
tmp_msgs.pop_front(); tmp_msgs.pop_front();
} }
} }
private: private:
using message_list = std::deque<msg>; using message_list = std::deque<msg>;
using safe_message_list = safe::Safe<message_list>; using safe_message_list = safe::Safe<message_list>;
@ -99,11 +105,12 @@ using service_list = std::vector<std::shared_ptr<service_base>>;
struct supervisor { struct supervisor {
explicit supervisor(service_list servs = {}, explicit supervisor(service_list servs = {},
service_base *parent = nullptr); service_base* parent = nullptr);
~supervisor(); ~supervisor();
bool empty() const { bool empty() const
{
return this->s_service_list.empty(); return this->s_service_list.empty();
} }
@ -112,39 +119,46 @@ struct supervisor {
void stop_children(); void stop_children();
void cleanup_children(); void cleanup_children();
protected: protected:
service_list s_service_list; service_list s_service_list;
service_base *s_parent; service_base* s_parent;
}; };
class service_base : public std::enable_shared_from_this<service_base> { class service_base : public std::enable_shared_from_this<service_base> {
public: public:
explicit service_base(std::string name) explicit service_base(std::string name)
: s_name(std::move(name)), s_children({}, this) { : s_name(std::move(name)), s_children({}, this)
{
} }
virtual ~service_base() = default; virtual ~service_base() = default;
bool is_looping() const { bool is_looping() const
{
return this->s_looping; return this->s_looping;
} }
msg_port& get_port() { msg_port& get_port()
{
return this->s_port; return this->s_port;
} }
friend supervisor; friend supervisor;
private: private:
void start(); void start();
void stop(); void stop();
protected: protected:
virtual void *run(); virtual void* run();
virtual void loop_body() {}; virtual void loop_body(){};
virtual void child_finished(std::shared_ptr<service_base> child) {}; virtual void child_finished(std::shared_ptr<service_base> child){};
virtual void stopped() {}; virtual void stopped(){};
virtual std::chrono::milliseconds compute_timeout(mstime_t current_time) const { virtual std::chrono::milliseconds compute_timeout(
mstime_t current_time) const
{
using namespace std::literals::chrono_literals; using namespace std::literals::chrono_literals;
return 1s; return 1s;
@ -162,36 +176,37 @@ template<typename T>
class service : public service_base { class service : public service_base {
public: public:
explicit service(std::string sub_name = "") explicit service(std::string sub_name = "")
: service_base(std::string(__PRETTY_FUNCTION__) + " " + sub_name) { : service_base(std::string(__PRETTY_FUNCTION__) + " " + sub_name)
{
} }
template<typename F> template<typename F>
void send(F msg) { void send(F msg)
this->s_port.send({ {
[lifetime = this->shared_from_this(), this, msg]() { this->s_port.send({[lifetime = this->shared_from_this(), this, msg]() {
msg(*(static_cast<T *>(this))); msg(*(static_cast<T*>(this)));
} }});
});
} }
template<typename F, class Rep, class Period> template<typename F, class Rep, class Period>
void send_and_wait(F msg, void send_and_wait(F msg,
const std::chrono::duration<Rep, Period>& rel_time) { const std::chrono::duration<Rep, Period>& rel_time)
{
msg_port reply_port; msg_port reply_port;
this->s_port.send({ this->s_port.send(
[lifetime = this->shared_from_this(), this, &reply_port, msg]() { {[lifetime = this->shared_from_this(), this, &reply_port, msg]() {
msg(*(static_cast<T *>(this))); msg(*(static_cast<T*>(this)));
reply_port.send(empty_msg()); reply_port.send(empty_msg());
} }});
});
reply_port.template process_for(rel_time); reply_port.template process_for(rel_time);
} }
}; };
template<typename T, typename Service, typename...Annotations> template<typename T, typename Service, typename... Annotations>
struct to { struct to {
void send(std::function<void(T&)> cb) { void send(std::function<void(T&)> cb)
{
auto& service = injector::get<T&, Service>(); auto& service = injector::get<T&, Service>();
service.send(cb); service.send(cb);
@ -199,19 +214,21 @@ struct to {
template<class Rep, class Period> template<class Rep, class Period>
void send_and_wait(std::function<void(T)> cb, void send_and_wait(std::function<void(T)> cb,
const std::chrono::duration<Rep, Period>& rel_time) { const std::chrono::duration<Rep, Period>& rel_time)
{
auto& service = injector::get<T&, Service>(); auto& service = injector::get<T&, Service>();
service.send_and_wait(cb, rel_time); service.send_and_wait(cb, rel_time);
} }
void send_and_wait(std::function<void(T)> cb) { void send_and_wait(std::function<void(T)> cb)
{
using namespace std::literals::chrono_literals; using namespace std::literals::chrono_literals;
this->send_and_wait(cb, 48h); this->send_and_wait(cb, 48h);
} }
}; };
} } // namespace isc
#endif #endif

@ -21,30 +21,32 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file lnav.gzip.cc * @file lnav.gzip.cc
*/ */
#include "config.h" #include "lnav.gzip.hh"
#include <zlib.h> #include <zlib.h>
#include "config.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "lnav.gzip.hh"
namespace lnav { namespace lnav {
namespace gzip { namespace gzip {
bool is_gzipped(const char *buffer, size_t len) bool
is_gzipped(const char* buffer, size_t len)
{ {
return len > 2 && buffer[0] == '\037' && buffer[1] == '\213'; return len > 2 && buffer[0] == '\037' && buffer[1] == '\213';
} }
Result<auto_buffer, std::string> compress(const void* input, size_t len) Result<auto_buffer, std::string>
compress(const void* input, size_t len)
{ {
auto retval = auto_buffer::alloc(len + 4096); auto retval = auto_buffer::alloc(len + 4096);
@ -52,14 +54,16 @@ Result<auto_buffer, std::string> compress(const void* input, size_t len)
zs.zalloc = Z_NULL; zs.zalloc = Z_NULL;
zs.zfree = Z_NULL; zs.zfree = Z_NULL;
zs.opaque = Z_NULL; zs.opaque = Z_NULL;
zs.avail_in = (uInt)len; zs.avail_in = (uInt) len;
zs.next_in = (Bytef *)input; zs.next_in = (Bytef*) input;
zs.avail_out = (uInt)retval.size(); zs.avail_out = (uInt) retval.size();
zs.next_out = (Bytef *)retval.in(); zs.next_out = (Bytef*) retval.in();
auto rc = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY); auto rc = deflateInit2(
&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
if (rc != Z_OK) { if (rc != Z_OK) {
return Err(fmt::format("unable to initialize compressor -- {}", zError(rc))); return Err(
fmt::format("unable to initialize compressor -- {}", zError(rc)));
} }
rc = deflate(&zs, Z_FINISH); rc = deflate(&zs, Z_FINISH);
if (rc != Z_STREAM_END) { if (rc != Z_STREAM_END) {
@ -67,20 +71,20 @@ Result<auto_buffer, std::string> compress(const void* input, size_t len)
} }
rc = deflateEnd(&zs); rc = deflateEnd(&zs);
if (rc != Z_OK) { if (rc != Z_OK) {
return Err(fmt::format("unable to finalize compression -- {}", zError(rc))); return Err(
fmt::format("unable to finalize compression -- {}", zError(rc)));
} }
return Ok(std::move(retval.shrink_to(zs.total_out))); return Ok(std::move(retval.shrink_to(zs.total_out)));
} }
Result<auto_buffer, std::string> uncompress(const std::string& src, Result<auto_buffer, std::string>
const void *buffer, uncompress(const std::string& src, const void* buffer, size_t size)
size_t size)
{ {
auto uncomp = auto_buffer::alloc(size * 2); auto uncomp = auto_buffer::alloc(size * 2);
z_stream strm; z_stream strm;
int err; int err;
strm.next_in = (Bytef *) buffer; strm.next_in = (Bytef*) buffer;
strm.avail_in = size; strm.avail_in = size;
strm.total_out = 0; strm.total_out = 0;
strm.zalloc = Z_NULL; strm.zalloc = Z_NULL;
@ -88,7 +92,8 @@ Result<auto_buffer, std::string> uncompress(const std::string& src,
if ((err = inflateInit2(&strm, (16 + MAX_WBITS))) != Z_OK) { if ((err = inflateInit2(&strm, (16 + MAX_WBITS))) != Z_OK) {
return Err(fmt::format("invalid gzip data: {} -- {}", return Err(fmt::format("invalid gzip data: {} -- {}",
src, strm.msg ? strm.msg : zError(err))); src,
strm.msg ? strm.msg : zError(err)));
} }
bool done = false; bool done = false;
@ -98,7 +103,7 @@ Result<auto_buffer, std::string> uncompress(const std::string& src,
uncomp.expand_by(size / 2); uncomp.expand_by(size / 2);
} }
strm.next_out = (Bytef *) (uncomp.in() + strm.total_out); strm.next_out = (Bytef*) (uncomp.in() + strm.total_out);
strm.avail_out = uncomp.size() - strm.total_out; strm.avail_out = uncomp.size() - strm.total_out;
// Inflate another chunk. // Inflate another chunk.
@ -108,17 +113,19 @@ Result<auto_buffer, std::string> uncompress(const std::string& src,
} else if (err != Z_OK) { } else if (err != Z_OK) {
inflateEnd(&strm); inflateEnd(&strm);
return Err(fmt::format("unable to uncompress: {} -- {}", return Err(fmt::format("unable to uncompress: {} -- {}",
src, strm.msg ? strm.msg : zError(err))); src,
strm.msg ? strm.msg : zError(err)));
} }
} }
if (inflateEnd(&strm) != Z_OK) { if (inflateEnd(&strm) != Z_OK) {
return Err(fmt::format("unable to uncompress: {} -- {}", return Err(fmt::format("unable to uncompress: {} -- {}",
src, strm.msg ? strm.msg : zError(err))); src,
strm.msg ? strm.msg : zError(err)));
} }
return Ok(std::move(uncomp.shrink_to(strm.total_out))); return Ok(std::move(uncomp.shrink_to(strm.total_out)));
} }
} } // namespace gzip
} } // namespace lnav

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -40,15 +40,15 @@
namespace lnav { namespace lnav {
namespace gzip { namespace gzip {
bool is_gzipped(const char *buffer, size_t len); bool is_gzipped(const char* buffer, size_t len);
Result<auto_buffer, std::string> compress(const void *input, size_t len); Result<auto_buffer, std::string> compress(const void* input, size_t len);
Result<auto_buffer, std::string> uncompress(const std::string& src, Result<auto_buffer, std::string> uncompress(const std::string& src,
const void *buffer, const void* buffer,
size_t size); size_t size);
} } // namespace gzip
} } // namespace lnav
#endif #endif

@ -21,35 +21,35 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <zlib.h>
#include <iostream> #include <iostream>
#include "doctest/doctest.h" #include <zlib.h>
#include "base/lnav.gzip.hh" #include "base/lnav.gzip.hh"
#include "config.h"
#include "doctest/doctest.h"
TEST_CASE("lnav::gzip::uncompress") { TEST_CASE("lnav::gzip::uncompress")
{
{ {
auto u_res = lnav::gzip::uncompress("empty", nullptr, 0); auto u_res = lnav::gzip::uncompress("empty", nullptr, 0);
CHECK(u_res.isErr()); CHECK(u_res.isErr());
CHECK(u_res.unwrapErr() == CHECK(u_res.unwrapErr()
"unable to uncompress: empty -- buffer error"); == "unable to uncompress: empty -- buffer error");
} }
{ {
auto u_res = lnav::gzip::uncompress("garbage", "abc", 3); auto u_res = lnav::gzip::uncompress("garbage", "abc", 3);
CHECK(u_res.isErr()); CHECK(u_res.isErr());
CHECK(u_res.unwrapErr() == CHECK(u_res.unwrapErr()
"unable to uncompress: garbage -- incorrect header check"); == "unable to uncompress: garbage -- incorrect header check");
} }
} }

@ -21,101 +21,104 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file lnav_log.cc * @file lnav_log.cc
*/ */
#include "config.h" #include <assert.h>
#include <time.h>
#include <ctype.h> #include <ctype.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <signal.h>
#include <assert.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <signal.h>
#include <termios.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#include "config.h"
#ifdef HAVE_EXECINFO_H #ifdef HAVE_EXECINFO_H
#include <execinfo.h> # include <execinfo.h>
#endif #endif
#if BACKWARD_HAS_DW == 1 #if BACKWARD_HAS_DW == 1
#include "backward-cpp/backward.hpp" # include "backward-cpp/backward.hpp"
#endif #endif
#include <sys/types.h> #include <algorithm>
#include <sys/time.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <mutex> #include <mutex>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <algorithm>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#ifdef HAVE_PCRE_H #ifdef HAVE_PCRE_H
#include <pcre.h> # include <pcre.h>
#elif HAVE_PCRE_PCRE_H #elif HAVE_PCRE_PCRE_H
#include <pcre/pcre.h> # include <pcre/pcre.h>
#else #else
#error "pcre.h not found?" # error "pcre.h not found?"
#endif #endif
#if defined HAVE_NCURSESW_CURSES_H #if defined HAVE_NCURSESW_CURSES_H
# include <ncursesw/termcap.h> # include <ncursesw/curses.h>
# include <ncursesw/curses.h> # include <ncursesw/termcap.h>
#elif defined HAVE_NCURSESW_H #elif defined HAVE_NCURSESW_H
# include <termcap.h> # include <ncursesw.h>
# include <ncursesw.h> # include <termcap.h>
#elif defined HAVE_NCURSES_CURSES_H #elif defined HAVE_NCURSES_CURSES_H
# include <ncurses/termcap.h> # include <ncurses/curses.h>
# include <ncurses/curses.h> # include <ncurses/termcap.h>
#elif defined HAVE_NCURSES_H #elif defined HAVE_NCURSES_H
# include <termcap.h> # include <ncurses.h>
# include <ncurses.h> # include <termcap.h>
#elif defined HAVE_CURSES_H #elif defined HAVE_CURSES_H
# include <termcap.h> # include <curses.h>
# include <curses.h> # include <termcap.h>
#else #else
# error "SysV or X/Open-compatible Curses header file required" # error "SysV or X/Open-compatible Curses header file required"
#endif #endif
#include "opt_util.hh"
#include "lnav_log.hh"
#include "enum_util.hh"
#include "auto_mem.hh" #include "auto_mem.hh"
#include "enum_util.hh"
#include "lnav_log.hh"
#include "opt_util.hh"
static const size_t BUFFER_SIZE = 256 * 1024; static const size_t BUFFER_SIZE = 256 * 1024;
static const size_t MAX_LOG_LINE_SIZE = 2048; static const size_t MAX_LOG_LINE_SIZE = 2048;
static const char *CRASH_MSG = static const char* CRASH_MSG
"\n" = "\n"
"\n" "\n"
"==== GURU MEDITATION ====\n" "==== GURU MEDITATION ====\n"
"Unfortunately, lnav has crashed, sorry for the inconvenience.\n" "Unfortunately, lnav has crashed, sorry for the inconvenience.\n"
"\n" "\n"
"You can help improve lnav by sending the following file to " PACKAGE_BUGREPORT " :\n" "You can help improve lnav by sending the following file "
" %s\n" "to " PACKAGE_BUGREPORT
"=========================\n"; " :\n"
" %s\n"
nonstd::optional<FILE *> lnav_log_file; "=========================\n";
nonstd::optional<FILE*> lnav_log_file;
lnav_log_level_t lnav_log_level = lnav_log_level_t::DEBUG; lnav_log_level_t lnav_log_level = lnav_log_level_t::DEBUG;
const char *lnav_log_crash_dir; const char* lnav_log_crash_dir;
nonstd::optional<const struct termios *> lnav_log_orig_termios; nonstd::optional<const struct termios*> lnav_log_orig_termios;
// NOTE: This mutex is leaked so that it is not destroyed during exit. // NOTE: This mutex is leaked so that it is not destroyed during exit.
// Otherwise, any attempts to log will fail. // Otherwise, any attempts to log will fail.
static std::mutex *lnav_log_mutex = new std::mutex(); static std::mutex* lnav_log_mutex = new std::mutex();
static std::vector<log_state_dumper*>& DUMPER_LIST() static std::vector<log_state_dumper*>&
DUMPER_LIST()
{ {
static std::vector<log_state_dumper*> retval; static std::vector<log_state_dumper*> retval;
@ -141,14 +144,9 @@ static struct {
off_t lr_frag_start; off_t lr_frag_start;
off_t lr_frag_end; off_t lr_frag_end;
char lr_data[BUFFER_SIZE]; char lr_data[BUFFER_SIZE];
} log_ring = { } log_ring = {0, BUFFER_SIZE, 0, {}};
0,
BUFFER_SIZE,
0,
{}
};
static const char *LEVEL_NAMES[] = { static const char* LEVEL_NAMES[] = {
"T", "T",
"D", "D",
"I", "I",
@ -156,38 +154,40 @@ static const char *LEVEL_NAMES[] = {
"E", "E",
}; };
static char *log_alloc() static char*
log_alloc()
{ {
off_t data_end = log_ring.lr_length + MAX_LOG_LINE_SIZE; off_t data_end = log_ring.lr_length + MAX_LOG_LINE_SIZE;
if (data_end >= (off_t)BUFFER_SIZE) { if (data_end >= (off_t) BUFFER_SIZE) {
const char *new_start = &log_ring.lr_data[MAX_LOG_LINE_SIZE]; const char* new_start = &log_ring.lr_data[MAX_LOG_LINE_SIZE];
new_start = (const char *)memchr( new_start = (const char*) memchr(
new_start, '\n', log_ring.lr_length - MAX_LOG_LINE_SIZE); new_start, '\n', log_ring.lr_length - MAX_LOG_LINE_SIZE);
log_ring.lr_frag_start = new_start - log_ring.lr_data; log_ring.lr_frag_start = new_start - log_ring.lr_data;
log_ring.lr_frag_end = log_ring.lr_length; log_ring.lr_frag_end = log_ring.lr_length;
log_ring.lr_length = 0; log_ring.lr_length = 0;
assert(log_ring.lr_frag_start >= 0); assert(log_ring.lr_frag_start >= 0);
assert(log_ring.lr_frag_start <= (off_t)BUFFER_SIZE); assert(log_ring.lr_frag_start <= (off_t) BUFFER_SIZE);
} else if (data_end >= log_ring.lr_frag_start) { } else if (data_end >= log_ring.lr_frag_start) {
const char *new_start = &log_ring.lr_data[log_ring.lr_frag_start]; const char* new_start = &log_ring.lr_data[log_ring.lr_frag_start];
new_start = (const char *)memchr( new_start = (const char*) memchr(
new_start, '\n', log_ring.lr_frag_end - log_ring.lr_frag_start); new_start, '\n', log_ring.lr_frag_end - log_ring.lr_frag_start);
assert(new_start != nullptr); assert(new_start != nullptr);
log_ring.lr_frag_start = new_start - log_ring.lr_data; log_ring.lr_frag_start = new_start - log_ring.lr_data;
assert(log_ring.lr_frag_start >= 0); assert(log_ring.lr_frag_start >= 0);
assert(log_ring.lr_frag_start <= (off_t)BUFFER_SIZE); assert(log_ring.lr_frag_start <= (off_t) BUFFER_SIZE);
} }
return &log_ring.lr_data[log_ring.lr_length]; return &log_ring.lr_data[log_ring.lr_length];
} }
void log_argv(int argc, char *argv[]) void
log_argv(int argc, char* argv[])
{ {
const char *log_path = getenv("LNAV_LOG_PATH"); const char* log_path = getenv("LNAV_LOG_PATH");
if (log_path != nullptr) { if (log_path != nullptr) {
lnav_log_file = make_optional_from_nullable(fopen(log_path, "a")); lnav_log_file = make_optional_from_nullable(fopen(log_path, "a"));
@ -199,15 +199,17 @@ void log_argv(int argc, char *argv[])
} }
} }
void log_set_thread_prefix(std::string prefix) void
log_set_thread_prefix(std::string prefix)
{ {
// thread_log_prefix = std::move(prefix); // thread_log_prefix = std::move(prefix);
} }
void log_host_info() void
log_host_info()
{ {
char cwd[MAXPATHLEN]; char cwd[MAXPATHLEN];
const char *jittarget; const char* jittarget;
struct utsname un; struct utsname un;
struct rusage ru; struct rusage ru;
int pcre_jit; int pcre_jit;
@ -242,8 +244,7 @@ void log_host_info()
log_info(" egid=%d", getegid()); log_info(" egid=%d", getegid());
if (getcwd(cwd, sizeof(cwd)) == nullptr) { if (getcwd(cwd, sizeof(cwd)) == nullptr) {
log_info(" ERROR: getcwd failed"); log_info(" ERROR: getcwd failed");
} } else {
else {
log_info(" cwd=%s", cwd); log_info(" cwd=%s", cwd);
} }
log_info("Executable:"); log_info("Executable:");
@ -253,13 +254,25 @@ void log_host_info()
log_rusage(lnav_log_level_t::INFO, ru); log_rusage(lnav_log_level_t::INFO, ru);
} }
void log_rusage_raw(enum lnav_log_level_t level, const char *src_file, int line_number, const struct rusage &ru) void
log_rusage_raw(enum lnav_log_level_t level,
const char* src_file,
int line_number,
const struct rusage& ru)
{ {
log_msg(level, src_file, line_number, "rusage:"); log_msg(level, src_file, line_number, "rusage:");
log_msg(level, src_file, line_number, " utime=%d.%06d", log_msg(level,
ru.ru_utime.tv_sec, ru.ru_utime.tv_usec); src_file,
log_msg(level, src_file, line_number, " stime=%d.%06d", line_number,
ru.ru_stime.tv_sec, ru.ru_stime.tv_usec); " utime=%d.%06d",
ru.ru_utime.tv_sec,
ru.ru_utime.tv_usec);
log_msg(level,
src_file,
line_number,
" stime=%d.%06d",
ru.ru_stime.tv_sec,
ru.ru_stime.tv_usec);
log_msg(level, src_file, line_number, " maxrss=%ld", ru.ru_maxrss); log_msg(level, src_file, line_number, " maxrss=%ld", ru.ru_maxrss);
log_msg(level, src_file, line_number, " ixrss=%ld", ru.ru_ixrss); log_msg(level, src_file, line_number, " ixrss=%ld", ru.ru_ixrss);
log_msg(level, src_file, line_number, " idrss=%ld", ru.ru_idrss); log_msg(level, src_file, line_number, " idrss=%ld", ru.ru_idrss);
@ -276,8 +289,12 @@ void log_rusage_raw(enum lnav_log_level_t level, const char *src_file, int line_
log_msg(level, src_file, line_number, " nivcsw=%ld", ru.ru_nivcsw); log_msg(level, src_file, line_number, " nivcsw=%ld", ru.ru_nivcsw);
} }
void log_msg(lnav_log_level_t level, const char *src_file, int line_number, void
const char *fmt, ...) log_msg(lnav_log_level_t level,
const char* src_file,
int line_number,
const char* fmt,
...)
{ {
struct timeval curr_time; struct timeval curr_time;
struct tm localtm; struct tm localtm;
@ -294,7 +311,7 @@ void log_msg(lnav_log_level_t level, const char *src_file, int line_number,
{ {
// get the base name of the file. NB: can't use basename() since it // get the base name of the file. NB: can't use basename() since it
// can modify its argument // can modify its argument
const char *last_slash = src_file; const char* last_slash = src_file;
for (int lpc = 0; src_file[lpc]; lpc++) { for (int lpc = 0; src_file[lpc]; lpc++) {
if (src_file[lpc] == '/' || src_file[lpc] == '\\') { if (src_file[lpc] == '/' || src_file[lpc] == '\\') {
@ -309,20 +326,20 @@ void log_msg(lnav_log_level_t level, const char *src_file, int line_number,
gettimeofday(&curr_time, nullptr); gettimeofday(&curr_time, nullptr);
localtime_r(&curr_time.tv_sec, &localtm); localtime_r(&curr_time.tv_sec, &localtm);
auto line = log_alloc(); auto line = log_alloc();
prefix_size = snprintf( prefix_size = snprintf(line,
line, MAX_LOG_LINE_SIZE, MAX_LOG_LINE_SIZE,
"%4d-%02d-%02dT%02d:%02d:%02d.%03d %s t%u %s:%d ", "%4d-%02d-%02dT%02d:%02d:%02d.%03d %s t%u %s:%d ",
localtm.tm_year + 1900, localtm.tm_year + 1900,
localtm.tm_mon + 1, localtm.tm_mon + 1,
localtm.tm_mday, localtm.tm_mday,
localtm.tm_hour, localtm.tm_hour,
localtm.tm_min, localtm.tm_min,
localtm.tm_sec, localtm.tm_sec,
(int)(curr_time.tv_usec / 1000), (int) (curr_time.tv_usec / 1000),
LEVEL_NAMES[lnav::enums::to_underlying(level)], LEVEL_NAMES[lnav::enums::to_underlying(level)],
current_thid.t_id, current_thid.t_id,
src_file, src_file,
line_number); line_number);
#if 0 #if 0
if (!thread_log_prefix.empty()) { if (!thread_log_prefix.empty()) {
prefix_size += snprintf( prefix_size += snprintf(
@ -331,9 +348,9 @@ void log_msg(lnav_log_level_t level, const char *src_file, int line_number,
thread_log_prefix.c_str()); thread_log_prefix.c_str());
} }
#endif #endif
rc = vsnprintf(&line[prefix_size], MAX_LOG_LINE_SIZE - prefix_size, rc = vsnprintf(
fmt, args); &line[prefix_size], MAX_LOG_LINE_SIZE - prefix_size, fmt, args);
if (rc >= (ssize_t)(MAX_LOG_LINE_SIZE - prefix_size)) { if (rc >= (ssize_t) (MAX_LOG_LINE_SIZE - prefix_size)) {
rc = MAX_LOG_LINE_SIZE - prefix_size - 1; rc = MAX_LOG_LINE_SIZE - prefix_size - 1;
} }
line[prefix_size + rc] = '\n'; line[prefix_size + rc] = '\n';
@ -345,7 +362,8 @@ void log_msg(lnav_log_level_t level, const char *src_file, int line_number,
va_end(args); va_end(args);
} }
void log_msg_extra(const char *fmt, ...) void
log_msg_extra(const char* fmt, ...)
{ {
std::lock_guard<std::mutex> mg(*lnav_log_mutex); std::lock_guard<std::mutex> mg(*lnav_log_mutex);
va_list args; va_list args;
@ -361,7 +379,8 @@ void log_msg_extra(const char *fmt, ...)
va_end(args); va_end(args);
} }
void log_msg_extra_complete() void
log_msg_extra_complete()
{ {
std::lock_guard<std::mutex> mg(*lnav_log_mutex); std::lock_guard<std::mutex> mg(*lnav_log_mutex);
auto line = log_alloc(); auto line = log_alloc();
@ -375,13 +394,14 @@ void log_msg_extra_complete()
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result" #pragma GCC diagnostic ignored "-Wunused-result"
static void sigabrt(int sig, siginfo_t *info, void *ctx) static void
sigabrt(int sig, siginfo_t* info, void* ctx)
{ {
char crash_path[1024], latest_crash_path[1024]; char crash_path[1024], latest_crash_path[1024];
int fd; int fd;
#ifdef HAVE_EXECINFO_H #ifdef HAVE_EXECINFO_H
int frame_count; int frame_count;
void *frames[128]; void* frames[128];
#endif #endif
struct tm localtm; struct tm localtm;
time_t curr_time; time_t curr_time;
@ -398,45 +418,53 @@ static void sigabrt(int sig, siginfo_t *info, void *ctx)
#endif #endif
curr_time = time(nullptr); curr_time = time(nullptr);
localtime_r(&curr_time, &localtm); localtime_r(&curr_time, &localtm);
snprintf(crash_path, sizeof(crash_path), snprintf(crash_path,
"%s/crash-%4d-%02d-%02d-%02d-%02d-%02d.%d.log", sizeof(crash_path),
lnav_log_crash_dir, "%s/crash-%4d-%02d-%02d-%02d-%02d-%02d.%d.log",
localtm.tm_year + 1900, lnav_log_crash_dir,
localtm.tm_mon + 1, localtm.tm_year + 1900,
localtm.tm_mday, localtm.tm_mon + 1,
localtm.tm_hour, localtm.tm_mday,
localtm.tm_min, localtm.tm_hour,
localtm.tm_sec, localtm.tm_min,
getpid()); localtm.tm_sec,
snprintf(latest_crash_path, sizeof(latest_crash_path), getpid());
"%s/latest-crash.log", lnav_log_crash_dir); snprintf(latest_crash_path,
if ((fd = open(crash_path, O_CREAT|O_TRUNC|O_RDWR, 0600)) != -1) { sizeof(latest_crash_path),
if (log_ring.lr_frag_start < (off_t)BUFFER_SIZE) { "%s/latest-crash.log",
(void)write(fd, &log_ring.lr_data[log_ring.lr_frag_start], lnav_log_crash_dir);
log_ring.lr_frag_end - log_ring.lr_frag_start); if ((fd = open(crash_path, O_CREAT | O_TRUNC | O_RDWR, 0600)) != -1) {
if (log_ring.lr_frag_start < (off_t) BUFFER_SIZE) {
(void) write(fd,
&log_ring.lr_data[log_ring.lr_frag_start],
log_ring.lr_frag_end - log_ring.lr_frag_start);
} }
(void)write(fd, log_ring.lr_data, log_ring.lr_length); (void) write(fd, log_ring.lr_data, log_ring.lr_length);
#ifdef HAVE_EXECINFO_H #ifdef HAVE_EXECINFO_H
backtrace_symbols_fd(frames, frame_count, fd); backtrace_symbols_fd(frames, frame_count, fd);
#endif #endif
#if BACKWARD_HAS_DW == 1 #if BACKWARD_HAS_DW == 1
{ {
ucontext_t *uctx = static_cast<ucontext_t *>(ctx); ucontext_t* uctx = static_cast<ucontext_t*>(ctx);
void *error_addr = nullptr; void* error_addr = nullptr;
#ifdef REG_RIP // x86_64 # ifdef REG_RIP // x86_64
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.gregs[REG_RIP]); error_addr
#elif defined(REG_EIP) // x86_32 = reinterpret_cast<void*>(uctx->uc_mcontext.gregs[REG_RIP]);
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.gregs[REG_EIP]); # elif defined(REG_EIP) // x86_32
#endif error_addr
= reinterpret_cast<void*>(uctx->uc_mcontext.gregs[REG_EIP]);
# endif
backward::StackTrace st; backward::StackTrace st;
if (error_addr) { if (error_addr) {
st.load_from(error_addr, 32, reinterpret_cast<void *>(uctx), st.load_from(error_addr,
32,
reinterpret_cast<void*>(uctx),
info->si_addr); info->si_addr);
} else { } else {
st.load_here(32, reinterpret_cast<void *>(uctx), info->si_addr); st.load_here(32, reinterpret_cast<void*>(uctx), info->si_addr);
} }
backward::TraceResolver tr; backward::TraceResolver tr;
@ -445,7 +473,8 @@ static void sigabrt(int sig, siginfo_t *info, void *ctx)
auto trace = tr.resolve(st[lpc]); auto trace = tr.resolve(st[lpc]);
char buf[1024]; char buf[1024];
snprintf(buf, sizeof(buf), snprintf(buf,
sizeof(buf),
"Frame %lu:%s:%s (%s:%d)\n", "Frame %lu:%s:%s (%s:%d)\n",
lpc, lpc,
trace.object_filename.c_str(), trace.object_filename.c_str(),
@ -466,9 +495,10 @@ static void sigabrt(int sig, siginfo_t *info, void *ctx)
lsd->log_state(); lsd->log_state();
} }
if (log_ring.lr_frag_start < (off_t)BUFFER_SIZE) { if (log_ring.lr_frag_start < (off_t) BUFFER_SIZE) {
write(fd, &log_ring.lr_data[log_ring.lr_frag_start], write(fd,
log_ring.lr_frag_end - log_ring.lr_frag_start); &log_ring.lr_data[log_ring.lr_frag_start],
log_ring.lr_frag_end - log_ring.lr_frag_start);
} }
write(fd, log_ring.lr_data, log_ring.lr_length); write(fd, log_ring.lr_data, log_ring.lr_length);
if (getenv("DUMP_CRASH") != nullptr) { if (getenv("DUMP_CRASH") != nullptr) {
@ -530,7 +560,6 @@ static void sigabrt(int sig, siginfo_t *info, void *ctx)
int status; int status;
while (wait(&status) < 0) { while (wait(&status) < 0) {
} }
break; break;
} }
@ -543,7 +572,8 @@ static void sigabrt(int sig, siginfo_t *info, void *ctx)
} }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
void log_install_handlers() void
log_install_handlers()
{ {
const size_t stack_size = 8 * 1024 * 1024; const size_t stack_size = 8 * 1024 * 1024;
const int sigs[] = { const int sigs[] = {
@ -575,15 +605,17 @@ void log_install_handlers()
} }
} }
void log_abort() void
log_abort()
{ {
raise(SIGABRT); raise(SIGABRT);
_exit(1); _exit(1);
} }
void log_pipe_err(int fd) void
log_pipe_err(int fd)
{ {
std::thread reader([fd] () { std::thread reader([fd]() {
char buffer[1024]; char buffer[1024];
bool done = false; bool done = false;
@ -596,8 +628,7 @@ void log_pipe_err(int fd)
done = true; done = true;
break; break;
default: default:
while (buffer[rc - 1] == '\n' || while (buffer[rc - 1] == '\n' || buffer[rc - 1] == '\r') {
buffer[rc - 1] == '\r') {
rc -= 1; rc -= 1;
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,13 +32,14 @@
#ifndef lnav_log_hh #ifndef lnav_log_hh
#define lnav_log_hh #define lnav_log_hh
#include <string>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <string>
#ifndef lnav_dead2 #ifndef lnav_dead2
#define lnav_dead2 __attribute__((noreturn)) # define lnav_dead2 __attribute__((noreturn))
#endif #endif
#include "optional.hpp" #include "optional.hpp"
@ -53,12 +54,18 @@ enum class lnav_log_level_t : uint32_t {
ERROR, ERROR,
}; };
void log_argv(int argc, char *argv[]); void log_argv(int argc, char* argv[]);
void log_host_info(); void log_host_info();
void log_rusage_raw(enum lnav_log_level_t level, const char *src_file, int line_number, const struct rusage &ru); void log_rusage_raw(enum lnav_log_level_t level,
void log_msg(enum lnav_log_level_t level, const char *src_file, int line_number, const char* src_file,
const char *fmt, ...); int line_number,
void log_msg_extra(const char *fmt, ...); const struct rusage& ru);
void log_msg(enum lnav_log_level_t level,
const char* src_file,
int line_number,
const char* fmt,
...);
void log_msg_extra(const char* fmt, ...);
void log_msg_extra_complete(); void log_msg_extra_complete();
void log_install_handlers(); void log_install_handlers();
void log_abort() lnav_dead2; void log_abort() lnav_dead2;
@ -71,7 +78,7 @@ public:
virtual ~log_state_dumper(); virtual ~log_state_dumper();
virtual void log_state() { virtual void log_state(){
}; };
@ -88,9 +95,9 @@ public:
virtual void log_crash_recover() = 0; virtual void log_crash_recover() = 0;
}; };
extern nonstd::optional<FILE *> lnav_log_file; extern nonstd::optional<FILE*> lnav_log_file;
extern const char *lnav_log_crash_dir; extern const char* lnav_log_crash_dir;
extern nonstd::optional<const struct termios *> lnav_log_orig_termios; extern nonstd::optional<const struct termios*> lnav_log_orig_termios;
extern enum lnav_log_level_t lnav_log_level; extern enum lnav_log_level_t lnav_log_level;
#define log_msg_wrapper(level, fmt...) \ #define log_msg_wrapper(level, fmt...) \
@ -98,40 +105,43 @@ extern enum lnav_log_level_t lnav_log_level;
if (lnav_log_level <= level) { \ if (lnav_log_level <= level) { \
log_msg(level, __FILE__, __LINE__, fmt); \ log_msg(level, __FILE__, __LINE__, fmt); \
} \ } \
} \ } while (false)
while (false)
#define log_rusage(level, ru) \ #define log_rusage(level, ru) log_rusage_raw(level, __FILE__, __LINE__, ru);
log_rusage_raw(level, __FILE__, __LINE__, ru);
#define log_error(fmt...) \ #define log_error(fmt...) log_msg_wrapper(lnav_log_level_t::ERROR, fmt);
log_msg_wrapper(lnav_log_level_t::ERROR, fmt);
#define log_warning(fmt...) \ #define log_warning(fmt...) log_msg_wrapper(lnav_log_level_t::WARNING, fmt);
log_msg_wrapper(lnav_log_level_t::WARNING, fmt);
#define log_info(fmt...) \ #define log_info(fmt...) log_msg_wrapper(lnav_log_level_t::INFO, fmt);
log_msg_wrapper(lnav_log_level_t::INFO, fmt);
#define log_debug(fmt...) \ #define log_debug(fmt...) log_msg_wrapper(lnav_log_level_t::DEBUG, fmt);
log_msg_wrapper(lnav_log_level_t::DEBUG, fmt);
#define log_trace(fmt...) \ #define log_trace(fmt...) log_msg_wrapper(lnav_log_level_t::TRACE, fmt);
log_msg_wrapper(lnav_log_level_t::TRACE, fmt);
#define require(e) \ #define require(e) ((void) ((e) ? 0 : lnav_require(#e, __FILE__, __LINE__)))
((void) ((e) ? 0 : lnav_require (#e, __FILE__, __LINE__)))
#define lnav_require(e, file, line) \ #define lnav_require(e, file, line) \
(log_msg(lnav_log_level_t::ERROR, file, line, "failed precondition `%s'", e), log_abort(), 1) (log_msg( \
lnav_log_level_t::ERROR, file, line, "failed precondition `%s'", e), \
log_abort(), \
1)
#define ensure(e) \ #define ensure(e) ((void) ((e) ? 0 : lnav_ensure(#e, __FILE__, __LINE__)))
((void) ((e) ? 0 : lnav_ensure (#e, __FILE__, __LINE__)))
#define lnav_ensure(e, file, line) \ #define lnav_ensure(e, file, line) \
(log_msg(lnav_log_level_t::ERROR, file, line, "failed postcondition `%s'", e), log_abort(), 1) (log_msg( \
lnav_log_level_t::ERROR, file, line, "failed postcondition `%s'", e), \
log_abort(), \
1)
#define log_perror(e) \ #define log_perror(e) \
((void) ((e != -1) ? 0 : lnav_log_perror (#e, __FILE__, __LINE__))) ((void) ((e != -1) ? 0 : lnav_log_perror(#e, __FILE__, __LINE__)))
#define lnav_log_perror(e, file, line) \ #define lnav_log_perror(e, file, line) \
(log_msg(lnav_log_level_t::ERROR, file, line, "syscall failed `%s' -- %s", e, strerror(errno)), 1) (log_msg(lnav_log_level_t::ERROR, \
file, \
line, \
"syscall failed `%s' -- %s", \
e, \
strerror(errno)), \
1)
#endif #endif

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -41,17 +41,20 @@
* @param step The granularity. * @param step The granularity.
*/ */
template<typename Size, typename Step> template<typename Size, typename Step>
inline int rounddown(Size size, Step step) inline int
rounddown(Size size, Step step)
{ {
return size - (size % step); return size - (size % step);
} }
inline int rounddown_offset(size_t size, int step, int offset) inline int
rounddown_offset(size_t size, int step, int offset)
{ {
return size - ((size - offset) % step); return size - ((size - offset) % step);
} }
inline size_t roundup_size(size_t size, int step) inline size_t
roundup_size(size_t size, int step)
{ {
size_t retval = size + step; size_t retval = size + step;

@ -21,27 +21,26 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h" #include "network.tcp.hh"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h> #include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "config.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "network.tcp.hh"
namespace network { namespace network {
namespace tcp { namespace tcp {
Result<auto_fd, std::string> connect(const char *hostname, Result<auto_fd, std::string>
const char *servname) connect(const char* hostname, const char* servname)
{ {
struct addrinfo hints, *ai; struct addrinfo hints, *ai;
@ -52,7 +51,9 @@ Result<auto_fd, std::string> connect(const char *hostname,
if (rc != 0) { if (rc != 0) {
return Err(fmt::format("unable to resolve {}:{} -- {}", return Err(fmt::format("unable to resolve {}:{} -- {}",
hostname, servname, gai_strerror(rc))); hostname,
servname,
gai_strerror(rc)));
} }
auto retval = auto_fd(socket(ai->ai_family, ai->ai_socktype, 0)); auto retval = auto_fd(socket(ai->ai_family, ai->ai_socktype, 0));
@ -60,11 +61,13 @@ Result<auto_fd, std::string> connect(const char *hostname,
rc = ::connect(retval, ai->ai_addr, ai->ai_addrlen); rc = ::connect(retval, ai->ai_addr, ai->ai_addrlen);
if (rc != 0) { if (rc != 0) {
return Err(fmt::format("unable to connect to {}:{} -- {}", return Err(fmt::format("unable to connect to {}:{} -- {}",
hostname, servname, strerror(rc))); hostname,
servname,
strerror(rc)));
} }
return Ok(retval); return Ok(retval);
} }
} } // namespace tcp
} } // namespace network

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -40,11 +40,11 @@ namespace network {
struct locality { struct locality {
locality(nonstd::optional<std::string> username, locality(nonstd::optional<std::string> username,
std::string hostname, std::string hostname,
nonstd::optional<std::string> service) : nonstd::optional<std::string> service)
l_username(std::move(username)), : l_username(std::move(username)), l_hostname(std::move(hostname)),
l_hostname(std::move(hostname)), l_service(std::move(service))
l_service(std::move(service)) {
{} }
nonstd::optional<std::string> l_username; nonstd::optional<std::string> l_username;
std::string l_hostname; std::string l_hostname;
@ -55,11 +55,13 @@ struct path {
locality p_locality; locality p_locality;
std::string p_path; std::string p_path;
path(locality l, std::string path) : p_locality(std::move(l)), path(locality l, std::string path)
p_path(std::move(path)) : p_locality(std::move(l)), p_path(std::move(path))
{} {
}
path home() const { path home() const
{
return { return {
this->p_locality, this->p_locality,
".", ".",
@ -69,10 +71,10 @@ struct path {
namespace tcp { namespace tcp {
Result<auto_fd, std::string> connect(const char *hostname, Result<auto_fd, std::string> connect(const char* hostname,
const char *servname); const char* servname);
} }
} } // namespace network
#endif #endif

@ -21,21 +21,19 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <iostream> #include <iostream>
#include "config.h"
#include "doctest/doctest.h" #include "doctest/doctest.h"
#include "network.tcp.hh" #include "network.tcp.hh"
TEST_CASE ("bad hostname") TEST_CASE("bad hostname")
{ {
auto connect_res = network::tcp::connect("foobar.bazzer", "http"); auto connect_res = network::tcp::connect("foobar.bazzer", "http");
CHECK(connect_res.unwrapErr() == CHECK(connect_res.unwrapErr() ==
@ -43,7 +41,7 @@ TEST_CASE ("bad hostname")
"provided, or not known"); "provided, or not known");
} }
TEST_CASE ("bad servname") TEST_CASE("bad servname")
{ {
auto connect_res = network::tcp::connect("www.cnn.com", "non-existent"); auto connect_res = network::tcp::connect("www.cnn.com", "non-existent");
CHECK(connect_res.unwrapErr() == CHECK(connect_res.unwrapErr() ==

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -37,36 +37,47 @@
namespace detail { namespace detail {
template <class T> template<class T>
typename std::enable_if<std::is_void<T>::value, T>::type typename std::enable_if<std::is_void<T>::value, T>::type
void_or_nullopt() void_or_nullopt()
{ {
return; return;
} }
template <class T> template<class T>
typename std::enable_if<not std::is_void<T>::value, T>::type typename std::enable_if<not std::is_void<T>::value, T>::type
void_or_nullopt() void_or_nullopt()
{ {
return nonstd::nullopt; return nonstd::nullopt;
} }
template <class T> template<class T>
struct is_optional : std::false_type {}; struct is_optional : std::false_type {
};
template <class T> template<class T>
struct is_optional<nonstd::optional<T>> : std::true_type {}; struct is_optional<nonstd::optional<T>> : std::true_type {
} };
} // namespace detail
template <class T, class F, std::enable_if_t<detail::is_optional<std::decay_t<T>>::value, int> = 0> template<class T,
auto operator|(T&& t, F f) -> decltype(detail::void_or_nullopt<decltype(f(std::forward<T>(t).operator*()))>()) { class F,
std::enable_if_t<detail::is_optional<std::decay_t<T>>::value, int> = 0>
auto
operator|(T&& t, F f)
-> decltype(detail::void_or_nullopt<decltype(f(std::forward<T>(t).
operator*()))>())
{
using return_type = decltype(f(std::forward<T>(t).operator*())); using return_type = decltype(f(std::forward<T>(t).operator*()));
if (t) return f(std::forward<T>(t).operator*()); if (t)
else return detail::void_or_nullopt<return_type>(); return f(std::forward<T>(t).operator*());
else
return detail::void_or_nullopt<return_type>();
} }
template< class T > template<class T>
optional_constexpr nonstd::optional< typename std::decay<T>::type > make_optional_from_nullable( T && v ) optional_constexpr nonstd::optional<typename std::decay<T>::type>
make_optional_from_nullable(T&& v)
{ {
if (v != nullptr) { if (v != nullptr) {
return nonstd::optional<typename std::decay<T>::type>( return nonstd::optional<typename std::decay<T>::type>(
@ -75,8 +86,9 @@ optional_constexpr nonstd::optional< typename std::decay<T>::type > make_optiona
return nonstd::nullopt; return nonstd::nullopt;
} }
template<template <typename, typename...> class C, typename T> template<template<typename, typename...> class C, typename T>
nonstd::optional<T> cget(const C<T> &container, size_t index) nonstd::optional<T>
cget(const C<T>& container, size_t index)
{ {
if (index < container.size()) { if (index < container.size()) {
return container[index]; return container[index];
@ -85,7 +97,9 @@ nonstd::optional<T> cget(const C<T> &container, size_t index)
return nonstd::nullopt; return nonstd::nullopt;
} }
inline nonstd::optional<const char *> getenv_opt(const char *name) { inline nonstd::optional<const char*>
getenv_opt(const char* name)
{
return make_optional_from_nullable(getenv(name)); return make_optional_from_nullable(getenv(name));
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -30,32 +30,38 @@
#include "config.h" #include "config.h"
#ifdef __CYGWIN__ #ifdef __CYGWIN__
#include <sstream> # include <iostream>
#include <iostream> # include <sstream>
#endif #endif
#include "paths.hh"
#include "fmt/format.h" #include "fmt/format.h"
#include "paths.hh"
namespace lnav { namespace lnav {
namespace paths { namespace paths {
#ifdef __CYGWIN__ #ifdef __CYGWIN__
char* windows_to_unix_file_path(char* input) { char*
windows_to_unix_file_path(char* input)
{
if (input == nullptr) { if (input == nullptr) {
return nullptr; return nullptr;
} }
std::string file_path; std::string file_path;
file_path.assign(input); file_path.assign(input);
// Replace the slashes // Replace the slashes
std::replace(file_path.begin(), file_path.end(), WINDOWS_FILE_PATH_SEPARATOR, UNIX_FILE_PATH_SEPARATOR); std::replace(file_path.begin(),
file_path.end(),
WINDOWS_FILE_PATH_SEPARATOR,
UNIX_FILE_PATH_SEPARATOR);
// Convert the drive letter to lowercase // Convert the drive letter to lowercase
std::transform(file_path.begin(), file_path.begin() + 1, file_path.begin(), std::transform(
[](unsigned char character) { file_path.begin(),
return std::tolower(character); file_path.begin() + 1,
}); file_path.begin(),
[](unsigned char character) { return std::tolower(character); });
// Remove the colon // Remove the colon
const auto drive_letter = file_path.substr(0, 1); const auto drive_letter = file_path.substr(0, 1);
@ -70,7 +76,8 @@ char* windows_to_unix_file_path(char* input) {
} }
#endif #endif
ghc::filesystem::path dotlnav() ghc::filesystem::path
dotlnav()
{ {
#ifdef __CYGWIN__ #ifdef __CYGWIN__
auto home_env = windows_to_unix_file_path(getenv("APPDATA")); auto home_env = windows_to_unix_file_path(getenv("APPDATA"));
@ -110,7 +117,8 @@ ghc::filesystem::path dotlnav()
return ghc::filesystem::current_path(); return ghc::filesystem::current_path();
} }
ghc::filesystem::path workdir() ghc::filesystem::path
workdir()
{ {
auto subdir_name = fmt::format("lnav-user-{}-work", getuid()); auto subdir_name = fmt::format("lnav-user-{}-work", getuid());
auto tmp_path = ghc::filesystem::temp_directory_path(); auto tmp_path = ghc::filesystem::temp_directory_path();
@ -118,5 +126,5 @@ ghc::filesystem::path workdir()
return tmp_path / ghc::filesystem::path(subdir_name); return tmp_path / ghc::filesystem::path(subdir_name);
} }
} } // namespace paths
} } // namespace lnav

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -52,7 +52,7 @@ ghc::filesystem::path dotlnav();
ghc::filesystem::path workdir(); ghc::filesystem::path workdir();
} } // namespace paths
} } // namespace lnav
#endif #endif

@ -21,30 +21,31 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <iterator> #include <iterator>
#include <regex> #include <regex>
#include <sstream> #include <sstream>
#include "lnav_log.hh"
#include "is_utf8.hh"
#include "string_util.hh" #include "string_util.hh"
void scrub_to_utf8(char *buffer, size_t length) #include "config.h"
#include "is_utf8.hh"
#include "lnav_log.hh"
void
scrub_to_utf8(char* buffer, size_t length)
{ {
const char *msg; const char* msg;
int faulty_bytes; int faulty_bytes;
while (true) { while (true) {
ssize_t utf8_end = is_utf8( ssize_t utf8_end
(unsigned char *) buffer, length, &msg, &faulty_bytes); = is_utf8((unsigned char*) buffer, length, &msg, &faulty_bytes);
if (msg == nullptr) { if (msg == nullptr) {
break; break;
@ -55,7 +56,8 @@ void scrub_to_utf8(char *buffer, size_t length)
} }
} }
size_t unquote(char *dst, const char *str, size_t len) size_t
unquote(char* dst, const char* str, size_t len)
{ {
if (str[0] == 'r' || str[0] == 'u') { if (str[0] == 'r' || str[0] == 'u') {
str += 1; str += 1;
@ -70,8 +72,7 @@ size_t unquote(char *dst, const char *str, size_t len)
dst[index] = str[lpc]; dst[index] = str[lpc];
if (str[lpc] == quote_char) { if (str[lpc] == quote_char) {
lpc += 1; lpc += 1;
} } else if (str[lpc] == '\\' && (lpc + 1) < len) {
else if (str[lpc] == '\\' && (lpc + 1) < len) {
switch (str[lpc + 1]) { switch (str[lpc + 1]) {
case 'n': case 'n':
dst[index] = '\n'; dst[index] = '\n';
@ -94,7 +95,8 @@ size_t unquote(char *dst, const char *str, size_t len)
return index; return index;
} }
size_t unquote_w3c(char *dst, const char *str, size_t len) size_t
unquote_w3c(char* dst, const char* str, size_t len)
{ {
size_t index = 0; size_t index = 0;
@ -111,7 +113,8 @@ size_t unquote_w3c(char *dst, const char *str, size_t len)
return index; return index;
} }
void truncate_to(std::string &str, size_t max_char_len) void
truncate_to(std::string& str, size_t max_char_len)
{ {
static const std::string ELLIPSIS = "\u22ef"; static const std::string ELLIPSIS = "\u22ef";
@ -139,23 +142,25 @@ void truncate_to(std::string &str, size_t max_char_len)
auto chars_to_remove = (str_char_len - max_char_len) + 1; auto chars_to_remove = (str_char_len - max_char_len) + 1;
auto midpoint = str_char_len / 2; auto midpoint = str_char_len / 2;
auto chars_to_keep_at_front = midpoint - (chars_to_remove / 2); auto chars_to_keep_at_front = midpoint - (chars_to_remove / 2);
auto bytes_to_keep_at_front = auto bytes_to_keep_at_front
utf8_char_to_byte_index(str, chars_to_keep_at_front); = utf8_char_to_byte_index(str, chars_to_keep_at_front);
auto remove_up_to_bytes = auto remove_up_to_bytes = utf8_char_to_byte_index(
utf8_char_to_byte_index(str, chars_to_keep_at_front + chars_to_remove); str, chars_to_keep_at_front + chars_to_remove);
auto bytes_to_remove = remove_up_to_bytes - bytes_to_keep_at_front; auto bytes_to_remove = remove_up_to_bytes - bytes_to_keep_at_front;
str.erase(bytes_to_keep_at_front, bytes_to_remove); str.erase(bytes_to_keep_at_front, bytes_to_remove);
str.insert(bytes_to_keep_at_front, ELLIPSIS); str.insert(bytes_to_keep_at_front, ELLIPSIS);
} }
bool is_url(const char *fn) bool
is_url(const char* fn)
{ {
static const auto url_re = std::regex("^(file|https?|ftps?|scp|sftp):.*"); static const auto url_re = std::regex("^(file|https?|ftps?|scp|sftp):.*");
return std::regex_match(fn, url_re); return std::regex_match(fn, url_re);
} }
size_t abbreviate_str(char *str, size_t len, size_t max_len) size_t
abbreviate_str(char* str, size_t len, size_t max_len)
{ {
size_t last_start = 1; size_t last_start = 1;
@ -184,7 +189,8 @@ size_t abbreviate_str(char *str, size_t len, size_t max_len)
return len; return len;
} }
void split_ws(const std::string &str, std::vector<std::string> &toks_out) void
split_ws(const std::string& str, std::vector<std::string>& toks_out)
{ {
std::stringstream ss(str); std::stringstream ss(str);
std::string buf; std::string buf;
@ -194,14 +200,16 @@ void split_ws(const std::string &str, std::vector<std::string> &toks_out)
} }
} }
std::string repeat(const std::string& input, size_t num) std::string
repeat(const std::string& input, size_t num)
{ {
std::ostringstream os; std::ostringstream os;
std::fill_n(std::ostream_iterator<std::string>(os), num, input); std::fill_n(std::ostream_iterator<std::string>(os), num, input);
return os.str(); return os.str();
} }
std::string center_str(const std::string &subject, size_t width) std::string
center_str(const std::string& subject, size_t width)
{ {
std::string retval = subject; std::string retval = subject;
@ -219,18 +227,23 @@ std::string center_str(const std::string &subject, size_t width)
} }
template<typename T> template<typename T>
size_t strtonum(T &num_out, const char *string, size_t len) size_t
strtonum(T& num_out, const char* string, size_t len)
{ {
size_t retval = 0; size_t retval = 0;
T sign = 1; T sign = 1;
num_out = 0; num_out = 0;
for (; retval < len && isspace(string[retval]); retval++); for (; retval < len && isspace(string[retval]); retval++) {
;
}
for (; retval < len && string[retval] == '-'; retval++) { for (; retval < len && string[retval] == '-'; retval++) {
sign *= -1; sign *= -1;
} }
for (; retval < len && string[retval] == '+'; retval++); for (; retval < len && string[retval] == '+'; retval++) {
;
}
for (; retval < len && isdigit(string[retval]); retval++) { for (; retval < len && isdigit(string[retval]); retval++) {
num_out *= 10; num_out *= 10;
num_out += string[retval] - '0'; num_out += string[retval] - '0';
@ -240,11 +253,10 @@ size_t strtonum(T &num_out, const char *string, size_t len)
return retval; return retval;
} }
template template size_t strtonum<long long>(long long& num_out,
size_t strtonum<long long>(long long &num_out, const char *string, size_t len); const char* string,
size_t len);
template template size_t strtonum<long>(long& num_out, const char* string, size_t len);
size_t strtonum<long>(long &num_out, const char *string, size_t len);
template template size_t strtonum<int>(int& num_out, const char* string, size_t len);
size_t strtonum<int>(int &num_out, const char *string, size_t len);

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -30,38 +30,45 @@
#ifndef lnav_string_util_hh #ifndef lnav_string_util_hh
#define lnav_string_util_hh #define lnav_string_util_hh
#include <string.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <string.h>
#include "ww898/cp_utf8.hpp" #include "ww898/cp_utf8.hpp"
void scrub_to_utf8(char *buffer, size_t length); void scrub_to_utf8(char* buffer, size_t length);
inline bool is_line_ending(char ch) { inline bool
is_line_ending(char ch)
{
return ch == '\r' || ch == '\n'; return ch == '\r' || ch == '\n';
} }
size_t unquote(char *dst, const char *str, size_t len); size_t unquote(char* dst, const char* str, size_t len);
size_t unquote_w3c(char *dst, const char *str, size_t len); size_t unquote_w3c(char* dst, const char* str, size_t len);
inline bool startswith(const char *str, const char *prefix) inline bool
startswith(const char* str, const char* prefix)
{ {
return strncmp(str, prefix, strlen(prefix)) == 0; return strncmp(str, prefix, strlen(prefix)) == 0;
} }
inline bool startswith(const std::string &str, const char *prefix) inline bool
startswith(const std::string& str, const char* prefix)
{ {
return startswith(str.c_str(), prefix); return startswith(str.c_str(), prefix);
} }
inline bool startswith(const std::string &str, const std::string& prefix) inline bool
startswith(const std::string& str, const std::string& prefix)
{ {
return startswith(str.c_str(), prefix.c_str()); return startswith(str.c_str(), prefix.c_str());
} }
inline bool endswith(const char *str, const char *suffix) inline bool
endswith(const char* str, const char* suffix)
{ {
size_t len = strlen(str), suffix_len = strlen(suffix); size_t len = strlen(str), suffix_len = strlen(suffix);
@ -73,7 +80,8 @@ inline bool endswith(const char *str, const char *suffix)
} }
template<int N> template<int N>
inline bool endswith(const std::string& str, const char (&suffix) [N]) inline bool
endswith(const std::string& str, const char (&suffix)[N])
{ {
if (N - 1 > str.length()) { if (N - 1 > str.length()) {
return false; return false;
@ -82,19 +90,23 @@ inline bool endswith(const std::string& str, const char (&suffix) [N])
return strcmp(&str[str.size() - (N - 1)], suffix) == 0; return strcmp(&str[str.size() - (N - 1)], suffix) == 0;
} }
void truncate_to(std::string &str, size_t max_char_len); void truncate_to(std::string& str, size_t max_char_len);
inline std::string trim(const std::string &str) inline std::string
trim(const std::string& str)
{ {
std::string::size_type start, end; std::string::size_type start, end;
for (start = 0; start < str.size() && isspace(str[start]); start++); for (start = 0; start < str.size() && isspace(str[start]); start++)
for (end = str.size(); end > 0 && isspace(str[end - 1]); end--); ;
for (end = str.size(); end > 0 && isspace(str[end - 1]); end--)
;
return str.substr(start, end - start); return str.substr(start, end - start);
} }
inline std::string tolower(const char *str) inline std::string
tolower(const char* str)
{ {
std::string retval; std::string retval;
@ -105,12 +117,14 @@ inline std::string tolower(const char *str)
return retval; return retval;
} }
inline std::string tolower(const std::string &str) inline std::string
tolower(const std::string& str)
{ {
return tolower(str.c_str()); return tolower(str.c_str());
} }
inline std::string toupper(const char *str) inline std::string
toupper(const char* str)
{ {
std::string retval; std::string retval;
@ -121,19 +135,22 @@ inline std::string toupper(const char *str)
return retval; return retval;
} }
inline std::string toupper(const std::string &str) inline std::string
toupper(const std::string& str)
{ {
return toupper(str.c_str()); return toupper(str.c_str());
} }
inline ssize_t utf8_char_to_byte_index(const std::string &str, ssize_t ch_index) inline ssize_t
utf8_char_to_byte_index(const std::string& str, ssize_t ch_index)
{ {
ssize_t retval = 0; ssize_t retval = 0;
while (ch_index > 0) { while (ch_index > 0) {
auto ch_len = ww898::utf::utf8::char_size([&str, retval]() { auto ch_len
return std::make_pair(str[retval], str.length() - retval - 1); = ww898::utf::utf8::char_size([&str, retval]() {
}).unwrapOr(1); return std::make_pair(str[retval], str.length() - retval - 1);
}).unwrapOr(1);
retval += ch_len; retval += ch_len;
ch_index -= 1; ch_index -= 1;
@ -142,7 +159,8 @@ inline ssize_t utf8_char_to_byte_index(const std::string &str, ssize_t ch_index)
return retval; return retval;
} }
inline Result<size_t, const char *> utf8_string_length(const char *str, ssize_t len = -1) inline Result<size_t, const char*>
utf8_string_length(const char* str, ssize_t len = -1)
{ {
size_t retval = 0; size_t retval = 0;
@ -151,9 +169,10 @@ inline Result<size_t, const char *> utf8_string_length(const char *str, ssize_t
} }
for (ssize_t byte_index = 0; byte_index < len;) { for (ssize_t byte_index = 0; byte_index < len;) {
auto ch_size = TRY(ww898::utf::utf8::char_size([str, len, byte_index]() { auto ch_size
return std::make_pair(str[byte_index], len - byte_index); = TRY(ww898::utf::utf8::char_size([str, len, byte_index]() {
})); return std::make_pair(str[byte_index], len - byte_index);
}));
byte_index += ch_size; byte_index += ch_size;
retval += 1; retval += 1;
} }
@ -161,22 +180,23 @@ inline Result<size_t, const char *> utf8_string_length(const char *str, ssize_t
return Ok(retval); return Ok(retval);
} }
inline Result<size_t, const char *> utf8_string_length(const std::string& str) inline Result<size_t, const char*>
utf8_string_length(const std::string& str)
{ {
return utf8_string_length(str.c_str(), str.length()); return utf8_string_length(str.c_str(), str.length());
} }
bool is_url(const char *fn); bool is_url(const char* fn);
size_t abbreviate_str(char *str, size_t len, size_t max_len); size_t abbreviate_str(char* str, size_t len, size_t max_len);
void split_ws(const std::string &str, std::vector<std::string> &toks_out); void split_ws(const std::string& str, std::vector<std::string>& toks_out);
std::string repeat(const std::string& input, size_t num); std::string repeat(const std::string& input, size_t num);
std::string center_str(const std::string& subject, size_t width); std::string center_str(const std::string& subject, size_t width);
template<typename T> template<typename T>
size_t strtonum(T &num_out, const char *data, size_t len); size_t strtonum(T& num_out, const char* data, size_t len);
#endif #endif

@ -21,22 +21,21 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <iostream> #include <iostream>
#include "doctest/doctest.h" #include "base/string_util.hh"
#include "base/strnatcmp.h" #include "base/strnatcmp.h"
#include "base/string_util.hh" #include "config.h"
#include "doctest/doctest.h"
TEST_CASE ("endswith") TEST_CASE("endswith")
{ {
std::string hw("hello"); std::string hw("hello");
@ -44,7 +43,7 @@ TEST_CASE ("endswith")
CHECK(endswith(hw, "lo") == true); CHECK(endswith(hw, "lo") == true);
} }
TEST_CASE ("truncate_to") TEST_CASE("truncate_to")
{ {
const std::string orig = "0123456789abcdefghijklmnopqrstuvwxyz"; const std::string orig = "0123456789abcdefghijklmnopqrstuvwxyz";
std::string str; std::string str;
@ -74,16 +73,17 @@ TEST_CASE ("truncate_to")
CHECK(str == "01\u22efyz"); CHECK(str == "01\u22efyz");
} }
TEST_CASE("strnatcmp") { TEST_CASE("strnatcmp")
{
{ {
constexpr const char *n1 = "010"; constexpr const char* n1 = "010";
constexpr const char *n2 = "020"; constexpr const char* n2 = "020";
CHECK(strnatcmp(strlen(n1), n1, strlen(n2), n2) < 0); CHECK(strnatcmp(strlen(n1), n1, strlen(n2), n2) < 0);
} }
{ {
constexpr const char *n1 = "2"; constexpr const char* n1 = "2";
constexpr const char *n2 = "10"; constexpr const char* n2 = "10";
CHECK(strnatcmp(strlen(n1), n1, strlen(n2), n2) < 0); CHECK(strnatcmp(strlen(n1), n1, strlen(n2), n2) < 0);
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */

@ -21,27 +21,29 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file time_util.cc * @file time_util.cc
*/ */
#include "config.h"
#include <chrono> #include <chrono>
#include "time_util.hh" #include "time_util.hh"
#include "config.h"
static time_t BAD_DATE = -1; static time_t BAD_DATE = -1;
time_t tm2sec(const struct tm *t) time_t
tm2sec(const struct tm* t)
{ {
int year; int year;
time_t days, secs; time_t days, secs;
const int dayoffset[12] = const int dayoffset[12]
{ 306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275 }; = {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275};
year = t->tm_year; year = t->tm_year;
@ -57,7 +59,7 @@ time_t tm2sec(const struct tm *t)
/* Find number of days since 1st March 1900 (in the Gregorian calendar). */ /* Find number of days since 1st March 1900 (in the Gregorian calendar). */
days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4; days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4;
days += dayoffset[t->tm_mon] + t->tm_mday - 1; days += dayoffset[t->tm_mon] + t->tm_mday - 1;
days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */ days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
@ -65,15 +67,16 @@ time_t tm2sec(const struct tm *t)
if (secs < 0) { if (secs < 0) {
return BAD_DATE; return BAD_DATE;
} /* must have overflowed */ } /* must have overflowed */
else { else
{
#ifdef HAVE_STRUCT_TM_TM_ZONE #ifdef HAVE_STRUCT_TM_TM_ZONE
if (t->tm_zone) { if (t->tm_zone) {
secs -= t->tm_gmtoff; secs -= t->tm_gmtoff;
} }
#endif #endif
return secs; return secs;
} /* must be a valid time */ } /* must be a valid time */
} }
static const int SECSPERMIN = 60; static const int SECSPERMIN = 60;
@ -86,19 +89,16 @@ static const int EPOCH_YEAR = 1970;
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
static const int year_lengths[2] = { static const int year_lengths[2] = {365, 366};
365,
366
};
const unsigned short int mon_yday[2][13] = { const unsigned short int mon_yday[2][13] = {
/* Normal years. */ /* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
/* Leap years. */ /* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}};
};
void secs2wday(const struct timeval &tv, struct tm *res) void
secs2wday(const struct timeval& tv, struct tm* res)
{ {
long days, rem; long days, rem;
time_t lcltime; time_t lcltime;
@ -118,21 +118,21 @@ void secs2wday(const struct timeval &tv, struct tm *res)
res->tm_wday += DAYSPERWEEK; res->tm_wday += DAYSPERWEEK;
} }
struct tm *secs2tm(lnav::time64_t tim, struct tm *res) struct tm*
secs2tm(lnav::time64_t tim, struct tm* res)
{ {
long days, rem; long days, rem;
lnav::time64_t lcltime; lnav::time64_t lcltime;
int y; int y;
int yleap; int yleap;
const unsigned short int *ip; const unsigned short int* ip;
/* base decision about std/dst time on current time */ /* base decision about std/dst time on current time */
lcltime = tim; lcltime = tim;
days = ((long)lcltime) / SECSPERDAY; days = ((long) lcltime) / SECSPERDAY;
rem = ((long)lcltime) % SECSPERDAY; rem = ((long) lcltime) % SECSPERDAY;
while (rem < 0) while (rem < 0) {
{
rem += SECSPERDAY; rem += SECSPERDAY;
--days; --days;
} }
@ -149,21 +149,16 @@ struct tm *secs2tm(lnav::time64_t tim, struct tm *res)
/* compute year & day of year */ /* compute year & day of year */
y = EPOCH_YEAR; y = EPOCH_YEAR;
if (days >= 0) if (days >= 0) {
{ for (;;) {
for (;;)
{
yleap = isleap(y); yleap = isleap(y);
if (days < year_lengths[yleap]) if (days < year_lengths[yleap])
break; break;
y++; y++;
days -= year_lengths[yleap]; days -= year_lengths[yleap];
} }
} } else {
else do {
{
do
{
--y; --y;
yleap = isleap(y); yleap = isleap(y);
days += year_lengths[yleap]; days += year_lengths[yleap];
@ -184,14 +179,15 @@ struct tm *secs2tm(lnav::time64_t tim, struct tm *res)
return (res); return (res);
} }
struct timeval exttm::to_timeval() const struct timeval
exttm::to_timeval() const
{ {
struct timeval retval; struct timeval retval;
retval.tv_sec = tm2sec(&this->et_tm); retval.tv_sec = tm2sec(&this->et_tm);
retval.tv_usec = retval.tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::duration_cast<std::chrono::microseconds>( std::chrono::nanoseconds(this->et_nsec))
std::chrono::nanoseconds(this->et_nsec)).count(); .count();
return retval; return retval;
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -30,11 +30,13 @@
#ifndef lnav_time_util_hh #ifndef lnav_time_util_hh
#define lnav_time_util_hh #define lnav_time_util_hh
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <inttypes.h> #include <inttypes.h>
#include <string.h> #include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include "config.h"
namespace lnav { namespace lnav {
@ -42,18 +44,19 @@ using time64_t = uint64_t;
} }
struct tm *secs2tm(lnav::time64_t tim, struct tm *res); struct tm* secs2tm(lnav::time64_t tim, struct tm* res);
/** /**
* Convert the time stored in a 'tm' struct into epoch time. * Convert the time stored in a 'tm' struct into epoch time.
* *
* @param t The 'tm' structure to convert to epoch time. * @param t The 'tm' structure to convert to epoch time.
* @return The given time in seconds since the epoch. * @return The given time in seconds since the epoch.
*/ */
time_t tm2sec(const struct tm *t); time_t tm2sec(const struct tm* t);
void secs2wday(const struct timeval &tv, struct tm *res); void secs2wday(const struct timeval& tv, struct tm* res);
inline inline time_t
time_t convert_log_time_to_local(time_t value) { convert_log_time_to_local(time_t value)
{
struct tm tm; struct tm tm;
localtime_r(&value, &tm); localtime_r(&value, &tm);
@ -88,37 +91,41 @@ struct exttm {
unsigned int et_flags; unsigned int et_flags;
long et_gmtoff; long et_gmtoff;
bool operator==(const exttm &other) const { bool operator==(const exttm& other) const
{
return memcmp(this, &other, sizeof(exttm)) == 0; return memcmp(this, &other, sizeof(exttm)) == 0;
}; };
struct timeval to_timeval() const; struct timeval to_timeval() const;
}; };
inline inline bool
bool operator<(const struct timeval &left, time_t right) { operator<(const struct timeval& left, time_t right)
{
return left.tv_sec < right; return left.tv_sec < right;
} }
inline inline bool
bool operator<(time_t left, const struct timeval &right) { operator<(time_t left, const struct timeval& right)
{
return left < right.tv_sec; return left < right.tv_sec;
} }
inline inline bool
bool operator<(const struct timeval &left, const struct timeval &right) { operator<(const struct timeval& left, const struct timeval& right)
return left.tv_sec < right.tv_sec || {
((left.tv_sec == right.tv_sec) && (left.tv_usec < right.tv_usec)); return left.tv_sec < right.tv_sec
|| ((left.tv_sec == right.tv_sec) && (left.tv_usec < right.tv_usec));
} }
inline inline bool
bool operator!=(const struct timeval &left, const struct timeval &right) { operator!=(const struct timeval& left, const struct timeval& right)
return left.tv_sec != right.tv_sec || {
left.tv_usec != right.tv_usec; return left.tv_sec != right.tv_sec || left.tv_usec != right.tv_usec;
} }
inline inline struct timeval
struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs) operator-(const struct timeval& lhs, const struct timeval& rhs)
{ {
struct timeval diff; struct timeval diff;
@ -128,7 +135,9 @@ struct timeval operator-(const struct timeval& lhs, const struct timeval& rhs)
typedef int64_t mstime_t; typedef int64_t mstime_t;
inline mstime_t getmstime() { inline mstime_t
getmstime()
{
struct timeval tv; struct timeval tv;
gettimeofday(&tv, nullptr); gettimeofday(&tv, nullptr);
@ -136,7 +145,9 @@ inline mstime_t getmstime() {
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL; return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
} }
inline struct timeval current_timeval() { inline struct timeval
current_timeval()
{
struct timeval retval; struct timeval retval;
gettimeofday(&retval, nullptr); gettimeofday(&retval, nullptr);
@ -144,7 +155,9 @@ inline struct timeval current_timeval() {
return retval; return retval;
} }
inline struct timespec current_timespec() { inline struct timespec
current_timespec()
{
struct timespec retval; struct timespec retval;
clock_gettime(CLOCK_REALTIME, &retval); clock_gettime(CLOCK_REALTIME, &retval);
@ -152,12 +165,14 @@ inline struct timespec current_timespec() {
return retval; return retval;
} }
inline time_t day_num(time_t ti) inline time_t
day_num(time_t ti)
{ {
return ti / (24 * 60 * 60); return ti / (24 * 60 * 60);
} }
inline time_t hour_num(time_t ti) inline time_t
hour_num(time_t ti)
{ {
return ti / (60 * 60); return ti / (60 * 60);
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -33,6 +33,7 @@
#define lnav_big_array_hh #define lnav_big_array_hh
#include <sys/mman.h> #include <sys/mman.h>
#include <unistd.h>
#include "base/math_util.hh" #include "base/math_util.hh"
@ -40,11 +41,13 @@ template<typename T>
struct big_array { struct big_array {
static const size_t DEFAULT_INCREMENT = 100 * 1000; static const size_t DEFAULT_INCREMENT = 100 * 1000;
big_array() : ba_ptr(nullptr), ba_size(0), ba_capacity(0) { big_array()
: ba_ptr(nullptr), ba_size(0), ba_capacity(0){
}; };
bool reserve(size_t size) { bool reserve(size_t size)
{
if (size < this->ba_capacity) { if (size < this->ba_capacity) {
return false; return false;
} }
@ -55,67 +58,77 @@ struct big_array {
} }
this->ba_capacity = size + DEFAULT_INCREMENT; this->ba_capacity = size + DEFAULT_INCREMENT;
void *result = mmap(nullptr, void* result
roundup_size(this->ba_capacity * sizeof(T), = mmap(nullptr,
getpagesize()), roundup_size(this->ba_capacity * sizeof(T), getpagesize()),
PROT_READ|PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS|MAP_PRIVATE, MAP_ANONYMOUS | MAP_PRIVATE,
-1, -1,
0); 0);
ensure(result != MAP_FAILED); ensure(result != MAP_FAILED);
this->ba_ptr = (T *) result; this->ba_ptr = (T*) result;
return true; return true;
}; };
void clear() { void clear()
{
this->ba_size = 0; this->ba_size = 0;
}; };
size_t size() const { size_t size() const
{
return this->ba_size; return this->ba_size;
}; };
void shrink_to(size_t new_size) { void shrink_to(size_t new_size)
{
require(new_size <= this->ba_size); require(new_size <= this->ba_size);
this->ba_size = new_size; this->ba_size = new_size;
} }
bool empty() const { bool empty() const
{
return this->ba_size == 0; return this->ba_size == 0;
}; };
void push_back(const T &val) { void push_back(const T& val)
{
this->ba_ptr[this->ba_size] = val; this->ba_ptr[this->ba_size] = val;
this->ba_size += 1; this->ba_size += 1;
}; };
T &operator[](size_t index) { T& operator[](size_t index)
{
return this->ba_ptr[index]; return this->ba_ptr[index];
}; };
const T &operator[](size_t index) const { const T& operator[](size_t index) const
{
return this->ba_ptr[index]; return this->ba_ptr[index];
}; };
T &back() { T& back()
{
return this->ba_ptr[this->ba_size - 1]; return this->ba_ptr[this->ba_size - 1];
} }
typedef T *iterator; typedef T* iterator;
iterator begin() { iterator begin()
{
return this->ba_ptr; return this->ba_ptr;
}; };
iterator end() { iterator end()
{
return this->ba_ptr + this->ba_size; return this->ba_ptr + this->ba_size;
}; };
T *ba_ptr; T* ba_ptr;
size_t ba_size; size_t ba_size;
size_t ba_capacity; size_t ba_capacity;
}; };

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,21 +32,24 @@
#ifndef lnav_bin2c_hh #ifndef lnav_bin2c_hh
#define lnav_bin2c_hh #define lnav_bin2c_hh
#include <zlib.h> #include <memory>
#include <assert.h> #include <assert.h>
#include <sys/types.h> #include <sys/types.h>
#include <zlib.h>
#include <memory>
#include "base/intern_string.hh" #include "base/intern_string.hh"
struct bin_src_file { struct bin_src_file {
bin_src_file(const char *name, const unsigned char *data, bin_src_file(const char* name,
size_t compressed_size, size_t size) const unsigned char* data,
size_t compressed_size,
size_t size)
: bsf_name(name), bsf_data(new unsigned char[size + 1]), bsf_size(size) : bsf_name(name), bsf_data(new unsigned char[size + 1]), bsf_size(size)
{ {
uLongf zsize = size; uLongf zsize = size;
auto rc = uncompress(this->bsf_data.get(), &zsize, data, compressed_size); auto rc
= uncompress(this->bsf_data.get(), &zsize, data, compressed_size);
assert(rc == Z_OK); assert(rc == Z_OK);
assert(zsize == size); assert(zsize == size);
this->bsf_data[size] = '\0'; this->bsf_data[size] = '\0';
@ -57,13 +60,13 @@ struct bin_src_file {
return string_fragment{this->bsf_data.get(), 0, (int) this->bsf_size}; return string_fragment{this->bsf_data.get(), 0, (int) this->bsf_size};
} }
const char *get_name() const const char* get_name() const
{ {
return this->bsf_name; return this->bsf_name;
} }
private: private:
const char *bsf_name; const char* bsf_name;
std::unique_ptr<unsigned char[]> bsf_data; std::unique_ptr<unsigned char[]> bsf_data;
ssize_t bsf_size; ssize_t bsf_size;
}; };

@ -21,18 +21,18 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file bookmarks.cc * @file bookmarks.cc
*/ */
#include "config.h"
#include "bookmarks.hh" #include "bookmarks.hh"
#include "config.h"
using namespace std; using namespace std;
set<string> bookmark_metadata::KNOWN_TAGS; set<string> bookmark_metadata::KNOWN_TAGS;

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,11 +32,11 @@
#ifndef bookmarks_hh #ifndef bookmarks_hh
#define bookmarks_hh #define bookmarks_hh
#include <algorithm>
#include <map> #include <map>
#include <set> #include <set>
#include <string> #include <string>
#include <vector> #include <vector>
#include <algorithm>
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
@ -47,18 +47,18 @@ struct bookmark_metadata {
std::string bm_comment; std::string bm_comment;
std::vector<std::string> bm_tags; std::vector<std::string> bm_tags;
void add_tag(const std::string &tag) { void add_tag(const std::string& tag)
if (std::find(this->bm_tags.begin(), {
this->bm_tags.end(), if (std::find(this->bm_tags.begin(), this->bm_tags.end(), tag)
tag) == this->bm_tags.end()) { == this->bm_tags.end())
{
this->bm_tags.push_back(tag); this->bm_tags.push_back(tag);
} }
}; };
bool remove_tag(const std::string &tag) { bool remove_tag(const std::string& tag)
auto iter = std::find(this->bm_tags.begin(), {
this->bm_tags.end(), auto iter = std::find(this->bm_tags.begin(), this->bm_tags.end(), tag);
tag);
bool retval = false; bool retval = false;
if (iter != this->bm_tags.end()) { if (iter != this->bm_tags.end()) {
@ -68,13 +68,14 @@ struct bookmark_metadata {
return retval; return retval;
}; };
bool empty() const { bool empty() const
return this->bm_name.empty() && {
this->bm_comment.empty() && return this->bm_name.empty() && this->bm_comment.empty()
this->bm_tags.empty(); && this->bm_tags.empty();
}; };
void clear() { void clear()
{
this->bm_comment.clear(); this->bm_comment.clear();
this->bm_tags.clear(); this->bm_tags.clear();
}; };
@ -98,9 +99,9 @@ class bookmark_vector : public std::vector<LineType> {
typedef std::vector<LineType> base_vector; typedef std::vector<LineType> base_vector;
public: public:
typedef typename base_vector::size_type size_type; typedef typename base_vector::size_type size_type;
typedef typename base_vector::iterator iterator; typedef typename base_vector::iterator iterator;
typedef typename base_vector::const_iterator const_iterator; typedef typename base_vector::const_iterator const_iterator;
/** /**
* Insert a bookmark into this vector, but only if it is not already in the * Insert a bookmark into this vector, but only if it is not already in the
@ -118,15 +119,15 @@ public:
if (lb == this->end() || *lb != vl) { if (lb == this->end() || *lb != vl) {
this->insert(lb, vl); this->insert(lb, vl);
retval = this->end(); retval = this->end();
} } else {
else {
retval = lb; retval = lb;
} }
return retval; return retval;
}; };
std::pair<iterator, iterator> equal_range(LineType start, LineType stop) { std::pair<iterator, iterator> equal_range(LineType start, LineType stop)
{
auto lb = std::lower_bound(this->begin(), this->end(), start); auto lb = std::lower_bound(this->begin(), this->end(), start);
if (stop == LineType(-1)) { if (stop == LineType(-1)) {
@ -163,19 +164,22 @@ public:
*/ */
class bookmark_type_t { class bookmark_type_t {
public: public:
typedef std::vector<bookmark_type_t *>::iterator type_iterator; typedef std::vector<bookmark_type_t*>::iterator type_iterator;
static type_iterator type_begin() { static type_iterator type_begin()
{
return get_all_types().begin(); return get_all_types().begin();
}; };
static type_iterator type_end() { static type_iterator type_end()
{
return get_all_types().end(); return get_all_types().end();
}; };
static bookmark_type_t *find_type(const std::string &name) { static bookmark_type_t* find_type(const std::string& name)
{
auto iter = find_if(type_begin(), type_end(), mark_eq(name)); auto iter = find_if(type_begin(), type_end(), mark_eq(name));
bookmark_type_t *retval = nullptr; bookmark_type_t* retval = nullptr;
if (iter != type_end()) { if (iter != type_end()) {
retval = (*iter); retval = (*iter);
@ -183,36 +187,41 @@ public:
return retval; return retval;
}; };
static std::vector<bookmark_type_t *> &get_all_types() { static std::vector<bookmark_type_t*>& get_all_types()
static std::vector<bookmark_type_t *> all_types; {
static std::vector<bookmark_type_t*> all_types;
return all_types; return all_types;
}; };
explicit bookmark_type_t(std::string name) : bt_name(std::move(name)) { explicit bookmark_type_t(std::string name) : bt_name(std::move(name))
{
get_all_types().push_back(this); get_all_types().push_back(this);
}; };
const std::string &get_name() const { const std::string& get_name() const
{
return this->bt_name; return this->bt_name;
}; };
private: private:
struct mark_eq { struct mark_eq {
explicit mark_eq(const std::string &name) : me_name(name) { }; explicit mark_eq(const std::string& name) : me_name(name){};
bool operator()(bookmark_type_t *bt) { bool operator()(bookmark_type_t* bt)
{
return bt->bt_name == this->me_name; return bt->bt_name == this->me_name;
}; };
const std::string &me_name; const std::string& me_name;
}; };
const std::string bt_name; const std::string bt_name;
}; };
template<typename LineType> template<typename LineType>
LineType bookmark_vector<LineType>::next(LineType start) const LineType
bookmark_vector<LineType>::next(LineType start) const
{ {
LineType retval(-1); LineType retval(-1);
@ -229,7 +238,8 @@ LineType bookmark_vector<LineType>::next(LineType start) const
} }
template<typename LineType> template<typename LineType>
LineType bookmark_vector<LineType>::prev(LineType start) const LineType
bookmark_vector<LineType>::prev(LineType start) const
{ {
LineType retval(-1); LineType retval(-1);
@ -237,7 +247,7 @@ LineType bookmark_vector<LineType>::prev(LineType start) const
auto lb = lower_bound(this->cbegin(), this->cend(), start); auto lb = lower_bound(this->cbegin(), this->cend(), start);
if (lb != this->cbegin()) { if (lb != this->cbegin()) {
lb -= 1; lb -= 1;
retval = *lb; retval = *lb;
} }
@ -251,7 +261,7 @@ LineType bookmark_vector<LineType>::prev(LineType start) const
*/ */
template<typename LineType> template<typename LineType>
struct bookmarks { struct bookmarks {
typedef std::map<bookmark_type_t *, bookmark_vector<LineType> > type; typedef std::map<const bookmark_type_t*, bookmark_vector<LineType> > type;
}; };
#endif #endif

@ -21,16 +21,15 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include "bottom_status_source.hh" #include "bottom_status_source.hh"
#include "config.h"
bottom_status_source::bottom_status_source() bottom_status_source::bottom_status_source()
{ {
@ -59,22 +58,22 @@ bottom_status_source::bottom_status_source()
this->bss_line_error.set_share(1); this->bss_line_error.set_share(1);
} }
void bottom_status_source::update_line_number(listview_curses *lc) void
bottom_status_source::update_line_number(listview_curses* lc)
{ {
status_field &sf = this->bss_fields[BSF_LINE_NUMBER]; status_field& sf = this->bss_fields[BSF_LINE_NUMBER];
if (lc->get_inner_height() == 0) { if (lc->get_inner_height() == 0) {
sf.set_value(" L0"); sf.set_value(" L0");
} } else {
else {
sf.set_value(" L%'d", (int) lc->get_top()); sf.set_value(" L%'d", (int) lc->get_top());
} }
if (lc->get_inner_height() > 0) { if (lc->get_inner_height() > 0) {
std::vector<attr_line_t> rows(1); std::vector<attr_line_t> rows(1);
lc->get_data_source()-> lc->get_data_source()->listview_value_for_rows(
listview_value_for_rows(*lc, lc->get_top(), rows); *lc, lc->get_top(), rows);
auto& sa = rows[0].get_attrs(); auto& sa = rows[0].get_attrs();
auto iter = find_string_attr(sa, &SA_ERROR); auto iter = find_string_attr(sa, &SA_ERROR);
if (iter != sa.end()) { if (iter != sa.end()) {
@ -87,9 +86,10 @@ void bottom_status_source::update_line_number(listview_curses *lc)
} }
} }
void bottom_status_source::update_search_term(textview_curses &tc) void
bottom_status_source::update_search_term(textview_curses& tc)
{ {
auto &sf = this->bss_fields[BSF_SEARCH_TERM]; auto& sf = this->bss_fields[BSF_SEARCH_TERM];
auto search_term = tc.get_current_search(); auto search_term = tc.get_current_search();
if (search_term.empty()) { if (search_term.empty()) {
@ -102,74 +102,73 @@ void bottom_status_source::update_search_term(textview_curses &tc)
this->update_loading(0, 0); this->update_loading(0, 0);
} }
void bottom_status_source::update_percent(listview_curses *lc) void
bottom_status_source::update_percent(listview_curses* lc)
{ {
status_field &sf = this->bss_fields[BSF_PERCENT]; status_field& sf = this->bss_fields[BSF_PERCENT];
vis_line_t top = lc->get_top(); vis_line_t top = lc->get_top();
vis_line_t bottom, height; vis_line_t bottom, height;
unsigned long width; unsigned long width;
double percent; double percent;
lc->get_dimensions(height, width); lc->get_dimensions(height, width);
if (lc->get_inner_height() > 0) { if (lc->get_inner_height() > 0) {
bottom = std::min(top + height - vis_line_t(1), bottom = std::min(top + height - vis_line_t(1),
vis_line_t(lc->get_inner_height() - 1)); vis_line_t(lc->get_inner_height() - 1));
percent = (double)(bottom + 1); percent = (double) (bottom + 1);
percent /= (double)lc->get_inner_height(); percent /= (double) lc->get_inner_height();
percent *= 100.0; percent *= 100.0;
} } else {
else {
percent = 0.0; percent = 0.0;
} }
sf.set_value("%3d%% ", (int)percent); sf.set_value("%3d%% ", (int) percent);
} }
void bottom_status_source::update_marks(listview_curses *lc) void
bottom_status_source::update_marks(listview_curses* lc)
{ {
auto *tc = static_cast<textview_curses *>(lc); auto* tc = static_cast<textview_curses*>(lc);
vis_bookmarks &bm = tc->get_bookmarks(); vis_bookmarks& bm = tc->get_bookmarks();
status_field &sf = this->bss_fields[BSF_HITS]; status_field& sf = this->bss_fields[BSF_HITS];
if (bm.find(&textview_curses::BM_SEARCH) != bm.end()) { if (bm.find(&textview_curses::BM_SEARCH) != bm.end()) {
bookmark_vector<vis_line_t> &bv = bm[&textview_curses::BM_SEARCH]; bookmark_vector<vis_line_t>& bv = bm[&textview_curses::BM_SEARCH];
if (!bv.empty() || !tc->get_current_search().empty()) { if (!bv.empty() || !tc->get_current_search().empty()) {
bookmark_vector<vis_line_t>::iterator lb; bookmark_vector<vis_line_t>::iterator lb;
lb = std::lower_bound(bv.begin(), bv.end(), tc->get_top()); lb = std::lower_bound(bv.begin(), bv.end(), tc->get_top());
if (lb != bv.end() && *lb == tc->get_top()) { if (lb != bv.end() && *lb == tc->get_top()) {
sf.set_value( sf.set_value(" Hit %'d of %'d for ",
" Hit %'d of %'d for ", std::distance(bv.begin(), lb) + 1,
std::distance(bv.begin(), lb) + 1, tc->get_match_count()); tc->get_match_count());
} else { } else {
sf.set_value(" %'d hits for ", tc->get_match_count()); sf.set_value(" %'d hits for ", tc->get_match_count());
} }
} else { } else {
sf.clear(); sf.clear();
} }
} } else {
else {
sf.clear(); sf.clear();
} }
} }
void bottom_status_source::update_hits(textview_curses *tc) void
bottom_status_source::update_hits(textview_curses* tc)
{ {
status_field & sf = this->bss_fields[BSF_HITS]; status_field& sf = this->bss_fields[BSF_HITS];
view_colors::role_t new_role; view_colors::role_t new_role;
if (tc->is_searching()) { if (tc->is_searching()) {
this->bss_hit_spinner += 1; this->bss_hit_spinner += 1;
if (this->bss_hit_spinner % 2) { if (this->bss_hit_spinner % 2) {
new_role = view_colors::VCR_ACTIVE_STATUS; new_role = view_colors::VCR_ACTIVE_STATUS;
} } else {
else{
new_role = view_colors::VCR_ACTIVE_STATUS2; new_role = view_colors::VCR_ACTIVE_STATUS2;
} }
sf.set_cylon(true); sf.set_cylon(true);
} } else {
else {
new_role = view_colors::VCR_STATUS; new_role = view_colors::VCR_STATUS;
sf.set_cylon(false); sf.set_cylon(false);
} }
@ -178,13 +177,14 @@ void bottom_status_source::update_hits(textview_curses *tc)
this->update_marks(tc); this->update_marks(tc);
} }
void bottom_status_source::update_loading(file_off_t off, file_size_t total) void
bottom_status_source::update_loading(file_off_t off, file_size_t total)
{ {
auto &sf = this->bss_fields[BSF_LOADING]; auto& sf = this->bss_fields[BSF_LOADING];
require(off >= 0); require(off >= 0);
if (total == 0 || (size_t)off == total) { if (total == 0 || (size_t) off == total) {
sf.set_cylon(false); sf.set_cylon(false);
sf.set_role(view_colors::VCR_STATUS); sf.set_role(view_colors::VCR_STATUS);
if (this->bss_paused) { if (this->bss_paused) {
@ -192,9 +192,8 @@ void bottom_status_source::update_loading(file_off_t off, file_size_t total)
} else { } else {
sf.clear(); sf.clear();
} }
} } else {
else { int pct = (int) (((double) off / (double) total) * 100.0);
int pct = (int)(((double)off / (double)total) * 100.0);
if (this->bss_load_percent != pct) { if (this->bss_load_percent != pct) {
this->bss_load_percent = pct; this->bss_load_percent = pct;
@ -206,34 +205,32 @@ void bottom_status_source::update_loading(file_off_t off, file_size_t total)
} }
} }
size_t bottom_status_source::statusview_fields() size_t
bottom_status_source::statusview_fields()
{ {
size_t retval; size_t retval;
if (this->bss_prompt.empty() && if (this->bss_prompt.empty() && this->bss_error.empty()
this->bss_error.empty() && && this->bss_line_error.empty())
this->bss_line_error.empty()) { {
retval = BSF__MAX; retval = BSF__MAX;
} } else {
else{
retval = 1; retval = 1;
} }
return retval; return retval;
} }
status_field &bottom_status_source::statusview_value_for_field(int field) status_field&
bottom_status_source::statusview_value_for_field(int field)
{ {
if (!this->bss_error.empty()) { if (!this->bss_error.empty()) {
return this->bss_error; return this->bss_error;
} } else if (!this->bss_prompt.empty()) {
else if (!this->bss_prompt.empty()) {
return this->bss_prompt; return this->bss_prompt;
} } else if (!this->bss_line_error.empty()) {
else if (!this->bss_line_error.empty()) {
return this->bss_line_error; return this->bss_line_error;
} } else {
else { return this->get_field((field_t) field);
return this->get_field((field_t)field);
} }
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -33,12 +33,12 @@
#include <string> #include <string>
#include "grep_proc.hh" #include "grep_proc.hh"
#include "textview_curses.hh"
#include "statusview_curses.hh" #include "statusview_curses.hh"
#include "textview_curses.hh"
class bottom_status_source class bottom_status_source
: public status_data_source, : public status_data_source
public grep_proc_control { , public grep_proc_control {
public: public:
typedef enum { typedef enum {
BSF_LINE_NUMBER, BSF_LINE_NUMBER,
@ -53,9 +53,12 @@ public:
bottom_status_source(); bottom_status_source();
status_field &get_field(field_t id) { return this->bss_fields[id]; }; status_field& get_field(field_t id)
{
return this->bss_fields[id];
};
void set_prompt(const std::string &prompt) void set_prompt(const std::string& prompt)
{ {
this->bss_prompt.set_value(prompt); this->bss_prompt.set_value(prompt);
}; };
@ -67,17 +70,17 @@ public:
size_t statusview_fields() override; size_t statusview_fields() override;
status_field &statusview_value_for_field(int field) override; status_field& statusview_value_for_field(int field) override;
void update_line_number(listview_curses *lc); void update_line_number(listview_curses* lc);
void update_search_term(textview_curses &tc); void update_search_term(textview_curses& tc);
void update_percent(listview_curses *lc); void update_percent(listview_curses* lc);
void update_marks(listview_curses *lc); void update_marks(listview_curses* lc);
void update_hits(textview_curses *tc); void update_hits(textview_curses* tc);
void update_loading(file_off_t off, file_size_t total); void update_loading(file_off_t off, file_size_t total);
@ -86,9 +89,9 @@ private:
status_field bss_error{1024, view_colors::VCR_ALERT_STATUS}; status_field bss_error{1024, view_colors::VCR_ALERT_STATUS};
status_field bss_line_error{1024, view_colors::VCR_ALERT_STATUS}; status_field bss_line_error{1024, view_colors::VCR_ALERT_STATUS};
status_field bss_fields[BSF__MAX]; status_field bss_fields[BSF__MAX];
int bss_hit_spinner{0}; int bss_hit_spinner{0};
int bss_load_percent{0}; int bss_load_percent{0};
bool bss_paused{false}; bool bss_paused{false};
}; };
#endif #endif

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,10 +32,13 @@
#ifndef lnav_bound_tags_hh #ifndef lnav_bound_tags_hh
#define lnav_bound_tags_hh #define lnav_bound_tags_hh
struct last_relative_time_tag {}; struct last_relative_time_tag {
};
struct sqlite_db_tag {}; struct sqlite_db_tag {
};
struct sql_cmd_map_tag {}; struct sql_cmd_map_tag {
};
#endif #endif

@ -21,21 +21,21 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef byte_array_hh #ifndef byte_array_hh
#define byte_array_hh #define byte_array_hh
#include <ostream>
#include <string>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <string>
#include <ostream>
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
template<size_t COUNT, typename T = unsigned char> template<size_t COUNT, typename T = unsigned char>
@ -43,24 +43,24 @@ struct byte_array {
static constexpr size_t BYTE_COUNT = COUNT * sizeof(T); static constexpr size_t BYTE_COUNT = COUNT * sizeof(T);
static constexpr size_t STRING_SIZE = BYTE_COUNT * 2 + 1; static constexpr size_t STRING_SIZE = BYTE_COUNT * 2 + 1;
byte_array() { }; byte_array(){};
byte_array(const byte_array &other) byte_array(const byte_array& other)
{ {
memcpy(this->ba_data, other.ba_data, BYTE_COUNT); memcpy(this->ba_data, other.ba_data, BYTE_COUNT);
}; };
bool operator<(const byte_array &other) const bool operator<(const byte_array& other) const
{ {
return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) < 0; return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) < 0;
}; };
bool operator!=(const byte_array &other) const bool operator!=(const byte_array& other) const
{ {
return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) != 0; return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) != 0;
}; };
bool operator==(const byte_array &other) const bool operator==(const byte_array& other) const
{ {
return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) == 0; return memcmp(this->ba_data, other.ba_data, BYTE_COUNT) == 0;
}; };
@ -70,7 +70,7 @@ struct byte_array {
memset(this->ba_data, 0, BYTE_COUNT); memset(this->ba_data, 0, BYTE_COUNT);
}; };
void to_string(char *buffer) const void to_string(char* buffer) const
{ {
require(buffer != nullptr); require(buffer != nullptr);
@ -87,10 +87,14 @@ struct byte_array {
return std::string(buffer); return std::string(buffer);
} }
const unsigned char *in() const { return this->ba_data; }; const unsigned char* in() const
{
return this->ba_data;
};
T *out(int offset = 0) { T* out(int offset = 0)
T *ptr = (T *)this->ba_data; {
T* ptr = (T*) this->ba_data;
return &ptr[offset]; return &ptr[offset];
}; };
@ -99,7 +103,8 @@ struct byte_array {
}; };
template<size_t COUNT, typename T = unsigned char> template<size_t COUNT, typename T = unsigned char>
std::ostream& operator<<(std::ostream& os, const byte_array<COUNT, T>& ba) std::ostream&
operator<<(std::ostream& os, const byte_array<COUNT, T>& ba)
{ {
os << ba.to_string(); os << ba.to_string();
return os; return os;

@ -21,34 +21,33 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file logfile_sub_source.hh * @file logfile_sub_source.hh
*/ */
#include "config.h"
#include <string.h>
#include <sqlite3.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sqlite3.h>
#include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
#include "log_level.hh"
#include "base/strnatcmp.h" #include "base/strnatcmp.h"
#include "config.h"
#include "log_level.hh"
#define MAX_ADDR_LEN 128 #define MAX_ADDR_LEN 128
static int try_inet_pton(int p_len, const char *p, char *n) static int
try_inet_pton(int p_len, const char* p, char* n)
{ {
static int ADDR_FAMILIES[] = { AF_INET, AF_INET6 }; static int ADDR_FAMILIES[] = {AF_INET, AF_INET6};
char buf[MAX_ADDR_LEN + 1]; char buf[MAX_ADDR_LEN + 1];
int retval = AF_MAX; int retval = AF_MAX;
strncpy(buf, p, p_len); strncpy(buf, p, p_len);
buf[p_len] = '\0'; buf[p_len] = '\0';
@ -62,13 +61,14 @@ static int try_inet_pton(int p_len, const char *p, char *n)
return retval; return retval;
} }
static int convert_v6_to_v4(int family, char *n) static int
convert_v6_to_v4(int family, char* n)
{ {
struct in6_addr *ia = (struct in6_addr *)n; struct in6_addr* ia = (struct in6_addr*) n;
if (family == AF_INET6 && if (family == AF_INET6
(IN6_IS_ADDR_V4COMPAT(ia) || && (IN6_IS_ADDR_V4COMPAT(ia) || IN6_IS_ADDR_V4MAPPED(ia)))
IN6_IS_ADDR_V4MAPPED(ia))) { {
family = AF_INET; family = AF_INET;
memmove(n, n + 12, sizeof(struct in_addr)); memmove(n, n + 12, sizeof(struct in_addr));
} }
@ -76,15 +76,12 @@ static int convert_v6_to_v4(int family, char *n)
return family; return family;
} }
static static int
int ipaddress(void *ptr, ipaddress(void* ptr, int a_len, const void* a_in, int b_len, const void* b_in)
int a_len, const void *a_in,
int b_len, const void *b_in)
{ {
char a_addr[sizeof(struct in6_addr)], char a_addr[sizeof(struct in6_addr)], b_addr[sizeof(struct in6_addr)];
b_addr[sizeof(struct in6_addr)]; const char *a_str = (const char*) a_in, *b_str = (const char*) b_in;
const char *a_str = (const char *)a_in, *b_str = (const char *)b_in; int a_family, b_family, retval;
int a_family, b_family, retval;
if ((a_len > MAX_ADDR_LEN) || (b_len > MAX_ADDR_LEN)) { if ((a_len > MAX_ADDR_LEN) || (b_len > MAX_ADDR_LEN)) {
return strnatcasecmp(a_len, a_str, b_len, b_str); return strnatcasecmp(a_len, a_str, b_len, b_str);
@ -100,26 +97,21 @@ int ipaddress(void *ptr,
if (a_family == AF_MAX && b_family == AF_MAX) { if (a_family == AF_MAX && b_family == AF_MAX) {
return strnatcasecmp(a_len, a_str, b_len, b_str); return strnatcasecmp(a_len, a_str, b_len, b_str);
} } else if (a_family == AF_MAX && b_family != AF_MAX) {
else if (a_family == AF_MAX && b_family != AF_MAX) {
retval = -1; retval = -1;
} } else if (a_family != AF_MAX && b_family == AF_MAX) {
else if (a_family != AF_MAX && b_family == AF_MAX) {
retval = 1; retval = 1;
} } else {
else {
a_family = convert_v6_to_v4(a_family, a_addr); a_family = convert_v6_to_v4(a_family, a_addr);
b_family = convert_v6_to_v4(b_family, b_addr); b_family = convert_v6_to_v4(b_family, b_addr);
if (a_family == b_family) { if (a_family == b_family) {
retval = memcmp(a_addr, b_addr, retval = memcmp(a_addr,
a_family == AF_INET ? b_addr,
sizeof(struct in_addr) : a_family == AF_INET ? sizeof(struct in_addr)
sizeof(struct in6_addr)); : sizeof(struct in6_addr));
} } else if (a_family == AF_INET) {
else if (a_family == AF_INET) {
retval = -1; retval = -1;
} } else {
else {
retval = 1; retval = 1;
} }
} }
@ -127,40 +119,37 @@ int ipaddress(void *ptr,
return retval; return retval;
} }
static static int
int sql_strnatcmp(void *ptr, sql_strnatcmp(
int a_len, const void *a_in, void* ptr, int a_len, const void* a_in, int b_len, const void* b_in)
int b_len, const void *b_in)
{ {
return strnatcmp(a_len, (char *)a_in, b_len, (char *)b_in); return strnatcmp(a_len, (char*) a_in, b_len, (char*) b_in);
} }
static static int
int sql_strnatcasecmp(void *ptr, sql_strnatcasecmp(
int a_len, const void *a_in, void* ptr, int a_len, const void* a_in, int b_len, const void* b_in)
int b_len, const void *b_in)
{ {
return strnatcasecmp(a_len, (char *)a_in, b_len, (char *)b_in); return strnatcasecmp(a_len, (char*) a_in, b_len, (char*) b_in);
} }
static static int
int sql_loglevelcmp(void *ptr, sql_loglevelcmp(
int a_len, const void *a_in, void* ptr, int a_len, const void* a_in, int b_len, const void* b_in)
int b_len, const void *b_in)
{ {
return levelcmp((const char *)a_in, a_len, return levelcmp((const char*) a_in, a_len, (const char*) b_in, b_len);
(const char *)b_in, b_len);
} }
int register_collation_functions(sqlite3 *db) int
register_collation_functions(sqlite3* db)
{ {
sqlite3_create_collation(db, "ipaddress", SQLITE_UTF8, nullptr, ipaddress); sqlite3_create_collation(db, "ipaddress", SQLITE_UTF8, nullptr, ipaddress);
sqlite3_create_collation(db, "naturalcase", SQLITE_UTF8, nullptr, sqlite3_create_collation(
sql_strnatcmp); db, "naturalcase", SQLITE_UTF8, nullptr, sql_strnatcmp);
sqlite3_create_collation(db, "naturalnocase", SQLITE_UTF8, nullptr, sqlite3_create_collation(
sql_strnatcasecmp); db, "naturalnocase", SQLITE_UTF8, nullptr, sql_strnatcasecmp);
sqlite3_create_collation(db, "loglevel", SQLITE_UTF8, nullptr, sqlite3_create_collation(
sql_loglevelcmp); db, "loglevel", SQLITE_UTF8, nullptr, sql_loglevelcmp);
return 0; return 0;
} }

@ -21,52 +21,55 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file column_namer.cc * @file column_namer.cc
*/ */
#include "config.h"
#include <algorithm> #include <algorithm>
#include "base/string_util.hh" #include "column_namer.hh"
#include "sql_util.hh"
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "column_namer.hh" #include "base/string_util.hh"
#include "config.h"
#include "sql_util.hh"
bool column_namer::existing_name(const std::string &in_name) const bool
column_namer::existing_name(const std::string& in_name) const
{ {
if (std::binary_search(std::begin(sql_keywords), if (std::binary_search(
std::end(sql_keywords), std::begin(sql_keywords), std::end(sql_keywords), toupper(in_name)))
toupper(in_name))) { {
return true; return true;
} }
if (std::find(this->cn_builtin_names.begin(), if (std::find(this->cn_builtin_names.begin(),
this->cn_builtin_names.end(), this->cn_builtin_names.end(),
in_name) != this->cn_builtin_names.end()) { in_name)
!= this->cn_builtin_names.end())
{
return true; return true;
} }
if (std::find(this->cn_names.begin(), if (std::find(this->cn_names.begin(), this->cn_names.end(), in_name)
this->cn_names.end(), != this->cn_names.end())
in_name) != this->cn_names.end()) { {
return true; return true;
} }
return false; return false;
} }
std::string column_namer::add_column(const std::string &in_name) std::string
column_namer::add_column(const std::string& in_name)
{ {
std::string base_name = in_name, retval; std::string base_name = in_name, retval;
size_t buf_size; size_t buf_size;
int num = 0; int num = 0;
buf_size = in_name.length() + 64; buf_size = in_name.length() + 64;
char buffer[buf_size]; char buffer[buf_size];

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -33,16 +33,16 @@
#define lnav_column_namer_hh #define lnav_column_namer_hh
#include <string> #include <string>
#include <vector>
#include <unordered_map> #include <unordered_map>
#include <vector>
class column_namer { class column_namer {
public: public:
column_namer() : cn_builtin_names({"col"}) {} column_namer() : cn_builtin_names({"col"}) {}
bool existing_name(const std::string &in_name) const; bool existing_name(const std::string& in_name) const;
std::string add_column(const std::string &in_name); std::string add_column(const std::string& in_name);
std::vector<std::string> cn_builtin_names; std::vector<std::string> cn_builtin_names;
std::vector<std::string> cn_names; std::vector<std::string> cn_names;

@ -21,32 +21,31 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <vector> #include <vector>
#include "command_executor.hh"
#include "base/fs_util.hh" #include "base/fs_util.hh"
#include "base/string_util.hh"
#include "base/injector.hh" #include "base/injector.hh"
#include "yajlpp/json_ptr.hh" #include "base/string_util.hh"
#include "bound_tags.hh"
#include "config.h"
#include "db_sub_source.hh"
#include "lnav.hh" #include "lnav.hh"
#include "lnav_config.hh"
#include "lnav_util.hh"
#include "log_format_loader.hh" #include "log_format_loader.hh"
#include "papertrail_proc.hh"
#include "service_tags.hh"
#include "shlex.hh" #include "shlex.hh"
#include "lnav_util.hh"
#include "sql_util.hh" #include "sql_util.hh"
#include "lnav_config.hh" #include "yajlpp/json_ptr.hh"
#include "service_tags.hh"
#include "bound_tags.hh"
#include "command_executor.hh"
#include "db_sub_source.hh"
#include "papertrail_proc.hh"
using namespace std; using namespace std;
@ -59,12 +58,13 @@ SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format
ORDER BY total DESC ORDER BY total DESC
)"; )";
int sql_progress(const struct log_cursor &lc) int
sql_progress(const struct log_cursor& lc)
{ {
static sig_atomic_t sql_counter = 0; static sig_atomic_t sql_counter = 0;
size_t total = lnav_data.ld_log_source.text_line_count(); size_t total = lnav_data.ld_log_source.text_line_count();
off_t off = lc.lc_curr_line; off_t off = lc.lc_curr_line;
if (off < 0) { if (off < 0) {
return 0; return 0;
@ -89,7 +89,8 @@ int sql_progress(const struct log_cursor &lc)
return 0; return 0;
} }
void sql_progress_finished() void
sql_progress_finished()
{ {
if (lnav_data.ld_window == nullptr) { if (lnav_data.ld_window == nullptr) {
return; return;
@ -102,9 +103,14 @@ void sql_progress_finished()
lnav_data.ld_views[LNV_DB].redo_search(); lnav_data.ld_views[LNV_DB].redo_search();
} }
Result<string, string> execute_from_file(exec_context &ec, const ghc::filesystem::path &path, int line_number, char mode, const string &cmdline); Result<string, string> execute_from_file(exec_context& ec,
const ghc::filesystem::path& path,
int line_number,
char mode,
const string& cmdline);
Result<string, string> execute_command(exec_context &ec, const string &cmdline) Result<string, string>
execute_command(exec_context& ec, const string& cmdline)
{ {
vector<string> args; vector<string> args;
@ -117,8 +123,7 @@ Result<string, string> execute_command(exec_context &ec, const string &cmdline)
if ((iter = lnav_commands.find(args[0])) == lnav_commands.end()) { if ((iter = lnav_commands.find(args[0])) == lnav_commands.end()) {
return ec.make_error("unknown command - {}", args[0]); return ec.make_error("unknown command - {}", args[0]);
} } else {
else {
return iter->second->c_func(ec, cmdline, args); return iter->second->c_func(ec, cmdline, args);
} }
} }
@ -126,9 +131,10 @@ Result<string, string> execute_command(exec_context &ec, const string &cmdline)
return ec.make_error("no command to execute"); return ec.make_error("no command to execute");
} }
Result<string, string> execute_sql(exec_context &ec, const string &sql, string &alt_msg) Result<string, string>
execute_sql(exec_context& ec, const string& sql, string& alt_msg)
{ {
db_label_source &dls = lnav_data.ld_db_row_source; db_label_source& dls = lnav_data.ld_db_row_source;
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize); auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
struct timeval start_tv, end_tv; struct timeval start_tv, end_tv;
string stmt_str = trim(sql); string stmt_str = trim(sql);
@ -143,8 +149,8 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
vector<string> args; vector<string> args;
split_ws(stmt_str, args); split_ws(stmt_str, args);
auto sql_cmd_map = injector::get< auto sql_cmd_map = injector::get<readline_context::command_map_t*,
readline_context::command_map_t *, sql_cmd_map_tag>(); sql_cmd_map_tag>();
auto cmd_iter = sql_cmd_map->find(args[0]); auto cmd_iter = sql_cmd_map->find(args[0]);
if (cmd_iter != sql_cmd_map->end()) { if (cmd_iter != sql_cmd_map->end()) {
@ -159,40 +165,36 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
ec.ec_accumulator->clear(); ec.ec_accumulator->clear();
pair<string, int> source = ec.ec_source.top(); pair<string, int> source = ec.ec_source.top();
sql_progress_guard progress_guard(sql_progress, sql_progress_guard progress_guard(
sql_progress_finished, sql_progress, sql_progress_finished, source.first, source.second);
source.first,
source.second);
gettimeofday(&start_tv, nullptr); gettimeofday(&start_tv, nullptr);
retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(), retcode = sqlite3_prepare_v2(
stmt_str.c_str(), lnav_data.ld_db.in(), stmt_str.c_str(), -1, stmt.out(), nullptr);
-1,
stmt.out(),
nullptr);
if (retcode != SQLITE_OK) { if (retcode != SQLITE_OK) {
const char *errmsg = sqlite3_errmsg(lnav_data.ld_db); const char* errmsg = sqlite3_errmsg(lnav_data.ld_db);
alt_msg = ""; alt_msg = "";
return ec.make_error("{}", errmsg); return ec.make_error("{}", errmsg);
} } else if (stmt == nullptr) {
else if (stmt == nullptr) {
alt_msg = ""; alt_msg = "";
return ec.make_error("No statement given"); return ec.make_error("No statement given");
} }
#ifdef HAVE_SQLITE3_STMT_READONLY #ifdef HAVE_SQLITE3_STMT_READONLY
else if (ec.is_read_only() && !sqlite3_stmt_readonly(stmt.in())) { else if (ec.is_read_only() && !sqlite3_stmt_readonly(stmt.in()))
{
return ec.make_error( return ec.make_error(
"modifying statements are not allowed in this context: {}", sql); "modifying statements are not allowed in this context: {}", sql);
} }
#endif #endif
else { else
{
bool done = false; bool done = false;
int param_count; int param_count;
param_count = sqlite3_bind_parameter_count(stmt.in()); param_count = sqlite3_bind_parameter_count(stmt.in());
for (int lpc = 0; lpc < param_count; lpc++) { for (int lpc = 0; lpc < param_count; lpc++) {
map<string, string>::iterator ov_iter; map<string, string>::iterator ov_iter;
const char *name; const char* name;
name = sqlite3_bind_parameter_name(stmt.in(), lpc + 1); name = sqlite3_bind_parameter_name(stmt.in(), lpc + 1);
ov_iter = ec.ec_override.find(name); ov_iter = ec.ec_override.find(name);
@ -202,12 +204,11 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
ov_iter->second.c_str(), ov_iter->second.c_str(),
ov_iter->second.length(), ov_iter->second.length(),
SQLITE_TRANSIENT); SQLITE_TRANSIENT);
} } else if (name[0] == '$') {
else if (name[0] == '$') { const auto& lvars = ec.ec_local_vars.top();
const auto &lvars = ec.ec_local_vars.top(); const auto& gvars = ec.ec_global_vars;
const auto &gvars = ec.ec_global_vars;
map<string, string>::const_iterator local_var, global_var; map<string, string>::const_iterator local_var, global_var;
const char *env_value; const char* env_value;
if (lnav_data.ld_window) { if (lnav_data.ld_window) {
char buf[32]; char buf[32];
@ -216,45 +217,48 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
getmaxyx(lnav_data.ld_window, lines, cols); getmaxyx(lnav_data.ld_window, lines, cols);
if (strcmp(name, "$LINES") == 0) { if (strcmp(name, "$LINES") == 0) {
snprintf(buf, sizeof(buf), "%d", lines); snprintf(buf, sizeof(buf), "%d", lines);
sqlite3_bind_text(stmt.in(), lpc + 1, sqlite3_bind_text(
buf, -1, stmt.in(), lpc + 1, buf, -1, SQLITE_TRANSIENT);
SQLITE_TRANSIENT);
} else if (strcmp(name, "$COLS") == 0) { } else if (strcmp(name, "$COLS") == 0) {
snprintf(buf, sizeof(buf), "%d", cols); snprintf(buf, sizeof(buf), "%d", cols);
sqlite3_bind_text(stmt.in(), lpc + 1, sqlite3_bind_text(
buf, -1, stmt.in(), lpc + 1, buf, -1, SQLITE_TRANSIENT);
SQLITE_TRANSIENT);
} }
} }
if ((local_var = lvars.find(&name[1])) != lvars.end()) { if ((local_var = lvars.find(&name[1])) != lvars.end()) {
sqlite3_bind_text(stmt.in(), lpc + 1, sqlite3_bind_text(stmt.in(),
local_var->second.c_str(), -1, lpc + 1,
local_var->second.c_str(),
-1,
SQLITE_TRANSIENT); SQLITE_TRANSIENT);
} } else if ((global_var = gvars.find(&name[1])) != gvars.end()) {
else if ((global_var = gvars.find(&name[1])) != gvars.end()) { sqlite3_bind_text(stmt.in(),
sqlite3_bind_text(stmt.in(), lpc + 1, lpc + 1,
global_var->second.c_str(), -1, global_var->second.c_str(),
-1,
SQLITE_TRANSIENT); SQLITE_TRANSIENT);
} else if ((env_value = getenv(&name[1])) != nullptr) {
sqlite3_bind_text(
stmt.in(), lpc + 1, env_value, -1, SQLITE_STATIC);
} }
else if ((env_value = getenv(&name[1])) != nullptr) { } else if (name[0] == ':' && ec.ec_line_values != nullptr) {
sqlite3_bind_text(stmt.in(), lpc + 1, env_value, -1, SQLITE_STATIC);
}
}
else if (name[0] == ':' && ec.ec_line_values != nullptr) {
for (auto& lv : *ec.ec_line_values) { for (auto& lv : *ec.ec_line_values) {
if (lv.lv_meta.lvm_name != &name[1]) { if (lv.lv_meta.lvm_name != &name[1]) {
continue; continue;
} }
switch (lv.lv_meta.lvm_kind) { switch (lv.lv_meta.lvm_kind) {
case value_kind_t::VALUE_BOOLEAN: case value_kind_t::VALUE_BOOLEAN:
sqlite3_bind_int64(stmt.in(), lpc + 1, lv.lv_value.i); sqlite3_bind_int64(
stmt.in(), lpc + 1, lv.lv_value.i);
break; break;
case value_kind_t::VALUE_FLOAT: case value_kind_t::VALUE_FLOAT:
sqlite3_bind_double(stmt.in(), lpc + 1, lv.lv_value.d); sqlite3_bind_double(
stmt.in(), lpc + 1, lv.lv_value.d);
break; break;
case value_kind_t::VALUE_INTEGER: case value_kind_t::VALUE_INTEGER:
sqlite3_bind_int64(stmt.in(), lpc + 1, lv.lv_value.i); sqlite3_bind_int64(
stmt.in(), lpc + 1, lv.lv_value.i);
break; break;
case value_kind_t::VALUE_NULL: case value_kind_t::VALUE_NULL:
sqlite3_bind_null(stmt.in(), lpc + 1); sqlite3_bind_null(stmt.in(), lpc + 1);
@ -268,8 +272,7 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
break; break;
} }
} }
} } else {
else {
sqlite3_bind_null(stmt.in(), lpc + 1); sqlite3_bind_null(stmt.in(), lpc + 1);
log_warning("Could not bind variable: %s", name); log_warning("Could not bind variable: %s", name);
} }
@ -294,7 +297,7 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
break; break;
default: { default: {
const char *errmsg; const char* errmsg;
log_error("sqlite3_step error code: %d", retcode); log_error("sqlite3_step error code: %d", retcode);
errmsg = sqlite3_errmsg(lnav_data.ld_db); errmsg = sqlite3_errmsg(lnav_data.ld_db);
@ -304,12 +307,12 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
} }
} }
if (!dls.dls_rows.empty() && !ec.ec_local_vars.empty() && if (!dls.dls_rows.empty() && !ec.ec_local_vars.empty()
!ec.ec_dry_run) { && !ec.ec_dry_run) {
auto &vars = ec.ec_local_vars.top(); auto& vars = ec.ec_local_vars.top();
for (unsigned int lpc = 0; lpc < dls.dls_headers.size(); lpc++) { for (unsigned int lpc = 0; lpc < dls.dls_headers.size(); lpc++) {
const auto &column_name = dls.dls_headers[lpc].hm_name; const auto& column_name = dls.dls_headers[lpc].hm_name;
if (sql_ident_needs_quote(column_name.c_str())) { if (sql_ident_needs_quote(column_name.c_str())) {
continue; continue;
@ -342,8 +345,7 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
if (!ec.ec_accumulator->empty()) { if (!ec.ec_accumulator->empty()) {
retval = ec.ec_accumulator->get_string(); retval = ec.ec_accumulator->get_string();
} } else if (!dls.dls_rows.empty()) {
else if (!dls.dls_rows.empty()) {
if (lnav_data.ld_flags & LNF_HEADLESS) { if (lnav_data.ld_flags & LNF_HEADLESS) {
if (ec.ec_local_vars.size() == 1) { if (ec.ec_local_vars.size() == 1) {
ensure_view(&lnav_data.ld_views[LNV_DB]); ensure_view(&lnav_data.ld_views[LNV_DB]);
@ -351,14 +353,14 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
retval = ""; retval = "";
alt_msg = ""; alt_msg = "";
} } else if (dls.dls_rows.size() == 1) {
else if (dls.dls_rows.size() == 1) { auto& row = dls.dls_rows[0];
auto &row = dls.dls_rows[0];
if (dls.dls_headers.size() == 1) { if (dls.dls_headers.size() == 1) {
retval = row[0]; retval = row[0];
} else { } else {
for (unsigned int lpc = 0; lpc < dls.dls_headers.size(); lpc++) { for (unsigned int lpc = 0; lpc < dls.dls_headers.size();
lpc++) {
if (lpc > 0) { if (lpc > 0) {
retval.append("; "); retval.append("; ");
} }
@ -367,29 +369,31 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
retval.append(row[lpc]); retval.append(row[lpc]);
} }
} }
} } else {
else {
int row_count = dls.dls_rows.size(); int row_count = dls.dls_rows.size();
char row_count_buf[128]; char row_count_buf[128];
struct timeval diff_tv; struct timeval diff_tv;
timersub(&end_tv, &start_tv, &diff_tv); timersub(&end_tv, &start_tv, &diff_tv);
snprintf(row_count_buf, sizeof(row_count_buf), snprintf(row_count_buf,
ANSI_BOLD("%'d") " row%s matched in " sizeof(row_count_buf),
ANSI_BOLD("%ld.%03ld") " seconds", ANSI_BOLD("%'d") " row%s matched in " ANSI_BOLD(
"%ld.%03ld") " seconds",
row_count, row_count,
row_count == 1 ? "" : "s", row_count == 1 ? "" : "s",
diff_tv.tv_sec, diff_tv.tv_sec,
std::max((long) diff_tv.tv_usec / 1000, 1L)); std::max((long) diff_tv.tv_usec / 1000, 1L));
retval = row_count_buf; retval = row_count_buf;
alt_msg = HELP_MSG_2( alt_msg = HELP_MSG_2(
y, Y, y,
Y,
"to move forward/backward through query results " "to move forward/backward through query results "
"in the log view"); "in the log view");
} }
} }
#ifdef HAVE_SQLITE3_STMT_READONLY #ifdef HAVE_SQLITE3_STMT_READONLY
else if (sqlite3_stmt_readonly(stmt.in())) { else if (sqlite3_stmt_readonly(stmt.in()))
{
retval = "info: No rows matched"; retval = "info: No rows matched";
alt_msg = ""; alt_msg = "";
@ -405,25 +409,27 @@ Result<string, string> execute_sql(exec_context &ec, const string &sql, string &
return Ok(retval); return Ok(retval);
} }
static Result<string, string> execute_file_contents(exec_context &ec, const ghc::filesystem::path &path, bool multiline) static Result<string, string>
execute_file_contents(exec_context& ec,
const ghc::filesystem::path& path,
bool multiline)
{ {
static ghc::filesystem::path stdin_path("-"); static ghc::filesystem::path stdin_path("-");
static ghc::filesystem::path dev_stdin_path("/dev/stdin"); static ghc::filesystem::path dev_stdin_path("/dev/stdin");
string retval; string retval;
FILE *file; FILE* file;
if (path == stdin_path || path == dev_stdin_path) { if (path == stdin_path || path == dev_stdin_path) {
if (isatty(STDIN_FILENO)) { if (isatty(STDIN_FILENO)) {
return ec.make_error("stdin has already been consumed"); return ec.make_error("stdin has already been consumed");
} }
file = stdin; file = stdin;
} } else if ((file = fopen(path.c_str(), "r")) == nullptr) {
else if ((file = fopen(path.c_str(), "r")) == nullptr) {
return ec.make_error("unable to open file"); return ec.make_error("unable to open file");
} }
int line_number = 0, starting_line_number = 0; int line_number = 0, starting_line_number = 0;
auto_mem<char> line; auto_mem<char> line;
size_t line_max_size; size_t line_max_size;
ssize_t line_size; ssize_t line_size;
@ -448,7 +454,8 @@ static Result<string, string> execute_file_contents(exec_context &ec, const ghc:
case ';': case ';':
case '|': case '|':
if (mode) { if (mode) {
retval = TRY(execute_from_file(ec, path, starting_line_number, mode, trim(cmdline))); retval = TRY(execute_from_file(
ec, path, starting_line_number, mode, trim(cmdline)));
} }
starting_line_number = line_number; starting_line_number = line_number;
@ -458,17 +465,17 @@ static Result<string, string> execute_file_contents(exec_context &ec, const ghc:
default: default:
if (multiline) { if (multiline) {
cmdline += line; cmdline += line;
} } else {
else { retval = TRY(execute_from_file(
retval = TRY(execute_from_file(ec, path, line_number, ':', line.in())); ec, path, line_number, ':', line.in()));
} }
break; break;
} }
} }
if (mode) { if (mode) {
retval = TRY(execute_from_file(ec, path, starting_line_number, mode, trim(cmdline))); retval = TRY(execute_from_file(
ec, path, starting_line_number, mode, trim(cmdline)));
} }
if (file == stdin) { if (file == stdin) {
@ -483,7 +490,8 @@ static Result<string, string> execute_file_contents(exec_context &ec, const ghc:
return Ok(retval); return Ok(retval);
} }
Result<string, string> execute_file(exec_context &ec, const string &path_and_args, bool multiline) Result<string, string>
execute_file(exec_context& ec, const string& path_and_args, bool multiline)
{ {
available_scripts scripts; available_scripts scripts;
vector<string> split_args; vector<string> split_args;
@ -508,8 +516,8 @@ Result<string, string> execute_file(exec_context &ec, const string &path_and_arg
add_ansi_vars(vars); add_ansi_vars(vars);
snprintf(env_arg_name, sizeof(env_arg_name), "%d", snprintf(
(int) split_args.size() - 1); env_arg_name, sizeof(env_arg_name), "%d", (int) split_args.size() - 1);
vars["#"] = env_arg_name; vars["#"] = env_arg_name;
for (size_t lpc = 0; lpc < split_args.size(); lpc++) { for (size_t lpc = 0; lpc < split_args.size(); lpc++) {
@ -560,22 +568,27 @@ Result<string, string> execute_file(exec_context &ec, const string &path_and_arg
} }
if (!paths_to_exec.empty()) { if (!paths_to_exec.empty()) {
for (auto &path_iter : paths_to_exec) { for (auto& path_iter : paths_to_exec) {
retval = TRY( retval
execute_file_contents(ec, path_iter.sm_path, multiline)); = TRY(execute_file_contents(ec, path_iter.sm_path, multiline));
} }
} }
ec.ec_local_vars.pop(); ec.ec_local_vars.pop();
if (paths_to_exec.empty()) { if (paths_to_exec.empty()) {
return ec.make_error("unknown script -- {} -- {}", return ec.make_error(
script_name, open_error); "unknown script -- {} -- {}", script_name, open_error);
} }
return Ok(retval); return Ok(retval);
} }
Result<string, string> execute_from_file(exec_context &ec, const ghc::filesystem::path &path, int line_number, char mode, const string &cmdline) Result<string, string>
execute_from_file(exec_context& ec,
const ghc::filesystem::path& path,
int line_number,
char mode,
const string& cmdline)
{ {
string retval, alt_msg; string retval, alt_msg;
auto _sg = ec.enter_source(path.string(), line_number); auto _sg = ec.enter_source(path.string(), line_number);
@ -585,9 +598,8 @@ Result<string, string> execute_from_file(exec_context &ec, const ghc::filesystem
retval = TRY(execute_command(ec, cmdline)); retval = TRY(execute_command(ec, cmdline));
break; break;
case '/': case '/':
lnav_data.ld_view_stack.top() | [cmdline] (auto tc) { lnav_data.ld_view_stack.top() |
tc->execute_search(cmdline.substr(1)); [cmdline](auto tc) { tc->execute_search(cmdline.substr(1)); };
};
break; break;
case ';': case ';':
setup_logline_table(ec); setup_logline_table(ec);
@ -609,15 +621,16 @@ Result<string, string> execute_from_file(exec_context &ec, const ghc::filesystem
return Ok(retval); return Ok(retval);
} }
Result<string, string> execute_any(exec_context &ec, const string &cmdline_with_mode) Result<string, string>
execute_any(exec_context& ec, const string& cmdline_with_mode)
{ {
string retval, alt_msg, cmdline = cmdline_with_mode.substr(1); string retval, alt_msg, cmdline = cmdline_with_mode.substr(1);
auto _cleanup = finally([&ec] { auto _cleanup = finally([&ec] {
if (ec.is_read_write() && if (ec.is_read_write() &&
// only rebuild in a script or non-interactive mode so we don't // only rebuild in a script or non-interactive mode so we don't
// block the UI. // block the UI.
(lnav_data.ld_flags & LNF_HEADLESS || (lnav_data.ld_flags & LNF_HEADLESS || ec.ec_path_stack.size() > 1))
ec.ec_path_stack.size() > 1)) { {
rescan_files(); rescan_files();
rebuild_indexes_repeatedly(); rebuild_indexes_repeatedly();
} }
@ -628,9 +641,8 @@ Result<string, string> execute_any(exec_context &ec, const string &cmdline_with_
retval = TRY(execute_command(ec, cmdline)); retval = TRY(execute_command(ec, cmdline));
break; break;
case '/': case '/':
lnav_data.ld_view_stack.top() | [cmdline] (auto tc) { lnav_data.ld_view_stack.top() |
tc->execute_search(cmdline.substr(1)); [cmdline](auto tc) { tc->execute_search(cmdline.substr(1)); };
};
break; break;
case ';': case ';':
setup_logline_table(ec); setup_logline_table(ec);
@ -648,38 +660,40 @@ Result<string, string> execute_any(exec_context &ec, const string &cmdline_with_
return Ok(retval); return Ok(retval);
} }
void execute_init_commands(exec_context &ec, vector<pair<Result<string, string>, string> > &msgs) void
execute_init_commands(exec_context& ec,
vector<pair<Result<string, string>, string> >& msgs)
{ {
if (lnav_data.ld_cmd_init_done) { if (lnav_data.ld_cmd_init_done) {
return; return;
} }
db_label_source &dls = lnav_data.ld_db_row_source; db_label_source& dls = lnav_data.ld_db_row_source;
int option_index = 1; int option_index = 1;
log_info("Executing initial commands"); log_info("Executing initial commands");
for (auto &cmd : lnav_data.ld_commands) { for (auto& cmd : lnav_data.ld_commands) {
string alt_msg; string alt_msg;
wait_for_children(); wait_for_children();
ec.ec_source.emplace("command-option", option_index++); ec.ec_source.emplace("command-option", option_index++);
switch (cmd.at(0)) { switch (cmd.at(0)) {
case ':': case ':':
msgs.emplace_back(execute_command(ec, cmd.substr(1)), alt_msg); msgs.emplace_back(execute_command(ec, cmd.substr(1)), alt_msg);
break; break;
case '/': case '/':
lnav_data.ld_view_stack.top() | [cmd] (auto tc) { lnav_data.ld_view_stack.top() |
tc->execute_search(cmd.substr(1)); [cmd](auto tc) { tc->execute_search(cmd.substr(1)); };
}; break;
break; case ';':
case ';': setup_logline_table(ec);
setup_logline_table(ec); msgs.emplace_back(execute_sql(ec, cmd.substr(1), alt_msg),
msgs.emplace_back(execute_sql(ec, cmd.substr(1), alt_msg), alt_msg); alt_msg);
break; break;
case '|': case '|':
msgs.emplace_back(execute_file(ec, cmd.substr(1)), alt_msg); msgs.emplace_back(execute_file(ec, cmd.substr(1)), alt_msg);
break; break;
} }
rescan_files(); rescan_files();
@ -691,16 +705,13 @@ void execute_init_commands(exec_context &ec, vector<pair<Result<string, string>,
if (!lnav_data.ld_pt_search.empty()) { if (!lnav_data.ld_pt_search.empty()) {
#ifdef HAVE_LIBCURL #ifdef HAVE_LIBCURL
auto pt = make_shared<papertrail_proc>( auto pt = make_shared<papertrail_proc>(lnav_data.ld_pt_search.substr(3),
lnav_data.ld_pt_search.substr(3), lnav_data.ld_pt_min_time,
lnav_data.ld_pt_min_time, lnav_data.ld_pt_max_time);
lnav_data.ld_pt_max_time); lnav_data.ld_active_files.fc_file_names[lnav_data.ld_pt_search].with_fd(
lnav_data.ld_active_files.fc_file_names[lnav_data.ld_pt_search] pt->copy_fd());
.with_fd(pt->copy_fd()); isc::to<curl_looper&, services::curl_streamer_t>().send(
isc::to<curl_looper&, services::curl_streamer_t>() [pt](auto& clooper) { clooper.add_request(pt); });
.send([pt](auto& clooper) {
clooper.add_request(pt);
});
#endif #endif
} }
@ -711,9 +722,10 @@ void execute_init_commands(exec_context &ec, vector<pair<Result<string, string>,
lnav_data.ld_cmd_init_done = true; lnav_data.ld_cmd_init_done = true;
} }
int sql_callback(exec_context &ec, sqlite3_stmt *stmt) int
sql_callback(exec_context& ec, sqlite3_stmt* stmt)
{ {
auto &dls = lnav_data.ld_db_row_source; auto& dls = lnav_data.ld_db_row_source;
if (!sqlite3_stmt_busy(stmt)) { if (!sqlite3_stmt_busy(stmt)) {
dls.clear(); dls.clear();
@ -721,8 +733,8 @@ int sql_callback(exec_context &ec, sqlite3_stmt *stmt)
return 0; return 0;
} }
stacked_bar_chart<std::string> &chart = dls.dls_chart; stacked_bar_chart<std::string>& chart = dls.dls_chart;
view_colors &vc = view_colors::singleton(); view_colors& vc = view_colors::singleton();
int ncols = sqlite3_column_count(stmt); int ncols = sqlite3_column_count(stmt);
int row_number; int row_number;
int lpc, retval = 0; int lpc, retval = 0;
@ -731,14 +743,14 @@ int sql_callback(exec_context &ec, sqlite3_stmt *stmt)
dls.dls_rows.resize(row_number + 1); dls.dls_rows.resize(row_number + 1);
if (dls.dls_headers.empty()) { if (dls.dls_headers.empty()) {
for (lpc = 0; lpc < ncols; lpc++) { for (lpc = 0; lpc < ncols; lpc++) {
int type = sqlite3_column_type(stmt, lpc); int type = sqlite3_column_type(stmt, lpc);
string colname = sqlite3_column_name(stmt, lpc); string colname = sqlite3_column_name(stmt, lpc);
bool graphable; bool graphable;
graphable = ((type == SQLITE_INTEGER || type == SQLITE_FLOAT) && graphable = ((type == SQLITE_INTEGER || type == SQLITE_FLOAT)
!binary_search(lnav_data.ld_db_key_names.begin(), && !binary_search(lnav_data.ld_db_key_names.begin(),
lnav_data.ld_db_key_names.end(), lnav_data.ld_db_key_names.end(),
colname)); colname));
dls.push_header(colname, type, graphable); dls.push_header(colname, type, graphable);
if (graphable) { if (graphable) {
@ -748,13 +760,15 @@ int sql_callback(exec_context &ec, sqlite3_stmt *stmt)
} }
} }
for (lpc = 0; lpc < ncols; lpc++) { for (lpc = 0; lpc < ncols; lpc++) {
const char *value = (const char *)sqlite3_column_text(stmt, lpc); const char* value = (const char*) sqlite3_column_text(stmt, lpc);
db_label_source::header_meta &hm = dls.dls_headers[lpc]; db_label_source::header_meta& hm = dls.dls_headers[lpc];
dls.push_column(value); dls.push_column(value);
if ((hm.hm_column_type == SQLITE_TEXT || if ((hm.hm_column_type == SQLITE_TEXT
hm.hm_column_type == SQLITE_NULL) && hm.hm_sub_type == 0) { || hm.hm_column_type == SQLITE_NULL)
sqlite3_value *raw_value = sqlite3_column_value(stmt, lpc); && hm.hm_sub_type == 0)
{
sqlite3_value* raw_value = sqlite3_column_value(stmt, lpc);
switch (sqlite3_value_type(raw_value)) { switch (sqlite3_value_type(raw_value)) {
case SQLITE_TEXT: case SQLITE_TEXT:
@ -768,12 +782,13 @@ int sql_callback(exec_context &ec, sqlite3_stmt *stmt)
return retval; return retval;
} }
future<string> pipe_callback(exec_context &ec, const string &cmdline, auto_fd &fd) future<string>
pipe_callback(exec_context& ec, const string& cmdline, auto_fd& fd)
{ {
auto out = ec.get_output(); auto out = ec.get_output();
if (out) { if (out) {
FILE *file = *out; FILE* file = *out;
return std::async(std::launch::async, [&fd, file]() { return std::async(std::launch::async, [&fd, file]() {
char buffer[1024]; char buffer[1024];
@ -791,8 +806,10 @@ future<string> pipe_callback(exec_context &ec, const string &cmdline, auto_fd &f
}); });
} else { } else {
auto pp = make_shared<piper_proc>( auto pp = make_shared<piper_proc>(
fd, false, lnav::filesystem::open_temp_file(ghc::filesystem::temp_directory_path() / fd,
"lnav.out.XXXXXX") false,
lnav::filesystem::open_temp_file(
ghc::filesystem::temp_directory_path() / "lnav.out.XXXXXX")
.map([](auto pair) { .map([](auto pair) {
ghc::filesystem::remove(pair.first); ghc::filesystem::remove(pair.first);
@ -805,7 +822,8 @@ future<string> pipe_callback(exec_context &ec, const string &cmdline, auto_fd &f
lnav_data.ld_pipers.push_back(pp); lnav_data.ld_pipers.push_back(pp);
snprintf(desc, snprintf(desc,
sizeof(desc), "[%d] Output of %s", sizeof(desc),
"[%d] Output of %s",
exec_count++, exec_count++,
cmdline.c_str()); cmdline.c_str());
lnav_data.ld_active_files.fc_file_names[desc] lnav_data.ld_active_files.fc_file_names[desc]
@ -822,9 +840,10 @@ future<string> pipe_callback(exec_context &ec, const string &cmdline, auto_fd &f
} }
} }
void add_global_vars(exec_context &ec) void
add_global_vars(exec_context& ec)
{ {
for (const auto &iter : lnav_config.lc_global_vars) { for (const auto& iter : lnav_config.lc_global_vars) {
shlex subber(iter.second); shlex subber(iter.second);
string str; string str;
@ -838,7 +857,8 @@ void add_global_vars(exec_context &ec)
} }
} }
std::string exec_context::get_error_prefix() std::string
exec_context::get_error_prefix()
{ {
if (this->ec_source.size() <= 1) { if (this->ec_source.size() <= 1) {
return "error: "; return "error: ";
@ -849,7 +869,8 @@ std::string exec_context::get_error_prefix()
return fmt::format("{}:{}: error: ", source.first, source.second); return fmt::format("{}:{}: error: ", source.first, source.second);
} }
void exec_context::set_output(const string &name, FILE *file, int (*closer)(FILE *)) void
exec_context::set_output(const string& name, FILE* file, int (*closer)(FILE*))
{ {
log_info("redirecting command output to: %s", name.c_str()); log_info("redirecting command output to: %s", name.c_str());
this->ec_output_stack.back().second | [](auto out) { this->ec_output_stack.back().second | [](auto out) {
@ -857,10 +878,12 @@ void exec_context::set_output(const string &name, FILE *file, int (*closer)(FILE
out.second(out.first); out.second(out.first);
} }
}; };
this->ec_output_stack.back() = std::make_pair(name, std::make_pair(file, closer)); this->ec_output_stack.back()
= std::make_pair(name, std::make_pair(file, closer));
} }
void exec_context::clear_output() void
exec_context::clear_output()
{ {
log_info("redirecting command output to screen"); log_info("redirecting command output to screen");
this->ec_output_stack.back().second | [](auto out) { this->ec_output_stack.back().second | [](auto out) {
@ -871,23 +894,24 @@ void exec_context::clear_output()
this->ec_output_stack.back() = std::make_pair("default", nonstd::nullopt); this->ec_output_stack.back() = std::make_pair("default", nonstd::nullopt);
} }
exec_context::exec_context(std::vector<logline_value> *line_values, exec_context::exec_context(std::vector<logline_value>* line_values,
sql_callback_t sql_callback, sql_callback_t sql_callback,
pipe_callback_t pipe_callback) pipe_callback_t pipe_callback)
: ec_line_values(line_values), : ec_line_values(line_values),
ec_accumulator(std::make_unique<attr_line_t>()), ec_accumulator(std::make_unique<attr_line_t>()),
ec_sql_callback(sql_callback), ec_sql_callback(sql_callback), ec_pipe_callback(pipe_callback)
ec_pipe_callback(pipe_callback) { {
this->ec_local_vars.push(std::map<std::string, std::string>()); this->ec_local_vars.push(std::map<std::string, std::string>());
this->ec_path_stack.emplace_back("."); this->ec_path_stack.emplace_back(".");
this->ec_source.emplace("command", 1); this->ec_source.emplace("command", 1);
this->ec_output_stack.emplace_back("screen", nonstd::nullopt); this->ec_output_stack.emplace_back("screen", nonstd::nullopt);
} }
exec_context::output_guard::output_guard(exec_context &context, exec_context::output_guard::output_guard(exec_context& context,
std::string name, std::string name,
const nonstd::optional<output_t> &file) const nonstd::optional<output_t>& file)
: sg_context(context) { : sg_context(context)
{
if (file) { if (file) {
log_info("redirecting command output to: %s", name.c_str()); log_info("redirecting command output to: %s", name.c_str());
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -30,26 +30,30 @@
#ifndef LNAV_COMMAND_EXECUTOR_H #ifndef LNAV_COMMAND_EXECUTOR_H
#define LNAV_COMMAND_EXECUTOR_H #define LNAV_COMMAND_EXECUTOR_H
#include <sqlite3.h>
#include <future> #include <future>
#include <stack>
#include <string> #include <string>
#include "fmt/format.h" #include <sqlite3.h>
#include "optional.hpp"
#include "auto_fd.hh" #include "auto_fd.hh"
#include "bookmarks.hh" #include "bookmarks.hh"
#include "fmt/format.h"
#include "ghc/filesystem.hpp"
#include "optional.hpp"
#include "shlex.resolver.hh" #include "shlex.resolver.hh"
#include "vis_line.hh"
struct exec_context; struct exec_context;
class attr_line_t; class attr_line_t;
class logline_value; class logline_value;
typedef int (*sql_callback_t)(exec_context &ec, sqlite3_stmt *stmt); typedef int (*sql_callback_t)(exec_context& ec, sqlite3_stmt* stmt);
int sql_callback(exec_context &ec, sqlite3_stmt *stmt); int sql_callback(exec_context& ec, sqlite3_stmt* stmt);
typedef std::future<std::string> (*pipe_callback_t)( typedef std::future<std::string> (*pipe_callback_t)(exec_context& ec,
exec_context &ec, const std::string &cmdline, auto_fd &fd); const std::string& cmdline,
auto_fd& fd);
struct exec_context { struct exec_context {
enum class perm_t { enum class perm_t {
@ -57,38 +61,44 @@ struct exec_context {
READ_ONLY, READ_ONLY,
}; };
using output_t = std::pair<FILE *, int(*)(FILE *)>; using output_t = std::pair<FILE*, int (*)(FILE*)>;
exec_context(std::vector<logline_value> *line_values = nullptr, exec_context(std::vector<logline_value>* line_values = nullptr,
sql_callback_t sql_callback = ::sql_callback, sql_callback_t sql_callback = ::sql_callback,
pipe_callback_t pipe_callback = nullptr); pipe_callback_t pipe_callback = nullptr);
bool is_read_write() const { bool is_read_write() const
{
return this->ec_perms == perm_t::READ_WRITE; return this->ec_perms == perm_t::READ_WRITE;
} }
bool is_read_only() const { bool is_read_only() const
{
return this->ec_perms == perm_t::READ_ONLY; return this->ec_perms == perm_t::READ_ONLY;
} }
exec_context& with_perms(perm_t perms) { exec_context& with_perms(perm_t perms)
{
this->ec_perms = perms; this->ec_perms = perms;
return *this; return *this;
} }
std::string get_error_prefix(); std::string get_error_prefix();
template<typename ...Args> template<typename... Args>
Result<std::string, std::string> make_error( Result<std::string, std::string> make_error(fmt::string_view format_str,
fmt::string_view format_str, const Args& ...args) { const Args&... args)
return Err(this->get_error_prefix() + {
fmt::vformat(format_str, fmt::make_format_args(args...))); return Err(this->get_error_prefix()
+ fmt::vformat(format_str, fmt::make_format_args(args...)));
} }
nonstd::optional<FILE *> get_output() { nonstd::optional<FILE*> get_output()
{
for (auto iter = this->ec_output_stack.rbegin(); for (auto iter = this->ec_output_stack.rbegin();
iter != this->ec_output_stack.rend(); iter != this->ec_output_stack.rend();
++iter) { ++iter)
{
if (iter->second && (*iter->second).first) { if (iter->second && (*iter->second).first) {
return (*iter->second).first; return (*iter->second).first;
} }
@ -97,38 +107,40 @@ struct exec_context {
return nonstd::nullopt; return nonstd::nullopt;
} }
void set_output(const std::string& name, FILE *file, int (*closer)(FILE *)); void set_output(const std::string& name, FILE* file, int (*closer)(FILE*));
void clear_output(); void clear_output();
struct source_guard { struct source_guard {
source_guard(exec_context &context) : sg_context(context) { source_guard(exec_context& context) : sg_context(context) {}
}
~source_guard() { ~source_guard()
{
this->sg_context.ec_source.pop(); this->sg_context.ec_source.pop();
} }
exec_context &sg_context; exec_context& sg_context;
}; };
struct output_guard { struct output_guard {
explicit output_guard(exec_context &context, explicit output_guard(exec_context& context,
std::string name = "default", std::string name = "default",
const nonstd::optional<output_t>& file = nonstd::nullopt); const nonstd::optional<output_t>& file
= nonstd::nullopt);
~output_guard(); ~output_guard();
exec_context &sg_context; exec_context& sg_context;
}; };
source_guard enter_source(const std::string& path, int line_number) { source_guard enter_source(const std::string& path, int line_number)
{
this->ec_source.emplace(path, line_number); this->ec_source.emplace(path, line_number);
return {*this}; return {*this};
} }
scoped_resolver create_resolver() { scoped_resolver create_resolver()
{
return { return {
&this->ec_local_vars.top(), &this->ec_local_vars.top(),
&this->ec_global_vars, &this->ec_global_vars,
@ -140,13 +152,14 @@ struct exec_context {
perm_t ec_perms{perm_t::READ_WRITE}; perm_t ec_perms{perm_t::READ_WRITE};
std::map<std::string, std::string> ec_override; std::map<std::string, std::string> ec_override;
std::vector<logline_value> *ec_line_values; std::vector<logline_value>* ec_line_values;
std::stack<std::map<std::string, std::string> > ec_local_vars; std::stack<std::map<std::string, std::string>> ec_local_vars;
std::map<std::string, std::string> ec_global_vars; std::map<std::string, std::string> ec_global_vars;
std::vector<ghc::filesystem::path> ec_path_stack; std::vector<ghc::filesystem::path> ec_path_stack;
std::stack<std::pair<std::string, int>> ec_source; std::stack<std::pair<std::string, int>> ec_source;
std::vector<std::pair<std::string, nonstd::optional<output_t>>> ec_output_stack; std::vector<std::pair<std::string, nonstd::optional<output_t>>>
ec_output_stack;
std::unique_ptr<attr_line_t> ec_accumulator; std::unique_ptr<attr_line_t> ec_accumulator;
@ -154,19 +167,29 @@ struct exec_context {
pipe_callback_t ec_pipe_callback; pipe_callback_t ec_pipe_callback;
}; };
Result<std::string, std::string> execute_command(exec_context &ec, const std::string &cmdline); Result<std::string, std::string> execute_command(exec_context& ec,
const std::string& cmdline);
Result<std::string, std::string> execute_sql(exec_context &ec, const std::string &sql, std::string &alt_msg);
Result<std::string, std::string> execute_file(exec_context &ec, const std::string &path_and_args, bool multiline = true); Result<std::string, std::string> execute_sql(exec_context& ec,
Result<std::string, std::string> execute_any(exec_context &ec, const std::string &cmdline); const std::string& sql,
void execute_init_commands(exec_context &ec, std::vector<std::pair<Result<std::string, std::string>, std::string> > &msgs); std::string& alt_msg);
Result<std::string, std::string> execute_file(exec_context& ec,
std::future<std::string> pipe_callback( const std::string& path_and_args,
exec_context &ec, const std::string &cmdline, auto_fd &fd); bool multiline = true);
Result<std::string, std::string> execute_any(exec_context& ec,
int sql_progress(const struct log_cursor &lc); const std::string& cmdline);
void execute_init_commands(
exec_context& ec,
std::vector<std::pair<Result<std::string, std::string>, std::string>>&
msgs);
std::future<std::string> pipe_callback(exec_context& ec,
const std::string& cmdline,
auto_fd& fd);
int sql_progress(const struct log_cursor& lc);
void sql_progress_finished(); void sql_progress_finished();
void add_global_vars(exec_context &ec); void add_global_vars(exec_context& ec);
#endif //LNAV_COMMAND_EXECUTOR_H #endif // LNAV_COMMAND_EXECUTOR_H

@ -21,46 +21,47 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file curl_looper.cc * @file curl_looper.cc
*/ */
#include "config.h"
#include <algorithm> #include <algorithm>
#include "config.h"
#if defined(HAVE_LIBCURL) #if defined(HAVE_LIBCURL)
#include <curl/multi.h> # include <curl/multi.h>
#include "curl_looper.hh" # include "curl_looper.hh"
using namespace std; using namespace std;
struct curl_request_eq { struct curl_request_eq {
explicit curl_request_eq(const std::string &name) : cre_name(name) { explicit curl_request_eq(const std::string& name) : cre_name(name){};
};
bool operator()(const std::shared_ptr<curl_request>& cr) const { bool operator()(const std::shared_ptr<curl_request>& cr) const
{
return this->cre_name == cr->get_name(); return this->cre_name == cr->get_name();
}; };
bool operator()(const pair<mstime_t, std::shared_ptr<curl_request>> &pair) const { bool operator()(
const pair<mstime_t, std::shared_ptr<curl_request>>& pair) const
{
return this->cre_name == pair.second->get_name(); return this->cre_name == pair.second->get_name();
}; };
const std::string &cre_name; const std::string& cre_name;
}; };
int curl_request::debug_cb(CURL *handle, int
curl_infotype type, curl_request::debug_cb(
char *data, CURL* handle, curl_infotype type, char* data, size_t size, void* userp)
size_t size, {
void *userp) { curl_request* cr = (curl_request*) userp;
curl_request *cr = (curl_request *) userp;
bool write_to_log; bool write_to_log;
switch (type) { switch (type) {
@ -71,8 +72,7 @@ int curl_request::debug_cb(CURL *handle,
case CURLINFO_HEADER_OUT: case CURLINFO_HEADER_OUT:
if (lnav_log_level == lnav_log_level_t::TRACE) { if (lnav_log_level == lnav_log_level_t::TRACE) {
write_to_log = true; write_to_log = true;
} } else {
else {
write_to_log = false; write_to_log = false;
} }
break; break;
@ -91,7 +91,8 @@ int curl_request::debug_cb(CURL *handle,
return 0; return 0;
} }
void curl_looper::loop_body() void
curl_looper::loop_body()
{ {
mstime_t current_time = getmstime(); mstime_t current_time = getmstime();
@ -104,7 +105,8 @@ void curl_looper::loop_body()
this->requeue_requests(current_time + 5); this->requeue_requests(current_time + 5);
} }
void curl_looper::perform_io() void
curl_looper::perform_io()
{ {
if (this->cl_handle_to_request.empty()) { if (this->cl_handle_to_request.empty()) {
return; return;
@ -114,57 +116,54 @@ void curl_looper::perform_io()
auto timeout = this->compute_timeout(current_time); auto timeout = this->compute_timeout(current_time);
int running_handles; int running_handles;
curl_multi_wait(this->cl_curl_multi, curl_multi_wait(this->cl_curl_multi, nullptr, 0, timeout.count(), nullptr);
nullptr,
0,
timeout.count(),
nullptr);
curl_multi_perform(this->cl_curl_multi, &running_handles); curl_multi_perform(this->cl_curl_multi, &running_handles);
} }
void curl_looper::requeue_requests(mstime_t up_to_time) void
curl_looper::requeue_requests(mstime_t up_to_time)
{ {
while (!this->cl_poll_queue.empty() && while (!this->cl_poll_queue.empty()
this->cl_poll_queue.front().first <= up_to_time) { && this->cl_poll_queue.front().first <= up_to_time)
{
auto cr = this->cl_poll_queue.front().second; auto cr = this->cl_poll_queue.front().second;
log_debug("%s:polling request is ready again -- %p", log_debug("%s:polling request is ready again -- %p",
cr->get_name().c_str(), cr.get()); cr->get_name().c_str(),
cr.get());
this->cl_handle_to_request[cr->get_handle()] = cr; this->cl_handle_to_request[cr->get_handle()] = cr;
curl_multi_add_handle(this->cl_curl_multi, cr->get_handle()); curl_multi_add_handle(this->cl_curl_multi, cr->get_handle());
this->cl_poll_queue.erase(this->cl_poll_queue.begin()); this->cl_poll_queue.erase(this->cl_poll_queue.begin());
} }
} }
void curl_looper::check_for_new_requests() { void
curl_looper::check_for_new_requests()
{
while (!this->cl_new_requests.empty()) { while (!this->cl_new_requests.empty()) {
auto cr = this->cl_new_requests.back(); auto cr = this->cl_new_requests.back();
log_info("%s:new curl request %p", log_info("%s:new curl request %p", cr->get_name().c_str(), cr.get());
cr->get_name().c_str(),
cr.get());
this->cl_handle_to_request[cr->get_handle()] = cr; this->cl_handle_to_request[cr->get_handle()] = cr;
curl_multi_add_handle(this->cl_curl_multi, cr->get_handle()); curl_multi_add_handle(this->cl_curl_multi, cr->get_handle());
this->cl_new_requests.pop_back(); this->cl_new_requests.pop_back();
} }
while (!this->cl_close_requests.empty()) { while (!this->cl_close_requests.empty()) {
const std::string &name = this->cl_close_requests.back(); const std::string& name = this->cl_close_requests.back();
auto all_iter = find_if( auto all_iter = find_if(this->cl_all_requests.begin(),
this->cl_all_requests.begin(), this->cl_all_requests.end(),
this->cl_all_requests.end(), curl_request_eq(name));
curl_request_eq(name));
log_info("attempting to close request -- %s", name.c_str()); log_info("attempting to close request -- %s", name.c_str());
if (all_iter != this->cl_all_requests.end()) { if (all_iter != this->cl_all_requests.end()) {
auto cr = *all_iter; auto cr = *all_iter;
log_info("%s:closing request -- %p", log_info(
cr->get_name().c_str(), cr.get()); "%s:closing request -- %p", cr->get_name().c_str(), cr.get());
(*all_iter)->close(); (*all_iter)->close();
auto act_iter = this->cl_handle_to_request.find(cr->get_handle()); auto act_iter = this->cl_handle_to_request.find(cr->get_handle());
if (act_iter != this->cl_handle_to_request.end()) { if (act_iter != this->cl_handle_to_request.end()) {
curl_multi_remove_handle(this->cl_curl_multi, curl_multi_remove_handle(this->cl_curl_multi, cr->get_handle());
cr->get_handle());
this->cl_handle_to_request.erase(act_iter); this->cl_handle_to_request.erase(act_iter);
} }
auto poll_iter = find_if(this->cl_poll_queue.begin(), auto poll_iter = find_if(this->cl_poll_queue.begin(),
@ -174,8 +173,7 @@ void curl_looper::check_for_new_requests() {
this->cl_poll_queue.erase(poll_iter); this->cl_poll_queue.erase(poll_iter);
} }
this->cl_all_requests.erase(all_iter); this->cl_all_requests.erase(all_iter);
} } else {
else {
log_error("Unable to find request with the name -- %s", log_error("Unable to find request with the name -- %s",
name.c_str()); name.c_str());
} }
@ -184,18 +182,19 @@ void curl_looper::check_for_new_requests() {
} }
} }
void curl_looper::check_for_finished_requests() void
curl_looper::check_for_finished_requests()
{ {
CURLMsg *msg; CURLMsg* msg;
int msgs_left; int msgs_left;
while ((msg = curl_multi_info_read(this->cl_curl_multi, &msgs_left)) != while ((msg = curl_multi_info_read(this->cl_curl_multi, &msgs_left))
nullptr) { != nullptr) {
if (msg->msg != CURLMSG_DONE) { if (msg->msg != CURLMSG_DONE) {
continue; continue;
} }
CURL *easy = msg->easy_handle; CURL* easy = msg->easy_handle;
auto iter = this->cl_handle_to_request.find(easy); auto iter = this->cl_handle_to_request.find(easy);
curl_multi_remove_handle(this->cl_curl_multi, easy); curl_multi_remove_handle(this->cl_curl_multi, easy);
@ -207,15 +206,15 @@ void curl_looper::check_for_finished_requests()
delay_ms = cr->complete(msg->data.result); delay_ms = cr->complete(msg->data.result);
if (delay_ms < 0) { if (delay_ms < 0) {
log_info("%s:curl_request %p finished, deleting...", log_info("%s:curl_request %p finished, deleting...",
cr->get_name().c_str(), cr.get()); cr->get_name().c_str(),
cr.get());
auto all_iter = find(this->cl_all_requests.begin(), auto all_iter = find(this->cl_all_requests.begin(),
this->cl_all_requests.end(), this->cl_all_requests.end(),
cr); cr);
if (all_iter != this->cl_all_requests.end()) { if (all_iter != this->cl_all_requests.end()) {
this->cl_all_requests.erase(all_iter); this->cl_all_requests.erase(all_iter);
} }
} } else {
else {
log_debug("%s:curl_request %p is polling, requeueing in %d", log_debug("%s:curl_request %p is polling, requeueing in %d",
cr->get_name().c_str(), cr->get_name().c_str(),
cr.get(), cr.get(),
@ -227,16 +226,18 @@ void curl_looper::check_for_finished_requests()
} }
} }
std::chrono::milliseconds curl_looper::compute_timeout(mstime_t current_time) const std::chrono::milliseconds
curl_looper::compute_timeout(mstime_t current_time) const
{ {
std::chrono::milliseconds retval = 1s; std::chrono::milliseconds retval = 1s;
if (!this->cl_handle_to_request.empty()) { if (!this->cl_handle_to_request.empty()) {
retval = 1ms; retval = 1ms;
} else if (!this->cl_poll_queue.empty()) { } else if (!this->cl_poll_queue.empty()) {
retval = std::max( retval
1ms, = std::max(1ms,
std::chrono::milliseconds(this->cl_poll_queue.front().first - current_time)); std::chrono::milliseconds(
this->cl_poll_queue.front().first - current_time));
} }
ensure(retval.count() > 0); ensure(retval.count() > 0);

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,6 +32,8 @@
#ifndef curl_looper_hh #ifndef curl_looper_hh
#define curl_looper_hh #define curl_looper_hh
#include "config.h"
#include <atomic> #include <atomic>
#include <map> #include <map>
#include <string> #include <string>
@ -46,95 +48,100 @@ typedef int CURLcode;
class curl_request { class curl_request {
public: public:
curl_request(const std::string &name) { curl_request(const std::string& name){};
};
}; };
class curl_looper : public isc::service<curl_looper> { class curl_looper : public isc::service<curl_looper> {
public: public:
void start() { }; void start(){};
void stop() { }; void stop(){};
void add_request(std::shared_ptr<curl_request> cr) { }; void add_request(std::shared_ptr<curl_request> cr){};
void close_request(const std::string &name) { }; void close_request(const std::string& name){};
void process_all() { }; void process_all(){};
}; };
#else #else
#include <mutex> # include <condition_variable>
#include <thread> # include <mutex>
#include <condition_variable> # include <thread>
#include <curl/curl.h> # include <curl/curl.h>
#include "auto_mem.hh" # include "auto_mem.hh"
#include "base/lnav_log.hh" # include "base/lnav_log.hh"
#include "base/time_util.hh" # include "base/time_util.hh"
class curl_request { class curl_request {
public: public:
curl_request(std::string name) curl_request(std::string name)
: cr_name(std::move(name)), : cr_name(std::move(name)), cr_open(true), cr_handle(curl_easy_cleanup),
cr_open(true), cr_completions(0)
cr_handle(curl_easy_cleanup), {
cr_completions(0) {
this->cr_handle.reset(curl_easy_init()); this->cr_handle.reset(curl_easy_init());
curl_easy_setopt(this->cr_handle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(this->cr_handle, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(this->cr_handle, CURLOPT_ERRORBUFFER, this->cr_error_buffer); curl_easy_setopt(
this->cr_handle, CURLOPT_ERRORBUFFER, this->cr_error_buffer);
curl_easy_setopt(this->cr_handle, CURLOPT_DEBUGFUNCTION, debug_cb); curl_easy_setopt(this->cr_handle, CURLOPT_DEBUGFUNCTION, debug_cb);
curl_easy_setopt(this->cr_handle, CURLOPT_DEBUGDATA, this); curl_easy_setopt(this->cr_handle, CURLOPT_DEBUGDATA, this);
curl_easy_setopt(this->cr_handle, CURLOPT_VERBOSE, 1); curl_easy_setopt(this->cr_handle, CURLOPT_VERBOSE, 1);
if (getenv("SSH_AUTH_SOCK") != nullptr) { if (getenv("SSH_AUTH_SOCK") != nullptr) {
curl_easy_setopt(this->cr_handle, CURLOPT_SSH_AUTH_TYPES, curl_easy_setopt(this->cr_handle,
#ifdef CURLSSH_AUTH_AGENT CURLOPT_SSH_AUTH_TYPES,
CURLSSH_AUTH_AGENT| # ifdef CURLSSH_AUTH_AGENT
#endif CURLSSH_AUTH_AGENT |
CURLSSH_AUTH_PASSWORD); # endif
CURLSSH_AUTH_PASSWORD);
} }
}; };
virtual ~curl_request() = default; virtual ~curl_request() = default;
const std::string &get_name() const { const std::string& get_name() const
{
return this->cr_name; return this->cr_name;
}; };
virtual void close() { virtual void close()
{
this->cr_open = false; this->cr_open = false;
}; };
bool is_open() const { bool is_open() const
{
return this->cr_open; return this->cr_open;
}; };
CURL *get_handle() const { CURL* get_handle() const
{
return this->cr_handle; return this->cr_handle;
}; };
int get_completions() const { int get_completions() const
{
return this->cr_completions; return this->cr_completions;
}; };
virtual long complete(CURLcode result) { virtual long complete(CURLcode result)
{
double total_time = 0, download_size = 0, download_speed = 0; double total_time = 0, download_size = 0, download_speed = 0;
this->cr_completions += 1; this->cr_completions += 1;
curl_easy_getinfo(this->cr_handle, CURLINFO_TOTAL_TIME, &total_time); curl_easy_getinfo(this->cr_handle, CURLINFO_TOTAL_TIME, &total_time);
log_debug("%s: total_time=%f", this->cr_name.c_str(), total_time); log_debug("%s: total_time=%f", this->cr_name.c_str(), total_time);
curl_easy_getinfo(this->cr_handle, CURLINFO_SIZE_DOWNLOAD, &download_size); curl_easy_getinfo(
this->cr_handle, CURLINFO_SIZE_DOWNLOAD, &download_size);
log_debug("%s: download_size=%f", this->cr_name.c_str(), download_size); log_debug("%s: download_size=%f", this->cr_name.c_str(), download_size);
curl_easy_getinfo(this->cr_handle, CURLINFO_SPEED_DOWNLOAD, &download_speed); curl_easy_getinfo(
log_debug("%s: download_speed=%f", this->cr_name.c_str(), download_speed); this->cr_handle, CURLINFO_SPEED_DOWNLOAD, &download_speed);
log_debug(
"%s: download_speed=%f", this->cr_name.c_str(), download_speed);
return -1; return -1;
}; };
protected: protected:
static int debug_cb(
static int debug_cb(CURL *handle, CURL* handle, curl_infotype type, char* data, size_t size, void* userp);
curl_infotype type,
char *data,
size_t size,
void *userp);
const std::string cr_name; const std::string cr_name;
bool cr_open; bool cr_open;
@ -145,12 +152,13 @@ protected:
class curl_looper : public isc::service<curl_looper> { class curl_looper : public isc::service<curl_looper> {
public: public:
curl_looper() curl_looper() : cl_curl_multi(curl_multi_cleanup)
: cl_curl_multi(curl_multi_cleanup) { {
this->cl_curl_multi.reset(curl_multi_init()); this->cl_curl_multi.reset(curl_multi_init());
}; };
void process_all() { void process_all()
{
this->check_for_new_requests(); this->check_for_new_requests();
this->requeue_requests(LONG_MAX); this->requeue_requests(LONG_MAX);
@ -162,14 +170,16 @@ public:
} }
}; };
void add_request(const std::shared_ptr<curl_request>& cr) { void add_request(const std::shared_ptr<curl_request>& cr)
{
require(cr != nullptr); require(cr != nullptr);
this->cl_all_requests.emplace_back(cr); this->cl_all_requests.emplace_back(cr);
this->cl_new_requests.emplace_back(cr); this->cl_new_requests.emplace_back(cr);
}; };
void close_request(const std::string &name) { void close_request(const std::string& name)
{
this->cl_close_requests.emplace_back(name); this->cl_close_requests.emplace_back(name);
}; };
@ -181,14 +191,16 @@ private:
void check_for_new_requests(); void check_for_new_requests();
void check_for_finished_requests(); void check_for_finished_requests();
void requeue_requests(mstime_t up_to_time); void requeue_requests(mstime_t up_to_time);
std::chrono::milliseconds compute_timeout(mstime_t current_time) const override; std::chrono::milliseconds compute_timeout(
mstime_t current_time) const override;
auto_mem<CURLM> cl_curl_multi; auto_mem<CURLM> cl_curl_multi;
std::vector<std::shared_ptr<curl_request>> cl_all_requests; std::vector<std::shared_ptr<curl_request> > cl_all_requests;
std::vector<std::shared_ptr<curl_request>> cl_new_requests; std::vector<std::shared_ptr<curl_request> > cl_new_requests;
std::vector<std::string> cl_close_requests; std::vector<std::string> cl_close_requests;
std::map<CURL *, std::shared_ptr<curl_request>> cl_handle_to_request; std::map<CURL*, std::shared_ptr<curl_request> > cl_handle_to_request;
std::vector<std::pair<mstime_t, std::shared_ptr<curl_request>>> cl_poll_queue; std::vector<std::pair<mstime_t, std::shared_ptr<curl_request> > >
cl_poll_queue;
}; };
#endif #endif

@ -21,44 +21,44 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <algorithm> #include <algorithm>
#include "spookyhash/SpookyV2.h"
#include "data_parser.hh" #include "data_parser.hh"
#include "config.h"
#include "spookyhash/SpookyV2.h"
using namespace std; using namespace std;
data_format data_parser::FORMAT_SEMI("semi", DT_COMMA, DT_SEMI); data_format data_parser::FORMAT_SEMI("semi", DT_COMMA, DT_SEMI);
data_format data_parser::FORMAT_COMMA("comma", DT_INVALID, DT_COMMA); data_format data_parser::FORMAT_COMMA("comma", DT_INVALID, DT_COMMA);
data_format data_parser::FORMAT_PLAIN("plain", DT_INVALID, DT_INVALID); data_format data_parser::FORMAT_PLAIN("plain", DT_INVALID, DT_INVALID);
data_parser::data_parser(data_scanner *ds) data_parser::data_parser(data_scanner* ds)
: dp_errors("dp_errors", __FILE__, __LINE__), : dp_errors("dp_errors", __FILE__, __LINE__),
dp_pairs("dp_pairs", __FILE__, __LINE__), dp_pairs("dp_pairs", __FILE__, __LINE__), dp_msg_format(nullptr),
dp_msg_format(nullptr), dp_msg_format_begin(ds->get_input().pi_offset), dp_scanner(ds)
dp_msg_format_begin(ds->get_input().pi_offset),
dp_scanner(ds)
{ {
if (TRACE_FILE != nullptr) { if (TRACE_FILE != nullptr) {
fprintf(TRACE_FILE, "input %s\n", ds->get_input().get_string()); fprintf(TRACE_FILE, "input %s\n", ds->get_input().get_string());
} }
} }
void data_parser::pairup(data_parser::schema_id_t *schema, void
data_parser::element_list_t &pairs_out, data_parser::pairup(data_parser::schema_id_t* schema,
data_parser::element_list_t &in_list, int group_depth) data_parser::element_list_t& pairs_out,
data_parser::element_list_t& in_list,
int group_depth)
{ {
element_list_t ELEMENT_LIST_T(el_stack), ELEMENT_LIST_T(free_row), element_list_t ELEMENT_LIST_T(el_stack), ELEMENT_LIST_T(free_row),
ELEMENT_LIST_T(key_comps), ELEMENT_LIST_T(value), ELEMENT_LIST_T(key_comps), ELEMENT_LIST_T(value),
ELEMENT_LIST_T(prefix); ELEMENT_LIST_T(prefix);
SpookyHash context; SpookyHash context;
require(in_list.el_format.df_name != nullptr); require(in_list.el_format.df_name != nullptr);
@ -67,14 +67,12 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
FORMAT_TRACE(in_list); FORMAT_TRACE(in_list);
for (auto iter = in_list.begin(); for (auto iter = in_list.begin(); iter != in_list.end(); ++iter) {
iter != in_list.end();
++iter) {
if (iter->e_token == DNT_GROUP) { if (iter->e_token == DNT_GROUP) {
element_list_t ELEMENT_LIST_T(group_pairs); element_list_t ELEMENT_LIST_T(group_pairs);
this->pairup(nullptr, group_pairs, *iter->e_sub_elements, this->pairup(
group_depth + 1); nullptr, group_pairs, *iter->e_sub_elements, group_depth + 1);
if (!group_pairs.empty()) { if (!group_pairs.empty()) {
iter->assign_elements(group_pairs); iter->assign_elements(group_pairs);
} }
@ -87,15 +85,13 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
el_stack.PUSH_BACK(*iter); el_stack.PUSH_BACK(*iter);
} }
} else if (iter->e_token == in_list.el_format.df_terminator) { } else if (iter->e_token == in_list.el_format.df_terminator) {
this->end_of_value(el_stack, key_comps, value, in_list, this->end_of_value(
group_depth); el_stack, key_comps, value, in_list, group_depth);
key_comps.PUSH_BACK(*iter); key_comps.PUSH_BACK(*iter);
} else if (iter->e_token == in_list.el_format.df_qualifier) { } else if (iter->e_token == in_list.el_format.df_qualifier) {
value.SPLICE(value.end(), value.SPLICE(
key_comps, value.end(), key_comps, key_comps.begin(), key_comps.end());
key_comps.begin(),
key_comps.end());
strip(value, element_if(DT_WHITE)); strip(value, element_if(DT_WHITE));
if (!value.empty()) { if (!value.empty()) {
el_stack.PUSH_BACK(element(value, DNT_VALUE)); el_stack.PUSH_BACK(element(value, DNT_VALUE));
@ -107,8 +103,7 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
if (!key_comps.empty()) { if (!key_comps.empty()) {
do { do {
--key_iter; --key_iter;
if (key_iter->e_token == if (key_iter->e_token == in_list.el_format.df_appender) {
in_list.el_format.df_appender) {
++key_iter; ++key_iter;
value.SPLICE(value.end(), value.SPLICE(value.end(),
key_comps, key_comps,
@ -116,8 +111,8 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
key_iter); key_iter);
key_comps.POP_FRONT(); key_comps.POP_FRONT();
found = true; found = true;
} else if (key_iter->e_token == } else if (key_iter->e_token
in_list.el_format.df_terminator) { == in_list.el_format.df_terminator) {
std::vector<element> key_copy; std::vector<element> key_copy;
value.SPLICE(value.end(), value.SPLICE(value.end(),
@ -148,24 +143,21 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
if (!found && !el_stack.empty() && !key_comps.empty()) { if (!found && !el_stack.empty() && !key_comps.empty()) {
element_list_t::iterator value_iter; element_list_t::iterator value_iter;
if (el_stack.size() > 1 && if (el_stack.size() > 1
in_list.el_format.df_appender != DT_INVALID && && in_list.el_format.df_appender != DT_INVALID
in_list.el_format.df_terminator != DT_INVALID) { && in_list.el_format.df_terminator != DT_INVALID)
{
/* If we're expecting a terminator and haven't found it */ /* If we're expecting a terminator and haven't found it */
/* then this is part of the value. */ /* then this is part of the value. */
continue; continue;
} }
value.SPLICE(value.end(), value.SPLICE(
key_comps, value.end(), key_comps, key_comps.begin(), key_comps.end());
key_comps.begin(),
key_comps.end());
value_iter = value.end(); value_iter = value.end();
std::advance(value_iter, -1); std::advance(value_iter, -1);
key_comps.SPLICE(key_comps.begin(), key_comps.SPLICE(
value, key_comps.begin(), value, value_iter, value.end());
value_iter,
value.end());
key_comps.resize(1); key_comps.resize(1);
} }
@ -198,8 +190,8 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
// Only perform the free-row logic at the top level, if we're in a group // Only perform the free-row logic at the top level, if we're in a group
// assume it is a list. // assume it is a list.
if (group_depth < 1 && el_stack.empty()) { if (group_depth < 1 && el_stack.empty()) {
free_row.SPLICE(free_row.begin(), free_row.SPLICE(
key_comps, key_comps.begin(), key_comps.end()); free_row.begin(), key_comps, key_comps.begin(), key_comps.end());
} else { } else {
this->end_of_value(el_stack, key_comps, value, in_list, group_depth); this->end_of_value(el_stack, key_comps, value, in_list, group_depth);
} }
@ -216,9 +208,8 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
element_list_t ELEMENT_LIST_T(free_pair_subs); element_list_t ELEMENT_LIST_T(free_pair_subs);
struct element blank; struct element blank;
blank.e_capture.c_begin = blank.e_capture.c_end = blank.e_capture.c_begin = blank.e_capture.c_end
el_stack.front().e_capture. = el_stack.front().e_capture.c_begin;
c_begin;
blank.e_token = DNT_KEY; blank.e_token = DNT_KEY;
free_pair_subs.PUSH_BACK(blank); free_pair_subs.PUSH_BACK(blank);
free_pair_subs.PUSH_BACK(el_stack.front()); free_pair_subs.PUSH_BACK(el_stack.front());
@ -240,8 +231,8 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
if (schema != nullptr) { if (schema != nullptr) {
size_t key_len; size_t key_len;
const char *key_val = const char* key_val
this->get_element_string(el_stack.front(), key_len); = this->get_element_string(el_stack.front(), key_len);
context.Update(key_val, key_len); context.Update(key_val, key_len);
} }
@ -249,9 +240,8 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
element_list_t ELEMENT_LIST_T(free_pair_subs); element_list_t ELEMENT_LIST_T(free_pair_subs);
struct element blank; struct element blank;
blank.e_capture.c_begin = blank.e_capture.c_end = blank.e_capture.c_begin = blank.e_capture.c_end
free_row.front().e_capture. = free_row.front().e_capture.c_begin;
c_begin;
blank.e_token = DNT_KEY; blank.e_token = DNT_KEY;
free_pair_subs.PUSH_BACK(blank); free_pair_subs.PUSH_BACK(blank);
free_pair_subs.PUSH_BACK(free_row.front()); free_pair_subs.PUSH_BACK(free_row.front());
@ -266,22 +256,21 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
has_value = true; has_value = true;
} }
pair_subs.SPLICE(pair_subs.begin(), pair_subs.SPLICE(
el_stack, pair_subs.begin(), el_stack, el_stack.begin(), kv_iter);
el_stack.begin(),
kv_iter);
if (!has_value) { if (!has_value) {
element_list_t ELEMENT_LIST_T(blank_value); element_list_t ELEMENT_LIST_T(blank_value);
pcre_input &pi = this->dp_scanner->get_input(); pcre_input& pi = this->dp_scanner->get_input();
const char *str = pi.get_string(); const char* str = pi.get_string();
struct element blank; struct element blank;
blank.e_token = DT_QUOTED_STRING; blank.e_token = DT_QUOTED_STRING;
blank.e_capture.c_begin = blank.e_capture.c_end = blank.e_capture.c_begin = blank.e_capture.c_end
pair_subs.front().e_capture.c_end; = pair_subs.front().e_capture.c_end;
if ((blank.e_capture.c_begin >= 0) && if ((blank.e_capture.c_begin >= 0)
((size_t) blank.e_capture.c_begin < pi.pi_length)) { && ((size_t) blank.e_capture.c_begin < pi.pi_length))
{
switch (str[blank.e_capture.c_begin]) { switch (str[blank.e_capture.c_begin]) {
case '=': case '=':
case ':': case ':':
@ -298,12 +287,12 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
} }
if (pairs_out.size() == 1) { if (pairs_out.size() == 1) {
element &pair = pairs_out.front(); element& pair = pairs_out.front();
element &evalue = pair.e_sub_elements->back(); element& evalue = pair.e_sub_elements->back();
if (evalue.e_token == DNT_VALUE && if (evalue.e_token == DNT_VALUE && evalue.e_sub_elements != nullptr
evalue.e_sub_elements != nullptr && && evalue.e_sub_elements->size() > 1)
evalue.e_sub_elements->size() > 1) { {
element_list_t::iterator next_sub; element_list_t::iterator next_sub;
next_sub = pair.e_sub_elements->begin(); next_sub = pair.e_sub_elements->begin();
@ -355,8 +344,8 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
element_list_t ELEMENT_LIST_T(pair_subs); element_list_t ELEMENT_LIST_T(pair_subs);
struct element blank; struct element blank;
blank.e_capture.c_begin = blank.e_capture.c_end = blank.e_capture.c_begin = blank.e_capture.c_end
free_row.front().e_capture.c_begin; = free_row.front().e_capture.c_begin;
blank.e_token = DNT_KEY; blank.e_token = DNT_KEY;
pair_subs.PUSH_BACK(blank); pair_subs.PUSH_BACK(blank);
pair_subs.PUSH_BACK(free_row.front()); pair_subs.PUSH_BACK(free_row.front());
@ -367,17 +356,15 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
// use the token ID since some columns values might vary // use the token ID since some columns values might vary
// between rows. // between rows.
context.Update(" ", 1); context.Update(" ", 1);
} } break;
break;
default: { default: {
size_t key_len; size_t key_len;
const char *key_val = this->get_element_string( const char* key_val
free_row.front(), key_len); = this->get_element_string(free_row.front(), key_len);
context.Update(key_val, key_len); context.Update(key_val, key_len);
} } break;
break;
} }
free_row.POP_FRONT(); free_row.POP_FRONT();
@ -388,8 +375,8 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
element_list_t ELEMENT_LIST_T(pair_subs); element_list_t ELEMENT_LIST_T(pair_subs);
struct element blank; struct element blank;
blank.e_capture.c_begin = blank.e_capture.c_end = blank.e_capture.c_begin = blank.e_capture.c_end
prefix.front().e_capture.c_begin; = prefix.front().e_capture.c_begin;
blank.e_token = DNT_KEY; blank.e_token = DNT_KEY;
pair_subs.PUSH_BACK(blank); pair_subs.PUSH_BACK(blank);
pair_subs.PUSH_BACK(prefix.front()); pair_subs.PUSH_BACK(prefix.front());
@ -401,13 +388,13 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
} }
if (schema != nullptr && this->dp_msg_format != nullptr) { if (schema != nullptr && this->dp_msg_format != nullptr) {
pcre_input &pi = this->dp_scanner->get_input(); pcre_input& pi = this->dp_scanner->get_input();
for (auto &fiter : pairs_out) { for (auto& fiter : pairs_out) {
*(this->dp_msg_format) += this->get_string_up_to_value(fiter); *(this->dp_msg_format) += this->get_string_up_to_value(fiter);
this->dp_msg_format->append("#"); this->dp_msg_format->append("#");
} }
if ((size_t) this->dp_msg_format_begin < pi.pi_length) { if ((size_t) this->dp_msg_format_begin < pi.pi_length) {
const char *str = pi.get_string(); const char* str = pi.get_string();
pcre_context::capture_t last(this->dp_msg_format_begin, pcre_context::capture_t last(this->dp_msg_format_begin,
pi.pi_length); pi.pi_length);
@ -426,7 +413,8 @@ void data_parser::pairup(data_parser::schema_id_t *schema,
} }
} }
void data_parser::discover_format() void
data_parser::discover_format()
{ {
pcre_context_static<30> pc; pcre_context_static<30> pc;
std::stack<discover_format_state> state_stack; std::stack<discover_format_state> state_stack;
@ -459,7 +447,7 @@ void data_parser::discover_format()
break; break;
case DT_EMPTY_CONTAINER: { case DT_EMPTY_CONTAINER: {
auto &curr_group = this->dp_group_stack.back(); auto& curr_group = this->dp_group_stack.back();
auto empty_list = element_list_t("_anon_", __FILE__, __LINE__); auto empty_list = element_list_t("_anon_", __FILE__, __LINE__);
discover_format_state dfs; discover_format_state dfs;
@ -468,7 +456,7 @@ void data_parser::discover_format()
empty_list.el_format = dfs.dfs_format; empty_list.el_format = dfs.dfs_format;
curr_group.PUSH_BACK(element()); curr_group.PUSH_BACK(element());
auto &empty = curr_group.back(); auto& empty = curr_group.back();
empty.e_capture.c_begin = elem.e_capture.c_begin + 1; empty.e_capture.c_begin = elem.e_capture.c_begin + 1;
empty.e_capture.c_end = elem.e_capture.c_begin + 1; empty.e_capture.c_end = elem.e_capture.c_begin + 1;
empty.e_token = DNT_GROUP; empty.e_token = DNT_GROUP;
@ -486,15 +474,16 @@ void data_parser::discover_format()
auto riter = this->dp_group_stack.rbegin(); auto riter = this->dp_group_stack.rbegin();
++riter; ++riter;
state_stack.top().finalize(); state_stack.top().finalize();
this->dp_group_stack.back().el_format = this->dp_group_stack.back().el_format
state_stack.top().dfs_format; = state_stack.top().dfs_format;
state_stack.pop(); state_stack.pop();
if (!this->dp_group_stack.back().empty()) { if (!this->dp_group_stack.back().empty()) {
(*riter).PUSH_BACK(element(this->dp_group_stack.back(), (*riter).PUSH_BACK(
DNT_GROUP)); element(this->dp_group_stack.back(), DNT_GROUP));
} else { } else {
(*riter).PUSH_BACK(element()); (*riter).PUSH_BACK(element());
riter->back().e_capture.c_begin = elem.e_capture.c_begin; riter->back().e_capture.c_begin
= elem.e_capture.c_begin;
riter->back().e_capture.c_end = elem.e_capture.c_begin; riter->back().e_capture.c_end = elem.e_capture.c_begin;
riter->back().e_token = DNT_GROUP; riter->back().e_token = DNT_GROUP;
riter->back().assign_elements( riter->back().assign_elements(
@ -519,10 +508,10 @@ void data_parser::discover_format()
++riter; ++riter;
if (!this->dp_group_stack.back().empty()) { if (!this->dp_group_stack.back().empty()) {
state_stack.top().finalize(); state_stack.top().finalize();
this->dp_group_stack.back().el_format = state_stack.top().dfs_format; this->dp_group_stack.back().el_format
= state_stack.top().dfs_format;
state_stack.pop(); state_stack.pop();
(*riter).PUSH_BACK(element(this->dp_group_stack.back(), (*riter).PUSH_BACK(element(this->dp_group_stack.back(), DNT_GROUP));
DNT_GROUP));
} }
this->dp_group_stack.pop_back(); this->dp_group_stack.pop_back();
} }
@ -531,11 +520,12 @@ void data_parser::discover_format()
this->dp_group_stack.back().el_format = state_stack.top().dfs_format; this->dp_group_stack.back().el_format = state_stack.top().dfs_format;
} }
void data_parser::end_of_value(data_parser::element_list_t &el_stack, void
data_parser::element_list_t &key_comps, data_parser::end_of_value(data_parser::element_list_t& el_stack,
data_parser::element_list_t &value, data_parser::element_list_t& key_comps,
const data_parser::element_list_t &in_list, data_parser::element_list_t& value,
int group_depth) const data_parser::element_list_t& in_list,
int group_depth)
{ {
key_comps.remove_if(element_if(in_list.el_format.df_terminator)); key_comps.remove_if(element_if(in_list.el_format.df_terminator));
key_comps.remove_if(element_if(DT_COMMA)); key_comps.remove_if(element_if(DT_COMMA));
@ -543,24 +533,24 @@ void data_parser::end_of_value(data_parser::element_list_t &el_stack,
value.remove_if(element_if(DT_COMMA)); value.remove_if(element_if(DT_COMMA));
strip(key_comps, element_if(DT_WHITE)); strip(key_comps, element_if(DT_WHITE));
strip(value, element_if(DT_WHITE)); strip(value, element_if(DT_WHITE));
if ((el_stack.empty() || el_stack.back().e_token != DNT_KEY) && if ((el_stack.empty() || el_stack.back().e_token != DNT_KEY)
value.empty() && key_comps.size() > 1 && && value.empty() && key_comps.size() > 1
(key_comps.front().e_token == DT_WORD || && (key_comps.front().e_token == DT_WORD
key_comps.front().e_token == DT_SYMBOL)) { || key_comps.front().e_token == DT_SYMBOL))
{
element_list_t::iterator key_iter, key_end; element_list_t::iterator key_iter, key_end;
bool found_value = false; bool found_value = false;
int word_count = 0; int word_count = 0;
key_iter = key_comps.begin(); key_iter = key_comps.begin();
key_end = key_comps.begin(); key_end = key_comps.begin();
for (; key_iter != key_comps.end(); ++key_iter) { for (; key_iter != key_comps.end(); ++key_iter) {
if (key_iter->e_token == DT_WORD || if (key_iter->e_token == DT_WORD || key_iter->e_token == DT_SYMBOL)
key_iter->e_token == DT_SYMBOL) { {
word_count += 1; word_count += 1;
if (found_value) { if (found_value) {
key_end = key_comps.begin(); key_end = key_comps.begin();
} }
} else if (key_iter->e_token == DT_WHITE) { } else if (key_iter->e_token == DT_WHITE) {
} else { } else {
if (!found_value) { if (!found_value) {
key_end = key_iter; key_end = key_iter;
@ -571,20 +561,15 @@ void data_parser::end_of_value(data_parser::element_list_t &el_stack,
if (word_count != 1) { if (word_count != 1) {
key_end = key_comps.begin(); key_end = key_comps.begin();
} }
value.SPLICE(value.end(), value.SPLICE(value.end(), key_comps, key_end, key_comps.end());
key_comps,
key_end,
key_comps.end());
strip(key_comps, element_if(DT_WHITE)); strip(key_comps, element_if(DT_WHITE));
if (!key_comps.empty()) { if (!key_comps.empty()) {
el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false)); el_stack.PUSH_BACK(element(key_comps, DNT_KEY, false));
} }
key_comps.CLEAR(); key_comps.CLEAR();
} else { } else {
value.SPLICE(value.end(), value.SPLICE(
key_comps, value.end(), key_comps, key_comps.begin(), key_comps.end());
key_comps.begin(),
key_comps.end());
} }
strip(value, element_if(DT_WHITE)); strip(value, element_if(DT_WHITE));
strip(value, element_if(DT_COLON)); strip(value, element_if(DT_COLON));
@ -602,34 +587,34 @@ void data_parser::end_of_value(data_parser::element_list_t &el_stack,
value.CLEAR(); value.CLEAR();
} }
void data_parser::parse() void
data_parser::parse()
{ {
this->discover_format(); this->discover_format();
this->pairup(&this->dp_schema_id, this->pairup(
this->dp_pairs, &this->dp_schema_id, this->dp_pairs, this->dp_group_stack.front());
this->dp_group_stack.front());
} }
std::string std::string
data_parser::get_element_string(const data_parser::element &elem) const data_parser::get_element_string(const data_parser::element& elem) const
{ {
pcre_input &pi = this->dp_scanner->get_input(); pcre_input& pi = this->dp_scanner->get_input();
return pi.get_substr(&elem.e_capture); return pi.get_substr(&elem.e_capture);
} }
std::string std::string
data_parser::get_string_up_to_value(const data_parser::element &elem) data_parser::get_string_up_to_value(const data_parser::element& elem)
{ {
pcre_input &pi = this->dp_scanner->get_input(); pcre_input& pi = this->dp_scanner->get_input();
const element &val_elem = elem.e_token == DNT_PAIR ? const element& val_elem
elem.e_sub_elements->back() : elem; = elem.e_token == DNT_PAIR ? elem.e_sub_elements->back() : elem;
if (this->dp_msg_format_begin <= val_elem.e_capture.c_begin) { if (this->dp_msg_format_begin <= val_elem.e_capture.c_begin) {
pcre_context::capture_t leading_and_key = pcre_context::capture_t( pcre_context::capture_t leading_and_key = pcre_context::capture_t(
this->dp_msg_format_begin, val_elem.e_capture.c_begin); this->dp_msg_format_begin, val_elem.e_capture.c_begin);
const char *str = pi.get_string(); const char* str = pi.get_string();
if (leading_and_key.length() >= 2) { if (leading_and_key.length() >= 2) {
switch (str[leading_and_key.c_end - 1]) { switch (str[leading_and_key.c_end - 1]) {
case '\'': case '\'':
@ -658,28 +643,30 @@ data_parser::get_string_up_to_value(const data_parser::element &elem)
return ""; return "";
} }
const char *data_parser::get_element_string(const data_parser::element &elem, const char*
size_t &len_out) data_parser::get_element_string(const data_parser::element& elem,
size_t& len_out)
{ {
pcre_input &pi = this->dp_scanner->get_input(); pcre_input& pi = this->dp_scanner->get_input();
len_out = elem.e_capture.length(); len_out = elem.e_capture.length();
return pi.get_substr_start(&elem.e_capture); return pi.get_substr_start(&elem.e_capture);
} }
void data_parser::print(FILE *out, data_parser::element_list_t &el) void
data_parser::print(FILE* out, data_parser::element_list_t& el)
{ {
fprintf(out, " %s\n", fprintf(
this->dp_scanner->get_input().get_string()); out, " %s\n", this->dp_scanner->get_input().get_string());
for (auto &iter : el) { for (auto& iter : el) {
iter.print(out, this->dp_scanner->get_input()); iter.print(out, this->dp_scanner->get_input());
} }
} }
FILE *data_parser::TRACE_FILE; FILE* data_parser::TRACE_FILE;
data_format_state_t dfs_prefix_next(data_format_state_t state, data_format_state_t
data_token_t next_token) dfs_prefix_next(data_format_state_t state, data_token_t next_token)
{ {
data_format_state_t retval = state; data_format_state_t retval = state;
@ -722,8 +709,8 @@ data_format_state_t dfs_prefix_next(data_format_state_t state,
return retval; return retval;
} }
data_format_state_t dfs_semi_next(data_format_state_t state, data_format_state_t
data_token_t next_token) dfs_semi_next(data_format_state_t state, data_token_t next_token)
{ {
data_format_state_t retval = state; data_format_state_t retval = state;
@ -777,8 +764,8 @@ data_format_state_t dfs_semi_next(data_format_state_t state,
return retval; return retval;
} }
data_format_state_t dfs_comma_next(data_format_state_t state, data_format_state_t
data_token_t next_token) dfs_comma_next(data_format_state_t state, data_token_t next_token)
{ {
data_format_state_t retval = state; data_format_state_t retval = state;
@ -872,25 +859,22 @@ data_format_state_t dfs_comma_next(data_format_state_t state,
} }
data_parser::element::element() data_parser::element::element()
: e_capture(-1, -1), : e_capture(-1, -1), e_token(DT_INVALID), e_sub_elements(nullptr)
e_token(DT_INVALID),
e_sub_elements(nullptr)
{ {
} }
data_parser::element::element(data_parser::element_list_t &subs, data_parser::element::element(data_parser::element_list_t& subs,
data_token_t token, bool assign_subs_elements) data_token_t token,
: e_capture(subs.front().e_capture.c_begin, bool assign_subs_elements)
subs.back().e_capture.c_end), : e_capture(subs.front().e_capture.c_begin, subs.back().e_capture.c_end),
e_token(token), e_token(token), e_sub_elements(nullptr)
e_sub_elements(nullptr)
{ {
if (assign_subs_elements) { if (assign_subs_elements) {
this->assign_elements(subs); this->assign_elements(subs);
} }
} }
data_parser::element::element(const data_parser::element &other) data_parser::element::element(const data_parser::element& other)
{ {
/* require(other.e_sub_elements == nullptr); */ /* require(other.e_sub_elements == nullptr); */
@ -908,8 +892,8 @@ data_parser::element::~element()
this->e_sub_elements = nullptr; this->e_sub_elements = nullptr;
} }
data_parser::element & data_parser::element&
data_parser::element::operator=(const data_parser::element &other) data_parser::element::operator=(const data_parser::element& other)
{ {
this->e_capture = other.e_capture; this->e_capture = other.e_capture;
this->e_token = other.e_token; this->e_token = other.e_token;
@ -920,41 +904,43 @@ data_parser::element::operator=(const data_parser::element &other)
return *this; return *this;
} }
void data_parser::element::assign_elements(data_parser::element_list_t &subs) void
data_parser::element::assign_elements(data_parser::element_list_t& subs)
{ {
if (this->e_sub_elements == nullptr) { if (this->e_sub_elements == nullptr) {
this->e_sub_elements = new element_list_t("_sub_", __FILE__, this->e_sub_elements = new element_list_t("_sub_", __FILE__, __LINE__);
__LINE__);
this->e_sub_elements->el_format = subs.el_format; this->e_sub_elements->el_format = subs.el_format;
} }
this->e_sub_elements->SWAP(subs); this->e_sub_elements->SWAP(subs);
this->update_capture(); this->update_capture();
} }
void data_parser::element::update_capture() void
data_parser::element::update_capture()
{ {
if (this->e_sub_elements != nullptr && !this->e_sub_elements->empty()) { if (this->e_sub_elements != nullptr && !this->e_sub_elements->empty()) {
this->e_capture.c_begin = this->e_capture.c_begin
this->e_sub_elements->front().e_capture.c_begin; = this->e_sub_elements->front().e_capture.c_begin;
this->e_capture.c_end = this->e_capture.c_end = this->e_sub_elements->back().e_capture.c_end;
this->e_sub_elements->back().e_capture.c_end;
} }
} }
const data_parser::element &data_parser::element::get_pair_value() const const data_parser::element&
data_parser::element::get_pair_value() const
{ {
require(this->e_token == DNT_PAIR); require(this->e_token == DNT_PAIR);
return this->e_sub_elements->back(); return this->e_sub_elements->back();
} }
data_token_t data_parser::element::value_token() const data_token_t
data_parser::element::value_token() const
{ {
data_token_t retval = DT_INVALID; data_token_t retval = DT_INVALID;
if (this->e_token == DNT_VALUE) { if (this->e_token == DNT_VALUE) {
if (this->e_sub_elements != nullptr && if (this->e_sub_elements != nullptr
this->e_sub_elements->size() == 1) { && this->e_sub_elements->size() == 1) {
retval = this->e_sub_elements->front().e_token; retval = this->e_sub_elements->front().e_token;
} else { } else {
retval = DT_SYMBOL; retval = DT_SYMBOL;
@ -965,18 +951,20 @@ data_token_t data_parser::element::value_token() const
return retval; return retval;
} }
const data_parser::element &data_parser::element::get_value_elem() const const data_parser::element&
data_parser::element::get_value_elem() const
{ {
if (this->e_token == DNT_VALUE) { if (this->e_token == DNT_VALUE) {
if (this->e_sub_elements != nullptr && if (this->e_sub_elements != nullptr
this->e_sub_elements->size() == 1) { && this->e_sub_elements->size() == 1) {
return this->e_sub_elements->front(); return this->e_sub_elements->front();
} }
} }
return *this; return *this;
} }
const data_parser::element &data_parser::element::get_pair_elem() const const data_parser::element&
data_parser::element::get_pair_elem() const
{ {
if (this->e_token == DNT_VALUE) { if (this->e_token == DNT_VALUE) {
return this->e_sub_elements->front(); return this->e_sub_elements->front();
@ -984,17 +972,19 @@ const data_parser::element &data_parser::element::get_pair_elem() const
return *this; return *this;
} }
void data_parser::element::print(FILE *out, pcre_input &pi, int offset) const void
data_parser::element::print(FILE* out, pcre_input& pi, int offset) const
{ {
int lpc; int lpc;
if (this->e_sub_elements != nullptr) { if (this->e_sub_elements != nullptr) {
for (auto &e_sub_element : *this->e_sub_elements) { for (auto& e_sub_element : *this->e_sub_elements) {
e_sub_element.print(out, pi, offset + 1); e_sub_element.print(out, pi, offset + 1);
} }
} }
fprintf(out, "%4s %3d:%-3d ", fprintf(out,
"%4s %3d:%-3d ",
data_scanner::token2name(this->e_token), data_scanner::token2name(this->e_token),
this->e_capture.c_begin, this->e_capture.c_begin,
this->e_capture.c_end); this->e_capture.c_end);
@ -1018,18 +1008,18 @@ void data_parser::element::print(FILE *out, pcre_input &pi, int offset) const
} }
data_parser::discover_format_state::discover_format_state() data_parser::discover_format_state::discover_format_state()
: dfs_prefix_state(DFS_INIT), : dfs_prefix_state(DFS_INIT), dfs_semi_state(DFS_INIT),
dfs_semi_state(DFS_INIT),
dfs_comma_state(DFS_INIT) dfs_comma_state(DFS_INIT)
{ {
memset(this->dfs_hist, 0, sizeof(this->dfs_hist)); memset(this->dfs_hist, 0, sizeof(this->dfs_hist));
} }
void data_parser::discover_format_state::update_for_element( void
const data_parser::element &elem) data_parser::discover_format_state::update_for_element(
const data_parser::element& elem)
{ {
this->dfs_prefix_state = dfs_prefix_next(this->dfs_prefix_state, this->dfs_prefix_state
elem.e_token); = dfs_prefix_next(this->dfs_prefix_state, elem.e_token);
this->dfs_semi_state = dfs_semi_next(this->dfs_semi_state, elem.e_token); this->dfs_semi_state = dfs_semi_next(this->dfs_semi_state, elem.e_token);
this->dfs_comma_state = dfs_comma_next(this->dfs_comma_state, elem.e_token); this->dfs_comma_state = dfs_comma_next(this->dfs_comma_state, elem.e_token);
if (this->dfs_prefix_state != DFS_ERROR) { if (this->dfs_prefix_state != DFS_ERROR) {
@ -1043,7 +1033,8 @@ void data_parser::discover_format_state::update_for_element(
this->dfs_hist[elem.e_token] += 1; this->dfs_hist[elem.e_token] += 1;
} }
void data_parser::discover_format_state::finalize() void
data_parser::discover_format_state::finalize()
{ {
data_token_t qualifier = this->dfs_format.df_qualifier; data_token_t qualifier = this->dfs_format.df_qualifier;
data_token_t separator = this->dfs_format.df_separator; data_token_t separator = this->dfs_format.df_separator;
@ -1060,9 +1051,10 @@ void data_parser::discover_format_state::finalize()
} else if (this->dfs_comma_state != DFS_ERROR) { } else if (this->dfs_comma_state != DFS_ERROR) {
this->dfs_format = FORMAT_COMMA; this->dfs_format = FORMAT_COMMA;
if (separator == DT_COLON && this->dfs_hist[DT_COMMA] > 0) { if (separator == DT_COLON && this->dfs_hist[DT_COMMA] > 0) {
if (!((this->dfs_hist[DT_COLON] == this->dfs_hist[DT_COMMA]) || if (!((this->dfs_hist[DT_COLON] == this->dfs_hist[DT_COMMA])
((this->dfs_hist[DT_COLON] - 1) == || ((this->dfs_hist[DT_COLON] - 1)
this->dfs_hist[DT_COMMA]))) { == this->dfs_hist[DT_COMMA])))
{
separator = DT_INVALID; separator = DT_INVALID;
if (this->dfs_hist[DT_COLON] == 1) { if (this->dfs_hist[DT_COLON] == 1) {
prefix_term = DT_COLON; prefix_term = DT_COLON;

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -30,30 +30,31 @@
#ifndef data_parser_hh #ifndef data_parser_hh
#define data_parser_hh #define data_parser_hh
#include <stdio.h> #include <iterator>
#include <list> #include <list>
#include <stack> #include <stack>
#include <vector> #include <vector>
#include <iterator>
#include <stdio.h>
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "pcrepp/pcrepp.hh"
#include "byte_array.hh" #include "byte_array.hh"
#include "data_scanner.hh" #include "data_scanner.hh"
#include "pcrepp/pcrepp.hh"
#define ELEMENT_LIST_T(var) var("" #var, __FILE__, __LINE__, group_depth) #define ELEMENT_LIST_T(var) var("" #var, __FILE__, __LINE__, group_depth)
#define PUSH_FRONT(elem) push_front(elem, __FILE__, __LINE__) #define PUSH_FRONT(elem) push_front(elem, __FILE__, __LINE__)
#define PUSH_BACK(elem) push_back(elem, __FILE__, __LINE__) #define PUSH_BACK(elem) push_back(elem, __FILE__, __LINE__)
#define POP_FRONT(elem) pop_front(__FILE__, __LINE__) #define POP_FRONT(elem) pop_front(__FILE__, __LINE__)
#define POP_BACK(elem) pop_back(__FILE__, __LINE__) #define POP_BACK(elem) pop_back(__FILE__, __LINE__)
#define CLEAR(elem) clear2(__FILE__, __LINE__) #define CLEAR(elem) clear2(__FILE__, __LINE__)
#define SWAP(other) swap(other, __FILE__, __LINE__) #define SWAP(other) swap(other, __FILE__, __LINE__)
#define SPLICE(pos, other, first, last) splice(pos, other, first, last, \ #define SPLICE(pos, other, first, last) \
__FILE__, __LINE__) splice(pos, other, first, last, __FILE__, __LINE__)
template<class Container, class UnaryPredicate> template<class Container, class UnaryPredicate>
void strip(Container &container, UnaryPredicate p) void
strip(Container& container, UnaryPredicate p)
{ {
while (!container.empty() && p(container.front())) { while (!container.empty() && p(container.front())) {
container.POP_FRONT(); container.POP_FRONT();
@ -72,18 +73,14 @@ enum data_format_state_t {
}; };
struct data_format { struct data_format {
data_format(const char *name = nullptr, data_format(const char* name = nullptr,
data_token_t appender = DT_INVALID, data_token_t appender = DT_INVALID,
data_token_t terminator = DT_INVALID) noexcept data_token_t terminator = DT_INVALID) noexcept
: df_name(name), : df_name(name), df_appender(appender), df_terminator(terminator),
df_appender(appender), df_qualifier(DT_INVALID), df_separator(DT_COLON),
df_terminator(terminator), df_prefix_terminator(DT_INVALID){};
df_qualifier(DT_INVALID),
df_separator(DT_COLON), const char* df_name;
df_prefix_terminator(DT_INVALID)
{};
const char * df_name;
data_token_t df_appender; data_token_t df_appender;
data_token_t df_terminator; data_token_t df_terminator;
data_token_t df_qualifier; data_token_t df_qualifier;
@ -98,117 +95,113 @@ data_format_state_t dfs_semi_next(data_format_state_t state,
data_format_state_t dfs_comma_next(data_format_state_t state, data_format_state_t dfs_comma_next(data_format_state_t state,
data_token_t next_token); data_token_t next_token);
#define LIST_INIT_TRACE \ #define LIST_INIT_TRACE \
do { \ do { \
if (TRACE_FILE != NULL) { \ if (TRACE_FILE != NULL) { \
fprintf(TRACE_FILE, \ fprintf(TRACE_FILE, \
"%p %s:%d %s %s %d\n", \ "%p %s:%d %s %s %d\n", \
this, \ this, \
fn, line, \ fn, \
__func__, \ line, \
varname, \ __func__, \
group_depth); \ varname, \
} \ group_depth); \
} \
} while (false) } while (false)
#define LIST_DEINIT_TRACE \ #define LIST_DEINIT_TRACE \
do { \ do { \
if (TRACE_FILE != NULL) { \ if (TRACE_FILE != NULL) { \
fprintf(TRACE_FILE, \ fprintf(TRACE_FILE, "%p %s:%d %s\n", this, fn, line, __func__); \
"%p %s:%d %s\n", \ } \
this, \
fn, line, \
__func__); \
} \
} while (false) } while (false)
#define ELEMENT_TRACE \ #define ELEMENT_TRACE \
do { \ do { \
if (TRACE_FILE != NULL) { \ if (TRACE_FILE != NULL) { \
fprintf(TRACE_FILE, \ fprintf(TRACE_FILE, \
"%p %s:%d %s %s %d:%d\n", \ "%p %s:%d %s %s %d:%d\n", \
this, \ this, \
fn, line, \ fn, \
__func__, \ line, \
__func__, \
data_scanner::token2name(elem.e_token), \ data_scanner::token2name(elem.e_token), \
elem.e_capture.c_begin, \ elem.e_capture.c_begin, \
elem.e_capture.c_end); \ elem.e_capture.c_end); \
} \ } \
} while (false) } while (false)
#define LIST_TRACE \ #define LIST_TRACE \
do { \ do { \
if (TRACE_FILE != NULL) { \ if (TRACE_FILE != NULL) { \
fprintf(TRACE_FILE, \ fprintf(TRACE_FILE, "%p %s:%d %s\n", this, fn, line, __func__); \
"%p %s:%d %s\n", \ } \
this, \
fn, line, \
__func__); \
} \
} while (false) } while (false)
#define SPLICE_TRACE \ #define SPLICE_TRACE \
do { \ do { \
if (TRACE_FILE != NULL) { \ if (TRACE_FILE != NULL) { \
fprintf(TRACE_FILE, \ fprintf(TRACE_FILE, \
"%p %s:%d %s %d %p %d:%d\n", \ "%p %s:%d %s %d %p %d:%d\n", \
this, \ this, \
fn, line, \ fn, \
__func__, \ line, \
(int)std::distance(this->begin(), pos), \ __func__, \
&other, \ (int) std::distance(this->begin(), pos), \
(int)std::distance(other.begin(), first), \ &other, \
(int)std::distance(last, other.end())); \ (int) std::distance(other.begin(), first), \
} \ (int) std::distance(last, other.end())); \
} \
} while (false); } while (false);
#define SWAP_TRACE(other) \ #define SWAP_TRACE(other) \
do { \ do { \
if (TRACE_FILE != NULL) { \ if (TRACE_FILE != NULL) { \
fprintf(TRACE_FILE, \ fprintf(TRACE_FILE, \
"%p %s:%d %s %p\n", \ "%p %s:%d %s %p\n", \
this, \ this, \
fn, line, \ fn, \
__func__, \ line, \
&other); \ __func__, \
} \ &other); \
} \
} while (false); } while (false);
#define POINT_TRACE(name) \ #define POINT_TRACE(name) \
do { \ do { \
if (TRACE_FILE) { \ if (TRACE_FILE) { \
fprintf(TRACE_FILE, \ fprintf( \
"0x0 %s:%d point %s\n", \ TRACE_FILE, "0x0 %s:%d point %s\n", __FILE__, __LINE__, name); \
__FILE__, __LINE__, \ } \
name); \
} \
} while (false); } while (false);
#define FORMAT_TRACE(elist) \ #define FORMAT_TRACE(elist) \
do { \ do { \
if (TRACE_FILE) { \ if (TRACE_FILE) { \
const data_format &df = elist.el_format; \ const data_format& df = elist.el_format; \
fprintf(TRACE_FILE, \ fprintf(TRACE_FILE, \
"%p %s:%d format %d %s %s %s %s %s\n", \ "%p %s:%d format %d %s %s %s %s %s\n", \
&elist, \ &elist, \
__FILE__, __LINE__, \ __FILE__, \
__LINE__, \
group_depth, \ group_depth, \
data_scanner::token2name(df.df_appender), \ data_scanner::token2name(df.df_appender), \
data_scanner::token2name(df.df_terminator), \ data_scanner::token2name(df.df_terminator), \
data_scanner::token2name(df.df_qualifier), \ data_scanner::token2name(df.df_qualifier), \
data_scanner::token2name(df.df_separator), \ data_scanner::token2name(df.df_separator), \
data_scanner::token2name(df.df_prefix_terminator)); \ data_scanner::token2name(df.df_prefix_terminator)); \
} \ } \
} while (false); } while (false);
#define CONSUMED_TRACE(elist) \ #define CONSUMED_TRACE(elist) \
do { \ do { \
if (TRACE_FILE) { \ if (TRACE_FILE) { \
fprintf(TRACE_FILE, \ fprintf(TRACE_FILE, \
"%p %s:%d consumed\n", \ "%p %s:%d consumed\n", \
&elist, \ &elist, \
__FILE__, __LINE__); \ __FILE__, \
} \ __LINE__); \
} \
} while (false); } while (false);
class data_parser { class data_parser {
@ -217,7 +210,7 @@ public:
static data_format FORMAT_COMMA; static data_format FORMAT_COMMA;
static data_format FORMAT_PLAIN; static data_format FORMAT_PLAIN;
static FILE *TRACE_FILE; static FILE* TRACE_FILE;
typedef byte_array<2, uint64_t> schema_id_t; typedef byte_array<2, uint64_t> schema_id_t;
@ -225,35 +218,39 @@ public:
/* typedef std::list<element> element_list_t; */ /* typedef std::list<element> element_list_t; */
class element_list_t : public std::list<element> { class element_list_t : public std::list<element> {
public: public:
element_list_t(const char *varname, const char *fn, int line, int group_depth = -1) element_list_t(const char* varname,
const char* fn,
int line,
int group_depth = -1)
{ {
LIST_INIT_TRACE; LIST_INIT_TRACE;
} }
element_list_t() element_list_t()
{ {
const char *varname = "_anon2_"; const char* varname = "_anon2_";
const char *fn = __FILE__; const char* fn = __FILE__;
int line = __LINE__; int line = __LINE__;
int group_depth = -1; int group_depth = -1;
LIST_INIT_TRACE; LIST_INIT_TRACE;
}; };
element_list_t(const element_list_t &other) : std::list<element>(other) { element_list_t(const element_list_t& other) : std::list<element>(other)
{
this->el_format = other.el_format; this->el_format = other.el_format;
} }
~element_list_t() ~element_list_t()
{ {
const char *fn = __FILE__; const char* fn = __FILE__;
int line = __LINE__; int line = __LINE__;
LIST_DEINIT_TRACE; LIST_DEINIT_TRACE;
}; };
void push_front(const element &elem, const char *fn, int line) void push_front(const element& elem, const char* fn, int line)
{ {
ELEMENT_TRACE; ELEMENT_TRACE;
@ -261,7 +258,7 @@ public:
this->std::list<element>::push_front(elem); this->std::list<element>::push_front(elem);
}; };
void push_back(const element &elem, const char *fn, int line) void push_back(const element& elem, const char* fn, int line)
{ {
ELEMENT_TRACE; ELEMENT_TRACE;
@ -269,38 +266,39 @@ public:
this->std::list<element>::push_back(elem); this->std::list<element>::push_back(elem);
}; };
void pop_front(const char *fn, int line) void pop_front(const char* fn, int line)
{ {
LIST_TRACE; LIST_TRACE;
this->std::list<element>::pop_front(); this->std::list<element>::pop_front();
}; };
void pop_back(const char *fn, int line) void pop_back(const char* fn, int line)
{ {
LIST_TRACE; LIST_TRACE;
this->std::list<element>::pop_back(); this->std::list<element>::pop_back();
}; };
void clear2(const char *fn, int line) void clear2(const char* fn, int line)
{ {
LIST_TRACE; LIST_TRACE;
this->std::list<element>::clear(); this->std::list<element>::clear();
}; };
void swap(element_list_t &other, const char *fn, int line) { void swap(element_list_t& other, const char* fn, int line)
{
SWAP_TRACE(other); SWAP_TRACE(other);
this->std::list<element>::swap(other); this->std::list<element>::swap(other);
} }
void splice(iterator pos, void splice(iterator pos,
element_list_t &other, element_list_t& other,
iterator first, iterator first,
iterator last, iterator last,
const char *fn, const char* fn,
int line) int line)
{ {
SPLICE_TRACE; SPLICE_TRACE;
@ -314,64 +312,64 @@ public:
struct element { struct element {
element(); element();
element(element_list_t &subs, element(element_list_t& subs,
data_token_t token, data_token_t token,
bool assign_subs_elements = true); bool assign_subs_elements = true);
element(const element &other); element(const element& other);
~element(); ~element();
element & operator=(const element &other); element& operator=(const element& other);
void assign_elements(element_list_t &subs); void assign_elements(element_list_t& subs);
void update_capture(); void update_capture();
const element &get_pair_value() const; const element& get_pair_value() const;
data_token_t value_token() const; data_token_t value_token() const;
const element &get_value_elem() const; const element& get_value_elem() const;
const element &get_pair_elem() const; const element& get_pair_elem() const;
void print(FILE *out, pcre_input &pi, int offset = 0) const; void print(FILE* out, pcre_input& pi, int offset = 0) const;
pcre_context::capture_t e_capture; pcre_context::capture_t e_capture;
data_token_t e_token; data_token_t e_token;
element_list_t * e_sub_elements; element_list_t* e_sub_elements;
}; };
struct element_cmp { struct element_cmp {
bool operator()(data_token_t token, const element &elem) const bool operator()(data_token_t token, const element& elem) const
{ {
return token == elem.e_token || token == DT_ANY; return token == elem.e_token || token == DT_ANY;
}; };
bool operator()(const element &elem, data_token_t token) const bool operator()(const element& elem, data_token_t token) const
{ {
return (*this)(token, elem); return (*this)(token, elem);
}; };
}; };
struct element_if { struct element_if {
element_if(data_token_t token) : ei_token(token) { }; element_if(data_token_t token) : ei_token(token){};
bool operator()(const element &a) const bool operator()(const element& a) const
{ {
return a.e_token == this->ei_token; return a.e_token == this->ei_token;
}; };
private: private:
data_token_t ei_token; data_token_t ei_token;
}; };
struct discover_format_state { struct discover_format_state {
discover_format_state(); discover_format_state();
void update_for_element(const element &elem); void update_for_element(const element& elem);
void finalize(); void finalize();
@ -383,28 +381,30 @@ private:
data_format dfs_format; data_format dfs_format;
}; };
data_parser(data_scanner *ds); data_parser(data_scanner* ds);
void pairup(schema_id_t *schema, element_list_t &pairs_out, void pairup(schema_id_t* schema,
element_list_t &in_list, int group_depth = 0); element_list_t& pairs_out,
element_list_t& in_list,
int group_depth = 0);
void discover_format(); void discover_format();
void end_of_value(element_list_t &el_stack, void end_of_value(element_list_t& el_stack,
element_list_t &key_comps, element_list_t& key_comps,
element_list_t &value, element_list_t& value,
const element_list_t &in_list, const element_list_t& in_list,
int group_depth); int group_depth);
void parse(); void parse();
std::string get_element_string(const element &elem) const; std::string get_element_string(const element& elem) const;
std::string get_string_up_to_value(const element &elem); std::string get_string_up_to_value(const element& elem);
const char *get_element_string(const element &elem, size_t &len_out); const char* get_element_string(const element& elem, size_t& len_out);
void print(FILE *out, element_list_t &el); void print(FILE* out, element_list_t& el);
std::vector<data_token_t> dp_group_token; std::vector<data_token_t> dp_group_token;
std::list<element_list_t> dp_group_stack; std::list<element_list_t> dp_group_stack;
@ -412,11 +412,11 @@ private:
element_list_t dp_errors; element_list_t dp_errors;
element_list_t dp_pairs; element_list_t dp_pairs;
schema_id_t dp_schema_id; schema_id_t dp_schema_id;
std::string *dp_msg_format; std::string* dp_msg_format;
int dp_msg_format_begin; int dp_msg_format_begin;
private: private:
data_scanner *dp_scanner; data_scanner* dp_scanner;
}; };
#endif #endif

@ -21,137 +21,219 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h" #include "data_scanner.hh"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h> #include <sys/socket.h>
#include "config.h"
#include "pcrepp/pcrepp.hh" #include "pcrepp/pcrepp.hh"
#include "data_scanner.hh"
using namespace std; using namespace std;
static struct { static struct {
const char *name; const char* name;
pcrepp pcre; pcrepp pcre;
} MATCHERS[DT_TERMINAL_MAX] = { } MATCHERS[DT_TERMINAL_MAX] = {
{ "quot", pcrepp("\\A(?:(?:u|r)?\"((?:\\\\.|[^\"])+)\"|" {
"(?:u|r)?'((?:\\\\.|[^'])+)')"), }, "quot",
{ "url", pcrepp("\\A([\\w]+://[^\\s'\"\\[\\](){}]+[/a-zA-Z0-9\\-=&])"), pcrepp("\\A(?:(?:u|r)?\"((?:\\\\.|[^\"])+)\"|"
}, "(?:u|r)?'((?:\\\\.|[^'])+)')"),
{ "path", pcrepp("\\A((?:/|\\./|\\.\\./)[\\w\\.\\-_\\~/]*)"), },
}, {
{ "mac", pcrepp( "url",
"\\A([0-9a-fA-F][0-9a-fA-F](?::[0-9a-fA-F][0-9a-fA-F]){5})(?!:)"), }, pcrepp("\\A([\\w]+://[^\\s'\"\\[\\](){}]+[/a-zA-Z0-9\\-=&])"),
{ "date", },
pcrepp("\\A(" {
"\\d{4}/\\d{1,2}/\\d{1,2}|" "path",
"\\d{4}-\\d{1,2}-\\d{1,2}|" pcrepp("\\A((?:/|\\./|\\.\\./)[\\w\\.\\-_\\~/]*)"),
"\\d{2}/\\w{3}/\\d{4}" },
")T?"), }, {
{ "time", pcrepp( "mac",
"\\A([\\s\\d]\\d:\\d\\d(?:(?!:\\d)|:\\d\\d(?:[\\.,]\\d{3,6})?Z?))\\b"), }, pcrepp(
"\\A([0-9a-fA-F][0-9a-fA-F](?::[0-9a-fA-F][0-9a-fA-F]){5})(?!:)"),
},
{
"date",
pcrepp("\\A("
"\\d{4}/\\d{1,2}/\\d{1,2}|"
"\\d{4}-\\d{1,2}-\\d{1,2}|"
"\\d{2}/\\w{3}/\\d{4}"
")T?"),
},
{
"time",
pcrepp("\\A([\\s\\d]\\d:\\d\\d(?:(?!:\\d)|:\\d\\d(?:[\\.,]\\d{3,6})?Z?)"
")\\b"),
},
/* { "qual", pcrepp("\\A([^\\s:=]+:[^\\s:=,]+(?!,)(?::[^\\s:=,]+)*)"), }, */ /* { "qual", pcrepp("\\A([^\\s:=]+:[^\\s:=,]+(?!,)(?::[^\\s:=,]+)*)"), }, */
{ "ipv6", pcrepp("\\A(::|[:\\da-fA-F\\.]+[a-fA-F\\d](?:%\\w+)?)"), }, {
{ "hexd", pcrepp( "ipv6",
"\\A([0-9a-fA-F][0-9a-fA-F](?::[0-9a-fA-F][0-9a-fA-F])+)"), }, pcrepp("\\A(::|[:\\da-fA-F\\.]+[a-fA-F\\d](?:%\\w+)?)"),
},
{
"hexd",
pcrepp("\\A([0-9a-fA-F][0-9a-fA-F](?::[0-9a-fA-F][0-9a-fA-F])+)"),
},
{ "xmlt", pcrepp( {
"\\A(<\\??[\\w:]+\\s*(?:[\\w:]+(?:\\s*=\\s*" "xmlt",
"(?:\"((?:\\\\.|[^\"])+)\"|'((?:\\\\.|[^'])+)'|[^>]+)" pcrepp("\\A(<\\??[\\w:]+\\s*(?:[\\w:]+(?:\\s*=\\s*"
"))*\\s*(?:/|\\?)>)"), }, "(?:\"((?:\\\\.|[^\"])+)\"|'((?:\\\\.|[^'])+)'|[^>]+)"
{ "xmlo", pcrepp( "))*\\s*(?:/|\\?)>)"),
"\\A(<[\\w:]+\\s*(?:[\\w:]+(?:\\s*=\\s*" },
"(?:\"((?:\\\\.|[^\"])+)\"|'((?:\\\\.|[^'])+)'|[^>]+)" {
"))*\\s*>)"), }, "xmlo",
pcrepp("\\A(<[\\w:]+\\s*(?:[\\w:]+(?:\\s*=\\s*"
"(?:\"((?:\\\\.|[^\"])+)\"|'((?:\\\\.|[^'])+)'|[^>]+)"
"))*\\s*>)"),
},
{ "xmlc", pcrepp("\\A(</[\\w:]+\\s*>)"), }, {
"xmlc",
pcrepp("\\A(</[\\w:]+\\s*>)"),
},
{ "coln", pcrepp("\\A(:)"), {
"coln",
pcrepp("\\A(:)"),
}, },
{ "eq", pcrepp("\\A(=)"), {
"eq",
pcrepp("\\A(=)"),
}, },
{ "comm", pcrepp("\\A(,)"), {
"comm",
pcrepp("\\A(,)"),
}, },
{ "semi", pcrepp("\\A(;)"), {
"semi",
pcrepp("\\A(;)"),
}, },
{ "empt", pcrepp("\\A(\\(\\)|\\{\\}|\\[\\])"), {
"empt",
pcrepp("\\A(\\(\\)|\\{\\}|\\[\\])"),
}, },
{ "lcurly", pcrepp("\\A({)"), {
"lcurly",
pcrepp("\\A({)"),
}, },
{ "rcurly", pcrepp("\\A(})"), {
"rcurly",
pcrepp("\\A(})"),
}, },
{ "lsquare", pcrepp("\\A(\\[)"), {
"lsquare",
pcrepp("\\A(\\[)"),
}, },
{ "rsquare", pcrepp("\\A(\\])"), {
"rsquare",
pcrepp("\\A(\\])"),
}, },
{ "lparen", pcrepp("\\A(\\()"), {
"lparen",
pcrepp("\\A(\\()"),
}, },
{ "rparen", pcrepp("\\A(\\))"), {
"rparen",
pcrepp("\\A(\\))"),
}, },
{ "langle", pcrepp("\\A(\\<)"), {
"langle",
pcrepp("\\A(\\<)"),
}, },
{ "rangle", pcrepp("\\A(\\>)"), {
"rangle",
pcrepp("\\A(\\>)"),
}, },
{ "ipv4", pcrepp("\\A(" {
"(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}" "ipv4",
"(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?![\\d]))"), pcrepp("\\A("
"(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}"
"(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(?![\\d]))"),
}, },
{ "uuid", pcrepp( {
"\\A([0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12})"), }, "uuid",
pcrepp("\\A([0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12})"),
},
{ "vers", pcrepp( {
"\\A(" "vers",
"[0-9]+(?:\\.[0-9]+\\w*){2,}(?:-\\w+)?|" pcrepp("\\A("
"[0-9]+(?:\\.[0-9]+\\w*)+(?<!\\d[eE])-\\w+?" "[0-9]+(?:\\.[0-9]+\\w*){2,}(?:-\\w+)?|"
")\\b"), "[0-9]+(?:\\.[0-9]+\\w*)+(?<!\\d[eE])-\\w+?"
")\\b"),
},
{
"oct",
pcrepp("\\A(-?0[0-7]+\\b)"),
}, },
{ "oct", pcrepp("\\A(-?0[0-7]+\\b)"), {
"pcnt",
pcrepp("\\A(-?[0-9]+(\\.[0-9]+)?[ ]*%\\b)"),
}, },
{ "pcnt", pcrepp("\\A(-?[0-9]+(\\.[0-9]+)?[ ]*%\\b)"), {
"num",
pcrepp("\\A(-?[0-9]+(\\.[0-9]+)?([eE][\\-+][0-9]+)?)"
"\\b(?![\\._\\-][a-zA-Z])"),
},
{
"hex",
pcrepp("\\A(-?(?:0x|[0-9])[0-9a-fA-F]+)"
"\\b(?![\\._\\-][a-zA-Z])"),
}, },
{ "num", pcrepp("\\A(-?[0-9]+(\\.[0-9]+)?([eE][\\-+][0-9]+)?)"
"\\b(?![\\._\\-][a-zA-Z])"), },
{ "hex", pcrepp("\\A(-?(?:0x|[0-9])[0-9a-fA-F]+)"
"\\b(?![\\._\\-][a-zA-Z])"), },
{ "mail", pcrepp( {
"\\A([a-zA-Z0-9\\._%+-]+@[a-zA-Z0-9\\.-]+\\.[a-zA-Z]+)\\b"), }, "mail",
{ "cnst", pcrepp("\\A([a-zA-Z0-9\\._%+-]+@[a-zA-Z0-9\\.-]+\\.[a-zA-Z]+)\\b"),
pcrepp("\\A(true|True|TRUE|false|False|FALSE|None|null)\\b") }, },
{ "word", pcrepp( {"cnst", pcrepp("\\A(true|True|TRUE|false|False|FALSE|None|null)\\b")},
"\\A([a-zA-Z][a-z']+(?=[\\s\\(\\)!\\*:;'\\\"\\?,]|[\\.\\!,\\?]\\s|$))"), {
"word",
pcrepp("\\A([a-zA-Z][a-z']+(?=[\\s\\(\\)!\\*:;'\\\"\\?,]|[\\.\\!,\\?]"
"\\s|$))"),
}, },
{ "sym", pcrepp( {
"\\A([^\";\\s:=,\\(\\)\\{\\}\\[\\]\\+#!@%\\^&\\*'\\?<>\\~`\\|\\\\]+" "sym",
"(?:::[^\";\\s:=,\\(\\)\\{\\}\\[\\]\\+#!@%\\^&\\*'\\?<>\\~`\\|\\\\]+)*)"), pcrepp(
"\\A([^\";\\s:=,\\(\\)\\{\\}\\[\\]\\+#!@%\\^&\\*'\\?<>\\~`\\|\\\\]+"
"(?:::[^\";\\s:=,\\(\\)\\{\\}\\[\\]\\+#!@%\\^&\\*'\\?<>\\~`\\|\\\\]"
"+)*)"),
}, },
{ "line", pcrepp("\\A(\r?\n|\r|;)"), {
"line",
pcrepp("\\A(\r?\n|\r|;)"),
}, },
{ "wspc", pcrepp("\\A([ \\r\\t\\n]+)"), {
"wspc",
pcrepp("\\A([ \\r\\t\\n]+)"),
}, },
{ "dot", pcrepp("\\A(\\.)"), {
"dot",
pcrepp("\\A(\\.)"),
}, },
{ "gbg", pcrepp("\\A(.)"), {
"gbg",
pcrepp("\\A(.)"),
}, },
}; };
const char *DNT_NAMES[DNT_MAX - DNT_KEY] = { const char* DNT_NAMES[DNT_MAX - DNT_KEY] = {
"key", "key",
"pair", "pair",
"val", "val",
@ -164,24 +246,22 @@ const char *DNT_NAMES[DNT_MAX - DNT_KEY] = {
"grp", "grp",
}; };
const char *data_scanner::token2name(data_token_t token) const char*
data_scanner::token2name(data_token_t token)
{ {
if (token < 0) { if (token < 0) {
return "inv"; return "inv";
} } else if (token < DT_TERMINAL_MAX) {
else if (token < DT_TERMINAL_MAX) {
return MATCHERS[token].name; return MATCHERS[token].name;
} } else if (token == DT_ANY) {
else if (token == DT_ANY) {
return "any"; return "any";
} } else {
else{
return DNT_NAMES[token - DNT_KEY]; return DNT_NAMES[token - DNT_KEY];
} }
} }
static static bool
bool find_string_end(const char *str, size_t &start, size_t length, char term) find_string_end(const char* str, size_t& start, size_t length, char term)
{ {
for (; start < length; start++) { for (; start < length; start++) {
if (str[start] == term) { if (str[start] == term) {
@ -198,29 +278,29 @@ bool find_string_end(const char *str, size_t &start, size_t length, char term)
return false; return false;
} }
static static void
void single_char_capture(pcre_context &pc, pcre_input &pi) single_char_capture(pcre_context& pc, pcre_input& pi)
{ {
pc.all()[0].c_begin = pi.pi_offset; pc.all()[0].c_begin = pi.pi_offset;
pc.all()[0].c_end = pi.pi_offset + 1; pc.all()[0].c_end = pi.pi_offset + 1;
pc.all()[1] = pc.all()[0]; pc.all()[1] = pc.all()[0];
pc.set_count(2); pc.set_count(2);
pi.pi_next_offset = pi.pi_offset + 1; pi.pi_next_offset = pi.pi_offset + 1;
} }
bool data_scanner::tokenize(pcre_context &pc, data_token_t &token_out) bool
data_scanner::tokenize(pcre_context& pc, data_token_t& token_out)
{ {
const char *str = this->ds_pcre_input.get_string(); const char* str = this->ds_pcre_input.get_string();
pcre_input &pi = this->ds_pcre_input; pcre_input& pi = this->ds_pcre_input;
int lpc; int lpc;
token_out = data_token_t(-1); token_out = data_token_t(-1);
if (this->ds_pcre_input.pi_next_offset > this->ds_pcre_input.pi_length) { if (this->ds_pcre_input.pi_next_offset > this->ds_pcre_input.pi_length) {
return false; return false;
} } else if (this->ds_pcre_input.pi_next_offset
else if (this->ds_pcre_input.pi_next_offset == == this->ds_pcre_input.pi_length) {
this->ds_pcre_input.pi_length) {
this->ds_pcre_input.pi_next_offset += 1; this->ds_pcre_input.pi_next_offset += 1;
token_out = DT_LINE; token_out = DT_LINE;
return false; return false;
@ -228,118 +308,113 @@ bool data_scanner::tokenize(pcre_context &pc, data_token_t &token_out)
for (lpc = 0; lpc < DT_TERMINAL_MAX; lpc++) { for (lpc = 0; lpc < DT_TERMINAL_MAX; lpc++) {
switch (lpc) { switch (lpc) {
case DT_QUOTED_STRING: { case DT_QUOTED_STRING: {
size_t str_start, str_end; size_t str_start, str_end;
bool found = false; bool found = false;
pi.pi_offset = pi.pi_next_offset; pi.pi_offset = pi.pi_next_offset;
str_end = str_start = pi.pi_offset + 1; str_end = str_start = pi.pi_offset + 1;
switch (str[pi.pi_offset]) { switch (str[pi.pi_offset]) {
case 'u': case 'u':
case 'r': case 'r':
if (pi.pi_offset + 1 < pi.pi_length && if (pi.pi_offset + 1 < pi.pi_length
(str[pi.pi_offset + 1] == '\'' || && (str[pi.pi_offset + 1] == '\''
str[pi.pi_offset + 1] == '\"')) { || str[pi.pi_offset + 1] == '\"'))
str_start += 1; {
str_end += 1; str_start += 1;
found = find_string_end(str, str_end += 1;
str_end, found = find_string_end(str,
pi.pi_length, str_end,
str[pi.pi_offset + 1]); pi.pi_length,
} str[pi.pi_offset + 1]);
break; }
break;
case '\'':
case '\"':
found = find_string_end(str,
str_end,
pi.pi_length,
str[pi.pi_offset]);
break;
}
if (found) {
token_out = data_token_t(DT_QUOTED_STRING);
pi.pi_next_offset = str_end;
pc.all()[0].c_begin = pi.pi_offset;
pc.all()[0].c_end = str_end;
pc.all()[1].c_begin = str_start;
pc.all()[1].c_end = str_end - 1;
pc.set_count(2);
return true;
}
}
break;
case DT_COLON: {
pi.pi_offset = pi.pi_next_offset;
if (str[pi.pi_offset] == ':') {
token_out = data_token_t(DT_COLON);
single_char_capture(pc, pi);
return true;
}
}
break;
case DT_EQUALS: { case '\'':
pi.pi_offset = pi.pi_next_offset; case '\"':
found = find_string_end(
str, str_end, pi.pi_length, str[pi.pi_offset]);
break;
}
if (found) {
token_out = data_token_t(DT_QUOTED_STRING);
pi.pi_next_offset = str_end;
pc.all()[0].c_begin = pi.pi_offset;
pc.all()[0].c_end = str_end;
pc.all()[1].c_begin = str_start;
pc.all()[1].c_end = str_end - 1;
pc.set_count(2);
return true;
}
} break;
if (str[pi.pi_offset] == '=') { case DT_COLON: {
token_out = data_token_t(DT_EQUALS); pi.pi_offset = pi.pi_next_offset;
single_char_capture(pc, pi);
return true;
}
}
break;
case DT_COMMA: { if (str[pi.pi_offset] == ':') {
pi.pi_offset = pi.pi_next_offset; token_out = data_token_t(DT_COLON);
single_char_capture(pc, pi);
return true;
}
} break;
if (str[pi.pi_offset] == ',') { case DT_EQUALS: {
token_out = data_token_t(DT_COMMA); pi.pi_offset = pi.pi_next_offset;
single_char_capture(pc, pi);
return true;
}
}
break;
case DT_SEMI: { if (str[pi.pi_offset] == '=') {
pi.pi_offset = pi.pi_next_offset; token_out = data_token_t(DT_EQUALS);
single_char_capture(pc, pi);
return true;
}
} break;
if (str[pi.pi_offset] == ';') { case DT_COMMA: {
token_out = data_token_t(DT_SEMI); pi.pi_offset = pi.pi_next_offset;
single_char_capture(pc, pi);
return true;
}
}
break;
default: if (str[pi.pi_offset] == ',') {
if (MATCHERS[lpc].pcre.match(pc, this->ds_pcre_input, token_out = data_token_t(DT_COMMA);
PCRE_ANCHORED)) { single_char_capture(pc, pi);
switch (lpc) { return true;
case DT_IPV6_ADDRESS: }
if (pc.all()->length() <= INET6_ADDRSTRLEN) { } break;
char in6str[INET6_ADDRSTRLEN];
char buf[sizeof(struct in6_addr)];
this->ds_pcre_input.get_substr(pc.all(), in6str); case DT_SEMI: {
pi.pi_offset = pi.pi_next_offset;
if (inet_pton(AF_INET6, in6str, buf) == 1) { if (str[pi.pi_offset] == ';') {
token_out = data_token_t(DT_SEMI);
single_char_capture(pc, pi);
return true;
}
} break;
default:
if (MATCHERS[lpc].pcre.match(
pc, this->ds_pcre_input, PCRE_ANCHORED)) {
switch (lpc) {
case DT_IPV6_ADDRESS:
if (pc.all()->length() <= INET6_ADDRSTRLEN) {
char in6str[INET6_ADDRSTRLEN];
char buf[sizeof(struct in6_addr)];
this->ds_pcre_input.get_substr(pc.all(),
in6str);
if (inet_pton(AF_INET6, in6str, buf) == 1) {
token_out = data_token_t(lpc);
return true;
}
}
this->ds_pcre_input.pi_next_offset
= this->ds_pcre_input.pi_offset;
break;
default:
token_out = data_token_t(lpc); token_out = data_token_t(lpc);
return true; return true;
}
} }
this->ds_pcre_input.pi_next_offset =
this->ds_pcre_input.pi_offset;
break;
default:
token_out = data_token_t(lpc);
return true;
} }
} break;
break;
} }
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -36,7 +36,7 @@
#include "shared_buffer.hh" #include "shared_buffer.hh"
enum data_token_t { enum data_token_t {
DT_INVALID = -1, DT_INVALID = -1,
DT_QUOTED_STRING = 0, DT_QUOTED_STRING = 0,
DT_URL, DT_URL,
@ -91,7 +91,7 @@ enum data_token_t {
DT_TERMINAL_MAX = DT_GARBAGE + 1, DT_TERMINAL_MAX = DT_GARBAGE + 1,
DNT_KEY = 50, DNT_KEY = 50,
DNT_PAIR, DNT_PAIR,
DNT_VALUE, DNT_VALUE,
DNT_ROW, DNT_ROW,
@ -109,19 +109,24 @@ enum data_token_t {
class data_scanner { class data_scanner {
public: public:
static const char *token2name(data_token_t token); static const char* token2name(data_token_t token);
data_scanner(const std::string &line, size_t off = 0, size_t len = (size_t) -1) data_scanner(const std::string& line,
: ds_line(line), size_t off = 0,
ds_pcre_input(ds_line.c_str(), off, len) size_t len = (size_t) -1)
: ds_line(line), ds_pcre_input(ds_line.c_str(), off, len)
{ {
if (!line.empty() && line[line.length() - 1] == '.') { if (!line.empty() && line[line.length() - 1] == '.') {
this->ds_pcre_input.pi_length -= 1; this->ds_pcre_input.pi_length -= 1;
} }
}; };
data_scanner(shared_buffer_ref &line, size_t off = 0, size_t len = (size_t) -1) data_scanner(shared_buffer_ref& line,
: ds_sbr(line), ds_pcre_input(line.get_data(), off, len == (size_t) -1 ? line.length() : len) size_t off = 0,
size_t len = (size_t) -1)
: ds_sbr(line),
ds_pcre_input(
line.get_data(), off, len == (size_t) -1 ? line.length() : len)
{ {
require(len == (size_t) -1 || len <= line.length()); require(len == (size_t) -1 || len <= line.length());
if (line.length() > 0 && line.get_data()[line.length() - 1] == '.') { if (line.length() > 0 && line.get_data()[line.length() - 1] == '.') {
@ -129,12 +134,16 @@ public:
} }
}; };
bool tokenize(pcre_context &pc, data_token_t &token_out); bool tokenize(pcre_context& pc, data_token_t& token_out);
bool tokenize2(pcre_context &pc, data_token_t &token_out); bool tokenize2(pcre_context& pc, data_token_t& token_out);
pcre_input &get_input() { return this->ds_pcre_input; }; pcre_input& get_input()
{
return this->ds_pcre_input;
};
void reset() { void reset()
{
this->ds_pcre_input.reset_next_offset(); this->ds_pcre_input.reset_next_offset();
}; };

File diff suppressed because it is too large Load Diff

@ -21,29 +21,30 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <regex> #include <regex>
#include "db_sub_source.hh"
#include "base/date_time_scanner.hh" #include "base/date_time_scanner.hh"
#include "base/time_util.hh" #include "base/time_util.hh"
#include "config.h"
#include "yajlpp/json_ptr.hh" #include "yajlpp/json_ptr.hh"
#include "db_sub_source.hh"
const char *db_label_source::NULL_STR = "<NULL>"; const char* db_label_source::NULL_STR = "<NULL>";
constexpr size_t MAX_COLUMN_WIDTH = 120; constexpr size_t MAX_COLUMN_WIDTH = 120;
void db_label_source::text_value_for_line(textview_curses &tc, int row, void
std::string &label_out, db_label_source::text_value_for_line(textview_curses& tc,
text_sub_source::line_flags_t flags) int row,
std::string& label_out,
text_sub_source::line_flags_t flags)
{ {
static const std::string TAB_SYMBOL = "\u21e5"; static const std::string TAB_SYMBOL = "\u21e5";
static const std::string LF_SYMBOL = "\u240a"; static const std::string LF_SYMBOL = "\u240a";
@ -55,12 +56,12 @@ void db_label_source::text_value_for_line(textview_curses &tc, int row,
*/ */
label_out.clear(); label_out.clear();
if (row >= (int)this->dls_rows.size()) { if (row >= (int) this->dls_rows.size()) {
return; return;
} }
for (int lpc = 0; lpc < (int)this->dls_rows[row].size(); lpc++) { for (int lpc = 0; lpc < (int) this->dls_rows[row].size(); lpc++) {
auto actual_col_size = std::min(MAX_COLUMN_WIDTH, auto actual_col_size
this->dls_headers[lpc].hm_column_size); = std::min(MAX_COLUMN_WIDTH, this->dls_headers[lpc].hm_column_size);
auto raw_cell_str = std::string(this->dls_rows[row][lpc]); auto raw_cell_str = std::string(this->dls_rows[row][lpc]);
std::string cell_str; std::string cell_str;
@ -83,8 +84,8 @@ void db_label_source::text_value_for_line(textview_curses &tc, int row,
truncate_to(cell_str, MAX_COLUMN_WIDTH); truncate_to(cell_str, MAX_COLUMN_WIDTH);
auto cell_length = utf8_string_length(cell_str) auto cell_length
.unwrapOr(actual_col_size); = utf8_string_length(cell_str).unwrapOr(actual_col_size);
auto padding = actual_col_size - cell_length; auto padding = actual_col_size - cell_length;
this->dls_cell_width[lpc] = cell_str.length() + padding; this->dls_cell_width[lpc] = cell_str.length() + padding;
if (this->dls_headers[lpc].hm_column_type != SQLITE3_TEXT) { if (this->dls_headers[lpc].hm_column_type != SQLITE3_TEXT) {
@ -98,13 +99,15 @@ void db_label_source::text_value_for_line(textview_curses &tc, int row,
} }
} }
void db_label_source::text_attrs_for_line(textview_curses &tc, int row, void
string_attrs_t &sa) db_label_source::text_attrs_for_line(textview_curses& tc,
int row,
string_attrs_t& sa)
{ {
struct line_range lr(0, 0); struct line_range lr(0, 0);
struct line_range lr2(0, -1); struct line_range lr2(0, -1);
if (row >= (int)this->dls_rows.size()) { if (row >= (int) this->dls_rows.size()) {
return; return;
} }
for (size_t lpc = 0; lpc < this->dls_headers.size() - 1; lpc++) { for (size_t lpc = 0; lpc < this->dls_headers.size() - 1; lpc++) {
@ -119,30 +122,35 @@ void db_label_source::text_attrs_for_line(textview_curses &tc, int row,
int left = 0; int left = 0;
for (size_t lpc = 0; lpc < this->dls_headers.size(); lpc++) { for (size_t lpc = 0; lpc < this->dls_headers.size(); lpc++) {
const char *row_value = this->dls_rows[row][lpc]; const char* row_value = this->dls_rows[row][lpc];
size_t row_len = strlen(row_value); size_t row_len = strlen(row_value);
if (this->dls_headers[lpc].hm_graphable) { if (this->dls_headers[lpc].hm_graphable) {
double num_value; double num_value;
if (sscanf(row_value, "%lf", &num_value) == 1) { if (sscanf(row_value, "%lf", &num_value) == 1) {
this->dls_chart.chart_attrs_for_value(tc, left, this->dls_headers[lpc].hm_name, num_value, sa); this->dls_chart.chart_attrs_for_value(
tc, left, this->dls_headers[lpc].hm_name, num_value, sa);
} }
} }
if (row_len > 2 && row_len < MAX_COLUMN_WIDTH && if (row_len > 2 && row_len < MAX_COLUMN_WIDTH
((row_value[0] == '{' && row_value[row_len - 1] == '}') || && ((row_value[0] == '{' && row_value[row_len - 1] == '}')
(row_value[0] == '[' && row_value[row_len - 1] == ']'))) { || (row_value[0] == '[' && row_value[row_len - 1] == ']')))
{
json_ptr_walk jpw; json_ptr_walk jpw;
if (jpw.parse(row_value, row_len) == yajl_status_ok && if (jpw.parse(row_value, row_len) == yajl_status_ok
jpw.complete_parse() == yajl_status_ok) { && jpw.complete_parse() == yajl_status_ok)
for (auto &jpw_value : jpw.jpw_values) { {
for (auto& jpw_value : jpw.jpw_values) {
double num_value; double num_value;
if (jpw_value.wt_type == yajl_t_number && if (jpw_value.wt_type == yajl_t_number
sscanf(jpw_value.wt_value.c_str(), "%lf", &num_value) == 1) { && sscanf(jpw_value.wt_value.c_str(), "%lf", &num_value)
this->dls_chart.chart_attrs_for_value(tc, left, == 1)
jpw_value.wt_ptr, num_value, sa); {
this->dls_chart.chart_attrs_for_value(
tc, left, jpw_value.wt_ptr, num_value, sa);
} }
} }
} }
@ -150,13 +158,15 @@ void db_label_source::text_attrs_for_line(textview_curses &tc, int row,
} }
} }
void db_label_source::push_header(const std::string &colstr, int type, void
bool graphable) db_label_source::push_header(const std::string& colstr,
int type,
bool graphable)
{ {
this->dls_headers.emplace_back(colstr); this->dls_headers.emplace_back(colstr);
this->dls_cell_width.push_back(0); this->dls_cell_width.push_back(0);
header_meta &hm = this->dls_headers.back(); header_meta& hm = this->dls_headers.back();
hm.hm_column_size = utf8_string_length(colstr).unwrapOr(colstr.length()); hm.hm_column_size = utf8_string_length(colstr).unwrapOr(colstr.length());
hm.hm_column_type = type; hm.hm_column_type = type;
@ -166,17 +176,17 @@ void db_label_source::push_header(const std::string &colstr, int type,
} }
} }
void db_label_source::push_column(const char *colstr) void
db_label_source::push_column(const char* colstr)
{ {
view_colors &vc = view_colors::singleton(); view_colors& vc = view_colors::singleton();
int index = this->dls_rows.back().size(); int index = this->dls_rows.back().size();
double num_value = 0.0; double num_value = 0.0;
size_t value_len; size_t value_len;
if (colstr == nullptr) { if (colstr == nullptr) {
colstr = NULL_STR; colstr = NULL_STR;
} } else {
else {
colstr = strdup(colstr); colstr = strdup(colstr);
if (colstr == nullptr) { if (colstr == nullptr) {
throw "out of memory"; throw "out of memory";
@ -192,36 +202,39 @@ void db_label_source::push_column(const char *colstr)
tv.tv_sec = -1; tv.tv_sec = -1;
tv.tv_usec = -1; tv.tv_usec = -1;
} }
if (!this->dls_time_column.empty() && tv < this->dls_time_column.back()) { if (!this->dls_time_column.empty() && tv < this->dls_time_column.back())
{
this->dls_time_column_index = -1; this->dls_time_column_index = -1;
this->dls_time_column.clear(); this->dls_time_column.clear();
} } else {
else {
this->dls_time_column.push_back(tv); this->dls_time_column.push_back(tv);
} }
} }
this->dls_rows.back().push_back(colstr); this->dls_rows.back().push_back(colstr);
this->dls_headers[index].hm_column_size = this->dls_headers[index].hm_column_size
std::max(this->dls_headers[index].hm_column_size, = std::max(this->dls_headers[index].hm_column_size,
utf8_string_length(colstr, value_len).unwrapOr(value_len)); utf8_string_length(colstr, value_len).unwrapOr(value_len));
if (colstr != nullptr && this->dls_headers[index].hm_graphable) { if (colstr != nullptr && this->dls_headers[index].hm_graphable) {
if (sscanf(colstr, "%lf", &num_value) != 1) { if (sscanf(colstr, "%lf", &num_value) != 1) {
num_value = 0.0; num_value = 0.0;
} }
this->dls_chart.add_value(this->dls_headers[index].hm_name, num_value); this->dls_chart.add_value(this->dls_headers[index].hm_name, num_value);
} } else if (value_len > 2
else if (value_len > 2 && && ((colstr[0] == '{' && colstr[value_len - 1] == '}')
((colstr[0] == '{' && colstr[value_len - 1] == '}') || || (colstr[0] == '[' && colstr[value_len - 1] == ']')))
(colstr[0] == '[' && colstr[value_len - 1] == ']'))) { {
json_ptr_walk jpw; json_ptr_walk jpw;
if (jpw.parse(colstr, value_len) == yajl_status_ok && if (jpw.parse(colstr, value_len) == yajl_status_ok
jpw.complete_parse() == yajl_status_ok) { && jpw.complete_parse() == yajl_status_ok)
for (auto &jpw_value : jpw.jpw_values) { {
if (jpw_value.wt_type == yajl_t_number && for (auto& jpw_value : jpw.jpw_values) {
sscanf(jpw_value.wt_value.c_str(), "%lf", &num_value) == 1) { if (jpw_value.wt_type == yajl_t_number
&& sscanf(jpw_value.wt_value.c_str(), "%lf", &num_value)
== 1)
{
this->dls_chart.add_value(jpw_value.wt_ptr, num_value); this->dls_chart.add_value(jpw_value.wt_ptr, num_value);
this->dls_chart.with_attrs_for_ident( this->dls_chart.with_attrs_for_ident(
jpw_value.wt_ptr, vc.attrs_for_ident(jpw_value.wt_ptr)); jpw_value.wt_ptr, vc.attrs_for_ident(jpw_value.wt_ptr));
@ -231,14 +244,15 @@ void db_label_source::push_column(const char *colstr)
} }
} }
void db_label_source::clear() void
db_label_source::clear()
{ {
this->dls_chart.clear(); this->dls_chart.clear();
this->dls_headers.clear(); this->dls_headers.clear();
for (size_t row = 0; row < this->dls_rows.size(); row++) { for (size_t row = 0; row < this->dls_rows.size(); row++) {
for (size_t col = 0; col < this->dls_rows[row].size(); col++) { for (size_t col = 0; col < this->dls_rows[row].size(); col++) {
if (this->dls_rows[row][col] != NULL_STR) { if (this->dls_rows[row][col] != NULL_STR) {
free((void *)this->dls_rows[row][col]); free((void*) this->dls_rows[row][col]);
} }
} }
} }
@ -247,13 +261,12 @@ void db_label_source::clear()
this->dls_cell_width.clear(); this->dls_cell_width.clear();
} }
long db_label_source::column_name_to_index(const std::string &name) const long
db_label_source::column_name_to_index(const std::string& name) const
{ {
std::vector<header_meta>::const_iterator iter; std::vector<header_meta>::const_iterator iter;
iter = std::find(this->dls_headers.begin(), iter = std::find(this->dls_headers.begin(), this->dls_headers.end(), name);
this->dls_headers.end(),
name);
if (iter == this->dls_headers.end()) { if (iter == this->dls_headers.end()) {
return -1; return -1;
} }
@ -261,7 +274,8 @@ long db_label_source::column_name_to_index(const std::string &name) const
return std::distance(this->dls_headers.begin(), iter); return std::distance(this->dls_headers.begin(), iter);
} }
nonstd::optional<vis_line_t> db_label_source::row_for_time(struct timeval time_bucket) nonstd::optional<vis_line_t>
db_label_source::row_for_time(struct timeval time_bucket)
{ {
std::vector<struct timeval>::iterator iter; std::vector<struct timeval>::iterator iter;
@ -274,7 +288,8 @@ nonstd::optional<vis_line_t> db_label_source::row_for_time(struct timeval time_b
return nonstd::nullopt; return nonstd::nullopt;
} }
size_t db_overlay_source::list_overlay_count(const listview_curses &lv) size_t
db_overlay_source::list_overlay_count(const listview_curses& lv)
{ {
size_t retval = 1; size_t retval = 1;
@ -284,9 +299,9 @@ size_t db_overlay_source::list_overlay_count(const listview_curses &lv)
return retval; return retval;
} }
view_colors &vc = view_colors::singleton(); view_colors& vc = view_colors::singleton();
vis_line_t top = lv.get_top(); vis_line_t top = lv.get_top();
const std::vector<const char *> &cols = this->dos_labels->dls_rows[top]; const std::vector<const char*>& cols = this->dos_labels->dls_rows[top];
unsigned long width; unsigned long width;
vis_line_t height; vis_line_t height;
@ -294,22 +309,24 @@ size_t db_overlay_source::list_overlay_count(const listview_curses &lv)
this->dos_lines.clear(); this->dos_lines.clear();
for (size_t col = 0; col < cols.size(); col++) { for (size_t col = 0; col < cols.size(); col++) {
const char *col_value = cols[col]; const char* col_value = cols[col];
size_t col_len = strlen(col_value); size_t col_len = strlen(col_value);
if (!(col_len >= 2 && if (!(col_len >= 2
((col_value[0] == '{' && col_value[col_len - 1] == '}') || && ((col_value[0] == '{' && col_value[col_len - 1] == '}')
(col_value[0] == '[' && col_value[col_len - 1] == ']')))) { || (col_value[0] == '[' && col_value[col_len - 1] == ']'))))
{
continue; continue;
} }
json_ptr_walk jpw; json_ptr_walk jpw;
if (jpw.parse(col_value, col_len) == yajl_status_ok && if (jpw.parse(col_value, col_len) == yajl_status_ok
jpw.complete_parse() == yajl_status_ok) { && jpw.complete_parse() == yajl_status_ok)
{
{ {
const std::string &header = this->dos_labels->dls_headers[col].hm_name; const std::string& header
= this->dos_labels->dls_headers[col].hm_name;
this->dos_lines.emplace_back(" JSON Column: " + header); this->dos_lines.emplace_back(" JSON Column: " + header);
retval += 1; retval += 1;
@ -318,14 +335,13 @@ size_t db_overlay_source::list_overlay_count(const listview_curses &lv)
stacked_bar_chart<std::string> chart; stacked_bar_chart<std::string> chart;
int start_line = this->dos_lines.size(); int start_line = this->dos_lines.size();
chart.with_stacking_enabled(false) chart.with_stacking_enabled(false).with_margins(3, 0);
.with_margins(3, 0);
for (auto &jpw_value : jpw.jpw_values) { for (auto& jpw_value : jpw.jpw_values) {
this->dos_lines.emplace_back(" " + jpw_value.wt_ptr + " = " + this->dos_lines.emplace_back(" " + jpw_value.wt_ptr + " = "
jpw_value.wt_value); + jpw_value.wt_value);
string_attrs_t &sa = this->dos_lines.back().get_attrs(); string_attrs_t& sa = this->dos_lines.back().get_attrs();
struct line_range lr(1, 2); struct line_range lr(1, 2);
sa.emplace_back(lr, &view_curses::VC_GRAPHIC, ACS_LTEE); sa.emplace_back(lr, &view_curses::VC_GRAPHIC, ACS_LTEE);
@ -335,8 +351,10 @@ size_t db_overlay_source::list_overlay_count(const listview_curses &lv)
double num_value = 0.0; double num_value = 0.0;
if (jpw_value.wt_type == yajl_t_number && if (jpw_value.wt_type == yajl_t_number
sscanf(jpw_value.wt_value.c_str(), "%lf", &num_value) == 1) { && sscanf(jpw_value.wt_value.c_str(), "%lf", &num_value)
== 1)
{
int attrs = vc.attrs_for_ident(jpw_value.wt_ptr); int attrs = vc.attrs_for_ident(jpw_value.wt_ptr);
chart.add_value(jpw_value.wt_ptr, num_value); chart.add_value(jpw_value.wt_ptr, num_value);
@ -349,15 +367,18 @@ size_t db_overlay_source::list_overlay_count(const listview_curses &lv)
int curr_line = start_line; int curr_line = start_line;
for (auto iter = jpw.jpw_values.begin(); for (auto iter = jpw.jpw_values.begin();
iter != jpw.jpw_values.end(); iter != jpw.jpw_values.end();
++iter, curr_line++) { ++iter, curr_line++)
{
double num_value = 0.0; double num_value = 0.0;
if (iter->wt_type == yajl_t_number && if (iter->wt_type == yajl_t_number
sscanf(iter->wt_value.c_str(), "%lf", &num_value) == 1) { && sscanf(iter->wt_value.c_str(), "%lf", &num_value) == 1)
string_attrs_t &sa = this->dos_lines[curr_line].get_attrs(); {
string_attrs_t& sa = this->dos_lines[curr_line].get_attrs();
int left = 3; int left = 3;
chart.chart_attrs_for_value(lv, left, iter->wt_ptr, num_value, sa); chart.chart_attrs_for_value(
lv, left, iter->wt_ptr, num_value, sa);
} }
} }
} }
@ -366,7 +387,7 @@ size_t db_overlay_source::list_overlay_count(const listview_curses &lv)
if (retval > 1) { if (retval > 1) {
this->dos_lines.emplace_back(""); this->dos_lines.emplace_back("");
string_attrs_t &sa = this->dos_lines.back().get_attrs(); string_attrs_t& sa = this->dos_lines.back().get_attrs();
struct line_range lr(1, 2); struct line_range lr(1, 2);
sa.emplace_back(lr, &view_curses::VC_GRAPHIC, ACS_LLCORNER); sa.emplace_back(lr, &view_curses::VC_GRAPHIC, ACS_LLCORNER);
@ -380,29 +401,31 @@ size_t db_overlay_source::list_overlay_count(const listview_curses &lv)
return retval; return retval;
} }
bool db_overlay_source::list_value_for_overlay(const listview_curses &lv, int y, bool
int bottom, vis_line_t row, db_overlay_source::list_value_for_overlay(const listview_curses& lv,
attr_line_t &value_out) int y,
int bottom,
vis_line_t row,
attr_line_t& value_out)
{ {
view_colors &vc = view_colors::singleton(); view_colors& vc = view_colors::singleton();
if (y == 0) { if (y == 0) {
this->list_overlay_count(lv); this->list_overlay_count(lv);
std::string &line = value_out.get_string(); std::string& line = value_out.get_string();
db_label_source *dls = this->dos_labels; db_label_source* dls = this->dos_labels;
string_attrs_t &sa = value_out.get_attrs(); string_attrs_t& sa = value_out.get_attrs();
for (size_t lpc = 0; for (size_t lpc = 0; lpc < this->dos_labels->dls_headers.size(); lpc++)
lpc < this->dos_labels->dls_headers.size(); {
lpc++) {
auto actual_col_size = std::min( auto actual_col_size = std::min(
MAX_COLUMN_WIDTH, dls->dls_headers[lpc].hm_column_size); MAX_COLUMN_WIDTH, dls->dls_headers[lpc].hm_column_size);
std::string cell_title = dls->dls_headers[lpc].hm_name; std::string cell_title = dls->dls_headers[lpc].hm_name;
truncate_to(cell_title, MAX_COLUMN_WIDTH); truncate_to(cell_title, MAX_COLUMN_WIDTH);
auto cell_length = utf8_string_length(cell_title) auto cell_length
.unwrapOr(actual_col_size); = utf8_string_length(cell_title).unwrapOr(actual_col_size);
int before, total_fill = actual_col_size - cell_length; int before, total_fill = actual_col_size - cell_length;
auto line_len_before = line.length(); auto line_len_before = line.length();
@ -415,8 +438,8 @@ bool db_overlay_source::list_value_for_overlay(const listview_curses &lv, int y,
struct line_range header_range(line_len_before, line.length()); struct line_range header_range(line_len_before, line.length());
int attrs = int attrs
vc.attrs_for_ident(dls->dls_headers[lpc].hm_name) | A_REVERSE; = vc.attrs_for_ident(dls->dls_headers[lpc].hm_name) | A_REVERSE;
if (!this->dos_labels->dls_headers[lpc].hm_graphable) { if (!this->dos_labels->dls_headers[lpc].hm_graphable) {
attrs = A_UNDERLINE; attrs = A_UNDERLINE;
} }
@ -427,8 +450,9 @@ bool db_overlay_source::list_value_for_overlay(const listview_curses &lv, int y,
sa.emplace_back(lr, &view_curses::VC_STYLE, A_BOLD | A_UNDERLINE); sa.emplace_back(lr, &view_curses::VC_STYLE, A_BOLD | A_UNDERLINE);
return true; return true;
} } else if (this->dos_active && y >= 2
else if (this->dos_active && y >= 2 && ((size_t) y) < (this->dos_lines.size() + 2)) { && ((size_t) y) < (this->dos_lines.size() + 2))
{
value_out = this->dos_lines[y - 2]; value_out = this->dos_lines[y - 2];
return true; return true;
} }

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -30,60 +30,68 @@
#ifndef db_sub_source_hh #ifndef db_sub_source_hh
#define db_sub_source_hh #define db_sub_source_hh
#include <iterator>
#include <string> #include <string>
#include <vector> #include <vector>
#include <iterator>
#include <sqlite3.h> #include <sqlite3.h>
#include "textview_curses.hh"
#include "hist_source.hh" #include "hist_source.hh"
#include "textview_curses.hh"
class db_label_source : public text_sub_source, public text_time_translator { class db_label_source
: public text_sub_source
, public text_time_translator {
public: public:
~db_label_source() { ~db_label_source()
{
this->clear(); this->clear();
} }
bool has_log_time_column() const { bool has_log_time_column() const
{
return !this->dls_time_column.empty(); return !this->dls_time_column.empty();
}; };
size_t text_line_count() { size_t text_line_count()
{
return this->dls_rows.size(); return this->dls_rows.size();
}; };
size_t text_size_for_line(textview_curses &tc, int line, line_flags_t flags) { size_t text_size_for_line(textview_curses& tc, int line, line_flags_t flags)
{
return this->text_line_width(tc); return this->text_line_width(tc);
}; };
size_t text_line_width(textview_curses &curses) { size_t text_line_width(textview_curses& curses)
{
size_t retval = 0; size_t retval = 0;
for (auto &dls_header : this->dls_headers) { for (auto& dls_header : this->dls_headers) {
retval += dls_header.hm_column_size + 1; retval += dls_header.hm_column_size + 1;
} }
return retval; return retval;
}; };
void text_value_for_line(textview_curses &tc, void text_value_for_line(textview_curses& tc,
int row, int row,
std::string &label_out, std::string& label_out,
line_flags_t flags); line_flags_t flags);
void text_attrs_for_line(textview_curses &tc, int row, string_attrs_t &sa); void text_attrs_for_line(textview_curses& tc, int row, string_attrs_t& sa);
void push_header(const std::string &colstr, int type, bool graphable); void push_header(const std::string& colstr, int type, bool graphable);
void push_column(const char *colstr); void push_column(const char* colstr);
void clear(); void clear();
long column_name_to_index(const std::string &name) const; long column_name_to_index(const std::string& name) const;
nonstd::optional<vis_line_t> row_for_time(struct timeval time_bucket); nonstd::optional<vis_line_t> row_for_time(struct timeval time_bucket);
nonstd::optional<struct timeval> time_for_row(vis_line_t row) { nonstd::optional<struct timeval> time_for_row(vis_line_t row)
{
if ((row < 0_vl) || (((size_t) row) >= this->dls_time_column.size())) { if ((row < 0_vl) || (((size_t) row) >= this->dls_time_column.size())) {
return nonstd::nullopt; return nonstd::nullopt;
} }
@ -93,15 +101,13 @@ public:
struct header_meta { struct header_meta {
explicit header_meta(std::string name) explicit header_meta(std::string name)
: hm_name(std::move(name)), : hm_name(std::move(name)), hm_column_type(SQLITE3_TEXT),
hm_column_type(SQLITE3_TEXT), hm_graphable(false), hm_log_time(false), hm_column_size(0){
hm_graphable(false),
hm_log_time(false),
hm_column_size(0) {
}; };
bool operator==(const std::string &name) const { bool operator==(const std::string& name) const
{
return this->hm_name == name; return this->hm_name == name;
}; };
@ -115,25 +121,26 @@ public:
stacked_bar_chart<std::string> dls_chart; stacked_bar_chart<std::string> dls_chart;
std::vector<header_meta> dls_headers; std::vector<header_meta> dls_headers;
std::vector<std::vector<const char *>> dls_rows; std::vector<std::vector<const char*>> dls_rows;
std::vector<struct timeval> dls_time_column; std::vector<struct timeval> dls_time_column;
std::vector<size_t> dls_cell_width; std::vector<size_t> dls_cell_width;
int dls_time_column_index{-1}; int dls_time_column_index{-1};
static const char *NULL_STR; static const char* NULL_STR;
}; };
class db_overlay_source : public list_overlay_source { class db_overlay_source : public list_overlay_source {
public: public:
size_t list_overlay_count(const listview_curses &lv); size_t list_overlay_count(const listview_curses& lv);
bool list_value_for_overlay(const listview_curses &lv, bool list_value_for_overlay(const listview_curses& lv,
int y, int bottom, int y,
int bottom,
vis_line_t row, vis_line_t row,
attr_line_t &value_out) override; attr_line_t& value_out) override;
bool dos_active{false}; bool dos_active{false};
db_label_source *dos_labels{nullptr}; db_label_source* dos_labels{nullptr};
std::vector<attr_line_t> dos_lines; std::vector<attr_line_t> dos_lines;
}; };
#endif #endif

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -34,8 +34,7 @@
#include "statusview_curses.hh" #include "statusview_curses.hh"
class doc_status_source class doc_status_source : public status_data_source {
: public status_data_source {
public: public:
typedef enum { typedef enum {
TSF_TITLE, TSF_TITLE,
@ -45,7 +44,8 @@ public:
TSF__MAX TSF__MAX
} field_t; } field_t;
doc_status_source() { doc_status_source()
{
this->tss_fields[TSF_TITLE].set_width(14); this->tss_fields[TSF_TITLE].set_width(14);
this->tss_fields[TSF_TITLE].set_left_pad(1); this->tss_fields[TSF_TITLE].set_left_pad(1);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_STATUS_TITLE); this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_STATUS_TITLE);
@ -57,19 +57,23 @@ public:
this->tss_fields[TSF_DESCRIPTION].set_role(view_colors::VCR_STATUS); this->tss_fields[TSF_DESCRIPTION].set_role(view_colors::VCR_STATUS);
}; };
size_t statusview_fields() override { size_t statusview_fields() override
{
return TSF__MAX; return TSF__MAX;
}; };
status_field &statusview_value_for_field(int field) override { status_field& statusview_value_for_field(int field) override
{
return this->tss_fields[field]; return this->tss_fields[field];
}; };
void set_title(const std::string &title) { void set_title(const std::string& title)
{
this->tss_fields[TSF_TITLE].set_value(title); this->tss_fields[TSF_TITLE].set_value(title);
} }
void set_description(const std::string &description) { void set_description(const std::string& description)
{
this->tss_fields[TSF_DESCRIPTION].set_value(description); this->tss_fields[TSF_DESCRIPTION].set_value(description);
} }

@ -21,26 +21,26 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <algorithm> #include <algorithm>
#include "elem_to_json.hh" #include "elem_to_json.hh"
#include "config.h"
#include "yajlpp/yajlpp.hh" #include "yajlpp/yajlpp.hh"
using namespace std; using namespace std;
static static void
void element_to_json(yajl_gen gen, data_parser &dp, const data_parser::element &elem) element_to_json(yajl_gen gen, data_parser& dp, const data_parser::element& elem)
{ {
size_t value_len; size_t value_len;
const char *value_str = dp.get_element_string(elem, value_len); const char* value_str = dp.get_element_string(elem, value_len);
switch (elem.value_token()) { switch (elem.value_token()) {
case DT_NUMBER: { case DT_NUMBER: {
@ -48,20 +48,21 @@ void element_to_json(yajl_gen gen, data_parser &dp, const data_parser::element &
break; break;
} }
case DNT_GROUP: { case DNT_GROUP: {
elements_to_json(gen, dp, elem.get_value_elem().e_sub_elements, false); elements_to_json(
gen, dp, elem.get_value_elem().e_sub_elements, false);
break; break;
} }
case DNT_PAIR: { case DNT_PAIR: {
const data_parser::element &pair_elem = elem.get_pair_elem(); const data_parser::element& pair_elem = elem.get_pair_elem();
string key_str = dp.get_element_string(pair_elem.e_sub_elements->front()); string key_str
= dp.get_element_string(pair_elem.e_sub_elements->front());
if (!key_str.empty()) { if (!key_str.empty()) {
yajlpp_map singleton_map(gen); yajlpp_map singleton_map(gen);
singleton_map.gen(key_str); singleton_map.gen(key_str);
element_to_json(gen, dp, pair_elem.get_pair_value()); element_to_json(gen, dp, pair_elem.get_pair_value());
} } else {
else {
element_to_json(gen, dp, pair_elem.get_pair_value()); element_to_json(gen, dp, pair_elem.get_pair_value());
} }
break; break;
@ -69,11 +70,9 @@ void element_to_json(yajl_gen gen, data_parser &dp, const data_parser::element &
case DT_CONSTANT: { case DT_CONSTANT: {
if (strncasecmp("true", value_str, value_len) == 0) { if (strncasecmp("true", value_str, value_len) == 0) {
yajl_gen_bool(gen, true); yajl_gen_bool(gen, true);
} } else if (strncasecmp("false", value_str, value_len) == 0) {
else if (strncasecmp("false", value_str, value_len) == 0) {
yajl_gen_bool(gen, false); yajl_gen_bool(gen, false);
} } else {
else {
yajl_gen_null(gen); yajl_gen_null(gen);
} }
break; break;
@ -84,22 +83,24 @@ void element_to_json(yajl_gen gen, data_parser &dp, const data_parser::element &
} }
} }
static static void
void map_elements_to_json2(yajl_gen gen, data_parser &dp, data_parser::element_list_t *el) map_elements_to_json2(yajl_gen gen,
data_parser& dp,
data_parser::element_list_t* el)
{ {
yajlpp_map root_map(gen); yajlpp_map root_map(gen);
int col = 0; int col = 0;
for (auto &iter : *el) { for (auto& iter : *el) {
const data_parser::element &pvalue = iter.get_pair_value(); const data_parser::element& pvalue = iter.get_pair_value();
if (pvalue.value_token() == DT_INVALID) { if (pvalue.value_token() == DT_INVALID) {
log_debug("invalid!!"); log_debug("invalid!!");
// continue; // continue;
} }
std::string key_str = dp.get_element_string( std::string key_str
iter.e_sub_elements->front()); = dp.get_element_string(iter.e_sub_elements->front());
if (key_str.empty()) { if (key_str.empty()) {
char buffer[32]; char buffer[32];
@ -113,46 +114,51 @@ void map_elements_to_json2(yajl_gen gen, data_parser &dp, data_parser::element_l
} }
} }
static static void
void list_body_elements_to_json(yajl_gen gen, data_parser &dp, data_parser::element_list_t *el) list_body_elements_to_json(yajl_gen gen,
data_parser& dp,
data_parser::element_list_t* el)
{ {
for (auto &iter : *el) { for (auto& iter : *el) {
element_to_json(gen, dp, iter); element_to_json(gen, dp, iter);
} }
} }
static static void
void list_elements_to_json(yajl_gen gen, data_parser &dp, data_parser::element_list_t *el) list_elements_to_json(yajl_gen gen,
data_parser& dp,
data_parser::element_list_t* el)
{ {
yajlpp_array root_array(gen); yajlpp_array root_array(gen);
list_body_elements_to_json(gen, dp, el); list_body_elements_to_json(gen, dp, el);
} }
static static void
void map_elements_to_json(yajl_gen gen, data_parser &dp, data_parser::element_list_t *el) map_elements_to_json(yajl_gen gen,
data_parser& dp,
data_parser::element_list_t* el)
{ {
bool unique_names = el->size() > 1; bool unique_names = el->size() > 1;
vector<string> names; vector<string> names;
for (auto &iter : *el) { for (auto& iter : *el) {
const data_parser::element &pvalue = iter.get_pair_value(); const data_parser::element& pvalue = iter.get_pair_value();
if (pvalue.value_token() == DT_INVALID) { if (pvalue.value_token() == DT_INVALID) {
log_debug("invalid!!"); log_debug("invalid!!");
// continue; // continue;
} }
std::string key_str = dp.get_element_string( std::string key_str
iter.e_sub_elements->front()); = dp.get_element_string(iter.e_sub_elements->front());
if (key_str.empty()) { if (key_str.empty()) {
continue; continue;
} }
if (find(names.begin(), names.end(), key_str) != names.end()) { if (find(names.begin(), names.end(), key_str) != names.end()) {
unique_names = false; unique_names = false;
break; break;
} } else {
else {
names.push_back(key_str); names.push_back(key_str);
} }
} }
@ -161,30 +167,33 @@ void map_elements_to_json(yajl_gen gen, data_parser &dp, data_parser::element_li
if (unique_names) { if (unique_names) {
map_elements_to_json2(gen, dp, el); map_elements_to_json2(gen, dp, el);
} } else {
else {
list_elements_to_json(gen, dp, el); list_elements_to_json(gen, dp, el);
} }
} }
void elements_to_json(yajl_gen gen, data_parser &dp, data_parser::element_list_t *el, bool root) void
elements_to_json(yajl_gen gen,
data_parser& dp,
data_parser::element_list_t* el,
bool root)
{ {
if (el->empty()) { if (el->empty()) {
yajl_gen_null(gen); yajl_gen_null(gen);
} } else {
else {
switch (el->front().e_token) { switch (el->front().e_token) {
case DNT_PAIR: { case DNT_PAIR: {
if (root && el->size() == 1) { if (root && el->size() == 1) {
const data_parser::element &pair_elem = el->front().get_pair_elem(); const data_parser::element& pair_elem
= el->front().get_pair_elem();
std::string key_str = dp.get_element_string( std::string key_str = dp.get_element_string(
pair_elem.e_sub_elements->front()); pair_elem.e_sub_elements->front());
if (key_str.empty() && if (key_str.empty()
el->front().get_pair_value().value_token() == DNT_GROUP) { && el->front().get_pair_value().value_token()
== DNT_GROUP) {
element_to_json(gen, dp, el->front().get_pair_value()); element_to_json(gen, dp, el->front().get_pair_value());
} } else {
else {
yajlpp_map singleton_map(gen); yajlpp_map singleton_map(gen);
if (key_str.empty()) { if (key_str.empty()) {
@ -193,8 +202,7 @@ void elements_to_json(yajl_gen gen, data_parser &dp, data_parser::element_list_t
singleton_map.gen(key_str); singleton_map.gen(key_str);
element_to_json(gen, dp, pair_elem.get_pair_value()); element_to_json(gen, dp, pair_elem.get_pair_value());
} }
} } else {
else {
map_elements_to_json(gen, dp, el); map_elements_to_json(gen, dp, el);
} }
break; break;

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -30,9 +30,12 @@
#ifndef elem_to_json_hh #ifndef elem_to_json_hh
#define elem_to_json_hh #define elem_to_json_hh
#include "yajl/api/yajl_gen.h"
#include "data_parser.hh" #include "data_parser.hh"
#include "yajl/api/yajl_gen.h"
void elements_to_json(yajl_gen gen, data_parser &dp, data_parser::element_list_t *el, bool root = true); void elements_to_json(yajl_gen gen,
data_parser& dp,
data_parser::element_list_t* el,
bool root = true);
#endif #endif

@ -21,26 +21,26 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h" #include "environ_vtab.hh"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "auto_mem.hh" #include "auto_mem.hh"
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "environ_vtab.hh" #include "config.h"
using namespace std; using namespace std;
extern char **environ; extern char** environ;
const char *ENVIRON_CREATE_STMT = R"( const char* ENVIRON_CREATE_STMT = R"(
-- Access lnav's environment variables through this table. -- Access lnav's environment variables through this table.
CREATE TABLE environ ( CREATE TABLE environ (
name text PRIMARY KEY, name text PRIMARY KEY,
@ -49,27 +49,29 @@ CREATE TABLE environ (
)"; )";
struct vtab { struct vtab {
sqlite3_vtab base; sqlite3_vtab base;
sqlite3 * db; sqlite3* db;
}; };
struct vtab_cursor { struct vtab_cursor {
sqlite3_vtab_cursor base; sqlite3_vtab_cursor base;
char **env_cursor; char** env_cursor;
}; };
static int vt_destructor(sqlite3_vtab *p_svt); static int vt_destructor(sqlite3_vtab* p_svt);
static int vt_create(sqlite3 *db, static int
void *pAux, vt_create(sqlite3* db,
int argc, const char *const *argv, void* pAux,
sqlite3_vtab **pp_vt, int argc,
char **pzErr) const char* const* argv,
sqlite3_vtab** pp_vt,
char** pzErr)
{ {
vtab *p_vt; vtab* p_vt;
/* Allocate the sqlite3_vtab/vtab structure itself */ /* Allocate the sqlite3_vtab/vtab structure itself */
p_vt = (vtab *)sqlite3_malloc(sizeof(*p_vt)); p_vt = (vtab*) sqlite3_malloc(sizeof(*p_vt));
if (p_vt == NULL) { if (p_vt == NULL) {
return SQLITE_NOMEM; return SQLITE_NOMEM;
@ -85,10 +87,10 @@ static int vt_create(sqlite3 *db,
return rc; return rc;
} }
static int
static int vt_destructor(sqlite3_vtab *p_svt) vt_destructor(sqlite3_vtab* p_svt)
{ {
vtab *p_vt = (vtab *)p_svt; vtab* p_vt = (vtab*) p_svt;
/* Free the SQLite structure */ /* Free the SQLite structure */
sqlite3_free(p_vt); sqlite3_free(p_vt);
@ -96,37 +98,44 @@ static int vt_destructor(sqlite3_vtab *p_svt)
return SQLITE_OK; return SQLITE_OK;
} }
static int vt_connect(sqlite3 *db, void *p_aux, static int
int argc, const char *const *argv, vt_connect(sqlite3* db,
sqlite3_vtab **pp_vt, char **pzErr) void* p_aux,
int argc,
const char* const* argv,
sqlite3_vtab** pp_vt,
char** pzErr)
{ {
return vt_create(db, p_aux, argc, argv, pp_vt, pzErr); return vt_create(db, p_aux, argc, argv, pp_vt, pzErr);
} }
static int vt_disconnect(sqlite3_vtab *pVtab) static int
vt_disconnect(sqlite3_vtab* pVtab)
{ {
return vt_destructor(pVtab); return vt_destructor(pVtab);
} }
static int vt_destroy(sqlite3_vtab *p_vt) static int
vt_destroy(sqlite3_vtab* p_vt)
{ {
return vt_destructor(p_vt); return vt_destructor(p_vt);
} }
static int vt_next(sqlite3_vtab_cursor *cur); static int vt_next(sqlite3_vtab_cursor* cur);
static int vt_open(sqlite3_vtab *p_svt, sqlite3_vtab_cursor **pp_cursor) static int
vt_open(sqlite3_vtab* p_svt, sqlite3_vtab_cursor** pp_cursor)
{ {
vtab *p_vt = (vtab *)p_svt; vtab* p_vt = (vtab*) p_svt;
p_vt->base.zErrMsg = NULL; p_vt->base.zErrMsg = NULL;
vtab_cursor *p_cur = (vtab_cursor *)new vtab_cursor(); vtab_cursor* p_cur = (vtab_cursor*) new vtab_cursor();
if (p_cur == NULL) { if (p_cur == NULL) {
return SQLITE_NOMEM; return SQLITE_NOMEM;
} else { } else {
*pp_cursor = (sqlite3_vtab_cursor *)p_cur; *pp_cursor = (sqlite3_vtab_cursor*) p_cur;
p_cur->base.pVtab = p_svt; p_cur->base.pVtab = p_svt;
p_cur->env_cursor = environ; p_cur->env_cursor = environ;
@ -135,9 +144,10 @@ static int vt_open(sqlite3_vtab *p_svt, sqlite3_vtab_cursor **pp_cursor)
return SQLITE_OK; return SQLITE_OK;
} }
static int vt_close(sqlite3_vtab_cursor *cur) static int
vt_close(sqlite3_vtab_cursor* cur)
{ {
vtab_cursor *p_cur = (vtab_cursor *)cur; vtab_cursor* p_cur = (vtab_cursor*) cur;
/* Free cursor struct. */ /* Free cursor struct. */
delete p_cur; delete p_cur;
@ -145,16 +155,18 @@ static int vt_close(sqlite3_vtab_cursor *cur)
return SQLITE_OK; return SQLITE_OK;
} }
static int vt_eof(sqlite3_vtab_cursor *cur) static int
vt_eof(sqlite3_vtab_cursor* cur)
{ {
vtab_cursor *vc = (vtab_cursor *)cur; vtab_cursor* vc = (vtab_cursor*) cur;
return vc->env_cursor[0] == NULL; return vc->env_cursor[0] == NULL;
} }
static int vt_next(sqlite3_vtab_cursor *cur) static int
vt_next(sqlite3_vtab_cursor* cur)
{ {
vtab_cursor *vc = (vtab_cursor *)cur; vtab_cursor* vc = (vtab_cursor*) cur;
if (vc->env_cursor[0] != NULL) { if (vc->env_cursor[0] != NULL) {
vc->env_cursor += 1; vc->env_cursor += 1;
@ -163,61 +175,69 @@ static int vt_next(sqlite3_vtab_cursor *cur)
return SQLITE_OK; return SQLITE_OK;
} }
static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col) static int
vt_column(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col)
{ {
vtab_cursor *vc = (vtab_cursor *)cur; vtab_cursor* vc = (vtab_cursor*) cur;
const char *eq = strchr(vc->env_cursor[0], '='); const char* eq = strchr(vc->env_cursor[0], '=');
switch (col) { switch (col) {
case 0: case 0:
sqlite3_result_text(ctx, sqlite3_result_text(ctx,
vc->env_cursor[0], eq - vc->env_cursor[0], vc->env_cursor[0],
SQLITE_TRANSIENT); eq - vc->env_cursor[0],
break; SQLITE_TRANSIENT);
case 1: break;
sqlite3_result_text(ctx, eq + 1, -1, SQLITE_TRANSIENT); case 1:
break; sqlite3_result_text(ctx, eq + 1, -1, SQLITE_TRANSIENT);
break;
} }
return SQLITE_OK; return SQLITE_OK;
} }
static int vt_rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *p_rowid) static int
vt_rowid(sqlite3_vtab_cursor* cur, sqlite_int64* p_rowid)
{ {
vtab_cursor *p_cur = (vtab_cursor *)cur; vtab_cursor* p_cur = (vtab_cursor*) cur;
*p_rowid = (int64_t)p_cur->env_cursor[0]; *p_rowid = (int64_t) p_cur->env_cursor[0];
return SQLITE_OK; return SQLITE_OK;
} }
static int vt_best_index(sqlite3_vtab *tab, sqlite3_index_info *p_info) static int
vt_best_index(sqlite3_vtab* tab, sqlite3_index_info* p_info)
{ {
return SQLITE_OK; return SQLITE_OK;
} }
static int vt_filter(sqlite3_vtab_cursor *p_vtc, static int
int idxNum, const char *idxStr, vt_filter(sqlite3_vtab_cursor* p_vtc,
int argc, sqlite3_value **argv) int idxNum,
const char* idxStr,
int argc,
sqlite3_value** argv)
{ {
return SQLITE_OK; return SQLITE_OK;
} }
static int vt_update(sqlite3_vtab *tab, static int
int argc, vt_update(sqlite3_vtab* tab,
sqlite3_value **argv, int argc,
sqlite_int64 *rowid) sqlite3_value** argv,
sqlite_int64* rowid)
{ {
const char *name = ( const char* name
argc > 2 ? (const char *)sqlite3_value_text(argv[2]) : nullptr); = (argc > 2 ? (const char*) sqlite3_value_text(argv[2]) : nullptr);
vtab *p_vt = (vtab *)tab; vtab* p_vt = (vtab*) tab;
int retval = SQLITE_ERROR; int retval = SQLITE_ERROR;
if (argc != 1 && if (argc != 1
(argc < 3 || && (argc < 3 || sqlite3_value_type(argv[2]) == SQLITE_NULL
sqlite3_value_type(argv[2]) == SQLITE_NULL || || sqlite3_value_type(argv[3]) == SQLITE_NULL
sqlite3_value_type(argv[3]) == SQLITE_NULL || || sqlite3_value_text(argv[2])[0] == '\0'))
sqlite3_value_text(argv[2])[0] == '\0')) { {
tab->zErrMsg = sqlite3_mprintf( tab->zErrMsg = sqlite3_mprintf(
"A non-empty name and value must be provided when inserting an " "A non-empty name and value must be provided when inserting an "
"environment variable"); "environment variable");
@ -233,8 +253,8 @@ static int vt_update(sqlite3_vtab *tab,
if (sqlite3_value_type(argv[0]) != SQLITE_NULL) { if (sqlite3_value_type(argv[0]) != SQLITE_NULL) {
int64_t index = sqlite3_value_int64(argv[0]); int64_t index = sqlite3_value_int64(argv[0]);
const char *var = (const char *)index; const char* var = (const char*) index;
const char *eq = strchr(var, '='); const char* eq = strchr(var, '=');
size_t namelen = eq - var; size_t namelen = eq - var;
char name[namelen + 1]; char name[namelen + 1];
@ -249,26 +269,26 @@ static int vt_update(sqlite3_vtab *tab,
rc = sqlite3_vtab_on_conflict(p_vt->db); rc = sqlite3_vtab_on_conflict(p_vt->db);
switch (rc) { switch (rc) {
case SQLITE_FAIL: case SQLITE_FAIL:
case SQLITE_ABORT: case SQLITE_ABORT:
tab->zErrMsg = sqlite3_mprintf( tab->zErrMsg = sqlite3_mprintf(
"An environment variable with the name '%s' already exists", "An environment variable with the name '%s' already exists",
name); name);
return rc; return rc;
case SQLITE_IGNORE: case SQLITE_IGNORE:
return SQLITE_OK; return SQLITE_OK;
case SQLITE_REPLACE: case SQLITE_REPLACE:
break; break;
default: default:
return rc; return rc;
} }
#endif #endif
} }
if (name != nullptr && argc == 4) { if (name != nullptr && argc == 4) {
const unsigned char *value = sqlite3_value_text(argv[3]); const unsigned char* value = sqlite3_value_text(argv[3]);
setenv((const char *)name, (const char *)value, 1); setenv((const char*) name, (const char*) value, 1);
return SQLITE_OK; return SQLITE_OK;
} }
@ -277,37 +297,43 @@ static int vt_update(sqlite3_vtab *tab,
} }
static sqlite3_module vtab_module = { static sqlite3_module vtab_module = {
0, /* iVersion */ 0, /* iVersion */
vt_create, /* xCreate - create a vtable */ vt_create, /* xCreate - create a vtable */
vt_connect, /* xConnect - associate a vtable with a connection */ vt_connect, /* xConnect - associate a vtable with a connection */
vt_best_index, /* xBestIndex - best index */ vt_best_index, /* xBestIndex - best index */
vt_disconnect, /* xDisconnect - disassociate a vtable with a connection */ vt_disconnect, /* xDisconnect - disassociate a vtable with a connection */
vt_destroy, /* xDestroy - destroy a vtable */ vt_destroy, /* xDestroy - destroy a vtable */
vt_open, /* xOpen - open a cursor */ vt_open, /* xOpen - open a cursor */
vt_close, /* xClose - close a cursor */ vt_close, /* xClose - close a cursor */
vt_filter, /* xFilter - configure scan constraints */ vt_filter, /* xFilter - configure scan constraints */
vt_next, /* xNext - advance a cursor */ vt_next, /* xNext - advance a cursor */
vt_eof, /* xEof - inidicate end of result set*/ vt_eof, /* xEof - inidicate end of result set*/
vt_column, /* xColumn - read data */ vt_column, /* xColumn - read data */
vt_rowid, /* xRowid - read data */ vt_rowid, /* xRowid - read data */
vt_update, /* xUpdate - write data */ vt_update, /* xUpdate - write data */
NULL, /* xBegin - begin transaction */ NULL, /* xBegin - begin transaction */
NULL, /* xSync - sync transaction */ NULL, /* xSync - sync transaction */
NULL, /* xCommit - commit transaction */ NULL, /* xCommit - commit transaction */
NULL, /* xRollback - rollback transaction */ NULL, /* xRollback - rollback transaction */
NULL, /* xFindFunction - function overloading */ NULL, /* xFindFunction - function overloading */
}; };
int register_environ_vtab(sqlite3 *db) int
register_environ_vtab(sqlite3* db)
{ {
auto_mem<char, sqlite3_free> errmsg; auto_mem<char, sqlite3_free> errmsg;
int rc; int rc;
rc = sqlite3_create_module(db, "environ_vtab_impl", &vtab_module, NULL); rc = sqlite3_create_module(db, "environ_vtab_impl", &vtab_module, NULL);
ensure(rc == SQLITE_OK); ensure(rc == SQLITE_OK);
if ((rc = sqlite3_exec(db, if ((rc = sqlite3_exec(
db,
"CREATE VIRTUAL TABLE environ USING environ_vtab_impl()", "CREATE VIRTUAL TABLE environ USING environ_vtab_impl()",
NULL, NULL, errmsg.out())) != SQLITE_OK) { NULL,
NULL,
errmsg.out()))
!= SQLITE_OK)
{
fprintf(stderr, "unable to create environ table %s\n", errmsg.in()); fprintf(stderr, "unable to create environ table %s\n", errmsg.in());
} }
return rc; return rc;

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -32,8 +32,8 @@
#include <sqlite3.h> #include <sqlite3.h>
int register_environ_vtab(sqlite3 *db); int register_environ_vtab(sqlite3* db);
extern const char *ENVIRON_CREATE_STMT; extern const char* ENVIRON_CREATE_STMT;
#endif #endif

File diff suppressed because it is too large Load Diff

@ -21,40 +21,41 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h" #include "field_overlay_source.hh"
#include "ansi_scrubber.hh"
#include "base/humanize.time.hh" #include "base/humanize.time.hh"
#include "config.h"
#include "lnav_util.hh" #include "lnav_util.hh"
#include "ansi_scrubber.hh"
#include "vtab_module.hh"
#include "relative_time.hh"
#include "field_overlay_source.hh"
#include "readline_highlighters.hh"
#include "vtab_module_json.hh"
#include "log_format_ext.hh" #include "log_format_ext.hh"
#include "log_vtab_impl.hh" #include "log_vtab_impl.hh"
#include "readline_highlighters.hh"
#include "relative_time.hh"
#include "vtab_module.hh"
#include "vtab_module_json.hh"
using namespace std; using namespace std;
json_string extract(const char *str); json_string extract(const char* str);
void field_overlay_source::build_summary_lines(const listview_curses &lv) void
field_overlay_source::build_summary_lines(const listview_curses& lv)
{ {
auto& tc = dynamic_cast<const textview_curses &>(lv); auto& tc = dynamic_cast<const textview_curses&>(lv);
textfile_sub_source &tss = this->fos_tss; textfile_sub_source& tss = this->fos_tss;
logfile_sub_source &lss = this->fos_lss; logfile_sub_source& lss = this->fos_lss;
this->fos_summary_lines.clear(); this->fos_summary_lines.clear();
{ {
vis_line_t filled_rows = lv.rows_available( vis_line_t filled_rows
lv.get_top(), listview_curses::RD_DOWN); = lv.rows_available(lv.get_top(), listview_curses::RD_DOWN);
vis_line_t height, free_rows; vis_line_t height, free_rows;
unsigned long width; unsigned long width;
long rate_len = 0; long rate_len = 0;
@ -63,69 +64,72 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv)
free_rows = height - filled_rows - vis_line_t(this->fos_lines.size()); free_rows = height - filled_rows - vis_line_t(this->fos_lines.size());
if (free_rows < 2 || !this->fos_show_status) { if (free_rows < 2 || !this->fos_show_status) {
this->fos_summary_lines.clear(); this->fos_summary_lines.clear();
} } else {
else {
string time_span; string time_span;
double error_rate = 0.0; double error_rate = 0.0;
if (lv.get_inner_height() == 0) { if (lv.get_inner_height() == 0) {
time_span = "None"; time_span = "None";
} } else {
else {
logline *first_line, *last_line; logline *first_line, *last_line;
time_t now = time(nullptr); time_t now = time(nullptr);
first_line = lss.find_line(lss.at(vis_line_t(0))); first_line = lss.find_line(lss.at(vis_line_t(0)));
last_line = lss.find_line(lss.at(lv.get_bottom())); last_line = lss.find_line(lss.at(lv.get_bottom()));
time_span = humanize::time::duration::from_tv( time_span
last_line->get_timeval() - first_line->get_timeval()) = humanize::time::duration::from_tv(
.to_string(); last_line->get_timeval() - first_line->get_timeval())
.to_string();
time_t local_now = convert_log_time_to_local(now); time_t local_now = convert_log_time_to_local(now);
time_t five_minutes_ago = local_now - (5 * 60 * 60); time_t five_minutes_ago = local_now - (5 * 60 * 60);
time_t ten_secs_ago = local_now - 10; time_t ten_secs_ago = local_now - 10;
auto from_five_min_ago_opt = lss.find_from_time(five_minutes_ago); auto from_five_min_ago_opt
= lss.find_from_time(five_minutes_ago);
auto from_ten_secs_ago_opt = lss.find_from_time(ten_secs_ago); auto from_ten_secs_ago_opt = lss.find_from_time(ten_secs_ago);
auto &bm = tc.get_bookmarks(); auto& bm = tc.get_bookmarks();
auto error_bm_iter = bm.find(&logfile_sub_source::BM_ERRORS); auto error_bm_iter = bm.find(&logfile_sub_source::BM_ERRORS);
if (now > last_line->get_time() && from_five_min_ago_opt && if (now > last_line->get_time() && from_five_min_ago_opt
error_bm_iter != bm.end()) { && error_bm_iter != bm.end())
{
auto& error_bookmarks = error_bm_iter->second; auto& error_bookmarks = error_bm_iter->second;
auto five_min_lower = auto five_min_lower
lower_bound(error_bookmarks.begin(), = lower_bound(error_bookmarks.begin(),
error_bookmarks.end(), error_bookmarks.end(),
from_five_min_ago_opt.value()); from_five_min_ago_opt.value());
if (five_min_lower != error_bookmarks.end()) { if (five_min_lower != error_bookmarks.end()) {
double error_count = distance( double error_count
five_min_lower, error_bookmarks.end()); = distance(five_min_lower, error_bookmarks.end());
double time_diff = 5.0; double time_diff = 5.0;
if (first_line->get_time() > five_minutes_ago) { if (first_line->get_time() > five_minutes_ago) {
time_diff = (double) (local_now - first_line->get_time()) / time_diff
60.0; = (double) (local_now - first_line->get_time())
/ 60.0;
} }
error_rate = error_count / time_diff; error_rate = error_count / time_diff;
if (from_ten_secs_ago_opt) { if (from_ten_secs_ago_opt) {
auto ten_sec_lower = auto ten_sec_lower
lower_bound(error_bookmarks.begin(), = lower_bound(error_bookmarks.begin(),
error_bookmarks.end(), error_bookmarks.end(),
from_ten_secs_ago_opt.value()); from_ten_secs_ago_opt.value());
if (ten_sec_lower != error_bookmarks.end()) { if (ten_sec_lower != error_bookmarks.end()) {
double recent_error_count = distance( double recent_error_count = distance(
ten_sec_lower, error_bookmarks.end()); ten_sec_lower, error_bookmarks.end());
double recent_error_rate = double recent_error_rate
recent_error_count / 10.0; = recent_error_count / 10.0;
double long_error_rate = double long_error_rate
error_count / (time_diff * 60.0 / 10.0); = error_count / (time_diff * 60.0 / 10.0);
if (long_error_rate == 0.0) { if (long_error_rate == 0.0) {
long_error_rate = 1.0; long_error_rate = 1.0;
} }
long computed_rate_len = lrint(ceil( long computed_rate_len
(recent_error_rate * 40.0) / long_error_rate)); = lrint(ceil((recent_error_rate * 40.0)
/ long_error_rate));
rate_len = min(10L, computed_rate_len); rate_len = min(10L, computed_rate_len);
} }
} }
@ -134,7 +138,7 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv)
} }
this->fos_summary_lines.emplace_back(); this->fos_summary_lines.emplace_back();
attr_line_t &sum_line = this->fos_summary_lines.back(); attr_line_t& sum_line = this->fos_summary_lines.back();
if (tss.empty()) { if (tss.empty()) {
sum_line.with_ansi_string( sum_line.with_ansi_string(
" " " "
@ -160,44 +164,35 @@ void field_overlay_source::build_summary_lines(const listview_curses &lv)
error_rate, error_rate,
time_span.c_str()); time_span.c_str());
} }
string &sum_msg = sum_line.get_string(); string& sum_msg = sum_line.get_string();
sum_line.with_attr(string_attr( sum_line
.with_attr(string_attr(
line_range(sum_msg.find("Error rate"), line_range(sum_msg.find("Error rate"),
sum_msg.find("Error rate") + rate_len), sum_msg.find("Error rate") + rate_len),
&view_curses::VC_STYLE, &view_curses::VC_STYLE,
A_REVERSE A_REVERSE))
))
.with_attr(string_attr( .with_attr(string_attr(
line_range(1, 2), line_range(1, 2), &view_curses::VC_GRAPHIC, ACS_ULCORNER))
&view_curses::VC_GRAPHIC,
ACS_ULCORNER
))
.with_attr(string_attr( .with_attr(string_attr(
line_range(2, 6), line_range(2, 6), &view_curses::VC_GRAPHIC, ACS_HLINE))
&view_curses::VC_GRAPHIC,
ACS_HLINE
))
.with_attr(string_attr( .with_attr(string_attr(
line_range(sum_msg.length() + 1, line_range(sum_msg.length() + 1, sum_msg.length() + 5),
sum_msg.length() + 5),
&view_curses::VC_GRAPHIC, &view_curses::VC_GRAPHIC,
ACS_HLINE ACS_HLINE))
))
.with_attr(string_attr( .with_attr(string_attr(
line_range(sum_msg.length() + 5, line_range(sum_msg.length() + 5, sum_msg.length() + 6),
sum_msg.length() + 6),
&view_curses::VC_GRAPHIC, &view_curses::VC_GRAPHIC,
ACS_URCORNER ACS_URCORNER))
))
.right_justify(width - 2); .right_justify(width - 2);
} }
} }
} }
void field_overlay_source::build_field_lines(const listview_curses &lv) void
field_overlay_source::build_field_lines(const listview_curses& lv)
{ {
logfile_sub_source &lss = this->fos_lss; logfile_sub_source& lss = this->fos_lss;
view_colors &vc = view_colors::singleton(); view_colors& vc = view_colors::singleton();
this->fos_lines.clear(); this->fos_lines.clear();
@ -213,8 +208,8 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
auto format = file->get_format(); auto format = file->get_format();
bool display = false; bool display = false;
if (ll->is_time_skewed() || if (ll->is_time_skewed()
ll->get_msg_level() == log_level_t::LEVEL_INVALID) { || ll->get_msg_level() == log_level_t::LEVEL_INVALID) {
display = true; display = true;
} }
if (!this->fos_contexts.empty()) { if (!this->fos_contexts.empty()) {
@ -238,25 +233,26 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
} }
auto emsg = fmt::format(" Invalid log message: {}", auto emsg = fmt::format(" Invalid log message: {}",
(const char *) sattr.sa_value.sav_ptr); (const char*) sattr.sa_value.sav_ptr);
auto al = attr_line_t(emsg) auto al = attr_line_t(emsg)
.with_attr(string_attr(line_range{1, 2}, .with_attr(string_attr(line_range{1, 2},
&view_curses::VC_GRAPHIC, &view_curses::VC_GRAPHIC,
ACS_LLCORNER)) ACS_LLCORNER))
.with_attr(string_attr(line_range{0, 22}, .with_attr(string_attr(line_range{0, 22},
&view_curses::VC_ROLE, &view_curses::VC_ROLE,
view_colors::VCR_INVALID_MSG)); view_colors::VCR_INVALID_MSG));
this->fos_lines.emplace_back(al); this->fos_lines.emplace_back(al);
} }
} }
char old_timestamp[64], curr_timestamp[64], orig_timestamp[64]; char old_timestamp[64], curr_timestamp[64], orig_timestamp[64];
struct timeval curr_tv, offset_tv, orig_tv, diff_tv = { 0, 0 }; struct timeval curr_tv, offset_tv, orig_tv, diff_tv = {0, 0};
attr_line_t time_line; attr_line_t time_line;
string &time_str = time_line.get_string(); string& time_str = time_line.get_string();
struct line_range time_lr; struct line_range time_lr;
sql_strftime(curr_timestamp, sizeof(curr_timestamp), sql_strftime(curr_timestamp,
sizeof(curr_timestamp),
ll->get_time(), ll->get_time(),
ll->get_millis(), ll->get_millis(),
'T'); 'T');
@ -264,13 +260,13 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
if (ll->is_time_skewed()) { if (ll->is_time_skewed()) {
time_lr.lr_start = 1; time_lr.lr_start = 1;
time_lr.lr_end = 2; time_lr.lr_end = 2;
time_line.with_attr(string_attr(time_lr, &view_curses::VC_GRAPHIC, time_line.with_attr(
ACS_LLCORNER)); string_attr(time_lr, &view_curses::VC_GRAPHIC, ACS_LLCORNER));
time_str.append(" Out-Of-Time-Order Message"); time_str.append(" Out-Of-Time-Order Message");
time_lr.lr_start = 3; time_lr.lr_start = 3;
time_lr.lr_end = time_str.length(); time_lr.lr_end = time_str.length();
time_line.with_attr(string_attr(time_lr, &view_curses::VC_ROLE, time_line.with_attr(string_attr(
view_colors::VCR_SKEWED_TIME)); time_lr, &view_curses::VC_ROLE, view_colors::VCR_SKEWED_TIME));
time_str.append(" --"); time_str.append(" --");
} }
@ -292,62 +288,65 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
curr_tv = this->fos_log_helper.ldh_line->get_timeval(); curr_tv = this->fos_log_helper.ldh_line->get_timeval();
if (ll->is_time_skewed() && time_range.lr_end != -1) { if (ll->is_time_skewed() && time_range.lr_end != -1) {
const char *time_src = this->fos_log_helper.ldh_msg.get_data() + const char* time_src
time_range.lr_start; = this->fos_log_helper.ldh_msg.get_data() + time_range.lr_start;
struct timeval actual_tv; struct timeval actual_tv;
date_time_scanner dts; date_time_scanner dts;
struct exttm tm; struct exttm tm;
dts.set_base_time(format->lf_date_time.dts_base_time); dts.set_base_time(format->lf_date_time.dts_base_time);
if (format->lf_date_time.scan(time_src, time_range.length(), if (format->lf_date_time.scan(time_src,
time_range.length(),
format->get_timestamp_formats(), format->get_timestamp_formats(),
&tm, actual_tv, &tm,
false) || actual_tv,
dts.scan(time_src, time_range.length(), false)
nullptr, || dts.scan(
&tm, actual_tv, time_src, time_range.length(), nullptr, &tm, actual_tv, false))
false)) { {
sql_strftime(orig_timestamp, sizeof(orig_timestamp), actual_tv, 'T'); sql_strftime(
orig_timestamp, sizeof(orig_timestamp), actual_tv, 'T');
time_str.append("; Actual Time: "); time_str.append("; Actual Time: ");
time_lr.lr_start = time_str.length(); time_lr.lr_start = time_str.length();
time_str.append(orig_timestamp); time_str.append(orig_timestamp);
time_lr.lr_end = time_str.length(); time_lr.lr_end = time_str.length();
time_line.with_attr(string_attr( time_line.with_attr(string_attr(
time_lr, time_lr, &view_curses::VC_ROLE, view_colors::VCR_SKEWED_TIME));
&view_curses::VC_ROLE,
view_colors::VCR_SKEWED_TIME));
timersub(&curr_tv, &actual_tv, &diff_tv); timersub(&curr_tv, &actual_tv, &diff_tv);
time_str.append("; Diff: "); time_str.append("; Diff: ");
time_lr.lr_start = time_str.length(); time_lr.lr_start = time_str.length();
time_str.append(humanize::time::duration::from_tv(diff_tv) time_str.append(
.to_string()); humanize::time::duration::from_tv(diff_tv).to_string());
time_lr.lr_end = time_str.length(); time_lr.lr_end = time_str.length();
time_line.with_attr(string_attr( time_line.with_attr(
time_lr, string_attr(time_lr, &view_curses::VC_STYLE, A_BOLD));
&view_curses::VC_STYLE,
A_BOLD));
} }
} }
offset_tv = this->fos_log_helper.ldh_file->get_time_offset(); offset_tv = this->fos_log_helper.ldh_file->get_time_offset();
timersub(&curr_tv, &offset_tv, &orig_tv); timersub(&curr_tv, &offset_tv, &orig_tv);
sql_strftime(old_timestamp, sizeof(old_timestamp), sql_strftime(old_timestamp,
orig_tv.tv_sec, orig_tv.tv_usec / 1000, sizeof(old_timestamp),
orig_tv.tv_sec,
orig_tv.tv_usec / 1000,
'T'); 'T');
if (offset_tv.tv_sec || offset_tv.tv_usec) { if (offset_tv.tv_sec || offset_tv.tv_usec) {
char offset_str[32]; char offset_str[32];
time_str.append(" Pre-adjust Time: "); time_str.append(" Pre-adjust Time: ");
time_str.append(old_timestamp); time_str.append(old_timestamp);
snprintf(offset_str, sizeof(offset_str), snprintf(offset_str,
sizeof(offset_str),
" Offset: %+d.%03d", " Offset: %+d.%03d",
(int)offset_tv.tv_sec, (int)(offset_tv.tv_usec / 1000)); (int) offset_tv.tv_sec,
(int) (offset_tv.tv_usec / 1000));
time_str.append(offset_str); time_str.append(offset_str);
} }
if ((!this->fos_contexts.empty() && this->fos_contexts.top().c_show) || if ((!this->fos_contexts.empty() && this->fos_contexts.top().c_show)
diff_tv.tv_sec > 0) { || diff_tv.tv_sec > 0)
{
this->fos_lines.emplace_back(time_line); this->fos_lines.emplace_back(time_line);
} }
@ -358,7 +357,7 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
this->fos_known_key_size = LOG_BODY.length(); this->fos_known_key_size = LOG_BODY.length();
this->fos_unknown_key_size = 0; this->fos_unknown_key_size = 0;
for (auto & ldh_line_value : this->fos_log_helper.ldh_line_values) { for (auto& ldh_line_value : this->fos_log_helper.ldh_line_values) {
auto& meta = ldh_line_value.lv_meta; auto& meta = ldh_line_value.lv_meta;
int this_key_size = meta.lvm_name.size(); int this_key_size = meta.lvm_name.size();
@ -376,19 +375,21 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
for (auto iter = this->fos_log_helper.ldh_parser->dp_pairs.begin(); for (auto iter = this->fos_log_helper.ldh_parser->dp_pairs.begin();
iter != this->fos_log_helper.ldh_parser->dp_pairs.end(); iter != this->fos_log_helper.ldh_parser->dp_pairs.end();
++iter) { ++iter)
std::string colname = this->fos_log_helper.ldh_parser->get_element_string( {
std::string colname
= this->fos_log_helper.ldh_parser->get_element_string(
iter->e_sub_elements->front()); iter->e_sub_elements->front());
colname = this->fos_log_helper.ldh_namer->add_column(colname); colname = this->fos_log_helper.ldh_namer->add_column(colname);
this->fos_unknown_key_size = max( this->fos_unknown_key_size
this->fos_unknown_key_size, (int)colname.length()); = max(this->fos_unknown_key_size, (int) colname.length());
} }
auto lf = this->fos_log_helper.ldh_file->get_format(); auto lf = this->fos_log_helper.ldh_file->get_format();
if (!lf->get_pattern_regex(cl).empty()) { if (!lf->get_pattern_regex(cl).empty()) {
attr_line_t pattern_al; attr_line_t pattern_al;
std::string &pattern_str = pattern_al.get_string(); std::string& pattern_str = pattern_al.get_string();
pattern_str = " Pattern: " + lf->get_pattern_name(cl) + " = "; pattern_str = " Pattern: " + lf->get_pattern_name(cl) + " = ";
int skip = pattern_str.length(); int skip = pattern_str.length();
pattern_str += lf->get_pattern_regex(cl); pattern_str += lf->get_pattern_regex(cl);
@ -396,32 +397,30 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
this->fos_lines.emplace_back(pattern_al); this->fos_lines.emplace_back(pattern_al);
} }
if (this->fos_log_helper.ldh_line_values.empty()) { if (this->fos_log_helper.ldh_line_values.empty()) {
this->fos_lines.emplace_back(" No known message fields"); this->fos_lines.emplace_back(" No known message fields");
} }
const log_format *last_format = nullptr; const log_format* last_format = nullptr;
for (auto & lv : this->fos_log_helper.ldh_line_values) { for (auto& lv : this->fos_log_helper.ldh_line_values) {
if (!lv.lv_meta.lvm_format) { if (!lv.lv_meta.lvm_format) {
continue; continue;
} }
auto curr_format = lv.lv_meta.lvm_format.value(); auto curr_format = lv.lv_meta.lvm_format.value();
auto curr_elf = dynamic_cast<external_log_format *>(curr_format); auto curr_elf = dynamic_cast<external_log_format*>(curr_format);
string format_name = curr_format->get_name().to_string(); string format_name = curr_format->get_name().to_string();
attr_line_t al; attr_line_t al;
string str, value_str = lv.to_string(); string str, value_str = lv.to_string();
if (curr_format != last_format) { if (curr_format != last_format) {
this->fos_lines.emplace_back(" Known message fields for table " + this->fos_lines.emplace_back(" Known message fields for table "
format_name + + format_name + ":");
":"); this->fos_lines.back().with_attr(
this->fos_lines.back().with_attr(string_attr( string_attr(line_range(32, 32 + format_name.length()),
line_range(32, 32 + format_name.length()), &view_curses::VC_STYLE,
&view_curses::VC_STYLE, vc.attrs_for_ident(format_name) | A_BOLD));
vc.attrs_for_ident(format_name) | A_BOLD));
last_format = curr_format; last_format = curr_format;
} }
@ -429,7 +428,8 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
if (lv.lv_meta.lvm_struct_name.empty()) { if (lv.lv_meta.lvm_struct_name.empty()) {
if (curr_elf && curr_elf->elf_body_field == lv.lv_meta.lvm_name) { if (curr_elf && curr_elf->elf_body_field == lv.lv_meta.lvm_name) {
field_name = LOG_BODY; field_name = LOG_BODY;
} else if (curr_elf && curr_elf->lf_timestamp_field == lv.lv_meta.lvm_name) { } else if (curr_elf
&& curr_elf->lf_timestamp_field == lv.lv_meta.lvm_name) {
field_name = LOG_TIME; field_name = LOG_TIME;
} else { } else {
field_name = lv.lv_meta.lvm_name.to_string(); field_name = lv.lv_meta.lvm_name.to_string();
@ -471,21 +471,23 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
json_string js = extract(value_str.c_str()); json_string js = extract(value_str.c_str());
al.clear() al.clear()
.append(" extract(") .append(" extract(")
.append(lv.lv_meta.lvm_name.get(), .append(lv.lv_meta.lvm_name.get(),
&view_curses::VC_STYLE, &view_curses::VC_STYLE,
vc.attrs_for_ident(lv.lv_meta.lvm_name)) vc.attrs_for_ident(lv.lv_meta.lvm_name))
.append(")") .append(")")
.append(this->fos_known_key_size - lv.lv_meta.lvm_name.size() - 9 + 3, ' ') .append(this->fos_known_key_size - lv.lv_meta.lvm_name.size()
.append(" = ") - 9 + 3,
.append((const char *) js.js_content.in(), js.js_len); ' ')
.append(" = ")
.append((const char*) js.js_content.in(), js.js_len);
this->fos_lines.emplace_back(al); this->fos_lines.emplace_back(al);
this->add_key_line_attrs(this->fos_known_key_size); this->add_key_line_attrs(this->fos_known_key_size);
} }
} }
std::map<const intern_string_t, json_ptr_walk::walk_list_t>::iterator json_iter; std::map<const intern_string_t, json_ptr_walk::walk_list_t>::iterator
json_iter;
if (!this->fos_log_helper.ldh_json_pairs.empty()) { if (!this->fos_log_helper.ldh_json_pairs.empty()) {
this->fos_lines.emplace_back(" JSON fields:"); this->fos_lines.emplace_back(" JSON fields:");
@ -493,14 +495,15 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
for (json_iter = this->fos_log_helper.ldh_json_pairs.begin(); for (json_iter = this->fos_log_helper.ldh_json_pairs.begin();
json_iter != this->fos_log_helper.ldh_json_pairs.end(); json_iter != this->fos_log_helper.ldh_json_pairs.end();
++json_iter) { ++json_iter)
json_ptr_walk::walk_list_t &jpairs = json_iter->second; {
json_ptr_walk::walk_list_t& jpairs = json_iter->second;
for (size_t lpc = 0; lpc < jpairs.size(); lpc++) { for (size_t lpc = 0; lpc < jpairs.size(); lpc++) {
this->fos_lines.emplace_back( this->fos_lines.emplace_back(
" " + " "
this->fos_log_helper.format_json_getter(json_iter->first, lpc) + " = " + + this->fos_log_helper.format_json_getter(json_iter->first, lpc)
jpairs[lpc].wt_value); + " = " + jpairs[lpc].wt_value);
this->add_key_line_attrs(0); this->add_key_line_attrs(0);
} }
} }
@ -514,101 +517,94 @@ void field_overlay_source::build_field_lines(const listview_curses &lv)
auto_mem<char, sqlite3_free> xp_call; auto_mem<char, sqlite3_free> xp_call;
qname = sql_quote_ident(xml_pair.first.first.get()); qname = sql_quote_ident(xml_pair.first.first.get());
xp_call = sqlite3_mprintf("xpath(%Q, %s)", xp_call = sqlite3_mprintf(
xml_pair.first.second.c_str(), "xpath(%Q, %s)", xml_pair.first.second.c_str(), qname.in());
qname.in()); this->fos_lines.emplace_back(
this->fos_lines.emplace_back(fmt::format( fmt::format(" {} = {}", xp_call, xml_pair.second));
" {} = {}", xp_call, xml_pair.second));
this->add_key_line_attrs(0); this->add_key_line_attrs(0);
} }
if (!this->fos_contexts.empty() && if (!this->fos_contexts.empty()
!this->fos_contexts.top().c_show_discovered) { && !this->fos_contexts.top().c_show_discovered) {
return; return;
} }
if (this->fos_log_helper.ldh_parser->dp_pairs.empty()) { if (this->fos_log_helper.ldh_parser->dp_pairs.empty()) {
this->fos_lines.emplace_back(" No discovered message fields"); this->fos_lines.emplace_back(" No discovered message fields");
} } else {
else { this->fos_lines.emplace_back(
this->fos_lines.emplace_back(" Discovered fields for logline table from message format: "); " Discovered fields for logline table from message format: ");
this->fos_lines.back().with_attr(string_attr( this->fos_lines.back().with_attr(
line_range(23, 23 + 7), string_attr(line_range(23, 23 + 7),
&view_curses::VC_STYLE, &view_curses::VC_STYLE,
vc.attrs_for_ident("logline") vc.attrs_for_ident("logline")));
)); auto& al = this->fos_lines.back();
auto &al = this->fos_lines.back(); auto& disc_str = al.get_string();
auto &disc_str = al.get_string();
al.with_attr(string_attr( al.with_attr(string_attr(
line_range(disc_str.length(), -1), line_range(disc_str.length(), -1), &view_curses::VC_STYLE, A_BOLD));
&view_curses::VC_STYLE,
A_BOLD));
disc_str.append(this->fos_log_helper.ldh_msg_format); disc_str.append(this->fos_log_helper.ldh_msg_format);
} }
auto iter = this->fos_log_helper.ldh_parser->dp_pairs.begin(); auto iter = this->fos_log_helper.ldh_parser->dp_pairs.begin();
for (size_t lpc = 0; for (size_t lpc = 0; lpc < this->fos_log_helper.ldh_parser->dp_pairs.size();
lpc < this->fos_log_helper.ldh_parser->dp_pairs.size(); lpc++, ++iter) { lpc++, ++iter)
auto &name = this->fos_log_helper.ldh_namer->cn_names[lpc]; {
auto& name = this->fos_log_helper.ldh_namer->cn_names[lpc];
auto val = this->fos_log_helper.ldh_parser->get_element_string( auto val = this->fos_log_helper.ldh_parser->get_element_string(
iter->e_sub_elements->back()); iter->e_sub_elements->back());
attr_line_t al(fmt::format(" {} = {}", name, val)); attr_line_t al(fmt::format(" {} = {}", name, val));
al.with_attr(string_attr( al.with_attr(string_attr(line_range(3, 3 + name.length()),
line_range(3, 3 + name.length()), &view_curses::VC_STYLE,
&view_curses::VC_STYLE, vc.attrs_for_ident(name)));
vc.attrs_for_ident(name)));
this->fos_lines.emplace_back(al); this->fos_lines.emplace_back(al);
this->add_key_line_attrs(this->fos_unknown_key_size, this->add_key_line_attrs(
lpc == (this->fos_log_helper.ldh_parser->dp_pairs.size() - 1)); this->fos_unknown_key_size,
lpc == (this->fos_log_helper.ldh_parser->dp_pairs.size() - 1));
} }
} }
void field_overlay_source::build_meta_line(const listview_curses &lv, void
std::vector<attr_line_t> &dst, field_overlay_source::build_meta_line(const listview_curses& lv,
vis_line_t row) std::vector<attr_line_t>& dst,
vis_line_t row)
{ {
content_line_t cl = this->fos_lss.at(row); content_line_t cl = this->fos_lss.at(row);
auto const &bm = this->fos_lss.get_user_bookmark_metadata(); auto const& bm = this->fos_lss.get_user_bookmark_metadata();
view_colors &vc = view_colors::singleton(); view_colors& vc = view_colors::singleton();
auto iter = bm.find(cl); auto iter = bm.find(cl);
if (iter != bm.end()) { if (iter != bm.end()) {
const bookmark_metadata &line_meta = iter->second; const bookmark_metadata& line_meta = iter->second;
size_t filename_width = this->fos_lss.get_filename_offset(); size_t filename_width = this->fos_lss.get_filename_offset();
if (!line_meta.bm_comment.empty()) { if (!line_meta.bm_comment.empty()) {
attr_line_t al; attr_line_t al;
al.with_string(" + ") al.with_string(" + ")
.with_attr(string_attr( .with_attr(string_attr(
line_range(1, 2), line_range(1, 2),
&view_curses::VC_GRAPHIC, &view_curses::VC_GRAPHIC,
line_meta.bm_tags.empty() ? ACS_LLCORNER : ACS_LTEE line_meta.bm_tags.empty() ? ACS_LLCORNER : ACS_LTEE))
)) .append(line_meta.bm_comment);
.append(line_meta.bm_comment);
al.insert(0, filename_width, ' '); al.insert(0, filename_width, ' ');
dst.emplace_back(al); dst.emplace_back(al);
} }
if (!line_meta.bm_tags.empty()) { if (!line_meta.bm_tags.empty()) {
attr_line_t al; attr_line_t al;
al.with_string(" +") al.with_string(" +").with_attr(string_attr(
.with_attr(string_attr( line_range(1, 2), &view_curses::VC_GRAPHIC, ACS_LLCORNER));
line_range(1, 2), for (const auto& str : line_meta.bm_tags) {
&view_curses::VC_GRAPHIC, al.append(1, ' ').append(
ACS_LLCORNER str, &view_curses::VC_STYLE, vc.attrs_for_ident(str));
));
for (const auto &str : line_meta.bm_tags) {
al.append(1, ' ')
.append(str, &view_curses::VC_STYLE, vc.attrs_for_ident(str));
} }
const auto *tc = dynamic_cast<const textview_curses *>(&lv); const auto* tc = dynamic_cast<const textview_curses*>(&lv);
if (tc) { if (tc) {
const auto &hm = tc->get_highlights(); const auto& hm = tc->get_highlights();
auto hl_iter = hm.find({highlight_source_t::PREVIEW, "search"}); auto hl_iter = hm.find({highlight_source_t::PREVIEW, "search"});
if (hl_iter != hm.end()) { if (hl_iter != hm.end()) {

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -40,32 +40,37 @@
class field_overlay_source : public list_overlay_source { class field_overlay_source : public list_overlay_source {
public: public:
explicit field_overlay_source(logfile_sub_source &lss, textfile_sub_source &tss) explicit field_overlay_source(logfile_sub_source& lss,
: fos_lss(lss), fos_tss(tss), fos_log_helper(lss) { textfile_sub_source& tss)
: fos_lss(lss), fos_tss(tss), fos_log_helper(lss){
}; };
void add_key_line_attrs(int key_size, bool last_line = false) { void add_key_line_attrs(int key_size, bool last_line = false)
string_attrs_t &sa = this->fos_lines.back().get_attrs(); {
string_attrs_t& sa = this->fos_lines.back().get_attrs();
struct line_range lr(1, 2); struct line_range lr(1, 2);
sa.emplace_back(lr, &view_curses::VC_GRAPHIC, last_line ? ACS_LLCORNER : ACS_LTEE); sa.emplace_back(
lr, &view_curses::VC_GRAPHIC, last_line ? ACS_LLCORNER : ACS_LTEE);
lr.lr_start = 3 + key_size + 3; lr.lr_start = 3 + key_size + 3;
lr.lr_end = -1; lr.lr_end = -1;
sa.emplace_back(lr, &view_curses::VC_STYLE, A_BOLD); sa.emplace_back(lr, &view_curses::VC_STYLE, A_BOLD);
}; };
bool list_value_for_overlay(const listview_curses &lv, bool list_value_for_overlay(const listview_curses& lv,
int y, int bottom, int y,
int bottom,
vis_line_t row, vis_line_t row,
attr_line_t &value_out) override { attr_line_t& value_out) override
{
if (y == 0) { if (y == 0) {
this->build_field_lines(lv); this->build_field_lines(lv);
this->build_summary_lines(lv); this->build_summary_lines(lv);
return false; return false;
} }
if (1 <= y && y <= (int)this->fos_lines.size()) { if (1 <= y && y <= (int) this->fos_lines.size()) {
value_out = this->fos_lines[y - 1]; value_out = this->fos_lines[y - 1];
return true; return true;
} }
@ -89,17 +94,18 @@ public:
return false; return false;
}; };
void build_field_lines(const listview_curses &lv); void build_field_lines(const listview_curses& lv);
void build_summary_lines(const listview_curses &lv); void build_summary_lines(const listview_curses& lv);
void build_meta_line(const listview_curses &lv, void build_meta_line(const listview_curses& lv,
std::vector<attr_line_t> &dst, std::vector<attr_line_t>& dst,
vis_line_t row); vis_line_t row);
struct context { struct context {
context(std::string prefix, bool show, bool show_discovered) context(std::string prefix, bool show, bool show_discovered)
: c_prefix(std::move(prefix)), c_show(show), : c_prefix(std::move(prefix)), c_show(show),
c_show_discovered(show_discovered) c_show_discovered(show_discovered)
{} {
}
std::string c_prefix; std::string c_prefix;
bool c_show{false}; bool c_show{false};
@ -108,8 +114,8 @@ public:
bool fos_show_status{true}; bool fos_show_status{true};
std::stack<context> fos_contexts; std::stack<context> fos_contexts;
logfile_sub_source &fos_lss; logfile_sub_source& fos_lss;
textfile_sub_source &fos_tss; textfile_sub_source& fos_tss;
log_data_helper fos_log_helper; log_data_helper fos_log_helper;
int fos_known_key_size{0}; int fos_known_key_size{0};
int fos_unknown_key_size{0}; int fos_unknown_key_size{0};
@ -118,4 +124,4 @@ public:
std::vector<attr_line_t> fos_meta_lines; std::vector<attr_line_t> fos_meta_lines;
}; };
#endif //LNAV_FIELD_OVERLAY_SOURCE_H #endif // LNAV_FIELD_OVERLAY_SOURCE_H

@ -21,35 +21,36 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file file_collection.cc * @file file_collection.cc
*/ */
#include "config.h" #include <unordered_map>
#include <glob.h> #include "file_collection.hh"
#include <unordered_map> #include <glob.h>
#include "base/opt_util.hh"
#include "base/humanize.network.hh" #include "base/humanize.network.hh"
#include "base/isc.hh" #include "base/isc.hh"
#include "base/opt_util.hh"
#include "config.h"
#include "lnav_util.hh"
#include "logfile.hh" #include "logfile.hh"
#include "file_collection.hh" #include "pcap_manager.hh"
#include "pcrepp/pcrepp.hh" #include "pcrepp/pcrepp.hh"
#include "tailer/tailer.looper.hh"
#include "service_tags.hh" #include "service_tags.hh"
#include "lnav_util.hh" #include "tailer/tailer.looper.hh"
#include "pcap_manager.hh"
static std::mutex REALPATH_CACHE_MUTEX; static std::mutex REALPATH_CACHE_MUTEX;
static std::unordered_map<std::string, std::string> REALPATH_CACHE; static std::unordered_map<std::string, std::string> REALPATH_CACHE;
child_poll_result_t child_poller::poll(file_collection& fc) child_poll_result_t
child_poller::poll(file_collection& fc)
{ {
if (!this->cp_child) { if (!this->cp_child) {
return child_poll_result_t::FINISHED; return child_poll_result_t::FINISHED;
@ -58,18 +59,18 @@ child_poll_result_t child_poller::poll(file_collection& fc)
auto poll_res = std::move(this->cp_child.value()).poll(); auto poll_res = std::move(this->cp_child.value()).poll();
this->cp_child = nonstd::nullopt; this->cp_child = nonstd::nullopt;
return poll_res.match( return poll_res.match(
[this](auto_pid<process_state::RUNNING>& alive) { [this](auto_pid<process_state::running>& alive) {
this->cp_child = std::move(alive); this->cp_child = std::move(alive);
return child_poll_result_t::ALIVE; return child_poll_result_t::ALIVE;
}, },
[this, &fc](auto_pid<process_state::FINISHED>& finished) { [this, &fc](auto_pid<process_state::finished>& finished) {
this->cp_finalizer(fc, finished); this->cp_finalizer(fc, finished);
return child_poll_result_t::FINISHED; return child_poll_result_t::FINISHED;
} });
);
} }
void file_collection::close_files(const std::vector<std::shared_ptr<logfile>> &files) void
file_collection::close_files(const std::vector<std::shared_ptr<logfile>>& files)
{ {
for (const auto& lf : files) { for (const auto& lf : files) {
auto actual_path_opt = lf->get_actual_path(); auto actual_path_opt = lf->get_actual_path();
@ -89,9 +90,7 @@ void file_collection::close_files(const std::vector<std::shared_ptr<logfile>> &f
} else { } else {
this->fc_file_names.erase(lf->get_filename()); this->fc_file_names.erase(lf->get_filename());
} }
auto file_iter = find(this->fc_files.begin(), auto file_iter = find(this->fc_files.begin(), this->fc_files.end(), lf);
this->fc_files.end(),
lf);
if (file_iter != this->fc_files.end()) { if (file_iter != this->fc_files.end()) {
this->fc_files.erase(file_iter); this->fc_files.erase(file_iter);
} }
@ -101,32 +100,33 @@ void file_collection::close_files(const std::vector<std::shared_ptr<logfile>> &f
this->regenerate_unique_file_names(); this->regenerate_unique_file_names();
} }
void file_collection::regenerate_unique_file_names() void
file_collection::regenerate_unique_file_names()
{ {
unique_path_generator upg; unique_path_generator upg;
for (const auto &lf : this->fc_files) { for (const auto& lf : this->fc_files) {
upg.add_source(lf); upg.add_source(lf);
} }
upg.generate(); upg.generate();
this->fc_largest_path_length = 0; this->fc_largest_path_length = 0;
for (const auto &pair : this->fc_name_to_errors) { for (const auto& pair : this->fc_name_to_errors) {
auto path = ghc::filesystem::path(pair.first).filename().string(); auto path = ghc::filesystem::path(pair.first).filename().string();
if (path.length() > this->fc_largest_path_length) { if (path.length() > this->fc_largest_path_length) {
this->fc_largest_path_length = path.length(); this->fc_largest_path_length = path.length();
} }
} }
for (const auto &lf : this->fc_files) { for (const auto& lf : this->fc_files) {
const auto &path = lf->get_unique_path(); const auto& path = lf->get_unique_path();
if (path.length() > this->fc_largest_path_length) { if (path.length() > this->fc_largest_path_length) {
this->fc_largest_path_length = path.length(); this->fc_largest_path_length = path.length();
} }
} }
for (const auto &pair : this->fc_other_files) { for (const auto& pair : this->fc_other_files) {
switch (pair.second.ofd_format) { switch (pair.second.ofd_format) {
case file_format_t::FF_UNKNOWN: case file_format_t::FF_UNKNOWN:
case file_format_t::FF_ARCHIVE: case file_format_t::FF_ARCHIVE:
@ -148,7 +148,8 @@ void file_collection::regenerate_unique_file_names()
} }
} }
void file_collection::merge(file_collection &other) void
file_collection::merge(file_collection& other)
{ {
this->fc_recursive = this->fc_recursive || other.fc_recursive; this->fc_recursive = this->fc_recursive || other.fc_recursive;
this->fc_rotated = this->fc_rotated || other.fc_rotated; this->fc_rotated = this->fc_rotated || other.fc_rotated;
@ -163,12 +164,11 @@ void file_collection::merge(file_collection &other)
for (const auto& lf : other.fc_files) { for (const auto& lf : other.fc_files) {
this->fc_name_to_errors.erase(lf->get_filename()); this->fc_name_to_errors.erase(lf->get_filename());
} }
this->fc_files.insert(this->fc_files.end(), this->fc_files.insert(
other.fc_files.begin(), this->fc_files.end(), other.fc_files.begin(), other.fc_files.end());
other.fc_files.end());
this->fc_files_generation += 1; this->fc_files_generation += 1;
} }
for (auto &pair : other.fc_renamed_files) { for (auto& pair : other.fc_renamed_files) {
pair.first->set_filename(pair.second); pair.first->set_filename(pair.second);
} }
this->fc_closed_files.insert(other.fc_closed_files.begin(), this->fc_closed_files.insert(other.fc_closed_files.begin(),
@ -188,7 +188,7 @@ void file_collection::merge(file_collection &other)
* Functor used to compare files based on their device and inode number. * Functor used to compare files based on their device and inode number.
*/ */
struct same_file { struct same_file {
explicit same_file(const struct stat &stat) : sf_stat(stat) {}; explicit same_file(const struct stat& stat) : sf_stat(stat){};
/** /**
* Compare the given log file against the 'stat' given in the constructor. * Compare the given log file against the 'stat' given in the constructor.
@ -196,14 +196,13 @@ struct same_file {
* @return True if the dev/inode values in the stat given in the * @return True if the dev/inode values in the stat given in the
* constructor matches the stat in the logfile object. * constructor matches the stat in the logfile object.
*/ */
bool operator()(const std::shared_ptr<logfile> &lf) const bool operator()(const std::shared_ptr<logfile>& lf) const
{ {
return !lf->is_closed() && return !lf->is_closed() && this->sf_stat.st_dev == lf->get_stat().st_dev
this->sf_stat.st_dev == lf->get_stat().st_dev && && this->sf_stat.st_ino == lf->get_stat().st_ino;
this->sf_stat.st_ino == lf->get_stat().st_ino;
}; };
const struct stat &sf_stat; const struct stat& sf_stat;
}; };
/** /**
@ -216,8 +215,9 @@ struct same_file {
* @param required Specifies whether or not the file must exist and be valid. * @param required Specifies whether or not the file must exist and be valid.
*/ */
std::future<file_collection> std::future<file_collection>
file_collection::watch_logfile(const std::string &filename, file_collection::watch_logfile(const std::string& filename,
logfile_open_options &loo, bool required) logfile_open_options& loo,
bool required)
{ {
file_collection retval; file_collection retval;
struct stat st; struct stat st;
@ -237,8 +237,8 @@ file_collection::watch_logfile(const std::string &filename,
if (S_ISDIR(st.st_mode) && this->fc_recursive) { if (S_ISDIR(st.st_mode) && this->fc_recursive) {
std::string wilddir = filename + "/*"; std::string wilddir = filename + "/*";
if (this->fc_file_names.find(wilddir) == if (this->fc_file_names.find(wilddir) == this->fc_file_names.end())
this->fc_file_names.end()) { {
retval.fc_file_names.emplace(wilddir, logfile_open_options()); retval.fc_file_names.emplace(wilddir, logfile_open_options());
} }
return lnav::futures::make_ready_future(std::move(retval)); return lnav::futures::make_ready_future(std::move(retval));
@ -260,10 +260,11 @@ file_collection::watch_logfile(const std::string &filename,
} }
if (rc == -1) { if (rc == -1) {
if (required) { if (required) {
retval.fc_name_to_errors.emplace(filename, file_error_info{ retval.fc_name_to_errors.emplace(filename,
time(nullptr), file_error_info{
std::string(strerror(errno)), time(nullptr),
}); std::string(strerror(errno)),
});
} }
return lnav::futures::make_ready_future(std::move(retval)); return lnav::futures::make_ready_future(std::move(retval));
} }
@ -271,8 +272,8 @@ file_collection::watch_logfile(const std::string &filename,
auto stat_iter = find_if(this->fc_new_stats.begin(), auto stat_iter = find_if(this->fc_new_stats.begin(),
this->fc_new_stats.end(), this->fc_new_stats.end(),
[&st](auto& elem) { [&st](auto& elem) {
return st.st_ino == elem.st_ino && return st.st_ino == elem.st_ino
st.st_dev == elem.st_dev; && st.st_dev == elem.st_dev;
}); });
if (stat_iter != this->fc_new_stats.end()) { if (stat_iter != this->fc_new_stats.end()) {
// this file is probably a link that we have already scanned in this // this file is probably a link that we have already scanned in this
@ -281,16 +282,19 @@ file_collection::watch_logfile(const std::string &filename,
} }
this->fc_new_stats.emplace_back(st); this->fc_new_stats.emplace_back(st);
auto file_iter = find_if(this->fc_files.begin(), auto file_iter
this->fc_files.end(), = find_if(this->fc_files.begin(), this->fc_files.end(), same_file(st));
same_file(st));
if (file_iter == this->fc_files.end()) { if (file_iter == this->fc_files.end()) {
if (this->fc_other_files.find(filename) != this->fc_other_files.end()) { if (this->fc_other_files.find(filename) != this->fc_other_files.end()) {
return lnav::futures::make_ready_future(std::move(retval)); return lnav::futures::make_ready_future(std::move(retval));
} }
auto func = [filename, st, loo, prog = this->fc_progress, errs = this->fc_name_to_errors]() mutable { auto func = [filename,
st,
loo,
prog = this->fc_progress,
errs = this->fc_name_to_errors]() mutable {
file_collection retval; file_collection retval;
if (errs.find(filename) != errs.end()) { if (errs.find(filename) != errs.end()) {
@ -315,16 +319,27 @@ file_collection::watch_logfile(const std::string &filename,
loo.loo_fd = std::move(convert_res.cr_destination); loo.loo_fd = std::move(convert_res.cr_destination);
retval.fc_child_pollers.emplace_back(child_poller{ retval.fc_child_pollers.emplace_back(child_poller{
std::move(convert_res.cr_child), std::move(convert_res.cr_child),
[filename, st, error_queue = convert_res.cr_error_queue](auto& fc, auto& child) { [filename,
if (child.was_normal_exit() && child.exit_status() == EXIT_SUCCESS) { st,
log_info("pcap[%d] exited normally", child.in()); error_queue = convert_res.cr_error_queue](
auto& fc, auto& child) {
if (child.was_normal_exit()
&& child.exit_status() == EXIT_SUCCESS) {
log_info("pcap[%d] exited normally",
child.in());
return; return;
} }
log_error("pcap[%d] exited with %d", child.in(), child.status()); log_error("pcap[%d] exited with %d",
fc.fc_name_to_errors.emplace(filename, file_error_info{ child.in(),
st.st_mtime, child.status());
fmt::format("{}", fmt::join(*error_queue, "\n")), fc.fc_name_to_errors.emplace(
}); filename,
file_error_info{
st.st_mtime,
fmt::format(
"{}",
fmt::join(*error_queue, "\n")),
});
}, },
}); });
auto open_res = logfile::open(filename, loo); auto open_res = logfile::open(filename, loo);
@ -332,22 +347,25 @@ file_collection::watch_logfile(const std::string &filename,
retval.fc_files.push_back(open_res.unwrap()); retval.fc_files.push_back(open_res.unwrap());
} else { } else {
retval.fc_name_to_errors.emplace( retval.fc_name_to_errors.emplace(
filename, file_error_info{ filename,
file_error_info{
st.st_mtime, st.st_mtime,
open_res.unwrapErr(), open_res.unwrapErr(),
}); });
} }
} else { } else {
retval.fc_name_to_errors.emplace(filename, file_error_info{ retval.fc_name_to_errors.emplace(filename,
st.st_mtime, file_error_info{
res.unwrapErr(), st.st_mtime,
}); res.unwrapErr(),
});
} }
break; break;
} }
case file_format_t::FF_ARCHIVE: { case file_format_t::FF_ARCHIVE: {
nonstd::optional<std::list<archive_manager::extract_progress>::iterator> nonstd::optional<
std::list<archive_manager::extract_progress>::iterator>
prog_iter_opt; prog_iter_opt;
if (loo.loo_source == logfile_name_source::ARCHIVE) { if (loo.loo_source == logfile_name_source::ARCHIVE) {
@ -357,24 +375,21 @@ file_collection::watch_logfile(const std::string &filename,
auto res = archive_manager::walk_archive_files( auto res = archive_manager::walk_archive_files(
filename, filename,
[prog, &prog_iter_opt]( [prog, &prog_iter_opt](const auto& path,
const auto &path, const auto total) {
const auto total) {
safe::WriteAccess<safe_scan_progress> sp(*prog); safe::WriteAccess<safe_scan_progress> sp(*prog);
prog_iter_opt | [&sp](auto prog_iter) { prog_iter_opt | [&sp](auto prog_iter) {
sp->sp_extractions.erase(prog_iter); sp->sp_extractions.erase(prog_iter);
}; };
auto prog_iter = sp->sp_extractions.emplace( auto prog_iter = sp->sp_extractions.emplace(
sp->sp_extractions.begin(), sp->sp_extractions.begin(), path, total);
path, total);
prog_iter_opt = prog_iter; prog_iter_opt = prog_iter;
return &(*prog_iter); return &(*prog_iter);
}, },
[&filename, &retval]( [&filename, &retval](const auto& tmp_path,
const auto &tmp_path, const auto& entry) {
const auto &entry) {
auto arc_path = ghc::filesystem::relative( auto arc_path = ghc::filesystem::relative(
entry.path(), tmp_path); entry.path(), tmp_path);
auto custom_name = filename / arc_path; auto custom_name = filename / arc_path;
@ -400,18 +415,16 @@ file_collection::watch_logfile(const std::string &filename,
log_error("archive extraction failed: %s", log_error("archive extraction failed: %s",
res.unwrapErr().c_str()); res.unwrapErr().c_str());
retval.clear(); retval.clear();
retval.fc_name_to_errors.emplace( retval.fc_name_to_errors.emplace(filename,
filename, file_error_info{
file_error_info{ st.st_mtime,
st.st_mtime, res.unwrapErr(),
res.unwrapErr(), });
});
} else { } else {
retval.fc_other_files[filename] = ff; retval.fc_other_files[filename] = ff;
} }
{ {
prog_iter_opt | prog_iter_opt | [&prog](auto prog_iter) {
[&prog](auto prog_iter) {
prog->writeAccess()->sp_extractions.erase( prog->writeAccess()->sp_extractions.erase(
prog_iter); prog_iter);
}; };
@ -425,10 +438,10 @@ file_collection::watch_logfile(const std::string &filename,
auto open_res = logfile::open(filename, loo); auto open_res = logfile::open(filename, loo);
if (open_res.isOk()) { if (open_res.isOk()) {
retval.fc_files.push_back(open_res.unwrap()); retval.fc_files.push_back(open_res.unwrap());
} } else {
else {
retval.fc_name_to_errors.emplace( retval.fc_name_to_errors.emplace(
filename, file_error_info{ filename,
file_error_info{
st.st_mtime, st.st_mtime,
open_res.unwrapErr(), open_res.unwrapErr(),
}); });
@ -460,10 +473,12 @@ file_collection::watch_logfile(const std::string &filename,
* @param path The glob pattern to expand. * @param path The glob pattern to expand.
* @param required Passed to watch_logfile. * @param required Passed to watch_logfile.
*/ */
void file_collection::expand_filename(lnav::futures::future_queue<file_collection> &fq, void
const std::string &path, file_collection::expand_filename(
logfile_open_options &loo, lnav::futures::future_queue<file_collection>& fq,
bool required) const std::string& path,
logfile_open_options& loo,
bool required)
{ {
static_root_mem<glob_t, globfree> gl; static_root_mem<glob_t, globfree> gl;
@ -499,26 +514,25 @@ void file_collection::expand_filename(lnav::futures::future_queue<file_collectio
file_collection retval; file_collection retval;
isc::to<tailer::looper &, services::remote_tailer_t>() isc::to<tailer::looper&, services::remote_tailer_t>().send(
.send([=](auto &tlooper) { [=](auto& tlooper) { tlooper.add_remote(rp, loo); });
tlooper.add_remote(rp, loo);
});
retval.fc_other_files[path] = file_format_t::FF_REMOTE; retval.fc_other_files[path] = file_format_t::FF_REMOTE;
{ {
this->fc_progress->writeAccess()-> this->fc_progress->writeAccess()
sp_tailers[fmt::format("{}", rp.home())].tp_message = ->sp_tailers[fmt::format("{}", rp.home())]
"Initializing..."; .tp_message
= "Initializing...";
} }
fq.push_back(lnav::futures::make_ready_future(std::move(retval))); fq.push_back(
lnav::futures::make_ready_future(std::move(retval)));
return; return;
} }
required = false; required = false;
} }
} }
if (gl->gl_pathc > 1 || if (gl->gl_pathc > 1 || strcmp(path.c_str(), gl->gl_pathv[0]) != 0) {
strcmp(path.c_str(), gl->gl_pathv[0]) != 0) {
required = false; required = false;
} }
@ -530,13 +544,15 @@ void file_collection::expand_filename(lnav::futures::future_queue<file_collectio
if (iter == REALPATH_CACHE.end()) { if (iter == REALPATH_CACHE.end()) {
auto_mem<char> abspath; auto_mem<char> abspath;
if ((abspath = realpath(gl->gl_pathv[lpc], nullptr)) == if ((abspath = realpath(gl->gl_pathv[lpc], nullptr)) == nullptr)
nullptr) { {
auto errmsg = strerror(errno); auto errmsg = strerror(errno);
if (required) { if (required) {
fprintf(stderr, "Cannot find file: %s -- %s", fprintf(stderr,
gl->gl_pathv[lpc], errmsg); "Cannot find file: %s -- %s",
gl->gl_pathv[lpc],
errmsg);
} else if (loo.loo_source != logfile_name_source::REMOTE) { } else if (loo.loo_source != logfile_name_source::REMOTE) {
// XXX The remote code path adds the file name before // XXX The remote code path adds the file name before
// the file exists... not sure checking for that here // the file exists... not sure checking for that here
@ -544,19 +560,20 @@ void file_collection::expand_filename(lnav::futures::future_queue<file_collectio
file_collection retval; file_collection retval;
if (gl->gl_pathc == 1) { if (gl->gl_pathc == 1) {
retval.fc_name_to_errors.emplace( retval.fc_name_to_errors.emplace(path,
path, file_error_info{ file_error_info{
time(nullptr), time(nullptr),
errmsg, errmsg,
}); });
} else { } else {
retval.fc_name_to_errors.emplace( retval.fc_name_to_errors.emplace(path_str,
path_str, file_error_info{ file_error_info{
time(nullptr), time(nullptr),
errmsg, errmsg,
}); });
} }
fq.push_back(lnav::futures::make_ready_future(std::move(retval))); fq.push_back(lnav::futures::make_ready_future(
std::move(retval)));
} }
continue; continue;
} else { } else {
@ -573,14 +590,14 @@ void file_collection::expand_filename(lnav::futures::future_queue<file_collectio
} }
} }
file_collection file_collection::rescan_files(bool required) file_collection
file_collection::rescan_files(bool required)
{ {
file_collection retval; file_collection retval;
lnav::futures::future_queue<file_collection> fq([&retval](auto &fc) { lnav::futures::future_queue<file_collection> fq(
retval.merge(fc); [&retval](auto& fc) { retval.merge(fc); });
});
for (auto &pair : this->fc_file_names) { for (auto& pair : this->fc_file_names) {
if (pair.second.loo_fd == -1) { if (pair.second.loo_fd == -1) {
this->expand_filename(fq, pair.first, pair.second, required); this->expand_filename(fq, pair.first, pair.second, required);
if (this->fc_rotated) { if (this->fc_rotated) {

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -32,19 +32,18 @@
#ifndef lnav_file_collection_hh #ifndef lnav_file_collection_hh
#define lnav_file_collection_hh #define lnav_file_collection_hh
#include <forward_list>
#include <list>
#include <map> #include <map>
#include <set> #include <set>
#include <list>
#include <string> #include <string>
#include <utility> #include <utility>
#include <forward_list>
#include "safe/safe.h"
#include "base/future_util.hh"
#include "logfile_fwd.hh"
#include "archive_manager.hh" #include "archive_manager.hh"
#include "base/future_util.hh"
#include "file_format.hh" #include "file_format.hh"
#include "logfile_fwd.hh"
#include "safe/safe.h"
#include "tailer/tailer.looper.hh" #include "tailer/tailer.looper.hh"
struct tailer_progress { struct tailer_progress {
@ -64,7 +63,9 @@ struct other_file_descriptor {
other_file_descriptor(file_format_t format = file_format_t::FF_UNKNOWN, other_file_descriptor(file_format_t format = file_format_t::FF_UNKNOWN,
std::string description = "") std::string description = "")
: ofd_format(format), ofd_description(std::move(description)) {} : ofd_format(format), ofd_description(std::move(description))
{
}
}; };
struct file_error_info { struct file_error_info {
@ -81,16 +82,22 @@ enum class child_poll_result_t {
class child_poller { class child_poller {
public: public:
explicit child_poller(auto_pid<process_state::RUNNING> child, explicit child_poller(
std::function<void(file_collection&, auto_pid<process_state::FINISHED>&)> finalizer) auto_pid<process_state::running> child,
: cp_child(std::move(child)), cp_finalizer(std::move(finalizer)) { std::function<void(file_collection&,
auto_pid<process_state::finished>&)> finalizer)
: cp_child(std::move(child)), cp_finalizer(std::move(finalizer))
{
} }
child_poller(child_poller&& other) noexcept child_poller(child_poller&& other) noexcept
: cp_child(std::move(other.cp_child)), : cp_child(std::move(other.cp_child)),
cp_finalizer(std::move(other.cp_finalizer)) {} cp_finalizer(std::move(other.cp_finalizer))
{
}
child_poller& operator=(child_poller&& other) noexcept { child_poller& operator=(child_poller&& other) noexcept
{
this->cp_child = std::move(other.cp_child); this->cp_child = std::move(other.cp_child);
this->cp_finalizer = std::move(other.cp_finalizer); this->cp_finalizer = std::move(other.cp_finalizer);
@ -100,9 +107,11 @@ public:
~child_poller() noexcept = default; ~child_poller() noexcept = default;
child_poll_result_t poll(file_collection& fc); child_poll_result_t poll(file_collection& fc);
private: private:
nonstd::optional<auto_pid<process_state::RUNNING>> cp_child; nonstd::optional<auto_pid<process_state::running>> cp_child;
std::function<void(file_collection&, auto_pid<process_state::FINISHED>&)> cp_finalizer; std::function<void(file_collection&, auto_pid<process_state::finished>&)>
cp_finalizer;
}; };
struct file_collection { struct file_collection {
@ -127,7 +136,8 @@ struct file_collection {
file_collection() file_collection()
: fc_progress(std::make_shared<safe::Safe<scan_progress>>()) : fc_progress(std::make_shared<safe::Safe<scan_progress>>())
{} {
}
void clear() void clear()
{ {
@ -141,22 +151,20 @@ struct file_collection {
file_collection rescan_files(bool required = false); file_collection rescan_files(bool required = false);
void void expand_filename(lnav::futures::future_queue<file_collection>& fq,
expand_filename(lnav::futures::future_queue<file_collection> &fq, const std::string& path,
const std::string &path, logfile_open_options& loo,
logfile_open_options &loo, bool required);
bool required);
std::future<file_collection> std::future<file_collection> watch_logfile(const std::string& filename,
watch_logfile(const std::string &filename, logfile_open_options &loo, logfile_open_options& loo,
bool required); bool required);
void merge(file_collection &other); void merge(file_collection& other);
void close_files(const std::vector<std::shared_ptr<logfile>> &files); void close_files(const std::vector<std::shared_ptr<logfile>>& files);
void regenerate_unique_file_names(); void regenerate_unique_file_names();
}; };
#endif #endif

@ -21,88 +21,83 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* @file file_format.hh * @file file_format.hh
*/ */
#include "config.h"
#include <unordered_map> #include <unordered_map>
#include "file_format.hh"
#include "archive_manager.hh"
#include "auto_fd.hh"
#include "base/fs_util.hh" #include "base/fs_util.hh"
#include "base/intern_string.hh" #include "base/intern_string.hh"
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "auto_fd.hh" #include "config.h"
#include "file_format.hh"
#include "archive_manager.hh"
static bool is_pcap_header(uint8_t *buffer) static bool
is_pcap_header(uint8_t* buffer)
{ {
size_t offset = 0; size_t offset = 0;
if (buffer[0] == 0x0a && if (buffer[0] == 0x0a && buffer[1] == 0x0d && buffer[2] == 0x0d
buffer[1] == 0x0d && && buffer[3] == 0x0a)
buffer[2] == 0x0d && {
buffer[3] == 0x0a) {
offset += sizeof(uint32_t) * 2; offset += sizeof(uint32_t) * 2;
if (buffer[offset + 0] == 0x1a && if (buffer[offset + 0] == 0x1a && buffer[offset + 1] == 0x2b
buffer[offset + 1] == 0x2b && && buffer[offset + 2] == 0x3c && buffer[offset + 3] == 0x4d)
buffer[offset + 2] == 0x3c && {
buffer[offset + 3] == 0x4d) {
return true; return true;
} }
if (buffer[offset + 0] == 0x4d && if (buffer[offset + 0] == 0x4d && buffer[offset + 1] == 0x3c
buffer[offset + 1] == 0x3c && && buffer[offset + 2] == 0x2b && buffer[offset + 3] == 0x1a)
buffer[offset + 2] == 0x2b && {
buffer[offset + 3] == 0x1a) {
return true; return true;
} }
return false; return false;
} }
if (buffer[0] == 0xa1 && if (buffer[0] == 0xa1 && buffer[1] == 0xb2 && buffer[2] == 0xc3
buffer[1] == 0xb2 && && buffer[3] == 0xd4)
buffer[2] == 0xc3 && {
buffer[3] == 0xd4) {
return true; return true;
} }
if (buffer[0] == 0xd4 && if (buffer[0] == 0xd4 && buffer[1] == 0xc3 && buffer[2] == 0xb2
buffer[1] == 0xc3 && && buffer[3] == 0xa1)
buffer[2] == 0xb2 && {
buffer[3] == 0xa1) {
return true; return true;
} }
if (buffer[0] == 0xa1 && if (buffer[0] == 0xa1 && buffer[1] == 0xb2 && buffer[2] == 0x3c
buffer[1] == 0xb2 && && buffer[3] == 0x4d)
buffer[2] == 0x3c && {
buffer[3] == 0x4d) {
return true; return true;
} }
if (buffer[0] == 0x4d && if (buffer[0] == 0x4d && buffer[1] == 0x3c && buffer[2] == 0xb2
buffer[1] == 0x3c && && buffer[3] == 0xa1)
buffer[2] == 0xb2 && {
buffer[3] == 0xa1) {
return true; return true;
} }
return false; return false;
} }
file_format_t detect_file_format(const ghc::filesystem::path &filename) file_format_t
detect_file_format(const ghc::filesystem::path& filename)
{ {
if (archive_manager::is_archive(filename)) { if (archive_manager::is_archive(filename)) {
return file_format_t::FF_ARCHIVE; return file_format_t::FF_ARCHIVE;
} }
file_format_t retval = file_format_t::FF_UNKNOWN; file_format_t retval = file_format_t::FF_UNKNOWN;
auto_fd fd; auto_fd fd;
if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) != -1) { if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) != -1) {
uint8_t buffer[32]; uint8_t buffer[32];

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -49,7 +49,7 @@ namespace fmt {
template<> template<>
struct formatter<file_format_t> : formatter<string_view> { struct formatter<file_format_t> : formatter<string_view> {
template<typename FormatContext> template<typename FormatContext>
auto format(file_format_t ff, FormatContext &ctx) auto format(file_format_t ff, FormatContext& ctx)
{ {
string_view name = "unknown"; string_view name = "unknown";
switch (ff) { switch (ff) {
@ -71,6 +71,6 @@ struct formatter<file_format_t> : formatter<string_view> {
return formatter<string_view>::format(name, ctx); return formatter<string_view>::format(name, ctx);
} }
}; };
} } // namespace fmt
#endif #endif

@ -21,34 +21,33 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h"
#include <unistd.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "base/injector.bind.hh" #include "base/injector.bind.hh"
#include "base/lnav.gzip.hh" #include "base/lnav.gzip.hh"
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "config.h"
#include "file_collection.hh" #include "file_collection.hh"
#include "file_vtab.cfg.hh"
#include "log_format.hh"
#include "logfile.hh" #include "logfile.hh"
#include "session_data.hh" #include "session_data.hh"
#include "vtab_module.hh" #include "vtab_module.hh"
#include "log_format.hh"
#include "file_vtab.cfg.hh"
using namespace std; using namespace std;
struct lnav_file : public tvt_iterator_cursor<lnav_file> { struct lnav_file : public tvt_iterator_cursor<lnav_file> {
using iterator = vector<shared_ptr<logfile>>::iterator; using iterator = vector<shared_ptr<logfile>>::iterator;
static constexpr const char *NAME = "lnav_file"; static constexpr const char* NAME = "lnav_file";
static constexpr const char *CREATE_STMT = R"( static constexpr const char* CREATE_STMT = R"(
-- Access lnav's open file list through this table. -- Access lnav's open file list through this table.
CREATE TABLE lnav_file ( CREATE TABLE lnav_file (
device integer, -- The device the file is stored on. device integer, -- The device the file is stored on.
@ -63,24 +62,26 @@ CREATE TABLE lnav_file (
); );
)"; )";
explicit lnav_file(file_collection& fc) : lf_collection(fc) { explicit lnav_file(file_collection& fc) : lf_collection(fc) {}
}
iterator begin() { iterator begin()
{
return this->lf_collection.fc_files.begin(); return this->lf_collection.fc_files.begin();
} }
iterator end() { iterator end()
{
return this->lf_collection.fc_files.end(); return this->lf_collection.fc_files.end();
} }
int get_column(const cursor &vc, sqlite3_context *ctx, int col) { int get_column(const cursor& vc, sqlite3_context* ctx, int col)
{
auto lf = *vc.iter; auto lf = *vc.iter;
const struct stat &st = lf->get_stat(); const struct stat& st = lf->get_stat();
const string &name = lf->get_filename(); const string& name = lf->get_filename();
auto format = lf->get_format(); auto format = lf->get_format();
const char *format_name = const char* format_name = format != nullptr ? format->get_name().get()
format != nullptr ? format->get_name().get() : nullptr; : nullptr;
switch (col) { switch (col) {
case 0: case 0:
@ -117,35 +118,32 @@ CREATE TABLE lnav_file (
} else { } else {
auto fd = lf->get_fd(); auto fd = lf->get_fd();
auto_mem<char> buf; auto_mem<char> buf;
buf = (char *) malloc(lf_stat.st_size); buf = (char*) malloc(lf_stat.st_size);
auto rc = pread(fd, buf, lf_stat.st_size, 0); auto rc = pread(fd, buf, lf_stat.st_size, 0);
if (rc == -1) { if (rc == -1) {
auto errmsg = fmt::format("unable to read file: {}", auto errmsg = fmt::format("unable to read file: {}",
strerror(errno)); strerror(errno));
sqlite3_result_error(ctx, errmsg.c_str(), sqlite3_result_error(
errmsg.length()); ctx, errmsg.c_str(), errmsg.length());
} else if (rc != lf_stat.st_size) { } else if (rc != lf_stat.st_size) {
auto errmsg = fmt::format("short read of file: {} < {}", auto errmsg = fmt::format(
rc, lf_stat.st_size); "short read of file: {} < {}", rc, lf_stat.st_size);
sqlite3_result_error(ctx, errmsg.c_str(), sqlite3_result_error(
errmsg.length()); ctx, errmsg.c_str(), errmsg.length());
} else if (lnav::gzip::is_gzipped(buf, rc)) { } else if (lnav::gzip::is_gzipped(buf, rc)) {
lnav::gzip::uncompress(lf->get_unique_path(), buf, rc) lnav::gzip::uncompress(lf->get_unique_path(), buf, rc)
.then([ctx](auto uncomp) { .then([ctx](auto uncomp) {
auto pair = uncomp.release(); auto pair = uncomp.release();
sqlite3_result_blob64(ctx, sqlite3_result_blob64(
pair.first, ctx, pair.first, pair.second, free);
pair.second,
free);
}) })
.otherwise([ctx](auto msg) { .otherwise([ctx](auto msg) {
sqlite3_result_error(ctx, sqlite3_result_error(
msg.c_str(), ctx, msg.c_str(), msg.size());
msg.size());
}); });
} else { } else {
sqlite3_result_blob64(ctx, buf.release(), rc, free); sqlite3_result_blob64(ctx, buf.release(), rc, free);
@ -161,28 +159,30 @@ CREATE TABLE lnav_file (
return SQLITE_OK; return SQLITE_OK;
} }
int delete_row(sqlite3_vtab *vt, sqlite3_int64 rowid) { int delete_row(sqlite3_vtab* vt, sqlite3_int64 rowid)
vt->zErrMsg = sqlite3_mprintf( {
"Rows cannot be deleted from this table"); vt->zErrMsg = sqlite3_mprintf("Rows cannot be deleted from this table");
return SQLITE_ERROR; return SQLITE_ERROR;
}; };
int insert_row(sqlite3_vtab *tab, sqlite3_int64 &rowid_out) { int insert_row(sqlite3_vtab* tab, sqlite3_int64& rowid_out)
tab->zErrMsg = sqlite3_mprintf( {
"Rows cannot be inserted into this table"); tab->zErrMsg
= sqlite3_mprintf("Rows cannot be inserted into this table");
return SQLITE_ERROR; return SQLITE_ERROR;
}; };
int update_row(sqlite3_vtab *tab, int update_row(sqlite3_vtab* tab,
sqlite3_int64 &rowid, sqlite3_int64& rowid,
int64_t device, int64_t device,
int64_t inode, int64_t inode,
std::string path, std::string path,
const char *text_format, const char* text_format,
const char *format, const char* format,
int64_t lines, int64_t lines,
int64_t time_offset, int64_t time_offset,
const char *content) { const char* content)
{
auto lf = this->lf_collection.fc_files[rowid]; auto lf = this->lf_collection.fc_files[rowid];
struct timeval tv = { struct timeval tv = {
(int) (time_offset / 1000LL), (int) (time_offset / 1000LL),
@ -197,7 +197,8 @@ CREATE TABLE lnav_file (
"real file paths cannot be updated, only symbolic ones"); "real file paths cannot be updated, only symbolic ones");
} }
auto iter = this->lf_collection.fc_file_names.find(lf->get_filename()); auto iter
= this->lf_collection.fc_file_names.find(lf->get_filename());
if (iter != this->lf_collection.fc_file_names.end()) { if (iter != this->lf_collection.fc_file_names.end()) {
auto loo = std::move(iter->second); auto loo = std::move(iter->second);
@ -216,7 +217,7 @@ CREATE TABLE lnav_file (
return SQLITE_OK; return SQLITE_OK;
}; };
file_collection &lf_collection; file_collection& lf_collection;
}; };
struct injectable_lnav_file : vtab_module<lnav_file> { struct injectable_lnav_file : vtab_module<lnav_file> {
@ -224,5 +225,5 @@ struct injectable_lnav_file : vtab_module<lnav_file> {
using injectable = injectable_lnav_file(file_collection&); using injectable = injectable_lnav_file(file_collection&);
}; };
static auto file_binder = injector::bind_multiple<vtab_module_base>() static auto file_binder
.add<injectable_lnav_file>(); = injector::bind_multiple<vtab_module_base>().add<injectable_lnav_file>();

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
@ -38,6 +38,6 @@ struct config {
int64_t fvc_max_content_size{32 * 1024 * 1024}; int64_t fvc_max_content_size{32 * 1024 * 1024};
}; };
} } // namespace file_vtab
#endif #endif

@ -21,27 +21,27 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h" #include "files_sub_source.hh"
#include "base/humanize.hh" #include "base/humanize.hh"
#include "base/humanize.network.hh" #include "base/humanize.network.hh"
#include "base/opt_util.hh" #include "base/opt_util.hh"
#include "base/string_util.hh" #include "base/string_util.hh"
#include "mapbox/variant.hpp" #include "config.h"
#include "lnav.hh" #include "lnav.hh"
#include "files_sub_source.hh" #include "mapbox/variant.hpp"
namespace files_model { namespace files_model {
files_list_selection from_selection(vis_line_t sel_vis) files_list_selection
from_selection(vis_line_t sel_vis)
{ {
auto &fc = lnav_data.ld_active_files; auto& fc = lnav_data.ld_active_files;
int sel = (int) sel_vis; int sel = (int) sel_vis;
if (sel < fc.fc_name_to_errors.size()) { if (sel < fc.fc_name_to_errors.size()) {
@ -71,52 +71,49 @@ files_list_selection from_selection(vis_line_t sel_vis)
return no_selection{}; return no_selection{};
} }
} } // namespace files_model
files_sub_source::files_sub_source() files_sub_source::files_sub_source() {}
{
} bool
files_sub_source::list_input_handle_key(listview_curses& lv, int ch)
bool files_sub_source::list_input_handle_key(listview_curses &lv, int ch)
{ {
switch (ch) { switch (ch) {
case KEY_ENTER: case KEY_ENTER:
case '\r': { case '\r': {
auto sel = files_model::from_selection(lv.get_selection()); auto sel = files_model::from_selection(lv.get_selection());
sel.match( sel.match([](files_model::no_selection) {},
[](files_model::no_selection) {}, [](files_model::error_selection) {},
[](files_model::error_selection) {}, [](files_model::other_selection) {},
[](files_model::other_selection) {}, [&](files_model::file_selection& fs) {
[&](files_model::file_selection& fs) { auto& lss = lnav_data.ld_log_source;
auto& lss = lnav_data.ld_log_source; auto lf = *fs.sb_iter;
auto lf = *fs.sb_iter;
lss.find_data(lf) | [](auto ld) {
lss.find_data(lf) | [](auto ld) { ld->set_visibility(true);
ld->set_visibility(true); lnav_data.ld_log_source.text_filters_changed();
lnav_data.ld_log_source.text_filters_changed(); };
};
if (lf->get_format() != nullptr) {
if (lf->get_format() != nullptr) { auto& log_view = lnav_data.ld_views[LNV_LOG];
auto& log_view = lnav_data.ld_views[LNV_LOG]; lss.row_for_time(lf->front().get_timeval()) |
lss.row_for_time(lf->front().get_timeval()) | [](auto row) { [](auto row) {
lnav_data.ld_views[LNV_LOG].set_top(row); lnav_data.ld_views[LNV_LOG].set_top(row);
}; };
ensure_view(&log_view); ensure_view(&log_view);
} else { } else {
auto& tv = lnav_data.ld_views[LNV_TEXT]; auto& tv = lnav_data.ld_views[LNV_TEXT];
auto& tss = lnav_data.ld_text_source; auto& tss = lnav_data.ld_text_source;
tss.to_front(lf); tss.to_front(lf);
tv.reload_data(); tv.reload_data();
ensure_view(&tv); ensure_view(&tv);
} }
lv.reload_data(); lv.reload_data();
lnav_data.ld_mode = LNM_PAGING; lnav_data.ld_mode = LNM_PAGING;
} });
);
return true; return true;
} }
@ -124,29 +121,27 @@ bool files_sub_source::list_input_handle_key(listview_curses &lv, int ch)
case ' ': { case ' ': {
auto sel = files_model::from_selection(lv.get_selection()); auto sel = files_model::from_selection(lv.get_selection());
sel.match( sel.match([](files_model::no_selection) {},
[](files_model::no_selection) {}, [](files_model::error_selection) {},
[](files_model::error_selection) {}, [](files_model::other_selection) {},
[](files_model::other_selection) {}, [&](files_model::file_selection& fs) {
[&](files_model::file_selection& fs) { auto& lss = lnav_data.ld_log_source;
auto& lss = lnav_data.ld_log_source; auto lf = *fs.sb_iter;
auto lf = *fs.sb_iter;
lss.find_data(lf) | [](auto ld) {
lss.find_data(lf) | [](auto ld) { ld->set_visibility(!ld->ld_visible);
ld->set_visibility(!ld->ld_visible); };
};
auto top_view = *lnav_data.ld_view_stack.top();
auto top_view = *lnav_data.ld_view_stack.top(); auto tss = top_view->get_sub_source();
auto tss = top_view->get_sub_source();
if (tss != nullptr) {
if (tss != nullptr) { tss->text_filters_changed();
tss->text_filters_changed(); top_view->reload_data();
top_view->reload_data(); }
}
lv.reload_data();
lv.reload_data(); });
}
);
return true; return true;
} }
case 'n': { case 'n': {
@ -158,83 +153,87 @@ bool files_sub_source::list_input_handle_key(listview_curses &lv, int ch)
return true; return true;
} }
case '/': { case '/': {
execute_command(lnav_data.ld_exec_context, execute_command(lnav_data.ld_exec_context, "prompt search-files");
"prompt search-files");
return true; return true;
} }
case 'X': { case 'X': {
auto sel = files_model::from_selection(lv.get_selection()); auto sel = files_model::from_selection(lv.get_selection());
sel.match( sel.match([](files_model::no_selection) {},
[](files_model::no_selection) {}, [&](files_model::error_selection& es) {
[&](files_model::error_selection& es) { auto& fc = lnav_data.ld_active_files;
auto &fc = lnav_data.ld_active_files;
fc.fc_file_names.erase(es.sb_iter->first);
fc.fc_file_names.erase(es.sb_iter->first);
auto name_iter = fc.fc_file_names.begin();
auto name_iter = fc.fc_file_names.begin(); while (name_iter != fc.fc_file_names.end()) {
while (name_iter != fc.fc_file_names.end()) { if (name_iter->first == es.sb_iter->first) {
if (name_iter->first == es.sb_iter->first) { name_iter = fc.fc_file_names.erase(name_iter);
name_iter = fc.fc_file_names.erase(name_iter); continue;
continue; }
}
auto rp_opt = humanize::network::path::from_str(
auto rp_opt = humanize::network::path::from_str(name_iter->first); name_iter->first);
if (rp_opt) { if (rp_opt) {
auto rp = *rp_opt; auto rp = *rp_opt;
if (fmt::format("{}", rp.home()) == es.sb_iter->first) { if (fmt::format("{}", rp.home())
fc.fc_other_files.erase(name_iter->first); == es.sb_iter->first) {
name_iter = fc.fc_file_names.erase(name_iter); fc.fc_other_files.erase(name_iter->first);
continue; name_iter
} = fc.fc_file_names.erase(name_iter);
} continue;
++name_iter; }
} }
++name_iter;
fc.fc_name_to_errors.erase(es.sb_iter); }
fc.fc_invalidate_merge = true;
lv.reload_data(); fc.fc_name_to_errors.erase(es.sb_iter);
}, fc.fc_invalidate_merge = true;
[](files_model::other_selection) {}, lv.reload_data();
[](files_model::file_selection) {} },
); [](files_model::other_selection) {},
[](files_model::file_selection) {});
return true; return true;
} }
} }
return false; return false;
} }
void files_sub_source::list_input_handle_scroll_out(listview_curses &lv) void
files_sub_source::list_input_handle_scroll_out(listview_curses& lv)
{ {
lnav_data.ld_mode = LNM_PAGING; lnav_data.ld_mode = LNM_PAGING;
lnav_data.ld_filter_view.reload_data(); lnav_data.ld_filter_view.reload_data();
} }
size_t files_sub_source::text_line_count() size_t
files_sub_source::text_line_count()
{ {
const auto &fc = lnav_data.ld_active_files; const auto& fc = lnav_data.ld_active_files;
return fc.fc_name_to_errors.size() + return fc.fc_name_to_errors.size() + fc.fc_other_files.size()
fc.fc_other_files.size() + + fc.fc_files.size();
fc.fc_files.size();
} }
size_t files_sub_source::text_line_width(textview_curses &curses) size_t
files_sub_source::text_line_width(textview_curses& curses)
{ {
return 512; return 512;
} }
void files_sub_source::text_value_for_line(textview_curses &tc, int line, void
std::string &value_out, files_sub_source::text_value_for_line(textview_curses& tc,
text_sub_source::line_flags_t flags) int line,
std::string& value_out,
text_sub_source::line_flags_t flags)
{ {
const auto dim = tc.get_dimensions(); const auto dim = tc.get_dimensions();
const auto &fc = lnav_data.ld_active_files; const auto& fc = lnav_data.ld_active_files;
auto filename_width = auto filename_width
std::min(fc.fc_largest_path_length, = std::min(fc.fc_largest_path_length,
std::max((size_t) 40, (size_t) dim.second - 30)); std::max((size_t) 40, (size_t) dim.second - 30));
if (line < fc.fc_name_to_errors.size()) { if (line < fc.fc_name_to_errors.size()) {
auto iter = fc.fc_name_to_errors.begin(); auto iter = fc.fc_name_to_errors.begin();
@ -243,9 +242,10 @@ void files_sub_source::text_value_for_line(textview_curses &tc, int line,
auto fn = path.filename().string(); auto fn = path.filename().string();
truncate_to(fn, filename_width); truncate_to(fn, filename_width);
value_out = fmt::format( value_out = fmt::format(FMT_STRING(" {:<{}} {}"),
FMT_STRING(" {:<{}} {}"), fn,
fn, filename_width, iter->second.fei_description); filename_width,
iter->second.fei_description);
return; return;
} }
@ -258,16 +258,17 @@ void files_sub_source::text_value_for_line(textview_curses &tc, int line,
auto fn = path.string(); auto fn = path.string();
truncate_to(fn, filename_width); truncate_to(fn, filename_width);
value_out = fmt::format( value_out = fmt::format(FMT_STRING(" {:<{}} {:14} {}"),
FMT_STRING(" {:<{}} {:14} {}"), fn,
fn, filename_width, iter->second.ofd_format, filename_width,
iter->second.ofd_description); iter->second.ofd_format,
iter->second.ofd_description);
return; return;
} }
line -= fc.fc_other_files.size(); line -= fc.fc_other_files.size();
const auto &lf = fc.fc_files[line]; const auto& lf = fc.fc_files[line];
auto fn = lf->get_unique_path(); auto fn = lf->get_unique_path();
char start_time[64] = "", end_time[64] = ""; char start_time[64] = "", end_time[64] = "";
std::vector<std::string> file_notes; std::vector<std::string> file_notes;
@ -280,31 +281,34 @@ void files_sub_source::text_value_for_line(textview_curses &tc, int line,
for (const auto& pair : lf->get_notes()) { for (const auto& pair : lf->get_notes()) {
file_notes.push_back(pair.second); file_notes.push_back(pair.second);
} }
value_out = fmt::format( value_out = fmt::format(FMT_STRING(" {:<{}} {:>8} {} \u2014 {} {}"),
FMT_STRING(" {:<{}} {:>8} {} \u2014 {} {}"), fn,
fn, filename_width,
filename_width, humanize::file_size(lf->get_index_size()),
humanize::file_size(lf->get_index_size()), start_time,
start_time, end_time,
end_time, fmt::join(file_notes, "; "));
fmt::join(file_notes, "; ")); this->fss_last_line_len
this->fss_last_line_len = = filename_width + 23 + strlen(start_time) + strlen(end_time);
filename_width + 23 + strlen(start_time) + strlen(end_time);
} }
void files_sub_source::text_attrs_for_line(textview_curses &tc, int line, void
string_attrs_t &value_out) files_sub_source::text_attrs_for_line(textview_curses& tc,
int line,
string_attrs_t& value_out)
{ {
bool selected = lnav_data.ld_mode == LNM_FILES && line == tc.get_selection(); bool selected
const auto &fc = lnav_data.ld_active_files; = lnav_data.ld_mode == LNM_FILES && line == tc.get_selection();
auto &vcolors = view_colors::singleton(); const auto& fc = lnav_data.ld_active_files;
auto& vcolors = view_colors::singleton();
const auto dim = tc.get_dimensions(); const auto dim = tc.get_dimensions();
auto filename_width = auto filename_width
std::min(fc.fc_largest_path_length, = std::min(fc.fc_largest_path_length,
std::max((size_t) 40, (size_t) dim.second - 30)); std::max((size_t) 40, (size_t) dim.second - 30));
if (selected) { if (selected) {
value_out.emplace_back(line_range{0, 1}, &view_curses::VC_GRAPHIC, ACS_RARROW); value_out.emplace_back(
line_range{0, 1}, &view_curses::VC_GRAPHIC, ACS_RARROW);
} }
if (line < fc.fc_name_to_errors.size()) { if (line < fc.fc_name_to_errors.size()) {
@ -328,9 +332,8 @@ void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
view_colors::VCR_DISABLED_FOCUSED); view_colors::VCR_DISABLED_FOCUSED);
} }
if (line == fc.fc_other_files.size() - 1) { if (line == fc.fc_other_files.size() - 1) {
value_out.emplace_back(line_range{0, -1}, value_out.emplace_back(
&view_curses::VC_STYLE, line_range{0, -1}, &view_curses::VC_STYLE, A_UNDERLINE);
A_UNDERLINE);
} }
return; return;
} }
@ -338,13 +341,12 @@ void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
line -= fc.fc_other_files.size(); line -= fc.fc_other_files.size();
if (selected) { if (selected) {
value_out.emplace_back(line_range{0, -1}, value_out.emplace_back(
&view_curses::VC_ROLE, line_range{0, -1}, &view_curses::VC_ROLE, view_colors::VCR_FOCUSED);
view_colors::VCR_FOCUSED);
} }
auto& lss = lnav_data.ld_log_source; auto& lss = lnav_data.ld_log_source;
auto &lf = fc.fc_files[line]; auto& lf = fc.fc_files[line];
auto ld_opt = lss.find_data(lf); auto ld_opt = lss.find_data(lf);
chtype visible = ACS_DIAMOND; chtype visible = ACS_DIAMOND;
@ -353,7 +355,8 @@ void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
} }
value_out.emplace_back(line_range{2, 3}, &view_curses::VC_GRAPHIC, visible); value_out.emplace_back(line_range{2, 3}, &view_curses::VC_GRAPHIC, visible);
if (visible == ACS_DIAMOND) { if (visible == ACS_DIAMOND) {
value_out.emplace_back(line_range{2, 3}, &view_curses::VC_FOREGROUND, value_out.emplace_back(line_range{2, 3},
&view_curses::VC_FOREGROUND,
vcolors.ansi_to_theme_color(COLOR_GREEN)); vcolors.ansi_to_theme_color(COLOR_GREEN));
} }
@ -365,45 +368,51 @@ void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
lr.lr_start = this->fss_last_line_len; lr.lr_start = this->fss_last_line_len;
lr.lr_end = -1; lr.lr_end = -1;
value_out.emplace_back(lr, &view_curses::VC_FOREGROUND, value_out.emplace_back(lr,
&view_curses::VC_FOREGROUND,
vcolors.ansi_to_theme_color(COLOR_YELLOW)); vcolors.ansi_to_theme_color(COLOR_YELLOW));
} }
size_t files_sub_source::text_size_for_line(textview_curses &tc, int line, size_t
text_sub_source::line_flags_t raw) files_sub_source::text_size_for_line(textview_curses& tc,
int line,
text_sub_source::line_flags_t raw)
{ {
return 0; return 0;
} }
static static auto
auto spinner_index() spinner_index()
{ {
auto now = ui_clock::now(); auto now = ui_clock::now();
return std::chrono::duration_cast<std::chrono::milliseconds>( return std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch()).count() / 100; now.time_since_epoch())
.count()
/ 100;
} }
bool bool
files_overlay_source::list_value_for_overlay(const listview_curses &lv, int y, files_overlay_source::list_value_for_overlay(const listview_curses& lv,
int bottom, vis_line_t line, int y,
attr_line_t &value_out) int bottom,
vis_line_t line,
attr_line_t& value_out)
{ {
if (y == 0) { if (y == 0) {
static const char PROG[] = "-\\|/"; static const char PROG[] = "-\\|/";
constexpr size_t PROG_SIZE = sizeof(PROG) - 1; constexpr size_t PROG_SIZE = sizeof(PROG) - 1;
auto &fc = lnav_data.ld_active_files; auto& fc = lnav_data.ld_active_files;
auto &fc_prog = fc.fc_progress; auto& fc_prog = fc.fc_progress;
safe::WriteAccess<safe_scan_progress> sp(*fc_prog); safe::WriteAccess<safe_scan_progress> sp(*fc_prog);
if (!sp->sp_extractions.empty()) { if (!sp->sp_extractions.empty()) {
const auto& prog = sp->sp_extractions.front(); const auto& prog = sp->sp_extractions.front();
value_out.with_ansi_string(fmt::format( value_out.with_ansi_string(fmt::format(
"{} Extracting " "{} Extracting " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM
ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM "... {:>8}/{}",
"... {:>8}/{}",
PROG[spinner_index() % PROG_SIZE], PROG[spinner_index() % PROG_SIZE],
prog.ep_path.filename().string(), prog.ep_path.filename().string(),
humanize::file_size(prog.ep_out_size), humanize::file_size(prog.ep_out_size),
@ -414,9 +423,8 @@ files_overlay_source::list_value_for_overlay(const listview_curses &lv, int y,
auto first_iter = sp->sp_tailers.begin(); auto first_iter = sp->sp_tailers.begin();
value_out.with_ansi_string(fmt::format( value_out.with_ansi_string(fmt::format(
"{} Connecting to " "{} Connecting to " ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM
ANSI_COLOR(COLOR_CYAN) "{}" ANSI_NORM ": {}",
": {}",
PROG[spinner_index() % PROG_SIZE], PROG[spinner_index() % PROG_SIZE],
first_iter->first, first_iter->first,
first_iter->second.tp_message)); first_iter->second.tp_message));

@ -21,8 +21,8 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
@ -30,31 +30,35 @@
#ifndef files_sub_source_hh #ifndef files_sub_source_hh
#define files_sub_source_hh #define files_sub_source_hh
#include "textview_curses.hh"
#include "file_collection.hh" #include "file_collection.hh"
#include "textview_curses.hh"
class files_sub_source class files_sub_source
: public text_sub_source, public list_input_delegate { : public text_sub_source
, public list_input_delegate {
public: public:
files_sub_source(); files_sub_source();
bool list_input_handle_key(listview_curses &lv, int ch) override; bool list_input_handle_key(listview_curses& lv, int ch) override;
void list_input_handle_scroll_out(listview_curses &lv) override; void list_input_handle_scroll_out(listview_curses& lv) override;
size_t text_line_count() override; size_t text_line_count() override;
size_t text_line_width(textview_curses &curses) override; size_t text_line_width(textview_curses& curses) override;
void void text_value_for_line(textview_curses& tc,
text_value_for_line(textview_curses &tc, int line, std::string &value_out, int line,
line_flags_t flags) override; std::string& value_out,
line_flags_t flags) override;
void text_attrs_for_line(textview_curses &tc, int line, void text_attrs_for_line(textview_curses& tc,
string_attrs_t &value_out) override; int line,
string_attrs_t& value_out) override;
size_t size_t text_size_for_line(textview_curses& tc,
text_size_for_line(textview_curses &tc, int line, line_flags_t raw) override; int line,
line_flags_t raw) override;
bool fss_editing{false}; bool fss_editing{false};
bool fss_filter_state{false}; bool fss_filter_state{false};
@ -62,21 +66,25 @@ public:
}; };
struct files_overlay_source : public list_overlay_source { struct files_overlay_source : public list_overlay_source {
bool list_value_for_overlay(const listview_curses &lv, int y, int bottom, bool list_value_for_overlay(const listview_curses& lv,
int y,
int bottom,
vis_line_t line, vis_line_t line,
attr_line_t &value_out) override; attr_line_t& value_out) override;
}; };
namespace files_model { namespace files_model {
struct no_selection {}; struct no_selection {
};
template<typename C, typename T> template<typename C, typename T>
struct selection_base { struct selection_base {
int sb_index{0}; int sb_index{0};
T sb_iter; T sb_iter;
static C build(int index, T iter) { static C build(int index, T iter)
{
C retval; C retval;
retval.sb_index = index; retval.sb_index = index;
@ -86,22 +94,26 @@ struct selection_base {
}; };
struct error_selection struct error_selection
: public selection_base<error_selection, std::map<std::string, file_error_info>::iterator> { : public selection_base<error_selection,
std::map<std::string, file_error_info>::iterator> {
}; };
struct other_selection struct other_selection
: public selection_base<other_selection, std::map<std::string, other_file_descriptor>::iterator> { : public selection_base<
other_selection,
std::map<std::string, other_file_descriptor>::iterator> {
}; };
struct file_selection struct file_selection
: public selection_base<file_selection, std::vector<std::shared_ptr<logfile>>::iterator> { : public selection_base<file_selection,
std::vector<std::shared_ptr<logfile>>::iterator> {
}; };
using files_list_selection = mapbox::util::variant< using files_list_selection = mapbox::util::
no_selection, error_selection, other_selection, file_selection>; variant<no_selection, error_selection, other_selection, file_selection>;
files_list_selection from_selection(vis_line_t sel_vis); files_list_selection from_selection(vis_line_t sel_vis);
} } // namespace files_model
#endif #endif

@ -21,22 +21,22 @@
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "config.h" #include "filter_observer.hh"
#include "config.h"
#include "log_format.hh" #include "log_format.hh"
#include "filter_observer.hh" void
line_filter_observer::logline_new_lines(const logfile& lf,
void line_filter_observer::logline_new_lines(const logfile &lf, logfile::const_iterator ll_begin,
logfile::const_iterator ll_begin, logfile::const_iterator ll_end,
logfile::const_iterator ll_end, shared_buffer_ref& sbr)
shared_buffer_ref &sbr)
{ {
size_t offset = std::distance(lf.begin(), ll_begin); size_t offset = std::distance(lf.begin(), ll_begin);
@ -51,21 +51,23 @@ void line_filter_observer::logline_new_lines(const logfile &lf,
if (lf.get_format() != nullptr) { if (lf.get_format() != nullptr) {
lf.get_format()->get_subline(*ll_begin, sbr); lf.get_format()->get_subline(*ll_begin, sbr);
} }
for (auto &filter : this->lfo_filter_stack) { for (auto& filter : this->lfo_filter_stack) {
if (filter->lf_deleted) { if (filter->lf_deleted) {
continue; continue;
} }
if (offset >= if (offset
this->lfo_filter_state.tfs_filter_count[filter->get_index()]) { >= this->lfo_filter_state.tfs_filter_count[filter->get_index()])
{
filter->add_line(this->lfo_filter_state, ll_begin, sbr); filter->add_line(this->lfo_filter_state, ll_begin, sbr);
} }
} }
} }
} }
void line_filter_observer::logline_eof(const logfile &lf) void
line_filter_observer::logline_eof(const logfile& lf)
{ {
for (auto &iter : this->lfo_filter_stack) { for (auto& iter : this->lfo_filter_stack) {
if (iter->lf_deleted) { if (iter->lf_deleted) {
continue; continue;
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save