packages feed

ginsu-0.8.0: ConfigFile.hs

module ConfigFile(
    configLookupBool,
    configLookup,
    configLookupList,
    configLookupElse,
    -- new interface
    configGet,
    configSetup,
    reloadConfigFiles,
    mapConfig,
    configFile,
    configEnv,
    configDefault,
    configShow,
    toConfig,
    defaultConfig,
    parseConfigFile


    ) where

import Char
import System
import System.IO.Unsafe

import CacheIO
import ErrorLog
import Data.Monoid

newtype Config = Config (String -> IO [(String,(String,String))])
type ConfigFile = [(String, String)]

toConfig = Config

-- dealing with global config settings


{-# NOTINLINE config_default #-}
config_default :: JVar Config
config_default = unsafePerformIO (newJVar mempty)


configSetup :: Config -> IO ()
configSetup c = writeVal config_default c >> reloadConfigFiles

defaultConfig :: IO Config
defaultConfig = readVal config_default

configGet :: String -> IO [(String,(String,String))]
configGet k = do
        Config c <- defaultConfig
        v <- c k
        mapM fixUp v where
            fixUp (w,(k,v)) = do
                v' <- conv v
                return (w,(k,v'))
            conv v = case (span (/= '$') v) of
                (xs,"") -> return xs
                (xs,('$':'$':ys)) -> conv ys >>= \r -> return (xs ++ "$" ++ r)
                (xs,('$':c:ys)) | isDigit c -> conv ys >>= \r -> return (xs ++ ['$',c] ++ r)
                (xs,('$':ys)) -> case span isPropName ys of
                    (pn,ys) -> do
                        n <- configLookupElse pn ""
                        r <- conv ys
                        return (xs ++ n ++ r)
                _ -> error "shouldn't happen"


-- | reload all configuration files emit a config signal.
reloadConfigFiles :: IO ()
reloadConfigFiles = writeVal config_files_var [] {->> signal configSignal ()-}




{-# NOTINLINE config_files_var #-}
config_files_var :: JVar [(String,ConfigFile)]
config_files_var = unsafePerformIO (newJVar [])


basicLookup n cl k = return [ (n,(k,v)) | (k',v) <- cl, k == k']

configDefault :: [(String,String)] -> Config
configDefault cl = Config $ \k -> basicLookup "default" cl k

configFile :: String -> Config
configFile fn = Config $ \k -> do
    cf <- readVal config_files_var
    case lookup fn cf of
	Just cl -> basicLookup fn cl k
	Nothing -> do
	    cl <- catchMost (fmap parseConfigFile $ readFile fn) (\_ -> return [])
	    mapVal config_files_var ((fn,cl):)
	    basicLookup fn cl k

configEnv :: Config
configEnv = Config $ \k -> do
    ev <- catch (fmap return $ getEnv k) (\_ -> return [])
    return $ fmap (\v -> ("enviornment", (k,v))) ev

mapConfig :: (String -> String) -> Config -> Config
mapConfig f (Config c) = Config $ \s -> c (f s)

instance Monoid Config where
    mempty =  Config $ \_ -> return []
    mappend (Config c1) (Config c2) = Config $ \s -> do
	x <- c1 s
	y <- c2 s
	return (x ++ y)

configShow :: [String] -> Config -> IO String
configShow ss (Config c) = do
    v <- mapM c ss
    return $ unlines $ map p $ zip ss v where
	p (k,((w,(k',v))):_) = k ++ " " ++ v ++ "\n#  in " ++ w ++
	    if k' /= k then " as " ++ k' else ""
	p (k,[]) = "#" ++ k ++ " Not Found."


-- types of config sources:
-- enviornment
-- enviornment after transformation of query
-- file
-- default





isPropName c = isAlphaNum c || c `elem` "-_"

parseConfigFile :: String -> ConfigFile
parseConfigFile s = concatMap bl (fixup $ lines (uncomment s)) where
    uncomment ('#':xs) = uncomment (dropWhile (/= '\n') xs)
    uncomment ('-':'-':xs) = uncomment (dropWhile (/= '\n') xs)
    uncomment (x:xs) = x:uncomment xs
    uncomment [] = []
    fixup (x:y@(c:_):xs) | isSpace c = fixup ((x ++ y):xs)
    fixup (x:xs) = x: fixup xs
    fixup [] = []
    bl s = let (n,r) = span isPropName (dropWhile isSpace s) in
	if null n then [] else [(n,dropWhile isSpace r)]

{-

{-# NOINLINE config_file_var #-}
config_file_var :: SVar ConfigFile
config_file_var = unsafePerformIO $ newSVar []


loadStdConfiguration :: String -> IO ()
loadStdConfiguration f = do
    c <- getConfiguration f
    writeSVar config_file_var c
    return ()

getConfiguration :: String -> IO ConfigFile
getConfiguration s = do
    e <- getEnv "HOME"
    c <- readFile (e ++ "/" ++ s)
    return $ parseConfigFile c
-}

configLookupBool k = do
    x <- configLookup k
    case x of
	Just s | cond -> return True where
	    cond = (map toLower s) `elem` ["true", "yes", "on", "y", "t"]
	_ -> return False


configLookup k = do
    vs <- configGet k
    case vs of
        ((_,(_,v)):_) -> return $ Just v
        [] -> return Nothing


configLookupList k = do
    vs <- configGet k
    return $ [ y| (_,(_,y)) <- vs]

configLookupElse k e = do
    v <- configLookup k
    case v of
        Just v -> return v
        Nothing -> return e