{-# LANGUAGE Safe #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
module System.FileArchive.GZip (
Header(..), Section, GZipError(..),
Footer(..),
decompress,
hDecompress,
read_sections,
read_header,
read_section
)
where
import Control.Monad.Except (MonadError(..))
import Data.Bits ((.&.))
import Data.Bits.Utils (fromBytes)
import Data.Char (ord)
import Data.Compression.Inflate (inflate_string_remainder)
import Data.Hash.CRC32.GZip (update_crc)
import Data.Word (Word32)
import System.IO (Handle, hGetContents, hPutStr)
data GZipError = CRCError
| NotGZIPFile
| UnknownMethod
| UnknownError String
deriving (GZipError -> GZipError -> Bool
(GZipError -> GZipError -> Bool)
-> (GZipError -> GZipError -> Bool) -> Eq GZipError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GZipError -> GZipError -> Bool
$c/= :: GZipError -> GZipError -> Bool
== :: GZipError -> GZipError -> Bool
$c== :: GZipError -> GZipError -> Bool
Eq, Int -> GZipError -> ShowS
[GZipError] -> ShowS
GZipError -> [Char]
(Int -> GZipError -> ShowS)
-> (GZipError -> [Char])
-> ([GZipError] -> ShowS)
-> Show GZipError
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
showList :: [GZipError] -> ShowS
$cshowList :: [GZipError] -> ShowS
show :: GZipError -> [Char]
$cshow :: GZipError -> [Char]
showsPrec :: Int -> GZipError -> ShowS
$cshowsPrec :: Int -> GZipError -> ShowS
Show)
magic :: String
magic :: [Char]
magic = [Char]
"\x1f\x8b"
fFHCRC, fFEXTRA, fFNAME, fFCOMMENT :: Int
fFHCRC :: Int
fFHCRC = Int
2
= Int
4
fFNAME :: Int
fFNAME = Int
8
= Int
16
data = {
Header -> Int
method :: Int,
Header -> Int
flags :: Int,
:: Maybe String,
Header -> Maybe [Char]
filename :: Maybe String,
:: Maybe String,
Header -> Word32
mtime :: Word32,
Header -> Int
xfl :: Int,
Header -> Int
os :: Int
} deriving (Header -> Header -> Bool
(Header -> Header -> Bool)
-> (Header -> Header -> Bool) -> Eq Header
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Header -> Header -> Bool
$c/= :: Header -> Header -> Bool
== :: Header -> Header -> Bool
$c== :: Header -> Header -> Bool
Eq, Int -> Header -> ShowS
[Header] -> ShowS
Header -> [Char]
(Int -> Header -> ShowS)
-> (Header -> [Char]) -> ([Header] -> ShowS) -> Show Header
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
showList :: [Header] -> ShowS
$cshowList :: [Header] -> ShowS
show :: Header -> [Char]
$cshow :: Header -> [Char]
showsPrec :: Int -> Header -> ShowS
$cshowsPrec :: Int -> Header -> ShowS
Show)
data = {
Footer -> Word32
size :: Word32,
Footer -> Word32
crc32 :: Word32,
Footer -> Bool
crc32valid :: Bool
}
type Section = (Header, String, Footer)
split1 :: String -> (Char, String)
split1 :: [Char] -> (Char, [Char])
split1 [Char]
s = ([Char] -> Char
forall a. [a] -> a
head [Char]
s, ShowS
forall a. [a] -> [a]
tail [Char]
s)
hDecompress :: Handle
-> Handle
-> IO (Maybe GZipError)
hDecompress :: Handle -> Handle -> IO (Maybe GZipError)
hDecompress Handle
infd Handle
outfd =
do [Char]
inc <- Handle -> IO [Char]
hGetContents Handle
infd
let ([Char]
outstr, Maybe GZipError
err) = [Char] -> ([Char], Maybe GZipError)
decompress [Char]
inc
Handle -> [Char] -> IO ()
hPutStr Handle
outfd [Char]
outstr
Maybe GZipError -> IO (Maybe GZipError)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe GZipError
err
decompress :: String -> (String, Maybe GZipError)
decompress :: [Char] -> ([Char], Maybe GZipError)
decompress [Char]
s =
let procs :: [Section] -> (String, Bool)
procs :: [Section] -> ([Char], Bool)
procs [] = ([], Bool
True)
procs ((Header
_, [Char]
content, Footer
foot):[Section]
xs) =
let ([Char]
nexth, Bool
nextb) = [Section] -> ([Char], Bool)
procs [Section]
xs in
([Char]
content [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
nexth, (Footer -> Bool
crc32valid Footer
foot) Bool -> Bool -> Bool
&& Bool
nextb)
in case [Char] -> Either GZipError [Section]
read_sections [Char]
s of
Left GZipError
x -> ([Char]
"", GZipError -> Maybe GZipError
forall a. a -> Maybe a
Just GZipError
x)
Right [Section]
x -> let ([Char]
decomp, Bool
iscrcok) = [Section] -> ([Char], Bool)
procs [Section]
x
in ([Char]
decomp, if Bool
iscrcok then Maybe GZipError
forall a. Maybe a
Nothing else GZipError -> Maybe GZipError
forall a. a -> Maybe a
Just GZipError
CRCError)
read_sections :: String -> Either GZipError [Section]
read_sections :: [Char] -> Either GZipError [Section]
read_sections [] = [Section] -> Either GZipError [Section]
forall a b. b -> Either a b
Right []
read_sections [Char]
s =
do (Section, [Char])
x <- [Char] -> Either GZipError (Section, [Char])
read_section [Char]
s
case (Section, [Char])
x of
(Section
sect, [Char]
remain) ->
do [Section]
next <- [Char] -> Either GZipError [Section]
read_sections [Char]
remain
[Section] -> Either GZipError [Section]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Section] -> Either GZipError [Section])
-> [Section] -> Either GZipError [Section]
forall a b. (a -> b) -> a -> b
$ Section
sect Section -> [Section] -> [Section]
forall a. a -> [a] -> [a]
: [Section]
next
parseword :: String -> Word32
parseword :: [Char] -> Word32
parseword [Char]
s = [Word32] -> Word32
forall a. (Bits a, Num a) => [a] -> a
fromBytes ([Word32] -> Word32) -> [Word32] -> Word32
forall a b. (a -> b) -> a -> b
$ (Char -> Word32) -> [Char] -> [Word32]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Word32) -> (Char -> Int) -> Char -> Word32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Int
ord) ([Char] -> [Word32]) -> [Char] -> [Word32]
forall a b. (a -> b) -> a -> b
$ ShowS
forall a. [a] -> [a]
reverse [Char]
s
read_section :: String -> Either GZipError (Section, String)
read_section :: [Char] -> Either GZipError (Section, [Char])
read_section [Char]
s =
do (Header, [Char])
x <- [Char] -> Either GZipError (Header, [Char])
read_header [Char]
s
let headerrem :: [Char]
headerrem = (Header, [Char]) -> [Char]
forall a b. (a, b) -> b
snd (Header, [Char])
x
let ([Char]
decompressed, Word32
crc, [Char]
remainder) = [Char] -> ([Char], Word32, [Char])
read_data [Char]
headerrem
let ([Char]
crc32str, [Char]
rm) = Int -> [Char] -> ([Char], [Char])
forall a. Int -> [a] -> ([a], [a])
splitAt Int
4 [Char]
remainder
let ([Char]
sizestr, [Char]
rem2) = Int -> [Char] -> ([Char], [Char])
forall a. Int -> [a] -> ([a], [a])
splitAt Int
4 [Char]
rm
let filecrc32 :: Word32
filecrc32 = [Char] -> Word32
parseword [Char]
crc32str
let filesize :: Word32
filesize = [Char] -> Word32
parseword [Char]
sizestr
(Section, [Char]) -> Either GZipError (Section, [Char])
forall (m :: * -> *) a. Monad m => a -> m a
return (((Header, [Char]) -> Header
forall a b. (a, b) -> a
fst (Header, [Char])
x, [Char]
decompressed,
Footer {size :: Word32
size = Word32
filesize, crc32 :: Word32
crc32 = Word32
filecrc32,
crc32valid :: Bool
crc32valid = Word32
filecrc32 Word32 -> Word32 -> Bool
forall a. Eq a => a -> a -> Bool
== Word32
crc})
,[Char]
rem2)
read_data :: String -> (String, Word32, String)
read_data :: [Char] -> ([Char], Word32, [Char])
read_data [Char]
x =
let ([Char]
decompressed1, [Char]
remainder) = [Char] -> ([Char], [Char])
inflate_string_remainder [Char]
x
([Char]
decompressed, Word32
crc32) = [Char] -> Word32 -> ([Char], Word32)
read_data_internal [Char]
decompressed1 Word32
0
in
([Char]
decompressed, Word32
crc32, [Char]
remainder)
where
read_data_internal :: [Char] -> Word32 -> ([Char], Word32)
read_data_internal [] Word32
ck = ([], Word32
ck)
read_data_internal (Char
y:[Char]
ys) Word32
ck =
let newcrc :: Word32
newcrc = Word32 -> Char -> Word32
update_crc Word32
ck Char
y
n :: ([Char], Word32)
n = Word32
newcrc Word32 -> ([Char], Word32) -> ([Char], Word32)
`seq` [Char] -> Word32 -> ([Char], Word32)
read_data_internal [Char]
ys Word32
newcrc
in
(Char
y Char -> ShowS
forall a. a -> [a] -> [a]
: ([Char], Word32) -> [Char]
forall a b. (a, b) -> a
fst ([Char], Word32)
n, ([Char], Word32) -> Word32
forall a b. (a, b) -> b
snd ([Char], Word32)
n)
read_header :: String -> Either GZipError (Header, String)
[Char]
s =
let ok :: Either a [Char]
ok = [Char] -> Either a [Char]
forall a b. b -> Either a b
Right [Char]
"ok" in
do let ([Char]
mag, [Char]
rem1) = Int -> [Char] -> ([Char], [Char])
forall a. Int -> [a] -> ([a], [a])
splitAt Int
2 [Char]
s
[Char]
_ <- if [Char]
mag [Char] -> [Char] -> Bool
forall a. Eq a => a -> a -> Bool
/= [Char]
magic
then GZipError -> Either GZipError [Char]
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError GZipError
NotGZIPFile
else Either GZipError [Char]
forall {a}. Either a [Char]
ok
let (Char
method, [Char]
rem2) = [Char] -> (Char, [Char])
split1 [Char]
rem1
[Char]
_ <- if (Char -> Int
ord(Char
method) Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
8)
then GZipError -> Either GZipError [Char]
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError GZipError
UnknownMethod
else Either GZipError [Char]
forall {a}. Either a [Char]
ok
let (Char
flag_S, [Char]
rem3) = [Char] -> (Char, [Char])
split1 [Char]
rem2
let flag :: Int
flag = Char -> Int
ord Char
flag_S
let ([Char]
mtimea, [Char]
rem3a) = Int -> [Char] -> ([Char], [Char])
forall a. Int -> [a] -> ([a], [a])
splitAt Int
4 [Char]
rem3
let mtime :: Word32
mtime = [Char] -> Word32
parseword [Char]
mtimea
let (Char
xfla, [Char]
rem3b) = [Char] -> (Char, [Char])
split1 [Char]
rem3a
let xfl :: Int
xfl = Char -> Int
ord Char
xfla
let (Char
osa, [Char]
_) = [Char] -> (Char, [Char])
split1 [Char]
rem3b
let os :: Int
os = Char -> Int
ord Char
osa
let rem4 :: [Char]
rem4 = Int -> ShowS
forall a. Int -> [a] -> [a]
drop Int
6 [Char]
rem3
let (Maybe [Char]
extra, [Char]
rem5) =
if (Int
flag Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
fFEXTRA Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0)
then let (Char
xlen_S, [Char]
_) = [Char] -> (Char, [Char])
split1 [Char]
rem4
(Char
xlen2_S, [Char]
rem4b) = [Char] -> (Char, [Char])
split1 [Char]
rem4
xlen :: Int
xlen = (Char -> Int
ord Char
xlen_S) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
256 Int -> Int -> Int
forall a. Num a => a -> a -> a
* (Char -> Int
ord Char
xlen2_S)
([Char]
ex, [Char]
rrem) = Int -> [Char] -> ([Char], [Char])
forall a. Int -> [a] -> ([a], [a])
splitAt Int
xlen [Char]
rem4b
in ([Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
ex, [Char]
rrem)
else (Maybe [Char]
forall a. Maybe a
Nothing, [Char]
rem4)
let (Maybe [Char]
filename, [Char]
rem6) =
if (Int
flag Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
fFNAME Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0)
then let fn :: [Char]
fn = (Char -> Bool) -> ShowS
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\x00') [Char]
rem5
in ([Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
fn, Int -> ShowS
forall a. Int -> [a] -> [a]
drop (([Char] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
fn) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) [Char]
rem5)
else (Maybe [Char]
forall a. Maybe a
Nothing, [Char]
rem5)
let (Maybe [Char]
comment, [Char]
rem7) =
if (Int
flag Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
fFCOMMENT Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0)
then let cm :: [Char]
cm = (Char -> Bool) -> ShowS
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\x00') [Char]
rem6
in ([Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
cm, Int -> ShowS
forall a. Int -> [a] -> [a]
drop (([Char] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
cm) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) [Char]
rem6)
else (Maybe [Char]
forall a. Maybe a
Nothing, [Char]
rem6)
[Char]
rem8 <- if (Int
flag Int -> Int -> Int
forall a. Bits a => a -> a -> a
.&. Int
fFHCRC Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0)
then [Char] -> Either GZipError [Char]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Char] -> Either GZipError [Char])
-> [Char] -> Either GZipError [Char]
forall a b. (a -> b) -> a -> b
$ Int -> ShowS
forall a. Int -> [a] -> [a]
drop Int
2 [Char]
rem7
else [Char] -> Either GZipError [Char]
forall (m :: * -> *) a. Monad m => a -> m a
return [Char]
rem7
(Header, [Char]) -> Either GZipError (Header, [Char])
forall (m :: * -> *) a. Monad m => a -> m a
return (Header {method :: Int
method = Char -> Int
ord Char
method,
flags :: Int
flags = Int
flag,
extra :: Maybe [Char]
extra = Maybe [Char]
extra,
filename :: Maybe [Char]
filename = Maybe [Char]
filename,
comment :: Maybe [Char]
comment = Maybe [Char]
comment,
mtime :: Word32
mtime = Word32
mtime,
xfl :: Int
xfl = Int
xfl,
os :: Int
os = Int
os}, [Char]
rem8)