diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2018, Thomas Eding
+
+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 Thomas Eding 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/src/Main.hs b/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,12 @@
+module Main (
+    main
+) where
+
+
+import qualified Tabs
+
+
+main :: IO ()
+main = Tabs.main
+
+
diff --git a/src/Tabs.hs b/src/Tabs.hs
new file mode 100644
--- /dev/null
+++ b/src/Tabs.hs
@@ -0,0 +1,75 @@
+module Tabs (
+    main
+) where
+
+
+import Data.Char (isSpace, toLower)
+import Data.List (partition)
+import Data.Tagged
+import System.Environment (getArgs)
+import System.FilePath (takeExtension)
+import System.IO
+import Text.Indent
+
+
+main :: IO ()
+main = do
+  args <- getArgs
+  let (opts, files) = partition ((== "-") . take 1) args
+  if any (`elem` ["-h", "--help"]) opts
+    then help
+    else if null files
+        then byStdin runIndent
+        else mapM_ (byFile runIndent) files
+
+
+help :: IO ()
+help = mapM_ putStrLn [
+    "Usage: myindent [options] files"
+  , "Description: Indents files based on filetype."
+  ]
+
+
+data IndentType
+    = HcCodeGen
+
+
+byStdin :: (IndentType -> String -> String) -> IO ()
+byStdin f = do
+    contents <- getContents
+    putStrLn $ f HcCodeGen contents
+
+
+byFile :: (IndentType -> String -> String) -> FilePath -> IO ()
+byFile f file = do
+    contents <- readFile file
+    mIndentType <- case map toLower $ drop 1 $ takeExtension file of
+        "" -> return Nothing
+        "c" -> return $ Just HcCodeGen
+        "cpp" -> return $ Just HcCodeGen
+        _ -> do
+            hPutStrLn stderr $ "Uknown filetype: Skipping " ++ file
+            return Nothing
+    case mIndentType of
+        Nothing -> return ()
+        Just indentType -> do
+            let newContents = f indentType contents
+            length contents `seq` writeFile file newContents
+
+
+type Indent a = String -> Tagged a String
+
+
+runIndent :: IndentType -> String -> String
+runIndent indentType = case indentType of
+    HcCodeGen -> untag . (indent DropOldTabs :: Indent CodeGen)
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Text/Indent.hs b/src/Text/Indent.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Indent.hs
@@ -0,0 +1,10 @@
+module Text.Indent (
+      module Text.Indent.Class
+    , module Text.Indent.Type.CodeGen
+    ) where
+
+
+import Text.Indent.Class
+import Text.Indent.Type.CodeGen
+
+
diff --git a/src/Text/Indent/Class.hs b/src/Text/Indent/Class.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Indent/Class.hs
@@ -0,0 +1,17 @@
+module Text.Indent.Class (
+      Indenter(..)
+    , IndentMode(..)
+    , Tagged
+    ) where
+
+
+import Data.Tagged
+
+
+data IndentMode = DropOldTabs | KeepOldTabs
+
+
+class Indenter a where
+    indent :: IndentMode -> String -> Tagged a String
+
+
diff --git a/src/Text/Indent/Type/CodeGen.hs b/src/Text/Indent/Type/CodeGen.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Indent/Type/CodeGen.hs
@@ -0,0 +1,141 @@
+{-# LANGUAGE EmptyDataDecls #-}
+{-# LANGUAGE ViewPatterns #-}
+
+module Text.Indent.Type.CodeGen (
+    CodeGen
+) where
+
+
+import Control.Arrow
+import Control.Monad.ListM
+import Control.Monad.State.Strict
+import Data.Char
+import Data.List
+import Data.Maybe
+import Data.Tagged
+import Text.Indent.Class
+
+
+data CodeGen
+
+
+type IndentAmount = Int
+type Line = String
+
+
+data IndentItem
+    = Open
+    | Begin
+    | Brace
+    deriving (Show, Eq)
+
+
+data MalformedStack = MalformedStack
+
+
+data IndentState = IndentState {
+    indentAmount :: IndentAmount,
+    itemStack :: Either MalformedStack [IndentItem]
+}
+
+
+initState :: IndentState
+initState = IndentState {
+    indentAmount = 0,
+    itemStack = Right [] }
+
+
+instance Indenter CodeGen where
+    indent mode = Tagged . unlines . flip evalState initState . mapM (tabify . wsOp) . lines
+        where
+            wsOp = case mode of
+                DropOldTabs -> dropWs
+                KeepOldTabs -> id
+            tabify line = do
+                newTabAmount <- calculateTabs $ dropWs line
+                return $ replicate newTabAmount '\t' ++ line
+
+
+dropWs :: String -> String
+dropWs = dropWhile isSpace
+
+
+lastNonWs :: String -> Maybe Char
+lastNonWs s = case dropWs $ reverse s of
+    [] -> Nothing
+    c : _ -> Just c
+
+
+calculateTabs :: Line -> State IndentState IndentAmount
+calculateTabs line = case line of
+    (stripPrefix "DEFINE" -> Just (stripPrefix "(" . dropWs -> Just rest)) -> calculateTabs rest
+    (isPrefixOf "HC_Open_" -> True) -> do
+        n <- gets indentAmount
+        push Open
+        return n
+    (isPrefixOf "HC_KOpen_" -> True) -> do
+        n <- gets indentAmount
+        push Open
+        return n
+    (isPrefixOf "HC_Close_" -> True) -> do
+        pop Open
+        gets indentAmount
+    (isPrefixOf "HC_Begin_" -> True) -> do
+        n <- gets indentAmount
+        push Begin
+        return n
+    (isPrefixOf "HC_End_" -> True) -> do
+        pop Begin
+        gets indentAmount
+    (lastNonWs -> Just '{') -> do
+        n <- gets indentAmount
+        push Brace
+        return n
+    (isPrefixOf "}" -> True) -> do
+        popTill Brace
+        pop Brace
+        gets indentAmount
+    _ -> gets indentAmount
+
+
+topItem :: State IndentState (Maybe IndentItem)
+topItem = gets $ either (const Nothing) listToMaybe . itemStack
+
+
+push :: IndentItem -> State IndentState ()
+push item = do
+    modify $ \st -> st { itemStack = fmap (item :) $ itemStack st }
+    modify $ \st -> st { indentAmount = indentAmount st + 1 }
+
+
+pop :: IndentItem -> State IndentState ()
+pop item = do
+    mItem <- topItem
+    if Just item == mItem
+        then do
+            modify $ \st -> st { itemStack = fmap tail $ itemStack st }
+            modify $ \st -> st { indentAmount = indentAmount st - 1 }
+        else modify $ \st -> st { itemStack = Left MalformedStack }
+
+
+popTill :: IndentItem -> State IndentState ()
+popTill destItem = do
+    mItem <- topItem
+    case mItem of
+        Nothing -> return ()
+        Just item -> if item == destItem
+            then return ()
+            else do
+                pop item
+                popTill destItem
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tabs.cabal b/tabs.cabal
new file mode 100644
--- /dev/null
+++ b/tabs.cabal
@@ -0,0 +1,42 @@
+-- Initial indent.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                tabs
+version:             0.1.0.0
+synopsis:            Indents source files
+-- description:         
+license:             BSD3
+license-file:        LICENSE
+author:              Thomas Eding
+maintainer:          thomasedingcode@gmail.com
+-- copyright:           
+-- category:            
+build-type:          Simple
+cabal-version:       >=1.8
+
+
+library
+    build-depends:
+        base >=4.10 && <4.11, tagged >=0.8 && <0.9, filepath >=1.4 && <1.5, monadlist >=0.0 && <0.1, mtl >=2.2 && <2.3
+    hs-source-dirs:
+        src
+    exposed-modules:
+        Text.Indent
+        Text.Indent.Class
+        Text.Indent.Type.CodeGen
+
+executable tabs
+    main-is:
+        Main.hs
+    build-depends:
+        base >=4.10 && <4.11, tagged >=0.8 && <0.9, filepath >=1.4 && <1.5, monadlist >=0.0 && <0.1, mtl >=2.2 && <2.3
+    hs-source-dirs:
+        src
+    other-modules:
+        Tabs
+        Text.Indent
+        Text.Indent.Class
+        Text.Indent.Type.CodeGen
+
+
+
