diff --git a/DataencQC.hs b/DataencQC.hs
new file mode 100644
--- /dev/null
+++ b/DataencQC.hs
@@ -0,0 +1,180 @@
+{-
+ - Copyright : (c) 2007 Magnus Therning
+ - License   : BSD3
+ -}
+
+module DataencQC
+    where
+
+import Data.Maybe
+import Data.Word
+import Test.QuickCheck
+import Test.Framework.Providers.QuickCheck2
+
+import qualified Codec.Binary.Uu as Uu
+import qualified Codec.Binary.Uu as Xx
+import qualified Codec.Binary.Base85 as Base85
+import qualified Codec.Binary.Base64 as Base64
+import qualified Codec.Binary.Base64Url as Base64Url
+import qualified Codec.Binary.Base32 as Base32
+import qualified Codec.Binary.Base32Hex as Base32Hex
+import qualified Codec.Binary.Base16 as Base16
+import qualified Codec.Binary.Yenc as Yenc
+import qualified Codec.Binary.Hexadecimal as Hex
+import qualified Codec.Binary.QuotedPrintable as QP
+import qualified Codec.Binary.PythonString as Py
+import qualified Codec.Binary.Url as Url
+
+-- {{{1 Arbitrary instances
+instance Arbitrary Word8 where
+    arbitrary = do
+        n <- choose ((fromIntegral (minBound::Word8))::Int,
+                (fromIntegral (maxBound::Word8))::Int)
+        return $ fromIntegral n
+
+-- {{{1 uuencode properties
+prop_uuEncode ws = ws == (fromJust . Uu.decode . Uu.encode) ws
+    where types = ws::[Word8]
+
+prop_uuChop n ws = s == (Uu.unchop . Uu.chop n) s
+    where
+        types = (n :: Int, ws::[Word8])
+        s = Uu.encode ws
+
+prop_uuCombined n ws = ws == (fromJust $ Uu.decode $ Uu.unchop $ Uu.chop n $ Uu.encode ws)
+    where types = (n::Int, ws::[Word8])
+
+-- {{{1 xxencode properties
+prop_xxEncode ws = ws == (fromJust . Xx.decode . Xx.encode) ws
+    where types = ws::[Word8]
+
+prop_xxChop n ws = s == (Xx.unchop . Xx.chop n) s
+    where
+        types = (n:: Int, ws::[Word8])
+        s = Xx.encode ws
+
+prop_xxCombined n ws = ws == (fromJust $ Xx.decode $ Xx.unchop $ Xx.chop n $ Xx.encode ws)
+    where types = (n::Int, ws::[Word8])
+
+-- {{{1 base85 properties
+prop_base85Encode ws = ws == (fromJust $ Base85.decode $ Base85.encode ws)
+    where types = ws::[Word8]
+
+prop_base85Chop n s = s == (Base85.unchop $ Base85.chop n s)
+    where types = (n::Int, s::String)
+
+-- {{{1 base64 properties
+prop_base64Encode ws = ws == (fromJust $ Base64.decode $ Base64.encode ws)
+    where types = ws::[Word8]
+
+prop_base64Chop n s = s == (Base64.unchop $ Base64.chop n s)
+    where types = (n::Int, s::String)
+
+-- {{{1 base64url properties
+prop_base64UrlEncode ws = ws == (fromJust $ Base64Url.decode $ Base64Url.encode ws)
+    where types = ws::[Word8]
+
+prop_base64UrlChop n s = s == (Base64Url.unchop $ Base64Url.chop n s)
+    where types = (n::Int, s::String)
+
+-- {{{1 base32
+prop_base32Encode ws = ws == (fromJust $ Base32.decode $ Base32.encode ws)
+    where types = ws::[Word8]
+
+prop_base32Chop n s = s == (Base32.unchop $ Base32.chop n s)
+    where types = (n::Int, s::String)
+
+-- {{{1 base32hex
+prop_base32HexEncode ws = ws == (fromJust $ Base32Hex.decode $ Base32Hex.encode ws)
+    where types = ws::[Word8]
+
+prop_base32HexChop n s = s == (Base32Hex.unchop $ Base32Hex.chop n s)
+    where types = (n::Int, s::String)
+
+-- {{{1 base16
+prop_base16Encode ws = ws == (fromJust $ Base16.decode $ Base16.encode ws)
+    where types = ws::[Word8]
+
+prop_base16Chop n s = s == (Base16.unchop $ Base16.chop n s)
+    where types = (n::Int, s::String)
+
+-- {{{1 yEncoding
+prop_yencEncode ws = ws == (fromJust $ Yenc.decode $ Yenc.encode ws)
+    where types = ws ::[Word8]
+
+prop_yencChop n ws = ws == (Yenc.unchop $ Yenc.chop n ws)
+    where types = (n::Int, ws :: [Word8])
+
+-- {{{1 hexadecimal
+prop_hexEncode ws = ws == (fromJust $ Hex.decode $ Hex.encode ws)
+    where types = ws :: [Word8]
+
+prop_hexChop n s = s == (Hex.unchop $ Hex.chop n s)
+    where types = (n :: Int, s :: String)
+
+-- {{{1 qp
+prop_qpEncode ws = ws == (fromJust $ QP.decode $ QP.encode ws)
+    where types = ws :: [Word8]
+
+prop_qpChop n ws = s == (QP.unchop . QP.chop n) s
+    where
+        types = (n::Int, ws::[Word8])
+        s = QP.encode ws
+
+prop_qpCombined n ws = ws == (fromJust $ QP.decode $ QP.unchop $ QP.chop n $ QP.encode ws)
+    where types = (n::Int, ws::[Word8])
+
+-- {{{1 py
+prop_pyEncode ws = ws == (fromJust $ Py.decode $ Py.encode ws)
+    where types = ws :: [Word8]
+
+prop_pyChop n s = s == (Py.unchop $ Py.chop n s)
+    where types = (n :: Int, s :: String)
+
+prop_pyCombined n ws = ws == (fromJust $ runAll ws)
+    where runAll = Py.decode . Py.unchop . Py.chop n . Py.encode
+
+-- {{{1 url
+prop_urlEncode ws = ws == (fromJust $ Url.decode $ Url.encode ws)
+    where types = ws :: [Word8]
+
+prop_urlChop n s = s == (Url.unchop $ Url.chop n s)
+    where types = (n :: Int, s :: String)
+
+prop_urlCombined n ws = ws == (fromJust $ runAll ws)
+    where runAll = Url.decode . Url.unchop . Url.chop n . Url.encode
+
+-- {{{1 all the tests
+allTests =
+    [ testProperty "uuEncode" prop_uuEncode
+    , testProperty "uuChop" prop_uuChop
+    , testProperty "uuCombined" prop_uuCombined
+    , testProperty "xxEncode" prop_xxEncode
+    , testProperty "xxChop" prop_xxChop
+    , testProperty "xxCombined" prop_xxCombined
+    , testProperty "base85Encode" prop_base85Encode
+    , testProperty "base85Chop" prop_base85Chop
+    , testProperty "base64Encode" prop_base64Encode
+    , testProperty "base64Chop" prop_base64Chop
+    , testProperty "base64UrlEncode" prop_base64UrlEncode
+    , testProperty "base64UrlChop" prop_base64UrlChop
+    , testProperty "base32Encode" prop_base32Encode
+    , testProperty "base32Chop" prop_base32Chop
+    , testProperty "base32HexEncode" prop_base32HexEncode
+    , testProperty "base32HexChop" prop_base32HexChop
+    , testProperty "base16Encode" prop_base16Encode
+    , testProperty "base16Chop" prop_base16Chop
+    , testProperty "yencEncode" prop_yencEncode
+    , testProperty "yencChop" prop_yencChop
+    , testProperty "hexEncode" prop_hexEncode
+    , testProperty "hexChop" prop_hexChop
+    , testProperty "qpEncode" prop_qpEncode
+    , testProperty "qpChop" prop_qpChop
+    , testProperty "qpCombined" prop_qpCombined
+    , testProperty "pyEncode" prop_pyEncode
+    , testProperty "pyChop" prop_pyChop
+    , testProperty "pyCombined" prop_pyCombined
+    , testProperty "urlEncode" prop_urlEncode
+    , testProperty "urlChop" prop_urlChop
+    , testProperty "urlCombined" prop_urlCombined
+    ]
diff --git a/DataencUT.hs b/DataencUT.hs
new file mode 100644
--- /dev/null
+++ b/DataencUT.hs
@@ -0,0 +1,306 @@
+{-
+ - Copyright : (c) 2007 Magnus Therning
+ - License   : BSD3
+ -}
+
+module DataencUT
+    where
+
+import Test.HUnit
+import Control.Monad
+import System.Exit
+import Data.Maybe
+import qualified Test.Framework.Providers.API as TFAPI
+import Test.Framework.Providers.HUnit
+
+import Codec.Binary.DataEncoding
+import qualified Codec.Binary.Yenc as Yenc
+
+-- {{{1 buildTestList
+-- builds a list of successful tests based on a tuple (suite, description,
+-- encoded data, decoded data, codec)
+buildTestList td = TestList $ concat [
+    [ TestLabel (suite ++ " encode " ++ desc) (enc ~=? (encode codec dec))
+    , TestLabel (suite ++ " decode " ++ desc) (dec ~=? (fromJust $ decode codec $ enc))
+    ] | (suite, desc, enc, dec, codec) <- td ]
+
+-- {{{1 unitTest2TFTest
+unitTest2TFTest :: Test -> [TFAPI.Test]
+unitTest2TFTest t = let
+        unitTest2TFTest' desc (TestCase a) = [testCase desc a]
+        unitTest2TFTest' desc (TestLabel s t) = unitTest2TFTest' (desc ++ ":" ++ s) t
+        unitTest2TFTest' desc (TestList ts) = concat $ map (unitTest2TFTest' desc) ts
+    in unitTest2TFTest' "" t
+
+
+-- {{{1 uuencode tests
+uuTestData =
+    [ ("uu", "empty", "", [], uu)
+    , ("uu", "\\0", "``", [0], uu)
+    , ("uu", "\\255", "_P", [255], uu)
+    , ("uu", "AA", "04$", [65, 65], uu)
+    , ("uu", "AAA", "04%!", [65, 65, 65], uu)
+    , ("uu", "AAAA", "04%!00", [65, 65, 65, 65], uu)
+    , ("uu", "Example", "17AA;7!L90", [69,120,97,109,112,108,101], uu)
+    ]
+uuTests = buildTestList uuTestData
+
+uuTests2 = test
+    [ "uu unchop.chop" ~: "EI2" ~=? (unchop uu $ chop uu 1 "EI2")
+    , "uu unchop.chop" ~: "EI3-" ~=? (unchop uu $ chop uu 1 "EI3-")
+    , "uu unchop.chop" ~: "EI3-EE" ~=? (unchop uu $ chop uu 1 "EI3-EE")
+    , "uu full circle" ~: [0..255] ~=? fromJust (decode uu $ unchop uu $ chop uu 1 $ encode uu [0..255])
+    , "uu full circle" ~: [0..255] ~=? fromJust (decode uu $ unchop uu $ chop uu 61 $ encode uu [0..255])
+    , "uu full circle" ~: [0..255] ~=? fromJust (decode uu $ unchop uu $ chop uu 100 $ encode uu [0..255])
+    ]
+
+uuTestsFail = test
+    [ "uu decode short" ~: Nothing ~=? decode uu "A"
+    , "uu decode' short" ~: [Nothing] ~=? decode' uu "A"
+    , "uu decode illegal" ~: Nothing ~=? decode uu "aa"
+    , "uu decode' illegal" ~: [Nothing] ~=? decode' uu "aa"
+    ]
+
+-- {{{1 xxencode tests
+xxTestData =
+    [ ("xx", "empty", "", [], xx)
+    , ("xx", "\\0", "++", [0], xx)
+    , ("xx", "\\255", "zk", [255], xx)
+    , ("xx", "AA", "EI2", [65, 65], xx)
+    , ("xx", "AAA", "EI3-", [65, 65, 65], xx)
+    , ("xx", "AAAA", "EI3-EE", [65, 65, 65, 65], xx)
+    , ("xx", "Example", "FLVVPL-gNE", [69,120,97,109,112,108,101], xx)
+    ]
+xxTests = buildTestList xxTestData
+
+xxTests2 = test
+    [ "xx unchop.chop" ~: "EI2" ~=? (unchop xx $ chop xx 1 "EI2")
+    , "xx unchop.chop" ~: "EI3-" ~=? (unchop xx $ chop xx 1 "EI3-")
+    , "xx unchop.chop" ~: "EI3-EE" ~=? (unchop xx $ chop xx 1 "EI3-EE")
+    , "xx full circle" ~: [0..255] ~=? fromJust (decode xx $ unchop xx $ chop xx 1 $ encode xx [0..255])
+    , "xx full circle" ~: [0..255] ~=? fromJust (decode xx $ unchop xx $ chop xx 61 $ encode xx [0..255])
+    , "xx full circle" ~: [0..255] ~=? fromJust (decode xx $ unchop xx $ chop xx 100 $ encode xx [0..255])
+    ]
+
+xxTestsFail = test
+    [ "xx decode short" ~: Nothing ~=? decode xx "A"
+    , "xx decode' short" ~: [Nothing] ~=? decode' xx "A"
+    , "xx decode illegal" ~: Nothing ~=? decode xx "''"
+    , "xx decode' illegal" ~: [Nothing] ~=? decode' xx "''"
+    ]
+
+-- {{{1 base85 tests
+base85TestData =
+    [ ("base85", "empty", "", [], base85)
+    , ("base85", "f", "Ac", [102], base85)
+    , ("base85", "fo", "Ao@", [102,111], base85)
+    , ("base85", "foo", "AoDS", [102,111,111], base85)
+    , ("base85", "foob", "AoDTs", [102,111,111,98], base85)
+    , ("base85", "fooba", "AoDTs@/", [102,111,111,98,97], base85)
+    , ("base85", "foobar", "AoDTs@<)", [102,111,111,98,97,114], base85)
+    , ("base85", "\0", "!!", [0], base85)
+    , ("base85", "foob\0\0\0\0ar", "AoDTszEW", [102,111,111,98,0,0,0,0,114], base85)
+    , ("base85", "Example", "7<i6XE,9(", [69,120,97,109,112,108,101], base85)
+    ]
+base85Tests = buildTestList base85TestData
+
+base85TestsFail = test
+    [ "base85 decode short" ~: Nothing ~=? decode base85 "A"
+    , "base85 decode' short" ~: [Nothing] ~=? decode' base85 "A"
+    , "base85 decode illegal" ~: Nothing ~=? decode base85 "!z"
+    , "base85 decode' illegal" ~: [Nothing] ~=? decode' base85 "!z"
+    ]
+
+-- {{{1 base64 tests
+base64TestData =
+    [ ("base64", "empty", "", [], base64)
+    , ("base64", "f", "Zg==", [102], base64)
+    , ("base64", "fo", "Zm8=", [102,111], base64)
+    , ("base64", "foo", "Zm9v", [102,111,111], base64)
+    , ("base64", "foob", "Zm9vYg==", [102,111,111,98], base64)
+    , ("base64", "fooba", "Zm9vYmE=", [102,111,111,98,97], base64)
+    , ("base64", "foobar", "Zm9vYmFy", [102,111,111,98,97,114], base64)
+    , ("base64", "\0", "AA==", [0], base64)
+    , ("base64", "\255", "/w==", [255], base64)
+    , ("base64", "Example", "RXhhbXBsZQ==", [69,120,97,109,112,108,101], base64)
+    ]
+base64Tests = buildTestList base64TestData
+
+base64TestsFail = test
+    [ "base64 decode short" ~: Nothing ~=? decode base64 "A"
+    , "base64 decode' short" ~: [Nothing] ~=? decode' base64 "A"
+    , "base64 decode illegal" ~: Nothing ~=? decode base64 "!!"
+    , "base64 decode' illegal" ~: [Nothing] ~=? decode' base64 "!!"
+    ]
+
+-- {{{1 base64url tests
+base64UrlTestData =
+    [ ("base64url", "empty", "", [], base64Url)
+    , ("base64url", "\0", "AA==", [0], base64Url)
+    , ("base64url", "\255", "_w==", [255], base64Url)
+    , ("base64url", "Example", "RXhhbXBsZQ==", [69,120,97,109,112,108,101], base64Url)
+    ]
+base64UrlTests = buildTestList base64UrlTestData
+
+-- {{{1 base32 tests
+base32TestData =
+    [ ("base32", "empty", "", [], base32)
+    , ("base32", "f", "MY======", [102], base32)
+    , ("base32", "fo", "MZXQ====", [102,111], base32)
+    , ("base32", "foo", "MZXW6===", [102,111,111], base32)
+    , ("base32", "foob", "MZXW6YQ=", [102,111,111,98], base32)
+    , ("base32", "fooba", "MZXW6YTB", [102,111,111,98,97], base32)
+    , ("base32", "foobar", "MZXW6YTBOI======", [102,111,111,98,97,114], base32)
+    ]
+base32Tests = buildTestList base32TestData
+
+base32TestsFail = test
+    [ "base32 decode short" ~: Nothing ~=? decode base32 "A"
+    , "base32 decode' short" ~: [Nothing] ~=? decode' base32 "A"
+    , "base32 decode illegal" ~: Nothing ~=? decode base32 "gh"
+    , "base32 decode' illegal" ~: [Nothing] ~=? decode' base32 "gh"
+    ]
+
+-- {{{1 base32hex tests
+base32HexTestData =
+    [ ("base32hex", "empty", "", [], base32Hex)
+    , ("base32hex", "f", "CO======", [102], base32Hex)
+    , ("base32hex", "fo", "CPNG====", [102,111], base32Hex)
+    , ("base32hex", "foo", "CPNMU===", [102,111,111], base32Hex)
+    , ("base32hex", "foob", "CPNMUOG=", [102,111,111,98], base32Hex)
+    , ("base32hex", "fooba", "CPNMUOJ1", [102,111,111,98,97], base32Hex)
+    , ("base32hex", "foobar", "CPNMUOJ1E8======", [102,111,111,98,97,114], base32Hex)
+    ]
+base32HexTests = buildTestList base32HexTestData
+
+base32HexTestsFail = test
+    [ "base32hex decode short" ~: Nothing ~=? decode base32Hex "A"
+    , "base32hex decode' short" ~: [Nothing] ~=? decode' base32Hex "A"
+    , "base32hex decode illegal" ~: Nothing ~=? decode base32Hex "gh"
+    , "base32hex decode' illegal" ~: [Nothing] ~=? decode' base32Hex "gh"
+    ]
+
+-- {{{1 base16 (hex)
+base16TestData =
+    [ ("base16", "empty", "", [], base16)
+    , ("base16", "f", "66", [102], base16)
+    , ("base16", "fo", "666F", [102,111], base16)
+    , ("base16", "foo", "666F6F", [102,111,111], base16)
+    , ("base16", "foob", "666F6F62", [102,111,111,98], base16)
+    , ("base16", "fooba", "666F6F6261", [102,111,111,98,97], base16)
+    , ("base16", "foobar", "666F6F626172", [102,111,111,98,97,114], base16)
+    ]
+base16Tests = buildTestList base16TestData
+
+base16TestsFail = test
+    [ "base16 decode short" ~: Nothing ~=? decode base16 "A"
+    , "base16 decode' short" ~: [Nothing] ~=? decode' base16 "A"
+    , "base16 decode illegal" ~: Nothing ~=? decode base16 "GH"
+    , "base16 decode' illegal" ~: [Nothing] ~=? decode' base16 "GH"
+    ]
+
+-- {{{1 yEncoding
+yencTests = test
+    [ "yEnc encode empty" ~: [] ~=? Yenc.encode []
+    , "yEnc decode empty" ~: Just [] ~=? Yenc.decode []
+    , "yEnc encode 'f'" ~: [0x90] ~=? Yenc.encode [0x66]
+    , "yEnc decode enc('f')" ~: Just [0x66] ~=? Yenc.decode [0x90]
+    , "yEnc encode 'foobar'" ~: [0x90, 0x99, 0x99, 0x8c, 0x8b, 0x9c] ~=? Yenc.encode [0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72]
+    , "yEnc decode enc('foobar')" ~: Just [0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72] ~=? Yenc.decode [0x90, 0x99, 0x99, 0x8c, 0x8b, 0x9c]
+    , "yEnc encode [0xd6, 0xd7]" ~: [0x3d, 0x40, 0x01] ~=? Yenc.encode [0xd6, 0xd7]
+    , "yEnc decode enc([0xd6, 0xd7])" ~:  Just [0xd6, 0xd7] ~=? Yenc.decode [0x3d, 0x40, 0x01]
+    , "yEnc encode criticals" ~: [0x3d, 0x40, 0x3d, 0x4a, 0x3d, 0x4d, 0x3d, 0x7d] ~=? Yenc.encode [0xd6, 0xe0, 0xe3, 0x13]
+    , "yEnc decode criticals" ~:  Just [0xd6, 0xe0, 0xe3, 0x13] ~=? Yenc.decode [0x3d, 0x40, 0x3d, 0x4a, 0x3d, 0x4d, 0x3d, 0x7d]
+    , "yEnc chop" ~: [[0x3d, 0x40], [0x01, 0x3d, 0x4a]] ~=? Yenc.chop 2 [0x3d, 0x40, 0x01, 0x3d, 0x4a]
+    ]
+
+-- {{{1 hexadecimal
+hexTestData =
+    [ ("hex", "empty", "", [], hex)
+    , ("hex", "foobar", "666F6F626172", [102,111,111,98,97,114], hex)
+    ]
+hexTests = buildTestList hexTestData
+
+hexTestsFail = test
+    [ "hex decode short" ~: Nothing ~=? decode hex "0"
+    , "hex decode' short" ~: [Nothing] ~=? decode' hex "0"
+    , "hex decode illegal" ~: Nothing ~=? decode hex "H"
+    , "hex decode' illegal" ~: [Nothing] ~=? decode' hex "H"
+    ]
+
+-- {{{1 quoted-printable
+qpTestData =
+    [ ("qp", "empty", "", [], qp)
+    , ("qp", "foo=bar", "foo=3Dbar", [102,111,111,61,98,97,114], qp)
+    ]
+qpTests = buildTestList qpTestData
+
+qpTestsSucc = test
+    [ "qp chop one" ~: ["foo=","=3D=","bar"] ~=? chop qp 4 "foo=3Dbar"
+    ]
+
+qpTestsFail = test
+    [ "qp decode short" ~: Nothing ~=? decode qp "=4"
+    , "qp decode' short" ~: [Nothing] ~=? decode' qp "=4"
+    , "qp decode non-hex" ~: Nothing ~=? decode qp "=G"
+    , "qp decode' non-hex" ~: [Nothing] ~=? decode' qp "=G"
+    ]
+
+-- {{{1 python string
+pyTestData =
+    [ ("py", "empty", "", [], py)
+    , ("py", "<0x00><0x1f><0x20><0x7e><0x7f><0xff>", "\\x00\\x1F ~\\x7F\\xFF", [0x00, 0x1f, 0x20, 0x7e, 0x7f, 0xff], py)
+    , ("py", "\"\'\\", "\\\"\\'\\\\", [34, 39, 92], py)
+    ]
+pyTests = buildTestList pyTestData
+
+pyTestsFail = test
+    [ "py decode bad char after \\" ~: Nothing ~=? decode py "\\z"
+    , "py decode' bad char after \\" ~: [Nothing] ~=? decode' py "\\z"
+    ]
+
+-- {{{1 url encoding
+urlTestData =
+    [ ("url", "empty", "", [], url)
+    , ("url", "aA", "aA", [97, 65], url)
+    , ("url", "~ ", "~%20", [126, 0x20], url)
+    ]
+urlTests = buildTestList urlTestData
+
+urlTestsFail = test
+    [ "url decode bad char after %" ~: Nothing ~=? decode url "%ga"
+    , "url decode' bad char after %" ~: [Nothing] ~=? decode' url "%ga"
+    , "url decode bad char after %" ~: Nothing ~=? decode url "%%"
+    , "url decode' bad char after %" ~: [Nothing] ~=? decode' url "%%"
+    ]
+
+-- {{{1 all the tests
+allTests = concat
+    [ unitTest2TFTest uuTests
+    , unitTest2TFTest uuTests2
+    , unitTest2TFTest uuTestsFail
+    , unitTest2TFTest xxTests
+    , unitTest2TFTest xxTests2
+    , unitTest2TFTest xxTestsFail
+    , unitTest2TFTest base85Tests
+    , unitTest2TFTest base85TestsFail
+    , unitTest2TFTest base64Tests
+    , unitTest2TFTest base64TestsFail
+    , unitTest2TFTest base64UrlTests
+    , unitTest2TFTest base32Tests
+    , unitTest2TFTest base32TestsFail
+    , unitTest2TFTest base32HexTests
+    , unitTest2TFTest base32HexTestsFail
+    , unitTest2TFTest base16Tests
+    , unitTest2TFTest base16TestsFail
+    , unitTest2TFTest yencTests
+    , unitTest2TFTest hexTests
+    , unitTest2TFTest hexTestsFail
+    , unitTest2TFTest qpTests
+    , unitTest2TFTest qpTestsSucc
+    , unitTest2TFTest qpTestsFail
+    , unitTest2TFTest pyTests
+    , unitTest2TFTest pyTestsFail
+    , unitTest2TFTest urlTests
+    , unitTest2TFTest urlTestsFail
+    ]
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644
--- /dev/null
+++ b/GNUmakefile
@@ -0,0 +1,32 @@
+.PHONY: all clean markup report test
+
+TESTS = Test
+ifeq (,$(shell ghc-pkg list dataenc | grep dataenc))
+GHCOPTS = -fhpc -isrc
+else
+GHCOPTS = -fhpc -hide-package dataenc -isrc
+endif
+HPC_SUM_OPTS = --exclude=Main --exclude=DataencUT --exclude=DataencQC
+
+% : %.hs
+	ghc --make $(GHCOPTS) $<
+
+all: $(TESTS)
+
+test: $(TESTS)
+	./Test
+
+report : test
+	hpc6 sum $(HPC_SUM_OPTS) --output test.tix Test.tix
+	hpc6 report test.tix
+
+markup : test
+	hpc6 sum $(HPC_SUM_OPTS) --output test.tix Test.tix
+	hpc6 markup test.tix
+
+clean:
+	rm -f *~ Test *.tix *.html *.o *.hi
+	rm -rf .hpc
+	rm -f src/Codec/Binary/*.o
+	rm -f src/Codec/Binary/*.hi
+	rm -f src/Codec/Binary/*~
diff --git a/QC.hs b/QC.hs
deleted file mode 100644
--- a/QC.hs
+++ /dev/null
@@ -1,114 +0,0 @@
-{-
- - Copyright : (c) 2007 Magnus Therning
- - License   : BSD3
- -}
-
-module Main
-    where
-
-import Data.Maybe
-import Data.Word
-import Test.QuickCheck
-
-import qualified Codec.Binary.Uu as Uu
-import qualified Codec.Binary.Base85 as Base85
-import qualified Codec.Binary.Base64 as Base64
-import qualified Codec.Binary.Base64Url as Base64Url
-import qualified Codec.Binary.Base32 as Base32
-import qualified Codec.Binary.Base32Hex as Base32Hex
-import qualified Codec.Binary.Base16 as Base16
-import qualified Codec.Binary.Yenc as Yenc
-
--- {{{1 Arbitrary instances
-instance Arbitrary Char where
-    arbitrary = do
-        n <- choose ((fromEnum (minBound::Char))::Int,
-                (fromEnum (maxBound::Char))::Int)
-        return $ toEnum n
-
-instance Arbitrary Word8 where
-    arbitrary = do
-        n <- choose ((fromIntegral (minBound::Word8))::Int,
-                (fromIntegral (maxBound::Word8))::Int)
-        return $ fromIntegral n
-
--- {{{1 uuencode properties
-prop_uuEncode ws = ws == (fromJust . Uu.decode . Uu.encode) ws
-    where types = ws::[Word8]
-
-prop_uuChop s = properLength s ==> s == (Uu.unchop . Uu.chop 45) s
-    where
-        types = s::String
-        properLength s = length s `mod` 4 /= 1 -- property of uuencode guarantees this
-
-prop_uuCombined ws = ws == (fromJust $ Uu.decode $ Uu.unchop $ Uu.chop 45 $ Uu.encode ws)
-    where types = ws::[Word8]
-
--- {{{1 base85 properties
-prop_base85Encode ws = ws == (fromJust $ Base85.decode $ Base85.encode ws)
-    where types = ws::[Word8]
-
-prop_base85Chop s = s == (Base85.unchop $ Base85.chop 20 s)
-    where types = s::String
-
--- {{{1 base64 properties
-prop_base64Encode ws = ws == (fromJust $ Base64.decode $ Base64.encode ws)
-    where types = ws::[Word8]
-
-prop_base64Chop s = s == (Base64.unchop $ Base64.chop 4 s)
-    where types = s::String
-
--- {{{1 base64url properties
-prop_base64UrlEncode ws = ws == (fromJust $ Base64Url.decode $ Base64Url.encode ws)
-    where types = ws::[Word8]
-
-prop_base64UrlChop s = s == (Base64Url.unchop $ Base64Url.chop 64 s)
-    where types = s::String
-
--- {{{1 base32
-prop_base32Encode ws = ws == (fromJust $ Base32.decode $ Base32.encode ws)
-    where types = ws::[Word8]
-
-prop_base32Chop s = s == (Base32.unchop $ Base32.chop 8 s)
-    where types = s::String
-
--- {{{1 base32hex
-prop_base32HexEncode ws = ws == (fromJust $ Base32Hex.decode $ Base32Hex.encode ws)
-    where types = ws::[Word8]
-
-prop_base32HexChop s = s == (Base32Hex.unchop $ Base32Hex.chop 64 s)
-    where types = s::String
-
--- {{{1 base16
-prop_base16Encode ws = ws == (fromJust $ Base16.decode $ Base16.encode ws)
-    where types = ws::[Word8]
-
-prop_base16Chop s = s == (Base16.unchop $ Base16.chop 6 s)
-    where types = s::String
-
--- {{{1 yEncoding
-prop_yencEncode ws = ws == (fromJust $ Yenc.decode $ Yenc.encode ws)
-    where types = ws ::[Word8]
-
-prop_yencChop ws = ws == (Yenc.unchop $ Yenc.chop 6 ws)
-    where types = ws :: [Word8]
-
--- {{{1 main
-main = do
-    quickCheck prop_uuEncode
-    quickCheck prop_uuChop
-    quickCheck prop_uuCombined
-    quickCheck prop_base85Encode
-    quickCheck prop_base85Chop
-    quickCheck prop_base64Encode
-    quickCheck prop_base64Chop
-    quickCheck prop_base64UrlEncode
-    quickCheck prop_base64UrlChop
-    quickCheck prop_base32Encode
-    quickCheck prop_base32Chop
-    quickCheck prop_base32HexEncode
-    quickCheck prop_base32HexChop
-    quickCheck prop_base16Encode
-    quickCheck prop_base16Chop
-    quickCheck prop_yencEncode
-    quickCheck prop_yencChop
diff --git a/Test.hs b/Test.hs
new file mode 100644
--- /dev/null
+++ b/Test.hs
@@ -0,0 +1,19 @@
+{-
+ - Copyright : (c) 2009 Magnus Therning
+ - License   : BSD3
+ -}
+
+module Main
+    where
+
+import Test.Framework
+
+import qualified DataencQC as DQC
+import qualified DataencUT as DUT
+
+tests =
+    [ testGroup "quickcheck" DQC.allTests
+    , testGroup "unit test" DUT.allTests
+    ]
+
+main = defaultMain tests
diff --git a/UT.hs b/UT.hs
deleted file mode 100644
--- a/UT.hs
+++ /dev/null
@@ -1,186 +0,0 @@
-{-
- - Copyright : (c) 2007 Magnus Therning
- - License   : BSD3
- -}
-
-module Main
-    where
-
-import Test.HUnit
-import Control.Monad
-import System.Exit
-import Data.Maybe
-
-import Codec.Binary.DataEncoding
-import qualified Codec.Binary.Yenc as Yenc
-
--- {{{1 buildTestList
--- builds a list of successful tests based on a tuple (suite, description,
--- encoded data, decoded data, codec)
-buildTestList td = TestList $ concat
-    [[ TestLabel (suite ++ " encode " ++ desc) (enc ~=? (encode codec dec))
-     , TestLabel (suite ++ " decode " ++ desc) (dec ~=? (fromJust $ decode codec $ enc))] 
-        | (suite, desc, enc, dec, codec) <- td ]
-
--- {{{1 uuencode tests
-uuTestData =
-    [ ("uu", "empty", "", [], uu)
-    , ("uu", "\0", "``", [0], uu)
-    , ("uu", "\255", "_P", [255], uu)
-    , ("uu", "Example", "17AA;7!L90", [69,120,97,109,112,108,101], uu)
-    ]
-uuTests = buildTestList uuTestData
-
-uuTests2 = test
-    [ "chop . encode Example" ~: ["'17AA;7!L90"] ~=? (chop uu 61 . encode uu) [69,120,97,109,112,108,101]
-    , "decode . unchop Example" ~: [69,120,97,109,112,108,101] ~=? fromJust ((decode uu . unchop uu) ["'17AA;7!L90"])
-    ]
-
-uuTestsFail = test
-    [ "uu decode short" ~: Nothing ~=? decode uu "A"
-    , "uu decode' short" ~: [Nothing] ~=? decode' uu "A"
-    , "uu decode illegal" ~: Nothing ~=? decode uu "aa"
-    , "uu decode' illegal" ~: [Nothing] ~=? decode' uu "aa"
-    ]
-
--- {{{1 base85 tests
-base85TestData =
-    [ ("base85", "empty", "", [], base85)
-    , ("base85", "f", "Ac", [102], base85)
-    , ("base85", "fo", "Ao@", [102,111], base85)
-    , ("base85", "foo", "AoDS", [102,111,111], base85)
-    , ("base85", "foob", "AoDTs", [102,111,111,98], base85)
-    , ("base85", "fooba", "AoDTs@/", [102,111,111,98,97], base85)
-    , ("base85", "foobar", "AoDTs@<)", [102,111,111,98,97,114], base85)
-    , ("base85", "\0", "!!", [0], base85)
-    , ("base85", "foob\0\0\0\0ar", "AoDTszEW", [102,111,111,98,0,0,0,0,114], base85)
-    , ("base85", "Example", "7<i6XE,9(", [69,120,97,109,112,108,101], base85)
-    ]
-base85Tests = buildTestList base85TestData
-
-base85TestsFail = test
-    [ "base85 decode short" ~: Nothing ~=? decode base85 "A"
-    , "base85 decode' short" ~: [Nothing] ~=? decode' base85 "A"
-    , "base85 decode illegal" ~: Nothing ~=? decode base85 "!z"
-    , "base85 decode' illegal" ~: [Nothing] ~=? decode' base85 "!z"
-    ]
-
--- {{{1 base64 tests
-base64TestData =
-    [ ("base64", "empty", "", [], base64)
-    , ("base64", "f", "Zg==", [102], base64)
-    , ("base64", "fo", "Zm8=", [102,111], base64)
-    , ("base64", "foo", "Zm9v", [102,111,111], base64)
-    , ("base64", "foob", "Zm9vYg==", [102,111,111,98], base64)
-    , ("base64", "fooba", "Zm9vYmE=", [102,111,111,98,97], base64)
-    , ("base64", "foobar", "Zm9vYmFy", [102,111,111,98,97,114], base64)
-    , ("base64", "\0", "AA==", [0], base64)
-    , ("base64", "\255", "/w==", [255], base64)
-    , ("base64", "Example", "RXhhbXBsZQ==", [69,120,97,109,112,108,101], base64)
-    ]
-base64Tests = buildTestList base64TestData
-
-base64TestsFail = test
-    [ "base64 decode short" ~: Nothing ~=? decode base64 "A"
-    , "base64 decode' short" ~: [Nothing] ~=? decode' base64 "A"
-    , "base64 decode illegal" ~: Nothing ~=? decode base64 "!!"
-    , "base64 decode' illegal" ~: [Nothing] ~=? decode' base64 "!!"
-    ]
-
--- {{{1 base64url tests
-base64UrlTestData =
-    [ ("base64url", "empty", "", [], base64Url)
-    , ("base64url", "\0", "AA==", [0], base64Url)
-    , ("base64url", "\255", "_w==", [255], base64Url)
-    , ("base64url", "Example", "RXhhbXBsZQ==", [69,120,97,109,112,108,101], base64Url)
-    ]
-base64UrlTests = buildTestList base64UrlTestData
-
--- {{{1 base32 tests
-base32TestData =
-    [ ("base32", "empty", "", [], base32)
-    , ("base32", "f", "MY======", [102], base32)
-    , ("base32", "fo", "MZXQ====", [102,111], base32)
-    , ("base32", "foo", "MZXW6===", [102,111,111], base32)
-    , ("base32", "foob", "MZXW6YQ=", [102,111,111,98], base32)
-    , ("base32", "fooba", "MZXW6YTB", [102,111,111,98,97], base32)
-    , ("base32", "foobar", "MZXW6YTBOI======", [102,111,111,98,97,114], base32)
-    ]
-base32Tests = buildTestList base32TestData
-
-base32TestsFail = test
-    [ "base32 decode short" ~: Nothing ~=? decode base32 "A"
-    , "base32 decode' short" ~: [Nothing] ~=? decode' base32 "A"
-    , "base32 decode illegal" ~: Nothing ~=? decode base32 "gh"
-    , "base32 decode' illegal" ~: [Nothing] ~=? decode' base32 "gh"
-    ]
-
--- {{{1 base32hex tests
-base32HexTestData =
-    [ ("base32hex", "empty", "", [], base32Hex)
-    , ("base32hex", "f", "CO======", [102], base32Hex)
-    , ("base32hex", "fo", "CPNG====", [102,111], base32Hex)
-    , ("base32hex", "foo", "CPNMU===", [102,111,111], base32Hex)
-    , ("base32hex", "foob", "CPNMUOG=", [102,111,111,98], base32Hex)
-    , ("base32hex", "fooba", "CPNMUOJ1", [102,111,111,98,97], base32Hex)
-    , ("base32hex", "foobar", "CPNMUOJ1E8======", [102,111,111,98,97,114], base32Hex)
-    ]
-base32HexTests = buildTestList base32HexTestData
-
-base32HexTestsFail = test
-    [ "base32hex decode short" ~: Nothing ~=? decode base32Hex "A"
-    , "base32hex decode' short" ~: [Nothing] ~=? decode' base32Hex "A"
-    , "base32hex decode illegal" ~: Nothing ~=? decode base32Hex "gh"
-    , "base32hex decode' illegal" ~: [Nothing] ~=? decode' base32Hex "gh"
-    ]
-
--- {{{1 base16 (hex)
-base16TestData =
-    [ ("base16", "empty", "", [], base16)
-    , ("base16", "f", "66", [102], base16)
-    , ("base16", "fo", "666F", [102,111], base16)
-    , ("base16", "foo", "666F6F", [102,111,111], base16)
-    , ("base16", "foob", "666F6F62", [102,111,111,98], base16)
-    , ("base16", "fooba", "666F6F6261", [102,111,111,98,97], base16)
-    , ("base16", "foobar", "666F6F626172", [102,111,111,98,97,114], base16)
-    ]
-base16Tests = buildTestList base16TestData
-
-base16TestsFail = test
-    [ "base16 decode short" ~: Nothing ~=? decode base16 "A"
-    , "base16 decode' short" ~: [Nothing] ~=? decode' base16 "A"
-    , "base16 decode illegal" ~: Nothing ~=? decode base16 "GH"
-    , "base16 decode' illegal" ~: [Nothing] ~=? decode' base16 "GH"
-    ]
-
--- {{{1 yEncoding
-yencTests = test
-    [ "yEnc encode empty" ~: [] ~=? Yenc.encode []
-    , "yEnc decode empty" ~: Just [] ~=? Yenc.decode []
-    , "yEnc encode 'f'" ~: [0x90] ~=? Yenc.encode [0x66]
-    , "yEnc decode enc('f')" ~: Just [0x66] ~=? Yenc.decode [0x90]
-    , "yEnc encode 'foobar'" ~: [0x90, 0x99, 0x99, 0x8c, 0x8b, 0x9c] ~=? Yenc.encode [0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72]
-    , "yEnc decode enc('foobar')" ~: Just [0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72] ~=? Yenc.decode [0x90, 0x99, 0x99, 0x8c, 0x8b, 0x9c]
-    , "yEnc encode [0xd6, 0xd7]" ~: [0x3d, 0x40, 0x01] ~=? Yenc.encode [0xd6, 0xd7]
-    , "yEnc decode enc([0xd6, 0xd7])" ~:  Just [0xd6, 0xd7] ~=? Yenc.decode [0x3d, 0x40, 0x01]
-    , "yEnc encode criticals" ~: [0x3d, 0x40, 0x3d, 0x4a, 0x3d, 0x4d, 0x3d, 0x7d] ~=? Yenc.encode [0xd6, 0xe0, 0xe3, 0x13]
-    , "yEnc decode criticals" ~:  Just [0xd6, 0xe0, 0xe3, 0x13] ~=? Yenc.decode [0x3d, 0x40, 0x3d, 0x4a, 0x3d, 0x4d, 0x3d, 0x7d]
-    , "yEnc chop" ~: [[0x3d, 0x40], [0x01, 0x3d, 0x4a]] ~=? Yenc.chop 2 [0x3d, 0x40, 0x01, 0x3d, 0x4a]
-    ]
-
--- {{{1 test list and main
-testList = TestList
-    [ uuTests, uuTests2, uuTestsFail
-    , base85Tests, base85TestsFail
-    , base64Tests, base64TestsFail
-    , base64UrlTests
-    , base32Tests, base32TestsFail
-    , base32HexTests, base32HexTestsFail
-    , base16Tests,  base16TestsFail
-    , yencTests
-    ]
-
-main :: IO ()
-main = do
-    counts <- runTestTT testList
-    when ((errors counts, failures counts) /= (0, 0)) $ exitWith (ExitFailure 1)
diff --git a/dataenc.cabal b/dataenc.cabal
--- a/dataenc.cabal
+++ b/dataenc.cabal
@@ -1,5 +1,5 @@
 name: dataenc
-version: 0.12
+version: 0.12.1.0
 license: BSD3
 license-file: LICENSE
 cabal-version: >= 1.2
@@ -7,30 +7,30 @@
 author: Magnus Therning
 maintainer: magnus@therning.org
 homepage: http://www.haskell.org/haskellwiki/Library/Data_encoding
-copyright: Magnus Therning, 2007
+copyright: Magnus Therning, 2007-2009
 category: Codec
 synopsis: Data encoding library
 description: Data encoding library currently providing Uuencode, Base64,
         Base64Url, Base32, Base32Hex, Base16, Base85, and yEncoding.
-extra-source-files: UT.hs QC.hs
-
-flag large_base
-        description: Choose the old larger, monolithic base package.
-        default: False
+extra-source-files: DataencUT.hs DataencQC.hs Test.hs GNUmakefile
 
 library
         hs-source-dirs: src
-        if flag(large_base)
-                build-depends: base
-        else
-                build-depends: array, base, containers
+        build-depends: array >= 0.2.0 && < 0.3, base >= 4.0.0 && < 4.1, containers >= 0.2.0 && < 0.3
         exposed-modules:
-                Codec.Binary.DataEncoding
                 Codec.Binary.Base16
-                Codec.Binary.Base32Hex
                 Codec.Binary.Base32
-                Codec.Binary.Uu
-                Codec.Binary.Base64Url
+                Codec.Binary.Base32Hex
                 Codec.Binary.Base64
+                Codec.Binary.Base64Url
                 Codec.Binary.Base85
+                Codec.Binary.DataEncoding
+                Codec.Binary.Hexadecimal
+                Codec.Binary.PythonString
+                Codec.Binary.QuotedPrintable
+                Codec.Binary.Url
+                Codec.Binary.Uu
+                Codec.Binary.Xx
                 Codec.Binary.Yenc
+        other-modules:
+                Codec.Binary.Util
diff --git a/src/Codec/Binary/Base85.hs b/src/Codec/Binary/Base85.hs
--- a/src/Codec/Binary/Base85.hs
+++ b/src/Codec/Binary/Base85.hs
@@ -47,12 +47,12 @@
 encode (0:0:0:0:bs) = "z" ++ encode bs
 encode (b1:b2:b3:b4:bs) = (foldr (\ i s -> (encodeArray ! i) : s) "" group) ++ (encode bs)
     where
-        group2Int :: Int
-        group2Int = foldl (\ a b -> a `shiftL` 8 + (fromIntegral b)) 0 [b1, b2, b3, b4]
-        encodeInt2Ints :: Int -> [Word8]
-        encodeInt2Ints = map fromIntegral . map (`mod` 85) . take 5 . iterate (`div` 85)
+        group2Word32 :: Word32
+        group2Word32 = foldl (\ a b -> a `shiftL` 8 + (fromIntegral b)) 0 [b1, b2, b3, b4]
+        encodeWord32ToWord8s :: Word32 -> [Word8]
+        encodeWord32ToWord8s = map fromIntegral . map (`mod` 85) . take 5 . iterate (`div` 85)
         adjustNReverse = reverse . map (+ 33)
-        group = (adjustNReverse .encodeInt2Ints) group2Int
+        group = (adjustNReverse .encodeWord32ToWord8s) group2Word32
 
 -- {{{1 decode
 -- | Decode data (lazy).
@@ -70,11 +70,11 @@
         dec l@[Just c1, Just c2, Just c3, Just c4] = take 3 . dec $ l ++ pad 1
         dec l@[Just c1, Just c2, Just c3, Just c4, Just c5] = let
                 adjRev = map (flip (-) 33) [c5, c4, c3, c2, c1]
-                group2Int :: [Word8] -> Int
-                group2Int = foldl1 (+) . map (uncurry (*)) . zip (map (85 ^) [0..4]) . map fromIntegral
-                int2Group :: Int -> [Maybe Word8]
-                int2Group = map Just . map fromIntegral . reverse . take 4 . iterate (`div` 256)
-            in (int2Group . group2Int) adjRev
+                group2Word32 :: [Word8] -> Word32
+                group2Word32 = foldl1 (+) . map (uncurry (*)) . zip (map (85 ^) [0..4]) . map fromIntegral
+                word32ToGroup :: Word32 -> [Maybe Word8]
+                word32ToGroup = map Just . map fromIntegral . reverse . take 4 . iterate (`div` 256)
+            in (word32ToGroup . group2Word32) adjRev
         dec _ = [Nothing]
     in (dec . map (flip M.lookup decodeMap) $ take 5 cs) ++ decode' (drop 5 cs)
 
diff --git a/src/Codec/Binary/DataEncoding.hs b/src/Codec/Binary/DataEncoding.hs
--- a/src/Codec/Binary/DataEncoding.hs
+++ b/src/Codec/Binary/DataEncoding.hs
@@ -11,18 +11,23 @@
 -- <http://www.haskell.org/haskellwiki/Library/Data_encoding>.
 module Codec.Binary.DataEncoding
     ( DataCodec
-    , encode
-    , decode
-    , decode'
-    , chop
-    , unchop
     , base16
     , base32
     , base32Hex
     , base64
     , base64Url
     , base85
+    , chop
+    , decode
+    , decode'
+    , encode
+    , hex
+    , py
+    , qp
+    , unchop
+    , url
     , uu
+    , xx
     )
     where
 
@@ -34,7 +39,12 @@
 import qualified Codec.Binary.Base64 as Base64
 import qualified Codec.Binary.Base64Url as Base64Url
 import qualified Codec.Binary.Base85 as Base85
+import qualified Codec.Binary.Url as Url
 import qualified Codec.Binary.Uu as Uu
+import qualified Codec.Binary.Xx as Xx
+import qualified Codec.Binary.Hexadecimal as Hex
+import qualified Codec.Binary.QuotedPrintable as QP
+import qualified Codec.Binary.PythonString as Py
 
 -- {{{1 DataCodec
 -- | Used to group a specific data encoding's functions.
@@ -129,3 +139,63 @@
     chop=Uu.chop,
     unchop=Uu.unchop
 }
+
+-- {{{1 xx
+-- | Xxencoding, see "Codec.Binary.Xx" for more details on the
+--   individual functions.
+xx :: DataCodec
+xx = DataCodec {
+    encode=Xx.encode,
+    decode=Xx.decode,
+    decode'=Xx.decode',
+    chop=Xx.chop,
+    unchop=Xx.unchop
+}
+
+-- {{{1 hexadecimal
+-- | Hexadecimal, see "Codec.Binary.Hexadecimal" for more details on the
+-- individual functions.
+hex :: DataCodec
+hex = DataCodec
+    { encode  = Hex.encode
+    , decode  = Hex.decode
+    , decode' = Hex.decode'
+    , chop    = Hex.chop
+    , unchop  = Hex.unchop
+    }
+
+-- {{{1 quoted-printable
+-- | Quoted-printable, see "Codec.Binary.QuotedPrintable" for more details on
+-- the individual functions.
+qp :: DataCodec
+qp = DataCodec
+    { encode  = QP.encode
+    , decode  = QP.decode
+    , decode' = QP.decode'
+    , chop    = QP.chop
+    , unchop  = QP.unchop
+    }
+
+-- {{{1 python string
+-- | Quoted-printable, see "Codec.Binary.PythonString" for more details on
+-- the individual functions.
+py :: DataCodec
+py = DataCodec
+    { encode  = Py.encode
+    , decode  = Py.decode
+    , decode' = Py.decode'
+    , chop    = Py.chop
+    , unchop  = Py.unchop
+    }
+
+-- {{{1 url encoding
+-- | URL encoding, see "Codec.Binary.Url" for more details on the individual
+-- functions.
+url :: DataCodec
+url = DataCodec
+    { encode  = Url.encode
+    , decode  = Url.decode
+    , decode' = Url.decode'
+    , chop    = Url.chop
+    , unchop  = Url.unchop
+    }
diff --git a/src/Codec/Binary/Hexadecimal.hs b/src/Codec/Binary/Hexadecimal.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Hexadecimal.hs
@@ -0,0 +1,56 @@
+-- |
+-- Module    : Codec.Binary.Hexadecimal
+-- Copyright : (c) 2009 Magnus Therning
+-- License   : BSD3
+--
+-- Simple hexadecimal encoding.
+--
+-- Further documentation and information can be found at
+-- <http://www.haskell.org/haskellwiki/Library/Data_encoding>.
+module Codec.Binary.Hexadecimal
+    ( encode
+    , decode
+    , decode'
+    , chop
+    , unchop
+    ) where
+
+import Data.Word
+
+import Codec.Binary.Util
+
+-- {{{1 encode
+-- | Encode data.  Each nibble of the byte is converted into one character in
+-- [0-9A-F].
+encode :: [Word8]
+    -> String
+encode [] = ""
+encode (o:os) = toHex o ++ encode os
+
+-- {{{1 decode
+-- | Decode data (lazy).
+decode' :: String
+    -> [Maybe Word8]
+decode' "" = []
+decode' (hn:ln:cs) = fromHex [hn, ln] : decode' cs
+decode' _ = [Nothing]
+
+-- | Decode data (strict).
+decode :: String -> Maybe [Word8]
+decode = sequence . decode'
+
+-- {{{1 chop
+-- | Chop up a string in parts.
+chop :: Int     -- ^ length of individual lines
+    -> String
+    -> [String]
+chop n "" = []
+chop n s = let
+        enc_len | n < 2 = 2
+                | otherwise = n `div` 2 * 2
+    in (take enc_len s) : chop n (drop enc_len s)
+
+-- {{{1 unchop
+-- | Concatenate the list of strings into one long string.
+unchop :: [String] -> String
+unchop = foldr (++) ""
diff --git a/src/Codec/Binary/PythonString.hs b/src/Codec/Binary/PythonString.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/PythonString.hs
@@ -0,0 +1,86 @@
+-- |
+-- Module    : Codec.Binary.PythonString
+-- Copyright : (c) 2009 Magnus Therning
+-- License   : BSD3
+--
+-- Implementation of python escaping.
+--
+-- This implementation encodes non-printable characters (0x00-0x1f, 0x7f-0xff)
+-- to hex-value characters ('\xhh') while leaving printable characters as such:
+--
+-- @
+-- \> encode [0, 10, 13, 110]
+-- \"\\\\x00\\\\x0A\\\\x0Dn\"
+-- \> putStrLn $ encode [0, 10, 13, 110]
+-- \\x00\\x0A\\x0Dn
+-- @
+--
+-- It also properly handles escaping of a few characters that require it:
+--
+-- @
+-- \> encode [34, 39, 92]
+-- \"\\\\\\\"\\\\\'\\\\\\\\\"
+-- putStrLn $ encode [34, 39, 92]
+-- \\\"\\'\\\\
+-- @
+--
+-- Further documentation and information can be found at
+-- <http://www.haskell.org/haskellwiki/Library/Data_encoding>.
+module Codec.Binary.PythonString
+    ( encode
+    , decode
+    , decode'
+    , chop
+    , unchop
+    ) where
+
+import Data.Char
+import Data.Word
+
+import Codec.Binary.Util
+
+-- {{{1 encode
+-- | Encode data.
+encode :: [Word8]
+    -> String
+encode [] = ""
+encode (o:os)
+    | o < 0x20 || o > 0x7e = ('\\' : 'x' : toHex o) ++ encode os
+    | o == 34 = "\\\"" ++ encode os
+    | o == 39 = "\\'" ++ encode os
+    | o == 92 = "\\\\" ++ encode os
+    | otherwise = chr (fromIntegral o) : encode os
+
+-- {{{1 decode
+-- -- | Decode data (lazy).
+decode' :: String
+    -> [Maybe Word8]
+decode' [] = []
+decode' ('\\':'x':c0:c1:cs) = fromHex [c0, c1] : decode' cs
+decode' ('\\':'\\':cs) = (Just $ fromIntegral $ ord '\\') : decode' cs
+decode' ('\\':'\'':cs) = (Just $ fromIntegral $ ord '\'') : decode' cs
+decode' ('\\':'\"':cs) = (Just $ fromIntegral $ ord '\"') : decode' cs
+decode' (c:cs)
+    | c /= '\\' = (Just $ fromIntegral $ ord c) : decode' cs
+    | otherwise = [Nothing]
+
+-- | Decode data (strict).
+decode :: String -> Maybe [Word8]
+decode = sequence . decode'
+
+-- {{{1 chop
+-- | Chop up a string in parts.
+chop :: Int     -- ^ length of individual lines (values @\< 1@ are ignored)
+    -> String
+    -> [String]
+chop n = let
+        _n = max 1 n
+        _chop [] = []
+        _chop cs = take _n cs : (_chop $ drop _n cs)
+    in _chop
+
+-- {{{1 unchop
+-- | Concatenate the list of strings into one long string.
+unchop :: [String]
+    -> String
+unchop = foldr (++) ""
diff --git a/src/Codec/Binary/QuotedPrintable.hs b/src/Codec/Binary/QuotedPrintable.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/QuotedPrintable.hs
@@ -0,0 +1,79 @@
+-- |
+-- Module    : Codec.Binary.QuotedPrintable
+-- Copyright : (c) 2009 Magnus Therning
+-- License   : BSD3
+--
+-- Implementation of Quoted-Printable based on RFC 2045
+-- (<http://tools.ietf.org/html/rfc2045>).
+--
+-- This encoding encodes _everything_ that is passed in, it will not try to
+-- guess the native line ending for your architecture.  In other words, if you
+-- are using this to encode text you need to split it into separate lines
+-- before encoding and chopping it up.
+--
+-- Further documentation and information can be found at
+-- <http://www.haskell.org/haskellwiki/Library/Data_encoding>.
+module Codec.Binary.QuotedPrintable
+    ( encode
+    , decode
+    , decode'
+    , chop
+    , unchop
+    ) where
+
+import Data.Char
+import Data.Word
+
+import Codec.Binary.Util
+
+-- {{{1 encode
+-- | Encode data.
+encode :: [Word8]
+    -> String
+encode [] = ""
+encode (o:os)
+    | o < 33 || o == 61 || o > 126 = ('=' : toHex o) ++ encode os
+    | otherwise = chr (fromIntegral o) : encode os
+
+
+-- {{{1 decode
+-- -- | Decode data (lazy).
+decode' :: String
+    -> [Maybe Word8]
+decode' [] = []
+decode' ('=':c0:c1:cs) = fromHex [c0, c1] : decode' cs
+decode' (c:cs)
+    | c /= '=' = (Just $ fromIntegral $ ord c) : decode' cs
+    | otherwise = [Nothing]
+
+-- | Decode data (strict).
+decode :: String -> Maybe [Word8]
+decode = sequence . decode'
+
+-- {{{1 chop
+-- | Chop up a string in parts.
+chop :: Int     -- ^ length of individual lines (values @\< 4@ are ignored)
+    -> String
+    -> [String]
+chop n "" = []
+chop n s = let
+        n' = max 3 $ n - 1
+        _c i ts "" acc = ts : acc
+        _c i ts tss@('=':tss') acc
+            | i + 2 < n' = _c (i + 1) ('=' : ts) tss' acc
+            | otherwise = _c 0 "" tss (('=' : ts) : acc)
+        _c i ts tss@(c:tss') acc
+            | i < n' = _c (i + 1) (c : ts) tss' acc
+            | otherwise = _c 0 "" tss (('=' : ts) : acc)
+    in map reverse . reverse $ _c 0 "" s []
+
+-- {{{1 unchop
+-- | Concatenate the list of strings into one long string.
+unchop :: [String] -> String
+unchop [] = ""
+unchop (s:ss) = let
+        dropLast = last s == '='
+        len = length s
+    in if dropLast
+        then (take (len - 1) s) ++ (unchop ss)
+        else s ++ (unchop ss)
diff --git a/src/Codec/Binary/Url.hs b/src/Codec/Binary/Url.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Url.hs
@@ -0,0 +1,76 @@
+-- |
+-- Module    : Codec.Binary.Url
+-- Copyright : (c) 2009 Magnus Therning
+-- License   : BSD3
+--
+-- URL encoding, sometimes referred to as URI encoding or percent encoding.
+-- Implemented based on RFC 3986 (<http://tools.ietf.org/html/rfc3986>).
+--
+-- Further documentation and information can be found at
+-- <http://www.haskell.org/haskellwiki/Library/Data_encoding>.
+
+module Codec.Binary.Url
+    ( encode
+    , decode
+    , decode'
+    , chop
+    , unchop
+    ) where
+
+import qualified Data.Map as M
+import Data.Char(ord)
+import Data.Word(Word8)
+
+import Codec.Binary.Util(toHex,fromHex)
+
+_unreservedChars = (zip [65..90] "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+        ++ (zip [97..122] "abcdefghijklmnopqrstuvwxyz")
+        ++ (zip [48..57] "0123456789")
+        ++ [(45, '-'), (95, '_'), (46, '.'), (126, '~')]
+
+encodeMap :: M.Map Word8 Char
+encodeMap = M.fromList _unreservedChars
+
+decodeMap :: M.Map Char Word8
+decodeMap = M.fromList [(b, a) | (a, b) <- _unreservedChars]
+
+-- {{{1 encode
+-- | Encode data.
+encode :: [Word8]
+    -> String
+encode [] = ""
+encode (o:os) = case (M.lookup o encodeMap) of
+    Just c -> c : encode os
+    Nothing -> ('%' : toHex o) ++ encode os
+
+-- {{{1 decode
+-- | Decode data (lazy).
+decode' :: String
+    -> [Maybe Word8]
+decode' [] = []
+decode' ('%':c0:c1:cs) = fromHex [c0, c1] : decode' cs
+decode' (c:cs)
+    | c /= '%' = (Just $ fromIntegral $ ord c) : decode' cs
+    | otherwise = [Nothing]
+
+-- | Decode data (strict).
+decode :: String
+    -> Maybe [Word8]
+decode = sequence . decode'
+
+-- {{{1 chop
+-- | Chop up a string in parts.
+chop :: Int     -- ^ length of individual lines
+    -> String
+    -> [String]
+chop n = let
+        _n = max 1 n
+        _chop [] = []
+        _chop cs = take _n cs : (_chop $ drop _n cs)
+    in _chop
+
+-- {{{1 unchop
+-- | Concatenate the strings into one long string
+unchop :: [String]
+    -> String
+unchop = foldr (++) ""
diff --git a/src/Codec/Binary/Util.hs b/src/Codec/Binary/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Util.hs
@@ -0,0 +1,41 @@
+-- |
+-- Module    : Codec.Binary.Util
+-- Copyright : (c) 2009 Magnus Therning
+-- License   : BSD3
+--
+-- Utility functions used in the other module.
+module Codec.Binary.Util
+    ( toHex
+    , fromHex
+    ) where
+
+import Data.Array
+import Data.Bits
+import Data.Char
+import Data.Word
+import qualified Data.Map as M
+
+-- {{{1 hex enc/dec assoc list and maps
+hexEncMap = zip [0..] "0123456789ABCDEF"
+
+hexEncodeArray :: Array Word8 Char
+hexEncodeArray = array (0, 16) hexEncMap
+
+hexDecodeMap :: M.Map Char Word8
+hexDecodeMap = M.fromList [(b, a) | (a, b) <- hexEncMap]
+
+-- {{{1 toHex
+toHex :: Word8 -> String
+toHex o = let
+        hn = o `shiftR` 4
+        ln = o .&. 0xf
+    in [hexEncodeArray ! hn, hexEncodeArray ! ln]
+
+-- {{{1 fromHex
+fromHex :: String -> Maybe Word8
+fromHex = let
+        dec [Just hn, Just ln] = let
+                o = hn `shiftL` 4 .|. ln
+            in Just o
+        dec _ = Nothing
+    in dec . map ((flip M.lookup hexDecodeMap) . toUpper)
diff --git a/src/Codec/Binary/Uu.hs b/src/Codec/Binary/Uu.hs
--- a/src/Codec/Binary/Uu.hs
+++ b/src/Codec/Binary/Uu.hs
@@ -25,20 +25,7 @@
 import qualified Data.Map as M
 
 -- {{{1 enc/dec map
-_encMap =
-    [ (0, '`'), (1, '!'), (2, '"'), (3, '#'), (4, '$')
-    , (5, '%'), (6, '&'), (7, '\''), (8, '('), (9, ')')
-    , (10, '*'), (11, '+'), (12, ','), (13, '-'), (14, '.')
-    , (15, '/'), (16, '0'), (17, '1'), (18, '2'), (19, '3')
-    , (20, '4'), (21, '5'), (22, '6'), (23, '7'), (24, '8')
-    , (25, '9'), (26, ':'), (27, ';'), (28, '<'), (29, '=')
-    , (30, '>'), (31, '?'), (32, '@'), (33, 'A'), (34, 'B')
-    , (35, 'C'), (36, 'D'), (37, 'E'), (38, 'F'), (39, 'G')
-    , (40, 'H'), (41, 'I'), (42, 'J'), (43, 'K'), (44, 'L')
-    , (45, 'M'), (46, 'N'), (47, 'O'), (48, 'P'), (49, 'Q')
-    , (50, 'R'), (51, 'S'), (52, 'T'), (53, 'U') ,(54, 'V')
-    , (55, 'W'), (56, 'X'), (57, 'Y'), (58, 'Z'), (59, '[')
-    , (60, '\\'), (61, ']'), (62, '^'), (63, '_') ]
+_encMap = zip [0..] "`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
 
 -- {{{1 encodeArray
 encodeArray :: Array Word8 Char
@@ -94,18 +81,19 @@
 --
 --   /Notes:/
 --
---   * The length of the strings in the result will be @(n -1) `div` 4 * 4@.
---   The @-1@ comes from the need to prepend the length.  Keeping it to a
---   multiple of 4 means that strings returned from 'encode' can be chopped
---   without requiring any changes.
+--   * The length of the strings in the result will be @(n -1) `div` 4 * 4 +
+--   1@.  The @-1@ comes from the need to prepend the length (which explains
+--   the final @+1@).  Keeping it to a multiple of 4 means that strings
+--   returned from 'encode' can be chopped without requiring any changes.
 --
 --   * The length of lines in GNU's sharutils is 61.
-chop :: Int     -- ^ length (@1 < n <= 65@, not checked)
+chop :: Int     -- ^ length (value should be in the range @[5..85]@)
     -> String
     -> [String]
 chop n "" = []
 chop n s = let
-        enc_len | n < 5 = 4
+        enc_len | n < 5     = 4
+                | n >= 85    = 84
                 | otherwise = (n - 1) `div` 4 * 4
         enc_line = take enc_len s
         act_len = fromIntegral $ case (length enc_line `divMod` 4) of
diff --git a/src/Codec/Binary/Xx.hs b/src/Codec/Binary/Xx.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Binary/Xx.hs
@@ -0,0 +1,119 @@
+-- |
+-- Module    : Codec.Binary.Xx
+-- Copyright : (c) 2007 Magnus Therning
+-- License   : BSD3
+--
+-- Xxencoding is obsolete but still included for completeness.  Further
+-- information on the encoding can be found at
+-- <http://en.wikipedia.org/wiki/Xxencode>.  It should be noted that this
+-- implementation performs no padding, due to the splitting up between encoding
+-- and chopping.
+--
+-- Further documentation and information can be found at
+-- <http://www.haskell.org/haskellwiki/Library/Data_encoding>.
+module Codec.Binary.Xx
+    ( encode
+    , decode
+    , decode'
+    , chop
+    , unchop
+    ) where
+
+import Control.Monad
+import Data.Array
+import Data.Bits
+import Data.Maybe
+import Data.Word
+import qualified Data.Map as M
+
+-- {{{1 enc/dec map
+_encMap = zip [0..] "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+
+-- {{{1 encodeArray
+encodeArray :: Array Word8 Char
+encodeArray = array (0, 64) _encMap
+
+-- {{{1 decodeMap
+decodeMap :: M.Map Char Word8
+decodeMap = M.fromList [(snd i, fst i) | i <- _encMap]
+
+-- {{{1 encode
+-- | Encode data.
+encode :: [Word8]
+    -> String
+encode = let
+        pad n = take n $ repeat 0
+        enc [] = ""
+        enc l@[o] = take 2 . enc $ l ++ pad 2
+        enc l@[o1, o2] = take 3 . enc $ l ++ pad 1
+        enc (o1:o2:o3:os) = let
+                i1 = o1 `shiftR` 2
+                i2 = (o1 `shiftL` 4 .|. o2 `shiftR` 4) .&. 0x3f
+                i3 = (o2 `shiftL` 2 .|. o3 `shiftR` 6) .&. 0x3f
+                i4 = o3 .&. 0x3f
+            in (foldr (\ i s -> (encodeArray ! i) : s) "" [i1, i2, i3, i4]) ++ enc os
+    in enc
+
+-- {{{1 decode
+-- | Decode data (lazy).
+decode' :: String
+    -> [Maybe Word8]
+decode' = let
+        pad n = take n . repeat $ Just 0
+        dec [] = []
+        dec l@[Just eo1, Just eo2] = take 1 . dec $ l ++ pad 2
+        dec l@[Just eo1, Just eo2, Just eo3] = take 2 . dec $ l ++ pad 1
+        dec (Just eo1:Just eo2:Just eo3:Just eo4:eos) = let
+                o1 = eo1 `shiftL` 2 .|. eo2 `shiftR` 4
+                o2 = eo2 `shiftL` 4 .|. eo3 `shiftR` 2
+                o3 = eo3 `shiftL` 6 .|. eo4
+            in Just o1:Just o2:Just o3:(dec eos)
+        dec _ = [Nothing]
+    in
+        dec . map (flip M.lookup decodeMap)
+
+-- | Decode data (strict).
+decode :: String
+    -> Maybe [Word8]
+decode = sequence . decode'
+
+-- {{{1 chop
+-- | Chop up a string in parts.  Each string in the resulting list is prepended
+--   with the length according to the xxencode \"specificiation\".
+--
+--   /Notes:/
+--
+--   * The length of the strings in the result will be @(n -1) `div` 4 * 4 +
+--   1@.  The @-1@ comes from the need to prepend the length (which explains
+--   the final @+1@).  Keeping it to a multiple of 4 means that strings
+--   returned from 'encode' can be chopped without requiring any changes.
+chop :: Int     -- ^ length (value should be in the range @[5..85]@)
+    -> String
+    -> [String]
+chop n "" = []
+chop n s = let
+        enc_len | n < 5     = 4
+                | n >= 85   = 84
+                | otherwise = min 64 $ (n - 1) `div` 4 * 4
+        enc_line = take enc_len s
+        act_len = fromIntegral $ case (length enc_line `divMod` 4) of
+            (l, 0) -> l * 3
+            (l, 2) -> l * 3 + 1
+            (l, 3) -> l * 3 + 2
+        len = (encodeArray ! act_len)
+    in (len : enc_line) : chop n (drop enc_len s)
+
+-- {{{1 unchop
+-- | Concatenate the strings into one long string.  Each string is assumed to
+--   be prepended with the length according to the xxencode specification.
+unchop :: [String]
+    -> String
+unchop ss = let
+        singleUnchop (l:cs) = let
+                act_len = fromIntegral $ decodeMap M.! l
+                enc_len = case (act_len `divMod` 3) of
+                    (n, 0) -> n * 4
+                    (n, 1) -> n * 4 + 2
+                    (n, 2) -> n * 4 + 3
+            in take enc_len cs
+    in foldr (\ s res -> (singleUnchop s) ++ res) "" ss
diff --git a/src/Codec/Binary/Yenc.hs b/src/Codec/Binary/Yenc.hs
--- a/src/Codec/Binary/Yenc.hs
+++ b/src/Codec/Binary/Yenc.hs
@@ -50,11 +50,12 @@
     -> [[Word8]]
 chop _ [] = []
 chop n ws = let
-        (p1, p2) = splitAt n ws
+        _n = max n 1
+        (p1, p2) = splitAt _n ws
     in
         if (last p1 == _equal)
-            then (p1 ++ (take 1 p2)) : chop n (drop 1 p2)
-            else p1 : chop n p2
+            then (p1 ++ (take 1 p2)) : chop _n (drop 1 p2)
+            else p1 : chop _n p2
 
 -- {{{1 unchop
 -- | Concatenate the strings into one long string.
