{-# LANGUAGE DeriveDataTypeable #-}

-- |
-- Module: Codec.RPM.Tags
-- Copyright: (c) 2016-2017 Red Hat, Inc.
-- License: LGPL
--
-- Maintainer: https://github.com/weldr
-- Stability: stable
-- Portability: portable

module Codec.RPM.Tags(
    -- * Types
    Tag(..),
    Null(..),
    -- * Tag finding functions
    findTag,
    findByteStringTag,
    findStringTag,
    findStringListTag,
    findWord16Tag,
    findWord16ListTag,
    findWord32Tag,
    findWord32ListTag,
    -- * Tag making functions
    mkTag,
    -- * Tag inspection functions
    tagValue)
 where

import           Data.Bits((.&.), shiftR)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as C
import           Data.Data(Data, cast, gmapQi, showConstr, toConstr)
import           Data.List(find)
import           Data.Maybe(fromMaybe, listToMaybe)
import           Data.Typeable(Typeable)
import           Data.Word
import           Text.PrettyPrint.HughesPJClass(Pretty(..))
import           Text.PrettyPrint(text)

import Codec.RPM.Internal.Numbers

{-# ANN module "HLint: ignore Use camelCase" #-}

-- The character lists are actually lists of characters, ignore the suggestions
-- to use String instead
{-# ANN module "HLint: ignore Use String" #-}

-- | A very large data type that holds all the possibilities for the various tags that can
-- be contained in an 'Codec.RPM.Types.RPM' 'Codec.RPM.Types.Header'.  Each tag describes
-- one piece of metadata.  Most tags include some typed value, such as a 'String' or
-- 'Word32'.  Many tags contain lists of these values, for instance any tag involving files
-- or changelog entries.  Some tags contain no useful value at all.
--
-- Because there are so many possibilities for tags and each 'Codec.RPM.Types.RPM' likely
-- contains dozens of tags, it is unwieldy to write functions that pattern match on tags and
-- take some action.  This module therefore provides a variety of find*Tag functions for
-- searching the list of tags by name and returning a 'Maybe' value.  The name provided to
-- each should be the constructor you are looking for in this data type.
--
-- To find the list of all files in the RPM, you would therefore do:
--
-- > findStringTag "FileNames" tags
data Tag = DEPRECATED                   Tag
         | INTERNAL                     Tag
         | OBSOLETE                     Tag
         | UNIMPLEMENTED                Tag
         | UNUSED                       Tag

         | HeaderImage                  Null
         | HeaderSignatures             Null
         | HeaderImmutable              Null
         | HeaderRegions                Null
         | HeaderI18NTable              [String]

         | SigBase                      Null
         | SigSize                      Word32
         | SigLEMD5_1                   Null
         | SigPGP                       BS.ByteString
         | SigLEMD5_2                   Null
         | SigMD5                       BS.ByteString
         | SigGPG                       BS.ByteString
         | SigPGP5                      Null
         | SigBadSHA1_1                 Null
         | SigBadSHA1_2                 Null
         | PubKeys                      [String]
         | DSAHeader                    BS.ByteString
         | RSAHeader                    BS.ByteString
         | SHA1Header                   String
         | LongSigSize                  Word64
         | LongArchiveSize              Word64

         | Name                         String
         | Version                      String
         | Release                      String
         | Epoch                        Word32
         | Summary                      BS.ByteString
         | Description                  BS.ByteString
         | BuildTime                    Word32
         | BuildHost                    String
         | InstallTime                  Word32
         | Size                         Word32
         | Distribution                 String
         | Vendor                       String
         | GIF                          BS.ByteString
         | XPM                          BS.ByteString
         | License                      String
         | Packager                     String
         | Group                        BS.ByteString
         | ChangeLog                    [String]
         | Source                       [String]
         | Patch                        [String]
         | URL                          String
         | OS                           String
         | Arch                         String
         | PreIn                        String
         | PostIn                       String
         | PreUn                        String
         | PostUn                       String
         | OldFileNames                 [String]
         | FileSizes                    [Word32]
         | FileStates                   [Char]
         | FileModes                    [Word16]
         | FileUIDs                     [Word32]
         | FileGIDs                     [Word32]
         | FileRDevs                    [Word16]
         | FileMTimes                   [Word32]
         | FileMD5s                     [String]
         | FileLinkTos                  [String]
         | FileFlags                    [Word32]
         | Root                         Null
         | FileUserName                 [String]
         | FileGroupName                [String]
         | Exclude                      Null
         | Exclusive                    Null
         | Icon                         BS.ByteString
         | SourceRPM                    String
         | FileVerifyFlags              [Word32]
         | ArchiveSize                  Word32
         | ProvideName                  [String]
         | RequireFlags                 [Word32]
         | RequireName                  [String]
         | RequireVersion               [String]
         | NoSource                     [Word32]
         | NoPatch                      [Word32]
         | ConflictFlags                [Word32]
         | ConflictName                 [String]
         | ConflictVersion              [String]
         | DefaultPrefix                String
         | BuildRoot                    String
         | InstallPrefix                String
         | ExcludeArch                  [String]
         | ExcludeOS                    [String]
         | ExclusiveArch                [String]
         | ExclusiveOS                  [String]
         | AutoReqProv                  String
         | RPMVersion                   String
         | TriggerScripts               [String]
         | TriggerName                  [String]
         | TriggerVersion               [String]
         | TriggerFlags                 [Word32]
         | TriggerIndex                 [Word32]
         | VerifyScript                 String
         | ChangeLogTime                [Word32]
         | ChangeLogName                [String]
         | ChangeLogText                [String]
         | BrokenMD5                    Null
         | PreReq                       Null
         | PreInProg                    [String]
         | PostInProg                   [String]
         | PreUnProg                    [String]
         | PostUnProg                   [String]
         | BuildArchs                   [String]
         | ObsoleteName                 [String]
         | VerifyScriptProg             [String]
         | TriggerScriptProg            [String]
         | DocDir                       Null
         | Cookie                       String
         | FileDevices                  [Word32]
         | FileINodes                   [Word32]
         | FileLangs                    [String]
         | Prefixes                     [String]
         | InstPrefixes                 [String]
         | TriggerIn                    Null
         | TriggerUn                    Null
         | TriggerPostUn                Null
         | AutoReq                      Null
         | AutoProv                     Null
         | Capability                   Word32
         | SourcePackage                Word32
         | OldOrigFileNames             Null
         | BuildPreReq                  Null
         | BuildRequires                Null
         | BuildConflicts               Null
         | BuildMacros                  Null
         | ProvideFlags                 [Word32]
         | ProvideVersion               [String]
         | ObsoleteFlags                [Word32]
         | ObsoleteVersion              [String]
         | DirIndexes                   [Word32]
         | BaseNames                    [String]
         | DirNames                     [String]
         | OrigDirIndexes               [Word32]
         | OrigBaseNames                [String]
         | OrigDirNames                 [String]
         | OptFlags                     String
         | DistURL                      String
         | PayloadFormat                String
         | PayloadCompressor            String
         | PayloadFlags                 String
         | InstallColor                 Word32
         | InstallTID                   Word32
         | RemoveTID                    Word32
         | SHA1RHN                      Null
         | RHNPlatform                  String
         | Platform                     String
         | PatchesName                  [String]
         | PatchesFlags                 [Word32]
         | PatchesVersion               [String]
         | CacheCTime                   Word32
         | CachePkgPath                 String
         | CachePkgSize                 Word32
         | CachePkgMTime                Word32
         | FileColors                   [Word32]
         | FileClass                    [Word32]
         | ClassDict                    [String]
         | FileDependsX                 [Word32]
         | FileDependsN                 [Word32]
         | DependsDict                  [(Word32, Word32)]
         | SourcePkgID                  BS.ByteString
         | FileContexts                 [String]
         | FSContexts                   [String]
         | ReContexts                   [String]
         | Policies                     [String]
         | PreTrans                     String
         | PostTrans                    String
         | PreTransProg                 [String]
         | PostTransProg                [String]
         | DistTag                      String
         | OldSuggestsName              [String]
         | OldSuggestsVersion           [String]
         | OldSuggestsFlags             [Word32]
         | OldEnhancesName              [String]
         | OldEnhancesVersion           [String]
         | OldEnhancesFlags             [Word32]
         | Priority                     [Word32]
         | CVSID                        String
         | BLinkPkgID                   [String]
         | BLinkHdrID                   [String]
         | BLinkNEVRA                   [String]
         | FLinkPkgID                   [String]
         | FLinkHdrID                   [String]
         | FLinkNEVRA                   [String]
         | PackageOrigin                String
         | TriggerPreIn                 Null
         | BuildSuggests                Null
         | BuildEnhances                Null
         | ScriptStates                 [Word32]
         | ScriptMetrics                [Word32]
         | BuildCPUClock                Word32
         | FileDigestAlgos              [Word32]
         | Variants                     [String]
         | XMajor                       Word32
         | XMinor                       Word32
         | RepoTag                      String
         | Keywords                     [String]
         | BuildPlatforms               [String]
         | PackageColor                 Word32
         | PackagePrefColor             Word32
         | XattrsDict                   [String]
         | FileXattrsx                  [Word32]
         | DepAttrsDict                 [String]
         | ConflictAttrsx               [Word32]
         | ObsoleteAttrsx               [Word32]
         | ProvideAttrsx                [Word32]
         | RequireAttrsx                [Word32]
         | BuildProvides                Null
         | BuildObsoletes               Null
         | DBInstance                   Word32
         | NVRA                         String

         | FileNames                    [String]
         | FileProvide                  [String]
         | FileRequire                  [String]
         | FSNames                      [String]
         | FSSizes                      [Word64]
         | TriggerConds                 [String]
         | TriggerType                  [String]
         | OrigFileNames                [String]
         | LongFileSizes                [Word64]
         | LongSize                     Word64
         | FileCaps                     [String]
         | FileDigestAlgo               Word32
         | BugURL                       String
         | EVR                          String
         | NVR                          String
         | NEVR                         String
         | NEVRA                        String
         | HeaderColor                  Word32
         | Verbose                      Word32
         | EpochNum                     Word32
         | PreInFlags                   Word32
         | PostInFlags                  Word32
         | PreUnFlags                   Word32
         | PostUnFlags                  Word32
         | PreTransFlags                Word32
         | PostTransFlags               Word32
         | VerifyScriptFlags            Word32
         | TriggerScriptFlags           [Word32]
         | Collections                  [String]
         | PolicyNames                  [String]
         | PolicyTypes                  [String]
         | PolicyTypesIndexes           [Word32]
         | PolicyFlags                  [Word32]
         | PolicyVCS                    String
         | OrderName                    [String]
         | OrderVersion                 [String]
         | OrderFlags                   [Word32]
         | MSSFManifest                 [String]
         | MSSFDomain                   [String]
         | InstFileNames                [String]
         | RequireNEVRs                 [String]
         | ProvideNEVRs                 [String]
         | ObsoleteNEVRs                [String]
         | ConflictNEVRs                [String]
         | FileNLinks                   [Word32]
         | RecommendName                [String]
         | RecommendVersion             [String]
         | RecommendFlags               [Word32]
         | SuggestName                  [String]
         | SuggestVersion               [String]
         | SuggestFlags                 [Word32]
         | SupplementName               [String]
         | SupplementVersion            [String]
         | SupplementFlags              [Word32]
         | EnhanceName                  [String]
         | EnhanceVersion               [String]
         | EnhanceFlags                 [Word32]
         | RecommendNEVRs               [String]
         | SuggestNEVRs                 [String]
         | SupplementNEVRs              [String]
         | EnhanceNEVRs                 [String]
         | Encoding                     String
         | FileTriggerIn                Null
         | FileTriggerUn                Null
         | FileTriggerPostUn            Null
         | FileTriggerScripts           [String]
         | FileTriggerScriptProg        [String]
         | FileTriggerScriptFlags       [Word32]
         | FileTriggerName              [String]
         | FileTriggerIndex             [Word32]
         | FileTriggerVersion           [String]
         | FileTriggerFlags             [Word32]
         | TransFileTriggerIn           Null
         | TransFileTriggerUn           Null
         | TransFileTriggerPostUn       Null
         | TransFileTriggerScripts      [String]
         | TransFileTriggerScriptProg   [String]
         | TransFileTriggerScriptFlags  [Word32]
         | TransFileTriggerName         [String]
         | TransFileTriggerIndex        [Word32]
         | TransFileTriggerVersion      [String]
         | TransFileTriggerFlags        [Word32]
         | RemovePathPostFixes          String
         | FileTriggerPriorities        [Word32]
         | TransFileTriggerPriorities   [Word32]
         | FileTriggerConds             [String]
         | FileTriggerType              [String]
         | TransFileTriggerConds        [String]
         | TransFileTriggerType         [String]
         | FileSignatures               [String]
         | FileSignatureLength          Word32
  deriving(Eq, Show, Data, Typeable)

instance Pretty Tag where
    -- This is a lot quicker than having to provide a Pretty instance that takes every
    -- single Tag into account.
    pPrint = text . show

-- | Some 'Tag's do not contain any value, likely because support for that tag has been
-- removed.  RPM never removes a tag from its list of known values, however, so we must
-- still recognize them.  These tags have a special value of 'Null', which contains no
-- value.
data Null = Null
 deriving(Eq, Show, Data, Typeable)

-- | Attempt to create a 'Tag' based on various parameters.
mkTag :: BS.ByteString      -- ^ The 'headerStore' containing the value of the potential 'Tag'.
      -> Int                -- ^ The number of the 'Tag', as read out of the store.  Valid numbers
                            --   may be found in lib\/rpmtag.h in the RPM source, though most
                            --   users will not need to know this since it will be read from the
                            --   store.
      -> Word32             -- ^ What is the type of this tag's value?  Valid numbers may be found
                            --   in the rpmTagType_e enum in lib\/rpmtag.h in the RPM source, though
                            --   most users will not need to know this since it will be read from
                            --   the store.  Here, it is used as a simple form of type checking.
      -> Word32             -- ^ How far into the 'headerStore' is this 'Tag's value stored?
      -> Word32             -- ^ How many values are stored for this 'Tag'?
      -> Maybe Tag
mkTag store tag ty offset count = case tag of
    61      -> maker mkNull          >>=                 Just . HeaderImage
    62      -> maker mkNull          >>=                 Just . HeaderSignatures
    63      -> maker mkNull          >>=                 Just . HeaderImmutable
    64      -> maker mkNull          >>=                 Just . HeaderRegions
    100     -> maker mkStringArray   >>=                 Just . HeaderI18NTable

    256     -> maker mkNull          >>=                 Just . SigBase
    257     -> maker mkWord32        >>= listToMaybe >>= Just . SigSize
    258     -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . SigLEMD5_1
    259     -> maker mkBinary        >>=                 Just . SigPGP
    260     -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . SigLEMD5_2
    261     -> maker mkBinary        >>=                 Just . SigMD5
    262     -> maker mkBinary        >>=                 Just . SigGPG
    263     -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . SigPGP5
    264     -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . SigBadSHA1_1
    265     -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . SigBadSHA1_2
    266     -> maker mkStringArray   >>=                 Just . PubKeys
    267     -> maker mkBinary        >>=                 Just . DSAHeader
    268     -> maker mkBinary        >>=                 Just . RSAHeader
    269     -> maker mkString        >>=                 Just . SHA1Header
    270     -> maker mkWord64        >>= listToMaybe >>= Just . LongSigSize
    271     -> maker mkWord64        >>= listToMaybe >>= Just . LongArchiveSize

    1000    -> maker mkString        >>=                 Just . Name
    1001    -> maker mkString        >>=                 Just . Version
    1002    -> maker mkString        >>=                 Just . Release
    1003    -> maker mkWord32        >>= listToMaybe >>= Just . Epoch
    1004    -> maker mkI18NString    >>=                 Just . Summary
    1005    -> maker mkI18NString    >>=                 Just . Description
    1006    -> maker mkWord32        >>= listToMaybe >>= Just . BuildTime
    1007    -> maker mkString        >>=                 Just . BuildHost
    1008    -> maker mkWord32        >>= listToMaybe >>= Just . InstallTime
    1009    -> maker mkWord32        >>= listToMaybe >>= Just . Size
    1010    -> maker mkString        >>=                 Just . Distribution
    1011    -> maker mkString        >>=                 Just . Vendor
    1012    -> maker mkBinary        >>=                 Just . GIF
    1013    -> maker mkBinary        >>=                 Just . XPM
    1014    -> maker mkString        >>=                 Just . License
    1015    -> maker mkString        >>=                 Just . Packager
    1016    -> maker mkI18NString    >>=                 Just . Group
    1017    -> maker mkStringArray   >>=                 Just . INTERNAL . ChangeLog
    1018    -> maker mkStringArray   >>=                 Just . Source
    1019    -> maker mkStringArray   >>=                 Just . Patch
    1020    -> maker mkString        >>=                 Just . URL
    1021    -> maker mkString        >>=                 Just . OS
    1022    -> maker mkString        >>=                 Just . Arch
    1023    -> maker mkString        >>=                 Just . PreIn
    1024    -> maker mkString        >>=                 Just . PostIn
    1025    -> maker mkString        >>=                 Just . PreUn
    1026    -> maker mkString        >>=                 Just . PostUn
    1027    -> maker mkStringArray   >>=                 Just . OBSOLETE . OldFileNames
    1028    -> maker mkWord32        >>=                 Just . FileSizes
    1029    -> maker mkChar          >>=                 Just . FileStates
    1030    -> maker mkWord16        >>=                 Just . FileModes
    1031    -> maker mkWord32        >>=                 Just . INTERNAL . OBSOLETE . FileUIDs
    1032    -> maker mkWord32        >>=                 Just . INTERNAL . OBSOLETE . FileGIDs
    1033    -> maker mkWord16        >>=                 Just . FileRDevs
    1034    -> maker mkWord32        >>=                 Just . FileMTimes
    1035    -> maker mkStringArray   >>=                 Just . FileMD5s
    1036    -> maker mkStringArray   >>=                 Just . FileLinkTos
    1037    -> maker mkWord32        >>=                 Just . FileFlags
    1038    -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . Root
    1039    -> maker mkStringArray   >>=                 Just . FileUserName
    1040    -> maker mkStringArray   >>=                 Just . FileGroupName
    1041    -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . Exclude
    1042    -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . Exclusive
    1043    -> maker mkBinary        >>=                 Just . Icon
    1044    -> maker mkString        >>=                 Just . SourceRPM
    1045    -> maker mkWord32        >>=                 Just . FileVerifyFlags
    1046    -> maker mkWord32        >>= listToMaybe >>= Just . ArchiveSize
    1047    -> maker mkStringArray   >>=                 Just . ProvideName
    1048    -> maker mkWord32        >>=                 Just . RequireFlags
    1049    -> maker mkStringArray   >>=                 Just . RequireName
    1050    -> maker mkStringArray   >>=                 Just . RequireVersion
    1051    -> maker mkWord32        >>=                 Just . NoSource
    1052    -> maker mkWord32        >>=                 Just . NoPatch
    1053    -> maker mkWord32        >>=                 Just . ConflictFlags
    1054    -> maker mkStringArray   >>=                 Just . ConflictName
    1055    -> maker mkStringArray   >>=                 Just . ConflictVersion
    1056    -> maker mkString        >>=                 Just . INTERNAL . DEPRECATED . DefaultPrefix
    1057    -> maker mkString        >>=                 Just . INTERNAL . OBSOLETE . BuildRoot
    1058    -> maker mkString        >>=                 Just . INTERNAL . DEPRECATED . InstallPrefix
    1059    -> maker mkStringArray   >>=                 Just . ExcludeArch
    1060    -> maker mkStringArray   >>=                 Just . ExcludeOS
    1061    -> maker mkStringArray   >>=                 Just . ExclusiveArch
    1062    -> maker mkStringArray   >>=                 Just . ExclusiveOS
    1063    -> maker mkString        >>=                 Just . INTERNAL . AutoReqProv
    1064    -> maker mkString        >>=                 Just . RPMVersion
    1065    -> maker mkStringArray   >>=                 Just . TriggerScripts
    1066    -> maker mkStringArray   >>=                 Just . TriggerName
    1067    -> maker mkStringArray   >>=                 Just . TriggerVersion
    1068    -> maker mkWord32        >>=                 Just . TriggerFlags
    1069    -> maker mkWord32        >>=                 Just . TriggerIndex
    1079    -> maker mkString        >>=                 Just . VerifyScript
    1080    -> maker mkWord32        >>=                 Just . ChangeLogTime
    1081    -> maker mkStringArray   >>=                 Just . ChangeLogName
    1082    -> maker mkStringArray   >>=                 Just . ChangeLogText
    1083    -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . BrokenMD5
    1084    -> maker mkNull          >>=                 Just . INTERNAL . PreReq
    1085    -> maker mkStringArray   >>=                 Just . PreInProg
    1086    -> maker mkStringArray   >>=                 Just . PostInProg
    1087    -> maker mkStringArray   >>=                 Just . PreUnProg
    1088    -> maker mkStringArray   >>=                 Just . PostUnProg
    1089    -> maker mkStringArray   >>=                 Just . BuildArchs
    1090    -> maker mkStringArray   >>=                 Just . ObsoleteName
    1091    -> maker mkStringArray   >>=                 Just . VerifyScriptProg
    1092    -> maker mkStringArray   >>=                 Just . TriggerScriptProg
    1093    -> maker mkNull          >>=                 Just . INTERNAL . DocDir
    1094    -> maker mkString        >>=                 Just . Cookie
    1095    -> maker mkWord32        >>=                 Just . FileDevices
    1096    -> maker mkWord32        >>=                 Just . FileINodes
    1097    -> maker mkStringArray   >>=                 Just . FileLangs
    1098    -> maker mkStringArray   >>=                 Just . Prefixes
    1099    -> maker mkStringArray   >>=                 Just . InstPrefixes
    1100    -> maker mkNull          >>=                 Just . INTERNAL . TriggerIn
    1101    -> maker mkNull          >>=                 Just . INTERNAL . TriggerUn
    1102    -> maker mkNull          >>=                 Just . INTERNAL . TriggerPostUn
    1103    -> maker mkNull          >>=                 Just . INTERNAL . AutoReq
    1104    -> maker mkNull          >>=                 Just . INTERNAL . AutoProv
    1105    -> maker mkWord32        >>= listToMaybe >>= Just . INTERNAL . OBSOLETE . Capability
    1106    -> maker mkWord32        >>= listToMaybe >>= Just . SourcePackage
    1107    -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . OldOrigFileNames
    1108    -> maker mkNull          >>=                 Just . INTERNAL . BuildPreReq
    1109    -> maker mkNull          >>=                 Just . INTERNAL . BuildRequires
    1110    -> maker mkNull          >>=                 Just . INTERNAL . BuildConflicts
    1111    -> maker mkNull          >>=                 Just . INTERNAL . UNUSED . BuildMacros
    1112    -> maker mkWord32        >>=                 Just . ProvideFlags
    1113    -> maker mkStringArray   >>=                 Just . ProvideVersion
    1114    -> maker mkWord32        >>=                 Just . ObsoleteFlags
    1115    -> maker mkStringArray   >>=                 Just . ObsoleteVersion
    1116    -> maker mkWord32        >>=                 Just . DirIndexes
    1117    -> maker mkStringArray   >>=                 Just . BaseNames
    1118    -> maker mkStringArray   >>=                 Just . DirNames
    1119    -> maker mkWord32        >>=                 Just . OrigDirIndexes
    1120    -> maker mkStringArray   >>=                 Just . OrigBaseNames
    1121    -> maker mkStringArray   >>=                 Just . OrigDirNames
    1122    -> maker mkString        >>=                 Just . OptFlags
    1123    -> maker mkString        >>=                 Just . DistURL
    1124    -> maker mkString        >>=                 Just . PayloadFormat
    1125    -> maker mkString        >>=                 Just . PayloadCompressor
    1126    -> maker mkString        >>=                 Just . PayloadFlags
    1127    -> maker mkWord32        >>= listToMaybe >>= Just . InstallColor
    1128    -> maker mkWord32        >>= listToMaybe >>= Just . InstallTID
    1129    -> maker mkWord32        >>= listToMaybe >>= Just . RemoveTID
    1130    -> maker mkNull          >>=                 Just . INTERNAL . OBSOLETE . SHA1RHN
    1131    -> maker mkString        >>=                 Just . INTERNAL . OBSOLETE . RHNPlatform
    1132    -> maker mkString        >>=                 Just . Platform
    1133    -> maker mkStringArray   >>=                 Just . DEPRECATED . PatchesName
    1134    -> maker mkWord32        >>=                 Just . DEPRECATED . PatchesFlags
    1135    -> maker mkStringArray   >>=                 Just . DEPRECATED . PatchesVersion
    1136    -> maker mkWord32        >>= listToMaybe >>= Just . INTERNAL . OBSOLETE . CacheCTime
    1137    -> maker mkString        >>=                 Just . INTERNAL . OBSOLETE . CachePkgPath
    1138    -> maker mkWord32        >>= listToMaybe >>= Just . INTERNAL . OBSOLETE . CachePkgSize
    1139    -> maker mkWord32        >>= listToMaybe >>= Just . INTERNAL . OBSOLETE . CachePkgMTime
    1140    -> maker mkWord32        >>=                 Just . FileColors
    1141    -> maker mkWord32        >>=                 Just . FileClass
    1142    -> maker mkStringArray   >>=                 Just . ClassDict
    1143    -> maker mkWord32        >>=                 Just . FileDependsX
    1144    -> maker mkWord32        >>=                 Just . FileDependsN
    1145    -> maker mkWord32        >>=                 Just . DependsDict . map (\x -> ((x `shiftR` 24) .&. 0xff, x .&. 0x00ffffff))
    1146    -> maker mkBinary        >>=                 Just . SourcePkgID
    1147    -> maker mkStringArray   >>=                 Just . OBSOLETE . FileContexts
    1148    -> maker mkStringArray   >>=                 Just . FSContexts
    1149    -> maker mkStringArray   >>=                 Just . ReContexts
    1150    -> maker mkStringArray   >>=                 Just . Policies
    1151    -> maker mkString        >>=                 Just . PreTrans
    1152    -> maker mkString        >>=                 Just . PostTrans
    1153    -> maker mkStringArray   >>=                 Just . PreTransProg
    1154    -> maker mkStringArray   >>=                 Just . PostTransProg
    1155    -> maker mkString        >>=                 Just . DistTag
    1156    -> maker mkStringArray   >>=                 Just . OBSOLETE . OldSuggestsName
    1157    -> maker mkStringArray   >>=                 Just . OBSOLETE . OldSuggestsVersion
    1158    -> maker mkWord32        >>=                 Just . OBSOLETE . OldSuggestsFlags
    1159    -> maker mkStringArray   >>=                 Just . OBSOLETE . OldEnhancesName
    1160    -> maker mkStringArray   >>=                 Just . OBSOLETE . OldEnhancesVersion
    1161    -> maker mkWord32        >>=                 Just . OBSOLETE . OldEnhancesFlags
    1162    -> maker mkWord32        >>=                 Just . UNIMPLEMENTED . Priority
    1163    -> maker mkString        >>=                 Just . UNIMPLEMENTED . CVSID
    1164    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . BLinkPkgID
    1165    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . BLinkHdrID
    1166    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . BLinkNEVRA
    1167    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . FLinkPkgID
    1168    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . FLinkHdrID
    1169    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . FLinkNEVRA
    1170    -> maker mkString        >>=                 Just . UNIMPLEMENTED . PackageOrigin
    1171    -> maker mkNull          >>=                 Just . INTERNAL . TriggerPreIn
    1172    -> maker mkNull          >>=                 Just . INTERNAL . UNIMPLEMENTED . BuildSuggests
    1173    -> maker mkNull          >>=                 Just . INTERNAL . UNIMPLEMENTED . BuildEnhances
    1174    -> maker mkWord32        >>=                 Just . UNIMPLEMENTED . ScriptStates
    1175    -> maker mkWord32        >>=                 Just . UNIMPLEMENTED . ScriptMetrics
    1176    -> maker mkWord32        >>= listToMaybe >>= Just . UNIMPLEMENTED . BuildCPUClock
    1177    -> maker mkWord32        >>=                 Just . UNIMPLEMENTED . FileDigestAlgos
    1178    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . Variants
    1179    -> maker mkWord32        >>= listToMaybe >>= Just . UNIMPLEMENTED . XMajor
    1180    -> maker mkWord32        >>= listToMaybe >>= Just . UNIMPLEMENTED . XMinor
    1181    -> maker mkString        >>=                 Just . UNIMPLEMENTED . RepoTag
    1182    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . Keywords
    1183    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . BuildPlatforms
    1184    -> maker mkWord32        >>= listToMaybe >>= Just . UNIMPLEMENTED . PackageColor
    1185    -> maker mkWord32        >>= listToMaybe >>= Just . UNIMPLEMENTED . PackagePrefColor
    1186    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . XattrsDict
    1187    -> maker mkWord32        >>=                 Just . UNIMPLEMENTED . FileXattrsx
    1188    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . DepAttrsDict
    1189    -> maker mkWord32        >>=                 Just . UNIMPLEMENTED . ConflictAttrsx
    1190    -> maker mkWord32        >>=                 Just . UNIMPLEMENTED . ObsoleteAttrsx
    1191    -> maker mkWord32        >>=                 Just . UNIMPLEMENTED . ProvideAttrsx
    1192    -> maker mkWord32        >>=                 Just . UNIMPLEMENTED . RequireAttrsx
    1193    -> maker mkNull          >>=                 Just . UNIMPLEMENTED . BuildProvides
    1194    -> maker mkNull          >>=                 Just . UNIMPLEMENTED . BuildObsoletes
    1195    -> maker mkWord32        >>= listToMaybe >>= Just . DBInstance
    1196    -> maker mkString        >>=                 Just . NVRA

    5000    -> maker mkStringArray   >>=                 Just . FileNames
    5001    -> maker mkStringArray   >>=                 Just . FileProvide
    5002    -> maker mkStringArray   >>=                 Just . FileRequire
    5003    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . FSNames
    5004    -> maker mkWord64        >>=                 Just . UNIMPLEMENTED . FSSizes
    5005    -> maker mkStringArray   >>=                 Just . TriggerConds
    5006    -> maker mkStringArray   >>=                 Just . TriggerType
    5007    -> maker mkStringArray   >>=                 Just . OrigFileNames
    5008    -> maker mkWord64        >>=                 Just . LongFileSizes
    5009    -> maker mkWord64        >>= listToMaybe >>= Just . LongSize
    5010    -> maker mkStringArray   >>=                 Just . FileCaps
    5011    -> maker mkWord32        >>= listToMaybe >>= Just . FileDigestAlgo
    5012    -> maker mkString        >>=                 Just . BugURL
    5013    -> maker mkString        >>=                 Just . EVR
    5014    -> maker mkString        >>=                 Just . NVR
    5015    -> maker mkString        >>=                 Just . NEVR
    5016    -> maker mkString        >>=                 Just . NEVRA
    5017    -> maker mkWord32        >>= listToMaybe >>= Just . HeaderColor
    5018    -> maker mkWord32        >>= listToMaybe >>= Just . Verbose
    5019    -> maker mkWord32        >>= listToMaybe >>= Just . EpochNum
    5020    -> maker mkWord32        >>= listToMaybe >>= Just . PreInFlags
    5021    -> maker mkWord32        >>= listToMaybe >>= Just . PostInFlags
    5022    -> maker mkWord32        >>= listToMaybe >>= Just . PreUnFlags
    5023    -> maker mkWord32        >>= listToMaybe >>= Just . PostUnFlags
    5024    -> maker mkWord32        >>= listToMaybe >>= Just . PreTransFlags
    5025    -> maker mkWord32        >>= listToMaybe >>= Just . PostTransFlags
    5026    -> maker mkWord32        >>= listToMaybe >>= Just . VerifyScriptFlags
    5027    -> maker mkWord32        >>=                 Just . TriggerScriptFlags
    5029    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . Collections
    5030    -> maker mkStringArray   >>=                 Just . PolicyNames
    5031    -> maker mkStringArray   >>=                 Just . PolicyTypes
    5032    -> maker mkWord32        >>=                 Just . PolicyTypesIndexes
    5033    -> maker mkWord32        >>=                 Just . PolicyFlags
    5034    -> maker mkString        >>=                 Just . PolicyVCS
    5035    -> maker mkStringArray   >>=                 Just . OrderName
    5036    -> maker mkStringArray   >>=                 Just . OrderVersion
    5037    -> maker mkWord32        >>=                 Just . OrderFlags
    5038    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . MSSFManifest
    5039    -> maker mkStringArray   >>=                 Just . UNIMPLEMENTED . MSSFDomain
    5040    -> maker mkStringArray   >>=                 Just . InstFileNames
    5041    -> maker mkStringArray   >>=                 Just . RequireNEVRs
    5042    -> maker mkStringArray   >>=                 Just . ProvideNEVRs
    5043    -> maker mkStringArray   >>=                 Just . ObsoleteNEVRs
    5044    -> maker mkStringArray   >>=                 Just . ConflictNEVRs
    5045    -> maker mkWord32        >>=                 Just . FileNLinks
    5046    -> maker mkStringArray   >>=                 Just . RecommendName
    5047    -> maker mkStringArray   >>=                 Just . RecommendVersion
    5048    -> maker mkWord32        >>=                 Just . RecommendFlags
    5049    -> maker mkStringArray   >>=                 Just . SuggestName
    5050    -> maker mkStringArray   >>=                 Just . SuggestVersion
    5051    -> maker mkWord32        >>=                 Just . SuggestFlags
    5052    -> maker mkStringArray   >>=                 Just . SupplementName
    5053    -> maker mkStringArray   >>=                 Just . SupplementVersion
    5054    -> maker mkWord32        >>=                 Just . SupplementFlags
    5055    -> maker mkStringArray   >>=                 Just . EnhanceName
    5056    -> maker mkStringArray   >>=                 Just . EnhanceVersion
    5057    -> maker mkWord32        >>=                 Just . EnhanceFlags
    5058    -> maker mkStringArray   >>=                 Just . RecommendNEVRs
    5059    -> maker mkStringArray   >>=                 Just . SuggestNEVRs
    5060    -> maker mkStringArray   >>=                 Just . SupplementNEVRs
    5061    -> maker mkStringArray   >>=                 Just . EnhanceNEVRs
    5062    -> maker mkString        >>=                 Just . Encoding
    5063    -> maker mkNull          >>=                 Just . INTERNAL . FileTriggerIn
    5064    -> maker mkNull          >>=                 Just . INTERNAL . FileTriggerUn
    5065    -> maker mkNull          >>=                 Just . INTERNAL . FileTriggerPostUn
    5066    -> maker mkStringArray   >>=                 Just . FileTriggerScripts
    5067    -> maker mkStringArray   >>=                 Just . FileTriggerScriptProg
    5068    -> maker mkWord32        >>=                 Just . FileTriggerScriptFlags
    5069    -> maker mkStringArray   >>=                 Just . FileTriggerName
    5070    -> maker mkWord32        >>=                 Just . FileTriggerIndex
    5071    -> maker mkStringArray   >>=                 Just . FileTriggerVersion
    5072    -> maker mkWord32        >>=                 Just . FileTriggerFlags
    5073    -> maker mkNull          >>=                 Just . INTERNAL . TransFileTriggerIn
    5074    -> maker mkNull          >>=                 Just . INTERNAL . TransFileTriggerUn
    5075    -> maker mkNull          >>=                 Just . INTERNAL . TransFileTriggerPostUn
    5076    -> maker mkStringArray   >>=                 Just . TransFileTriggerScripts
    5077    -> maker mkStringArray   >>=                 Just . TransFileTriggerScriptProg
    5078    -> maker mkWord32        >>=                 Just . TransFileTriggerScriptFlags
    5079    -> maker mkStringArray   >>=                 Just . TransFileTriggerName
    5080    -> maker mkWord32        >>=                 Just . TransFileTriggerIndex
    5081    -> maker mkStringArray   >>=                 Just . TransFileTriggerVersion
    5082    -> maker mkWord32        >>=                 Just . TransFileTriggerFlags
    5083    -> maker mkString        >>=                 Just . INTERNAL . RemovePathPostFixes
    5084    -> maker mkWord32        >>=                 Just . FileTriggerPriorities
    5085    -> maker mkWord32        >>=                 Just . TransFileTriggerPriorities
    5086    -> maker mkStringArray   >>=                 Just . FileTriggerConds
    5087    -> maker mkStringArray   >>=                 Just . FileTriggerType
    5088    -> maker mkStringArray   >>=                 Just . TransFileTriggerConds
    5089    -> maker mkStringArray   >>=                 Just . TransFileTriggerType
    5090    -> maker mkStringArray   >>=                 Just . FileSignatures
    5091    -> maker mkWord32        >>= listToMaybe >>= Just . FileSignatureLength

    _       -> Nothing
 where
    maker fn = fn store ty offset count

mkNull :: BS.ByteString -> Word32 -> Word32 -> Word32 -> Maybe Null
mkNull _ ty _ _ | ty == 0    = Just Null
                | otherwise  = Nothing

mkChar :: BS.ByteString -> Word32 -> Word32 -> Word32 -> Maybe [Char]
mkChar store ty offset count | fromIntegral (BS.length store) - offset < count = Nothing
                             | ty == 1   = Just $ C.unpack $ BS.take count' start
                             | otherwise = Nothing
 where
    count' = fromIntegral count
    start = BS.drop (fromIntegral offset) store

mkWord16 :: BS.ByteString -> Word32 -> Word32 -> Word32 -> Maybe [Word16]
mkWord16 store ty offset count | fromIntegral (BS.length store) - offset < (2*count) = Nothing
                               | ty == 3     = Just $ readWords store 2 asWord16 offsets
                               | otherwise   = Nothing
 where
    offsets = map (\n -> offset + (n*2)) [0 .. count-1]

mkWord32 :: BS.ByteString -> Word32 -> Word32 -> Word32 -> Maybe [Word32]
mkWord32 store ty offset count | fromIntegral (BS.length store) - offset < (4*count) = Nothing
                               | ty == 4     = Just $ readWords store 4 asWord32 offsets
                               | otherwise   = Nothing
 where
    offsets = map (\n -> offset + (n*4)) [0 .. count-1]

mkWord64 :: BS.ByteString -> Word32 -> Word32 -> Word32 -> Maybe [Word64]
mkWord64 store ty offset count | fromIntegral (BS.length store) - offset < (8*count) = Nothing
                               | ty == 5     = Just $ readWords store 8 asWord64 offsets
                               | otherwise   = Nothing
 where
    offsets = map (\n -> offset + (n*8)) [0 .. count-1]

mkString :: BS.ByteString -> Word32 -> Word32 -> Word32 -> Maybe String
mkString store ty offset count | fromIntegral (BS.length store) - offset < count = Nothing
                               | ty == 6   = Just $ C.unpack $ BS.takeWhile (/= 0) start
                               | otherwise = Nothing
 where
    start = BS.drop (fromIntegral offset) store

mkBinary :: BS.ByteString -> Word32 -> Word32 -> Word32 -> Maybe BS.ByteString
mkBinary store ty offset count | fromIntegral (BS.length store) - offset < count = Nothing
                               | ty == 7     = Just $ BS.take count' start
                               | otherwise   = Nothing
 where
    count' = fromIntegral count
    start  = BS.drop (fromIntegral offset) store

mkStringArray :: BS.ByteString -> Word32 -> Word32 -> Word32 -> Maybe [String]
mkStringArray store ty offset count | fromIntegral (BS.length store) - offset < count = Nothing
                                    | ty == 8    = Just $ map C.unpack $ readStrings start count
                                    | otherwise  = Nothing
 where
    start = BS.drop (fromIntegral offset) store

mkI18NString :: BS.ByteString -> Word32 -> Word32 -> Word32 -> Maybe BS.ByteString
mkI18NString store ty offset count | fromIntegral (BS.length store) - offset < count = Nothing
                                   | ty == 9     = Just $ BS.takeWhile (/= 0) start
                                   | otherwise   = Nothing
 where
    start  = BS.drop (fromIntegral offset) store

-- I don't know how to split a ByteString up into chunks of a given size, so here's what I'm doing.  Take
-- a list of offsets of where in the ByteString to read.  Skip to each of those offsets, grab size bytes, and
-- convert those bytes into the type using the given conversion function.  Return that list.
{-# ANN readWords "HLint: ignore Eta reduce" #-}
readWords :: BS.ByteString -> Int -> (BS.ByteString -> a) -> [Word32] -> [a]
readWords bs size conv offsets = map (\offset -> conv $ BS.take size $ BS.drop (fromIntegral offset) bs) offsets

readStrings :: BS.ByteString -> Word32 -> [BS.ByteString]
readStrings bytestring count  = take (fromIntegral count) $ BS.split 0 bytestring

-- | Given the name of a 'Tag' and a list of 'Tag's (say, from the 'Codec.RPM.Types.Header' of some
-- 'Codec.RPM.Types.RPM'), find the match and return it as a 'Maybe'.  This is the most generic of
-- the various finding functions - it will return any match regardless of its type.  You are
-- expected to know what type you are looking for.
findTag :: String -> [Tag] -> Maybe Tag
findTag name = find (\t -> name == showConstr (toConstr t))

-- | Given the name of a 'Tag' and a list of 'Tag's, find the match, convert it into a
-- 'ByteString', and return it as a 'Maybe'.  If the value of the 'Tag' cannot be converted
-- into a 'ByteString' (say, because it is of the wrong type), 'Nothing' will be returned.
-- Thus, this should only be used on tags whose value is known - see the definition of 'Tag'
-- for the possibilities.
findByteStringTag :: String -> [Tag] -> Maybe BS.ByteString
findByteStringTag name tags = findTag name tags >>= \t -> tagValue t :: Maybe BS.ByteString

-- | Given the name of a 'Tag' and a list of 'Tag's, find the match, convert it into a
-- 'String', and return it as a 'Maybe'.  If the value of the 'Tag' cannot be converted
-- into a 'String' (say, because it is of the wrong type), 'Nothing' will be returned.
-- Thus, this should only be used on tags whose value is known - see the definition of
-- 'Tag' for the possibilities.
findStringTag :: String -> [Tag] -> Maybe String
findStringTag name tags = findTag name tags >>= \t -> tagValue t :: Maybe String

-- | Given the name of a 'Tag' and a list of 'Tag's, find all matches, convert them into
-- 'String's, and return a list.  If no results are found or the value of a single 'Tag'
-- cannot be converted into a 'String' (say, because it is of the wrong type), an empty
-- list will be returned.  Thus, this should only be used on tags whose value is known -
-- see the definition of 'Tag' for the possibilities.
findStringListTag :: String -> [Tag] -> [String]
findStringListTag name tags = fromMaybe [] $ findTag name tags >>= \t -> tagValue t :: Maybe [String]

-- | Given the name of a 'Tag' and a list of 'Tag's, find the match, convert it into a
-- 'Word16', and return it as a 'Maybe'.  If the value of the 'Tag' cannot be converted
-- into a 'Word16' (say, because it is of the wrong type), 'Nothing' will be returned.
-- Thus, this should only be used on tags whose value is known - see the definition of 'Tag'
-- for the possibilities.
findWord16Tag :: String -> [Tag] -> Maybe Word16
findWord16Tag name tags = findTag name tags >>= \t -> tagValue t :: Maybe Word16

-- | Given the name of a 'Tag' and a list of 'Tag's, find all matches, convert them into
-- 'Word16's, and return a list.  If no results are found or the value of a single 'Tag'
-- cannot be converted into a 'Word16' (say, because it is of the wrong type), an empty
-- list will be returned.  Thus, this should only be used on tags whose value is known -
-- see the definition of 'Tag' for the possibilities.
findWord16ListTag :: String -> [Tag] -> [Word16]
findWord16ListTag name tags = fromMaybe [] $ findTag name tags >>= \t -> tagValue t :: Maybe [Word16]

-- | Given the name of a 'Tag' and a list of 'Tag's, find the match, convert it into a
-- 'Word16', and return it as a 'Maybe'.  If the value of the 'Tag' cannot be converted
-- into a 'Word16' (say, because it is of the wrong type), 'Nothing' will be returned.
-- Thus, this should only be used on tags whose value is known - see the definition of 'Tag'
-- for the possibilities.
findWord32Tag :: String -> [Tag] -> Maybe Word32
findWord32Tag name tags = findTag name tags >>= \t -> tagValue t :: Maybe Word32

-- | Given the name of a 'Tag' and a list of 'Tag's, find all matches, convert them into
-- 'Word32's, and return a list.  If no results are found or the value of a single 'Tag'
-- cannot be converted into a 'Word32' (say, because it is of the wrong type), an empty
-- list will be returned.  Thus, this should only be used on tags whose value is known -
-- see the definition of 'Tag' for the possibilities.
findWord32ListTag :: String -> [Tag] -> [Word32]
findWord32ListTag name tags = fromMaybe [] $ findTag name tags >>= \t -> tagValue t :: Maybe [Word32]

-- | Given a 'Tag', return its value.  This is a helper function to be used with 'findTag',
-- essentially as a type-safe way to cast the value into a known type.  It is used internally
-- in all the type-specific find*Tag functions but can also be used on its own.  A function
-- to find the Epoch tag could be written as follows:
--
-- > epoch = findTag "Epoch" tags >>= \t -> tagValue t :: Maybe Word32
tagValue :: Typeable a => Tag -> Maybe a
tagValue = gmapQi 0 cast