diff --git a/Adelie/Colour.hs b/Adelie/Colour.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/Colour.hs
@@ -0,0 +1,55 @@
+-- Colour.hs
+--
+-- Escape codes for colouring in output.
+
+module Adelie.Colour (
+    gray,     inGray,
+    red,      inRed,
+    green,    inGreen,
+    yellow,   inYellow,
+    blue,     inBlue,
+    magenta,  inMagenta,
+    cyan,     inCyan,
+    white,    inWhite,
+    off,
+    off2
+) where
+
+import Monad (when)
+
+import Adelie.Options
+
+gray, red, green, yellow, blue, magenta, cyan, white, off, off2 :: IO ()
+
+gray    = whenM colourEnabled $ putStr "\27[30;01m"
+red     = whenM colourEnabled $ putStr "\27[31;01m"
+green   = whenM colourEnabled $ putStr "\27[32;01m"
+yellow  = whenM colourEnabled $ putStr "\27[33;01m"
+blue    = whenM colourEnabled $ putStr "\27[34;01m"
+magenta = whenM colourEnabled $ putStr "\27[35;01m"
+cyan    = whenM colourEnabled $ putStr "\27[36;01m"
+white   = whenM colourEnabled $ putStr "\27[37;01m"
+off     = whenM colourEnabled $ putStr "\27[0m"
+off2    = do
+  true <- colourEnabled
+  if true
+    then putStrLn "\27[0m"
+    else putChar '\n'
+
+whenM :: IO Bool -> IO () -> IO ()
+whenM cond f = cond >>= (flip when f)
+
+----------------------------------------------------------------
+
+inGray, inRed, inGreen, inYellow    :: IO () -> IO ()
+inBlue, inMagenta, inCyan, inWhite  :: IO () -> IO ()
+
+inGray    f = gray    >> f >> off
+inRed     f = red     >> f >> off
+inGreen   f = yellow  >> f >> off
+inYellow  f = yellow  >> f >> off
+inBlue    f = blue    >> f >> off
+inMagenta f = magenta >> f >> off
+inCyan    f = cyan    >> f >> off
+inWhite   f = white   >> f >> off
+
diff --git a/Adelie/CompareVersion.hs b/Adelie/CompareVersion.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/CompareVersion.hs
@@ -0,0 +1,35 @@
+-- CompareVersion.hs
+--
+-- Version string comparitor, which handles numbers differently: 1.10 > 1.9.
+
+module Adelie.CompareVersion (compareVersion) where
+
+import Char (isDigit)
+
+import Adelie.ListEx (digitsToInt)
+
+----------------------------------------------------------------
+
+compareVersion :: String -> String -> Ordering
+compareVersion = cmpA
+
+cmpA :: String -> String -> Ordering
+cmpA a0 b0
+  | r == EQ   = cmpB as bs
+  | otherwise = r
+  where (a, as) = break (not.isDigit) a0
+        (b, bs) = break (not.isDigit) b0
+        r = compare (digitsToInt a) (digitsToInt b)
+
+cmpB :: String -> String -> Ordering
+cmpB [] []      = EQ
+cmpB ('*':_) _  = EQ
+cmpB _ ('*':_)  = EQ
+cmpB []  _      = LT
+cmpB  _ []      = GT
+cmpB a0 b0
+  | r == EQ   = cmpA as bs
+  | otherwise = r
+  where (a, as) = break isDigit a0
+        (b, bs) = break isDigit b0
+        r = compare a b
diff --git a/Adelie/Config.hs.in b/Adelie/Config.hs.in
new file mode 100644
--- /dev/null
+++ b/Adelie/Config.hs.in
@@ -0,0 +1,13 @@
+-- Config.hs
+--
+-- Auto-generated.
+
+module Adelie.Config (
+  portageTree,
+  portageDB
+) where
+
+portageTree, portageDB :: String
+
+portageTree = @@PortageTree@@
+portageDB = @@PortageDB@@
diff --git a/Adelie/Contents.hs b/Adelie/Contents.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/Contents.hs
@@ -0,0 +1,94 @@
+-- Contents.hs
+--
+-- Module for parsing CONTENTS files, located in portageDB/category/package/.
+
+module Adelie.Contents (
+  Contents(..),
+
+  contentsFromCatName,
+  putContents,
+  putContentsLn,
+  readContents
+) where
+
+import Char (isDigit, isHexDigit, isSpace)
+import IO
+
+import Adelie.Colour
+import Adelie.ListEx
+import Adelie.Portage
+
+data Contents
+  = Dir String
+  | Obj String String Int
+  | Sym String String Int
+  deriving Show
+
+----------------------------------------------------------------
+
+contentsFromCatName :: (String, String) -> String
+contentsFromCatName (cat, name) = concatPath [portageDB,cat,name,"CONTENTS"]
+
+----------------------------------------------------------------
+
+readContents :: (Contents -> a -> IO (Bool, a)) -> FilePath -> a -> IO a
+readContents f fn a = do
+  r <- try read'
+  case r of
+    Left  _  -> return a
+    Right a' -> return a'
+  where
+    read' =
+      bracket (openFile fn ReadMode)
+              hClose
+              (readContents' f a)
+
+readContents' :: (Contents -> a -> IO (Bool, a)) -> a -> Handle -> IO a
+readContents' f a fp = do
+  eof <- hIsEOF fp
+  if eof
+    then return a
+    else do
+      ln <- hGetLine fp
+      (done, a') <- f (contentsParser ln) a
+      if done
+        then return a'
+        else readContents' f a' fp
+
+----------------------------------------------------------------
+
+putContents, putContentsLn :: Contents -> IO ()
+putContents   = putContents' off
+putContentsLn = putContents' off2
+
+putContents' :: IO () -> Contents -> IO ()
+putContents' f (Dir d)     = blue  >> putStr d >> f
+putContents' f (Obj o _ _) = white >> putStr o >> f
+putContents' f (Sym l t _) = cyan  >> putStr (l ++ " -> " ++ t) >> f
+
+----------------------------------------------------------------
+
+contentsParser :: String -> Contents
+contentsParser ('d':'i':'r':' ':dir) = (Dir dir)
+
+contentsParser ('o':'b':'j':' ':ln0) = (Obj obj md5 time)
+  where ln1 = dropWhile isSpace $ reverse ln0
+        (time', ln2) = break2 (not.isDigit) ln1
+        (md5', obj') = break2 (not.isHexDigit) ln2
+        obj  = reverse obj'
+        md5  = reverse md5'
+        time = digitsToInt (reverse time')
+
+contentsParser ('s':'y':'m':' ':ln0) = (Sym link target time)
+  where (link, ln1) = breakLink ln0
+        ln2 = reverse ln1
+        (time', target') = break2 (not.isDigit) ln2
+        target = reverse target'
+        time = digitsToInt (reverse time')
+contentsParser cont = error $ "contentsParser: 'dir: /obj: /sym: ' not found in " ++ show cont
+
+breakLink :: String -> (String, String)
+breakLink (' ':'-':'>':' ':xs) = ([], xs)
+breakLink (x:xs) = (x:as, bs)
+  where (as, bs) = breakLink xs
+breakLink [] = error "breakLink: ' -> ' not found"
diff --git a/Adelie/Depend.hs b/Adelie/Depend.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/Depend.hs
@@ -0,0 +1,140 @@
+-- Depend.hs
+--
+-- Module for parsing DEPEND and RDEPEND files, located in
+-- portageDB/cateogry/package/.
+
+module Adelie.Depend (
+  Version,
+  Dependency(..),
+
+  dependFromCatName,
+  readDepend,
+  putDependency
+) where
+
+import Data.Char (isSpace)
+import Data.List (nub)
+import Text.ParserCombinators.Parsec
+import Text.ParserCombinators.Parsec.Language
+import Text.ParserCombinators.Parsec.Token
+
+import Adelie.Portage
+
+type Version = String
+
+data Dependency
+  = GreaterEqual  String Version
+  | Equal         String Version
+  | Unstable      String Version
+  | NotInstalled  String
+  | Any           String
+    deriving (Eq, Show)
+
+----------------------------------------------------------------
+
+dependFromCatName :: (String, String) -> String
+dependFromCatName (cat, name) = concatPath [portageDB,cat,name,"RDEPEND"]
+
+----------------------------------------------------------------
+
+readDepend :: FilePath -> [String] -> IO [Dependency]
+readDepend fn iUse = do
+  r <- (start_parser fn) `catch` (\ _ -> return $ Right [])
+  case r of
+    Left err -> putStr "Parse error at " >> print err >> error "Aborting"
+    Right x  -> return $ nub x
+  where start_parser = parseFromFile (dependParser iUse)
+
+----------------------------------------------------------------
+
+putDependency :: Dependency -> IO ()
+putDependency (GreaterEqual p v) = putStr $ ">=" ++ p ++ '-':v
+putDependency (Equal p v)        = putStr $ '=':p ++ '-':v
+putDependency (Unstable p v)     = putStr $ '~':p ++ '-':v
+putDependency (NotInstalled p)   = putStr $ '!':p
+putDependency (Any p)            = putStr p
+
+----------------------------------------------------------------
+
+dependParser :: [String] -> Parser [Dependency]
+dependParser iUse = do
+  _skip <- spaces
+  packages <- many (dependParser' iUse)
+  eof
+  return $ concat packages
+
+dependParser' :: [String] -> Parser [Dependency]
+dependParser' iUse = lexeme tp pp
+  where tp = makeTokenParser emptyDef
+        pp = parseOr iUse
+         <|> parsePackageOrUse iUse
+
+parseOr :: [String] -> Parser [Dependency]
+parseOr iUse = do { string "||"
+                  ; spaces
+                  ; parseBrackets iUse
+                  }
+             <|>
+                parseBrackets iUse
+
+parsePackageOrUse :: [String] -> Parser [Dependency]
+parsePackageOrUse iUse =
+  do { p <- parsePackageOrUseWord
+     ; do { char '?'        -- useFlag
+          ; spaces
+          ; r <- parseBrackets iUse
+          ; let filt | head p == '!' = not $ (tail p) `elem` iUse
+                     | otherwise     = p `elem` iUse
+          ; if filt
+              then return r
+              else return []
+          }
+   <|> return [toDependency p]
+     }
+
+parsePackageOrUseWord :: Parser String
+parsePackageOrUseWord = do { result <- many1 (satisfy cond)
+                           -- skip[use]
+                           -- TODO: add it to output
+                           ; optionMaybe (do { char '['
+                                             ; _use <- many1 (satisfy (/= ']'))
+                                             ; char ']'
+                                             -- ; return use
+                                             ; return ()
+                                             })
+                           ; return result
+                           }
+  where
+    cond '?' = False
+    cond ')' = False
+    cond '[' = False
+    cond x = not $ isSpace x
+
+parseBrackets :: [String] -> Parser [Dependency]
+parseBrackets iUse = do { char '('
+                        ; spaces
+                        ; r <- manyTill (dependParser' iUse) (try (char ')'))
+                        ; return $ concat r
+                        }
+
+----------------------------------------------------------------
+
+breakVersion :: String -> (String, String)
+breakVersion str = (n, v)
+  where n = dropVersion str
+        v = drop (length n+1) str
+
+toDependency :: String -> Dependency
+
+toDependency ('>':'=':str) = (GreaterEqual n v)
+  where (n, v) = breakVersion str
+
+toDependency ('=':str) = (Equal n v)
+  where (n, v) = breakVersion str
+
+toDependency ('~':str) = (Unstable n v)
+  where (n, v) = breakVersion str
+
+toDependency ('!':n) = (NotInstalled n)
+
+toDependency n = (Any n)
diff --git a/Adelie/ListEx.hs b/Adelie/ListEx.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/ListEx.hs
@@ -0,0 +1,46 @@
+-- ListEx.hs
+--
+-- Extra list functions.
+
+module Adelie.ListEx (
+  break2,
+  concatMapM,
+  digitsToInt,
+  dropTail,
+  dropUntilAfter,
+  foldMUntil,
+  pad
+) where
+
+import Char       (digitToInt)
+import Data.List  (foldl')
+import Monad      (liftM)
+
+----------------------------------------------------------------
+
+break2 :: (a -> Bool) -> [a] -> ([a], [a])
+break2 f l = (h, drop 1 t)
+  where (h, t) = break f l
+
+concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
+concatMapM f xs = liftM concat $ mapM f xs
+
+digitsToInt :: String -> Int
+digitsToInt = foldl' (\ a b -> a*10 + digitToInt b) 0
+
+dropTail :: Int -> [a] -> [a]
+dropTail n s = take (length s-n) s
+
+dropUntilAfter :: (a -> Bool) -> [a] -> [a]
+dropUntilAfter f = dropWhile f . dropWhile (not.f)
+
+foldMUntil :: Monad m => (a -> b -> m a) -> (a -> Bool) -> a -> [b] -> m a
+foldMUntil _ _ a [] = return a
+foldMUntil f g a (x:xs) = do
+  a' <- f a x
+  case g a' of
+    True  -> return a'
+    False -> foldMUntil f g a' xs
+
+pad :: Int -> a -> [a] -> [a]
+pad n a str = take n (str ++ repeat a)
diff --git a/Adelie/Options.hs b/Adelie/Options.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/Options.hs
@@ -0,0 +1,13 @@
+{-# OPTIONS -fglasgow-exts #-}
+
+-- Options.hs
+--
+-- We keep some globals in C so we don't have to pass them around everywhere.
+
+module Adelie.Options (
+  colourEnabled,
+  setColourEnabled
+) where
+
+foreign import ccall unsafe "opts.h colour_enabled" colourEnabled :: IO Bool
+foreign import ccall unsafe "opts.h set_colour_enabled" setColourEnabled :: Bool -> IO ()
diff --git a/Adelie/Portage.hs b/Adelie/Portage.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/Portage.hs
@@ -0,0 +1,102 @@
+-- Portage.hs
+--
+-- Portage directories and file paths.
+
+module Adelie.Portage (
+  portageTree,
+  portageDB,
+  useDesc,
+  useDescPackage,
+
+  dropVersion,
+  concatPath,
+  fullnameFromCatName,
+  allInstalledPackages,
+  findInstalledPackages
+) where
+
+import Data.Char         (isDigit)
+import System.Directory  (getDirectoryContents, doesDirectoryExist)
+import Data.List         (intersperse, sort)
+import Control.Monad     (liftM)
+
+import Adelie.ListEx
+import Adelie.Config
+
+-- Skips non-directory entries from returned list
+-- (symlinks to world file in our case)
+-- racy actually
+safeGetDirectoryContents :: FilePath -> IO [FilePath]
+safeGetDirectoryContents dir =
+    do yet <- doesDirectoryExist dir
+       if yet then getDirectoryContents dir
+              else return []
+
+portageProfiles :: String
+portageProfiles = portageTree ++ "/profiles"
+
+-- Where the global use flag descriptions are, 'use.desc'.
+useDesc :: String
+useDesc = portageProfiles ++ "/use.desc"
+
+-- Where the package specific use flag descriptions are, 'use.local.desc'.
+useDescPackage :: String
+useDescPackage = portageProfiles ++ "/use.local.desc"
+
+----------------------------------------------------------------
+
+dropVersion :: String -> String
+dropVersion [] = []
+dropVersion ('-':x:xs)
+  | isDigit x = []
+  | otherwise = '-':x:(dropVersion xs)
+dropVersion (x:xs) = x:(dropVersion xs)
+
+concatPath :: [String] -> String
+concatPath = concat.intersperse "/"
+
+fullnameFromCatName :: (String, String) -> String
+fullnameFromCatName (cat, name) = cat ++ '/':name
+
+----------------------------------------------------------------
+
+allInstalledPackages :: IO [(String, String)]
+allInstalledPackages = do
+  cats <- liftM (sort.filter filterHidden) (safeGetDirectoryContents portageDB)
+  concatMapM allInstalledPackagesInCategory cats
+
+allInstalledPackagesInCategory :: String -> IO [(String, String)]
+allInstalledPackagesInCategory cat = do
+  names <- liftM (sort.filter filterHidden) (safeGetDirectoryContents path)
+  return $ map (\ a -> (cat, a)) names
+  where path = portageDB ++ '/':cat
+
+----------------------------------------------------------------
+
+findInstalledPackages :: [String] -> IO [(String, String)]
+findInstalledPackages names = concatMapM findInstPackages names
+
+findInstPackages :: String -> IO [(String, String)]
+findInstPackages name =
+  if null b
+    then findInstPackages' a
+    else findInstPackagesInCategory a b
+  where (a, b) = break2 (== '/') name
+
+findInstPackages' :: String -> IO [(String, String)]
+findInstPackages' pack = do
+  cats  <- liftM (sort.filter filterHidden) (safeGetDirectoryContents portageDB)
+  concatMapM (flip findInstPackagesInCategory pack) cats
+
+findInstPackagesInCategory :: String -> String -> IO [(String, String)]
+findInstPackagesInCategory cat pack = do
+  packs <- liftM (sort.filter cond) (safeGetDirectoryContents (portageDB++'/':cat))
+  return (zip (repeat cat) packs)
+  where
+    cond ('.':_) = False
+    cond p = (pack == p) || (pack == dropVersion p)
+
+----------------------------------------------------------------
+filterHidden :: String -> Bool
+filterHidden ('.':_) = False
+filterHidden _ = True
diff --git a/Adelie/Pretty.hs b/Adelie/Pretty.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/Pretty.hs
@@ -0,0 +1,31 @@
+-- Pretty.hs
+--
+-- Colourful output.
+
+module Adelie.Pretty (
+  putCatName,
+  putCatNameLn,
+  putNum,
+  putNumLn
+) where
+
+import Adelie.Colour
+
+----------------------------------------------------------------
+
+putCatName, putCatNameLn :: (String, String) -> IO ()
+putCatName   = putCatName' off
+putCatNameLn = putCatName' off2
+
+putCatName' :: IO () -> (String, String) -> IO ()
+putCatName' f (c, n) =
+  inYellow (putStr c) >> putChar '/' >> yellow >> putStr n >> f
+
+----------------------------------------------------------------
+
+putNum, putNumLn :: Int -> IO ()
+putNum   = putNum' off
+putNumLn = putNum' off2
+
+putNum' :: IO () -> Int -> IO ()
+putNum' f n = cyan >> putStr (show n) >> f
diff --git a/Adelie/Provide.hs b/Adelie/Provide.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/Provide.hs
@@ -0,0 +1,22 @@
+-- Provide.hs
+--
+-- Module for parsing PROVIDE files, located in portageDB/category/package/.
+
+module Adelie.Provide (
+  provideFromCatName,
+  readProvide
+) where
+
+import Monad (liftM)
+
+import Adelie.Portage
+
+----------------------------------------------------------------
+
+provideFromCatName :: (String, String) -> String
+provideFromCatName (cat,name) = concatPath [portageDB,cat,name,"PROVIDE"]
+
+----------------------------------------------------------------
+
+readProvide :: FilePath -> IO [String]
+readProvide fn = (liftM words (readFile fn)) `catch` (\ _ -> return [])
diff --git a/Adelie/QChangelog.hs b/Adelie/QChangelog.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/QChangelog.hs
@@ -0,0 +1,169 @@
+-- QChangelog.hs
+--
+-- Module to find the changelog of a package.
+
+module Adelie.QChangelog (
+  qChangelog,
+  qLogFile
+) where
+
+import Char   (isDigit, isSpace)
+import Monad  (unless)
+
+import Adelie.Colour
+import Adelie.CompareVersion
+import Adelie.ListEx
+import Adelie.Portage
+import Adelie.Pretty
+
+----------------------------------------------------------------
+
+qLogFile :: [String] -> IO ()
+qLogFile args = mapM_ (putStrLn.logFile) =<< findInstalledPackages args
+
+logFile :: (String, String) -> String
+logFile (cat, name) = 
+  portageTree ++ '/':cat ++ '/':(dropVersion name) ++ "/ChangeLog"
+
+----------------------------------------------------------------
+
+qChangelog :: [String] -> IO ()
+qChangelog []       = return ()
+qChangelog [x]      = mapM_ (changelog Nothing)  =<< findInstalledPackages [x]
+qChangelog (x:y:_)  = mapM_ (changelog (Just y)) =<< findInstalledPackages [x]
+
+changelog :: Maybe String -> (String, String) -> IO ()
+changelog end catname@(_, name) = do
+  putStr "ChangeLog since " >> putCatNameLn catname
+  log' <- readFile (logFile catname)
+  puts name end log'
+
+----------------------------------------------------------------
+
+puts :: String -> Maybe String -> String -> IO ()
+
+puts _ _ [] = return ()
+puts inst end ('#':l) = puts inst end (dropUntilAfter isNewLine l)
+
+puts inst end ('*':l0) = do
+  case maybeCompareVersion package end of
+    GT -> puts inst end next
+    _  -> case compareVersion inst package of
+      LT -> putSection package date >> putDesc ls >> puts inst end next
+      _  -> puts inst end next
+  where (section, next) = breakSection l0
+        (line, ls) = break2 isNewLine section
+        (l1, l2) = break2 ('(' ==) line
+        package = takeWhile (not.isSpace) l1
+        date = takeWhile (')' /=) l2
+
+puts inst end (_:ls) = puts inst end ls
+
+maybeCompareVersion :: String -> Maybe String -> Ordering
+maybeCompareVersion _ Nothing   = LT
+maybeCompareVersion a (Just b)  = compareVersion a b
+
+----------------------------------------------------------------
+
+breakSection :: String -> (String, String)
+breakSection str = (reverse h, t)
+  where (h, t) = breakSection' [] str
+
+breakSection' :: String -> String -> (String, String)
+breakSection' acc [] = (acc, [])
+breakSection' acc nx@('\n':'*':_) = (acc, nx)
+breakSection' acc (x:xs) = breakSection' (x:acc) xs
+
+----------------------------------------------------------------
+
+putSection :: String -> String -> IO ()
+putSection package date = do
+  putStr "* " >> inYellow (putStr package) 
+  putStr " (" >> inWhite (putStr date) >> putStrLn ")"
+
+----------------------------------------------------------------
+
+putDesc :: String -> IO ()
+putDesc [] = return ()
+putDesc str = mapM_ putDesc' ls
+  where ls = doublelines (dropWhile isNewLine str)
+
+putDesc' :: String -> IO ()
+putDesc' str = do
+  if beginsWithDate (dropWhile isSpace str)
+    then do
+      putHeader header
+      putBody body
+    else
+      putBody str
+  where (header, body) = break2 (':' ==) str
+
+doublelines :: String -> [String]
+doublelines str =
+  case t of
+    []           -> h : []
+    (_:'\n':xs)  -> h : doublelines xs
+    (_:xs)       -> let (l0:l1) = doublelines xs 
+                    in (h ++ ('\n' : l0)) : l1
+  where (h, t) = break ('\n' ==) str
+
+beginsWithDate :: String -> Bool
+beginsWithDate []   = False
+beginsWithDate [_]  = False
+beginsWithDate (x:y:_)
+  | not (isDigit x)         = False
+  | not (isDigitOrSpace y)  = False
+  | otherwise = True
+
+----------------------------------------------------------------
+
+putHeader :: String -> IO ()
+putHeader str0 = do
+  putChar '\n'
+  inWhite (putStr date)
+  unless (null name) (putStr name)
+  red >> putStr ('<':mail) >> putChar '>' >> off2
+  putStr "    " >> putFiles 76 files
+  where
+    (date, str1) = break2 (';' ==) str0
+    (name, str2) = break2 ('<' ==) str1
+    (mail, str3) = break2 ('>' ==) str2
+    files = words str3
+
+putFiles :: Int -> [String] -> IO ()
+putFiles _ [] = putChar '\n'
+putFiles rem' (";":files) = putFiles rem' files
+putFiles rem' (f:files) =
+  if rem' < len
+    then do
+      putChar '\n' >> putStr "    "
+      putFiles 76 (f:files)
+    else do
+      cyan
+      if (last f == ',')
+        then putStr (dropTail 1 f) >> off >> putStr ", "
+        else putStr f >> off >> putChar ' '
+      putFiles (rem' - len - 1) files
+  where len = length f
+
+----------------------------------------------------------------
+
+putBody :: String -> IO ()
+putBody [] = putChar '\n'
+
+putBody ('#':c0) =
+  case bug of
+    [] -> putChar '#' >> putBody cs
+    _  -> inMagenta (putStr ('#':bug)) >> putBody cs
+  where (bug, cs) = span (isDigitOrSpace) c0
+
+putBody (c:cs) = putChar c >> putBody cs
+
+----------------------------------------------------------------
+
+isNewLine :: Char -> Bool
+isNewLine '\n'  = True
+isNewLine _     = False
+
+isDigitOrSpace :: Char -> Bool
+isDigitOrSpace x = isDigit x || isSpace x
diff --git a/Adelie/QCheck.hs b/Adelie/QCheck.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/QCheck.hs
@@ -0,0 +1,77 @@
+-- QMD5sum.hs
+--
+-- Check MD5sums and timestamps of installed packages.
+
+module Adelie.QCheck (qCheck) where
+
+import Char               (isHexDigit)
+import IO
+
+import System.Process     (runProcess, waitForProcess, ProcessHandle)
+import System.Posix.Files (getFileStatus)
+import System.Posix.IO    (createPipe, fdToHandle)
+
+import Adelie.Colour
+import Adelie.Contents
+import Adelie.Portage
+import Adelie.Pretty
+
+-- Good, Bad
+type Count = (Int, Int)
+
+----------------------------------------------------------------
+
+qCheck :: [String] -> IO ()
+qCheck args = mapM_ check =<< findInstalledPackages args
+
+check :: (String, String) -> IO ()
+check catname = do
+  putStr "Checking " >> putCatNameLn catname
+  (g, b) <- readContents check' contents (0, 0)
+  putNum g >> putStr " out of " >> putNum (b+g) >> putStrLn " files good"
+  putChar '\n'
+  where contents = contentsFromCatName catname
+
+check' :: Contents -> Count -> IO (Bool, Count)
+
+check' (Dir _) (g, b) = return (False, (g+1, b))
+
+check' (Obj o m _) (g, b) = do
+  r <- try (getFileStatus o)
+  case r of
+    Left _e -> do
+      inRed (putStr "!!! ") >> putStr o >> putStrLn " does not exist"
+      return (False, (g, b+1))
+    Right _stat -> do
+      (rd, wr) <- createPipeHandle
+      runMD5sum o (Just wr) >>= waitForProcess
+      ln <- hGetLine rd
+      hClose rd
+      hClose wr
+      if m == (takeWhile isHexDigit ln)
+        then return (False, (g+1, b))
+        else putMD5error o >> return (False, (g, b+1))
+
+check' (Sym _ _ _) (g, b) = return (False, (g+1, b))
+
+----------------------------------------------------------------
+
+createPipeHandle :: IO (Handle, Handle)
+createPipeHandle = do
+  (read', write) <- createPipe
+  hRead  <- fdToHandle read'
+  hWrite <- fdToHandle write
+  return (hRead, hWrite)
+
+----------------------------------------------------------------
+runMD5sum :: String
+             -> Maybe Handle
+             -> IO ProcessHandle
+runMD5sum f stdout' = runProcess md5sum [f] Nothing Nothing stdin' stdout' stderr'
+  where md5sum = "/usr/bin/md5sum"
+        stdin'  = Nothing
+        stderr' = Nothing
+
+putMD5error :: String -> IO ()
+putMD5error file =
+  inRed (putStr "!!! ") >> putStrLn (file ++ " has incorrect md5sum")
diff --git a/Adelie/QDepend.hs b/Adelie/QDepend.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/QDepend.hs
@@ -0,0 +1,75 @@
+-- QDepend.hs
+--
+-- Module to list packages depending on an installed package.
+
+module Adelie.QDepend (qDepend) where
+
+import Adelie.Colour
+import Adelie.CompareVersion
+import Adelie.Depend
+import Adelie.ListEx
+import Adelie.Portage
+import Adelie.Pretty
+import Adelie.Provide
+import Adelie.Use
+
+----------------------------------------------------------------
+
+qDepend :: [String] -> IO ()
+qDepend [] = return ()
+qDepend args = qDepend' =<< findInstalledPackages args
+
+qDepend' :: [(String, String)] -> IO ()
+qDepend' catnames = do
+  allPacks <- allInstalledPackages
+  let allPacks2 = map fullnameFromCatName allPacks
+  mapM_ (dep allPacks2) catnames
+
+dep :: [String] -> (String, String) -> IO ()
+dep allPacks catname = do
+  putStr "Packages depending on " >> putCatNameLn catname
+  provide <- readProvide fnProvide
+  mapM_ (dep' (fullname:provide)) allPacks
+  putChar '\n'
+  where fullname = fullnameFromCatName catname
+        fnProvide = provideFromCatName catname
+
+dep' :: [String] -> String -> IO ()
+dep' provided fullname =
+  readUse fnIUse >>= readDepend fnDepend >>= puts fullname provided
+  where fnDepend = concatPath [portageDB,fullname,"RDEPEND"]
+        fnIUse = concatPath [portageDB,fullname,"USE"]
+
+puts :: String -> [String] -> [Dependency] -> IO ()
+puts str provided iWant = mapM_ print' perms
+  where perms = [ (p, w) | p <- provided, w <- iWant, w `satisfiedBy` p ]
+        print' (_p, w) =
+          white >> putStr (pad 32 ' ' str) >> off >>
+          putStr "\t( " >> putDependency w >> putStrLn " )"
+
+-------------------------------------------------------------
+
+breakVersion :: String -> (String, String)
+breakVersion str = (n, v)
+  where n = dropVersion str
+        v = drop (length n+1) str
+
+satisfiedBy :: Dependency -> String -> Bool
+
+(GreaterEqual wantName wantVer) `satisfiedBy` provided =
+  (wantName == provName) && (compareVersion provVer wantVer) /= LT
+  where (provName, provVer) = breakVersion provided
+
+(Equal wantName wantVer) `satisfiedBy` provided = 
+  (wantName == provName) && (compareVersion provVer wantVer) == EQ
+  where (provName, provVer) = breakVersion provided
+
+(Unstable wantName wantVer) `satisfiedBy` provided =
+  (wantName == provName) && (compareVersion provVer wantVer) == EQ
+  where (provName, provVer) = breakVersion provided
+
+(NotInstalled _) `satisfiedBy` _ = False
+
+(Any wantName) `satisfiedBy` provided =
+  wantName == provName
+  where provName = dropVersion provided
diff --git a/Adelie/QHasUse.hs b/Adelie/QHasUse.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/QHasUse.hs
@@ -0,0 +1,38 @@
+-- QHasUse.hs
+--
+-- Module to list all installed packages with a particular use flag.
+
+module Adelie.QHasUse (qHasUse) where
+
+import Monad (when)
+
+import Adelie.Colour
+import Adelie.Portage
+import Adelie.Pretty
+import Adelie.Use
+
+----------------------------------------------------------------
+
+qHasUse :: [String] -> IO ()
+qHasUse [] = return ()
+qHasUse args = qHasUse' args =<< allInstalledPackages 
+
+qHasUse' :: [String] -> [(String, String)] -> IO ()
+qHasUse' uses catnames = mapM_ (hasUse catnames) uses
+
+hasUse :: [(String, String)] -> String -> IO ()
+hasUse catnames use = do
+  putStr "Packages installed with " >> putUse use >> putStrLn " USE flag"
+  mapM_ (flip hasUse' use) catnames
+  putChar '\n'
+
+hasUse' :: (String, String) -> String -> IO ()
+hasUse' catname use = do
+  iUse <- readIUse fnIUse
+  when (use `elem` iUse) (putCatNameLn catname)
+  where fnIUse = iUseFromCatName catname
+
+----------------------------------------------------------------
+
+putUse :: String -> IO ()
+putUse u = blue >> putStr u >> off
diff --git a/Adelie/QList.hs b/Adelie/QList.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/QList.hs
@@ -0,0 +1,56 @@
+-- QList.hs
+--
+-- Module to list the contents of an installed package.
+
+module Adelie.QList (
+  ListTypes(..),
+
+  qList
+) where
+
+import Adelie.Contents
+import Adelie.Portage
+import Adelie.Pretty
+
+data ListTypes
+  = ListDirs
+  | ListFiles
+  | ListLinks
+  | ListAll
+  deriving Eq
+
+---------------------------------------------------------------- 
+
+qList :: ListTypes -> [String] -> IO ()
+qList types args = mapM_ (list puts) =<< findInstalledPackages args
+  where
+    puts = case types of
+      ListDirs  -> putsD
+      ListFiles -> putsF
+      ListLinks -> putsL
+      _         -> putsA
+
+list :: (Contents -> () -> IO (Bool, ()))
+        -> (String, String)
+        -> IO ()
+list puts catname = do
+  putStr "Contents of " >> putCatNameLn catname
+  readContents puts contents ()
+  putChar '\n'
+  where contents = contentsFromCatName catname
+
+----------------------------------------------------------------
+
+putsA, putsD, putsF, putsL :: Contents -> () -> IO (Bool, ())
+
+putsA (Obj o _ _) _ = putStrLn o      >> return (False, ())
+putsA c _           = putContentsLn c >> return (False, ())
+
+putsD c@(Dir _) _   = putContentsLn c >> return (False, ())
+putsD _ _           = return (False, ())
+
+putsF (Obj o _ _) _ = putStrLn o >> return (False, ())
+putsF _ _           = return (False, ())
+
+putsL c@(Sym _ _ _)_= putContentsLn c >> return (False, ())
+putsL _ _           = return (False, ())
diff --git a/Adelie/QOwn.hs b/Adelie/QOwn.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/QOwn.hs
@@ -0,0 +1,79 @@
+-- QOwn.hs
+--
+-- Module to query who owns a particular file.
+
+module Adelie.QOwn (
+  qOwn,
+  qOwnRegex
+) where
+
+import List       (delete)
+import Monad      (when)
+import Text.Regex (Regex, matchRegex, mkRegex)
+
+import Adelie.Contents
+import Adelie.ListEx
+import Adelie.Portage
+import Adelie.Pretty
+
+----------------------------------------------------------------
+
+qOwn :: [String] -> IO ()
+qOwn [] = return ()
+qOwn args = do
+  foldMUntil qOwn' null args =<< allInstalledPackages
+  putChar '\n'
+
+qOwn' :: [String] -> (String, String) -> IO [String]
+qOwn' files catname = readContents (puts catname) contents files
+  where contents = contentsFromCatName catname
+
+----------------------------------------------------------------
+
+puts :: (String, String) -> Contents -> [String] -> IO (Bool, [String])
+puts catname c fs =
+  if c `contentsElem` fs
+    then do
+      let fs' = deleteContent c fs
+      putCatName catname >> putStr " (" >> putContents c >> putStrLn ")"
+      return (null fs', fs')
+    else
+      return (False, fs)
+
+contentsElem :: Contents -> [String] -> Bool
+(Dir d)     `contentsElem` fs = d `elem` fs
+(Obj o _ _) `contentsElem` fs = o `elem` fs
+(Sym l _ _) `contentsElem` fs = l `elem` fs
+
+-- Only remove objects from the list.
+deleteContent :: Contents -> [String] -> [String]
+deleteContent (Obj o _ _) fs = delete o fs
+deleteContent _ fs = fs
+
+----------------------------------------------------------------
+
+qOwnRegex :: [String] -> IO ()
+qOwnRegex args = mapM_ (qOwnRegex' pats) =<< allInstalledPackages
+  where pats = map mkRegex args
+
+qOwnRegex' :: [Regex] -> (String, String) -> IO ()
+qOwnRegex' pats catname = readContents (putsRegex catname pats) contents ()
+  where contents = contentsFromCatName catname
+
+putsRegex :: (String, String) -> [Regex] -> Contents -> () -> IO (Bool, ())
+putsRegex catname pats c _ = do
+  when (c `regexElem` pats) match
+  return (False, ())
+  where match = putCatName catname >> putStr " (" >> putContents c>>putStrLn ")"
+
+regexElem :: Contents -> [Regex] -> Bool
+(Dir d)     `regexElem` pats = mapMaybeOnce (flip matchRegex d) pats
+(Obj o _ _) `regexElem` pats = mapMaybeOnce (flip matchRegex o) pats
+(Sym l _ _) `regexElem` pats = mapMaybeOnce (flip matchRegex l) pats
+
+mapMaybeOnce :: (a -> Maybe b) -> [a] -> Bool
+mapMaybeOnce _ [] = False
+mapMaybeOnce f (x:xs) =
+  case f x of 
+    Just _a  -> True
+    Nothing -> mapMaybeOnce f xs
diff --git a/Adelie/QSize.hs b/Adelie/QSize.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/QSize.hs
@@ -0,0 +1,61 @@
+-- QSize.hs
+--
+-- Check the disk usage of an installed packaged.
+
+module Adelie.QSize (qSize) where
+
+import Directory            (doesDirectoryExist)
+import IO                   (try)
+import Monad                (mapM_, when)
+import System.Posix.Files   (fileSize, getFileStatus)
+import System.Posix.Types   (FileOffset)
+
+import Adelie.Colour
+import Adelie.Contents
+import Adelie.Portage
+import Adelie.Pretty
+
+-- Size, Dir, Obj, Sym, Inaccessible
+type Count = (FileOffset, Int, Int, Int, Int)
+
+----------------------------------------------------------------
+
+qSize :: [String] -> IO ()
+qSize args = mapM_ size =<< findInstalledPackages args
+
+size :: (String, String) -> IO ()
+size catname = do
+  putStr "Size of " >> putCatNameLn catname
+  (size', dir, obj, sym, err) <- readContents count contents (0, 0, 0, 0, 0)
+  putStr "    Directories: " >> putNumLn dir
+  putStr "          Files: " >> putNumLn obj
+
+  when (sym > 0) (putStr "          Links: " >> putNumLn sym)
+  when (err > 0) (putStr "   Inaccessible: " >> putNumLn err)
+
+  putStr "     Total size: " >> putSizeLn size'
+  putChar '\n'
+  where contents = contentsFromCatName catname
+
+----------------------------------------------------------------
+
+count :: Contents -> Count -> IO (Bool, Count)
+
+count (Dir d) (s,w,x,y,z) = do
+  r <- doesDirectoryExist d
+  if r
+    then return (False, (s,w+1,x,y,z))
+    else return (False, (s,w,x,y,z+1)) 
+
+count (Obj o _ _) (s,w,x,y,z) = do
+  r <- try (getFileStatus o)
+  case r of
+    Left _err -> return (False, (s, w, x, y, z+1))
+    Right st -> return (False, (s+fileSize st, w, x+1, y, z))
+
+count (Sym _ _ _) (s,w,x,y,z) = return (False, (s,w,x,y+1,z))
+
+----------------------------------------------------------------
+
+putSizeLn :: FileOffset -> IO ()
+putSizeLn n = cyan >> putStr (show (n `div` 1024)) >> putStr " kb" >> off2
diff --git a/Adelie/QUse.hs b/Adelie/QUse.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/QUse.hs
@@ -0,0 +1,75 @@
+-- QUse.hs
+--
+-- Module to describe the use flags of an installed package.
+
+module Adelie.QUse (qUse) where
+
+import Data.HashTable as HashTable
+import Monad (mapM_, unless)
+
+import Adelie.Colour
+import Adelie.ListEx
+import Adelie.Portage
+import Adelie.Pretty
+import Adelie.Use
+import Adelie.UseDesc
+
+----------------------------------------------------------------
+
+qUse :: [String] -> IO ()
+qUse args = qUse' =<< findInstalledPackages args
+
+qUse' :: [(String, String)] -> IO ()
+qUse' [] = return ()
+qUse' catnames = do
+  useDesc' <- readUseDesc
+  useDescPackage' <- readUseDescPackage min' max'
+  mapM_ (use useDesc' useDescPackage') catnames
+  where min' = dropVersion $ fullnameFromCatName $ minimum catnames
+        max' = dropVersion $ fullnameFromCatName $ maximum catnames
+
+use :: UseDescriptions -> UseDescriptions -> (String, String) -> IO ()
+use useDesc' useDescPackage' catname = do
+  iUse <- readIUse fnIUse
+  pUse <- readUse  fnPUse
+  let len = maximum $ map length iUse
+  use' catname len useDesc' useDescPackage' iUse pUse
+  where fnIUse = iUseFromCatName catname
+        fnPUse = useFromCatName catname
+
+use' :: (String, String) -> Int -> UseDescriptions -> UseDescriptions ->
+        [String] -> [String] -> IO ()
+
+use' catname _ _ _ [] _ = putStr "No USE flags for " >> putCatNameLn catname
+use' catname len useDesc' useDescPackage' iUse pUse = do
+  putStr "USE flags for " >> putCatNameLn catname
+  mapM_ (format len useDesc' useDescPackage' pUse) iUse
+  putChar '\n'
+
+----------------------------------------------------------------
+
+format :: Int -> UseDescriptions -> UseDescriptions ->
+          [String] -> String -> IO ()
+
+format len useDesc' useDescPackage' pUse iUse =
+  inst >> putStr (pad len ' ' iUse) >> off >> putStr " : " >> desc
+  where
+    inst = if iUse `elem` pUse
+            then putStr " + " >> red
+            else putStr "   " >> blue
+
+    desc = do
+      end <- desc' useDescPackage'
+      unless end (do
+        end' <- desc' useDesc'
+        unless end' (putStrLn "<< no description >>"))
+
+    desc' descs = do
+      r <- HashTable.lookup descs iUse 
+      case r of
+        Just d  -> puts d >> return True
+        Nothing -> return False
+
+puts :: String -> IO ()
+puts d@('!':'!':_) = red >> putStr d >> off2
+puts d = putStrLn d
diff --git a/Adelie/QWant.hs b/Adelie/QWant.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/QWant.hs
@@ -0,0 +1,26 @@
+-- QWant.hs
+--
+-- List the direct dependencies of a file.
+
+module Adelie.QWant (qWant) where
+
+import Adelie.Depend
+import Adelie.Portage
+import Adelie.Pretty
+import Adelie.Use
+
+-------------------------------------------------------------
+
+qWant :: [String] -> IO ()
+qWant args = mapM_ qWant' =<< findInstalledPackages args
+
+qWant' :: (String, String) -> IO ()
+qWant' catname = do
+  putStr "Dependencies for " >> putCatNameLn catname
+  readUse fnIUse >>= readDepend fnDepend >>= mapM_ puts
+  putChar '\n'
+  where fnDepend = dependFromCatName catname
+        fnIUse = useFromCatName catname
+
+puts :: Dependency -> IO ()
+puts a = putDependency a >> putChar '\n'
diff --git a/Adelie/Use.hs b/Adelie/Use.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/Use.hs
@@ -0,0 +1,34 @@
+-- Use.hs
+--
+-- Module for parsing USE and IUSE, files, located in
+-- portageDB/category/package/.
+
+module Adelie.Use (
+  useFromCatName,
+  iUseFromCatName,
+  readUse,
+  readIUse
+) where
+
+import List   (nub, sort)
+import Monad  (liftM)
+
+import Adelie.Portage
+
+----------------------------------------------------------------
+
+useFromCatName :: (String, String) -> String
+useFromCatName (cat, name) = concatPath [portageDB,cat,name,"USE"]
+
+iUseFromCatName :: (String, String) -> String
+iUseFromCatName (cat, name) = concatPath [portageDB,cat,name,"IUSE"]
+
+----------------------------------------------------------------
+
+readUse :: FilePath -> IO [String]
+readUse fn = (liftM words $ readFile fn) `catch` (\ _ -> return [])
+
+-- IUSE files sometimes have duplicate USE flags.  I am not sure if it is the
+-- intended behaviour, but I filter them out.
+readIUse :: FilePath -> IO [String]
+readIUse fn = (liftM (nub.sort.words) $ readFile fn) `catch` (\ _ -> return [])
diff --git a/Adelie/UseDesc.hs b/Adelie/UseDesc.hs
new file mode 100644
--- /dev/null
+++ b/Adelie/UseDesc.hs
@@ -0,0 +1,86 @@
+-- UseDesc.hs
+--
+-- Module for parsing portageProfile/use.desc and use.local.desc files.
+
+module Adelie.UseDesc (
+  UseDescriptions,
+  readUseDesc,
+  readUseDescPackage
+) where
+
+import Char (isSpace)
+import Data.HashTable as HashTable
+import Monad (when)
+
+import Adelie.ListEx
+import Adelie.Portage
+
+type UseDescriptions = HashTable String String
+
+----------------------------------------------------------------
+
+readUseDesc :: IO UseDescriptions
+readUseDesc = do
+  table <- HashTable.new (==) hashString
+  ls <- readFile useDesc
+  mapM_ (useParser table) (lines ls)
+  return table
+
+useParser :: UseDescriptions -> String -> IO ()
+useParser _ ('#':_) = return ()
+useParser table line = insert table use desc
+  where (use, desc) = myBreak line
+
+myBreak :: String -> (String, String)
+myBreak [] = ("", "")
+myBreak (' ':'-':' ':xs) = ("", xs)
+myBreak (x:xs) = (x:ys, zs)
+  where (ys, zs) = myBreak xs
+
+----------------------------------------------------------------
+
+readUseDescPackage :: String -> String -> IO UseDescriptions
+readUseDescPackage start end = do
+  table <- HashTable.new (==) hashString
+  ls <- readFile useDescPackage
+  mapMUntil_ (useParser2 table start end) (lines ls)
+  return table
+
+mapMUntil_ :: Monad m => (a -> m Bool) -> [a] -> m ()
+mapMUntil_ _ [] = return ()
+mapMUntil_ f (x:xs) = do
+  r <- f x
+  when r (mapMUntil_ f xs)
+
+useParser2 :: UseDescriptions -> String -> String -> String -> IO Bool
+useParser2 _ _ _ [] = return True
+useParser2 _ _ _ ('#':_) = return True
+useParser2 table start end str = do
+  case mid start catname end of
+      LT -> return True
+      EQ -> insert table use desc >> return True
+      GT -> return False
+  where str' = reverse $ dropWhile isSpace $ reverse str
+        (catname, rest) = break2 (':' ==) str'
+        (use, desc) = myBreak rest
+  
+----------------------------------------------------------------
+
+-- In Haskell, vim-core > vim
+-- In sort,    vim-core < vim
+-- Work around it.
+myCompare :: String -> String -> Ordering
+myCompare [] [] = EQ
+myCompare  _ [] = LT
+myCompare []  _ = GT
+myCompare (a:as) (b:bs) =
+  if r == EQ
+    then myCompare as bs 
+    else r
+  where r = compare a b
+
+mid :: String -> String -> String -> Ordering
+mid l m r
+  | myCompare l m == GT = LT
+  | myCompare m r == GT = GT
+  | otherwise = EQ
diff --git a/Adelie/opts.c b/Adelie/opts.c
new file mode 100644
--- /dev/null
+++ b/Adelie/opts.c
@@ -0,0 +1,13 @@
+#include "opts.h"
+
+static bool colour = true;
+
+bool colour_enabled(void)
+{
+    return colour;
+}
+
+void set_colour_enabled(const bool on)
+{
+    colour = on;
+}
diff --git a/Adelie/opts.h b/Adelie/opts.h
new file mode 100644
--- /dev/null
+++ b/Adelie/opts.h
@@ -0,0 +1,9 @@
+#ifndef __included_opts_h
+#define __included_opts_h
+
+#include <stdbool.h>
+
+extern bool colour_enabled(void);
+extern void set_colour_enabled(const bool on);
+
+#endif
diff --git a/LICENCE.txt b/LICENCE.txt
new file mode 100644
--- /dev/null
+++ b/LICENCE.txt
@@ -0,0 +1,23 @@
+
+  Copyright (C) 2006 David Wang
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+  2. Altered source versions must be plainly marked as such, and must not
+     be misrepresented as being the original software.
+
+  3. This notice may not be removed or altered from any source distribution.
+
+
+  [For informational purposes, this is the zlib licence.]
diff --git a/Main.hs b/Main.hs
new file mode 100644
--- /dev/null
+++ b/Main.hs
@@ -0,0 +1,164 @@
+-- Main.hs
+--
+-- Adelie is a collection of scripts to querying portage packages. 
+
+module Main (main) where
+
+import System (getArgs, getProgName)
+
+import Adelie.Colour
+import Adelie.Options
+import Adelie.QChangelog
+import Adelie.QCheck
+import Adelie.QDepend
+import Adelie.QHasUse
+import Adelie.QList
+import Adelie.QOwn
+import Adelie.QSize
+import Adelie.QUse
+import Adelie.QWant
+
+type CommandProc = [String] -> IO ()
+
+data Command
+  = Short String String CommandProc
+  | Long  String String String CommandProc
+
+logCommands :: [Command]
+logCommands = [
+  (Long   "c" "changes"
+          "list changes since the installed version"
+          qChangelog),
+  (Short  "cl"
+          "find the changelog of a package"
+          qLogFile)
+  ]
+
+listCommands :: [Command]
+listCommands = [
+  (Long   "f" "files"
+          "list the contents of a package"
+          (qList ListAll)),
+  (Short  "fd"
+          "list the directories in a package"
+          (qList ListDirs)),
+  (Short  "ff"
+          "list the files in a package"
+          (qList ListFiles)),
+  (Short  "fl"
+          "list the links in a package"
+          (qList ListLinks))
+  ]
+
+ownCommands :: [Command]
+ownCommands = [
+  (Long   "b" "belongs"
+          "find the package(s) owning a file"
+          qOwn),
+  (Short  "bp"
+          "find the package(s) owning a file with regexp"
+          qOwnRegex),
+  (Long   "s" "size"
+          "find the size of files in a package"
+          qSize),
+  (Long   "k" "check"
+          "check MD5sums and timestamps of a package"
+          qCheck)
+  ]
+
+dependCommands :: [Command]
+dependCommands = [
+  (Long   "d" "depends"
+          "list packages directly depending on this package"
+          qDepend),
+  (Short  "dd"
+          "list direct dependencies of a package"
+          qWant)
+  ]
+
+useCommands :: [Command]
+useCommands = [
+  (Long   "u" "uses"
+          "describe a package's USE flags"
+          qUse),
+  (Long   "h" "hasuse"
+          "list all packages with a USE flag"
+          qHasUse)
+  ]
+
+allCommands :: [Command]
+allCommands = logCommands ++ listCommands ++ ownCommands ++ dependCommands ++ useCommands
+
+----------------------------------------------------------------
+
+main :: IO ()
+main = do
+  args0 <- getArgs
+  let (options, commands) = span isOption args0
+  mapM_ parseOptions options
+  case commands of
+    [] -> usage
+    (cmd:cargs) -> (runCommand cmd allCommands) cargs
+
+
+isOption :: String -> Bool
+isOption = ('-' ==) . head
+
+----------------------------------------------------------------
+
+parseOptions :: String -> IO ()
+parseOptions [] = return ()
+
+parseOptions "-C"         = setColourEnabled False
+parseOptions "--nocolor"  = setColourEnabled False
+parseOptions "--nocolour" = setColourEnabled False
+
+parseOptions _ = return ()
+
+----------------------------------------------------------------
+
+runCommand :: String -> [Command] -> CommandProc
+runCommand _ [] = (\ _ -> usage)
+
+runCommand command ((Short cmd _ f):cs)
+  | command == cmd  = f
+  | otherwise       = runCommand command cs
+
+runCommand command ((Long cmd0 cmd1 _ f):cs)
+  | command == cmd0 = f
+  | command == cmd1 = f
+  | otherwise       = runCommand command cs
+
+----------------------------------------------------------------
+
+usage :: IO ()
+usage = do
+  prog <- getProgName
+  putStrLn "fquery 0.2\n"
+  putStrLn $ "Usage: " ++ prog ++ " [options] <command> <arguments>\n"
+
+  cyan >> putStr "Options:" >> off2
+  inYellow (putStr "    -C --nocolour") >> tab >> putStrLn "turn off colours"
+  nl
+
+  cyan >> putStr "Commands for Installed Packages:" >> off2
+  mapM_ putCommand logCommands; nl
+  mapM_ putCommand listCommands; nl
+  mapM_ putCommand ownCommands; nl
+  mapM_ putCommand dependCommands; nl
+  mapM_ putCommand useCommands; nl
+
+putCommand :: Command -> IO ()
+putCommand (Short cmd desc _) = f `withDesc` desc
+  where f = green >> putStr cmd >> off >> tab
+
+putCommand (Long cmd0 cmd1 desc _) = f `withDesc` desc
+  where f = green >> putStr (cmd0 ++ "  " ++ cmd1) >> off
+
+withDesc :: IO () -> String -> IO ()
+f `withDesc` desc = putStr "    " >> f >> tab >> putStrLn desc
+
+tab :: IO ()
+tab = putChar '\t'
+nl :: IO ()
+nl  = putChar '\n'
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+main = defaultMainWithHooks autoconfUserHooks
diff --git a/TODO b/TODO
new file mode 100644
--- /dev/null
+++ b/TODO
@@ -0,0 +1,4 @@
+ * make fquery a drop-in replacement of equery:
+    o outpout compatible
+    o options full
+ * explore attoparsec in search of faster performance
diff --git a/configure b/configure
new file mode 100644
--- /dev/null
+++ b/configure
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# Fake configure script.
+
+portageq=/usr/bin/portageq
+
+PORTAGE_TREE=`${portageq} portdir 2>/dev/null`
+PORTAGE_DB=`${portageq} vdb_path 2>/dev/null`
+
+if [ -z $PORTAGE_TREE ]; then
+  PORTAGE_TREE="/usr/portage"
+fi
+
+if [ -z $PORTAGE_DB ]; then
+  PORTAGE_DB="/var/db/pkg"
+fi
+
+echo "portdir: ${PORTAGE_TREE}"
+echo "vdb_path: ${PORTAGE_DB}"
+
+sed -e s,'@@PortageTree@@',"\"${PORTAGE_TREE}\"",	\
+    -e s,'@@PortageDB@@',"\"${PORTAGE_DB}\"",		\
+    "Adelie/Config.hs.in" > "Adelie/Config.hs"
diff --git a/fquery.cabal b/fquery.cabal
new file mode 100644
--- /dev/null
+++ b/fquery.cabal
@@ -0,0 +1,53 @@
+Name:		fquery
+Version:	0.2.1
+
+Author:		David Wang <millimillenary@gmail.com>, Sergei Trofimovich <slyfox@inbox.ru>
+Maintainer:	Sergei Trofimovich <slyfox@inbox.ru>
+Copyright:	2006 David Wang, 2009 Sergei Trofimovich
+
+License-File:	LICENCE.txt
+License:	OtherLicense
+
+Synopsis:	Installed package query tool for Gentoo Linux
+Description:	Installed package query tool for Gentoo Linux
+
+		Home page http://home.exetel.com.au/tjaden/fquery/
+		Public repository is http://repo.or.cz/w/fquery.git
+
+Category:	Gentoo
+
+Build-Type:	Configure
+Build-Depends:	base, haskell98, parsec, unix, regex-compat, process, directory
+Extra-Source-Files:
+		configure
+		Adelie/Config.hs.in
+		Adelie/opts.h
+		TODO
+
+Executable:	fquery
+Main-is:	Main.hs
+Other-Modules:	Adelie.Colour
+		Adelie.CompareVersion
+		Adelie.Contents
+		Adelie.Depend
+		Adelie.ListEx
+		Adelie.Options
+		Adelie.Portage
+		Adelie.Pretty
+		Adelie.Provide
+		Adelie.QChangelog
+		Adelie.QCheck
+		Adelie.QDepend
+		Adelie.QHasUse
+		Adelie.QList
+		Adelie.QOwn
+		Adelie.QSize
+		Adelie.QUse
+		Adelie.QWant
+		Adelie.Use
+		Adelie.UseDesc
+
+GHC-Options:	-O2 -Wall
+-- GHC-Options:	-Werror
+
+c-sources:	Adelie/opts.c
