diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c)2010, John MacFarlane
+
+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 John MacFarlane 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.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+> import Distribution.Simple
+> main = defaultMain
diff --git a/hsb2hs.cabal b/hsb2hs.cabal
new file mode 100644
--- /dev/null
+++ b/hsb2hs.cabal
@@ -0,0 +1,25 @@
+Name:         hsb2hs
+Version:      0.1
+Synopsis:     Preprocesses a file, adding blobs from files as string literals.
+Description:
+    hsb2hs is a preprocessor that allows you to include the contents of
+    files as string literals in your Haskell programs and libraries.
+    It is an alternative to file-embed for those who do not want to rely
+    on Template Haskell.  See @README.markdown@ for instructions.
+
+License:      BSD3
+License-file: LICENSE
+Author:       John MacFarlane
+Maintainer:   jgm@berkeley.edu
+Copyright:    Copyright (C) 2013 John MacFarlane
+Category:     Development
+Cabal-version:  >=1.6
+Build-type:   Simple
+Source-repository head
+  type:              git
+  location:          git://github.com/jgm/hsb2hs.git
+Executable hsb2hs
+    Main-is: hsb2hs.hs
+    Ghc-Options: -Wall
+    Build-depends: base >= 4 && < 5, containers, bytestring, directory,
+                   filepath, preprocessor-tools > 0.1 && < 0.2
diff --git a/hsb2hs.hs b/hsb2hs.hs
new file mode 100644
--- /dev/null
+++ b/hsb2hs.hs
@@ -0,0 +1,60 @@
+module Main
+where
+import Language.Haskell.Preprocessor
+import System.IO.Unsafe (unsafePerformIO)
+
+import qualified Data.ByteString as B
+import System.Directory
+import System.Environment (getArgs)
+import Control.Arrow ((&&&), second)
+import Control.Applicative ((<$>))
+import Control.Monad
+import System.FilePath
+
+main :: IO ()
+main = do
+  args <- getArgs
+  transform blobExtension args
+
+blobExtension :: Extension
+blobExtension = base{
+    transformer = processBlobs
+}
+
+processBlobs :: [Ast] -> [Ast]
+processBlobs [] = []
+processBlobs (Single (Token Operator _ l "%") :
+              Single (Token Variable _ _ kw) :
+              Single (Token StringLit _ _ lit) :
+              xs) | kw == "blobs" || kw == "blob" =
+  (Single (Token StringLit [] l (getLiteral $ stripQuotes lit))) :
+     processBlobs xs
+   where stripQuotes = reverse . stripLeadingQuote . reverse . stripLeadingQuote
+         stripLeadingQuote ('"':ys) = ys
+         stripLeadingQuote ys = ys
+         getLiteral f = unsafePerformIO $ if kw == "blob"
+                                             then show `fmap` B.readFile f
+                                             else show `fmap` fileList' f ""
+processBlobs (x:xs) =
+  (case x of
+      Single tok -> Single tok
+      Block i l b r n -> Block i l (processBlobs b) r n
+      Empty      -> Empty) : processBlobs xs
+
+-- fileList' is taken from Michael Snoyman's file-embed
+fileList' :: FilePath -> FilePath -> IO [(FilePath, B.ByteString)]
+fileList' realTop top = do
+    allContents <- filter notHidden <$> getDirectoryContents (realTop </> top)
+    let all' = map ((top </>) &&& (\x -> realTop </> top </> x)) allContents
+    files <- filterM (doesFileExist . snd) all' >>=
+             mapM (liftPair2 . second B.readFile)
+    dirs <- filterM (doesDirectoryExist . snd) all' >>=
+            mapM (fileList' realTop . fst)
+    return $ concat $ files : dirs
+
+notHidden :: FilePath -> Bool
+notHidden ('.':_) = False
+notHidden _ = True
+
+liftPair2 :: Monad m => (a, m b) -> m (a, b)
+liftPair2 (a, b) = b >>= \b' -> return (a, b')
