module Porte.Ports where
import Control.Exception.Extensible
import Control.Monad
import qualified Data.ByteString.Lazy.Search.KMP as D
import qualified Data.ByteString.Lazy.Char8 as B
import qualified Data.ByteString.Char8 as C
import qualified Data.List as L
import qualified System.IO as I
import qualified System.Environment as E
portsIndex = "/usr/ports/INDEX-8"
portsPrefix = "/usr/ports/"
type Name = B.ByteString
type DistributionName = B.ByteString
type Path = B.ByteString
type Prefix = B.ByteString
type Comment = B.ByteString
type DescriptionPath = B.ByteString
type PortDescription = Maybe B.ByteString
type Maintainer = B.ByteString
type Categories = [B.ByteString]
type ExtractDepends = [B.ByteString]
type PatchDepends = [B.ByteString]
type FetchDepends = [B.ByteString]
type BuildDepends = [B.ByteString]
type RunDepends = [B.ByteString]
type Website = B.ByteString
data Port = Port {
name :: Name,
distributionName :: DistributionName,
path :: Path,
comment :: Comment,
descriptionPath :: DescriptionPath,
maintainer :: Maintainer,
categories :: Categories,
buildDepends :: BuildDepends,
website :: Website,
extractDepends :: ExtractDepends
} deriving (Eq, Show, Read)
type PortField = Port -> B.ByteString
type PortQuery = [(PortField, String)]
toField :: String -> Maybe PortField
toField "name" = Just name
toField "package" = Just distributionName
toField "distributionName" = Just distributionName
toField "path" = Just path
toField "comment" = Just comment
toField "category" = Just category
toField "maintainer" = Just maintainer
toField "website" = Just website
toField _ = Nothing
search :: PortQuery -> [Port] -> [Port]
search (x:xs) p =
search xs $ filter f p
where
f p = case (D.indices (C.pack $ snd x) (fst x p)) of
[] -> False
_ -> True
search _ p = p
find :: PortQuery -> [Port] -> [Port]
find (x:xs) p = find xs $ filter f p
where
f p = (B.pack $ snd x) == (fst x p)
find _ p = p
description :: Port -> IO (Maybe B.ByteString)
description p =
do
o <- try $ (B.readFile . B.unpack . descriptionPath) p
case (o) of
Right h -> return $ Just $ B.filter (/= '\n') h
Left (e :: IOException) -> return Nothing
glue :: (Port -> [B.ByteString]) -> Port -> B.ByteString
glue f p = B.concat $ L.intersperse (B.pack " ") (f p)
category = glue categories
buildDepend = glue buildDepends
extractDepend = glue extractDepends
parseEntry :: B.ByteString -> Port
parseEntry =
parseEntry' . B.split '|'
where
parseEntry'
(name :
path :
prefix :
comment :
pkgDescr :
maintainer :
categories :
buildDepends :
runDepends :
website :
extractDepends : _) =
Port (B.tail . B.dropWhile (/= '/') $ path')
(name)
(path')
(comment)
(pkgDescr)
(maintainer)
(B.words categories)
(B.words buildDepends)
(website)
(B.words extractDepends)
where
path' = B.drop (B.length $ B.pack portsPrefix) $ path
parseIndex :: B.ByteString -> [Port]
parseIndex = map parseEntry . B.lines
index :: (Maybe String) -> IO [Port]
index (Just []) = index (Just portsIndex)
index Nothing = do
o <- try $ E.getEnv "INDEX"
case o of
Right h -> index $ Just h
Left (e :: IOException) -> index $ Just portsIndex
index (Just path) = do
o <- try $ B.readFile path
case o of
Right h -> return $ parseIndex h
Left (e :: IOException) -> error $ show e ++ "\nporte: Consider setting the $INDEX " ++
"environment variable correctly."
putPorts :: [PortField] -> [Port] -> IO ()
putPorts f = hPutPorts I.stdout f
hPutPorts :: I.Handle -> [PortField] -> [Port] -> IO ()
hPutPorts h f p = forM_ p (hPutPort h f)
putPort :: [PortField] -> Port -> IO ()
putPort f = hPutPort I.stdout f
hPutPort :: I.Handle -> [PortField] -> Port -> IO ()
hPutPort h [f] p = B.hPut h (f p) >> I.hPutChar h '\n'
hPutPort h (f:fs) p = B.hPut h (f p) >> I.hPutStr h ", " >> hPutPort h fs p
hPutPort h _ p = I.hPutChar h '\n'