diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+0.2.2.0
+=======
+
+- Added `sections`, `sectionOf`, and `sectionsOf` helpers to the
+  vanilla API for more flexibility in working with section names
+- Put `test-doctest` behind a flag, which is disabled by default
+
 0.2.1.1
 =======
 
diff --git a/config-ini.cabal b/config-ini.cabal
--- a/config-ini.cabal
+++ b/config-ini.cabal
@@ -1,5 +1,5 @@
 name:             config-ini
-version:          0.2.1.1
+version:          0.2.2.0
 synopsis:         A library for simple INI-based configuration files.
 homepage:         https://github.com/aisamanra/config-ini
 bug-reports:      https://github.com/aisamanra/config-ini/issues
@@ -18,7 +18,7 @@
 license-file:     LICENSE
 author:           Getty Ritter <config-ini@infinitenegativeutility.com>
 maintainer:       Getty Ritter <config-ini@infinitenegativeutility.com>
-copyright:        ©2017 Getty Ritter
+copyright:        ©2018 Getty Ritter
 category:         Configuration
 build-type:       Simple
 cabal-version:    >= 1.18
@@ -33,6 +33,10 @@
   type: git
   location: git://github.com/aisamanra/config-ini.git
 
+flag enable-doctests
+  description: Build doctest modules as well (can be finicky)
+  default:     False
+
 library
   hs-source-dirs:      src
   exposed-modules:     Data.Ini.Config
@@ -75,7 +79,7 @@
                   , directory
 
 test-suite test-doctest
-  if impl(ghc < 7.10)
+  if impl(ghc < 7.10) || !flag(enable-doctests)
     buildable:      False
   type:             exitcode-stdio-1.0
   ghc-options:      -Wall
diff --git a/src/Data/Ini/Config.hs b/src/Data/Ini/Config.hs
--- a/src/Data/Ini/Config.hs
+++ b/src/Data/Ini/Config.hs
@@ -12,7 +12,7 @@
 For example, the following INI file has two sections, @NETWORK@
 and @LOCAL@, and each contains its own key-value pairs. Comments,
 which begin with @#@ or @;@, are ignored:
---
+
 > [NETWORK]
 > host = example.com
 > port = 7878
@@ -20,7 +20,7 @@
 > # here is a comment
 > [LOCAL]
 > user = terry
---
+
 The combinators provided here are designed to write quick and
 idiomatic parsers for files of this form. Sections are parsed by
 'IniParser' computations, like 'section' and its variations,
@@ -28,7 +28,7 @@
 computations, like 'field' and its variations. If we want to
 parse an INI file like the one above, treating the entire
 @LOCAL@ section as optional, we can write it like this:
---
+
 > data Config = Config
 >   { cfNetwork :: NetworkConfig, cfLocal :: Maybe LocalConfig }
 >     deriving (Eq, Show)
@@ -50,11 +50,12 @@
 >   locCf <- sectionMb "LOCAL" $
 >     LocalConfig <$> field "user"
 >   return Config { cfNetwork = netCf, cfLocal = locCf }
---
+
+
 We can run our computation with 'parseIniFile', which,
 when run on our example file above, would produce the
 following:
---
+
 >>> parseIniFile example configParser
 Right (Config {cfNetwork = NetworkConfig {netHost = "example.com", netPort = 7878}, cfLocal = Just (LocalConfig {localUser = "terry"})})
 
@@ -73,6 +74,9 @@
 , SectionParser
 -- * Section-Level Parsing
 , section
+, sections
+, sectionOf
+, sectionsOf
 , sectionMb
 , sectionDef
 -- * Field-Level Parsing
@@ -149,6 +153,65 @@
   case lkp (normalize name) ini of
     Nothing  -> Left ("No top-level section named " ++ show name)
     Just sec -> runExceptT thunk sec
+
+-- | Find multiple named sections in the INI file and parse them all
+--   with the provided section parser. In order to support classic INI
+--   files with capitalized section names, section lookup is
+--   __case-insensitive__.
+--
+--   >>> parseIniFile "[ONE]\nx = hello\n[ONE]\nx = goodbye\n" $ sections "ONE" (field "x")
+--   Right (fromList ["hello","goodbye"])
+--   >>> parseIniFile "[ONE]\nx = hello\n" $ sections "TWO" (field "x")
+--   Right (fromList [])
+sections :: Text -> SectionParser a -> IniParser (Seq a)
+sections name (SectionParser thunk) = IniParser $ ExceptT $ \(RawIni ini) ->
+  let name' = normalize name
+  in mapM (runExceptT thunk . snd)
+          (Seq.filter (\ (t, _) -> t == name') ini)
+
+-- | A call to @sectionOf f@ will apply @f@ to each section name and,
+--   if @f@ produces a "Just" value, pass the extracted value in order
+--   to get the "SectionParser" to use for that section. This will
+--   find at most one section, and will produce an error if no section
+--   exists.
+--
+--   >>> parseIniFile "[FOO]\nx = hello\n" $ sectionOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x"))
+--   Right ("F","hello")
+--   >>> parseIniFile "[BAR]\nx = hello\n" $ sectionOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x"))
+--   Left "No matching top-level section"
+sectionOf :: (Text -> Maybe b) -> (b -> SectionParser a) -> IniParser a
+sectionOf fn sectionParser = IniParser $ ExceptT $ \(RawIni ini) ->
+  let go Seq.EmptyL = Left "No matching top-level section"
+      go ((t, sec) Seq.:< rs)
+        | Just v <- fn (actualText t) =
+            let SectionParser thunk = sectionParser v
+            in runExceptT thunk sec
+        | otherwise = go (Seq.viewl rs)
+  in go (Seq.viewl ini)
+
+
+-- | A call to @sectionsOf f@ will apply @f@ to each section name and,
+--   if @f@ produces a @Just@ value, pass the extracted value in order
+--   to get the "SectionParser" to use for that section. This will
+--   return every section for which the call to @f@ produces a "Just"
+--   value.
+--
+--   >>> parseIniFile "[FOO]\nx = hello\n[BOO]\nx = goodbye\n" $ sectionsOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x"))
+--   Right (fromList [("F","hello"),("B","goodbye")])
+--   >>> parseIniFile "[BAR]\nx = hello\n" $ sectionsOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x"))
+--   Right (fromList [])
+sectionsOf :: (Text -> Maybe b) -> (b -> SectionParser a) -> IniParser (Seq a)
+sectionsOf fn sectionParser = IniParser $ ExceptT $ \(RawIni ini) ->
+  let go Seq.EmptyL = return Seq.empty
+      go ((t, sec) Seq.:< rs)
+        | Just v <- fn (actualText t) =
+            let SectionParser thunk = sectionParser v
+            in do
+              x <- runExceptT thunk sec
+              xs <- go (Seq.viewl rs)
+              return (x Seq.<| xs)
+        | otherwise = go (Seq.viewl rs)
+  in go (Seq.viewl ini)
 
 -- | Find a named section in the INI file and parse it with the provided
 --   section parser, returning 'Nothing' if the section does not exist.
