{-# LANGUAGE DeriveDataTypeable #-}
module System.Locale.Read
( getLocale
, getCurrentLocale
, parseLocale
, TimeLocale(..)
, LocaleParseException(..)
) where
import Control.Applicative
import Control.Exception
import Data.Attoparsec.Text
import qualified Data.Text.Encoding as Text
import qualified Data.Text.Encoding.Error as Text
import qualified Data.ByteString as BS
import Data.Time.Format (TimeLocale(..))
import Data.Typeable
import System.Process
import System.Environment
import System.Exit (ExitCode(..))
import Data.List (isPrefixOf)
data LocaleParseException
= LocaleParseException String
| LocaleUtf8Exception Text.UnicodeException
| LocaleExitException ExitCode
deriving (Show,Eq,Typeable)
instance Exception LocaleParseException
parseLocale :: Parser TimeLocale
parseLocale = do
abDay <- parseSemicolonSeparatedLine
day <- parseSemicolonSeparatedLine
abMon <- parseSemicolonSeparatedLine
mon <- parseSemicolonSeparatedLine
[am, pm] <- parseSemicolonSeparatedLine
dateTimeFmt' <- manyTill anyChar newline
dateFmt' <- manyTill anyChar newline
timeFmt' <- manyTill anyChar newline
time12Fmt' <- manyTill anyChar newline
pure
(TimeLocale
(zip day abDay)
(zip mon abMon)
(am, pm)
dateTimeFmt'
dateFmt'
timeFmt'
time12Fmt'
[])
getLocale :: Maybe String -> IO TimeLocale
getLocale localeName = do
process <- getLocaleProcess localeName
output <- withCreateProcess process $
\_stdin (Just stdout) _stderr ph -> do
bsOutput <- BS.hGetContents stdout
exitCode <- waitForProcess ph
case exitCode of
ExitSuccess -> return ()
e@(ExitFailure _) -> throwIO $ LocaleExitException e
either (throwIO . LocaleUtf8Exception) return $ Text.decodeUtf8' bsOutput
case parseOnly (parseLocale <* endOfInput) output of
Left err -> throwIO (LocaleParseException err)
Right locale -> pure locale
getCurrentLocale :: IO TimeLocale
getCurrentLocale = getLocale Nothing
getLocaleProcess :: Maybe String -> IO CreateProcess
getLocaleProcess localeName = do
oldEnv <- getEnvironment
return $ (proc "locale"
[ "abday"
, "day"
, "abmon"
, "mon"
, "am_pm"
, "d_t_fmt"
, "d_fmt"
, "t_fmt"
, "t_fmt_ampm"
]) { std_out = CreatePipe, env = toLangEnv oldEnv <$> localeName}
newline :: Parser Char
newline = char '\n'
parseSemicolonSeparatedLine :: Parser [String]
parseSemicolonSeparatedLine =
sepBy (many (satisfy (not . finalChar))) (char ';') <* newline
where
finalChar c = c == ';' || c == '\n'
toLangEnv :: [(String, String)] -> String -> [(String,String)]
toLangEnv oldEnv s = ("LC_ALL", s) : [ e | e@(k, _) <- oldEnv, not $ "LC_" `isPrefixOf` k ]