{-# LANGUAGE UndecidableInstances, OverlappingInstances #-}
{-
Copyright (C) 2004-2008 John Goerzen <jgoerzen@complete.org>

This program is free software; you can redistribute it and/or modify it, as
specified in the COPYRIGHT file, under the terms of either version 2.1 of
the LGPL (or, at your option, any later version) or the 3-clause BSD license.
-}

{- |
   Module     : Data.ConfigFile
   Copyright  : Copyright (C) 2004-2008 John Goerzen
   License    : Either LGPL or BSD3, as specified in the COPYRIGHT file.

   Maintainer : John Goerzen <jgoerzen@complete.org>
   Stability  : provisional
   Portability: portable

Configuration file parsing, generation, and manipulation

Copyright (c) 2004-2008 John Goerzen, jgoerzen\@complete.org

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

This module contains extensive documentation.  Please scroll down to the Introduction section to continue reading.
-}
module Data.ConfigFile
    (
     -- * Introduction
     -- $introduction

     -- ** Features
     -- $features

     -- ** History
     -- $history

     -- * Configuration File Format
     -- $format

     -- ** White Space
     -- $whitespace

     -- ** Comments
     -- $comments

     -- ** Case Sensitivity
     -- $casesens

     -- ** Interpolation
     -- $interpolation

     -- * Usage Examples
     -- $usage

     -- ** Non-Monadic Usage
     -- $usagenomonad

     -- ** Error Monad Usage
     -- $usageerrormonad

     -- ** Combined Error\/IO Monad Usage
     -- $usageerroriomonad

     -- * Types
     -- $types
     SectionSpec, OptionSpec, ConfigParser(..),
     CPErrorData(..), CPError,
     -- * Initialization
     -- $initialization
     emptyCP,

     -- * Configuring the ConfigParser
     -- $configuringcp

     -- ** Access Functions
     simpleAccess, interpolatingAccess,

     -- * Reading
     -- $reading
     readfile, readhandle, readstring,

     -- * Accessing Data
     Get_C(..),
     sections, has_section,
     options, has_option,
     items,

     -- * Modifying Data
     set, setshow, remove_option,
     add_section, remove_section,
     merge,

     -- * Output Data
     to_string

    ) where

import Data.ConfigFile.Types
import Data.ConfigFile.Parser
import Data.Either.Utils
import Data.String.Utils
import qualified Data.Map as Map
import Data.List
import System.IO(Handle)
import Data.Char
import Control.Monad.Error

-- For interpolatingAccess
import Text.ParserCombinators.Parsec.Error (errorMessages, Message(..))
import Text.ParserCombinators.Parsec (parse)

----------------------------------------------------------------------
-- Basic types / default values
----------------------------------------------------------------------

{- | The default empty 'Data.ConfigFile' object.

The content contains only an empty mandatory @DEFAULT@ section.

'optionxform' is set to @map toLower@.

'usedefault' is set to @True@.

'accessfunc' is set to 'simpleAccess'.
-}
emptyCP :: ConfigParser
emptyCP :: ConfigParser
emptyCP = ConfigParser :: CPData
-> (SectionSpec -> SectionSpec)
-> (ConfigParser
    -> SectionSpec -> SectionSpec -> Either CPError SectionSpec)
-> Bool
-> (ConfigParser
    -> SectionSpec -> SectionSpec -> Either CPError SectionSpec)
-> ConfigParser
ConfigParser { content :: CPData
content = ParseOutput -> CPData
fromAL [(SectionSpec
"DEFAULT", [])],
                       defaulthandler :: ConfigParser
-> SectionSpec -> SectionSpec -> Either CPError SectionSpec
defaulthandler = ConfigParser
-> SectionSpec -> SectionSpec -> Either CPError SectionSpec
forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
defdefaulthandler,
                       optionxform :: SectionSpec -> SectionSpec
optionxform = (Char -> Char) -> SectionSpec -> SectionSpec
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower,
                       usedefault :: Bool
usedefault = Bool
True,
                       accessfunc :: ConfigParser
-> SectionSpec -> SectionSpec -> Either CPError SectionSpec
accessfunc = ConfigParser
-> SectionSpec -> SectionSpec -> Either CPError SectionSpec
forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
simpleAccess}

{- | Low-level tool to convert a parsed object into a 'CPData'
representation.  Performs no option conversions or special handling
of @DEFAULT@. -}
fromAL :: ParseOutput -> CPData
fromAL :: ParseOutput -> CPData
fromAL ParseOutput
origal =
    let conv :: CPData -> (String, [(String, String)]) -> CPData
        conv :: CPData -> (SectionSpec, [(SectionSpec, SectionSpec)]) -> CPData
conv CPData
fm (SectionSpec, [(SectionSpec, SectionSpec)])
sect = SectionSpec -> Map SectionSpec SectionSpec -> CPData -> CPData
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert ((SectionSpec, [(SectionSpec, SectionSpec)]) -> SectionSpec
forall a b. (a, b) -> a
fst (SectionSpec, [(SectionSpec, SectionSpec)])
sect) ([(SectionSpec, SectionSpec)] -> Map SectionSpec SectionSpec
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(SectionSpec, SectionSpec)] -> Map SectionSpec SectionSpec)
-> [(SectionSpec, SectionSpec)] -> Map SectionSpec SectionSpec
forall a b. (a -> b) -> a -> b
$ (SectionSpec, [(SectionSpec, SectionSpec)])
-> [(SectionSpec, SectionSpec)]
forall a b. (a, b) -> b
snd (SectionSpec, [(SectionSpec, SectionSpec)])
sect) CPData
fm
        in
        (CPData -> (SectionSpec, [(SectionSpec, SectionSpec)]) -> CPData)
-> CPData -> ParseOutput -> CPData
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl CPData -> (SectionSpec, [(SectionSpec, SectionSpec)]) -> CPData
conv CPData
forall k a. Map k a
Map.empty ParseOutput
origal

{- | Default (non-interpolating) access function -}
simpleAccess ::  MonadError CPError m =>
                 ConfigParser -> SectionSpec -> OptionSpec -> m String
simpleAccess :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
simpleAccess ConfigParser
cp SectionSpec
s SectionSpec
o = ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
defdefaulthandler ConfigParser
cp SectionSpec
s (ConfigParser -> SectionSpec -> SectionSpec
optionxform ConfigParser
cp (SectionSpec -> SectionSpec) -> SectionSpec -> SectionSpec
forall a b. (a -> b) -> a -> b
$ SectionSpec
o)

{- | Interpolating access function.  Please see the Interpolation section
above for a background on interpolation.

Although the format string looks similar to one used by "Text.Printf",
it is not the same.  In particular, only the %(...)s format is supported.
No width specifiers are supported and no conversions other than s are supported.

To use this function, you must specify a maximum recursion depth for
interpolation.  This is used to prevent a stack overflow in the event that
the configuration file contains an endless interpolation loop.  Values of 10
or so are usually more than enough, though you could probably go into the
hundreds or thousands before you have actual problems.

A value less than one will cause an instant error every time you attempt
a lookup.

This access method can cause 'get' and friends to return a new 'CPError':
'InterpolationError'.  This error would be returned when:

 * The configuration file makes a reference to an option that does
   not exist

 * The maximum interpolation depth is exceeded

 * There is a syntax error processing a %-directive in the configuration
   file

An interpolation lookup name specifies an option only.  There is no provision
to specify a section.  Interpolation variables are looked up in the current
section, and, if 'usedefault' is True, in @DEFAULT@ according to the normal
logic.

To use a literal percent sign, you must place @%%@ in the configuration
file when interpolation is used.

Here is how you might enable interpolation:

>let cp2 = cp {accessfunc = interpolatingAccess 10}

The @cp2@ object will now support interpolation with a maximum depth of 10.
 -}
interpolatingAccess :: MonadError CPError m =>
                       Int ->
                       ConfigParser -> SectionSpec -> OptionSpec
                       -> m String

interpolatingAccess :: forall (m :: * -> *).
MonadError CPError m =>
Int -> ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
interpolatingAccess Int
maxdepth ConfigParser
cp SectionSpec
s SectionSpec
o =
    if Int
maxdepth Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
1
       then SectionSpec -> m SectionSpec
forall {m :: * -> *} {a}.
MonadError CPError m =>
SectionSpec -> m a
interError SectionSpec
"maximum interpolation depth exceeded"
       else do SectionSpec
x <- ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
simpleAccess ConfigParser
cp SectionSpec
s SectionSpec
o
               case Parsec SectionSpec () SectionSpec
-> SectionSpec -> SectionSpec -> Either ParseError SectionSpec
forall s t a.
Stream s Identity t =>
Parsec s () a -> SectionSpec -> s -> Either ParseError a
parse ((SectionSpec -> Either CPError SectionSpec)
-> Parsec SectionSpec () SectionSpec
interpmain ((SectionSpec -> Either CPError SectionSpec)
 -> Parsec SectionSpec () SectionSpec)
-> (SectionSpec -> Either CPError SectionSpec)
-> Parsec SectionSpec () SectionSpec
forall a b. (a -> b) -> a -> b
$ SectionSpec -> Either CPError SectionSpec
lookupfunc) (SectionSpec
s SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
"/" SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
o) SectionSpec
x of
                 Left ParseError
y -> case [Message] -> Message
forall a. [a] -> a
head (ParseError -> [Message]
errorMessages ParseError
y) of
                                Message SectionSpec
z -> SectionSpec -> m SectionSpec
forall {m :: * -> *} {a}.
MonadError CPError m =>
SectionSpec -> m a
interError SectionSpec
z
                                Message
_ -> SectionSpec -> m SectionSpec
forall {m :: * -> *} {a}.
MonadError CPError m =>
SectionSpec -> m a
interError (ParseError -> SectionSpec
forall a. Show a => a -> SectionSpec
show ParseError
y)
                 Right SectionSpec
y -> SectionSpec -> m SectionSpec
forall (m :: * -> *) a. Monad m => a -> m a
return SectionSpec
y
    where
    lookupfunc :: SectionSpec -> Either CPError SectionSpec
lookupfunc = Int
-> ConfigParser
-> SectionSpec
-> SectionSpec
-> Either CPError SectionSpec
forall (m :: * -> *).
MonadError CPError m =>
Int -> ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
interpolatingAccess (Int
maxdepth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) ConfigParser
cp SectionSpec
s
    interError :: SectionSpec -> m a
interError SectionSpec
x = CPError -> m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (SectionSpec -> CPErrorData
InterpolationError SectionSpec
x, SectionSpec
"interpolatingAccess")

-- internal function: default handler
defdefaulthandler ::  MonadError CPError m =>
                      ConfigParser -> SectionSpec -> OptionSpec -> m String

defdefaulthandler :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
defdefaulthandler ConfigParser
cp SectionSpec
sectn SectionSpec
opt =
    let fm :: CPData
fm = ConfigParser -> CPData
content ConfigParser
cp
        lookUp :: SectionSpec -> SectionSpec -> m SectionSpec
lookUp SectionSpec
s SectionSpec
o = do Map SectionSpec SectionSpec
sect <- CPError
-> Maybe (Map SectionSpec SectionSpec)
-> m (Map SectionSpec SectionSpec)
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither (SectionSpec -> CPErrorData
NoSection SectionSpec
s,
                                               SectionSpec
"get " SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec -> SectionSpec -> SectionSpec
formatSO SectionSpec
sectn SectionSpec
opt) (Maybe (Map SectionSpec SectionSpec)
 -> m (Map SectionSpec SectionSpec))
-> Maybe (Map SectionSpec SectionSpec)
-> m (Map SectionSpec SectionSpec)
forall a b. (a -> b) -> a -> b
$
                                SectionSpec -> CPData -> Maybe (Map SectionSpec SectionSpec)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup SectionSpec
s CPData
fm
                        CPError -> Maybe SectionSpec -> m SectionSpec
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither (SectionSpec -> CPErrorData
NoOption SectionSpec
o,
                                       SectionSpec
"get " SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec -> SectionSpec -> SectionSpec
formatSO SectionSpec
sectn SectionSpec
opt) (Maybe SectionSpec -> m SectionSpec)
-> Maybe SectionSpec -> m SectionSpec
forall a b. (a -> b) -> a -> b
$
                                SectionSpec -> Map SectionSpec SectionSpec -> Maybe SectionSpec
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup SectionSpec
o Map SectionSpec SectionSpec
sect
        trydefault :: CPError -> m SectionSpec
trydefault CPError
e = if (ConfigParser -> Bool
usedefault ConfigParser
cp)
                       then
                            SectionSpec -> SectionSpec -> m SectionSpec
forall {m :: * -> *}.
MonadError CPError m =>
SectionSpec -> SectionSpec -> m SectionSpec
lookUp SectionSpec
"DEFAULT" SectionSpec
opt
                                       -- Use original error if it's not in DEFAULT either
                                       m SectionSpec -> (CPError -> m SectionSpec) -> m SectionSpec
forall e (m :: * -> *) a.
MonadError e m =>
m a -> (e -> m a) -> m a
`catchError` (\CPError
_ -> CPError -> m SectionSpec
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError CPError
e)
                       else CPError -> m SectionSpec
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError CPError
e
        in
        SectionSpec -> SectionSpec -> m SectionSpec
forall {m :: * -> *}.
MonadError CPError m =>
SectionSpec -> SectionSpec -> m SectionSpec
lookUp SectionSpec
sectn SectionSpec
opt m SectionSpec -> (CPError -> m SectionSpec) -> m SectionSpec
forall e (m :: * -> *) a.
MonadError e m =>
m a -> (e -> m a) -> m a
`catchError` CPError -> m SectionSpec
forall {m :: * -> *}.
MonadError CPError m =>
CPError -> m SectionSpec
trydefault


{- | Combines two 'ConfigParser's into one.

Any duplicate options are resolved to contain the value specified in
the second parser.

The 'ConfigParser' options in the resulting object will be set as they
are in the second one passed to this function. -}
merge :: ConfigParser -> ConfigParser -> ConfigParser
merge :: ConfigParser -> ConfigParser -> ConfigParser
merge ConfigParser
src ConfigParser
dest =
    let conv :: String -> String
        conv :: SectionSpec -> SectionSpec
conv = ConfigParser -> SectionSpec -> SectionSpec
optionxform ConfigParser
dest
        convFM :: CPOptions -> CPOptions
        convFM :: Map SectionSpec SectionSpec -> Map SectionSpec SectionSpec
convFM = [(SectionSpec, SectionSpec)] -> Map SectionSpec SectionSpec
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(SectionSpec, SectionSpec)] -> Map SectionSpec SectionSpec)
-> (Map SectionSpec SectionSpec -> [(SectionSpec, SectionSpec)])
-> Map SectionSpec SectionSpec
-> Map SectionSpec SectionSpec
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((SectionSpec, SectionSpec) -> (SectionSpec, SectionSpec))
-> [(SectionSpec, SectionSpec)] -> [(SectionSpec, SectionSpec)]
forall a b. (a -> b) -> [a] -> [b]
map (\(SectionSpec, SectionSpec)
x -> (SectionSpec -> SectionSpec
conv ((SectionSpec, SectionSpec) -> SectionSpec
forall a b. (a, b) -> a
fst (SectionSpec, SectionSpec)
x), (SectionSpec, SectionSpec) -> SectionSpec
forall a b. (a, b) -> b
snd (SectionSpec, SectionSpec)
x)) ([(SectionSpec, SectionSpec)] -> [(SectionSpec, SectionSpec)])
-> (Map SectionSpec SectionSpec -> [(SectionSpec, SectionSpec)])
-> Map SectionSpec SectionSpec
-> [(SectionSpec, SectionSpec)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map SectionSpec SectionSpec -> [(SectionSpec, SectionSpec)]
forall k a. Map k a -> [(k, a)]
Map.toList
        mergesects :: Map k a -> Map k a -> Map k a
mergesects Map k a
a Map k a
b = Map k a -> Map k a -> Map k a
forall k a. Ord k => Map k a -> Map k a -> Map k a
Map.union Map k a
a Map k a
b
        in
        ConfigParser
dest { content :: CPData
content = (Map SectionSpec SectionSpec
 -> Map SectionSpec SectionSpec -> Map SectionSpec SectionSpec)
-> CPData -> CPData -> CPData
forall k a. Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a
Map.unionWith Map SectionSpec SectionSpec
-> Map SectionSpec SectionSpec -> Map SectionSpec SectionSpec
forall k a. Ord k => Map k a -> Map k a -> Map k a
mergesects
                         (ConfigParser -> CPData
content ConfigParser
dest) ((Map SectionSpec SectionSpec -> Map SectionSpec SectionSpec)
-> CPData -> CPData
forall a b k. (a -> b) -> Map k a -> Map k b
Map.map Map SectionSpec SectionSpec -> Map SectionSpec SectionSpec
convFM (ConfigParser -> CPData
content ConfigParser
src)) }

{- | Utility to do a special case merge. -}
readutil :: ConfigParser -> ParseOutput -> ConfigParser
readutil :: ConfigParser -> ParseOutput -> ConfigParser
readutil ConfigParser
old ParseOutput
new = ConfigParser -> ConfigParser -> ConfigParser
merge ConfigParser
old (ConfigParser -> ConfigParser) -> ConfigParser -> ConfigParser
forall a b. (a -> b) -> a -> b
$ ConfigParser
old { content :: CPData
content = ParseOutput -> CPData
fromAL ParseOutput
new }

{- | Loads data from the specified file.  It is then combined with the
given 'ConfigParser' using the semantics documented under 'merge' with the
new data taking precedence over the old.  However, unlike
'merge', all the options
as set in the old object are preserved since the on-disk representation
does not convey those options.

May return an error if there is a syntax error.  May raise an exception if the file could not be accessed.
-}
--readfile :: ConfigParser -> FilePath ->IO (CPResult ConfigParser)
readfile :: MonadError CPError m => ConfigParser -> FilePath -> IO (m ConfigParser)
{-
readfile cp fp = do n <- parse_file fp
                    return $ do y <- n
                                return $ readutil cp y
-}
readfile :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> IO (m ConfigParser)
readfile ConfigParser
cp SectionSpec
fp = do m ParseOutput
n <- SectionSpec -> IO (m ParseOutput)
forall (m :: * -> *).
MonadError CPError m =>
SectionSpec -> IO (m ParseOutput)
parse_file SectionSpec
fp
                    m ConfigParser -> IO (m ConfigParser)
forall (m :: * -> *) a. Monad m => a -> m a
return (m ConfigParser -> IO (m ConfigParser))
-> m ConfigParser -> IO (m ConfigParser)
forall a b. (a -> b) -> a -> b
$ m ParseOutput
n m ParseOutput -> (ParseOutput -> m ConfigParser) -> m ConfigParser
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (ConfigParser -> m ConfigParser
forall (m :: * -> *) a. Monad m => a -> m a
return (ConfigParser -> m ConfigParser)
-> (ParseOutput -> ConfigParser) -> ParseOutput -> m ConfigParser
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ConfigParser -> ParseOutput -> ConfigParser
readutil ConfigParser
cp)

{- | Like 'readfile', but uses an already-open handle.  You should
use 'readfile' instead of this if possible, since it will be able to
generate better error messages.

Errors would be returned on a syntax error.
-}
--readhandle :: ConfigParser -> Handle -> IO (CPResult ConfigParser)
readhandle :: MonadError CPError m => ConfigParser -> Handle -> IO (m ConfigParser)
readhandle :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> Handle -> IO (m ConfigParser)
readhandle ConfigParser
cp Handle
h = do m ParseOutput
n <- Handle -> IO (m ParseOutput)
forall (m :: * -> *).
MonadError CPError m =>
Handle -> IO (m ParseOutput)
parse_handle Handle
h
                     m ConfigParser -> IO (m ConfigParser)
forall (m :: * -> *) a. Monad m => a -> m a
return (m ConfigParser -> IO (m ConfigParser))
-> m ConfigParser -> IO (m ConfigParser)
forall a b. (a -> b) -> a -> b
$ m ParseOutput
n m ParseOutput -> (ParseOutput -> m ConfigParser) -> m ConfigParser
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (ConfigParser -> m ConfigParser
forall (m :: * -> *) a. Monad m => a -> m a
return (ConfigParser -> m ConfigParser)
-> (ParseOutput -> ConfigParser) -> ParseOutput -> m ConfigParser
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ConfigParser -> ParseOutput -> ConfigParser
readutil ConfigParser
cp))

{- | Like 'readfile', but uses a string.  You should use 'readfile'
instead of this if you are processing a file, since it can generate
better error messages.

Errors would be returned on a syntax error.
-}
readstring ::  MonadError CPError m =>
               ConfigParser -> String -> m ConfigParser
readstring :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> m ConfigParser
readstring ConfigParser
cp SectionSpec
s = do
                  ParseOutput
n <- SectionSpec -> m ParseOutput
forall (m :: * -> *).
MonadError CPError m =>
SectionSpec -> m ParseOutput
parse_string SectionSpec
s
                  ConfigParser -> m ConfigParser
forall (m :: * -> *) a. Monad m => a -> m a
return (ConfigParser -> m ConfigParser) -> ConfigParser -> m ConfigParser
forall a b. (a -> b) -> a -> b
$ ConfigParser -> ParseOutput -> ConfigParser
readutil ConfigParser
cp ParseOutput
n

{- | Returns a list of sections in your configuration file.  Never includes
the always-present section @DEFAULT@. -}
sections :: ConfigParser -> [SectionSpec]
sections :: ConfigParser -> [SectionSpec]
sections = (SectionSpec -> Bool) -> [SectionSpec] -> [SectionSpec]
forall a. (a -> Bool) -> [a] -> [a]
filter (SectionSpec -> SectionSpec -> Bool
forall a. Eq a => a -> a -> Bool
/= SectionSpec
"DEFAULT") ([SectionSpec] -> [SectionSpec])
-> (ConfigParser -> [SectionSpec]) -> ConfigParser -> [SectionSpec]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CPData -> [SectionSpec]
forall k a. Map k a -> [k]
Map.keys (CPData -> [SectionSpec])
-> (ConfigParser -> CPData) -> ConfigParser -> [SectionSpec]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ConfigParser -> CPData
content

{- | Indicates whether the given section exists.

No special @DEFAULT@ processing is done. -}
has_section :: ConfigParser -> SectionSpec -> Bool
has_section :: ConfigParser -> SectionSpec -> Bool
has_section ConfigParser
cp SectionSpec
x = SectionSpec -> CPData -> Bool
forall k a. Ord k => k -> Map k a -> Bool
Map.member SectionSpec
x (ConfigParser -> CPData
content ConfigParser
cp)

{- | Adds the specified section name.  Returns a
'SectionAlreadyExists' error if the
section was already present.  Otherwise, returns the new
'ConfigParser' object.-}
add_section ::  MonadError CPError m =>
                ConfigParser -> SectionSpec -> m ConfigParser
add_section :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> m ConfigParser
add_section ConfigParser
cp SectionSpec
s =
    if ConfigParser -> SectionSpec -> Bool
has_section ConfigParser
cp SectionSpec
s
       then CPError -> m ConfigParser
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (CPError -> m ConfigParser) -> CPError -> m ConfigParser
forall a b. (a -> b) -> a -> b
$ (SectionSpec -> CPErrorData
SectionAlreadyExists SectionSpec
s, SectionSpec
"add_section")
       else ConfigParser -> m ConfigParser
forall (m :: * -> *) a. Monad m => a -> m a
return (ConfigParser -> m ConfigParser) -> ConfigParser -> m ConfigParser
forall a b. (a -> b) -> a -> b
$ ConfigParser
cp {content :: CPData
content = SectionSpec -> Map SectionSpec SectionSpec -> CPData -> CPData
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert SectionSpec
s Map SectionSpec SectionSpec
forall k a. Map k a
Map.empty (ConfigParser -> CPData
content ConfigParser
cp)}

{- | Removes the specified section.  Returns a 'NoSection' error if
the section does not exist; otherwise, returns the new 'ConfigParser'
object.

This call may not be used to remove the @DEFAULT@ section.  Attempting to do
so will always cause a 'NoSection' error.
 -}
remove_section ::  MonadError CPError m =>
                   ConfigParser -> SectionSpec -> m ConfigParser
remove_section :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> m ConfigParser
remove_section ConfigParser
_ SectionSpec
"DEFAULT" = CPError -> m ConfigParser
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (CPError -> m ConfigParser) -> CPError -> m ConfigParser
forall a b. (a -> b) -> a -> b
$ (SectionSpec -> CPErrorData
NoSection SectionSpec
"DEFAULT", SectionSpec
"remove_section")
remove_section ConfigParser
cp SectionSpec
s =
    if ConfigParser -> SectionSpec -> Bool
has_section ConfigParser
cp SectionSpec
s
       then ConfigParser -> m ConfigParser
forall (m :: * -> *) a. Monad m => a -> m a
return (ConfigParser -> m ConfigParser) -> ConfigParser -> m ConfigParser
forall a b. (a -> b) -> a -> b
$ ConfigParser
cp {content :: CPData
content = SectionSpec -> CPData -> CPData
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete SectionSpec
s (ConfigParser -> CPData
content ConfigParser
cp)}
       else CPError -> m ConfigParser
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (CPError -> m ConfigParser) -> CPError -> m ConfigParser
forall a b. (a -> b) -> a -> b
$ (SectionSpec -> CPErrorData
NoSection SectionSpec
s, SectionSpec
"remove_section")

{- | Removes the specified option.  Returns a 'NoSection' error if the
section does not exist and a 'NoOption' error if the option does not
exist.  Otherwise, returns the new 'ConfigParser' object.
-}
remove_option ::  MonadError CPError m =>
                  ConfigParser -> SectionSpec -> OptionSpec -> m ConfigParser
remove_option :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m ConfigParser
remove_option ConfigParser
cp SectionSpec
s SectionSpec
passedo =
    do Map SectionSpec SectionSpec
sectmap <- CPError
-> Maybe (Map SectionSpec SectionSpec)
-> m (Map SectionSpec SectionSpec)
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither (SectionSpec -> CPErrorData
NoSection SectionSpec
s,
                                 SectionSpec
"remove_option " SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec -> SectionSpec -> SectionSpec
formatSO SectionSpec
s SectionSpec
passedo) (Maybe (Map SectionSpec SectionSpec)
 -> m (Map SectionSpec SectionSpec))
-> Maybe (Map SectionSpec SectionSpec)
-> m (Map SectionSpec SectionSpec)
forall a b. (a -> b) -> a -> b
$
                  SectionSpec -> CPData -> Maybe (Map SectionSpec SectionSpec)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup SectionSpec
s (ConfigParser -> CPData
content ConfigParser
cp)
       let o :: SectionSpec
o = (ConfigParser -> SectionSpec -> SectionSpec
optionxform ConfigParser
cp) SectionSpec
passedo
       let newsect :: Map SectionSpec SectionSpec
newsect = SectionSpec
-> Map SectionSpec SectionSpec -> Map SectionSpec SectionSpec
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete SectionSpec
o Map SectionSpec SectionSpec
sectmap
       let newmap :: CPData
newmap = SectionSpec -> Map SectionSpec SectionSpec -> CPData -> CPData
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert SectionSpec
s Map SectionSpec SectionSpec
newsect (ConfigParser -> CPData
content ConfigParser
cp)
       if SectionSpec -> Map SectionSpec SectionSpec -> Bool
forall k a. Ord k => k -> Map k a -> Bool
Map.member SectionSpec
o Map SectionSpec SectionSpec
sectmap
          then ConfigParser -> m ConfigParser
forall (m :: * -> *) a. Monad m => a -> m a
return (ConfigParser -> m ConfigParser) -> ConfigParser -> m ConfigParser
forall a b. (a -> b) -> a -> b
$ ConfigParser
cp {content :: CPData
content = CPData
newmap}
          else CPError -> m ConfigParser
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (CPError -> m ConfigParser) -> CPError -> m ConfigParser
forall a b. (a -> b) -> a -> b
$ (SectionSpec -> CPErrorData
NoOption SectionSpec
o,
                             SectionSpec
"remove_option " SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec -> SectionSpec -> SectionSpec
formatSO SectionSpec
s SectionSpec
passedo)

{- | Returns a list of the names of all the options present in the
given section.

Returns an error if the given section does not exist.
-}
options ::  MonadError CPError m =>
            ConfigParser -> SectionSpec -> m [OptionSpec]
options :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> m [SectionSpec]
options ConfigParser
cp SectionSpec
x = CPError -> Maybe [SectionSpec] -> m [SectionSpec]
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither (SectionSpec -> CPErrorData
NoSection SectionSpec
x, SectionSpec
"options") (Maybe [SectionSpec] -> m [SectionSpec])
-> Maybe [SectionSpec] -> m [SectionSpec]
forall a b. (a -> b) -> a -> b
$
               do
               Map SectionSpec SectionSpec
o <- SectionSpec -> CPData -> Maybe (Map SectionSpec SectionSpec)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup SectionSpec
x (ConfigParser -> CPData
content ConfigParser
cp)
               [SectionSpec] -> Maybe [SectionSpec]
forall (m :: * -> *) a. Monad m => a -> m a
return ([SectionSpec] -> Maybe [SectionSpec])
-> [SectionSpec] -> Maybe [SectionSpec]
forall a b. (a -> b) -> a -> b
$ Map SectionSpec SectionSpec -> [SectionSpec]
forall k a. Map k a -> [k]
Map.keys Map SectionSpec SectionSpec
o

{- | Indicates whether the given option is present.  Returns True
only if the given section is present AND the given option is present
in that section.  No special @DEFAULT@ processing is done.  No
exception could be raised or error returned.
-}
has_option :: ConfigParser -> SectionSpec -> OptionSpec -> Bool
has_option :: ConfigParser -> SectionSpec -> SectionSpec -> Bool
has_option ConfigParser
cp SectionSpec
s SectionSpec
o =
    let c :: CPData
c = ConfigParser -> CPData
content ConfigParser
cp
        v :: Maybe Bool
v = do Map SectionSpec SectionSpec
secthash <- SectionSpec -> CPData -> Maybe (Map SectionSpec SectionSpec)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup SectionSpec
s CPData
c
               Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ SectionSpec -> Map SectionSpec SectionSpec -> Bool
forall k a. Ord k => k -> Map k a -> Bool
Map.member (ConfigParser -> SectionSpec -> SectionSpec
optionxform ConfigParser
cp (SectionSpec -> SectionSpec) -> SectionSpec -> SectionSpec
forall a b. (a -> b) -> a -> b
$ SectionSpec
o) Map SectionSpec SectionSpec
secthash
        in Bool -> (Bool -> Bool) -> Maybe Bool -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False Bool -> Bool
forall a. a -> a
id Maybe Bool
v

{- | The class representing the data types that can be returned by "get".
-}
class Get_C a where
    {- | Retrieves a string from the configuration file.

When used in a context where a String is expected, returns that string verbatim.

When used in a context where a Bool is expected, parses the string to
a Boolean value (see logic below).

When used in a context where anything that is an instance of Read is expected,
calls read to parse the item.

An error will be returned of no such option could be found or if it could
not be parsed as a boolean (when returning a Bool).

When parsing to a Bool, strings are case-insentively converted as follows:

The following will produce a True value:

 * 1

 * yes

 * on

 * enabled

 * true

The following will produce a False value:

 * 0

 * no

 * off

 * disabled

 * false -}
    get :: MonadError CPError m => ConfigParser -> SectionSpec -> OptionSpec -> m a

instance Get_C String where
    get :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
get ConfigParser
cp SectionSpec
s SectionSpec
o = Either CPError SectionSpec -> m SectionSpec
forall e (m :: * -> *) a. MonadError e m => Either e a -> m a
eitherToMonadError (Either CPError SectionSpec -> m SectionSpec)
-> Either CPError SectionSpec -> m SectionSpec
forall a b. (a -> b) -> a -> b
$ (ConfigParser
-> ConfigParser
-> SectionSpec
-> SectionSpec
-> Either CPError SectionSpec
accessfunc ConfigParser
cp) ConfigParser
cp SectionSpec
s SectionSpec
o

instance Get_C Bool where
    get :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m Bool
get = ConfigParser -> SectionSpec -> SectionSpec -> m Bool
forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m Bool
getbool

instance Read t => Get_C t where
    get :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m t
get = ConfigParser -> SectionSpec -> SectionSpec -> m t
forall b (m :: * -> *).
(Read b, MonadError CPError m) =>
ConfigParser -> SectionSpec -> SectionSpec -> m b
genericget

-- Based on code from Neil Mitchell's safe-0.3.3 package.
readMaybe :: Read a => String -> Maybe a
readMaybe :: forall a. Read a => SectionSpec -> Maybe a
readMaybe SectionSpec
s = case [a
x | (a
x, SectionSpec
t) <- ReadS a
forall a. Read a => ReadS a
reads SectionSpec
s, (SectionSpec
"",SectionSpec
"") <- ReadS SectionSpec
lex SectionSpec
t] of
                [a
x] -> a -> Maybe a
forall a. a -> Maybe a
Just a
x
                [a]
_   -> Maybe a
forall a. Maybe a
Nothing

genericget :: (Read b, MonadError CPError m) => ConfigParser -> SectionSpec -> OptionSpec -> m b
genericget :: forall b (m :: * -> *).
(Read b, MonadError CPError m) =>
ConfigParser -> SectionSpec -> SectionSpec -> m b
genericget ConfigParser
cp SectionSpec
s SectionSpec
o = do
    SectionSpec
val <- ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
forall a (m :: * -> *).
(Get_C a, MonadError CPError m) =>
ConfigParser -> SectionSpec -> SectionSpec -> m a
get ConfigParser
cp SectionSpec
s SectionSpec
o
    let errMsg :: SectionSpec
errMsg = SectionSpec
"couldn't parse value " SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
val SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
" from " SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec -> SectionSpec -> SectionSpec
formatSO SectionSpec
s SectionSpec
o
    m b -> (b -> m b) -> Maybe b -> m b
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (CPError -> m b
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (SectionSpec -> CPErrorData
ParseError SectionSpec
errMsg, SectionSpec
"genericget"))
          b -> m b
forall (m :: * -> *) a. Monad m => a -> m a
return
          (Maybe b -> m b) -> Maybe b -> m b
forall a b. (a -> b) -> a -> b
$ SectionSpec -> Maybe b
forall a. Read a => SectionSpec -> Maybe a
readMaybe SectionSpec
val

getbool ::  MonadError CPError m =>
            ConfigParser -> SectionSpec -> OptionSpec -> m Bool
getbool :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> SectionSpec -> m Bool
getbool ConfigParser
cp SectionSpec
s SectionSpec
o =
    do SectionSpec
val <- ConfigParser -> SectionSpec -> SectionSpec -> m SectionSpec
forall a (m :: * -> *).
(Get_C a, MonadError CPError m) =>
ConfigParser -> SectionSpec -> SectionSpec -> m a
get ConfigParser
cp SectionSpec
s SectionSpec
o
       case (Char -> Char) -> SectionSpec -> SectionSpec
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower (SectionSpec -> SectionSpec)
-> (SectionSpec -> SectionSpec) -> SectionSpec -> SectionSpec
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SectionSpec -> SectionSpec
strip (SectionSpec -> SectionSpec) -> SectionSpec -> SectionSpec
forall a b. (a -> b) -> a -> b
$ SectionSpec
val of
                  SectionSpec
"1" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                  SectionSpec
"yes" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                  SectionSpec
"on" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                  SectionSpec
"enabled" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                  SectionSpec
"true" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
                  SectionSpec
"0" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                  SectionSpec
"no" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                  SectionSpec
"off" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                  SectionSpec
"disabled" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                  SectionSpec
"false" -> Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                  SectionSpec
_ -> CPError -> m Bool
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (SectionSpec -> CPErrorData
ParseError (SectionSpec -> CPErrorData) -> SectionSpec -> CPErrorData
forall a b. (a -> b) -> a -> b
$ SectionSpec
"couldn't parse bool " SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++
                                   SectionSpec
val SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
" from " SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec -> SectionSpec -> SectionSpec
formatSO SectionSpec
s SectionSpec
o, SectionSpec
"getbool")

formatSO :: [Char] -> [Char] -> [Char]
formatSO :: SectionSpec -> SectionSpec -> SectionSpec
formatSO SectionSpec
s SectionSpec
o =
    SectionSpec
"(" SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
s SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
"/" SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
o SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
")"


{- | Returns a list of @(optionname, value)@ pairs representing the content
of the given section.  Returns an error the section is invalid. -}
items ::  MonadError CPError m =>
          ConfigParser -> SectionSpec -> m [(OptionSpec, String)]
items :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser -> SectionSpec -> m [(SectionSpec, SectionSpec)]
items ConfigParser
cp SectionSpec
s = do Map SectionSpec SectionSpec
fm <- CPError
-> Maybe (Map SectionSpec SectionSpec)
-> m (Map SectionSpec SectionSpec)
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither (SectionSpec -> CPErrorData
NoSection SectionSpec
s, SectionSpec
"items") (Maybe (Map SectionSpec SectionSpec)
 -> m (Map SectionSpec SectionSpec))
-> Maybe (Map SectionSpec SectionSpec)
-> m (Map SectionSpec SectionSpec)
forall a b. (a -> b) -> a -> b
$
                      SectionSpec -> CPData -> Maybe (Map SectionSpec SectionSpec)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup SectionSpec
s (ConfigParser -> CPData
content ConfigParser
cp)
                [(SectionSpec, SectionSpec)] -> m [(SectionSpec, SectionSpec)]
forall (m :: * -> *) a. Monad m => a -> m a
return ([(SectionSpec, SectionSpec)] -> m [(SectionSpec, SectionSpec)])
-> [(SectionSpec, SectionSpec)] -> m [(SectionSpec, SectionSpec)]
forall a b. (a -> b) -> a -> b
$ Map SectionSpec SectionSpec -> [(SectionSpec, SectionSpec)]
forall k a. Map k a -> [(k, a)]
Map.toList Map SectionSpec SectionSpec
fm

{- | Sets the option to a new value, replacing an existing one if it exists.

Returns an error if the section does not exist. -}
set ::  MonadError CPError m =>
        ConfigParser -> SectionSpec -> OptionSpec -> String -> m ConfigParser
set :: forall (m :: * -> *).
MonadError CPError m =>
ConfigParser
-> SectionSpec -> SectionSpec -> SectionSpec -> m ConfigParser
set ConfigParser
cp SectionSpec
s SectionSpec
passedo SectionSpec
val =
    do Map SectionSpec SectionSpec
sectmap <- CPError
-> Maybe (Map SectionSpec SectionSpec)
-> m (Map SectionSpec SectionSpec)
forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither (SectionSpec -> CPErrorData
NoSection SectionSpec
s, SectionSpec
"set " SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec -> SectionSpec -> SectionSpec
formatSO SectionSpec
s SectionSpec
passedo) (Maybe (Map SectionSpec SectionSpec)
 -> m (Map SectionSpec SectionSpec))
-> Maybe (Map SectionSpec SectionSpec)
-> m (Map SectionSpec SectionSpec)
forall a b. (a -> b) -> a -> b
$
                  SectionSpec -> CPData -> Maybe (Map SectionSpec SectionSpec)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup SectionSpec
s (ConfigParser -> CPData
content ConfigParser
cp)
       let o :: SectionSpec
o = (ConfigParser -> SectionSpec -> SectionSpec
optionxform ConfigParser
cp) SectionSpec
passedo
       let newsect :: Map SectionSpec SectionSpec
newsect = SectionSpec
-> SectionSpec
-> Map SectionSpec SectionSpec
-> Map SectionSpec SectionSpec
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert SectionSpec
o SectionSpec
val Map SectionSpec SectionSpec
sectmap
       let newmap :: CPData
newmap = SectionSpec -> Map SectionSpec SectionSpec -> CPData -> CPData
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert SectionSpec
s Map SectionSpec SectionSpec
newsect (ConfigParser -> CPData
content ConfigParser
cp)
       ConfigParser -> m ConfigParser
forall (m :: * -> *) a. Monad m => a -> m a
return (ConfigParser -> m ConfigParser) -> ConfigParser -> m ConfigParser
forall a b. (a -> b) -> a -> b
$ ConfigParser
cp { content :: CPData
content = CPData
newmap}

{- | Sets the option to a new value, replacing an existing one if it exists.
It requires only a showable value as its parameter.
This can be used with bool values, as well as numeric ones.

Returns an error if the section does not exist. -}
setshow :: (Show a, MonadError CPError m) =>
           ConfigParser -> SectionSpec -> OptionSpec -> a -> m ConfigParser
setshow :: forall a (m :: * -> *).
(Show a, MonadError CPError m) =>
ConfigParser -> SectionSpec -> SectionSpec -> a -> m ConfigParser
setshow ConfigParser
cp SectionSpec
s SectionSpec
o a
val = ConfigParser
-> SectionSpec -> SectionSpec -> SectionSpec -> m ConfigParser
forall (m :: * -> *).
MonadError CPError m =>
ConfigParser
-> SectionSpec -> SectionSpec -> SectionSpec -> m ConfigParser
set ConfigParser
cp SectionSpec
s SectionSpec
o (a -> SectionSpec
forall a. Show a => a -> SectionSpec
show a
val)

{- | Converts the 'ConfigParser' to a string representation that could be
later re-parsed by this module or modified by a human.

Note that this does not necessarily re-create a file that was originally
loaded.  Things may occur in a different order, comments will be removed,
etc.  The conversion makes an effort to make the result human-editable,
but it does not make an effort to make the result identical to the original
input.

The result is, however, guaranteed to parse the same as the original input.
 -}
to_string :: ConfigParser -> String
to_string :: ConfigParser -> SectionSpec
to_string ConfigParser
cp =
    let gen_option :: (SectionSpec, SectionSpec) -> SectionSpec
gen_option (SectionSpec
key, SectionSpec
value) =
            SectionSpec
key SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
": " SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ (SectionSpec -> SectionSpec -> SectionSpec -> SectionSpec
forall a. Eq a => [a] -> [a] -> [a] -> [a]
replace SectionSpec
"\n" SectionSpec
"\n    " SectionSpec
value) SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
"\n"
        gen_section :: (SectionSpec, Map SectionSpec SectionSpec) -> SectionSpec
gen_section (SectionSpec
sect, Map SectionSpec SectionSpec
valfm) = -- gen a section, but omit DEFAULT if empty
            if (SectionSpec
sect SectionSpec -> SectionSpec -> Bool
forall a. Eq a => a -> a -> Bool
/= SectionSpec
"DEFAULT") Bool -> Bool -> Bool
|| (Map SectionSpec SectionSpec -> Int
forall k a. Map k a -> Int
Map.size Map SectionSpec SectionSpec
valfm Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0)
               then SectionSpec
"[" SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
sect SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
"]\n" SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++
                        ([SectionSpec] -> SectionSpec
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([SectionSpec] -> SectionSpec) -> [SectionSpec] -> SectionSpec
forall a b. (a -> b) -> a -> b
$ ((SectionSpec, SectionSpec) -> SectionSpec)
-> [(SectionSpec, SectionSpec)] -> [SectionSpec]
forall a b. (a -> b) -> [a] -> [b]
map (SectionSpec, SectionSpec) -> SectionSpec
gen_option (Map SectionSpec SectionSpec -> [(SectionSpec, SectionSpec)]
forall k a. Map k a -> [(k, a)]
Map.toList Map SectionSpec SectionSpec
valfm)) SectionSpec -> SectionSpec -> SectionSpec
forall a. [a] -> [a] -> [a]
++ SectionSpec
"\n"
               else SectionSpec
""
        in
        [SectionSpec] -> SectionSpec
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([SectionSpec] -> SectionSpec) -> [SectionSpec] -> SectionSpec
forall a b. (a -> b) -> a -> b
$ ((SectionSpec, Map SectionSpec SectionSpec) -> SectionSpec)
-> [(SectionSpec, Map SectionSpec SectionSpec)] -> [SectionSpec]
forall a b. (a -> b) -> [a] -> [b]
map (SectionSpec, Map SectionSpec SectionSpec) -> SectionSpec
gen_section (CPData -> [(SectionSpec, Map SectionSpec SectionSpec)]
forall k a. Map k a -> [(k, a)]
Map.toList (ConfigParser -> CPData
content ConfigParser
cp))

----------------------------------------------------------------------
-- Docs
----------------------------------------------------------------------

{- $introduction

Many programs need configuration files. These configuration files are
typically used to configure certain runtime behaviors that need to be
saved across sessions. Various different configuration file formats
exist.

The ConfigParser module attempts to define a standard format that is
easy for the user to edit, easy for the programmer to work with, yet
remains powerful and flexible.
-}

{- $features

For the programmer, this module provides:

 * Simple calls to both read /and write/ configuration files

 * Call that can generate a string version of a file that is
   re-parsable by this module (useful for, for instance, sending the
   file down a network)

 * Segmented configuration files that let you separate configuration
   into distinct sections, each with its own namespace. This can be
   used to configure multiple modules in one file, to configure
   multiple instances of a single object, etc.

 * On-the-fly parsing of integer, boolean, float, multi-line string values,
   and anything else Haskell's read can deal with

 * It is possible to make a configuration file parsable by this
   module, the Unix shell, and\/or Unix make, though some feautres are,
   of course, not compatible with these other tools.

 * Syntax checking with error reporting including line numbers

 * Implemented in pure Haskell.  No dependencies on modules outside
   the standard library distributed with Haskell compilers or interpreters.
   All calls except those that read directly from a handle are pure calls
   and can be used outside the IO monad.

 * Comprehensive documentation

 * Extensible API

 * Complete compatibility with Python's ConfigParser module, or my
   ConfigParser module for OCaml, part of my MissingLib package.

For the user, this module provides:

 * Easily human-editable configuration files with a clear, concise,
   and consistent format

 * Configuration file format consistent with other familiar formats
   (\/etc\/passwd is a valid ConfigParser file)

 * No need to understand semantics of markup languages like XML
-}

{- $history

This module is based on Python's ConfigParser module at
<http://www.python.org/doc/current/lib/module-ConfigParser.html>.  I had
earlier developed an OCaml implementation as part of my MissingLib library
at <gopher://gopher.quux.org/devel/missinglib>.

While the API of these three modules is similar, and the aim is to preserve all
useful features of the original Python module, there are some differences
in the implementation details.  This module is a complete, clean re-implementation
in Haskell, not a Haskell translation of a Python program.  As such, the feature
set is slightly different.
-}

{- $format

The basic configuration file format resembles that of an old-style
Windows .INI file. Here are two samples:

>debug = yes
>inputfile = /etc/passwd
>names = Peter, Paul, Mary, George, Abrahaham, John, Bill, Gerald, Richard,
>        Franklin, Woodrow
>color = red

This defines a file without any explicit section, so all items will
occur within the default section @DEFAULT@. The @debug@ option can be read
as a boolean or a string. The remaining items can be read as a string
only. The @names@ entry spans two lines -- any line starting with
whitespace, and containing something other than whitespace or
comments, is taken as a continuation of the previous line.

Here's another example:

># Default options
>[DEFAULT]
>hostname: localhost
># Options for the first file
>[file1]
>location: /usr/local
>user: Fred
>uid: 1000
>optionaltext: Hello, this  entire string is included
>[file2]
>location: /opt
>user: Fred
>uid: 1001

This file defines three sections. The @DEFAULT@ section specifies an
entry @hostname@. If you attempt to read the hostname option in any
section, and that section doesn't define @hostname@, you will get the
value from @DEFAULT@ instead. This is a nice time-saver. You can also
note that you can use colons instead of the = character to separate
option names from option entries.
-}

{- $whitespace

Whitespace (spaces, tabs, etc) is automatically stripped from the
beginning and end of all strings. Thus, users can insert whitespace
before\/after the colon or equal sign if they like, and it will be
automatically stripped.

Blank lines or lines consisting solely of whitespace are ignored.

A line giving an option or a section name may not begin with white space.
This requirement is necessary so there is no ambiguity between such lines
and continuation lines for multi-line options.

-}

{- $comments

Comments are introduced with the pound sign @#@ or the semicolon @;@. They
cause the parser to ignore everything from that character to the end
of the line.

Comments /may not/ occur within the definitions of options; that is, you
may not place a comment in the middle of a line such as @user: Fred@.
That is because the parser considers the comment characters part
of the string; otherwise, you'd be unable to use those characters in
your strings. You can, however, \"comment out\" options by putting the
comment character at the start of the line.

-}

{- $casesens

By default, section names are case-sensitive but option names are
not. The latter can be adjusted by adjusting 'optionxform'.  -}

{- $interpolation

Interpolation is an optional feature, disabled by default.  If you replace
the default 'accessfunc' ('simpleAccess') with 'interpolatingAccess',
then you get interpolation support with 'get' and the other 'get'-based functions.

As an example, consider the following file:

>arch = i386
>project = test
>filename = test_%(arch)s.c
>dir = /usr/src/%(filename)s
>percent = 5%%

With interpolation, you would get these results:

>get cp "DEFAULT" "filename" -> "test_i386.c"
>get cp "DEFAULT" "dir" -> "/usr/src/test_i386.c"
>get cp "DEFAULT" "percent" -> "5%"

For more details on interpolation, please see the documentation for the
'interpolatingAccess' function.
-}

{- $usage

The basic theory of working with ConfigParser is this:

 1. Parse or build a 'ConfigParser' object

 2. Work with it in one of several ways

 3. To make changes, you discard the original object and use a new one.
    Changes can be "chained" through one of several monads.

The default 'ConfigParser' object that you always start with is 'emptyCP'.
From here, you load data into it (merging data into the empty object),
set up structures yourself, or adjust options.

Let's take a look at some basic use cases.

-}

{- $usagenomonad
You'll notice that many functions in this module return a
@MonadError 'CPError'@ over some
type.  Although its definition is not this simple, you can consider this to be
the same as returning @Either CPError a@.

That is, these functions will return @Left error@ if there's a problem
or @Right result@ if things are fine.  The documentation for individual
functions describes the specific circumstances in which an error may occur in
more detail.

Some people find it annoying to have to deal with errors manually.
You can transform errors into exceptions in your code by using
'Data.Either.Utils.forceEither'.  Here's an example of this style of programming:

> import Data.Either.Utils
> do
>    val <- readfile emptyCP "/etc/foo.cfg"
>    let cp = forceEither val
>    putStrLn "Your setting is:"
>    putStrLn $ forceEither $ get cp "sect1" "opt1"

In short, you can just put @forceEither $@ in front of every call that returns
something that is a MonadError.
This is still a pure functional call, so it can be used outside
of the IO monads.  The exception, however, can only be caught in the IO
monad.

If you don't want to bother with 'forceEither', you can use the error monad.  It's simple and better... read on.
-}

{- $usageerrormonad

The return type is actually defined in terms of the Error monad, which is
itself based on the Either data type.

Here's a neat example of chaining together calls to build up a 'ConfigParser'
object:

>do let cp = emptyCP
>   cp <- add_section cp "sect1"
>   cp <- set cp "sect1" "opt1" "foo"
>   cp <- set cp "sect1" "opt2" "bar"
>   options cp "sect1"

The return value of this little snippet is @Right [\"opt1\", \"opt2\"]@.
(Note to beginners: unlike the IO monad, you /can/ escape from the Error
monad.)

Although it's not obvious, there actually was error checking there.  If
any of those calls would have generated an error, processing would have
stopped immediately and a @Left@ value would have been returned.  Consider
this example:

>do let cp = emptyCP
>   cp <- add_section cp "sect1"
>   cp <- set cp "sect1" "opt1" "foo"
>   cp <- set cp "sect2" "opt2" "bar"
>   options cp "sect1"

The return value from this is @Left ('NoSection' \"sect2\", \"set\")@.  The
second call to 'set' failed, so the final call was skipped, and the result
of the entire computation was considered to be an error.

You can combine this with the non-monadic style to get a final, pure value
out of it:

>forceEither $ do let cp = emptyCP
>                 cp <- add_section cp "sect1"
>                 cp <- set cp "sect1" "opt1" "foo"
>                 cp <- set cp "sect1" "opt2" "bar"
>                 options cp "sect1"

This returns @[\"opt1\", \"opt2\"]@.  A quite normal value.

-}

{- $usageerroriomonad

You've seen a nice way to use this module in the Error monad and get an Either
value out.  But that's the Error monad, so IO is not permitted.
Using Haskell's monad transformers, you can run it in the combined
Error\/IO monad.  That is, you will get an IO result back.  Here is a full
standalone example of doing that:

>import Data.ConfigFile
>import Control.Monad.Error
>
>main = do
>          rv <- runErrorT $
>              do
>              cp <- join $ liftIO $ readfile emptyCP "/etc/passwd"
>              let x = cp
>              liftIO $ putStrLn "In the test"
>              nb <- get x "DEFAULT" "nobody"
>              liftIO $ putStrLn nb
>              foo <- get x "DEFAULT" "foo"
>              liftIO $ putStrLn foo
>              return "done"
>          print rv

On my system, this prints:

>In the test
>x:65534:65534:nobody:/nonexistent:/bin/sh
>Left (NoOption "foo","get")

That is, my @\/etc\/passwd@ file contains a @nobody@ user but not a @foo@ user.

Let's look at how that works.

First, @main@ always runs in the IO monad only, so we take the result from
the later calls and put it in @rv@.  Note that the combined block
is started with @runErrorT $ do@ instead of just @do@.

To get something out of the call to 'readfile', we use
@join $ liftIO $ readfile@.  This will bring the result out of the IO monad
into the combined monad and process it like usual.  From here on,
everything looks normal, except for IO calls.  They are all executed under
@liftIO@ so that the result value is properly brought into the combined
monad.  This finally returns @\"done\"@.  Since we are in the Error monad, that means that the literal value is @Right \"done\"@.  Since we are also in the IO
monad, this is wrapped in IO.  So the final return type after applying
@runErrorT@ is @IO (Either CPError String)@.

In this case, there was an error, and processing stopped at that point just
like the example of the pure Error monad.  We print out the return value,
so you see the error displayed as a @Left@ value.

It all works quite easily.

-}

{- $configuringcp

You may notice that the 'ConfigParser' object has some configurable parameters,
such as 'usedefault'.  In case you're not familiar with the Haskell syntax
for working with these, you can use syntax like this to set these options:

>let cp2 = cp { usedefault = False }

This will create a new 'ConfigParser' that is the same as @cp@ except for
the 'usedefault' field, which is now always False.  The new object will be
called @cp2@ in this example.
-}

{- $reading

You can use these functions to read data from a file.

A common idiom for loading a new object from stratch is:

@cp <- 'readfile' 'emptyCP' \"\/etc\/foo.cfg\"@

Note the use of 'emptyCP'; this will essentially cause the file's data
to be merged with the empty 'ConfigParser'.
-}

{- $types

The code used to say this:

>type CPResult a = MonadError CPError m => m a
>simpleAccess :: ConfigParser -> SectionSpec -> OptionSpec -> CPResult String

But Hugs did not support that type declaration.  Therefore, types are now
given like this:

>simpleAccess :: MonadError CPError m =>
>                ConfigParser -> SectionSpec -> OptionSpec -> m String

Although it looks more confusing than before, it still means the same.
The return value can still be treated as @Either CPError String@ if you so
desire.
-}