diff --git a/lib/Filesystem/Path.hs b/lib/Filesystem/Path.hs
--- a/lib/Filesystem/Path.hs
+++ b/lib/Filesystem/Path.hs
@@ -132,11 +132,8 @@
 dirname :: FilePath -> FilePath
 dirname p = case reverse (pathDirectories p) of
 	[] -> FilePath Nothing [] Nothing []
-	(d:_) -> let
-		d':exts = T.split (== '.') (chunkText d)
-		chunk txt = d { chunkText = txt }
-		in FilePath Nothing [] (Just (chunk d')) (map chunk exts)
-
+	(d:_) -> case parseFilename d of
+		(base, exts) -> FilePath Nothing [] base exts
 
 -- | Retrieve a 'FilePath'&#x2019;s basename component.
 --
@@ -183,7 +180,7 @@
 	directories = xDirectories ++ pathDirectories y
 	xDirectories = (pathDirectories x ++) $ if null (filename x)
 		then []
-		else [filenameChunk True x]
+		else [filenameText x]
 
 -- | An alias for 'append'.
 (</>) :: FilePath -> FilePath -> FilePath
@@ -285,7 +282,7 @@
 
 -- | Get a 'FilePath'&#x2019;s full extension list.
 extensions :: FilePath -> [T.Text]
-extensions = map chunkText . pathExtensions
+extensions = map unescape' . pathExtensions
 
 -- | Get whether a 'FilePath'&#x2019;s last extension is the predicate.
 hasExtension :: FilePath -> T.Text -> Bool
@@ -298,8 +295,7 @@
 -- | Append many extensions to the end of a 'FilePath'.
 addExtensions :: FilePath -> [T.Text] -> FilePath
 addExtensions p exts = p { pathExtensions = newExtensions } where
-	newExtensions = pathExtensions p ++ chunks
-	chunks = map (\e -> Chunk e True) exts
+	newExtensions = pathExtensions p ++ exts
 
 -- | An alias for 'addExtension'.
 (<.>) :: FilePath -> T.Text -> FilePath
diff --git a/lib/Filesystem/Path/CurrentOS.hs b/lib/Filesystem/Path/CurrentOS.hs
--- a/lib/Filesystem/Path/CurrentOS.hs
+++ b/lib/Filesystem/Path/CurrentOS.hs
@@ -41,7 +41,7 @@
 import qualified Filesystem.Path as F
 import qualified Filesystem.Path.Rules as R
 
-#if defined(CABAL_OS_WINDOWS)
+#if defined(CABAL_OS_WINDOWS) || defined(CABAL_OS_DARWIN)
 #define PLATFORM_PATH_FORMAT T.Text
 #else
 #define PLATFORM_PATH_FORMAT B.ByteString
@@ -50,6 +50,12 @@
 currentOS :: R.Rules PLATFORM_PATH_FORMAT
 #if defined(CABAL_OS_WINDOWS)
 currentOS = R.windows
+#elif defined(CABAL_OS_DARWIN)
+#if __GLASGOW_HASKELL__ >= 702
+currentOS = R.darwin_ghc702
+#else
+currentOS = R.darwin
+#endif
 #else
 #if __GLASGOW_HASKELL__ >= 702
 currentOS = R.posix_ghc702
diff --git a/lib/Filesystem/Path/Internal.hs b/lib/Filesystem/Path/Internal.hs
--- a/lib/Filesystem/Path/Internal.hs
+++ b/lib/Filesystem/Path/Internal.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 
 -- |
@@ -12,29 +13,27 @@
 
 import           Prelude hiding (FilePath)
 
+import           Control.DeepSeq (NFData, rnf)
+import qualified Control.Exception as Exc
+import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as B8
+import           Data.Char (chr, ord)
 import           Data.Data (Data)
 import           Data.List (intersperse)
+import           Data.Ord (comparing)
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as TE
+import           Data.Text.Encoding.Error (UnicodeException)
 import           Data.Typeable (Typeable)
+import           System.IO.Unsafe (unsafePerformIO)
 
 -------------------------------------------------------------------------------
 -- File Paths
 -------------------------------------------------------------------------------
 
-data Chunk = Chunk
-	{ chunkText :: T.Text
-	, chunkGood :: Bool
-	}
-	deriving (Ord, Data, Typeable)
-
-instance Eq Chunk where
-	(Chunk x _) == (Chunk y _) = x == y
-
-type Directory = Chunk
-type Basename = Chunk
-type Extension = Chunk
+type Directory = T.Text
+type Basename = T.Text
+type Extension = T.Text
 
 data Root
 	= RootPosix
@@ -48,34 +47,42 @@
 	, pathBasename :: Maybe Basename
 	, pathExtensions :: [Extension]
 	}
-	deriving (Eq, Ord, Data, Typeable)
+	deriving (Data, Typeable)
 
+instance Eq FilePath where
+	x == y = compare x y == EQ
+
+instance Ord FilePath where
+	compare = comparing (\p ->
+		(pathRoot p
+		, fmap unescape' (pathDirectories p)
+		, fmap unescape' (pathBasename p)
+		, fmap unescape' (pathExtensions p)
+		))
+
+instance NFData Root where
+	rnf (RootWindowsVolume c) = rnf c
+	rnf _ = ()
+
+instance NFData FilePath where
+	rnf p = rnf (pathRoot p) `seq` rnf (pathDirectories p) `seq` rnf (pathBasename p) `seq` rnf (pathExtensions p)
+
 -- | A file path with no root, directory, or filename
 empty :: FilePath
 empty = FilePath Nothing [] Nothing []
 
-dot :: Chunk
-dot = Chunk (T.pack ".") True
+dot :: T.Text
+dot = T.pack "."
 
-dots :: Chunk
-dots = Chunk (T.pack "..") True
+dots :: T.Text
+dots = T.pack ".."
 
-filenameChunk :: Bool -> FilePath -> Chunk
-filenameChunk strict p = Chunk (T.concat texts) allGood where
-	name = maybe (Chunk T.empty True) id (pathBasename p)
+filenameText :: FilePath -> T.Text
+filenameText p = T.concat (name:exts) where
+	name = maybe T.empty id (pathBasename p)
 	exts = case pathExtensions p of
 		[] -> []
-		exts' -> intersperse dot ((Chunk T.empty True):exts')
-	chunks = name:exts
-	
-	texts = map chunkText' chunks
-	allGood = and (map chunkGood chunks)
-	
-	chunkText' c = if chunkGood c
-		then if allGood || not strict
-			then chunkText c
-			else T.pack (B8.unpack (TE.encodeUtf8 (chunkText c)))
-		else chunkText c
+		exts' -> intersperse dot (T.empty:exts')
 
 -------------------------------------------------------------------------------
 -- Rules
@@ -157,3 +164,52 @@
 instance Show (Rules a) where
 	showsPrec d r = showParen (d > 10)
 		(showString "Rules " . shows (rulesName r))
+
+textSplitBy :: (Char -> Bool) -> T.Text -> [T.Text]
+#if MIN_VERSION_text(0,11,0)
+textSplitBy = T.split
+#else
+textSplitBy = T.splitBy
+#endif
+
+unescape :: T.Text -> (T.Text, Bool)
+unescape t = if T.any (\c -> ord c >= 0xEF00 && ord c <= 0xEFFF) t
+	then (T.map (\c -> if ord c >= 0xEF00 && ord c <= 0xEFFF
+		then chr (ord c - 0xEF00)
+		else c) t, False)
+	else (t, True)
+
+unescape' :: T.Text -> T.Text
+unescape' = fst . unescape
+
+unescapeBytes' :: T.Text -> B8.ByteString
+unescapeBytes' t = B8.concat (map (\c -> if ord c >= 0xEF00 && ord c <= 0xEFFF
+	then B8.singleton (chr (ord c - 0xEF00))
+	else TE.encodeUtf8 (T.singleton c)) (T.unpack t))
+
+parseFilename :: T.Text -> (Maybe Basename, [Extension])
+parseFilename filename = parsed where
+	parsed = if T.null filename
+		then (Nothing, [])
+		else case textSplitBy (== '.') filename of
+			[] -> (Nothing, [])
+			(name':exts') -> (Just (checkChunk name'), map checkChunk exts')
+	
+	checkChunk t = if chunkGood t
+		then t
+		else case maybeDecodeUtf8 (unescapeBytes' t) of
+			Just text -> text
+			Nothing -> t
+	
+	chunkGood t = not (T.any (\c -> ord c >= 0xEF00 && ord c <= 0xEFFF) t)
+
+maybeDecodeUtf8 :: B.ByteString -> Maybe T.Text
+maybeDecodeUtf8 = excToMaybe . TE.decodeUtf8 where
+	excToMaybe :: a -> Maybe a
+	excToMaybe x = unsafePerformIO $ Exc.catch
+		(fmap Just (Exc.evaluate x))
+		unicodeError
+	
+	unicodeError :: UnicodeException -> IO (Maybe a)
+	unicodeError _ = return Nothing
+
diff --git a/lib/Filesystem/Path/Rules.hs b/lib/Filesystem/Path/Rules.hs
--- a/lib/Filesystem/Path/Rules.hs
+++ b/lib/Filesystem/Path/Rules.hs
@@ -11,6 +11,8 @@
 	, posix
 	, posix_ghc702
 	, windows
+	, darwin
+	, darwin_ghc702
 	
 	-- * Type conversions
 	, toText
@@ -28,16 +30,13 @@
 import           Prelude hiding (FilePath, null)
 import qualified Prelude as P
 
-import qualified Control.Exception as Exc
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as B8
-import           Data.Char (toUpper, chr)
+import           Data.Char (toUpper, chr, ord)
 import           Data.List (intersperse)
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as TE
-import           Data.Text.Encoding.Error (UnicodeException)
 import           System.IO ()
-import           System.IO.Unsafe (unsafePerformIO)
 
 import           Filesystem.Path hiding (root, filename, basename)
 import           Filesystem.Path.Internal
@@ -52,24 +51,14 @@
 	RootWindowsVolume c -> c : ":\\"
 	RootWindowsCurrentVolume -> "\\"
 
-directoryChunks :: Bool -> FilePath -> [Chunk]
-directoryChunks strict path = pathDirectories path ++ [filenameChunk strict path]
-
-maybeDecodeUtf8 :: B.ByteString -> Maybe T.Text
-maybeDecodeUtf8 = excToMaybe . TE.decodeUtf8 where
-	excToMaybe :: a -> Maybe a
-	excToMaybe x = unsafePerformIO $ Exc.catch
-		(fmap Just (Exc.evaluate x))
-		unicodeError
-	
-	unicodeError :: UnicodeException -> IO (Maybe a)
-	unicodeError _ = return Nothing
+directoryChunks :: FilePath -> [T.Text]
+directoryChunks path = pathDirectories path ++ [filenameText path]
 
 -------------------------------------------------------------------------------
 -- POSIX
 -------------------------------------------------------------------------------
 
--- | Linux, BSD, OS X, and other UNIX or UNIX-like operating systems.
+-- | Linux, BSD, and other UNIX or UNIX-like operating systems.
 posix :: Rules B.ByteString
 posix = Rules
 	{ rulesName = T.pack "POSIX"
@@ -83,75 +72,78 @@
 	, decodeString = posixFromBytes . B8.pack
 	}
 
--- | Linux, BSD, OS X, and other UNIX or UNIX-like operating systems.
+-- | Linux, BSD, and other UNIX or UNIX-like operating systems.
 --
--- This variant is for use with GHC 7.2 or later, which tries to decode
--- file paths in its IO computations.
+-- This is a variant of 'posix' for use with GHC 7.2 or later, which tries to
+-- decode file paths in its IO computations.
+--
+-- Since: 0.3.3
 posix_ghc702 :: Rules B.ByteString
 posix_ghc702 = posix
-	{ encodeString = T.unpack . either id id . posixToText
-	, decodeString = posixFromText . T.pack
+	{ rulesName = T.pack "POSIX (GHC 7.2)"
+	, encodeString = posixToGhcString
+	, decodeString = posixFromGhcString
 	}
 
 posixToText :: FilePath -> Either T.Text T.Text
 posixToText p = if good then Right text else Left text where
-	good = and (map chunkGood chunks)
-	text = T.concat (root : map chunkText chunks)
+	good = and (map snd chunks)
+	text = T.concat (root : map fst chunks)
 	
 	root = rootText (pathRoot p)
-	chunks = intersperse (Chunk (T.pack "/") True) (directoryChunks False p)
+	chunks = intersperse (T.pack "/", True) (map unescape (directoryChunks p))
 
-posixFromChunks :: [Chunk] -> FilePath
+posixFromChunks :: [T.Text] -> FilePath
 posixFromChunks chunks = FilePath root directories basename exts where
-	(root, pastRoot) = if T.null (chunkText (head chunks))
+	(root, pastRoot) = if T.null (head chunks)
 		then (Just RootPosix, tail chunks)
 		else (Nothing, chunks)
 	
 	(directories, filename)
-		| P.null pastRoot = ([], Chunk T.empty True)
+		| P.null pastRoot = ([], T.empty)
 		| otherwise = case last pastRoot of
-			fn | fn == dot -> (goodDirs pastRoot, Chunk T.empty True)
-			fn | fn == dots -> (goodDirs pastRoot, Chunk T.empty True)
+			fn | fn == dot -> (goodDirs pastRoot, T.empty)
+			fn | fn == dots -> (goodDirs pastRoot, T.empty)
 			fn -> (goodDirs (init pastRoot), fn)
 	
-	goodDirs = filter (not . T.null . chunkText)
-	
-	(basename, exts) = if T.null (chunkText filename)
-		then (Nothing, [])
-		else case T.split (== '.') (chunkText filename) of
-			[] -> (Nothing, [])
-			(name':exts') -> if chunkGood filename
-				then (Just (Chunk name' True), map (\e -> Chunk e True) exts')
-				else (Just (checkChunk name'), map checkChunk exts')
+	goodDirs = filter (not . T.null)
 	
-	checkChunk raw = case maybeDecodeUtf8 (B8.pack (T.unpack raw)) of
-		Just text -> Chunk text True
-		Nothing -> Chunk raw False
+	(basename, exts) = parseFilename filename
 
 posixFromText :: T.Text -> FilePath
 posixFromText text = if T.null text
 	then empty
-	else posixFromChunks (map (\t -> Chunk t True) (T.split (== '/') text))
+	else posixFromChunks (textSplitBy (== '/') text)
 
 posixToBytes :: FilePath -> B.ByteString
 posixToBytes p = B.concat (root : chunks) where
 	root = TE.encodeUtf8 (rootText (pathRoot p))
-	chunks = intersperse (B8.pack "/") (map chunkBytes (directoryChunks True p))
-	chunkBytes c = if chunkGood c
-		then TE.encodeUtf8 (chunkText c)
-		else B8.pack (T.unpack (chunkText c))
+	chunks = intersperse (B8.pack "/") (map chunkBytes (directoryChunks p))
+	chunkBytes t = if T.any (\c -> ord c >= 0xEF00 && ord c <= 0xEFFF) t
+		then unescapeBytes' t
+		else TE.encodeUtf8 t
 
 posixFromBytes :: B.ByteString -> FilePath
 posixFromBytes bytes = if B.null bytes
 	then empty
 	else posixFromChunks $ flip map (B.split 0x2F bytes) $ \b -> case maybeDecodeUtf8 b of
-		Just text -> Chunk text True
-		Nothing -> Chunk (T.pack (B8.unpack b)) False
+		Just text -> text
+		Nothing -> T.pack (map (\c -> if ord c >= 0x80
+			then chr (ord c + 0xEF00)
+			else c) (B8.unpack b))
 
+posixToGhcString :: FilePath -> String
+posixToGhcString p = P.concat (root : chunks) where
+	root = T.unpack (rootText (pathRoot p))
+	chunks = intersperse "/" (map T.unpack (directoryChunks p))
+
+posixFromGhcString :: String -> FilePath
+posixFromGhcString = posixFromText . T.pack
+
 posixValid :: FilePath -> Bool
 posixValid p = validRoot && validDirectories where
-	validDirectories = all validChunk (directoryChunks True p)
-	validChunk ch = not (T.any (\c -> c == '\0' || c == '/') (chunkText ch))
+	validDirectories = all validChunk (directoryChunks p)
+	validChunk ch = not (T.any (\c -> c == '\0' || c == '/') ch)
 	validRoot = case pathRoot p of
 		Nothing -> True
 		Just RootPosix -> True
@@ -162,6 +154,57 @@
 	normSearch bytes = if B.null bytes then B8.pack "." else bytes
 
 -------------------------------------------------------------------------------
+-- Darwin
+-------------------------------------------------------------------------------
+
+-- | Darwin and Mac OS X.
+--
+-- This is almost identical to 'posix', but with a native path type of 'T.Text'
+-- rather than 'B.ByteString'.
+--
+-- Since: 0.3.4
+darwin :: Rules T.Text
+darwin = Rules
+	{ rulesName = T.pack "Darwin"
+	, valid = posixValid
+	, splitSearchPath = darwinSplitSearch
+	, toText = Right . darwinToText
+	, fromText = posixFromText
+	, encode = darwinToText
+	, decode = posixFromText
+	, encodeString = darwinToString
+	, decodeString = darwinFromString
+	}
+
+-- | Darwin and Mac OS X.
+--
+-- This is a variant of 'darwin' for use with GHC 7.2 or later, which tries to
+-- decode file paths in its IO computations.
+--
+-- Since: 0.3.4
+darwin_ghc702 :: Rules T.Text
+darwin_ghc702 = darwin
+	{ rulesName = T.pack "Darwin (GHC 7.2)"
+	, encodeString = T.unpack . darwinToText
+	, decodeString = posixFromText . T.pack
+	}
+
+darwinToText :: FilePath -> T.Text
+darwinToText p = T.concat (root : chunks) where
+	root = rootText (pathRoot p)
+	chunks = intersperse (T.pack "/") (directoryChunks p)
+
+darwinToString :: FilePath -> String
+darwinToString = B8.unpack . TE.encodeUtf8 . darwinToText
+
+darwinFromString :: String -> FilePath
+darwinFromString = posixFromText . TE.decodeUtf8 . B8.pack
+
+darwinSplitSearch :: T.Text -> [FilePath]
+darwinSplitSearch = map (posixFromText . normSearch) . textSplitBy (== ':') where
+	normSearch text = if T.null text then T.pack "." else text
+
+-------------------------------------------------------------------------------
 -- Windows
 -------------------------------------------------------------------------------
 
@@ -182,13 +225,13 @@
 winToText :: FilePath -> T.Text
 winToText p = T.concat (root : chunks) where
 	root = rootText (pathRoot p)
-	chunks = intersperse (T.pack "\\") (map chunkText (directoryChunks False p))
+	chunks = intersperse (T.pack "\\") (directoryChunks p)
 
 winFromText :: T.Text -> FilePath
 winFromText text = if T.null text then empty else path where
 	path = FilePath root directories basename exts
 	
-	split = T.split (\c -> c == '/' || c == '\\') text
+	split = textSplitBy (\c -> c == '/' || c == '\\') text
 	
 	(root, pastRoot) = let
 		head' = head split
@@ -204,17 +247,13 @@
 	(directories, filename)
 		| P.null pastRoot = ([], T.empty)
 		| otherwise = case last pastRoot of
-			fn | fn == chunkText dot -> (goodDirs pastRoot, T.empty)
-			fn | fn == chunkText dots -> (goodDirs pastRoot, T.empty)
+			fn | fn == dot -> (goodDirs pastRoot, T.empty)
+			fn | fn == dots -> (goodDirs pastRoot, T.empty)
 			fn -> (goodDirs (init pastRoot), fn)
 	
-	goodDirs = map (\t -> Chunk t True) . filter (not . T.null)
+	goodDirs = filter (not . T.null)
 	
-	(basename, exts) = if T.null filename
-		then (Nothing, [])
-		else case T.split (== '.') filename of
-			[] -> (Nothing, [])
-			(name':exts') -> (Just (Chunk name' True), map (\e -> Chunk e True) exts')
+	(basename, exts) = parseFilename filename
 
 winValid :: FilePath -> Bool
 winValid p = validRoot && noReserved && validCharacters where
@@ -233,11 +272,11 @@
 		_ -> False
 	
 	noExt = p { pathExtensions = [] }
-	noReserved = flip all (directoryChunks False noExt)
-		$ \fn -> notElem (T.toUpper (chunkText fn)) reservedNames
+	noReserved = flip all (directoryChunks noExt)
+		$ \fn -> notElem (T.toUpper fn) reservedNames
 	
-	validCharacters = flip all (directoryChunks False p)
-		$ not . T.any (`elem` reservedChars) . chunkText
+	validCharacters = flip all (directoryChunks p)
+		$ not . T.any (`elem` reservedChars)
 
 winSplit :: T.Text -> [FilePath]
-winSplit = map winFromText . filter (not . T.null) . T.split (== ';')
+winSplit = map winFromText . filter (not . T.null) . textSplitBy (== ';')
diff --git a/scripts/common.bash b/scripts/common.bash
new file mode 100644
--- /dev/null
+++ b/scripts/common.bash
@@ -0,0 +1,23 @@
+PATH="$PATH:$PWD/cabal-dev/bin/"
+
+VERSION=$(awk '/^version:/{print $2}' system-filepath.cabal)
+
+CABAL_DEV=$(which cabal-dev)
+XZ=$(which xz)
+
+require_cabal_dev()
+{
+	if [ -z "$CABAL_DEV" ]; then
+		echo "Can't find 'cabal-dev' executable; make sure it exists on your "'$PATH'
+		echo "Cowardly refusing to fuck with the global package database"
+		exit 1
+	fi
+}
+
+clean_dev_install()
+{
+	require_cabal_dev
+	
+	rm -rf dist
+	$CABAL_DEV install || exit 1
+}
diff --git a/scripts/run-benchmarks b/scripts/run-benchmarks
new file mode 100644
--- /dev/null
+++ b/scripts/run-benchmarks
@@ -0,0 +1,21 @@
+#!/bin/bash
+if [ ! -f 'system-filepath.cabal' ]; then
+	echo -n "Can't find system-filepath.cabal; please run this script as"
+	echo -n " ./scripts/run-benchmarks from within the system-filepath source"
+	echo " directory"
+	exit 1
+fi
+
+. scripts/common.bash
+
+require_cabal_dev
+
+rm -rf dist
+cabal-dev install || exit 1
+
+pushd benchmarks
+rm -rf dist
+$CABAL_DEV -s ../cabal-dev install || exit 1
+popd
+
+cabal-dev/bin/system-filepath_benchmarks $@
diff --git a/scripts/run-coverage b/scripts/run-coverage
new file mode 100644
--- /dev/null
+++ b/scripts/run-coverage
@@ -0,0 +1,21 @@
+#!/bin/bash
+if [ ! -f 'system-filepath.cabal' ]; then
+	echo -n "Can't find system-filepath.cabal; please run this script as"
+	echo -n " ./scripts/run-coverage from within the system-filepath source"
+	echo " directory"
+	exit 1
+fi
+
+. scripts/common.bash
+
+require_cabal_dev
+
+pushd tests
+$CABAL_DEV -s ../cabal-dev install --flags="coverage" || exit 1
+popd
+
+rm -f system-filepath_tests.tix
+cabal-dev/bin/system-filepath_tests $@
+
+hpc markup --srcdir=src --srcdir=tests/ system-filepath_tests.tix --destdir=hpc-markup --exclude=Main
+hpc report --srcdir=src --srcdir=tests/ system-filepath_tests.tix --exclude=Main
diff --git a/scripts/run-tests b/scripts/run-tests
new file mode 100644
--- /dev/null
+++ b/scripts/run-tests
@@ -0,0 +1,17 @@
+#!/bin/bash
+if [ ! -f 'system-filepath.cabal' ]; then
+	echo -n "Can't find system-filepath.cabal; please run this script as"
+	echo -n " ./scripts/run-tests from within the system-filepath source"
+	echo " directory"
+	exit 1
+fi
+
+. scripts/common.bash
+
+require_cabal_dev
+
+pushd tests
+$CABAL_DEV -s ../cabal-dev install || exit 1
+popd
+
+cabal-dev/bin/system-filepath_tests $@
diff --git a/system-filepath.cabal b/system-filepath.cabal
--- a/system-filepath.cabal
+++ b/system-filepath.cabal
@@ -1,5 +1,5 @@
 name: system-filepath
-version: 0.4.2
+version: 0.4.3
 synopsis: High-level, byte-based file and directory path manipulations
 license: MIT
 license-file: license.txt
@@ -14,6 +14,11 @@
 bug-reports: mailto:jmillikin@gmail.com
 
 extra-source-files:
+  scripts/common.bash
+  scripts/run-benchmarks
+  scripts/run-coverage
+  scripts/run-tests
+  --
   tests/system-filepath-tests.cabal
   tests/Tests.hs
 
@@ -24,7 +29,7 @@
 source-repository this
   type: bzr
   location: https://john-millikin.com/branches/hs-filepath/0.4/
-  tag: system-filepath_0.4.2
+  tag: system-filepath_0.4.3
 
 library
   ghc-options: -Wall -O2
@@ -33,10 +38,14 @@
   build-depends:
       base >= 3 && < 5
     , bytestring >= 0.9 && < 0.10
-    , text >= 0.3 && < 0.12
+    , deepseq >= 1.1 && < 1.3
+    , text >= 0.7.1 && < 0.12
 
   if os(windows)
     cpp-options: -DCABAL_OS_WINDOWS
+
+  if os(darwin)
+    cpp-options: -DCABAL_OS_DARWIN
 
   exposed-modules:
     Filesystem.Path
diff --git a/tests/Tests.hs b/tests/Tests.hs
--- a/tests/Tests.hs
+++ b/tests/Tests.hs
@@ -5,8 +5,8 @@
 
 import           Prelude hiding (FilePath)
 
-import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as B8
+import           Data.Char (toUpper)
 import           Data.List (intercalate)
 import qualified Data.Text as T
 import           Data.Text (Text)
@@ -64,6 +64,7 @@
 	, test_EncodeString
 	, test_DecodeString
 	, test_EqualsIgnoresPosixEncoding
+	, test_ShowRules
 	]
 
 test_Empty :: Suite
@@ -137,7 +138,7 @@
 	$expect $ equal (dirnameExts "foo.d/bar") ["d"]
 	
 	-- reparsing preserves good/bad encoding state
-	$expect $ equal (dirnameExts "foo.\xB1.\xDD\xAA/bar") ["\xB1", "\xDD\xAA"]
+	$expect $ equal (dirnameExts "foo.\xB1.\xDD\xAA/bar") ["\xB1", "\x76A"]
 
 test_Basename :: Suite
 test_Basename = assertions "basename" $ do
@@ -201,7 +202,7 @@
 	$expect $ equal (toText posix (p "\xF0\x9D\x84\x9E/\xED\xA0\x80")) (Left (T.pack "\x1D11E/\xED\xA0\x80"))
 	$expect $ equal (toText posix (p "\xED.\xF0\x9D\x84\x9E.\xA0\x80")) (Left (T.pack "\xED.\x1D11E.\xA0\x80"))
 	$expect $ equal (toText posix (p "\xB1.\xDD\xAA")) (Left (T.pack "\xB1.\x76A"))
-	$expect $ equal (toText posix (p "\xB1.\xDD\xAA" </> p "foo")) (Left (T.pack "\xB1.\xDD\xAA/foo"))
+	$expect $ equal (toText posix (p "\xB1.\xDD\xAA" </> p "foo")) (Left (T.pack "\xB1.\x76A/foo"))
 
 test_FromText :: Suite
 test_FromText = assertions "fromText" $ do
@@ -384,8 +385,8 @@
 test_EncodeString_Posix_Ghc702 = assertions "posix_ghc702" $ do
 	let enc = encodeString posix_ghc702
 	$expect $ equal (enc (fromChar8 "test")) "test"
-	$expect $ equal (enc (fromChar8 "test\xA1\xA2")) "test\xA1\xA2"
-	$expect $ equal (enc (fromChar8 "\xC2\xA1\xC2\xA2/test\xA1\xA2")) "\xA1\xA2/test\xA1\xA2"
+	$expect $ equal (enc (fromChar8 "test\xA1\xA2")) "test\xEFA1\xEFA2"
+	$expect $ equal (enc (fromChar8 "\xC2\xA1\xC2\xA2/test\xA1\xA2")) "\xA1\xA2/test\xEFA1\xEFA2"
 	$expect $ equal (enc (fromText posix_ghc702 "test\xA1\xA2")) "test\xA1\xA2"
 
 test_EncodeString_Win32 :: Suite
@@ -399,6 +400,8 @@
 test_DecodeString = suite "decodeString"
 	[ test_DecodeString_Posix
 	, test_DecodeString_Posix_Ghc702
+	, test_DecodeString_Darwin
+	, test_DecodeString_Darwin_Ghc702
 	, test_DecodeString_Win32
 	]
 
@@ -417,7 +420,25 @@
 	$expect $ equal (dec r "test") (fromText r "test")
 	$expect $ equal (dec r "test\xC2\xA1\xC2\xA2") (fromText r "test\xC2\xA1\xC2\xA2")
 	$expect $ equal (dec r "test\xA1\xA2") (fromText r "test\xA1\xA2")
+	$expect $ equal (dec r "test\xEFA1\xEFA2") (fromChar8 "test\xA1\xA2")
+	$expect $ equal
+		(toText r (dec r "test\xEFA1\xEFA2"))
+		(Left "test\xA1\xA2")
 
+test_DecodeString_Darwin :: Suite
+test_DecodeString_Darwin = assertions "darwin" $ do
+	let r = darwin
+	let dec = decodeString
+	$expect $ equal (dec r "test\xC2\xA1\xC2\xA2") (fromText r "test\xA1\xA2")
+
+test_DecodeString_Darwin_Ghc702 :: Suite
+test_DecodeString_Darwin_Ghc702 = assertions "darwin_ghc702" $ do
+	let r = darwin_ghc702
+	let dec = decodeString
+	$expect $ equal (dec r "test\xC2\xA1\xC2\xA2") (fromText r "test\xC2\xA1\xC2\xA2")
+	$expect $ equal (dec r "test\xA1\xA2") (fromText r "test\xA1\xA2")
+	$expect $ equal (dec r "test\xEFA1\xEFA2") (fromChar8 "test\xA1\xA2")
+
 test_DecodeString_Win32 :: Suite
 test_DecodeString_Win32 = assertions "windows" $ do
 	let r = windows
@@ -432,6 +453,14 @@
 		(fromChar8 "test\xA1\xA2")
 		(fromText posix "test\xA1\xA2")
 
+test_ShowRules :: Suite
+test_ShowRules = assertions "show-rules" $ do
+	$expect $ equal (showsPrec 11 darwin "") "(Rules \"Darwin\")"
+	$expect $ equal (showsPrec 11 darwin_ghc702 "") "(Rules \"Darwin (GHC 7.2)\")"
+	$expect $ equal (showsPrec 11 posix "") "(Rules \"POSIX\")"
+	$expect $ equal (showsPrec 11 posix_ghc702 "") "(Rules \"POSIX (GHC 7.2)\")"
+	$expect $ equal (showsPrec 11 windows "") "(Rules \"Windows\")"
+
 posixPaths :: Gen FilePath
 posixPaths = sized $ fmap merge . genComponents where
 	merge = fromChar8 . intercalate "/"
@@ -458,7 +487,7 @@
 		, "LPT7", "LPT8", "LPT9", "NUL", "PRN"
 		]
 	validChar c = not (elem c reserved)
-	validComponent c = not (elem c reservedNames)
+	validComponent c = not (elem (map toUpper c) reservedNames)
 	component = do
 		size <- choose (0, 10)
 		vectorOf size $ arbitrary `suchThat` validChar
diff --git a/tests/system-filepath-tests.cabal b/tests/system-filepath-tests.cabal
--- a/tests/system-filepath-tests.cabal
+++ b/tests/system-filepath-tests.cabal
@@ -20,5 +20,6 @@
     , bytestring
     , chell >= 0.2 && < 0.3
     , chell-quickcheck >= 0.1 && < 0.2
+    , deepseq >= 1.1 && < 1.3
     , QuickCheck
     , text
