diff --git a/Ansi2Html.cabal b/Ansi2Html.cabal
new file mode 100644
--- /dev/null
+++ b/Ansi2Html.cabal
@@ -0,0 +1,16 @@
+Name:                Ansi2Html
+Version:             0.1
+Synopsis:            Convert ANSI Terminal Sequences to nice HTML markup
+Description:	     Particularly with xterm in mind, this software enables integration of terminal screen state in html pages. See project homepage for hints on how to setup xterm for it.
+Homepage:            http://janzzstimmpfle.de/~jens/software/Ansi2Html/
+License:             BSD3
+License-file:        LICENSE
+Author:              Jens Stimpfle
+Maintainer:          jens@janzzstimmpfle.de
+Category:            Web
+Build-type:          Simple
+Extra-source-files:  ansi2html.css
+Cabal-version:       >=1.2
+Executable ansi2html
+  Main-is:           Ansi2Html.hs
+  Build-depends:     parsec, mtl, base < 5, xhtml
diff --git a/Ansi2Html.hs b/Ansi2Html.hs
new file mode 100644
--- /dev/null
+++ b/Ansi2Html.hs
@@ -0,0 +1,237 @@
+import Text.ParserCombinators.Parsec
+import System.Environment
+import System.IO
+import System.Exit
+import Control.Applicative ((<$>))
+import Data.Char
+import Data.Maybe
+import Text.XHtml as X
+import Control.Monad
+import Control.Monad.State as S
+
+-- the (Integer,Integer) state holds an (couner,columns) state
+type MyParser = GenParser Char (Integer,Integer)
+
+data ANSIEnv = AnsiEnv { ansiBrightOrBold :: Bool
+                       , ansiFaint :: Bool
+                       , ansiItalic :: Bool
+                       , ansiUnderline :: Bool
+                       , ansiReverse :: Bool
+                       , ansiFontNumber :: Int
+                       , ansiFramed :: Bool
+                       , ansiEncircled :: Bool
+                       , ansiOverlined :: Bool
+                       , ansiFGColor :: ANSIColor
+                       , ansiBGColor :: ANSIColor
+                       }
+
+defaultEnv :: ANSIEnv
+defaultEnv = AnsiEnv False False False False False 0 False False False AnsiWhite AnsiBlack
+
+modifyEnv :: ANSIEnv -> ANSIToken -> ANSIEnv
+modifyEnv env ctl = if (finalByte ctl /= 'm')
+    then env
+    else foldl (flip ($)) env modifiers
+ where modifiers = flip map (optParms ctl)
+        (\parm -> case parm of
+            0  -> \_ -> defaultEnv
+            1  -> \e -> e { ansiBrightOrBold = True }
+            2  -> \e -> e { ansiFaint = True }
+            3  -> \e -> e { ansiItalic = True }
+            4  -> \e -> e { ansiUnderline = True }
+            7  -> \e -> e { ansiReverse = True }
+            10 -> \e -> e { ansiFontNumber = 0 }
+            11 -> \e -> e { ansiFontNumber = 1 }
+            12 -> \e -> e { ansiFontNumber = 2 }
+            13 -> \e -> e { ansiFontNumber = 3 }
+            14 -> \e -> e { ansiFontNumber = 4 }
+            15 -> \e -> e { ansiFontNumber = 5 }
+            16 -> \e -> e { ansiFontNumber = 6 }
+            17 -> \e -> e { ansiFontNumber = 7 }
+            18 -> \e -> e { ansiFontNumber = 8 }
+            19 -> \e -> e { ansiFontNumber = 9 }
+            21 -> \e -> e { ansiBrightOrBold = False }
+            22 -> \e -> e { ansiBrightOrBold = False
+                          , ansiFaint = False
+                          }
+            23 -> \e -> e { ansiItalic = False }
+            24 -> \e -> e { ansiUnderline = False }
+            26 -> \e -> e { ansiUnderline = False }
+            27 -> \e -> e { ansiReverse = False }
+            30 -> \e -> e { ansiFGColor = AnsiBlack }
+            31 -> \e -> e { ansiFGColor = AnsiRed }
+            32 -> \e -> e { ansiFGColor = AnsiGreen }
+            33 -> \e -> e { ansiFGColor = AnsiYellow }
+            34 -> \e -> e { ansiFGColor = AnsiBlue }
+            35 -> \e -> e { ansiFGColor = AnsiMagenta }
+            36 -> \e -> e { ansiFGColor = AnsiCyan }
+            37 -> \e -> e { ansiFGColor = AnsiWhite }
+            40 -> \e -> e { ansiBGColor = AnsiBlack }
+            41 -> \e -> e { ansiBGColor = AnsiRed }
+            42 -> \e -> e { ansiBGColor = AnsiGreen }
+            43 -> \e -> e { ansiBGColor = AnsiYellow }
+            44 -> \e -> e { ansiBGColor = AnsiBlue }
+            45 -> \e -> e { ansiBGColor = AnsiMagenta }
+            46 -> \e -> e { ansiBGColor = AnsiCyan }
+            47 -> \e -> e { ansiBGColor = AnsiWhite }
+            51 -> \e -> e { ansiFramed = True }
+            52 -> \e -> e { ansiEncircled = True }
+            53 -> \e -> e { ansiOverlined = True }
+            54 -> \e -> e { ansiFramed = False
+                          , ansiEncircled = False
+                          }
+            55 -> \e -> e { ansiOverlined = False }
+            _  -> id)
+
+
+data ANSIColor = AnsiBlack
+               | AnsiRed
+               | AnsiGreen
+               | AnsiYellow
+               | AnsiBlue
+               | AnsiMagenta
+               | AnsiCyan
+               | AnsiWhite
+    deriving Eq
+
+envAttrs :: ANSIEnv -> [HtmlAttr]
+envAttrs e =
+    let r = ansiReverse env
+        env = reverseEnv r e
+        bob       = when' (ansiBrightOrBold env) ("bold")
+        italic    = when' (ansiItalic env) ("italic")
+        underline = when' (ansiUnderline env) ("underline")
+        fontnum   = when' (ansiFontNumber env /= 0) ("fontNum" ++ (show $ ansiFontNumber env))
+        framed    = when' (ansiFramed env) ("framed")
+        fgcolor   = when' (ansiFGColor env /= AnsiBlack) ("fg" ++ (show $ ansiFGColor env))
+        bgcolor   = when' (ansiBGColor env /= AnsiBlack) ("bg" ++ (show $ ansiBGColor env))
+        classes = map ("ansi2html-"++) $ catMaybes [bob, italic, underline, fontnum, framed, fgcolor, bgcolor]
+        joined = foldl (\a b -> if null a then b else a ++ " " ++ b) "" classes
+    in [theclass joined]
+    where
+        when' x y = if x then Just y else Nothing
+        -- TODO
+        reverseEnv = flip const
+
+instance Show ANSIColor where
+    show color = case color of
+        AnsiBlack       -> "black"
+        AnsiRed         -> "red"
+        AnsiGreen       -> "green"
+        AnsiYellow      -> "yellow"
+        AnsiBlue        -> "blue"
+        AnsiMagenta     -> "magenta"
+        AnsiCyan        -> "cyan"
+        AnsiWhite       -> "white"
+
+data ANSIToken = AnsiText String
+           | CtlSeq { privModeChars :: String
+                    , optParms      :: [Int]
+                    , interMedChars :: String
+                    , finalByte     :: Char
+                    }
+    deriving Show
+
+ansiParser :: MyParser [ANSIToken]
+ansiParser = do
+    result <- many ansiToken
+    eof
+    return result
+
+ansiToken :: MyParser ANSIToken
+ansiToken = do
+    {- xterm prints a strange file with each line prefixed by '\ESC#5'
+     - Let's simply remove those prefixes -}
+    try (string "\ESC#5") <|> return ""
+    try ctlSeq <|> ansiText
+
+ansiText :: MyParser ANSIToken
+ansiText = do
+    strings <- many1 nonControlChar
+    column <- getState
+    return $ AnsiText $ concat strings
+
+nonControlChar :: MyParser String
+nonControlChar = do
+    -- filter out \CR
+    try (char '\CR') <|> return ' '
+    (counter,columns) <- getState
+    r <- satisfy (/='\ESC')
+    implicitbreak <- if counter == columns && r /= '\n'
+                        then do
+                            updateState (mapFst (const 0))
+                            return "\n"
+                        else return ""
+    rr <- if (r == '\n')
+     then do
+        updateState (mapFst (const 0))
+        return $ replicate (fromInteger (columns - counter)) ' ' ++ "\n"
+     else do
+        updateState (mapFst (+1))
+        return [r]
+    return $ implicitbreak ++ rr
+    where mapFst f (a,b) = (f a,b)
+
+ctlSeq :: MyParser ANSIToken
+ctlSeq = do
+    csiCode
+    parms <- number `sepBy` char ';'
+    iMBs <- interMedBytes
+    l <- letter
+    return $ CtlSeq "" parms iMBs l
+
+number  :: MyParser Int
+number  = do{ ds <- many1 digit
+            ; return (read ds)
+            }
+        <?> "number"
+
+interMedBytes :: MyParser String
+interMedBytes = return ""
+
+csiCode :: MyParser ()
+csiCode = do
+    escapeChar
+    char '['
+    return ()
+
+escapeChar :: MyParser ()
+escapeChar = do
+    char '\ESC'
+    return ()
+
+ansiToken2Html :: ANSIToken -> S.State ANSIEnv X.Html
+ansiToken2Html tok = do
+    env <- S.get
+    case tok of
+        AnsiText t -> let html = (linesToHtml $ lines t) +++ (if last t == '\n' then X.br else X.noHtml)
+                        in return $ (X.thespan ! envAttrs env) << html
+        ctlseq     -> do
+                        S.put $ modifyEnv env ctlseq
+                        return X.noHtml
+
+ansi2Html :: [ANSIToken] -> X.Html
+ansi2Html toks = foldl (+++) noHtml htmls
+    where htmls = evalState (mapM ansiToken2Html toks) defaultEnv
+
+makeHtml :: X.Html -> X.Html
+makeHtml content = X.thespan ! [theclass "ansi2html"] << content
+
+printUsage :: IO ()
+printUsage = do
+    hPutStrLn stderr "Usage: ansi2html <columns>"
+    hFlush stderr
+
+main = do
+    args <- getArgs
+    columns <- case args of
+        []    -> return 80
+        x:xs  -> case reads x :: [(Integer,String)] of
+                    [(c,[])]    -> return c
+                    _           -> printUsage >> exitFailure
+    contents <- getContents
+    tokens <- case runParser ansiParser (0,columns) "" contents of
+                Left err -> do
+                        error $ "parse error at " ++ show err
+                Right x -> return x
+    print . makeHtml $ ansi2Html tokens
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c)2011, Jens Stimpfle
+
+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 Jens Stimpfle 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/ansi2html.css b/ansi2html.css
new file mode 100644
--- /dev/null
+++ b/ansi2html.css
@@ -0,0 +1,56 @@
+span.ansi2html {
+	font-family: monospace;
+	background-color: black;
+}
+
+/* foreground colors */
+.ansi2html-fgblack {
+	color: #000;
+}
+.ansi2html-fgcyan {
+	color: #00cdcd;
+}
+.ansi2html-fgblue {
+	color: #0000ee;
+}
+.ansi2html-fggreen {
+	color: #00cd00;
+}
+.ansi2html-fgmagenta {
+	color: #cd00cd;
+}
+.ansi2html-fgred {
+	color: #cd0000;
+}
+.ansi2html-fgwhite {
+	color: #e5e5e5;
+}
+.ansi2html-fgyellow {
+	color: #cdcd00;
+}
+
+/* Background background-colors */
+.ansi2html-bgblack {
+	background-color: #000;
+}
+.ansi2html-bgcyan {
+	background-color: #00cdcd;
+}
+.ansi2html-bgblue {
+	background-color: #0000ee;
+}
+.ansi2html-bggreen {
+	background-color: #00cd00;
+}
+.ansi2html-bgmagenta {
+	background-color: #cd00cd;
+}
+.ansi2html-bgred {
+	background-color: #cd0000;
+}
+.ansi2html-bgwhite {
+	background-color: #e5e5e5;
+}
+.ansi2html-bgyellow {
+	background-color: #cdcd00;
+}
