1{-# LANGUAGE UndecidableInstances, OverlappingInstances #-} 2{- 3Copyright (C) 2004-2008 John Goerzen <jgoerzen@complete.org> 4 5This program is free software; you can redistribute it and/or modify it, as 6specified in the COPYRIGHT file, under the terms of either version 2.1 of 7the LGPL (or, at your option, any later version) or the 3-clause BSD license. 8-} 9 10{- | 11 Module : Data.ConfigFile 12 Copyright : Copyright (C) 2004-2008 John Goerzen 13 License : Either LGPL or BSD3, as specified in the COPYRIGHT file. 14 15 Maintainer : John Goerzen <jgoerzen@complete.org> 16 Stability : provisional 17 Portability: portable 18 19Configuration file parsing, generation, and manipulation 20 21Copyright (c) 2004-2008 John Goerzen, jgoerzen\@complete.org 22 23This program is distributed in the hope that it will be useful, 24but WITHOUT ANY WARRANTY; without even the implied warranty of 25MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 26 27This module contains extensive documentation. Please scroll down to the Introduction section to continue reading. 28-} 29module Data.ConfigFile 30 ( 31 -- * Introduction 32 -- $introduction 33 34 -- ** Features 35 -- $features 36 37 -- ** History 38 -- $history 39 40 -- * Configuration File Format 41 -- $format 42 43 -- ** White Space 44 -- $whitespace 45 46 -- ** Comments 47 -- $comments 48 49 -- ** Case Sensitivity 50 -- $casesens 51 52 -- ** Interpolation 53 -- $interpolation 54 55 -- * Usage Examples 56 -- $usage 57 58 -- ** Non-Monadic Usage 59 -- $usagenomonad 60 61 -- ** Error Monad Usage 62 -- $usageerrormonad 63 64 -- ** Combined Error\/IO Monad Usage 65 -- $usageerroriomonad 66 67 -- * Types 68 -- $types 69 SectionSpec, OptionSpec, ConfigParser(..), 70 CPErrorData(..), CPError, 71 -- * Initialization 72 -- $initialization 73 emptyCP, 74 75 -- * Configuring the ConfigParser 76 -- $configuringcp 77 78 -- ** Access Functions 79 simpleAccess, interpolatingAccess, 80 81 -- * Reading 82 -- $reading 83 readfile, readhandle, readstring, 84 85 -- * Accessing Data 86 Get_C(..), 87 sections, has_section, 88 options, has_option, 89 items, 90 91 -- * Modifying Data 92 set, setshow, remove_option, 93 add_section, remove_section, 94 merge, 95 96 -- * Output Data 97 to_string 98 99 ) where 100 101import Data.ConfigFile.Types 102import Data.ConfigFile.Parser 103import Data.Either.Utils 104import Data.String.Utils 105import qualified Data.Map as Map 106import Data.List 107import System.IO(Handle) 108import Data.Char 109import Control.Monad.Error 110 111-- For interpolatingAccess 112import Text.ParserCombinators.Parsec.Error (errorMessages, Message(..)) 113import Text.ParserCombinators.Parsec (parse) 114 115---------------------------------------------------------------------- 116-- Basic types / default values 117---------------------------------------------------------------------- 118 119{- | The default empty 'Data.ConfigFile' object. 120 121The content contains only an empty mandatory @DEFAULT@ section. 122 123'optionxform' is set to @map toLower@. 124 125'usedefault' is set to @True@. 126 127'accessfunc' is set to 'simpleAccess'. 128-} 129emptyCP :: ConfigParser 130emptyCP = ConfigParser { content = fromAL [("DEFAULT", [])], 131 defaulthandler = defdefaulthandler, 132 optionxform = map toLower, 133 usedefault = True, 134 accessfunc = simpleAccess} 135 136{- | Low-level tool to convert a parsed object into a 'CPData' 137representation. Performs no option conversions or special handling 138of @DEFAULT@. -} 139fromAL :: ParseOutput -> CPData 140fromAL origal = 141 let conv :: CPData -> (String, [(String, String)]) -> CPData 142 conv fm sect = Map.insert (fst sect) (Map.fromList $ snd sect) fm 143 in 144 foldl conv Map.empty origal 145 146{- | Default (non-interpolating) access function -} 147simpleAccess :: MonadError CPError m => 148 ConfigParser -> SectionSpec -> OptionSpec -> m String 149simpleAccess cp s o = defdefaulthandler cp s (optionxform cp $ o) 150 151{- | Interpolating access function. Please see the Interpolation section 152above for a background on interpolation. 153 154Although the format string looks similar to one used by "Text.Printf", 155it is not the same. In particular, only the %(...)s format is supported. 156No width specifiers are supported and no conversions other than s are supported. 157 158To use this function, you must specify a maximum recursion depth for 159interpolation. This is used to prevent a stack overflow in the event that 160the configuration file contains an endless interpolation loop. Values of 10 161or so are usually more than enough, though you could probably go into the 162hundreds or thousands before you have actual problems. 163 164A value less than one will cause an instant error every time you attempt 165a lookup. 166 167This access method can cause 'get' and friends to return a new 'CPError': 168'InterpolationError'. This error would be returned when: 169 170 * The configuration file makes a reference to an option that does 171 not exist 172 173 * The maximum interpolation depth is exceeded 174 175 * There is a syntax error processing a %-directive in the configuration 176 file 177 178An interpolation lookup name specifies an option only. There is no provision 179to specify a section. Interpolation variables are looked up in the current 180section, and, if 'usedefault' is True, in @DEFAULT@ according to the normal 181logic. 182 183To use a literal percent sign, you must place @%%@ in the configuration 184file when interpolation is used. 185 186Here is how you might enable interpolation: 187 188>let cp2 = cp {accessfunc = interpolatingAccess 10} 189 190The @cp2@ object will now support interpolation with a maximum depth of 10. 191 -} 192interpolatingAccess :: MonadError CPError m => 193 Int -> 194 ConfigParser -> SectionSpec -> OptionSpec 195 -> m String 196 197interpolatingAccess maxdepth cp s o = 198 if maxdepth < 1 199 then interError "maximum interpolation depth exceeded" 200 else do x <- simpleAccess cp s o 201 case parse (interpmain $ lookupfunc) (s ++ "/" ++ o) x of 202 Left y -> case head (errorMessages y) of 203 Message z -> interError z 204 _ -> interError (show y) 205 Right y -> return y 206 where 207 lookupfunc = interpolatingAccess (maxdepth - 1) cp s 208 interError x = throwError (InterpolationError x, "interpolatingAccess") 209 210-- internal function: default handler 211defdefaulthandler :: MonadError CPError m => 212 ConfigParser -> SectionSpec -> OptionSpec -> m String 213 214defdefaulthandler cp sectn opt = 215 let fm = content cp 216 lookUp s o = do sect <- maybeToEither (NoSection s, 217 "get " ++ formatSO sectn opt) $ 218 Map.lookup s fm 219 maybeToEither (NoOption o, 220 "get " ++ formatSO sectn opt) $ 221 Map.lookup o sect 222 trydefault e = if (usedefault cp) 223 then 224 lookUp "DEFAULT" opt 225 -- Use original error if it's not in DEFAULT either 226 `catchError` (\_ -> throwError e) 227 else throwError e 228 in 229 lookUp sectn opt `catchError` trydefault 230 231 232{- | Combines two 'ConfigParser's into one. 233 234Any duplicate options are resolved to contain the value specified in 235the second parser. 236 237The 'ConfigParser' options in the resulting object will be set as they 238are in the second one passed to this function. -} 239merge :: ConfigParser -> ConfigParser -> ConfigParser 240merge src dest = 241 let conv :: String -> String 242 conv = optionxform dest 243 convFM :: CPOptions -> CPOptions 244 convFM = Map.fromList . map (\x -> (conv (fst x), snd x)) . Map.toList 245 mergesects a b = Map.union a b 246 in 247 dest { content = Map.unionWith mergesects 248 (content dest) (Map.map convFM (content src)) } 249 250{- | Utility to do a special case merge. -} 251readutil :: ConfigParser -> ParseOutput -> ConfigParser 252readutil old new = merge old $ old { content = fromAL new } 253 254{- | Loads data from the specified file. It is then combined with the 255given 'ConfigParser' using the semantics documented under 'merge' with the 256new data taking precedence over the old. However, unlike 257'merge', all the options 258as set in the old object are preserved since the on-disk representation 259does not convey those options. 260 261May return an error if there is a syntax error. May raise an exception if the file could not be accessed. 262-} 263--readfile :: ConfigParser -> FilePath ->IO (CPResult ConfigParser) 264readfile :: MonadError CPError m => ConfigParser -> FilePath -> IO (m ConfigParser) 265{- 266readfile cp fp = do n <- parse_file fp 267 return $ do y <- n 268 return $ readutil cp y 269-} 270readfile cp fp = do n <- parse_file fp 271 return $ n >>= (return . readutil cp) 272 273{- | Like 'readfile', but uses an already-open handle. You should 274use 'readfile' instead of this if possible, since it will be able to 275generate better error messages. 276 277Errors would be returned on a syntax error. 278-} 279--readhandle :: ConfigParser -> Handle -> IO (CPResult ConfigParser) 280readhandle :: MonadError CPError m => ConfigParser -> Handle -> IO (m ConfigParser) 281readhandle cp h = do n <- parse_handle h 282 return $ n >>= (return . (readutil cp)) 283 284{- | Like 'readfile', but uses a string. You should use 'readfile' 285instead of this if you are processing a file, since it can generate 286better error messages. 287 288Errors would be returned on a syntax error. 289-} 290readstring :: MonadError CPError m => 291 ConfigParser -> String -> m ConfigParser 292readstring cp s = do 293 n <- parse_string s 294 return $ readutil cp n 295 296{- | Returns a list of sections in your configuration file. Never includes 297the always-present section @DEFAULT@. -} 298sections :: ConfigParser -> [SectionSpec] 299sections = filter (/= "DEFAULT") . Map.keys . content 300 301{- | Indicates whether the given section exists. 302 303No special @DEFAULT@ processing is done. -} 304has_section :: ConfigParser -> SectionSpec -> Bool 305has_section cp x = Map.member x (content cp) 306 307{- | Adds the specified section name. Returns a 308'SectionAlreadyExists' error if the 309section was already present. Otherwise, returns the new 310'ConfigParser' object.-} 311add_section :: MonadError CPError m => 312 ConfigParser -> SectionSpec -> m ConfigParser 313add_section cp s = 314 if has_section cp s 315 then throwError $ (SectionAlreadyExists s, "add_section") 316 else return $ cp {content = Map.insert s Map.empty (content cp)} 317 318{- | Removes the specified section. Returns a 'NoSection' error if 319the section does not exist; otherwise, returns the new 'ConfigParser' 320object. 321 322This call may not be used to remove the @DEFAULT@ section. Attempting to do 323so will always cause a 'NoSection' error. 324 -} 325remove_section :: MonadError CPError m => 326 ConfigParser -> SectionSpec -> m ConfigParser 327remove_section _ "DEFAULT" = throwError $ (NoSection "DEFAULT", "remove_section") 328remove_section cp s = 329 if has_section cp s 330 then return $ cp {content = Map.delete s (content cp)} 331 else throwError $ (NoSection s, "remove_section") 332 333{- | Removes the specified option. Returns a 'NoSection' error if the 334section does not exist and a 'NoOption' error if the option does not 335exist. Otherwise, returns the new 'ConfigParser' object. 336-} 337remove_option :: MonadError CPError m => 338 ConfigParser -> SectionSpec -> OptionSpec -> m ConfigParser 339remove_option cp s passedo = 340 do sectmap <- maybeToEither (NoSection s, 341 "remove_option " ++ formatSO s passedo) $ 342 Map.lookup s (content cp) 343 let o = (optionxform cp) passedo 344 let newsect = Map.delete o sectmap 345 let newmap = Map.insert s newsect (content cp) 346 if Map.member o sectmap 347 then return $ cp {content = newmap} 348 else throwError $ (NoOption o, 349 "remove_option " ++ formatSO s passedo) 350 351{- | Returns a list of the names of all the options present in the 352given section. 353 354Returns an error if the given section does not exist. 355-} 356options :: MonadError CPError m => 357 ConfigParser -> SectionSpec -> m [OptionSpec] 358options cp x = maybeToEither (NoSection x, "options") $ 359 do 360 o <- Map.lookup x (content cp) 361 return $ Map.keys o 362 363{- | Indicates whether the given option is present. Returns True 364only if the given section is present AND the given option is present 365in that section. No special @DEFAULT@ processing is done. No 366exception could be raised or error returned. 367-} 368has_option :: ConfigParser -> SectionSpec -> OptionSpec -> Bool 369has_option cp s o = 370 let c = content cp 371 v = do secthash <- Map.lookup s c 372 return $ Map.member (optionxform cp $ o) secthash 373 in maybe False id v 374 375{- | The class representing the data types that can be returned by "get". 376-} 377class Get_C a where 378 {- | Retrieves a string from the configuration file. 379 380When used in a context where a String is expected, returns that string verbatim. 381 382When used in a context where a Bool is expected, parses the string to 383a Boolean value (see logic below). 384 385When used in a context where anything that is an instance of Read is expected, 386calls read to parse the item. 387 388An error will be returned of no such option could be found or if it could 389not be parsed as a boolean (when returning a Bool). 390 391When parsing to a Bool, strings are case-insentively converted as follows: 392 393The following will produce a True value: 394 395 * 1 396 397 * yes 398 399 * on 400 401 * enabled 402 403 * true 404 405The following will produce a False value: 406 407 * 0 408 409 * no 410 411 * off 412 413 * disabled 414 415 * false -} 416 get :: MonadError CPError m => ConfigParser -> SectionSpec -> OptionSpec -> m a 417 418instance Get_C String where 419 get cp s o = eitherToMonadError $ (accessfunc cp) cp s o 420 421instance Get_C Bool where 422 get = getbool 423 424instance Read t => Get_C t where 425 get = genericget 426 427-- Based on code from Neil Mitchell's safe-0.3.3 package. 428readMaybe :: Read a => String -> Maybe a 429readMaybe s = case [x | (x, t) <- reads s, ("","") <- lex t] of 430 [x] -> Just x 431 _ -> Nothing 432 433genericget :: (Read b, MonadError CPError m) => ConfigParser -> SectionSpec -> OptionSpec -> m b 434genericget cp s o = do 435 val <- get cp s o 436 let errMsg = "couldn't parse value " ++ val ++ " from " ++ formatSO s o 437 maybe (throwError (ParseError errMsg, "genericget")) 438 return 439 $ readMaybe val 440 441getbool :: MonadError CPError m => 442 ConfigParser -> SectionSpec -> OptionSpec -> m Bool 443getbool cp s o = 444 do val <- get cp s o 445 case map toLower . strip $ val of 446 "1" -> return True 447 "yes" -> return True 448 "on" -> return True 449 "enabled" -> return True 450 "true" -> return True 451 "0" -> return False 452 "no" -> return False 453 "off" -> return False 454 "disabled" -> return False 455 "false" -> return False 456 _ -> throwError (ParseError $ "couldn't parse bool " ++ 457 val ++ " from " ++ formatSO s o, "getbool") 458 459formatSO :: [Char] -> [Char] -> [Char] 460formatSO s o = 461 "(" ++ s ++ "/" ++ o ++ ")" 462 463 464{- | Returns a list of @(optionname, value)@ pairs representing the content 465of the given section. Returns an error the section is invalid. -} 466items :: MonadError CPError m => 467 ConfigParser -> SectionSpec -> m [(OptionSpec, String)] 468items cp s = do fm <- maybeToEither (NoSection s, "items") $ 469 Map.lookup s (content cp) 470 return $ Map.toList fm 471 472{- | Sets the option to a new value, replacing an existing one if it exists. 473 474Returns an error if the section does not exist. -} 475set :: MonadError CPError m => 476 ConfigParser -> SectionSpec -> OptionSpec -> String -> m ConfigParser 477set cp s passedo val = 478 do sectmap <- maybeToEither (NoSection s, "set " ++ formatSO s passedo) $ 479 Map.lookup s (content cp) 480 let o = (optionxform cp) passedo 481 let newsect = Map.insert o val sectmap 482 let newmap = Map.insert s newsect (content cp) 483 return $ cp { content = newmap} 484 485{- | Sets the option to a new value, replacing an existing one if it exists. 486It requires only a showable value as its parameter. 487This can be used with bool values, as well as numeric ones. 488 489Returns an error if the section does not exist. -} 490setshow :: (Show a, MonadError CPError m) => 491 ConfigParser -> SectionSpec -> OptionSpec -> a -> m ConfigParser 492setshow cp s o val = set cp s o (show val) 493 494{- | Converts the 'ConfigParser' to a string representation that could be 495later re-parsed by this module or modified by a human. 496 497Note that this does not necessarily re-create a file that was originally 498loaded. Things may occur in a different order, comments will be removed, 499etc. The conversion makes an effort to make the result human-editable, 500but it does not make an effort to make the result identical to the original 501input. 502 503The result is, however, guaranteed to parse the same as the original input. 504 -} 505to_string :: ConfigParser -> String 506to_string cp = 507 let gen_option (key, value) = 508 key ++ ": " ++ (replace "\n" "\n " value) ++ "\n" 509 gen_section (sect, valfm) = -- gen a section, but omit DEFAULT if empty 510 if (sect /= "DEFAULT") || (Map.size valfm > 0) 511 then "[" ++ sect ++ "]\n" ++ 512 (concat $ map gen_option (Map.toList valfm)) ++ "\n" 513 else "" 514 in 515 concat $ map gen_section (Map.toList (content cp)) 516 517---------------------------------------------------------------------- 518-- Docs 519---------------------------------------------------------------------- 520 521{- $introduction 522 523Many programs need configuration files. These configuration files are 524typically used to configure certain runtime behaviors that need to be 525saved across sessions. Various different configuration file formats 526exist. 527 528The ConfigParser module attempts to define a standard format that is 529easy for the user to edit, easy for the programmer to work with, yet 530remains powerful and flexible. 531-} 532 533{- $features 534 535For the programmer, this module provides: 536 537 * Simple calls to both read /and write/ configuration files 538 539 * Call that can generate a string version of a file that is 540 re-parsable by this module (useful for, for instance, sending the 541 file down a network) 542 543 * Segmented configuration files that let you separate configuration 544 into distinct sections, each with its own namespace. This can be 545 used to configure multiple modules in one file, to configure 546 multiple instances of a single object, etc. 547 548 * On-the-fly parsing of integer, boolean, float, multi-line string values, 549 and anything else Haskell's read can deal with 550 551 * It is possible to make a configuration file parsable by this 552 module, the Unix shell, and\/or Unix make, though some feautres are, 553 of course, not compatible with these other tools. 554 555 * Syntax checking with error reporting including line numbers 556 557 * Implemented in pure Haskell. No dependencies on modules outside 558 the standard library distributed with Haskell compilers or interpreters. 559 All calls except those that read directly from a handle are pure calls 560 and can be used outside the IO monad. 561 562 * Comprehensive documentation 563 564 * Extensible API 565 566 * Complete compatibility with Python's ConfigParser module, or my 567 ConfigParser module for OCaml, part of my MissingLib package. 568 569For the user, this module provides: 570 571 * Easily human-editable configuration files with a clear, concise, 572 and consistent format 573 574 * Configuration file format consistent with other familiar formats 575 (\/etc\/passwd is a valid ConfigParser file) 576 577 * No need to understand semantics of markup languages like XML 578-} 579 580{- $history 581 582This module is based on Python's ConfigParser module at 583<http://www.python.org/doc/current/lib/module-ConfigParser.html>. I had 584earlier developed an OCaml implementation as part of my MissingLib library 585at <gopher://gopher.quux.org/devel/missinglib>. 586 587While the API of these three modules is similar, and the aim is to preserve all 588useful features of the original Python module, there are some differences 589in the implementation details. This module is a complete, clean re-implementation 590in Haskell, not a Haskell translation of a Python program. As such, the feature 591set is slightly different. 592-} 593 594{- $format 595 596The basic configuration file format resembles that of an old-style 597Windows .INI file. Here are two samples: 598 599>debug = yes 600>inputfile = /etc/passwd 601>names = Peter, Paul, Mary, George, Abrahaham, John, Bill, Gerald, Richard, 602> Franklin, Woodrow 603>color = red 604 605This defines a file without any explicit section, so all items will 606occur within the default section @DEFAULT@. The @debug@ option can be read 607as a boolean or a string. The remaining items can be read as a string 608only. The @names@ entry spans two lines -- any line starting with 609whitespace, and containing something other than whitespace or 610comments, is taken as a continuation of the previous line. 611 612Here's another example: 613 614># Default options 615>[DEFAULT] 616>hostname: localhost 617># Options for the first file 618>[file1] 619>location: /usr/local 620>user: Fred 621>uid: 1000 622>optionaltext: Hello, this entire string is included 623>[file2] 624>location: /opt 625>user: Fred 626>uid: 1001 627 628This file defines three sections. The @DEFAULT@ section specifies an 629entry @hostname@. If you attempt to read the hostname option in any 630section, and that section doesn't define @hostname@, you will get the 631value from @DEFAULT@ instead. This is a nice time-saver. You can also 632note that you can use colons instead of the = character to separate 633option names from option entries. 634-} 635 636{- $whitespace 637 638Whitespace (spaces, tabs, etc) is automatically stripped from the 639beginning and end of all strings. Thus, users can insert whitespace 640before\/after the colon or equal sign if they like, and it will be 641automatically stripped. 642 643Blank lines or lines consisting solely of whitespace are ignored. 644 645A line giving an option or a section name may not begin with white space. 646This requirement is necessary so there is no ambiguity between such lines 647and continuation lines for multi-line options. 648 649-} 650 651{- $comments 652 653Comments are introduced with the pound sign @#@ or the semicolon @;@. They 654cause the parser to ignore everything from that character to the end 655of the line. 656 657Comments /may not/ occur within the definitions of options; that is, you 658may not place a comment in the middle of a line such as @user: Fred@. 659That is because the parser considers the comment characters part 660of the string; otherwise, you'd be unable to use those characters in 661your strings. You can, however, \"comment out\" options by putting the 662comment character at the start of the line. 663 664-} 665 666{- $casesens 667 668By default, section names are case-sensitive but option names are 669not. The latter can be adjusted by adjusting 'optionxform'. -} 670 671{- $interpolation 672 673Interpolation is an optional feature, disabled by default. If you replace 674the default 'accessfunc' ('simpleAccess') with 'interpolatingAccess', 675then you get interpolation support with 'get' and the other 'get'-based functions. 676 677As an example, consider the following file: 678 679>arch = i386 680>project = test 681>filename = test_%(arch)s.c 682>dir = /usr/src/%(filename)s 683>percent = 5%% 684 685With interpolation, you would get these results: 686 687>get cp "DEFAULT" "filename" -> "test_i386.c" 688>get cp "DEFAULT" "dir" -> "/usr/src/test_i386.c" 689>get cp "DEFAULT" "percent" -> "5%" 690 691For more details on interpolation, please see the documentation for the 692'interpolatingAccess' function. 693-} 694 695{- $usage 696 697The basic theory of working with ConfigParser is this: 698 699 1. Parse or build a 'ConfigParser' object 700 701 2. Work with it in one of several ways 702 703 3. To make changes, you discard the original object and use a new one. 704 Changes can be "chained" through one of several monads. 705 706The default 'ConfigParser' object that you always start with is 'emptyCP'. 707From here, you load data into it (merging data into the empty object), 708set up structures yourself, or adjust options. 709 710Let's take a look at some basic use cases. 711 712-} 713 714{- $usagenomonad 715You'll notice that many functions in this module return a 716@MonadError 'CPError'@ over some 717type. Although its definition is not this simple, you can consider this to be 718the same as returning @Either CPError a@. 719 720That is, these functions will return @Left error@ if there's a problem 721or @Right result@ if things are fine. The documentation for individual 722functions describes the specific circumstances in which an error may occur in 723more detail. 724 725Some people find it annoying to have to deal with errors manually. 726You can transform errors into exceptions in your code by using 727'Data.Either.Utils.forceEither'. Here's an example of this style of programming: 728 729> import Data.Either.Utils 730> do 731> val <- readfile emptyCP "/etc/foo.cfg" 732> let cp = forceEither val 733> putStrLn "Your setting is:" 734> putStrLn $ forceEither $ get cp "sect1" "opt1" 735 736In short, you can just put @forceEither $@ in front of every call that returns 737something that is a MonadError. 738This is still a pure functional call, so it can be used outside 739of the IO monads. The exception, however, can only be caught in the IO 740monad. 741 742If you don't want to bother with 'forceEither', you can use the error monad. It's simple and better... read on. 743-} 744 745{- $usageerrormonad 746 747The return type is actually defined in terms of the Error monad, which is 748itself based on the Either data type. 749 750Here's a neat example of chaining together calls to build up a 'ConfigParser' 751object: 752 753>do let cp = emptyCP 754> cp <- add_section cp "sect1" 755> cp <- set cp "sect1" "opt1" "foo" 756> cp <- set cp "sect1" "opt2" "bar" 757> options cp "sect1" 758 759The return value of this little snippet is @Right [\"opt1\", \"opt2\"]@. 760(Note to beginners: unlike the IO monad, you /can/ escape from the Error 761monad.) 762 763Although it's not obvious, there actually was error checking there. If 764any of those calls would have generated an error, processing would have 765stopped immediately and a @Left@ value would have been returned. Consider 766this example: 767 768>do let cp = emptyCP 769> cp <- add_section cp "sect1" 770> cp <- set cp "sect1" "opt1" "foo" 771> cp <- set cp "sect2" "opt2" "bar" 772> options cp "sect1" 773 774The return value from this is @Left ('NoSection' \"sect2\", \"set\")@. The 775second call to 'set' failed, so the final call was skipped, and the result 776of the entire computation was considered to be an error. 777 778You can combine this with the non-monadic style to get a final, pure value 779out of it: 780 781>forceEither $ do let cp = emptyCP 782> cp <- add_section cp "sect1" 783> cp <- set cp "sect1" "opt1" "foo" 784> cp <- set cp "sect1" "opt2" "bar" 785> options cp "sect1" 786 787This returns @[\"opt1\", \"opt2\"]@. A quite normal value. 788 789-} 790 791{- $usageerroriomonad 792 793You've seen a nice way to use this module in the Error monad and get an Either 794value out. But that's the Error monad, so IO is not permitted. 795Using Haskell's monad transformers, you can run it in the combined 796Error\/IO monad. That is, you will get an IO result back. Here is a full 797standalone example of doing that: 798 799>import Data.ConfigFile 800>import Control.Monad.Error 801> 802>main = do 803> rv <- runErrorT $ 804> do 805> cp <- join $ liftIO $ readfile emptyCP "/etc/passwd" 806> let x = cp 807> liftIO $ putStrLn "In the test" 808> nb <- get x "DEFAULT" "nobody" 809> liftIO $ putStrLn nb 810> foo <- get x "DEFAULT" "foo" 811> liftIO $ putStrLn foo 812> return "done" 813> print rv 814 815On my system, this prints: 816 817>In the test 818>x:65534:65534:nobody:/nonexistent:/bin/sh 819>Left (NoOption "foo","get") 820 821That is, my @\/etc\/passwd@ file contains a @nobody@ user but not a @foo@ user. 822 823Let's look at how that works. 824 825First, @main@ always runs in the IO monad only, so we take the result from 826the later calls and put it in @rv@. Note that the combined block 827is started with @runErrorT $ do@ instead of just @do@. 828 829To get something out of the call to 'readfile', we use 830@join $ liftIO $ readfile@. This will bring the result out of the IO monad 831into the combined monad and process it like usual. From here on, 832everything looks normal, except for IO calls. They are all executed under 833@liftIO@ so that the result value is properly brought into the combined 834monad. 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 835monad, this is wrapped in IO. So the final return type after applying 836@runErrorT@ is @IO (Either CPError String)@. 837 838In this case, there was an error, and processing stopped at that point just 839like the example of the pure Error monad. We print out the return value, 840so you see the error displayed as a @Left@ value. 841 842It all works quite easily. 843 844-} 845 846{- $configuringcp 847 848You may notice that the 'ConfigParser' object has some configurable parameters, 849such as 'usedefault'. In case you're not familiar with the Haskell syntax 850for working with these, you can use syntax like this to set these options: 851 852>let cp2 = cp { usedefault = False } 853 854This will create a new 'ConfigParser' that is the same as @cp@ except for 855the 'usedefault' field, which is now always False. The new object will be 856called @cp2@ in this example. 857-} 858 859{- $reading 860 861You can use these functions to read data from a file. 862 863A common idiom for loading a new object from stratch is: 864 865@cp <- 'readfile' 'emptyCP' \"\/etc\/foo.cfg\"@ 866 867Note the use of 'emptyCP'; this will essentially cause the file's data 868to be merged with the empty 'ConfigParser'. 869-} 870 871{- $types 872 873The code used to say this: 874 875>type CPResult a = MonadError CPError m => m a 876>simpleAccess :: ConfigParser -> SectionSpec -> OptionSpec -> CPResult String 877 878But Hugs did not support that type declaration. Therefore, types are now 879given like this: 880 881>simpleAccess :: MonadError CPError m => 882> ConfigParser -> SectionSpec -> OptionSpec -> m String 883 884Although it looks more confusing than before, it still means the same. 885The return value can still be treated as @Either CPError String@ if you so 886desire. 887-} 888