diff --git a/cabal2nix.cabal b/cabal2nix.cabal
--- a/cabal2nix.cabal
+++ b/cabal2nix.cabal
@@ -1,40 +1,55 @@
 Name:                   cabal2nix
-Version:                1.7
-Copyright:              (c) 2011 Peter Simons
+Version:                1.8
+Copyright:              (c) 2011 Peter Simons and Andres Loeh
 License:                BSD3
 License-File:           LICENSE
-Author:                 Peter Simons <simons@cryp.to>
-Maintainer:             Peter Simons <simons@cryp.to>
-Homepage:               http://github.com/peti/cabal2nix
+Author:                 Peter Simons <simons@cryp.to>, Andres Loeh <mail@andres-loeh.de>
+Maintainer:             Nix Developers <nix-dev@cs.uu.nl>
+Homepage:               http://github.com/haskell4nix/cabal2nix
 Category:               Distribution
 Synopsis:               Convert Cabal files into Nix build instructions
 Cabal-Version:          >= 1.6
 Build-Type:             Simple
 Tested-With:            GHC == 7.0.4
-Description:            This utility converts Cabal files into Nix build
-			instructions. The commandline syntax is:
-			.
-			> cabal2nix cabal-file-uri [sha256-hash]
-			.
-			The first argument is the path to the cabal
-			file. That path can be an HTTP URL or local file
-			path. For example:
-			.
-			> cabal2nix http://hackage.haskell.org/packages/archive/cabal2nix/1.7/cabal2nix.cabal
-			> cabal2nix file:///tmp/cabal2nix.cabal
-			> cabal2nix /tmp/cabal2nix.cabal 0m7zgsd1pxmw504xpvl7dg25ana6dkk1mcyjj4c1wdbkvhvbn3gn
-			.
-			The second argument -- the hash of the source
-			tarball -- is optional. If it's not specified,
-			cabal2nix calls @nix-prefetch-url@ to determine
-			the hash automatically. This causes network
-			traffic, obviously.
+Description:
+  The cabal2nix utility converts Cabal files into Nix build instructions. The
+  commandline syntax is:
+  .
+  > Usage: cabal2nix [options] url-to-cabal-file
+  >   -h             --help                   show this help text
+  >                  --sha256=HASH            sha256 hash of source tarball
+  >   -m MAINTAINER  --maintainer=MAINTAINER  maintainer of this package (may be specified multiple times)
+  >   -p PLATFORM    --platform=PLATFORM      supported build platforms (may be specified multiple times)
+  >
+  > Recognized URI schemes:
+  >
+  >   cabal://pkgname-pkgversion       download the specified package from Hackage
+  >   http://host/path                 fetch the Cabel file via HTTP
+  >   file:///local/path               load the Cabal file from the local disk
+  >   /local/path                      abbreviated version of file URI
+  .
+  The only required argument is the path to the cabal file. For example:
+  .
+  > cabal2nix http://hackage.haskell.org/packages/archive/cabal2nix/1.8/cabal2nix.cabal
+  > cabal2nix cabal://cabal2nix-1.8
+  .
+  If the @--sha256@ option has not been specified, cabal2nix calls
+  @nix-prefetch-url@ to determine the hash automatically. This causes
+  network traffic, obviously.
 
 Source-Repository head
   Type:                 git
-  Location:             git://github.com/peti/cabal2nix.git
+  Location:             git://github.com/haskell4nix/cabal2nix.git
 
 Executable cabal2nix
-  main-is:              cabal2nix.hs
-  Build-Depends:        base >= 3 && < 5, Cabal, process, HTTP
+  main-is:              Cabal2Nix.hs
+  hs-source-dirs:       src
+  Build-Depends:        base >= 3 && < 5, Cabal, process, HTTP, pretty, filepath, directory
+  Ghc-Options:          -Wall
+
+Executable hackage4nix
+  main-is:              Hackage4Nix.hs
+  hs-source-dirs:       src
+  Build-Depends:        base >= 3 && < 5, Cabal, pretty, filepath, directory, bytestring,
+                        regex-posix, mtl, containers
   Ghc-Options:          -Wall
diff --git a/cabal2nix.hs b/cabal2nix.hs
deleted file mode 100644
--- a/cabal2nix.hs
+++ /dev/null
@@ -1,148 +0,0 @@
--- cabal2nix.hs
---
--- Copyright (c) 20011 Peter Simons <simons@cryp.to>
--- See LICENSE file for licensing details.
-
-module Main ( main ) where
-
-import System.IO
-import System.Environment
-import Control.Exception
-import System.Exit
-import Distribution.PackageDescription.Parse
-import Distribution.PackageDescription
-import Distribution.Package
-import Distribution.License
-import Data.Version
-import Data.List
-import Control.Monad
-import Data.Char
-import Network.HTTP
-import System.Process
-
-type PkgName = String
-type PkgVersion = [Int]
-type PkgSHA256 = String
-type PkgURL = String
-type PkgDescription = String
-type PkgLicense = License
-type PkgDependencies = [CondTree ConfVar [Dependency] ()]
-type PkgExtraLibs = [String]
-
-data Pkg = Pkg PkgName PkgVersion PkgSHA256 PkgURL PkgDescription PkgLicense PkgDependencies PkgExtraLibs
-  deriving (Show)
-
-toNixName :: String -> String
-toNixName [] = error "toNixName: empty string is not a valid argument"
-toNixName "Cabal" = "cabal"
-toNixName name = f name
-  where
-    f []                            = []
-    f ('-':c:cs) | c `notElem` "-"  = toUpper c : f cs
-    f ('-':_)                       = error ("unexpected package name " ++ show name)
-    f (c:cs)                        = c : f cs
-
-toNix :: Pkg -> String
-toNix (Pkg name ver sha256 url desc lic deps libs) =
-       "{" ++ exprArgs ++"}:\n\n"
-    ++ "cabal.mkDerivation (self : {\n"
-    ++ "  pname = " ++ show name ++ ";\n"
-    ++ "  version = \"" ++ showVer ++ "\";\n"
-    ++ "  sha256 = " ++ show sha256 ++ ";\n"
-    ++ "  propagatedBuildInputs = [" ++ depList ++ "];\n"
-    ++ "  meta = {\n"
-    ++ "    homepage = \"" ++ url ++ "\";\n"
-    ++ "    description = " ++ show desc ++ ";\n"
-    ++ "    license = " ++ showLic lic ++ ";\n"
-    ++ "  };\n"
-    ++ "})\n"
-    where
-      exprArgs = concat (intersperse "," ("cabal":pkgDeps))
-      showVer = concat (intersperse "." (map show ver))
-      depList = concat (intersperse " " pkgDeps)
-      pkgDeps :: [String]
-      pkgDeps = filter (/="cabal") $ nub $ sort $ map toNixName $
-                  libs ++ [ n | dep <- deps, Dependency (PackageName n) _ <- condTreeConstraints dep
-                              , n `notElem` ["base","containers"]
-                          ]
-      showLic (GPL Nothing)                     = show "GPL"
-      showLic (GPL (Just (Version [2] [])))     = "self.stdenv.lib.licenses.gpl2"
-      showLic (GPL (Just (Version [3] [])))     = "self.stdenv.lib.licenses.gpl3"
-      showLic (LGPL Nothing)                    = show "LGPL"
-      showLic (LGPL (Just (Version [2,1] [])))  = "self.stdenv.lib.licenses.lgpl21"
-      showLic (LGPL (Just (Version [3] [])))    = "self.stdenv.lib.licenses.lgpl3"
-      showLic BSD3                              = "self.stdenv.lib.licenses.bsd3"
-      showLic BSD4                              = "self.stdenv.lib.licenses.bsd4"
-      showLic MIT                               = "self.stdenv.lib.licenses.mit"
-      showLic PublicDomain                      = "self.stdenv.lib.licenses.publicDomain"
-      showLic AllRightsReserved                 = "unknown"
-      showLic OtherLicense                      = "unknown"
-      showLic l                                 = error $ "unknown license: " ++ show l
-
-cabal2nix :: GenericPackageDescription -> PkgSHA256 -> Pkg
-cabal2nix cabal sha256 = Pkg pkgname pkgver sha256 url desc lic (map simplify libDeps ++ map simplify exeDeps) (libs++libs')
-  where
-    pkg = packageDescription cabal
-    PackageName pkgname = pkgName (package pkg)
-    pkgver = versionBranch (pkgVersion (package pkg))
-    lic = license pkg
-    url = homepage pkg
-    desc = synopsis pkg
-    -- globalDeps = buildDepends pkg
-    libDeps = maybe [] (\x -> [x]) (condLibrary cabal)
-    exeDeps = [ tree | (_,tree) <- condExecutables cabal ]
-    libs = concat [ extraLibs (libBuildInfo (condTreeData x)) | x <- libDeps ]
-    libs' = concat [ extraLibs (buildInfo (condTreeData x)) | x <- exeDeps ]
-
-simplify :: CondTree ConfVar [Dependency] a -> CondTree ConfVar [Dependency] ()
-simplify (CondNode _ deps nodes) = CondNode () deps (map simp nodes)
-  where
-    simp (cond,tree,mtree) = (cond, simplify tree, maybe Nothing (Just . simplify) mtree)
-
-readCabalFile :: FilePath -> IO String
-readCabalFile path
-  | "http://" `isPrefixOf` path = Network.HTTP.simpleHTTP (getRequest path) >>= getResponseBody
-  | "file://" `isPrefixOf` path = readCabalFile (drop 7 path)
-  | otherwise                   = readFile path
-
-hashPackage :: GenericPackageDescription -> IO String
-hashPackage pkg = do
-    hash <- readProcess "bash" ["-c", "exec nix-prefetch-url 2>/dev/tty " ++ url] ""
-    return (reverse (dropWhile (=='\n') (reverse hash)))
-
-  where
-    url = "http://hackage.haskell.org/packages/archive/" ++ name ++ "/" ++ version ++ "/" ++ name ++ "-" ++ version ++ ".tar.gz"
-    PackageIdentifier (PackageName name) version' = package (packageDescription pkg)
-    version = showVersion version'
-
-main :: IO ()
-main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do
-  args <- getArgs
-
-  let usage = "Usage: cabal2nix url-to-cabal-file [sha256-hash]"
-
-  when (length args < 1 || length args > 2) $ do
-    mapM_ (hPutStrLn stderr) [ "*** invalid command line syntax"
-                             , usage
-                             ]
-    exitFailure
-
-  when ("--help" `elem` args || "-h" `elem` args) $ do
-    putStrLn usage
-    exitFailure
-
-  cabal' <- fmap parsePackageDescription (readCabalFile (head args))
-  cabal <- case cabal' of
-             ParseOk _ a -> return a
-             ParseFailed err -> do
-               hPutStrLn stderr ("*** cannot parse cabal file: " ++ show err)
-               exitFailure
-
-  sha256 <- case args of
-              _:hash:[] -> return hash
-              _         -> hashPackage cabal
-
-  let pkg = cabal2nix cabal sha256
-  putStr (toNix pkg)
-
-
diff --git a/src/Cabal2Nix.hs b/src/Cabal2Nix.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix.hs
@@ -0,0 +1,81 @@
+module Main ( main ) where
+
+import System.IO ( hPutStrLn, hFlush, stdout, stderr )
+import System.Environment ( getArgs )
+import Control.Exception ( bracket )
+import System.Exit ( exitFailure )
+import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
+import Distribution.PackageDescription ( package, packageDescription )
+import Distribution.Text ( simpleParse )
+import Data.List ( isPrefixOf )
+import Control.Monad ( when )
+import Network.HTTP ( simpleHTTP, getRequest, getResponseBody )
+import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt )
+
+import Cabal2Nix.Package ( showNixPkg, cabal2nix )
+import Cabal2Nix.Hackage ( hackagePath, Ext(..), hashPackage )
+
+readCabalFile :: FilePath -> IO String
+readCabalFile path
+  | "cabal://" `isPrefixOf` path = let Just pid = simpleParse (drop 8 path) in readCabalFile (hackagePath pid Cabal)
+  | "http://"  `isPrefixOf` path = simpleHTTP (getRequest path) >>= getResponseBody
+  | "file://"  `isPrefixOf` path = readCabalFile (drop 7 path)
+  | otherwise                    = readFile path
+
+data CliOption = PrintHelp | SHA256 String | Maintainer String | Platform String
+  deriving (Eq)
+
+main :: IO ()
+main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do
+  let options :: [OptDescr CliOption]
+      options =
+        [ Option ['h'] ["help"]       (NoArg PrintHelp)                 "show this help text"
+        , Option []    ["sha256"]     (ReqArg SHA256 "HASH")            "sha256 hash of source tarball"
+        , Option ['m'] ["maintainer"] (ReqArg Maintainer "MAINTAINER")  "maintainer of this package (may be specified multiple times)"
+        , Option ['p'] ["platform"]   (ReqArg Platform "PLATFORM")      "supported build platforms (may be specified multiple times)"
+        ]
+
+      usage :: String
+      usage = usageInfo "Usage: cabal2nix [options] url-to-cabal-file" options ++
+             "\n\
+             \Recognized URI schemes:\n\
+             \\n\
+             \  cabal://pkgname-pkgversion       download the specified package from Hackage\n\
+             \  http://host/path                 fetch the Cabel file via HTTP\n\
+             \  file:///local/path               load the Cabal file from the local disk\n\
+             \  /local/path                      abbreviated version of file URI\n"
+
+      cmdlineError :: String -> IO a
+      cmdlineError ""     = hPutStrLn stderr usage >> exitFailure
+      cmdlineError errMsg = hPutStrLn stderr errMsg >> cmdlineError ""
+
+  args' <- getArgs
+  (opts,args) <- case getOpt Permute options args' of
+     (o,n,[]  ) -> return (o,n)
+     (_,_,errs) -> cmdlineError (concatMap (\e -> '*':'*':'*':' ':e) errs)
+
+  when (PrintHelp `elem` opts) (cmdlineError "")
+
+  let uri         = args
+      hash        = [ h | SHA256 h <- opts ]
+      maintainers = [ m | Maintainer m <- opts ]
+      platforms'  = [ p | Platform p <- opts ]
+      platforms
+        | null platforms' = if not (null maintainers) then ["self.ghc.meta.platforms"] else []
+        | otherwise       = platforms'
+
+  when (length uri /= 1) (cmdlineError "*** exactly one URI must be specified")
+  when (length hash > 1) (cmdlineError "*** the --sha256 option may be specified only once")
+
+  cabal' <- fmap parsePackageDescription (readCabalFile (head uri))
+  cabal <- case cabal' of
+             ParseOk _ a -> return a
+             ParseFailed err -> do
+               hPutStrLn stderr ("*** cannot parse cabal file: " ++ show err)
+               exitFailure
+
+  let packageId = package (packageDescription cabal)
+
+  sha256 <- if null hash then hashPackage packageId else return (head hash)
+
+  putStr (showNixPkg (cabal2nix cabal sha256 platforms maintainers))
diff --git a/src/Hackage4Nix.hs b/src/Hackage4Nix.hs
new file mode 100644
--- /dev/null
+++ b/src/Hackage4Nix.hs
@@ -0,0 +1,294 @@
+{-# LANGUAGE OverloadedStrings, PatternGuards #-}
+
+module Main ( main ) where
+
+import System.IO
+import System.FilePath
+import System.Directory
+import System.Environment
+import System.Exit
+import Data.List
+import qualified Data.Set as Set
+import Control.Monad.State
+import Control.Exception ( bracket )
+import qualified Data.ByteString.Char8 as BS
+import Text.Regex.Posix
+import Data.Version
+import Text.ParserCombinators.ReadP ( readP_to_S )
+import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
+import Distribution.PackageDescription ( GenericPackageDescription() )
+import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt )
+import Cabal2Nix.Package ( cabal2nix, showNixPkg, PkgName, PkgSHA256, PkgPlatforms, PkgMaintainers )
+
+type ByteString = BS.ByteString
+
+pack :: String -> ByteString
+pack = BS.pack
+
+unpack :: ByteString -> String
+unpack = BS.unpack
+
+type PkgSet = Set.Set Pkg
+
+data Configuration = Configuration
+  { _msgDebug  :: String -> IO ()
+  , _msgInfo   :: String -> IO ()
+  , _hackageDb :: FilePath
+  , _pkgset    :: PkgSet
+  }
+
+defaultConfiguration :: Configuration
+defaultConfiguration = Configuration
+  { _msgDebug  = hPutStrLn stderr
+  , _msgInfo   = hPutStrLn stderr
+  , _hackageDb = "/dev/shm/hackage"
+  , _pkgset    = Set.empty
+  }
+
+type Hackage4Nix a = StateT Configuration IO a
+
+io :: (MonadIO m) => IO a -> m a
+io = liftIO
+
+readDirectory :: FilePath -> IO [FilePath]
+readDirectory dirpath = do
+  entries <- getDirectoryContents dirpath
+  return [ x | x <- entries, x /= ".", x /= ".." ]
+
+msgDebug, msgInfo :: String -> Hackage4Nix ()
+msgDebug msg = get >>= \s -> io (_msgDebug s msg)
+msgInfo msg = get >>= \s -> io (_msgInfo s msg)
+
+readCabalFile :: String -> String -> Hackage4Nix GenericPackageDescription
+readCabalFile name vers = do
+  hackageDir <- gets _hackageDb
+  let cabal = hackageDir </> name </> vers </> name <.> "cabal"
+  pkg' <- fmap parsePackageDescription (io (readFile cabal))
+  pkg <- case pkg' of
+           ParseOk _ a -> return a
+           ParseFailed err -> fail ("cannot parse cabal file " ++ cabal ++ ": " ++ show err)
+  return pkg
+
+discoverNixFiles :: (FilePath -> Hackage4Nix ()) -> FilePath -> Hackage4Nix ()
+discoverNixFiles yield dirOrFile
+  | "." `isPrefixOf` takeFileName dirOrFile  = msgDebug $ "ignore file or directory " ++ dirOrFile
+  | otherwise                                = do
+     isFile <- io (doesFileExist dirOrFile)
+     case (isFile, takeExtension dirOrFile) of
+       (True,".nix") -> do
+         msgDebug ("discovered file " ++ dirOrFile)
+         yield dirOrFile
+       (True,_) -> msgDebug $ "ignore file " ++ dirOrFile
+       (False,_) -> do
+         msgDebug ("discovered dir " ++ dirOrFile)
+         io (readDirectory dirOrFile) >>= mapM_ (discoverNixFiles yield . (dirOrFile </>))
+
+type PkgVersion = String
+data Pkg = Pkg PkgName PkgVersion PkgSHA256 PkgPlatforms PkgMaintainers FilePath
+  deriving (Show, Eq, Ord)
+
+regmatch :: ByteString -> String -> Bool
+regmatch buf patt = match (makeRegexOpts compExtended execBlank (pack patt)) buf
+
+regsubmatch :: ByteString -> String -> [ByteString]
+regsubmatch buf patt = let (_,_,_,x) = f in x
+  where f :: (ByteString,ByteString,ByteString,[ByteString])
+        f = match (makeRegexOpts compExtended execBlank (pack patt)) buf
+
+normalizeMaintainer :: String -> String
+normalizeMaintainer x
+  | "self.stdenv.lib.maintainers." `isPrefixOf` x = drop 28 x
+  | otherwise                                     = x
+
+parseNixFile :: FilePath -> ByteString -> Hackage4Nix (Maybe Pkg)
+parseNixFile path buf
+  | True    <- (pack path) `regmatch` (concat (intersperse "|" badPackages))
+               = msgDebug ("ignore known bad package " ++ path) >> return Nothing
+  | True    <- buf `regmatch` "src = (fetchgit|sourceFromHead)"
+               = msgDebug ("ignore non-hackage package " ++ path) >> return Nothing
+  | True    <- buf `regmatch` "noHaddock"
+               = msgDebug ("ignore non-haddock package " ++ path) >> return Nothing
+  | True    <- buf =~ pack "cabal.mkDerivation"
+  , [name]  <- buf `regsubmatch` "name *= *\"([^\"]+)\""
+  , [vers]  <- buf `regsubmatch` "version *= *\"([^\"]+)\""
+  , [sha]   <- buf `regsubmatch` "sha256 *= *\"([^\"]+)\""
+  , plats   <- buf `regsubmatch` "platforms *= *([^;]+);"
+  , maint   <- buf `regsubmatch` "maintainers *= *\\[([^\"]+)]"
+              = let plats' = concatMap BS.words (map (BS.map (\c -> if c == '+' then ' ' else c)) plats)
+                    maint' = concatMap BS.words maint
+                in
+                  return $ Just $ Pkg (unpack name)
+                                      (unpack vers)
+                                      (unpack sha)
+                                      (map unpack plats')
+                                      (map (normalizeMaintainer . unpack) maint')
+                                      (path)
+  | True <- buf `regmatch` "cabal.mkDerivation"
+              = msgInfo ("failed to parse file " ++ path) >> return Nothing
+  | otherwise = return Nothing
+
+readVersion :: String -> Version
+readVersion str =
+  case [ v | (v,[]) <- readP_to_S parseVersion str ] of
+    [ v' ] -> v'
+    _      -> error ("invalid version specifier " ++ show str)
+
+selectLatestVersions :: PkgSet -> PkgSet
+selectLatestVersions = Set.fromList . nubBy f2 . sortBy f1 . Set.toList
+  where
+    f1 (Pkg n1 v1 _ _ _ _) (Pkg n2 v2 _ _ _ _)
+      | n1 == n2  = compare v2 v1
+      | otherwise = compare n1 n2
+    f2 (Pkg n1 _ _ _ _ _) (Pkg n2 _ _ _ _ _)
+      = n1 == n2
+
+discoverUpdates :: PkgName -> PkgVersion -> Hackage4Nix [PkgVersion]
+discoverUpdates name vers = do
+  hackage <- gets _hackageDb
+  versionStrings <- io $ readDirectory (hackage </> name)
+  let versions = map readVersion versionStrings
+  return [ showVersion v | v <- versions, v > readVersion vers ]
+
+updateNixPkgs :: [FilePath] -> Hackage4Nix ()
+updateNixPkgs paths = do
+  msgDebug $ "updating = " ++ show paths
+  flip mapM_ paths $ \fileOrDir ->
+    flip discoverNixFiles fileOrDir $ \file -> do
+      nix' <- io (BS.readFile file) >>= parseNixFile file
+      flip (maybe (return ())) nix' $ \nix -> do
+        let Pkg name vers sha plats maints path = nix
+            maints' = nub (sort (maints ++ ["andres","simons"]))
+            plats'
+              | null plats && not (null maints) = ["self.ghc.meta.platforms"]
+              | otherwise                       = plats
+        msgDebug ("re-generate " ++ path)
+        when (null maints) (msgInfo ("warning: no maintainers configured for " ++ path))
+        pkg <- readCabalFile name vers
+        io $ writeFile path (showNixPkg (cabal2nix pkg sha plats' maints'))
+        modify $ \cfg -> cfg { _pkgset = Set.insert nix (_pkgset cfg) }
+  pkgset <- gets (selectLatestVersions . _pkgset)
+  updates' <- flip mapM (Set.elems pkgset) $ \pkg -> do
+    let Pkg name vers _ _ _ _ = pkg
+    updates <- discoverUpdates name vers
+    return (pkg,updates)
+  let updates = [ u | u@(_,(_:_)) <- updates' ]
+  when (not (null updates)) $ do
+    msgInfo "The following updates are available:"
+    flip mapM_ updates $ \(pkg,versions) -> do
+      let Pkg name vers _ plats maints path = pkg
+      msgInfo ""
+      msgInfo $ name ++ "-" ++ vers ++ ":"
+      flip mapM_ versions $ \newVersion -> do
+        msgInfo $ "  " ++ genCabal2NixCmdline (Pkg name newVersion undefined plats maints path)
+  return ()
+
+genCabal2NixCmdline :: Pkg -> String
+genCabal2NixCmdline (Pkg name vers _ plats maints path) = unwords $ ["cabal2nix"] ++ opts ++ [">"++path']
+    where
+      opts = [cabal] ++ maints' ++ plats'
+      cabal = "cabal://" ++ name ++ "-" ++ vers
+      maints' = [ "--maintainer=" ++ m | m <- maints ]
+      plats'
+        | ["self.ghc.meta.platforms"] == plats     = []
+        | otherwise                                =  [ "--platform=" ++ p | p <- plats ]
+      path'
+        | pack path `regmatch` "/[0-9\\.]+\\.nix$" = replaceFileName path (vers <.> "nix")
+        | otherwise                                = path
+
+data CliOption = PrintHelp | Verbose | HackageDB FilePath
+  deriving (Eq)
+
+main :: IO ()
+main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do
+  let options :: [OptDescr CliOption]
+      options =
+        [ Option ['h'] ["help"]     (NoArg PrintHelp)                 "show this help text"
+        , Option ['v'] ["verbose"]  (NoArg Verbose)                   "enable noisy debug output"
+        , Option []    ["hackage"]  (ReqArg HackageDB "HACKAGE-DIR")  "path to hackage database"
+        ]
+
+      usage :: String
+      usage = usageInfo "Usage: hackage4nix [options] [dir-or-file ...]" options ++
+        "\n\
+        \The purpose of 'hackage4nix' is to keep all Haskell packages in our\n\
+        \repository packages up-to-date. It scans a checked-out copy of\n\
+        \Nixpkgs for expressions that use 'cabal.mkDerivation', and\n\
+        \re-generates them in-place with cabal2nix.\n\
+        \\n\
+        \Because we don't want to generate a barrage of HTTP requests during\n\
+        \that procedure, the tool expects a copy of the Hackage database\n\
+        \available at some local path, i.e. \"/dev/shm/hackage\" by default.\n\
+        \That directory can be set up as follows:\n\
+        \\n\
+        \  cabal update\n\
+        \  mkdir -p /dev/shm/hackage\n\
+        \  tar xf ~/.cabal/packages/hackage.haskell.org/00-index.tar -C /dev/shm/hackage\n"
+
+      cmdlineError :: String -> IO a
+      cmdlineError ""     = hPutStrLn stderr usage >> exitFailure
+      cmdlineError errMsg = hPutStrLn stderr errMsg >> cmdlineError ""
+
+  args' <- getArgs
+  (opts,args) <- case getOpt Permute options args' of
+     (o,n,[]  ) -> return (o,n)
+     (_,_,errs) -> cmdlineError (concatMap (\e -> '*':'*':'*':' ':e) errs)
+
+  when (PrintHelp `elem` opts) (cmdlineError "")
+
+  let cfg = defaultConfiguration
+            { _msgDebug  = if Verbose `elem` opts then _msgDebug defaultConfiguration else const (return ())
+            , _hackageDb = last $ _hackageDb defaultConfiguration : [ p | HackageDB p <- opts ]
+            }
+  flip evalStateT cfg (updateNixPkgs args)
+
+
+
+-- Packages that we cannot regenerate automatically yet. This list
+-- should be empty.
+
+badPackages :: [String]
+badPackages = [ "/"++p++"/" | p <- names ]
+  where names =
+          [ "alex"
+          , "cairo"
+          , "citeproc"
+          , "citeproc-hs"
+          , "editline"
+          , "flapjax"
+          , "ghc-events"
+          , "glade"
+          , "glib"
+          , "glut"
+          , "GLUT"
+          , "gtk"
+          , "gtksourceview2"
+          , "haddock"
+          , "haskell-platform"
+          , "haskell-src"
+          , "hfuse"
+          , "hmatrix"
+          , "hp2any-graph"
+          , "idris"
+          , "LambdaHack"
+          , "lhs2tex"
+          , "MazesOfMonad"
+          , "OpenAL"
+          , "OpenGL"
+          , "pango"
+          , "readline"
+          , "repa-examples"
+          , "scion"
+          , "SDL"
+          , "SDL-image"
+          , "SDL-mixer"
+          , "SDL-ttf"
+          , "svgcairo"
+          , "tar"
+          , "terminfo"
+          , "threadscope"
+          , "vacuum"
+          , "wxHaskell"
+          , "X11"
+          , "X11-xft"
+          , "xmobar"
+          ]
