diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright 2013, Ingolf Wagner
+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.
+ 
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND THE 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 THE
+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,6 @@
+module Main (main) where
+
+import Distribution.Simple
+
+main :: IO ()
+main = defaultMain
diff --git a/csv-to-qif.cabal b/csv-to-qif.cabal
new file mode 100644
--- /dev/null
+++ b/csv-to-qif.cabal
@@ -0,0 +1,49 @@
+name: csv-to-qif
+version: 0.3
+cabal-version: >=1.10
+build-type: Simple
+license: BSD3
+license-file: LICENSE
+copyright: (c) Ingolf Wagner
+maintainer: Ingolf Wagner <palipalo9@googlemail.com>
+stability: stable
+homepage: https://github.com/mrVanDalo/csv-to-qif/
+bug-reports: mailto:palipalo9@gmail.com
+synopsis: A small program that will read csv files and create qif files
+description: Binary to convert a wide range of csv files to qif files.
+            The main target is to read them into GnuCash.
+category: Console, Text
+author: Ingolf Wagner
+data-dir: ""
+
+source-repository head
+    type: git
+    location: https://github.com/mrVanDalo/csv-to-qif
+
+flag threaded
+    Default: False
+
+executable to-qif
+    build-depends: base >=4.2 && <5, split >=0.2.2, regex-tdfa >= 1.2.0,
+                   spreadsheet >= 0.1.3, explicit-exception >= 0.1.7
+
+    if flag(threaded)
+        buildable: True
+        ghc-options: -threaded
+    main-is: Main.hs
+    buildable: True
+    default-language: Haskell2010
+    hs-source-dirs: src main
+    other-modules: Main Parser QifOptions Qifer
+
+test-suite tester
+    build-depends: base >=4.2 && <5, split >=0.2.2, regex-tdfa >= 1.2.0,
+                   Cabal >=1.9.2, hspec >=1.8
+    type: exitcode-stdio-1.0
+    main-is: Tester.hs
+    buildable: True
+    default-language: Haskell2010
+    default-extensions: OverloadedStrings
+    hs-source-dirs: src test
+    other-modules: Parser
+    ghc-options: -Wall
diff --git a/main/Main.hs b/main/Main.hs
new file mode 100644
--- /dev/null
+++ b/main/Main.hs
@@ -0,0 +1,120 @@
+-----------------------------------------------------------------------------
+--
+-- Module      :  Main
+-- Copyright   :  (c) Ingolf Wagner
+-- License     :  BSD3
+--
+-- Maintainer  :  Ingolf Wagner <palipalo9@googlemail.com>
+-- Stability   :  unstable
+-- Portability :
+--
+-- |
+--
+-----------------------------------------------------------------------------
+
+module Main (main) where
+
+import QifOptions
+import Qifer
+import Parser
+import Data.List.Split
+
+import System.Environment
+import System.Console.GetOpt
+import System.IO
+import Data.Maybe
+import System.Exit
+
+
+
+
+
+-- | the famous main method
+main = do
+    args <- getArgs
+
+    -- Parse options, getting a list of option actions
+    let (actions, nonOptions, errors) = getOpt RequireOrder options args
+
+    -- Here we thread startOptions through all supplied option actions
+    opts <- foldl (>>=) (return startOptions) actions
+
+    let Options { optInput    = input
+                , optOutput   = output   } = opts
+
+    checkArguments opts
+    let rules = rule opts
+
+    parseResult <- parseCSVFromFile (optSkip opts) (optInput opts) (optSeparator opts)
+    case parseResult of
+        Left error -> do
+            hPutStrLn stderr $ error
+            exitFailure
+        Right csv  -> do
+
+            let actions     = toTransactions rules csv
+
+            updatedActions  <- case (optUpdater opts) of
+                    Nothing   ->
+                        return $ actions
+                    Just file -> do
+                        updaterConfig <- readUpdaterFile file
+                        return $ update updaterConfig actions
+
+            let qif = transToQif updatedActions
+
+            withFile (optOutput opts) WriteMode (\h ->
+                mapM_ ( hPutStrLn h) qif)
+
+
+-- | reads updater file
+readUpdaterFile :: String -> IO [(String,String)]
+readUpdaterFile fileName = do
+    content <- readFile fileName
+    return $ tupler . onlyTuple . splitter . lines $ content
+    where
+        tupler    = map (\(x:(y:[])) -> (x,y))
+        onlyTuple = filter (\l -> length l == 2)
+        splitter  = map (\l -> splitOn "<->" l)
+
+
+
+
+-- | this function is unsafe call it after checkArguments
+-- @todo : make me safer
+rule :: Options -> Rule
+rule opts = Rule {
+    dateField = fromJust $ optDate opts
+    , balanceField = fromJust $ optBalance opts
+    , textField = optLongText opts
+    , descField = optText opts
+    }
+
+-- | checks input arguments for minimum of configuration
+checkArguments :: Options -> IO ()
+checkArguments opts = do
+    case errors of
+        [] ->
+            return ()
+        _  -> do
+            mapM_ putStrLn  errors
+            exitFailure
+            return ()
+    where
+        errors = catMaybes list
+        list =  [ (check optDate "need date column")
+                , (check optBalance "need balance column")
+                , (checkL optText "need text columns")
+                , (checkL optLongText "need long text columns")
+                , (checkL optInput "need input file")
+                ]
+        check getter text = case (getter opts) of
+                                Nothing -> Just text
+                                Just _  -> Nothing
+        checkL getter text = case (getter opts) of
+                                []      -> Just text
+                                _       -> Nothing
+
+
+
+
diff --git a/main/QifOptions.hs b/main/QifOptions.hs
new file mode 100644
--- /dev/null
+++ b/main/QifOptions.hs
@@ -0,0 +1,148 @@
+-----------------------------------------------------------------------------
+--
+-- Module      :  QifOptions
+-- Copyright   :  (c) Ingolf Wagner
+-- License     :  BSD3
+--
+-- Maintainer  :  Ingolf Wagner <palipalo9@googlemail.com>
+-- Stability   :  unstable
+-- Portability :
+--
+-- |
+--
+-----------------------------------------------------------------------------
+
+module QifOptions where
+
+import System.Console.GetOpt
+import System.Exit
+import System.IO
+import System.Environment
+import Control.Monad
+import Text.Read
+import Data.List.Split
+import Data.Maybe
+
+
+
+data Options = Options  { optVerbose    :: Bool
+                        , optInput      :: String
+                        , optOutput     :: String
+                        , optDate       :: Maybe Int
+                        , optBalance    :: Maybe Int
+                        , optText       :: [Int]
+                        , optLongText   :: [Int]
+                        , optSkip       :: Int
+                        , optSeparator  :: Char
+                        , optUpdater    :: Maybe String
+                        } deriving (Show)
+
+startOptions :: Options
+startOptions = Options  { optVerbose    = False
+                        , optInput      = ""
+                        , optOutput     = ""
+                        , optDate       = Nothing
+                        , optBalance    = Nothing
+                        , optText       = []
+                        , optLongText   = []
+                        , optSkip       = 0
+                        , optSeparator  = ','
+                        , optUpdater    = Nothing
+                        }
+
+--instance Show (a -> b) where
+--    show _ = "<function>"
+
+instance Show (IO a) where
+    show _ = "<IoString>"
+
+
+options :: [ OptDescr (Options -> IO Options) ]
+options =
+    [ Option "i" ["input"]
+        (ReqArg
+            (\arg opt -> return opt { optInput = arg })
+            "FILE")
+        "Input file (CSV Format)"
+
+    , Option "o" ["output"]
+        (ReqArg
+            (\arg opt -> return opt { optOutput = arg })
+            "FILE")
+        "Output file (Qif Format)"
+
+    , Option "u" ["updater"]
+        (ReqArg
+            (\arg opt -> return opt { optUpdater = Just arg })
+            "FILE")
+        "Updater file format is `match`<->`replacement`"
+    , Option "z" ["separator"]
+        (ReqArg
+            (\arg opt -> return opt { optSeparator = head arg })
+            "char")
+        "separator of the csv file"
+
+    , Option "d" ["date"]
+        (ReqArg
+            (\arg opt -> return opt { optDate = readColumn arg})
+            "<column number>")
+        "Date Column"
+    , Option "b" ["balance"]
+        (ReqArg
+            (\arg opt -> return opt { optBalance = readColumn arg})
+            "<column number>")
+        "Balance Column"
+    , Option "t" ["text"]
+        (ReqArg
+            (\arg opt -> return opt { optText = readColumns arg})
+            "<column numbers>")
+        "Text Columns"
+    , Option "l" ["longtext"]
+        (ReqArg
+            (\arg opt -> return opt { optLongText = readColumns arg})
+            "<column numbers>")
+        "Long Text Columns"
+    , Option "s" ["skip"]
+        (ReqArg
+            (\arg opt -> case (readColumn arg) of
+                Nothing -> return opt
+                Just r  -> return opt { optSkip = r} )
+            "<number of rows>")
+        "Rows to Skip before reading"
+
+    , Option "v" ["verbose"]
+        (NoArg
+            (\opt -> return opt { optVerbose = True }))
+        "Enable verbose messages"
+
+    , Option "V" ["version"]
+        (NoArg
+            (\_ -> do
+                hPutStrLn stderr "Version 0.2"
+                exitWith ExitSuccess))
+        "Print version"
+
+    , Option "h" ["help"]
+        (NoArg
+            (\_ -> do
+                prg <- getProgName
+                hPutStrLn stderr (usageInfo (useage prg) options)
+                exitWith ExitSuccess))
+        "Show help"
+    ]
+
+useage :: String -> String
+useage prog = prog ++ "\n\n" ++
+    "author : Ingolf Wagner\n\n" ++
+    header ++ "\n\n" ++
+    information ++ "\n\n"
+    where   header      = "Converting CSV files to Qif Files"
+            information = "all column numbers start at 0!"
+
+
+readColumn :: String -> Maybe Int
+readColumn input = readMaybe input
+
+readColumns :: String -> [Int]
+readColumns input = catMaybes $ map readColumn $ splitOn "," input
+
diff --git a/src/Parser.hs b/src/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Parser.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+-----------------------------------------------------------------------------
+--
+-- Module      :  Parser
+-- Copyright   :  (c) Ingolf Wagner
+-- License     :  BSD3
+--
+-- Maintainer  :  Ingolf Wagner <palipalo9@googlemail.com>
+-- Stability   :  unstable
+-- Portability :
+--
+-- | This package should create a parser for csv files.
+-- it's just a wrapper
+--
+-----------------------------------------------------------------------------
+
+module Parser where
+
+import Data.Char
+import Data.Spreadsheet
+import Control.Monad.Exception.Asynchronous.Lazy(Exceptional(..))
+import Data.List
+import System.IO.Error
+import System.IO
+
+import Qifer
+
+
+
+
+type ParseError = String
+
+-- | parse csv file to read transactions from it
+parseCSVFromFile :: Int -> String -> Char -> IO (Either ParseError CSV)
+parseCSVFromFile skip file separator = do
+    firstContent <- readFile file
+    let content           = unlines . separatorAtEndFix . drop skip . lines $ firstContent
+        separatorAtEndFix = map (\l -> l ++ " ")
+    case (fromString '\n' separator content) of
+        Exceptional (Just s) result -> do
+            hPutStrLn stderr s
+            return $ Right $ strip result
+        Exceptional Nothing result -> return $ Right $ strip result
+
+strip :: CSV -> CSV
+strip = map (\line -> map (\word -> stripWord word) line)
+    where   stripWord     = dropWhile dontNeed . dropWhileEnd dontNeed
+            dontNeed c    = (c == '"') || (isSpace c)
+
diff --git a/src/Qifer.hs b/src/Qifer.hs
new file mode 100644
--- /dev/null
+++ b/src/Qifer.hs
@@ -0,0 +1,91 @@
+-----------------------------------------------------------------------------
+--
+-- Module      :  Qifer
+-- Copyright   :  (c) Ingolf Wagner
+-- License     :  BSD3
+--
+-- Maintainer  :  Ingolf Wagner <palipalo9@googlemail.com>
+-- Stability   :  unstable
+-- Portability :
+--
+-- |
+--
+-----------------------------------------------------------------------------
+
+module Qifer where
+
+import Data.List
+import Text.Regex.TDFA
+
+type CSV = [Row]
+type Row = [Column]
+type Column = String
+
+data Transaction = Transaction { date :: String
+                                , description :: String
+                                , text :: String
+                                , balance :: String
+                               } deriving (Eq,Show)
+
+type Position = Int
+
+data Rule = Rule { dateField :: Position
+                   , descField :: [Position]
+                   , textField :: [Position]
+                   , balanceField :: Position
+                 } deriving (Show)
+
+highestPosition :: Rule -> Position
+highestPosition (Rule dF tF ltF bF) =
+    maximum . concat $ [[dF],tF,ltF,[bF]]
+
+toTransactions :: Rule -> CSV -> [Transaction]
+toTransactions _ []        = []
+toTransactions rule (c:sv)
+    | (highestPosition rule) >= (length c) = stepDeeper
+    | otherwise                            = transform : stepDeeper
+    where pick n     = (c !! (n rule))
+          pock n     = concat . intersperse " " . map (\k -> c !! k ) $ (n rule)
+          stepDeeper = toTransactions rule sv
+          transform  = (Transaction (pick dateField)
+                                    (pock descField)
+                                    (pock textField)
+                                    (pick balanceField))
+
+qifHeader :: String
+qifHeader = "!Type:Bank"
+
+toQif :: Transaction -> [String]
+toQif trans = ["P" ++ d, "T" ++ money, "D" ++ time, "M" ++ msg]
+    where   d     = (description trans)
+            money = (balance trans)
+            time  = (date trans)
+            msg   = (text trans)
+
+transToQif :: [Transaction] -> [String]
+transToQif trans = qifHeader : (foo trans)
+    where   foo []     = []
+            foo (t:ts) = (toQif t) ++ ["^"] ++ (foo ts)
+
+-- | updates a Transaction if regex works
+updateTransaction :: String -> String -> Transaction -> Transaction
+updateTransaction regex replacement transaction =
+    transaction { description = updateDesc }
+    where
+        updateDesc  = if matches then replacement else (description transaction)
+        matches     = (description transaction) =~ regex :: Bool
+
+-- | very inefficient but better than nothing
+update :: [(String,String)] -> [Transaction] -> [Transaction]
+update _ []         = []
+update regex (t:ts) = (updateSingle regex t) : (update regex ts)
+    where
+        updateSingle [] r                                 = r
+        updateSingle ((rgx,replacement):rest) transaction =
+            if updatedTrans == transaction
+            then
+                updateSingle rest transaction
+            else
+                updatedTrans
+            where
+                updatedTrans = updateTransaction rgx replacement transaction
diff --git a/test/Tester.hs b/test/Tester.hs
new file mode 100644
--- /dev/null
+++ b/test/Tester.hs
@@ -0,0 +1,51 @@
+-----------------------------------------------------------------------------
+--
+-- Module      :  Tester
+-- Copyright   :  (c) Ingolf Wagner
+-- License     :  BSD3
+--
+-- Maintainer  :  Ingolf Wagner <palipalo9@googlemail.com>
+-- Stability   :  unstable
+-- Portability :
+--
+-- |
+--
+-----------------------------------------------------------------------------
+
+module Main (main) where
+
+
+import Test.Hspec
+import Qifer
+
+main :: IO ()
+main = hspec $ do
+  describe "Csv" $ do
+    it "should transform CSV" $ do
+        let rule = (Rule 1 [2] [0,3] 4)
+            trans= (Transaction "2" "3" "1 4" "5")
+            csv  = [["1","2","3","4","5"]]
+        (toTransactions rule csv) `shouldBe` [trans]
+    it "should generate Qif" $ do
+        let trans = (Transaction "heute" "short" "long bla bla" "100$")
+            qif   = ["Pshort","T100$","Dheute","Mlong bla bla"]
+        (toQif trans) `shouldBe` qif
+    it "should generate Qif File content" $ do
+        let trans = (Transaction "heute" "short" "long bla bla" "100$")
+            qif   = ["!Type:Bank","Pshort","T100$","Dheute","Mlong bla bla","^"]
+        (transToQif [trans]) `shouldBe` qif
+    it "should update a transaction" $ do
+        let trans  = (Transaction "heute" "short" "long bla bla" "100$")
+            theUpdate = (Transaction "heute" "bam" "long bla bla" "100$")
+        (updateTransaction "ort" "bam" trans) `shouldBe` theUpdate
+        (updateTransaction "foo" "bam" trans) `shouldBe` trans
+    it "should update multiple transactions" $ do
+        let transA       = (Transaction "heute"  "short story long its .." "long bla bla" "100$")
+            transB       = (Transaction "morgen" "balla ball bam bam boom"   "long bla bla" "100$")
+            transA1      = (Transaction "heute"  "found story" "long bla bla" "100$")
+            transB1      = (Transaction "morgen" "found balla" "long bla bla" "100$")
+            regex        = [("story","found story"),("balla","found balla")]
+            transactions = [transA,transB]
+        (update regex transactions) `shouldBe` [transA1,transB1]
+
+
