diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2011 Mike Meyer
+
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND 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 AUTHOR 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.
+
+The views and conclusions contained in the software and documentation are
+those of the authors and should not be interpreted as representing official
+policies, either expressed or implied, of Mike Meyer.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/eddie.1 b/eddie.1
new file mode 100644
--- /dev/null
+++ b/eddie.1
@@ -0,0 +1,105 @@
+.TH eddie 1
+.SH NAME
+eddie \- run haskell filters from the command line
+.SH SYNOPSYS
+.B eddie 
+[
+.I options
+]
+[
+.I expr
+]
+.I file ...
+.SH DESCRIPTION
+.B eddie
+evalutes the provided
+.I expression
+on either the contents of the
+.I file
+arguments concatenated together,
+or standard input if no file arguments are present.
+.PP
+If the
+.B \-l
+option is used, the 
+.I expression
+is used to process each line instead of the entire contents.
+.PP
+The prelude, Data.List and Data.Char modules are available for building
+expressions. Other modules may be added with the
+.B \-M
+and
+.B \-m
+options.
+.SH OPTIONS
+.IP \-e
+The
+.BI \-e\  expr
+(long form
+.BI \-\-expr\  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 ""
+If no
+.BI \-e\  expr
+option is present, the first non-flag argument will be used for
+the haskell expression.
+.IP \-l
+The
+.B \-l
+(long form
+.B \-\-line
+)
+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.
+.RS
+.PP
+The command
+.RS
+.PP
+.B eddie \-l
+.I expr file ...
+.RE
+.PP
+is equivalent to the command
+.RS
+.PP
+.B eddie
+"unlines . map
+.I expr
+\&. lines"
+.I file ...
+.
+.RE 1
+.IP \-m
+The
+.BI \-m name
+(long form
+.BI \-\-module name
+)
+option is used to import module
+.I name
+into haskell before evaluating the expression.
+.IP \-M
+The
+.BI \-M name,as
+(long form
+.BI \-\-Modules 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 SEE ALSO
+The wiki at <http://code.google.com/p/eddie/w/list> for examples.
+.SH BUGS
+See the issues list at <http://code.google.com/p/eddie/issues/list>.
+.SH AUTHOR
+Mike Meyer (mwm@mired.org)
diff --git a/eddie.cabal b/eddie.cabal
new file mode 100644
--- /dev/null
+++ b/eddie.cabal
@@ -0,0 +1,23 @@
+Name:                eddie
+Version:             0.1
+Synopsis:	     Command line file filtering with haskell
+Description:	     A tool to let you use short haskell expressions to filter
+		     files at the command line.
+Category:	     CLI tool
+License:             BSD3
+License-file:	     LICENSE
+Author:              Mike Meyer
+Copyright:	     (c) 2011 Mike Meyer
+Maintainer:          mwm@mired.org
+Build-type:          Simple
+Homepage:	     http://eddie.googlecode.com/
+Cabal-version:       >=1.8
+Extra-source-files:  test.sh eddie.1
+
+Executable eddie
+    Main-is:         eddie.hs
+    Build-Depends:   hint >= 0.3.3.2, base >= 4.3.1 && < 5, cmdargs >= 0.7
+
+Source-repository head
+    Type: hg
+    Location: http://eddie.googlecode.com/hg/
diff --git a/eddie.hs b/eddie.hs
new file mode 100644
--- /dev/null
+++ b/eddie.hs
@@ -0,0 +1,83 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+
+-- import Data.ByteString.Lazy (readFile)
+import Language.Haskell.Interpreter
+  (setImportsQ, interpret, runInterpreter, as, MonadInterpreter)
+import Data.List (intercalate)
+import Data.IORef (newIORef, readIORef, writeIORef)
+import System.Environment (getArgs, getProgName)
+import System.IO (openFile, hClose, stdin, IOMode (ReadMode), hIsEOF, hGetChar, withFile)
+import System.IO.Unsafe (unsafeInterleaveIO)
+import System.Console.CmdArgs.Implicit
+  (cmdArgs, (&=), Data, Typeable, help, explicit, name, args, summary, program,
+   details)
+import Control.Exception (finally, evaluate)
+
+main = do
+  myname <- getProgName
+  opts <- cmdArgs (eddie &= program myname)
+  let opts' = parseOpts opts
+  fun <- runInterpreter $ makeFun opts'
+  case fun of
+    Left e -> putStrLn $ myname ++ ": Error: " ++ show e
+    Right f -> withFiles  (files opts') (putStrLn . f)
+
+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
+
+-- an even lazier version of  withFile (courtesy of Heinrich Apfelmus)
+-- Tweaked by mwm so withFiles uses stdin if [FilePath] is an empty list
+withFile' :: Maybe FilePath -> (String -> IO a) -> IO a
+withFile' name f = do
+    fin <- newIORef (return ())
+    let
+        close = readIORef fin >>= id
+        open  = do
+          h <- maybe (return stdin) (flip openFile ReadMode) name
+          writeIORef fin (hClose h)
+          lazyRead h
+    finally (unsafeInterleaveIO open >>= f >>= evaluate) close
+
+    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)
+
+
+withFiles :: [FilePath] -> (String -> IO a) -> IO a
+withFiles []     f = withFile' Nothing f
+withFiles [x]    f = withFile' (Just x) f
+withFiles (x:xs) f = withFile' (Just x) $ \s ->
+    let f' t = f (s ++ t) in withFiles xs f'
+
+
+-- argument processing
+data Eddie = Eddie { line :: Bool,
+                     expr :: [String],
+                     files :: [String],
+                     modules :: [String],
+                     asModules :: [(String, Maybe String)]
+                   } deriving (Show, Data, Typeable)
+
+parseOpts :: Eddie -> Eddie
+parseOpts opts = opts {expr = [e], asModules = mods, files = fs' }
+  where es = expr opts
+        fs = files opts
+        e:fs' = if null es then fs else intercalate "\n" es:fs
+        mods = zip (modules opts) (repeat Nothing) ++ asModules opts
+   
+eddie = Eddie {line = False &= help "Process line at a time.",
+               expr = [] &= help "Line of expression to evaluate.",
+               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.1" &= details ["Haskell for shell scripts."]
diff --git a/test.sh b/test.sh
new file mode 100644
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,41 @@
+#!/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 "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 /etc/motd" "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
+
