-- |
-- 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
    ( EncIncData(..)
    , EncIncRes(..)
    , encodeInc
    , encode
    , DecIncData(..)
    , DecIncRes(..)
    , decodeInc
    , decode
    , chop
    , unchop
    ) where

import Codec.Binary.Util

import Data.Char
import Data.Maybe
import Data.Word

-- {{{1 encode
-- | Incremental encoder function.
encodeInc :: EncIncData -> EncIncRes String
encodeInc :: EncIncData -> EncIncRes String
encodeInc EncIncData
e = EncIncData -> EncIncRes String
eI EncIncData
e
    where
        enc :: [Word8] -> String
enc [] = []
        enc (Word8
o:[Word8]
os)
            | Word8
o Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
< Word8
0x20 Bool -> Bool -> Bool
|| Word8
o Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
> Word8
0x7e = (Char
'\\' Char -> String -> String
forall a. a -> [a] -> [a]
: Char
'x' Char -> String -> String
forall a. a -> [a] -> [a]
: Word8 -> String
toHex Word8
o) String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Word8] -> String
enc [Word8]
os
            | Word8
o Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
34 = String
"\\\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Word8] -> String
enc [Word8]
os
            | Word8
o Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
39 = String
"\\'" String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Word8] -> String
enc [Word8]
os
            | Word8
o Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
92 = String
"\\\\" String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Word8] -> String
enc [Word8]
os
            | Bool
otherwise = Int -> Char
chr (Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
o) Char -> String -> String
forall a. a -> [a] -> [a]
: [Word8] -> String
enc [Word8]
os

        eI :: EncIncData -> EncIncRes String
eI EncIncData
EDone = String -> EncIncRes String
forall i. i -> EncIncRes i
EFinal []
        eI (EChunk [Word8]
bs) = String -> (EncIncData -> EncIncRes String) -> EncIncRes String
forall i. i -> (EncIncData -> EncIncRes i) -> EncIncRes i
EPart ([Word8] -> String
enc [Word8]
bs) EncIncData -> EncIncRes String
encodeInc

-- | Encode data.
encode :: [Word8] -> String
encode :: [Word8] -> String
encode = (EncIncData -> EncIncRes String) -> [Word8] -> String
forall {a}. (EncIncData -> EncIncRes [a]) -> [Word8] -> [a]
encoder EncIncData -> EncIncRes String
encodeInc

-- {{{1 decode
-- | Incremental decoder function.
decodeInc :: DecIncData String -> DecIncRes String
decodeInc :: DecIncData String -> DecIncRes String
decodeInc DecIncData String
d = String -> DecIncData String -> DecIncRes String
dI [] DecIncData String
d
    where
        dI :: String -> DecIncData String -> DecIncRes String
dI [] DecIncData String
DDone = [Word8] -> String -> DecIncRes String
forall i. [Word8] -> i -> DecIncRes i
DFinal [] []
        dI String
lo DecIncData String
DDone = [Word8] -> String -> DecIncRes String
forall i. [Word8] -> i -> DecIncRes i
DFail [] String
lo
        dI String
lo (DChunk String
s) = [Word8] -> String -> DecIncRes String
doDec [] (String
lo String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
s)
            where
                doDec :: [Word8] -> String -> DecIncRes String
doDec [Word8]
acc [] = [Word8]
-> (DecIncData String -> DecIncRes String) -> DecIncRes String
forall i. [Word8] -> (DecIncData i -> DecIncRes i) -> DecIncRes i
DPart [Word8]
acc (String -> DecIncData String -> DecIncRes String
dI [])
                doDec [Word8]
acc s' :: String
s'@(Char
'\\':Char
'x':Char
c0:Char
c1:String
cs) = let
                        o :: Maybe Word8
o = String -> Maybe Word8
fromHex [Char
c0, Char
c1]
                    in if Maybe Word8 -> Bool
forall a. Maybe a -> Bool
isJust Maybe Word8
o
                        then [Word8] -> String -> DecIncRes String
doDec ([Word8]
acc [Word8] -> [Word8] -> [Word8]
forall a. [a] -> [a] -> [a]
++ [Maybe Word8 -> Word8
forall a. HasCallStack => Maybe a -> a
fromJust Maybe Word8
o]) String
cs
                        else [Word8] -> String -> DecIncRes String
forall i. [Word8] -> i -> DecIncRes i
DFail [Word8]
acc String
s'
                doDec [Word8]
acc s' :: String
s'@(Char
'\\':Char
'\\':String
cs) = [Word8] -> String -> DecIncRes String
doDec ([Word8]
acc [Word8] -> [Word8] -> [Word8]
forall a. [a] -> [a] -> [a]
++ [Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'\\']) String
cs
                doDec [Word8]
acc s' :: String
s'@(Char
'\\':Char
'\'':String
cs) = [Word8] -> String -> DecIncRes String
doDec ([Word8]
acc [Word8] -> [Word8] -> [Word8]
forall a. [a] -> [a] -> [a]
++ [Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'\'']) String
cs
                doDec [Word8]
acc s' :: String
s'@(Char
'\\':Char
'\"':String
cs) = [Word8] -> String -> DecIncRes String
doDec ([Word8]
acc [Word8] -> [Word8] -> [Word8]
forall a. [a] -> [a] -> [a]
++ [Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
'\"']) String
cs
                doDec [Word8]
acc s' :: String
s'@(Char
c:String
cs)
                    | Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\\' = [Word8] -> String -> DecIncRes String
doDec ([Word8]
acc [Word8] -> [Word8] -> [Word8]
forall a. [a] -> [a] -> [a]
++ [Int -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word8) -> Int -> Word8
forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
c]) String
cs
                    | Bool
otherwise = [Word8]
-> (DecIncData String -> DecIncRes String) -> DecIncRes String
forall i. [Word8] -> (DecIncData i -> DecIncRes i) -> DecIncRes i
DPart [Word8]
acc (String -> DecIncData String -> DecIncRes String
dI String
s')

-- | Decode data.
decode :: String -> Maybe [Word8]
decode :: String -> Maybe [Word8]
decode = (DecIncData String -> DecIncRes String) -> String -> Maybe [Word8]
forall i. (DecIncData i -> DecIncRes i) -> i -> Maybe [Word8]
decoder DecIncData String -> DecIncRes String
decodeInc

-- {{{1 chop
-- | Chop up a string in parts.
chop :: Int     -- ^ length of individual lines (values @\< 1@ are ignored)
    -> String
    -> [String]
chop :: Int -> String -> [String]
chop Int
n = let
        _n :: Int
_n = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
1 Int
n
        _chop :: [a] -> [[a]]
_chop [] = []
        _chop [a]
cs = Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
take Int
_n [a]
cs [a] -> [[a]] -> [[a]]
forall a. a -> [a] -> [a]
: [a] -> [[a]]
_chop (Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
drop Int
_n [a]
cs)
    in String -> [String]
forall {a}. [a] -> [[a]]
_chop

-- {{{1 unchop
-- | Concatenate the list of strings into one long string.
unchop :: [String]
    -> String
unchop :: [String] -> String
unchop = (String -> String -> String) -> String -> [String] -> String
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr String -> String -> String
forall a. [a] -> [a] -> [a]
(++) String
""