diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2014 Daniel Choi
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Main.hs b/Main.hs
new file mode 100644
--- /dev/null
+++ b/Main.hs
@@ -0,0 +1,177 @@
+{-# LANGUAGE RecordWildCards, ScopedTypeVariables #-} 
+module Main where
+import Data.List 
+import Data.Monoid
+import Text.Printf
+import Options.Applicative
+import Data.List.Split (splitOn)
+import Control.Applicative
+import Data.Char (isDigit, isSpace)
+import Control.Monad (when)
+import System.Process (readProcess)
+import qualified Text.CSV as CSV
+
+
+data Options = Options {
+    splitter :: String -> [String]   -- delimiter splitting function
+  , csvInput :: Bool
+  , suppressRowDividers :: Bool 
+  , printHeaderDivider :: Bool 
+  , maxWidth :: Int
+  } 
+
+parseOptions :: Parser Options
+parseOptions = Options
+  <$> (setDelimiter <|> whiteSpaceDelimiter <|> pure (splitOn "\t"))
+  <*> switch (short 'c' <> help "Parse input as CSV")
+  <*> switch (short 'R' <> help "Don't print row dividers")
+  <*> switch (short 'H' <> help "Print header row divider")
+  <*> parseMaxWidth
+
+setDelimiter = 
+   splitOn <$> strOption (metavar "DELIM" <> short 'd' <> help "Input field delimiter. Default is TAB (\\t).")
+
+whiteSpaceDelimiter = flag' 
+    words
+    (short 's' <> help "Use any run of whitespace as input field delimiter")
+
+parseMaxWidth = read <$> strOption (
+      value "0" <> short 'w' <> metavar "WIDTH" 
+      <> help "Max table width. Defaults to value of `tput cols` command.")
+
+opts = info (helper <*> parseOptions)
+            (fullDesc 
+              <> progDesc "Pretty format TSV input into table with aligned and wrapped cells"
+              <> header "table"
+              <> footer "https://github.com/danchoi/table")
+
+main = do 
+  Options {..} <- execParser opts
+  s <- getContents 
+  let rawRows = if csvInput 
+                then case CSV.parseCSV "" s of
+                      Left err -> error $ "Error: " ++ show err
+                      Right xs' -> stripBlankRows xs'
+                else map splitter . lines $ s 
+  let initialWidths = getCellWidths rawRows
+  -- Adjust the max width subtract padding for gutters on side and ' | ' between cells
+  maxWidth' <- do 
+      if maxWidth == 0
+      then (read <$> readProcess "tput" ["cols"] [])
+      else (return maxWidth)
+  let adjMaxWidth = maxWidth' - 2 - ((length initialWidths - 1) * 3)
+  let adjustedWidths = adjustWidths adjMaxWidth initialWidths
+  let rows = mkCells adjustedWidths rawRows
+  mapM_ (\(n, row) -> do
+      when (not suppressRowDividers || (printHeaderDivider && n == 1)) $ 
+         putStrLn $ printDivider 1 $ map width row
+      putStrLn . printRow 1 $ row 
+    ) $ zip [0..] rows
+  when (not suppressRowDividers) $ 
+    putStrLn $ printDivider 1 $ map width (head rows)
+
+
+stripBlankRows :: [[String]] -> [[String]]
+stripBlankRows xs = [x | x <- xs, all (> 0) $ map length x ]
+
+adjustWidths :: Int -> [Int] -> [Int]
+adjustWidths maxWidth xs | sum xs <= maxWidth = xs
+                    | otherwise     = adjustWidths maxWidth $ reduceWidest xs
+
+reduceWidest :: [Int] -> [Int]
+reduceWidest xs = let m = maximum xs
+                  in [ if x == m then x - 1 else x | x <- xs ]
+
+data Cell = Cell {
+    content :: [String]
+  , width :: Int
+  , height :: Int 
+  , isNumeric :: Bool
+  } deriving (Show)
+
+-- | get initial column widths
+getCellWidths :: [[String]] -> [Int]
+getCellWidths rows = map (maximum . map length) . transpose $ rows
+
+{- Each row is represented as Cell, which contains dimension information. The
+first value is the text content; the second is the normalized of the column
+width for that cell.  -}
+
+mkCells :: [Int] -> [[String]] -> [[Cell]]
+mkCells columnWidths rows = 
+    let cols     = transpose rows
+        colCells = map (\(width, cell) -> addCellDimensions width cell) $ zip columnWidths cols 
+    in transpose colCells
+
+{- Input is a column of strings. Wraps data in a Cell, which adds width and
+height to each cell in a column, and also a flag if the column looks numeric,
+which determines the alignment.  Also wraps stings to max cell width -} 
+
+addCellDimensions :: Int -> [String] -> [Cell]
+addCellDimensions maxWidth xs = 
+  let w = min (maximum . map length $ xs) maxWidth
+      xs' = map (wrapString w) xs       -- wrapped string content
+      numeric = all (all isDigit) (if length xs > 1 then (tail xs) else xs)
+  in map (\lines -> Cell lines w (length lines) numeric) xs'
+
+wrapString :: Int -> String -> [String]
+wrapString maxWidth x = map trim . wrapLine maxWidth $ x
+
+-- | prints a row of cells with dividers
+--   gutter is the width of the blank space at left and right of table
+printRow :: Int -> [Cell] -> String
+printRow gutter xs = 
+    let rowHeight = maximum $ map height xs
+        subcells :: [[String]]
+        subcells = map content xs
+        lines = map (\n -> 
+                       let ss :: [String] 
+                           ss = map (cellLine n) xs
+                       in formatRow ss)
+                    [0..(rowHeight - 1)] 
+    in mconcat $ intersperse "\n" $ lines
+  where formatRow :: [String] -> String
+        formatRow ss = 
+            mconcat [margin gutter ' ' , (intercalate " | " ss) , margin gutter ' '] 
+
+-- prints the nth line of a cell 
+cellLine :: Int -> Cell -> String
+cellLine n Cell {..} = 
+      if n < length content 
+      then printf fmt (content !! n)  
+      else printf fmt ""
+    where fmt | isNumeric = "%" ++ show width ++ "s"
+              | otherwise = "%-" ++ show width ++ "s"
+
+margin :: Int -> Char -> String
+margin w c = take w $ repeat c
+
+printDivider :: Int -> [Int] -> String
+printDivider gutter widths = 
+    mconcat [margin gutter '-'
+      , (intercalate "-+-" 
+        $ map (\w -> take w $ repeat '-') widths)
+      , margin gutter '-']
+
+
+------------------------------------------------------------------------
+-- Word wrapping
+-- taken from http://moreindirection.blogspot.com/2010/08/blog-post.html
+
+
+trim :: String -> String
+trim = trimAndReverse . trimAndReverse
+  where trimAndReverse = reverse . dropWhile isSpace
+
+reverseBreak :: (a -> Bool) -> [a] -> ([a], [a])
+reverseBreak f xs = (reverse before, reverse after)
+  where (after, before) = break f $ reverse xs
+
+wrapLine :: Int -> String -> [String]
+wrapLine maxLen line 
+  | length line <= maxLen  = [line]
+  | any isSpace beforeMax  = beforeSpace : (wrapLine maxLen $ afterSpace ++ afterMax)
+  | otherwise              = beforeMax : wrapLine maxLen afterMax
+    where (beforeMax, afterMax) = splitAt maxLen line
+          (beforeSpace, afterSpace) = reverseBreak isSpace beforeMax
+
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,88 @@
+# table
+
+Formats lines of TSV, CSV, or DSV (delimiter-separated values) into a pretty
+plain text table, wrappings cells with long content to try to fit the table in
+the screen.
+
+## Usage 
+
+```
+table
+
+Usage: table ([-d DELIM] | [-s]) [-c] [-R] [-H] [-w WIDTH]
+  Pretty format TSV input into table with aligned and wrapped cells
+
+Available options:
+  -h,--help                Show this help text
+  -d DELIM                 Input field delimiter. Default is TAB (\t).
+  -s                       Use any run of whitespace as input field delimiter
+  -c                       Parse input as CSV
+  -R                       Don't print row dividers
+  -H                       Print header row divider
+  -w WIDTH                 Max table width. Defaults to value of `tput cols`
+                           command.
+
+https://github.com/danchoi/table
+```
+
+## Example
+
+Format Rails issues from the GitHub API into a table. This example uses
+[jq](http://stedolan.github.io/jq/) and
+[jsontsv](https://github.com/danchoi/jsontsv) to transform the GitHub API JSON
+into tab separated values before piping that data into `table`:
+
+```bash
+curl -s "https://api.github.com/repos/rails/rails/issues" | 
+jq -M '.[]' | 
+jsontsv -H 'number title user.login:user state' |
+table
+```
+
+Outputs this when the terminal width is 72 characters: 
+
+```
+--------+--------------------------------------+----------------+-------
+ number | title                                | user           | state 
+--------+--------------------------------------+----------------+-------
+  18290 | Updating guides for Rails 5.0        | lucascaton     | open  
+--------+--------------------------------------+----------------+-------
+  18288 | Add --skip-action-mailer (or -M) to  | claudiob       | open  
+        | rails generate                       |                |       
+--------+--------------------------------------+----------------+-------
+  18287 | Rails 4.2.0: Scaffold generator      | dldinternet    | open  
+        | with --helper=false produced an      |                |       
+        | error                                |                |       
+--------+--------------------------------------+----------------+-------
+  18285 | undefined method 'clear' for         | nPn-           | open  
+        | nil:NilClass in                      |                |       
+        | actionpack-4.2.0/lib/action_controll |                |       
+        | er/test_case.rb                      |                |       
+--------+--------------------------------------+----------------+-------
+  18283 | Prefer `array?` rather than `array`  | kamipo         | open  
+--------+--------------------------------------+----------------+-------
+  18279 | Refactoring of add_constraints in    | eileencodes    | open  
+        | AssociationScope                     |                |       
+--------+--------------------------------------+----------------+-------
+  18273 | Rails 3.2.21 (4.x too?):             | jensb          | open  
+        | attribute_will_change! with symbol   |                |       
+        | parameter causes "multiple           |                |       
+        | assignment" error in Postgres        |                |       
+--------+--------------------------------------+----------------+-------
+  18257 | ActionView::Helpers::TranslationHelp | jcoyne         | open  
+        | er.translate makes unsafe values     |                |       
+        | html_safe                            |                |       
+...
+```
+
+You can use `table` to pretty-format TSV output for `mysql`, `psql`, or
+`sqlite3` as well, especially when you need row cell content to wrap to fit
+everything nicely on the screen.
+
+## Author
+
+* Daniel Choi <https://github.com/danchoi>
+
+## License
+
+MIT License
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/input b/input
new file mode 100644
--- /dev/null
+++ b/input
@@ -0,0 +1,4 @@
+id fruit
+1 apple
+2 banana
+3 yogurt
diff --git a/table.cabal b/table.cabal
new file mode 100644
--- /dev/null
+++ b/table.cabal
@@ -0,0 +1,29 @@
+-- Initial table.cabal generated by cabal init.  For further documentation,
+--  see http://haskell.org/cabal/users-guide/
+
+name:                table
+version:             0.1.0.0
+synopsis:            Simple tool to generate tables from DSV input
+-- description:         
+homepage:            https://github.com/danchoi/table
+license:             MIT
+license-file:        LICENSE
+author:              Daniel Choi
+maintainer:          dhchoi@gmail.com
+-- copyright:           
+category:            Text
+build-type:          Simple
+-- extra-source-files:  
+cabal-version:       >=1.10
+
+executable table
+  main-is:             Main.hs
+  -- other-modules:       
+  -- other-extensions:    
+  build-depends:       base >=4.6 && <4.8
+                     , optparse-applicative
+                     , split >=0.2
+                     , process
+                     , csv >= 0.1.0
+  -- hs-source-dirs:      
+  default-language:    Haskell2010
