diff --git a/CMakeLists.txt b/CMakeLists.txt index 5525b9c2..d7db7f5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,8 +11,8 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O3") include("cmake/HunterGate.cmake") HunterGate( - URL "https://github.com/cpp-pm/hunter/archive/v0.23.269.tar.gz" - SHA1 "64024b7b95b4c86d50ae05b926814448c93a70a0" + URL "https://github.com/cpp-pm/hunter/archive/v0.23.291.tar.gz" + SHA1 "4f57243b24a02b9c7ec5ea02c09d5c77c6a97cba" LOCAL ) @@ -20,12 +20,14 @@ set(CMAKE_CXX_STANDARD 14) project(lnav VERSION 0.9.1) +# include(cmake/CodeCoverage.cmake) +# append_coverage_compiler_flags() + include(CTest) include(GNUInstallDirs) hunter_add_package(libpcre) find_package(libpcre CONFIG REQUIRED) -find_package(libpcrecpp CONFIG REQUIRED) hunter_add_package(BZip2) find_package(BZip2 CONFIG REQUIRED) @@ -45,16 +47,19 @@ find_package(ncursesw CONFIG REQUIRED) hunter_add_package(readline) find_package(readline REQUIRED) +hunter_add_package(libarchive) +find_package(libarchive CONFIG REQUIRED) + set(lnav_LIBS ZLIB::zlib BZip2::bz2 sqlite3::sqlite3 - PkgConfig::libpcrecpp PkgConfig::libpcre CURL::libcurl readline::readline readline::history - PkgConfig::ncursesw) + PkgConfig::ncursesw + PkgConfig::libarchive) add_subdirectory(src) add_subdirectory(test) diff --git a/README.md b/README.md index afb92765..4ab9af4b 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Alternatively, you can get the generated binary from [AppVeyor](https://ci.appve Remember that you still need the lnav dependencies under Cygwin, here is a quick way to do it: - setup-x86_64.exe -q -P libpcre1 -P libpcrecpp0 -P libsqlite3_0 -P libstdc++6 + setup-x86_64.exe -q -P libpcre1 -P libsqlite3_0 -P libstdc++6 Currently, the x64 version seems to be working better than the x86 one. diff --git a/aminclude_static.am b/aminclude_static.am new file mode 100644 index 00000000..9462dbcc --- /dev/null +++ b/aminclude_static.am @@ -0,0 +1,126 @@ + +# aminclude_static.am generated automatically by Autoconf +# from AX_AM_MACROS_STATIC on Thu Jan 28 11:06:02 PST 2021 + + +# Code coverage +# +# Optional: +# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. +# Multiple directories may be specified, separated by whitespace. +# (Default: $(top_builddir)) +# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated +# by lcov for code coverage. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info) +# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage +# reports to be created. (Default: +# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage) +# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, +# set to 0 to disable it and leave empty to stay with the default. +# (Default: empty) +# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov +# instances. (Default: based on ) +# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov +# instances. (Default: ) +# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov +# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the +# collecting lcov instance. (Default: ) +# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov +# instance. (Default: ) +# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering +# lcov instance. (Default: empty) +# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov +# instance. (Default: ) +# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the +# genhtml instance. (Default: based on ) +# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml +# instance. (Default: ) +# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore +# +# The generated report will be titled using the $(PACKAGE_NAME) and +# $(PACKAGE_VERSION). In order to add the current git hash to the title, +# use the git-version-gen script, available online. +# Optional variables +# run only on top dir +if CODE_COVERAGE_ENABLED + ifeq ($(abs_builddir), $(abs_top_builddir)) +CODE_COVERAGE_DIRECTORY ?= $(top_builddir) +CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info +CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage + +CODE_COVERAGE_BRANCH_COVERAGE ?= +CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),--rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)" +CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= +CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=$(if $(CODE_COVERAGE_BRANCH_COVERAGE),--rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +CODE_COVERAGE_IGNORE_PATTERN ?= + +GITIGNOREFILES := $(GITIGNOREFILES) $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY) +code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V)) +code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_cap_0 = @echo " LCOV --capture" $(CODE_COVERAGE_OUTPUT_FILE); +code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V)) +code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN); +code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V)) +code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_genhtml_0 = @echo " GEN " "$(CODE_COVERAGE_OUTPUT_DIRECTORY)"; +code_coverage_quiet = $(code_coverage_quiet_$(V)) +code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY)) +code_coverage_quiet_0 = --quiet + +# sanitizes the test-name: replaces with underscores: dashes and dots +code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1))) + +# Use recursive makes in order to ignore errors during check +check-code-coverage: + -$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check + $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture + +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) + $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS) + -@rm -f "$(CODE_COVERAGE_OUTPUT_FILE).tmp" + $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS) + @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html" + +code-coverage-clean: + -$(LCOV) --directory $(top_builddir) -z + -rm -rf "$(CODE_COVERAGE_OUTPUT_FILE)" "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" + -find . \( -name "*.gcda" -o -name "*.gcno" -o -name "*.gcov" \) -delete + +code-coverage-dist-clean: + +AM_DISTCHECK_CONFIGURE_FLAGS := $(AM_DISTCHECK_CONFIGURE_FLAGS) --disable-code-coverage + else # ifneq ($(abs_builddir), $(abs_top_builddir)) +check-code-coverage: + +code-coverage-capture: code-coverage-capture-hook + +code-coverage-clean: + +code-coverage-dist-clean: + endif # ifeq ($(abs_builddir), $(abs_top_builddir)) +else #! CODE_COVERAGE_ENABLED +# Use recursive makes in order to ignore errors during check +check-code-coverage: + @echo "Need to reconfigure with --enable-code-coverage" +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + @echo "Need to reconfigure with --enable-code-coverage" + +code-coverage-clean: + +code-coverage-dist-clean: + +endif #CODE_COVERAGE_ENABLED +# Hook rule executed before code-coverage-capture, overridable by the user +code-coverage-capture-hook: + +.PHONY: check-code-coverage code-coverage-capture code-coverage-dist-clean code-coverage-clean code-coverage-capture-hook diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake new file mode 100644 index 00000000..2f54ee01 --- /dev/null +++ b/cmake/CodeCoverage.cmake @@ -0,0 +1,438 @@ +# Copyright (c) 2012 - 2017, Lars Bilke +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# CHANGES: +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# 2016-02-03, Lars Bilke +# - Refactored functions to use named parameters +# +# 2017-06-02, Lars Bilke +# - Merged with modified version from github.com/ufz/ogs +# +# 2019-05-06, Anatolii Kurotych +# - Remove unnecessary --coverage flag +# +# 2019-12-13, FeRD (Frank Dana) +# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor +# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. +# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY +# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list +# - Set lcov basedir with -b argument +# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be +# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) +# - Delete output dir, .info file on 'make clean' +# - Remove Python detection, since version mismatches will break gcovr +# - Minor cleanup (lowercase function names, update examples...) +# +# 2019-12-19, FeRD (Frank Dana) +# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets +# +# 2020-01-19, Bob Apthorpe +# - Added gfortran support +# +# 2020-02-17, FeRD (Frank Dana) +# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters +# in EXCLUDEs, and remove manual escaping from gcovr targets +# +# USAGE: +# +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt (best inside an if-condition +# using a CMake option() to enable it just optionally): +# include(CodeCoverage) +# +# 3. Append necessary compiler flags: +# append_coverage_compiler_flags() +# +# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og +# +# 4. If you need to exclude additional directories from the report, specify them +# using full paths in the COVERAGE_EXCLUDES variable before calling +# setup_target_for_coverage_*(). +# Example: +# set(COVERAGE_EXCLUDES +# '${PROJECT_SOURCE_DIR}/src/dir1/*' +# '/path/to/my/src/dir2/*') +# Or, use the EXCLUDE argument to setup_target_for_coverage_*(). +# Example: +# setup_target_for_coverage_lcov( +# NAME coverage +# EXECUTABLE testrunner +# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") +# +# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set +# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) +# Example: +# set(COVERAGE_EXCLUDES "dir1/*") +# setup_target_for_coverage_gcovr_html( +# NAME coverage +# EXECUTABLE testrunner +# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" +# EXCLUDE "dir2/*") +# +# 5. Use the functions described below to create a custom make target which +# runs your test executable and produces a code coverage report. +# +# 6. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# + +include(CMakeParseArguments) + +# Check prereqs +find_program( GCOV_PATH gcov ) +find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) +find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) +find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) +find_program( CPPFILT_PATH NAMES c++filt ) + +if(NOT GCOV_PATH) + message(FATAL_ERROR "gcov not found! Aborting...") +endif() # NOT GCOV_PATH + +message("foo ${CMAKE_CXX_COMPILER_ID}") + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + endif() +elseif(NOT CMAKE_COMPILER_IS_GNUCXX) + if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang") + # Do nothing; exit conditional without error if true + elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") + # Do nothing; exit conditional without error if true + else() + message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...") + endif() +endif() + +set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage" + CACHE INTERNAL "") + +set(CMAKE_Fortran_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the Fortran compiler during coverage builds." + FORCE ) +set(CMAKE_CXX_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE ) +set(CMAKE_C_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C compiler during coverage builds." + FORCE ) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE ) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE ) +mark_as_advanced( + CMAKE_Fortran_FLAGS_COVERAGE + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) + +if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") +endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug" + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + link_libraries(gcov) +endif() + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_lcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# ) +function(setup_target_for_coverage_lcov) + + set(options NO_DEMANGLE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() # NOT LCOV_PATH + + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() # NOT GENHTML_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(${Coverage_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(LCOV_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND LCOV_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES LCOV_EXCLUDES) + + # Conditional arguments + if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif() + + # Setup target + add_custom_target(${Coverage_NAME} + + # Cleanup lcov + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . -b ${BASEDIR} --zerocounters + # Create baseline to make sure untouched files show up in the report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b ${BASEDIR} -o ${Coverage_NAME}.base + + # Run tests + COMMAND ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture + # add baseline counters + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total + # filter collected data to final coverage report + COMMAND ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info + + # Generate HTML output + COMMAND ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o ${Coverage_NAME} ${Coverage_NAME}.info + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.base + ${Coverage_NAME}.capture + ${Coverage_NAME}.total + ${Coverage_NAME}.info + ${Coverage_NAME} # report directory + + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show where to find the lcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_lcov + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_xml( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +function(setup_target_for_coverage_gcovr_xml) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(${Coverage_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + add_custom_target(${Coverage_NAME} + # Run tests + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + + # Running gcovr + COMMAND ${GCOVR_PATH} --xml + -r ${BASEDIR} ${GCOVR_EXCLUDE_ARGS} + --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}.xml + BYPRODUCTS ${Coverage_NAME}.xml + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." + ) +endfunction() # setup_target_for_coverage_gcovr_xml + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_html( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +function(setup_target_for_coverage_gcovr_html) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif() # NOT GCOVR_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if(${Coverage_BASE_DIRECTORY}) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else() + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif() + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if(CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif() + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach() + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach(EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach() + + add_custom_target(${Coverage_NAME} + # Run tests + ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS} + + # Create folder + COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} + + # Running gcovr + COMMAND ${GCOVR_PATH} --html --html-details + -r ${BASEDIR} ${GCOVR_EXCLUDE_ARGS} + --object-directory=${PROJECT_BINARY_DIR} + -o ${Coverage_NAME}/index.html + + BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME} # report directory + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce HTML code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_gcovr_html + +function(append_coverage_compiler_flags) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") +endfunction() # append_coverage_compiler_flags diff --git a/configure.ac b/configure.ac index 3eab5cf5..bc238961 100644 --- a/configure.ac +++ b/configure.ac @@ -168,6 +168,8 @@ AX_PATH_LIB_ARCHIVE AX_PATH_LIB_PCRE([], [AC_MSG_ERROR([pcre required to build])]) AX_PATH_LIB_READLINE +AX_CODE_COVERAGE + LNAV_WITH_SQLITE3("3.9.0") dnl case "$host_os" in @@ -185,7 +187,7 @@ AS_VAR_SET(ALL_LDFLAGS, "$SQLITE3_LDFLAGS $READLINE_LDFLAGS $LDFLAGS") AS_VAR_SET(static_lib_list, ["libncurses.a libncursesw.a libreadline.a libsqlite3.a libz.a libtinfo.a libtinfow.a"]) AS_VAR_SET(static_lib_list, - ["$static_lib_list libpcre.a libpcrecpp.a libncursesw.a libbz2.a"]) + ["$static_lib_list libpcre.a libncursesw.a libbz2.a"]) AS_VAR_SET(static_lib_list, ["$static_lib_list libgpm.a libcurl.a libcrypto.a libssl.a libssh2.a"]) AS_VAR_SET(static_lib_list, diff --git a/m4/ax_ac_append_to_file.m4 b/m4/ax_ac_append_to_file.m4 new file mode 100644 index 00000000..242b3d52 --- /dev/null +++ b/m4/ax_ac_append_to_file.m4 @@ -0,0 +1,32 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_ac_append_to_file.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_AC_APPEND_TO_FILE([FILE],[DATA]) +# +# DESCRIPTION +# +# Appends the specified data to the specified Autoconf is run. If you want +# to append to a file when configure is run use AX_APPEND_TO_FILE instead. +# +# LICENSE +# +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AC_DEFUN([AX_AC_APPEND_TO_FILE],[ +AC_REQUIRE([AX_FILE_ESCAPES]) +m4_esyscmd( +AX_FILE_ESCAPES +[ +printf "%s" "$2" >> "$1" +]) +]) diff --git a/m4/ax_ac_print_to_file.m4 b/m4/ax_ac_print_to_file.m4 new file mode 100644 index 00000000..642dfc15 --- /dev/null +++ b/m4/ax_ac_print_to_file.m4 @@ -0,0 +1,32 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_ac_print_to_file.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_AC_PRINT_TO_FILE([FILE],[DATA]) +# +# DESCRIPTION +# +# Writes the specified data to the specified file when Autoconf is run. If +# you want to print to a file when configure is run use AX_PRINT_TO_FILE +# instead. +# +# LICENSE +# +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AC_DEFUN([AX_AC_PRINT_TO_FILE],[ +m4_esyscmd( +AC_REQUIRE([AX_FILE_ESCAPES]) +[ +printf "%s" "$2" > "$1" +]) +]) diff --git a/m4/ax_add_am_macro_static.m4 b/m4/ax_add_am_macro_static.m4 new file mode 100644 index 00000000..6442d24b --- /dev/null +++ b/m4/ax_add_am_macro_static.m4 @@ -0,0 +1,28 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_add_am_macro_static.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_ADD_AM_MACRO_STATIC([RULE]) +# +# DESCRIPTION +# +# Adds the specified rule to $AMINCLUDE. +# +# LICENSE +# +# Copyright (c) 2009 Tom Howard +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AC_DEFUN([AX_ADD_AM_MACRO_STATIC],[ + AC_REQUIRE([AX_AM_MACROS_STATIC]) + AX_AC_APPEND_TO_FILE(AMINCLUDE_STATIC,[$1]) +]) diff --git a/m4/ax_am_macros_static.m4 b/m4/ax_am_macros_static.m4 new file mode 100644 index 00000000..f4cee8c8 --- /dev/null +++ b/m4/ax_am_macros_static.m4 @@ -0,0 +1,38 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_am_macros_static.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_AM_MACROS_STATIC +# +# DESCRIPTION +# +# Adds support for macros that create Automake rules. You must manually +# add the following line +# +# include $(top_srcdir)/aminclude_static.am +# +# to your Makefile.am files. +# +# LICENSE +# +# Copyright (c) 2009 Tom Howard +# Copyright (c) 2009 Allan Caffee +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 11 + +AC_DEFUN([AMINCLUDE_STATIC],[aminclude_static.am]) + +AC_DEFUN([AX_AM_MACROS_STATIC], +[ +AX_AC_PRINT_TO_FILE(AMINCLUDE_STATIC,[ +# ]AMINCLUDE_STATIC[ generated automatically by Autoconf +# from AX_AM_MACROS_STATIC on ]m4_esyscmd([LC_ALL=C date])[ +]) +]) diff --git a/m4/ax_check_gnu_make.m4 b/m4/ax_check_gnu_make.m4 new file mode 100644 index 00000000..785dc96d --- /dev/null +++ b/m4/ax_check_gnu_make.m4 @@ -0,0 +1,95 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_gnu_make.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_GNU_MAKE([run-if-true],[run-if-false]) +# +# DESCRIPTION +# +# This macro searches for a GNU version of make. If a match is found: +# +# * The makefile variable `ifGNUmake' is set to the empty string, otherwise +# it is set to "#". This is useful for including a special features in a +# Makefile, which cannot be handled by other versions of make. +# * The makefile variable `ifnGNUmake' is set to #, otherwise +# it is set to the empty string. This is useful for including a special +# features in a Makefile, which can be handled +# by other versions of make or to specify else like clause. +# * The variable `_cv_gnu_make_command` is set to the command to invoke +# GNU make if it exists, the empty string otherwise. +# * The variable `ax_cv_gnu_make_command` is set to the command to invoke +# GNU make by copying `_cv_gnu_make_command`, otherwise it is unset. +# * If GNU Make is found, its version is extracted from the output of +# `make --version` as the last field of a record of space-separated +# columns and saved into the variable `ax_check_gnu_make_version`. +# * Additionally if GNU Make is found, run shell code run-if-true +# else run shell code run-if-false. +# +# Here is an example of its use: +# +# Makefile.in might contain: +# +# # A failsafe way of putting a dependency rule into a makefile +# $(DEPEND): +# $(CC) -MM $(srcdir)/*.c > $(DEPEND) +# +# @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND))) +# @ifGNUmake@ include $(DEPEND) +# @ifGNUmake@ else +# fallback code +# @ifGNUmake@ endif +# +# Then configure.in would normally contain: +# +# AX_CHECK_GNU_MAKE() +# AC_OUTPUT(Makefile) +# +# Then perhaps to cause gnu make to override any other make, we could do +# something like this (note that GNU make always looks for GNUmakefile +# first): +# +# if ! test x$_cv_gnu_make_command = x ; then +# mv Makefile GNUmakefile +# echo .DEFAULT: > Makefile ; +# echo \ $_cv_gnu_make_command \$@ >> Makefile; +# fi +# +# Then, if any (well almost any) other make is called, and GNU make also +# exists, then the other make wraps the GNU make. +# +# LICENSE +# +# Copyright (c) 2008 John Darrington +# Copyright (c) 2015 Enrico M. Crisostomo +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 12 + +AC_DEFUN([AX_CHECK_GNU_MAKE],dnl + [AC_PROG_AWK + AC_CACHE_CHECK([for GNU make],[_cv_gnu_make_command],[dnl + _cv_gnu_make_command="" ; +dnl Search all the common names for GNU make + for a in "$MAKE" make gmake gnumake ; do + if test -z "$a" ; then continue ; fi ; + if "$a" --version 2> /dev/null | grep GNU 2>&1 > /dev/null ; then + _cv_gnu_make_command=$a ; + AX_CHECK_GNU_MAKE_HEADLINE=$("$a" --version 2> /dev/null | grep "GNU Make") + ax_check_gnu_make_version=$(echo ${AX_CHECK_GNU_MAKE_HEADLINE} | ${AWK} -F " " '{ print $(NF); }') + break ; + fi + done ;]) +dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifGNUmake], ["#"])], [AS_VAR_SET([ifGNUmake], [""])]) + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_VAR_SET([ifnGNUmake], [""])], [AS_VAR_SET([ifnGNUmake], ["#"])]) + AS_VAR_IF([_cv_gnu_make_command], [""], [AS_UNSET(ax_cv_gnu_make_command)], [AS_VAR_SET([ax_cv_gnu_make_command], [${_cv_gnu_make_command}])]) + AS_VAR_IF([_cv_gnu_make_command], [""],[$2],[$1]) + AC_SUBST([ifGNUmake]) + AC_SUBST([ifnGNUmake]) +]) diff --git a/m4/ax_code_coverage.m4 b/m4/ax_code_coverage.m4 new file mode 100644 index 00000000..352165b4 --- /dev/null +++ b/m4/ax_code_coverage.m4 @@ -0,0 +1,272 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_code_coverage.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CODE_COVERAGE() +# +# DESCRIPTION +# +# Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS, +# CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LIBS which should be included +# in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LIBADD variables of every +# build target (program or library) which should be built with code +# coverage support. Also add rules using AX_ADD_AM_MACRO_STATIC; and +# $enable_code_coverage which can be used in subsequent configure output. +# CODE_COVERAGE_ENABLED is defined and substituted, and corresponds to the +# value of the --enable-code-coverage option, which defaults to being +# disabled. +# +# Test also for gcov program and create GCOV variable that could be +# substituted. +# +# Note that all optimization flags in CFLAGS must be disabled when code +# coverage is enabled. +# +# Usage example: +# +# configure.ac: +# +# AX_CODE_COVERAGE +# +# Makefile.am: +# +# include $(top_srcdir)/aminclude_static.am +# +# my_program_LIBS = ... $(CODE_COVERAGE_LIBS) ... +# my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ... +# my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ... +# my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ... +# +# clean-local: code-coverage-clean +# distclean-local: code-coverage-dist-clean +# +# This results in a "check-code-coverage" rule being added to any +# Makefile.am which do "include $(top_srcdir)/aminclude_static.am" +# (assuming the module has been configured with --enable-code-coverage). +# Running `make check-code-coverage` in that directory will run the +# module's test suite (`make check`) and build a code coverage report +# detailing the code which was touched, then print the URI for the report. +# +# This code was derived from Makefile.decl in GLib, originally licensed +# under LGPLv2.1+. +# +# LICENSE +# +# Copyright (c) 2012, 2016 Philip Withnall +# Copyright (c) 2012 Xan Lopez +# Copyright (c) 2012 Christian Persch +# Copyright (c) 2012 Paolo Borelli +# Copyright (c) 2012 Dan Winship +# Copyright (c) 2015,2018 Bastien ROUCARIES +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or (at +# your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +#serial 34 + +m4_define(_AX_CODE_COVERAGE_RULES,[ +AX_ADD_AM_MACRO_STATIC([ +# Code coverage +# +# Optional: +# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. +# Multiple directories may be specified, separated by whitespace. +# (Default: \$(top_builddir)) +# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated +# by lcov for code coverage. (Default: +# \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage.info) +# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage +# reports to be created. (Default: +# \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage) +# - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, +# set to 0 to disable it and leave empty to stay with the default. +# (Default: empty) +# - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov +# instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov +# instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov +# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the +# collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov +# instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +# - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering +# lcov instance. (Default: empty) +# - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov +# instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +# - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the +# genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) +# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml +# instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore +# +# The generated report will be titled using the \$(PACKAGE_NAME) and +# \$(PACKAGE_VERSION). In order to add the current git hash to the title, +# use the git-version-gen script, available online. +# Optional variables +# run only on top dir +if CODE_COVERAGE_ENABLED + ifeq (\$(abs_builddir), \$(abs_top_builddir)) +CODE_COVERAGE_DIRECTORY ?= \$(top_builddir) +CODE_COVERAGE_OUTPUT_FILE ?= \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage.info +CODE_COVERAGE_OUTPUT_DIRECTORY ?= \$(PACKAGE_NAME)-\$(PACKAGE_VERSION)-coverage + +CODE_COVERAGE_BRANCH_COVERAGE ?= +CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= \$(if \$(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc lcov_branch_coverage=\$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_LCOV_SHOPTS ?= \$(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) +CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool \"\$(GCOV)\" +CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= \$(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) +CODE_COVERAGE_LCOV_OPTIONS ?= \$(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) +CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= +CODE_COVERAGE_LCOV_RMOPTS ?= \$(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) +CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\ +\$(if \$(CODE_COVERAGE_BRANCH_COVERAGE),\ +--rc genhtml_branch_coverage=\$(CODE_COVERAGE_BRANCH_COVERAGE)) +CODE_COVERAGE_GENHTML_OPTIONS ?= \$(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) +CODE_COVERAGE_IGNORE_PATTERN ?= + +GITIGNOREFILES := \$(GITIGNOREFILES) \$(CODE_COVERAGE_OUTPUT_FILE) \$(CODE_COVERAGE_OUTPUT_DIRECTORY) +code_coverage_v_lcov_cap = \$(code_coverage_v_lcov_cap_\$(V)) +code_coverage_v_lcov_cap_ = \$(code_coverage_v_lcov_cap_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_cap_0 = @echo \" LCOV --capture\" \$(CODE_COVERAGE_OUTPUT_FILE); +code_coverage_v_lcov_ign = \$(code_coverage_v_lcov_ign_\$(V)) +code_coverage_v_lcov_ign_ = \$(code_coverage_v_lcov_ign_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_lcov_ign_0 = @echo \" LCOV --remove /tmp/*\" \$(CODE_COVERAGE_IGNORE_PATTERN); +code_coverage_v_genhtml = \$(code_coverage_v_genhtml_\$(V)) +code_coverage_v_genhtml_ = \$(code_coverage_v_genhtml_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_v_genhtml_0 = @echo \" GEN \" \"\$(CODE_COVERAGE_OUTPUT_DIRECTORY)\"; +code_coverage_quiet = \$(code_coverage_quiet_\$(V)) +code_coverage_quiet_ = \$(code_coverage_quiet_\$(AM_DEFAULT_VERBOSITY)) +code_coverage_quiet_0 = --quiet + +# sanitizes the test-name: replaces with underscores: dashes and dots +code_coverage_sanitize = \$(subst -,_,\$(subst .,_,\$(1))) + +# Use recursive makes in order to ignore errors during check +check-code-coverage: + -\$(AM_V_at)\$(MAKE) \$(AM_MAKEFLAGS) -k check + \$(AM_V_at)\$(MAKE) \$(AM_MAKEFLAGS) code-coverage-capture + +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + \$(code_coverage_v_lcov_cap)\$(LCOV) \$(code_coverage_quiet) \$(addprefix --directory ,\$(CODE_COVERAGE_DIRECTORY)) --capture --output-file \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" --test-name \"\$(call code_coverage_sanitize,\$(PACKAGE_NAME)-\$(PACKAGE_VERSION))\" --no-checksum --compat-libtool \$(CODE_COVERAGE_LCOV_SHOPTS) \$(CODE_COVERAGE_LCOV_OPTIONS) + \$(code_coverage_v_lcov_ign)\$(LCOV) \$(code_coverage_quiet) \$(addprefix --directory ,\$(CODE_COVERAGE_DIRECTORY)) --remove \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" \"/tmp/*\" \$(CODE_COVERAGE_IGNORE_PATTERN) --output-file \"\$(CODE_COVERAGE_OUTPUT_FILE)\" \$(CODE_COVERAGE_LCOV_SHOPTS) \$(CODE_COVERAGE_LCOV_RMOPTS) + -@rm -f \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" + \$(code_coverage_v_genhtml)LANG=C \$(GENHTML) \$(code_coverage_quiet) \$(addprefix --prefix ,\$(CODE_COVERAGE_DIRECTORY)) --output-directory \"\$(CODE_COVERAGE_OUTPUT_DIRECTORY)\" --title \"\$(PACKAGE_NAME)-\$(PACKAGE_VERSION) Code Coverage\" --legend --show-details \"\$(CODE_COVERAGE_OUTPUT_FILE)\" \$(CODE_COVERAGE_GENHTML_OPTIONS) + @echo \"file://\$(abs_builddir)/\$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html\" + +code-coverage-clean: + -\$(LCOV) --directory \$(top_builddir) -z + -rm -rf \"\$(CODE_COVERAGE_OUTPUT_FILE)\" \"\$(CODE_COVERAGE_OUTPUT_FILE).tmp\" \"\$(CODE_COVERAGE_OUTPUT_DIRECTORY)\" + -find . \\( -name \"*.gcda\" -o -name \"*.gcno\" -o -name \"*.gcov\" \\) -delete + +code-coverage-dist-clean: + +A][M_DISTCHECK_CONFIGURE_FLAGS := \$(A][M_DISTCHECK_CONFIGURE_FLAGS) --disable-code-coverage + else # ifneq (\$(abs_builddir), \$(abs_top_builddir)) +check-code-coverage: + +code-coverage-capture: code-coverage-capture-hook + +code-coverage-clean: + +code-coverage-dist-clean: + endif # ifeq (\$(abs_builddir), \$(abs_top_builddir)) +else #! CODE_COVERAGE_ENABLED +# Use recursive makes in order to ignore errors during check +check-code-coverage: + @echo \"Need to reconfigure with --enable-code-coverage\" +# Capture code coverage data +code-coverage-capture: code-coverage-capture-hook + @echo \"Need to reconfigure with --enable-code-coverage\" + +code-coverage-clean: + +code-coverage-dist-clean: + +endif #CODE_COVERAGE_ENABLED +# Hook rule executed before code-coverage-capture, overridable by the user +code-coverage-capture-hook: + +.PHONY: check-code-coverage code-coverage-capture code-coverage-dist-clean code-coverage-clean code-coverage-capture-hook +]) +]) + +AC_DEFUN([_AX_CODE_COVERAGE_ENABLED],[ + AX_CHECK_GNU_MAKE([],[AC_MSG_ERROR([not using GNU make that is needed for coverage])]) + AC_REQUIRE([AX_ADD_AM_MACRO_STATIC]) + # check for gcov + AC_CHECK_TOOL([GCOV], + [$_AX_CODE_COVERAGE_GCOV_PROG_WITH], + [:]) + AS_IF([test "X$GCOV" = "X:"], + [AC_MSG_ERROR([gcov is needed to do coverage])]) + AC_SUBST([GCOV]) + + dnl Check if gcc is being used + AS_IF([ test "$GCC" = "no" ], [ + AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage]) + ]) + + AC_CHECK_PROG([LCOV], [lcov], [lcov]) + AC_CHECK_PROG([GENHTML], [genhtml], [genhtml]) + + AS_IF([ test x"$LCOV" = x ], [ + AC_MSG_ERROR([To enable code coverage reporting you must have lcov installed]) + ]) + + AS_IF([ test x"$GENHTML" = x ], [ + AC_MSG_ERROR([Could not find genhtml from the lcov package]) + ]) + + dnl Build the code coverage flags + dnl Define CODE_COVERAGE_LDFLAGS for backwards compatibility + CODE_COVERAGE_CPPFLAGS="-DNDEBUG" + CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" + CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" + CODE_COVERAGE_LIBS="-lgcov" + + AC_SUBST([CODE_COVERAGE_CPPFLAGS]) + AC_SUBST([CODE_COVERAGE_CFLAGS]) + AC_SUBST([CODE_COVERAGE_CXXFLAGS]) + AC_SUBST([CODE_COVERAGE_LIBS]) +]) + +AC_DEFUN([AX_CODE_COVERAGE],[ + dnl Check for --enable-code-coverage + + # allow to override gcov location + AC_ARG_WITH([gcov], + [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov], + [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov]) + + AC_MSG_CHECKING([whether to build with code coverage support]) + AC_ARG_ENABLE([code-coverage], + AS_HELP_STRING([--enable-code-coverage], + [Whether to enable code coverage support]),, + enable_code_coverage=no) + + AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test "x$enable_code_coverage" = xyes]) + AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage]) + AC_MSG_RESULT($enable_code_coverage) + + AS_IF([ test "x$enable_code_coverage" = xyes ], [ + _AX_CODE_COVERAGE_ENABLED + ]) + + _AX_CODE_COVERAGE_RULES +]) diff --git a/m4/ax_file_escapes.m4 b/m4/ax_file_escapes.m4 new file mode 100644 index 00000000..a86fdc32 --- /dev/null +++ b/m4/ax_file_escapes.m4 @@ -0,0 +1,30 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_file_escapes.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_FILE_ESCAPES +# +# DESCRIPTION +# +# Writes the specified data to the specified file. +# +# LICENSE +# +# Copyright (c) 2008 Tom Howard +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AC_DEFUN([AX_FILE_ESCAPES],[ +AX_DOLLAR="\$" +AX_SRB="\\135" +AX_SLB="\\133" +AX_BS="\\\\" +AX_DQ="\"" +]) diff --git a/snapcraft.yaml b/snapcraft.yaml index d1554fa1..d9fdce0d 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -54,5 +54,4 @@ parts: - zlib1g - libncursesw5 - libpcre3 - - libpcrecpp0v5 - libgpm2 diff --git a/src/Makefile.am b/src/Makefile.am index b201be6d..8551f5b2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,6 @@ +include $(top_srcdir)/aminclude_static.am + SUBDIRS = base fmtlib pcrepp pugixml yajl yajlpp . bin_PROGRAMS = lnav @@ -203,6 +205,10 @@ LNAV_BUILT_FILES = \ xterm-palette-json.h \ xterm-palette-json.cc +AM_LIBS = $(CODE_COVERAGE_LIBS) +AM_CFLAGS = $(CODE_COVERAGE_CFLAGS) +AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) + AM_LDFLAGS = \ $(STATIC_LDFLAGS) \ $(LIBARCHIVE_LDFLAGS) \ @@ -214,6 +220,7 @@ AM_CPPFLAGS = \ -DSYSCONFDIR='"$(sysconfdir)"' \ -I$(srcdir)/fmtlib \ -Wall \ + $(CODE_COVERAGE_CPPFLAGS) \ $(LIBARCHIVE_CFLAGS) \ $(READLINE_CFLAGS) \ $(SQLITE3_CFLAGS) \ @@ -231,8 +238,7 @@ LDADD = \ $(CURSES_LIB) \ $(SQLITE3_LIBS) \ $(LIBARCHIVE_LIBS) \ - $(LIBCURL) \ - -lpcrecpp + $(LIBCURL) dist_noinst_DATA = \ alpha-release.sh \ @@ -397,6 +403,8 @@ libdiag_a_SOURCES = \ column_namer.cc \ command_executor.cc \ curl_looper.cc \ + data_scanner.cc \ + data_scanner_re.cc \ db_sub_source.cc \ elem_to_json.cc \ environ_vtab.cc \ @@ -435,8 +443,6 @@ libdiag_a_SOURCES = \ logfile.cc \ logfile_sub_source.cc \ network-extension-functions.cc \ - data_scanner.cc \ - data_scanner_re.cc \ data_parser.cc \ papertrail_proc.cc \ pretty_printer.cc \ diff --git a/src/ansi_scrubber.cc b/src/ansi_scrubber.cc index 381a0cd7..dd4ba11f 100644 --- a/src/ansi_scrubber.cc +++ b/src/ansi_scrubber.cc @@ -31,7 +31,6 @@ #include "config.h" -#include #include #include "base/opt_util.hh" diff --git a/src/auto_mem.hh b/src/auto_mem.hh index 552c0e41..944efde1 100644 --- a/src/auto_mem.hh +++ b/src/auto_mem.hh @@ -76,7 +76,9 @@ public: return *this; }; - auto_mem &operator =(auto_mem & am) + auto_mem &operator=(auto_mem &) = delete; + + auto_mem &operator =(auto_mem && am) noexcept { this->reset(am.release()); this->am_free_func = am.am_free_func; diff --git a/src/base/Makefile.am b/src/base/Makefile.am index 96db02e9..1ec4c196 100644 --- a/src/base/Makefile.am +++ b/src/base/Makefile.am @@ -1,9 +1,16 @@ +include $(top_srcdir)/aminclude_static.am + AM_CPPFLAGS = \ + $(CODE_COVERAGE_CPPFLAGS) \ -Wall \ -I$(top_srcdir)/src/ \ -I$(top_srcdir)/src/fmtlib +AM_LIBS = $(CODE_COVERAGE_LIBS) +AM_CFLAGS = $(CODE_COVERAGE_CFLAGS) +AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) + noinst_LIBRARIES = libbase.a noinst_HEADERS = \ diff --git a/src/base/lnav_log.cc b/src/base/lnav_log.cc index 58d8a75f..e49f78ce 100644 --- a/src/base/lnav_log.cc +++ b/src/base/lnav_log.cc @@ -140,6 +140,7 @@ static struct { 0, BUFFER_SIZE, 0, + {} }; static const char *LEVEL_NAMES[] = { diff --git a/src/base/result.h b/src/base/result.h index e1f17478..5edc7ad6 100644 --- a/src/base/result.h +++ b/src/base/result.h @@ -856,7 +856,7 @@ struct Result { >::type unwrap() const { if (isOk()) { - return storage().template get(); + return std::move(storage().template get()); } ::fprintf(stderr, "Attempting to unwrap an error Result\n"); diff --git a/src/command_executor.cc b/src/command_executor.cc index 1aca6e74..e36e1bbe 100644 --- a/src/command_executor.cc +++ b/src/command_executor.cc @@ -33,7 +33,6 @@ #include "base/string_util.hh" #include "yajlpp/json_ptr.hh" -#include "pcrecpp.h" #include "lnav.hh" #include "log_format_loader.hh" #include "shlex.hh" diff --git a/src/data_scanner_re.cc b/src/data_scanner_re.cc index d84ccbf0..e9302aa1 100644 --- a/src/data_scanner_re.cc +++ b/src/data_scanner_re.cc @@ -1,5 +1,5 @@ -/* Generated by re2c 2.0.3 on Thu Sep 10 10:03:52 2020 */ -#line 1 "../../lnav2/src/data_scanner_re.re" +/* Generated by re2c 2.0.3 on Wed Jan 27 16:33:33 2021 */ +#line 1 "../../lnav/src/data_scanner_re.re" /** * Copyright (c) 2015, Timothy Stack * @@ -105,7 +105,7 @@ bool data_scanner::tokenize2(pcre_context &pc, data_token_t &token_out) cap[1].c_begin = pi.pi_next_offset; -#line 109 "../../lnav2/src/data_scanner_re.cc" +#line 109 "../../lnav/src/data_scanner_re.cc" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -360,9 +360,9 @@ yy2: } yy3: ++YYCURSOR; -#line 132 "../../lnav2/src/data_scanner_re.re" +#line 132 "../../lnav/src/data_scanner_re.re" { return false; } -#line 366 "../../lnav2/src/data_scanner_re.cc" +#line 366 "../../lnav/src/data_scanner_re.cc" yy5: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -520,11 +520,11 @@ yy6: default: goto yy7; } yy7: -#line 226 "../../lnav2/src/data_scanner_re.re" +#line 226 "../../lnav/src/data_scanner_re.re" { RET(DT_SYMBOL); } -#line 528 "../../lnav2/src/data_scanner_re.cc" +#line 528 "../../lnav/src/data_scanner_re.cc" yy8: yyaccept = 1; yych = *(YYMARKER = ++YYCURSOR); @@ -543,14 +543,14 @@ yy8: default: goto yy74; } yy9: -#line 231 "../../lnav2/src/data_scanner_re.re" +#line 231 "../../lnav/src/data_scanner_re.re" { RET(DT_WHITE); } -#line 549 "../../lnav2/src/data_scanner_re.cc" +#line 549 "../../lnav/src/data_scanner_re.cc" yy10: ++YYCURSOR; -#line 230 "../../lnav2/src/data_scanner_re.re" +#line 230 "../../lnav/src/data_scanner_re.re" { RET(DT_LINE); } -#line 554 "../../lnav2/src/data_scanner_re.cc" +#line 554 "../../lnav/src/data_scanner_re.cc" yy12: yyaccept = 1; yych = *(YYMARKER = ++YYCURSOR); @@ -572,9 +572,9 @@ yy12: yy13: ++YYCURSOR; yy14: -#line 233 "../../lnav2/src/data_scanner_re.re" +#line 233 "../../lnav/src/data_scanner_re.re" { RET(DT_GARBAGE); } -#line 578 "../../lnav2/src/data_scanner_re.cc" +#line 578 "../../lnav/src/data_scanner_re.cc" yy15: yyaccept = 2; yych = *(YYMARKER = ++YYCURSOR); @@ -1024,19 +1024,19 @@ yy18: default: goto yy19; } yy19: -#line 198 "../../lnav2/src/data_scanner_re.re" +#line 198 "../../lnav/src/data_scanner_re.re" { RET(DT_LPAREN); } -#line 1030 "../../lnav2/src/data_scanner_re.cc" +#line 1030 "../../lnav/src/data_scanner_re.cc" yy20: ++YYCURSOR; -#line 199 "../../lnav2/src/data_scanner_re.re" +#line 199 "../../lnav/src/data_scanner_re.re" { RET(DT_RPAREN); } -#line 1035 "../../lnav2/src/data_scanner_re.cc" +#line 1035 "../../lnav/src/data_scanner_re.cc" yy22: ++YYCURSOR; -#line 191 "../../lnav2/src/data_scanner_re.re" +#line 191 "../../lnav/src/data_scanner_re.re" { RET(DT_COMMA); } -#line 1040 "../../lnav2/src/data_scanner_re.cc" +#line 1040 "../../lnav/src/data_scanner_re.cc" yy24: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -1351,9 +1351,9 @@ yy26: default: goto yy28; } yy28: -#line 162 "../../lnav2/src/data_scanner_re.re" +#line 162 "../../lnav/src/data_scanner_re.re" { RET(DT_PATH); } -#line 1357 "../../lnav2/src/data_scanner_re.cc" +#line 1357 "../../lnav/src/data_scanner_re.cc" yy29: yyaccept = 4; yych = *(YYMARKER = ++YYCURSOR); @@ -1513,9 +1513,9 @@ yy29: default: goto yy30; } yy30: -#line 217 "../../lnav2/src/data_scanner_re.re" +#line 217 "../../lnav/src/data_scanner_re.re" { RET(DT_NUMBER); } -#line 1519 "../../lnav2/src/data_scanner_re.cc" +#line 1519 "../../lnav/src/data_scanner_re.cc" yy31: yyaccept = 4; yych = *(YYMARKER = ++YYCURSOR); @@ -1998,14 +1998,14 @@ yy34: default: goto yy35; } yy35: -#line 189 "../../lnav2/src/data_scanner_re.re" +#line 189 "../../lnav/src/data_scanner_re.re" { RET(DT_COLON); } -#line 2004 "../../lnav2/src/data_scanner_re.cc" +#line 2004 "../../lnav/src/data_scanner_re.cc" yy36: ++YYCURSOR; -#line 192 "../../lnav2/src/data_scanner_re.re" +#line 192 "../../lnav/src/data_scanner_re.re" { RET(DT_SEMI); } -#line 2009 "../../lnav2/src/data_scanner_re.cc" +#line 2009 "../../lnav/src/data_scanner_re.cc" yy38: yyaccept = 6; yych = *(YYMARKER = ++YYCURSOR); @@ -2080,19 +2080,19 @@ yy38: default: goto yy39; } yy39: -#line 200 "../../lnav2/src/data_scanner_re.re" +#line 200 "../../lnav/src/data_scanner_re.re" { RET(DT_LANGLE); } -#line 2086 "../../lnav2/src/data_scanner_re.cc" +#line 2086 "../../lnav/src/data_scanner_re.cc" yy40: ++YYCURSOR; -#line 190 "../../lnav2/src/data_scanner_re.re" +#line 190 "../../lnav/src/data_scanner_re.re" { RET(DT_EQUALS); } -#line 2091 "../../lnav2/src/data_scanner_re.cc" +#line 2091 "../../lnav/src/data_scanner_re.cc" yy42: ++YYCURSOR; -#line 201 "../../lnav2/src/data_scanner_re.re" +#line 201 "../../lnav/src/data_scanner_re.re" { RET(DT_RANGLE); } -#line 2096 "../../lnav2/src/data_scanner_re.cc" +#line 2096 "../../lnav/src/data_scanner_re.cc" yy44: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -2556,9 +2556,9 @@ yy50: default: goto yy51; } yy51: -#line 196 "../../lnav2/src/data_scanner_re.re" +#line 196 "../../lnav/src/data_scanner_re.re" { RET(DT_LSQUARE); } -#line 2562 "../../lnav2/src/data_scanner_re.cc" +#line 2562 "../../lnav/src/data_scanner_re.cc" yy52: yych = *++YYCURSOR; switch (yych) { @@ -2567,9 +2567,9 @@ yy52: } yy53: ++YYCURSOR; -#line 197 "../../lnav2/src/data_scanner_re.re" +#line 197 "../../lnav/src/data_scanner_re.re" { RET(DT_RSQUARE); } -#line 2573 "../../lnav2/src/data_scanner_re.cc" +#line 2573 "../../lnav/src/data_scanner_re.cc" yy55: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -3116,14 +3116,14 @@ yy62: default: goto yy63; } yy63: -#line 194 "../../lnav2/src/data_scanner_re.re" +#line 194 "../../lnav/src/data_scanner_re.re" { RET(DT_LCURLY); } -#line 3122 "../../lnav2/src/data_scanner_re.cc" +#line 3122 "../../lnav/src/data_scanner_re.cc" yy64: ++YYCURSOR; -#line 195 "../../lnav2/src/data_scanner_re.re" +#line 195 "../../lnav/src/data_scanner_re.re" { RET(DT_RCURLY); } -#line 3127 "../../lnav2/src/data_scanner_re.cc" +#line 3127 "../../lnav/src/data_scanner_re.cc" yy66: yych = *++YYCURSOR; switch (yych) { @@ -3670,7 +3670,7 @@ yy79: default: goto yy80; } yy80: -#line 134 "../../lnav2/src/data_scanner_re.re" +#line 134 "../../lnav/src/data_scanner_re.re" { CAPTURE(DT_QUOTED_STRING); switch (pi.get_string()[cap[1].c_begin]) { @@ -3683,7 +3683,7 @@ yy80: cap[1].c_end -= 1; return true; } -#line 3687 "../../lnav2/src/data_scanner_re.cc" +#line 3687 "../../lnav/src/data_scanner_re.cc" yy81: yych = *++YYCURSOR; switch (yych) { @@ -5152,9 +5152,9 @@ yy102: } yy103: ++YYCURSOR; -#line 193 "../../lnav2/src/data_scanner_re.re" +#line 193 "../../lnav/src/data_scanner_re.re" { RET(DT_EMPTY_CONTAINER); } -#line 5158 "../../lnav2/src/data_scanner_re.cc" +#line 5158 "../../lnav/src/data_scanner_re.cc" yy105: yyaccept = 4; yych = *(YYMARKER = ++YYCURSOR); @@ -5780,9 +5780,9 @@ yy114: default: goto yy115; } yy115: -#line 216 "../../lnav2/src/data_scanner_re.re" +#line 216 "../../lnav/src/data_scanner_re.re" { RET(DT_PERCENTAGE); } -#line 5786 "../../lnav2/src/data_scanner_re.cc" +#line 5786 "../../lnav/src/data_scanner_re.cc" yy116: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -6016,9 +6016,9 @@ yy117: default: goto yy118; } yy118: -#line 215 "../../lnav2/src/data_scanner_re.re" +#line 215 "../../lnav/src/data_scanner_re.re" { RET(DT_OCTAL_NUMBER); } -#line 6022 "../../lnav2/src/data_scanner_re.cc" +#line 6022 "../../lnav/src/data_scanner_re.cc" yy119: yyaccept = 4; yych = *(YYMARKER = ++YYCURSOR); @@ -6364,9 +6364,9 @@ yy121: default: goto yy122; } yy122: -#line 218 "../../lnav2/src/data_scanner_re.re" +#line 218 "../../lnav/src/data_scanner_re.re" { RET(DT_HEX_NUMBER); } -#line 6370 "../../lnav2/src/data_scanner_re.cc" +#line 6370 "../../lnav/src/data_scanner_re.cc" yy123: yyaccept = 10; yych = *(YYMARKER = ++YYCURSOR); @@ -7489,11 +7489,11 @@ yy133: default: goto yy134; } yy134: -#line 146 "../../lnav2/src/data_scanner_re.re" +#line 146 "../../lnav/src/data_scanner_re.re" { CAPTURE(DT_WORD); } -#line 7497 "../../lnav2/src/data_scanner_re.cc" +#line 7497 "../../lnav/src/data_scanner_re.cc" yy135: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -9296,7 +9296,7 @@ yy160: yyt1 = yyt2; yy161: YYCURSOR = yyt1; -#line 149 "../../lnav2/src/data_scanner_re.re" +#line 149 "../../lnav/src/data_scanner_re.re" { CAPTURE(DT_QUOTED_STRING); switch (pi.get_string()[cap[1].c_begin]) { @@ -9309,7 +9309,7 @@ yy161: cap[1].c_end -= 1; return true; } -#line 9313 "../../lnav2/src/data_scanner_re.cc" +#line 9313 "../../lnav/src/data_scanner_re.cc" yy162: yyaccept = 12; yych = *(YYMARKER = ++YYCURSOR); @@ -12758,9 +12758,9 @@ yy200: ++YYCURSOR; yy201: YYCURSOR = yyt2; -#line 175 "../../lnav2/src/data_scanner_re.re" +#line 175 "../../lnav/src/data_scanner_re.re" { RET(DT_IPV6_ADDRESS); } -#line 12764 "../../lnav2/src/data_scanner_re.cc" +#line 12764 "../../lnav/src/data_scanner_re.cc" yy202: yych = *++YYCURSOR; switch (yych) { @@ -14038,11 +14038,11 @@ yy217: yy218: ++YYCURSOR; yy219: -#line 181 "../../lnav2/src/data_scanner_re.re" +#line 181 "../../lnav/src/data_scanner_re.re" { RET(DT_XML_OPEN_TAG); } -#line 14046 "../../lnav2/src/data_scanner_re.cc" +#line 14046 "../../lnav/src/data_scanner_re.cc" yy220: yych = *++YYCURSOR; switch (yych) { @@ -14191,9 +14191,9 @@ yy223: yyt3 = yyt4; yy224: YYCURSOR = yyt3; -#line 224 "../../lnav2/src/data_scanner_re.re" +#line 224 "../../lnav/src/data_scanner_re.re" { RET(DT_WORD); } -#line 14197 "../../lnav2/src/data_scanner_re.cc" +#line 14197 "../../lnav/src/data_scanner_re.cc" yy225: yych = *++YYCURSOR; switch (yych) { @@ -21698,11 +21698,11 @@ yy310: yy311: ++YYCURSOR; yy312: -#line 177 "../../lnav2/src/data_scanner_re.re" +#line 177 "../../lnav/src/data_scanner_re.re" { RET(DT_XML_EMPTY_TAG); } -#line 21706 "../../lnav2/src/data_scanner_re.cc" +#line 21706 "../../lnav/src/data_scanner_re.cc" yy313: yych = *++YYCURSOR; switch (yych) { @@ -21714,11 +21714,11 @@ yy313: } yy315: ++YYCURSOR; -#line 185 "../../lnav2/src/data_scanner_re.re" +#line 185 "../../lnav/src/data_scanner_re.re" { RET(DT_XML_CLOSE_TAG); } -#line 21722 "../../lnav2/src/data_scanner_re.cc" +#line 21722 "../../lnav/src/data_scanner_re.cc" yy317: yych = *++YYCURSOR; yy318: @@ -22883,9 +22883,9 @@ yy332: default: goto yy334; } yy334: -#line 220 "../../lnav2/src/data_scanner_re.re" +#line 220 "../../lnav/src/data_scanner_re.re" { RET(DT_EMAIL); } -#line 22889 "../../lnav2/src/data_scanner_re.cc" +#line 22889 "../../lnav/src/data_scanner_re.cc" yy335: yyaccept = 0; yych = *(YYMARKER = ++YYCURSOR); @@ -23273,11 +23273,11 @@ yy338: default: goto yy340; } yy340: -#line 211 "../../lnav2/src/data_scanner_re.re" +#line 211 "../../lnav/src/data_scanner_re.re" { RET(DT_VERSION_NUMBER); } -#line 23281 "../../lnav2/src/data_scanner_re.cc" +#line 23281 "../../lnav/src/data_scanner_re.cc" yy341: yyaccept = 18; yych = *(YYMARKER = ++YYCURSOR); @@ -25711,7 +25711,7 @@ yy362: default: goto yy363; } yy363: -#line 165 "../../lnav2/src/data_scanner_re.re" +#line 165 "../../lnav/src/data_scanner_re.re" { if ((YYCURSOR - (const unsigned char *) pi.get_string()) == 17) { RET(DT_MAC_ADDRESS); @@ -25719,7 +25719,7 @@ yy363: RET(DT_HEX_DUMP); } } -#line 25723 "../../lnav2/src/data_scanner_re.cc" +#line 25723 "../../lnav/src/data_scanner_re.cc" yy364: yyaccept = 19; yych = *(YYMARKER = ++YYCURSOR); @@ -29755,9 +29755,9 @@ yy414: ++YYCURSOR; yy415: YYCURSOR = yyt1; -#line 222 "../../lnav2/src/data_scanner_re.re" +#line 222 "../../lnav/src/data_scanner_re.re" { RET(DT_CONSTANT); } -#line 29761 "../../lnav2/src/data_scanner_re.cc" +#line 29761 "../../lnav/src/data_scanner_re.cc" yy416: yych = *++YYCURSOR; switch (yych) { @@ -30086,9 +30086,9 @@ yy422: ++YYCURSOR; yy423: YYCURSOR = yyt1; -#line 163 "../../lnav2/src/data_scanner_re.re" +#line 163 "../../lnav/src/data_scanner_re.re" { RET(DT_TIME); } -#line 30092 "../../lnav2/src/data_scanner_re.cc" +#line 30092 "../../lnav/src/data_scanner_re.cc" yy424: yych = *++YYCURSOR; switch (yych) { @@ -31042,9 +31042,9 @@ yy436: default: goto yy438; } yy438: -#line 209 "../../lnav2/src/data_scanner_re.re" +#line 209 "../../lnav/src/data_scanner_re.re" { RET(DT_NUMBER); } -#line 31048 "../../lnav2/src/data_scanner_re.cc" +#line 31048 "../../lnav/src/data_scanner_re.cc" yy439: yyaccept = 21; yych = *(YYMARKER = ++YYCURSOR); @@ -32454,9 +32454,9 @@ yy453: default: goto yy455; } yy455: -#line 161 "../../lnav2/src/data_scanner_re.re" +#line 161 "../../lnav/src/data_scanner_re.re" { RET(DT_URL); } -#line 32460 "../../lnav2/src/data_scanner_re.cc" +#line 32460 "../../lnav/src/data_scanner_re.cc" yy456: yych = *++YYCURSOR; switch (yych) { @@ -46940,9 +46940,9 @@ yy625: yyt1 = yyt2; yy626: YYCURSOR = yyt1; -#line 164 "../../lnav2/src/data_scanner_re.re" +#line 164 "../../lnav/src/data_scanner_re.re" { RET(DT_TIME); } -#line 46946 "../../lnav2/src/data_scanner_re.cc" +#line 46946 "../../lnav/src/data_scanner_re.cc" yy627: yyaccept = 26; yych = *(YYMARKER = ++YYCURSOR); @@ -47282,11 +47282,11 @@ yy634: ++YYCURSOR; yy635: YYCURSOR = yyt1; -#line 203 "../../lnav2/src/data_scanner_re.re" +#line 203 "../../lnav/src/data_scanner_re.re" { RET(DT_IPV4_ADDRESS); } -#line 47290 "../../lnav2/src/data_scanner_re.cc" +#line 47290 "../../lnav/src/data_scanner_re.cc" yy636: yyaccept = 27; yych = *(YYMARKER = ++YYCURSOR); @@ -49067,11 +49067,11 @@ yy652: default: goto yy653; } yy653: -#line 172 "../../lnav2/src/data_scanner_re.re" +#line 172 "../../lnav/src/data_scanner_re.re" { RET(DT_DATE); } -#line 49075 "../../lnav2/src/data_scanner_re.cc" +#line 49075 "../../lnav/src/data_scanner_re.cc" yy654: yyaccept = 28; yych = *(YYMARKER = ++YYCURSOR); @@ -82935,10 +82935,10 @@ yy990: default: goto yy991; } yy991: -#line 207 "../../lnav2/src/data_scanner_re.re" +#line 207 "../../lnav/src/data_scanner_re.re" { RET(DT_UUID); } -#line 82941 "../../lnav2/src/data_scanner_re.cc" +#line 82941 "../../lnav/src/data_scanner_re.cc" } -#line 235 "../../lnav2/src/data_scanner_re.re" +#line 235 "../../lnav/src/data_scanner_re.re" } diff --git a/src/field_overlay_source.cc b/src/field_overlay_source.cc index 13059e33..b1241ade 100644 --- a/src/field_overlay_source.cc +++ b/src/field_overlay_source.cc @@ -253,7 +253,7 @@ void field_overlay_source::build_field_lines(const listview_curses &lv) } char old_timestamp[64], curr_timestamp[64], orig_timestamp[64]; - struct timeval curr_tv, offset_tv, orig_tv, diff_tv = { 0 }; + struct timeval curr_tv, offset_tv, orig_tv, diff_tv = { 0, 0 }; attr_line_t time_line; string &time_str = time_line.get_string(); struct line_range time_lr; diff --git a/src/highlighter.cc b/src/highlighter.cc index ea21d167..e8dad383 100644 --- a/src/highlighter.cc +++ b/src/highlighter.cc @@ -49,6 +49,10 @@ highlighter::highlighter(const highlighter &other) highlighter &highlighter::operator=(const highlighter &other) { + if (this == &other) { + return *this; + } + if (this->h_code != nullptr && pcre_refcount(this->h_code, -1) == 0) { free(this->h_code); this->h_code = nullptr; diff --git a/src/lnav.cc b/src/lnav.cc index c57a840b..b8d1185a 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -100,7 +100,6 @@ #include "bottom_status_source.hh" #include "piper_proc.hh" #include "log_vtab_impl.hh" -#include "pcrecpp.h" #include "termios_guard.hh" #include "xterm_mouse.hh" #include "lnav_commands.hh" @@ -153,6 +152,7 @@ using namespace std; using namespace std::literals::chrono_literals; +static bool initial_build = false; static multimap DEFAULT_FILES; struct lnav_data_t lnav_data; @@ -420,7 +420,7 @@ private: for (auto &sc : lnav_data.ld_status) { sc.do_update(); } - if (lnav_data.ld_mode == LNM_FILES && !lnav_data.ld_session_loaded) { + if (lnav_data.ld_mode == LNM_FILES && !initial_build) { auto &fc = lnav_data.ld_active_files; auto iter = std::find(fc.fc_files.begin(), fc.fc_files.end(), lf); @@ -1401,8 +1401,6 @@ static void looper() rlc.do_update(); while (lnav_data.ld_looping) { - static bool initial_build = false; - vector pollfds; auto poll_to = initial_rescan_completed ? timeval{ 0, 333000 } : diff --git a/src/lnav_commands.cc b/src/lnav_commands.cc index 71c047b6..42f69898 100644 --- a/src/lnav_commands.cc +++ b/src/lnav_commands.cc @@ -34,13 +34,13 @@ #include #include +#include #include #include #include #include #include -#include #include #include "bound_tags.hh" @@ -499,11 +499,10 @@ static bool csv_needs_quoting(const string &str) static string csv_quote_string(const string &str) { - static pcrecpp::RE csv_column_quoter("\""); + static std::regex csv_column_quoter("\""); - string retval = str; + string retval = std::regex_replace(str, csv_column_quoter, "\"\""); - csv_column_quoter.GlobalReplace("\"\"", &retval); retval.insert(0, 1, '\"'); retval.append(1, '\"'); @@ -1684,11 +1683,7 @@ static Result com_create_search_table(exec_context &ec, string c } else if (args.size() >= 2) { - std::shared_ptr lst; - auto_mem code; - const char *errptr; string regex; - int eoff; if (args.size() >= 3) { regex = remaining_args(cmdline, args, 2); @@ -1697,29 +1692,21 @@ static Result com_create_search_table(exec_context &ec, string c regex = lnav_data.ld_views[LNV_LOG].get_current_search(); } - if ((code = pcre_compile(regex.c_str(), - PCRE_CASELESS, - &errptr, - &eoff, - NULL)) == NULL) { - return ec.make_error("{}", errptr); - } - - try { - lst = std::make_shared( - regex.c_str(), intern_string::lookup(args[1])); - } catch (pcrepp::error &e) { - return ec.make_error("unable to compile regex -- {}", regex); + auto re_res = pcrepp::from_str(regex, log_search_table::pattern_options()); + + if (re_res.isErr()) { + return ec.make_error("{}", re_res.unwrapErr().ce_msg); } + auto re = re_res.unwrap(); + auto lst = std::make_shared( + re, intern_string::lookup(args[1])); if (ec.ec_dry_run) { textview_curses *tc = &lnav_data.ld_views[LNV_LOG]; auto &hm = tc->get_highlights(); - view_colors &vc = view_colors::singleton(); - highlighter hl(code.release()); + highlighter hl(re.p_code); - hl.with_attrs( - vc.ansi_color_pair(COLOR_BLACK, COLOR_CYAN) | A_BLINK); + hl.with_attrs(view_colors::ansi_color_pair(COLOR_BLACK, COLOR_CYAN) | A_BLINK); hm[{highlight_source_t::PREVIEW, "preview"}] = hl; tc->reload_data(); @@ -4283,12 +4270,11 @@ static void search_filters_prompt(vector &args) static void search_files_prompt(vector &args) { - static pcrecpp::RE re_escape(R"(([.\^$*+?()\[\]{}\\|]))"); + static std::regex re_escape(R"(([.\^$*+?()\[\]{}\\|]))"); lnav_data.ld_mode = LNM_SEARCH_FILES; for (const auto& lf : lnav_data.ld_active_files.fc_files) { - auto path = lf->get_unique_path(); - re_escape.GlobalReplace(R"(\\\1)", &path); + auto path = pcrepp::quote(lf->get_unique_path()); lnav_data.ld_rl_view->add_possibility(LNM_SEARCH_FILES, "*", path); diff --git a/src/lnav_config.cc b/src/lnav_config.cc index fa75bec5..c198b964 100644 --- a/src/lnav_config.cc +++ b/src/lnav_config.cc @@ -40,13 +40,12 @@ #include #include +#include #include #include #include #include -#include "pcrecpp.h" - #include "auto_fd.hh" #include "base/injector.hh" #include "base/injector.bind.hh" @@ -192,14 +191,12 @@ void ensure_dotlnav() bool install_from_git(const char *repo) { - static pcrecpp::RE repo_name_converter("[^\\w]"); + static std::regex repo_name_converter("[^\\w]"); auto formats_path = dotlnav_path() / "formats"; auto configs_path = dotlnav_path() / "configs"; auto staging_path = dotlnav_path() / "staging"; - string local_name = repo; - - repo_name_converter.GlobalReplace("_", &local_name); + string local_name = std::regex_replace(repo, repo_name_converter, "_"); auto local_formats_path = formats_path / local_name; auto local_configs_path = configs_path / local_name; diff --git a/src/log_format.cc b/src/log_format.cc index 3f13c832..8d39381c 100644 --- a/src/log_format.cc +++ b/src/log_format.cc @@ -2000,26 +2000,25 @@ void external_log_format::build(std::vector &errors) { void external_log_format::register_vtabs(log_vtab_manager *vtab_manager, std::vector &errors) { - vector >::iterator search_iter; + vector>::iterator search_iter; for (search_iter = this->elf_search_tables.begin(); search_iter != this->elf_search_tables.end(); ++search_iter) { - std::shared_ptr lst; + auto re_res = pcrepp::from_str(search_iter->second, + log_search_table::pattern_options()); - try { - lst = std::make_shared( - search_iter->second.c_str(), search_iter->first); - } catch (pcrepp::error &e) { - errors.push_back( - "error:" + - this->elf_name.to_string() + - ":" + - search_iter->first.to_string() + - ":unable to compile regex -- " + - search_iter->second); + if (re_res.isErr()) { + errors.push_back(fmt::format( + "error:{}:{}:unable to compile regex '{}': {}", + this->elf_name.get(), + search_iter->first.get(), + search_iter->second, + re_res.unwrapErr().ce_msg)); continue; } + auto lst = std::make_shared( + re_res.unwrap(), search_iter->first); string errmsg; errmsg = vtab_manager->register_vtab(lst); diff --git a/src/log_level_re.cc b/src/log_level_re.cc index f029b2d5..c720fdc2 100644 --- a/src/log_level_re.cc +++ b/src/log_level_re.cc @@ -1,5 +1,5 @@ -/* Generated by re2c 2.0.3 on Tue Nov 3 13:27:28 2020 */ -#line 1 "../../lnav2/src/log_level_re.re" +/* Generated by re2c 2.0.3 on Wed Jan 27 16:33:33 2021 */ +#line 1 "../../lnav/src/log_level_re.re" /** * Copyright (c) 2018, Timothy Stack * @@ -67,7 +67,7 @@ log_level_t string2level(const char *levelstr, ssize_t len, bool exact) loop: -#line 71 "../../lnav2/src/log_level_re.cc" +#line 71 "../../lnav/src/log_level_re.cc" { YYCTYPE yych; unsigned int yyaccept = 0; @@ -96,15 +96,15 @@ log_level_t string2level(const char *levelstr, ssize_t len, bool exact) } yy2: YYSKIP(); -#line 73 "../../lnav2/src/log_level_re.re" +#line 73 "../../lnav/src/log_level_re.re" { RET(LEVEL_UNKNOWN); } -#line 102 "../../lnav2/src/log_level_re.cc" +#line 102 "../../lnav/src/log_level_re.cc" yy4: YYSKIP(); yy5: -#line 100 "../../lnav2/src/log_level_re.re" +#line 100 "../../lnav/src/log_level_re.re" { goto loop; } -#line 108 "../../lnav2/src/log_level_re.cc" +#line 108 "../../lnav/src/log_level_re.cc" yy6: yyaccept = 0; YYSKIP(); @@ -314,9 +314,9 @@ yy28: default: goto yy29; } yy29: -#line 96 "../../lnav2/src/log_level_re.re" +#line 96 "../../lnav/src/log_level_re.re" { RET(LEVEL_ERROR); } -#line 320 "../../lnav2/src/log_level_re.cc" +#line 320 "../../lnav/src/log_level_re.cc" yy30: YYSKIP(); yych = YYPEEK(); @@ -407,9 +407,9 @@ yy40: } yy41: YYSKIP(); -#line 92 "../../lnav2/src/log_level_re.re" +#line 92 "../../lnav/src/log_level_re.re" { RET(LEVEL_INFO); } -#line 413 "../../lnav2/src/log_level_re.cc" +#line 413 "../../lnav/src/log_level_re.cc" yy43: YYSKIP(); yych = YYPEEK(); @@ -453,9 +453,9 @@ yy47: default: goto yy48; } yy48: -#line 95 "../../lnav2/src/log_level_re.re" +#line 95 "../../lnav/src/log_level_re.re" { RET(LEVEL_WARNING); } -#line 459 "../../lnav2/src/log_level_re.cc" +#line 459 "../../lnav/src/log_level_re.cc" yy49: YYSKIP(); yych = YYPEEK(); @@ -476,7 +476,7 @@ yy50: } yy51: YYSTAGP(debug_level); -#line 75 "../../lnav2/src/log_level_re.re" +#line 75 "../../lnav/src/log_level_re.re" { if (debug_level == nullptr) { RET(LEVEL_DEBUG); @@ -494,15 +494,15 @@ yy51: RET(LEVEL_DEBUG); } } -#line 498 "../../lnav2/src/log_level_re.cc" +#line 498 "../../lnav/src/log_level_re.cc" yy52: YYSKIP(); goto yy29; yy53: YYSKIP(); -#line 99 "../../lnav2/src/log_level_re.re" +#line 99 "../../lnav/src/log_level_re.re" { RET(LEVEL_FATAL); } -#line 506 "../../lnav2/src/log_level_re.cc" +#line 506 "../../lnav/src/log_level_re.cc" yy55: YYSKIP(); yych = YYPEEK(); @@ -521,14 +521,14 @@ yy56: } yy57: YYSKIP(); -#line 94 "../../lnav2/src/log_level_re.re" +#line 94 "../../lnav/src/log_level_re.re" { RET(LEVEL_STATS); } -#line 527 "../../lnav2/src/log_level_re.cc" +#line 527 "../../lnav/src/log_level_re.cc" yy59: YYSKIP(); -#line 74 "../../lnav2/src/log_level_re.re" +#line 74 "../../lnav/src/log_level_re.re" { RET(LEVEL_TRACE); } -#line 532 "../../lnav2/src/log_level_re.cc" +#line 532 "../../lnav/src/log_level_re.cc" yy61: YYSKIP(); yych = YYPEEK(); @@ -550,14 +550,14 @@ yy63: goto yy51; yy64: YYSKIP(); -#line 93 "../../lnav2/src/log_level_re.re" +#line 93 "../../lnav/src/log_level_re.re" { RET(LEVEL_NOTICE); } -#line 556 "../../lnav2/src/log_level_re.cc" +#line 556 "../../lnav/src/log_level_re.cc" yy66: YYSKIP(); -#line 98 "../../lnav2/src/log_level_re.re" +#line 98 "../../lnav/src/log_level_re.re" { RET(LEVEL_CRITICAL); } -#line 561 "../../lnav2/src/log_level_re.cc" +#line 561 "../../lnav/src/log_level_re.cc" yy68: YYSKIP(); yych = YYPEEK(); @@ -579,10 +579,10 @@ yy70: goto yy48; yy71: YYSKIP(); -#line 97 "../../lnav2/src/log_level_re.re" +#line 97 "../../lnav/src/log_level_re.re" { RET(LEVEL_CRITICAL); } -#line 585 "../../lnav2/src/log_level_re.cc" +#line 585 "../../lnav/src/log_level_re.cc" } -#line 102 "../../lnav2/src/log_level_re.re" +#line 102 "../../lnav/src/log_level_re.re" } diff --git a/src/log_search_table.cc b/src/log_search_table.cc index c08588b8..d590e436 100644 --- a/src/log_search_table.cc +++ b/src/log_search_table.cc @@ -39,11 +39,10 @@ static auto instance_meta = logline_value_meta( instance_name, value_kind_t::VALUE_INTEGER, 0); static auto empty = intern_string::lookup("", 0); -log_search_table::log_search_table(const char *regex, +log_search_table::log_search_table(pcrepp pattern, intern_string_t table_name) : log_vtab_impl(table_name), - lst_regex_string(regex), - lst_regex(regex, PCRE_CASELESS), + lst_regex(std::move(pattern)), lst_instance(-1) { this->vi_supports_indexes = false; @@ -64,8 +63,8 @@ void log_search_table::get_columns_int(std::vector &cols) if (this->lst_regex.captures().size() == (size_t) this->lst_regex.get_capture_count()) { auto iter = this->lst_regex.cap_begin() + lpc; - auto cap_re = this->lst_regex_string.substr(iter->c_begin, - iter->length()); + auto cap_re = this->lst_regex.get_pattern() + .substr(iter->c_begin, iter->length()); sqlite_type = guess_type_from_pcre(cap_re, collator); switch (sqlite_type) { case SQLITE_FLOAT: diff --git a/src/log_search_table.hh b/src/log_search_table.hh index b55a61ce..cb57ef4d 100644 --- a/src/log_search_table.hh +++ b/src/log_search_table.hh @@ -35,13 +35,17 @@ #include #include +#include "pcrepp/pcrepp.hh" #include "shared_buffer.hh" #include "log_vtab_impl.hh" class log_search_table : public log_vtab_impl { public: + static int pattern_options() { + return PCRE_CASELESS; + } - log_search_table(const char *regex, intern_string_t table_name); + log_search_table(pcrepp pattern, intern_string_t table_name); void get_columns_int(std::vector &cols); @@ -58,7 +62,6 @@ public: shared_buffer_ref &line, std::vector &values) override; - std::string lst_regex_string; pcrepp lst_regex; shared_buffer_ref lst_current_line; pcre_context_static<128> lst_match_context; diff --git a/src/pcrepp/pcrepp.cc b/src/pcrepp/pcrepp.cc index 4552236a..9f427276 100644 --- a/src/pcrepp/pcrepp.cc +++ b/src/pcrepp/pcrepp.cc @@ -31,8 +31,6 @@ #include "config.h" -#include - #include "pcrepp.hh" using namespace std; @@ -42,7 +40,7 @@ const int JIT_STACK_MAX_SIZE = 512 * 1024; pcre_context::capture_t *pcre_context::operator[](const char *name) const { - capture_t *retval = NULL; + capture_t *retval = nullptr; int index; index = this->pc_pcre->name_index(name); @@ -53,23 +51,61 @@ pcre_context::capture_t *pcre_context::operator[](const char *name) const return retval; } +std::string pcrepp::quote(const char *unquoted) +{ + std::string retval; + + for (int lpc = 0; unquoted[lpc]; lpc++) { + if (isalnum(unquoted[lpc]) || + unquoted[lpc] == '_' || + unquoted[lpc] & 0x80) { + retval.push_back(unquoted[lpc]); + } else { + retval.push_back('\\'); + retval.push_back(unquoted[lpc]); + } + } + + return retval; +} + +Result pcrepp::from_str(std::string pattern, int options) +{ + const char *errptr; + int eoff; + auto code = pcre_compile(pattern.c_str(), + options | PCRE_UTF8, + &errptr, + &eoff, + nullptr); + + if (!code) { + return Err(compile_error{errptr, eoff}); + } + + return Ok(pcrepp(std::move(pattern), code)); +} + void pcrepp::find_captures(const char *pattern) { bool in_class = false, in_escape = false, in_literal = false; vector cap_in_progress; for (int lpc = 0; pattern[lpc]; lpc++) { - if (in_class) { - if (pattern[lpc] == ']') { - in_class = false; - } - } - else if (in_escape) { + if (in_escape) { in_escape = false; if (pattern[lpc] == 'Q') { in_literal = true; } } + else if (in_class) { + if (pattern[lpc] == ']') { + in_class = false; + } + if (pattern[lpc] == '\\') { + in_escape = true; + } + } else if (in_literal) { if (pattern[lpc] == '\\' && pattern[lpc + 1] == 'E') { in_literal = false; @@ -104,7 +140,11 @@ void pcrepp::find_captures(const char *pattern) third = pattern[cap.c_begin + 3]; } if (first == '?') { - if (second == '<' || second == '\'') { + if (second == '\'') { + is_cap = true; + } + if (second == '<' && + (isalpha(third) || third == '_')) { is_cap = true; } if (second == 'P' && third == '<') { @@ -124,6 +164,8 @@ void pcrepp::find_captures(const char *pattern) } } } + + ensure(this->p_capture_count == this->p_captures.size()); } bool pcrepp::match(pcre_context &pc, pcre_input &pi, int options) const @@ -175,6 +217,9 @@ bool pcrepp::match(pcre_context &pc, pcre_input &pi, int options) const } else if (pc.all()->c_begin == pc.all()->c_end) { rc = 0; + if (pi.pi_next_offset + 1 < pi.pi_length) { + pi.pi_next_offset += 1; + } } else { if (options & PCRE_ANCHORED) { @@ -194,6 +239,62 @@ bool pcrepp::match(pcre_context &pc, pcre_input &pi, int options) const return rc > 0; } +std::string pcrepp::replace(const char *str, const char *repl) const +{ + pcre_context_static<30> pc; + pcre_input pi(str); + std::string retval; + std::string::size_type start = 0; + + while (pi.pi_offset < pi.pi_length) { + this->match(pc, pi); + auto all = pc.all(); + bool in_escape = false; + + if (pc.get_count() < 0) { + break; + } + + retval.append(str, start, (all->c_begin - start)); + start = all->c_end; + for (int lpc = 0; repl[lpc]; lpc++) { + auto ch = repl[lpc]; + + if (in_escape) { + if (isdigit(ch)) { + auto capture_index = (ch - '0'); + + if (capture_index < pc.get_count()) { + retval.append(pi.get_substr_start(&all[capture_index]), + pi.get_substr_len(&all[capture_index])); + } else if (capture_index > this->p_capture_count) { + retval.push_back('\\'); + retval.push_back(ch); + } + } else { + if (ch != '\\') { + retval.push_back('\\'); + } + retval.push_back(ch); + } + in_escape = false; + } else { + switch (ch) { + case '\\': + in_escape = true; + break; + default: + retval.push_back(ch); + break; + } + } + } + } + retval.append(str, start, std::string::npos); + + return retval; +} + void pcrepp::study() { const char *errptr; @@ -208,7 +309,7 @@ void pcrepp::study() if (!this->p_code_extra && errptr) { log_error("pcre_study error: %s", errptr); } - if (this->p_code_extra != NULL) { + if (this->p_code_extra != nullptr) { pcre_extra *extra = this->p_code_extra; extra->flags |= (PCRE_EXTRA_MATCH_LIMIT | @@ -216,9 +317,13 @@ void pcrepp::study() extra->match_limit = 10000; extra->match_limit_recursion = 500; #ifdef PCRE_STUDY_JIT_COMPILE - // pcre_assign_jit_stack(extra, NULL, jit_stack()); + // pcre_assign_jit_stack(extra, nullptr, jit_stack()); #endif } + pcre_fullinfo(this->p_code, + this->p_code_extra, + PCRE_INFO_OPTIONS, + &this->p_options); pcre_fullinfo(this->p_code, this->p_code_extra, PCRE_INFO_CAPTURECOUNT, @@ -240,9 +345,9 @@ void pcrepp::study() #ifdef PCRE_STUDY_JIT_COMPILE pcre_jit_stack *pcrepp::jit_stack() { - static pcre_jit_stack *retval = NULL; + static pcre_jit_stack *retval = nullptr; - if (retval == NULL) { + if (retval == nullptr) { retval = pcre_jit_stack_alloc(JIT_STACK_MIN_SIZE, JIT_STACK_MAX_SIZE); } diff --git a/src/pcrepp/pcrepp.hh b/src/pcrepp/pcrepp.hh index c961b68c..b952327f 100644 --- a/src/pcrepp/pcrepp.hh +++ b/src/pcrepp/pcrepp.hh @@ -58,6 +58,7 @@ #include "base/lnav_log.hh" #include "auto_mem.hh" #include "base/intern_string.hh" +#include "base/result.h" #include @@ -96,6 +97,8 @@ public: bool is_valid() const { return this->c_begin != -1; }; int length() const { return this->c_end - this->c_begin; }; + + bool empty() const { return this->c_begin == this->c_end; }; } capture_t; typedef capture_t *iterator; typedef const capture_t *const_iterator; @@ -252,7 +255,7 @@ public: iter->length()); }; - const intern_string_t get_substr_i(pcre_context::const_iterator iter) const { + intern_string_t get_substr_i(pcre_context::const_iterator iter) const { return intern_string::lookup(&this->pi_string[iter->c_begin], iter->length()); }; @@ -364,12 +367,35 @@ public: int e_offset; }; + static std::string quote(const char *unquoted); + + static std::string quote(const std::string& unquoted) { + return quote(unquoted.c_str()); + } + + struct compile_error { + const char *ce_msg; + int ce_offset; + }; + + static Result from_str(std::string pattern, int options = 0); + pcrepp(pcre *code) : p_code(code), p_code_extra(pcre_free_study) { pcre_refcount(this->p_code, 1); this->study(); }; + pcrepp(std::string pattern, pcre *code) + : p_code(code), + p_pattern(std::move(pattern)), + p_code_extra(pcre_free_study) + { + pcre_refcount(this->p_code, 1); + this->study(); + this->find_captures(this->p_pattern.c_str()); + }; + explicit pcrepp(const char *pattern, int options = 0) : p_pattern(pattern), p_code_extra(pcre_free_study) { @@ -408,23 +434,80 @@ public: this->find_captures(pattern.c_str()); }; + pcrepp() { + } + pcrepp(const pcrepp &other) : p_code(other.p_code), p_pattern(other.p_pattern), - p_code_extra(pcre_free_study) + p_code_extra(pcre_free_study), + p_captures(other.p_captures) { pcre_refcount(this->p_code, 1); this->study(); }; + pcrepp(pcrepp &&other) + : p_code(other.p_code), + p_pattern(std::move(other.p_pattern)), + p_code_extra(pcre_free_study), + p_capture_count(other.p_capture_count), + p_named_count(other.p_named_count), + p_name_len(other.p_name_len), + p_options(other.p_options), + p_named_entries(other.p_named_entries), + p_captures(std::move(other.p_captures)) { + pcre_refcount(this->p_code, 1); + this->p_code_extra = std::move(other.p_code_extra); + } + virtual ~pcrepp() { - if (pcre_refcount(this->p_code, -1) == 0) { - free(this->p_code); - this->p_code = 0; - } + this->clear(); }; + pcrepp& operator=(pcrepp&& other) noexcept { + if (this == &other) { + return *this; + } + + this->p_code = other.p_code; + pcre_refcount(this->p_code, 1); + this->p_pattern = std::move(other.p_pattern); + this->p_code_extra = std::move(other.p_code_extra); + this->p_capture_count = other.p_capture_count; + this->p_named_count = other.p_named_count; + this->p_name_len = other.p_name_len; + this->p_options = other.p_options; + this->p_named_entries = other.p_named_entries; + this->p_captures = std::move(other.p_captures); + + return *this; + } + + const std::string& get_pattern() const { + return this->p_pattern; + } + + bool empty() const { + return this->p_pattern.empty(); + } + + void clear() { + if (this->p_code && pcre_refcount(this->p_code, -1) == 0) { + free(this->p_code); + this->p_code = nullptr; + } + this->p_pattern.clear(); + this->p_code_extra.reset(); + this->p_capture_count = 0; + this->p_named_count = 0; + this->p_name_len = 0; + this->p_options = 0; + this->p_named_entries = nullptr; + this->p_captures.clear(); + } + pcre_named_capture::iterator named_begin() const { return {this->p_named_entries, static_cast(this->p_name_len)}; }; @@ -480,6 +563,8 @@ public: bool match(pcre_context &pc, pcre_input &pi, int options = 0) const; + std::string replace(const char *str, const char *repl) const; + size_t match_partial(pcre_input &pi) const { size_t length = pi.pi_length; int rc; @@ -516,13 +601,14 @@ public: void find_captures(const char *pattern); - pcre *p_code; - const std::string p_pattern; + pcre *p_code{nullptr}; + std::string p_pattern; auto_mem p_code_extra; - int p_capture_count; - int p_named_count; - int p_name_len; - pcre_named_capture *p_named_entries; + int p_capture_count{0}; + int p_named_count{0}; + int p_name_len{0}; + unsigned long p_options{0}; + pcre_named_capture *p_named_entries{nullptr}; std::vector p_captures; }; diff --git a/src/readline_callbacks.cc b/src/readline_callbacks.cc index a9aeb9a6..1c7c655e 100644 --- a/src/readline_callbacks.cc +++ b/src/readline_callbacks.cc @@ -39,7 +39,6 @@ #include "command_executor.hh" #include "readline_curses.hh" #include "readline_highlighters.hh" -#include "log_search_table.hh" #include "log_format_loader.hh" #include "help_text_formatter.hh" #include "sqlite-extension-func.hh" diff --git a/src/readline_possibilities.cc b/src/readline_possibilities.cc index c81e53a7..551673ef 100644 --- a/src/readline_possibilities.cc +++ b/src/readline_possibilities.cc @@ -29,8 +29,7 @@ #include "config.h" -#include - +#include #include #include "lnav.hh" @@ -125,8 +124,8 @@ struct sqlite_metadata_callbacks lnav_sql_meta_callbacks = { static void add_text_possibilities(readline_curses *rlc, int context, const string &type, const std::string &str) { - static pcrecpp::RE re_escape(R"(([.\^$*+?()\[\]{}\\|]))"); - static pcrecpp::RE re_escape_no_dot(R"(([\^$*+?()\[\]{}\\|]))"); + static std::regex re_escape(R"(([.\^$*+?()\[\]{}\\|]))"); + static std::regex re_escape_no_dot(R"(([\^$*+?()\[\]{}\\|]))"); pcre_context_static<30> pc; data_scanner ds(str); @@ -160,8 +159,8 @@ static void add_text_possibilities(readline_curses *rlc, int context, const stri token_value_no_dot = token_value = ds.get_input().get_substr(pc.all()); - re_escape.GlobalReplace(R"(\\\1)", &token_value); - re_escape_no_dot.GlobalReplace(R"(\\\1)", &token_value_no_dot); + token_value = std::regex_replace(token_value, re_escape, R"(\\\1)"); + token_value_no_dot = std::regex_replace(token_value_no_dot, re_escape_no_dot, R"(\\\1)"); rlc->add_possibility(context, type, token_value); if (token_value != token_value_no_dot) { rlc->add_possibility(context, type, token_value_no_dot); @@ -364,7 +363,7 @@ void add_filter_possibilities(textview_curses *tc) void add_file_possibilities() { - static pcrecpp::RE sh_escape(R"(([\s\'\"]+))"); + static std::regex sh_escape(R"(([\s\'\"]+))"); readline_curses *rc = lnav_data.ld_rl_view; @@ -376,8 +375,7 @@ void add_file_possibilities() } lnav_data.ld_log_source.find_data(lf) | [&lf, rc](auto ld) { - auto escaped_fn = lf->get_filename(); - sh_escape.GlobalReplace(R"(\\\1)", &escaped_fn); + auto escaped_fn = std::regex_replace(lf->get_filename(), sh_escape, R"(\\\1)"); rc->add_possibility(LNM_COMMAND, ld->is_visible() ? "visible-files" : "hidden-files", diff --git a/src/regexp_vtab.cc b/src/regexp_vtab.cc index a7182a71..72bc1c6a 100644 --- a/src/regexp_vtab.cc +++ b/src/regexp_vtab.cc @@ -68,10 +68,9 @@ CREATE TABLE regexp_capture ( struct cursor { sqlite3_vtab_cursor base; - unique_ptr c_pattern; + pcrepp c_pattern; pcre_context_static<30> c_context; unique_ptr c_input; - string c_pattern_string; string c_content; int c_index; int c_start_index; @@ -95,12 +94,12 @@ CREATE TABLE regexp_capture ( int next() { if (this->c_index >= (this->c_context.get_count() - 1)) { this->c_input->pi_offset = this->c_input->pi_next_offset; - this->c_matched = this->c_pattern->match(this->c_context, *(this->c_input)); + this->c_matched = this->c_pattern.match(this->c_context, *(this->c_input)); this->c_index = -1; this->c_match_index += 1; } - if (!this->c_pattern || !this->c_matched) { + if (this->c_pattern.empty() || !this->c_matched) { return SQLITE_OK; } @@ -110,7 +109,7 @@ CREATE TABLE regexp_capture ( }; int eof() { - return !this->c_pattern || !this->c_matched; + return this->c_pattern.empty() || !this->c_matched; }; int get_rowid(sqlite3_int64 &rowid_out) { @@ -134,7 +133,7 @@ CREATE TABLE regexp_capture ( if (vc.c_index == 0) { sqlite3_result_null(ctx); } else { - sqlite3_result_text(ctx, vc.c_pattern->name_for_capture( + sqlite3_result_text(ctx, vc.c_pattern.name_for_capture( vc.c_index - 1), -1, SQLITE_TRANSIENT); } break; @@ -163,12 +162,13 @@ CREATE TABLE regexp_capture ( vc.c_content.length(), SQLITE_TRANSIENT); break; - case RC_COL_PATTERN: - sqlite3_result_text(ctx, - vc.c_pattern_string.c_str(), - vc.c_pattern_string.length(), + case RC_COL_PATTERN: { + auto str = vc.c_pattern.get_pattern(); + + sqlite3_result_text(ctx, str.c_str(), str.length(), SQLITE_TRANSIENT); break; + } } return SQLITE_OK; @@ -205,7 +205,7 @@ static int rcFilter(sqlite3_vtab_cursor *pVtabCursor, if (argc != 2) { pCur->c_content.clear(); - pCur->c_pattern.reset(); + pCur->c_pattern.clear(); return SQLITE_OK; } @@ -214,20 +214,20 @@ static int rcFilter(sqlite3_vtab_cursor *pVtabCursor, pCur->c_content = value; - try { - pCur->c_pattern = make_unique(pattern); - pCur->c_pattern_string = pattern; - } catch (const pcrepp::error &e) { + auto re_res = pcrepp::from_str(pattern); + if (re_res.isErr()) { pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf( - "Invalid regular expression: %s", e.e_msg.c_str()); + "Invalid regular expression: %s", re_res.unwrapErr().ce_msg); return SQLITE_ERROR; } + pCur->c_pattern = re_res.unwrap(); + pCur->c_index = 0; pCur->c_context.set_count(0); pCur->c_input = make_unique(pCur->c_content); - pCur->c_matched = pCur->c_pattern->match(pCur->c_context, *(pCur->c_input)); + pCur->c_matched = pCur->c_pattern.match(pCur->c_context, *(pCur->c_input)); return SQLITE_OK; } diff --git a/src/sql_util.cc b/src/sql_util.cc index c01447b4..4c794d2c 100644 --- a/src/sql_util.cc +++ b/src/sql_util.cc @@ -34,8 +34,8 @@ #include #include #include -#include +#include #include #include @@ -471,7 +471,7 @@ void dump_sqlite_schema(sqlite3 *db, std::string &schema_out) void attach_sqlite_db(sqlite3 *db, const std::string &filename) { - static pcrecpp::RE db_name_converter("[^\\w]"); + static std::regex db_name_converter("[^\\w]"); auto_mem stmt(sqlite3_finalize); @@ -503,7 +503,7 @@ void attach_sqlite_db(sqlite3 *db, const std::string &filename) db_name = filename.substr(base_start + 1); } - db_name_converter.GlobalReplace("_", &db_name); + db_name = std::regex_replace(db_name, db_name_converter, "_"); if (sqlite3_bind_text(stmt.in(), 2, db_name.c_str(), db_name.length(), @@ -820,7 +820,7 @@ static struct { int guess_type_from_pcre(const string &pattern, std::string &collator) { try { - pcrepp re(pattern.c_str()); + pcrepp re(pattern); vector matches; int retval = SQLITE3_TEXT; int index = 0; diff --git a/src/sql_util.hh b/src/sql_util.hh index c9ad388b..13577f9c 100644 --- a/src/sql_util.hh +++ b/src/sql_util.hh @@ -58,7 +58,7 @@ struct sqlite_metadata_callbacks { sqlite_exec_callback smc_table_info; sqlite_exec_callback smc_foreign_key_list; void *smc_userdata{nullptr}; - db_table_map_t smc_db_list; + db_table_map_t smc_db_list{}; }; int walk_sqlite_metadata(sqlite3 *db, struct sqlite_metadata_callbacks &smc); diff --git a/src/sqlite-extension-func.hh b/src/sqlite-extension-func.hh index 1e9de588..db09c2e6 100644 --- a/src/sqlite-extension-func.hh +++ b/src/sqlite-extension-func.hh @@ -41,12 +41,12 @@ #include "help_text_formatter.hh" struct FuncDef { - const char *zName; - signed char nArg; - int eTextRep; /* 1: UTF-16. 0: UTF-8 */ - uint8_t needCollSeq; - void (*xFunc)(sqlite3_context*,int,sqlite3_value **); - help_text fd_help; + const char *zName{nullptr}; + signed char nArg{0}; + int eTextRep{0}; /* 1: UTF-16. 0: UTF-8 */ + uint8_t needCollSeq{0}; + void (*xFunc)(sqlite3_context*,int,sqlite3_value **){nullptr}; + help_text fd_help{}; FuncDef &with_flags(int flags) { this->eTextRep = flags; @@ -55,12 +55,12 @@ struct FuncDef { }; struct FuncDefAgg { - const char *zName; - signed char nArg; - uint8_t needCollSeq; - void (*xStep)(sqlite3_context*,int,sqlite3_value**); - void (*xFinalize)(sqlite3_context*); - help_text fda_help; + const char *zName{nullptr}; + signed char nArg{0}; + uint8_t needCollSeq{0}; + void (*xStep)(sqlite3_context*,int,sqlite3_value**){nullptr}; + void (*xFinalize)(sqlite3_context*){nullptr}; + help_text fda_help{}; }; typedef int (*sqlite_registration_func_t)(struct FuncDef **basic_funcs, diff --git a/src/string-extension-functions.cc b/src/string-extension-functions.cc index 758f5515..719fe8f2 100644 --- a/src/string-extension-functions.cc +++ b/src/string-extension-functions.cc @@ -12,7 +12,6 @@ #include #include #include -#include #include @@ -38,7 +37,6 @@ using namespace std; using namespace mapbox; typedef struct { - shared_ptr re; shared_ptr re2; } cache_entry; @@ -55,13 +53,6 @@ static cache_entry *find_re(const char *re) cache_entry c; c.re2 = make_shared(re_str); - c.re = make_shared(re); - if (!c.re->error().empty()) { - auto_mem e2; - - e2 = sqlite3_mprintf("%s: %s", re, c.re->error().c_str()); - throw pcrepp::error(e2.in(), 0); - } auto pair = wcache->insert(std::make_pair(re_str, c)); iter = pair.first; @@ -73,8 +64,10 @@ static cache_entry *find_re(const char *re) static bool regexp(const char *re, const char *str) { cache_entry *reobj = find_re(re); + pcre_context_static<30> pc; + pcre_input pi(str); - return reobj->re->PartialMatch(str); + return reobj->re2->match(pc, pi); } static @@ -193,10 +186,8 @@ static string regexp_replace(const char *str, const char *re, const char *repl) { cache_entry *reobj = find_re(re); - string dest(str); - reobj->re->GlobalReplace(repl, &dest); - return dest; + return reobj->re2->replace(str, repl); } static diff --git a/src/textview_curses.cc b/src/textview_curses.cc index aead7b97..63a11dcc 100644 --- a/src/textview_curses.cc +++ b/src/textview_curses.cc @@ -32,8 +32,6 @@ #include #include -#include - #include "base/time_util.hh" #include "shlex.hh" #include "fmt/format.h" @@ -257,10 +255,10 @@ void textview_curses::grep_end_batch(grep_proc &gp) } else { if (this->tc_follow_func) { if (this->tc_follow_func()) { - this->tc_follow_deadline = {0}; + this->tc_follow_deadline = {0, 0}; } } else { - this->tc_follow_deadline = {0}; + this->tc_follow_deadline = {0, 0}; } } } @@ -532,7 +530,7 @@ void textview_curses::execute_search(const std::string ®ex_orig) nullptr)) == nullptr) { string errmsg = string(errptr); - regex = pcrecpp::RE::QuoteMeta(regex); + regex = pcrepp::quote(regex); log_info("invalid search regex, using quoted: %s", regex.c_str()); if ((code = pcre_compile(regex.c_str(), diff --git a/src/yajlpp/Makefile.am b/src/yajlpp/Makefile.am index ff7f2096..ea236d6d 100644 --- a/src/yajlpp/Makefile.am +++ b/src/yajlpp/Makefile.am @@ -1,8 +1,11 @@ +include $(top_srcdir)/aminclude_static.am + TESTS_ENVIRONMENT = $(SHELL) $(top_builddir)/TESTS_ENVIRONMENT LOG_COMPILER = $(SHELL) $(top_builddir)/TESTS_ENVIRONMENT AM_CPPFLAGS = \ + $(CODE_COVERAGE_CPPFLAGS) \ -Wall \ $(LIBARCHIVE_CFLAGS) \ -I$(top_srcdir)/src/ \ @@ -12,6 +15,10 @@ AM_LDFLAGS = \ $(LIBARCHIVE_LDFLAGS) \ $(STATIC_LDFLAGS) +AM_LIBS = $(CODE_COVERAGE_LIBS) +AM_CFLAGS = $(CODE_COVERAGE_CFLAGS) +AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) + noinst_LIBRARIES = libyajlpp.a noinst_HEADERS = \ diff --git a/src/yajlpp/yajlpp.cc b/src/yajlpp/yajlpp.cc index 14a545ca..791088e5 100644 --- a/src/yajlpp/yajlpp.cc +++ b/src/yajlpp/yajlpp.cc @@ -31,7 +31,8 @@ #include "config.h" -#include +#include +#include #include "fmt/format.h" @@ -47,7 +48,7 @@ json_path_handler_base::json_path_handler_base(const string &property) : jph_property(property.back() == '#' ? property.substr(0, property.size() - 1) : property), - jph_regex(pcrecpp::RE::QuoteMeta(property), PCRE_ANCHORED), + jph_regex(pcrepp::quote(property), PCRE_ANCHORED), jph_is_array(property.back() == '#') { memset(&this->jph_callbacks, 0, sizeof(this->jph_callbacks)); @@ -55,12 +56,9 @@ json_path_handler_base::json_path_handler_base(const string &property) static std::string scrub_pattern(const std::string &pattern) { - static pcrecpp::RE CAPTURE(R"(\(\?\<\w+\>)"); - std::string retval = pattern; + static std::regex CAPTURE(R"(\(\?\<\w+\>)"); - CAPTURE.GlobalReplace("(", &retval); - - return retval; + return std::regex_replace(pattern, CAPTURE, "("); } json_path_handler_base::json_path_handler_base(const pcrepp &property) @@ -72,9 +70,9 @@ json_path_handler_base::json_path_handler_base(const pcrepp &property) memset(&this->jph_callbacks, 0, sizeof(this->jph_callbacks)); } -json_path_handler_base::json_path_handler_base(const string &property, +json_path_handler_base::json_path_handler_base(string property, const pcrepp &property_re) - : jph_property(property), + : jph_property(std::move(property)), jph_regex(property_re), jph_is_array(property_re.p_pattern.find('#') != string::npos) { diff --git a/src/yajlpp/yajlpp.hh b/src/yajlpp/yajlpp.hh index e9ee84fb..656a90ea 100644 --- a/src/yajlpp/yajlpp.hh +++ b/src/yajlpp/yajlpp.hh @@ -136,7 +136,7 @@ struct json_path_handler_base { explicit json_path_handler_base(const pcrepp& property); - json_path_handler_base(const std::string &property, + json_path_handler_base(std::string property, const pcrepp& property_re); bool is_array() const { diff --git a/test/Makefile.am b/test/Makefile.am index babfee9d..5dd637ae 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,6 @@ +include $(top_srcdir)/aminclude_static.am + TESTS_ENVIRONMENT = $(SHELL) $(top_builddir)/TESTS_ENVIRONMENT LOG_COMPILER = $(SHELL) $(top_builddir)/TESTS_ENVIRONMENT @@ -6,10 +8,15 @@ RM_V = $(RM_V_@AM_V@) RM_V_ = $(RM_V_@AM_DEFAULT_V@) RM_V_0 = @echo " RM " $@; +AM_LIBS = $(CODE_COVERAGE_LIBS) +AM_CFLAGS = $(CODE_COVERAGE_CFLAGS) +AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) + AM_CPPFLAGS = \ -Wall \ -I$(top_srcdir)/src \ -I$(top_srcdir)/src/fmtlib \ + $(CODE_COVERAGE_CPPFLAGS) \ $(LIBARCHIVE_CFLAGS) \ $(READLINE_CFLAGS) \ $(SQLITE3_CFLAGS) @@ -83,8 +90,7 @@ LDADD = \ $(SQLITE3_LIBS) \ $(PCRE_LIBS) \ $(READLINE_LIBS) \ - $(LIBCURL) \ - -lpcrecpp + $(LIBCURL) test_ansi_scrubber_SOURCES = test_ansi_scrubber.cc diff --git a/test/test_abbrev.cc b/test/test_abbrev.cc index 1837b4f2..58e16750 100644 --- a/test/test_abbrev.cc +++ b/test/test_abbrev.cc @@ -44,6 +44,7 @@ static struct test_data { { "no dots in here", "no dots in here", 5 }, }; + int main(int argc, char *argv[]) { for (const auto& td : TEST_DATA) { diff --git a/test/test_auto_mem.cc b/test/test_auto_mem.cc index c7b35403..f9c1e026 100644 --- a/test/test_auto_mem.cc +++ b/test/test_auto_mem.cc @@ -29,11 +29,8 @@ #include "config.h" -#include -#include #include #include -#include #include "auto_mem.hh" @@ -59,7 +56,7 @@ int main(int argc, char *argv[]) md1 = &md1_val; assert(free_count == 0); - md1 = md2; + md1 = std::move(md2); assert(free_count == 1); assert(last_free == &md1_val); assert(md1 == NULL); diff --git a/test/test_pcrepp.cc b/test/test_pcrepp.cc index cf9d1bc8..f50a3cff 100644 --- a/test/test_pcrepp.cc +++ b/test/test_pcrepp.cc @@ -41,7 +41,7 @@ int main(int argc, char *argv[]) { pcre_context_static<30> context; int retval = EXIT_SUCCESS; - + { pcrepp nomatch("nothing-to-match"); pcre_input pi("dummy"); diff --git a/test/test_sql_str_func.sh b/test/test_sql_str_func.sh index 59c3c945..4665d0d3 100644 --- a/test/test_sql_str_func.sh +++ b/test/test_sql_str_func.sh @@ -77,6 +77,41 @@ Row 0: Column regexp_replace('test 1 2 3', '\d+', 'N'): test N N N EOF +run_test ./drive_sql "select regexp_replace('test 1 2 3', '\\s+', '{\\0}') as repl" + +check_output "" <') as repl" + +check_output "" <<\3> <\3><\3> +EOF + +run_test ./drive_sql "select regexp_replace('123 abc', '(\w*)', '<\\\\>') as repl" + +check_output "" <<\> <\><\> +EOF + +run_test ./drive_sql "select regexp_replace('abc: def', '(\w*):\s*(.*)', '\1=\2') as repl" + +check_output "" <