Skip to content

Commit

Permalink
Support PCRE2
Browse files Browse the repository at this point in the history
  • Loading branch information
ellert committed Oct 2, 2023
1 parent 76e4705 commit f89fc64
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 15 deletions.
1 change: 1 addition & 0 deletions bindings/pyroot/pythonizations/test/import_load_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class ImportLoadLibs(unittest.TestCase):
'libc',
'libdl',
'libpcre',
'libpcre2-8',
# libCling and dependencies
'libCling.*',
'librt',
Expand Down
106 changes: 106 additions & 0 deletions cmake/modules/FindPCRE2.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Copyright (C) 1995-2019, Rene Brun and Fons Rademakers.
# All rights reserved.
#
# For the licensing terms see $ROOTSYS/LICENSE.
# For the list of contributors see $ROOTSYS/README/CREDITS.

#.rst:
# FindPCRE2
# --------
#
# Find PCRE2 library
#
# Imported Targets
# ^^^^^^^^^^^^^^^^
#
# This module defines :prop_tgt:`IMPORTED` target:
#
# ``PCRE2::PCRE2``
# The pcre2 library, if found.
#
# Result Variables
# ^^^^^^^^^^^^^^^^
# This module will set the following variables in your project:
#
# ``PCRE2_FOUND``
# True if PCRE2 has been found.
# ``PCRE2_INCLUDE_DIRS``
# Where to find pcre2.h
# ``PCRE2_LIBRARIES``
# The libraries to link against to use PCRE2.
# ``PCRE2_VERSION``
# The version of the PCRE2 found (e.g. 10.42)
#
# Obsolete variables
# ^^^^^^^^^^^^^^^^^^
#
# The following variables may also be set, for backwards compatibility:
#
# ``PCRE2_PCRE2_LIBRARY``
# where to find the PCRE2_PCRE2 library.
# ``PCRE2_INCLUDE_DIR``
# where to find the pcre2.h header (same as PCRE2_INCLUDE_DIRS)
#

foreach(var PCRE2_FOUND PCRE2_INCLUDE_DIR PCRE2_PCRE2_LIBRARY PCRE2_LIBRARIES)
unset(${var} CACHE)
endforeach()

find_path(PCRE2_INCLUDE_DIR NAMES pcre2.h PATH_SUFFIXES include)
mark_as_advanced(PCRE2_INCLUDE_DIR)

if (PCRE2_INCLUDE_DIR AND EXISTS "${PCRE2_INCLUDE_DIR}/pcre2.h")
file(STRINGS "${PCRE2_INCLUDE_DIR}/pcre2.h" PCRE2_H REGEX "^#define PCRE2_(MAJOR|MINOR).*$")
string(REGEX REPLACE "^.*PCRE2_MAJOR[ ]+([0-9]+).*$" "\\1" PCRE2_VERSION_MAJOR "${PCRE2_H}")
string(REGEX REPLACE "^.*PCRE2_MINOR[ ]+([0-9]+).*$" "\\1" PCRE2_VERSION_MINOR "${PCRE2_H}")
set(PCRE2_VERSION "${PCRE2_VERSION_MAJOR}.${PCRE2_VERSION_MINOR}")
endif()

if(NOT PCRE2_PCRE2_LIBRARY)
find_library(PCRE2_PCRE2_LIBRARY_RELEASE NAMES pcre2-8)
find_library(PCRE2_PCRE2_LIBRARY_DEBUG NAMES pcre2-8${CMAKE_DEBUG_POSTFIX} pcre2-8d)
include(SelectLibraryConfigurations)
select_library_configurations(PCRE2_PCRE2)
endif()

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PCRE2
REQUIRED_VARS
PCRE2_INCLUDE_DIR
PCRE2_PCRE2_LIBRARY
VERSION_VAR
PCRE2_VERSION
)

if(PCRE2_FOUND)
set(PCRE2_INCLUDE_DIRS "${PCRE2_INCLUDE_DIR}")

if (NOT PCRE2_LIBRARIES)
set(PCRE2_LIBRARIES "${PCRE2_PCRE2_LIBRARY}")
endif()

if(NOT TARGET PCRE2::PCRE2)
add_library(PCRE2::PCRE2 UNKNOWN IMPORTED)
set_target_properties(PCRE2::PCRE2 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${PCRE2_INCLUDE_DIRS}")

if(PCRE2_PCRE2_LIBRARY_DEBUG)
set_property(TARGET PCRE2::PCRE2 APPEND PROPERTY
IMPORTED_CONFIGURATIONS DEBUG)
set_target_properties(PCRE2::PCRE2 PROPERTIES
IMPORTED_LOCATION_DEBUG "${PCRE2_PCRE2_LIBRARY_DEBUG}")
endif()

if(PCRE2_PCRE2_LIBRARY_RELEASE)
set_property(TARGET PCRE2::PCRE2 APPEND PROPERTY
IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(PCRE2::PCRE2 PROPERTIES
IMPORTED_LOCATION_RELEASE "${PCRE2_PCRE2_LIBRARY_RELEASE}")
endif()

if(NOT PCRE2_PCRE2_LIBRARY_DEBUG AND NOT PCRE2_PCRE2_LIBRARY_RELEASE)
set_property(TARGET PCRE2::PCRE2 APPEND PROPERTY
IMPORTED_LOCATION "${PCRE2_PCRE2_LIBRARY}")
endif()
endif()
endif()
17 changes: 10 additions & 7 deletions cmake/modules/SearchInstalledSoftware.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,16 @@ if(NOT builtin_pcre)
foreach(suffix FOUND INCLUDE_DIR PCRE_LIBRARY)
unset(PCRE_${suffix} CACHE)
endforeach()
if(fail-on-missing)
find_package(PCRE REQUIRED)
else()
find_package(PCRE)
if(NOT PCRE_FOUND)
message(STATUS "PCRE not found. Switching on builtin_pcre option")
set(builtin_pcre ON CACHE BOOL "Enabled because PCRE not found (${builtin_pcre_description})" FORCE)
find_package(PCRE2)
if(NOT PCRE2_FOUND)
if(fail-on-missing)
find_package(PCRE REQUIRED)
else()
find_package(PCRE)
if(NOT PCRE_FOUND)
message(STATUS "PCRE not found. Switching on builtin_pcre option")
set(builtin_pcre ON CACHE BOOL "Enabled because PCRE not found (${builtin_pcre_description})" FORCE)
endif()
endif()
endif()
endif()
Expand Down
9 changes: 8 additions & 1 deletion core/base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,14 @@ target_include_directories(Core PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/v7/inc>
)

target_link_libraries(Core PRIVATE PCRE::PCRE)
if(PCRE2_FOUND)
target_link_libraries(Core PRIVATE PCRE2::PCRE2)
set_source_files_properties(src/TPRegexp.cxx
TARGET_DIRECTORY Core
PROPERTIES COMPILE_DEFINITIONS USE_PCRE2)
else()
target_link_libraries(Core PRIVATE PCRE::PCRE)
endif()

ROOT_INSTALL_HEADERS(${BASE_HEADER_DIRS})

Expand Down
99 changes: 95 additions & 4 deletions core/base/src/TPRegexp.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,36 @@ found at : http://perldoc.perl.org/perlre.html
#include "TObjString.h"
#include "TError.h"

#ifdef USE_PCRE2
#ifdef R__WIN32
#define PCRE2_STATIC
#endif
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#define PCRE_CASELESS PCRE2_CASELESS
#define PCRE_MULTILINE PCRE2_MULTILINE
#define PCRE_DOTALL PCRE2_DOTALL
#define PCRE_EXTENDED PCRE2_EXTENDED
#define PCRE_ERROR_NOMATCH PCRE2_ERROR_NOMATCH
#else
#ifdef R__WIN32
#define PCRE_STATIC
#endif
#include <pcre.h>
#endif

#include <vector>
#include <stdexcept>

struct PCREPriv_t {
#ifdef USE_PCRE2
pcre2_code *fPCRE;
PCREPriv_t() { fPCRE = nullptr; }
#else
pcre *fPCRE;
pcre_extra *fPCREExtra;

PCREPriv_t() { fPCRE = nullptr; fPCREExtra = nullptr; }
#endif
};


Expand Down Expand Up @@ -79,10 +96,15 @@ TPRegexp::TPRegexp(const TPRegexp &p)

TPRegexp::~TPRegexp()
{
#ifdef USE_PCRE2
if (fPriv->fPCRE)
pcre2_code_free(fPriv->fPCRE);
#else
if (fPriv->fPCRE)
pcre_free(fPriv->fPCRE);
if (fPriv->fPCREExtra)
pcre_free(fPriv->fPCREExtra);
#endif
delete fPriv;
}

Expand All @@ -93,12 +115,18 @@ TPRegexp &TPRegexp::operator=(const TPRegexp &p)
{
if (this != &p) {
fPattern = p.fPattern;
#ifdef USE_PCRE2
if (fPriv->fPCRE)
pcre2_code_free(fPriv->fPCRE);
fPriv->fPCRE = nullptr;
#else
if (fPriv->fPCRE)
pcre_free(fPriv->fPCRE);
fPriv->fPCRE = nullptr;
if (fPriv->fPCREExtra)
pcre_free(fPriv->fPCREExtra);
fPriv->fPCREExtra = nullptr;
#endif
fPCREOpts = p.fPCREOpts;
}
return *this;
Expand Down Expand Up @@ -197,38 +225,58 @@ TString TPRegexp::GetModifiers() const

void TPRegexp::Compile()
{
#ifdef USE_PCRE2
if (fPriv->fPCRE)
pcre2_code_free(fPriv->fPCRE);
#else
if (fPriv->fPCRE)
pcre_free(fPriv->fPCRE);
#endif

if (fPCREOpts & kPCRE_DEBUG_MSGS)
Info("Compile", "PREGEX compiling %s", fPattern.Data());

#ifdef USE_PCRE2
int errcode;
PCRE2_SIZE patIndex;
fPriv->fPCRE = pcre2_compile((PCRE2_SPTR)fPattern.Data(), fPattern.Length(),
fPCREOpts & kPCRE_INTMASK,
&errcode, &patIndex, nullptr);
#else
const char *errstr;
Int_t patIndex;
fPriv->fPCRE = pcre_compile(fPattern.Data(), fPCREOpts & kPCRE_INTMASK,
&errstr, &patIndex, nullptr);
#endif

if (!fPriv->fPCRE) {
#ifdef USE_PCRE2
PCRE2_UCHAR errstr[256];
pcre2_get_error_message(errcode, errstr, 256);
#endif
if (fgThrowAtCompileError) {
throw std::runtime_error
(TString::Format("TPRegexp::Compile() compilation of TPRegexp(%s) failed at: %d because %s",
fPattern.Data(), patIndex, errstr).Data());
fPattern.Data(), (int)patIndex, errstr).Data());
} else {
Error("Compile", "compilation of TPRegexp(%s) failed at: %d because %s",
fPattern.Data(), patIndex, errstr);
fPattern.Data(), (int)patIndex, errstr);
return;
}
}

#ifndef USE_PCRE2
if (fPriv->fPCREExtra || (fPCREOpts & kPCRE_OPTIMIZE))
Optimize();
#endif
}

////////////////////////////////////////////////////////////////////////////////
/// Send the pattern through the optimizer.

void TPRegexp::Optimize()
{
#ifndef USE_PCRE2
if (fPriv->fPCREExtra)
pcre_free(fPriv->fPCREExtra);

Expand All @@ -243,6 +291,7 @@ void TPRegexp::Optimize()
Error("Optimize", "Optimization of TPRegexp(%s) failed: %s",
fPattern.Data(), errstr);
}
#endif
}

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -308,21 +357,43 @@ Int_t TPRegexp::MatchInternal(const TString &s, Int_t start,
Int_t nMaxMatch, TArrayI *pos) const
{
Int_t *offVec = new Int_t[3*nMaxMatch];

#ifdef USE_PCRE2
pcre2_match_data *match_data;
match_data = pcre2_match_data_create_from_pattern(fPriv->fPCRE, nullptr);
Int_t nrMatch = pcre2_match(fPriv->fPCRE, (PCRE2_SPTR8)s.Data(),
s.Length(), start, 0,
match_data, nullptr);
#else
// pcre_exec allows less options - see pcre_internal.h PUBLIC_EXEC_OPTIONS.
Int_t nrMatch = pcre_exec(fPriv->fPCRE, fPriv->fPCREExtra, s.Data(),
s.Length(), start, 0,
offVec, 3*nMaxMatch);
#endif

if (nrMatch == PCRE_ERROR_NOMATCH)
nrMatch = 0;
else if (nrMatch <= 0) {
Error("Match","pcre_exec error = %d", nrMatch);
#ifdef USE_PCRE2
pcre2_match_data_free(match_data);
#endif
delete [] offVec;
return 0;
}

if (pos)
if (pos) {
#ifdef USE_PCRE2
PCRE2_SIZE *oVec = pcre2_get_ovector_pointer(match_data);
for (int i = 0; i < 2 * nrMatch; ++i)
offVec[i] = oVec[i];
#endif
pos->Set(2*nrMatch, offVec);
}

#ifdef USE_PCRE2
pcre2_match_data_free(match_data);
#endif
delete [] offVec;

return nrMatch;
Expand Down Expand Up @@ -404,13 +475,24 @@ Int_t TPRegexp::SubstituteInternal(TString &s, const TString &replacePattern,
Int_t offset = start;
Int_t last = 0;

#ifdef USE_PCRE2
pcre2_match_data *match_data;
match_data = pcre2_match_data_create_from_pattern(fPriv->fPCRE, nullptr);
#endif

while (kTRUE) {

// find next matching subs
// pcre_exec allows less options - see pcre_internal.h PUBLIC_EXEC_OPTIONS.
#ifdef USE_PCRE2
Int_t nrMatch = pcre2_match(fPriv->fPCRE, (PCRE2_SPTR)s.Data(),
s.Length(), offset, 0,
match_data, nullptr);
#else
Int_t nrMatch = pcre_exec(fPriv->fPCRE, fPriv->fPCREExtra, s.Data(),
s.Length(), offset, 0,
offVec, 3*nMaxMatch);
#endif

if (nrMatch == PCRE_ERROR_NOMATCH) {
break;
Expand All @@ -419,6 +501,12 @@ Int_t TPRegexp::SubstituteInternal(TString &s, const TString &replacePattern,
break;
}

#ifdef USE_PCRE2
PCRE2_SIZE *oVec = pcre2_get_ovector_pointer(match_data);
for (int i = 0; i < 2 * nrMatch; ++i)
offVec[i] = oVec[i];
#endif

// append anything previously unmatched, but not substituted
if (last <= offVec[0]) {
fin += s(last,offVec[0]-last);
Expand Down Expand Up @@ -446,6 +534,9 @@ Int_t TPRegexp::SubstituteInternal(TString &s, const TString &replacePattern,
}
}

#ifdef USE_PCRE2
pcre2_match_data_free(match_data);
#endif
delete [] offVec;

fin += s(last,s.Length()-last);
Expand Down
Loading

0 comments on commit f89fc64

Please sign in to comment.