From 23712f0770740cbe3182d8049bb9a660b03c0358 Mon Sep 17 00:00:00 2001
From: Reuben Thomas <rrt@sc3d.org>
Date: Wed, 10 Mar 2010 15:10:08 +0000
Subject: [PATCH] Optionally dlopen PCRE.

Adds a trivial "plugin" interface, ppp.h, which requires typeof.
---
 configure.ac    |   26 ++++++++++++++++++++++++
 src/Makefile.am |   10 +++++++-
 src/ppp.h       |   33 ++++++++++++++++++++++++++++++
 src/search.c    |   59 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 121 insertions(+), 7 deletions(-)
 create mode 100644 src/ppp.h

diff --git a/configure.ac b/configure.ac
index 5949f0e..be862c3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -159,6 +159,15 @@ if test "$ac_use_included_regex" = no; then
   AC_MSG_WARN([Included lib/regex.c not used])
 fi
 
+# support for dlopen
+AC_ARG_WITH(dl,
+            AC_HELP_STRING([--without-dl], [Don't try to use libdl]))
+if test "$with_dl" != "no"; then
+  AC_CHECK_LIB([dl], [dlopen])
+  AC_C_TYPEOF
+fi
+AM_CONDITIONAL(USING_LIBDL, [test x$ac_cv_lib_dl_dlopen = xyes])
+
 # support for pcre
 PKG_PROG_PKG_CONFIG
 if test x"$testpcre" = x"yes"; then
@@ -169,6 +178,23 @@ if test x"$testpcre" = x"yes"; then
 	  [:])
 fi
 
+# support for dlopening pcre
+AC_ARG_WITH([pcre-soname], [Specify the name of the PCRE shared library object], with_pcre_soname=$withval, [
+  AS_IF([test "$ac_cv_lib_dl_dlopen" = "yes"], [
+  save_LIBS="$LIBS"
+  LIBS="$PCRE_LIBS $LIBS"
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pcre.h>],
+    [pcre_maketables()])],[
+    with_pcre_soname=`readelf -d conftest 2>/dev/null | grep NEEDED.*libpcre | awk '{print $NF}' | tr -d '[\[\]]'`
+    if test -z "$with_pcre_soname" ; then
+      AC_MSG_ERROR([unable to detect libpcre SONAME; please use --with-pcre-soname])
+    fi
+    PCRE_SONAME="$with_pcre_soname"
+    AC_SUBST(PCRE_SONAME)],
+    [AC_MSG_ERROR([unable to link against PCRE: something went wrong!])])
+  LIBS="$save_LIBS"
+])])
+
 AC_CONFIG_FILES([
   Makefile
   lib/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 5104793..6fe9951 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,15 +17,21 @@
 LN = ln
 
 AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS)
+grep_CFLAGS = -DPCRE_SONAME='"$(PCRE_SONAME)"'
 
 bin_PROGRAMS = grep egrep fgrep
 grep_SOURCES  =  grep.c  search.c kwset.c dfa.c
 egrep_SOURCES = egrep.c esearch.c kwset.c dfa.c
 fgrep_SOURCES = fgrep.c fsearch.c kwset.c
-noinst_HEADERS = grep.h dfa.h kwset.h getpagesize.h system.h mbsupport.h
+noinst_HEADERS = grep.h dfa.h kwset.h getpagesize.h ppp.h system.h mbsupport.h
 
 LDADD = $(LIBINTL) ../lib/libgreputils.a
-grep_LDADD = $(PCRE_LIBS) $(LDADD)
+if !USING_LIBDL
+  grep_LDADD = $(PCRE_LIBS) $(LDADD)
+else
+  grep_LDADD = $(LDADD)
+endif
+
 localedir = $(datadir)/locale
 INCLUDES = -I$(top_srcdir)/lib -DLOCALEDIR=\"$(localedir)\"
 
diff --git a/src/ppp.h b/src/ppp.h
new file mode 100644
index 0000000..2f1965f
--- /dev/null
+++ b/src/ppp.h
@@ -0,0 +1,33 @@
+/* ppp.h - poor programmer's plugins.
+   Copyright 2010 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include <config.h>
+
+#if HAVE_LIBDL
+# include <dlfcn.h>
+
+# define PPP_DECL(f)                            \
+  static typeof (f) * dl_ ## f;
+# define PPP_BIND(l, f)                         \
+  (dl_ ## f = dlsym(l, #f))
+#else
+
+# define PPP_DECL(f)                            \
+  static typeof (f) * dl_ ## f = f;
+
+#endif
diff --git a/src/search.c b/src/search.c
index bd44696..5c8b816 100644
--- a/src/search.c
+++ b/src/search.c
@@ -40,9 +40,44 @@
 #include "error.h"
 #include "xalloc.h"
 #ifdef HAVE_LIBPCRE
+# include "ppp.h"
 # include <pcre.h>
 #endif
 
+#ifdef GREP_PROGRAM
+#ifdef HAVE_LIBPCRE
+PPP_DECL(pcre_compile);
+PPP_DECL(pcre_study);
+PPP_DECL(pcre_exec);
+PPP_DECL(pcre_maketables);
+
+#ifdef HAVE_LIBDL
+static bool
+load_pcre(void)
+{
+  static bool tried = false;
+  static bool loaded = false;
+
+  if (!tried)
+    {
+      void *lib = dlopen(PCRE_SONAME, RTLD_NOW);
+
+      if (lib)
+        loaded =
+          PPP_BIND(lib, pcre_compile) &&
+          PPP_BIND(lib, pcre_study) &&
+          PPP_BIND(lib, pcre_exec) &&
+          PPP_BIND(lib, pcre_maketables);
+
+      tried = true;
+    }
+
+  return loaded;
+}
+#endif /* HAVE_LIBDL */
+#endif /* HAVE_LIBPCRE */
+#endif /* GREP_PROGRAM */
+
 #define NCHAR (UCHAR_MAX + 1)
 
 /* For -w, we also consider _ to be word constituent.  */
@@ -676,6 +711,12 @@ COMPILE_FCT(Pcompile)
   char const *p;
   char const *pnul;
 
+#ifdef HAVE_LIBDL
+  if (!load_pcre ())
+    error (EXIT_TROUBLE, 0, "%s",
+           _("Support for the -P option cannot be found"));
+#endif
+
   /* FIXME: Remove these restrictions.  */
   if (memchr(pattern, '\n', size))
     error (EXIT_TROUBLE, 0, _("The -P option only supports a single pattern"));
@@ -714,11 +755,11 @@ COMPILE_FCT(Pcompile)
   if (match_lines)
     strcpy (n, ")$");
 
-  cre = pcre_compile (re, flags, &ep, &e, pcre_maketables ());
+  cre = dl_pcre_compile (re, flags, &ep, &e, dl_pcre_maketables ());
   if (!cre)
     error (EXIT_TROUBLE, 0, "%s", ep);
 
-  extra = pcre_study (cre, 0, &ep);
+  extra = dl_pcre_study (cre, 0, &ep);
   if (ep)
     error (EXIT_TROUBLE, 0, "%s", ep);
 
@@ -740,6 +781,14 @@ EXECUTE_FCT(Pexecute)
   int e = PCRE_ERROR_NOMATCH;
   ptrdiff_t start_ofs = start_ptr ? start_ptr - buf : 0;
 
+#ifdef HAVE_LIBDL
+  if (!load_pcre ())
+    {
+      abort ();
+      return -1;
+    }
+#endif
+
   /* PCRE can't limit the matching to single lines, therefore we have to
      match each line in the buffer separately.  */
   for (line_next = buf;
@@ -756,9 +805,9 @@ EXECUTE_FCT(Pexecute)
       if (start_ptr && start_ptr >= line_end)
         continue;
 
-      e = pcre_exec (cre, extra, line_buf, line_end - line_buf,
-                     start_ofs < 0 ? 0 : start_ofs, 0,
-                     sub, sizeof sub / sizeof *sub);
+      e = dl_pcre_exec (cre, extra, line_buf, line_end - line_buf,
+                        start_ofs < 0 ? 0 : start_ofs, 0,
+                        sub, sizeof sub / sizeof *sub);
     }
 
   if (e <= 0)
-- 
1.6.3.3

