diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+Copyright 2011, Thomas Schilling
+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 name of the author nor the names of its contributors may be
+used to endorse or promote products derived from this software without
+specific prior written permission. 
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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 UNIVERSITY
+COURT OF THE UNIVERSITY OF GLASGOW 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,4 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+main :: IO ()
+main = defaultMain
diff --git a/System/FilePath/Canonical.hs b/System/FilePath/Canonical.hs
new file mode 100644
--- /dev/null
+++ b/System/FilePath/Canonical.hs
@@ -0,0 +1,138 @@
+-- |
+-- Module      :  System.FilePath
+-- Copyright   :  (c) Thomas Schilling 2011
+-- License     :  BSD-style
+-- 
+-- Maintainer  :  nominolo@googlemail.com
+-- Stability   :  stable
+-- Portability :  portable
+--
+-- Abstract data type for canonical file paths.
+--
+-- Due to the context sensitive way in which file paths are specified
+-- on today's computer systems it is useful to have the notion of a
+-- /canonical/ 'FilePath'.
+--
+-- The basic feature is that two canonical file paths @cfp1@ and
+-- @cfp2@ refer to the same file if and only if @cfp1 == cfp2@.
+-- This property can be achieved using 'canonicalizePath'.
+-- However, if given an arbitrary @FilePath@ we don't know whether
+-- it is canonical so having a separate type is useful.  Secondly,
+-- @canonicalizePath@ might fail if the target path does not exist
+-- on the system.
+-- 
+-- This module therefore provides an abstract type that represents
+-- paths that have been canonicalised.  The intended use
+-- straightforward, just use 'canonical' to create a
+-- 'CanonicalFilePath'.
+--
+-- @example = do 
+--  cfp1 \<- 'canonical' \"./foo\"
+--  curr \<- getCurrentDirectory
+--  cfp2 \<- canonical (curr \</> \"foo\")
+--  print (cfp1 == cfp2)  \-- should print \"True\"
+-- @
+--
+-- To extract a canonical @FilePath@ use 'canonicalFilePath' (don't
+-- use 'show').
+module System.FilePath.Canonical
+  ( -- * Abstract Type
+    CanonicalFilePath,
+    -- * Creation
+    canonical,
+    -- * Deconstruction 
+    originalFilePath, canonicalFilePath,
+    -- * Unsafe\/Internal Operations
+    unsafeCanonicalise
+  )
+where
+
+import Control.DeepSeq
+import Control.Monad ( liftM2 )
+import System.FilePath ( equalFilePath, (</>) )
+import System.Directory ( canonicalizePath, getCurrentDirectory,
+                          doesFileExist, doesDirectoryExist )
+{-
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.Put
+import qualified Codec.Binary.UTF8.String as UTF8
+-}
+
+-- | A canonical 'FilePath'.
+--
+-- Construct values of this type using the 'canonical' function.
+--
+-- Note that it is not always possible to guarantee a truly canonical
+-- path due to various file system features.  For more information see
+-- the documentation of @realpath@ in your favourite man page.
+--
+data CanonicalFilePath =
+  UnsafeCanonicalise {
+    -- | The original input to 'canonical'.
+    originalFilePath :: FilePath,
+    -- | The canonical path.
+    canonicalFilePath :: FilePath }
+
+instance Show CanonicalFilePath where
+  show = originalFilePath
+
+instance Eq CanonicalFilePath where
+  cfp1 == cfp2 =
+    canonicalFilePath cfp1 `equalFilePath` canonicalFilePath cfp2
+
+instance Ord CanonicalFilePath where
+  cfp1 `compare` cfp2 =
+    canonicalFilePath cfp1 `compare` canonicalFilePath cfp2
+
+instance NFData CanonicalFilePath where
+  rnf (UnsafeCanonicalise a b) = rnf a `seq` rnf b
+
+{-
+instance Binary CanonicalFilePath where
+  get = liftM2 UnsafeCanonicalise getUTF8String getUTF8String
+   where
+     getUTF8String = do
+       n <- getWord32le
+       fmap UTF8.decode $ sequence (replicate (fromIntegral n) getWord8)
+
+  put (UnsafeCanonicalise fp1 fp2) = putUTF8String fp1 >> putUTF8String fp2
+   where
+     putUTF8String s_ = do
+       let s = UTF8.encode s_
+       putWord32le (fromIntegral (length s))
+       mapM_ putWord8 s
+-}
+
+-- | Construct a canonical file path from the given path.
+--
+-- Unlike 'canonicalizePath' this operation works even if the file or
+-- its containing directory does not exist, but it may fail due to
+-- other reasons (e.g., permission errors).
+--
+canonical :: FilePath -> IO CanonicalFilePath
+canonical fp = do
+  exists <- liftM2 (||) (doesFileExist fp) (doesDirectoryExist fp)
+  if exists
+    then fmap (UnsafeCanonicalise fp) $ canonicalizePath fp
+    else fmap (UnsafeCanonicalise fp . (</> fp)) getCurrentDirectory
+
+-- | Unsafely constructs a 'CanonicalFilePath'.
+--
+-- It is unsafe in the sense that it is up to the caller to guarantee
+-- that the second argument is indeed a canonical file path.
+--
+-- This function is intended mainly to aid in writing custom
+-- serialisation instances.
+unsafeCanonicalise :: FilePath -- ^ Original file path
+                   -> FilePath -- ^ Canonical file path.
+                   -> CanonicalFilePath
+unsafeCanonicalise = UnsafeCanonicalise
+
+{- from the docs
+test = do
+  cfp1 <- canonical "./foo"
+  curr <- getCurrentDirectory
+  cfp2 <- canonical (curr </> "foo")
+  print (cfp1 == cfp2)  -- should print "True"
+-}
diff --git a/canonical-filepath.cabal b/canonical-filepath.cabal
new file mode 100644
--- /dev/null
+++ b/canonical-filepath.cabal
@@ -0,0 +1,23 @@
+name:                   canonical-filepath
+version:                1.0
+license:                BSD3
+license-file:           LICENSE
+author:                 Thomas Schilling <nominolo@googlemail.com>
+maintainer:             Thomas Schilling <nominolo@googlemail.com>
+homepage:               http://github.com/nominolo/canonical-filepath
+synopsis:               Abstract data type for canonical file paths.
+description:
+  Abstract data type for canonical file paths.
+category:               System
+stability:              provisional
+build-type:             Simple
+cabal-version:          >= 1.6
+
+library
+  build-depends:
+    base                >= 4     && < 5,
+    deepseq             == 1.1.*,
+    directory           == 1.1.*,
+    filepath            == 1.2.*
+  exposed-modules:
+    System.FilePath.Canonical
