diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644
--- /dev/null
+++ b/CONTRIBUTORS
@@ -0,0 +1,8 @@
+Thanks to the following individuals for contributing code to this project.
+
+Oleg Kiselyov
+John Lato
+Paulo Tanimoto
+
+In addition to the above, many thanks for comments and advice provided by:
+Johan Tibell
diff --git a/Examples/headers.hs b/Examples/headers.hs
new file mode 100644
--- /dev/null
+++ b/Examples/headers.hs
@@ -0,0 +1,179 @@
+import Data.Iteratee
+import qualified Data.Iteratee as Iter
+import Data.Iteratee.Char
+import qualified Data.Iteratee.IO as IIO
+import Control.Monad.Trans
+import Control.Monad.Identity
+import Data.Char
+import Data.Word
+
+
+-- HTTP chunk decoding
+-- Each chunk has the following format:
+--
+-- 	  <chunk-size> CRLF <chunk-data> CRLF
+--
+-- where <chunk-size> is the hexadecimal number; <chunk-data> is a
+-- sequence of <chunk-size> bytes.
+-- The last chunk (so-called EOF chunk) has the format
+-- 0 CRLF CRLF (where 0 is an ASCII zero, a character with the decimal code 48).
+-- For more detail, see "Chunked Transfer Coding", Sec 3.6.1 of
+-- the HTTP/1.1 standard:
+-- http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
+
+-- The following enum_chunk_decoded has the signature of the enumerator
+-- of the nested (encapsulated and chunk-encoded) stream. It receives
+-- an iteratee for the embedded stream and returns the iteratee for
+-- the base, embedding stream. Thus what is an enumerator and what
+-- is an iteratee may be a matter of perspective.
+
+-- We have a decision to make: Suppose an iteratee has finished (either because
+-- it obtained all needed data or encountered an error that makes further
+-- processing meaningless). While skipping the rest of the stream/the trailer,
+-- we encountered a framing error (e.g., missing CRLF after chunk data).
+-- What do we do? We chose to disregard the latter problem.
+-- Rationale: when the iteratee has finished, we are in the process
+-- of skipping up to the EOF (draining the source).
+-- Disregarding the errors seems OK then.
+-- Also, the iteratee may have found an error and decided to abort further
+-- processing. Flushing the remainder of the input is reasonable then.
+-- One can make a different choice...
+
+enum_chunk_decoded :: Monad m => Iteratee m a -> IterateeM m a
+enum_chunk_decoded = docase
+ where
+ docase iter@Done{} =
+    liftI iter >>= (\r -> (enum_chunk_decoded ==<< skipToEof) >> return r)
+ docase iter@(Cont k) = line >>= check_size
+  where
+  check_size (Right "0") = line >> k EOF
+  check_size (Right str) =
+     maybe (k . Error $ "Bad chunk size: " ++ str) (read_chunk iter)
+         $ read_hex 0 str
+  check_size _ = k (Error "Error reading chunk size")
+
+ read_chunk iter size =
+     do
+     r  <- Iter.take size iter
+     c1 <- Iter.head
+     c2 <- Iter.head
+     case (c1,c2) of
+       (Just '\r',Just '\n') -> docase r
+       _ -> (enum_chunk_decoded ==<< skipToEof) >>
+	    enumErr "Bad chunk trailer" r
+
+ read_hex acc "" = Just acc
+ read_hex acc (d:rest) | isHexDigit d = read_hex (16*acc + digitToInt d) rest
+ read_hex _acc _ = Nothing
+
+
+-- ------------------------------------------------------------------------
+-- Tests
+
+-- Pure tests, requiring no IO
+
+test_str1 :: String
+test_str1 =
+    "header1: v1\rheader2: v2\r\nheader3: v3\nheader4: v4\n" ++
+    "header5: v5\r\nheader6: v6\r\nheader7: v7\r\n\nrest\n"
+
+testp1 :: Bool
+testp1 =
+    let Done (Done lines' EOF) (Chunk rest)
+	    = runIdentity . unIM $ enumPure1Chunk test_str1 ==<<
+	                             (enum_lines ==<< stream2list)
+    in
+    lines' == ["header1: v1","header2: v2","header3: v3","header4: v4",
+	      "header5: v5","header6: v6","header7: v7"]
+    && rest == "rest\n"
+
+testp2 :: Bool
+testp2 =
+    let Done (Done lines' EOF) (Chunk rest)
+	    = runIdentity . unIM $ enumPureNChunk test_str1 5 ==<<
+	                             (enum_lines ==<< stream2list)
+    in
+    lines' == ["header1: v1","header2: v2","header3: v3","header4: v4",
+	      "header5: v5","header6: v6","header7: v7"]
+    && rest == "r"
+
+
+testw1 :: Bool
+testw1 =
+    let test_str = "header1: v1\rheader2: v2\r\nheader3:\t v3"
+	expected = ["header1:","v1","header2:","v2","header3:","v3"] in
+    let run_test test_str' =
+         let Done (Done words' EOF) EOF
+	       = runIdentity . unIM $ (enumPureNChunk test_str' 5 >. enumEof)
+	                                ==<< (enum_words ==<< stream2list)
+         in words'
+    in
+    and [run_test test_str == expected,
+	 run_test (test_str ++ " ") == expected]
+
+-- Run the complete test, reading the headers and the body
+
+-- This simple iteratee is used to process a variety of streams:
+-- embedded, interleaved, etc.
+line_printer :: IterateeGM [] Char IO (IterateeG [] Line IO ())
+line_printer = enum_lines ==<< print_lines
+
+-- Two sample processors
+
+-- Read the headers, print the headers, read the lines of the chunk-encoded
+-- body and print each line as it has been read
+read_headers_print_body :: IterateeGM [] Char IO (IterateeG [] Line IO ())
+read_headers_print_body = do
+     headers' <- enum_lines ==<< stream2list
+     case headers' of
+	Done headers EOF -> lift $ do
+	   putStrLn "Complete headers"
+	   print headers
+	Done headers (Error err) -> lift $ do
+	   putStrLn $ "Incomplete headers due to " ++ err
+	   print headers
+        _ -> lift $ putStrLn "Pattern not matched"
+
+     lift $ putStrLn "\nLines of the body follow"
+     enum_chunk_decoded ==<< line_printer
+
+-- Read the headers and print the header right after it has been read
+-- Read the lines of the chunk-encoded body and print each line as
+-- it has been read
+print_headers_print_body :: IterateeGM [] Char IO (IterateeG [] Line IO ())
+print_headers_print_body = do
+     lift $ putStrLn "\nLines of the headers follow"
+     line_printer
+     lift $ putStrLn "\nLines of the body follow"
+     enum_chunk_decoded ==<< line_printer
+
+-- This iteratee uses a mapStream to convert the stream from
+-- Word8 elements to Char elements.
+-- This is necessary because we want only ASCII chars, not unicode chars.
+-- this version only works on Posix systems
+{-
+test_driver_full iter filepath = do
+  fd <- openFd filepath ReadOnly Nothing defaultFileFlags
+  putStrLn "About to read headers"
+  unIM $ (IIO.enumFd fd >. enumEof) ==<< (mapStream mapfn ==<< iter)
+  closeFd fd
+  putStrLn "Finished reading"
+ where
+  mapfn :: Word8 -> Char
+  mapfn = chr . fromIntegral
+-}
+
+test_driver_full iter filepath = do
+  putStrLn "About to read headers"
+  fileDriverRandom (mapStream mapfn ==<< iter) filepath
+  putStrLn "Finished reading"
+ where
+  mapfn :: Word8 -> Char
+  mapfn = chr . fromIntegral
+
+test31 = test_driver_full read_headers_print_body "test_full1.txt"
+test32 = test_driver_full read_headers_print_body "test_full2.txt"
+test33 = test_driver_full read_headers_print_body "test_full3.txt"
+
+test34 = test_driver_full print_headers_print_body "test_full3.txt"
+
diff --git a/Examples/test_full1.txt b/Examples/test_full1.txt
new file mode 100644
--- /dev/null
+++ b/Examples/test_full1.txt
@@ -0,0 +1,15 @@
+header1: v1
+header2: v2header3: v3
+header4: v4
+
+1C
+body line 1
+body line    2
+
+7
+body li
+37
+ne       3body line          4
+body line             5
+0
+
diff --git a/Examples/test_full2.txt b/Examples/test_full2.txt
new file mode 100644
--- /dev/null
+++ b/Examples/test_full2.txt
@@ -0,0 +1,16 @@
+header1: v1
+header2: v2
+header3: v3
+header4: v4
+
+1C
+body line 1
+body line    2
+
+2
+body li
+37
+ne       3body line          4
+body line             5
+0
+
diff --git a/Examples/test_full3.txt b/Examples/test_full3.txt
new file mode 100644
--- /dev/null
+++ b/Examples/test_full3.txt
@@ -0,0 +1,17 @@
+header1: v1
+header2: v2
+header3: v3
+header4: v4
+
+1C
+body line 1
+body line    2
+
+7
+body li
+38
+ne       3body line          4
+body line             5
+
+0
+
diff --git a/Examples/wave_reader.hs b/Examples/wave_reader.hs
new file mode 100644
--- /dev/null
+++ b/Examples/wave_reader.hs
@@ -0,0 +1,48 @@
+-- Read a wave file and return some information about it.
+
+{-# LANGUAGE BangPatterns #-}
+module Main where
+
+import Data.Iteratee
+import Data.Iteratee.Codecs.Wave
+import qualified Data.IntMap as IM
+import Data.List (foldl')
+import Data.Word (Word8)
+import Control.Monad.Trans
+import System
+
+main :: IO ()
+main = do
+  args <- getArgs
+  case args of
+    [] -> putStrLn "Usage: wave_reader FileName"
+    fname:xs -> do
+      putStrLn $ "Reading file: " ++ fname
+      fileDriverRandom (wave_reader >>= test) fname
+      return ()
+
+-- Use the collection of [WAVEDE] returned from wave_reader to
+-- do further processing.  The IntMap has an entry for each type of chunk
+-- in the wave file.  Read the first format chunk and disply the 
+-- format information, then use the dict_process_data function
+-- to enumerate over the max_iter iteratee to find the maximum value
+-- (peak amplitude) in the file.
+test :: Maybe (IM.IntMap [WAVEDE]) -> IterateeGM [] Word8 IO ()
+test Nothing = lift $ putStrLn "No dictionary"
+test (Just dict) = do
+  fmtm <- dict_read_first_format dict
+  lift . putStrLn $ show fmtm
+  maxm <- dict_process_data 0 dict max_iter
+  lift . putStrLn $ show maxm
+  return ()
+
+-- an iteratee that calculates the maximum value found so far.
+-- this could be written with snext as well, however it is more
+-- efficient to operate on an entire chunk at once.
+max_iter :: IterateeGM [] Double IO Double
+max_iter = m' 0
+  where
+  m' acc = liftI $ Cont (step acc)
+  step acc (Chunk []) = m' acc
+  step acc (Chunk xs) = m' $! foldl' (max . abs) acc xs
+  step acc str = liftI $ Done acc str
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,32 @@
+Copyright (c) Oleg Kiselyov, John Lato, Paulo Tanimoto
+
+Portions by Oleg Kiselyov are in Public Domain.
+
+All rights reserved.
+
+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. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE 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 AUTHORS 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/README b/README
new file mode 100644
--- /dev/null
+++ b/README
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/iteratee.cabal b/iteratee.cabal
new file mode 100644
--- /dev/null
+++ b/iteratee.cabal
@@ -0,0 +1,52 @@
+Name:		iteratee
+Version:        0.1.0
+Cabal-Version:  >= 1.2
+Description:	The IterateeGM monad provides strict, safe, and functional
+                I/O.  In addition to pure Iteratee processors, file IO and 
+                combinator functions are provided.
+License:	BSD3
+License-file:	LICENSE
+Author:		Oleg Kiselyov
+Maintainer:	John W. Lato, jwlato@gmail.com
+homepage:       http://inmachina.net/~jwlato/haskell/iteratee
+Stability:	experimental
+synopsis:       Iteratee-based I/O
+category:       System, Data
+build-type:     Simple
+cabal-version:  >= 1.2
+tested-with:    GHC == 6.10.1, GHC == 6.8.3
+extra-source-files: README LICENSE CONTRIBUTORS Examples/headers.hs Examples/test_full1.txt Examples/test_full2.txt Examples/test_full3.txt Examples/wave_reader.hs
+
+flag splitBase
+  description: Choose the new split-up base package.
+
+Library
+ Hs-Source-Dirs:        src
+ ghc-options:           -O2 -Wall
+ if flag(splitBase)
+   build-depends:       base >= 3, base < 5
+ else
+   build-depends:       base < 3
+ if impl(ghc >= 6.10)
+   ghc-options:         -fsimplifier-phases=4
+ if os(windows)
+   cpp-options:         -DUSE_WINDOWS
+   other-modules:       Data.Iteratee.IO.Windows
+ else
+   cpp-options:         -DUSE_POSIX
+   other-modules:       Data.Iteratee.IO.Posix
+   build-depends:       unix
+ build-depends:         haskell98, mtl, containers, bytestring, extensible-exceptions
+ exposed-modules:       Data.Iteratee
+                        Data.Iteratee.Base
+                        Data.Iteratee.Base.StreamChunk
+                        Data.Iteratee.Binary
+                        Data.Iteratee.Char
+                        Data.Iteratee.Codecs.Wave
+                        Data.Iteratee.Codecs.Tiff
+                        Data.Iteratee.IO
+                        Data.Iteratee.IO.Base
+                        Data.Iteratee.WrappedByteString
+ other-modules:         Data.Iteratee.IO.Fd
+                        Data.Iteratee.IO.Handle
+
diff --git a/src/Data/Iteratee.hs b/src/Data/Iteratee.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee.hs
@@ -0,0 +1,18 @@
+{- | Provide iteratee-based IO as described in Oleg Kiselyov's paper http://okmij.org/ftp/Haskell/Iteratee/.
+
+Oleg's original code uses lists to store buffers of data for reading in the iteratee.  This package allows the use of arbitrary types through use of the StreamChunk type class.  See Data.Iteratee.WrappedByteString for implementation details.
+
+-}
+
+module Data.Iteratee (
+  module Data.Iteratee.Base,
+  module Data.Iteratee.Binary,
+  fileDriverRandom
+)
+
+where
+
+import Data.Iteratee.Base
+import Data.Iteratee.Binary
+import Data.Iteratee.IO
+
diff --git a/src/Data/Iteratee/Base.hs b/src/Data/Iteratee/Base.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/Base.hs
@@ -0,0 +1,483 @@
+-- |Monadic and General Iteratees:
+-- incremental input parsers, processors and transformers
+
+module Data.Iteratee.Base (
+  -- * Types
+  StreamG (..),
+  IterateeG (..),
+  IterateeGM (..),
+  EnumeratorN,
+  EnumeratorGM,
+  EnumeratorGMM,
+  -- * Iteratees
+  -- ** Iteratee Combinators
+  liftI,
+  (>>==),
+  (==<<),
+  joinI,
+  stream2list,
+  -- ** Error handling
+  iterErr,
+  iterReportError,
+  -- ** Basic Iteratees
+  break,
+  dropWhile,
+  drop,
+  head,
+  peek,
+  skipToEof,
+  seek,
+  -- ** Advanced iteratee combinators
+  take,
+  takeR,
+  mapStream,
+  convStream,
+  -- * Enumerators
+  enumEof,
+  enumErr,
+  (>.),
+  enumPure1Chunk,
+  enumPureNChunk,
+  -- * Misc.
+  FileOffset,
+  bindm
+)
+where
+
+import Prelude hiding (head, drop, dropWhile, take, break)
+import qualified Prelude as P
+
+import qualified Data.Iteratee.Base.StreamChunk as SC
+import Data.Iteratee.IO.Base
+import Control.Monad.Trans
+import Control.Monad.Identity
+import System.IO
+
+-- |A useful combinator.
+bindm :: Monad m => m (Maybe a) -> (a -> m (Maybe b)) -> m (Maybe b)
+bindm m f = m >>= maybe (return Nothing) f
+
+-- |A stream is a (continuing) sequence of elements bundled in Chunks.
+-- The first two variants indicate termination of the stream.
+-- Chunk a gives the currently available part of the stream.
+-- The stream is not terminated yet.
+-- The case (null Chunk) signifies a stream with no currently available
+-- data but which is still continuing. A stream processor should,
+-- informally speaking, ``suspend itself'' and wait for more data
+-- to arrive.
+data (SC.StreamChunk c el) => StreamG c el = EOF | Error String | Chunk (c el)
+
+-- |Iteratee -- a generic stream processor, what is being folded over
+-- a stream
+-- When Iteratee is in the 'done' state, it contains the computed
+-- result and the remaining part of the stream.
+-- In the 'cont' state, the iteratee has not finished the computation
+-- and needs more input.
+-- We assume that all iteratees are `good' -- given bounded input,
+-- they do the bounded amount of computation and take the bounded amount
+-- of resources. The monad m describes the sort of computations done
+-- by the iteratee as it processes the stream. The monad m could be
+-- the identity monad (for pure computations) or the IO monad
+-- (to let the iteratee store the stream processing results as they
+-- are computed).
+-- We also assume that given a terminated stream, an iteratee
+-- moves to the done state, so the results computed so far could be returned.
+
+data IterateeG s el m a
+  = Done a (StreamG s el)
+  | Cont (StreamG s el -> IterateeGM s el m a)
+  | Seek FileOffset (StreamG s el -> IterateeGM s el m a)
+
+instance (Show a) => Show (IterateeG s el m a) where
+  show (Done a _)  = "Iteratee done: " ++ P.show a
+  show (Cont   _k) = "Iteratee: incomplete"
+  show (Seek f _k) = "Iteratee: seek to " ++ P.show f ++ "requested"
+
+newtype IterateeGM s el m a = IM {unIM :: m (IterateeG s el m a)}
+
+
+-- Useful combinators for implementing iteratees and enumerators
+
+-- |Lift an 'IterateeG' into an 'IterateeGM'.
+liftI :: Monad m => IterateeG s el m a -> IterateeGM s el m a
+liftI = IM . return
+
+{-# INLINE liftI #-}
+
+-- |Just like bind (at run-time, this is indeed exactly bind)
+infixl 1 >>==
+(>>==) :: Monad m =>
+          IterateeGM s el m a ->
+          (IterateeG s el m a -> IterateeGM s' el' m b) ->
+          IterateeGM s' el' m b
+m >>== f = IM (unIM m >>= unIM . f)
+
+{-# INLINE (>>==) #-}
+
+-- |Just like an application -- a call-by-value-like application
+infixr 1 ==<<
+(==<<) :: Monad m =>
+          (IterateeG s el m a -> IterateeGM s' el' m b)
+          -> IterateeGM s el m a
+          -> IterateeGM s' el' m b
+(==<<) = flip (>>==)
+
+
+-- |The following is a `variant' of join in the IterateeGM s el m monad
+-- When el' is the same as el, the type of joinI is indeed that of
+-- true monadic join.  However, joinI is subtly different: since
+-- generally el' is different from el, it makes no sense to
+-- continue using the internal, IterateeG el' m a: we no longer
+-- have elements of the type el' to feed to that iteratee.
+-- We thus send EOF to the internal Iteratee and propagate its result.
+-- This join function is useful when dealing with `derived iteratees'
+-- for embedded/nested streams.  In particular, joinI is useful to
+-- process the result of take, mapStream, or convStream below.
+joinI :: (SC.StreamChunk s el, SC.StreamChunk s' el', Monad m) =>
+         IterateeGM s el m (IterateeG s' el' m a) ->
+         IterateeGM s el m a
+joinI m = m >>= (\iter -> enumEof iter >>== check)
+  where
+  check (Done x (Error str)) = liftI $ Done x (Error str)
+  check (Done x _) = liftI $ Done x EOF
+  check (Cont _)   = error "joinI: can't happen: EOF didn't terminate"
+  check (Seek _ _) = error "joinI: can't happen: EOF didn't terminate"
+
+-- It turns out, IterateeGM form a monad. We can use the familiar do
+-- notation for composing Iteratees
+
+instance (SC.StreamChunk s el, Monad m) => Monad (IterateeGM s el m) where
+  return x = liftI  $ Done  x (Chunk SC.empty)
+  m >>= f = iter_bind m f
+
+iter_bind :: (SC.StreamChunk s el, Monad m ) =>
+             IterateeGM s el m a ->
+             (a -> IterateeGM s el m b) ->
+             IterateeGM s el m b
+iter_bind m f = m >>== docase
+  where
+  docase (Done a (Chunk vec))
+    | SC.null vec        = f a
+  docase (Done a stream) = f a >>== (\r -> case r of
+    Done x _             -> liftI $ Done x stream
+    Cont k               -> k stream
+    iter                 -> liftI iter)
+  docase (Cont k)        = liftI $ Cont ((>>= f) . k)
+  docase (Seek off k)    = liftI $ Seek off ((>>= f) . k)
+
+{-# SPECIALIZE iter_bind :: SC.StreamChunk s el => IterateeGM s el IO a -> (a -> IterateeGM s el IO b) -> IterateeGM s el IO b #-}
+
+instance (Monad m, Functor m) => Functor (IterateeGM s el m) where
+  fmap f m = m >>== docase
+    where
+    docase (Done a stream) = liftI $ Done (f a) stream
+    docase (Cont k)        = liftI $ Cont (fmap f . k)
+    docase (Seek off k)    = liftI $ Seek off (fmap f . k)
+
+instance (SC.StreamChunk s el) => MonadTrans (IterateeGM s el) where
+  lift m = IM (m >>= unIM . return)
+
+instance (SC.StreamChunk s el, MonadIO m) => MonadIO (IterateeGM s el m) where
+  liftIO = lift . liftIO
+
+-- ------------------------------------------------------------------------
+-- Primitive iteratees
+
+-- |Read a stream to the end and return all of its elements as a list
+stream2list :: (SC.StreamChunk s el, Monad m) => IterateeGM s el m [el]
+stream2list = liftI $ Cont (step SC.empty)
+ where
+ step acc (Chunk ls) | SC.null ls   = liftI $ Cont (step acc)
+                     | otherwise    = liftI $ Cont (step $ acc `SC.append` ls)
+ step acc stream                    = liftI $ Done (SC.toList acc) stream
+
+-- |Report and propagate an error.  Disregard the input first and then
+-- propagate the error.
+iterErr :: (SC.StreamChunk s el, Monad m) => String -> IterateeGM s el m ()
+iterErr err = liftI $ Cont step
+  where
+  step _ = liftI $ Done () (Error err)
+
+-- |Check to see if the stream is in error
+iterReportError :: (SC.StreamChunk s el, Monad m) =>
+                   IterateeGM s el m (Maybe String)
+iterReportError = liftI $ Cont step
+  where
+  step s@(Error str) = liftI $ Done (Just str) s
+  step s             = liftI $ Done Nothing s
+
+-- ------------------------------------------------------------------------
+-- Parser combinators
+
+-- |The analogue of List.break
+-- It takes an element predicate and returns a pair:
+--  (str, Just c) -- the element 'c' is the first element of the stream
+--                   satisfying the break predicate;
+--                   The chunk str is the prefix of the stream up
+--                   to but including 'c'
+--  (str,Nothing) -- The stream is terminated with EOF or error before
+--                   any element satisfying the break predicate was found.
+--                   str is the scanned part of the stream.
+-- None of the element in str satisfy the break predicate.
+
+break :: (SC.StreamChunk s el, Monad m) =>
+          (el -> Bool) ->
+          IterateeGM s el m (s el, Maybe el)
+break cpred = liftI $ Cont (liftI . step SC.empty)
+  where
+  step before (Chunk str)
+    | SC.null str = Cont (liftI . step before)
+    | otherwise = case SC.findIndex cpred str of
+        Nothing -> Cont (liftI . step (before `SC.append` str))
+        Just ix -> let (str', tail') = SC.splitAt ix str
+                   in
+                   done (before `SC.append` str') (Just $ SC.head tail') (Chunk $ SC.tail tail')
+  step before stream = done before Nothing stream
+  done line' char = Done (line', char)
+
+
+-- |A particular optimized case of 'drop': skip all elements of the stream
+-- satisfying the given predicate - until the first element
+-- that does not satisfy the predicate, or the end of the stream.
+-- This is the analogue of List.dropWhile
+dropWhile :: (SC.StreamChunk s el, Monad m) =>
+              (el -> Bool) ->
+              IterateeGM s el m ()
+dropWhile cpred = liftI $ Cont step
+  where
+  step (Chunk str)
+    | SC.null str = dropWhile cpred
+    | otherwise = let remm = SC.dropWhile cpred str
+                  in
+                  case SC.null remm of
+                    True  -> dropWhile cpred
+                    False -> liftI $ Done () (Chunk remm)
+  step stream   = liftI $ Done () stream
+
+
+-- |Attempt to read the next element of the stream
+-- Return (Just c) if successful, return Nothing if the stream is
+-- terminated (by EOF or an error)
+head :: (SC.StreamChunk s el, Monad m) =>
+         IterateeGM s el m (Maybe el)
+head = liftI $ Cont step
+  where
+  step (Chunk vec)
+    | SC.null vec  = head
+    | otherwise      = liftI $ Done (Just $ SC.head vec) (Chunk $ SC.tail vec)
+  step stream        = liftI $ Done Nothing stream
+
+
+-- |Look ahead at the next element of the stream, without removing
+-- it from the stream.
+-- Return (Just c) if successful, return Nothing if the stream is
+-- terminated (by EOF or an error)
+peek :: (SC.StreamChunk s el, Monad m) =>
+         IterateeGM s el m (Maybe el)
+peek = liftI $ Cont step
+  where
+  step s@(Chunk vec)
+    | SC.null vec = peek
+    | otherwise = liftI $ Done (Just $ SC.head vec) s
+  step stream   = liftI $ Done Nothing stream
+
+
+-- |Skip the rest of the stream
+skipToEof :: (SC.StreamChunk s el, Monad m) => IterateeGM s el m ()
+skipToEof = liftI $ Cont step
+  where
+  step (Chunk _) = skipToEof
+  step _         = return ()
+
+-- |Skip n elements of the stream, if there are that many
+-- This is the analogue of List.drop
+drop :: (SC.StreamChunk s el, Monad m) => Int -> IterateeGM s el m ()
+drop 0 = return ()
+drop n = liftI $ Cont step
+  where
+  step (Chunk str) | SC.length str <= n = drop (n - SC.length str)
+  step (Chunk str) = liftI $ Done () (Chunk s2)
+   where
+   (_s1,s2) = SC.splitAt n str
+  step stream      = liftI $ Done () stream
+
+-- |Create a request to seek within an input stream.  This will result in
+-- an error if the enumerator is not capable of responding to a seek request.
+seek :: (SC.StreamChunk s el, Monad m) => FileOffset -> IterateeGM s el m ()
+seek off = liftI (Seek off step)
+  where
+  step = liftI . Done ()
+
+-- ---------------------------------------------------
+-- The converters show a different way of composing two iteratees:
+-- `vertical' rather than `horizontal'
+
+-- |The type of the converter from the stream with elements el_outer
+-- to the stream with element el_inner.  The result is the iteratee
+-- for the outer stream that uses an `IterateeG el_inner m a'
+-- to process the embedded, inner stream as it reads the outer stream.
+type EnumeratorN s_outer el_outer s_inner el_inner m a =
+  IterateeG s_inner el_inner m a ->
+  IterateeGM s_outer el_outer m (IterateeG s_inner el_inner m a)
+
+-- |Read n elements from a stream and apply the given iteratee to the
+-- stream of the read elements. Unless the stream is terminated early, we
+-- read exactly n elements (even if the iteratee has accepted fewer).
+take :: (SC.StreamChunk s el, Monad m) =>
+   Int -> EnumeratorN s el s el m a
+take 0 iter = return iter
+take n iter@Done{} = drop n >> return iter
+take n (Seek _off k) = liftI $ Cont step
+  where
+  step chunk@(Chunk str)
+    | SC.null str        = liftI $ Cont step
+    | SC.length str <= n = take (n - SC.length str) ==<< k chunk
+  step (Chunk str)       = done (Chunk s1) (Chunk s2)
+    where (s1,s2) = SC.splitAt n str
+  step stream            = done stream stream
+  done s1 s2             = k s1 >>== \r -> liftI $ Done r s2
+take n (Cont k) = liftI $ Cont step
+  where
+  step chunk@(Chunk str)
+    | SC.null str        = liftI $ Cont step
+    | SC.length str <= n = take (n - SC.length str) ==<< k chunk
+  step (Chunk str)       = done (Chunk s1) (Chunk s2)
+    where (s1,s2) = SC.splitAt n str
+  step stream            = done stream stream
+  done s1 s2             = k s1 >>== \r -> liftI $ Done r s2
+
+
+-- |Read n elements from a stream and apply the given iteratee to the
+-- stream of the read elements. If the given iteratee accepted fewer
+-- elements, we stop.
+-- This is the variation of `take' with the early termination
+-- of processing of the outer stream once the processing of the inner stream
+-- finished early. This variation is particularly useful for randomIO,
+-- where we do not have to care to `drain the input stream'.
+takeR :: (SC.StreamChunk s el, Monad m) =>
+          Int ->
+          EnumeratorN s el s el m a
+takeR 0 iter = return iter
+takeR _n iter@Done{} = return iter
+takeR _n iter@Seek{} = return iter
+takeR n (Cont k)     = liftI $ Cont step
+  where
+  step chunk@(Chunk str)
+    | SC.null str        = liftI $ Cont step
+    | SC.length str <= n = takeR (n - SC.length str) ==<< k chunk
+  step (Chunk str)       = done (Chunk s1) (Chunk s2)
+    where (s1,s2) = SC.splitAt n str
+  step stream            = done stream stream
+  done s1 s2             = k s1 >>== \r -> liftI $ Done r s2
+
+-- |Map the stream: yet another iteratee transformer
+-- Given the stream of elements of the type el and the function el->el',
+-- build a nested stream of elements of the type el' and apply the
+-- given iteratee to it.
+-- Note the contravariance
+
+mapStream :: (SC.StreamChunk s el, SC.StreamChunk s el', Monad m) =>
+              (el -> el') ->
+              EnumeratorN s el s el' m a
+mapStream _f iter@Done{} = return iter
+mapStream f (Cont k) = liftI $ Cont step
+  where
+  step (Chunk str)
+    | SC.null str  = liftI $ Cont step
+  step (Chunk str) = k (Chunk (SC.cMap f str)) >>== mapStream f
+  step EOF         = k EOF                     >>== \r -> liftI $ Done r EOF
+  step (Error err) = k (Error err)             >>== \r ->
+                       liftI $ Done r (Error err)
+mapStream f (Seek off k) = liftI $ Seek off step
+  where
+  step (Chunk str)
+    | SC.null str  = liftI $ Cont step
+  step (Chunk str) = k (Chunk (SC.cMap f str)) >>== mapStream f
+  step EOF         = k EOF                     >>== \r -> liftI $ Done r EOF
+  step (Error err) = k (Error err)             >>== \r ->
+                       liftI $ Done r (Error err)
+
+-- |Convert one stream into another, not necessarily in `lockstep'
+-- The transformer mapStream maps one element of the outer stream
+-- to one element of the nested stream.  The transformer below is more
+-- general: it may take several elements of the outer stream to produce
+-- one element of the inner stream, or the other way around.
+-- The transformation from one stream to the other is specified as
+-- IterateeGM s el m (Maybe [el']).  The `Maybe' type reflects the
+-- possibility of the conversion error.
+convStream :: (SC.StreamChunk s el, SC.StreamChunk s' el', Monad m) =>
+  IterateeGM s el m (Maybe (s' el')) -> EnumeratorN s el s' el' m a
+convStream _fi iter@Done{} = return iter
+convStream fi (Cont k) = fi >>=
+  (convStream fi ==<<) . k . maybe (Error "conv: stream error") Chunk
+convStream fi (Seek _off k) = fi >>=
+  (convStream fi ==<<) . k . maybe (Error "conv: stream error") Chunk
+
+{-# SPECIALIZE convStream :: (SC.StreamChunk s el, SC.StreamChunk s' el') => IterateeGM s el IO (Maybe (s' el')) -> EnumeratorN s el s' el' IO a #-}
+
+
+-- ------------------------------------------------------------------------
+-- Enumerators
+-- |Each enumerator takes an iteratee and returns an iteratee
+-- an Enumerator is an iteratee transformer.
+-- The enumerator normally stops when the stream is terminated
+-- or when the iteratee moves to the done state, whichever comes first.
+-- When to stop is of course up to the enumerator...
+
+-- We have two choices of composition: compose iteratees or compose
+-- enumerators. The latter is useful when one iteratee
+-- reads from the concatenation of two data sources.
+
+type EnumeratorGM s el m a = IterateeG s el m a -> IterateeGM s el m a
+
+-- |More general enumerator type: enumerator that maps
+-- streams (not necessarily in lock-step).  This is
+-- a flattened (`joinI-ed') EnumeratorN sfrom elfrom sto elto m a
+type EnumeratorGMM sfrom elfrom sto elto m a =
+  IterateeG sto elto m a -> IterateeGM sfrom elfrom m a
+
+-- |The most primitive enumerator: applies the iteratee to the terminated
+-- stream. The result is the iteratee usually in the done state.
+enumEof :: (SC.StreamChunk s el, Monad m) => EnumeratorGM s el m a
+enumEof (Done x _)    = liftI $ Done x EOF
+enumEof (Cont k)      = k EOF
+enumEof (Seek _off k) = k EOF
+
+-- |Another primitive enumerator: report an error
+enumErr :: (SC.StreamChunk s el, Monad m) => String -> EnumeratorGM s el m a
+enumErr str (Done x _)    = liftI $ Done x (Error str)
+enumErr str (Cont k)      = k (Error str)
+enumErr str (Seek _off k) = k (Error str)
+
+-- |The composition of two enumerators: essentially the functional composition
+-- It is convenient to flip the order of the arguments of the composition
+-- though: in e1 >. e2, e1 is executed first
+
+(>.):: (SC.StreamChunk s el, Monad m) =>
+       EnumeratorGM s el m a -> EnumeratorGM s el m a -> EnumeratorGM s el m a
+e1 >. e2 = (e2 ==<<) . e1
+
+-- |The pure 1-chunk enumerator
+-- It passes a given list of elements to the iteratee in one chunk
+-- This enumerator does no IO and is useful for testing of base parsing
+enumPure1Chunk :: (SC.StreamChunk s el, Monad m) =>
+                    s el ->
+                    EnumeratorGM s el m a
+enumPure1Chunk _str iter@Done{} = liftI iter
+enumPure1Chunk str (Cont k) = k (Chunk str)
+enumPure1Chunk _str (Seek _off _k) = fail "enumPure1Chunk cannot handle random IO"
+
+-- |The pure n-chunk enumerator
+-- It passes a given lift of elements to the iteratee in n chunks
+-- This enumerator does no IO and is useful for testing of base parsing
+-- and handling of chunk boundaries
+enumPureNChunk :: (SC.StreamChunk s el, Monad m) =>
+                    s el ->
+                    Int ->
+                    EnumeratorGM s el m a
+enumPureNChunk _str _n iter@Done{}   = liftI iter
+enumPureNChunk str  _n iter | SC.null str = liftI iter
+enumPureNChunk str n (Cont k)        = enumPureNChunk s2 n ==<< k (Chunk s1)
+  where (s1,s2) = SC.splitAt n str
+enumPureNChunk _str _n (Seek _off _k) = fail "enumPureNChunk cannot handle ranom IO"
+
diff --git a/src/Data/Iteratee/Base/StreamChunk.hs b/src/Data/Iteratee/Base/StreamChunk.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/Base/StreamChunk.hs
@@ -0,0 +1,80 @@
+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
+
+-- |Monadic and General Iteratees:
+-- incremental input parsers, processors and transformers
+
+module Data.Iteratee.Base.StreamChunk (
+  -- * Types
+  StreamChunk (..),
+  ReadableChunk (..)
+)
+where
+
+import Prelude hiding (head, tail, dropWhile, length, splitAt )
+import qualified Prelude as P
+
+import qualified Data.List as L
+import Foreign.Ptr
+import Foreign.Storable
+import Foreign.Marshal.Array
+import System.IO
+
+-- |Class of types that can be used to hold chunks of data within Iteratee
+-- streams.
+class StreamChunk c el where
+  -- |Length of currently available data.
+  length :: c el -> Int
+  -- |Create an empty chunk of data.
+  empty :: c el
+  -- |Test if the current stream is null.
+  null :: c el -> Bool
+  -- |Prepend an element to the front of the data.
+  cons :: el -> c el -> c el
+  -- |Return the first element of the stream.
+  head :: c el -> el
+  -- |Return the tail of the stream.
+  tail :: c el -> c el
+  -- |First index matching the predicate.
+  findIndex :: (el -> Bool) -> c el -> Maybe Int
+  -- |Split the data at the specified index.
+  splitAt :: Int -> c el -> (c el, c el)
+  -- |Drop data matching the predicate.
+  dropWhile :: (el -> Bool) -> c el -> c el
+  -- |Append to chunks of data into one.
+  append :: c el -> c el -> c el
+  -- |Create a stream from a list.
+  fromList :: [el] -> c el
+  -- |Create a list from the stream.
+  toList :: c el -> [el]
+  -- |Map a computation over the stream.
+  cMap :: (StreamChunk c' el') => (el -> el') -> c el -> c' el'
+
+instance StreamChunk [] el where
+  length    = P.length
+  empty     = []
+  null []   = True
+  null _    = False
+  cons      = (:)
+  head      = P.head
+  tail      = P.tail
+  findIndex = L.findIndex
+  splitAt   = P.splitAt
+  dropWhile = P.dropWhile
+  append    = (++)
+  fromList   = id
+  toList     = id
+  cMap       = listmap
+
+listmap :: (StreamChunk s' el') => (el -> el') -> [el] -> s' el'
+listmap f = foldr (cons . f) empty
+
+{-# RULES "listmap/map" listmap = map #-}
+
+-- |Class of streams which can be filled from a 'Ptr'.  Typically these
+-- are streams which can be read from a file.
+class StreamChunk s el => ReadableChunk s el where
+  readFromPtr :: Ptr (el) -> Int -> IO (s el)
+
+instance (Storable el) => ReadableChunk [] el where
+  readFromPtr = flip peekArray
+
diff --git a/src/Data/Iteratee/Binary.hs b/src/Data/Iteratee/Binary.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/Binary.hs
@@ -0,0 +1,77 @@
+{-# LANGUAGE FlexibleContexts #-}
+
+-- |Iteratees for parsing binary data.
+module Data.Iteratee.Binary (
+  -- * Types
+  Endian (..),
+  -- * Endian multi-byte iteratees
+  endian_read2,
+  endian_read3,
+  endian_read4
+)
+where
+
+import Data.Iteratee.Base.StreamChunk (StreamChunk)
+import Data.Iteratee.Base (IterateeGM)
+import qualified Data.Iteratee.Base as It
+import Data.Word
+import Data.Bits
+import Data.Int
+
+
+-- ------------------------------------------------------------------------
+-- Binary Random IO Iteratees
+
+-- Iteratees to read unsigned integers written in Big- or Little-endian ways
+
+-- |Indicate endian-ness.
+data Endian = MSB -- ^ Most Significant Byte is first (big-endian)
+  | LSB           -- ^ Least Significan Byte is first (little-endian)
+  deriving (Eq, Ord, Show, Enum)
+
+endian_read2 :: (StreamChunk s Word8, Monad m) => Endian -> IterateeGM s Word8 m (Maybe Word16)
+endian_read2 e =
+  It.bindm It.head $ \c1 ->
+  It.bindm It.head $ \c2 ->
+  case e of
+    MSB -> return $ return $ (fromIntegral c1 `shiftL` 8) .|. fromIntegral c2
+    LSB -> return $ return $ (fromIntegral c2 `shiftL` 8) .|. fromIntegral c1
+
+-- |read 3 bytes in an endian manner.  If the first bit is set (negative),
+-- set the entire first byte so the Word32 can be properly set negative as
+-- well.
+endian_read3 :: (StreamChunk s Word8, Monad m) => Endian -> IterateeGM s Word8 m (Maybe Word32)
+endian_read3 e = 
+  It.bindm It.head $ \c1 ->
+  It.bindm It.head $ \c2 ->
+  It.bindm It.head $ \c3 ->
+  case e of
+    MSB -> return $ return $ (((fromIntegral c1
+                        `shiftL` 8) .|. fromIntegral c2)
+                        `shiftL` 8) .|. fromIntegral c3
+    LSB ->
+     let m :: Int32
+         m = shiftR (shiftL (fromIntegral c3) 24) 8 in
+     return $ return $ (((fromIntegral c3
+                        `shiftL` 8) .|. fromIntegral c2)
+                        `shiftL` 8) .|. fromIntegral m
+
+endian_read4 :: (StreamChunk s Word8, Monad m) => Endian -> IterateeGM s Word8 m (Maybe Word32)
+endian_read4 e =
+  It.bindm It.head $ \c1 ->
+  It.bindm It.head $ \c2 ->
+  It.bindm It.head $ \c3 ->
+  It.bindm It.head $ \c4 ->
+  case e of
+    MSB -> return $ return $ 
+	       (((((fromIntegral c1
+		`shiftL` 8) .|. fromIntegral c2)
+	        `shiftL` 8) .|. fromIntegral c3)
+	        `shiftL` 8) .|. fromIntegral c4
+    LSB -> return $ return $ 
+	       (((((fromIntegral c4
+		`shiftL` 8) .|. fromIntegral c3)
+	        `shiftL` 8) .|. fromIntegral c2)
+	        `shiftL` 8) .|. fromIntegral c1
+
+
diff --git a/src/Data/Iteratee/Char.hs b/src/Data/Iteratee/Char.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/Char.hs
@@ -0,0 +1,131 @@
+-- Haskell98!
+
+-- |Utilties for Char-based iteratee processing.
+
+-- The running example, parts 1 and 2
+-- Part 1 is reading the headers, the sequence of lines terminated by an
+-- empty line. Each line is terminated by CR, LF, or CRLF.
+-- We should return the headers in order. In the case of error,
+-- we should return the headers read so far and the description of the error.
+-- Part 2 is reading the headers and reading all the lines from the
+-- HTTP-chunk-encoded content that follows the headers. Part 2 thus
+-- verifies layering of streams, and processing of one stream
+-- embedded (chunk encoded) into another stream.
+
+module Data.Iteratee.Char (
+  -- * Type synonyms
+  Stream,
+  Iteratee,
+  IterateeM,
+  EnumeratorM,
+  Line,
+  -- * Word and Line processors
+  line,
+  print_lines,
+  enum_lines,
+  enum_words,
+
+  module Data.Iteratee.Base
+)
+
+where
+
+import Data.Iteratee.Base (StreamG (..), IterateeG, IterateeGM, EnumeratorGM, (==<<), liftI)
+import qualified Data.Iteratee.Base as Iter
+import Data.Char
+import Control.Monad.Trans
+
+-- |A particular instance of StreamG: the stream of characters.
+-- This stream is used by many input parsers.
+type Stream = StreamG [] Char
+
+type Iteratee  m a = IterateeG  [] Char m a
+type IterateeM m a = IterateeGM [] Char m a
+
+-- Useful combinators for implementing iteratees and enumerators
+
+type Line = String	-- The line of text, terminators are not included
+
+-- |Read the line of text from the stream
+-- The line can be terminated by CR, LF or CRLF.
+-- Return (Right Line) if successful. Return (Left Line) if EOF or
+-- a stream error were encountered before the terminator is seen.
+-- The returned line is the string read so far.
+
+-- The code is the same as that of pure Iteratee, only the signature
+-- has changed.
+-- Compare the code below with GHCBufferIO.line_lazy
+line :: Monad m => IterateeM m (Either Line Line)
+line = Iter.break (\c -> c == '\r' || c == '\n') >>= check_next
+ where
+ check_next (line',Just '\r') = Iter.peek >>= \c ->
+	case c of
+	  Just '\n' -> Iter.head >> return (Right line')
+	  Just _    -> return (Right line')
+	  Nothing   -> return (Left line')
+ check_next (line',Just _)  = return (Right line')
+ check_next (line',Nothing) = return (Left line')
+
+
+-- Line iteratees: processors of a stream whose elements are made of Lines
+
+-- Collect all read lines and return them as a list
+-- see stream2list
+
+-- |Print lines as they are received. This is the first `impure' iteratee
+-- with non-trivial actions during chunk processing
+print_lines :: IterateeGM [] Line IO ()
+print_lines = liftI $ Iter.Cont step
+ where
+ step (Chunk []) = print_lines
+ step (Chunk ls) = lift (mapM_ pr_line ls) >> print_lines
+ step EOF        = lift (putStrLn ">> natural end") >> liftI (Iter.Done () EOF)
+ step stream     = lift (putStrLn ">> unnatural end") >>
+		   liftI (Iter.Done () stream)
+ pr_line line'   = putStrLn $ ">> read line: " ++ line'
+
+
+-- |Convert the stream of characters to the stream of lines, and
+-- apply the given iteratee to enumerate the latter.
+-- The stream of lines is normally terminated by the empty line.
+-- When the stream of characters is terminated, the stream of lines
+-- is also terminated, abnormally.
+-- This is the first proper iteratee-enumerator: it is the iteratee of the
+-- character stream and the enumerator of the line stream.
+
+enum_lines :: Monad m =>
+	      IterateeG [] Line m a ->
+              IterateeGM [] Char m (IterateeG [] Line m a)
+enum_lines iter@Iter.Done{} = return iter
+enum_lines Iter.Seek{}      = error "Seeking is not supported by enum_lines"
+enum_lines (Iter.Cont k)    = line >>= check_line k
+ where
+ check_line k' (Right "") =
+   enum_lines ==<< k' EOF      -- empty line, normal term
+ check_line k' (Right l)  = enum_lines ==<< k' (Chunk [l])
+ check_line k' _          = enum_lines ==<< k' (Error "EOF") -- abnormal termin
+
+-- |Convert the stream of characters to the stream of words, and
+-- apply the given iteratee to enumerate the latter.
+-- Words are delimited by white space.
+-- This is the analogue of List.words
+-- One should keep in mind that enum_words is a more general, monadic
+-- function.
+
+enum_words :: Monad m =>
+	      IterateeG [] String m a ->
+              IterateeGM [] Char m (IterateeG [] String m a)
+enum_words iter@Iter.Done{} = return iter
+enum_words Iter.Seek{}      = error "Seeking not supported by enum_words"
+enum_words (Iter.Cont k)    = Iter.dropWhile isSpace >>
+                              Iter.break isSpace >>= check_word k
+ where
+ check_word k' ("",_)  = enum_words ==<< k' EOF
+ check_word k' (str,_) = enum_words ==<< k' (Chunk [str])
+
+
+-- ------------------------------------------------------------------------
+-- Enumerators
+
+type EnumeratorM m a = EnumeratorGM [] Char m a
+
diff --git a/src/Data/Iteratee/Codecs/Tiff.hs b/src/Data/Iteratee/Codecs/Tiff.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/Codecs/Tiff.hs
@@ -0,0 +1,635 @@
+{-# LANGUAGE Rank2Types #-}
+
+-- Random and Binary IO with IterateeM
+
+-- A general-purpose TIFF library
+
+-- The library gives the user the TIFF dictionary, which the user
+-- can search for specific tags and obtain the values associated with 
+-- the tags, including the pixel matrix.
+--
+-- The overarching theme is incremental processing: initially,
+-- only the TIFF dictionary is read. The value associated with a tag
+-- is read only when that tag is looked up (unless the value was short
+-- and was packed in the TIFF dictionary entry). The pixel matrix
+-- (let alone the whole TIFF file) is not loaded in memory -- 
+-- the pixel matrix is not even located before it is needed.
+-- The matrix is processed incrementally, by a user-supplied 
+-- iteratee.
+--
+-- The incremental processing is accomplished by iteratees and enumerators.
+-- The enumerators are indeed first-class, they are stored
+-- in the interned TIFF dictionary data structure. These enumerators
+-- represent the values associated with tags; the values will be read
+-- on demand, when the enumerator is applied to a user-given iteratee.
+--
+-- The library extensively uses nested streams, tacitly converting the 
+-- stream of raw bytes from the file into streams of integers, 
+-- rationals and other user-friendly items. The pixel matrix is
+-- presented as a contiguous stream, regardless of its segmentation
+-- into strips and physical arrangement.
+-- The library exhibits random IO and binary parsing, reading
+-- of multi-byte numeric data in big- or little-endian formats.
+-- The library can be easily adopted for AIFF, RIFF and other
+-- IFF formats.
+--
+-- We show a representative application of the library: reading a sample
+-- TIFF file, printing selected values from the TIFF dictionary,
+-- verifying the values of selected pixels and computing the histogram
+-- of pixel values. The pixel verification procedure stops reading the
+-- pixel matrix as soon as all specified pixel values are verified.
+-- The histogram accumulation does read the entire matrix, but
+-- incrementally. Neither pixel matrix processing procedure loads
+-- the whole matrix in memory. In fact, we never read and retain
+-- more than the IO-buffer-full of raw data.
+
+-- This TIFF library is to be contrasted with the corresponding Scheme
+-- code:
+--     http://okmij.org/ftp/Scheme/binary-io.html#tiff
+-- The main distinction is using iteratees for on-demand processing.
+
+module Data.Iteratee.Codecs.Tiff where
+
+import Data.Iteratee.Base (StreamG (..), IterateeG (..), IterateeGM, EnumeratorGMM, EnumeratorN, bindm, liftI, (==<<), (>.), (>>==), iterErr, enumErr, convStream, iterReportError, enumEof)
+import qualified Data.Iteratee.Base as Iter
+import qualified Data.Iteratee.Base.StreamChunk as SC
+import Data.Iteratee.Binary
+import Control.Monad.State
+import Data.Char (chr)
+import Data.Int
+import Data.Word
+import Data.Ratio
+import Data.Maybe
+import qualified Data.IntMap as IM
+
+
+-- ========================================================================
+-- Sample TIFF user code
+-- The following is sample code using the TIFF library (whose implementation
+-- is in the second part of this file).
+-- Our sample code prints interesting information from the TIFF
+-- dictionary (such as the dimensions, the resolution and the name
+-- of the image)
+
+-- The main user function. tiff_reader is the library function,
+-- which builds the TIFF dictionary.
+-- process_tiff is the user function, to extract useful data
+-- from the dictionary
+-- test_tiff :: IO (Maybe String)
+-- test_tiff = test_driver_random (tiff_reader >>= process_tiff) "filename.tiff"
+
+-- Sample TIFF processing function
+process_tiff :: MonadIO m => Maybe (IM.IntMap TIFFDE) ->
+  IterateeGM [] Word8 m (Maybe String)
+process_tiff Nothing = return $ Just "No dictionary"
+process_tiff (Just dict) = do
+  note ["dict size: ", show $ IM.size dict]
+  -- Check tag values against the known values for the sample image
+  check_tag TG_IMAGEWIDTH  (flip dict_read_int dict) 129
+  check_tag TG_IMAGELENGTH (flip dict_read_int dict) 122
+  check_tag TG_BITSPERSAMPLE (flip dict_read_int dict) 8
+  check_tag TG_IMAGEDESCRIPTION (flip dict_read_string dict)
+		"JPEG:gnu-head-sm.jpg 129x122"
+  check_tag TG_COMPRESSION (flip dict_read_int dict) 1
+  check_tag TG_SAMPLESPERPIXEL (flip dict_read_int dict) 1
+  check_tag TG_STRIPBYTECOUNTS (flip dict_read_int dict) 15738 -- nrows*ncols
+  check_tag TG_XRESOLUTION (flip dict_read_rat dict) (72%1)
+  check_tag TG_YRESOLUTION (flip dict_read_rat dict) (72%1)
+
+  (n,hist) <- compute_hist dict
+  note ["computed histogram over ", show n, " values\n", show hist]
+  iterReportError >>= maybe (return ()) error
+  note ["Verifying values of sample pixels"]
+  verify_pixel_vals dict [(0,255), (17,248)]
+  err <- iterReportError
+  maybe (return ()) error err
+  return err
+ where check_tag tag action v = do
+	   vc <- action tag
+	   case vc of
+	     Just v' | v' == v -> note ["Tag ",show tag, " value ", show v]
+	     _ -> error $ unwords ["Tag", show tag, "unexpected:", show vc]
+
+-- process_tiff Nothing = return Nothing
+
+-- sample processing of the pixel matrix: computing the histogram
+compute_hist :: MonadIO m =>
+                TIFFDict ->
+                IterateeGM [] Word8 m (Int,IM.IntMap Int)
+compute_hist dict = Iter.joinI $ pixel_matrix_enum dict ==<< compute_hist' 0 IM.empty
+ where
+ compute_hist' count = liftI . Cont .step count
+ step count hist (Chunk ch)
+   | SC.null ch  = compute_hist' count hist
+   | otherwise = compute_hist' (count + SC.length ch) (foldr accum hist ch)
+ step count hist s        = liftI $ Done (count,hist) s
+ accum e = IM.insertWith (+) (fromIntegral e) 1
+
+-- Another sample processor of the pixel matrix: verifying values of
+-- some pixels
+-- This processor does not read the whole matrix; it stops as soon
+-- as everything is verified or the error is detected
+verify_pixel_vals :: MonadIO m =>
+                     TIFFDict -> [(IM.Key, Word8)] -> IterateeGM [] Word8 m ()
+verify_pixel_vals dict pixels = Iter.joinI $ pixel_matrix_enum dict ==<< 
+				verify 0 (IM.fromList pixels)
+ where
+ verify _ m | IM.null m = return ()
+ verify n m = liftI $ Cont (step n m)
+ step n m (Chunk xs)
+   | SC.null xs = verify n m
+   | otherwise = let (h, t) = (SC.head xs, SC.tail xs) in
+   case IM.updateLookupWithKey (\_k _e -> Nothing) n m of
+    (Just v,m') -> if v == h then step (succ n) m' (Chunk t)
+		     else iterErr $ unwords ["Pixel #",show n,
+					      "expected:",show v,
+					      "found", show h]
+    (Nothing,m')->    step (succ n) m' (Chunk t)
+ step _n _m s = liftI $ Done () s
+
+
+-- ========================================================================
+-- TIFF library code
+
+-- A TIFF directory is a finite map associating a TIFF tag with
+-- a record TIFFDE
+type TIFFDict = IM.IntMap TIFFDE
+
+data TIFFDE = TIFFDE{tiffde_count :: Int,        -- number of items
+		     tiffde_enum  :: TIFFDE_ENUM -- enumerator to get values
+		    }
+
+data TIFFDE_ENUM =
+  TEN_CHAR (forall a m. Monad m => EnumeratorGMM [] Word8 [] Char m a)
+  | TEN_BYTE (forall a m. Monad m => EnumeratorGMM [] Word8 [] Word8 m a)
+  | TEN_INT  (forall a m. Monad m => EnumeratorGMM [] Word8 [] Int m a)
+  | TEN_RAT  (forall a m. Monad m => EnumeratorGMM [] Word8 [] (Ratio Int) m a)
+
+-- Standard TIFF data types
+data TIFF_TYPE = TT_NONE  -- 0
+  | TT_byte      -- 1   8-bit unsigned integer
+  | TT_ascii     -- 2   8-bit bytes with last byte null
+  | TT_short     -- 3   16-bit unsigned integer
+  | TT_long      -- 4   32-bit unsigned integer
+  | TT_rational  -- 5   64-bit fractional (numer+denominator)
+    				-- The following was added in TIFF 6.0
+  | TT_sbyte     -- 6   8-bit signed (2s-complement) integer
+  | TT_undefined -- 7   An 8-bit byte, "8-bit chunk"
+  | TT_sshort    -- 8   16-bit signed (2s-complement) integer
+  | TT_slong     -- 9   32-bit signed (2s-complement) integer
+  | TT_srational -- 10  "signed rational",  two SLONGs (num+denominator)
+  | TT_float     -- 11  "IEEE 32-bit float", single precision (4-byte)
+  | TT_double    -- 12  "IEEE 64-bit double", double precision (8-byte)
+ deriving (Eq, Enum, Ord, Bounded, Show)
+
+
+-- Standard TIFF tags
+data TIFF_TAG = TG_other Int		-- other than below
+  | TG_SUBFILETYPE 	        -- subfile data descriptor
+  | TG_OSUBFILETYPE             -- +kind of data in subfile
+  | TG_IMAGEWIDTH	        -- image width in pixels
+  | TG_IMAGELENGTH	        -- image height in pixels
+  | TG_BITSPERSAMPLE	        -- bits per channel (sample)
+  | TG_COMPRESSION	        -- data compression technique
+  | TG_PHOTOMETRIC	        -- photometric interpretation
+  | TG_THRESHOLDING		-- +thresholding used on data
+  | TG_CELLWIDTH		-- +dithering matrix width
+  | TG_CELLLENGTH	        -- +dithering matrix height
+  | TG_FILLORDER		-- +data order within a byte
+  | TG_DOCUMENTNAME	        -- name of doc. image is from
+  | TG_IMAGEDESCRIPTION	        -- info about image
+  | TG_MAKE			-- scanner manufacturer name
+  | TG_MODEL			-- scanner model name/number
+  | TG_STRIPOFFSETS		-- offsets to data strips
+  | TG_ORIENTATION	        -- +image orientation
+  | TG_SAMPLESPERPIXEL          -- samples per pixel
+  | TG_ROWSPERSTRIP	        -- rows per strip of data
+  | TG_STRIPBYTECOUNTS          -- bytes counts for strips
+  | TG_MINSAMPLEVALUE	        -- +minimum sample value
+  | TG_MAXSAMPLEVALUE           -- maximum sample value
+  | TG_XRESOLUTION              -- pixels/resolution in x
+  | TG_YRESOLUTION              -- pixels/resolution in y
+  | TG_PLANARCONFIG             -- storage organization
+  | TG_PAGENAME		        -- page name image is from
+  | TG_XPOSITION		-- x page offset of image lhs
+  | TG_YPOSITION		-- y page offset of image lhs
+  | TG_FREEOFFSETS	        -- +byte offset to free block
+  | TG_FREEBYTECOUNTS	        -- +sizes of free blocks
+  | TG_GRAYRESPONSEUNIT         -- gray scale curve accuracy
+  | TG_GRAYRESPONSECURVE	-- gray scale response curve
+  | TG_GROUP3OPTIONS            -- 32 flag bits
+  | TG_GROUP4OPTIONS 	        -- 32 flag bits
+  | TG_RESOLUTIONUNIT           -- units of resolutions
+  | TG_PAGENUMBER	        -- page numbers of multi-page
+  | TG_COLORRESPONSEUNIT 	-- color scale curve accuracy
+  | TG_COLORRESPONSECURVE       -- RGB response curve
+  | TG_SOFTWARE			-- name & release
+  | TG_DATETIME 		-- creation date and time
+  | TG_ARTIST			-- creator of image
+  | TG_HOSTCOMPUTER		-- machine where created
+  | TG_PREDICTOR 		-- prediction scheme w/ LZW
+  | TG_WHITEPOINT		-- image white point
+  | TG_PRIMARYCHROMATICITIES    -- primary chromaticities
+  | TG_COLORMAP 		-- RGB map for pallette image
+  | TG_BADFAXLINES		-- lines w/ wrong pixel count
+  | TG_CLEANFAXDATA		-- regenerated line info
+  | TG_CONSECUTIVEBADFAXLINES   -- max consecutive bad lines
+  | TG_MATTEING                 -- alpha channel is present
+ deriving (Eq, Show)
+
+tag_map :: Num t => [(TIFF_TAG, t)]
+tag_map = [
+   (TG_SUBFILETYPE,254),
+   (TG_OSUBFILETYPE,255),
+   (TG_IMAGEWIDTH,256),
+   (TG_IMAGELENGTH,257),
+   (TG_BITSPERSAMPLE,258),
+   (TG_COMPRESSION,259),
+   (TG_PHOTOMETRIC,262),
+   (TG_THRESHOLDING,263),
+   (TG_CELLWIDTH,264),
+   (TG_CELLLENGTH,265),
+   (TG_FILLORDER,266),
+   (TG_DOCUMENTNAME,269),
+   (TG_IMAGEDESCRIPTION,270),
+   (TG_MAKE,271),
+   (TG_MODEL,272),
+   (TG_STRIPOFFSETS,273),
+   (TG_ORIENTATION,274),
+   (TG_SAMPLESPERPIXEL,277),
+   (TG_ROWSPERSTRIP,278),
+   (TG_STRIPBYTECOUNTS,279),
+   (TG_MINSAMPLEVALUE,280),
+   (TG_MAXSAMPLEVALUE,281),
+   (TG_XRESOLUTION,282),
+   (TG_YRESOLUTION,283),
+   (TG_PLANARCONFIG,284),
+   (TG_PAGENAME,285),
+   (TG_XPOSITION,286),
+   (TG_YPOSITION,287),
+   (TG_FREEOFFSETS,288),
+   (TG_FREEBYTECOUNTS,289),
+   (TG_GRAYRESPONSEUNIT,290),
+   (TG_GRAYRESPONSECURVE,291),
+   (TG_GROUP3OPTIONS,292),
+   (TG_GROUP4OPTIONS,293),
+   (TG_RESOLUTIONUNIT,296),
+   (TG_PAGENUMBER,297),
+   (TG_COLORRESPONSEUNIT,300),
+   (TG_COLORRESPONSECURVE,301),
+   (TG_SOFTWARE,305),
+   (TG_DATETIME,306),
+   (TG_ARTIST,315),
+   (TG_HOSTCOMPUTER,316),
+   (TG_PREDICTOR,317),
+   (TG_WHITEPOINT,318),
+   (TG_PRIMARYCHROMATICITIES,319),
+   (TG_COLORMAP,320),
+   (TG_BADFAXLINES,326),
+   (TG_CLEANFAXDATA,327),
+   (TG_CONSECUTIVEBADFAXLINES,328),
+   (TG_MATTEING,32995)
+   ]
+
+tag_map' :: IM.IntMap TIFF_TAG
+tag_map' = IM.fromList $ map (\(tag,v) -> (v,tag)) tag_map
+
+tag_to_int :: TIFF_TAG -> Int
+tag_to_int (TG_other x) = x
+tag_to_int x = fromMaybe (error $ "not found tag: " ++ show x) $ lookup x tag_map
+
+int_to_tag :: Int -> TIFF_TAG
+int_to_tag x = fromMaybe (TG_other x) $ IM.lookup x tag_map'
+
+
+-- The library function to read the TIFF dictionary
+tiff_reader :: IterateeGM [] Word8 IO (Maybe TIFFDict)
+tiff_reader = do
+  endian <- read_magic
+  check_version
+  case endian of
+    Just e -> bindm (endian_read4 e) $ \dict_offset -> do
+              Iter.seek (fromIntegral dict_offset)
+              load_dict e
+    Nothing -> return Nothing
+ where
+   -- Read the magic and set the endianness
+   read_magic = do
+     c1 <- Iter.head
+     c2 <- Iter.head
+     case (c1,c2) of
+      (Just 0x4d, Just 0x4d) -> return $ Just MSB
+      (Just 0x49, Just 0x49) -> return $ Just LSB
+      _ -> (iterErr $ "Bad TIFF magic word: " ++ show [c1,c2])
+           >> return Nothing
+
+   -- Check the version in the header. It is always ...
+   tiff_version = 42
+   check_version = do
+     v <- endian_read2 MSB
+     case v of
+      Just v' | v' == tiff_version -> return ()
+      _ -> iterErr $ "Bad TIFF version: " ++ show v
+
+-- A few conversion procedures
+u32_to_float :: Word32 -> Double
+u32_to_float _x = 		-- unsigned 32-bit int -> IEEE float
+  error "u32->float is not yet implemented"
+
+u32_to_s32 :: Word32 -> Int32   -- unsigned 32-bit int -> signed 32 bit
+u32_to_s32 = fromIntegral
+-- u32_to_s32 0x7fffffff == 0x7fffffff
+-- u32_to_s32 0xffffffff == -1
+
+u16_to_s16 :: Word16 -> Int16   -- unsigned 16-bit int -> signed 16 bit
+u16_to_s16 = fromIntegral
+-- u16_to_s16 32767 == 32767
+-- u16_to_s16 32768 == -32768
+-- u16_to_s16 65535 == -1
+
+u8_to_s8 :: Word8 -> Int8   -- unsigned 8-bit int -> signed 8 bit
+u8_to_s8 = fromIntegral
+-- u8_to_s8 127 == 127
+-- u8_to_s8 128 == -128
+-- u8_to_s8 255 == -1
+
+note :: (MonadIO m) => [String] -> IterateeGM [] el m ()
+note = lift . liftIO . putStrLn . concat
+
+-- An internal function to load the dictionary. It assumes that the stream
+-- is positioned to read the dictionary
+load_dict :: MonadIO m => Endian -> IterateeGM [] Word8 m (Maybe TIFFDict)
+load_dict e =
+  bindm (endian_read2 e) $ \nentries -> do
+   dict <- foldr (const read_entry) (return (Just IM.empty)) [1..nentries]
+   bindm (endian_read4 e) $ \next_dict -> do
+   when (next_dict > 0) $
+      note ["The TIFF file contains several images, ",
+	         "only the first one will be considered"]
+   return dict
+ where
+  read_entry dictM =
+    bindm dictM $ \dict ->
+     bindm (endian_read2 e) $ \tag ->    
+     bindm (endian_read2 e) $ \typ' ->    
+     bindm (convert_type (fromIntegral typ')) $ \typ ->    
+     bindm (endian_read4 e) $ \count -> do
+      -- we read the val-offset later. We need to check the size and the type
+      -- of the datum, because val-offset may contain the value itself,
+      -- in its lower-numbered bytes, regardless of the big/little endian
+      -- order!
+
+     note ["TIFFEntry: tag ",show . int_to_tag . fromIntegral $ tag, 
+	   " type ", show typ, " count ", show count]
+     enum_m <- read_value typ e (fromIntegral count)
+     case enum_m of
+      Just enum ->
+       return . Just $ IM.insert (fromIntegral tag) 
+		                 (TIFFDE (fromIntegral count) enum) dict
+      _ -> return (Just dict)
+
+  convert_type :: (Monad m) => Int -> IterateeGM [] el m (Maybe TIFF_TYPE)
+  convert_type typ | typ > 0 && typ <= fromEnum (maxBound::TIFF_TYPE)
+      = return . Just . toEnum $ typ
+  convert_type typ = do
+      iterErr $ "Bad type of entry: " ++ show typ
+      return Nothing
+
+  read_value :: MonadIO m => TIFF_TYPE -> Endian -> Int -> 
+                IterateeGM [] Word8 m (Maybe TIFFDE_ENUM)
+
+  read_value typ e' 0 =
+    bindm (endian_read4 e') $ \_offset -> do
+      iterErr $ "Zero count in the entry of type: " ++ show typ
+      return Nothing
+
+            		-- Read an ascii string from the offset in the
+			-- dictionary. The last byte of
+            		-- an ascii string is always zero, which is
+            		-- included in 'count' but we don't need to read it
+  read_value TT_ascii e' count | count > 4 = -- for sure, val-offset is offset
+    bindm (endian_read4 e') $ \offset ->
+      return . Just . TEN_CHAR $ \iter_char -> do
+            Iter.seek (fromIntegral offset)
+            let iter = convStream 
+                         (bindm Iter.head (return. Just .(:[]). chr . fromIntegral))
+                         iter_char
+            Iter.joinI $ Iter.joinI $ Iter.takeR (pred count) ==<< iter
+
+			-- Read the string of 0 to 3 characters long
+                        -- The zero terminator is included in count, but
+			-- we don't need to read it
+  read_value TT_ascii _e count = do	-- count is within 1..4
+    let len = pred count		-- string length
+    let loop acc 0 = return . Just . reverse $ acc
+        loop acc n = bindm Iter.head (\v -> loop ((chr . fromIntegral $ v):acc)
+                                             (pred n))
+    bindm (loop [] len) $ \str -> do
+      Iter.drop (4-len)
+      return . Just . TEN_CHAR $ immed_value str
+
+			-- Read the array of signed or unsigned bytes
+  read_value typ e' count | count > 4 && typ == TT_byte || typ == TT_sbyte =
+    bindm (endian_read4 e') $ \offset ->
+      return . Just . TEN_INT $ \iter_int -> do
+            Iter.seek (fromIntegral offset)
+            let iter = convStream 
+                         (bindm Iter.head (return . Just . (:[]) . conv_byte typ))
+                         iter_int
+            Iter.joinI $ Iter.joinI $ Iter.takeR count ==<< iter
+
+			-- Read the array of 1 to 4 bytes
+  read_value typ _e count | typ == TT_byte || typ == TT_sbyte = do
+    let loop acc 0 = return . Just . reverse $ acc
+        loop acc n = bindm Iter.head (\v -> loop (conv_byte typ v:acc)
+                                             (pred n))
+    bindm (loop [] count) $ \str -> do
+      Iter.drop (4-count)
+      return . Just . TEN_INT $ immed_value str
+
+			-- Read the array of Word8
+  read_value TT_undefined e' count | count > 4 =
+    bindm (endian_read4 e') $ \offset ->
+      return . Just . TEN_BYTE $ \iter -> do
+            Iter.seek (fromIntegral offset)
+            Iter.joinI $ Iter.takeR count iter
+
+			-- Read the array of Word8 of 1..4 elements,
+			-- packed in the offset field
+  read_value TT_undefined _e count = do 
+    let loop acc 0 = return . Just . reverse $ acc
+        loop acc n = bindm Iter.head (\v -> loop (v:acc) (pred n))
+    bindm (loop [] count) $ \str -> do
+      Iter.drop (4-count)
+      return . Just . TEN_BYTE $ immed_value str
+
+			-- Read the array of short integers
+
+			-- of 1 element: the offset field contains the value
+  read_value typ e' 1 | typ == TT_short || typ == TT_sshort =
+    bindm (endian_read2 e') $ \item -> do
+      Iter.drop 2				-- skip the padding
+      return . Just . TEN_INT $ immed_value [conv_short typ item]
+
+			-- of 2 elements: the offset field contains the value
+  read_value typ e' 2 | typ == TT_short || typ == TT_sshort =
+    bindm (endian_read2 e') $ \i1 -> 
+     bindm (endian_read2 e') $ \i2 ->
+      return . Just . TEN_INT $ 
+	     immed_value [conv_short typ i1, conv_short typ i2]
+
+			-- of n elements
+  read_value typ e' count | typ == TT_short || typ == TT_sshort =
+    bindm (endian_read4 e') $ \offset ->
+      return . Just . TEN_INT $ \iter_int -> do
+            Iter.seek (fromIntegral offset)
+            let iter = convStream 
+                         (bindm (endian_read2 e') 
+			  (return . Just . (:[]) . conv_short typ))
+                         iter_int
+            Iter.joinI $ Iter.joinI $ Iter.takeR (2*count) ==<< iter
+
+
+			-- Read the array of long integers
+			-- of 1 element: the offset field contains the value
+  read_value typ e' 1 | typ == TT_long || typ == TT_slong =
+    bindm (endian_read4 e') $ \item ->
+      return . Just . TEN_INT $ immed_value [conv_long typ item]
+
+			-- of n elements
+  read_value typ e' count | typ == TT_long || typ == TT_slong =
+    bindm (endian_read4 e') $ \offset ->
+      return . Just . TEN_INT $ \iter_int -> do
+            Iter.seek (fromIntegral offset)
+            let iter = convStream 
+                         (bindm (endian_read4 e')  
+			  (return . Just . (:[]) . conv_long typ))
+                         iter_int
+            Iter.joinI $ Iter.joinI $ Iter.takeR (4*count) ==<< iter
+
+			-- Read the array of rationals. A rational can't
+			-- be packed into the offset field
+{-
+  read_value typ e count | typ == TT_rational || typ == TT_srational = do 
+    bindm (endian_read4 e) $ \offset ->
+      return . Just . TEN_RAT $ \iter_rat -> do
+            Iter.seek (fromIntegral offset)
+            let iter = convStream 
+                         (bindm (endian_read4 e) $ \i1 ->
+			   bindm (endian_read4 e) $ \i2 ->
+			    (return . Just . (:[]) $ conv_rat typ i1 i2))
+                         iter_rat
+            Iter.joinI $ Iter.joinI $ Iter.takeR (8*count) ==<< iter
+-}
+
+
+  read_value typ e' count = -- stub
+    bindm (endian_read4 e') $ \_offset -> do
+     note ["unhandled type: ", show typ, " with count ", show count]
+     return Nothing
+
+  immed_value :: (Monad m) => [el] -> EnumeratorGMM [] Word8 [] el m a
+  immed_value item iter =
+     (Iter.enumPure1Chunk item >. enumEof) iter >>== Iter.joinI . return
+
+  conv_byte :: TIFF_TYPE -> Word8 -> Int
+  conv_byte TT_byte  = fromIntegral
+  conv_byte TT_sbyte = fromIntegral . u8_to_s8
+  conv_byte _ = error "This should never happen"
+
+  conv_short :: TIFF_TYPE -> Word16 -> Int
+  conv_short TT_short  = fromIntegral
+  conv_short TT_sshort = fromIntegral . u16_to_s16
+  conv_short _ = error "This should never happen"
+
+  conv_long :: TIFF_TYPE -> Word32 -> Int
+  conv_long TT_long  = fromIntegral
+  conv_long TT_slong = fromIntegral . u32_to_s32
+  conv_long _ = error "This should never happen"
+
+{- this code is never used...
+  conv_rat :: TIFF_TYPE -> Word32 -> Word32 -> Rational
+  conv_rat TT_rational v1 v2 = (fromIntegral v1) % (fromIntegral v2)
+  conv_rat TT_srational v1 v2 = (fromIntegral (u32_to_s32 v1)) % 
+				(fromIntegral (u32_to_s32 v2))
+  conv_rat _tt _ _ = error "This should never happen"
+-}
+
+-- Reading the pixel matrix
+-- For simplicity, we assume no compression and 8-bit pixels
+pixel_matrix_enum :: MonadIO m => TIFFDict -> EnumeratorN [] Word8 [] Word8 m a
+pixel_matrix_enum dict iter = validate_dict >>= proceed
+ where
+   -- Make sure we can handle this particular TIFF image
+   validate_dict =
+      dict_assert TG_COMPRESSION 1         `bindm`  \() ->
+      dict_assert TG_SAMPLESPERPIXEL 1    `bindm`  \() ->
+      dict_assert TG_BITSPERSAMPLE 8      `bindm`  \() ->
+      dict_read_int TG_IMAGEWIDTH dict    `bindm`  \ncols ->
+      dict_read_int TG_IMAGELENGTH dict   `bindm`  \nrows ->
+      dict_read_ints TG_STRIPOFFSETS dict `bindm`  \strip_offsets -> do
+        rps <- liftM (fromMaybe nrows) (dict_read_int TG_ROWSPERSTRIP dict)
+	if ncols > 0 && nrows > 0 && rps > 0 
+	   then return $ Just (ncols,nrows,rps,strip_offsets)
+	   else return Nothing
+	   
+   dict_assert tag v = do
+      vfound <- dict_read_int tag dict
+      case vfound of
+        Just v' | v' == v -> return $ Just ()
+	_ -> iterErr (unwords ["dict_assert: tag:", show tag,
+				"expected:", show v, "found:", show vfound]) >>
+             return Nothing
+
+   proceed Nothing = enumErr "Can't handle this TIFF" iter >>== return
+
+   proceed (Just (ncols,nrows,rows_per_strip,strip_offsets)) = do
+     let strip_size = rows_per_strip * ncols
+	 image_size = nrows * ncols
+     note ["Processing the pixel matrix, ", show image_size, " bytes"]
+     let loop _pos _ iter'@Done{} = return iter'
+         loop _pos [] iter'          = return iter'
+         loop pos (strip:strips) iter' = do
+	   Iter.seek (fromIntegral strip)
+	   let len = min strip_size (image_size - pos)
+	   iter'' <- Iter.takeR (fromIntegral len) iter'
+	   loop (pos+len) strips iter''
+     loop 0 strip_offsets iter
+
+
+-- A few helpers for getting data from TIFF dictionary
+
+dict_read_int :: Monad m => TIFF_TAG -> TIFFDict ->
+                 IterateeGM [] Word8 m (Maybe Int)
+dict_read_int tag dict = do
+  els <- dict_read_ints tag dict
+  case els of
+   Just (e:_) -> return $ Just e
+   _          -> return Nothing
+
+dict_read_ints :: Monad m => TIFF_TAG -> TIFFDict -> 
+		  IterateeGM [] Word8 m (Maybe [Int])
+dict_read_ints tag dict = 
+  case IM.lookup (tag_to_int tag) dict of
+      Just (TIFFDE _ (TEN_INT enum)) -> do
+	     e <- enum  ==<< Iter.stream2list
+	     return (Just e)
+      _ -> return Nothing
+
+dict_read_rat :: Monad m => TIFF_TAG -> TIFFDict ->
+                 IterateeGM [] Word8 m (Maybe (Ratio Int))
+dict_read_rat tag dict = 
+  case IM.lookup (tag_to_int tag) dict of
+      Just (TIFFDE 1 (TEN_RAT enum)) -> do
+	     [e] <- enum  ==<< Iter.stream2list
+	     return (Just e)
+      _ -> return Nothing
+
+dict_read_string :: Monad m => TIFF_TAG -> TIFFDict ->
+                    IterateeGM [] Word8 m (Maybe String)
+dict_read_string tag dict = 
+  case IM.lookup (tag_to_int tag) dict of
+      Just (TIFFDE _ (TEN_CHAR enum)) -> do
+	     e <- enum  ==<< Iter.stream2list
+	     return (Just e)
+      _ -> return Nothing
diff --git a/src/Data/Iteratee/Codecs/Wave.hs b/src/Data/Iteratee/Codecs/Wave.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/Codecs/Wave.hs
@@ -0,0 +1,339 @@
+{-# LANGUAGE RankNTypes, FlexibleContexts #-}
+
+{-
+
+This module is not meant primarily for instructive and pedagogical purposes.  As such, it is not fully featured, and sacrifices performance and generality for clarity of code and commonly installed packages.
+
+-}
+
+module Data.Iteratee.Codecs.Wave (
+  WAVEDE (..),
+  WAVEDE_ENUM (..),
+  WAVE_CHUNK (..),
+  AudioFormat (..),
+  wave_reader,
+  read_riff,
+  wave_chunk,
+  chunk_to_string,
+  dict_read_format,
+  dict_read_first_format,
+  dict_read_last_format,
+  dict_read_first_data,
+  dict_read_last_data,
+  dict_read_data,
+  dict_process_data
+)
+where
+
+import Data.Iteratee.Base
+import qualified Data.Iteratee.Base as Iter
+import Data.Iteratee.Binary
+import Data.Char (chr)
+import Data.Int
+import Data.Word
+import Data.Bits (shiftL)
+import Data.Maybe
+import qualified Data.IntMap as IM
+
+-- =====================================================
+-- WAVE libary code
+
+-- useful type synonyms
+
+type L = []
+
+-- |A WAVE directory is a list associating WAVE chunks with
+-- a record WAVEDE
+type WAVEDict = IM.IntMap [WAVEDE]
+
+data WAVEDE = WAVEDE{
+  wavede_count :: Int, -- ^length of chunk
+  wavede_type :: WAVE_CHUNK, -- ^type of chunk
+  wavede_enum :: WAVEDE_ENUM -- ^enumerator to get values of chunk
+  }
+
+data WAVEDE_ENUM =
+  WEN_BYTE  (forall a. EnumeratorGMM L Word8 L Word8 IO a)
+  | WEN_DUB (forall a. EnumeratorGMM L Word8 L Double IO a)
+
+-- |Standard WAVE Chunks
+data WAVE_CHUNK = WAVE_FMT -- ^Format
+  | WAVE_DATA              -- ^Data
+  | WAVE_OTHER String      -- ^Other
+  deriving (Eq, Ord, Show)
+instance Enum WAVE_CHUNK where
+  fromEnum WAVE_FMT = 1
+  fromEnum WAVE_DATA = 2
+  fromEnum (WAVE_OTHER _) = 3
+  toEnum 1 = WAVE_FMT
+  toEnum 2 = WAVE_DATA
+  toEnum 3 = WAVE_OTHER ""
+  toEnum _ = error "Invalid enumeration value"
+
+-- -----------------
+-- wave chunk reading/writing functions
+
+-- |Convert a string to WAVE_CHUNK type
+wave_chunk :: String -> Maybe WAVE_CHUNK
+wave_chunk str
+  | str == "fmt " = Just WAVE_FMT
+  | str == "data" = Just WAVE_DATA
+  | length str == 4 = Just $ WAVE_OTHER str
+  | otherwise = Nothing
+
+-- |Convert a WAVE_CHUNK to the representative string
+chunk_to_string :: WAVE_CHUNK -> String
+chunk_to_string WAVE_FMT = "fmt "
+chunk_to_string WAVE_DATA = "data"
+chunk_to_string (WAVE_OTHER str) = str
+
+-- -----------------
+-- throw this in here for now...
+data AudioFormat = AudioFormat {
+  numberOfChannels :: NumChannels, -- ^Number of channels in the audio data
+  sampleRate :: SampleRate, -- ^Sample rate of the audio
+  bitDepth :: BitDepth -- ^Bit depth of the audio data
+  } deriving (Show, Eq)
+
+type NumChannels = Integer
+type SampleRate = Integer
+type BitDepth = Integer
+
+-- convenience function to read a 4-byte ASCII string
+string_read4 :: Monad m => IterateeGM L Word8 m (Maybe String)
+string_read4 =
+  bindm Iter.head $ \s1 ->
+   bindm Iter.head $ \s2 ->
+   bindm Iter.head $ \s3 ->
+   bindm Iter.head $ \s4 ->
+   return . Just $ map (chr . fromIntegral) [s1, s2, s3, s4]
+
+-- -----------------
+
+-- |The library function to read the WAVE dictionary
+wave_reader :: IterateeGM L Word8 IO (Maybe WAVEDict)
+wave_reader = do
+  read_riff
+  bindm (endian_read4 LSB) $ \tot_size -> do
+    read_riff_wave
+    chunks_m <- find_chunks $ fromIntegral tot_size
+    load_dict $ join_m chunks_m
+
+-- |Read the RIFF header of a file.
+read_riff :: IterateeGM L Word8 IO ()
+read_riff = do
+  s <- string_read4
+  case s == Just "RIFF" of
+    True -> return ()
+    False -> iterErr $ "Bad RIFF header: " ++ show s
+
+-- | Read the WAVE part of the RIFF header.
+read_riff_wave :: IterateeGM L Word8 IO ()
+read_riff_wave = do
+  s <- string_read4
+  case s == Just "WAVE" of
+    True -> return ()
+    False -> iterErr $ "Bad RIFF/WAVE header: " ++ show s
+
+-- | An internal function to find all the chunks.  It assumes that the
+-- stream is positioned to read the first chunk.
+find_chunks :: Int -> IterateeGM L Word8 IO (Maybe [(Int, WAVE_CHUNK, Int)])
+find_chunks n = find_chunks' 12 []
+  where
+  find_chunks' offset acc =
+    bindm string_read4 $ \typ -> do
+      count <- endian_read4 LSB
+      case (wave_chunk typ, count) of
+        (Nothing, _) -> (iterErr $ "Bad subchunk descriptor: " ++ show typ)
+          >> return Nothing
+        (_, Nothing) -> iterErr "Bad subchunk length" >> return Nothing
+        (Just chk, Just count') -> let newpos = offset + 8 + count' in
+          case newpos >= fromIntegral n of
+            True -> return . Just $ reverse $
+                (fromIntegral offset, chk, fromIntegral count') : acc
+            False -> do
+              Iter.seek $ fromIntegral newpos
+              find_chunks' newpos $
+               (fromIntegral offset, chk, fromIntegral count') : acc
+
+load_dict :: [(Int, WAVE_CHUNK, Int)] ->
+               IterateeGM L Word8 IO (Maybe WAVEDict)
+load_dict = foldl read_entry (return (Just IM.empty))
+  where
+  read_entry dictM (offset, typ, count) =
+    bindm dictM $ \dict -> do
+      enum_m <- read_value dict offset typ count
+      case (enum_m, IM.lookup (fromEnum typ) dict) of
+        (Just enum, Nothing) -> --insert new entry
+          return . Just $ IM.insert (fromEnum typ)
+                                    [WAVEDE (fromIntegral count) typ enum] dict
+        (Just enum, Just _vals) -> --existing entry
+          return . Just $ IM.update
+            (\ls -> Just $ ls ++ [WAVEDE (fromIntegral count) typ enum])
+            (fromEnum typ) dict
+        (Nothing, _) -> return (Just dict)
+
+read_value :: WAVEDict ->
+              Int -> -- Offset
+              WAVE_CHUNK -> -- Chunk type
+              Int -> -- Count
+              IterateeGM L Word8 IO (Maybe WAVEDE_ENUM)
+read_value _dict offset _ 0 = do
+  iterErr $ "Zero count in the entry of chunk at: " ++ show offset
+  return Nothing
+
+read_value dict offset WAVE_DATA count = do
+  fmt_m <- dict_read_last_format dict
+  case fmt_m of
+    Just fmt ->
+      return . Just . WEN_DUB $ \iter_dub -> do
+        Iter.seek (8 + fromIntegral offset)
+        let iter = Iter.convStream (conv_func fmt) iter_dub
+        Iter.joinI $ Iter.joinI $ Iter.takeR count ==<< iter
+    Nothing -> do
+      iterErr $ "No valid format for data chunk at: " ++ show offset
+      return Nothing
+
+-- return the WaveFormat iteratee
+read_value _dict offset WAVE_FMT count =
+  return . Just . WEN_BYTE $ \iter -> do
+    Iter.seek (8 + fromIntegral offset)
+    Iter.joinI $ Iter.takeR count iter
+
+-- for WAVE_OTHER, return Word8s and maybe the user can parse them
+read_value _dict offset (WAVE_OTHER _str) count =
+  return . Just . WEN_BYTE $ \iter -> do
+    Iter.seek (8 + fromIntegral offset)
+    Iter.joinI $ Iter.takeR count iter
+
+unroller :: (Integral a, Monad m) =>
+            IterateeGM L Word8 m (Maybe a) ->
+            IterateeGM L Word8 m (Maybe (L a))
+unroller iter = do
+  w1 <- iter
+  w2 <- iter
+  w3 <- iter
+  w4 <- iter
+  w5 <- iter
+  w6 <- iter
+  w7 <- iter
+  w8 <- iter
+  case catMaybes [w1, w2, w3, w4, w5, w6, w7, w8] of
+    [] -> return Nothing
+    xs -> return $ Just xs
+
+-- |Convert Word8s to Doubles
+conv_func :: AudioFormat -> IterateeGM L Word8 IO (Maybe (L Double))
+conv_func (AudioFormat _nc _sr 8) = (fmap . fmap . fmap)
+  (normalize 8 . (fromIntegral :: Word8 -> Int8)) (unroller Iter.head)
+conv_func (AudioFormat _nc _sr 16) = (fmap . fmap . fmap)
+  (normalize 16 . (fromIntegral :: Word16 -> Int16))
+    (unroller (endian_read2 LSB))
+conv_func (AudioFormat _nc _sr 24) = (fmap . fmap . fmap)
+  (normalize 24 . (fromIntegral :: Word32 -> Int32))
+    (unroller (endian_read3 LSB))
+conv_func (AudioFormat _nc _sr 32) = (fmap . fmap . fmap)
+  (normalize 32 . (fromIntegral :: Word32 -> Int32))
+    (unroller (endian_read4 LSB))
+conv_func _ = iterErr "Invalid wave bit depth" >> return Nothing
+
+-- |An Iteratee to read a wave format chunk
+sWaveFormat :: IterateeGM L Word8 IO (Maybe AudioFormat)
+sWaveFormat =
+  bindm (endian_read2 LSB) $ \f' -> --data format, 1==PCM
+   bindm (endian_read2 LSB) $ \nc ->
+   bindm (endian_read4 LSB) $ \sr -> do
+     Iter.drop 6
+     bindm (endian_read2 LSB) $ \bd ->
+       case f' == 1 of
+         True -> return . Just $ AudioFormat (fromIntegral nc)
+                                             (fromIntegral sr)
+                                             (fromIntegral bd)
+         False -> return Nothing
+
+-- ---------------------
+-- functions to assist with reading from the dictionary
+
+-- |Read the first format chunk in the WAVE dictionary.
+dict_read_first_format :: WAVEDict -> IterateeGM L Word8 IO (Maybe AudioFormat)
+dict_read_first_format dict = case IM.lookup (fromEnum WAVE_FMT) dict of
+  Just [] -> return Nothing
+  Just ((WAVEDE _ WAVE_FMT (WEN_BYTE enum)) : _xs) -> enum ==<< sWaveFormat
+  _ -> return Nothing
+
+-- |Read the last fromat chunk from the WAVE dictionary.  This is useful
+-- when parsing all chunks in the dictionary.
+dict_read_last_format :: WAVEDict -> IterateeGM L Word8 IO (Maybe AudioFormat)
+dict_read_last_format dict = case IM.lookup (fromEnum WAVE_FMT) dict of
+  Just [] -> return Nothing
+  Just xs -> let (WAVEDE _ WAVE_FMT (WEN_BYTE enum)) = last xs in
+    enum ==<< sWaveFormat
+  _ -> return Nothing
+
+-- |Read the specified format chunk from the WAVE dictionary
+dict_read_format :: Int -> --Index in the format chunk list to read
+                    WAVEDict -> --Dictionary
+                    IterateeGM L Word8 IO (Maybe AudioFormat)
+dict_read_format ix dict = case IM.lookup (fromEnum WAVE_FMT) dict of
+  Just xs -> let (WAVEDE _ WAVE_FMT (WEN_BYTE enum)) = (!!) xs ix in
+    enum ==<< sWaveFormat
+  _ -> return Nothing
+
+-- |Read the first data chunk in the WAVE dictionary.
+dict_read_first_data :: WAVEDict -> IterateeGM L Word8 IO (Maybe [Double])
+dict_read_first_data dict = case IM.lookup (fromEnum WAVE_DATA) dict of
+  Just [] -> return Nothing
+  Just ((WAVEDE _ WAVE_DATA (WEN_DUB enum)) : _xs) -> do
+       e <- enum ==<< Iter.stream2list
+       return $ Just e
+  _ -> return Nothing
+
+-- |Read the last data chunk in the WAVE dictionary.
+dict_read_last_data :: WAVEDict -> IterateeGM L Word8 IO (Maybe [Double])
+dict_read_last_data dict = case IM.lookup (fromEnum WAVE_DATA) dict of
+  Just [] -> return Nothing
+  Just xs -> let (WAVEDE _ WAVE_DATA (WEN_DUB enum)) = last xs in do
+    e <- enum ==<< Iter.stream2list
+    return $ Just e
+  _ -> return Nothing
+
+-- |Read the specified data chunk from the WAVE dictionary.
+dict_read_data :: Int -> --Index in the data chunk list to read
+                  WAVEDict -> --Dictionary
+                  IterateeGM L Word8 IO (Maybe [Double])
+dict_read_data ix dict = case IM.lookup (fromEnum WAVE_DATA) dict of
+  Just xs -> let (WAVEDE _ WAVE_DATA (WEN_DUB enum)) = (!!) xs ix in do
+    e <- enum ==<< Iter.stream2list
+    return $ Just e
+  _ -> return Nothing
+
+-- |Read the specified data chunk from the dictionary, applying the
+-- data to the specified IterateeGM.
+dict_process_data :: Int -> -- Index in the data chunk list to read
+                     WAVEDict -> -- Dictionary
+                     IterateeGM L Double IO a ->
+                     IterateeGM L Word8 IO (Maybe a)
+dict_process_data ix dict iter = case IM.lookup (fromEnum WAVE_DATA) dict of
+  Just xs -> let (WAVEDE _ WAVE_DATA (WEN_DUB enum)) = (!!) xs ix in do
+    e <- enum ==<< iter
+    return $ Just e
+  _ -> return Nothing
+
+-- ---------------------
+-- convenience functions
+
+-- |Convert (Maybe []) to [].  Nothing maps to an empty list.
+join_m :: Maybe [a] -> [a]
+join_m Nothing = []
+join_m (Just a) = a
+
+-- |Normalize a given value for the provided bit depth.
+normalize :: Integral a => BitDepth -> a -> Double
+normalize 8 a = (fromIntegral a - 128) / 128
+normalize bd a = case (a > 0) of
+  True ->  fromIntegral a / divPos
+  False -> fromIntegral a / divNeg
+  where
+    divPos = fromIntegral (1 `shiftL` fromIntegral (bd - 1) :: Int) - 1
+    divNeg = fromIntegral (1 `shiftL` fromIntegral (bd - 1) :: Int)
+
diff --git a/src/Data/Iteratee/IO.hs b/src/Data/Iteratee/IO.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/IO.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE CPP #-}
+
+-- |Random and Binary IO with generic Iteratees.
+
+module Data.Iteratee.IO(
+  -- * File enumerators
+  -- ** Handle-based enumerators
+  enumHandle,
+  enumHandleRandom,
+#if defined(USE_POSIX)
+  -- ** FileDescriptor based enumerators
+  enumFd,
+  enumFdRandom,
+#endif
+  -- * Iteratee drivers
+  fileDriver,
+  fileDriverRandom,
+)
+
+where
+
+import Data.Iteratee.Base.StreamChunk (ReadableChunk (..))
+import Data.Iteratee.Base
+import Data.Iteratee.Binary()
+import Data.Iteratee.IO.Handle
+
+#if defined(USE_POSIX)
+import Data.Iteratee.IO.Fd
+#endif
+
+-- If Posix is available, use the fileDriverRandomFd as fileDriverRandom.  Otherwise, use a handle-based variant.
+#if defined(USE_POSIX)
+
+-- |Process a file using the given IterateeGM.  This function wraps
+-- enumFd as a convenience.
+fileDriver :: ReadableChunk s el => IterateeGM s el IO a ->
+              FilePath ->
+              IO (Either (String, a) a)
+fileDriver = fileDriverFd
+
+-- |Process a file using the given IterateeGM.  This function wraps
+-- enumFdRandom as a convenience.
+fileDriverRandom :: ReadableChunk s el => IterateeGM s el IO a ->
+                    FilePath ->
+                    IO (Either (String, a) a)
+fileDriverRandom = fileDriverRandomFd
+
+#else
+
+-- -----------------------------------------------
+-- Handle-based operations for compatibility.
+
+-- |Process a file using the given IterateeGM.  This function wraps
+-- enumHandle as a convenience.
+fileDriver :: ReadableChunk s el => IterateeGM s el IO a ->
+              FilePath ->
+              IO (Either (String, a) a)
+fileDriver = fileDriverHandle
+
+-- |Process a file using the given IterateeGM.  This function wraps
+-- enumFdHandle as a convenience.
+fileDriverRandom :: ReadableChunk s el => IterateeGM s el IO a ->
+                    FilePath ->
+                    IO (Either (String, a) a)
+fileDriverRandom = fileDriverRandomHandle
+
+#endif
diff --git a/src/Data/Iteratee/IO/Base.hs b/src/Data/Iteratee/IO/Base.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/IO/Base.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE CPP #-}
+
+module Data.Iteratee.IO.Base (
+#if defined(USE_WINDOWS)
+  module Data.Iteratee.IO.Windows,
+#endif
+#if defined(USE_POSIX)
+  module Data.Iteratee.IO.Posix,
+#else
+  FileOffset
+#endif
+)
+where
+
+#if defined(USE_WINDOWS)
+import Data.Iteratee.IO.Windows
+#endif
+
+#if defined(USE_POSIX)
+import Data.Iteratee.IO.Posix
+#else
+type FileOffset = Integer
+#endif
+
diff --git a/src/Data/Iteratee/IO/Fd.hs b/src/Data/Iteratee/IO/Fd.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/IO/Fd.hs
@@ -0,0 +1,131 @@
+{-# LANGUAGE CPP #-}
+
+-- |Random and Binary IO with generic Iteratees, using File Descriptors for IO.
+-- when available, these are the preferred functions for performing IO as they
+-- run in constant space and function properly with sockets, pipes, etc.
+
+module Data.Iteratee.IO.Fd(
+#if defined(USE_POSIX)
+  -- * File enumerators
+  -- ** FileDescriptor based enumerators
+  enumFd,
+  enumFdRandom,
+  -- * Iteratee drivers
+  fileDriverFd,
+  fileDriverRandomFd,
+#endif
+)
+
+where
+
+#if defined(USE_POSIX)
+import Data.Iteratee.Base.StreamChunk (ReadableChunk (..))
+import Data.Iteratee.Base
+import Data.Iteratee.Binary()
+import Data.Iteratee.IO.Base
+import Data.Int
+
+import Foreign.Ptr
+import Foreign.Marshal.Alloc
+
+import System.IO (SeekMode(..))
+
+import System.Posix hiding (FileOffset)
+
+-- ------------------------------------------------------------------------
+-- Binary Random IO enumerators
+
+-- |The enumerator of a POSIX File Descriptor.  This version enumerates
+-- over the entire contents of a file, in order, unless stopped by
+-- the iteratee.  In particular, seeking is not supported.
+enumFd :: ReadableChunk s el => Fd -> EnumeratorGM s el IO a
+enumFd fd iter' = IM $ allocaBytes (fromIntegral buffer_size) $ loop iter'
+  where
+    buffer_size = 4096
+    loop iter@Done{} _p = return iter
+    loop _iter@Seek{} _p = error "enumFd is not compatabile with seek IO"
+    loop (Cont step) p = do
+      n <- myfdRead fd (castPtr p) buffer_size
+      case n of
+        Left _errno -> unIM $ step (Error "IO error")
+        Right 0 -> return iter'
+        Right n' -> do
+          s <- readFromPtr p (fromIntegral n')
+          im <- unIM $ step (Chunk s)
+	  loop im p
+
+-- |The enumerator of a POSIX File Descriptor: a variation of enumFd that
+-- supports RandomIO (seek requests)
+enumFdRandom :: ReadableChunk s el => Fd -> EnumeratorGM s el IO a
+enumFdRandom fd iter =
+ IM $ allocaBytes (fromIntegral buffer_size) (loop (0,0) iter)
+ where
+  -- this can be usefully varied.  Values between 512 and 4096 seem
+  -- to provide the best performance for most cases.
+  buffer_size = 4096
+  -- the first argument of loop is (off,len), describing which part
+  -- of the file is currently in the buffer 'p'
+  loop :: (ReadableChunk s el) =>
+          (FileOffset,Int) ->
+          IterateeG s el IO a -> 
+	  Ptr el ->
+          IO (IterateeG s el IO a)
+  loop _pos iter'@Done{} _p = return iter'
+  loop pos@(off,len) (Seek off' c) p | 
+    off <= off' && off' < off + fromIntegral len =	-- Seek within buffer p
+    do
+    let local_off = fromIntegral $ off' - off
+    s <- readFromPtr (p `plusPtr` local_off) (len - local_off)
+    im <- unIM $ c (Chunk s)
+    loop pos im p
+  loop _pos iter'@(Seek off c) p = do -- Seek outside the buffer
+   off' <- myfdSeek fd AbsoluteSeek (fromIntegral off)
+   case off' of
+    Left _errno -> unIM $ enumErr "IO error" iter'
+    Right off''  -> loop (off'',0) (Cont c) p
+    -- Thanks to John Lato for the strictness annotation
+    -- Otherwise, the `off + fromIntegral len' below accumulates thunks
+  loop (off,len) _iter' _p | off `seq` len `seq` False = undefined
+  loop (off,len) iter'@(Cont step) p = do
+   n <- myfdRead fd (castPtr p) buffer_size
+   case n of
+    Left _errno -> unIM $ step (Error "IO error")
+    Right 0 -> return iter'
+    Right n' -> do
+         s <- readFromPtr p (fromIntegral n')
+         im <- unIM $ step (Chunk s)
+	 loop (off + fromIntegral len,fromIntegral n') im p
+
+-- |Process a file using the given IterateeGM.  This function wraps
+-- enumFd as a convenience.
+fileDriverFd :: ReadableChunk s el => IterateeGM s el IO a ->
+                FilePath ->
+                IO (Either (String, a) a)
+fileDriverFd iter filepath = do
+  fd <- openFd filepath ReadOnly Nothing defaultFileFlags
+  result <- unIM $ (enumFd fd >. enumEof) ==<< iter
+  closeFd fd
+  print_res result
+ where
+  print_res (Done a (Error err)) = return $ Left (err, a)
+  print_res (Done a _) = return $ Right a
+  print_res (Cont _)   = return $ Left ("Iteratee unfinished", undefined)
+  print_res (Seek _ _) = return $ Left ("Iteratee unfinished", undefined)
+
+-- |Process a file using the given IterateeGM.  This function wraps
+-- enumFdRandom as a convenience.
+fileDriverRandomFd :: ReadableChunk s el => IterateeGM s el IO a ->
+                      FilePath ->
+                      IO (Either (String, a) a)
+fileDriverRandomFd iter filepath = do
+  fd <- openFd filepath ReadOnly Nothing defaultFileFlags
+  result <- unIM $ (enumFdRandom fd >. enumEof) ==<< iter
+  closeFd fd
+  print_res result
+ where
+  print_res (Done a (Error err)) = return $ Left (err, a)
+  print_res (Done a _) = return $ Right a
+  print_res (Cont _)   = return $ Left ("Iteratee unfinished", undefined)
+  print_res (Seek _ _) = return $ Left ("Iteratee unfinished", undefined)
+
+#endif
diff --git a/src/Data/Iteratee/IO/Handle.hs b/src/Data/Iteratee/IO/Handle.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/IO/Handle.hs
@@ -0,0 +1,132 @@
+-- |Random and Binary IO with generic Iteratees.  These functions use Handles
+-- for IO operations, and are provided for compatibility.  When available,
+-- the File Descriptor based functions are preferred as these wastefully
+-- allocate memory rather than running in constant space.
+
+module Data.Iteratee.IO.Handle(
+  -- * File enumerators
+  enumHandle,
+  enumHandleRandom,
+  -- * Iteratee drivers
+  fileDriverHandle,
+  fileDriverRandomHandle,
+)
+
+where
+
+import Data.Iteratee.Base.StreamChunk (ReadableChunk (..))
+import Data.Iteratee.Base
+import Data.Iteratee.Binary()
+import Data.Iteratee.IO.Base
+
+import Data.Int
+import Control.Exception.Extensible
+
+import Foreign.Ptr
+import Foreign.Marshal.Alloc
+
+import System.IO
+
+
+-- ------------------------------------------------------------------------
+-- Binary Random IO enumerators
+
+-- |The enumerator of a file Handle.  This version enumerates
+-- over the entire contents of a file, in order, unless stopped by
+-- the iteratee.  In particular, seeking is not supported.
+enumHandle :: ReadableChunk s el => Handle -> EnumeratorGM s el IO a
+enumHandle h iter' = IM $ allocaBytes (fromIntegral buffer_size) $ loop iter'
+  where
+    buffer_size = 4096
+    loop iter@Done{} _p     = return iter
+    loop _iter@Seek{} _p    = error "enumH is not compatabile with seek IO"
+    loop iter@(Cont step) p = do
+      n <- try $ hGetBuf h p buffer_size
+      case n of
+        Left ex -> unIM $ step (Error $ show (ex :: SomeException))
+        Right 0 -> return iter
+        Right n' -> do
+          s <- readFromPtr p (fromIntegral n')
+          im <- unIM $ step (Chunk s)
+	  loop im p
+
+-- |The enumerator of a POSIX File Descriptor: a variation of enumFd that
+-- supports RandomIO (seek requests)
+enumHandleRandom :: ReadableChunk s el => Handle -> EnumeratorGM s el IO a
+enumHandleRandom h iter =
+ IM $ allocaBytes (fromIntegral buffer_size) (loop (0,0) iter)
+ where
+  -- this can be usefully varied.  Values between 512 and 4096 seem
+  -- to provide the best performance for most cases.
+  buffer_size = 4096
+  -- the first argument of loop is (off,len), describing which part
+  -- of the file is currently in the buffer 'p'
+  loop :: (ReadableChunk s el) =>
+          (FileOffset,Int) ->
+          IterateeG s el IO a -> 
+	  Ptr el ->
+          IO (IterateeG s el IO a)
+  loop _pos iter'@Done{} _p = return iter'
+  loop pos@(off,len) (Seek off' c) p | 
+    off <= off' && off' < off + fromIntegral len =	-- Seek within buffer p
+    do
+    let local_off = fromIntegral $ off' - off
+    s <- readFromPtr (p `plusPtr` local_off) (len - local_off)
+    im <- unIM $ c (Chunk s)
+    loop pos im p
+  loop _pos iter'@(Seek off c) p = do -- Seek outside the buffer
+   off' <- (try $ hSeek h AbsoluteSeek
+            (fromIntegral off)) :: IO (Either SomeException ())
+   case off' of
+    Left _errno -> unIM $ enumErr "IO error" iter'
+    Right _     -> loop (off,0) (Cont c) p
+    -- Thanks to John Lato for the strictness annotation
+    -- Otherwise, the `off + fromIntegral len' below accumulates thunks
+  loop (off,len) _iter' _p | off `seq` len `seq` False = undefined
+  loop (off,len) iter'@(Cont step) p = do
+   n <- (try $ hGetBuf h p buffer_size) :: IO (Either SomeException Int)
+   case n of
+    Left _errno -> unIM $ step (Error "IO error")
+    Right 0 -> return iter'
+    Right n' -> do
+         s <- readFromPtr p (fromIntegral n')
+         im <- unIM $ step (Chunk s)
+	 loop (off + fromIntegral len,fromIntegral n') im p
+
+-- ----------------------------------------------
+-- File Driver wrapper functions.
+
+-- |Process a file using the given IterateeGM.  This function wraps
+-- enumHandle as a convenience.
+fileDriverHandle :: ReadableChunk s el =>
+                    IterateeGM s el IO a ->
+                    FilePath ->
+                    IO (Either (String, a) a)
+fileDriverHandle iter filepath = do
+  h <- openBinaryFile filepath ReadMode
+  result <- unIM $ (enumHandle h >. enumEof) ==<< iter
+  hClose h
+  print_res result
+ where
+  print_res (Done a (Error err)) = return $ Left (err, a)
+  print_res (Done a _) = return $ Right a
+  print_res (Cont _)   = return $ Left ("Iteratee unfinished", undefined)
+  print_res (Seek _ _) = return $ Left ("Iteratee unfinished", undefined)
+
+-- |Process a file using the given IterateeGM.  This function wraps
+-- enumHandleRandom as a convenience.
+fileDriverRandomHandle :: ReadableChunk s el =>
+                          IterateeGM s el IO a ->
+                          FilePath ->
+                          IO (Either (String, a) a)
+fileDriverRandomHandle iter filepath = do
+  h <- openBinaryFile filepath ReadMode
+  result <- unIM $ (enumHandleRandom h >. enumEof) ==<< iter
+  hClose h
+  print_res result
+ where
+  print_res (Done a (Error err)) = return $ Left (err, a)
+  print_res (Done a _) = return $ Right a
+  print_res (Cont _)   = return $ Left ("Iteratee unfinished", undefined)
+  print_res (Seek _ _) = return $ Left ("Iteratee unfinished", undefined)
+
diff --git a/src/Data/Iteratee/IO/Posix.hs b/src/Data/Iteratee/IO/Posix.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/IO/Posix.hs
@@ -0,0 +1,107 @@
+{-# LANGUAGE CPP, ForeignFunctionInterface #-}
+
+-- Low-level IO operations 
+-- These operations are either missing from the GHC run-time library,
+-- or implemented suboptimally or heavy-handedly
+
+module Data.Iteratee.IO.Posix (
+#if defined(USE_POSIX)
+  FileOffset,
+  myfdRead,
+  myfdSeek,
+  Errno(..),
+  select'read'pending
+#endif
+)
+
+where
+
+#if defined(USE_POSIX)
+
+import Foreign.C
+import Foreign.Ptr
+import System.Posix
+import System.IO (SeekMode(..))
+import Control.Monad
+import Data.Bits			-- for select
+import Foreign.Marshal.Array		-- for select
+
+-- |Alas, GHC provides no function to read from Fd to an allocated buffer.
+-- The library function fdRead is not appropriate as it returns a string
+-- already. I'd rather get data from a buffer.
+-- Furthermore, fdRead (at least in GHC) allocates a new buffer each
+-- time it is called. This is a waste. Yet another problem with fdRead
+-- is in raising an exception on any IOError or even EOF. I'd rather
+-- avoid exceptions altogether.
+
+myfdRead :: Fd -> Ptr CChar -> ByteCount -> IO (Either Errno ByteCount)
+myfdRead (Fd fd) ptr n = do
+  n' <- cRead fd ptr n
+  if n' == -1 then liftM Left getErrno
+     else return . Right . fromIntegral $ n'
+
+foreign import ccall unsafe "unistd.h read" cRead
+  :: CInt -> Ptr CChar -> CSize -> IO CInt
+
+-- |The following fseek procedure throws no exceptions.
+myfdSeek:: Fd -> SeekMode -> FileOffset -> IO (Either Errno FileOffset)
+myfdSeek (Fd fd) mode off = do
+  n' <- cLSeek fd off (mode2Int mode)
+  if n' == -1 then liftM Left getErrno
+     else return . Right  $ n'
+ where mode2Int :: SeekMode -> CInt	-- From GHC source
+       mode2Int AbsoluteSeek = 0
+       mode2Int RelativeSeek = 1
+       mode2Int SeekFromEnd  = 2
+
+foreign import ccall unsafe "unistd.h lseek" cLSeek
+  :: CInt -> FileOffset -> CInt -> IO FileOffset
+
+
+-- Darn! GHC doesn't provide the real select over several descriptors! 
+-- We have to implement it ourselves
+
+type FDSET = CUInt
+type TIMEVAL = CLong -- Two longs
+foreign import ccall "unistd.h select" c_select
+  :: CInt -> Ptr FDSET -> Ptr FDSET -> Ptr FDSET -> Ptr TIMEVAL -> IO CInt
+
+-- Convert a file descriptor to an FDSet (for use with select)
+-- essentially encode a file descriptor in a big-endian notation
+fd2fds :: CInt -> [FDSET]
+fd2fds fd = replicate nb 0 ++ [setBit 0 off]
+  where
+    (nb,off) = quotRem (fromIntegral fd) (bitSize (undefined::FDSET))
+
+fds2mfd :: [FDSET] -> [CInt]
+fds2mfd fds = [fromIntegral (j+i*bitsize) | 
+	       (afds,i) <- zip fds [0..], j <- [0..bitsize],
+	       testBit afds j]
+  where bitsize = bitSize (undefined::FDSET)
+
+unFd :: Fd -> CInt
+unFd (Fd x) = x
+
+-- |poll if file descriptors have something to read
+-- Return the list of read-pending descriptors
+select'read'pending :: [Fd] -> IO (Either Errno [Fd])
+select'read'pending mfd =
+    withArray ([0,1]::[TIMEVAL]) ( -- holdover...
+    \_timeout ->
+      withArray fds (
+       \readfs ->
+         do
+         rc <- c_select (fdmax+1) readfs nullPtr nullPtr nullPtr
+         if rc == -1 then liftM Left getErrno
+         -- because the wait was indefinite, rc must be positive!
+            else peekArray (length fds) readfs >>=
+                 return . Right . map Fd . fds2mfd))
+  where
+    fds :: [FDSET]
+    fds  = foldr ormax [] (map (fd2fds . unFd) mfd)
+    fdmax = maximum $ map fromIntegral mfd
+    ormax [] x = x
+    ormax x [] = x
+    ormax (a:ar) (b:br) = (a .|. b) : ormax ar br
+
+#endif
diff --git a/src/Data/Iteratee/IO/Windows.hs b/src/Data/Iteratee/IO/Windows.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/IO/Windows.hs
@@ -0,0 +1,14 @@
+{-# LANGUAGE CPP #-}
+
+module Data.Iteratee.IO.Windows (
+#if defined(USE_WINDOWS)
+#endif
+)
+
+where
+
+#if defined(USE_WINDOWS)
+
+
+#endif
+
diff --git a/src/Data/Iteratee/WrappedByteString.hs b/src/Data/Iteratee/WrappedByteString.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Iteratee/WrappedByteString.hs
@@ -0,0 +1,96 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+module Data.Iteratee.WrappedByteString (
+  WrappedByteString (..)
+)
+
+where
+
+import qualified Data.Iteratee.Base.StreamChunk as SC
+import qualified Data.ByteString as BW
+import qualified Data.ByteString.Char8 as BC
+import qualified Data.ByteString.Internal as BBase
+import Data.Word
+import Foreign.ForeignPtr
+
+-- |Wrap a Data.ByteString ByteString
+newtype WrappedByteString a = WrapBS { unWrap :: BBase.ByteString }
+
+instance SC.ReadableChunk WrappedByteString Word8 where
+  readFromPtr p l = do
+    fptr <- newForeignPtr_ p
+    return . WrapBS $ BBase.fromForeignPtr fptr 0 l
+
+instance SC.ReadableChunk WrappedByteString Char where
+  readFromPtr p l = do
+    fptr <- newForeignPtr_ p
+    return . WrapBS $ BBase.fromForeignPtr (castForeignPtr fptr) 0 l
+
+instance SC.StreamChunk WrappedByteString Word8 where
+  length        = BW.length . unWrap
+  empty         = WrapBS BW.empty
+  null          = BW.null . unWrap
+  cons a        = WrapBS . BW.cons a . unWrap
+  head          = BW.head . unWrap
+  tail          = WrapBS . BW.tail . unWrap
+  findIndex p   = BW.findIndex p . unWrap
+  splitAt i s   = let (a1, a2) = BW.splitAt i $ unWrap s
+                  in (WrapBS a1, WrapBS a2)
+  dropWhile p   = WrapBS . BW.dropWhile p . unWrap
+  append a1 a2  = WrapBS (BW.append (unWrap a1) (unWrap a2))
+  fromList      = WrapBS . BW.pack
+  toList        = BW.unpack . unWrap
+  cMap          = bwmap
+
+bwmap :: (SC.StreamChunk s' el') =>
+         (Word8 -> el') ->
+         WrappedByteString Word8 ->
+         s' el'
+bwmap f xs = step xs
+  where
+  step bs
+    | SC.null bs = SC.empty
+    | True     = f (SC.head bs) `SC.cons` step (SC.tail bs)
+
+-- a specialized version to use in the RULE
+bwmap' :: (Word8 -> Word8) ->
+          WrappedByteString Word8 ->
+          WrappedByteString Word8
+bwmap' f = WrapBS . BW.map f . unWrap
+
+{-# RULES "bwmap/map" forall s (f :: Word8 -> Word8). bwmap f s = bwmap' f s #-}
+
+
+instance SC.StreamChunk WrappedByteString Char where
+  length        = BC.length . unWrap
+  empty         = WrapBS BC.empty
+  null          = BC.null . unWrap
+  cons a        = WrapBS . BC.cons a . unWrap
+  head          = BC.head . unWrap
+  tail          = WrapBS . BC.tail . unWrap
+  findIndex p   = BC.findIndex p . unWrap
+  splitAt i s   = let (a1, a2) = BC.splitAt i $ unWrap s
+                  in (WrapBS a1, WrapBS a2)
+  dropWhile p   = WrapBS . BC.dropWhile p . unWrap
+  append a1 a2  = WrapBS (BC.append (unWrap a1) (unWrap a2))
+  fromList      = WrapBS . BC.pack
+  toList        = BC.unpack . unWrap
+  cMap          = bcmap
+
+bcmap :: (SC.StreamChunk s' el') =>
+         (Char -> el') ->
+         WrappedByteString Char ->
+         s' el'
+bcmap f xs = step xs
+  where
+  step bs
+    | SC.null bs = SC.empty
+    | True     = f (SC.head bs) `SC.cons` step (SC.tail bs)
+
+-- a specialized version to use in the RULE
+bcmap' :: (Char -> Char) ->
+          WrappedByteString Char ->
+          WrappedByteString Char
+bcmap' f = WrapBS . BC.map f . unWrap
+
+{-# RULES "bcmap/map" forall s (f :: Char -> Char). bcmap f s = bcmap' f s #-}
