Implemented searching notification for characters which should use the AppleGothic...
authorJoseph Spiros <joseph.spiros@ithinksw.com>
Sun, 1 Mar 2009 10:35:43 +0000 (05:35 -0500)
committerJoseph Spiros <joseph.spiros@ithinksw.com>
Sun, 1 Mar 2009 11:03:45 +0000 (06:03 -0500)
English.lproj/GrowlITTSWPrefs.nib/info.nib
English.lproj/GrowlITTSWPrefs.nib/keyedobjects.nib
GrowlITTSW.xcodeproj/project.pbxproj
GrowlITTSWController.m
GrowlITTSWWindow.m
GrowlPositioningDefines.h [new file with mode: 0644]
RegexKitLite.h [new file with mode: 0644]
RegexKitLite.m [new file with mode: 0644]

index 5155f4a..61d25e2 100644 (file)
@@ -5,12 +5,12 @@
        <key>IBFramework Version</key>
        <string>672</string>
        <key>IBLastKnownRelativeProjectPath</key>
        <key>IBFramework Version</key>
        <string>672</string>
        <key>IBLastKnownRelativeProjectPath</key>
-       <string>../../../../Growl.xcodeproj</string>
+       <string>../GrowlITTSW.xcodeproj</string>
        <key>IBOldestOS</key>
        <integer>3</integer>
        <key>IBOpenObjects</key>
        <array>
        <key>IBOldestOS</key>
        <integer>3</integer>
        <key>IBOpenObjects</key>
        <array>
-               <integer>6</integer>
+               <integer>244</integer>
        </array>
        <key>IBSystem Version</key>
        <string>9G55</string>
        </array>
        <key>IBSystem Version</key>
        <string>9G55</string>
index 8994fc5..d017b7d 100644 (file)
Binary files a/English.lproj/GrowlITTSWPrefs.nib/keyedobjects.nib and b/English.lproj/GrowlITTSWPrefs.nib/keyedobjects.nib differ
index 479620c..d2fc688 100644 (file)
                8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; };
                FA4A47250F5A3C2A00F37A2B /* GrowlITTSWDisplay.m in Sources */ = {isa = PBXBuildFile; fileRef = FA4A471E0F5A3C2A00F37A2B /* GrowlITTSWDisplay.m */; };
                FA4A47270F5A3C2A00F37A2B /* GrowlITTSWController.m in Sources */ = {isa = PBXBuildFile; fileRef = FA4A47220F5A3C2A00F37A2B /* GrowlITTSWController.m */; };
                8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; };
                FA4A47250F5A3C2A00F37A2B /* GrowlITTSWDisplay.m in Sources */ = {isa = PBXBuildFile; fileRef = FA4A471E0F5A3C2A00F37A2B /* GrowlITTSWDisplay.m */; };
                FA4A47270F5A3C2A00F37A2B /* GrowlITTSWController.m in Sources */ = {isa = PBXBuildFile; fileRef = FA4A47220F5A3C2A00F37A2B /* GrowlITTSWController.m */; };
-               FA4A47280F5A3C2A00F37A2B /* GrowlITTSWWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = FA4A47240F5A3C2A00F37A2B /* GrowlITTSWWindow.m */; };
                FA4A47400F5A3C7100F37A2B /* ITKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA4A473D0F5A3C6900F37A2B /* ITKit.framework */; };
                FA4A47410F5A3C7300F37A2B /* ITFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA4A47340F5A3C5900F37A2B /* ITFoundation.framework */; };
                FA4A47510F5A3CFC00F37A2B /* GrowlITTSWPrefs.nib in Resources */ = {isa = PBXBuildFile; fileRef = FA4A474F0F5A3CFC00F37A2B /* GrowlITTSWPrefs.nib */; };
                FA5074430F5A56DA008DEAD9 /* ITFoundation.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = FA4A47340F5A3C5900F37A2B /* ITFoundation.framework */; };
                FA5074440F5A56DD008DEAD9 /* ITKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = FA4A473D0F5A3C6900F37A2B /* ITKit.framework */; };
                FA50774D0F5A7A2D008DEAD9 /* PreferencePanes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA50774C0F5A7A2D008DEAD9 /* PreferencePanes.framework */; };
                FA4A47400F5A3C7100F37A2B /* ITKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA4A473D0F5A3C6900F37A2B /* ITKit.framework */; };
                FA4A47410F5A3C7300F37A2B /* ITFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA4A47340F5A3C5900F37A2B /* ITFoundation.framework */; };
                FA4A47510F5A3CFC00F37A2B /* GrowlITTSWPrefs.nib in Resources */ = {isa = PBXBuildFile; fileRef = FA4A474F0F5A3CFC00F37A2B /* GrowlITTSWPrefs.nib */; };
                FA5074430F5A56DA008DEAD9 /* ITFoundation.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = FA4A47340F5A3C5900F37A2B /* ITFoundation.framework */; };
                FA5074440F5A56DD008DEAD9 /* ITKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = FA4A473D0F5A3C6900F37A2B /* ITKit.framework */; };
                FA50774D0F5A7A2D008DEAD9 /* PreferencePanes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA50774C0F5A7A2D008DEAD9 /* PreferencePanes.framework */; };
+               FA7C86AB0F5A936900213B2E /* RegexKitLite.m in Sources */ = {isa = PBXBuildFile; fileRef = FA7C86A90F5A936900213B2E /* RegexKitLite.m */; };
+               FA7C86D00F5A93DF00213B2E /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FA7C86CF0F5A93DF00213B2E /* libicucore.dylib */; };
+               FA7C87DA0F5A9C3400213B2E /* GrowlITTSWWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = FA4A47240F5A3C2A00F37A2B /* GrowlITTSWWindow.m */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
                FA5073AA0F5A4D9A008DEAD9 /* GrowlApplicationNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlApplicationNotification.h; sourceTree = "<group>"; };
                FA5074060F5A5562008DEAD9 /* GrowlDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlDefines.h; sourceTree = "<group>"; };
                FA50774C0F5A7A2D008DEAD9 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = "<absolute>"; };
                FA5073AA0F5A4D9A008DEAD9 /* GrowlApplicationNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlApplicationNotification.h; sourceTree = "<group>"; };
                FA5074060F5A5562008DEAD9 /* GrowlDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlDefines.h; sourceTree = "<group>"; };
                FA50774C0F5A7A2D008DEAD9 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = "<absolute>"; };
+               FA7C86A90F5A936900213B2E /* RegexKitLite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RegexKitLite.m; sourceTree = "<group>"; };
+               FA7C86AA0F5A936900213B2E /* RegexKitLite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegexKitLite.h; sourceTree = "<group>"; };
+               FA7C86CF0F5A93DF00213B2E /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = /usr/lib/libicucore.dylib; sourceTree = "<absolute>"; };
+               FA7C87E80F5A9FCB00213B2E /* GrowlPositioningDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlPositioningDefines.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
                                FA4A47400F5A3C7100F37A2B /* ITKit.framework in Frameworks */,
                                FA4A47410F5A3C7300F37A2B /* ITFoundation.framework in Frameworks */,
                                FA50774D0F5A7A2D008DEAD9 /* PreferencePanes.framework in Frameworks */,
                                FA4A47400F5A3C7100F37A2B /* ITKit.framework in Frameworks */,
                                FA4A47410F5A3C7300F37A2B /* ITFoundation.framework in Frameworks */,
                                FA50774D0F5A7A2D008DEAD9 /* PreferencePanes.framework in Frameworks */,
+                               FA7C86D00F5A93DF00213B2E /* libicucore.dylib in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = {
                        isa = PBXGroup;
                        children = (
                1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = {
                        isa = PBXGroup;
                        children = (
+                               FA7C86CF0F5A93DF00213B2E /* libicucore.dylib */,
                                1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */,
                                FA50774C0F5A7A2D008DEAD9 /* PreferencePanes.framework */,
                        );
                                1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */,
                                FA50774C0F5A7A2D008DEAD9 /* PreferencePanes.framework */,
                        );
                32C88E010371C26100C91783 /* Other Sources */ = {
                        isa = PBXGroup;
                        children = (
                32C88E010371C26100C91783 /* Other Sources */ = {
                        isa = PBXGroup;
                        children = (
+                               FA7C86AA0F5A936900213B2E /* RegexKitLite.h */,
+                               FA7C86A90F5A936900213B2E /* RegexKitLite.m */,
                                FA5074060F5A5562008DEAD9 /* GrowlDefines.h */,
                                FA50739B0F5A4D74008DEAD9 /* GrowlDefinesInternal.h */,
                                FA5074060F5A5562008DEAD9 /* GrowlDefines.h */,
                                FA50739B0F5A4D74008DEAD9 /* GrowlDefinesInternal.h */,
+                               FA7C87E80F5A9FCB00213B2E /* GrowlPositioningDefines.h */,
                                FA5073AA0F5A4D9A008DEAD9 /* GrowlApplicationNotification.h */,
                                FA4A472B0F5A3C3100F37A2B /* GrowlPlugin.h */,
                                FA4A472C0F5A3C3100F37A2B /* GrowlDisplayPlugin.h */,
                                FA5073AA0F5A4D9A008DEAD9 /* GrowlApplicationNotification.h */,
                                FA4A472B0F5A3C3100F37A2B /* GrowlPlugin.h */,
                                FA4A472C0F5A3C3100F37A2B /* GrowlDisplayPlugin.h */,
                        files = (
                                FA4A47250F5A3C2A00F37A2B /* GrowlITTSWDisplay.m in Sources */,
                                FA4A47270F5A3C2A00F37A2B /* GrowlITTSWController.m in Sources */,
                        files = (
                                FA4A47250F5A3C2A00F37A2B /* GrowlITTSWDisplay.m in Sources */,
                                FA4A47270F5A3C2A00F37A2B /* GrowlITTSWController.m in Sources */,
-                               FA4A47280F5A3C2A00F37A2B /* GrowlITTSWWindow.m in Sources */,
+                               FA7C86AB0F5A936900213B2E /* RegexKitLite.m in Sources */,
+                               FA7C87DA0F5A9C3400213B2E /* GrowlITTSWWindow.m in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index 0f1cd0e..3cf14a3 100644 (file)
@@ -7,11 +7,18 @@
 //
 
 #import "GrowlITTSWController.h"
 //
 
 #import "GrowlITTSWController.h"
-#import "GrowlITTSWWindow.h"
 
 #import <ITKit/ITTSWBackgroundView.h>
 #import <ITKit/ITWindowEffect.h>
 
 
 #import <ITKit/ITTSWBackgroundView.h>
 #import <ITKit/ITWindowEffect.h>
 
+#import "RegexKitLite.h"
+
+#import "GrowlPositioningDefines.h"
+
+@interface GrowlPositionController
++ (enum GrowlPosition)selectedOriginPosition;
+@end
+
 @implementation NSImage (SmoothAdditions)
 
 - (NSImage *)imageScaledSmoothlyToSize:(NSSize)scaledSize
 @implementation NSImage (SmoothAdditions)
 
 - (NSImage *)imageScaledSmoothlyToSize:(NSSize)scaledSize
@@ -46,9 +53,6 @@
         [_window setExitMode:ITTransientStatusWindowExitAfterDelay];
         [_window setExitDelay:4.0];
         
         [_window setExitMode:ITTransientStatusWindowExitAfterDelay];
         [_window setExitDelay:4.0];
         
-        [_window setHorizontalPosition:ITWindowPositionRight];
-        [_window setVerticalPosition:ITWindowPositionTop];
-        
         [_window setSizing:ITTransientStatusWindowMini];
         
         [_window setEntryEffect:[[[NSClassFromString(@"ITSlideVerticallyWindowEffect") alloc] initWithWindow:_window] autorelease]];
         [_window setSizing:ITTransientStatusWindowMini];
         
         [_window setEntryEffect:[[[NSClassFromString(@"ITSlideVerticallyWindowEffect") alloc] initWithWindow:_window] autorelease]];
@@ -72,7 +76,6 @@
 
 - (void)showWindowWithText:(NSString *)text image:(NSImage *)image
 {
 
 - (void)showWindowWithText:(NSString *)text image:(NSImage *)image
 {
-    NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text];
        NSSize newSize;
        NSSize oldSize = [image size];
        
        NSSize newSize;
        NSSize oldSize = [image size];
        
        
        image = [[[[NSImage alloc] initWithData:[image TIFFRepresentation]] autorelease] imageScaledSmoothlyToSize:newSize];
        
        
        image = [[[[NSImage alloc] initWithData:[image TIFFRepresentation]] autorelease] imageScaledSmoothlyToSize:newSize];
        
+       NSArray *gothicChars = [NSArray arrayWithObjects:[NSString stringWithUTF8String:"☆"], [NSString stringWithUTF8String:"★"], nil];
+       NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text];
+       
+       if (([gothicChars count] > 0) && ([text length] > 0)) {
+               NSMutableString *gothicRegex = [[NSMutableString alloc] init];
+               
+               [gothicRegex appendString:@"["];
+               for (NSString *gothicChar in gothicChars) {
+                       [gothicRegex appendString:gothicChar];
+               }
+               [gothicRegex appendString:@"]+"];
+               
+               NSUInteger endOfLastRange = 0;
+               NSRange foundRange;
+               while (endOfLastRange != NSNotFound) {
+                       foundRange = [text rangeOfRegex:gothicRegex inRange:NSMakeRange(endOfLastRange, ([text length] - endOfLastRange))];
+                       if (foundRange.location != NSNotFound) {
+                               [attributedText setAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[NSFont fontWithName:@"AppleGothic" size:(18.0 / MINI_DIVISOR)], NSFontAttributeName, nil, nil] range:foundRange];
+                               endOfLastRange = foundRange.location+foundRange.length;
+                               if (endOfLastRange >= [text length]) {
+                                       endOfLastRange = NSNotFound;
+                               }
+                       } else {
+                               endOfLastRange = NSNotFound;
+                       }
+               }
+       }
+       
+       switch ([GrowlPositionController selectedOriginPosition]) {
+               case GrowlMiddleColumnPosition:
+                       [_window setVerticalPosition:ITWindowPositionMiddle];
+                       [_window setHorizontalPosition:ITWindowPositionCenter];
+                       break;
+               case GrowlTopLeftPosition:
+                       [_window setVerticalPosition:ITWindowPositionTop];
+                       [_window setHorizontalPosition:ITWindowPositionLeft];
+                       break;
+               case GrowlBottomRightPosition:
+                       [_window setVerticalPosition:ITWindowPositionBottom];
+                       [_window setHorizontalPosition:ITWindowPositionRight];
+                       break;
+               case GrowlTopRightPosition:
+                       [_window setVerticalPosition:ITWindowPositionTop];
+                       [_window setHorizontalPosition:ITWindowPositionRight];
+                       break;
+               case GrowlBottomLeftPosition:
+                       [_window setVerticalPosition:ITWindowPositionBottom];
+                       [_window setHorizontalPosition:ITWindowPositionLeft];
+                       break;
+       }
+       
        [_window setImage:image];
     [_window buildTextWindowWithString:attributedText];
     [_window appear:self];
        [_window setImage:image];
     [_window buildTextWindowWithString:attributedText];
     [_window appear:self];
index 0616224..db280be 100644 (file)
@@ -60,6 +60,9 @@
 
 - (void)setImage:(NSImage *)newImage
 {
 
 - (void)setImage:(NSImage *)newImage
 {
+       if (!newImage) {
+               newImage = [NSImage imageNamed:@"NSApplicationIcon"];
+       }
     [_image autorelease];
     _image = [newImage copy];
 }
     [_image autorelease];
     _image = [newImage copy];
 }
diff --git a/GrowlPositioningDefines.h b/GrowlPositioningDefines.h
new file mode 100644 (file)
index 0000000..d2b04e0
--- /dev/null
@@ -0,0 +1,60 @@
+/*!    @header GrowlPositioningDefines.h
+*      @abstract   Defines all the positioning-related enumerators.
+*      @discussion Defines all the positioning-related enumerators for position, 
+expansion, and origin selection.
+*      @updated 2006-11-24
+*/
+
+/*!
+* @typedef GrowlPosition
+ * @abstract Represents a general position on the screen for display plugins.
+ *
+ * @constant GrowlTopLeftPosition The top left square of the screen.
+ * @constant GrowlTopMiddlePosition The top middle square of the screen.
+ * @constant GrowlTopRightPosition The top right square of the screen.
+ * @constant GrowlCenterLeftPosition The center left square of the screen.
+ * @constant GrowlCenterMiddlePosition The center middle square of the screen.
+ * @constant GrowlCenterRightPosition The center right square of the screen.
+ * @constant GrowlBottomLeftPosition The bottom left square of the screen.
+ * @constant GrowlBottomMiddlePosition The bottom left middle of the screen.
+ * @constant GrowlBottomRightPosition The bottom right square of the screen.
+ * @constant GrowlTopRowPosition The top oblong (row) of the screen.
+ * @constant GrowlCenterRowPosition The center oblong (row) of the screen.
+ * @constant GrowlBottomRowPosition The bottom oblong (row) of the screen.
+ * @constant GrowlLeftColumnPosition The left oblong (column) of the screen.
+ * @constant GrowlMiddleColumnPosition The middle oblong (column) of the screen.
+ * @constant GrowlRightColumnPosition The right oblong (column) of the screen.
+ */
+enum GrowlPosition {
+       GrowlTopLeftPosition,
+       GrowlTopMiddlePosition,
+       GrowlTopRightPosition,
+       GrowlCenterLeftPosition,
+       GrowlCenterMiddlePosition,
+       GrowlCenterRightPosition,
+       GrowlBottomLeftPosition,
+       GrowlBottomMiddlePosition,
+       GrowlBottomRightPosition,
+       GrowlTopRowPosition,
+       GrowlCenterRowPosition,
+       GrowlBottomRowPosition,
+       GrowlLeftColumnPosition,
+       GrowlMiddleColumnPosition,
+       GrowlRightColumnPosition
+};
+
+enum GrowlExpansionDirection {
+       GrowlNoExpansionDirection,
+       GrowlDownExpansionDirection,
+       GrowlUpExpansionDirection,
+       GrowlLeftExpansionDirection,
+       GrowlRightExpansionDirection
+};
+
+enum GrowlPositionOrigin {
+    GrowlNoOrigin,
+    GrowlTopLeftCorner,
+    GrowlBottomRightCorner,
+    GrowlTopRightCorner,
+    GrowlBottomLeftCorner
+};
diff --git a/RegexKitLite.h b/RegexKitLite.h
new file mode 100644 (file)
index 0000000..f69021b
--- /dev/null
@@ -0,0 +1,186 @@
+//
+//  RegexKitLite.h
+//  http://regexkit.sourceforge.net/
+//  Licensed under the terms of the BSD License, as specified below.
+//
+
+/*
+ Copyright (c) 2008, John Engelhart
+ All rights reserved.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Zang Industries nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/ 
+
+#ifdef    __OBJC__
+#import <Foundation/NSObjCRuntime.h>
+#import <Foundation/NSRange.h>
+#import <Foundation/NSString.h>
+#endif // __OBJC__
+
+#include <limits.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifndef REGEXKITLITE_VERSION_DEFINED
+#define REGEXKITLITE_VERSION_DEFINED
+
+#define REGEXKITLITE_VERSION_MAJOR 2
+#define REGEXKITLITE_VERSION_MINOR 2
+
+#define REGEXKITLITE_VERSION_CSTRING   _RKL_VERSION_STRING(REGEXKITLITE_VERSION_MAJOR, REGEXKITLITE_VERSION_MINOR)
+#define REGEXKITLITE_VERSION_NSSTRING  @REGEXKITLITE_VERSION_CSTRING
+
+#define _RKL__STRINGIFY(b)       #b
+#define _RKL_STRINGIFY(a)        _RKL__STRINGIFY(a)
+#define _RKL_JOIN_VERSION(a,b)   _RKL_STRINGIFY(a##.##b)
+#define _RKL_VERSION_STRING(a,b) _RKL_JOIN_VERSION(a,b)
+
+#endif // REGEXKITLITE_VERSION_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// For Mac OS X < 10.5.
+#ifndef NSINTEGER_DEFINED
+#define NSINTEGER_DEFINED
+#ifdef __LP64__ || NS_BUILD_32_LIKE_64
+typedef long           NSInteger;
+typedef unsigned long  NSUInteger;
+#define NSIntegerMin   LONG_MIN
+#define NSIntegerMax   LONG_MAX
+#define NSUIntegerMax  ULONG_MAX
+#else // 32-bit
+typedef int            NSInteger;
+typedef unsigned int   NSUInteger;
+#define NSIntegerMin   INT_MIN
+#define NSIntegerMax   INT_MAX
+#define NSUIntegerMax  UINT_MAX
+#endif // __LP64__ || NS_BUILD_32_LIKE_64
+#endif // NSINTEGER_DEFINED
+
+#ifndef RKLREGEXOPTIONS_DEFINED
+#define RKLREGEXOPTIONS_DEFINED
+
+// These must be idential to their ICU regex counterparts. See http://www.icu-project.org/userguide/regexp.html
+enum {
+  RKLNoOptions             = 0,
+  RKLCaseless              = 2,
+  RKLComments              = 4,
+  RKLDotAll                = 32,
+  RKLMultiline             = 8,
+  RKLUnicodeWordBoundaries = 256
+};
+typedef uint32_t RKLRegexOptions;
+
+#endif // RKLREGEXOPTIONS_DEFINED
+
+#ifndef _REGEXKITLITE_H_
+#define _REGEXKITLITE_H_
+
+#ifdef __OBJC__
+
+@class NSError;
+
+// NSException exception name.
+extern NSString * const RKLICURegexException;
+
+// NSError error domains and user info keys.
+extern NSString * const RKLICURegexErrorDomain;
+
+extern NSString * const RKLICURegexErrorCodeErrorKey;
+extern NSString * const RKLICURegexErrorNameErrorKey;
+extern NSString * const RKLICURegexLineErrorKey;
+extern NSString * const RKLICURegexOffsetErrorKey;
+extern NSString * const RKLICURegexPreContextErrorKey;
+extern NSString * const RKLICURegexPostContextErrorKey;
+extern NSString * const RKLICURegexRegexErrorKey;
+extern NSString * const RKLICURegexRegexOptionsErrorKey;
+
+// If it looks like low memory notifications might be available, add code to register and respond to them.
+// This is (should be) harmless if it turns out that this isn't the case, since the notification that we register for,
+// UIApplicationDidReceiveMemoryWarningNotification, is dynamically looked up via dlsym().
+#if (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE) && (!defined(RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS) || (RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS != 0))
+#define RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS 1
+#endif
+
+#ifdef RKL_PREPEND_TO_METHODS
+// This requires a few levels of rewriting to get the desired results.
+#define RKL_METHOD_PREPEND_2(c,d) c ## d
+#define RKL_METHOD_PREPEND_1(a,b) RKL_METHOD_PREPEND_2(a,b)
+#define RKL_METHOD_PREPEND(x) RKL_METHOD_PREPEND_1(RKL_PREPEND_TO_METHODS, x)
+#else
+#define RKL_METHOD_PREPEND(x) x
+#endif
+
+@interface NSString (RegexKitLiteAdditions)
+
++ (void)RKL_METHOD_PREPEND(clearStringCache);
+
++ (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex;
++ (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex options:(RKLRegexOptions)options error:(NSError **)error;
+
+- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex;
+- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex range:(NSRange)range;
+- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error;
+
+- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex;
+- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex inRange:(NSRange)range;
+- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error;
+
+- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex;
+- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex capture:(NSInteger)capture;
+- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex inRange:(NSRange)range;
+- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error;
+
+- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex;
+- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex capture:(NSInteger)capture;
+- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex inRange:(NSRange)range;
+- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error;
+
+- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement;
+- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange;
+- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error;
+
+@end
+
+@interface NSMutableString (RegexKitLiteAdditions)
+
+- (NSUInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement;
+- (NSUInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange;
+- (NSUInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error;
+
+@end
+
+#endif // __OBJC__
+
+#endif // _REGEXKITLITE_H_
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
diff --git a/RegexKitLite.m b/RegexKitLite.m
new file mode 100644 (file)
index 0000000..6bf66af
--- /dev/null
@@ -0,0 +1,1013 @@
+//
+//  RegexKitLite.m
+//  http://regexkit.sourceforge.net/
+//  Licensed under the terms of the BSD License, as specified below.
+//
+
+/*
+ Copyright (c) 2008, John Engelhart
+ All rights reserved.
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Zang Industries nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/ 
+
+#import <CoreFoundation/CFBase.h>
+#import <CoreFoundation/CFArray.h>
+#import <CoreFoundation/CFString.h>
+#import <Foundation/NSArray.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSError.h>
+#import <Foundation/NSException.h>
+#import <Foundation/NSNotification.h>
+#import <Foundation/NSRunLoop.h>
+#ifdef __OBJC_GC__
+#import <Foundation/NSGarbageCollector.h>
+#endif
+#import <libkern/OSAtomic.h>
+#import <AvailabilityMacros.h>
+#import <dlfcn.h>
+#import <string.h>
+#import <stdarg.h>
+#import <stdlib.h>
+#import "RegexKitLite.h"
+
+// Compile time tuneables.
+
+#ifndef RKL_CACHE_SIZE
+#define RKL_CACHE_SIZE 23
+#endif
+
+#ifndef RKL_FIXED_LENGTH
+#define RKL_FIXED_LENGTH 2048
+#endif
+
+#ifndef RKL_STACK_LIMIT
+#define RKL_STACK_LIMIT (128 * 1024)
+#endif
+
+#define SCRATCH_BUFFERS 4
+
+// These macros are nearly identical to their NSCParameterAssert siblings.
+// This is required because nearly everything is done while cacheSpinLock is locked.
+// We need to safely unlock before throwing any of these exceptions.
+// @try {} @finally {} significantly slows things down so it's not used.
+#define RKLCAssert(d, ...) RKLCAssertDictionary(__PRETTY_FUNCTION__, __FILE__, __LINE__, (d), ##__VA_ARGS__)
+#ifdef NS_BLOCK_ASSERTIONS
+#define _RKLCDelayedAssertBody(c, e, g, d, ...)
+#else
+#define _RKLCDelayedAssertBody(c, e, g, d, ...) do { id *_e=(e); if(*_e!=NULL) { goto g; } if(!(c)) { *_e = RKLCAssert((d), ##__VA_ARGS__); goto g; } } while(0)
+#endif // NS_BLOCK_ASSERTIONS
+#define RKLCDelayedAssert(c, e, g) _RKLCDelayedAssertBody(c, e, g, @"Invalid parameter not satisfying: %s", #c)
+
+#define RKLRaiseException(e, f, ...) [[NSException exceptionWithName:(e) reason:RKLStringFromClassAndMethod((self), (_cmd), (f), ##__VA_ARGS__) userInfo:NULL] raise]
+
+// Ugly macros to keep other parts clean.
+
+#define NSMaxRange(r)               ((r).location + (r).length)
+#define NSRangeInsideRange(in, win) (((((in).location - (win).location) <= (win).length) && ((NSMaxRange(in) - (win).location) <= (win).length)))
+#define NSEqualRanges(r1, r2)       ((((r1).location == (r2).location) && ((r1).length == (r2).length)))
+#define NSMakeRange(loc, len)       ((NSRange){(NSUInteger)(loc), (NSUInteger)(len)})
+#define CFMakeRange(loc, len)       ((CFRange){   (CFIndex)(loc),    (CFIndex)(len)})
+#define NSNotFoundRange             ((NSRange){NSNotFound, 0            })
+#define NSMaxiumRange               ((NSRange){         0, NSUIntegerMax})
+
+#if defined (__GNUC__) && (__GNUC__ >= 4)
+#define RKL_PREFETCH(ptr, off)      { const char *p = ((const char *)(ptr)) + ((off) + 64); __builtin_prefetch(p); __builtin_prefetch(p + 64); }
+#else
+#define RKL_PREFETCH(ptr, off)
+#endif
+
+// If the gcc flag -mmacosx-version-min is used with, for example, '=10.2', give a warning that the libicucore.dylib is only available on >= 10.3.
+// If you are reading this comment because of this warning, this is to let you know that linking to /usr/lib/libicucore.dylib will cause your executable to fail on < 10.3.
+// You will need to build your own version of the ICU library and link to that in order for RegexKitLite to work successfully on < 10.3.  This is not simple.
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1030
+#warning The ICU dynamic shared library, /usr/lib/libicucore.dylib, is only available on Mac OS X 10.3 and later.
+#warning You will need to supply a version of the ICU library to use RegexKitLite on Mac OS X 10.2 and earlier.
+#endif
+
+#define RKLGetRangeForCapture(re, s, c, r) ({ int32_t start = uregex_start((re), (int32_t)(c), (s)); if(start == -1) { r = NSNotFoundRange; } else { r.location = (NSUInteger)start; r.length = (NSUInteger)uregex_end((re), (int32_t)(c), (s)) - r.location; } *(s); })
+
+// Exported symbols.  Exception names, error domains, keys, etc.
+NSString * const RKLICURegexException            = @"RKLICURegexException";
+
+NSString * const RKLICURegexErrorDomain          = @"RKLICURegexErrorDomain";
+
+NSString * const RKLICURegexErrorCodeErrorKey    = @"RKLICURegexErrorCode";
+NSString * const RKLICURegexErrorNameErrorKey    = @"RKLICURegexErrorName";
+NSString * const RKLICURegexLineErrorKey         = @"RKLICURegexLine";
+NSString * const RKLICURegexOffsetErrorKey       = @"RKLICURegexOffset";
+NSString * const RKLICURegexPreContextErrorKey   = @"RKLICURegexPreContext";
+NSString * const RKLICURegexPostContextErrorKey  = @"RKLICURegexPostContext";
+NSString * const RKLICURegexRegexErrorKey        = @"RKLICURegexRegex";
+NSString * const RKLICURegexRegexOptionsErrorKey = @"RKLICURegexRegexOptions";
+
+// Type / struct definitions
+
+typedef struct uregex uregex; // Opaque ICU regex type.
+
+#define U_BUFFER_OVERFLOW_ERROR 15
+
+#define U_PARSE_CONTEXT_LEN 16
+
+typedef struct UParseError {
+  int32_t line;
+  int32_t offset;
+  UniChar preContext[U_PARSE_CONTEXT_LEN];
+  UniChar postContext[U_PARSE_CONTEXT_LEN];
+} UParseError;
+
+enum {
+  RKLSplitOp        = 1,
+  RKLReplaceOp      = 2,
+  RKLRangeOp        = 3,
+  RKLMaskOp         = 0xf,
+  RKLReplaceMutable = 1 << 4,
+};
+typedef NSUInteger RKLRegexOp;
+
+typedef struct {
+  CFStringRef  string;
+  CFHashCode   hash;
+  CFIndex      length;
+  UniChar     *uniChar;
+} RKLBuffer;
+
+typedef struct {
+  CFStringRef      regexString;
+  RKLRegexOptions  options;
+  uregex          *icu_regex;
+  NSInteger        captureCount;
+
+  CFStringRef      setToString;
+  CFHashCode       setToHash;
+  CFIndex          setToLength;
+  NSUInteger       setToIsImmutable:1;
+  NSUInteger       setToNeedsConversion:1;
+  const UniChar   *setToUniChar;
+  NSRange          setToRange, lastFindRange, lastMatchRange;
+  NSUInteger       pad[1]; // For 32 bits, this makes the struct 64 bytes exactly, which is good for cache line alignment.
+} RKLCacheSlot;
+
+// ICU functions.  See http://www.icu-project.org/apiref/icu4c/uregex_8h.html Tweaked slightly from the originals, but functionally identical.
+const char *u_errorName              (int32_t status);
+int32_t     u_strlen                 (const UniChar *s);
+int32_t     uregex_appendReplacement (uregex *regexp, const UniChar *replacementText, int32_t replacementLength, UniChar **destBuf, int32_t *destCapacity, int32_t *status);
+int32_t     uregex_appendTail        (uregex *regexp, UniChar **destBuf, int32_t *destCapacity, int32_t *status);
+void        uregex_close             (uregex *regexp);
+int32_t     uregex_end               (uregex *regexp, int32_t groupNum, int32_t *status);
+BOOL        uregex_find              (uregex *regexp, int32_t location, int32_t *status);
+BOOL        uregex_findNext          (uregex *regexp, int32_t *status);
+int32_t     uregex_groupCount        (uregex *regexp, int32_t *status);
+uregex     *uregex_open              (const UniChar *pattern, int32_t patternLength, RKLRegexOptions flags, UParseError *parseError, int32_t *status);
+void        uregex_reset             (uregex *regexp, int32_t newIndex, int32_t *status);
+void        uregex_setText           (uregex *regexp, const UniChar *text, int32_t textLength, int32_t *status);
+int32_t     uregex_split             (uregex *regexp, UniChar *destBuf, int32_t destCapacity, int32_t *requiredCapacity, UniChar *destFields[], int32_t destFieldsCapacity, int32_t *status);
+int32_t     uregex_start             (uregex *regexp, int32_t groupNum, int32_t *status);
+
+
+static RKLCacheSlot *getCachedRegex             (NSString *regexString, RKLRegexOptions options, NSError **error, id *exception);
+static BOOL          setCacheSlotToString       (RKLCacheSlot *cacheSlot, const NSRange *range, int32_t *status, id *exception);
+static RKLCacheSlot *getCachedRegexSetToString  (NSString *regexString, RKLRegexOptions options, NSString *matchString, NSUInteger *matchLengthPtr, NSRange *matchRange, NSError **error, id *exception, int32_t *status);
+static id            performRegexOp             (id self, SEL _cmd, RKLRegexOp doRegexOp, NSString *regexString, RKLRegexOptions options, NSInteger capture, id matchString, NSRange *matchRange, NSString *replacementString, NSError **error, void **result);
+
+static void          rkl_find                   (RKLCacheSlot *cacheSlot, NSInteger capture, NSRange searchRange, NSRange *resultRange, id *exception, int32_t *status);
+static NSArray      *rkl_splitArray             (RKLCacheSlot *cacheSlot, id *exception, int32_t *status);
+static NSString     *rkl_replaceString          (RKLCacheSlot *cacheSlot, id searchString, NSUInteger searchU16Length, NSString *replacementString, NSUInteger replacementU16Length, NSUInteger *replacedCount, int replaceMutable, id *exception, int32_t *status);
+static int32_t       rkl_replaceAll             (RKLCacheSlot *cacheSlot, const UniChar *replacementUniChar, int32_t replacementU16Length, UniChar *replacedUniChar, int32_t replacedU16Capacity, NSUInteger *replacedCount, id *exception, int32_t *status);
+
+static void          rkl_clearStringCache       (void);
+static void          clearBuffer                (RKLBuffer *buffer, int freeDynamicBuffer);
+static void          clearCacheSlotRegex        (RKLCacheSlot *cacheSlot);
+static void          clearCacheSlotSetTo        (RKLCacheSlot *cacheSlot);
+
+static NSDictionary *userInfoDictionary         (NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int status, ...);
+static NSError      *RKLNSErrorForRegex         (NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int status);
+static NSException  *RKLNSExceptionForRegex     (NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int status);
+static NSDictionary *RKLCAssertDictionary       (const char *function, const char *file, int line, NSString *format, ...);
+static NSString     *RKLStringFromClassAndMethod(id object, SEL selector, NSString *format, ...);
+
+#ifdef __OBJC_GC__
+// If compiled with Garbage Collection, we need to be able to do a few things slightly differently.
+// The basic premiss is that under GC we use a trampoline function pointer which is set to a _start function to catch the first invocation.
+// The _start function checks if GC is running and then overwrites the function pointer with the appropriate routine.  Think of it as 'lazy linking'.
+
+// rkl_collectingEnabled uses objc_getClass() to get the NSGarbageCollector class, which doesn't exist on earlier systems.
+// This allows for graceful failure should we find ourselves running on an earlier version of the OS without NSGarbageCollector.
+static BOOL  rkl_collectingEnabled_first (void);
+static BOOL  rkl_collectingEnabled_yes   (void) { return(YES); }
+static BOOL  rkl_collectingEnabled_no    (void) { return(NO); }
+static BOOL(*rkl_collectingEnabled)      (void) = rkl_collectingEnabled_first;
+static BOOL  rkl_collectingEnabled_first (void) { return((([objc_getClass("NSGarbageCollector") defaultCollector]!=NULL) ? (rkl_collectingEnabled=rkl_collectingEnabled_yes) : (rkl_collectingEnabled=rkl_collectingEnabled_no))()); }
+
+static void   *rkl_realloc_first (void **ptr, size_t size, NSUInteger flags);
+static void   *rkl_realloc_std   (void **ptr, size_t size, NSUInteger flags) { flags=flags; /*unused*/ return((*ptr = reallocf(*ptr, size))); }
+static void   *rkl_realloc_gc    (void **ptr, size_t size, NSUInteger flags) { void *p=NULL; if(flags!=0) { p=NSAllocateCollectable((NSUInteger)size,flags); if(*ptr!=NULL) { free(*ptr); *ptr=NULL; } } else { p=*ptr=reallocf(*ptr, size); } return(p); }
+static void *(*rkl_realloc)      (void **ptr, size_t size, NSUInteger flags) = rkl_realloc_first;
+static void   *rkl_realloc_first (void **ptr, size_t size, NSUInteger flags) { return(((rkl_collectingEnabled()==YES) ? (rkl_realloc=rkl_realloc_gc) : (rkl_realloc=rkl_realloc_std))(ptr, size, flags)); }
+
+static id  rkl_CFAutorelease_first (CFTypeRef obj);
+static id  rkl_CFAutorelease_std   (CFTypeRef obj) { return([(id)obj autorelease]); }
+static id  rkl_CFAutorelease_gc    (CFTypeRef obj) { return((id)CFMakeCollectable(obj)); }
+static id(*rkl_CFAutorelease)      (CFTypeRef obj) = rkl_CFAutorelease_first;
+static id  rkl_CFAutorelease_first (CFTypeRef obj) { return(((rkl_collectingEnabled()==YES) ? (rkl_CFAutorelease=rkl_CFAutorelease_gc) : (rkl_CFAutorelease=rkl_CFAutorelease_std))(obj)); }
+
+#else  // __OBJC_GC__ not defined
+
+static void *rkl_realloc       (void **ptr, size_t size, NSUInteger flags) { flags=flags; /*unused*/ return((*ptr = reallocf(*ptr, size))); }
+static id    rkl_CFAutorelease (CFTypeRef obj)                             { return([(id)obj autorelease]); }
+
+#endif // __OBJC_GC__
+
+#ifdef RKL_FAST_MUTABLE_CHECK
+// We use a trampoline function pointer to check at run time if the function __CFStringIsMutable is available.
+// If it is, the trampoline function pointer is replaced with the address of that function.
+// Otherwise, we assume the worst case that ever string is mutable.
+// This hopefully helps to protect us since we're using an undocumented, non-public API call.
+// We will keep on working if it ever does go away, just with a bit less performance due to the overhead of mutable checks.
+static BOOL  rkl_CFStringIsMutable_first (CFStringRef str);
+static BOOL  rkl_CFStringIsMutable_yes   (CFStringRef str) { str=str; /*unused*/ return(YES); }
+static BOOL(*rkl_CFStringIsMutable)      (CFStringRef str) = rkl_CFStringIsMutable_first;
+static BOOL  rkl_CFStringIsMutable_first (CFStringRef str) { if((rkl_CFStringIsMutable = dlsym(RTLD_DEFAULT, "__CFStringIsMutable")) == NULL) { rkl_CFStringIsMutable = rkl_CFStringIsMutable_yes; } return(rkl_CFStringIsMutable(str)); }
+#else // RKL_FAST_MUTABLE_CHECK is not defined.  Assume that all strings are potentially mutable.
+#define rkl_CFStringIsMutable(s) (YES)
+#endif
+BOOL __CFStringIsMutable(CFStringRef str);
+
+// Translation unit scope global variables.
+
+static UniChar        fixedUniChar[(RKL_FIXED_LENGTH)]; // This is the fixed sized UTF-16 conversion buffer.
+static RKLCacheSlot   RKLCache[(RKL_CACHE_SIZE)], *lastCacheSlot;
+static OSSpinLock     cacheSpinLock = OS_SPINLOCK_INIT;
+static RKLBuffer      dynamicBuffer, fixedBuffer = {NULL, 0UL, 0L, &fixedUniChar[0]};
+static const UniChar  emptyUniCharString[1];            // For safety, icu_regexes are 'set' to this when the string they were searched is cleared.
+static void          *scratchBuffer[(SCRATCH_BUFFERS)]; // Used to hold temporary allocations that are allocated via reallocf().
+
+// These are used when running under manual memory management for the array that rkl_splitArray creates.
+// The split strings are created, but not autoreleased.  The (immutable) array is created using these callbacks, which skips the CFRetain() call.
+// For each split string this saves the overhead of an autorelease, then an array retain, then a autoreleasepool release. This is good for a ~30% speed increase.
+static Boolean          RKLCFArrayEqualCallBack           (const void *value1,       const void *value2) { return(CFEqual(value1, value2));                          }
+static void             RKLCFArrayRelease                 (CFAllocatorRef allocator, const void *ptr)    { allocator=allocator;/*unused*/ CFRelease(ptr);            }
+static CFArrayCallBacks transferOwnershipArrayCallBacks =                                                { 0, NULL, RKLCFArrayRelease, NULL, RKLCFArrayEqualCallBack };
+
+#if defined(RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS) && (RKL_REGISTER_FOR_IPHONE_LOWMEM_NOTIFICATIONS == 1)
+
+// The next few lines are specifically for the iPhone to catch low memory conditions.
+// The basic idea is that rkl_RegisterForLowMemoryNotifications() is set to be run once by the linker at load time via __attribute((constructor)).
+// rkl_RegisterForLowMemoryNotifications() tries to find the iPhone low memory notification symbol.  If it can find it,
+// it registers with the default NSNotificationCenter to call the RKLLowMemoryWarningObserver class method +lowMemoryWarning:.
+// rkl_RegisterForLowMemoryNotifications() uses an atomic compare and swap to guarentee that it initalizes exactly once.
+// +lowMemoryWarning tries to acquire the cache lock.  If it gets the lock, it clears the cache.  If it can't, it calls performSelector:
+// with a delay of half a second to try again.  This will hopefully prevent any deadlocks, such as a RegexKitLite request for
+// memory triggering a notifcation while the lock is held.
+
+static void rkl_RegisterForLowMemoryNotifications(void);
+
+@interface      RKLLowMemoryWarningObserver : NSObject +(void)lowMemoryWarning:(id)notification; @end
+@implementation RKLLowMemoryWarningObserver 
++(void)lowMemoryWarning:(id)notification {
+  if(OSSpinLockTry(&cacheSpinLock)) { rkl_clearStringCache(); OSSpinLockUnlock(&cacheSpinLock); }
+  else { [[RKLLowMemoryWarningObserver class] performSelector:@selector(lowMemoryWarning:) withObject:NULL afterDelay:0.5]; }
+}
+@end
+
+static int rkl_HaveRegisteredForLowMemoryNotifications = 0;
+
+__attribute__((constructor)) static void rkl_RegisterForLowMemoryNotifications(void) {
+  void **memoryWarningNotification = NULL;
+
+  if(OSAtomicCompareAndSwapIntBarrier(0, 1, &rkl_HaveRegisteredForLowMemoryNotifications)) {
+    if((memoryWarningNotification = dlsym(RTLD_DEFAULT, "UIApplicationDidReceiveMemoryWarningNotification")) != NULL) {
+      [[NSNotificationCenter defaultCenter] addObserver:[RKLLowMemoryWarningObserver class] selector:@selector(lowMemoryWarning:) name:*memoryWarningNotification object:NULL];
+    }
+  }
+}
+
+#endif
+
+//  IMPORTANT!   This code is critical path code.  Because of this, it has been written for speed, not clarity.
+//  IMPORTANT!   Should only be called with cacheSpinLock already locked!
+//  ----------
+
+static RKLCacheSlot *getCachedRegex(NSString *regexString, RKLRegexOptions options, NSError **error, id *exception) {
+  RKLCacheSlot *cacheSlot = NULL;
+  CFHashCode    regexHash = 0;
+  int32_t       status    = 0;
+
+  RKLCDelayedAssert(regexString != NULL, exception, exitNow);
+
+  // Fast path the common case where this regex is exactly the same one used last time.
+  if((lastCacheSlot != NULL) && (lastCacheSlot->options == options) && (lastCacheSlot->icu_regex != NULL) && (lastCacheSlot->regexString != NULL) && (lastCacheSlot->regexString == (CFStringRef)regexString)) { return(lastCacheSlot); }
+
+  regexHash = CFHash((CFTypeRef)regexString);
+  cacheSlot = &RKLCache[(regexHash % RKL_CACHE_SIZE)]; // Retrieve the cache slot for this regex.
+
+  // Return the cached entry if it's a match, otherwise clear the slot and create a new ICU regex in its place.
+  if((cacheSlot->options == options) && (cacheSlot->icu_regex != NULL) && (cacheSlot->regexString != NULL) && ((cacheSlot->regexString == (CFStringRef)regexString) || (CFEqual((CFTypeRef)regexString, cacheSlot->regexString) == YES))) { lastCacheSlot = cacheSlot; return(cacheSlot); }
+
+  clearCacheSlotRegex(cacheSlot);
+
+  if((cacheSlot->regexString = CFStringCreateCopy(NULL, (CFStringRef)regexString)) == NULL) { goto exitNow; } ; // Get a cheap immutable copy.
+  cacheSlot->options = options;
+
+  CFIndex      regexStringU16Length = CFStringGetLength(cacheSlot->regexString); // In UTF16 code units.
+  UParseError  parseError           = (UParseError){-1, -1, {0}, {0}};
+  UniChar     *regexUniChar         = NULL;
+
+  // Try to quickly obtain regexString in UTF16 format.
+  if((regexUniChar = (UniChar *)CFStringGetCharactersPtr(cacheSlot->regexString)) == NULL) { // We didn't get the UTF16 pointer quickly and need to perform a full conversion in a temp buffer.
+    if((regexStringU16Length * sizeof(UniChar)) < RKL_STACK_LIMIT) { if((regexUniChar = alloca(regexStringU16Length * sizeof(UniChar))) == NULL) { goto exitNow; } } // Try to use the stack.
+    else { if((regexUniChar = rkl_realloc(&scratchBuffer[0], regexStringU16Length * sizeof(UniChar), 0UL)) == NULL) { goto exitNow; } } // Otherwise use the heap.
+    CFStringGetCharacters(cacheSlot->regexString, CFMakeRange(0, regexStringU16Length), (UniChar *)regexUniChar); // Convert regexString to UTF16.
+  }
+
+  // Create the ICU regex.
+  if((cacheSlot->icu_regex = uregex_open(regexUniChar, (int32_t)regexStringU16Length, options, &parseError, &status)) == NULL) { goto exitNow; }
+  if(status <= 0) { cacheSlot->captureCount = (NSInteger)uregex_groupCount(cacheSlot->icu_regex, &status); }
+  if(status <= 0) { lastCacheSlot           = cacheSlot; }
+
+ exitNow:
+  if(scratchBuffer[0] != NULL) { free(scratchBuffer[0]); scratchBuffer[0] = NULL; }
+  if(status            > 0)    { cacheSlot = NULL; if(error != NULL) { *error = RKLNSErrorForRegex(regexString, options, &parseError, status); } }
+  return(cacheSlot);
+}
+
+//  IMPORTANT!   This code is critical path code.  Because of this, it has been written for speed, not clarity.
+//  IMPORTANT!   Should only be called with cacheSpinLock already locked!
+//  ----------
+
+static BOOL setCacheSlotToString(RKLCacheSlot *cacheSlot, const NSRange *range, int32_t *status, id *exception) {
+  RKLCDelayedAssert((cacheSlot != NULL) && (cacheSlot->setToString != NULL) && (range != NULL) && (status != NULL), exception, exitNow);
+
+  if(cacheSlot->setToNeedsConversion == NO) { goto setRegexText; }
+
+  RKLBuffer *buffer = (cacheSlot->setToLength < RKL_FIXED_LENGTH) ? &fixedBuffer : &dynamicBuffer;
+  if((cacheSlot->setToUniChar != NULL) && ((cacheSlot->setToString == buffer->string) || ((cacheSlot->setToLength == buffer->length) && (cacheSlot->setToHash == buffer->hash)))) { goto setRegexText; }
+
+  clearBuffer(buffer, NO);
+
+  if(cacheSlot->setToLength >= RKL_FIXED_LENGTH) {
+    RKLCDelayedAssert(buffer == &dynamicBuffer, exception, exitNow);
+    if((dynamicBuffer.uniChar = rkl_realloc((void *)&dynamicBuffer.uniChar, (cacheSlot->setToLength * sizeof(UniChar)), 0UL)) == NULL) { return(NO); } // Resize the buffer.
+  }
+  RKLCDelayedAssert(buffer->uniChar != NULL, exception, exitNow);
+  CFStringGetCharacters(cacheSlot->setToString, CFMakeRange(0, cacheSlot->setToLength), (UniChar *)buffer->uniChar); // Convert to a UTF16 string.
+
+  if((buffer->string = CFRetain(cacheSlot->setToString)) == NULL) { return(NO); }
+  buffer->hash            = cacheSlot->setToHash;
+  buffer->length          = cacheSlot->setToLength;
+
+  cacheSlot->setToUniChar = buffer->uniChar;
+  cacheSlot->setToRange   = NSNotFoundRange;
+
+ setRegexText:
+
+  if(NSEqualRanges(cacheSlot->setToRange, *range) == NO) {
+    RKLCDelayedAssert((cacheSlot->icu_regex != NULL) && (cacheSlot->setToUniChar != NULL) && (NSMaxRange(*range) <= (NSUInteger)cacheSlot->setToLength), exception, exitNow);
+    cacheSlot->lastFindRange = cacheSlot->lastMatchRange = NSNotFoundRange;
+    cacheSlot->setToRange    = *range;
+    uregex_setText(cacheSlot->icu_regex, cacheSlot->setToUniChar + cacheSlot->setToRange.location, (int32_t)cacheSlot->setToRange.length, status);
+    if(*status > 0) { return(NO); }
+  }
+
+  return(YES);
+
+ exitNow:
+  return(NO);
+}
+
+//  IMPORTANT!   This code is critical path code.  Because of this, it has been written for speed, not clarity.
+//  IMPORTANT!   Should only be called with cacheSpinLock already locked!
+//  ----------
+
+static RKLCacheSlot *getCachedRegexSetToString(NSString *regexString, RKLRegexOptions options, NSString *matchString, NSUInteger *matchLengthPtr, NSRange *matchRange, NSError **error, id *exception, int32_t *status) {
+  RKLCacheSlot *cacheSlot = NULL;
+  RKLCDelayedAssert((regexString != NULL) && (exception != NULL) && (status != NULL), exception, exitNow);
+
+  // Fast path the common case where this regex is exactly the same one used last time.
+  if((lastCacheSlot != NULL) && (lastCacheSlot->regexString == (CFStringRef)regexString) && (lastCacheSlot->options == options)) { cacheSlot = lastCacheSlot; }
+  else { if((cacheSlot = getCachedRegex(regexString, options, error, exception)) == NULL) { goto exitNow; } }
+
+  // Optimize the case where the string to search (matchString) is immutable and the setToString immutable copy is the same string with its reference count incremented.
+  BOOL    isSetTo     = ((cacheSlot->setToString != NULL) && (cacheSlot->setToString      == (CFStringRef)matchString)) ? YES : NO;
+  CFIndex matchLength = ((isSetTo                == YES)  && (cacheSlot->setToIsImmutable == YES))                      ? cacheSlot->setToLength : CFStringGetLength((CFStringRef)matchString);
+
+  *matchLengthPtr = (NSUInteger)matchLength;
+  if(matchRange->length == NSUIntegerMax) { matchRange->length = matchLength; } // For convenience, allow NSUIntegerMax == string length.
+
+  if((NSUInteger)matchLength < NSMaxRange(*matchRange)) { *exception = [NSException exceptionWithName:NSRangeException reason:@"Range or index out of bounds" userInfo:NULL]; goto exitNow; }
+
+  if((cacheSlot->setToIsImmutable == NO) && (cacheSlot->setToString != NULL) && ((cacheSlot->setToLength != CFStringGetLength(cacheSlot->setToString)) || (cacheSlot->setToHash != CFHash(cacheSlot->setToString)))) { isSetTo = NO; }
+  else { // If the first pointer equality check failed, check the hash and length.
+    if(((isSetTo == NO) || (cacheSlot->setToIsImmutable == NO)) && (cacheSlot->setToString != NULL)) { isSetTo = ((cacheSlot->setToLength == matchLength) && (cacheSlot->setToHash == CFHash((CFStringRef)(matchString)))); }
+
+    if((isSetTo == YES)) { // Make sure that the UTF16 conversion cache is set to this string, if conversion is required.
+      if((cacheSlot->setToNeedsConversion == YES) && (setCacheSlotToString(cacheSlot, matchRange, status, exception) == NO)) { *exception = RKLCAssert(@"Failed to set up UTF16 buffer."); goto exitNow; }
+      if(NSEqualRanges(cacheSlot->setToRange, *matchRange) == YES) { goto exitNow; } // Verify that the range to search is what the cached regex was prepped for last time.
+    }
+  }
+
+  // Sometimes the range that the regex is set to isn't right, in which case we don't want to clear the cache slot.  Otherwise, flush it out.
+  if((cacheSlot->setToString != NULL) && (isSetTo == NO)) { clearCacheSlotSetTo(cacheSlot); }
+
+  if(cacheSlot->setToString == NULL) {
+    cacheSlot->setToString          = CFRetain(matchString);
+    RKLCDelayedAssert(cacheSlot->setToString != NULL, exception, exitNow);
+    cacheSlot->setToUniChar         = CFStringGetCharactersPtr(cacheSlot->setToString);
+    cacheSlot->setToNeedsConversion = (cacheSlot->setToUniChar == NULL) ? YES : NO;
+    cacheSlot->setToIsImmutable     = !rkl_CFStringIsMutable(cacheSlot->setToString); // If RKL_FAST_MUTABLE_CHECK is not defined then the result is '0', or in other words mutable..
+    cacheSlot->setToHash            = CFHash(cacheSlot->setToString);
+    cacheSlot->setToRange           = NSNotFoundRange;
+    cacheSlot->setToLength          = matchLength;
+  }
+
+  if(setCacheSlotToString(cacheSlot, matchRange, status, exception) == NO) { cacheSlot = NULL; goto exitNow; }
+
+ exitNow:
+  return(cacheSlot);
+}
+
+//  IMPORTANT!   This code is critical path code.  Because of this, it has been written for speed, not clarity.
+//  ----------
+
+static id performRegexOp(id self, SEL _cmd, RKLRegexOp doRegexOp, NSString *regexString, RKLRegexOptions options, NSInteger capture, id matchString, NSRange *matchRange, NSString *replacementString, NSError **error, void **result) {
+  BOOL       replaceMutable = ((doRegexOp & RKLReplaceMutable) != 0) ? YES : NO;
+  RKLRegexOp regexOp        = (doRegexOp & RKLMaskOp);
+
+  if((error != NULL) && (*error != NULL))                      { *error = NULL; }
+
+  if(regexString == NULL)                                      { RKLRaiseException(NSInvalidArgumentException, @"The regular expression argument is NULL."); }
+  if(matchString == NULL)                                      { RKLRaiseException(NSInternalInconsistencyException, @"The match string argument is NULL."); }
+  if((regexOp == RKLReplaceOp) && (replacementString == NULL)) { RKLRaiseException(NSInvalidArgumentException, @"The replacement string argument is NULL."); }
+
+  NSUInteger    stringU16Length = 0UL, replacementU16Length = (NSUInteger)((replacementString != NULL) ? CFStringGetLength((CFStringRef)replacementString) : 0); // In UTF16 code units.
+  NSRange       stringRange     = NSMakeRange(0, NSUIntegerMax), searchRange = (matchRange != NULL) ? *matchRange : NSNotFoundRange;
+  RKLCacheSlot *cacheSlot       = NULL;
+  id            exception       = NULL;
+  id            resultObject    = NULL;
+  int32_t       status          = 0;
+
+  // IMPORTANT!   Once we have obtained the lock, code MUST exit via 'goto exitNow;' to unlock the lock!  NO EXCEPTIONS!
+  // ----------
+  OSSpinLockLock(&cacheSpinLock); // Grab the lock and get cache entry.
+
+  if(((cacheSlot = getCachedRegexSetToString(regexString, options, matchString, &stringU16Length, (regexOp == RKLRangeOp) ? &stringRange : &searchRange, error, &exception, &status)) == NULL) || (exception != NULL) || (status > 0)) { goto exitNow; }
+
+  if(searchRange.length == NSUIntegerMax)           { searchRange.length = stringU16Length; } // For convenience.
+  if(stringU16Length     < NSMaxRange(searchRange)) { exception = [NSException exceptionWithName:NSRangeException reason:@"Range or index out of bounds" userInfo:NULL]; goto exitNow; }
+
+  RKLCDelayedAssert((cacheSlot->icu_regex != NULL) && (exception == NULL), &exception, exitNow);
+
+  if(cacheSlot->setToNeedsConversion != 0) {
+    RKLBuffer *buffer = (cacheSlot->setToLength < RKL_FIXED_LENGTH) ? &fixedBuffer : &dynamicBuffer;
+    RKLCDelayedAssert((cacheSlot->setToHash == buffer->hash) && (cacheSlot->setToLength == buffer->length) && (cacheSlot->setToUniChar == buffer->uniChar), &exception, exitNow);
+  }
+
+  switch(regexOp) {
+    case RKLRangeOp:                  rkl_find(cacheSlot, capture, searchRange, (NSRange *)result, &exception, &status); break;
+    case RKLSplitOp:   resultObject = rkl_splitArray(cacheSlot, &exception, &status);                                    break;
+    case RKLReplaceOp: resultObject = rkl_replaceString(cacheSlot, matchString, stringU16Length, replacementString, replacementU16Length, (NSUInteger *)result, replaceMutable, &exception, &status); break;
+    default:           exception    = RKLCAssert(@"Unknown regexOp code.");                                              break;
+  }
+
+ exitNow:
+  OSSpinLockUnlock(&cacheSpinLock);
+
+  if((status > 0) && (exception == NULL)) { exception = RKLNSExceptionForRegex(regexString, options, NULL, status); } // If we had a problem, throw an exception.
+  if(exception != NULL) {
+    if([exception isKindOfClass:[NSException class]]) { [[NSException exceptionWithName:[exception name] reason:RKLStringFromClassAndMethod(self, _cmd, [exception reason]) userInfo:[exception userInfo]] raise]; }
+    else { [[NSAssertionHandler currentHandler] handleFailureInFunction:[exception objectForKey:@"function"] file:[exception objectForKey:@"file"] lineNumber:[[exception objectForKey:@"line"] longValue] description:[exception objectForKey:@"description"]]; }
+  }
+  if(replaceMutable == YES) { // We're working on a mutable string and if there were successfull matches with replaced text we still have work to do.  Done outside the cache lock.
+    if(*((NSUInteger *)result) > 0) { NSCParameterAssert(resultObject != NULL); [matchString replaceCharactersInRange:searchRange withString:resultObject]; }
+  }
+
+  return(resultObject);
+}
+
+//  IMPORTANT!   This code is critical path code.  Because of this, it has been written for speed, not clarity.
+//  IMPORTANT!   Should only be called from performRegexOp().
+//  ----------
+
+static void rkl_find(RKLCacheSlot *cacheSlot, NSInteger capture, NSRange searchRange, NSRange *resultRange, id *exception, int32_t *status) {
+  NSRange captureRange = NSNotFoundRange;
+
+  RKLCDelayedAssert((cacheSlot != NULL) && (resultRange != NULL) && (exception != NULL) && (status != NULL), exception, exitNow);
+
+  if((capture < 0) || (capture > cacheSlot->captureCount)) { *exception = [NSException exceptionWithName:NSInvalidArgumentException reason:@"The capture argument is not valid." userInfo:NULL]; goto exitNow; }
+  
+  if((NSEqualRanges(searchRange, cacheSlot->lastFindRange) == NO)) { // Only perform an expensive 'find' operation iff the current find range is different than the last find range.
+    RKL_PREFETCH(cacheSlot->setToUniChar, searchRange.location << 1); // Spool up the CPU caches.
+
+    // Using uregex_findNext can be a slight performance win.
+    BOOL useFindNext = (searchRange.location == (NSMaxRange(cacheSlot->lastMatchRange) + ((cacheSlot->lastMatchRange.length == 0) ? 1 : 0))) ? YES : NO;
+    
+    cacheSlot->lastFindRange = NSNotFoundRange; // Cleared the cached search/find range.
+    if(useFindNext == NO) { if((uregex_find    (cacheSlot->icu_regex, (int32_t)searchRange.location, status) == NO) || (*status > 0)) { goto exitNow; } }
+    else {                  if((uregex_findNext(cacheSlot->icu_regex,                                status) == NO) || (*status > 0)) { goto exitNow; } }
+    
+    if(RKLGetRangeForCapture(cacheSlot->icu_regex, status, 0, cacheSlot->lastMatchRange) !=  0) { goto exitNow; }
+    if(NSRangeInsideRange(cacheSlot->lastMatchRange, searchRange)                        == NO) { goto exitNow; } // If the regex matched outside the requested range, exit.
+    
+    cacheSlot->lastFindRange = searchRange; // Cache the successful search/find range.
+  }
+  
+  if(capture == 0) { captureRange = cacheSlot->lastMatchRange; } else { RKLGetRangeForCapture(cacheSlot->icu_regex, status, capture, captureRange); }
+
+ exitNow:
+  *resultRange = captureRange;
+}
+
+//  IMPORTANT!   This code is critical path code.  Because of this, it has been written for speed, not clarity.
+//  IMPORTANT!   Should only be called from performRegexOp().
+//  ----------
+
+static NSArray *rkl_splitArray(RKLCacheSlot *cacheSlot, id *exception, int32_t *status) {
+  NSArray    *resultArray         = NULL;
+
+  RKLCDelayedAssert((cacheSlot != NULL) && (status != NULL), exception, exitNow);
+
+  const char *setToUniCharChar    = (const char *)(cacheSlot->setToUniChar + cacheSlot->setToRange.location);
+  NSUInteger  splitRangesCapacity = ((((RKL_STACK_LIMIT / sizeof(NSRange)) / 4) + ((cacheSlot->captureCount + 1) * 2)) + 2), splitRangesIndex = 0, lastLocation = 0, x = 0;
+  size_t      splitRangesSize     = (splitRangesCapacity * sizeof(NSRange)), stackUsed = 0;
+  NSInteger   captureCount        = cacheSlot->captureCount;
+  uregex     *icu_regex           = cacheSlot->icu_regex;
+  NSRange    *splitRanges         = NULL;
+  BOOL        copiedStackToHeap   = NO;
+
+  if(cacheSlot->setToLength == 0) { resultArray = [NSArray array]; goto exitNow; } // Return an empty array when there is nothing to search.
+
+  if(splitRangesSize < RKL_STACK_LIMIT) { if((splitRanges = alloca(splitRangesSize))                              == NULL) { goto exitNow; } stackUsed += splitRangesSize; }
+  else                                  { if((splitRanges = rkl_realloc(&scratchBuffer[0], splitRangesSize, 0UL)) == NULL) { goto exitNow; } }
+
+  cacheSlot->lastFindRange = cacheSlot->lastMatchRange = NSNotFoundRange; // Clear the cached find information for this regex so a subsequent find works correctly.
+  uregex_reset(icu_regex, 0, status); // Reset the regex to the start of the string.
+
+  for(splitRangesIndex = 0; splitRangesIndex < splitRangesCapacity; splitRangesIndex++) {
+
+    if(splitRangesIndex >= ((splitRangesCapacity - ((captureCount + 1) * 2)) - 1)) { // Check if we need to grow our NSRanges buffer.
+      NSUInteger newCapacity = (((splitRangesCapacity + (splitRangesCapacity / 2)) + ((captureCount + 1) * 2)) + 2);
+      size_t     newSize     = (newCapacity * sizeof(NSRange));
+      NSRange   *newRanges   = NULL;
+
+      if((newRanges = rkl_realloc(&scratchBuffer[0], newSize, 0UL)) == NULL) { goto exitNow; } // We only try to use the stack the first time, after that, we use the heap.
+      if((stackUsed > 0) && (copiedStackToHeap == NO)) { memcpy(newRanges, splitRanges, splitRangesSize); copiedStackToHeap = YES; }
+
+      splitRangesCapacity = newCapacity;
+      splitRangesSize     = newSize;
+      splitRanges         = newRanges;
+    }
+
+    RKL_PREFETCH(setToUniCharChar, lastLocation << 1); // Spool up the CPU caches.
+
+    NSUInteger baseMatchIndex = splitRangesIndex;
+    NSRange    tempRange;
+
+    if((uregex_findNext(icu_regex, status) == NO) || (*status > 0)) { break; }
+    if(RKLGetRangeForCapture(icu_regex, status, 0, tempRange) > 0) { goto exitNow; }
+
+    splitRanges[splitRangesIndex] = NSMakeRange(lastLocation, tempRange.location - lastLocation);
+    lastLocation                  = NSMaxRange(tempRange);
+
+    int32_t capture;
+    for(capture = 1; capture <= captureCount; capture++) {
+      RKLCDelayedAssert(splitRangesIndex < (splitRangesCapacity - 2), exception, exitNow);
+      splitRangesIndex++;
+      
+      if(RKLGetRangeForCapture(icu_regex, status, capture, splitRanges[splitRangesIndex]) > 0) { goto exitNow; }
+      if(splitRanges[splitRangesIndex].location == NSNotFound) { splitRanges[splitRangesIndex] = NSMakeRange(splitRanges[baseMatchIndex].location, 0); }
+    }
+  }
+
+  RKLCDelayedAssert(splitRangesIndex < (splitRangesCapacity - 2), exception, exitNow);
+  splitRanges[splitRangesIndex] = NSMakeRange(lastLocation, (NSMaxRange(cacheSlot->setToRange) - cacheSlot->setToRange.location) - lastLocation);
+  splitRangesIndex++;
+
+  CFIndex      setToLocation    = cacheSlot->setToRange.location;
+  CFStringRef  setToString      = cacheSlot->setToString;
+  size_t       splitStringsSize = (splitRangesIndex * sizeof(id));
+  id          *splitStrings     = NULL;
+
+  if((stackUsed + splitStringsSize) < RKL_STACK_LIMIT) { if((splitStrings = alloca(splitStringsSize)) == NULL) { goto exitNow; } stackUsed += splitStringsSize; }
+#ifdef __OBJC_GC__
+  else { if((splitStrings = rkl_realloc(&scratchBuffer[1], splitStringsSize, (NSUInteger)NSScannedOption)) == NULL) { goto exitNow; } }
+#else
+  // http://sourceforge.net/tracker/index.php?func=detail&aid=2050825&group_id=204582&atid=990188
+  // This is to get around an iPhone quirk.  For whatever reason, the iPhone NSZone.h explicitly removes all NSAllocateCollectable()
+  // bits and pieces using #if pre-processor conditions.  Since NSScannedOption is only really used when the compiler has -fobjc-gc enabled,
+  // we just chop it out here.
+  else { if((splitStrings = rkl_realloc(&scratchBuffer[1], splitStringsSize, 0)) == NULL) { goto exitNow; } }
+#endif
+
+#ifdef __OBJC_GC__ 
+  if(rkl_collectingEnabled() == YES) { // I just don't trust the GC system with the faster CF way of doing things...  It never seems to work quite the way you expect it to.
+    for(x = 0; x < splitRangesIndex; x++) { // Optimize the case where the length == 0 by substituting the string @"".
+      splitStrings[x] = (splitRanges[x].length == 0) ? @"" : [(id)setToString substringWithRange:NSMakeRange(setToLocation + splitRanges[x].location, splitRanges[x].length)];
+    }
+    resultArray = [NSArray arrayWithObjects:splitStrings count:splitRangesIndex];
+  } else
+#endif
+  { // This block of code is always compiled in.  It is used when not compiled with GC or when compiled with GC but the collector is not enabled.
+    for(x = 0; x < splitRangesIndex; x++) { // Optimize the case where the length == 0 by substituting the string @"".
+      splitStrings[x] = (splitRanges[x].length == 0) ? @"" : (id)CFStringCreateWithSubstring(NULL, setToString, CFMakeRange(setToLocation + splitRanges[x].location, (CFIndex)splitRanges[x].length));
+    }
+    resultArray = rkl_CFAutorelease(CFArrayCreate(NULL, (const void **)splitStrings, (CFIndex)splitRangesIndex, &transferOwnershipArrayCallBacks)); // Create the CF/NSArray of the split strings.
+  }
+  
+ exitNow:
+  if(scratchBuffer[0] != NULL) { free(scratchBuffer[0]); scratchBuffer[0] = NULL; }
+  if(scratchBuffer[1] != NULL) { free(scratchBuffer[1]); scratchBuffer[1] = NULL; }
+
+  return(resultArray);
+}
+
+//  IMPORTANT!   This code is critical path code.  Because of this, it has been written for speed, not clarity.
+//  IMPORTANT!   Should only be called from performRegexOp().
+//  ----------
+
+static NSString *rkl_replaceString(RKLCacheSlot *cacheSlot, id searchString, NSUInteger searchU16Length, NSString *replacementString, NSUInteger replacementU16Length, NSUInteger *replacedCountPtr, int replaceMutable, id *exception, int32_t *status) {
+  int32_t        resultU16Length    = 0, tempUniCharBufferU16Capacity = 0;
+  UniChar       *tempUniCharBuffer  = NULL;
+  const UniChar *replacementUniChar = NULL;
+  id             resultObject       = NULL;
+  NSUInteger     replacedCount      = 0;
+
+  // Zero order approximation of the buffer sizes for holding the replaced string or split strings and split strings pointer offsets.  As UTF16 code units.
+  tempUniCharBufferU16Capacity = (int32_t)(16 + (searchU16Length + (searchU16Length >> 1)) + (replacementU16Length * 2));
+
+  // Buffer sizes converted from native units to bytes.
+  size_t stackSize = 0, replacementSize = (replacementU16Length * sizeof(UniChar)), tempUniCharBufferSize = (tempUniCharBufferU16Capacity * sizeof(UniChar));
+  
+  // For the various buffers we require, we first try to allocate from the stack if we're not over the RKL_STACK_LIMIT.  If we are, switch to using the heap for the buffer.
+  
+  if((stackSize + tempUniCharBufferSize) < RKL_STACK_LIMIT) { if((tempUniCharBuffer = alloca(tempUniCharBufferSize))                              == NULL) { goto exitNow; } stackSize += tempUniCharBufferSize; }
+  else                                                      { if((tempUniCharBuffer = rkl_realloc(&scratchBuffer[0], tempUniCharBufferSize, 0UL)) == NULL) { goto exitNow; } }
+  
+  // Try to get the pointer to the replacement strings UTF16 data.  If we can't, allocate some buffer space, then covert to UTF16.
+  if((replacementUniChar = CFStringGetCharactersPtr((CFStringRef)replacementString)) == NULL) {
+    if((stackSize + replacementSize) < RKL_STACK_LIMIT) { if((replacementUniChar = alloca(replacementSize))                              == NULL) { goto exitNow; } stackSize += replacementSize; } 
+    else                                                { if((replacementUniChar = rkl_realloc(&scratchBuffer[1], replacementSize, 0UL)) == NULL) { goto exitNow; } }
+    CFStringGetCharacters((CFStringRef)replacementString, CFMakeRange(0, replacementU16Length), (UniChar *)replacementUniChar); // Convert to a UTF16 string.
+  }
+  
+  cacheSlot->lastFindRange = cacheSlot->lastMatchRange = NSNotFoundRange; // Clear the cached find information for this regex so a subsequent find works correctly.
+  
+  resultU16Length = rkl_replaceAll(cacheSlot, replacementUniChar, (int32_t)replacementU16Length, tempUniCharBuffer, tempUniCharBufferU16Capacity, &replacedCount, exception, status);
+  
+  if(*status == U_BUFFER_OVERFLOW_ERROR) { // Our buffer guess(es) were too small.  Resize the buffers and try again.
+    tempUniCharBufferSize = ((tempUniCharBufferU16Capacity = resultU16Length + 4) * sizeof(UniChar));
+    if((stackSize + tempUniCharBufferSize) < RKL_STACK_LIMIT) { if((tempUniCharBuffer = alloca(tempUniCharBufferSize))                              == NULL) { goto exitNow; } stackSize += tempUniCharBufferSize; }
+    else                                                      { if((tempUniCharBuffer = rkl_realloc(&scratchBuffer[0], tempUniCharBufferSize, 0UL)) == NULL) { goto exitNow; } }
+    
+    *status         = 0; // Make sure the status var is cleared and try again.
+    resultU16Length = rkl_replaceAll(cacheSlot, replacementUniChar, (int32_t)replacementU16Length, tempUniCharBuffer, tempUniCharBufferU16Capacity, &replacedCount, exception, status);
+  }
+  
+  if(*status > 0) { goto exitNow; } // Something went wrong.
+  
+  if(resultU16Length == 0) { resultObject = @""; } // Optimize the case where the replaced text length == 0 with a @"" string.
+  else if(((NSUInteger)resultU16Length == searchU16Length) && (replacedCount == 0)) { // Optimize the case where the replacement == original by creating a copy. Very fast if self is immutable.
+    if(replaceMutable == NO) { resultObject = rkl_CFAutorelease(CFStringCreateCopy(NULL, (CFStringRef)searchString)); } // .. but only if this is not replacing a mutable self.
+  } else { resultObject = rkl_CFAutorelease(CFStringCreateWithCharacters(NULL, tempUniCharBuffer, (CFIndex)resultU16Length)); } // otherwise, create a new string.
+  
+  // If replaceMutable == YES, we don't do the replacement here.  We wait until after we return and unlock the cache lock.
+  // This is because we may be trying to mutate an immutable string object.
+  if((replacedCount > 0) && (replaceMutable == YES)) { // We're working on a mutable string and there were successfull matches with replaced text, so there's work to do.
+    clearBuffer((cacheSlot->setToLength < RKL_FIXED_LENGTH) ? &fixedBuffer : &dynamicBuffer, NO);
+    clearCacheSlotSetTo(cacheSlot); // Flush any cached information about this string since it will mutate.
+  }
+
+ exitNow:
+  if(scratchBuffer[0] != NULL) { free(scratchBuffer[0]); scratchBuffer[0] = NULL; }
+  if(scratchBuffer[1] != NULL) { free(scratchBuffer[1]); scratchBuffer[1] = NULL; }
+  if(replacedCountPtr != NULL) { *replacedCountPtr = replacedCount; }
+  return(resultObject);
+}
+
+// Modified version of the ICU libraries uregex_replaceAll() that keeps count of the number of replacements made.
+static int32_t rkl_replaceAll(RKLCacheSlot *cacheSlot, const UniChar *replacementUniChar, int32_t replacementU16Length, UniChar *replacedUniChar, int32_t replacedU16Capacity, NSUInteger *replacedCount, id *exception, int32_t *status) {
+  NSUInteger replaced  = 0;
+  int32_t    u16Length = 0;
+  RKLCDelayedAssert((cacheSlot != NULL) && (replacementUniChar != NULL) && (replacedUniChar != NULL) && (status != NULL), exception, exitNow);
+  
+  uregex_reset(cacheSlot->icu_regex, 0, status);
+
+  // Work around for ICU uregex_reset() bug, see http://bugs.icu-project.org/trac/ticket/6545
+  // http://sourceforge.net/tracker/index.php?func=detail&aid=2105213&group_id=204582&atid=990188
+  if((cacheSlot->setToLength == 0) && (*status == 8)) { *status = 0; }
+
+  while(uregex_findNext(cacheSlot->icu_regex, status)) {
+    replaced++;
+    u16Length += uregex_appendReplacement(cacheSlot->icu_regex, replacementUniChar, replacementU16Length, &replacedUniChar, &replacedU16Capacity, status);
+  }
+  u16Length += uregex_appendTail(cacheSlot->icu_regex, &replacedUniChar, &replacedU16Capacity, status);
+  if(replacedCount != 0) { *replacedCount = replaced; }
+ exitNow:
+  return(u16Length);
+}
+
+static void rkl_clearStringCache(void) {
+  NSCParameterAssert(cacheSpinLock != 0);
+  lastCacheSlot = NULL;
+  NSUInteger x = 0;
+  for(x = 0; x < SCRATCH_BUFFERS; x++) { if(scratchBuffer[x] != NULL) { free(scratchBuffer[x]); scratchBuffer[x] = NULL; } }
+  for(x = 0; x < RKL_CACHE_SIZE;  x++) { clearCacheSlotRegex(&RKLCache[x]); clearCacheSlotSetTo(&RKLCache[x]); }
+  clearBuffer(&fixedBuffer,   NO);
+  clearBuffer(&dynamicBuffer, YES);
+}
+
+static void clearBuffer(RKLBuffer *buffer, int freeDynamicBuffer) {
+  if(buffer == NULL) { return; }
+  if((freeDynamicBuffer == YES) && (buffer->uniChar != NULL) && (buffer == &dynamicBuffer)) { free(dynamicBuffer.uniChar); dynamicBuffer.uniChar = NULL; }
+  if(buffer->string != NULL)                                                                { CFRelease(buffer->string);   buffer->string        = NULL; }
+  buffer->length = 0L;
+  buffer->hash   = 0UL;
+}
+
+static void clearCacheSlotRegex(RKLCacheSlot *cacheSlot) {
+  if(cacheSlot              == NULL) { return; }
+  if(cacheSlot->regexString != NULL) { CFRelease(cacheSlot->regexString);  cacheSlot->regexString = NULL; cacheSlot->options      =  0U; }
+  if(cacheSlot->icu_regex   != NULL) { uregex_close(cacheSlot->icu_regex); cacheSlot->icu_regex   = NULL; cacheSlot->captureCount = -1L; }
+  if(cacheSlot->setToString != NULL) { clearCacheSlotSetTo(cacheSlot); }
+}
+
+static void clearCacheSlotSetTo(RKLCacheSlot *cacheSlot) {
+  if(cacheSlot              == NULL) { return; }
+  if(cacheSlot->icu_regex   != NULL) { int32_t status = 0; uregex_setText(cacheSlot->icu_regex, &emptyUniCharString[0], 0, &status); }
+  if(cacheSlot->setToString != NULL) { CFRelease(cacheSlot->setToString); cacheSlot->setToString = NULL; }
+  cacheSlot->setToLength      = 0L;
+  cacheSlot->setToHash        = 0UL;
+  cacheSlot->setToIsImmutable = cacheSlot->setToNeedsConversion = 0UL;
+  cacheSlot->lastFindRange    = cacheSlot->lastMatchRange       = cacheSlot->setToRange = NSNotFoundRange;
+  cacheSlot->setToUniChar     = NULL;
+}
+
+// Helps to keep things tidy.
+#define addKeyAndObject(objs, keys, i, k, o) ({id _o=(o), _k=(k); if((_o != NULL) && (_k != NULL)) { objs[i] = _o; keys[i] = _k; i++; } })
+
+static NSDictionary *userInfoDictionary(NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int status, ...) {
+  va_list varArgsList;
+  va_start(varArgsList, status);
+
+  if(regexString == NULL) { return(NULL); }
+
+  id objects[64], keys[64];
+  NSUInteger count = 0;
+
+  NSString *errorNameString = [NSString stringWithUTF8String:u_errorName(status)];
+
+  addKeyAndObject(objects, keys, count, RKLICURegexRegexErrorKey,        regexString);
+  addKeyAndObject(objects, keys, count, RKLICURegexRegexOptionsErrorKey, [NSNumber numberWithUnsignedInt:options]);
+  addKeyAndObject(objects, keys, count, RKLICURegexErrorCodeErrorKey,    [NSNumber numberWithInt:status]);
+  addKeyAndObject(objects, keys, count, RKLICURegexErrorNameErrorKey,    errorNameString);
+
+  if((parseError != NULL) && (parseError->line != -1)) {
+    NSString *preContextString  = [NSString stringWithCharacters:&parseError->preContext[0]  length:(NSUInteger)u_strlen(&parseError->preContext[0])];
+    NSString *postContextString = [NSString stringWithCharacters:&parseError->postContext[0] length:(NSUInteger)u_strlen(&parseError->postContext[0])];
+
+    addKeyAndObject(objects, keys, count, RKLICURegexLineErrorKey,        [NSNumber numberWithInt:parseError->line]);
+    addKeyAndObject(objects, keys, count, RKLICURegexOffsetErrorKey,      [NSNumber numberWithInt:parseError->offset]);
+    addKeyAndObject(objects, keys, count, RKLICURegexPreContextErrorKey,  preContextString);
+    addKeyAndObject(objects, keys, count, RKLICURegexPostContextErrorKey, postContextString);
+    addKeyAndObject(objects, keys, count, @"NSLocalizedFailureReason",    ([NSString stringWithFormat:@"The error %@ occurred at line %d, column %d: %@<<HERE>>%@", errorNameString, parseError->line, parseError->offset, preContextString, postContextString]));
+  } else {
+    addKeyAndObject(objects, keys, count, @"NSLocalizedFailureReason",    ([NSString stringWithFormat:@"The error %@ occurred.", errorNameString]));
+  }
+
+  while(count < 62) { id obj = va_arg(varArgsList, id), key = va_arg(varArgsList, id); if((obj != NULL) && (key != NULL)) { addKeyAndObject(objects, keys, count, key, obj); } else { break; } }
+
+  return([NSDictionary dictionaryWithObjects:&objects[0] forKeys:&keys[0] count:count]);
+}
+
+static NSError *RKLNSErrorForRegex(NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int status) {
+  return([NSError errorWithDomain:RKLICURegexErrorDomain code:(NSInteger)status userInfo:userInfoDictionary(regexString, options, parseError, status, @"There was an error compiling the regular expression.", @"NSLocalizedDescription", NULL)]);
+}
+
+static NSException *RKLNSExceptionForRegex(NSString *regexString, RKLRegexOptions options, const UParseError *parseError, int status) {
+  return([NSException exceptionWithName:RKLICURegexException reason:[NSString stringWithFormat:@"ICU regular expression error #%d, %s", status, u_errorName(status)] userInfo:userInfoDictionary(regexString, options, parseError, status, NULL)]);
+}
+
+static NSDictionary *RKLCAssertDictionary(const char *function, const char *file, int line, NSString *format, ...) {
+  va_list varArgsList;
+  va_start(varArgsList, format);
+  NSString *formatString   = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease];
+  va_end(varArgsList);
+  NSString *functionString = [NSString stringWithUTF8String:function], *fileString = [NSString stringWithUTF8String:file];
+  return([NSDictionary dictionaryWithObjectsAndKeys:formatString, @"description", functionString, @"function", fileString, @"file", [NSNumber numberWithInt:line], @"line", NSInternalInconsistencyException, @"exceptionName", NULL]);
+}
+
+static NSString *RKLStringFromClassAndMethod(id object, SEL selector, NSString *format, ...) {
+  va_list varArgsList;
+  va_start(varArgsList, format);
+  NSString *formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease];
+  va_end(varArgsList);
+  Class     objectsClass = [object class];
+  return([NSString stringWithFormat:@"*** %c[%@ %@]: %@", (object == objectsClass) ? '+' : '-', NSStringFromClass(objectsClass), NSStringFromSelector(selector), formatString]);
+}
+
+@implementation NSString (RegexKitLiteAdditions)
+
+// Class methods
+
++ (void)RKL_METHOD_PREPEND(clearStringCache)
+{
+  OSSpinLockLock(&cacheSpinLock);
+  rkl_clearStringCache();
+  OSSpinLockUnlock(&cacheSpinLock);
+}
+
+// captureCountForRegex:
+
++ (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex
+{
+  return([self RKL_METHOD_PREPEND(captureCountForRegex):regex options:RKLNoOptions error:NULL]);
+}
+
++ (NSInteger)RKL_METHOD_PREPEND(captureCountForRegex):(NSString *)regex options:(RKLRegexOptions)options error:(NSError **)error
+{
+  if((error != NULL) && (*error != NULL)) { *error = NULL; }
+  if(regex == NULL) { RKLRaiseException(NSInvalidArgumentException, @"The regular expression argument is NULL."); }
+
+  NSException  *exception    = NULL;
+  RKLCacheSlot *cacheSlot    = NULL;
+  NSInteger     captureCount = -1;
+
+  OSSpinLockLock(&cacheSpinLock);
+  if((cacheSlot = getCachedRegex(regex, options, error, &exception)) != NULL) { captureCount = cacheSlot->captureCount; }
+  OSSpinLockUnlock(&cacheSpinLock);
+
+  if(exception != NULL) { [exception raise]; }
+  return(captureCount);
+}
+
+// Instance methods
+
+// componentsSeparatedByRegex:
+
+- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex
+{
+  NSRange range = NSMaxiumRange;
+  return(performRegexOp(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, 0,       0L, self, &range, NULL, NULL,  NULL));
+}
+
+- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex range:(NSRange)range
+{
+  return(performRegexOp(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, 0,       0L, self, &range, NULL, NULL,  NULL));
+}
+
+- (NSArray *)RKL_METHOD_PREPEND(componentsSeparatedByRegex):(NSString *)regex options:(RKLRegexOptions)options range:(NSRange)range error:(NSError **)error
+{
+  return(performRegexOp(self, _cmd, (RKLRegexOp)RKLSplitOp, regex, options, 0L, self, &range, NULL, error, NULL));
+}
+
+// isMatchedByRegex:
+
+- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex
+{
+  NSRange result = NSNotFoundRange, range = NSMaxiumRange;
+  performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, 0,       0L, self, &range, NULL, NULL,  (void **)((void *)&result));
+  return((result.location == NSNotFound) ? NO : YES);
+}
+
+- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex inRange:(NSRange)range
+{
+  NSRange result = NSNotFoundRange;
+  performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, 0,       0L, self, &range, NULL, NULL,  (void **)((void *)&result));
+  return((result.location == NSNotFound) ? NO : YES);
+}
+
+- (BOOL)RKL_METHOD_PREPEND(isMatchedByRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range error:(NSError **)error
+{
+  NSRange result = NSNotFoundRange;
+  performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, options, 0L, self, &range, NULL, error, (void **)((void *)&result));
+  return((result.location == NSNotFound) ? NO : YES);
+}
+
+// rangeOfRegex:
+
+- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex
+{
+  NSRange result = NSNotFoundRange, range = NSMaxiumRange;
+  performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, 0,       0L,      self, &range, NULL, NULL, (void **)((void *)&result));
+  return(result);
+}
+
+- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex capture:(NSInteger)capture
+{
+  NSRange result = NSNotFoundRange, range = NSMaxiumRange;
+  performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, 0,       capture, self, &range, NULL, NULL, (void **)((void *)&result));
+  return(result);
+}
+
+- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex inRange:(NSRange)range
+{
+  NSRange result = NSNotFoundRange;
+  performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, 0,       0L,      self, &range, NULL, NULL, (void **)((void *)&result));
+  return(result);
+}
+
+- (NSRange)RKL_METHOD_PREPEND(rangeOfRegex):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error
+{
+  NSRange result = NSNotFoundRange;
+  performRegexOp(self, _cmd, (RKLRegexOp)RKLRangeOp, regex, options, capture, self, &range, NULL, error, (void **)((void *)&result));
+  return(result);
+}
+
+// stringByMatching:
+
+- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex
+{
+  return([self RKL_METHOD_PREPEND(stringByMatching):regex options:RKLNoOptions inRange:NSMaxiumRange capture:0L      error:NULL]);
+}
+
+- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex capture:(NSInteger)capture
+{
+  return([self RKL_METHOD_PREPEND(stringByMatching):regex options:RKLNoOptions inRange:NSMaxiumRange capture:capture error:NULL]);
+}
+
+- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex inRange:(NSRange)range
+{
+  return([self RKL_METHOD_PREPEND(stringByMatching):regex options:RKLNoOptions inRange:range         capture:0L      error:NULL]);
+}
+
+- (NSString *)RKL_METHOD_PREPEND(stringByMatching):(NSString *)regex options:(RKLRegexOptions)options inRange:(NSRange)range capture:(NSInteger)capture error:(NSError **)error
+{
+  NSRange matchedRange = [self RKL_METHOD_PREPEND(rangeOfRegex):regex options:options inRange:range capture:capture error:error];
+  return((matchedRange.location == NSNotFound) ? NULL : rkl_CFAutorelease(CFStringCreateWithSubstring(NULL, (CFStringRef)self, CFMakeRange(matchedRange.location, matchedRange.length))));
+}
+
+// stringByReplacingOccurrencesOfRegex:
+
+- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement
+{
+  NSRange searchRange = NSMaxiumRange;
+  return(performRegexOp(self, _cmd, (RKLRegexOp)RKLReplaceOp, regex, 0,       0L, self, &searchRange, replacement, NULL,  NULL));
+}
+
+- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange
+{
+  return(performRegexOp(self, _cmd, (RKLRegexOp)RKLReplaceOp, regex, 0,       0L, self, &searchRange, replacement, NULL,  NULL));
+}
+
+- (NSString *)RKL_METHOD_PREPEND(stringByReplacingOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error
+{
+  return(performRegexOp(self, _cmd, (RKLRegexOp)RKLReplaceOp, regex, options, 0L, self, &searchRange, replacement, error, NULL));
+}
+
+@end
+
+
+@implementation NSMutableString (RegexKitLiteAdditions)
+
+// replaceOccurrencesOfRegex:
+
+- (NSUInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement
+{
+  NSRange    searchRange   = NSMaxiumRange;
+  NSUInteger replacedCount = 0;
+  performRegexOp(self, _cmd, (RKLRegexOp)(RKLReplaceOp | RKLReplaceMutable), regex, 0, 0L, self, &searchRange, replacement, NULL, (void **)((void *)&replacedCount));
+  return(replacedCount);
+}
+
+- (NSUInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement range:(NSRange)searchRange
+{
+  NSUInteger replacedCount = 0;
+  performRegexOp(self, _cmd, (RKLRegexOp)(RKLReplaceOp | RKLReplaceMutable), regex, 0, 0L, self, &searchRange, replacement, NULL, (void **)((void *)&replacedCount));
+  return(replacedCount);
+}
+
+- (NSUInteger)RKL_METHOD_PREPEND(replaceOccurrencesOfRegex):(NSString *)regex withString:(NSString *)replacement options:(RKLRegexOptions)options range:(NSRange)searchRange error:(NSError **)error
+{
+  NSUInteger replacedCount = 0;
+  performRegexOp(self, _cmd, (RKLRegexOp)(RKLReplaceOp | RKLReplaceMutable), regex, options, 0L, self, &searchRange, replacement, error, (void **)((void *)&replacedCount));
+  return(replacedCount);
+}
+
+@end
+