packages feed

augur-2008.10.19: src/Utils.hs

module Utils where

import Data.Char

import qualified Data.Map as M
import qualified Data.ByteString.Char8 as BS
import Numeric
import System.Process
import Text.XML.HaXml (xmlParse, Document)
import Data.Bits
import System.IO
import System.Exit
import Control.Monad.Trans
import Control.Monad

import Types
import Config

trunc :: String -> String
trunc cs = [ toLower c | c <- cs, isAlphaNum c ]

truncMinimal :: String -> String
truncMinimal = filter isAlphaNum

knownTags :: [TorrentTag]
knownTags = [minBound .. maxBound]

knownTagsMap :: M.Map String TorrentTag
knownTagsMap = M.fromList [ (map toLower (show tag), tag) | tag <- knownTags ]

extractTags :: String -> [TorrentTag]
extractTags str
    = let localTags = M.fromList [ (map toLower tag, ()) | tag <- splitWith isAlphaNum str ]
      in M.elems $ knownTagsMap `M.intersection` localTags

splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith _fn [] = []
splitWith fn lst
    = let (f,rest) = span fn lst
      in f:splitWith fn (dropWhile (not.fn) rest)


showSize :: Int -> String
showSize n'
    = loop sizes (fromIntegral n')
    where loop [] n = showFFloat (Just 0) (n::Float) " bytes"
          loop ((s,p):xs) n | n >= s = showFFloat (Just 2) (n/s) p
                            | otherwise = loop xs n
          sizes = [ (giga, " GiB")
                  , (mega, " MiB")
                  , (kilo, " KiB")]


kilo = 1024
mega = kilo*kilo
giga = mega*kilo

{-
getCurrentTime :: MonadIO m => m TimeStamp
getCurrentTime = liftIO $
    do TOD secs _ <- getClockTime
       return (fromIntegral secs)
-}

downloadToMem :: Config -> String -> IO BS.ByteString
downloadToMem cfg url
    = do when (confVerbose cfg >= 2) $ putStrLn $ "Downloading: " ++ url
         (_inh,outh,_errh,p) <- runInteractiveProcess (confWGetPath cfg)
                                ["--tries=3","-T","20","--quiet",url,"-O","-","-U","firefox"]
                                Nothing Nothing
         out <- BS.hGetContents outh
         waitForProcess p
         when (confVerbose cfg >= 2) $ putStrLn $ "Download finished: " ++ show (BS.length out) ++ "bytes"
         return out

mkGoogleUrl :: String -> String -> String
mkGoogleUrl site query
    = "http://www.google.com/search?hl=en&q=site%3A"++site++"+"++urlEncode query++"&btnI=I%27m+Feeling+Lucky"

htmlToXml :: Config -> BS.ByteString -> IO BS.ByteString
htmlToXml cfg html
    = do (inh, outh, _errh, p) <- runInteractiveProcess (confTidyPath cfg)
                                  ["-i","-asxml","-f","/dev/null"] Nothing Nothing
         BS.hPut inh html
         hFlush inh
         hClose inh
         out <- BS.hGetContents outh
         e <- waitForProcess p
         case e of
           ExitFailure 2 -> return ()
           _ -> return ()
         return out

downloadAsXML :: Config -> String -> IO Document
downloadAsXML cfg url
    = do html <- downloadToMem cfg url
         xml <- htmlToXml cfg html
         return $! xmlParse url (BS.unpack xml)

urlEncode :: String -> String
urlEncode (h:t) =
    let str = if reserved (ord h) then escape h else [h]
    in str ++ urlEncode t
    where
        reserved x
            | x >= ord 'a' && x <= ord 'z' = False
            | x >= ord 'A' && x <= ord 'Z' = False
            | x >= ord '0' && x <= ord '9' = False
            | x <= 0x20 || x >= 0x7F = True
            | otherwise = x `elem` map ord [';','/','?',':','@','&'
                                           ,'=','+',',','$','{','}'
                                           ,'|','\\','^','[',']','`'
                                           ,'<','>','#','%','"']
        -- wouldn't it be nice if the compiler
        -- optimised the above for us?

        escape x = 
            let y = ord x 
            in [ '%', intToDigit ((y `div` 16) .&. 0xf), intToDigit (y .&. 0xf) ]

urlEncode [] = []