diff --git a/eddie.1 b/eddie.1
--- a/eddie.1
+++ b/eddie.1
@@ -2,13 +2,9 @@
 .SH NAME
 eddie \- run haskell filters from the command line
 .SH SYNOPSYS
-.B eddie 
-[
-.I options
-]
-[
-.I expr
-]
+.B eddie
+.RI [ options ]
+.RI [ expr ]
 .I file ...
 .SH DESCRIPTION
 .B eddie
@@ -19,44 +15,53 @@
 arguments concatenated together,
 or standard input if no file arguments are present.
 .PP
-If the
-.B \-l
-option is used, the 
+The
+.BR \-\-line ,
+.B \-\-file
+and
+.B \-\-list
+options can be used to cause
 .I expression
-is used to process each line instead of the entire contents.
+to be used to process each line, each file,
+or a list of files or lines respectively.
 .PP
 The prelude, Data.List and Data.Char modules are available for building
 expressions. Other modules may be added with the
-.B \-M
+.B \-\-Modules
 and
-.B \-m
+.B \-\-modules
 options.
 .SH OPTIONS
-.IP \-e
+.IP \-\-expr
 The
-.BI \-e\  expr
-(long form
-.BI \-\-expr\  expr)
+.B \-\-expr
+.I expr
+(short form
+.B \-e
+.IR expr )
 option concatenates it's value to the
 haskell expression being evaluated with a newline separator.
 Multiple occurrences can be used to build up a
 multi-line expression.
-.IP ""
+.RS
+.PP
 If no
-.BI \-e\  expr
+.B \-e
+.I expr
 option is present, the first non-flag argument will be used for
 the haskell expression.
-.IP \-l
+.RE 1
+.IP \-\-line
 The
-.B \-l
-(long form
 .B \-\-line
-)
+(short form
+.BR \-l )
 option causes
 .B eddie
 to process input line at a time. The
 .I expr
-will be run on each line and the results concatenated together.
+will be run on each line and the results concatenated together
+(using unlines).
 .RS
 .PP
 The command
@@ -71,32 +76,101 @@
 .PP
 .B eddie
 "unlines . map
+.IR expr  .
+lines"
+.I file ...
+.
+.RE 1
+.IP \-\-file
+The
+.B \-\-file
+(short form
+.BR \-f )
+causes each file to be processed individually.
+.RS
+.PP
+Note that the
+.B \-l
+and
+.B \-f
+options cannot be used together.
+.RE 1
+.IP \-\-list
+The
+.B \-\-list
+(short form
+.BR \-L )
+option causes
+.B eddie
+to pass the expression a list of lines or files.
+This requires one of the
+.B \-l
+or
+.B \-f
+options be used.
+.RS
+.PP
+The command
+.RS
+.PP
+.B eddie \-lL
+.I expr file ...
+.RE
+.PP
+is equivalent to the command
+.RS
+.PP
+.B eddie
+"unlines .
 .I expr
 \&. lines"
 .I file ...
 .
 .RE 1
-.IP \-m
+.IP \-\-binary
 The
-.BI \-m name
-(long form
-.BI \-\-module name
-)
+.B \-\-binary
+(short form
+.BR \-b )
+option switches the output mode to binary.
+It also disables the maybe newline processing.
+Normally 
+.B eddie
+will output a final newline if the last character
+of the input is not a newline to place the prompt properly.
+If the
+.B \-b
+option is used, the final newline will not be output.
+.RE 1
+.IP \-\-modules
+The
+.B \-\-modules
+.I name
+(short form
+.B \-m 
+.IR name )
 option is used to import module
 .I name
 into haskell before evaluating the expression.
-.IP \-M
+.IP \-\-Modules
 The
-.BI \-M name,as
-(long form
-.BI \-\-Modules name,as
-)
+.B \-\-Modules
+.I name,as
+(short form
+.B \-M
+.IR name,as )
 option imports the
 .I name
 module using a qualified import with an as clause,
 with 
 .I as
 being the value for the as clause.
+.SH DIAGNOSTICS
+If the command runs with no problems, eddie will exit with a status of 0.
+If there are problems with the options, such that the expression is never
+evaluated, then eddie will exit with a status of 1.
+If haskell returns an error - probably a compilation problem - then
+eddie will exit with the status of 2.
 .SH SEE ALSO
 The wiki at <http://code.google.com/p/eddie/w/list> for examples.
 .SH BUGS
diff --git a/eddie.cabal b/eddie.cabal
--- a/eddie.cabal
+++ b/eddie.cabal
@@ -1,5 +1,5 @@
 Name:                eddie
-Version:             0.2
+Version:             0.5
 Synopsis:	     Command line file filtering with haskell
 Description:	     A tool to let you use short haskell expressions to filter
 		     files at the command line.
@@ -12,7 +12,7 @@
 Build-type:          Simple
 Homepage:	     http://eddie.googlecode.com/
 Cabal-version:       >=1.8
-Extra-source-files:  test.sh eddie.1
+Extra-source-files:  eddie.1 tests/*.test tests/*.data
 
 Executable eddie
     Main-is:         eddie.hs
diff --git a/eddie.hs b/eddie.hs
--- a/eddie.hs
+++ b/eddie.hs
@@ -1,76 +1,103 @@
+#!/usr/bin/env runghc
 {-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE GADTs #-}
 
 -- import Data.ByteString.Lazy (readFile)
 import Language.Haskell.Interpreter
   (setImportsQ, interpret, runInterpreter, as, MonadInterpreter)
 import Data.IORef (newIORef, readIORef, writeIORef)
 import System.Environment (getArgs, getProgName)
-import System.IO (openFile, openBinaryFile, hClose, stdin, IOMode (ReadMode), 
-                  hIsEOF, hGetChar, Handle, hSetBinaryMode, stdout)
+import System.IO
+       (openFile, openBinaryFile, hClose, stdin, IOMode (ReadMode), hIsEOF,
+        hGetChar, Handle, hSetBinaryMode, stdout, stderr, hPutStrLn)
 import System.IO.Unsafe (unsafeInterleaveIO)
 import System.Console.CmdArgs.Implicit
   (cmdArgs, (&=), Data, Typeable, help, explicit, name, args, summary, program,
    details)
+import System.Exit (exitWith, ExitCode (..))
 import Control.Exception (finally, evaluate)
-import Control.Monad     (when)
+import Control.Monad     (when, liftM, join)
 
 main = do
   myName <- getProgName
   opts <- cmdArgs (eddie &= program myName)
   case parseOpts opts of
-    Left e -> putStrLn $ "usage: " ++ myName ++ " " ++ e
+    Left e -> do hPutStrLn stderr $ "usage: " ++ myName ++ " " ++ e
+                 exitWith (ExitFailure 1)
     Right o -> runIt myName o
       where runIt name opts = do
-              fun <- runInterpreter $ makeFun opts
               let opener = if binary opts then openBinaryFile else openFile
-              let outputFunction = if binary opts then putStr else putStrLn
-              when (binary opts) $
-                hSetBinaryMode stdout True
+              let outputFunc = putStrMaybeLn (binary opts)
+              when (binary opts) $ hSetBinaryMode stdout True
+              fun <- runInterpreter $ makeFun opts
               case fun of
-                Left e -> putStrLn $ name ++ ": Error: " ++ show e
-                Right f -> withFiles (files opts) opener (outputFunction . f)
+                Left e -> do hPutStrLn stderr $ name ++ ": Error: " ++ show e
+                             exitWith (ExitFailure 2)
+                Right f -> withFiles (files opts) opener (outputFunc . f)
 
-makeFun :: MonadInterpreter m => Eddie -> m (String->String)
+putStrMaybeLn :: Bool -> String -> IO ()
+putStrMaybeLn binary val = 
+  (if binary || last val == '\n' then putStr else putStrLn) val
+
+
+makeFun :: MonadInterpreter m => Eddie -> m ([String] -> String)
 makeFun opts = do
   setImportsQ (asModules opts)
-  fun <- interpret (head (expr opts)) (as :: String -> String)
-  return $ if line opts then unlines . map fun . lines else fun
+  eval (head (expr opts)) mode
+    where mode | line opts && list opts = Mode (as :: [String] -> [String]) id
+                                               (lines . concat) unlines
+               | line opts = Mode (as :: String -> String) map 
+                                  (lines . concat) unlines
+               | file opts && list opts = Mode (as :: [String] -> [String]) id 
+                                               id concat
+               | file opts = Mode (as :: String -> String) map id concat
+               | otherwise = Mode (as :: String -> String) id concat id
 
+
+data Mode where
+    Mode :: (Typeable a, Typeable b)
+         => (a -> b)              -- witness to `interpret`
+         -> ((a -> b) -> c -> d)  -- application of interpreted function
+         -> ([String] -> c)       -- preprocess
+         -> (d -> String)         -- postprocess
+         -> Mode
+
+eval :: MonadInterpreter m => String -> Mode -> m ([String] -> String)
+eval s (Mode f t bra ket) = liftM ((ket .) . (. bra) . t) $ interpret s f
+
 -- an even lazier version of  withFile (courtesy of Heinrich Apfelmus)
--- Tweaked by mwm so withFiles uses stdin if [FilePath] is an empty list
--- Further tweaked by mwm to allow caller to specify opening function.
-withFile' :: Maybe FilePath -> (FilePath -> IOMode -> IO Handle) ->
-             (String -> IO a) -> IO a
-withFile' name opener f = do
+-- Tweaked by mwm to accept a handle instead of a file so caller can
+-- use favorite opening function or pass in stdin.
+withFile :: IO Handle -> (String -> IO a) -> IO a
+withFile ih f = do
     fin <- newIORef (return ())
-    let
-        close = readIORef fin >>= id
+    h <- ih
+    let close = join (readIORef fin)
         open  = do
-          h <- maybe (return stdin) (flip opener ReadMode) name
           writeIORef fin (hClose h)
           lazyRead h
     finally (unsafeInterleaveIO open >>= f >>= evaluate) close
 
-    where
-    lazyRead h = hIsEOF h >>= \b ->
-        if b
+    where lazyRead h = hIsEOF h >>= \b ->
+            if b
             then do hClose h; return []
-            else do
-                c  <- hGetChar h
-                cs <- unsafeInterleaveIO $ lazyRead h
-                return (c:cs)
+            else do c  <- hGetChar h
+                    cs <- unsafeInterleaveIO $ lazyRead h
+                    return (c:cs)
 
 
 withFiles :: [FilePath] -> (FilePath -> IOMode -> IO Handle) -> 
-             (String -> IO a) -> IO a
-withFiles []     o f = withFile' Nothing o f
-withFiles [x]    o f = withFile' (Just x) o f
-withFiles (x:xs) o f = withFile' (Just x) o $ \s ->
-    let f' t = f (s ++ t) in withFiles xs o f'
+             ([String] -> IO a) -> IO a
+withFiles []     o f = withFile (return stdin) (f . (:[]))
+withFiles [x]    o f = withFile (o x ReadMode) (f . (:[]))
+withFiles (x:xs) o f = withFile (o x ReadMode) $ \s ->
+    let f' t = f (s:t) in withFiles xs o f'
 
 
 -- argument processing
 data Eddie = Eddie { line :: Bool,
+                     file :: Bool,
+                     list :: Bool,
                      binary :: Bool,
                      expr :: [String],
                      files :: [String],
@@ -88,18 +115,22 @@
         (_, _) -> unlines es:fs
       mods = zip (modules opts) (repeat Nothing) ++ asModules opts
   in
-   if e == "" then Left $ unlines ["[options] (-e expr | expr) [files ...]",
+   if e == "" || (file opts && null fs') || (file opts && line opts)
+              || (list opts && not (file opts || line opts))
+   then Left $ unlines ["[options] (-e expr | expr) [files ...]",
                                    "--help for options"]
    else Right $ opts {expr = [e], asModules = mods, files = fs' }
 
    
-eddie = Eddie {line = False &= help "Process one line at a time",
+eddie = Eddie {line = False &= help "Process one line at a time (conflicts with --file)",
+               file = False &= help "Process files individually (requires at least one file name)",
+               list  = False &= help "Process the list of files/lines (requires --line or --file)" &= name "L",
                binary = False &= help "Process a binary file",
-               expr = [] &= help "Line of expression to evaluate",
+               expr = [] &= help "Line of expression to evaluate" &= name "e",
                modules = ["Prelude", "Data.List", "Data.Char"]
                          &= help "Modules to import for expr",
                asModules = [] &= help "Modules to import qualified" &= explicit
                            &= name "M" &= name "Modules",
                files = [] &= args} 
-        &= summary "eddie 0.2" &= details ["Haskell for shell scripts."]
+        &= summary "eddie 0.5" &= details ["Haskell for shell scripts."]
  
diff --git a/test.sh b/test.sh
deleted file mode 100644
--- a/test.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/sh
-
-# $1 is args to use with eddie.hs to process a file, $2 is a shell command to
-# run the same process. Run them both on our test file.
-# If the outputs differ, report failure of $3, including output unless $4
-# is present.
-test () {
-   TEST=$(eval runghc eddie.hs $1 < eddie.hs)
-   EXPECTED=$(eval "cat eddie.hs | $2")
-   if [ "$EXPECTED" != "$TEST" ]
-   then
-       FAILED="yes"
-       echo -e
-       if [ -n "$4" ]
-       then
-	   echo $3 failed "(output supressed)"
-       else
-	   echo $3 failed: expected $EXPECTED, got $TEST
-       fi
-   fi
-   echo -n .
-}       
-
-FAILED="no"
-test "" "echo 'usage: eddie.hs [options] (-e expr | expr) [files ...]'; echo '--help for options'" "No arguments test"
-test "show.length" "wc -c | xargs echo" "simple char count"
-test "show.length.lines" "wc -l | xargs echo" "line count"
-test "show.length.words" "wc -w | xargs echo " "word count"
-test "-e show.length" "wc -c | xargs echo" "test -e option"
-test "-e 'let taker = take 10 in' -e unlines.taker.lines" head "double -e option test"
-test "-l reverse" "rev eddie.hs" "test -l option" X
-test "-m Text.Printf 'unlines . (zipWith (printf \"%6d\t%s\") [(1::Int) ..]) . lines'" 'cat -n' 'test -m option' X
-test "-M Text.Printf,P 'unlines . (zipWith (P.printf \"%6d\t%s\") [(1::Int) ..]) . lines'" 'cat -n' 'test -M option' X
-echo -e
-
-if [ $FAILED = "yes" ]
-then
-    exit 1
-else
-    exit 0
-fi
-
diff --git a/tests/counters.test b/tests/counters.test
new file mode 100644
--- /dev/null
+++ b/tests/counters.test
@@ -0,0 +1,23 @@
+eddie show.length
+<<<
+asdf
+>>>
+5
+>>>= 0
+
+eddie show.length.lines
+<<<
+1
+2
+3
+4
+>>>
+4
+>>>= 0
+
+eddie show.length.words
+<<<
+now is the time for all good programmers to come to haskell
+>>>
+12
+>>>= 0
diff --git a/tests/error.test b/tests/error.test
new file mode 100644
--- /dev/null
+++ b/tests/error.test
@@ -0,0 +1,3 @@
+eddie the.lines
+>>>2 /Error: /
+>>>= 2
diff --git a/tests/expr.test b/tests/expr.test
new file mode 100644
--- /dev/null
+++ b/tests/expr.test
@@ -0,0 +1,27 @@
+eddie -e show.length.words
+<<<
+now is the time for all good programmers to come to haskell
+>>>
+12
+>>>= 0
+
+eddie -e "let taker = take 4 in" -e unlines.taker.lines
+<<<
+now
+is
+the
+time
+for
+all
+good
+programmers
+to
+come
+to
+haskell.
+>>>
+now
+is
+the
+time
+>>>= 0
diff --git a/tests/files.test b/tests/files.test
new file mode 100644
--- /dev/null
+++ b/tests/files.test
@@ -0,0 +1,9 @@
+eddie -fL "(:[]).show.length" tests/test.data tests/test.data
+>>>
+2
+>>>= 0
+
+eddie -f "show.length" tests/test.data tests/test.data
+>>>
+6161
+>>>= 0
diff --git a/tests/imports.test b/tests/imports.test
new file mode 100644
--- /dev/null
+++ b/tests/imports.test
@@ -0,0 +1,17 @@
+eddie -m GHC.Exts the.lines
+<<<
+aaaa
+aaaa
+aaaa
+>>>
+aaaa
+>>>= 0
+
+eddie -M GHC.Exts,E E.the.lines
+<<<
+aaaa
+aaaa
+aaaa
+>>>
+aaaa
+>>>= 0
diff --git a/tests/lines.test b/tests/lines.test
new file mode 100644
--- /dev/null
+++ b/tests/lines.test
@@ -0,0 +1,27 @@
+eddie -l reverse
+<<<
+now
+is
+the
+time
+>>>
+won
+si
+eht
+emit
+>>>= 0
+
+eddie -lL "take 4"
+<<<
+now
+is
+the
+time
+for
+all
+>>>
+now
+is
+the
+time
+>>>= 0
diff --git a/tests/test.data b/tests/test.data
new file mode 100644
--- /dev/null
+++ b/tests/test.data
@@ -0,0 +1,12 @@
+now
+is
+the
+time
+for
+all
+good
+programmers
+to
+come
+to
+haskell.
diff --git a/tests/usage.test b/tests/usage.test
new file mode 100644
--- /dev/null
+++ b/tests/usage.test
@@ -0,0 +1,11 @@
+eddie
+>>>2 /usage:/
+>>>= 1
+
+eddie -lf reverse
+>>>2 /usage:/
+>>>= 1
+
+eddie -L reverse
+>>>2 /usage:/
+>>>= 1
