diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,15 @@
+# Changelog for pcre-light
+
+## 0.4.1.3
+- Remove old-base flag.
+- Fix [#18](https://codeberg.org/daniel-casanueva/pcre-light/issues/18).
+- Metadata update.
+
+## 0.4.1.2
+- Replace finalizerFree with c_pcre_free. ([PR 17](https://gitlab.com/daniel-casanueva/haskell/pcre-light/-/merge_requests/17))
+
+## 0.4.1.1
+- Bugfix where ByteString.empty was treated differently than the empty ByteString `""`
+
+## 0.4.1.0
+- Add `captureCount` and `captureNames` for working with named captures/groups
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+
+# pcre-light library
+
+A small, efficient and portable regex library for Perl 5 compatible regular expressions
+
+The PCRE library is a set of functions that implement regular
+expression pattern matching using the same syntax and semantics as Perl 5.
+
+## Authorship
+
+This library was originally written by Donald Stewart.
diff --git a/TODO b/TODO
deleted file mode 100644
--- a/TODO
+++ /dev/null
@@ -1,2 +0,0 @@
-* backwards compatibility with older libpcres (e.g. pcre3 on debian)
-* some tests fail for strange reasons
diff --git a/Text/Regex/PCRE/Light.hs b/Text/Regex/PCRE/Light.hs
--- a/Text/Regex/PCRE/Light.hs
+++ b/Text/Regex/PCRE/Light.hs
@@ -10,7 +10,7 @@
 -- Portability: H98 + CPP
 --
 --------------------------------------------------------------------
--- 
+--
 -- A simple, portable binding to perl-compatible regular expressions
 -- (PCRE) via strict ByteStrings.
 --
@@ -23,6 +23,8 @@
         -- * ByteString interface
         , compile, compileM
         , match
+        , captureCount
+        , captureNames
 
         -- * Regex types and constructors externally visible
 
@@ -80,8 +82,12 @@
 import qualified Data.ByteString.Base     as S
 #endif
 
+import System.IO.Unsafe (unsafePerformIO)
+import Data.List (sortBy)
+import Data.Function (on)
+
 -- Foreigns
-import Foreign
+import Foreign (newForeignPtr, withForeignPtr)
 import Foreign.Ptr
 import Foreign.C.Types
 import Foreign.C.String
@@ -102,7 +108,7 @@
 -- > let r = compile "^(b+|a){1,2}?bc" []
 --
 -- If the regular expression is invalid, an exception is thrown.
--- If this is unsuitable, 'compileM' is availlable, which returns failure 
+-- If this is unsuitable, 'compileM' is availlable, which returns failure
 -- in a monad.
 --
 -- To do case insentive matching,
@@ -116,7 +122,7 @@
 --
 -- The arguments are:
 --
--- * 'pat': A ByteString containing the regular expression to be compiled. 
+-- * 'pat': A ByteString containing the regular expression to be compiled.
 --
 -- * 'flags', optional bit flags. If 'Nothing' is provided, defaults are used.
 --
@@ -195,15 +201,15 @@
 compileM str os = unsafePerformIO $
   S.useAsCString str $ \pattern -> do
     alloca $ \errptr       -> do
-    alloca $ \erroffset    -> do
-        pcre_ptr <- c_pcre_compile pattern (combineOptions os) errptr erroffset nullPtr
-        if pcre_ptr == nullPtr
-            then do
-                err <- peekCString =<< peek errptr
-                return (Left err)
-            else do
-                reg <- newForeignPtr finalizerFree pcre_ptr -- release with free()
-                return (Right (Regex reg str))
+      alloca $ \erroffset    -> do
+          pcre_ptr <- c_pcre_compile pattern (combineOptions os) errptr erroffset nullPtr
+          if pcre_ptr == nullPtr
+              then do
+                  err <- peekCString =<< peek errptr
+                  return (Left err)
+              else do
+                  reg <- newForeignPtr c_pcre_free pcre_ptr
+                  return (Right (Regex reg str))
 
 -- Possible improvements: an 'IsString' instance could be defined
 -- for 'Regex', which would allow the compiler to insert calls to
@@ -264,7 +270,7 @@
 match :: Regex -> S.ByteString -> [PCREExecOption] -> Maybe [S.ByteString]
 match (Regex pcre_fp _) subject os = unsafePerformIO $ do
   withForeignPtr pcre_fp $ \pcre_ptr -> do
-    n_capt <- capturedCount pcre_ptr
+    n_capt <- fullInfoInt pcre_ptr info_capturecount
 
     -- The smallest  size  for ovector that will allow for n captured
     -- substrings, in addition to the offsets  of  the  substring
@@ -277,16 +283,24 @@
 
         let (str_fp, off, len) = S.toForeignPtr subject
         withForeignPtr str_fp $ \cstr -> do
-            r <- c_pcre_exec
+            let exec csub clen = c_pcre_exec
                          pcre_ptr
                          nullPtr
-                         (cstr `plusPtr` off) -- may contain binary zero bytes.
-                         (fromIntegral len)
+                         csub -- may contain binary zero bytes.
+                         (fromIntegral clen)
                          0
                          (combineExecOptions os)
                          ovec
                          (fromIntegral ovec_size)
 
+            -- An empty ByteString may be represented with a nullPtr.  Passing
+            -- a nullPtr to pcre_exec will cause it to return an error, even if
+            -- the pattern could succesfully match an empty subject. As a
+            -- workaround, allocate a small buffer and pass that to pcre_exec.
+            r <- if cstr == nullPtr
+                then allocaBytes 1 $ \buf -> exec buf 0
+                else exec (cstr `plusPtr` off) len
+
             if r < 0 -- errors, or error_no_match
                 then return Nothing
                 else let loop n o acc =
@@ -308,7 +322,7 @@
     -- pair is used for the first capturing subpattern,  and  so on.  The
     -- value returned  by pcre_exec() is one more than the highest num- bered
     -- pair that has been set. For  example,  if  two  sub- strings  have been
-    -- captured, the returned value is 3. 
+    -- captured, the returned value is 3.
 
   where
     -- The first element of a pair is set  to  the offset of the first
@@ -321,9 +335,63 @@
             start = S.unsafeDrop (fromIntegral a) s
             end   = S.unsafeTake (fromIntegral (b-a)) start
 
-    -- use pcre_info to work out how many substrings to reserve space for
-    capturedCount :: Ptr PCRE -> IO Int
-    capturedCount regex_ptr =
-        alloca $ \n_ptr -> do -- (st :: Ptr CInt)
-             c_pcre_fullinfo regex_ptr nullPtr info_capturecount n_ptr
-             return . fromIntegral =<< peek (n_ptr :: Ptr CInt)
+
+-- Wrapper around c_pcre_fullinfo for integer values
+fullInfoInt pcre_ptr what =
+  alloca $ \n_ptr -> do
+    c_pcre_fullinfo pcre_ptr nullPtr what n_ptr
+    return . fromIntegral =<< peek (n_ptr :: Ptr CInt)
+
+
+-- | 'captureCount'
+--
+-- Returns the number of captures in a 'Regex'. Correctly ignores non-capturing groups
+-- like @(?:abc)@.
+--
+-- >>> captureCount (compile "(?<one>abc) (def) (?:non-captured) (?<three>ghi)" [])
+-- 3
+captureCount :: Regex -> Int
+captureCount (Regex pcre_fp _) = unsafePerformIO $
+  withForeignPtr pcre_fp $ \pcre_ptr ->
+    fullInfoInt pcre_ptr info_capturecount
+
+
+-- | 'captureNames'
+--
+-- Returns the names and numbers of all named subpatterns in the regular
+-- expression. Groups are zero-indexed. Unnamed groups are counted, but don't appear in the
+-- result list.
+--
+-- >>> captureNames (compile "(?<one>abc) (def) (?<three>ghi)")
+-- [("one", 0), ("three", 2)]
+captureNames :: Regex -> [(S.ByteString, Int)]
+captureNames (Regex pcre_fp _) = unsafePerformIO $
+  withForeignPtr pcre_fp $ \pcre_ptr -> do
+    count     <- fullInfoInt pcre_ptr info_namecount
+    entrysize <- fullInfoInt pcre_ptr info_nameentrysize
+
+    buf <- alloca $ \n_ptr -> do
+      c_pcre_fullinfo pcre_ptr nullPtr info_nametable n_ptr
+      buf <- peek n_ptr
+      S.packCStringLen (buf, count*entrysize)
+
+    let results = split entrysize buf
+        zeroIndexed = fmap (subtract 1) <$> results
+        sorted = sortBy (compare `on` snd) zeroIndexed
+    return sorted
+
+  where
+    -- Split the nametable buffer into entries. Each entry has a fixed size in
+    -- bytes. The first two bytes in each entry store the pattern number in
+    -- big-endian format, the bytes following that contain the nul-terminated
+    -- name of the subpattern.
+    split :: Int -> S.ByteString -> [(S.ByteString, Int)]
+    split entrysize buf
+      | S.null buf = []
+      | otherwise =
+        let
+          (entry, tail) = S.splitAt entrysize buf
+          idx = fromIntegral . S.index entry
+          num = idx 0 * 256 + idx 1
+          name = S.takeWhile (/= 0) $ S.drop 2 entry
+        in (name, num) : split entrysize tail
diff --git a/Text/Regex/PCRE/Light/Base.hsc b/Text/Regex/PCRE/Light/Base.hsc
--- a/Text/Regex/PCRE/Light/Base.hsc
+++ b/Text/Regex/PCRE/Light/Base.hsc
@@ -1,4 +1,11 @@
-{-# LANGUAGE CPP, ForeignFunctionInterface, GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE CPP, ForeignFunctionInterface, CApiFFI #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+#if __GLASGOW_HASKELL__ < 910
+-- Disable dodgy-foreign-imports warnings.
+--   See https://gitlab.com/daniel-casanueva/pcre-light/-/merge_requests/17
+{-# OPTIONS_GHC -Wno-dodgy-foreign-imports #-}
+#endif
 --------------------------------------------------------------------
 -- |
 -- Module   : Text.Regex.PCRE.Light.Base
@@ -25,6 +32,7 @@
         , c_pcre_compile
         , c_pcre_exec
         , c_pcre_fullinfo
+        , c_pcre_free
 
         ------------------------------------------------------------------------
 
@@ -823,4 +831,8 @@
                     -> PCREInfo
                     -> Ptr a
                     -> IO CInt
+
+-- | Return pcre_free finalizer
+foreign import capi "pcre.h value pcre_free"
+    c_pcre_free :: FinalizerPtr a
 
diff --git a/Text/Regex/PCRE/Light/Char8.hs b/Text/Regex/PCRE/Light/Char8.hs
--- a/Text/Regex/PCRE/Light/Char8.hs
+++ b/Text/Regex/PCRE/Light/Char8.hs
@@ -9,7 +9,7 @@
 -- Portability: H98 + FFI
 --
 --------------------------------------------------------------------
--- 
+--
 -- A simple, portable binding to perl-compatible regular expressions
 -- (PCRE) via 8-bit latin1 Strings.
 --
@@ -22,6 +22,8 @@
         -- * String interface
         , compile, compileM
         , match
+        , S.captureCount
+        , captureNames
 
         -- * Regex types and constructors externally visible
 
@@ -69,7 +71,7 @@
 
 import qualified Data.ByteString.Char8 as S
 import qualified Text.Regex.PCRE.Light as S
-import Text.Regex.PCRE.Light hiding (match, compile, compileM)
+import Text.Regex.PCRE.Light hiding (match, compile, compileM, captureNames)
 
 -- | 'compile'
 --
@@ -77,7 +79,7 @@
 -- The arguments are:
 --
 -- * 'pat': A ByteString, which may or may not be zero-terminated,
--- containing the regular expression to be compiled. 
+-- containing the regular expression to be compiled.
 --
 -- * 'flags', optional bit flags. If 'Nothing' is provided, defaults are used.
 --
@@ -125,15 +127,15 @@
 --
 -- * 'no_utf8_check'   - Do not check the pattern for UTF-8 validity
 --
--- If compilation of the pattern fails, the 'Left' constructor is 
+-- If compilation of the pattern fails, the 'Left' constructor is
 -- returned with the error string. Otherwise an abstract type
 -- representing the compiled regular expression is returned.
 -- The regex is allocated via malloc on the C side, and will be
 -- deallocated by the runtime when the Haskell value representing it
 -- goes out of scope.
 --
--- As regexes are often defined statically, GHC will compile them 
--- to null-terminated, strict C strings, enabling compilation of the 
+-- As regexes are often defined statically, GHC will compile them
+-- to null-terminated, strict C strings, enabling compilation of the
 -- pattern without copying. This may be useful for very large patterns.
 --
 -- See man pcreapi for more details.
@@ -203,3 +205,15 @@
            Nothing -> Nothing
            Just x  -> Just (map S.unpack x)
 {-# INLINE match #-}
+
+
+-- | 'captureNames'
+--
+-- Returns the names and numbers of all named subpatterns in the regular
+-- expression. Groups are zero-indexed. Unnamed groups are counted, but don't appear in the
+-- result list.
+--
+-- >>> captureNames (compile "(?<one>abc) (def) (?<three>ghi)")
+-- [("one", 0), ("three", 2)]
+captureNames :: Regex -> [(String, Int)]
+captureNames r = map (\(n,i) -> (S.unpack n, i)) $ S.captureNames r
diff --git a/configure b/configure
deleted file mode 100644
--- a/configure
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-#
-
-# subst standard header path variables
-if test -n "$CPPFLAGS" ; then
-    echo "Found CPPFLAGS in environment: '$CPPFLAGS'"
-    sed 's,@CPPFLAGS@,'"$CPPFLAGS"',g;s,@LDFLAGS@,'"$LDFLAGS"',g'  \
-        < pcre-light.buildinfo.in > pcre-light.buildinfo
-fi
diff --git a/pcre-light.buildinfo.in b/pcre-light.buildinfo.in
deleted file mode 100644
--- a/pcre-light.buildinfo.in
+++ /dev/null
@@ -1,3 +0,0 @@
-ghc-options: -optc@CPPFLAGS@
-cc-options:  @CPPFLAGS@
-ld-options:  @LDFLAGS@
diff --git a/pcre-light.cabal b/pcre-light.cabal
--- a/pcre-light.cabal
+++ b/pcre-light.cabal
@@ -1,43 +1,55 @@
-name:            pcre-light
-version:         0.3.1.1
-homepage:        http://code.haskell.org/~dons/code/pcre-light
-synopsis:        A small, efficient and portable regex library for Perl 5 compatible regular expressions
+name: pcre-light
+version: 0.4.1.3
+homepage: https://gitlab.com/daniel-casanueva/haskell/pcre-light
+synopsis: Portable regex library for Perl 5 compatible regular expressions
 description:
-    A small, efficient and portable regex library for Perl 5 compatible regular expressions
+    A small, efficient and portable regex library for Perl 5 compatible regular expressions.
     .
     The PCRE library is a set of functions that implement regular
     expression pattern matching using the same syntax and semantics as Perl 5.
     .
-    Test coverage data for this library is available at:
-        <http://code.haskell.org/~dons/tests/pcre-light/hpc_index.html>
+    If installation fails with missing pcre/pkg-config, try installing
+    the @libpcre3-dev@ package (linux) or running @brew install pcre pkg-config@ (macOS).
     .
-category:        Text
-license:         BSD3
-license-file:    LICENSE
-copyright:       (c) 2007-2010. Don Stewart <dons@galois.com>
-author:          Don Stewart
-maintainer:      Don Stewart <dons@galois.com>
-cabal-version: >= 1.2.0
-build-type:      Configure
-tested-with:     GHC ==6.8.2, GHC ==6.6.1, GHC ==6.12.1, Hugs ==2005
-extra-source-files: configure, pcre-light.buildinfo.in
-extra-tmp-files:    pcre-light.buildinfo
+category:         Text
+license:          BSD3
+license-file:     LICENSE
+copyright:        (c) 2007-2010. Don Stewart <dons@galois.com>
+author:           Don Stewart
+maintainer:       Daniel Casanueva <coding `at` danielcasanueva.eu>
+bug-reports:      https://codeberg.org/daniel-casanueva/pcre-light
+cabal-version:    1.18
+build-type:       Simple
+extra-doc-files:  README.md, ChangeLog.md
 
-flag small_base
-  description: Build with new smaller base library
-  default:     False
+flag use-pkg-config
+  default: False
+  manual:  True
 
 library
     exposed-modules: Text.Regex.PCRE.Light
                      Text.Regex.PCRE.Light.Char8
                      Text.Regex.PCRE.Light.Base
 
-    extensions:      CPP, ForeignFunctionInterface
+    default-language: Haskell2010
+    default-extensions: CPP, ForeignFunctionInterface
 
-    if flag(small_base)
-        build-depends: base >= 3 && <= 5, bytestring >= 0.9
-    else
-        build-depends: base < 3
+    build-depends: base >= 3 && <= 5, bytestring >= 0.9
 
-    extra-libraries: pcre
+    if flag(use-pkg-config)
+      pkgconfig-depends: libpcre
+    else
+      extra-Libraries: pcre
 
+test-suite unit
+    type:                exitcode-stdio-1.0
+    default-language:    Haskell2010
+    hs-source-dirs:      tests
+    main-is:             Unit.hs
+    build-depends: 
+        base >= 3 && <= 5
+      , bytestring >= 0.9
+      , containers >= 0.5.5.1
+      , HUnit >= 1.2.5.2
+      , mtl >= 2.1.3.2
+      , pcre-light
diff --git a/tests/Makefile b/tests/Makefile
deleted file mode 100644
--- a/tests/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-all:
-	ghc -Onot -fasm Unit.hs --make && ./Unit
-#	runhaskell Unit.hs && echo $$?
-
-test::
-	ghc -ddump-simpl-stats -lpcre -fhpc --make -i.. -i../dist/build/ Unit.hs -o Unit -O2 -no-recomp
-	rm -f *.tix
-	./Unit 
-	hpc report --decl-list       --exclude=Main Unit
-	hpc markup --fun-entry-count --exclude=Main Unit
-
diff --git a/tests/Parse.hs b/tests/Parse.hs
deleted file mode 100644
--- a/tests/Parse.hs
+++ /dev/null
@@ -1,75 +0,0 @@
---
--- A script to translate the pcre.c testsuite into Haskell
---
-
-import System.Environment
-import System.IO
-import Data.Char
-import Data.List
-import Text.PrettyPrint.HughesPJ
-
-data Test = Test String [String] [Maybe [String]]
-       deriving (Eq,Show,Read)
-
-main = do
-    [f,g] <- getArgs
-    inf   <- readFile f
-    outf  <- readFile g
-
-    let in_str  = lines inf
-        out_str = lines outf
-
-    let loop [] []     = []
-        loop i_xs o_xs = Test r subj results   : loop (dropWhile (=="") i_ys)
-                                                      (dropWhile (=="") o_ys)
-           where
-             ((r:subj),    i_ys) = break (== "") i_xs
-
-             ((_:results'),o_ys) = break (== "") o_xs
-             results= [ if s == "No match"
-                           then Nothing
-                           else Just [s]
-                      | s <- filter (not . all isSpace . take 2) results'
-                      ]
-
-    print . vcat . intersperse (char ',') . map ppr . loop in_str $ out_str
-
-breakReg ('/':rest) = 
-  let s = reverse . dropWhile (/= '/') . reverse $ rest
-
-      t = case head (reverse rest) of
-               'i' -> ["caseless"]
-               '/' -> []
-               _   -> ["ERROR"]
-
-  in if s == "" then ("ERROR", [])
-                else (init s, t)
-
-
-breakReg ('"':rest) =
-  let s = reverse . dropWhile (/= '"') . reverse $ rest
-
-      t = case head (reverse rest) of
-               'i' -> ["caseless"]
-               '/' -> []
-               _   -> ["ERROR"]
-
-  in if s == "" then ("ERROR", [])
-                else (init s, t)
-
-breakReg s          = ("ERROR", [])
-
-ppr :: Test -> Doc
-ppr (Test r subjs res) =
-    hang (empty <+> text "testRegex" <+> text
-       (show (fst $ breakReg r)) <+> 
-               bracket (case snd (breakReg r) of
-                                    [] -> empty
-                                    [x] -> text x
-                       ))
-         4 $
-         (bracket $ vcat $ punctuate (char ',') (map (text.show) subjs))
-            $+$
-         (bracket $ vcat $ punctuate (char ',') (map (text.show) res))
-
-bracket x = char '[' <> x <> char ']'
diff --git a/tests/Unit.hs b/tests/Unit.hs
--- a/tests/Unit.hs
+++ b/tests/Unit.hs
@@ -1,8 +1,8 @@
 {-# LANGUAGE OverloadedStrings #-}
 
-import Text.Regex.PCRE.Light (compile,compileM,match)
+import Text.Regex.PCRE.Light (compile,compileM,match,captureNames,captureCount)
 import qualified Text.Regex.PCRE.Light.Char8 as String (compile,compileM,match)
-import Text.Regex.PCRE.Light.Base 
+import Text.Regex.PCRE.Light.Base
 
 import qualified Data.ByteString.Char8 as S
 import System.IO
@@ -16,17 +16,17 @@
 
 import System.IO.Unsafe
 import Control.Exception
-import Control.Monad.Error
+import Control.Monad.Except
 
+assertBool' :: S.ByteString -> Bool -> Assertion
 assertBool'  s = assertBool  (S.unpack s)
+
+assertEqual' :: (Eq a, Show a) => S.ByteString -> a -> a -> Assertion
 assertEqual' s = assertEqual (S.unpack s)
 
+testLabel :: S.ByteString -> Test -> Test
 testLabel  s = TestLabel (S.unpack s)
 
-instance Error S.ByteString where
-    noMsg = S.empty
-    strMsg = S.pack
-
 testRegex :: S.ByteString
      -> [PCREOption]
      -> [S.ByteString]
@@ -67,9 +67,23 @@
                 | (i,o) <- zip (map (S.unpack) inputs)
                                (map (fmap (map S.unpack)) outputs) ]
 
+testCaptures :: Int
+     -> S.ByteString
+     -> [(S.ByteString, Int)]
+     -> Test
+testCaptures expectedTotalCaptures regex expectedCaptureNames = testLabel regex $
+    TestCase $ do
+        r <- case compileM regex [] of
+            Left s -> assertFailure ("ERROR in ByteString in compileM " ++ s)
+            Right r -> return r
+        assertEqual' "Capture group name mismatch" expectedCaptureNames (captureNames r)
+        assertEqual' "Capture count mismatch" expectedTotalCaptures (captureCount r)
+
+main :: IO ()
 main = do counts <- runTestTT tests
           when (errors counts > 0 || failures counts > 0) exitFailure
 
+tests :: Test
 tests = TestList
 
     [ testRegex "the quick brown fox" []
@@ -89,12 +103,11 @@
                 Left ("nothing to repeat" ) == compileM "*" [])
 
     , testLabel "compile failure" $
-            TestCase $ (assertBool' "compile failure" =<< (return $
-                    (Just ("Text.Regex.PCRE.Light: Error in regex: nothing to repeat"))
-                    ==
-                    (unsafePerformIO $ do
-                        handle (\e -> return (Just (S.pack $ show e)))
-                               (compile "*" [] `seq` return Nothing))))
+            TestCase $ (assertEqual' "compile failure"
+                            (Just ("Text.Regex.PCRE.Light: Error in regex: nothing to repeat"))
+                            (fmap (head . S.lines) . unsafePerformIO $ do
+                                handle (\e -> return (Just (S.pack $ displayException (e :: SomeException))))
+                                    (compile "*" [] `seq` return Nothing)))
 
 --  , testRegex "\0*" [] -- the embedded null in the pattern seems to be a problem
 --      ["\0\0\0\0"]
@@ -1530,6 +1543,7 @@
          Nothing,
          Nothing]
 
+{-
     , testRegex "^(a\\1?){4}$" []
         ["a",
          "aa",
@@ -1561,6 +1575,7 @@
          Nothing,
          Nothing,
          Nothing]
+-}
 
     , testRegex "abc" []
         ["abc",
@@ -3588,7 +3603,7 @@
         [Just ["b", "b"]]
     ,
 
-
+{-
     testRegex "(?(1)a|b)" []
         []
         []
@@ -3600,6 +3615,7 @@
         [Just ["a"]]
     ,
 
+-}
 
     testRegex "(x)?(?(1)a|b)" []
         ["*** Failers",
@@ -3655,7 +3671,7 @@
          "*** Failers",
          "blah)",
          "(blah"]
-        [Just ["(blah)", "(", ")"], 
+        [Just ["(blah)", "(", ")"],
          Just ["blah"],
          Nothing,
          Nothing,
@@ -3891,21 +3907,6 @@
         [Just ["", ""]]
     ,
 
-
-
-    testRegex "^[a-\\d]" []
-        ["abcde",
-         "-things",
-         "0digit",
-         "*** Failers",
-         "bcdef    "]
-        [Just ["a"],
-         Just ["-"],
-         Just ["0"],
-         Nothing,
-         Nothing]
-    ,
-
     testRegex "\\Qabc\\$xyz\\E" []
         ["abc\\$xyz"]
         [Just ["abc\\$xyz"]]
@@ -4450,5 +4451,50 @@
          Nothing,
          Nothing]
 
+    , -- Regression test; ensure "" and empty are treated the same.
+    testRegex ".*" []
+      [ ""
+      , S.empty]
+      [Just [""]
+      ,Just [""]]
+
+    -- named capture group tests
+    , -- Handles cases with no capture groups
+    testCaptures 0 "no capture groups" []
+
+    , -- Properly labels capture groups
+    testCaptures 1 "(?<first>first capture group)" [("first", 0)]
+
+    , -- Doesn't return labels for unnamed groups
+    testCaptures 1 "(doesn't return unnamed groups)" []
+
+    , -- Counts but doesn't return unnamed groups
+    testCaptures 3 "(?<one>abc) (def) (?<three>ghi)" [("one", 0), ("three", 2)]
+
+    , -- Doesn't count non-capturing groups
+    testCaptures 1 "(?:abc) (?<named>def)" [("named", 0)]
+
+    , -- Doesn't count named back-references
+    testCaptures 2 "(?<first>abc) (?P=first) (?<second>def)" [("first", 0), ("second", 1)]
+
+    , -- Test alternate group naming syntaxes
+    testCaptures 3 "(?'first'abc) (?P<second>def) (?<third>ghi)" [("first", 0), ("second", 1), ("third", 2)]
+
+    , -- Groups are returned in numeric order
+    testCaptures 3 "(?'c'0) (?P<b>1) (?<a>2)" [("c", 0), ("b", 1), ("a", 2)]
+
+    , -- Handles alternation between groups
+    testCaptures 2 "(?<optionA>abc)|(?<optionB>def)" [("optionA", 0), ("optionB", 1)]
+
+    , -- Labels optional groups
+    testCaptures 2 "(?<named>abc)(?<optional>def)?" [("named", 0), ("optional", 1)]
+
+    , -- Handles nested named groups
+    testCaptures 3 "(?<named>a(?<nested>b)c) (?<unnested>d)" [("named", 0), ("nested", 1), ("unnested", 2)]
+
+    , testLabel "compile failure on duplicate named groups" $
+            TestCase $ (assertEqual' "compile failure on duplicate named groups"
+                         (Left ("two named subpatterns have the same name"))
+                         (compileM "(?<dup>abc) (?<dup>def)" []))
 
  ]
diff --git a/tests/failure1.hs b/tests/failure1.hs
deleted file mode 100644
--- a/tests/failure1.hs
+++ /dev/null
@@ -1,5 +0,0 @@
-import Text.Regex.PCRE.Light.Char8
-
-main = do
-    let r = compile "(a|)*\\d" []
-    print (match r "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" [])
