diff --git a/Data/Text/ICU.hs b/Data/Text/ICU.hs
--- a/Data/Text/ICU.hs
+++ b/Data/Text/ICU.hs
@@ -62,6 +62,28 @@
     , collateIter
     , sortKey
     , uca
+    -- * Regular expressions
+    , MatchOption(..)
+    , ParseError(errError, errLine, errOffset)
+    , Match
+    , Regex
+    , Regular
+    -- ** Construction
+    , regex
+    , regex'
+    -- ** Inspection
+    , pattern
+    -- ** Searching
+    , find
+    , findAll
+    -- ** Match groups
+    -- $group
+    , groupCount
+    , unfold
+    , span
+    , group
+    , prefix
+    , suffix
     ) where
 
 import Data.Text.ICU.Break.Pure
@@ -69,6 +91,7 @@
 import Data.Text.ICU.Internal
 import Data.Text.ICU.Iterator
 import Data.Text.ICU.Normalize
+import Data.Text.ICU.Regex.Pure
 import Data.Text.ICU.Text
 #if defined(__HADDOCK__)
 import Data.Text.Foreign
@@ -129,3 +152,9 @@
 -- For the impure collation API (which is richer, but less easy to
 -- use than the pure API), see the 'Data.Text.ICU.Collate'
 -- module.
+
+-- $group
+--
+-- Capturing groups are numbered starting from zero.  Group zero is
+-- always the entire matching text.  Groups greater than zero contain
+-- the text matching each capturing group in a regular expression.
diff --git a/Data/Text/ICU/Error.hsc b/Data/Text/ICU/Error.hsc
--- a/Data/Text/ICU/Error.hsc
+++ b/Data/Text/ICU/Error.hsc
@@ -11,15 +11,19 @@
 -- Unicode (ICU) libraries.
 --
 -- Most ICU functions can throw an 'ICUError' value as an exception.
+-- Some can additionally throw a 'ParseError', if more detailed error
+-- information is necessary.
 module Data.Text.ICU.Error
     (
      -- * Types
      ICUError,
+     ParseError(errError, errLine, errOffset),
 
      -- * Functions
      isSuccess,
      isFailure,
      errorName,
+     isRegexError,
 
      -- * Warnings
      u_USING_FALLBACK_WARNING,
@@ -168,43 +172,43 @@
 
 #{enum ICUError, ICUError,
   u_USING_FALLBACK_WARNING = U_USING_FALLBACK_WARNING,
-  u_USING_DEFAULT_WARNING = U_USING_DEFAULT_WARNING,   
-  u_SAFECLONE_ALLOCATED_WARNING = U_SAFECLONE_ALLOCATED_WARNING, 
-  u_STATE_OLD_WARNING = U_STATE_OLD_WARNING,   
+  u_USING_DEFAULT_WARNING = U_USING_DEFAULT_WARNING,
+  u_SAFECLONE_ALLOCATED_WARNING = U_SAFECLONE_ALLOCATED_WARNING,
+  u_STATE_OLD_WARNING = U_STATE_OLD_WARNING,
   u_STRING_NOT_TERMINATED_WARNING = U_STRING_NOT_TERMINATED_WARNING,
-  u_SORT_KEY_TOO_SHORT_WARNING = U_SORT_KEY_TOO_SHORT_WARNING, 
-  u_AMBIGUOUS_ALIAS_WARNING = U_AMBIGUOUS_ALIAS_WARNING,   
-  u_DIFFERENT_UCA_VERSION = U_DIFFERENT_UCA_VERSION,     
-  u_ILLEGAL_ARGUMENT_ERROR = U_ILLEGAL_ARGUMENT_ERROR,     
-  u_MISSING_RESOURCE_ERROR = U_MISSING_RESOURCE_ERROR,     
-  u_INVALID_FORMAT_ERROR = U_INVALID_FORMAT_ERROR,     
-  u_FILE_ACCESS_ERROR = U_FILE_ACCESS_ERROR,     
-  u_INTERNAL_PROGRAM_ERROR = U_INTERNAL_PROGRAM_ERROR,     
-  u_MESSAGE_PARSE_ERROR = U_MESSAGE_PARSE_ERROR,     
-  u_MEMORY_ALLOCATION_ERROR = U_MEMORY_ALLOCATION_ERROR,     
-  u_INDEX_OUTOFBOUNDS_ERROR = U_INDEX_OUTOFBOUNDS_ERROR,     
-  u_PARSE_ERROR = U_PARSE_ERROR,     
-  u_INVALID_CHAR_FOUND = U_INVALID_CHAR_FOUND,     
-  u_TRUNCATED_CHAR_FOUND = U_TRUNCATED_CHAR_FOUND,     
-  u_ILLEGAL_CHAR_FOUND = U_ILLEGAL_CHAR_FOUND,     
-  u_INVALID_TABLE_FORMAT = U_INVALID_TABLE_FORMAT,     
-  u_INVALID_TABLE_FILE = U_INVALID_TABLE_FILE,     
-  u_BUFFER_OVERFLOW_ERROR = U_BUFFER_OVERFLOW_ERROR,     
-  u_UNSUPPORTED_ERROR = U_UNSUPPORTED_ERROR,     
-  u_RESOURCE_TYPE_MISMATCH = U_RESOURCE_TYPE_MISMATCH,     
-  u_ILLEGAL_ESCAPE_SEQUENCE = U_ILLEGAL_ESCAPE_SEQUENCE,     
-  u_UNSUPPORTED_ESCAPE_SEQUENCE = U_UNSUPPORTED_ESCAPE_SEQUENCE, 
-  u_NO_SPACE_AVAILABLE = U_NO_SPACE_AVAILABLE,     
-  u_CE_NOT_FOUND_ERROR = U_CE_NOT_FOUND_ERROR,     
-  u_PRIMARY_TOO_LONG_ERROR = U_PRIMARY_TOO_LONG_ERROR,     
-  u_STATE_TOO_OLD_ERROR = U_STATE_TOO_OLD_ERROR,     
-  u_TOO_MANY_ALIASES_ERROR = U_TOO_MANY_ALIASES_ERROR,     
-  u_ENUM_OUT_OF_SYNC_ERROR = U_ENUM_OUT_OF_SYNC_ERROR,     
-  u_INVARIANT_CONVERSION_ERROR = U_INVARIANT_CONVERSION_ERROR,  
-  u_INVALID_STATE_ERROR = U_INVALID_STATE_ERROR,     
-  u_COLLATOR_VERSION_MISMATCH = U_COLLATOR_VERSION_MISMATCH,   
-  u_USELESS_COLLATOR_ERROR = U_USELESS_COLLATOR_ERROR,     
-  u_NO_WRITE_PERMISSION = U_NO_WRITE_PERMISSION,     
+  u_SORT_KEY_TOO_SHORT_WARNING = U_SORT_KEY_TOO_SHORT_WARNING,
+  u_AMBIGUOUS_ALIAS_WARNING = U_AMBIGUOUS_ALIAS_WARNING,
+  u_DIFFERENT_UCA_VERSION = U_DIFFERENT_UCA_VERSION,
+  u_ILLEGAL_ARGUMENT_ERROR = U_ILLEGAL_ARGUMENT_ERROR,
+  u_MISSING_RESOURCE_ERROR = U_MISSING_RESOURCE_ERROR,
+  u_INVALID_FORMAT_ERROR = U_INVALID_FORMAT_ERROR,
+  u_FILE_ACCESS_ERROR = U_FILE_ACCESS_ERROR,
+  u_INTERNAL_PROGRAM_ERROR = U_INTERNAL_PROGRAM_ERROR,
+  u_MESSAGE_PARSE_ERROR = U_MESSAGE_PARSE_ERROR,
+  u_MEMORY_ALLOCATION_ERROR = U_MEMORY_ALLOCATION_ERROR,
+  u_INDEX_OUTOFBOUNDS_ERROR = U_INDEX_OUTOFBOUNDS_ERROR,
+  u_PARSE_ERROR = U_PARSE_ERROR,
+  u_INVALID_CHAR_FOUND = U_INVALID_CHAR_FOUND,
+  u_TRUNCATED_CHAR_FOUND = U_TRUNCATED_CHAR_FOUND,
+  u_ILLEGAL_CHAR_FOUND = U_ILLEGAL_CHAR_FOUND,
+  u_INVALID_TABLE_FORMAT = U_INVALID_TABLE_FORMAT,
+  u_INVALID_TABLE_FILE = U_INVALID_TABLE_FILE,
+  u_BUFFER_OVERFLOW_ERROR = U_BUFFER_OVERFLOW_ERROR,
+  u_UNSUPPORTED_ERROR = U_UNSUPPORTED_ERROR,
+  u_RESOURCE_TYPE_MISMATCH = U_RESOURCE_TYPE_MISMATCH,
+  u_ILLEGAL_ESCAPE_SEQUENCE = U_ILLEGAL_ESCAPE_SEQUENCE,
+  u_UNSUPPORTED_ESCAPE_SEQUENCE = U_UNSUPPORTED_ESCAPE_SEQUENCE,
+  u_NO_SPACE_AVAILABLE = U_NO_SPACE_AVAILABLE,
+  u_CE_NOT_FOUND_ERROR = U_CE_NOT_FOUND_ERROR,
+  u_PRIMARY_TOO_LONG_ERROR = U_PRIMARY_TOO_LONG_ERROR,
+  u_STATE_TOO_OLD_ERROR = U_STATE_TOO_OLD_ERROR,
+  u_TOO_MANY_ALIASES_ERROR = U_TOO_MANY_ALIASES_ERROR,
+  u_ENUM_OUT_OF_SYNC_ERROR = U_ENUM_OUT_OF_SYNC_ERROR,
+  u_INVARIANT_CONVERSION_ERROR = U_INVARIANT_CONVERSION_ERROR,
+  u_INVALID_STATE_ERROR = U_INVALID_STATE_ERROR,
+  u_COLLATOR_VERSION_MISMATCH = U_COLLATOR_VERSION_MISMATCH,
+  u_USELESS_COLLATOR_ERROR = U_USELESS_COLLATOR_ERROR,
+  u_NO_WRITE_PERMISSION = U_NO_WRITE_PERMISSION,
   u_BAD_VARIABLE_DEFINITION = U_BAD_VARIABLE_DEFINITION,
   u_MALFORMED_RULE = U_MALFORMED_RULE,
   u_MALFORMED_SET = U_MALFORMED_SET,
@@ -234,7 +238,7 @@
   u_INTERNAL_TRANSLITERATOR_ERROR = U_INTERNAL_TRANSLITERATOR_ERROR,
   u_INVALID_ID = U_INVALID_ID,
   u_INVALID_FUNCTION = U_INVALID_FUNCTION,
-  u_UNEXPECTED_TOKEN = U_UNEXPECTED_TOKEN,       
+  u_UNEXPECTED_TOKEN = U_UNEXPECTED_TOKEN,
   u_MULTIPLE_DECIMAL_SEPARATORS = U_MULTIPLE_DECIMAL_SEPARATORS,
   u_MULTIPLE_EXPONENTIAL_SYMBOLS = U_MULTIPLE_EXPONENTIAL_SYMBOLS,
   u_MALFORMED_EXPONENTIAL_PATTERN = U_MALFORMED_EXPONENTIAL_PATTERN,
@@ -248,7 +252,7 @@
   u_DUPLICATE_KEYWORD = U_DUPLICATE_KEYWORD,
   u_UNDEFINED_KEYWORD = U_UNDEFINED_KEYWORD,
   u_DEFAULT_KEYWORD_MISSING = U_DEFAULT_KEYWORD_MISSING,
-  u_BRK_INTERNAL_ERROR = U_BRK_INTERNAL_ERROR,          
+  u_BRK_INTERNAL_ERROR = U_BRK_INTERNAL_ERROR,
   u_BRK_HEX_DIGITS_EXPECTED = U_BRK_HEX_DIGITS_EXPECTED,
   u_BRK_SEMICOLON_EXPECTED = U_BRK_SEMICOLON_EXPECTED,
   u_BRK_RULE_SYNTAX = U_BRK_RULE_SYNTAX,
@@ -262,7 +266,7 @@
   u_BRK_RULE_EMPTY_SET = U_BRK_RULE_EMPTY_SET,
   u_BRK_UNRECOGNIZED_OPTION = U_BRK_UNRECOGNIZED_OPTION,
   u_BRK_MALFORMED_RULE_TAG = U_BRK_MALFORMED_RULE_TAG,
-  u_REGEX_INTERNAL_ERROR = U_REGEX_INTERNAL_ERROR,       
+  u_REGEX_INTERNAL_ERROR = U_REGEX_INTERNAL_ERROR,
   u_REGEX_RULE_SYNTAX = U_REGEX_RULE_SYNTAX,
   u_REGEX_INVALID_STATE = U_REGEX_INVALID_STATE,
   u_REGEX_BAD_ESCAPE_SEQUENCE = U_REGEX_BAD_ESCAPE_SEQUENCE,
@@ -290,3 +294,7 @@
   u_IDNA_ZERO_LENGTH_LABEL_ERROR = U_IDNA_ZERO_LENGTH_LABEL_ERROR,
   u_IDNA_DOMAIN_NAME_TOO_LONG_ERROR = U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR
 }
+
+isRegexError :: ICUError -> Bool
+isRegexError (ICUError err) =
+    err >= #{const U_REGEX_ERROR_START} && err < #{const U_REGEX_ERROR_LIMIT}
diff --git a/Data/Text/ICU/Error/Internal.hs b/Data/Text/ICU/Error/Internal.hs
deleted file mode 100644
--- a/Data/Text/ICU/Error/Internal.hs
+++ /dev/null
@@ -1,80 +0,0 @@
-{-# LANGUAGE DeriveDataTypeable, ForeignFunctionInterface #-}
-
-module Data.Text.ICU.Error.Internal
-    (
-    -- * Types
-      ICUError(..)
-    -- ** Low-level types
-    , UErrorCode
-    -- * Functions
-    , isFailure
-    , isSuccess
-    , errorName
-    , handleError
-    , throwOnError
-    , withError
-    ) where
-
-import Control.Exception (Exception, throw)
-import Foreign.Ptr (Ptr)
-import Foreign.Marshal.Utils (with)
-import Data.Typeable (Typeable)
-import Foreign.C.String (CString, peekCString)
-import Foreign.C.Types (CInt)
-import Foreign.Storable (peek)
-import System.IO.Unsafe (unsafePerformIO)
-
-type UErrorCode = CInt
-
--- | ICU error type.  This is an instance of the 'Exception' type
--- class.  A value of this type may be thrown as an exception by most
--- ICU functions.
-newtype ICUError = ICUError {
-      fromErrorCode :: UErrorCode
-    } deriving (Eq, Typeable)
-
-instance Show ICUError where
-    show code = "ICUError " ++ errorName code
-
-instance Exception ICUError
-
--- | Indicate whether the given error code is a success.
-isSuccess :: ICUError -> Bool
-{-# INLINE isSuccess #-}
-isSuccess = (<= 0) . fromErrorCode
-
--- | Indicate whether the given error code is a failure.
-isFailure :: ICUError -> Bool
-{-# INLINE isFailure #-}
-isFailure = (> 0) . fromErrorCode
-
--- | Throw an exception if the given code is actually an error.
-throwOnError :: UErrorCode -> IO ()
-{-# INLINE throwOnError #-}
-throwOnError code = do
-  let err = (ICUError code)
-  if isFailure err
-    then throw err
-    else return ()
-
-withError :: (Ptr UErrorCode -> IO a) -> IO (ICUError, a)
-{-# INLINE withError #-}
-withError action = with 0 $ \errPtr -> do
-                     ret <- action errPtr
-                     err <- peek errPtr
-                     return (ICUError err, ret)
-
-handleError :: (Ptr UErrorCode -> IO a) -> IO a
-{-# INLINE handleError #-}
-handleError action = with 0 $ \errPtr -> do
-                       ret <- action errPtr
-                       throwOnError =<< peek errPtr
-                       return ret
-
--- | Return a string representing the name of the given error code.
-errorName :: ICUError -> String
-errorName code = unsafePerformIO $
-                 peekCString (u_errorName (fromErrorCode code))
-
-foreign import ccall unsafe "hs_text_icu.h __hs_u_errorName" u_errorName
-    :: UErrorCode -> CString
diff --git a/Data/Text/ICU/Error/Internal.hsc b/Data/Text/ICU/Error/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/Data/Text/ICU/Error/Internal.hsc
@@ -0,0 +1,138 @@
+{-# LANGUAGE DeriveDataTypeable, ForeignFunctionInterface,
+    ScopedTypeVariables #-}
+
+module Data.Text.ICU.Error.Internal
+    (
+    -- * Types
+      ICUError(..)
+    -- ** Low-level types
+    , UErrorCode
+    , ParseError(errError, errLine, errOffset)
+    , UParseError
+    -- * Functions
+    , isFailure
+    , isSuccess
+    , errorName
+    , handleError
+    , handleParseError
+    , throwOnError
+    , withError
+    ) where
+
+import Control.Exception (Exception, throw)
+import Foreign.Ptr (Ptr)
+import Foreign.Marshal.Alloc (alloca)
+import Foreign.Marshal.Utils (with)
+import Data.Int (Int32)
+import Data.Typeable (Typeable)
+import Foreign.C.String (CString, peekCString)
+import Foreign.C.Types (CInt)
+import Foreign.Storable (Storable(..))
+import System.IO.Unsafe (unsafePerformIO)
+
+#include <unicode/parseerr.h>
+#include <unicode/utypes.h>
+
+type UErrorCode = CInt
+    
+-- | ICU error type.  This is an instance of the 'Exception' type
+-- class.  A value of this type may be thrown as an exception by most
+-- ICU functions.
+newtype ICUError = ICUError {
+      fromErrorCode :: UErrorCode
+    } deriving (Eq, Typeable)
+
+instance Show ICUError where
+    show code = "ICUError " ++ errorName code
+
+instance Exception ICUError
+
+-- | Detailed information about parsing errors.  Used by ICU parsing
+-- engines that parse long rules, patterns, or programs, where the
+-- text being parsed is long enough that more information than an
+-- 'ICUError' is needed to localize the error.
+data ParseError = ParseError {
+      errError :: ICUError
+    , errLine :: !(Maybe Int)
+    -- ^ The line on which the error occured.  If the parser uses this
+    -- field, it sets it to the line number of the source text line on
+    -- which the error appears, which will be be a positive value.  If
+    -- the parser does not support line numbers, the value will be
+    -- 'Nothing'.
+    , errOffset :: !(Maybe Int)
+    -- ^ The character offset to the error.  If the 'errLine' field is
+    -- 'Just' some value, then this field contains the offset from the
+    -- beginning of the line that contains the error.  Otherwise, it
+    -- represents the offset from the start of the text.  If the
+    -- parser does not support this field, it will have a value of
+    -- 'Nothing'.
+    } deriving (Show, Typeable)
+
+type UParseError = ParseError
+
+instance Exception ParseError
+
+instance Storable ParseError where
+    sizeOf _    = #{size UParseError}
+    alignment _ = alignment (undefined :: CString)
+    peek ptr = do
+      (line::Int32) <- #{peek UParseError, line} ptr
+      (offset::Int32) <- #{peek UParseError, offset} ptr
+      let wrap k = if k == -1 then Nothing else Just $! fromIntegral k
+      return $! ParseError undefined (wrap line) (wrap offset)
+
+-- | Indicate whether the given error code is a success.
+isSuccess :: ICUError -> Bool
+{-# INLINE isSuccess #-}
+isSuccess = (<= 0) . fromErrorCode
+
+-- | Indicate whether the given error code is a failure.
+isFailure :: ICUError -> Bool
+{-# INLINE isFailure #-}
+isFailure = (> 0) . fromErrorCode
+
+-- | Throw an exception if the given code is actually an error.
+throwOnError :: UErrorCode -> IO ()
+{-# INLINE throwOnError #-}
+throwOnError code = do
+  let err = (ICUError code)
+  if isFailure err
+    then throw err
+    else return ()
+
+withError :: (Ptr UErrorCode -> IO a) -> IO (ICUError, a)
+{-# INLINE withError #-}
+withError action = with 0 $ \errPtr -> do
+                     ret <- action errPtr
+                     err <- peek errPtr
+                     return (ICUError err, ret)
+
+handleError :: (Ptr UErrorCode -> IO a) -> IO a
+{-# INLINE handleError #-}
+handleError action = with 0 $ \errPtr -> do
+                       ret <- action errPtr
+                       throwOnError =<< peek errPtr
+                       return ret
+
+handleParseError :: (ICUError -> Bool)
+                 -> (Ptr UParseError -> Ptr UErrorCode -> IO a) -> IO a
+handleParseError isParseError action =
+    with 0 $ \uerrPtr ->
+      alloca $ \perrPtr -> do
+        ret <- action perrPtr uerrPtr
+        err <- ICUError `fmap` peek uerrPtr
+        if isParseError err
+          then do
+            perr <- peek perrPtr
+            throw perr { errError = err }
+          else if isFailure err
+               then throw err
+               else return ret
+
+-- | Return a string representing the name of the given error code.
+errorName :: ICUError -> String
+errorName code = unsafePerformIO $
+                 peekCString (u_errorName (fromErrorCode code))
+
+foreign import ccall unsafe "hs_text_icu.h __hs_u_errorName" u_errorName
+    :: UErrorCode -> CString
diff --git a/Data/Text/ICU/Regex.hs b/Data/Text/ICU/Regex.hs
new file mode 100644
--- /dev/null
+++ b/Data/Text/ICU/Regex.hs
@@ -0,0 +1,198 @@
+{-# LANGUAGE BangPatterns, EmptyDataDecls, MagicHash, RecordWildCards,
+    ScopedTypeVariables #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+-- |
+-- Module      : Data.Text.ICU.Regex
+-- Copyright   : (c) 2010 Bryan O'Sullivan
+--
+-- License     : BSD-style
+-- Maintainer  : bos@serpentine.com
+-- Stability   : experimental
+-- Portability : GHC
+--
+-- Regular expression support for Unicode, implemented as bindings to
+-- the International Components for Unicode (ICU) libraries.
+--
+-- The syntax and behaviour of ICU regular expressions are Perl-like.
+-- For complete details, see the ICU User Guide entry at
+-- <http://userguide.icu-project.org/strings/regexp>.
+--
+-- /Note/: The functions in this module are not thread safe.  For
+-- thread safe use, see 'clone' below, or use the pure functions in
+-- 'Data.Text.ICU'.
+
+module Data.Text.ICU.Regex
+    (
+    -- * Types
+      MatchOption(..)
+    , ParseError(errError, errLine, errOffset)
+    , Regex
+    -- * Functions
+    -- ** Construction
+    , regex
+    , regex'
+    , clone
+    -- ** Managing text to search
+    , setText
+    , getText
+    -- ** Inspection
+    , pattern
+    -- ** Searching
+    , find
+    , findNext
+    -- ** Match groups
+    -- $groups
+    , groupCount
+    , start
+    , end
+    , start_
+    , end_
+    ) where
+
+import Data.Text.ICU.Regex.Internal
+import Control.Exception (catch)
+import Data.IORef (newIORef, readIORef, writeIORef)
+import Data.Text (Text)
+import qualified Data.Text.Foreign as T
+import Data.Text.Foreign (I16)
+import Data.Text.ICU.Internal (asBool)
+import Data.Text.ICU.Error.Internal (ParseError(..), handleError)
+import Data.Word (Word16)
+import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, withForeignPtr)
+import Foreign.Marshal.Alloc (alloca)
+import Foreign.Storable (peek)
+import Prelude hiding (catch)
+import System.IO.Unsafe (unsafePerformIO)
+                   
+instance Show Regex where
+    show re = "Regex " ++ show (pattern re)
+
+-- $groups
+--
+-- Capturing groups are numbered starting from zero.  Group zero is
+-- always the entire matching text.  Groups greater than zero contain
+-- the text matching each capturing group in a regular expression.
+
+-- | Compile a regular expression with the given options.  This is
+-- safest to use when the pattern is constructed at run time.
+regex' :: [MatchOption] -> Text -> IO (Either ParseError Regex)
+regex' opts pat = (Right `fmap` regex opts pat) `catch` \(err::ParseError) ->
+                  return (Left err)
+
+-- | Set the subject text string upon which the regular expression
+-- will look for matches.  This function may be called any number of
+-- times, allowing the regular expression pattern to be applied to
+-- different strings.
+setText :: Regex -> Text -> IO ()
+setText Regex{..} t = do
+  (hayfp, hayLen) <- T.asForeignPtr t
+  withForeignPtr reRe $ \rePtr ->
+    withForeignPtr hayfp $ \hayPtr -> handleError $
+      uregex_setText rePtr hayPtr (fromIntegral hayLen)
+  writeIORef reText hayfp
+
+-- | Get the subject text that is currently associated with this
+-- regular expression object.
+getText :: Regex -> IO (ForeignPtr Word16, I16)
+getText Regex{..} =
+  alloca $ \lenPtr -> do
+    _ <- withForeignPtr reRe $ \rePtr -> handleError $
+         uregex_getText rePtr lenPtr
+    len <- peek lenPtr
+    fp <- readIORef reText
+    return (fp, fromIntegral len)
+
+-- | Return the source form of the pattern used to construct this
+-- regular expression or match.
+pattern :: Regex -> Text
+pattern Regex{..} = unsafePerformIO . withForeignPtr reRe $ \rePtr ->
+  alloca $ \lenPtr -> do
+    textPtr <- handleError $ uregex_pattern rePtr lenPtr
+    (T.fromPtr textPtr . fromIntegral) =<< peek lenPtr
+
+-- | Find the first matching substring of the input string that
+-- matches the pattern.
+--
+-- If /n/ is non-negative, the search for a match begins at the
+-- specified index, and any match region is reset.
+--
+-- If /n/ is -1, the search begins at the start of the input region,
+-- or at the start of the full string if no region has been specified.
+--
+-- If a match is found, 'start', 'end', and 'group' will provide more
+-- information regarding the match.
+find :: Regex -> I16 -> IO Bool
+find Regex{..} n =
+    fmap asBool . withForeignPtr reRe $ \rePtr -> handleError $
+    uregex_find rePtr (fromIntegral n)
+
+-- | Find the next pattern match in the input string.  Begin searching
+-- the input at the location following the end of he previous match,
+-- or at the start of the string (or region) if there is no previous
+-- match.
+--
+-- If a match is found, 'start', 'end', and 'group' will provide more
+-- information regarding the match.
+findNext :: Regex -> IO Bool
+findNext Regex{..} =
+    fmap asBool . withForeignPtr reRe $ handleError . uregex_findNext
+
+-- | Make a copy of a compiled regular expression.  Cloning a regular
+-- expression is faster than opening a second instance from the source
+-- form of the expression, and requires less memory.
+--
+-- Note that the current input string and the position of any matched
+-- text within it are not cloned; only the pattern itself and and the
+-- match mode flags are copied.
+--
+-- Cloning can be particularly useful to threaded applications that
+-- perform multiple match operations in parallel.  Each concurrent RE
+-- operation requires its own instance of a 'Regex'.
+clone :: Regex -> IO Regex
+{-# INLINE clone #-}
+clone Regex{..} = do
+  fp <- newForeignPtr uregex_close =<< withForeignPtr reRe (handleError . uregex_clone)
+  Regex fp `fmap` newIORef emptyForeignPtr
+
+-- | Return the number of capturing groups in this regular
+-- expression's pattern.
+groupCount :: Regex -> IO Int
+groupCount Regex{..} =
+    fmap fromIntegral . withForeignPtr reRe $ handleError . uregex_groupCount
+
+-- | Returns the index in the input string of the start of the text
+-- matched by the specified capture group during the previous match
+-- operation.  Returns @-1@ if the capture group was not part of the
+-- last match.
+start_ :: Regex -> Int -> IO I16
+start_ Regex{..} n =
+    fmap fromIntegral . withForeignPtr reRe $ \rePtr -> handleError $
+    uregex_start rePtr (fromIntegral n)
+
+-- | Returns the index in the input string of the end of the text
+-- matched by the specified capture group during the previous match
+-- operation.  Returns @-1@ if the capture group was not part of
+-- the last match.
+end_ :: Regex -> Int -> IO I16
+end_ Regex{..} n =
+  fmap fromIntegral . withForeignPtr reRe $ \rePtr -> handleError $
+  uregex_end rePtr (fromIntegral n)
+
+-- | Returns the index in the input string of the start of the text
+-- matched by the specified capture group during the previous match
+-- operation.  Returns 'Nothing' if the capture group was not part of
+-- the last match.
+start :: Regex -> Int -> IO (Maybe I16)
+start r n = check `fmap` start_ r n
+
+-- | Returns the index in the input string of the end of the text
+-- matched by the specified capture group during the previous match
+-- operation.  Returns 'Nothing' if the capture group was not part of
+-- the last match.
+end :: Regex -> Int -> IO (Maybe I16)
+end r n = check `fmap` end_ r n
+
+check :: I16 -> Maybe I16
+check (-1) = Nothing
+check k    = Just $! fromIntegral k
diff --git a/Data/Text/ICU/Regex/Internal.hsc b/Data/Text/ICU/Regex/Internal.hsc
new file mode 100644
--- /dev/null
+++ b/Data/Text/ICU/Regex/Internal.hsc
@@ -0,0 +1,230 @@
+{-# LANGUAGE BangPatterns, DeriveDataTypeable, EmptyDataDecls,
+    ForeignFunctionInterface, MagicHash, RecordWildCards,
+    ScopedTypeVariables #-}
+
+-- |
+-- Module      : Data.Text.ICU.Regex.Internal
+-- Copyright   : (c) 2010 Bryan O'Sullivan
+--
+-- License     : BSD-style
+-- Maintainer  : bos@serpentine.com
+-- Stability   : experimental
+-- Portability : GHC
+--
+-- Regular expression support for Unicode, implemented as bindings to
+-- the International Components for Unicode (ICU) libraries.
+--
+-- The syntax and behaviour of ICU regular expressions are Perl-like.
+-- For complete details, see the ICU User Guide entry at
+-- <http://userguide.icu-project.org/strings/regexp>.
+
+module Data.Text.ICU.Regex.Internal
+    (
+    -- * Types
+      MatchOption(..)
+    , Regex(..)
+    , URegularExpression
+    -- * Functions
+    , emptyForeignPtr
+    , regex
+    , uregex_clone
+    , uregex_close
+    , uregex_end
+    , uregex_find
+    , uregex_findNext
+    , uregex_getText
+    , uregex_group
+    , uregex_groupCount
+    , uregex_pattern
+    , uregex_setText
+    , uregex_start
+    ) where
+
+import Control.Monad (when)
+import Data.IORef (IORef, newIORef)
+import Data.Int (Int32)
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.Foreign as T
+import Data.Text.ICU.Internal (UBool, UChar)
+import Data.Text.ICU.Error (isRegexError)
+import Data.Text.ICU.Error.Internal (UParseError, UErrorCode,
+                                     handleError, handleParseError)
+import Data.Typeable (Typeable)
+import Data.Word (Word16, Word32)
+import Foreign.ForeignPtr (ForeignPtr, newForeignPtr, touchForeignPtr)
+import Foreign.Ptr (FunPtr, Ptr)
+import Prelude hiding (catch)
+import System.IO.Unsafe (unsafePerformIO)
+
+#include <unicode/uregex.h>
+
+-- | Options for controlling matching behaviour.
+data MatchOption
+    = CaseInsensitive
+    -- ^ Enable case insensitive matching.
+    | Comments
+    -- ^ Allow comments and white space within patterns.
+    | DotAll
+    -- ^ If set, @\'.\'@ matches line terminators. Otherwise @\'.\'@
+    -- matching stops at line end.
+    | Literal
+    -- ^ If set, treat the entire pattern as a literal string.
+    --  Metacharacters or escape sequences in the input sequence will
+    --  be given no special meaning.
+    --
+    --  The option 'CaseInsensitive' retains its meanings on matching
+    --  when used in conjunction with this option.  Other options
+    --  become superfluous.
+    | Multiline
+    -- ^ Control behaviour of @\'$\'@ and @\'^\'@. If set, recognize
+    -- line terminators within string, Otherwise, match only at start
+    -- and end of input string.
+    | HaskellLines
+    -- ^ Haskell-only line endings.  When this mode is enabled, only
+    -- @\'\\n\'@ is recognized as a line ending in the behavior of
+    -- @\'.\'@, @\'^\'@, and @\'$\'@.
+    | UnicodeWord
+    -- ^ Unicode word boundaries.  If set, @\'\\\\b\'@ uses the
+    -- Unicode TR 29 definition of word boundaries.
+    --
+    -- /Warning/: Unicode word boundaries are quite different from
+    -- traditional regular expression word boundaries.  See
+    -- <http://unicode.org/reports/tr29/#Word_Boundaries>.
+    | ErrorOnUnknownEscapes
+    -- ^ Throw an error on unrecognized backslash escapes.  If set,
+    -- fail with an error on patterns that contain backslash-escaped
+    -- ASCII letters without a known special meaning.  If this flag is
+    -- not set, these escaped letters represent themselves.
+    | WorkLimit Int
+    -- ^ Set a processing limit for match operations.
+    --
+    -- Some patterns, when matching certain strings, can run in
+    -- exponential time.  For practical purposes, the match operation
+    -- may appear to be in an infinite loop.  When a limit is set a
+    -- match operation will fail with an error if the limit is
+    -- exceeded.
+    --
+    -- The units of the limit are steps of the match engine.
+    -- Correspondence with actual processor time will depend on the
+    -- speed of the processor and the details of the specific pattern,
+    -- but will typically be on the order of milliseconds.
+    --
+    -- By default, the matching time is not limited.
+    | StackLimit Int
+    -- ^ Set the amount of heap storage avaliable for use by the match
+    -- backtracking stack.
+    --
+    -- ICU uses a backtracking regular expression engine, with the
+    -- backtrack stack maintained on the heap.  This function sets the
+    -- limit to the amount of memory that can be used for this
+    -- purpose.  A backtracking stack overflow will result in an error
+    -- from the match operation that caused it.
+    --
+    -- A limit is desirable because a malicious or poorly designed
+    -- pattern can use excessive memory, potentially crashing the
+    -- process.  A limit is enabled by default.
+      deriving (Eq, Show, Typeable)
+
+-- | A compiled regular expression.
+--
+-- 'Regex' values are usually constructed using the 'regex' or
+-- 'regex'' functions.  This type is also an instance of 'IsString',
+-- so if you have the @OverloadedStrings@ language extension enabled,
+-- you can construct a 'Regex' by simply writing the pattern in
+-- quotes (though this does not allow you to specify any 'Option's).
+data Regex = Regex {
+      reRe :: ForeignPtr URegularExpression
+    , reText :: IORef (ForeignPtr Word16)
+    }
+
+emptyForeignPtr :: ForeignPtr Word16
+emptyForeignPtr = unsafePerformIO $ fst `fmap` T.asForeignPtr T.empty
+{-# NOINLINE emptyForeignPtr #-}
+
+-- | Compile a regular expression with the given options.  This
+-- function throws a 'ParseError' if the pattern is invalid.
+regex :: [MatchOption] -> Text -> IO Regex
+regex opts pat = T.useAsPtr pat $ \pptr plen -> do
+  let (flags,workLimit,stackLimit) = toURegexpOpts opts
+  ptr <- handleParseError isRegexError $
+         uregex_open pptr (fromIntegral plen) flags
+  fp <- newForeignPtr uregex_close ptr
+  when (workLimit /= -1) .
+    handleError $ uregex_setTimeLimit ptr (fromIntegral workLimit)
+  when (stackLimit /= -1) .
+    handleError $ uregex_setStackLimit ptr (fromIntegral stackLimit)
+  touchForeignPtr fp
+  Regex fp `fmap` newIORef emptyForeignPtr
+
+data URegularExpression
+
+type URegexpFlag = Word32
+
+toURegexpOpts :: [MatchOption] -> (URegexpFlag,Int,Int)
+toURegexpOpts = foldl go (0,-1,-1)
+  where
+    go (!flag,work,stack) opt = (flag+flag',work',stack')
+     where
+      flag' = case opt of
+                CaseInsensitive       -> #const UREGEX_CASE_INSENSITIVE
+                Comments              -> #const UREGEX_COMMENTS
+                DotAll                -> #const UREGEX_DOTALL
+                Literal               -> #const UREGEX_LITERAL
+                Multiline             -> #const UREGEX_MULTILINE
+                HaskellLines          -> #const UREGEX_UNIX_LINES
+                UnicodeWord           -> #const UREGEX_UWORD
+                ErrorOnUnknownEscapes -> #const UREGEX_ERROR_ON_UNKNOWN_ESCAPES
+                _                     -> 0
+      work' = case opt of
+                WorkLimit limit       -> limit
+                _                     -> work
+      stack' = case opt of
+                 StackLimit limit     -> limit
+                 _                    -> stack
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_open" uregex_open
+    :: Ptr UChar -> Int32 -> Word32 -> Ptr UParseError -> Ptr UErrorCode
+    -> IO (Ptr URegularExpression)
+
+foreign import ccall unsafe "hs_text_icu.h &__hs_uregex_close" uregex_close
+    :: FunPtr (Ptr URegularExpression -> IO ())
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_clone" uregex_clone
+    :: Ptr URegularExpression -> Ptr UErrorCode
+    -> IO (Ptr URegularExpression)
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_pattern" uregex_pattern
+    :: Ptr URegularExpression -> Ptr Int32 -> Ptr UErrorCode
+    -> IO (Ptr UChar)
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_setText" uregex_setText
+    :: Ptr URegularExpression -> Ptr UChar -> Int32 -> Ptr UErrorCode -> IO ()
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_getText" uregex_getText
+    :: Ptr URegularExpression -> Ptr Int32 -> Ptr UErrorCode -> IO (Ptr UChar)
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_find" uregex_find
+    :: Ptr URegularExpression -> Int32 -> Ptr UErrorCode -> IO UBool
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_findNext" uregex_findNext
+    :: Ptr URegularExpression -> Ptr UErrorCode -> IO UBool
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_start" uregex_start
+    :: Ptr URegularExpression -> Int32 -> Ptr UErrorCode -> IO Int32
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_end" uregex_end
+    :: Ptr URegularExpression -> Int32 -> Ptr UErrorCode -> IO Int32
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_groupCount" uregex_groupCount
+    :: Ptr URegularExpression -> Ptr UErrorCode -> IO Int32
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_group" uregex_group
+    :: Ptr URegularExpression -> Int32 -> Ptr UChar -> Int32 -> Ptr UErrorCode
+    -> IO Int32
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_setTimeLimit" uregex_setTimeLimit
+    :: Ptr URegularExpression -> Int32 -> Ptr UErrorCode -> IO ()
+
+foreign import ccall unsafe "hs_text_icu.h __hs_uregex_setStackLimit" uregex_setStackLimit
+    :: Ptr URegularExpression -> Int32 -> Ptr UErrorCode -> IO ()
diff --git a/Data/Text/ICU/Regex/Pure.hs b/Data/Text/ICU/Regex/Pure.hs
new file mode 100644
--- /dev/null
+++ b/Data/Text/ICU/Regex/Pure.hs
@@ -0,0 +1,218 @@
+{-# LANGUAGE BangPatterns, EmptyDataDecls, ScopedTypeVariables #-}
+
+-- |
+-- Module      : Data.Text.ICU.Regex.Pure
+-- Copyright   : (c) 2010 Bryan O'Sullivan
+--
+-- License     : BSD-style
+-- Maintainer  : bos@serpentine.com
+-- Stability   : experimental
+-- Portability : GHC
+--
+-- Regular expression support for Unicode, implemented as bindings to
+-- the International Components for Unicode (ICU) libraries.
+--
+-- The functions in this module are pure and hence thread safe, but
+-- may not be as fast or as flexible as those in the
+-- 'Data.Text.ICU.Regex.IO' module.
+--
+-- The syntax and behaviour of ICU regular expressions are Perl-like.
+-- For complete details, see the ICU User Guide entry at
+-- <http://userguide.icu-project.org/strings/regexp>.
+
+module Data.Text.ICU.Regex.Pure
+    (
+    -- * Types
+      MatchOption(..)
+    , ParseError(errError, errLine, errOffset)
+    , Match
+    , Regex
+    , Regular
+    -- * Functions
+    -- ** Construction
+    , regex
+    , regex'
+    -- ** Inspection
+    , pattern
+    -- ** Searching
+    , find
+    , findAll
+    -- ** Match groups
+    -- $group
+    , groupCount
+    , unfold
+    , span
+    , group
+    , prefix
+    , suffix
+    ) where
+
+import Control.Exception (catch)
+import Data.String (IsString(..))
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.Foreign as T
+import Data.Text.ICU.Error.Internal (ParseError(..), handleError)
+import qualified Data.Text.ICU.Regex as IO
+import Data.Text.ICU.Regex.Internal hiding (Regex(..), regex)
+import qualified Data.Text.ICU.Regex.Internal as Internal
+import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
+import Foreign.Marshal.Alloc (alloca)
+import Foreign.Marshal.Array (advancePtr)
+import Foreign.Storable (peek)
+import Prelude hiding (catch, span)
+import System.IO.Unsafe (unsafeInterleaveIO, unsafePerformIO)
+
+-- | A compiled regular expression.
+--
+-- 'Regex' values are usually constructed using the 'regex' or
+-- 'regex'' functions.  This type is also an instance of 'IsString',
+-- so if you have the @OverloadedStrings@ language extension enabled,
+-- you can construct a 'Regex' by simply writing the pattern in
+-- quotes (though this does not allow you to specify any 'Option's).
+newtype Regex = Regex {
+      reRe :: Internal.Regex
+    }
+
+instance Show Regex where
+    show re = "Regex " ++ show (pattern re)
+
+instance IsString Regex where
+    fromString = regex [] . T.pack
+
+-- | A match for a regular expression.
+data Match = Match {
+      matchRe :: Internal.Regex
+    , _matchPrev :: T.I16
+    }
+
+instance Show Match where
+    show m = "Match " ++ show (unfold group m)
+
+-- | A typeclass for functions common to both 'Match' and 'Regex'
+-- types.
+class Regular r where
+    regRe :: r -> Internal.Regex
+
+    regFp :: r -> ForeignPtr URegularExpression
+    regFp = Internal.reRe . regRe
+    {-# INLINE regFp #-}
+
+instance Regular Match where
+    regRe = matchRe
+
+instance Regular Regex where
+    regRe = reRe
+
+-- | Compile a regular expression with the given options.  This
+-- function throws a 'ParseError' if the pattern is invalid, so it is
+-- best for use when the pattern is statically known.
+regex :: [MatchOption] -> Text -> Regex
+regex opts pat = Regex . unsafePerformIO $ IO.regex opts pat
+
+-- | Compile a regular expression with the given options.  This is
+-- safest to use when the pattern is constructed at run time.
+regex' :: [MatchOption] -> Text -> Either ParseError Regex
+regex' opts pat = unsafePerformIO $
+  ((Right . Regex) `fmap` Internal.regex opts pat) `catch`
+  \(err::ParseError) -> return (Left err)
+
+-- | Return the source form of the pattern used to construct this
+-- regular expression or match.
+pattern :: Regular r => r -> Text
+pattern r = unsafePerformIO . withForeignPtr (regFp r) $ \rePtr ->
+  alloca $ \lenPtr -> do
+    textPtr <- handleError $ uregex_pattern rePtr lenPtr
+    (T.fromPtr textPtr . fromIntegral) =<< peek lenPtr
+
+-- | Find the first match for the regular expression in the given text.
+find :: Regex -> Text -> Maybe Match
+find re0 haystack = unsafePerformIO .
+  matching re0 haystack $ \re -> do
+    m <- IO.findNext re
+    return $! if m then Nothing else Just (Match re 0)
+
+-- | Lazily find all matches for the regular expression in the given
+-- text.
+findAll :: Regex -> Text -> [Match]
+findAll re0 haystack = unsafePerformIO . unsafeInterleaveIO $ go 0
+  where
+    go !n = matching re0 haystack $ \re -> do
+      f <- IO.find re n
+      if f
+        then do
+          n' <- IO.end_ re 0
+          (Match re n:) `fmap` go n'
+        else return []
+
+matching :: Regex -> Text -> (IO.Regex -> IO a) -> IO a
+matching (Regex re0) haystack act = do
+  re <- IO.clone re0
+  IO.setText re haystack
+  act re
+
+-- $group
+--
+-- Capturing groups are numbered starting from zero.  Group zero is
+-- always the entire matching text.  Groups greater than zero contain
+-- the text matching each capturing group in a regular expression.
+
+-- | Return the number of capturing groups in this regular
+-- expression or match's pattern.
+groupCount :: Regular r => r -> Int
+groupCount = unsafePerformIO . IO.groupCount . regRe
+
+-- | A combinator for returning a list of all capturing groups on a
+-- 'Match'.
+unfold :: (Int -> Match -> Maybe Text) -> Match -> [Text]
+unfold f m = go 0
+  where go !n = case f n m of
+                  Nothing -> []
+                  Just z  -> z : go (n+1)
+
+-- | Return the /n/th capturing group in a match, or 'Nothing' if /n/
+-- is out of bounds.
+group :: Int -> Match -> Maybe Text
+group n m = grouping n m $ \re -> do
+  let n' = fromIntegral n
+  start <- fromIntegral `fmap` IO.start_ re n'
+  end <- fromIntegral `fmap` IO.end_ re n'
+  (fp,_) <- IO.getText re
+  withForeignPtr fp $ \ptr ->
+    T.fromPtr (ptr `advancePtr` fromIntegral start) (end - start)
+
+-- | Return the prefix of the /n/th capturing group in a match (the
+-- text from the start of the string to the start of the match), or
+-- 'Nothing' if /n/ is out of bounds.
+prefix :: Int -> Match -> Maybe Text
+prefix n m = grouping n m $ \re -> do
+  start <- fromIntegral `fmap` IO.start_ re n
+  (fp,_) <- IO.getText re
+  withForeignPtr fp (`T.fromPtr` start)
+
+-- | Return the span of text between the end of the previous match and
+-- the beginning of the current match.
+span :: Match -> Text
+span (Match re p) = unsafePerformIO $ do
+  start <- IO.start_ re 0
+  (fp,_) <- IO.getText re
+  withForeignPtr fp $ \ptr ->
+    T.fromPtr (ptr `advancePtr` fromIntegral p) (start - p)
+
+-- | Return the suffix of the /n/th capturing group in a match (the
+-- text from the end of the match to the end of the string), or
+-- 'Nothing' if /n/ is out of bounds.
+suffix :: Int -> Match -> Maybe Text
+suffix n m = grouping n m $ \re -> do
+  end <- fromIntegral `fmap` IO.end_ re n
+  (fp,len) <- IO.getText re
+  withForeignPtr fp $ \ptr -> do
+    T.fromPtr (ptr `advancePtr` fromIntegral end) (len - end)
+
+grouping :: Int -> Match -> (Internal.Regex -> IO a) -> Maybe a
+grouping n (Match m _) act = unsafePerformIO $ do
+  count <- IO.groupCount m
+  let n' = fromIntegral n
+  if n < 0 || (n' >= count && count > 0)
+    then return Nothing
+    else Just `fmap` act m
diff --git a/Data/Text/ICU/Types.hs b/Data/Text/ICU/Types.hs
--- a/Data/Text/ICU/Types.hs
+++ b/Data/Text/ICU/Types.hs
@@ -13,6 +13,8 @@
     (
     -- * Widely used types
       LocaleName(..)
+    , ParseError(errError, errLine, errOffset)
     ) where
 
+import Data.Text.ICU.Error.Internal (ParseError(..))
 import Data.Text.ICU.Internal (LocaleName(..))
diff --git a/README b/README
deleted file mode 100644
--- a/README
+++ /dev/null
@@ -1,31 +0,0 @@
-Text-ICU: Comprehensive support for string manipulation
--------------------------------------------------------
-
-This package provides the Data.Text.ICU library, for performing
-complex manipulation of Unicode text.  It provides features such as
-the following:
-
-- Unicode normalization
-- Conversion to and from many common and obscure encodings
-
-
-Prerequisites
--------------
-
-This library is implemented as bindings to the well-respected ICU
-library, which is not included.  The version of ICU currently
-supported is 4.0.
-
-http://www.icu-project.org/
-
-
-Source code
------------
-
-darcs get http://darcs.serpentine.com/text-icu
-
-
-Authors
--------
-
-This library was written by Bryan O'Sullivan.
diff --git a/README.markdown b/README.markdown
new file mode 100644
--- /dev/null
+++ b/README.markdown
@@ -0,0 +1,37 @@
+# Text-ICU: Comprehensive support for string manipulation
+
+This package provides the Data.Text.ICU library, for performing
+complex manipulation of Unicode text.  It provides features such as
+the following:
+
+* Unicode normalization
+
+* Conversion to and from many common and obscure encodings
+
+
+# Prerequisites
+
+This library is implemented as bindings to the well-respected [ICU
+library](http://www.icu-project.org/), which is not included.  The versions
+of ICU currently supported are 4.0 and newer.
+
+
+# Get involved!
+
+Please report bugs via the
+[bitbucket issue tracker](http://bitbucket.org/bos/text-icu/issues).
+
+Master [Mercurial repository](http://bitbucket.org/bos/text-icu):
+
+* `hg clone http://bitbucket.org/bos/text-icu`
+
+There's also a [git mirror](http://github.com/bos/text-icu):
+
+* `git clone git://github.com/bos/text-icu.git`
+
+(You can create and contribute changes using either Mercurial or git.)
+
+
+# Authors
+
+This library was written by Bryan O'Sullivan.
diff --git a/cbits/text_icu.c b/cbits/text_icu.c
--- a/cbits/text_icu.c
+++ b/cbits/text_icu.c
@@ -365,3 +365,93 @@
 {
     return u_getNumericValue(c);
 }
+
+URegularExpression * __hs_uregex_open(const UChar *pattern,
+				      int32_t patternLength, uint32_t flags,
+				      UParseError *pe, UErrorCode *status)
+{
+    return uregex_open(pattern, patternLength, flags, pe, status);
+}
+
+void __hs_uregex_setTimeLimit(URegularExpression *regexp,
+			      int32_t limit, UErrorCode *status)
+{
+    return uregex_setTimeLimit(regexp, limit, status);
+}
+
+void __hs_uregex_setStackLimit(URegularExpression *regexp,
+			       int32_t limit, UErrorCode *status)
+{
+    return uregex_setStackLimit(regexp, limit, status);
+}
+
+void __hs_uregex_close(URegularExpression *regexp)
+{
+    return uregex_close(regexp);
+}
+
+URegularExpression *__hs_uregex_clone(URegularExpression *regexp,
+				      UErrorCode *pErrorCode)
+{
+    return uregex_clone(regexp, pErrorCode);
+}
+
+const UChar *__hs_uregex_pattern(const URegularExpression *regexp,
+				 int32_t *patLength, UErrorCode *status)
+{
+    return uregex_pattern(regexp, patLength, status);
+}
+
+int32_t __hs_uregex_flags(const URegularExpression *regexp,
+			  UErrorCode *status)
+{
+    return uregex_flags(regexp, status);
+}
+
+void __hs_uregex_setText(URegularExpression *regexp, const UChar *text,
+			 int32_t textLength, UErrorCode *status)
+{
+    return uregex_setText(regexp, text, textLength, status);
+}
+
+const UChar *__hs_uregex_getText(URegularExpression *regexp,
+				 int32_t *textLength, UErrorCode *status)
+{
+    return uregex_getText(regexp, textLength, status);
+}
+
+UBool __hs_uregex_find(URegularExpression *regexp, int32_t startIndex, 
+		       UErrorCode *status)
+{
+    return uregex_find(regexp, startIndex, status);
+}
+
+UBool __hs_uregex_findNext(URegularExpression *regexp, UErrorCode *status)
+{
+    return uregex_findNext(regexp, status);
+}
+
+int32_t __hs_uregex_start(URegularExpression *regexp, int32_t groupNum,
+			  UErrorCode *status)
+{
+    return uregex_start(regexp, groupNum, status);
+}
+
+int32_t __hs_uregex_end(URegularExpression *regexp, int32_t groupNum,
+			  UErrorCode *status)
+{
+    return uregex_end(regexp, groupNum, status);
+}
+
+int32_t __hs_uregex_groupCount(URegularExpression *regexp, UErrorCode *status)
+{
+    return uregex_groupCount(regexp, status);
+}
+
+int32_t __hs_uregex_group(URegularExpression *regexp, int32_t groupNum,
+			  UChar *dest, int32_t destCapacity,
+			  UErrorCode *status)
+{
+    return uregex_group(regexp, groupNum, dest, destCapacity, status);
+}
+
diff --git a/include/hs_text_icu.h b/include/hs_text_icu.h
--- a/include/hs_text_icu.h
+++ b/include/hs_text_icu.h
@@ -10,6 +10,7 @@
 #include "unicode/ucnv.h"
 #include "unicode/uiter.h"
 #include "unicode/unorm.h"
+#include "unicode/uregex.h"
 #include "unicode/ustring.h"
 
 #include <stdint.h>
@@ -129,6 +130,38 @@
 			     UNormalizationMode mode, int32_t options,
 			     UChar *result, int32_t resultLength,
 			     UErrorCode *status);
+
+/* uregex.h */
+
+URegularExpression * __hs_uregex_open(const UChar *pattern,
+				      int32_t patternLength, uint32_t flags,
+				      UParseError *pe, UErrorCode *status);
+void __hs_uregex_setTimeLimit(URegularExpression *regexp,
+			      int32_t limit, UErrorCode *status);
+void __hs_uregex_setStackLimit(URegularExpression *regexp,
+			       int32_t limit, UErrorCode *status);
+void __hs_uregex_close(URegularExpression *regexp);
+URegularExpression *__hs_uregex_clone(URegularExpression *regexp,
+				      UErrorCode *pErrorCode);
+const UChar *__hs_uregex_pattern(const URegularExpression *regexp,
+				 int32_t *patLength, UErrorCode *status);
+int32_t __hs_uregex_flags(const URegularExpression *regexp,
+			  UErrorCode *status);
+void __hs_uregex_setText(URegularExpression *regexp, const UChar *text,
+			 int32_t textLength, UErrorCode *status);
+const UChar *__hs_uregex_getText(URegularExpression *regexp,
+				 int32_t *textLength, UErrorCode *status);
+UBool __hs_uregex_find(URegularExpression *regexp, int32_t startIndex, 
+		       UErrorCode *status);
+UBool __hs_uregex_findNext(URegularExpression *regexp, UErrorCode *status);
+int32_t __hs_uregex_start(URegularExpression *regexp, int32_t groupNum,
+			  UErrorCode *status);
+int32_t __hs_uregex_end(URegularExpression *regexp, int32_t groupNum,
+			UErrorCode *status);
+int32_t __hs_uregex_groupCount(URegularExpression *regexp, UErrorCode *status);
+int32_t __hs_uregex_group(URegularExpression *regexp, int32_t groupNum,
+			  UChar *dest, int32_t destCapacity,
+			  UErrorCode *status);
 
 /* ustring.h */
 
diff --git a/text-icu.cabal b/text-icu.cabal
--- a/text-icu.cabal
+++ b/text-icu.cabal
@@ -1,10 +1,34 @@
 name:           text-icu
-version:        0.6.0.1
+version:        0.6.2.1
 synopsis:       Bindings to the ICU library
-description:    Haskell bindings to the International Components for
-                Unicode (ICU) libraries.  These libraries provide
-                robust and full-featured Unicode services on a wide
-                variety of platforms.
+homepage:       http://bitbucket.org/bos/text-icu
+bug-reports:    http://bitbucket.org/bos/text-icu/issues
+description:
+  Haskell bindings to the International Components for Unicode (ICU)
+  libraries.  These libraries provide robust and full-featured Unicode
+  services on a wide variety of platforms.
+  .
+  Features include:
+  .
+  * Both pure and impure bindings, to allow for fine control over efficiency
+    and ease of use.
+  .
+  * Breaking of strings on character, word, sentence, and line boundaries.
+  .
+  * Access to the Unicode Character Database (UCD) of character metadata.
+  .
+  * String collation functions, for locales where the conventions for
+    lexicographic ordering differ from the simple numeric ordering of
+    character codes.
+  .
+  * Character set conversion functions, allowing conversion between
+    Unicode and over 220 character encodings.
+  .
+  * Unicode normalization.  (When implementations keep strings in a
+    normalized form, they can be assured that equivalent strings have a
+    unique binary representation.)
+  .
+  * Regular expression search and replace.
 maintainer:     Bryan O'Sullivan <bos@serpentine.com>
 copyright:      2009, 2010 Bryan O'Sullivan
 category:       Data, Text
@@ -13,10 +37,10 @@
 build-type:     Simple
 cabal-version:  >= 1.6
 extra-source-files:
-  README include/hs_text_icu.h tests/benchmarks/Breaker.hs
+  README.markdown include/hs_text_icu.h tests/benchmarks/Breaker.hs
 
 library
-  build-depends:     base < 5, bytestring, text == 0.9.*
+  build-depends:     base < 5, bytestring, text >= 0.9.1.0 && <= 0.11.0.0
   if impl(ghc >= 6.10)
     build-depends:   base >= 4
 
@@ -28,6 +52,7 @@
       Data.Text.ICU.Convert
       Data.Text.ICU.Error
       Data.Text.ICU.Normalize
+      Data.Text.ICU.Regex
       Data.Text.ICU.Types
   other-modules:
       Data.Text.ICU.Break.Pure
@@ -39,6 +64,8 @@
       Data.Text.ICU.Internal
       Data.Text.ICU.Iterator
       Data.Text.ICU.Normalize.Internal
+      Data.Text.ICU.Regex.Internal
+      Data.Text.ICU.Regex.Pure
       Data.Text.ICU.Text
   c-sources: cbits/text_icu.c
   include-dirs: include
@@ -53,5 +80,9 @@
     ghc-options: -fwarn-tabs
 
 source-repository head
-  type:     darcs
-  location: http://code.haskell.org/text-icu/
+  type:     mercurial
+  location: http://bitbucket.org/bos/text-icu
+
+source-repository head
+  type:     git
+  location: http://github.com/bos/text-icu
