diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Omari Norman
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * 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.
+
+    * Neither the name of Omari Norman nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+OWNER 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.
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/rewrite.cabal b/rewrite.cabal
new file mode 100644
--- /dev/null
+++ b/rewrite.cabal
@@ -0,0 +1,37 @@
+-- Initial rewrite.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                rewrite
+version:             0.2
+synopsis:            open file and rewrite it with new contents
+description:
+  In the Unix shell there is no easy way to use a filter program
+  to change the contents of a file in-place. For example, if you
+  want to sort a file in place, this will not work:
+  .
+  sort < myfile > myfile
+  .
+  All that will get you is an empty myfile. This package
+  gives you the rewrite program, so this will work:
+  .
+  rewrite myfile sort
+
+homepage:            http://www.github.com/massysett/rewrite
+license:             BSD3
+license-file:        LICENSE
+author:              Omari Norman
+maintainer:          omari@smileystation.com
+copyright:           2013 Omari Norman
+category:            System
+build-type:          Simple
+cabal-version:       >=1.8
+
+executable rewrite
+  main-is: rewrite.hs
+  build-depends:
+      base ==4.6.*
+    , multiarg ==0.16.*
+    , temporary ==1.1.*
+    , process ==1.1.*
+    , directory ==1.2.*
+  ghc-options: -Wall
diff --git a/rewrite.hs b/rewrite.hs
new file mode 100644
--- /dev/null
+++ b/rewrite.hs
@@ -0,0 +1,112 @@
+module Main (main) where
+
+import Data.Either (partitionEithers)
+import qualified System.Console.MultiArg as MA
+import qualified System.IO as IO
+import System.IO.Temp (withSystemTempDirectory)
+import qualified System.Exit as Exit
+import qualified System.Directory as D
+import qualified System.Process as P
+
+help :: String -> String
+help pn = unlines
+  [ "usage: " ++ pn ++ "[options] FILE PROGRAM [program options]"
+  , "opens given FILE and feeds it to standard input of PROGRAM"
+  , "and writes PROGRAM output back to FILE."
+  , ""
+  , "Aborts if PROGRAM exits with non-zero exit status."
+  , ""
+  , "Options:"
+  , "  -h, --help          Show help and exit"
+  , "  -b, --backup SUFFIX Back up FILE to file with given SUFFIX"
+  , "                      before writing new file"
+  ]
+
+type Backup = String
+type ProgramOpt = String
+type ProgName = String
+type InputFile = String
+
+errExit :: String -> IO a
+errExit msg = do
+  pn <- MA.getProgName
+  IO.hPutStrLn IO.stderr $ pn ++ ": error: " ++ msg
+  Exit.exitFailure
+
+-- | Parses command line arguments; returns whether to do backup and
+-- positional arguments
+parseArgs :: IO (Maybe Backup, InputFile, ProgName, [ProgramOpt])
+parseArgs = do
+  let opts = [ MA.OptSpec ["backup"] "b" (MA.OneArg Left) ]
+  as <- MA.simpleWithHelp help MA.StopOptions opts (return . Right)
+  let (baks, os) = partitionEithers as
+  bak <- case baks of
+    [] -> return Nothing
+    x:[] -> if null x
+            then errExit "empty backup suffix given"
+            else return $ Just x
+    _ -> errExit "multiple backup suffixes given"
+  (input, name, progOpts) <- case os of
+    [] -> errExit "no input file or program name given"
+    _:[] -> errExit "no program name given"
+    x:y:xs -> return (x, y, xs)
+  return (bak, input, name, progOpts)
+
+
+doBackup :: InputFile -> Backup -> IO ()
+doBackup inf bak = D.copyFile inf (inf ++ "." ++ bak)
+
+
+runProgram
+  :: Maybe Backup
+
+  -> InputFile
+  -- ^ Name of input file
+
+  -> ProgName
+
+  -> [ProgramOpt]
+
+  -> FilePath
+  -- ^ Temporary directory
+
+  -> IO ()
+runProgram mayBak inFile pn opts tempPath =
+  let outPath = tempPath ++ "/output"
+  in IO.withFile outPath IO.WriteMode $ \outHandle ->
+     IO.withFile inFile IO.ReadMode $ \inHandle -> do
+      writeReadme tempPath
+      let cp = P.CreateProcess
+            { P.cmdspec = P.RawCommand pn opts
+            , P.cwd = Nothing
+            , P.env = Nothing
+            , P.std_in = P.UseHandle inHandle
+            , P.std_out = P.UseHandle outHandle
+            , P.std_err = P.Inherit
+            , P.close_fds = False
+            , P.create_group = False }
+      (_, _, _, procHndle) <- P.createProcess cp
+      code <- P.waitForProcess procHndle
+      _ <- case code of
+        Exit.ExitSuccess -> return ()
+        Exit.ExitFailure bad ->
+          errExit $ "program " ++ pn ++ " exited with code "
+                    ++ show bad
+      _ <- case mayBak of
+        Nothing -> return ()
+        Just bak -> doBackup inFile bak
+      D.copyFile outPath inFile
+
+
+writeReadme
+  :: FilePath
+  -- ^ Temporary directory
+  -> IO ()
+
+writeReadme fp = writeFile (fp ++ "/README")
+  "This directory created by the rewrite program."
+
+main :: IO ()
+main = do
+  (mayBak, inf, pn, opts) <- parseArgs
+  withSystemTempDirectory "rewrite" $ runProgram mayBak inf pn opts
