diff --git a/COPYRIGHT b/COPYRIGHT
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -6,7 +6,7 @@
     (c) copyright 1998-2000    Malcolm Wallace
 The modules Text.ParserCombinators.Poly* and Text.Parse and Text.Parse.*
 are
-    (c) copyright 2006-2013    Malcolm Wallace
+    (c) copyright 2006-2014    Malcolm Wallace
 
 These modules are licensed under the terms of the GNU Lesser
 General Public Licence (LGPL), which can be found in the file called
diff --git a/polyparse.cabal b/polyparse.cabal
--- a/polyparse.cabal
+++ b/polyparse.cabal
@@ -1,5 +1,5 @@
 name:		polyparse
-version:	1.9
+version:	1.10
 license:	LGPL
 license-file:	COPYRIGHT
 author:		Malcolm Wallace <Malcolm.Wallace@me.com>
@@ -44,6 +44,6 @@
         Text.ParserCombinators.Poly.Text
         Text.ParserCombinators.Poly.StateText
 --      Text.Parse.Text
-  cpp-options:		-DVERSION="1.9"
+  cpp-options:		-DVERSION="1.10"
   nhc98-options:	-K6M
   extensions:		CPP
diff --git a/src/Text/Parse.hs b/src/Text/Parse.hs
--- a/src/Text/Parse.hs
+++ b/src/Text/Parse.hs
@@ -10,6 +10,7 @@
     -- ** Combinators specific to string input, lexed haskell-style
   , word	-- :: TextParser String
   , isWord	-- :: String -> TextParser ()
+  , literal	-- :: String -> TextParser ()
   , optionalParens	-- :: TextParser a -> TextParser a
   , parens	-- :: Bool -> TextParser a -> TextParser a
   , field	-- :: Parse a => String -> TextParser a
@@ -167,6 +168,18 @@
 isWord w = do { w' <- word
               ; if w'==w then return w else fail ("expected "++w++" got "++w')
               }
+
+-- | Ensure that the next input word is the given string.  (No
+--   lexing, so mixed spaces, symbols, are accepted.)
+literal :: String -> TextParser String
+literal w = do { w' <- walk w
+               ; if w'==w then return w else fail ("expected "++w++" got "++w')
+               }
+  where walk []     = return w
+        walk (c:cs) = do { x <- next
+                         ; if x==c then walk cs
+                                   else return []
+                         }
 
 -- | Allow nested parens around an item.
 optionalParens :: TextParser a -> TextParser a
diff --git a/src/Text/Parse/ByteString.hs b/src/Text/Parse/ByteString.hs
--- a/src/Text/Parse/ByteString.hs
+++ b/src/Text/Parse/ByteString.hs
@@ -11,6 +11,7 @@
     -- ** Combinators specific to bytestring input, lexed haskell-style
   , word	-- :: TextParser String
   , isWord	-- :: String -> TextParser ()
+  , literal	-- :: String -> TextParser ()
   , optionalParens	-- :: TextParser a -> TextParser a
   , parens	-- :: Bool -> TextParser a -> TextParser a
   , field	-- :: Parse a => String -> TextParser a
@@ -199,6 +200,14 @@
 isWord w = do { w' <- word
               ; if w'==w then return w else fail ("expected "++w++" got "++w')
               }
+
+-- | Ensure that the next input word is the given string.  (No
+--   lexing, so mixed spaces, symbols, are accepted.)
+literal :: String -> TextParser String
+literal w = do { w' <- exactly (length w) next
+               ; if w'==w then return w
+                          else fail ("expected "++w++" got "++w')
+               }
 
 -- | Allow optional nested string parens around an item.
 optionalParens :: TextParser a -> TextParser a
diff --git a/src/Text/ParserCombinators/Poly/Base.hs b/src/Text/ParserCombinators/Poly/Base.hs
--- a/src/Text/ParserCombinators/Poly/Base.hs
+++ b/src/Text/ParserCombinators/Poly/Base.hs
@@ -194,13 +194,18 @@
 --   could be due either to a badly-formed terminator or a badly-formed
 --   element, so it raises both possible errors.
 manyFinally :: PolyParse p => p a -> p z -> p [a]
+{-
+-- This implementation is incorrect.  If at least one item has been
+-- parsed, but the terminator is missing, then this erroneously succeeds
+-- returning the empty list.
 manyFinally p t =
     (many p `discard` t)
       <|>
     oneOf' [ ("sequence terminator", do { t; return [] } )
            , ("item in a sequence",  do { p; return [] } )
            ]
-{-
+-}
+
 manyFinally p t =
     do { xs <- many p
        ; oneOf' [ ("sequence terminator", do { t; return () } )
@@ -208,7 +213,6 @@
                 ]
        ; return xs
        }
--}
 
 -- | @manyFinally'@ is like @manyFinally@, except when the terminator
 --   parser overlaps with the element parser.  In @manyFinally e t@,
@@ -218,14 +222,23 @@
 --   @manyFinally (accept "01") (accept "0")@ on input @"0101010"@ returns
 --   @["01","01","01"]@, whereas @manyFinally'@ with the same arguments
 --   and input returns @[]@.
-manyFinally' :: PolyParse p => p a -> p z -> p [a]
-manyFinally' p t =
-    (do t; return [])
-      <|>
-    (do x <- p; return (x:) `apply` manyFinally' p t)
-      <|>
-    oneOf' [ ("sequence terminator", do { t; return [] } )
-           , ("item in a sequence",  do { p; return [] } )
-           ]
+manyFinally' :: (PolyParse p, Show a) => p a -> p z -> p [a]
+manyFinally' p t = fmap reverse $ go []
+  where
+    go acc = ( do t; return acc )
+             <|>
+             ( do { x <- p
+                         <|>
+                         oneOf' [ ( "terminator in a manyFinally' sequence"
+                                  , do { t; return undefined }
+                                  )
+                                , ( "item in a manyFinally' sequence", p)
+                                ]
+                         `adjustErr` (("After successful partial sequence "
+                                      ++show (reverse acc)++",\n")++)
+                  ; go (x: acc)
+                  }
+             )
+
 
 ------------------------------------------------------------------------
diff --git a/src/Text/ParserCombinators/Poly/Lazy.hs b/src/Text/ParserCombinators/Poly/Lazy.hs
--- a/src/Text/ParserCombinators/Poly/Lazy.hs
+++ b/src/Text/ParserCombinators/Poly/Lazy.hs
@@ -8,6 +8,7 @@
   , next	-- :: Parser t t
   , eof		-- :: Parser t ()
   , satisfy	-- :: (t->Bool) -> Parser t t
+  , satisfyMsg	-- :: (t->Bool) -> String -> Parser t t
   , onFail      -- :: Parser t a -> Parser t a -> Parser t a
 
     -- ** Re-parsing
@@ -94,6 +95,11 @@
 -- | Return the next token if it satisfies the given predicate.
 satisfy :: (t->Bool) -> Parser t t
 satisfy = P . P.satisfy
+
+-- | Return the next token if it satisfies the given predicate.  The String
+--   argument describes the predicate for better error messages.
+satisfyMsg :: Show t => (t->Bool) -> String -> Parser t t
+satisfyMsg p s = P (P.satisfyMsg p s)
 
 -- | @p `onFail` q@ means parse p, unless p fails, in which case
 --   parse q instead.
diff --git a/src/Text/ParserCombinators/Poly/Parser.hs b/src/Text/ParserCombinators/Poly/Parser.hs
--- a/src/Text/ParserCombinators/Poly/Parser.hs
+++ b/src/Text/ParserCombinators/Poly/Parser.hs
@@ -10,6 +10,7 @@
   , next	-- :: Parser t t
   , eof		-- :: Parser t ()
   , satisfy	-- :: (t->Bool) -> Parser t t
+  , satisfyMsg	-- :: Show t => (t->Bool) -> String -> Parser t t
   , onFail      -- :: Parser t a -> Parser t a -> Parser t a
 
     -- ** Re-parsing
@@ -96,6 +97,16 @@
 satisfy :: (t->Bool) -> Parser t t
 satisfy pred = do { x <- next
                   ; if pred x then return x else fail "Parse.satisfy: failed"
+                  }
+
+-- | Return the next token if it satisfies the given predicate.  The
+--   String argument describes the function, for better error messages.
+satisfyMsg :: Show t => (t->Bool) -> String -> Parser t t
+satisfyMsg pred s
+             = do { x <- next
+                  ; if pred x then return x
+                              else fail $ "Parse.satisfy ("++s++") ("
+                                                           ++show x++"): failed"
                   }
 
 ------------------------------------------------------------------------
diff --git a/src/Text/ParserCombinators/Poly/Plain.hs b/src/Text/ParserCombinators/Poly/Plain.hs
--- a/src/Text/ParserCombinators/Poly/Plain.hs
+++ b/src/Text/ParserCombinators/Poly/Plain.hs
@@ -7,6 +7,7 @@
   , next	-- :: Parser t t
   , eof		-- :: Parser t ()
   , satisfy	-- :: (t->Bool) -> Parser t t
+  , satisfyMsg	-- :: (t->Bool) -> String -> Parser t t
   , onFail	-- :: Parser t a -> Parser t a -> Parser t a
 
     -- ** Re-parsing
