diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,32 @@
+Copyright (c) 2010, Sebastian Fischer
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+ 2. 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.
+
+ 3. Neither the name of the author nor the names of his 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 AUTHORS 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/README.markdown b/README.markdown
new file mode 100644
--- /dev/null
+++ b/README.markdown
@@ -0,0 +1,330 @@
+barchart is a command-line program for generating bar charts from CSV
+files. It has special support for creating charts from data generated
+by the Haskell benchmarking tools [criterion] and
+[progression]. barchart can create PNG, SVG, PDF, and PS files using
+the Haskell [Diagrams] library for rendering and, hence, depends on a
+Haskell binding to Cairo which is part of [Gtk2Hs].
+
+# installation
+
+available soon. be patient.
+
+# usage
+
+Bar charts can be created from CSV files using the `barchart`
+command-line utility. For example, if you want to track how many hours
+you practice playing the guitar on each day of the week, you can
+create a file `guitar.csv` with the following contents:
+
+    Mon,1.2
+    Tue,0.3
+    Wed,2.1
+    Thu,0.9
+    Fri,1.1
+    Sat,3.2
+    Sun,3.1
+
+The call
+
+    # barchart --title="Practice hours" guitar.csv 
+
+creates a file `guitar.png` which looks like this:
+
+![Practice hours][guitar]
+
+Each bar is labeled with a weekday and has an associated practice
+hour. The chart is scaled automatically such that the largest bar
+spans the (configurable, see below) height of the chart. The `--title`
+flag passed to `barchart` in the above call is optional. If you do not
+supply one, then barchart uses the basename of the CSV file as title
+of the chart.
+
+If you want to track practice hours over multiple weeks, you can
+create a file with a _mean_, _minimal_, and _maximal_ values for each
+day of the week:
+
+    Mon,1.2,0.9,1.7
+    Tue,0.3,0.1,0.5
+    Wed,2.1,1.5,2.5
+    Thu,0.9,0.4,1.0
+    Fri,1.1,1.0,1.2
+    Sat,3.2,1.5,5.2
+    Sun,3.1,2.3,4.2
+
+Calling `barchart` in the `interval` mode
+
+    # barchart intervals --title="Mean practice hours" guitar-mean.csv
+
+produces the following bar chart:
+
+![Mean practice hours][guitar-mean]
+
+In this chart, each bar represents the mean practice hour for a day
+of the week and the minimum and maximum values are depicted with
+intervals on the right edge of a bar.
+
+If you want to compare your practice hours for each day of the week
+and split it by months, you can create a CSV file like this:
+
+    Mon,1.2,2.1,1.7
+    Tue,0.6,0.7,0.8
+    Wed,2.1,1.2,2.5
+    Thu,0.9,1.5,1.7
+    Fri,1.1,1.3,0.7
+    Sat,3.2,1.7,4.3
+    Sun,3.1,3.2,2.1
+
+We can use `barchart` (in the default mode) 
+
+    # barchart --title="Practice hours per month" --division="Jan Feb Mar" guitar-months.csv
+
+to create the following diagram:
+
+![Practice hours by month][guitar-months]
+
+Each bar is divided into different blocks which all have an associated
+amount of practice hours. Green blocks represent practice hours in
+January, red blocks in February, and blue blocks represent practice
+hours in March. The block labels are given to `barchart` via the
+`--division` flag. You can also draw multiple blocks per bar in
+`interval` mode but then three values (_mean_,_min_,_max_) are used
+for each block. Hence, if you want to depict mean practice times with
+deviations for January, February, and March, you must create a CSV
+file where each day of the week is followed by nine practice times.
+
+## flags
+
+The `barchart` program can be configured using command-line flags. We
+can use the `--help` flag to print a summary:
+
+    # barchart --help
+    Bar Chart 0.1
+    
+    barchart [blocks] [FLAG] [FILE]
+    
+    barchart intervals [FLAG] [FILE]
+    
+    barchart criterion [FLAG] [FILE]
+    
+         --summary               Show benchmark summary (default)
+      -s --summary-comparison    Compare different benchmark summaries
+      -b --benchmark-comparison  Compare different benchmarks
+    
+    barchart progression [FLAG] [FILE]
+    
+      -s --summary-comparison    Breakdown chart by benchmark summary (default)
+      -b --benchmark-comparison  Breakdown chart by benchmarks
+    
+    Common flags:
+      -? --help[=FORMAT]         Show usage information (optional format)
+      -V --version               Show version information
+      -v --verbose               Higher verbosity
+      -q --quiet                 Lower verbosity
+      -o --out-file=FILE         Name of generated file
+      -t --title=STRING          Title of bar chart
+      -x --xlabel=STRING         Label of x axis
+      -y --ylabel=STRING         Label of y axis
+      -g --guess-file-type       Guess output file type by name (default)
+         --png                   Generate .png file
+         --svg                   Generate .svg file
+         --pdf                   Generate .pdf file
+         --ps                    Generate .ps file
+      -d --division=STRINGS      Labels separated by whitespace
+      -c --colors=STRINGS        Color names separated by whitespace
+      -w --width=NUM             Width of generated bar chart (default=600)
+      -h --height=NUM            Height of generated bar chart (default=300)
+      -l --label-size=NUM        Font size used for labels (default=12)
+      -W --bar-width=FLOAT       Bar width between 0 and 1 (default=0.3)
+
+`barchart` can be run in different modes. We have already seen the
+default (`blocks`) mode and the `intervals` mode. The `criterion` and
+`progression` modes are described below. Most command-line flags are
+self explanatory. Apart from what we have seen in the example above,
+the following options are particularly interesting:
+
+  * `--xlabel` and `--ylabel` label the axis of the coordinate system.
+
+  * `--colors` change the colors of the different blocks of a bar. You
+    can use all color names listed in the [SVG 1.1
+    Specification][Colors]. If you specify fewer colors than there are
+    blocks, then colors are reused in a cyclic fashion. The default
+    value for this argument is `--colors="seagreen firebrick
+    midnightblue"`.
+
+  * `--width` and `--height` specify the dimensions of the generated
+    _chart_. The generated _picture_ is a little larger because of the
+    title and bar labels. If you want to draw a chart with many bars,
+    you should increase the width compared to the height or tweak the
+    bar width.
+
+  * `--bar-width` is a value between 0.0 and 1.0 hat specifies how
+    thick the bars are compared to the bar distance. With a value of
+    `1.0` the bars are drawn directly next to each other, a value of
+    0.0 draws bars that are actually lines.
+
+## criterion
+
+[criterion] is a Haskell tool for statistically robust benchmarking
+that can generate graphs which, for example, depict the densities of
+execution times. criterion can also generate a CSV file summarising
+all benchmarks but does not provide means to visualise these
+summaries.
+
+We can use `barchart` in `criterion` mode to draw graphs based on the
+summary files generated by criterion. To demonstrate the criterion
+mode, we write a small Haskell program that benchmarks a simple
+definition of the [factorial] function:
+
+    import Criterion.Main
+    
+    main = defaultMain [bgroup "fac" [bench (show n) (nf product [1..n]) | n <- ns]]
+     where ns = [k*10^4 | k <- [1,2,3]] :: [Integer]
+
+We can compile this program, generate a benchmark summary using
+criterion, and visualise it using barchart as follows:
+
+    # ghc -O2 --make factorial
+    # ./factorial --summary=factorial.csv
+    # barchart criterion factorial.csv
+
+These three calls generate a bar chart with one bar for each benchmark
+whose size is proportional to the corresponding run time.
+
+![fac]
+
+We can now modify the program to use an explicitly recursive
+definition of the fibonacci function to see whether this affects the
+run times.
+
+    fac 0 = 1
+    fac n = n * fac (n-1)
+
+After generating another summary file `factorial2.csv` we could
+generate another bar chart to visualise it and view both charts side
+by side to compare the run times of the different implementations of
+the fibonacci function. However, instead of generating two different
+charts we can also generate a single chart that combines information
+from multiple benchmark summaries.
+
+    # barchart criterion --summary-comparison factorial.csv factorial2.csv
+
+We can pass as many summary files as we like and barchart will draw a
+bar for each summary file with blocks representing the different
+benchmarks.
+
+![fac-summaries]
+
+We can see clearly that the original implementation using `product` is
+faster than the explicitly recursive definition of the factorial
+function. Instead of drawing different bars for different summaries,
+barchart can also draw different bars for the different benchmarks
+with blocks for the summaries.
+
+![fac-benchmarks]
+
+This is less useful for comparing different implementations but useful
+to compare the different benchmarks using all implementations at once.
+
+## progression
+
+[progression] is an alternative tool to depict criterion's benchmark
+results. It generates charts similar to the summary comparisons
+discussed above but draws bars for different benchmarks next to each
+other rather than as boxes on top of each other in a single
+bar. Drawing the results of all benchmarks for a specific
+implementation on top of each other lets you see immediately which
+implementation is the fastest _in total_ because the total height of
+the bar is the sum of all benchmarks for the corresponding
+implementation. However, progression provides features that barchart
+does not provide such as normalising run times and show them as
+percentages. Hence, the barchart program has a `progression` mode to
+generate bar charts from CSV files generated by progression.
+
+We can alter the factorial program as follows to generate
+progression's data:
+
+    import Criterion
+    import Progression.Main
+    
+    main = defaultMain (bgroup "fac" [bench (show n) (nf fac n) | n <- ns])
+     where ns = [k*10^4 | k <- [1,2,3]] :: [Integer]
+    
+    fac 0 = 1
+    fac n = n * fac (n-1)
+
+We do not need to change the fibonacci function itself because we
+won't execute benchmarks using progression. We only generate a
+comparison of the previous benchmarks:
+
+    # mv factorial.csv bench-factorial.csv
+    # mv factorial2.csv bench-factorial2.csv
+    # ./factorial --mode=graph --compare=factorial,factorial2
+
+progression wants the summary files to have specific names, hence, we
+rename them appropriately before generating a graph. The progression
+tool generates two files `plot.csv` and `plot.png`. Here, we are not
+interested in the generated PNG file but pass the CSV file to the
+barchart program in `progression` mode.
+
+    # barchart progression --division="product recursive" plot.csv
+
+As the CSV file generated by progression does not include information
+of the names of the summary files, we provide labels using the
+`--division` flag. The generated bar chart loooks like this:
+
+![progression-summaries]
+
+The run times of the first implementation are normalised to 100 % and
+the run times of the second implementation are depicted relative to
+the first. Like in `criterion` mode we can also flip the axis in
+`progression` mode.
+
+    # barchart progression --benchmark-comparison --division="product recursive" plot.csv
+
+Now the first benchmark is normalised to 100 % in each bar so this
+diagram is of questionable use.
+
+![progression-benchmarks]
+
+# collaboration
+
+The source code is on [GitHub] so you can create or vote on [issues]
+to ask for extensions or fork this project to write extensions
+yourself.
+
+## limitations
+
+Currently, barchart does not support negative quantities and draws
+weird bars if you use them anyway. Also, the legend for blocks is
+sometimes drawn suboptimally. I guess I made a mistake but I cannot
+find it.
+
+## thanks
+
+I would not have written this program without the [Diagrams] library
+by Brent Yorgey and the [CmdArgs] package by Neil Mitchell.
+
+# contact
+
+For questions or feedback email [Sebastian Fischer][email].
+
+[email]: mailto:sebf@informatik.uni-kiel.de
+[GitHub]: http://github.com/sebfisch/haskell-barchart 
+[issues]: http://github.com/sebfisch/haskell-barchart/issues
+
+[guitar]: http://sebfisch.github.com/haskell-barchart/examples/guitar.png
+[guitar-mean]: http://sebfisch.github.com/haskell-barchart/examples/guitar-mean.png
+[guitar-months]: http://sebfisch.github.com/haskell-barchart/examples/guitar-months.png
+[fac]: http://sebfisch.github.com/haskell-barchart/examples/factorial.png
+[fac-summaries]: http://sebfisch.github.com/haskell-barchart/examples/fac-summaries.png
+[fac-benchmarks]: http://sebfisch.github.com/haskell-barchart/examples/fac-benchmarks.png
+[progression-summaries]: http://sebfisch.github.com/haskell-barchart/examples/progression-summaries.png
+[progression-benchmarks]: http://sebfisch.github.com/haskell-barchart/examples/progression-benchmarks.png
+
+[criterion]: http://hackage.haskell.org/package/criterion
+[progression]: http://hackage.haskell.org/package/progression
+[Diagrams]: http://code.haskell.org/diagrams/
+[CmdArgs]: http://community.haskell.org/~ndm/cmdargs/
+[Gtk2Hs]: http://www.haskell.org/gtk2hs/
+[Colors]: http://www.w3.org/TR/SVG11/types.html#ColorKeywords
+[factorial]: http://en.wikipedia.org/wiki/Factorial
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,8 @@
+#! /usr/bin/env runhaskell
+
+> module Main (main) where
+>
+> import Distribution.Simple (defaultMain)
+>
+> main :: IO ()
+> main = defaultMain
diff --git a/barchart.cabal b/barchart.cabal
new file mode 100644
--- /dev/null
+++ b/barchart.cabal
@@ -0,0 +1,58 @@
+Name:          barchart
+Version:       0.1
+Cabal-Version: >= 1.6
+Synopsis:      Creating Bar Charts in Haskell
+
+Description:
+
+  This package provides a program that generates bar charts from CSV
+  files and has special support for visualising the benchmark results
+  of the criterion and progression tools. It comes with an associated
+  API that can be used to generate custom bar charts in case the
+  charts generated by the command-line program are insufficient for
+  your needs.
+
+Category:      Graphics
+License:       BSD3
+License-File:  LICENSE
+Author:        Sebastian Fischer
+Maintainer:    Sebastian Fischer
+Bug-Reports:   http://github.com/sebfisch/haskell-barchart/issues
+Homepage:      http://sebfish.github.com/haskell-barchart
+Build-Type:    Simple
+Stability:     experimental
+
+Extra-Source-Files:     README.markdown
+
+Executable barchart
+  Main-is:              barchart.hs
+  Hs-Source-Dirs:       src
+  Build-Depends:        base >= 3 && < 5,
+                        cmdargs >= 0.1 && < 0.2, 
+                        csv >= 0.1 && < 1.2, 
+                        diagrams >= 0.2 && < 0.3,
+                        filepath >= 1.1 && < 1.2
+  Extensions:           DeriveDataTypeable,
+                        FlexibleContexts,
+                        GeneralizedNewtypeDeriving,
+                        NamedFieldPuns, 
+                        RecordWildCards,
+                        TypeFamilies
+
+Library
+  Exposed-Modules:      Graphics.BarChart
+  Other-Modules:        Graphics.BarChart.Types,
+                        Graphics.BarChart.Rendering,
+                        Graphics.BarChart.Parser,
+                        Graphics.BarChart.Parser.Criterion,
+                        Graphics.BarChart.Parser.Progression
+  Hs-Source-Dirs:       src
+  Build-Depends:        base >= 3 && < 5,
+                        csv >= 0.1 && < 1.2, 
+                        diagrams >= 0.2 && < 0.3,
+                        filepath >= 1.1 && < 1.2
+  Extensions:           FlexibleContexts,
+                        GeneralizedNewtypeDeriving,
+                        NamedFieldPuns, 
+                        RecordWildCards,
+                        TypeFamilies
diff --git a/src/Graphics/BarChart.hs b/src/Graphics/BarChart.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/BarChart.hs
@@ -0,0 +1,99 @@
+-- |
+-- Module      : Graphics.BarChart
+-- Copyright   : Sebastian Fischer
+-- License     : BSD3
+-- Maintainer  : Sebastian Fischer <mailto:sebf@informatik.uni-kiel.de>
+-- Stability   : experimental
+-- 
+-- This library provides functions and data type for generating bar
+-- charts, for example, from CSV files. If the functionality provided
+-- by the command-line program is insufficient for your needs, you can
+-- use the API provided by this library to draw custom bar charts.
+-- 
+-- The library provides high-level functions to generate bar-chart
+-- values from different types but also exports the underlying
+-- datatype and all functions used for rendering such that you have
+-- access to all internal details.
+-- 
+module Graphics.BarChart (
+
+  -- * Reading from CSV files
+
+  -- | There are three different modes for parsing simple CSV files:
+  --   treating different values in one row as blocks of one bar,
+  --   treating them as deviations from a mean value, or both. 
+
+  -- ** Writing images
+
+  -- | The following three functions implement these modes parsing the
+  --   input from a CSV file and producing output in an image file.
+
+  writeMultiBarChart, writeIntervalChart, writeMultiBarIntervalChart,
+
+  Config(..), Label,
+
+  -- ** Creating bar charts
+
+  -- | The previous functions use the following helper functions to
+  --   create a 'BarChart' from a CSV file.
+
+  multiBarChart, intervalChart, multiBarIntervalChart,
+
+  Measurable(..),
+
+  -- ** Bar chart representations
+
+  -- | The three different parsing modes are reflected by three data
+  --   types that can be parsed from a CSV file.
+
+  MultiBars(..), parseMultiBars, drawMultiBars,
+  Intervals(..), parseIntervals, drawIntervals,
+  MultiBarIntervals(..), parseMultiBarIntervals, drawMultiBarIntervals,
+
+  -- | When you have multiple CSV files representing an interval
+  --   chart, you can merge them to a single multi-bar interval
+  --   chart. Such charts can be flipped such that bars and blocks
+  --   change their roles.
+
+  mergeIntervals, flipMultiBarIntervals,
+
+  -- | Bar charts are represented as values of type 'BarChart' @a@.
+
+  BarChart(..), Bar(..), Block(..),
+
+  -- * Rendering
+
+  -- | Values of type 'BarChart' @a@ can be rendered to PNG, SVG, PDF,
+  --   and PS files.
+
+  render, renderWith, conf,
+
+  -- ** Creating diagrams
+
+  -- | Bar charts are rendered by conversion into 'Diagram's.
+
+  diagram,
+
+  -- * Benchmarking visualisation
+
+  -- | There are special functions for parsing data generated by the
+  --   benchmarking tools criterion and progression.
+
+  -- ** Criterion
+
+  writeCriterionChart, writeComparisonChart,
+  criterionChart, comparisonChart,
+  RunTime(..),
+
+  -- ** Progression
+
+  writeProgressionChart, progressionChart,
+  Ratio(..)
+
+  ) where
+
+import Graphics.BarChart.Types
+import Graphics.BarChart.Rendering
+import Graphics.BarChart.Parser
+import Graphics.BarChart.Parser.Criterion
+import Graphics.BarChart.Parser.Progression
diff --git a/src/Graphics/BarChart/Parser.hs b/src/Graphics/BarChart/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/BarChart/Parser.hs
@@ -0,0 +1,108 @@
+module Graphics.BarChart.Parser where
+
+import Text.CSV
+
+import Data.List ( nub )
+import Data.Maybe ( fromJust, fromMaybe )
+
+import System.FilePath
+
+import Graphics.BarChart.Types
+import Graphics.BarChart.Rendering
+
+-- | Converts a CSV file to be drawn as a chart where each bar may
+--   consist of multiple blocks.
+-- 
+parseMultiBars :: Read a => [Label] -> CSV -> MultiBars a
+parseMultiBars block_labels = MultiBars block_labels . map parseRecord
+ where parseRecord (label:values) = (label,map read values)
+
+-- | Converts a CSV file to be drawn as a chart where each bar has an
+--   attached deviation depicted as an interval next to the bar.
+-- 
+parseIntervals :: Read a => CSV -> Intervals a
+parseIntervals = Intervals . map parseRecord
+ where parseRecord [label,m,l,u] = (label,(read m, read l, read u))
+
+-- | Converts a CSV file to be drawn as a chart where each bar may
+--   consist of multiple blocks which have an attached deviation
+--   depicted as an interval next to them.
+-- 
+parseMultiBarIntervals :: Read a => [Label] -> CSV -> MultiBarIntervals a
+parseMultiBarIntervals block_labels =
+  MBIntervals block_labels . map parseRecord
+ where parseRecord (label:fields) = (label, triples fields)
+       triples (x:y:z:xs) = (read x,read y,read z) : triples xs
+       triples _          = []
+
+-- | Used by 'writeMultiBarChart' to create a 'BarChart' from a CSV file.
+-- 
+multiBarChart :: (Measurable a, Read a)
+              => [Label]    -- ^ legend for blocks
+              -> CSV        -- ^ comma separated values to depict
+              -> BarChart a
+multiBarChart block_labels = drawMultiBars . parseMultiBars block_labels
+
+-- | Used by 'writeIntervalChart' to create a 'BarChart' from a CSV file.
+-- 
+intervalChart :: (Measurable a, Read a)
+              => CSV        -- ^ comma separated values to depict 
+              -> BarChart a
+intervalChart = drawIntervals . parseIntervals
+
+-- | Used by 'writeMultiBarIntervalChart' to create a 'BarChart' from
+-- | a CSV file.
+-- 
+multiBarIntervalChart :: (Measurable a, Read a)
+                      => [Label]    -- ^ legend for blocks
+                      -> CSV        -- ^ comma separated values to depict
+                      -> BarChart a
+multiBarIntervalChart block_labels =
+  drawMultiBarIntervals . parseMultiBarIntervals block_labels
+
+-- | The first column of the CSV file is parsed as names of the
+--   bars. The height of each bar corresponds to the sum of all
+--   subsequent entries. If there is more than one entry, the bars are
+--   split into blocks.
+-- 
+writeMultiBarChart :: Config   -- ^ where and how to draw the bar chart
+                   -> FilePath -- ^ CSV file to read
+                   -> [Label]  -- ^ if non-empty, used as legend for blocks
+                   -> IO ()
+writeMultiBarChart config file block_labels =
+  do csv <- readCSV file
+     let chart = multiBarChart block_labels csv :: BarChart Double
+     renderWith config chart
+
+-- | The first column of the CSV file is parsed as names of the
+--   bars. Three entries following each bar name are parsed as mean,
+--   minimum, and maximum value and depicted using an interval next to
+--   the bar.
+-- 
+writeIntervalChart :: Config   -- ^ where and how to draw the bar chart
+                   -> FilePath -- ^ CSV file to read
+                   -> IO ()
+writeIntervalChart config file =
+  do csv <- readCSV file
+     let chart = intervalChart csv :: BarChart Double
+     renderWith config chart
+
+-- | The first column of the CSV file is parsed as names of the
+--   bars. The entries following each bar name are parsed as triples
+--   of mean, minimum, and maximum value and depicted using an
+--   interval next to the bar. The number of subsequent entries must
+--   be a multiple of three and each bar is divided into a
+--   corresponding number of blocks.
+-- 
+writeMultiBarIntervalChart :: Config   -- ^ where and how to draw the bar chart
+                           -> FilePath -- ^ CSV file to read
+                           -> [Label]  -- ^ legend for blocks
+                           -> IO ()
+writeMultiBarIntervalChart config file block_labels =
+  do csv <- readCSV file
+     let chart = multiBarIntervalChart block_labels csv :: BarChart Double
+     renderWith config chart
+
+readCSV :: FilePath -> IO CSV
+readCSV file = either (fail . show) (return . clean) =<< parseCSVFromFile file
+ where clean = filter (not . all null)
diff --git a/src/Graphics/BarChart/Parser/Criterion.hs b/src/Graphics/BarChart/Parser/Criterion.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/BarChart/Parser/Criterion.hs
@@ -0,0 +1,45 @@
+{-# LANGUAGE NamedFieldPuns, RecordWildCards #-}
+
+module Graphics.BarChart.Parser.Criterion where
+
+import Text.CSV
+
+import System.FilePath
+
+import Graphics.BarChart.Types
+import Graphics.BarChart.Parser
+import Graphics.BarChart.Rendering
+
+-- | Used by 'writeCriterionChart' to generate a bar chart from
+--   criterion's summary file.
+-- 
+criterionChart :: CSV -> BarChart RunTime
+criterionChart (_:csv) = intervalChart $ map (take 4) csv
+
+-- | Used by 'writeComparisonChart' to generate a bar chart from
+--   multiple summary files generated by criterion.
+-- 
+comparisonChart :: Bool -> [(Label,CSV)] -> BarChart RunTime
+comparisonChart flip
+  = drawMultiBarIntervals
+  . (if flip then flipMultiBarIntervals else id)
+  . mergeIntervals
+  . map (\ (label,_:csv) -> (label, parseIntervals $ map (take 4) csv))
+
+-- | Reads a summary file generated by criterion and writes a
+--   corresponding bar chart.
+-- 
+writeCriterionChart :: Config -> FilePath -> IO ()
+writeCriterionChart config file =
+  renderWith config . criterionChart =<< readCSV file
+
+-- | Reads multiple summary files generated by criterion and creates a
+--   bar chart to compare them. If the first argument is 'True' the
+--   chart is flipped such that the bars represent different
+--   benchmarks rather than summaries.
+-- 
+writeComparisonChart :: Bool -> Config -> [FilePath] -> IO ()
+writeComparisonChart flip config@Config{..} files =
+  do csvs <- mapM readCSV files
+     renderWith config . comparisonChart flip $
+       zip (map dropExtension files) csvs
diff --git a/src/Graphics/BarChart/Parser/Progression.hs b/src/Graphics/BarChart/Parser/Progression.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/BarChart/Parser/Progression.hs
@@ -0,0 +1,32 @@
+{-# LANGUAGE NamedFieldPuns, RecordWildCards #-}
+
+module Graphics.BarChart.Parser.Progression where
+
+import Text.CSV
+
+import System.FilePath
+
+import Graphics.BarChart.Types
+import Graphics.BarChart.Parser
+import Graphics.BarChart.Rendering
+
+-- | Used by 'writeProgressionChart' to generate a bar chart from
+--   progression's @plot.csv@ file.
+-- 
+progressionChart :: Bool -> [Label] -> CSV -> BarChart Ratio
+progressionChart flip labels csv
+  = drawMultiBarIntervals
+  . (if flip then flipMultiBarIntervals else id)
+  . parseMultiBarIntervals block_labels
+  $ csv
+ where block_labels | null labels = replicate (length csv) ""
+                    | otherwise   = labels
+
+-- | Reads the @plot.csv@ file generated by progression and creates a
+--   corresponding bar chart.
+-- 
+writeProgressionChart :: Bool -> Config -> FilePath -> [Label] -> IO ()
+writeProgressionChart flip config@Config{..} file block_labels =
+  do csv <- readCSV file
+     let chart = progressionChart flip block_labels csv
+     renderWith config chart
diff --git a/src/Graphics/BarChart/Rendering.hs b/src/Graphics/BarChart/Rendering.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/BarChart/Rendering.hs
@@ -0,0 +1,127 @@
+{-# LANGUAGE NamedFieldPuns, RecordWildCards #-}
+
+module Graphics.BarChart.Rendering where
+
+import Data.List ( genericLength )
+
+import Graphics.Rendering.Diagrams
+import Graphics.Rendering.Diagrams.Types ( SomeColor )
+
+import Graphics.BarChart.Types
+
+-- | Renders a bar chart as @barchart.png@ according to the default
+--   configuration 'conf'.
+-- 
+render :: Measurable a => BarChart a -> IO ()
+render = renderWith conf{ outFile = "barchart.png" }
+
+-- | Renders a bar chart according to a custom configuration.
+-- 
+renderWith :: Measurable a => Config -> BarChart a -> IO ()
+renderWith config@Config{..} chart =
+  do renderAs outputType outFile (Width (fromIntegral width))
+       . diagram config
+       $ chart
+     putStrLn $ "Chart written in file " ++ outFile
+ where (width,_) = dimensions
+
+-- | This function can be used to embed bar charts into bigger
+--   'Diagram's.
+-- 
+diagram :: Measurable a => Config -> BarChart a -> Diagram
+diagram config@Config{..} chart@BarChart{..} =
+  drawBarChart config{ ratio     = hratio / wratio,
+                       fontSize = fontSize / wratio }
+               chart
+ where
+  (width,height) = dimensions
+  wratio = fromIntegral width / genericLength bars
+  hratio = fromIntegral height / barChartHeight chart
+
+barChartHeight :: Measurable a => BarChart a -> Double
+barChartHeight BarChart{..} | null bars = 0
+                            | otherwise = maximum (map barSize bars)
+
+barSize :: Measurable a => Bar a -> Double
+barSize Bar{..} | null blocks = 0
+                | otherwise   = sum (upperSize b:map valueSize bs)
+ where (b:bs) = reverse blocks
+
+valueSize :: Measurable a => Block a -> Double
+valueSize (Value x)    = size x
+valueSize Interval{..} = size mean
+
+upperSize :: Measurable a => Block a -> Double
+upperSize (Value x)    = size x
+upperSize Interval{..} = size upper
+
+drawBarChart :: Measurable a => Config -> BarChart a -> Diagram
+drawBarChart config@Config{..} chart@BarChart{..} =
+  pad fontSize fontSize $ vsepA fontSize hcenter [header,body]
+ where
+  width   = genericLength bars
+  height  = ratio * barChartHeight chart
+  bar_sep = 1 - barRatio
+
+  header  = vsepA fontSize hcenter [title,legend]
+  title   = text (1.5*fontSize) caption
+  legend  = hcat (zipWith (drawDescr config) (roll barColors) block_labels)
+
+  body    = vcat [yBars, xaxis, xLabels]
+
+  yBars   = hdistribA (bar_sep/2) left bottom [yaxis, cols]
+  yaxis   = vsep (fontSize/2)
+              [text fontSize yLabel,
+               straight (pathFromVectors [(0,height)])]
+  cols    = sideBySide left (map (drawBar config) bars)
+
+  xaxis   = hsep (fontSize/2)
+              [straight (pathFromVectors [(width,0)]), text fontSize xLabel]
+  xLabels = vspace (fontSize/2)
+         // sideBySide vcenter (map (drawBarLabel config) bars)
+
+roll :: [SomeColor] -> [SomeColor]
+roll colors | null colors = cycle [readColor "black"]
+            | otherwise   = cycle colors
+                                
+drawDescr :: Config -> SomeColor -> Label -> Diagram
+drawDescr Config{..} color string =
+  block color fontSize fontSize <> text fontSize string
+
+block :: SomeColor -> Double -> Double -> Diagram
+block color width height = fillColor color $ roundRectF width height 0.1
+
+drawBar :: Measurable a => Config -> Bar a -> Diagram
+drawBar config@Config{..} Bar{..} =
+  vcat . reverse $ zipWith (drawBlock config) (roll barColors) blocks
+
+drawBlock :: Measurable a => Config -> SomeColor -> Block a -> Diagram
+
+drawBlock Config{..} color (Value x) = hcatA bottom [bar, label]
+ where
+  bar   = block color barRatio (ratio * size x)
+  label = translateX (fontSize/2)
+        . translateY (fontSize - ratio * size x)
+        . ctext fontSize
+        $ show x
+
+drawBlock Config{..} color Interval{..} = hcatA bottom [bar, deviation, label]
+ where
+  bar       = block color barRatio (ratio * size mean)
+  deviation = translateY (-ratio * size lower) interval
+  interval  = vcat [bound,line,bound]
+  bound     = translateX (-fontSize/2) . straight $
+                pathFromVectors [(fontSize,0)]
+  line      = straight $ pathFromVectors [(0,ratio * size (upper-lower))]
+  label     = translateX (-fontSize/2)
+            . translateY (fontSize - ratio * size mean)
+            . ctext fontSize
+            $ show mean
+
+ctext :: Double -> String -> Diagram
+ctext size string = translateY (-size/2) $ text size string
+
+drawBarLabel :: Config -> Bar a -> Diagram
+drawBarLabel Config{..} Bar{..} = text fontSize label
+
+sideBySide valign ds = hdistribA 1 valign bottom ds
diff --git a/src/Graphics/BarChart/Types.hs b/src/Graphics/BarChart/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Graphics/BarChart/Types.hs
@@ -0,0 +1,225 @@
+{-# LANGUAGE TypeFamilies, FlexibleContexts, NamedFieldPuns, RecordWildCards,
+             GeneralizedNewtypeDeriving #-}
+
+module Graphics.BarChart.Types where
+
+import System.IO ( FilePath )
+import Graphics.Rendering.Diagrams
+import Graphics.Rendering.Diagrams.Types ( SomeColor(..) )
+
+import Data.Data     ( Data, Typeable )
+import Data.List     ( nub, transpose )
+import Data.Maybe    ( fromJust, fromMaybe )
+import Control.Arrow ( first )
+
+type Label = String
+
+-- | Bar charts consist of a (possibly empty) list of labels for the
+--   diferent blcks of bars and the bars themselves.
+-- 
+data BarChart a = BarChart {
+
+  -- | Labels of blocks in bars. Drawn as a legend if non-empty.
+  block_labels :: [Label],
+
+  -- | The different bars of the chart.
+  bars :: [Bar a] }
+ deriving Show
+
+-- | Represents one bar of a bar chart.
+data Bar a = Bar {
+
+  -- | Label written underneath
+  label :: Label,
+
+  -- | Different blocks of the bar. Simple charts contain only one
+  --   block per bar.
+  blocks :: [Block a] }
+ deriving Show
+
+-- | Bocks either have a single associated value or a mean value along
+--   with minimum and maximum deviation.
+-- 
+data Block a = Value a | Interval { mean :: a, lower :: a, upper :: a }
+ deriving Show
+
+-- | Instances of this class can be depicted in bar charts.
+-- 
+class Num a => Measurable a where
+
+  -- | Measures the given value to figure out the correponding height
+  --   of the bar.
+  -- 
+  size :: a -> Double
+
+instance Measurable Double  where size = id
+instance Measurable Float   where size = realToFrac
+instance Measurable Integer where size = fromIntegral
+instance Measurable Int     where size = fromIntegral
+
+type ColorName = String
+
+readColor :: ColorName -> SomeColor
+readColor color = SomeColor . fromMaybe (error $ "ivalid color: " ++ color) $
+                    readColourName color
+
+-- | Specifies how bar charts are rendered
+data Config = Config {
+  -- | file to which the bar chart is written
+  outFile :: FilePath,
+
+  -- | Type of generated file
+  outputType :: OutputType,
+
+  -- | Title of the generated chart
+  caption :: Label, 
+
+  -- | Label of the x-axis
+  xLabel :: Label, 
+
+  -- | label of the y-axis
+  yLabel :: Label,
+
+  -- | Colors for the different blocks of a bar. If there are fewer
+  --   colors than blocks, then colors are reused in a cyclic fashion.
+  barColors :: [SomeColor],
+
+  -- | Dimensions of the generated chart. The image will be a bit
+  --   larger because of additiona space used for labels.
+  dimensions :: (Int,Int),
+
+  -- | Scales the height of the chart. The given ratio is multiplied
+  --   with the 'size' of bars as given by the corresponding
+  --   'Measurable' instance.
+  ratio :: Double, 
+
+  -- | Specifies the size of fonts used for labels.
+  fontSize :: Double, 
+
+  -- | Value between 0.0 and 1.0 which pecifies the width of
+  --   bars. Zero means that the bars are lines and 1.0 means that the
+  --   is no space between bars.
+  barRatio :: Double }
+
+-- | The default configuration generates a PNG file with a chart of
+--   size 600x300 pixels. The output file is left unspecified and you
+--   should provide one if you use a cstom configuration.
+-- 
+conf :: Config
+conf = Config { outFile = "", outputType = PNG,
+                caption = "", xLabel = "", yLabel = "",
+                barColors = map SomeColor [forestgreen,firebrick,midnightblue],
+                dimensions = (600,300),
+                ratio = 1, fontSize = 12, barRatio = 0.3 }
+
+-- | Wrapper around the 'Double' type used in bar charts for criterion
+--   summary files. It has a custom 'Show' instance to produce labels
+--   like @10ms@ or @2h@ rather than showing the plain 'Double'
+--   values.
+-- 
+newtype RunTime = RunTime Double
+ deriving (Eq,Num,Measurable)
+
+instance Read RunTime where
+  readsPrec n = map (first RunTime) . readsPrec n
+
+instance Show RunTime where
+  show (RunTime t) | t >= hours   = display (t/hours)   ++ " h"
+                   | t >= minutes = display (t/minutes) ++ " m"
+                   | t >= seconds = display (t/seconds) ++ " s"
+                   | t >= millis  = display (t/millis)  ++ " ms"
+                   | t >= micros  = display (t/micros)  ++ " us"
+                   | otherwise    = display (t/nanos)   ++ " ns"
+   where hours   = 60 * minutes
+         minutes = 60 * seconds
+         seconds = 1
+         millis  = seconds / 1000
+         micros  = millis  / 1000
+         nanos   = micros  / 1000
+
+display :: Double -> String
+display x | x >= 100  = show (fromIntegral (round x))
+          | x >= 10   = show (fromIntegral (round (10*x)) / 10)
+          | otherwise = show (fromIntegral (round (100*x)) / 100)
+
+-- | Wrapper around the double type used in bar charts for progression
+--   summaries. It has a custom 'Show' instance that shows the 'Double'
+--   values as percentages.
+-- 
+newtype Ratio = Ratio Double
+ deriving (Eq,Num,Measurable)
+
+instance Read Ratio where
+  readsPrec n = map (first Ratio) . readsPrec n
+
+instance Show Ratio where
+  show (Ratio r) = display (100*r) ++ " %"
+
+-- | Values of this type are drawn as charts where each bar may
+--   consist of multiple blocks.
+-- 
+data MultiBars a = MultiBars [Label] [(Label,[a])]
+ deriving Show
+
+-- | Converts bars with multiple blocks into their 'BarChart'
+--   representation.
+drawMultiBars :: Measurable a => MultiBars a -> BarChart a
+drawMultiBars (MultiBars block_labels pairs) = BarChart{..}
+ where bars               = map (uncurry mkBar) pairs
+       mkBar label values = Bar{..} where blocks = map Value values
+
+-- | Values of this type are drawn as charts where each bar has an
+--   associated deviation depicted as an interval next to the bar.
+-- 
+newtype Intervals a = Intervals [(Label,(a,a,a))]
+ deriving Show
+
+-- | Converts bars with associated deviation into their 'BarChart'
+--   representation.
+-- 
+drawIntervals :: Measurable a => Intervals a -> BarChart a
+drawIntervals (Intervals pairs) = BarChart{..}
+ where block_labels                   = []
+       bars                           = map (uncurry mkBar) pairs
+       mkBar label (mean,lower,upper) = Bar{..} where blocks = [Interval{..}]
+
+-- | Values of this type are drawn as charts where each bar may be
+--   divided into multiple blocks with an associated deviation
+--   depicted as intervals next to them.
+-- 
+data MultiBarIntervals a = MBIntervals [Label] [(Label,[(a,a,a)])]
+ deriving Show
+
+-- | Merges several interval charts into a chart where each bar has
+--   multiple blocks that represent the different interval charts.
+-- 
+mergeIntervals :: Num a => [(Label,Intervals a)] -> MultiBarIntervals a
+mergeIntervals xs =
+  MBIntervals block_labels [ (label,intervals label) | label <- bar_labels ]
+ where
+  bar_labels   = map fst xs
+  block_labels = nub (concatMap ((\ (Intervals ys) -> map fst ys) . snd) xs)
+
+  intervals l  = map (fromMaybe (0,0,0) . flip lookup ys) block_labels
+   where Intervals ys = fromJust (lookup l xs)
+
+-- | Swaps bars and blocks of a chart that contains both and
+--   associated deviations.
+-- 
+flipMultiBarIntervals :: MultiBarIntervals a -> MultiBarIntervals a
+flipMultiBarIntervals (MBIntervals old_block_labels old_bars) =
+  MBIntervals new_block_labels new_bars
+ where
+  new_block_labels = map fst old_bars
+  new_bars = zip old_block_labels . transpose . map snd $ old_bars
+
+-- | Converts bars with multiple blocks and associated deviations into
+--   their 'BarChart' representation.
+-- 
+drawMultiBarIntervals :: Measurable a => MultiBarIntervals a -> BarChart a
+drawMultiBarIntervals (MBIntervals block_labels pairs) = BarChart{..}
+ where bars = map (uncurry mkBar) pairs
+
+       mkBar label ints = Bar{..}
+        where blocks = map mkInterval ints
+              mkInterval (mean,lower,upper) = Interval{..}
diff --git a/src/barchart.hs b/src/barchart.hs
new file mode 100644
--- /dev/null
+++ b/src/barchart.hs
@@ -0,0 +1,347 @@
+{-# LANGUAGE NamedFieldPuns, RecordWildCards, DeriveDataTypeable #-}
+
+import Graphics.BarChart hiding ( Intervals )
+import Graphics.BarChart.Types ( readColor )
+
+import Data.Char ( toLower )
+import Data.Maybe ( fromMaybe )
+
+import Control.Monad ( when, forM_ )
+
+import System.FilePath
+
+import System.Console.CmdArgs
+
+import qualified Graphics.Rendering.Diagrams as D
+
+data ExecMode
+  = Blocks      { out_file, title, xlabel, ylabel :: String,
+                  file_type :: FileType,
+                  division, colors :: String,
+                  width, height, label_size :: Int,
+                  bar_width :: Double,
+                  in_files :: [String] }
+  | Intervals   { out_file, title, xlabel, ylabel :: String,
+                  file_type :: FileType,
+                  division, colors :: String,
+                  width, height, label_size :: Int,
+                  bar_width :: Double,
+                  in_files :: [String] }
+  | Criterion   { breakdown :: Breakdown,
+
+                  out_file, title, xlabel, ylabel :: String,
+                  file_type :: FileType,
+                  division, colors :: String,
+                  width, height, label_size :: Int,
+                  bar_width :: Double,
+                  in_files :: [String] }
+  | Progression { breakdown :: Breakdown,
+
+                  out_file, title, xlabel, ylabel :: String,
+                  file_type :: FileType,
+                  division, colors :: String,
+                  width, height, label_size :: Int,
+                  bar_width :: Double,
+                  in_files :: [String] }
+ deriving (Show,Data,Typeable)
+
+data FileType = Guess_File_Type | PNG | SVG | PDF | PS
+ deriving (Eq,Show,Data,Typeable)
+
+suffixOf :: FileType -> String
+suffixOf PNG = "png"
+suffixOf SVG = "svg"
+suffixOf PDF = "pdf"
+suffixOf PS  = "ps"
+suffixOf _   = ""
+
+fromFileType :: FileType -> D.OutputType
+fromFileType PNG = D.PNG
+fromFileType SVG = D.SVG
+fromFileType PDF = D.PDF
+fromFileType PS  = D.PS
+fromFileType _   = error "fromFileType: cannot convert guessed file type"
+
+replaceUnknownFileType :: FileType -> FileType -> FileType
+replaceUnknownFileType Guess_File_Type t = t
+replaceUnknownFileType t               _ = t
+
+data Breakdown = Summary | Summary_Comparison | Benchmark_Comparison
+ deriving (Eq,Show,Data,Typeable)
+
+blocksMode =
+  Blocks {
+    out_file   = outFile conf
+                    &= text "Name of generated file"
+                    &  typFile,
+    file_type  = Guess_File_Type `enum`
+                    [Guess_File_Type
+                         &= text "Guess output file type by name (default)",
+                     PNG &= text "Generate .png file",
+                     SVG &= text "Generate .svg file",
+                     PDF &= text "Generate .pdf file",
+                     PS  &= text "Generate .ps file"],
+    title      = caption conf
+                    &= text "Title of bar chart"
+                    &  typString,
+    xlabel     = xLabel conf
+                    &= text "Label of x axis"
+                    &  typString,
+    ylabel     = xLabel conf
+                    &= text "Label of y axis"
+                    &  typString,
+    division  = "" &= text "Labels separated by whitespace"
+                    &  typStrings,
+    colors     = "" &= text "Color names separated by whitespace"
+                    &  typStrings,
+    width      = width
+                    &= text "Width of generated bar chart"
+                    &  typ "NUM",
+    height     = height
+                    &= text "Height of generated bar chart"
+                    &  typ "NUM",
+    label_size = 12 &= text "Font size used for labels"
+                    &  typ "NUM",
+    bar_width  = barRatio conf
+                    &= text "Bar width between 0 and 1"
+                    &  flag "W"
+                    &  typ "FLOAT",
+    in_files   = [] &= text "CVS files with data to visualise"
+                    &  args }
+ where (width,height) = dimensions conf
+
+-- cannot reuse flag attributes in cmdargs :(
+intervalsMode =
+  Intervals {
+    out_file   = outFile conf
+                    &= text "Name of generated file"
+                    &  typFile,
+    file_type  = Guess_File_Type `enum`
+                    [Guess_File_Type
+                         &= text "Guess output file type by name (default)",
+                     PNG &= text "Generate .png file",
+                     SVG &= text "Generate .svg file",
+                     PDF &= text "Generate .pdf file",
+                     PS  &= text "Generate .ps file"],
+    title      = caption conf
+                    &= text "Title of bar chart"
+                    &  typString,
+    xlabel     = xLabel conf
+                    &= text "Label of x axis"
+                    &  typString,
+    ylabel     = xLabel conf
+                    &= text "Label of y axis"
+                    &  typString,
+    division  = "" &= text "Labels separated by whitespace"
+                    &  typStrings,
+    colors     = "" &= text "Color names separated by whitespace"
+                    &  typStrings,
+    width      = width
+                    &= text "Width of generated bar chart"
+                    &  typ "NUM",
+    height     = height
+                    &= text "Height of generated bar chart"
+                    &  typ "NUM",
+    label_size = 12 &= text "Font size used for labels"
+                    &  typ "NUM",
+    bar_width  = barRatio conf
+                    &= text "Bar width between 0 and 1"
+                    &  flag "W"
+                    &  typ "FLOAT",
+    in_files   = [] &= text "CVS files with data to visualise"
+                    &  args }
+ where (width,height) = dimensions conf
+
+criterionMode =
+  Criterion {
+    out_file   = outFile conf
+                    &= text "Name of generated file"
+                    &  typFile,
+    file_type  = Guess_File_Type `enum`
+                    [Guess_File_Type
+                         &= text "Guess output file type by name (default)",
+                     PNG &= text "Generate .png file",
+                     SVG &= text "Generate .svg file",
+                     PDF &= text "Generate .pdf file",
+                     PS  &= text "Generate .ps file"],
+    title      = caption conf
+                    &= text "Title of bar chart"
+                    &  typString,
+    xlabel     = xLabel conf
+                    &= text "Label of x axis"
+                    &  typString,
+    ylabel     = xLabel conf
+                    &= text "Label of y axis"
+                    &  typString,
+    division  = "" &= text "Labels separated by whitespace"
+                    &  typStrings,
+    colors     = "" &= text "Color names separated by whitespace"
+                    &  typStrings,
+    width      = width
+                    &= text "Width of generated bar chart"
+                    &  typ "NUM",
+    height     = height
+                    &= text "Height of generated bar chart"
+                    &  typ "NUM",
+    label_size = 12 &= text "Font size used for labels"
+                    &  typ "NUM",
+    bar_width  = barRatio conf
+                    &= text "Bar width between 0 and 1"
+                    &  flag "W"
+                    &  typ "FLOAT",
+    in_files   = [] &= text "CVS files with data to visualise"
+                    &  args,
+    breakdown  = Summary `enum`
+                  [Summary
+                    &= text "Show benchmark summary (default)",
+                   Summary_Comparison
+                    &= text "Compare different benchmark summaries"
+                    &  flag "s",
+                   Benchmark_Comparison
+                    &= text "Compare different benchmarks"
+                    &  flag "b"] }
+ where (width,height) = dimensions conf
+
+progressionMode =
+  Progression {
+    out_file   = outFile conf
+                    &= text "Name of generated file"
+                    &  typFile,
+    file_type  = Guess_File_Type `enum`
+                    [Guess_File_Type
+                         &= text "Guess output file type by name (default)",
+                     PNG &= text "Generate .png file",
+                     SVG &= text "Generate .svg file",
+                     PDF &= text "Generate .pdf file",
+                     PS  &= text "Generate .ps file"],
+    title      = caption conf
+                    &= text "Title of bar chart"
+                    &  typString,
+    xlabel     = xLabel conf
+                    &= text "Label of x axis"
+                    &  typString,
+    ylabel     = xLabel conf
+                    &= text "Label of y axis"
+                    &  typString,
+    division  = "" &= text "Labels separated by whitespace"
+                    &  typStrings,
+    colors     = "" &= text "Color names separated by whitespace"
+                    &  typStrings,
+    width      = width
+                    &= text "Width of generated bar chart"
+                    &  typ "NUM",
+    height     = height
+                    &= text "Height of generated bar chart"
+                    &  typ "NUM",
+    label_size = 12 &= text "Font size used for labels"
+                    &  typ "NUM",
+    bar_width  = barRatio conf
+                    &= text "Bar width between 0 and 1"
+                    &  flag "W"
+                    &  typ "FLOAT",
+    in_files   = [] &= text "CVS files with data to visualise"
+                    &  args,
+    breakdown  = Summary_Comparison `enum`
+                  [Summary_Comparison
+                    &= text "Breakdown chart by benchmark summary (default)"
+                    & flag "s",
+                   Benchmark_Comparison
+                    &= text "Breakdown chart by benchmarks"
+                    &  flag "b"] }
+ where (width,height) = dimensions conf
+
+typString  = typ "STRING"
+typStrings = typ "STRINGS"
+
+execModes = [blocksMode &= defMode,
+             intervalsMode, criterionMode, progressionMode]
+
+exitIf msg cond = when cond (error msg)
+
+main = do execMode <- cmdArgs "Bar Chart 0.0" (map mode execModes)
+          exitIf "no input files given" $ null (in_files execMode)
+          dispatch execMode
+
+dispatch mode@Blocks{..} =
+  forM_ in_files $ \in_file ->
+    writeMultiBarChart
+      (config (guessDefaults in_file mode))
+      in_file
+      (words division)
+
+dispatch mode@Intervals{..} =
+  forM_ in_files $ \in_file ->
+    writeMultiBarIntervalChart
+      (config (guessDefaults in_file mode))
+      in_file
+      (words division)
+
+dispatch mode@Criterion{..} =
+  case breakdown of
+    Summary ->
+      forM_ in_files $ \in_file ->
+        writeCriterionChart
+          (config (guessBenchmarkDefaults in_file mode))
+          in_file
+    Summary_Comparison ->
+      writeComparisonChart False
+        (config (guessBenchmarkDefaults "summaries.csv" mode))
+        in_files
+    Benchmark_Comparison ->
+      writeComparisonChart True
+        (config (guessBenchmarkDefaults "benchmarks.csv" mode))
+        in_files
+
+dispatch mode@Progression{..} =
+  case breakdown of
+    Summary_Comparison ->
+      forM_ in_files $ \in_file ->
+        writeProgressionChart True
+          (config (guessBenchmarkDefaults in_file mode))
+          in_file
+          (words division)
+    Benchmark_Comparison ->
+      forM_ in_files $ \in_file ->
+        writeProgressionChart False
+          (config (guessBenchmarkDefaults in_file mode))
+          in_file
+          (words division)
+
+guessDefaults :: FilePath -> ExecMode -> ExecMode
+guessDefaults in_file = guessColors . guessTitle . guessFileType . guessOutFile
+ where
+  guessOutFile mode =
+    mode { out_file = out_file mode ? replaceExtension in_file suffix }
+   where suffix = suffixOf (file_type mode) ? ".png"
+
+  guessFileType mode =
+    mode { file_type = replaceUnknownFileType (file_type mode) $
+                         fromMaybe (error $ "unsupported type: " ++ suffix) $
+                           lookup suffix fileTypes }
+   where suffix    = map toLower $ takeExtension (out_file mode)
+         fileTypes = [(".png",PNG),(".svg",SVG),(".pdf",PDF),(".ps",PS)]
+
+  guessTitle mode =
+    mode { title = title mode ? takeBaseName (out_file mode) }
+
+  guessColors mode =
+    mode { colors = colors mode ? "forestgreen firebrick midnightblue" }
+
+guessBenchmarkDefaults in_file = guessAxis . guessDefaults in_file
+ where
+  guessAxis mode = mode { xlabel = xlabel mode ? "benchmark",
+                          ylabel = ylabel mode ? "run time" }
+
+config mode = Config {
+  outFile = out_file mode,
+  outputType = fromFileType $ file_type mode,
+  caption = title mode, xLabel = xlabel mode, yLabel = ylabel mode,
+  barColors = map readColor . words $ colors mode,
+  dimensions = (width mode,height mode),
+  ratio = 1.0,
+  fontSize = fromIntegral $ label_size mode,
+  barRatio = bar_width mode }
+
+(?) :: String -> String -> String
+"" ? s = s
+s  ? _ = s
