diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,6 @@
+module Main (main) where
+
+import Distribution.Simple
+
+main :: IO ()
+main = defaultMain
diff --git a/Setup.lhs b/Setup.lhs
deleted file mode 100644
--- a/Setup.lhs
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/usr/bin/env runhaskell
-
-> module Main (main) where
->
-> import Distribution.Simple
->
-> main :: IO ()
-> main = defaultMain
diff --git a/cabal2nix.cabal b/cabal2nix.cabal
--- a/cabal2nix.cabal
+++ b/cabal2nix.cabal
@@ -1,6 +1,6 @@
 Name:                   cabal2nix
-Version:                1.11
-Copyright:              (c) 2011 Peter Simons and Andres Loeh
+Version:                1.12
+Copyright:              Peter Simons, Andres Loeh
 License:                BSD3
 License-File:           LICENSE
 Author:                 Peter Simons <simons@cryp.to>, Andres Loeh <mail@andres-loeh.de>
@@ -8,9 +8,9 @@
 Homepage:               http://github.com/haskell4nix/cabal2nix
 Category:               Distribution
 Synopsis:               Convert Cabal files into Nix build instructions
-Cabal-Version:          >= 1.6
+Cabal-Version:          >= 1.8
 Build-Type:             Simple
-Tested-With:            GHC == 6.12.3, GHC == 7.0.4
+Tested-With:            GHC == 6.12.3, GHC == 7.0.4, GHC == 7.2.1
 Description:
   The cabal2nix utility converts Cabal files into Nix build instructions. The
   commandline syntax is:
@@ -41,43 +41,40 @@
   Type:                 git
   Location:             git://github.com/haskell4nix/cabal2nix.git
 
-Library
-  Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal
-  hs-source-dirs:       src
-  Extensions:           PatternGuards
-  Ghc-Options:          -Wall
-  Exposed-Modules:      Distribution.NixOS.Derivation.Cabal
-                        Distribution.NixOS.Derivation.License
-                        Distribution.NixOS.Derivation.Meta
-  other-modules:        Distribution.NixOS.Derivation.PrettyPrinting
-
 Executable cabal2nix
   main-is:              Cabal2Nix.hs
   hs-source-dirs:       src
-  Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal,
-                        filepath, directory, process, HTTP
-  Extensions:           PatternGuards
+  Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal >= 1.8,
+                        filepath, directory, process, HTTP, nixos-types
+  Extensions:           PatternGuards, RecordWildCards, CPP
   Ghc-Options:          -Wall
   other-modules:        Cabal2Nix.CorePackages
+                        Cabal2Nix.Flags
                         Cabal2Nix.Hackage
+                        Cabal2Nix.License
                         Cabal2Nix.Name
+                        Cabal2Nix.Normalize
                         Cabal2Nix.Package
-                        Distribution.NixOS.Derivation.Cabal
-                        Distribution.NixOS.Derivation.License
-                        Distribution.NixOS.Derivation.Meta
-                        Distribution.NixOS.Derivation.PrettyPrinting
+                        Cabal2Nix.PostProcess
 
 Executable hackage4nix
   main-is:              Hackage4Nix.hs
   hs-source-dirs:       src
-  Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal,
-                        mtl, containers, directory, filepath
-  Extensions:           PatternGuards
+  Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal >= 1.8,
+                        mtl, containers, directory, filepath, nixos-types
+  Extensions:           PatternGuards, RecordWildCards, CPP
   Ghc-Options:          -Wall
   other-modules:        Cabal2Nix.CorePackages
+                        Cabal2Nix.Flags
+                        Cabal2Nix.License
                         Cabal2Nix.Name
+                        Cabal2Nix.Normalize
                         Cabal2Nix.Package
-                        Distribution.NixOS.Derivation.Cabal
-                        Distribution.NixOS.Derivation.License
-                        Distribution.NixOS.Derivation.Meta
-                        Distribution.NixOS.Derivation.PrettyPrinting
+                        Cabal2Nix.PostProcess
+
+Executable packagelist
+  main-is:              PackageList.hs
+  hs-source-dirs:       src
+  Build-Depends:        base >= 3 && < 5, regex-posix, process, Cabal,
+                        filepath, directory
+  Ghc-Options:          -Wall
diff --git a/src/Cabal2Nix.hs b/src/Cabal2Nix.hs
--- a/src/Cabal2Nix.hs
+++ b/src/Cabal2Nix.hs
@@ -1,77 +1,76 @@
 module Main ( main ) where
 
-import System.IO ( hPutStrLn, hFlush, stdout, stderr )
-import System.Environment -- ( getArgs )
+import Cabal2Nix.Hackage ( hashPackage, readCabalFile )
+import Cabal2Nix.Package ( cabal2nix )
+import Cabal2Nix.Normalize ( normalize )
+import Distribution.NixOS.Derivation.Cabal
+import Distribution.NixOS.Derivation.Meta
+
 import Control.Exception ( bracket )
-import System.Exit ( exitFailure )
-import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
-import Distribution.PackageDescription ( package, packageDescription )
-import Distribution.Text
-import Data.List ( isPrefixOf )
 import Control.Monad ( when )
-import Network.HTTP ( simpleHTTP, getRequest, getResponseBody )
+import Distribution.PackageDescription ( package, packageDescription )
+import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
+import Distribution.Text ( disp )
 import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt )
+import System.Environment ( getArgs )
+import System.Exit ( exitFailure, exitSuccess )
+import System.IO ( hPutStrLn, hFlush, stdout, stderr )
 
-import Cabal2Nix.Package ( cabal2nix )
-import Cabal2Nix.Hackage ( hackagePath, Ext(..), hashPackage )
-import Distribution.NixOS.Derivation.Cabal
-import Distribution.NixOS.Derivation.Meta
+data Configuration = Configuration
+  { optPrintHelp :: Bool
+  , optPrintVersion :: Bool
+  , optSha256 :: String
+  , optMaintainer :: [String]
+  , optPlatform :: [String]
+  , optHaddock :: Bool
+  }
+  deriving (Show)
 
-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
+defaultConfiguration :: Configuration
+defaultConfiguration = Configuration
+  { optPrintHelp = False
+  , optPrintVersion = False
+  , optSha256 = ""
+  , optMaintainer = []
+  , optPlatform = []
+  , optHaddock = True
+  }
 
-data CliOption = PrintHelp | SHA256 String | Maintainer String | Platform String | NoHaddock
-  deriving (Eq)
+options :: [OptDescr (Configuration -> Configuration)]
+options =
+  [ Option ['h'] ["help"]       (NoArg (\o -> o { optPrintHelp = True }))                                  "show this help text"
+  , Option []    ["sha256"]     (ReqArg (\x o -> o { optSha256 = x }) "HASH")                              "sha256 hash of source tarball"
+  , Option ['m'] ["maintainer"] (ReqArg (\x o -> o { optMaintainer = x : optMaintainer o }) "MAINTAINER")  "maintainer of this package (may be specified multiple times)"
+  , Option ['p'] ["platform"]   (ReqArg (\x o -> o { optPlatform = x : optPlatform o }) "PLATFORM")        "supported build platforms (may be specified multiple times)"
+  , Option []    ["no-haddock"] (NoArg (\o -> o { optHaddock = False }))                                   "don't run Haddock when building this package"
+  ]
 
-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)"
-        , Option []    ["no-haddock"] (NoArg NoHaddock)                 "don't run Haddock when building this package"
+usage :: String
+usage = usageInfo "Usage: cabal2nix [options] url-to-cabal-file" options ++ unlines
+        [ ""
+        , "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"
         ]
 
-      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 ""
+cmdlineError :: String -> IO a
+cmdlineError ""     = hPutStrLn stderr usage >> exitFailure
+cmdlineError errMsg = hPutStrLn stderr errMsg >> cmdlineError ""
 
+main :: IO ()
+main = bracket (return ()) (\() -> hFlush stdout >> hFlush stderr) $ \() -> do
   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 ]
-      noHaddock   = NoHaddock `elem` opts
-      maints      = [ if '.' `elem` m then m else "self.stdenv.lib.maintainers." ++ m | Maintainer m <- opts ]
-      plats'      = [ if '.' `elem` p then p else "self.stdenv.lib.platforms." ++ p | Platform p <- opts ]
-      plats
-        | null plats' = if not (null maints) then ["self.ghc.meta.platforms"] else []
-        | otherwise   = plats'
+  (cfg,args) <- case getOpt Permute options args' of
+                  (o,n,[]  ) -> return (foldl (flip ($)) defaultConfiguration o,n)
+                  (_,_,errs) -> cmdlineError (concatMap ("*** "++) errs)
 
-  when (length uri /= 1) (cmdlineError "*** exactly one URI must be specified")
-  when (length hash > 1) (cmdlineError "*** the --sha256 option may be specified only once")
+  when (optPrintHelp cfg) (putStr usage >> exitSuccess)
+  when (length args /= 1) (cmdlineError "*** exactly one url-to-cabal-file must be specified\n")
 
-  cabal' <- fmap parsePackageDescription (readCabalFile (head uri))
+  cabal' <- fmap parsePackageDescription (readCabalFile (head args))
   cabal <- case cabal' of
              ParseOk _ a -> return a
              ParseFailed err -> do
@@ -79,14 +78,13 @@
                exitFailure
 
   let packageId = package (packageDescription cabal)
-
-  sha <- if null hash then hashPackage packageId else return (head hash)
+  sha <- if null (optSha256 cfg) then hashPackage packageId else return (optSha256 cfg)
 
-  let deriv  = (cabal2nix cabal) { sha256 = sha, runHaddock = not noHaddock }
+  let deriv  = (cabal2nix cabal) { sha256 = sha, runHaddock = optHaddock cfg }
       deriv' = deriv { metaSection = (metaSection deriv)
-                                     { maintainers = maints
-                                     , platforms   = plats
+                                     { maintainers = optMaintainer cfg
+                                     , platforms   = optPlatform cfg
                                      }
                      }
 
-  putStr (show (disp deriv'))
+  putStr (show (disp (normalize (deriv'))))
diff --git a/src/Cabal2Nix/Flags.hs b/src/Cabal2Nix/Flags.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix/Flags.hs
@@ -0,0 +1,15 @@
+module Cabal2Nix.Flags ( configureFlags ) where
+
+import Distribution.Package
+import Distribution.PackageDescription
+
+configureFlags :: PackageIdentifier -> FlagAssignment
+configureFlags (PackageIdentifier (PackageName name) _)
+ | name == "pandoc"     = [enable "highlighting", enable "threaded"]
+ | otherwise            = []
+
+enable :: String -> (FlagName,Bool)
+enable name = (FlagName name, True)
+
+disable :: String -> (FlagName,Bool)
+disable name = (FlagName name, False)
diff --git a/src/Cabal2Nix/Hackage.hs b/src/Cabal2Nix/Hackage.hs
--- a/src/Cabal2Nix/Hackage.hs
+++ b/src/Cabal2Nix/Hackage.hs
@@ -1,11 +1,14 @@
-module Cabal2Nix.Hackage ( hackagePath, Ext(..), hashPackage ) where
+module Cabal2Nix.Hackage ( hashPackage, readCabalFile ) where
 
+import Control.Monad ( when )
+import Data.List ( isPrefixOf )
+import Data.Version ( showVersion )
 import Distribution.Package ( PackageIdentifier(..), PackageName(..) )
-import System.Process ( readProcess )
+import Distribution.Text
+import Network.HTTP ( simpleHTTP, getRequest, getResponseBody )
 import System.Directory ( doesFileExist, getHomeDirectory, createDirectoryIfMissing )
 import System.FilePath ( dropFileName, (</>), (<.>) )
-import Data.Version ( showVersion )
-import Control.Monad ( when )
+import System.Process ( readProcess )
 
 data Ext = TarGz | Cabal deriving Eq
 
@@ -41,3 +44,10 @@
     return $ home ++ "/.cache/cabal2nix" </> name ++ "-" ++ version <.> "sha256"
   where
     version = showVersion version'
+
+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
diff --git a/src/Cabal2Nix/License.hs b/src/Cabal2Nix/License.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix/License.hs
@@ -0,0 +1,20 @@
+module Cabal2Nix.License ( fromCabalLicense ) where
+
+import Distribution.NixOS.Derivation.License
+import Distribution.License ( License(..) )
+import Data.Version
+
+fromCabalLicense :: Distribution.License.License -> Distribution.NixOS.Derivation.License.License
+fromCabalLicense (GPL Nothing)                     = Unknown (Just "GPL")
+fromCabalLicense (GPL (Just (Version [2] [])))     = Known "self.stdenv.lib.licenses.gpl2"
+fromCabalLicense (GPL (Just (Version [3] [])))     = Known "self.stdenv.lib.licenses.gpl3"
+fromCabalLicense (LGPL Nothing)                    = Unknown (Just "LGPL")
+fromCabalLicense (LGPL (Just (Version [2,1] [])))  = Known "self.stdenv.lib.licenses.lgpl21"
+fromCabalLicense (LGPL (Just (Version [3] [])))    = Known "self.stdenv.lib.licenses.gpl3"
+fromCabalLicense BSD3                              = Known "self.stdenv.lib.licenses.bsd3"
+fromCabalLicense BSD4                              = Known "self.stdenv.lib.licenses.bsd4"
+fromCabalLicense MIT                               = Known "self.stdenv.lib.licenses.mit"
+fromCabalLicense PublicDomain                      = Known "self.stdenv.lib.licenses.publicDomain"
+fromCabalLicense AllRightsReserved                 = Known "self.stdenv.lib.licenses.proprietary"
+fromCabalLicense OtherLicense                      = Unknown Nothing
+fromCabalLicense l                                 = error $ "Cabal2Nix.License.fromCabalLicense: unknown license " ++ show l
diff --git a/src/Cabal2Nix/Name.hs b/src/Cabal2Nix/Name.hs
--- a/src/Cabal2Nix/Name.hs
+++ b/src/Cabal2Nix/Name.hs
@@ -1,7 +1,8 @@
-module Cabal2Nix.Name ( toNixName, libNixName ) where
+module Cabal2Nix.Name ( toNixName, libNixName, buildToolNixName ) where
 
 import Data.Char
 
+-- | Map Cabal names to Nix attribute names.
 toNixName :: String -> String
 toNixName []      = error "toNixName: empty string is not a valid argument"
 toNixName name    = f name
@@ -16,24 +17,40 @@
 -- possibility of name clashes with Haskell libraries. I have included
 -- identity mappings to incicate that I have verified their correctness.
 libNixName :: String -> [String]
-libNixName "adns"      = return "adns"
-libNixName "cairo"     = return "cairo"
-libNixName "cairo-pdf" = return "cairo"
-libNixName "cairo-ps"  = return "cairo"
-libNixName "cairo-svg" = return "cairo"
-libNixName "crypto"    = return "openssl"
-libNixName "gnome-keyring" = return "gnome_keyring"
-libNixName "gnome-keyring-1" = return "gnome_keyring"
-libNixName "idn"       = return "idn"
-libNixName "libidn"    = return "idn"
-libNixName "libzip"    = return "libzip"
-libNixName "m"         = []  -- in stdenv
-libNixName "pcre"      = return "pcre"
-libNixName "pq"        = return "postgresql"
-libNixName "sndfile"   = return "libsndfile"
-libNixName "sqlite3"   = return "sqlite"
-libNixName "stdc++"    = []  -- in stdenv
-libNixName "xft"       = return "libXft"
-libNixName "X11"       = return "libX11"
-libNixName "z"         = return "zlib"
-libNixName x           = return x
+libNixName "adns"               = return "adns"
+libNixName "cairo-pdf"          = return "cairo"
+libNixName "cairo-ps"           = return "cairo"
+libNixName "cairo"              = return "cairo"
+libNixName "cairo-svg"          = return "cairo"
+libNixName "crypto"             = return "openssl"
+libNixName "glib-2.0"           = return "glib"
+libNixName "gnome-keyring-1"    = return "gnome_keyring"
+libNixName "gnome-keyring"      = return "gnome_keyring"
+libNixName "gobject-2.0"        = return "glib"
+libNixName "gthread-2.0"        = return "glib"
+libNixName "gtk+-2.0"           = return "gtk"
+libNixName "gtksourceview-2.0"  = return "gtksourceview"
+libNixName "idn"                = return "idn"
+libNixName "libglade-2.0"       = return "libglade"
+libNixName "libidn"             = return "idn"
+libNixName "librsvg-2.0"        = return "librsvg"
+libNixName "libzip"             = return "libzip"
+libNixName "m"                  = []  -- in stdenv
+libNixName "pangocairo"         = return "pango"
+libNixName "pcre"               = return "pcre"
+libNixName "pq"                 = return "postgresql"
+libNixName "sndfile"            = return "libsndfile"
+libNixName "sqlite3"            = return "sqlite"
+libNixName "stdc++"             = []  -- in stdenv
+libNixName "X11"                = return "libX11"
+libNixName "xft"                = return "libXft"
+libNixName "z"                  = return "zlib"
+libNixName x                    = return x
+
+-- | Map build tool names to Nix attribute names.
+buildToolNixName :: String -> [String]
+buildToolNixName "cabal"                = return "cabalInstall"
+buildToolNixName "gtk2hsC2hs"           = return "gtk2hsBuildtools"
+buildToolNixName "gtk2hsHookGenerator"  = return "gtk2hsBuildtools"
+buildToolNixName "gtk2hsTypeGen"        = return "gtk2hsBuildtools"
+buildToolNixName x                      = return (toNixName x)
diff --git a/src/Cabal2Nix/Normalize.hs b/src/Cabal2Nix/Normalize.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix/Normalize.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module Cabal2Nix.Normalize ( normalize ) where
+
+import Distribution.NixOS.Derivation.Cabal
+import Cabal2Nix.Name
+import Cabal2Nix.CorePackages
+import Data.List
+import Data.Char
+
+normalize :: Derivation -> Derivation
+normalize deriv@(MkDerivation {..}) = deriv
+  { buildDepends = normalizeNixNames (filter (`notElem` (pname : corePackages)) $ buildDepends)
+  , buildTools   = normalizeNixBuildTools (filter (`notElem` coreBuildTools) $ buildTools)
+  , extraLibs    = normalizeNixLibs extraLibs
+  , pkgConfDeps  = normalizeNixLibs pkgConfDeps
+  , metaSection  = normalizeMeta metaSection
+  }
+
+normalizeMeta :: Meta -> Meta
+normalizeMeta meta@(Meta {..}) = meta
+  { description = normalizeDescription description
+  , maintainers = normalizeMaintainers maintainers
+  , platforms   = normalizePlatforms platforms
+  }
+
+normalizeDescription :: String -> String
+normalizeDescription desc
+  | null desc                                             = []
+  | last desc == '.' && length (filter ('.'==) desc) == 1 = normalizeDescription (reverse (tail (reverse desc)))
+  | otherwise                                             = quote (unwords (words desc))
+
+quote :: String -> String
+quote ('\\':c:cs) = '\\' : c : quote cs
+quote ('"':cs)    = '\\' : '"' : quote cs
+quote (c:cs)      = c : quote cs
+quote []          = []
+
+normalizeList :: [String] -> [String]
+normalizeList = nub . sortBy (\x y -> compare (map toLower x) (map toLower y))
+
+normalizeNixNames :: [String] -> [String]
+normalizeNixNames = normalizeList . map toNixName
+
+normalizeNixLibs :: [String] -> [String]
+normalizeNixLibs = normalizeList . concatMap libNixName
+
+normalizeNixBuildTools :: [String] -> [String]
+normalizeNixBuildTools = normalizeList . concatMap buildToolNixName
+
+normalizeMaintainers :: [String] -> [String]
+normalizeMaintainers maints = normalizeList
+  [ if '.' `elem` m then m else "self.stdenv.lib.maintainers." ++ m | m <- maints ]
+
+normalizePlatforms :: [String] -> [String]
+normalizePlatforms [] = ["self.ghc.meta.platforms"]
+normalizePlatforms plats = normalizeList
+  [ if '.' `elem` p then p else "self.stdenv.lib.platforms." ++ p | p <- plats ]
diff --git a/src/Cabal2Nix/Package.hs b/src/Cabal2Nix/Package.hs
--- a/src/Cabal2Nix/Package.hs
+++ b/src/Cabal2Nix/Package.hs
@@ -1,5 +1,9 @@
 module Cabal2Nix.Package ( cabal2nix ) where
 
+import Cabal2Nix.License
+import Cabal2Nix.PostProcess
+import Cabal2Nix.Normalize
+import Cabal2Nix.Flags
 import Data.Maybe
 import Distribution.Compiler
 import qualified Distribution.Package as Cabal
@@ -8,26 +12,22 @@
 import Distribution.System
 import Distribution.Version
 import Distribution.NixOS.Derivation.Cabal
-import Cabal2Nix.Name
-import Cabal2Nix.CorePackages
-import Data.List
-import Data.Char
 
 cabal2nix :: Cabal.GenericPackageDescription -> Derivation
-cabal2nix cabal = MkDerivation
-  { pname        = Cabal.pkgName pkg
+cabal2nix cabal = normalize $ postProcess $ MkDerivation
+  { pname        = let Cabal.PackageName x = Cabal.pkgName pkg in x
   , version      = Cabal.pkgVersion pkg
   , sha256       = "cabal2nix left the she256 field undefined"
   , isLibrary    = isJust (Cabal.library tpkg)
   , isExecutable = not (null (Cabal.executables tpkg))
-  , buildDepends = normalizeNixNames (filter (`notElem` (name : corePackages)) $ map unDep deps)
-  , buildTools   = normalizeNixNames (filter (`notElem` coreBuildTools) $ map unDep tools)
-  , extraLibs    = normalizeNixLibs libs
-  , pkgConfDeps  = normalizeNixLibs pcs
+  , buildDepends = map unDep deps
+  , buildTools   = map unDep tools
+  , extraLibs    = libs
+  , pkgConfDeps  = pcs
   , runHaddock   = True
   , metaSection  = Meta
                    { homepage    = Cabal.homepage descr
-                   , description = normalizeDescription (Cabal.synopsis descr)
+                   , description = Cabal.synopsis descr
                    , license     = fromCabalLicense (Cabal.license descr)
                    , platforms   = []
                    , maintainers = []
@@ -36,36 +36,18 @@
   where
     descr = Cabal.packageDescription cabal
     pkg   = Cabal.package descr
-    Cabal.PackageName name  = Cabal.pkgName pkg
     deps    = Cabal.buildDepends tpkg
     libDeps = map Cabal.libBuildInfo (maybeToList (Cabal.library tpkg))
     exeDeps = map Cabal.buildInfo (Cabal.executables tpkg)
     tools   = concatMap Cabal.buildTools (libDeps ++ exeDeps)
     libs    = concatMap Cabal.extraLibs (libDeps ++ exeDeps)
     pcs     = map unDep (concatMap Cabal.pkgconfigDepends (libDeps ++ exeDeps))
-    Right (tpkg, _) = finalizePackageDescription [] (const True)
+    Right (tpkg, _) = finalizePackageDescription
+                        (configureFlags pkg)
+                        (const True)
                         (Platform I386 Linux)                   -- shouldn't be hardcoded
                         (CompilerId GHC (Version [7,0,4] []))   -- dito
                         [] cabal
 
 unDep :: Cabal.Dependency -> String
 unDep (Cabal.Dependency (Cabal.PackageName x) _) = x
-
-normalizeDescription :: String -> String
-normalizeDescription desc
-  | null desc                                             = []
-  | last desc == '.' && length (filter ('.'==) desc) == 1 = normalizeDescription (reverse (tail (reverse desc)))
-  | otherwise                                             = unwords (words desc) >>= quote
-
-quote :: Char -> [Char]
-quote '"'  = "\\\""
-quote c    = [c]
-
-normalizeList :: [String] -> [String]
-normalizeList = nub . sortBy (\x y -> compare (map toLower x) (map toLower y))
-
-normalizeNixNames :: [String] -> [String]
-normalizeNixNames = normalizeList . map toNixName
-
-normalizeNixLibs :: [String] -> [String]
-normalizeNixLibs = normalizeList . concatMap libNixName
diff --git a/src/Cabal2Nix/PostProcess.hs b/src/Cabal2Nix/PostProcess.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix/PostProcess.hs
@@ -0,0 +1,35 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module Cabal2Nix.PostProcess ( postProcess ) where
+
+import Distribution.NixOS.Derivation.Cabal
+import Data.List
+
+postProcess :: Derivation -> Derivation
+postProcess deriv@(MkDerivation {..})
+  | pname == "alex"             = deriv { buildTools = "perl":buildTools }
+  | pname == "cairo"            = deriv { extraLibs = "pkgconfig":"glibc":"cairo":"zlib":extraLibs }
+  | pname == "editline"         = deriv { extraLibs = "libedit":extraLibs }
+  | pname == "epic"             = deriv { extraLibs = "gmp":"boehmgc":extraLibs, buildTools = "happy":buildTools }
+  | pname == "glade"            = deriv { extraLibs = "pkgconfig":"glibc":extraLibs, pkgConfDeps = "gtkC":(delete "gtk" pkgConfDeps) }
+  | pname == "glib"             = deriv { extraLibs = "pkgconfig":"glibc":extraLibs }
+  | pname == "GLUT"             = deriv { extraLibs = "glut":"libSM":"libICE":"libXmu":"libXi":"mesa":extraLibs }
+  | pname == "gtk"              = deriv { extraLibs = "pkgconfig":"glibc":extraLibs, buildDepends = delete "gio" buildDepends }
+  | pname == "gtksourceview2"   = deriv { extraLibs = "pkgconfig":"glibc":extraLibs }
+  | pname == "haskell-src"      = deriv { buildTools = "happy":buildTools }
+  | pname == "hmatrix"          = deriv { extraLibs = "gsl":"liblapack":"blas":extraLibs }
+  | pname == "idris"            = deriv { buildTools = "happy":buildTools }
+  | pname == "OpenAL"           = deriv { extraLibs = "openal":extraLibs }
+  | pname == "OpenGL"           = deriv { extraLibs = "mesa":"libX11":extraLibs }
+  | pname == "pango"            = deriv { extraLibs = "pkgconfig":"glibc":extraLibs }
+  | pname == "repa-examples"    = deriv { extraLibs = "llvm":extraLibs }
+  | pname == "SDL-image"        = deriv { extraLibs = "SDL_image":extraLibs }
+  | pname == "SDL-mixer"        = deriv { extraLibs = "SDL_mixer":extraLibs }
+  | pname == "SDL-ttf"          = deriv { extraLibs = "SDL_ttf":extraLibs }
+  | pname == "svgcairo"         = deriv { extraLibs = "glibc":extraLibs }
+  | pname == "terminfo"         = deriv { extraLibs = "ncurses":extraLibs }
+  | pname == "vacuum"           = deriv { extraLibs = "ghcPaths":extraLibs }
+  | pname == "wxcore"           = deriv { extraLibs = "wxGTK":"mesa":"libX11":extraLibs }
+  | pname == "xmonad"           = deriv { extraLibs = delete "libmpd" extraLibs }
+  | pname == "xmonad-extras"    = deriv { buildDepends = delete "libmpd" buildDepends }
+  | otherwise                   = deriv
diff --git a/src/Distribution/NixOS/Derivation/Cabal.hs b/src/Distribution/NixOS/Derivation/Cabal.hs
deleted file mode 100644
--- a/src/Distribution/NixOS/Derivation/Cabal.hs
+++ /dev/null
@@ -1,106 +0,0 @@
-{-# LANGUAGE PatternGuards #-}
-
-module Distribution.NixOS.Derivation.Cabal
-  ( Derivation(..)
-  , parseDerivation
-  , module Distribution.NixOS.Derivation.Meta
-  , Version(..), PackageName(..)
-  )
-  where
-
-import Distribution.NixOS.Derivation.PrettyPrinting
-import Distribution.NixOS.Derivation.Meta
-import Distribution.Package
-import Distribution.Text
-import Data.Version
-import Data.List
-import Data.Char
-import Text.PrettyPrint
-import Text.ParserCombinators.ReadP ( readP_to_S )
-import Text.Regex.Posix
-
-data Derivation = MkDerivation
-  { pname        :: PackageName
-  , version      :: Version
-  , sha256       :: String
-  , isLibrary    :: Bool
-  , isExecutable :: Bool
-  , buildDepends :: [String]
-  , buildTools   :: [String]
-  , extraLibs    :: [String]
-  , pkgConfDeps  :: [String]
-  , runHaddock   :: Bool
-  , metaSection  :: Meta
-  }
-  deriving (Show, Eq, Ord)
-
-instance Text Derivation where
-  disp  = renderDerivation
-  parse = error "parsing Distribution.NixOS.Derivation.Cabal.Derivation is not supported yet"
-
-renderDerivation :: Derivation -> Doc
-renderDerivation deriv = funargs (map text ("cabal" : inputs)) $$ vcat
-  [ text ""
-  , text "cabal.mkDerivation" <+> lparen <> text "self" <> colon <+> lbrace
-  , nest 2 $ vcat
-    [ attr "pname"   $ doubleQuotes (disp (pname deriv))
-    , attr "version" $ doubleQuotes (disp (version deriv))
-    , attr "sha256"  $ string (sha256 deriv)
-    , boolattr "isLibrary" (not (isLibrary deriv) || (isExecutable deriv)) (isLibrary deriv)
-    , boolattr "isExecutable" (not (isLibrary deriv) || (isExecutable deriv)) (isExecutable deriv)
-    , listattr "buildDepends" (buildDepends deriv)
-    , listattr "buildTools" (buildTools deriv)
-    , listattr "extraLibraries" (extraLibs deriv)
-    , listattr "pkgconfigDepends" (pkgConfDeps deriv)
-    , boolattr "noHaddock" (not (runHaddock deriv)) (not (runHaddock deriv))
-    , disp (metaSection deriv)
-    ]
-  , rbrace <> rparen
-  , text ""
-  ]
-  where
-    inputs = nub $ sortBy (\x y -> compare (map toLower x) (map toLower y)) $
-              filter (/="cabal") $ filter (/="Cabal") $
-                buildDepends deriv ++ buildTools deriv ++ extraLibs deriv ++ pkgConfDeps deriv
-
-parseDerivation :: String -> Maybe Derivation
-parseDerivation buf
-  | buf =~ "cabal.mkDerivation"
-  , [name]    <- buf `regsubmatch` "pname *= *\"([^\"]+)\""
-  , [vers]    <- buf `regsubmatch` "version *= *\"([^\"]+)\""
-  , [sha]     <- buf `regsubmatch` "sha256 *= *\"([^\"]+)\""
-  , plats     <- buf `regsubmatch` "platforms *= *([^;]+);"
-  , maint     <- buf `regsubmatch` "maintainers *= *\\[([^\"]+)]"
-  , noHaddock <- buf `regsubmatch` "noHaddock *= *(true|false) *;"
-              = Just $ MkDerivation
-                  { pname        = PackageName name
-                  , version      = readVersion vers
-                  , sha256       = sha
-                  , isLibrary    = False
-                  , isExecutable = False
-                  , buildDepends = []
-                  , buildTools   = []
-                  , extraLibs    = []
-                  , pkgConfDeps  = []
-                  , runHaddock   = case noHaddock of "true":[] -> False
-                                                     _         -> True
-                  , metaSection  = Meta
-                                   { homepage    = ""
-                                   , description = ""
-                                   , license     = Unknown Nothing
-                                   , maintainers = concatMap words maint
-                                   , platforms   = concatMap words (map (map (\c -> if c == '+' then ' ' else c)) plats)
-                                   }
-                  }
-  | otherwise = Nothing
-
-regsubmatch :: String -> String -> [String]
-regsubmatch buf patt = let (_,_,_,x) = f in x
-  where f :: (String,String,String,[String])
-        f = match (makeRegexOpts compExtended execBlank patt) buf
-
-readVersion :: String -> Version
-readVersion str =
-  case [ v | (v,[]) <- readP_to_S parseVersion str ] of
-    [ v' ] -> v'
-    _      -> error ("invalid version specifier " ++ show str)
diff --git a/src/Distribution/NixOS/Derivation/License.hs b/src/Distribution/NixOS/Derivation/License.hs
deleted file mode 100644
--- a/src/Distribution/NixOS/Derivation/License.hs
+++ /dev/null
@@ -1,54 +0,0 @@
-module Distribution.NixOS.Derivation.License
-  ( License(..)
-  , fromCabalLicense
-  )
-  where
-
-import qualified Distribution.License as Cabal ( License(..) )
-import Data.Version ( Version(..) )
-import Distribution.Text
-import Text.PrettyPrint
-
-data License
-  = GPL2
-  | GPL3
-  | LGPL21
-  | LGPL3
-  | BSD3
-  | BSD4
-  | MIT
-  | PublicDomain
-  | Proprietary
-  | Unknown (Maybe String)
-  deriving (Eq, Ord)
-
-instance Show License where
-  show GPL2                     = "self.stdenv.lib.licenses.gpl2"
-  show GPL3                     = "self.stdenv.lib.licenses.gpl3"
-  show LGPL21                   = "self.stdenv.lib.licenses.lgpl21"
-  show LGPL3                    = "self.stdenv.lib.licenses.gpl3"
-  show BSD3                     = "self.stdenv.lib.licenses.bsd3"
-  show BSD4                     = "self.stdenv.lib.licenses.bsd4"
-  show MIT                      = "self.stdenv.lib.licenses.mit"
-  show PublicDomain             = "self.stdenv.lib.licenses.publicDomain"
-  show Proprietary              = "self.stdenv.lib.licenses.proprietary"
-  show (Unknown lic)            = show (maybe "unknown" id lic)
-
-instance Text License where
-  disp = text . show
-  parse = error "parsing Distribution.NixOS.Derivation.License is not supported yet"
-
-fromCabalLicense :: Cabal.License -> License
-fromCabalLicense (Cabal.GPL Nothing)                     = Unknown (Just "GPL")
-fromCabalLicense (Cabal.GPL (Just (Version [2] [])))     = GPL2
-fromCabalLicense (Cabal.GPL (Just (Version [3] [])))     = GPL3
-fromCabalLicense (Cabal.LGPL Nothing)                    = Unknown (Just "LGPL")
-fromCabalLicense (Cabal.LGPL (Just (Version [2,1] [])))  = LGPL21
-fromCabalLicense (Cabal.LGPL (Just (Version [3] [])))    = LGPL3
-fromCabalLicense Cabal.BSD3                              = BSD3
-fromCabalLicense Cabal.BSD4                              = BSD4
-fromCabalLicense Cabal.MIT                               = MIT
-fromCabalLicense Cabal.PublicDomain                      = PublicDomain
-fromCabalLicense Cabal.AllRightsReserved                 = Proprietary
-fromCabalLicense Cabal.OtherLicense                      = Unknown Nothing
-fromCabalLicense l                                       = error $ "Distribution.NixOS.Derivation.License.fromCabalLicense: unknown license " ++ show l
diff --git a/src/Distribution/NixOS/Derivation/Meta.hs b/src/Distribution/NixOS/Derivation/Meta.hs
deleted file mode 100644
--- a/src/Distribution/NixOS/Derivation/Meta.hs
+++ /dev/null
@@ -1,39 +0,0 @@
-module Distribution.NixOS.Derivation.Meta
-  ( Meta(..)
-  , module Distribution.NixOS.Derivation.License
-  )
-  where
-
-import Distribution.NixOS.Derivation.PrettyPrinting
-import Distribution.NixOS.Derivation.License
-import Distribution.Text
-import Text.PrettyPrint
-
-data Meta = Meta
-  { homepage    :: String
-  , description :: String
-  , license     :: License
-  , platforms   :: [String]
-  , maintainers :: [String]
-  }
-  deriving (Show, Eq, Ord)
-
-instance Text Meta where
-  disp  = renderMeta
-  parse = error "parsing Distribution.NixOS.Derivation.Cabal.Meta is not supported yet"
-
-renderMeta :: Meta -> Doc
-renderMeta meta = vcat
-  [ text "meta" <+> equals <+> lbrace
-  , nest 2 $ vcat
-    [ onlyIf (homepage meta) $ attr "homepage" $ string (homepage meta)
-    , onlyIf (description meta) $ attr "description" $ string (description meta)
-    , attr "license" $ disp (license meta)
-    , onlyIf (platforms meta) $ sep
-      [ text "platforms" <+> equals
-      , nest 2 ((fsep $ punctuate (text " ++") $ map text (platforms meta))) <> semi
-      ]
-    , listattr "maintainers" (maintainers meta)
-    ]
-  , rbrace <> semi
-  ]
diff --git a/src/Distribution/NixOS/Derivation/PrettyPrinting.hs b/src/Distribution/NixOS/Derivation/PrettyPrinting.hs
deleted file mode 100644
--- a/src/Distribution/NixOS/Derivation/PrettyPrinting.hs
+++ /dev/null
@@ -1,47 +0,0 @@
-module Distribution.NixOS.Derivation.PrettyPrinting
-  ( onlyIf
-  , listattr
-  , boolattr
-  , attr
-  , string
-  , funargs
-  )
-  where
-
-import Text.PrettyPrint
-
--- Pretty-printing helpers for Nix expressions.
-
-attr :: String -> Doc -> Doc
-attr n v = text n <+> equals <+> v <> semi
-
-onlyIf :: [a] -> Doc -> Doc
-onlyIf p d = if not (null p) then d else empty
-
-boolattr :: String -> Bool -> Bool -> Doc
-boolattr n p v = if p then attr n (bool v) else empty
-
-listattr :: String -> [String] -> Doc
-listattr n vs = onlyIf vs $
-                sep [ text n <+> equals <+> lbrack,
-                      nest 2 $ fsep $ map text vs,
-                      rbrack <> semi
-                    ]
-
-bool :: Bool -> Doc
-bool True  = text "true"
-bool False = text "false"
-
-string :: String -> Doc
-string = doubleQuotes . text
-
-prepunctuate :: Doc -> [Doc] -> [Doc]
-prepunctuate _ []     = []
-prepunctuate p (d:ds) = d : map (p <>) ds
-
-funargs :: [Doc] -> Doc
-funargs xs = sep [
-               lbrace <+> (fcat $ prepunctuate (comma <> text " ") $
-                           map (nest 2) xs),
-               rbrace <> colon
-             ]
diff --git a/src/Hackage4Nix.hs b/src/Hackage4Nix.hs
--- a/src/Hackage4Nix.hs
+++ b/src/Hackage4Nix.hs
@@ -11,12 +11,13 @@
 import Control.Exception ( bracket )
 import Text.Regex.Posix
 import Data.Version
-import Text.ParserCombinators.ReadP ( readP_to_S )
 import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
+import Distribution.Package
 import Distribution.PackageDescription ( GenericPackageDescription() )
 import Distribution.Text
 import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt )
 import Cabal2Nix.Package
+import Cabal2Nix.Normalize
 import Distribution.NixOS.Derivation.Cabal
 import Distribution.NixOS.Derivation.Meta
 
@@ -65,24 +66,19 @@
   return pkg
 
 discoverNixFiles :: (FilePath -> Hackage4Nix ()) -> FilePath -> Hackage4Nix ()
-discoverNixFiles yield dirOrFile
-  | "." `isPrefixOf` takeFileName dirOrFile  = return ()
-  | otherwise                                = do
-     isFile <- io (doesFileExist dirOrFile)
-     case (isFile, takeExtension dirOrFile) of
-       (True,".nix") -> yield dirOrFile
-       (True,_)     -> return ()
-       (False,_)    -> io (readDirectory dirOrFile) >>= mapM_ (discoverNixFiles yield . (dirOrFile </>))
+discoverNixFiles yield dirOrFile = do
+  isFile <- io (doesFileExist dirOrFile)
+  case (isFile, takeExtension dirOrFile) of
+    (True,".nix") -> yield dirOrFile
+    (True,_)     -> return ()
+    (False,_)    -> io (readDirectory dirOrFile) >>= mapM_ (discoverNixFiles yield . (dirOrFile </>))
 
 regenerateDerivation :: Derivation -> String -> Bool
-regenerateDerivation deriv buf = not (display (pname deriv) `elem` patchedPackages) &&
-                                 not (buf =~ "preConfigure|configureFlags|postInstall|patchPhase")
+regenerateDerivation deriv buf = not (pname deriv `elem` patchedPackages) &&
+                                 not (buf =~ "(pre|post)Configure|configureFlags|(pre|post)Install|patchPhase")
 
 readVersion :: String -> Version
-readVersion str =
-  case [ v | (v,[]) <- readP_to_S parseVersion str ] of
-    [ v' ] -> v'
-    _      -> error ("invalid version specifier " ++ show str)
+readVersion str = maybe (error $ "cannot parse version " ++ show str) id (simpleParse str)
 
 parseNixFile :: FilePath -> String -> Hackage4Nix (Maybe Pkg)
 parseNixFile path buf
@@ -121,26 +117,23 @@
         let Pkg deriv path regenerate = nix
             maints = maintainers (metaSection deriv)
             plats  = platforms (metaSection deriv)
-        when (null maints) (msgInfo ("no maintainers configured for " ++ path))
         modify $ \cfg -> cfg { _pkgset = Set.insert nix (_pkgset cfg) }
         when regenerate $ do
           msgDebug ("re-generate " ++ path)
-          let maints' = nub (sort (maints ++ ["self.stdenv.lib.maintainers.andres","self.stdenv.lib.maintainers.simons"]))
-              plats'
-                | null plats && not (null maints) = ["self.ghc.meta.platforms"]
-                | otherwise                       = plats
-          pkg <- readCabalFile (display (pname deriv)) (display (version deriv))
+          pkg <- readCabalFile (pname deriv) (display (version deriv))
           let deriv'  = (cabal2nix pkg) { sha256 = sha256 deriv, runHaddock = runHaddock deriv }
-              deriv'' = deriv' { metaSection = (metaSection deriv')
-                                               { maintainers = maints'
+              meta    = metaSection deriv'
+              plats'  = if null plats then platforms meta else plats
+              deriv'' = deriv' { metaSection = meta
+                                               { maintainers = maints ++ ["simons","andres"]
                                                , platforms   = plats'
                                                }
                                }
-          io $ writeFile path (show (disp deriv''))
+          io $ writeFile path (show (disp (normalize (deriv''))))
   pkgset <- gets (selectLatestVersions . _pkgset)
   updates' <- flip mapM (Set.elems pkgset) $ \pkg -> do
     let Pkg deriv _ _ = pkg
-    updates <- discoverUpdates (display (pname deriv)) (display (version deriv))
+    updates <- discoverUpdates (pname deriv) (display (version deriv))
     return (pkg,updates)
   let updates = [ u | u@(_,(_:_)) <- updates' ]
   when (not (null updates)) $ do
@@ -148,7 +141,7 @@
     flip mapM_ updates $ \(pkg,versions) -> do
       let Pkg deriv path regenerate = pkg
       msgInfo ""
-      msgInfo $ (display (pname deriv)) ++ "-" ++ (display (version deriv)) ++ ":"
+      msgInfo $ (display (packageId deriv)) ++ ":"
       flip mapM_ versions $ \newVersion -> do
         let deriv' = deriv { version = readVersion newVersion }
         msgInfo $ "  " ++ genCabal2NixCmdline (Pkg deriv' path regenerate)
@@ -159,7 +152,7 @@
   where
     meta = metaSection deriv
     opts = [cabal] ++ maints' ++ plats' ++ if runHaddock deriv then [] else ["--no-haddock"]
-    cabal = "cabal://" ++ display (pname deriv) ++ "-" ++ display (version deriv)
+    cabal = "cabal://" ++ display (packageId deriv)
     maints' = [ "--maintainer=" ++ normalizeMaintainer m | m <- maintainers meta ]
     plats'
       | ["self.ghc.meta.platforms"] == platforms meta = []
@@ -186,21 +179,22 @@
         ]
 
       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"
+      usage = usageInfo "Usage: hackage4nix [options] [dir-or-file ...]" options ++ unlines
+              [ ""
+              , "The purpose of 'hackage4nix' is to keep all Haskell packages in our"
+              , "repository packages up-to-date. It scans a checked-out copy of"
+              , "Nixpkgs for expressions that use 'cabal.mkDerivation', and"
+              , "re-generates them in-place with cabal2nix."
+              , ""
+              , "Because we don't want to generate a barrage of HTTP requests during"
+              , "that procedure, the tool expects a copy of the Hackage database"
+              , "available at some local path, i.e. \"/dev/shm/hackage\" by default."
+              , "That directory can be set up as follows:"
+              , ""
+              , "  cabal update"
+              , "  mkdir -p /dev/shm/hackage"
+              , "  tar xf ~/.cabal/packages/hackage.haskell.org/00-index.tar -C /dev/shm/hackage\n"
+              ]
 
       cmdlineError :: String -> IO a
       cmdlineError ""     = hPutStrLn stderr usage >> exitFailure
@@ -230,38 +224,6 @@
 
 patchedPackages :: [String]
 patchedPackages =
-   [ "alex"              -- undeclared dependencies
-   , "cairo"             -- undeclared dependencies
-   , "citeproc-hs"       -- undeclared dependencies
-   , "editline"          -- undeclared dependencies
-   , "glade"             -- undeclared dependencies
-   , "glib"              -- undeclared dependencies
-   , "epic"              -- undeclared dependencies
-   , "GLUT"              -- undeclared dependencies
-   , "gtk"               -- undeclared dependencies
-   , "gtksourceview2"    -- undeclared dependencies
-   , "haddock"           -- undeclared dependencies
-   , "haskell-src"       -- undeclared dependencies
-   , "hmatrix"           -- undeclared dependencies
-   , "hp2any-graph"      -- undeclared dependencies
-   , "lhs2tex"           -- undeclared dependencies
-   , "OpenAL"            -- undeclared dependencies
-   , "OpenGL"            -- undeclared dependencies
-   , "pango"             -- undeclared dependencies
-   , "idris"             -- undeclared dependencies
-   , "readline"          -- undeclared dependencies
-   , "repa-examples"     -- undeclared dependencies
-   , "scion"             -- expects non-existent networkBytestring
-   , "SDL"               -- undeclared dependencies
-   , "SDL-image"         -- undeclared dependencies
-   , "SDL-mixer"         -- undeclared dependencies
-   , "SDL-ttf"           -- undeclared dependencies
-   , "svgcairo"          -- undeclared dependencies
-   , "terminfo"          -- undeclared dependencies
-   , "xmonad"            -- undeclared dependencies
-   , "xmonad-extras"     -- undeclared dependencies
-   , "vacuum"            -- undeclared dependencies
-   , "wxcore"            -- undeclared dependencies
-   , "X11"               -- undeclared dependencies
-   , "X11-xft"           -- undeclared dependencies
+   [ "X11"               -- expression uses function arguments to determine feature set
+   , "xmonad-extras"     -- the generated expression doesn't build, don't know why
    ]
diff --git a/src/PackageList.hs b/src/PackageList.hs
new file mode 100644
--- /dev/null
+++ b/src/PackageList.hs
@@ -0,0 +1,73 @@
+module Main ( main ) where
+
+import Control.Exception ( assert )
+import Control.Monad ( when, filterM )
+import Data.Char ( toLower )
+import Data.List ( nubBy, sortBy )
+import Data.Ord ( comparing )
+import Data.Version ( Version(..) )
+import Distribution.Package ( PackageIdentifier(..), PackageName(..) )
+import Distribution.Text ( simpleParse, display )
+import System.Directory ( doesDirectoryExist, doesFileExist )
+import System.FilePath ( (</>), (<.>) )
+import System.Process ( readProcess )
+import Text.Regex.Posix ( (=~), match, makeRegexOpts, compExtended, execBlank )
+
+type Pkg    = (String,Version,String) -- (Name, Version, Attribute)
+type Pkgset = [Pkg]
+
+comparePkgByVersion :: Pkg -> Pkg -> Ordering  -- prefers the latest version
+comparePkgByVersion (n1,v1,a1) (n2,v2,a2)
+  | a1 == a2    = assert (n1 == n2) (compare v2 v1)
+  | otherwise   = compare a2 a1
+
+comparePkgByName :: Pkg -> Pkg-> Ordering
+comparePkgByName (n1,_,_) (n2,_,_) = comparing (map toLower) n1 n2
+
+parseHaskellPackageName :: String -> Maybe Pkg
+parseHaskellPackageName name =
+  case name `regsubmatch` "^(haskellPackages[^ \t]+)[ \t]+(.+)$" of
+    [attr,name'] -> case name' `regsubmatch` "^haskell-(.+)-ghc[0-9.]+-(.+)$" of
+                      [name'',version] -> case simpleParse version of
+                                            Just version' -> Just (name'',version',attr)
+                                            _             -> error ("cannot parse " ++ show name)
+                      _                -> case simpleParse name' of
+                                            Just (PackageIdentifier (PackageName n) v) -> Just (n,v,attr)
+                                            _                                          -> error ("cannot parse " ++ show name)
+    _            -> Nothing
+
+getHaskellPackageList :: IO Pkgset
+getHaskellPackageList = do
+  allPkgs <- fmap lines (readProcess "bash" ["-c", "exec nix-env -qaP \\* 2>/dev/tty"] "")
+  return [ p | Just p <- map parseHaskellPackageName allPkgs ]
+
+stripProfilingVersions :: Pkgset -> Pkgset
+stripProfilingVersions pkgs = [ p | p@(_,_,attr) <- pkgs , not (attr =~ "ghc[0-9.]+_profiling") ]
+
+stripGhc721Versions :: Pkgset -> Pkgset
+stripGhc721Versions pkgs = [ p | p@(_,_,attr) <- pkgs , not (attr =~ "ghc721") ]
+
+selectLatestVersions :: Pkgset -> Pkgset
+selectLatestVersions = nubBy (\x y -> comparePkgByName x y == EQ) . sortBy comparePkgByVersion
+
+isHackagePackage :: Pkg -> IO Bool
+isHackagePackage (name,version,_) = doesFileExist path
+  where path = "/dev/shm/hackage/" </> name </> display version </> name <.> "cabal"
+
+formatPackageLine :: Pkg -> String
+formatPackageLine (name,version,attr) = show (name, display version, Just url)
+  where
+    url = "http://hydra.nixos.org/job/nixpkgs/trunk/" ++ attr
+
+regsubmatch :: String -> String -> [String]
+regsubmatch buf patt = let (_,_,_,x) = f in x
+  where f :: (String,String,String,[String])
+        f = match (makeRegexOpts compExtended execBlank patt) buf
+
+main :: IO ()
+main = do
+  haveHackage <- doesDirectoryExist "/dev/shm/hackage"
+  when (not haveHackage) (fail "cannot find hackage database at /dev/shm/hackage")
+  pkgset' <- fmap (selectLatestVersions . stripGhc721Versions . stripProfilingVersions) getHaskellPackageList
+  pkgset <- filterM isHackagePackage pkgset'
+  mapM_ (putStrLn . formatPackageLine) (sortBy comparePkgByName pkgset)
