diff --git a/Configger.cabal b/Configger.cabal
new file mode 100644
--- /dev/null
+++ b/Configger.cabal
@@ -0,0 +1,26 @@
+Name:          Configger
+Version:       0.1
+License:       MIT
+License-file:  LICENSE
+Category:      Configuration
+Synopsis:      Parse config files
+Description:   Parses a config file into [(String, [(String, String)])]
+Author:        Nate Soares
+Maintainer:    nate@natesoares.com
+Build-Type:    Simple
+Cabal-Version: >=1.6
+
+source-repository head
+    type: git
+    location: git://github.com/Soares/Configger.hs.git
+
+Library
+    Hs-Source-Dirs:   src
+    Build-Depends:    base >= 4 && < 5,
+                      Dangerous >= 0.2.2,
+                      MissingH >= 1.0,
+                      mtl >= 1.1,
+                      parsec >= 3.1
+    Exposed-modules:  Data.Configger
+    Ghc-Options:      -Wall
+    Ghc-Prof-Options: -auto-all
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright © 2011 Nathaniel Soares
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/src/Data/Configger.hs b/src/Data/Configger.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Configger.hs
@@ -0,0 +1,92 @@
+module Data.Configger
+    ( Config
+    , load
+    , from
+    , merge
+    , mergeSection
+    , items
+    , set
+    , get
+    , values ) where
+import Control.Dangerous
+import Control.Monad.Trans
+import Data.Maybe
+import Data.String.Utils
+import Text.ParserCombinators.Parsec
+
+type Config = [(String, [(String, String)])]
+
+-- Merge two configurations
+merge :: Config -> Config -> Config
+merge a b = foldr mergeSection b a
+
+-- Merge a section into a configuration
+mergeSection :: (String, [(String, String)]) -> Config -> Config
+mergeSection (a, as) ((b, bs):rest) | a == b = (a, as ++ bs) : rest
+mergeSection a (b:bs) = b : mergeSection a bs
+mergeSection a [] = [a]
+
+-- Add a setting
+set :: String -> String -> String -> Config -> Config
+set s k v conf = mergeSection (s, [(k, v)]) conf
+
+-- Get the first value of a setting
+get :: String -> String -> Config -> Maybe String
+get s k conf = lookup s conf >>= lookup k
+
+-- Get all values of a setting
+values :: String -> String -> Config -> [String]
+values s k conf = let
+    vars = fromMaybe [] (lookup s conf)
+    ours = filter ((k ==) . fst) vars
+    in map snd ours
+
+-- Get the items in a section of a configuration
+items :: String -> Config -> [(String, String)]
+items name conf = fromMaybe [] (lookup name conf)
+
+-- Load a configuration from a file
+load :: (Errorable m, MonadIO m) => FilePath -> String -> m Config
+load defaults filename = do
+    text <- liftIO $ readFile filename
+    dangerize $ parse (file defaults) filename text
+
+-- Load a configuration from a string
+-- (Won't be able to throw as nice error messages)
+from :: (Errorable m) => String -> String -> m Config
+from str defaults = dangerize $ parse (file defaults) "(unknown)" str
+
+
+-- | Parsers
+
+file :: String -> GenParser Char st Config
+file defaults = do
+    free <- sepEndBy setting (many1 eol)
+    named <- sepEndBy section (many1 eol)
+    return $ mergeSection (defaults, free) named
+
+section :: GenParser Char st (String, [(String, String)])
+section = do
+    w >> char '[' >> w
+    name <- word
+    w >> char ']' >> many1 eol >> return ()
+    vars <- sepEndBy setting eol
+    return (name, vars)
+
+setting :: GenParser Char st (String, String)
+setting = do
+    var <- w >> word
+    w >> oneOf ":=" >> w
+    val <- w >> word
+    return (var, val)
+
+word :: GenParser Char st String
+word = do
+    str <- many1 (noneOf ":=[]\n")
+    return $ strip str
+
+w :: GenParser Char st ()
+w = many (oneOf " \t") >> return ()
+
+eol :: GenParser Char st ()
+eol = w >> char '\n' >> return ()
