# HG changeset patch
# User Rafael Laboissiere <rafael@laboissiere.net>
# Date 1668069042 10800
#      Thu Nov 10 05:30:42 2022 -0300
# Node ID 236c14a32d698941f9c9634c5b7a155ec7a7f60c
# Parent  870036573716139a9e3a162ecceeabe40de2424a
Switch from PCRE to PCRE2 (bug #61542)

* m4/acinclude.m4 (OCTAVE_CHECK_LIB_PCRE_OK): Adjust for PCRE2.

* configure.ac: Check for libpcre2-8 instead of libpcre and pcre2.h
  instead of pcre.h via OCTAVE_CHECK_LIB. Set argument INCLUDES of
  OCTAVE_CHECK_LIB when checking for PCRE2.

* liboctave/util/lo-regexp.h: Move inclusion of pcre2.h here. This
  simplifies the code in regexp.cc and lo-regexpp.cc. Define macro
  PCRE2_CODE_UNIT_WIDTH. Change the name of variable m_data to m_code,
  for increased coherence.

* libinterp/corefcn/regexp.cc: Drop inclusion of pcre.h.

* liboctave/util/lo-regexp.cc: Port code to PCRE2. All
  functionalities present in the previous version are preserved.

diff -r 870036573716 -r 236c14a32d69 configure.ac
--- a/configure.ac	Mon Nov 07 16:50:39 2022 -0500
+++ b/configure.ac	Thu Nov 10 05:30:42 2022 -0300
@@ -1375,14 +1375,17 @@
     [Define to 1 to build experimental Virtual Machine evaluator.])
 fi
 
-### Check for PCRE regex library.
-
+### Check for PCRE2 regex library.
+
+save_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="-DPCRE2_CODE_UNIT_WIDTH=8 $CPPFLAGS"
 OCTAVE_CHECK_LIB(pcre, PCRE,
-  [], [pcre.h pcre/pcre.h], [pcre_compile], [], [],
+  [], [pcre2.h pcre2/pcre2.h], [pcre2_compile_8], [], [],
   [OCTAVE_CHECK_LIB_PCRE_OK([],
     [AC_MSG_ERROR([PCRE library must be built with UTF support (--enable-utf)])])
   ],
-  [libpcre], [REQUIRED])
+  [libpcre2-8], [REQUIRED])
+CPPFLAGS="$save_CPPFLAGS"
 
 ### Check for Qhull library.
 
diff -r 870036573716 -r 236c14a32d69 libinterp/corefcn/regexp.cc
--- a/libinterp/corefcn/regexp.cc	Mon Nov 07 16:50:39 2022 -0500
+++ b/libinterp/corefcn/regexp.cc	Thu Nov 10 05:30:42 2022 -0300
@@ -30,8 +30,6 @@
 #include <list>
 #include <sstream>
 
-#include <pcre.h>
-
 #include "base-list.h"
 #include "oct-locbuf.h"
 #include "quit.h"
diff -r 870036573716 -r 236c14a32d69 liboctave/util/lo-regexp.cc
--- a/liboctave/util/lo-regexp.cc	Mon Nov 07 16:50:39 2022 -0500
+++ b/liboctave/util/lo-regexp.cc	Thu Nov 10 05:30:42 2022 -0300
@@ -32,10 +32,11 @@
 #include <string>
 #include <vector>
 
-#if defined (HAVE_PCRE_H)
-#  include <pcre.h>
-#elif defined (HAVE_PCRE_PCRE_H)
-#  include <pcre/pcre.h>
+#define PCRE2_CODE_UNIT_WIDTH 8
+#if defined (HAVE_PCRE2_H)
+#  include <pcre2.h>
+#elif defined (HAVE_PCRE2_PCRE2_H)
+#  include <pcre2/pcre2.h>
 #endif
 
 #include "Matrix.h"
@@ -46,6 +47,7 @@
 #include "lo-regexp.h"
 #include "str-vec.h"
 #include "unistr-wrappers.h"
+#include "unwind-prot.h"
 
 namespace octave
 {
@@ -64,8 +66,8 @@
   void
   regexp::free (void)
   {
-    if (m_data)
-      pcre_free (static_cast<pcre *> (m_data));
+    if (m_code)
+      pcre2_code_free (static_cast<pcre2_code *> (m_code));
   }
 
   void
@@ -229,22 +231,27 @@
     while ((pos = buf_str.find ('\0')) != std::string::npos)
       buf_str.replace (pos, 1, "\\000");
 
-    const char *err;
-    int erroffset;
+    PCRE2_SIZE erroffset;
+    int errnumber;
 
     int pcre_options
-      = (  (m_options.case_insensitive () ? PCRE_CASELESS : 0)
-         | (m_options.dotexceptnewline () ? 0 : PCRE_DOTALL)
-         | (m_options.lineanchors () ? PCRE_MULTILINE : 0)
-         | (m_options.freespacing () ? PCRE_EXTENDED : 0)
-         | PCRE_UTF8);
+      = (  (m_options.case_insensitive () ? PCRE2_CASELESS : 0)
+         | (m_options.dotexceptnewline () ? 0 : PCRE2_DOTALL)
+         | (m_options.lineanchors () ? PCRE2_MULTILINE : 0)
+         | (m_options.freespacing () ? PCRE2_EXTENDED : 0)
+         | PCRE2_UTF);
 
-    m_data = pcre_compile (buf_str.c_str (), pcre_options,
-                           &err, &erroffset, nullptr);
+    m_code = pcre2_compile (reinterpret_cast<PCRE2_SPTR> (buf_str.c_str ()),
+                            PCRE2_ZERO_TERMINATED, pcre_options,
+                            &errnumber, &erroffset, nullptr);
 
-    if (! m_data)
-      (*current_liboctave_error_handler)
-        ("%s: %s at position %d of expression", m_who.c_str (), err, erroffset);
+    if (! m_code)
+      {
+        PCRE2_UCHAR err [256];
+        pcre2_get_error_message (errnumber, err, sizeof (err));
+        (*current_liboctave_error_handler)
+          ("%s: %s at position %ld of expression", m_who.c_str (), err, erroffset);
+      }
   }
 
   regexp::match_data
@@ -266,14 +273,13 @@
     char *nametable;
     std::size_t idx = 0;
 
-    pcre *re = static_cast<pcre *> (m_data);
+    pcre2_code *re = static_cast<pcre2_code *> (m_code);
 
-    pcre_fullinfo (re, nullptr, PCRE_INFO_CAPTURECOUNT,  &subpatterns);
-    pcre_fullinfo (re, nullptr, PCRE_INFO_NAMECOUNT, &namecount);
-    pcre_fullinfo (re, nullptr, PCRE_INFO_NAMEENTRYSIZE, &nameentrysize);
-    pcre_fullinfo (re, nullptr, PCRE_INFO_NAMETABLE, &nametable);
+    pcre2_pattern_info (re, PCRE2_INFO_CAPTURECOUNT,  &subpatterns);
+    pcre2_pattern_info (re, PCRE2_INFO_NAMECOUNT, &namecount);
+    pcre2_pattern_info (re, PCRE2_INFO_NAMEENTRYSIZE, &nameentrysize);
+    pcre2_pattern_info (re, PCRE2_INFO_NAMETABLE, &nametable);
 
-    OCTAVE_LOCAL_BUFFER (int, ovector, (subpatterns+1)*3);
     OCTAVE_LOCAL_BUFFER (int, nidx, namecount);
 
     for (int i = 0; i < namecount; i++)
@@ -288,49 +294,27 @@
       {
         octave_quit ();
 
-        int matches = pcre_exec (re, nullptr, buffer.c_str (),
-                                 buffer.length (), idx,
-                                 PCRE_NO_UTF8_CHECK | (idx ? PCRE_NOTBOL : 0),
-                                 ovector, (subpatterns+1)*3);
+        PCRE2_SIZE *ovector;
+        pcre2_match_data *m_data = pcre2_match_data_create_from_pattern (re, NULL);
 
-        if (matches == PCRE_ERROR_MATCHLIMIT)
-          {
-            // Try harder; start with default value for MATCH_LIMIT
-            // and increase it.
-            (*current_liboctave_warning_with_id_handler)
-              ("Octave:regexp-match-limit",
-               "your pattern caused PCRE to hit its MATCH_LIMIT; trying harder now, but this will be slow");
+	unwind_action cleanup_match_data
+          ([=] () { pcre2_match_data_free (m_data); });
 
-            pcre_extra pe;
-
-            pcre_config (PCRE_CONFIG_MATCH_LIMIT,
-                         static_cast<void *> (&pe.match_limit));
-
-            pe.flags = PCRE_EXTRA_MATCH_LIMIT;
+        int matches = pcre2_match (re, reinterpret_cast<PCRE2_SPTR> (buffer.c_str ()),
+                                   buffer.length (), idx,
+                                   PCRE2_NO_UTF_CHECK | (idx ? PCRE2_NOTBOL : 0),
+                                   m_data, nullptr);
 
-            int i = 0;
-            while (matches == PCRE_ERROR_MATCHLIMIT
-                   && i++ < PCRE_MATCHLIMIT_MAX)
-              {
-                octave_quit ();
+        if (matches < 0 && matches != PCRE2_ERROR_NOMATCH)
+            (*current_liboctave_error_handler)
+              ("%s: internal error calling pcre2_match; "
+               "error code from pcre2_match is %i", m_who.c_str (), matches);
 
-                pe.match_limit *= 10;
-                matches = pcre_exec (re, &pe, buffer.c_str (),
-                                     buffer.length (), idx,
-                                     PCRE_NO_UTF8_CHECK
-                                     | (idx ? PCRE_NOTBOL : 0),
-                                     ovector, (subpatterns+1)*3);
-              }
-          }
+        if (matches == PCRE2_ERROR_NOMATCH)
+            break;
 
-        if (matches < 0 && matches != PCRE_ERROR_NOMATCH)
-          (*current_liboctave_error_handler)
-            ("%s: internal error calling pcre_exec; "
-             "error code from pcre_exec is %i", m_who.c_str (), matches);
-
-        if (matches == PCRE_ERROR_NOMATCH)
-          break;
-        else if (ovector[0] >= ovector[1] && ! m_options.emptymatch ())
+        ovector = pcre2_get_ovector_pointer (m_data);
+        if (ovector[0] >= ovector[1] && ! m_options.emptymatch ())
           {
             // Zero length match.  Skip to next char.
             idx = ovector[0] + 1;
@@ -346,7 +330,7 @@
 
             for (int i = 1; i < matches; i++)
               {
-                if (ovector[2*i] >= 0 && ovector[2*i+1] > 0
+                if (ovector[2*i] != PCRE2_SIZE_MAX && ovector[2*i+1] > 0
                     && (i == 1 || ovector[2*i] != ovector[2*i-2]
                         || ovector[2*i-1] != ovector[2*i+1]))
                   {
@@ -357,20 +341,12 @@
 
             token_extents.resize (pos_match, 2);
 
-            double start = double (ovector[0]+1);
-            double end = double (ovector[1]);
-
-            const char **listptr;
-            int status = pcre_get_substring_list (buffer.c_str (), ovector,
-                                                  matches, &listptr);
-
-            if (status == PCRE_ERROR_NOMEMORY)
-              (*current_liboctave_error_handler)
-                ("%s: cannot allocate memory in pcre_get_substring_list",
-                 m_who.c_str ());
+            PCRE2_SIZE start = ovector[0] + 1;
+            PCRE2_SIZE end = ovector[1];
 
             // Must use explicit length constructor as match can contain '\0'.
-            std::string match_string = std::string (*listptr, end - start + 1);
+            std::string match_string = std::string (buffer.c_str() + start - 1,
+                                                    end - start + 1);
 
             string_vector tokens (pos_match);
             string_vector named_tokens (m_names);
@@ -379,7 +355,7 @@
 
             for (int i = 1; i < matches; i++)
               {
-                if (ovector[2*i] >= 0 && ovector[2*i+1] > 0)
+                if (ovector[2*i] != PCRE2_SIZE_MAX && ovector[2*i+1] > 0)
                   {
                     if (i == 1 || ovector[2*i] != ovector[2*i-2]
                         || ovector[2*i-1] != ovector[2*i+1])
@@ -396,25 +372,25 @@
                                   {
                                     std::size_t len = ovector[2*i+1] - ovector[2*i];
                                     named_tokens(m_named_idx(j))
-                                      = std::string (*(listptr+i-pos_offset),
-                                                     len);
+                                        = std::string (buffer.c_str () + ovector[2*i],
+                                                       len);
                                     break;
                                   }
                               }
                           }
 
                         std::size_t len = ovector[2*i+1] - ovector[2*i];
-                        tokens(pos_match++) = std::string (*(listptr+i), len);
+                        tokens(pos_match++) = std::string (buffer.c_str() + ovector[2*i], len);
                       }
                     else
                       pos_offset++;
                   }
               }
 
-            pcre_free_substring_list (listptr);
-
             regexp::match_element new_elem (named_tokens, tokens, match_string,
-                                            token_extents, start, end);
+                                            token_extents,
+                                            static_cast<double> (start),
+                                            static_cast<double> (end));
             lst.push_back (new_elem);
 
             if (ovector[1] <= ovector[0])
@@ -430,6 +406,7 @@
             if (m_options.once () || idx >= buffer.length ())
               break;
           }
+
       }
 
     retval = regexp::match_data (lst, m_named_pats);
diff -r 870036573716 -r 236c14a32d69 liboctave/util/lo-regexp.h
--- a/liboctave/util/lo-regexp.h	Mon Nov 07 16:50:39 2022 -0500
+++ b/liboctave/util/lo-regexp.h	Thu Nov 10 05:30:42 2022 -0300
@@ -51,7 +51,7 @@
     regexp (const std::string& pat = "",
             const regexp::opts& opt = regexp::opts (),
             const std::string& w = "regexp")
-      : m_pattern (pat), m_options (opt), m_data (nullptr), m_named_pats (),
+      : m_pattern (pat), m_options (opt), m_code (nullptr), m_named_pats (),
         m_names (0), m_named_idx (), m_who (w)
     {
       compile_internal ();
@@ -225,7 +225,7 @@
     opts m_options;
 
     // Internal data describing the regular expression.
-    void *m_data;
+    void *m_code;
 
     string_vector m_named_pats;
     int m_names;
diff -r 870036573716 -r 236c14a32d69 m4/acinclude.m4
--- a/m4/acinclude.m4	Mon Nov 07 16:50:39 2022 -0500
+++ b/m4/acinclude.m4	Thu Nov 10 05:30:42 2022 -0300
@@ -1505,16 +1505,17 @@
     [AC_LANG_PUSH(C++)
     AC_RUN_IFELSE([AC_LANG_PROGRAM([[
         #include <stdio.h>
-        #if defined (HAVE_PCRE_H)
-        #  include <pcre.h>
-        #elif defined (HAVE_PCRE_PCRE_H)
-        #  include <pcre/pcre.h>
+        #define PCRE2_CODE_UNIT_WIDTH 8
+        #if defined (HAVE_PCRE2_H)
+        #  include <pcre2.h>
+        #elif defined (HAVE_PCRE2_PCRE2_H)
+        #  include <pcre2/pcre2.h>
         #endif
         ]], [[
         const char *pattern = "test";
-        const char *err;
-        int erroffset;
-        pcre *data = pcre_compile (pattern, PCRE_UTF8, &err, &erroffset, nullptr);
+        int err;
+        PCRE2_SIZE erroffset;
+        pcre2_code *data = pcre2_compile ((PCRE2_SPTR) pattern, PCRE2_ZERO_TERMINATED, PCRE2_UTF, &err, &erroffset, nullptr);
         return (! data);
       ]])],
       octave_cv_lib_pcre_ok=yes,
