diff --git a/iteratee.cabal b/iteratee.cabal
--- a/iteratee.cabal
+++ b/iteratee.cabal
@@ -1,5 +1,5 @@
 name:          iteratee
-version:       0.8.0.3
+version:       0.8.1.0
 synopsis:      Iteratee-based I/O
 description:
   The Iteratee monad provides strict, safe, and functional I/O. In addition
@@ -9,8 +9,8 @@
 maintainer:    John W. Lato <jwlato@gmail.com>
 license:       BSD3
 license-file:  LICENSE
-homepage:      http://inmachina.net/~jwlato/haskell/iteratee
-tested-with:   GHC == 6.12.3
+homepage:      http://www.tiresiaspress.us/haskell/iteratee
+tested-with:   GHC == 7.0.1, GHC == 6.12.3
 stability:     experimental
 
 cabal-version: >= 1.6
@@ -111,4 +111,4 @@
 
 source-repository head
   type:     darcs
-  location: http://inmachina.net/~jwlato/haskell/iteratee
+  location: http://www.tiresiaspress.us/haskell/iteratee
diff --git a/src/Data/Iteratee/Base/ReadableChunk.hs b/src/Data/Iteratee/Base/ReadableChunk.hs
--- a/src/Data/Iteratee/Base/ReadableChunk.hs
+++ b/src/Data/Iteratee/Base/ReadableChunk.hs
@@ -13,6 +13,7 @@
 import Prelude hiding (head, tail, dropWhile, length, splitAt )
 
 import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
 import Data.Word
 import Control.Monad.IO.Class
 import Foreign.C
@@ -45,3 +46,7 @@
 
 instance ReadableChunk B.ByteString Word8 where
   readFromPtr buf l = liftIO $ B.packCStringLen (castPtr buf, l)
+
+instance ReadableChunk L.ByteString Word8 where
+  readFromPtr buf l = liftIO $
+    return . L.fromChunks . (:[]) =<< readFromPtr buf l
diff --git a/src/Data/Iteratee/Char.hs b/src/Data/Iteratee/Char.hs
--- a/src/Data/Iteratee/Char.hs
+++ b/src/Data/Iteratee/Char.hs
@@ -1,10 +1,11 @@
-{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ScopedTypeVariables, FlexibleContexts #-}
 
 -- | Utilities for Char-based iteratee processing.
 
 module Data.Iteratee.Char (
   -- * Word and Line processors
   printLines
+  ,printLinesUnterminated
   ,enumLines
   ,enumLinesBS
   ,enumWords
@@ -26,14 +27,42 @@
 
 -- |Print lines as they are received. This is the first `impure' iteratee
 -- with non-trivial actions during chunk processing
+--
+--  Only lines ending with a newline are printed,
+--  data terminated with EOF is not printed.
 printLines :: Iteratee String IO ()
 printLines = lines'
-  where
+ where
   lines' = I.break (`elem` "\r\n") >>= \l -> terminators >>= check l
   check _  0 = return ()
   check "" _ = return ()
   check l  _ = liftIO (putStrLn l) >> lines'
-  terminators = heads "\r\n" >>= \l -> if l == 0 then heads "\n" else return l
+
+-- |Print lines as they are received.
+--
+--  All lines are printed, including a line with a terminating EOF.
+--  If the final line is terminated by EOF without a newline,
+--  no newline is printed.
+--  this function should be used in preference to printLines when possible,
+--  as it is more efficient with long lines.
+printLinesUnterminated :: forall s el.
+                       (Eq el, Nullable s, LL.StringLike s, LL.ListLike s el)
+                       => Iteratee s IO ()
+printLinesUnterminated = lines'
+ where
+  lines' = do
+    joinI $ I.breakE (`LL.elem` t1) (I.mapChunksM_ (putStr . LL.toString))
+    terminators >>= check
+  check 0 = return ()
+  check _ = liftIO (putStrLn "") >> lines'
+  t1 :: s
+  t1 = LL.fromString "\r\n"
+
+terminators :: (Eq el, Nullable s, LL.StringLike s, LL.ListLike s el)
+            => Iteratee s IO Int
+terminators = do
+  l <- heads (LL.fromString "\r\n")
+  if l == 0 then heads (LL.fromString "\n") else return l
 
 
 -- |Convert the stream of characters to the stream of lines, and
diff --git a/src/Data/Iteratee/Iteratee.hs b/src/Data/Iteratee/Iteratee.hs
--- a/src/Data/Iteratee/Iteratee.hs
+++ b/src/Data/Iteratee/Iteratee.hs
@@ -13,6 +13,8 @@
   ,identity
   ,skipToEof
   ,isStreamFinished
+  -- ** Chunkwise Iteratees
+  ,mapChunksM_
   -- ** Nested iteratee combinators
   ,convStream
   ,unfoldConvStream
@@ -46,6 +48,7 @@
 import Data.Iteratee.Base
 
 import Control.Exception
+import Control.Monad.Trans.Class
 import Data.Maybe
 import Data.Typeable
 
@@ -117,7 +120,21 @@
 seek :: (Monad m, NullPoint s) => FileOffset -> Iteratee s m ()
 seek o = throwRecoverableErr (toException $ SeekException o) (const identity)
 
+-- | Map a monadic function over the chunks of the stream and ignore the
+-- result.  Useful for creating efficient monadic iteratee consumers, e.g.
+--
+--   logger = mapChunksM_ (liftIO . putStrLn)
+--
+-- these can be efficiently run in parallel with other iteratees via
+-- enumPair.
+mapChunksM_ :: (Monad m, Nullable s) => (s -> m b) -> Iteratee s m ()
+mapChunksM_ f = liftI step
+  where
+    step (Chunk xs) = lift (f xs) >> liftI step
+    step s@(EOF _)  = idone () s
+{-# INLINE mapChunksM_ #-}
 
+
 -- ---------------------------------------------------
 -- The converters show a different way of composing two iteratees:
 -- `vertical' rather than `horizontal'
@@ -146,7 +163,7 @@
 -- 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
--- Iteratee s el s'.
+-- Iteratee s m s'.
 convStream ::
  (Monad m, Nullable s) =>
   Iteratee s m s'
@@ -154,7 +171,7 @@
 convStream fi = eneeCheckIfDone check
   where
     check k = isStreamFinished >>= maybe (step k) (idone (liftI k) . EOF . Just)
-    step k = fi >>= convStream fi . k . Chunk
+    step k = fi >>= eneeCheckIfDone check . k . Chunk
 
 -- |The most general stream converter.  Given a function to produce iteratee
 -- transformers and an initial state, convert the stream using iteratees
diff --git a/src/Data/Iteratee/ListLike.hs b/src/Data/Iteratee/ListLike.hs
--- a/src/Data/Iteratee/ListLike.hs
+++ b/src/Data/Iteratee/ListLike.hs
@@ -24,6 +24,7 @@
   ,roll
   ,length
   -- ** Nested iteratee combinators
+  ,breakE
   ,take
   ,takeUpTo
   ,mapStream
@@ -112,6 +113,10 @@
 -- If the stream is not terminated, the first character of the remaining stream
 -- satisfies the predicate.
 --
+-- N.B. @breakE@ should be used in preference to @break@.
+-- @break@ will retain all data until the predicate is met, which may
+-- result in a space leak.
+--
 -- The analogue of @List.break@
 
 break :: (Monad m, LL.ListLike s el) => (el -> Bool) -> Iteratee s m s
@@ -166,7 +171,7 @@
   where
   loop cnt xs
     | nullC xs = return cnt
-    | True    = liftI (step cnt xs)
+    | True     = liftI (step cnt xs)
   step cnt str (Chunk xs) | nullC xs  = liftI (step cnt str)
   step cnt str stream     | nullC str = idone cnt stream
   step cnt str s@(Chunk xs) =
@@ -248,8 +253,8 @@
 length :: (Monad m, Num a, LL.ListLike s el) => Iteratee s m a
 length = liftI (step 0)
   where
-    step !i (Chunk xs) = liftI (step $ i + LL.length xs)
-    step !i stream     = idone (fromIntegral i) stream
+    step !i (Chunk xs) = liftI (step $! i + fromIntegral (LL.length xs))
+    step !i stream     = idone i stream
 {-# INLINE length #-}
 
 
@@ -257,6 +262,25 @@
 -- The converters show a different way of composing two iteratees:
 -- `vertical' rather than `horizontal'
 
+-- |Takes an element predicate and an iteratee, running the iteratee
+-- on all elements of the stream until the predicate is met.
+--
+-- the following rule relates @break@ to @breakE@
+-- @break@ pred >> iter === @joinI@ (@breakE@ pred iter)
+--
+-- @breakE@ should be used in preference to @break@ whenever possible.
+breakE :: (Monad m, LL.ListLike s el, NullPoint s) => (el -> Bool) -> Enumeratee s s m a
+breakE cpred = eneeCheckIfDone (liftI . step)
+ where
+  step k (Chunk s)
+      | LL.null s  = liftI (step k)
+      | otherwise  = case LL.break cpred s of
+        (str', tail')
+          | LL.null tail' -> eneeCheckIfDone (liftI . step) . k $ Chunk str'
+          | otherwise     -> idone (k $ Chunk str') (Chunk tail')
+  step k stream           =  idone (k stream) stream
+{-# INLINE breakE #-}
+
 -- |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.
@@ -577,8 +601,8 @@
       -> Iteratee s m ()
 mapM_ f = liftI step
   where
-    step (Chunk xs) | LL.null xs = mapM_ f
-    step (Chunk xs) = lift (LL.mapM_ f xs) >> mapM_ f
+    step (Chunk xs) | LL.null xs = liftI step
+    step (Chunk xs) = lift (LL.mapM_ f xs) >> liftI step
     step s@(EOF _)  = idone () s
 {-# INLINE mapM_ #-}
 
diff --git a/src/Data/NullPoint.hs b/src/Data/NullPoint.hs
--- a/src/Data/NullPoint.hs
+++ b/src/Data/NullPoint.hs
@@ -9,6 +9,7 @@
 where
 
 import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
 
 -- ----------------------------------------------
 -- |NullPoint class.  Containers that have a null representation, corresponding
@@ -22,3 +23,5 @@
 instance NullPoint B.ByteString where
   empty = B.empty
 
+instance NullPoint L.ByteString where
+  empty = L.empty
diff --git a/src/Data/Nullable.hs b/src/Data/Nullable.hs
--- a/src/Data/Nullable.hs
+++ b/src/Data/Nullable.hs
@@ -9,6 +9,7 @@
 
 import Data.NullPoint
 import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as L
 
 
 -- ----------------------------------------------
@@ -22,3 +23,6 @@
 
 instance Nullable B.ByteString where
   nullC = B.null
+
+instance Nullable L.ByteString where
+  nullC = L.null
diff --git a/tests/foo.hs b/tests/foo.hs
new file mode 100644
--- /dev/null
+++ b/tests/foo.hs
@@ -0,0 +1,20 @@
+import Data.ByteString (ByteString)
+import Data.Iteratee as I
+import Data.Iteratee.Char
+import System
+
+main = do
+        args <- getArgs
+        let fname     = args !! 0
+        let blockSize = read $ args !! 1
+
+        fileDriver (leak blockSize) fname >>= print
+
+leak :: Int -> Iteratee ByteString IO ()
+leak blockSize = chunkedRead
+    where
+        consChunk :: Iteratee ByteString IO String
+        consChunk = (joinI $ I.take blockSize I.length) >>= return . show
+
+        chunkedRead :: Iteratee ByteString IO ()
+        chunkedRead = joinI $ convStream consChunk printLinesUnterminated
diff --git a/tests/testIteratee.hs b/tests/testIteratee.hs
--- a/tests/testIteratee.hs
+++ b/tests/testIteratee.hs
@@ -91,6 +91,13 @@
 prop_break2 f xs = runner1 (enumPure1Chunk xs (Iter.break f >> stream2list)) == snd (break f xs)
   where types = xs :: [Int]
 
+prop_breakE f xs = runner1 (enumPure1Chunk xs (joinI $ Iter.breakE f stream2stream)) == fst (break f xs)
+  where types = xs :: [Int]
+
+prop_breakE2 f xs = runner1 (enumPure1Chunk xs (joinI (Iter.breakE f stream2stream) >> stream2list)) == snd (break f xs)
+  where types = xs :: [Int]
+
+
 prop_head xs = P.length xs > 0 ==> runner1 (enumPure1Chunk xs Iter.head) == head xs
   where types = xs :: [Int]
 
@@ -260,7 +267,7 @@
   ]
   ,testGroup "Simple Iteratees" [
     testProperty "break" prop_break
-    ,testProperty "break remaineder" prop_break2
+    ,testProperty "break remainder" prop_break2
     ,testProperty "head" prop_head
     ,testProperty "head remainder" prop_head2
     ,testProperty "heads" prop_heads
@@ -289,6 +296,8 @@
     testProperty "mapStream identity" prop_mapStream
     ,testProperty "mapStream identity 2" prop_mapStream2
     ,testProperty "mapStream identity joinI" prop_mapjoin
+    ,testProperty "breakE" prop_breakE
+    ,testProperty "breakE remainder" prop_breakE2
     ,testProperty "take" prop_take
     ,testProperty "take (finished iteratee)" prop_take2
     ,testProperty "takeUpTo" prop_takeUpTo
