{-# OPTIONS -fglasgow-exts #-} ----------------------------------------------------------------------------- -- | -- Module : HAppS.Util.Common -- Copyright : (c) HAppS.org, 2005 -- License : BSD3 -- -- -- Various helper routines. ----------------------------------------------------------------------------- module HAppS.Util.Common where import System.Log.Logger import Control.Concurrent import Control.Monad import qualified Data.ByteString.Char8 as P import Data.Char import Data.Int import System.IO import System.Exit import System.IO.Error import System.Process import System.IO.Unsafe import System.Time type Seconds = Int type EpochSeconds = Int64 epochSeconds :: CalendarTime -> EpochSeconds epochSeconds ct = let TOD sec _ = toClockTime ct in fromIntegral sec eSecsToCalTime :: EpochSeconds -> IO CalendarTime eSecsToCalTime s = toCalendarTime (TOD (fromIntegral s) 0) epochPico :: CalendarTime -> Integer epochPico ct = fromIntegral (epochSeconds ct) * 1000 ----reliable getline and putline logMC = logM "HAppS.Util.Common" -- | Put a line into a handle followed by "\r\n" and echo to stdout hPutLine :: Handle -> String -> IO () hPutLine handle line = do hPutStr handle $ line hPutStr handle "\r\n" hFlush handle logMC DEBUG line return () -- | Get a line from the handle and echo to stdout hGetLn :: Handle -> IO String hGetLn handle = do let hGetLn' = do c <- hGetChar handle case c of '\n' -> do return [] '\r' -> do c2 <- hGetChar handle if c2 == '\n' then return [] else getRest c _ -> do getRest c getRest c = do fmap (c:) hGetLn' line <- hGetLn' logMC DEBUG line return line unBracket, ltrim, rtrim, trim :: String -> String unBracket = tail.reverse.tail.reverse.trim --ltrim [] = [] --ltrim (' ':t)= ltrim t --ltrim ('\t':t)= ltrim t --ltrim x = x ltrim = dropWhile isSpace rtrim = reverse.ltrim.reverse trim=ltrim.rtrim splitList :: Eq a => a -> [a] -> [[a]] splitList _ [] = [] splitList sep list = first:splitList sep rest where (first,rest)=split (==sep) list splitListBy :: (a -> Bool) -> [a] -> [[a]] splitListBy _ [] = [] splitListBy f list = first:splitListBy f rest where (first,rest)=split f list -- | Split is like break, but the matching element is dropped. split :: (a -> Bool) -> [a] -> ([a], [a]) split f s = (left,right) where (left,right')=break f s right = if null right' then [] else tail right' {- startsWith list [] = True startsWith [] _ = False startsWith (l:ist) (p:at) = l==p && (startsWith ist at) endsWith list pat = startsWith (reverse list) (reverse pat) readFileSince path pos = do h <- openFile path ReadMode hSetBinaryMode h True hSeek h AbsoluteSeek pos since <- hGetContents h return (since,h) copyFileSince origPath pos destPath = do (after,origHandle) <- readFileSince origPath pos writeFile destPath after hClose origHandle -} -- | Read file with a default value if the file does not exist. mbReadFile :: a -> (String -> a) -> FilePath -> IO a mbReadFile noth just path = (do text <- readFile path;return $ just text) `catch` \err -> if isDoesNotExistError err then return noth else ioError err {- atomicWriteFile path string = do current <- getClockTime >>= toCalendarTime writeFile (temp current path) string renameFile (temp current path) path where temp current path = path++(formatCalendarTime defaultTimeLocale form current) form=".%Y_%m_%d_%H_%M_%S.temp" readEither s = case reads s of [] -> Left s (x,s'):_->Right x ifFilesExist [] y = y >> return () ifFilesExist (f:fs) y = doesFileExist f >>= \x -> if x then ifFilesExist fs y else return () -} doSnd f (x,y) = (x,f y) doFst f (x,y) = (f x,y) mapFst :: (a -> b) -> [(a,x)] -> [(b,x)] mapFst f = map (\ (x,y)->(f x,y)) mapSnd :: (a -> b) -> [(x,a)] -> [(x,b)] mapSnd f = map (\ (x,y)->(x,f y)) revmap :: a -> [a -> b] -> [b] revmap item = map (\f->f item) comp :: Ord t => (a -> t) -> a -> a -> Ordering comp f e1 e2 = f e1 `compare` f e2 {- -- Consider replacing with better implementation -- in future. hGetContentsStrict h = do b <- hIsEOF h if b then return [] else do c <- hGetChar h r <- hGetContentsStrict h return (c:r) -} -- | Run an external command. Upon failure print status -- to stderr. runCommand :: String -> [String] -> IO () runCommand cmd args = do (_, outP, errP, pid) <- runInteractiveProcess cmd args Nothing Nothing let pGetContents h = do mv <- newEmptyMVar let put [] = putMVar mv [] put xs = last xs `seq` putMVar mv xs forkIO (hGetContents h >>= put) takeMVar mv os <- pGetContents outP es <- pGetContents errP ec <- waitForProcess pid case ec of ExitSuccess -> return () ExitFailure e -> do hPutStrLn stderr ("Running process "++unwords (cmd:args)++" FAILED ("++show e++")") hPutStrLn stderr os hPutStrLn stderr es hPutStrLn stderr ("Raising error...") fail "Running external command failed" -- | Unsafe tracing, outputs the message and the value to stderr. debug :: Show a => String -> a -> a debug msg s = seq (unsafePerformIO (hPutStr stderr ("DEBUG: "++msg++"\n") >> hPutStr stderr (show s++"\n"))) s {-# NOINLINE debugM #-} -- | Unsafe tracing messages inside a monad. debugM :: Monad m => String -> m () debugM msg = unsafePerformIO (P.hPutStr stderr (P.pack (msg++"\n")) >> hFlush stderr) `seq` return () -- | Read in any monad. readM :: (Monad m, Read t) => String -> m t readM s = case readsPrec 0 s of [(v,"")] -> return v _ -> fail "readM: parse error" -- | Convert Maybe into an another monad. maybeM :: Monad m => Maybe a -> m a maybeM (Just x) = return x maybeM _ = fail "maybeM: Nothing" -- ! Convert Bool into another monad boolM False = mzero boolM True = return True notMb::a->Maybe a->Maybe a notMb v1 v2 = maybe (Just v1) (const Nothing) $ v2 periodic ts x = forkIO $ periodic' ts x periodic' [] x = x periodic' (t:ts) x = x >> threadDelay (10^6*t) >> periodic' ts x