[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1000113: kodi: depends on obsolete pcre3 library



Control: tags -1 + patch

Please find attached a patch; I did my best to test it.

I believe the changes to the CRegExp class (xbmc/utils/RegExp.*) are
sane; they are tested in xbmc/utils/test/TestRegexp.cpp and some other
test programs that use CRegExp.  I added three more test cases:
invalid pattern, UTF-8 compilation/matching, and JIT.  The latter two
should pass on all the buildds since PCRE2 is built everywhere with
Unicode support, and on those architectures where JIT support is not
available, m_JitSupported will be false so the related functions in
the methods RegComp and PrivateRegFind will not be invoked.
Furthermore, according to the PCRE2 documentation, an application
doesn't need to check explicitly for JIT support because all of the
PCRE2 JIT-related functions have dummy placeholders and silently do
nothing if JIT support is not available.

The changes to CFTPParse (xbmc/filesystem/FTPParse.cpp) are clumsy,
overly verbose code; I'm not proud of it at all.  I tried to minimize
casting as much as possible.  AFAICS it's used only to obtain an ftp
directory contents.  I tried adding some directories from
ftp.gnustep.org as source, but of course there are no media files
there.  Then I installed an FTP server and populated /srv/ftp/ with
some video files.  Kodi displays their properties and plays them, but
I cannot be sure that CFTPParse methods are actually used.  I can't
put breakpoints in gdb as the app runs in fullscreen mode and I don't
know how to switch to normal mode.  (In hindsight, I guess I could
have added some printf statements but it's too late now and the beast
takes nearly 4 hours to build on my machine.)  An unpleasant obstacle
was that Kodi frequently crashes my videocard driver (nouveau) which
made testing very frustrating.

I guess the patch needs more testing and a closer look by someone
familiar with this package (ideally both as a user and maintainer), on
a machine that is capable of running Kodi.

Please let me know if there are problems that require correction.
Description: Port to PCRE2.
Bug-Debian: https://bugs.debian.org/1000113
Author: Yavor Doganov <yavor@gnu.org>
Forwarded: no
Last-Update: 2024-01-07
---

--- kodi-20.2+dfsg.orig/cmake/modules/FindPCRE.cmake
+++ kodi-20.2+dfsg/cmake/modules/FindPCRE.cmake
@@ -77,45 +77,34 @@
 
     else()
       # Populate paths for find_package_handle_standard_args
-      find_path(PCRE_INCLUDE_DIR pcre.h)
+      find_path(PCRE_INCLUDE_DIR pcre2.h)
 
-      find_library(PCRECPP_LIBRARY_RELEASE NAMES pcrecpp)
-      find_library(PCRECPP_LIBRARY_DEBUG NAMES pcrecppd)
-
-      find_library(PCRE_LIBRARY_RELEASE NAMES pcre)
-      find_library(PCRE_LIBRARY_DEBUG NAMES pcred)
+      find_library(PCRE_LIBRARY_RELEASE NAMES pcre2-8)
     endif()
   else()
 
     if(PKG_CONFIG_FOUND)
-      pkg_check_modules(PC_PCRE libpcrecpp QUIET)
+      pkg_check_modules(PC_PCRE libpcre2-8 QUIET)
     endif()
 
-    find_path(PCRE_INCLUDE_DIR pcrecpp.h
+    find_path(PCRE_INCLUDE_DIR pcre2.h
                                PATHS ${PC_PCRE_INCLUDEDIR})
-    find_library(PCRECPP_LIBRARY_RELEASE NAMES pcrecpp
-                                         PATHS ${PC_PCRE_LIBDIR})
-    find_library(PCRE_LIBRARY_RELEASE NAMES pcre
+    find_library(PCRE_LIBRARY_RELEASE NAMES pcre2-8
                                       PATHS ${PC_PCRE_LIBDIR})
-    find_library(PCRECPP_LIBRARY_DEBUG NAMES pcrecppd
-                                       PATHS ${PC_PCRE_LIBDIR})
-    find_library(PCRE_LIBRARY_DEBUG NAMES pcred
-                                       PATHS ${PC_PCRE_LIBDIR})
     set(PCRE_VERSION ${PC_PCRE_VERSION})
 
   endif()
 
   include(SelectLibraryConfigurations)
-  select_library_configurations(PCRECPP)
   select_library_configurations(PCRE)
 
   include(FindPackageHandleStandardArgs)
   find_package_handle_standard_args(PCRE
-                                    REQUIRED_VARS PCRECPP_LIBRARY PCRE_LIBRARY PCRE_INCLUDE_DIR
+                                    REQUIRED_VARS PCRE_LIBRARY PCRE_INCLUDE_DIR
                                     VERSION_VAR PCRE_VERSION)
 
   if(PCRE_FOUND)
-    set(PCRE_LIBRARIES ${PCRECPP_LIBRARY} ${PCRE_LIBRARY})
+    set(PCRE_LIBRARIES ${PCRE_LIBRARY})
     set(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
     if(WIN32)
       set(PCRE_DEFINITIONS -DPCRE_STATIC=1)
@@ -166,5 +155,5 @@
     endif()
   endif()
 
-  mark_as_advanced(PCRE_INCLUDE_DIR PCRECPP_LIBRARY PCRE_LIBRARY)
+  mark_as_advanced(PCRE_INCLUDE_DIR PCRE_LIBRARY)
 endif()
--- kodi-20.2+dfsg.orig/xbmc/utils/RegExp.h
+++ kodi-20.2+dfsg/xbmc/utils/RegExp.h
@@ -13,16 +13,8 @@
 #include <string>
 #include <vector>
 
-/* make sure stdlib.h is included before including pcre.h inside the
-   namespace; this works around stdlib.h definitions also living in
-   the PCRE namespace */
-#include <stdlib.h>
-
-namespace PCRE {
-struct real_pcre_jit_stack; // forward declaration for PCRE without JIT
-typedef struct real_pcre_jit_stack pcre_jit_stack;
-#include <pcre.h>
-}
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
 
 class CRegExp
 {
@@ -143,17 +135,17 @@
   void Cleanup();
   inline bool IsValidSubNumber(int iSub) const;
 
-  PCRE::pcre* m_re;
-  PCRE::pcre_extra* m_sd;
+  pcre2_code* m_re;
+  pcre2_match_context* m_ctxt;
   static const int OVECCOUNT=(m_MaxNumOfBackrefrences + 1) * 3;
   unsigned int m_offset;
-  int         m_iOvector[OVECCOUNT];
+  PCRE2_SIZE* m_iOvector;
   utf8Mode    m_utf8Mode;
   int         m_iMatchCount;
-  int         m_iOptions;
+  uint32_t    m_iOptions;
   bool        m_jitCompiled;
   bool        m_bMatched;
-  PCRE::pcre_jit_stack* m_jitStack;
+  pcre2_jit_stack* m_jitStack;
   std::string m_subject;
   std::string m_pattern;
   static int  m_Utf8Supported;
--- kodi-20.2+dfsg.orig/xbmc/filesystem/FTPParse.cpp
+++ kodi-20.2+dfsg/xbmc/filesystem/FTPParse.cpp
@@ -9,8 +9,10 @@
 #include "FTPParse.h"
 
 #include <cmath>
+#include <cstring>
 
-#include <pcrecpp.h>
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
 
 CFTPParse::CFTPParse()
 {
@@ -46,26 +48,31 @@
 
 void CFTPParse::setTime(const std::string& str)
 {
+  pcre2_code *re, *unix_re, *multinet_re, *msdos_re;
+  pcre2_match_data *md;
+  PCRE2_SPTR unix_pat, multinet_pat, msdos_pat, str_sptr;
+  PCRE2_SIZE offset;
+  int err;
   /* Variables used to capture patterns via the regexes */
-  std::string month;
-  std::string day;
-  std::string year;
-  std::string hour;
-  std::string minute;
-  std::string second;
-  std::string am_or_pm;
+  char *month = NULL;
+  PCRE2_UCHAR *day = NULL;
+  PCRE2_UCHAR *year = NULL;
+  PCRE2_UCHAR *hour = NULL;
+  PCRE2_UCHAR *minute = NULL;
+  PCRE2_UCHAR *second = NULL;
+  PCRE2_UCHAR *am_or_pm = NULL;
 
   /* time struct used to set the time_t variable */
   struct tm time_struct = {};
 
   /* Regex to read Unix, NetWare and NetPresenz time format */
-  pcrecpp::RE unix_re("^([A-Za-z]{3})" // month
+  unix_pat = reinterpret_cast<PCRE2_SPTR>("^([A-Za-z]{3})" // month
     "\\s+(\\d{1,2})" // day of month
     "\\s+([:\\d]{4,5})$" // time of day or year
   );
 
   /* Regex to read MultiNet time format */
-  pcrecpp::RE multinet_re("^(\\d{1,2})" // day of month
+  multinet_pat = reinterpret_cast<PCRE2_SPTR>("^(\\d{1,2})" // day of month
     "-([A-Za-z]{3})" // month
     "-(\\d{4})" // year
     "\\s+(\\d{2})" // hour
@@ -74,7 +81,7 @@
   );
 
   /* Regex to read MSDOS time format */
-  pcrecpp::RE msdos_re("^(\\d{2})" // month
+  msdos_pat = reinterpret_cast<PCRE2_SPTR>("^(\\d{2})" // month
     "-(\\d{2})" // day of month
     "-(\\d{2})" // year
     "\\s+(\\d{2})" // hour
@@ -82,48 +89,53 @@
     "([AP]M)$" // AM or PM
   );
 
-  if (unix_re.FullMatch(str, &month, &day, &year))
-  {
+  unix_re = pcre2_compile(unix_pat, PCRE2_ZERO_TERMINATED,
+                          0, &err, &offset, NULL);
+  multinet_re = pcre2_compile(multinet_pat, PCRE2_ZERO_TERMINATED,
+                              0, &err, &offset, NULL);
+  msdos_re = pcre2_compile(msdos_pat, PCRE2_ZERO_TERMINATED,
+                           0, &err, &offset, NULL);
+  str_sptr = reinterpret_cast<PCRE2_SPTR>(str.c_str());
+  md = pcre2_match_data_create(30, NULL);
+
+  if (pcre2_match(unix_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
+      > 0)
+  {
+    pcre2_substring_get_bynumber(md, 1,
+                                 reinterpret_cast<PCRE2_UCHAR **>(&month),
+                                 &offset);
+    pcre2_substring_get_bynumber(md, 2, &day, &offset);
+    pcre2_substring_get_bynumber(md, 3, &year, &offset);
+    re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>("(\\d{2}):(\\d{2})"),
+                       PCRE2_ZERO_TERMINATED, 0, &err, &offset, NULL);
     /* set the month */
-    if (pcrecpp::RE("jan",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    if (strcasestr(month, "jan"))
       time_struct.tm_mon = 0;
-    else if (pcrecpp::RE("feb",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "feb"))
       time_struct.tm_mon = 1;
-    else if (pcrecpp::RE("mar",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "mar"))
       time_struct.tm_mon = 2;
-    else if (pcrecpp::RE("apr",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "apr"))
       time_struct.tm_mon = 3;
-    else if (pcrecpp::RE("may",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "may"))
       time_struct.tm_mon = 4;
-    else if (pcrecpp::RE("jun",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "jun"))
       time_struct.tm_mon = 5;
-    else if (pcrecpp::RE("jul",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "jul"))
       time_struct.tm_mon = 6;
-    else if (pcrecpp::RE("aug",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "aug"))
       time_struct.tm_mon = 7;
-    else if (pcrecpp::RE("sep",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "sep"))
       time_struct.tm_mon = 8;
-    else if (pcrecpp::RE("oct",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "oct"))
       time_struct.tm_mon = 9;
-    else if (pcrecpp::RE("nov",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "nov"))
       time_struct.tm_mon = 10;
-    else if (pcrecpp::RE("dec",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "dec"))
       time_struct.tm_mon = 11;
 
     /* set the day of the month */
-    time_struct.tm_mday = atoi(day.c_str());
+    time_struct.tm_mday = atoi(reinterpret_cast<const char *>(day));
 
     time_t t = time(NULL);
     struct tm *current_time;
@@ -133,11 +145,14 @@
 #else
     current_time = localtime(&t);
 #endif
-    if (pcrecpp::RE("(\\d{2}):(\\d{2})").FullMatch(year, &hour, &minute))
+    if (pcre2_match(re, reinterpret_cast<PCRE2_SPTR>(year),
+                    PCRE2_ZERO_TERMINATED, 0, 0, md, NULL) > 0)
     {
+      pcre2_substring_get_bynumber(md, 1, &hour, &offset);
+      pcre2_substring_get_bynumber(md, 2, &minute, &offset);
       /* set the hour and minute */
-      time_struct.tm_hour = atoi(hour.c_str());
-      time_struct.tm_min = atoi(minute.c_str());
+      time_struct.tm_hour = atoi(reinterpret_cast<const char *>(hour));
+      time_struct.tm_min = atoi(reinterpret_cast<const char *>(minute));
 
       /* set the year */
       if ((current_time->tm_mon - time_struct.tm_mon < 0) ||
@@ -150,93 +165,99 @@
     else
     {
       /* set the year */
-      time_struct.tm_year = atoi(year.c_str()) - 1900;
+      time_struct.tm_year = atoi(reinterpret_cast<const char *>(year)) - 1900;
     }
 
     /* set the day of the week */
     time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
                                    time_struct.tm_mday,
                                    time_struct.tm_year + 1900);
+    pcre2_code_free(re);
   }
-  else if (multinet_re.FullMatch(str, &day, &month, &year,
-                            &hour, &minute, (void*)NULL, &second))
+  else if (pcre2_match(multinet_re, str_sptr, PCRE2_ZERO_TERMINATED,
+                       0, 0, md, NULL) > 0)
   {
+    pcre2_substring_get_bynumber(md, 1, &day, &offset);
+    pcre2_substring_get_bynumber(md, 2,
+                                 reinterpret_cast<PCRE2_UCHAR **>(&month),
+                                 &offset);
+    pcre2_substring_get_bynumber(md, 3, &year, &offset);
+    pcre2_substring_get_bynumber(md, 4, &hour, &offset);
+    pcre2_substring_get_bynumber(md, 5, &minute, &offset);
+    pcre2_substring_get_bynumber(md, 7, &second, &offset);
     /* set the month */
-    if (pcrecpp::RE("jan",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    if (strcasestr(month, "jan"))
       time_struct.tm_mon = 0;
-    else if (pcrecpp::RE("feb",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "feb"))
       time_struct.tm_mon = 1;
-    else if (pcrecpp::RE("mar",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "mar"))
       time_struct.tm_mon = 2;
-    else if (pcrecpp::RE("apr",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "apr"))
       time_struct.tm_mon = 3;
-    else if (pcrecpp::RE("may",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "may"))
       time_struct.tm_mon = 4;
-    else if (pcrecpp::RE("jun",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "jun"))
       time_struct.tm_mon = 5;
-    else if (pcrecpp::RE("jul",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "jul"))
       time_struct.tm_mon = 6;
-    else if (pcrecpp::RE("aug",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "aug"))
       time_struct.tm_mon = 7;
-    else if (pcrecpp::RE("sep",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "sep"))
       time_struct.tm_mon = 8;
-    else if (pcrecpp::RE("oct",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "oct"))
       time_struct.tm_mon = 9;
-    else if (pcrecpp::RE("nov",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "nov"))
       time_struct.tm_mon = 10;
-    else if (pcrecpp::RE("dec",
-        pcrecpp::RE_Options().set_caseless(true)).FullMatch(month))
+    else if (strcasestr(month, "dec"))
       time_struct.tm_mon = 11;
 
     /* set the day of the month and year */
-    time_struct.tm_mday = atoi(day.c_str());
-    time_struct.tm_year = atoi(year.c_str()) - 1900;
+    time_struct.tm_mday = atoi(reinterpret_cast<const char *>(day));
+    time_struct.tm_year = atoi(reinterpret_cast<const char *>(year)) - 1900;
 
     /* set the hour and minute */
-    time_struct.tm_hour = atoi(hour.c_str());
-    time_struct.tm_min = atoi(minute.c_str());
+    time_struct.tm_hour = atoi(reinterpret_cast<const char *>(hour));
+    time_struct.tm_min = atoi(reinterpret_cast<const char *>(minute));
 
     /* set the second if given*/
-    if (second.length() > 0)
-      time_struct.tm_sec = atoi(second.c_str());
+    if (strlen(reinterpret_cast<const char *>(second)) > 0)
+      time_struct.tm_sec = atoi(reinterpret_cast<const char *>(second));
 
     /* set the day of the week */
     time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
                                    time_struct.tm_mday,
                                    time_struct.tm_year + 1900);
   }
-  else if (msdos_re.FullMatch(str, &month, &day,
-                              &year, &hour, &minute, &am_or_pm))
+  else if (pcre2_match(msdos_re, str_sptr, PCRE2_ZERO_TERMINATED,
+                       0, 0, md, NULL) > 0)
   {
+    pcre2_substring_get_bynumber(md, 1,
+                                 reinterpret_cast<PCRE2_UCHAR **>(&month),
+                                 &offset);
+    pcre2_substring_get_bynumber(md, 2, &day, &offset);
+    pcre2_substring_get_bynumber(md, 3, &year, &offset);
+    pcre2_substring_get_bynumber(md, 4, &hour, &offset);
+    pcre2_substring_get_bynumber(md, 5, &minute, &offset);
+    pcre2_substring_get_bynumber(md, 6, &am_or_pm, &offset);
+
     /* set the month and the day of the month */
-    time_struct.tm_mon = atoi(month.c_str()) - 1;
-    time_struct.tm_mday = atoi(day.c_str());
+    time_struct.tm_mon = atoi(month) - 1;
+    time_struct.tm_mday = atoi(reinterpret_cast<const char *>(day));
 
     /* set the year */
-    time_struct.tm_year = atoi(year.c_str());
+    time_struct.tm_year = atoi(reinterpret_cast<const char *>(year));
     if (time_struct.tm_year < 70)
       time_struct.tm_year += 100;
 
     /* set the hour */
-    time_struct.tm_hour = atoi(hour.c_str());
+    time_struct.tm_hour = atoi(reinterpret_cast<const char *>(hour));
     if (time_struct.tm_hour == 12)
       time_struct.tm_hour -= 12;
-    if (pcrecpp::RE("PM").FullMatch(am_or_pm))
+    if (strstr(reinterpret_cast<const char *>(am_or_pm), "PM"))
       time_struct.tm_hour += 12;
 
     /* set the minute */
-    time_struct.tm_min = atoi(minute.c_str());
+    time_struct.tm_min = atoi(reinterpret_cast<const char *>(minute));
 
     /* set the day of the week */
     time_struct.tm_wday = getDayOfWeek(time_struct.tm_mon + 1,
@@ -246,6 +267,18 @@
 
   /* now set m_time */
   m_time = mktime(&time_struct);
+
+  pcre2_code_free(unix_re);
+  pcre2_code_free(multinet_re);
+  pcre2_code_free(msdos_re);
+  pcre2_match_data_free(md);
+  pcre2_substring_free(reinterpret_cast<PCRE2_UCHAR *>(month));
+  pcre2_substring_free(day);
+  pcre2_substring_free(year);
+  pcre2_substring_free(hour);
+  pcre2_substring_free(minute);
+  pcre2_substring_free(second);
+  pcre2_substring_free(am_or_pm);
 }
 
 int CFTPParse::getDayOfWeek(int month, int date, int year)
@@ -325,22 +358,22 @@
 
 int CFTPParse::FTPParse(const std::string& str)
 {
+  pcre2_code *unix_re, *netware_re, *netpresenz_re, *eplf_re, *multinet_re,
+    *msdos_re;
+  pcre2_match_data *md;
+  PCRE2_SPTR unix_pat, netware_pat, netpresenz_pat, eplf_pat, multinet_pat,
+    msdos_pat, str_sptr;
+  PCRE2_SIZE offset;
+  int err;
   /* Various variable to capture patterns via the regexes */
-  std::string permissions;
-  std::string link_count;
-  std::string owner;
-  std::string group;
-  std::string size;
-  std::string date;
-  std::string name;
-  std::string type;
-  std::string stuff;
-  std::string facts;
-  std::string version;
-  std::string file_id;
+  char *type = NULL;
+  PCRE2_UCHAR *size = NULL;
+  PCRE2_UCHAR *date = NULL;
+  PCRE2_UCHAR *name = NULL;
+  PCRE2_UCHAR *facts;
 
   /* Regex for standard Unix listing formats */
-  pcrecpp::RE unix_re("^([-bcdlps])" // type
+  unix_pat = reinterpret_cast<PCRE2_SPTR>("^([-bcdlps])" // type
     "([-rwxXsStT]{9})" // permissions
     "\\s+(\\d+)" // hard link count
     "\\s+(\\w+)" // owner
@@ -352,7 +385,7 @@
 
   /* Regex for NetWare listing formats */
   /* See http://www.novell.com/documentation/oes/ftp_enu/data/a3ep22p.html#fbhbaijf */
-  pcrecpp::RE netware_re("^([-d])" // type
+  netware_pat = reinterpret_cast<PCRE2_SPTR>("^([-d])" // type
     "\\s+(\\[[-SRWCIEMFA]{8}\\])" // rights
     "\\s+(\\w+)" // owner
     "\\s+(\\d+)" // size
@@ -363,7 +396,7 @@
   /* Regex for NetPresenz */
   /* See http://files.stairways.com/other/ftp-list-specs-info.txt */
   /* Here we will capture permissions and size if given */
-  pcrecpp::RE netpresenz_re("^([-dl])" // type
+  netpresenz_pat = reinterpret_cast<PCRE2_SPTR>("^([-dl])" // type
     "([-rwx]{9}|)" // permissions
     "\\s+(.*)" // stuff
     "\\s+(\\d+|)" // size
@@ -374,7 +407,7 @@
   /* Regex for EPLF */
   /* See http://cr.yp.to/ftp/list/eplf.html */
   /* SAVE: "(/,|r,|s\\d+,|m\\d+,|i[\\d!#@$%^&*()]+(\\.[\\d!#@$%^&*()]+|),)+" */
-  pcrecpp::RE eplf_re("^\\+" // initial "plus" sign
+  eplf_pat = reinterpret_cast<PCRE2_SPTR>("^\\+" // initial "plus" sign
     "([^\\s]+)" // facts
     "\\s(.+)$" // name
   );
@@ -382,7 +415,7 @@
   /* Regex for MultiNet */
   /* Best documentation found was
    * http://www-sld.slac.stanford.edu/SLDWWW/workbook/vms_files.html */
-  pcrecpp::RE multinet_re("^([^;]+)" // name
+  multinet_pat = reinterpret_cast<PCRE2_SPTR>("^([^;]+)" // name
     ";(\\d+)" // version
     "\\s+([\\d/]+)" // file id
     "\\s+(\\d{1,2}-[A-Za-z]{3}-\\d{4}\\s+\\d{2}:\\d{2}(:\\d{2})?)" // date
@@ -391,20 +424,42 @@
   );
 
   /* Regex for MSDOS */
-  pcrecpp::RE msdos_re("^(\\d{2}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}[AP]M)" // date
+  msdos_pat = reinterpret_cast<PCRE2_SPTR>("^(\\d{2}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}[AP]M)" // date
     "\\s+(<DIR>|[\\d]+)" // dir or size
     "\\s+(.+)$" // name
   );
 
-  if (unix_re.FullMatch(str, &type, &permissions, &link_count, &owner, &group, &size, &date, &name))
-  {
-    m_name = name;
-    m_size = (uint64_t)strtod(size.c_str(), NULL);
-    if (pcrecpp::RE("d").FullMatch(type))
+  unix_re = pcre2_compile(unix_pat, PCRE2_ZERO_TERMINATED,
+                          0, &err, &offset, NULL);
+  netware_re = pcre2_compile(netware_pat, PCRE2_ZERO_TERMINATED,
+                             0, &err, &offset, NULL);
+  netpresenz_re = pcre2_compile(netpresenz_pat, PCRE2_ZERO_TERMINATED,
+                                0, &err, &offset, NULL);
+  eplf_re = pcre2_compile(eplf_pat, PCRE2_ZERO_TERMINATED,
+                          0, &err, &offset, NULL);
+  multinet_re = pcre2_compile(multinet_pat, PCRE2_ZERO_TERMINATED,
+                              0, &err, &offset, NULL);
+  msdos_re = pcre2_compile(msdos_pat, PCRE2_ZERO_TERMINATED,
+                           0, &err, &offset, NULL);
+  md = pcre2_match_data_create(30, NULL);
+  str_sptr = reinterpret_cast<PCRE2_SPTR>(str.c_str());
+
+  if (pcre2_match(unix_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
+      > 0)
+  {
+    pcre2_substring_get_bynumber(md, 1,
+                                 reinterpret_cast<PCRE2_UCHAR **>(&type),
+                                 &offset);
+    pcre2_substring_get_bynumber(md, 6, &size, &offset);
+    pcre2_substring_get_bynumber(md, 7, &date, &offset);
+    pcre2_substring_get_bynumber(md, 8, &name, &offset);
+    m_name = reinterpret_cast<const char *>(name);
+    m_size = (uint64_t)strtod(reinterpret_cast<const char *>(size), NULL);
+    if (strstr(type, "d"))
       m_flagtrycwd = 1;
-    if (pcrecpp::RE("-").FullMatch(type))
+    if (strstr(type, "-"))
       m_flagtryretr = 1;
-    if (pcrecpp::RE("l").FullMatch(type))
+    if (strstr(type, "l"))
     {
       m_flagtrycwd = m_flagtryretr = 1;
       // handle symlink
@@ -412,31 +467,67 @@
       if (found != std::string::npos)
         m_name = m_name.substr(0, found);
     }
-    setTime(date);
+    setTime(reinterpret_cast<const char *>(date));
+    pcre2_substring_free(reinterpret_cast<PCRE2_UCHAR *>(type));
+    pcre2_substring_free(size);
+    pcre2_substring_free(date);
+    pcre2_substring_free(name);
+    pcre2_match_data_free(md);
+    pcre2_code_free(unix_re);
+    pcre2_code_free(netware_re);
+    pcre2_code_free(netpresenz_re);
+    pcre2_code_free(eplf_re);
+    pcre2_code_free(multinet_re);
+    pcre2_code_free(msdos_re);
 
     return 1;
   }
-  if (netware_re.FullMatch(str, &type, &permissions, &owner, &size, &date, &name))
+  if (pcre2_match(netware_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
+      > 0)
   {
-    m_name = name;
-    m_size = (uint64_t)strtod(size.c_str(), NULL);
-    if (pcrecpp::RE("d").FullMatch(type))
+    pcre2_substring_get_bynumber(md, 1,
+                                 reinterpret_cast<PCRE2_UCHAR **>(&type),
+                                 &offset);
+    pcre2_substring_get_bynumber(md, 4, &size, &offset);
+    pcre2_substring_get_bynumber(md, 5, &date, &offset);
+    pcre2_substring_get_bynumber(md, 6, &name, &offset);
+    m_name = reinterpret_cast<const char *>(name);
+    m_size = (uint64_t)strtod(reinterpret_cast<const char *>(size), NULL);
+    if (strstr(type, "d"))
       m_flagtrycwd = 1;
-    if (pcrecpp::RE("-").FullMatch(type))
+    if (strstr(type, "-"))
       m_flagtryretr = 1;
-    setTime(date);
+    setTime(reinterpret_cast<const char *>(date));
+    pcre2_substring_free(reinterpret_cast<PCRE2_UCHAR *>(type));
+    pcre2_substring_free(size);
+    pcre2_substring_free(date);
+    pcre2_substring_free(name);
+    pcre2_match_data_free(md);
+    pcre2_code_free(unix_re);
+    pcre2_code_free(netware_re);
+    pcre2_code_free(netpresenz_re);
+    pcre2_code_free(eplf_re);
+    pcre2_code_free(multinet_re);
+    pcre2_code_free(msdos_re);
 
     return 1;
   }
-  if (netpresenz_re.FullMatch(str, &type, &permissions, &stuff, &size, &date, &name))
+  if (pcre2_match(netpresenz_re, str_sptr, PCRE2_ZERO_TERMINATED,
+                  0, 0, md, NULL) > 0)
   {
-    m_name = name;
-    m_size = (uint64_t)strtod(size.c_str(), NULL);
-    if (pcrecpp::RE("d").FullMatch(type))
+    pcre2_substring_get_bynumber(md, 1,
+                                 reinterpret_cast<PCRE2_UCHAR **>(&type),
+                                 &offset);
+    pcre2_substring_get_bynumber(md, 4, &size, &offset);
+    pcre2_substring_get_bynumber(md, 5, &date, &offset);
+    pcre2_substring_get_bynumber(md, 6, &name, &offset);
+    m_name = reinterpret_cast<const char *>(name);
+    m_size = (uint64_t)strtod(reinterpret_cast<const char *>(size), NULL);
+    if (strstr(type, "d"))
       m_flagtrycwd = 1;
-    if (pcrecpp::RE("-").FullMatch(type))
+    if (strstr(type, "-"))
       m_flagtryretr = 1;
-    if (pcrecpp::RE("l").FullMatch(type))
+    if (strstr(type, "l"))
     {
       m_flagtrycwd = m_flagtryretr = 1;
       // handle symlink
@@ -444,48 +535,118 @@
       if (found != std::string::npos)
         m_name = m_name.substr(0, found);
     }
-    setTime(date);
+    setTime(reinterpret_cast<const char *>(date));
+    pcre2_substring_free(reinterpret_cast<PCRE2_UCHAR *>(type));
+    pcre2_substring_free(size);
+    pcre2_substring_free(date);
+    pcre2_substring_free(name);
+    pcre2_match_data_free(md);
+    pcre2_code_free(unix_re);
+    pcre2_code_free(netware_re);
+    pcre2_code_free(netpresenz_re);
+    pcre2_code_free(eplf_re);
+    pcre2_code_free(multinet_re);
+    pcre2_code_free(msdos_re);
 
     return 1;
   }
-  if (eplf_re.FullMatch(str, &facts, &name))
+  if (pcre2_match(eplf_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
+      > 0)
   {
+    pcre2_code *re;
+
+    pcre2_substring_get_bynumber(md, 1, &facts, &offset);
+    pcre2_substring_get_bynumber(md, 2, &name,  &offset);
     /* Get the type, size, and date from the facts */
-    pcrecpp::RE("(\\+|,)(r|/),").PartialMatch(facts, (void*)NULL, &type);
-    pcrecpp::RE("(\\+|,)s(\\d+),").PartialMatch(facts, (void*)NULL, &size);
-    pcrecpp::RE("(\\+|,)m(\\d+),").PartialMatch(facts, (void*)NULL, &date);
-
-    m_name = name;
-    m_size = (uint64_t)strtod(size.c_str(), NULL);
-    if (pcrecpp::RE("/").FullMatch(type))
+    re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>("(\\+|,)(r|/),"),
+                       PCRE2_ZERO_TERMINATED, 0, &err, &offset, NULL);
+    if (pcre2_match(re, facts, PCRE2_ZERO_TERMINATED, 0,
+                    PCRE2_PARTIAL_SOFT, md, NULL) > 0)
+      pcre2_substring_get_bynumber(md, 2,
+                                   reinterpret_cast<PCRE2_UCHAR **>(&type),
+                                   &offset);
+    pcre2_code_free(re);
+    re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>("(\\+|,)s(\\d+),"),
+                       PCRE2_ZERO_TERMINATED, 0, &err, &offset, NULL);
+    if (pcre2_match(re, facts, PCRE2_ZERO_TERMINATED, 0,
+                    PCRE2_PARTIAL_SOFT, md, NULL) > 0)
+      pcre2_substring_get_bynumber(md, 2, &size, &offset);
+    pcre2_code_free(re);
+    re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>("(\\+|,)m(\\d+),"),
+                       PCRE2_ZERO_TERMINATED, 0, &err, &offset, NULL);
+    if (pcre2_match(re, facts, PCRE2_ZERO_TERMINATED, 0,
+                    PCRE2_PARTIAL_SOFT, md, NULL) > 0)
+      pcre2_substring_get_bynumber(md, 2, &date, &offset);
+    pcre2_code_free(re);
+    pcre2_substring_free(facts);
+
+    m_name = reinterpret_cast<const char *>(name);
+    m_size = (uint64_t)strtod(reinterpret_cast<const char *>(size), NULL);
+    if (strstr(type, "/"))
       m_flagtrycwd = 1;
-    if (pcrecpp::RE("r").FullMatch(type))
+    if (strstr(type, "r"))
       m_flagtryretr = 1;
     /* eplf stores the date in time_t format already */
-    m_time = atoi(date.c_str());
+    m_time = atoi(reinterpret_cast<const char *>(date));
+    pcre2_substring_free(reinterpret_cast<PCRE2_UCHAR *>(type));
+    pcre2_substring_free(size);
+    pcre2_substring_free(date);
+    pcre2_substring_free(name);
+    pcre2_match_data_free(md);
+    pcre2_code_free(unix_re);
+    pcre2_code_free(netware_re);
+    pcre2_code_free(netpresenz_re);
+    pcre2_code_free(eplf_re);
+    pcre2_code_free(multinet_re);
+    pcre2_code_free(msdos_re);
 
     return 1;
   }
-  if (multinet_re.FullMatch(str, &name, &version, &file_id, &date, (void*)NULL, &owner, &permissions))
+  if (pcre2_match(multinet_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
+      > 0)
   {
-    if (pcrecpp::RE("\\.DIR$").PartialMatch(name))
+    pcre2_code *re;
+    std::string tmp;
+
+    pcre2_substring_get_bynumber(md, 1, &name, &offset);
+    pcre2_substring_get_bynumber(md, 4, &date, &offset);
+    re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>("\\.DIR$"),
+                       PCRE2_ZERO_TERMINATED, 0, &err, &offset, NULL);
+    //if (pcrecpp::RE("\\.DIR$").PartialMatch(name))
+    tmp = reinterpret_cast<const char *>(name);
+    if (pcre2_match(re, name, PCRE2_ZERO_TERMINATED, 0, PCRE2_PARTIAL_SOFT,
+                    md, NULL) > 0)
     {
-      name.resize(name.size() - 4);
+      tmp.resize(tmp.size() - 4);
       m_flagtrycwd = 1;
     }
     else
       m_flagtryretr = 1;
-    m_name = name;
-    setTime(date);
+    m_name = tmp;
+    setTime(reinterpret_cast<const char *>(date));
     /* Multinet doesn't provide a size */
     m_size = 0;
+    pcre2_substring_free(date);
+    pcre2_substring_free(name);
+    pcre2_match_data_free(md);
+    pcre2_code_free(re);
+    pcre2_code_free(unix_re);
+    pcre2_code_free(netware_re);
+    pcre2_code_free(netpresenz_re);
+    pcre2_code_free(eplf_re);
+    pcre2_code_free(multinet_re);
+    pcre2_code_free(msdos_re);
 
     return 1;
   }
-  if (msdos_re.FullMatch(str, &date, &size, &name))
+  if (pcre2_match(msdos_re, str_sptr, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL)
+      > 0)
   {
-    m_name = name;
-    if (pcrecpp::RE("<DIR>").FullMatch(size))
+    pcre2_substring_get_bynumber(md, 1, &date, &offset);
+    pcre2_substring_get_bynumber(md, 2, &size, &offset);
+    pcre2_substring_get_bynumber(md, 3, &name, &offset);
+    m_name = reinterpret_cast<const char *>(name);
+    if (strstr(reinterpret_cast<const char *>(size), "<DIR>"))
     {
       m_flagtrycwd = 1;
       m_size = 0;
@@ -493,12 +654,29 @@
     else
     {
       m_flagtryretr = 1;
-      m_size = (uint64_t)strtod(size.c_str(), NULL);
+      m_size = (uint64_t)strtod(reinterpret_cast<const char *>(size), NULL);
     }
-    setTime(date);
+    setTime(reinterpret_cast<const char *>(date));
+    pcre2_substring_free(date);
+    pcre2_substring_free(size);
+    pcre2_substring_free(name);
+    pcre2_match_data_free(md);
+    pcre2_code_free(unix_re);
+    pcre2_code_free(netware_re);
+    pcre2_code_free(netpresenz_re);
+    pcre2_code_free(eplf_re);
+    pcre2_code_free(multinet_re);
+    pcre2_code_free(msdos_re);
 
     return 1;
   }
+  pcre2_match_data_free(md);
+  pcre2_code_free(unix_re);
+  pcre2_code_free(netware_re);
+  pcre2_code_free(netpresenz_re);
+  pcre2_code_free(eplf_re);
+  pcre2_code_free(multinet_re);
+  pcre2_code_free(msdos_re);
 
   return 0;
 }
--- kodi-20.2+dfsg.orig/xbmc/utils/RegExp.cpp
+++ kodi-20.2+dfsg/xbmc/utils/RegExp.cpp
@@ -16,27 +16,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-using namespace PCRE;
-
-#ifndef PCRE_UCP
-#define PCRE_UCP 0
-#endif // PCRE_UCP
-
-#ifdef PCRE_CONFIG_JIT
-#define PCRE_HAS_JIT_CODE 1
-#endif
-
-#ifndef PCRE_STUDY_JIT_COMPILE
-#define PCRE_STUDY_JIT_COMPILE 0
-#endif
-#ifndef PCRE_INFO_JIT
-// some unused number
-#define PCRE_INFO_JIT 2048
-#endif
-#ifndef PCRE_HAS_JIT_CODE
-#define pcre_free_study(x) pcre_free((x))
-#endif
-
 int CRegExp::m_Utf8Supported = -1;
 int CRegExp::m_UcpSupported  = -1;
 int CRegExp::m_JitSupported  = -1;
@@ -51,25 +30,24 @@
 {
   m_utf8Mode    = utf8;
   m_re          = NULL;
-  m_sd          = NULL;
-  m_iOptions    = PCRE_DOTALL | PCRE_NEWLINE_ANY;
+  m_ctxt        = NULL;
+  m_iOptions    = PCRE2_DOTALL;
   if(caseless)
-    m_iOptions |= PCRE_CASELESS;
+    m_iOptions |= PCRE2_CASELESS;
   if (m_utf8Mode == forceUtf8)
   {
     if (IsUtf8Supported())
-      m_iOptions |= PCRE_UTF8;
+      m_iOptions |= PCRE2_UTF;
     if (AreUnicodePropertiesSupported())
-      m_iOptions |= PCRE_UCP;
+      m_iOptions |= PCRE2_UCP;
   }
 
   m_offset      = 0;
   m_jitCompiled = false;
   m_bMatched    = false;
   m_iMatchCount = 0;
+  m_iOvector    = NULL;
   m_jitStack    = NULL;
-
-  memset(m_iOvector, 0, sizeof(m_iOvector));
 }
 
 CRegExp::CRegExp(bool caseless, CRegExp::utf8Mode utf8, const char *re, studyMode study /*= NoStudy*/)
@@ -225,7 +203,8 @@
 CRegExp::CRegExp(const CRegExp& re)
 {
   m_re = NULL;
-  m_sd = NULL;
+  m_ctxt = NULL;
+  m_iOvector = NULL;
   m_jitStack = NULL;
   m_utf8Mode = re.m_utf8Mode;
   m_iOptions = re.m_iOptions;
@@ -240,12 +219,13 @@
   m_pattern = re.m_pattern;
   if (re.m_re)
   {
-    if (pcre_fullinfo(re.m_re, NULL, PCRE_INFO_SIZE, &size) >= 0)
+    if (pcre2_pattern_info(re.m_re, PCRE2_INFO_SIZE, &size) >= 0)
     {
-      if ((m_re = (pcre*)malloc(size)))
+      if ((m_re = pcre2_code_copy(re.m_re)))
       {
-        memcpy(m_re, re.m_re, size);
-        memcpy(m_iOvector, re.m_iOvector, OVECCOUNT*sizeof(int));
+        if (re.m_ctxt)
+          m_ctxt = pcre2_match_context_copy(re.m_ctxt);
+        m_iOvector = re.m_iOvector;
         m_offset = re.m_offset;
         m_iMatchCount = re.m_iMatchCount;
         m_bMatched = re.m_bMatched;
@@ -273,18 +253,28 @@
   m_jitCompiled      = false;
   m_bMatched         = false;
   m_iMatchCount      = 0;
-  const char *errMsg = NULL;
-  int errOffset      = 0;
-  int options        = m_iOptions;
+  pcre2_compile_context *ctxt;
+  int errCode;
+  char errMsg[120];
+  PCRE2_SIZE errOffset;
+  uint32_t options   = m_iOptions;
   if (m_utf8Mode == autoUtf8 && requireUtf8(re))
-    options |= (IsUtf8Supported() ? PCRE_UTF8 : 0) | (AreUnicodePropertiesSupported() ? PCRE_UCP : 0);
+    options |= (IsUtf8Supported() ? PCRE2_UTF : 0) | (AreUnicodePropertiesSupported() ? PCRE2_UCP : 0);
 
   Cleanup();
 
-  m_re = pcre_compile(re, options, &errMsg, &errOffset, NULL);
+  ctxt = pcre2_compile_context_create(NULL);
+  pcre2_set_newline(ctxt, PCRE2_NEWLINE_ANY);
+  m_re = pcre2_compile(reinterpret_cast<PCRE2_SPTR>(re),
+                       PCRE2_ZERO_TERMINATED, options,
+                       &errCode, &errOffset, ctxt);
+  pcre2_compile_context_free(ctxt);
+
   if (!m_re)
   {
     m_pattern.clear();
+    pcre2_get_error_message(errCode, reinterpret_cast<PCRE2_UCHAR *>(errMsg),
+                            sizeof(errMsg));
     CLog::Log(LOGERROR, "PCRE: {}. Compilation failed at offset {} in expression '{}'", errMsg,
               errOffset, re);
     return false;
@@ -295,23 +285,12 @@
   if (study)
   {
     const bool jitCompile = (study == StudyWithJitComp) && IsJitSupported();
-    const int studyOptions = jitCompile ? PCRE_STUDY_JIT_COMPILE : 0;
 
-    m_sd = pcre_study(m_re, studyOptions, &errMsg);
-    if (errMsg != NULL)
-    {
-      CLog::Log(LOGWARNING, "{}: PCRE error \"{}\" while studying expression", __FUNCTION__,
-                errMsg);
-      if (m_sd != NULL)
-      {
-        pcre_free_study(m_sd);
-        m_sd = NULL;
-      }
-    }
-    else if (jitCompile)
+    if (jitCompile)
     {
-      int jitPresent = 0;
-      m_jitCompiled = (pcre_fullinfo(m_re, m_sd, PCRE_INFO_JIT, &jitPresent) == 0 && jitPresent == 1);
+      pcre2_jit_compile(m_re, PCRE2_JIT_COMPLETE);
+      size_t jitPresent = 0;
+      m_jitCompiled = (pcre2_pattern_info(m_re, PCRE2_INFO_JITSIZE, &jitPresent) == 0 && jitPresent > 0);
     }
   }
 
@@ -325,6 +304,9 @@
 
 int CRegExp::PrivateRegFind(size_t bufferLen, const char *str, unsigned int startoffset /* = 0*/, int maxNumberOfCharsToTest /*= -1*/)
 {
+  pcre2_match_data *md;
+  PCRE2_SIZE offset;
+
   m_offset      = 0;
   m_bMatched    = false;
   m_iMatchCount = 0;
@@ -347,37 +329,47 @@
     return -1;
   }
 
-#ifdef PCRE_HAS_JIT_CODE
+  if (!m_ctxt)
+    m_ctxt = pcre2_match_context_create(NULL);
+
   if (m_jitCompiled && !m_jitStack)
   {
-    m_jitStack = pcre_jit_stack_alloc(32*1024, 512*1024);
+    m_jitStack = pcre2_jit_stack_create(32*1024, 512*1024, NULL);
     if (m_jitStack == NULL)
       CLog::Log(LOGWARNING, "{}: can't allocate address space for JIT stack", __FUNCTION__);
 
-    pcre_assign_jit_stack(m_sd, NULL, m_jitStack);
+    pcre2_jit_stack_assign(m_ctxt, NULL, m_jitStack);
   }
-#endif
 
   if (maxNumberOfCharsToTest >= 0)
     bufferLen = std::min<size_t>(bufferLen, startoffset + maxNumberOfCharsToTest);
 
   m_subject.assign(str + startoffset, bufferLen - startoffset);
-  int rc = pcre_exec(m_re, NULL, m_subject.c_str(), m_subject.length(), 0, 0, m_iOvector, OVECCOUNT);
+  md = pcre2_match_data_create(OVECCOUNT, NULL);
+  int rc = pcre2_match(m_re,
+                       reinterpret_cast<PCRE2_SPTR>(m_subject.c_str()),
+                       m_subject.length(), 0, 0, md, m_ctxt);
+  m_iOvector = pcre2_get_ovector_pointer(md);
+  offset = pcre2_get_startchar(md);
+  pcre2_match_data_free(md);
 
   if (rc<1)
   {
     static const int fragmentLen = 80; // length of excerpt before erroneous char for log
     switch(rc)
     {
-    case PCRE_ERROR_NOMATCH:
+    case PCRE2_ERROR_NOMATCH:
       return -1;
 
-    case PCRE_ERROR_MATCHLIMIT:
+    case PCRE2_ERROR_MATCHLIMIT:
       CLog::Log(LOGERROR, "PCRE: Match limit reached");
       return -1;
 
-#ifdef PCRE_ERROR_SHORTUTF8
-    case PCRE_ERROR_SHORTUTF8:
+    case PCRE2_ERROR_UTF8_ERR1:
+    case PCRE2_ERROR_UTF8_ERR2:
+    case PCRE2_ERROR_UTF8_ERR3:
+    case PCRE2_ERROR_UTF8_ERR4:
+    case PCRE2_ERROR_UTF8_ERR5:
       {
         const size_t startPos = (m_subject.length() > fragmentLen) ? CUtf8Utils::RFindValidUtf8Char(m_subject, m_subject.length() - fragmentLen) : 0;
         if (startPos != std::string::npos)
@@ -389,22 +381,41 @@
           CLog::Log(LOGERROR, "PCRE: Bad UTF-8 character at the end of string");
         return -1;
       }
-#endif
-    case PCRE_ERROR_BADUTF8:
+    case PCRE2_ERROR_UTF8_ERR6:
+    case PCRE2_ERROR_UTF8_ERR7:
+    case PCRE2_ERROR_UTF8_ERR8:
+    case PCRE2_ERROR_UTF8_ERR9:
+    case PCRE2_ERROR_UTF8_ERR10:
+    case PCRE2_ERROR_UTF8_ERR11:
+    case PCRE2_ERROR_UTF8_ERR12:
+    case PCRE2_ERROR_UTF8_ERR13:
+    case PCRE2_ERROR_UTF8_ERR14:
+    case PCRE2_ERROR_UTF8_ERR15:
+    case PCRE2_ERROR_UTF8_ERR16:
+    case PCRE2_ERROR_UTF8_ERR17:
+    case PCRE2_ERROR_UTF8_ERR18:
+    case PCRE2_ERROR_UTF8_ERR19:
+    case PCRE2_ERROR_UTF8_ERR20:
+    case PCRE2_ERROR_UTF8_ERR21:
       {
+        char errbuf[120];
+
+        pcre2_get_error_message(rc,
+                                reinterpret_cast<PCRE2_UCHAR *>(errbuf),
+                                sizeof(errbuf));
         const size_t startPos = (m_iOvector[0] > fragmentLen) ? CUtf8Utils::RFindValidUtf8Char(m_subject, m_iOvector[0] - fragmentLen) : 0;
-        if (m_iOvector[0] >= 0 && startPos != std::string::npos)
+        if ((int)m_iOvector[0] >= 0 && startPos != std::string::npos)
           CLog::Log(LOGERROR,
                     "PCRE: Bad UTF-8 character, error code: {}, position: {}. Text before bad "
                     "char: \"{}\"",
-                    m_iOvector[1], m_iOvector[0],
+                    errbuf, offset,
                     m_subject.substr(startPos, m_iOvector[0] - startPos + 1));
         else
           CLog::Log(LOGERROR, "PCRE: Bad UTF-8 character, error code: {}, position: {}",
-                    m_iOvector[1], m_iOvector[0]);
+                    errbuf, offset);
         return -1;
       }
-    case PCRE_ERROR_BADUTF8_OFFSET:
+    case PCRE2_ERROR_BADUTFOFFSET:
       CLog::Log(LOGERROR, "PCRE: Offset is pointing to the middle of UTF-8 character");
       return -1;
 
@@ -423,7 +434,7 @@
 {
   int c = -1;
   if (m_re)
-    pcre_fullinfo(m_re, NULL, PCRE_INFO_CAPTURECOUNT, &c);
+    pcre2_pattern_info(m_re, PCRE2_INFO_CAPTURECOUNT, &c);
   return c;
 }
 
@@ -524,7 +535,7 @@
 bool CRegExp::GetNamedSubPattern(const char* strName, std::string& strMatch) const
 {
   strMatch.clear();
-  int iSub = pcre_get_stringnumber(m_re, strName);
+  int iSub = pcre2_substring_number_from_name(m_re, reinterpret_cast<PCRE2_SPTR>(strName));
   if (!IsValidSubNumber(iSub))
     return false;
   strMatch = GetMatch(iSub);
@@ -533,7 +544,7 @@
 
 int CRegExp::GetNamedSubPatternNumber(const char* strName) const
 {
-  return pcre_get_stringnumber(m_re, strName);
+  return pcre2_substring_number_from_name(m_re, reinterpret_cast<PCRE2_SPTR>(strName));
 }
 
 void CRegExp::DumpOvector(int iLog /* = LOGDEBUG */)
@@ -558,23 +569,21 @@
 {
   if (m_re)
   {
-    pcre_free(m_re);
+    pcre2_code_free(m_re);
     m_re = NULL;
   }
 
-  if (m_sd)
+  if (m_ctxt)
   {
-    pcre_free_study(m_sd);
-    m_sd = NULL;
+    pcre2_match_context_free(m_ctxt);
+    m_ctxt = NULL;
   }
 
-#ifdef PCRE_HAS_JIT_CODE
   if (m_jitStack)
   {
-    pcre_jit_stack_free(m_jitStack);
+    pcre2_jit_stack_free(m_jitStack);
     m_jitStack = NULL;
   }
-#endif
 }
 
 inline bool CRegExp::IsValidSubNumber(int iSub) const
@@ -587,7 +596,7 @@
 {
   if (m_Utf8Supported == -1)
   {
-    if (pcre_config(PCRE_CONFIG_UTF8, &m_Utf8Supported) != 0)
+    if (pcre2_config(PCRE2_CONFIG_UNICODE, &m_Utf8Supported) < 0)
       m_Utf8Supported = 0;
   }
 
@@ -596,13 +605,11 @@
 
 bool CRegExp::AreUnicodePropertiesSupported(void)
 {
-#if defined(PCRE_CONFIG_UNICODE_PROPERTIES) && PCRE_UCP != 0
   if (m_UcpSupported == -1)
   {
-    if (pcre_config(PCRE_CONFIG_UNICODE_PROPERTIES, &m_UcpSupported) != 0)
+    if (pcre2_config(PCRE2_CONFIG_UNICODE, &m_UcpSupported) < 0)
       m_UcpSupported = 0;
   }
-#endif
 
   return m_UcpSupported == 1;
 }
@@ -625,13 +632,13 @@
 
   if (!utf8FullSupport)
   {
+    char ver[24];
+
+    pcre2_config(PCRE2_CONFIG_VERSION, ver);
     CLog::Log(LOGINFO,
-              "Consider installing PCRE lib version 8.10 or later with enabled Unicode properties "
+              "Consider installing PCRE lib version 10.10 or later with enabled Unicode properties "
               "and UTF-8 support. Your PCRE lib version: {}",
-              PCRE::pcre_version());
-#if PCRE_UCP == 0
-    CLog::Log(LOGINFO, "You will need to rebuild XBMC after PCRE lib update.");
-#endif
+              ver);
   }
 
   return utf8FullSupport;
@@ -641,9 +648,7 @@
 {
   if (m_JitSupported == -1)
   {
-#ifdef PCRE_HAS_JIT_CODE
-    if (pcre_config(PCRE_CONFIG_JIT, &m_JitSupported) != 0)
-#endif
+    if (pcre2_config(PCRE2_CONFIG_JIT, &m_JitSupported) < 0)
       m_JitSupported = 0;
   }
 
--- kodi-20.2+dfsg.orig/xbmc/utils/test/TestRegExp.cpp
+++ kodi-20.2+dfsg/xbmc/utils/test/TestRegExp.cpp
@@ -30,6 +30,29 @@
   EXPECT_EQ(-1, regex.RegFind("Test string."));
 }
 
+TEST(TestRegExp, InvalidPattern)
+{
+  CRegExp regex;
+
+  EXPECT_FALSE(regex.RegComp("+"));
+}
+
+TEST(TestRegExp, Unicode)
+{
+  CRegExp regex;
+
+  EXPECT_TRUE(regex.RegComp("Бог!$"));
+  EXPECT_EQ(12, regex.RegFind("С нами Бог!"));
+}
+
+TEST(TestRegExp, JIT)
+{
+  CRegExp regex;
+
+  EXPECT_TRUE(regex.RegComp(".JIT.", CRegExp::StudyWithJitComp));
+  EXPECT_EQ(12, regex.RegFind("Test string, JIT-matched."));
+}
+
 TEST(TestRegExp, GetReplaceString)
 {
   CRegExp regex;

Reply to: