diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+CopyrightJP Moresmau (c) 2016
+
+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 JP Moresmau 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/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,42 @@
+-- | application code
+module Main where
+
+import System.Console.GetOpt
+import Language.Haskell.DataFilesGen
+import System.Environment
+import qualified Data.Set as S
+import Data.Default
+
+-- | Application entry point
+main :: IO ()
+main = do
+  args <- getArgs
+  (opts,fps) <- parseOpts args
+  res <- mapM (generateList opts) fps
+  let ind = replicate (optIndent opts) ' '
+  mapM_ (\s->putStrLn (ind ++ s)) $ concat res
+
+-- | Parse options
+parseOpts :: [String] -> IO (Options,[String])
+parseOpts argv =
+   case getOpt Permute options argv of
+      (o,n,[]  ) -> return (foldl (flip id) def o, n)
+      (_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options))
+  where header = "Usage: data-files-gen [OPTION...] dir"
+
+-- | Option description
+options :: [OptDescr (Options -> Options)]
+options =  [
+    Option ['d']     ["dir"]
+    (ReqArg (\f opts -> opts { optExcludeDirs = S.insert f $ optExcludeDirs opts })
+              "DIR")
+      "directory name to exclude"
+    ,Option ['e']     ["ext"]
+    (ReqArg (\f opts -> opts { optExcludeExts = S.insert ("."++f) $ optExcludeExts opts })
+              "EXT")
+      "extension to exclude"
+    ,Option ['i']     ["indent"]
+    (ReqArg (\i opts -> opts { optIndent = read i })
+              "INT")
+      "number of spaces to use for indentation"
+  ]
diff --git a/data-files-gen.cabal b/data-files-gen.cabal
new file mode 100644
--- /dev/null
+++ b/data-files-gen.cabal
@@ -0,0 +1,50 @@
+name:                data-files-gen
+version:             0.0.0.1
+synopsis:            Generate data-files Cabal file field from existing files
+description:         Please see README.md
+homepage:            https://github.com/JPMoresmau/data-files-gen#readme
+license:             BSD3
+license-file:        LICENSE
+author:              JP Moresmau
+maintainer:          jp@moresmau.fr
+copyright:           2016 JP Moresmau
+category:            Tools
+build-type:          Simple
+-- extra-source-files:
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Language.Haskell.DataFilesGen
+  build-depends:       base >= 4.7 && < 5
+                     , containers
+                     , directory
+                     , filepath
+                     , data-default
+  default-language:    Haskell2010
+
+executable data-files-gen
+  hs-source-dirs:      app
+  main-is:             Main.hs
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  build-depends:       base
+                     , data-files-gen
+                     , containers
+                     , data-default
+  default-language:    Haskell2010
+
+test-suite data-files-gen-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  build-depends:       base
+                     , data-files-gen
+                     , hspec
+                     , data-default
+                     , containers
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/JPMoresmau/data-files-gen
diff --git a/src/Language/Haskell/DataFilesGen.hs b/src/Language/Haskell/DataFilesGen.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/Haskell/DataFilesGen.hs
@@ -0,0 +1,52 @@
+-- | Library module
+module Language.Haskell.DataFilesGen where
+
+import Control.Monad
+import System.Directory
+import System.FilePath
+import qualified Data.Set as S
+import Data.List (isPrefixOf, sort)
+import Data.Default
+
+-- | Options for processing
+data Options = Options
+ { optExcludeDirs :: S.Set String -- ^ Directory names to exclude
+ , optExcludeExts :: S.Set String -- ^ Extensions to exclude
+ , optIndent :: Int               -- ^ Number of spaces to indent
+ } deriving (Read,Show,Eq,Ord)
+
+-- | Default instance, excluding nothing
+instance Default Options where
+  def = Options S.empty S.empty 0
+
+-- | Generate the list of matching expressions
+generateList :: Options -> FilePath -> IO [String]
+generateList opts root = do
+  fs <- getDirectoryContents root
+  let vis = filter isVisible fs
+  (exts,foldrs,files) <- foldM collect (S.empty,S.empty,S.empty) vis
+  subs <- mapM (generateList opts) $ S.toList foldrs
+  let fullExts = map (\x -> root </> ("*"++x)) $ S.toList exts
+  return $ (S.toAscList files) ++ (sort fullExts) ++ ( sort $ concat subs)
+  where
+    isVisible :: FilePath -> Bool
+    isVisible fn
+      | fn == "." = False
+      | fn == ".." = False
+      | "." `isPrefixOf` fn = False
+      | otherwise = True
+    collect :: (S.Set String,S.Set FilePath,S.Set FilePath) -> FilePath -> IO (S.Set String,S.Set FilePath,S.Set FilePath)
+    collect (exts,fldrs,files) fp = do
+      let full = root </> fp
+      fex <- doesFileExist full
+      return $ if fex
+                then let ext = takeExtensions fp
+                     in if null ext
+                        then (exts,fldrs,S.insert full files)
+                        else
+                          if ext `S.member` optExcludeExts opts
+                            then (exts,fldrs,files)
+                            else (S.insert ext exts,fldrs,files)
+                else if fp `S.member` optExcludeDirs opts
+                        then (exts,fldrs,files)
+                        else (exts,S.insert full fldrs,files)
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
