module Network.MoHWS.Utility where
import Control.Exception (try, catchJust, )
import Control.Concurrent (newEmptyMVar, takeMVar, )
import Control.Monad (liftM, )
import Control.Monad.Trans.Maybe (MaybeT(MaybeT), runMaybeT, )
import Data.Maybe.HT (toMaybe, )
import Data.Maybe (fromMaybe, )
import Data.Tuple.HT (mapSnd, )
import Data.List (intersperse, )
import Data.List.HT (switchL, switchR, maybePrefixOf, dropWhileRev, takeWhileRev, inits, tails, )
import Data.Ratio (numerator, )
import Foreign.C.Error (getErrno, eNOENT, eNOTDIR, )
import Network.Socket as Socket
import qualified System.FilePath as FilePath
import qualified System.IO as IO
import qualified System.Directory as Dir
import System.IO.Error (isDoesNotExistError, )
import System.Exit (exitFailure, )
import System.Locale (defaultTimeLocale, )
import System.Posix (EpochTime, FileStatus,
getFileStatus, getSymbolicLinkStatus, isSymbolicLink, )
import System.Time (CalendarTime, formatCalendarTime, ClockTime(TOD), )
deHex :: String -> String
deHex s = s
hPutStrCrLf :: IO.Handle -> String -> IO ()
hPutStrCrLf h s = IO.hPutStr h s >> IO.hPutChar h '\r' >> IO.hPutChar h '\n'
die :: String -> IO ()
die err = do IO.hPutStrLn IO.stderr err
exitFailure
readM :: (Read a, Monad m) => String -> m a
readM s = readSM reads s
readSM :: Monad m => ReadS a -> String -> m a
readSM f s =
case f s of
[] -> fail $ "No parse of " ++ show s
[(x,[])] -> return x
[(_,_)] -> fail $ "Junk at end of " ++ show s
_ -> fail $ "Ambiguous parse of " ++ show s
splitBy :: (a -> Bool) -> [a] -> [[a]]
splitBy f =
let recourse =
uncurry (:) .
mapSnd (switchL [] (const recourse)) .
break f
in recourse
glue :: [a] -> [[a]] -> [a]
glue g = concat . intersperse g
splits :: [a] -> [([a],[a])]
splits xs = zip (inits xs) (tails xs)
dropPrefix :: Eq a => [a] -> [a] -> [a]
dropPrefix xs pref =
fromMaybe xs $ maybePrefixOf pref xs
dropSuffix :: Eq a => [a] -> [a] -> [a]
dropSuffix xs suf = reverse (reverse xs `dropPrefix` reverse suf)
splitPath :: FilePath -> [String]
splitPath = splitBy (=='/')
joinPath :: [String] -> FilePath
joinPath = glue "/"
dirname :: FilePath -> FilePath
dirname = dropWhileRev (/= '/')
basename :: FilePath -> FilePath
basename = takeWhileRev (/= '/')
hasTrailingSlash :: FilePath -> Bool
hasTrailingSlash =
switchR False (\_ -> ('/'==))
formatTimeSensibly :: CalendarTime -> String
formatTimeSensibly time
= formatCalendarTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT" time
epochTimeToClockTime :: EpochTime -> ClockTime
epochTimeToClockTime epoch_time = TOD (numToInteger epoch_time) 0
where numToInteger = numerator . toRational
wait :: IO a
wait = newEmptyMVar >>= takeMVar
accept :: Socket
-> IO (IO.Handle,SockAddr)
accept sock = do
(sock', addr) <- Socket.accept sock
hndle <- socketToHandle sock' IO.ReadWriteMode
return (hndle,addr)
statFile :: String -> MaybeT IO FileStatus
statFile = stat_ getFileStatus
statSymLink :: String -> MaybeT IO FileStatus
statSymLink = stat_ getSymbolicLinkStatus
stat_ :: (FilePath -> IO FileStatus) -> String -> MaybeT IO FileStatus
stat_ f filename = MaybeT $ do
maybe_stat <- try (f filename)
case maybe_stat of
Left e -> do
errno <- getErrno
if errno == eNOENT || errno == eNOTDIR
then return Nothing
else ioError e
Right stat ->
return $ Just stat
isSymLink :: FilePath -> IO Bool
isSymLink = liftM (maybe False isSymbolicLink) . runMaybeT . statSymLink
isPrefix :: FilePath -> FilePath -> Bool
isPrefix root absolute = FilePath.makeRelative root absolute /= absolute
canonicalizePath :: FilePath -> IO FilePath
canonicalizePath path = do
absolute <- Dir.canonicalizePath path
return $
if FilePath.hasTrailingPathSeparator path &&
not (FilePath.hasTrailingPathSeparator absolute)
then FilePath.addTrailingPathSeparator absolute
else absolute
localPath :: FilePath -> String -> IO (Maybe FilePath)
localPath root urlPath =
case urlPath of
'/' : _ ->
catchSomeIOErrors isDoesNotExistError
(do
absolute <- canonicalizePath (root ++ urlPath)
return $ toMaybe (isPrefix root absolute) absolute)
(const $ return Nothing)
_ -> return Nothing
catchSomeIOErrors :: (IOError -> Bool) -> IO a -> (IOError -> IO a) -> IO a
catchSomeIOErrors p =
catchJust (\e -> toMaybe (p e) e)