diff --git a/C2HS.hs b/C2HS.hs
new file mode 100644
--- /dev/null
+++ b/C2HS.hs
@@ -0,0 +1,220 @@
+--  C->Haskell Compiler: Marshalling library
+--
+--  Copyright (c) [1999...2005] Manuel M T Chakravarty
+--
+--  Redistribution and use in source and binary forms, with or without
+--  modification, are permitted provided that the following conditions are met:
+-- 
+--  1. Redistributions of source code must retain the above copyright notice,
+--     this list of conditions and the following disclaimer. 
+--  2. Redistributions in binary form must reproduce the above copyright
+--     notice, this list of conditions and the following disclaimer in the
+--     documentation and/or other materials provided with the distribution. 
+--  3. The name of the author may not be used to endorse or promote products
+--     derived from this software without specific prior written permission. 
+--
+--  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+--  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+--  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+--  NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+--  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+--  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+--  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+--  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+--  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+--  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+--
+--- Description ---------------------------------------------------------------
+--
+--  Language: Haskell 98
+--
+--  This module provides the marshaling routines for Haskell files produced by 
+--  C->Haskell for binding to C library interfaces.  It exports all of the
+--  low-level FFI (language-independent plus the C-specific parts) together
+--  with the C->HS-specific higher-level marshalling routines.
+--
+
+module C2HS (
+
+  -- * Re-export the language-independent component of the FFI 
+  module Foreign,
+
+  -- * Re-export the C language component of the FFI
+  module CForeign,
+
+  -- * Composite marshalling functions
+  withCStringLenIntConv, peekCStringLenIntConv, withIntConv, withFloatConv,
+  peekIntConv, peekFloatConv, withBool, peekBool, withEnum, peekEnum,
+
+  -- * Conditional results using 'Maybe'
+  nothingIf, nothingIfNull,
+
+  -- * Bit masks
+  combineBitMasks, containsBitMask, extractBitMasks,
+
+  -- * Conversion between C and Haskell types
+  cIntConv, cFloatConv, cToBool, cFromBool, cToEnum, cFromEnum
+) where 
+
+
+import Foreign
+       hiding       (Word)
+		    -- Should also hide the Foreign.Marshal.Pool exports in
+		    -- compilers that export them
+import CForeign
+
+import Monad        (when, liftM)
+
+
+-- Composite marshalling functions
+-- -------------------------------
+
+-- Strings with explicit length
+--
+withCStringLenIntConv s f    = withCStringLen s $ \(p, n) -> f (p, cIntConv n)
+peekCStringLenIntConv (s, n) = peekCStringLen (s, cIntConv n)
+
+-- Marshalling of numerals
+--
+
+withIntConv   :: (Storable b, Integral a, Integral b) 
+	      => a -> (Ptr b -> IO c) -> IO c
+withIntConv    = with . cIntConv
+
+withFloatConv :: (Storable b, RealFloat a, RealFloat b) 
+	      => a -> (Ptr b -> IO c) -> IO c
+withFloatConv  = with . cFloatConv
+
+peekIntConv   :: (Storable a, Integral a, Integral b) 
+	      => Ptr a -> IO b
+peekIntConv    = liftM cIntConv . peek
+
+peekFloatConv :: (Storable a, RealFloat a, RealFloat b) 
+	      => Ptr a -> IO b
+peekFloatConv  = liftM cFloatConv . peek
+
+-- Passing Booleans by reference
+--
+
+withBool :: (Integral a, Storable a) => Bool -> (Ptr a -> IO b) -> IO b
+withBool  = with . fromBool
+
+peekBool :: (Integral a, Storable a) => Ptr a -> IO Bool
+peekBool  = liftM toBool . peek
+
+
+-- Passing enums by reference
+--
+
+withEnum :: (Enum a, Integral b, Storable b) => a -> (Ptr b -> IO c) -> IO c
+withEnum  = with . cFromEnum
+
+peekEnum :: (Enum a, Integral b, Storable b) => Ptr b -> IO a
+peekEnum  = liftM cToEnum . peek
+
+
+-- Storing of 'Maybe' values
+-- -------------------------
+
+instance Storable a => Storable (Maybe a) where
+  sizeOf    _ = sizeOf    (undefined :: Ptr ())
+  alignment _ = alignment (undefined :: Ptr ())
+
+  peek p = do
+	     ptr <- peek (castPtr p)
+	     if ptr == nullPtr
+	       then return Nothing
+	       else liftM Just $ peek ptr
+
+  poke p v = do
+	       ptr <- case v of
+		        Nothing -> return nullPtr
+			Just v' -> new v'
+               poke (castPtr p) ptr
+
+
+-- Conditional results using 'Maybe'
+-- ---------------------------------
+
+-- Wrap the result into a 'Maybe' type.
+--
+-- * the predicate determines when the result is considered to be non-existing,
+--   ie, it is represented by `Nothing'
+--
+-- * the second argument allows to map a result wrapped into `Just' to some
+--   other domain
+--
+nothingIf       :: (a -> Bool) -> (a -> b) -> a -> Maybe b
+nothingIf p f x  = if p x then Nothing else Just $ f x
+
+-- |Instance for special casing null pointers.
+--
+nothingIfNull :: (Ptr a -> b) -> Ptr a -> Maybe b
+nothingIfNull  = nothingIf (== nullPtr)
+
+
+-- Support for bit masks
+-- ---------------------
+
+-- Given a list of enumeration values that represent bit masks, combine these
+-- masks using bitwise disjunction.
+--
+combineBitMasks :: (Enum a, Bits b) => [a] -> b
+combineBitMasks = foldl (.|.) 0 . map (fromIntegral . fromEnum)
+
+-- Tests whether the given bit mask is contained in the given bit pattern
+-- (i.e., all bits set in the mask are also set in the pattern).
+--
+containsBitMask :: (Bits a, Enum b) => a -> b -> Bool
+bits `containsBitMask` bm = let bm' = fromIntegral . fromEnum $ bm
+			    in
+			    bm' .&. bits == bm'
+
+-- |Given a bit pattern, yield all bit masks that it contains.
+--
+-- * This does *not* attempt to compute a minimal set of bit masks that when
+--   combined yield the bit pattern, instead all contained bit masks are
+--   produced.
+--
+extractBitMasks :: (Bits a, Enum b, Bounded b) => a -> [b]
+extractBitMasks bits = 
+  [bm | bm <- [minBound..maxBound], bits `containsBitMask` bm]
+
+
+-- Conversion routines
+-- -------------------
+
+-- |Integral conversion
+--
+cIntConv :: (Integral a, Integral b) => a -> b
+cIntConv  = fromIntegral
+
+-- |Floating conversion
+--
+cFloatConv :: (RealFloat a, RealFloat b) => a -> b
+cFloatConv  = realToFrac
+-- As this conversion by default goes via `Rational', it can be very slow...
+{-# RULES 
+  "cFloatConv/Float->Float"   forall (x::Float).  cFloatConv x = x;
+  "cFloatConv/Double->Double" forall (x::Double). cFloatConv x = x
+ #-}
+
+-- |Obtain C value from Haskell 'Bool'.
+--
+cFromBool :: Num a => Bool -> a
+cFromBool  = fromBool
+
+-- |Obtain Haskell 'Bool' from C value.
+--
+cToBool :: Num a => a -> Bool
+cToBool  = toBool
+
+-- |Convert a C enumeration to Haskell.
+--
+cToEnum :: (Integral i, Enum e) => i -> e
+cToEnum  = toEnum . cIntConv
+
+-- |Convert a Haskell enumeration to C.
+--
+cFromEnum :: (Enum e, Integral i) => e -> i
+cFromEnum  = cIntConv . fromEnum
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2008 Evan Martin <martine@danga.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+* Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+* Neither the name of the author nor the names of contributors may be
+  used to endorse or promote products derived from this software without
+  specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,4 @@
+#! /usr/bin/env runhaskell
+
+> import Distribution.Simple
+> main = defaultMain
diff --git a/Text/XML/Expat/IO.chs b/Text/XML/Expat/IO.chs
new file mode 100644
--- /dev/null
+++ b/Text/XML/Expat/IO.chs
@@ -0,0 +1,133 @@
+-- hexpat, a Haskell wrapper for expat
+-- Copyright (C) 2008 Evan Martin <martine@danga.com>
+
+-- |This module wraps the Expat API directly, with IO.
+
+module Text.XML.Expat.IO (
+  -- ** Parser Setup
+  Parser, newParser,
+
+  -- ** Parsing
+  parse,
+
+  -- ** Parser Callbacks
+  StartElementHandler, EndElementHandler, CharacterDataHandler,
+  setStartElementHandler, setEndElementHandler, setCharacterDataHandler
+) where
+
+import C2HS
+
+#include <expat.h>
+
+-- Expat functions start with "XML", but C2HS appears to ignore our "as"
+-- definitions if they only differ from the symbol in case.  So we write out
+-- XML_* in most cases anyway...  :(
+{#context lib = "expat" prefix = "XML"#}
+
+-- |Opaque parser type.
+type ParserPtr = Ptr ()
+newtype Parser = Parser (ForeignPtr ())
+
+withParser :: Parser -> (ParserPtr -> IO a) -> IO a
+withParser (Parser fp) = withForeignPtr fp
+
+withOptCString :: Maybe String -> (CString -> IO a) -> IO a
+withOptCString Nothing    f = f nullPtr
+withOptCString (Just str) f = withCString str f
+
+{#fun unsafe XML_ParserCreate as parserCreate
+    {withOptCString* `Maybe String'} -> `ParserPtr' id#}
+foreign import ccall "&XML_ParserFree" parserFree :: FunPtr (ParserPtr -> IO ())
+
+-- |Create a 'Parser'.  The optional parameter is the default character
+-- encoding, and can be one of
+--
+-- - \"US-ASCII\"
+--
+-- - \"UTF-8\"
+--
+-- - \"UTF-16\"
+--
+-- - \"ISO-8859-1\"
+newParser :: Maybe String -> IO Parser
+newParser enc = do
+  ptr <- parserCreate enc
+  fptr <- newForeignPtr parserFree ptr
+  return $ Parser fptr
+
+unStatus :: CInt -> Bool
+unStatus 0 = False
+unStatus 1 = True
+-- |@parse data False@ feeds mode data into a 'Parser'.  The end of the data
+-- is indicated by passing True for the final parameter.  @parse@ returns
+-- False on a parse error.
+{#fun XML_Parse as parse
+    {withParser* `Parser', `String' &, `Bool'} -> `Bool' unStatus#}
+
+-- |The type of the \"element started\" callback.  The first parameter is
+-- the element name; the second are the (attribute, value) pairs.
+type StartElementHandler  = String -> [(String,String)] -> IO ()
+-- |The type of the \"element ended\" callback.  The parameter is
+-- the element name.
+type EndElementHandler    = String -> IO ()
+-- |The type of the \"character data\" callback.  The parameter is
+-- the character data processed.  This callback may be called more than once
+-- while processing a single conceptual block of text.
+type CharacterDataHandler = String -> IO ()
+
+type CStartElementHandler = Ptr () -> CString -> Ptr CString -> IO ()
+foreign import ccall "wrapper"
+  mkCStartElementHandler :: CStartElementHandler
+                         -> IO (FunPtr CStartElementHandler)
+wrapStartElementHandler :: StartElementHandler
+                        -> IO (FunPtr CStartElementHandler)
+wrapStartElementHandler handler = mkCStartElementHandler h where
+  h ptr cname cattrs = do
+    name <- peekCString cname
+    cattrlist <- peekArray0 nullPtr cattrs
+    attrlist <- mapM peekCString cattrlist
+    handler name (pairwise attrlist)
+-- |Attach a StartElementHandler to a Parser.
+setStartElementHandler :: Parser -> StartElementHandler -> IO ()
+setStartElementHandler parser handler = do
+  withParser parser $ \p -> do
+  handler' <- wrapStartElementHandler handler
+  {#call unsafe XML_SetStartElementHandler as ^#} p handler'
+
+
+type CEndElementHandler = Ptr () -> CString -> IO ()
+foreign import ccall "wrapper"
+  mkCEndElementHandler :: CEndElementHandler
+                       -> IO (FunPtr CEndElementHandler)
+wrapEndElementHandler :: EndElementHandler
+                      -> IO (FunPtr CEndElementHandler)
+wrapEndElementHandler handler = mkCEndElementHandler h where
+  h ptr cname = do
+    name <- peekCString cname
+    handler name
+-- |Attach an EndElementHandler to a Parser.
+setEndElementHandler :: Parser -> EndElementHandler -> IO ()
+setEndElementHandler parser handler = do
+  withParser parser $ \p -> do
+  handler' <- wrapEndElementHandler handler
+  {#call unsafe XML_SetEndElementHandler as ^#} p handler'
+
+type CCharacterDataHandler = Ptr () -> CString -> CInt -> IO ()
+foreign import ccall "wrapper"
+  mkCCharacterDataHandler :: CCharacterDataHandler
+                          -> IO (FunPtr CCharacterDataHandler)
+wrapCharacterDataHandler :: CharacterDataHandler
+                      -> IO (FunPtr CCharacterDataHandler)
+wrapCharacterDataHandler handler = mkCCharacterDataHandler h where
+  h ptr cdata len = do
+    data_ <- peekCStringLen (cdata, fromIntegral len)
+    handler data_
+-- |Attach an CharacterDataHandler to a Parser.
+setCharacterDataHandler :: Parser -> CharacterDataHandler -> IO ()
+setCharacterDataHandler parser handler = do
+  withParser parser $ \p -> do
+  handler' <- wrapCharacterDataHandler handler
+  {#call unsafe XML_SetCharacterDataHandler as ^#} p handler'
+
+pairwise (x1:x2:xs) = (x1,x2) : pairwise xs
+pairwise []         = []
diff --git a/Text/XML/Expat/IO.hs b/Text/XML/Expat/IO.hs
new file mode 100644
--- /dev/null
+++ b/Text/XML/Expat/IO.hs
@@ -0,0 +1,164 @@
+-- GENERATED by C->Haskell Compiler, version 0.15.1 Rainy Days, 31 Aug 2007 (Haskell)
+-- Edit the ORIGNAL .chs file instead!
+
+
+{-# LINE 1 "IO.chs" #-}-- hexpat, a Haskell wrapper for expat
+-- Copyright (C) 2008 Evan Martin <martine@danga.com>
+
+-- |This module wraps the Expat API directly, with IO.
+
+module Text.XML.Expat.IO (
+  -- ** Parser Setup
+  Parser, newParser,
+
+  -- ** Parsing
+  parse,
+
+  -- ** Parser Callbacks
+  StartElementHandler, EndElementHandler, CharacterDataHandler,
+  setStartElementHandler, setEndElementHandler, setCharacterDataHandler
+) where
+
+import C2HS
+
+
+-- Expat functions start with "XML", but C2HS appears to ignore our "as"
+-- definitions if they only differ from the symbol in case.  So we write out
+-- XML_* in most cases anyway...  :(
+
+{-# LINE 25 "IO.chs" #-}
+
+-- |Opaque parser type.
+type ParserPtr = Ptr ()
+newtype Parser = Parser (ForeignPtr ())
+
+withParser :: Parser -> (ParserPtr -> IO a) -> IO a
+withParser (Parser fp) = withForeignPtr fp
+
+withOptCString :: Maybe String -> (CString -> IO a) -> IO a
+withOptCString Nothing    f = f nullPtr
+withOptCString (Just str) f = withCString str f
+
+parserCreate :: Maybe String -> IO (ParserPtr)
+parserCreate a1 =
+  withOptCString a1 $ \a1' -> 
+  parserCreate'_ a1' >>= \res ->
+  let {res' = id res} in
+  return (res')
+{-# LINE 39 "IO.chs" #-}
+foreign import ccall "&XML_ParserFree" parserFree :: FunPtr (ParserPtr -> IO ())
+
+-- |Create a 'Parser'.  The optional parameter is the default character
+-- encoding, and can be one of
+--
+-- - \"US-ASCII\"
+--
+-- - \"UTF-8\"
+--
+-- - \"UTF-16\"
+--
+-- - \"ISO-8859-1\"
+newParser :: Maybe String -> IO Parser
+newParser enc = do
+  ptr <- parserCreate enc
+  fptr <- newForeignPtr parserFree ptr
+  return $ Parser fptr
+
+unStatus :: CInt -> Bool
+unStatus 0 = False
+unStatus 1 = True
+-- |@parse data False@ feeds mode data into a 'Parser'.  The end of the data
+-- is indicated by passing True for the final parameter.  @parse@ returns
+-- False on a parse error.
+parse :: Parser -> String -> Bool -> IO (Bool)
+parse a1 a2 a3 =
+  withParser a1 $ \a1' -> 
+  withCStringLenIntConv a2 $ \(a2'1, a2'2) -> 
+  let {a3' = cFromBool a3} in 
+  parse'_ a1' a2'1  a2'2 a3' >>= \res ->
+  let {res' = unStatus res} in
+  return (res')
+{-# LINE 65 "IO.chs" #-}
+
+-- |The type of the \"element started\" callback.  The first parameter is
+-- the element name; the second are the (attribute, value) pairs.
+type StartElementHandler  = String -> [(String,String)] -> IO ()
+-- |The type of the \"element ended\" callback.  The parameter is
+-- the element name.
+type EndElementHandler    = String -> IO ()
+-- |The type of the \"character data\" callback.  The parameter is
+-- the character data processed.  This callback may be called more than once
+-- while processing a single conceptual block of text.
+type CharacterDataHandler = String -> IO ()
+
+type CStartElementHandler = Ptr () -> CString -> Ptr CString -> IO ()
+foreign import ccall "wrapper"
+  mkCStartElementHandler :: CStartElementHandler
+                         -> IO (FunPtr CStartElementHandler)
+wrapStartElementHandler :: StartElementHandler
+                        -> IO (FunPtr CStartElementHandler)
+wrapStartElementHandler handler = mkCStartElementHandler h where
+  h ptr cname cattrs = do
+    name <- peekCString cname
+    cattrlist <- peekArray0 nullPtr cattrs
+    attrlist <- mapM peekCString cattrlist
+    handler name (pairwise attrlist)
+-- |Attach a StartElementHandler to a Parser.
+setStartElementHandler :: Parser -> StartElementHandler -> IO ()
+setStartElementHandler parser handler = do
+  withParser parser $ \p -> do
+  handler' <- wrapStartElementHandler handler
+  xmlSetstartelementhandler p handler'
+
+
+type CEndElementHandler = Ptr () -> CString -> IO ()
+foreign import ccall "wrapper"
+  mkCEndElementHandler :: CEndElementHandler
+                       -> IO (FunPtr CEndElementHandler)
+wrapEndElementHandler :: EndElementHandler
+                      -> IO (FunPtr CEndElementHandler)
+wrapEndElementHandler handler = mkCEndElementHandler h where
+  h ptr cname = do
+    name <- peekCString cname
+    handler name
+-- |Attach an EndElementHandler to a Parser.
+setEndElementHandler :: Parser -> EndElementHandler -> IO ()
+setEndElementHandler parser handler = do
+  withParser parser $ \p -> do
+  handler' <- wrapEndElementHandler handler
+  xmlSetendelementhandler p handler'
+
+type CCharacterDataHandler = Ptr () -> CString -> CInt -> IO ()
+foreign import ccall "wrapper"
+  mkCCharacterDataHandler :: CCharacterDataHandler
+                          -> IO (FunPtr CCharacterDataHandler)
+wrapCharacterDataHandler :: CharacterDataHandler
+                      -> IO (FunPtr CCharacterDataHandler)
+wrapCharacterDataHandler handler = mkCCharacterDataHandler h where
+  h ptr cdata len = do
+    data_ <- peekCStringLen (cdata, fromIntegral len)
+    handler data_
+-- |Attach an CharacterDataHandler to a Parser.
+setCharacterDataHandler :: Parser -> CharacterDataHandler -> IO ()
+setCharacterDataHandler parser handler = do
+  withParser parser $ \p -> do
+  handler' <- wrapCharacterDataHandler handler
+  xmlSetcharacterdatahandler p handler'
+
+pairwise (x1:x2:xs) = (x1,x2) : pairwise xs
+pairwise []         = []
+
+foreign import ccall unsafe "IO.chs.h XML_ParserCreate"
+  parserCreate'_ :: ((Ptr CChar) -> (IO (Ptr ())))
+
+foreign import ccall safe "IO.chs.h XML_Parse"
+  parse'_ :: ((Ptr ()) -> ((Ptr CChar) -> (CInt -> (CInt -> (IO CInt)))))
+
+foreign import ccall unsafe "IO.chs.h XML_SetStartElementHandler"
+  xmlSetstartelementhandler :: ((Ptr ()) -> ((FunPtr ((Ptr ()) -> ((Ptr CChar) -> ((Ptr (Ptr CChar)) -> (IO ()))))) -> (IO ())))
+
+foreign import ccall unsafe "IO.chs.h XML_SetEndElementHandler"
+  xmlSetendelementhandler :: ((Ptr ()) -> ((FunPtr ((Ptr ()) -> ((Ptr CChar) -> (IO ())))) -> (IO ())))
+
+foreign import ccall unsafe "IO.chs.h XML_SetCharacterDataHandler"
+  xmlSetcharacterdatahandler :: ((Ptr ()) -> ((FunPtr ((Ptr ()) -> ((Ptr CChar) -> (CInt -> (IO ()))))) -> (IO ())))
diff --git a/Text/XML/Expat/Tree.hs b/Text/XML/Expat/Tree.hs
new file mode 100644
--- /dev/null
+++ b/Text/XML/Expat/Tree.hs
@@ -0,0 +1,50 @@
+-- hexpat, a Haskell wrapper for expat
+-- Copyright (C) 2008 Evan Martin <martine@danga.com>
+
+-- |The Expat.Tree module provides a simplified interface to parsing, that
+-- returns a tree of the XML structure.  (Note that this is not a lazy parse
+-- of the document: as soon as the root node is accessed, the entire document
+-- is parsed.)
+
+module Text.XML.Expat.Tree (
+  Text.XML.Expat.Tree.parse, Node(..)
+) where
+
+import Text.XML.Expat.IO as EIO
+import Data.IORef
+import System.IO.Unsafe (unsafePerformIO)
+
+-- |Simplistic XML tree representation.
+data Node = Element { eName :: String, eAttrs :: [(String,String)],
+                      eChildren :: [Node] }
+          | Text String
+          deriving Show
+
+modifyChildren :: ([Node] -> [Node]) -> Node -> Node
+modifyChildren f node = node { eChildren = f (eChildren node) }
+
+-- |@parse enc doc@ parses XML content @doc@ with optional encoding @enc@,
+-- and returns the root 'Node' of the document if there were no parsing errors.
+parse :: Maybe String -> String -> Maybe Node
+parse enc doc = unsafePerformIO $ runParse where
+  runParse = do
+    parser <- EIO.newParser enc
+    -- We maintain the invariant that the stack always has one element,
+    -- whose only child at the end of parsing is the root of the actual tree.
+    stack <- newIORef [Element "" [] []]
+    EIO.setStartElementHandler  parser (\n a -> modifyIORef stack (start n a))
+    EIO.setEndElementHandler    parser (\n -> modifyIORef stack (end n))
+    EIO.setCharacterDataHandler parser (\s -> modifyIORef stack (text s))
+    ok <- EIO.parse parser doc True
+    if ok
+      then do
+        [Element _ _ [root]] <- readIORef stack
+        return $ Just $ modifyChildren reverse root
+      else return Nothing
+  start name attrs stack = Element name attrs [] : stack
+  text str (cur:rest) = modifyChildren (Text str:) cur : rest
+  end name (cur:parent:rest) =
+    if eName cur /= name then error "name mismatch" else
+    let node = modifyChildren reverse cur in
+    modifyChildren (node:) parent : rest
+
diff --git a/hexpat.cabal b/hexpat.cabal
new file mode 100644
--- /dev/null
+++ b/hexpat.cabal
@@ -0,0 +1,20 @@
+Cabal-Version: >= 1.2
+Name: hexpat
+Version: 0.1
+Synopsis: wrapper for expat, the fast XML parser
+Category: XML
+License: BSD3
+License-File: LICENSE
+Author: Evan Martin
+Maintainer: martine@danga.com
+Copyright: (c) 2008 Evan Martin <martine@danga.com>
+Homepage: http://neugierig.org/software/darcs/browse/?r=hexpat;a=summary
+Extra-Source-Files: C2HS.hs test.hs
+Build-Type: Simple
+
+Library
+  Build-Depends: base, haskell98
+  Exposed-Modules: Text.XML.Expat.IO, Text.XML.Expat.Tree
+  Extensions: ForeignFunctionInterface
+  Extra-Libraries: expat
+
diff --git a/test.hs b/test.hs
new file mode 100644
--- /dev/null
+++ b/test.hs
@@ -0,0 +1,26 @@
+import qualified Text.XML.Expat.IO as EIO
+import qualified Text.XML.Expat.Tree as ETree
+import Data.Tree
+
+main_eio doc = do
+  parser <- EIO.newParser Nothing
+  EIO.setStartElementHandler parser startElement
+  EIO.parse parser doc True
+  putStrLn "ok"
+  where
+  startElement name attrs = putStrLn $ show name ++ " " ++ show attrs
+
+main_tree doc = do
+  let etree = ETree.parse Nothing doc
+  --let dtree = toDTree etree
+  --putStrLn (drawTree dtree)
+  etree `seq` putStrLn "ok"
+  where
+  toDTree (ETree.Element name attrs kids) =
+    Node ("<" ++ name ++ " " ++ show attrs ++ ">") (map toDTree kids)
+  toDTree (ETree.Text str) = Node (show str) []
+
+main = do
+  let doc = "<foo baz='bah'><bar/><text>some &amp; text</text></foo>"
+  xml <- readFile "test.xml"
+  main_eio xml
