diff --git a/cabal-install/Distribution/Client/ParseUtils.hs b/cabal-install/Distribution/Client/ParseUtils.hs
index c2cb1c5661708e8e17ac46713352f39ba65fc0f3..d54b3be1003ff2ed953980fe5738675a308ff138 100644
--- a/cabal-install/Distribution/Client/ParseUtils.hs
+++ b/cabal-install/Distribution/Client/ParseUtils.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE ExistentialQuantification, NamedFieldPuns #-}
+
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  Distribution.Client.ParseUtils
@@ -7,13 +9,41 @@
 -- Parsing utilities.
 -----------------------------------------------------------------------------
 
-module Distribution.Client.ParseUtils ( parseFields, ppFields, ppSection )
+module Distribution.Client.ParseUtils (
+
+    -- * Fields and field utilities
+    FieldDescr(..),
+    liftField,
+    liftFields,
+    filterFields,
+    mapFieldNames,
+    commandOptionToField,
+    commandOptionsToFields,
+
+    -- * Sections and utilities
+    SectionDescr(..),
+    liftSection,
+
+    -- * Parsing and printing flat config
+    parseFields,
+    ppFields,
+    ppSection,
+
+    -- * Parsing and printing config with sections and subsections
+    parseFieldsAndSections,
+    ppFieldsAndSections,
+
+    -- ** Top level of config files
+    parseConfig,
+    showConfig,
+  )
        where
 
 import Distribution.ParseUtils
-         ( FieldDescr(..), ParseResult(..), warning, lineNo )
-import qualified Distribution.ParseUtils as ParseUtils
-         ( Field(..) )
+         ( FieldDescr(..), ParseResult(..), warning, LineNo, lineNo
+         , Field(..), liftField, readFieldsFlat )
+import Distribution.Simple.Command
+         ( OptionField, viewAsFieldDescr )
 
 import Control.Monad    ( foldM )
 import Text.PrettyPrint ( (<>), (<+>), ($+$) )
@@ -21,17 +51,101 @@ import qualified Data.Map as Map
 import qualified Text.PrettyPrint as Disp
          ( Doc, text, colon, vcat, empty, isEmpty, nest )
 
-parseFields :: [FieldDescr a] -> a -> [ParseUtils.Field] -> ParseResult a
-parseFields fields = foldM setField
+
+-------------------------
+-- FieldDescr utilities
+--
+
+liftFields :: (b -> a)
+           -> (a -> b -> b)
+           -> [FieldDescr a]
+           -> [FieldDescr b]
+liftFields get set = map (liftField get set)
+
+
+-- | Given a collection of field descriptions, keep only a given list of them,
+-- identified by name.
+--
+filterFields :: [String] -> [FieldDescr a] -> [FieldDescr a]
+filterFields includeFields = filter ((`elem` includeFields) . fieldName)
+
+-- | Apply a name mangling function to the field names of all the field
+-- descriptions. The typical use case is to apply some prefix.
+--
+mapFieldNames :: (String -> String) -> [FieldDescr a] -> [FieldDescr a]
+mapFieldNames mangleName =
+    map (\descr -> descr { fieldName = mangleName (fieldName descr) })
+
+
+-- | Reuse a command line 'OptionField' as a config file 'FieldDescr'.
+--
+commandOptionToField :: OptionField a -> FieldDescr a
+commandOptionToField = viewAsFieldDescr
+
+-- | Reuse a bunch of command line 'OptionField's as config file 'FieldDescr's.
+--
+commandOptionsToFields :: [OptionField a] -> [FieldDescr a]
+commandOptionsToFields = map viewAsFieldDescr
+
+
+------------------------------------------
+-- SectionDescr definition and utilities
+--
+
+-- | The description of a section in a config file. It can contains both
+-- fields and optionally further subsections. See also 'FieldDescr'.
+--
+data SectionDescr a = forall b. SectionDescr {
+       sectionName        :: String,
+       sectionFields      :: [FieldDescr b],
+       sectionSubsections :: [SectionDescr b],
+       sectionGet         :: a -> [(String, b)],
+       sectionSet         :: LineNo -> String -> b -> a -> ParseResult a,
+       sectionEmpty       :: b
+     }
+
+-- | To help construction of config file descriptions in a modular way it is
+-- useful to define fields and sections on local types and then hoist them
+-- into the parent types when combining them in bigger descriptions.
+--
+-- This is essentially a lens operation for 'SectionDescr' to help embedding
+-- one inside another.
+--
+liftSection :: (b -> a)
+            -> (a -> b -> b)
+            -> SectionDescr a
+            -> SectionDescr b
+liftSection get' set' (SectionDescr name fields sections get set empty) =
+    let sectionGet' = get . get'
+        sectionSet' lineno param x y = do
+          x' <- set lineno param x (get' y)
+          return (set' x' y)
+     in SectionDescr name fields sections sectionGet' sectionSet' empty
+
+
+-------------------------------------
+-- Parsing and printing flat config
+--
+
+-- | Parse a bunch of semi-parsed 'Field's according to a set of field
+-- descriptions. It accumulates the result on top of a given initial value.
+--
+-- This only covers the case of flat configuration without subsections. See
+-- also 'parseFieldsAndSections'.
+--
+parseFields :: [FieldDescr a] -> a -> [Field] -> ParseResult a
+parseFields fieldDescrs =
+    foldM setField
   where
-    fieldMap = Map.fromList
-      [ (name, f) | f@(FieldDescr name _ _) <- fields ]
-    setField accum (ParseUtils.F line name value) =
+    fieldMap = Map.fromList [ (fieldName f, f) | f <- fieldDescrs ]
+
+    setField accum (F line name value) =
       case Map.lookup name fieldMap of
         Just (FieldDescr _ _ set) -> set line value accum
         Nothing -> do
           warning $ "Unrecognized field " ++ name ++ " on line " ++ show line
           return accum
+
     setField accum f = do
       warning $ "Unrecognized stanza on line " ++ show (lineNo f)
       return accum
@@ -40,8 +154,9 @@ parseFields fields = foldM setField
 -- that also optionally print default values for empty fields as comments.
 --
 ppFields :: [FieldDescr a] -> (Maybe a) -> a -> Disp.Doc
-ppFields fields def cur = Disp.vcat [ ppField name (fmap getter def) (getter cur)
-                                    | FieldDescr name getter _ <- fields]
+ppFields fields def cur =
+    Disp.vcat [ ppField name (fmap getter def) (getter cur)
+              | FieldDescr name getter _ <- fields]
 
 ppField :: String -> (Maybe Disp.Doc) -> Disp.Doc -> Disp.Doc
 ppField name mdef cur
@@ -50,6 +165,11 @@ ppField name mdef cur
                                 <> Disp.colon <+> def) mdef
   | otherwise        = Disp.text name <> Disp.colon <+> cur
 
+-- | Pretty print a section.
+--
+-- Since 'ppFields' does not cover subsections you can use this to add them.
+-- Or alternatively use a 'SectionDescr' and use 'ppFieldsAndSections'.
+--
 ppSection :: String -> String -> [FieldDescr a] -> (Maybe a) -> a -> Disp.Doc
 ppSection name arg fields def cur
   | Disp.isEmpty fieldsDoc = Disp.empty
@@ -59,3 +179,101 @@ ppSection name arg fields def cur
     fieldsDoc = ppFields fields def cur
     argDoc | arg == "" = Disp.empty
            | otherwise = Disp.text arg
+
+
+-----------------------------------------
+-- Parsing and printing non-flat config
+--
+
+-- | Much like 'parseFields' but it also allows subsections. The permitted
+-- subsections are given by a list of 'SectionDescr's.
+--
+parseFieldsAndSections :: [FieldDescr a] -> [SectionDescr a] -> a
+                       -> [Field] -> ParseResult a
+parseFieldsAndSections fieldDescrs sectionDescrs =
+    foldM setField
+  where
+    fieldMap   = Map.fromList [ (fieldName   f, f) | f <- fieldDescrs   ]
+    sectionMap = Map.fromList [ (sectionName s, s) | s <- sectionDescrs ]
+
+    setField a (F line name value) =
+      case Map.lookup name fieldMap of
+        Just (FieldDescr _ _ set) -> set line value a
+        Nothing -> do
+          warning $ "Unrecognized field '" ++ name
+                 ++ "' on line " ++ show line
+          return a
+
+    setField a (Section line name param fields) =
+      case Map.lookup name sectionMap of
+        Just (SectionDescr _ fieldDescrs' sectionDescrs' _ set sectionEmpty) -> do
+          b <- parseFieldsAndSections fieldDescrs' sectionDescrs' sectionEmpty fields
+          set line param b a
+        Nothing -> do
+          warning $ "Unrecognized section '" ++ name
+                 ++ "' on line " ++ show line
+          return a
+
+    setField accum (block@IfBlock {}) = do
+      warning $ "Unrecognized stanza on line " ++ show (lineNo block)
+      return accum
+
+-- | Much like 'ppFields' but also pretty prints any subsections. Subsection
+-- are only shown if they are non-empty.
+--
+-- Note that unlike 'ppFields', at present it does not support printing
+-- default values. If needed, adding such support would be quite reasonable.
+--
+ppFieldsAndSections :: [FieldDescr a] -> [SectionDescr a] -> a -> Disp.Doc
+ppFieldsAndSections fieldDescrs sectionDescrs val =
+    ppFields fieldDescrs Nothing val
+      $+$
+    Disp.vcat
+      [ Disp.text "" $+$ sectionDoc
+      | SectionDescr {
+          sectionName, sectionGet,
+          sectionFields, sectionSubsections
+        } <- sectionDescrs
+      , (param, x) <- sectionGet val
+      , let sectionDoc = ppSectionAndSubsections
+                           sectionName param
+                           sectionFields sectionSubsections x
+      , not (Disp.isEmpty sectionDoc)
+      ]
+
+-- | Unlike 'ppSection' which has to be called directly, this gets used via
+-- 'ppFieldsAndSections' and so does not need to be exported.
+--
+ppSectionAndSubsections :: String -> String
+                        -> [FieldDescr a] -> [SectionDescr a] -> a -> Disp.Doc
+ppSectionAndSubsections name arg fields sections cur
+  | Disp.isEmpty fieldsDoc = Disp.empty
+  | otherwise              = Disp.text name <+> argDoc
+                             $+$ (Disp.nest 2 fieldsDoc)
+  where
+    fieldsDoc = showConfig fields sections cur
+    argDoc | arg == "" = Disp.empty
+           | otherwise = Disp.text arg
+
+
+-----------------------------------------------
+-- Top level config file parsing and printing
+--
+
+-- | Parse a string in the config file syntax into a value, based on a
+-- description of the configuration file in terms of its fields and sections.
+--
+-- It accumulates the result on top of a given initial (typically empty) value.
+--
+parseConfig :: [FieldDescr a] -> [SectionDescr a] -> a
+            -> String -> ParseResult a
+parseConfig fieldDescrs sectionDescrs empty str =
+      parseFieldsAndSections fieldDescrs sectionDescrs empty
+  =<< readFieldsFlat str
+
+-- | Render a value in the config file syntax, based on a description of the
+-- configuration file in terms of its fields and sections.
+--
+showConfig :: [FieldDescr a] -> [SectionDescr a] -> a -> Disp.Doc
+showConfig = ppFieldsAndSections
+