diff --git a/hpp.cabal b/hpp.cabal
--- a/hpp.cabal
+++ b/hpp.cabal
@@ -1,5 +1,5 @@
 name:                hpp
-version:             0.4.0
+version:             0.4.1
 synopsis:            A Haskell pre-processor
 description:         See the README for usage examples
 license:             BSD3
@@ -30,7 +30,7 @@
                        Hpp.StringSig,
                        Hpp.Tokens,
                        Hpp.Types
-  build-depends:       base >=4.8 && <4.10, directory, time >=1.5, filepath,
+  build-depends:       base >=4.8 && <4.11, directory, time >=1.5, filepath,
                        transformers >=0.4, bytestring, bytestring-trie, ghc-prim
 
   if impl(ghc < 8)
@@ -42,7 +42,7 @@
 
 executable hpp
   main-is:             src/Main.hs
-  build-depends:       hpp, base >=4.8 && <4.10, directory, time >=1.5, filepath
+  build-depends:       hpp, base >=4.8 && <4.11, directory, time >=1.5, filepath
   hs-source-dirs:      .
   default-language:    Haskell2010
   ghc-options: -Wall
diff --git a/src/Hpp.hs b/src/Hpp.hs
--- a/src/Hpp.hs
+++ b/src/Hpp.hs
@@ -592,8 +592,8 @@
 
 -- | Run a stream of lines through the preprocessor.
 preprocess :: (Monad m, HppCaps m)
-           => ([String] -> src) -> [String] -> HppT [String] (Parser m [TOKEN]) ()
-preprocess _convertOutput src =
+           => [String] -> HppT [String] (Parser m [TOKEN]) ()
+preprocess src =
   do cfg <- getL config <$> getState
      prep <- prepareInput
      let prepOutput = if inhibitLinemarkers cfg then aux else pure
@@ -602,21 +602,35 @@
   where aux xs | sIsPrefixOf "#line" xs = []
                | otherwise = [xs]
 
+-- Note: `preprocess` is the workhorse of the library. We run the
+-- value it returns in `hppIO'` by interleaving interpretation of
+-- `HppT` with binds of types providing the `HppCaps`
+-- capabilities. When making things concrete, we specialize to
+-- `ExceptT`, `StateT`, and `Parser` (note that `Parser` is actually
+-- just another `StateT`).
+
+-- | A concreate choice of types to satisfy `HppCaps` as required by
+-- `preprocess`.
+dischargeHppCaps :: Monad m
+                 => Config -> Env
+                 -> Parser (StateT HppState (ExceptT Error m))
+                           i
+                           (Either (a, Error) b)
+                 -> m (Maybe Error)
+dischargeHppCaps cfg env' m =
+  runExceptT
+    (evalStateT
+       (evalParse (m >>= either (throwError . snd) return) [])
+       initialState)
+  >>= return . either Just (const Nothing)
+  where initialState = setL env env' $ emptyHppState cfg
+
 -- | General hpp runner against input source file lines; can return an
 -- 'Error' value if something goes wrong.
 hppIO' :: Config -> Env -> ([String] -> IO ()) -> [String] -> IO (Maybe Error)
 hppIO' cfg env' snk src =
-  runExceptT'
-    (evalStateT
-       (evalParse
-          ((>>= either (throwError . snd) return)
-           (runHpp (liftIO . readLines)
-                   (liftIO . snk)
-                   (preprocess id src)))
-          [])
-       initialState) >>= return . either Just (const Nothing)
-  where initialState = setL env env' $ emptyHppState cfg
-        runExceptT' = runExceptT :: ExceptT Error m a -> m (Either Error a)
+  dischargeHppCaps cfg env' $
+  runHpp (liftIO . readLines) (liftIO . snk) (preprocess src)
 
 -- | General hpp runner against input source file lines. Any errors
 -- encountered are thrown with 'error'.
diff --git a/tests/mcpp-validation.sh b/tests/mcpp-validation.sh
--- a/tests/mcpp-validation.sh
+++ b/tests/mcpp-validation.sh
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-HOST='http://tcpdiag.dl.sourceforge.net/project/mcpp/mcpp/V.2.7.2/'
+HOST='http://prdownloads.sourceforge.net/mcpp/mcpp-2.7.2.tar.gz?download'
 FILE='mcpp-2.7.2.tar.gz'
 GCC=gcc
 
@@ -18,7 +18,7 @@
 
 if ! [ -d "mcpp-2.7.2" ]; then
   echo 'Downloading MCPP source'
-  curl "${HOST}${FILE}" > "${FILE}"
+  curl -L "${HOST}" > "${FILE}"
   tar xf ${FILE}
 fi
 
