diff --git a/spreadsheet.cabal b/spreadsheet.cabal
--- a/spreadsheet.cabal
+++ b/spreadsheet.cabal
@@ -1,5 +1,5 @@
 Name:             spreadsheet
-Version:          0.1.2.1
+Version:          0.1.3
 License:          BSD3
 License-File:     LICENSE
 Author:           Henning Thielemann <haskell@henning-thielemann.de>
@@ -16,13 +16,55 @@
      <http://www.xoltar.org/languages/haskell/CSV.hs>.
   Both do not parse lazy.
   Reading from other source than plain 'String's could be easily added.
+  .
+  If you install this package by
+  .
+  cabal install -fbuildExamples
+  .
+  then an example program is compiled and installed, too.
+  This program fills a template text using data from a CSV file.
+  E.g. given a file @template.txt@ with content
+  .
+  > Name: FIRSTNAME SURNAME
+  > Born: BIRTH
+  .
+  and @names.csv@ with content
+  .
+  > "FIRSTNAME","SURNAME",BIRTH
+  > "Georg","Cantor",1845
+  > "Haskell","Curry",1900
+  > "Ada","Lovelace",1815
+  .
+  the call
+  .
+  > csvreplace --template=template.txt <names.csv
+  .
+  produces the output
+  .
+  > Name: Georg Cantor
+  > Born: 1845
+  > Name: Haskell Curry
+  > Born: 1900
+  > Name: Ada Lovelace
+  > Born: 1815
+  .
+  For similar (non-Haskell) programs see @cut@, @csvfix@, @csvtool@.
 Tested-With:       GHC==6.8.2, GHC==6.12.3
 Cabal-Version:     >=1.6
 Build-Type:        Simple
 Source-Repository head
-  type:     darcs
-  location: http://code.haskell.org/~thielema/spreadsheet/
+  Type:     darcs
+  Location: http://code.haskell.org/~thielema/spreadsheet/
 
+Source-Repository this
+  Tag:      0.1.3
+  Type:     darcs
+  Location: http://code.haskell.org/~thielema/spreadsheet/
+
+Flag buildExamples
+  description: Build example executables
+  default:     False
+
 Flag splitBase
   description: Choose the new smaller, split-up base package.
 
@@ -37,10 +79,19 @@
     Build-Depends: base >= 1.0 && < 2
 
   GHC-Options:      -Wall
-  Extensions:       GeneralizedNewtypeDeriving
   Hs-Source-Dirs:   src
   Exposed-Modules:
     Data.Spreadsheet
   Other-Modules:
     Data.Spreadsheet.Parser
     Data.Spreadsheet.CharSource
+
+Executable csvreplace
+  If flag(buildExamples)
+    Build-Depends:
+      bytestring >=0.9 && <0.10
+  Else
+    Buildable:      False
+  GHC-Options:      -Wall
+  Hs-Source-Dirs:   src
+  Main-Is: CSVReplace.hs
diff --git a/src/CSVReplace.hs b/src/CSVReplace.hs
new file mode 100644
--- /dev/null
+++ b/src/CSVReplace.hs
@@ -0,0 +1,103 @@
+module Main where
+
+import qualified Data.Spreadsheet as Sheet
+import qualified Data.List.HT as ListHT
+
+import System.Console.GetOpt (getOpt, ArgOrder(..), OptDescr(..), ArgDescr(..), usageInfo, )
+import System.Environment (getArgs, getProgName, )
+import qualified System.Exit as Exit
+import qualified System.IO as IO
+
+import qualified Data.ByteString.Char8 as B
+import qualified Data.ByteString.Lazy.Char8 as BL
+
+import qualified Control.Monad.Exception.Asynchronous as AExc
+import qualified Control.Monad.Exception.Synchronous as Exc
+import Control.Monad.Trans.Class (lift, )
+import Control.Monad (when, )
+import Data.Foldable (forM_, )
+
+
+data Flags =
+   Flags {
+      optMultiFile :: Maybe FilePath,
+      optQuotation,
+      optDelimiter :: Char
+   }
+
+defltFlags :: Flags
+defltFlags = Flags {
+      optMultiFile = Nothing,
+      optQuotation = '"',
+      optDelimiter = ','
+   }
+
+exitFailureMsg :: String -> IO a
+exitFailureMsg msg = do
+   IO.hPutStrLn IO.stderr msg
+   Exit.exitFailure
+
+options :: [OptDescr (Flags -> IO Flags)]
+options =
+   Option ['h'] ["help"]
+      (NoArg (\_ -> do
+         programName <- getProgName
+         putStrLn $ flip usageInfo options $
+            "Usage: " ++ programName ++ " [OPTIONS] TEMPLATE-FILE\n" ++
+            "The CSV file is read from standard input.\n"
+         Exit.exitSuccess))
+      "show options" :
+   Option [] ["multifile"]
+      (flip ReqArg "FILEPATTERN" $ \str flags ->
+         return $ flags{optMultiFile = Just str})
+      "generate one file per CSV row" :
+   Option ['d'] ["delimiter"]
+      (flip ReqArg "CHAR" $ \str flags -> do
+         case str of
+            [c] -> return $ flags{optDelimiter = c}
+            _ -> exitFailureMsg $ "delimiter must be one character, which " ++ show str ++ " is not")
+      "field delimiter character" :
+   Option ['q'] ["quotation"]
+      (flip ReqArg "CHAR" $ \str flags -> do
+         case str of
+            [c] -> return $ flags{optQuotation = c}
+            _ -> exitFailureMsg $ "quotation mark must be one character, which " ++ show str ++ " is not")
+      "quotation mark character" :
+   []
+
+
+replaceRow :: String -> [String] -> [String] -> String
+replaceRow template names row =
+   ListHT.multiReplace (filter (not . null . fst) $ zip names row) template
+
+replace :: String -> [String] -> Sheet.T -> String
+replace template names =
+   concatMap (replaceRow template names)
+
+
+main :: IO ()
+main =
+   Exc.resolveT (\e -> exitFailureMsg $ "Aborted: " ++ e) $ do
+      argv <- lift getArgs
+      let (opts, files, errors) = getOpt RequireOrder options argv
+      when (not (null errors)) $ Exc.throwT $ concat $ errors
+      flags <- lift $ foldr (=<<) (return defltFlags) opts
+      case files of
+         [templateName] -> do
+            template <- fmap B.unpack $ lift $ B.readFile templateName
+            sheet <-
+               fmap (Sheet.fromString (optQuotation flags) (optDelimiter flags)
+                      . BL.unpack) $
+               lift BL.getContents
+            case AExc.result sheet of
+               [] -> Exc.throwT "empty CSV input"
+               (names:rows) ->
+                  case optMultiFile flags of
+                     Nothing -> do
+                        lift $ putStr $ replace template names rows
+                        forM_ (AExc.exception sheet) $ Exc.throwT
+                     Just filePattern ->
+                        lift $ forM_ rows $ \row ->
+                           writeFile (replaceRow filePattern names row) $
+                              replaceRow template names row
+         _ -> Exc.throwT "I need exactly one template file."
diff --git a/src/Data/Spreadsheet/CharSource.hs b/src/Data/Spreadsheet/CharSource.hs
--- a/src/Data/Spreadsheet/CharSource.hs
+++ b/src/Data/Spreadsheet/CharSource.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 module Data.Spreadsheet.CharSource where
 
 import Control.Monad.Trans.State (StateT(StateT), gets, runStateT, mapStateT, )
