diff --git a/AUTHORS b/AUTHORS
new file mode 100644
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,1 @@
+fr33domlover <fr33domlover@riseup.net>
diff --git a/COPYING b/COPYING
new file mode 100644
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,17 @@
+The changes are recorded by the version control system, Darcs. To see a log
+quickly from the terminal, run:
+
+  $ darcs changes --repo http://hub.darcs.net/fr33domlover/link-relations
+
+There is also a web interface at <http://hub.darcs.net> which, among other
+things, can display the history log.
+
+To see the log in a local clone, first get a copy of the repository if you
+haven't yet:
+
+  $ darcs get http://hub.darcs.net/fr33domlover/link-relations
+
+Then move into the newly created directory and run darcs:
+
+  $ cd link-relations
+  $ darcs changes
diff --git a/INSTALL b/INSTALL
new file mode 100644
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,13 @@
+Install from Hackage:
+
+    $ cabal install link-relations
+
+Install from unpacked release tarball or source repo:
+
+    $ cd link-relations
+    $ cabal install
+
+Just play with it without installing:
+
+    $ cabal build
+    $ cabal repl
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,23 @@
+This file lists the user-visible interesting changes between releases. For a
+full list of changes to the source, see the ChangeLog.
+
+
+
+link-relations 0.1.0.0 -- 2015-12-15
+====================================
+
+General, build and documentation changes:
+
+* (This is the first release, so everything is new)
+
+New APIs, features and enhancements:
+
+* (This is the first release, so everything is a new feature)
+
+Bug fixes:
+
+* (This is just the first release, many bugs haven't been discovered yet)
+
+Dependency changes:
+
+* (This is the first release)
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,77 @@
+See the .cabal file for more info and link to project website the version
+control.
+
+The official download location is Hackage.
+
+This library is free software, and is committed to software freedom. It is
+released to the public domain using the CC0 Public Domain Dedication. For the
+boring "legal" details see the file `COPYING`.
+
+See the file `INSTALL` for hints on installation. The file `ChangeLog` explains
+how to see the history log of the changes done in the code. `NEWS.md` provides
+a friendly overview of the changes for each release.
+
+# How to Release
+
+This package's source code is auto-generated using 3 files:
+
+1. A Haskell source file template
+2. CSV file containing the IANA link relation registry data
+3. A Haskell program which embeds the data into the template as Haskell code
+
+All 3 files are found in the `util/` directory.
+
+## (1) Update the source
+
+- To update the non-generated part of the code, edit the template file.
+- To update the CSV to a new version, download it from
+  <https://www.iana.org/assignments/link-relations/link-relations-1.csv>
+- To modify the source generation process, edit the generator program
+
+## (2) Update version and date
+
+If you downloaded a new CSV version, update the `lastUpdated` value in the
+generator program. You can find that date at
+<http://www.iana.org/assignments/link-relations/link-relations.xhtml>.
+
+Update the package version number in the .cabal file. Use `darcs diff` to
+examine the changes in the generated source and pick a version number according
+to PVP. The most common change in the registry will probably be new link
+relations added, which means new API functions and therefore an increase of the
+minor version number (i.e. 3rd number, e.g. /0.1.5.3/ becomes /0.1.6.0/).
+That's because the `LinkRelation` constructors themselves aren't exported.
+
+## (3) Adjust other config
+
+Near the top of the generator program file there is a configuration section
+(e.g. the update date, file paths, etc.). If you made any changes which require
+updates there, make the required modifications.
+
+## (4) Build and test
+
+The generator program depends on the `cassava` package. The other dependencies
+are also dependencies of `cassava` (primarily `bytestring` and `vector`), which
+means you can install the dependencies like this:
+
+    $ cabal install cassava
+
+Run the generator program from the package toplevel. Either using the
+interpreter:
+
+    $ runhaskell util/generate.hs
+
+Or like this, if you installed `cassava` into a sandbox:
+
+    $ cabal exec runhaskell util/generate.hs
+
+Examine the result. Switch between the old generated source and the new one
+(which is generated as a separate file in the same directory).
+
+Build the library itself:
+
+    $ cabal build
+
+## (5) Record everything into the Darcs repo, make a tag, make a tarball etc.
+
+As usual. Note that the 3 files in `util/` (i.e. including the CSV data) and
+the generated source in `src/`, are all kept in the Darcs repo.
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/link-relations.cabal b/link-relations.cabal
new file mode 100644
--- /dev/null
+++ b/link-relations.cabal
@@ -0,0 +1,49 @@
+name:                link-relations
+version:             0.1.0.0
+synopsis:            Use web link relation types (RFC 5988) in Haskell
+description:
+  This library provides access to the IANA registry of link relation types, for
+  use in Haskell code in a type-safe manner.
+  .
+  The registry data can be found at
+  <https://www.iana.org/assignments/link-relations>. This package is generated
+  from that data.
+  .
+  To see to which revision of the registry this version of the package
+  corresponds, look at the intro comment in "Web.LinkRelations".
+homepage:            http://hub.darcs.net/fr33domlover/link-relations
+bug-reports:         http://hub.darcs.net/fr33domlover/link-relations/issues
+license:             PublicDomain
+license-file:        COPYING
+author:              fr33domlover
+maintainer:          fr33domlover@riseup.net
+copyright:           ♡ Copying is an act of love. Please copy, reuse and share.
+category:            Data, Web
+build-type:          Simple
+extra-source-files:  AUTHORS
+                     ChangeLog
+                     COPYING
+                     INSTALL
+                     NEWS.md
+                     README.md
+                     util/template.hs
+                     util/link-relations-1.csv
+                     util/generate.hs
+cabal-version:       >=1.10
+
+source-repository head
+  type:                darcs
+  location:            http://hub.darcs.net/fr33domlover/link-relations
+
+library
+  exposed-modules:     Web.LinkRelations
+  -- other-modules:       
+  -- other-extensions:    
+  build-depends:       base                 >=4.7 && <5
+                     , bytestring
+                     , hashable
+                     , unordered-containers >=0.2.5
+                     , uri-bytestring       >=0.1.7
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  ghc-options:         -Wall
diff --git a/src/Web/LinkRelations.hs b/src/Web/LinkRelations.hs
new file mode 100644
--- /dev/null
+++ b/src/Web/LinkRelations.hs
@@ -0,0 +1,916 @@
+{- This file is part of link-relations.
+ -
+ - Written in 2015 by fr33domlover <fr33domlover@riseup.net>.
+ - Also includes parts generated from the IANA link relation registry.
+ -
+ - ♡ Copying is an act of love. Please copy, reuse and share.
+ -
+ - The author(s) have dedicated all copyright and related and neighboring
+ - rights to this software to the public domain worldwide. This software is
+ - distributed without any warranty.
+ -
+ - You should have received a copy of the CC0 Public Domain Dedication along
+ - with this software. If not, see
+ - <http://creativecommons.org/publicdomain/zero/1.0/>.
+ -}
+
+{-# LANGUAGE OverloadedStrings #-}
+
+-- | A simple web link between two web resources, e.g. a hyperlink in a webpage
+-- referring to another page, suggests that the resources are somehow related,
+-- but it doesn't say /how/ they are related. For example, a blog post may link
+-- to the author's main page (relation: author of the post) but also link to
+-- the previous post (relation: previous post by chronological order).
+--
+-- Link relations provide a way to express the relation between resources
+-- linked by a web link.
+--
+-- A link relation type may be represented by a URI, or by a registered link
+-- relation name. IANA maintains a
+-- <https://www.iana.org/assignments/link-relations registry> of link
+-- relations. This module provides access to them through a dedicated
+-- 'LinkRelation' datatype.
+--
+-- This version of the package corresponds to the
+-- /2015-01-21/
+-- version of the registry.
+module Web.LinkRelations
+    ( -- * Types and Conversions
+      LinkRelation
+    , fromURI
+    , fromURIStr
+    , fromName
+    , fromByteString
+    , isURI
+    , isName
+    , toByteString
+      -- * Link Relation Identifiers
+    , relAbout
+    , relAlternate
+    , relAppendix
+    , relArchives
+    , relAuthor
+    , relBookmark
+    , relCanonical
+    , relChapter
+    , relCollection
+    , relContents
+    , relCopyright
+    , relCreateForm
+    , relCurrent
+    , relDerivedfrom
+    , relDescribedby
+    , relDescribes
+    , relDisclosure
+    , relDuplicate
+    , relEdit
+    , relEditForm
+    , relEditMedia
+    , relEnclosure
+    , relFirst
+    , relGlossary
+    , relHelp
+    , relHosts
+    , relHub
+    , relIcon
+    , relIndex
+    , relItem
+    , relLast
+    , relLatestVersion
+    , relLicense
+    , relLrdd
+    , relMemento
+    , relMonitor
+    , relMonitorGroup
+    , relNext
+    , relNextArchive
+    , relNofollow
+    , relNoreferrer
+    , relOriginal
+    , relPayment
+    , relPredecessorVersion
+    , relPrefetch
+    , relPrev
+    , relPreview
+    , relPrevious
+    , relPrevArchive
+    , relPrivacyPolicy
+    , relProfile
+    , relRelated
+    , relReplies
+    , relSearch
+    , relSection
+    , relSelf
+    , relService
+    , relStart
+    , relStylesheet
+    , relSubsection
+    , relSuccessorVersion
+    , relTag
+    , relTermsOfService
+    , relTimegate
+    , relTimemap
+    , relType
+    , relUp
+    , relVersionHistory
+    , relVia
+    , relWorkingCopy
+    , relWorkingCopyOf
+    )
+where
+
+import Control.Applicative ((<|>))
+import URI.ByteString
+
+import qualified Data.ByteString as B
+import qualified Data.HashMap.Strict as M
+
+-- | A web link relation type. Either a URI or a registered name.
+data LinkRelation
+    = FromURI URI
+    | RelAbout
+    | RelAlternate
+    | RelAppendix
+    | RelArchives
+    | RelAuthor
+    | RelBookmark
+    | RelCanonical
+    | RelChapter
+    | RelCollection
+    | RelContents
+    | RelCopyright
+    | RelCreateForm
+    | RelCurrent
+    | RelDerivedfrom
+    | RelDescribedby
+    | RelDescribes
+    | RelDisclosure
+    | RelDuplicate
+    | RelEdit
+    | RelEditForm
+    | RelEditMedia
+    | RelEnclosure
+    | RelFirst
+    | RelGlossary
+    | RelHelp
+    | RelHosts
+    | RelHub
+    | RelIcon
+    | RelIndex
+    | RelItem
+    | RelLast
+    | RelLatestVersion
+    | RelLicense
+    | RelLrdd
+    | RelMemento
+    | RelMonitor
+    | RelMonitorGroup
+    | RelNext
+    | RelNextArchive
+    | RelNofollow
+    | RelNoreferrer
+    | RelOriginal
+    | RelPayment
+    | RelPredecessorVersion
+    | RelPrefetch
+    | RelPrev
+    | RelPreview
+    | RelPrevious
+    | RelPrevArchive
+    | RelPrivacyPolicy
+    | RelProfile
+    | RelRelated
+    | RelReplies
+    | RelSearch
+    | RelSection
+    | RelSelf
+    | RelService
+    | RelStart
+    | RelStylesheet
+    | RelSubsection
+    | RelSuccessorVersion
+    | RelTag
+    | RelTermsOfService
+    | RelTimegate
+    | RelTimemap
+    | RelType
+    | RelUp
+    | RelVersionHistory
+    | RelVia
+    | RelWorkingCopy
+    | RelWorkingCopyOf
+    deriving (Eq, Show)
+
+-- | A mapping from name string to identifiers, for parsing a link relation
+-- name from a string.
+hashmap :: M.HashMap B.ByteString LinkRelation
+hashmap = M.fromList
+    [ ("about", RelAbout)
+    , ("alternate", RelAlternate)
+    , ("appendix", RelAppendix)
+    , ("archives", RelArchives)
+    , ("author", RelAuthor)
+    , ("bookmark", RelBookmark)
+    , ("canonical", RelCanonical)
+    , ("chapter", RelChapter)
+    , ("collection", RelCollection)
+    , ("contents", RelContents)
+    , ("copyright", RelCopyright)
+    , ("create-form", RelCreateForm)
+    , ("current", RelCurrent)
+    , ("derivedfrom", RelDerivedfrom)
+    , ("describedby", RelDescribedby)
+    , ("describes", RelDescribes)
+    , ("disclosure", RelDisclosure)
+    , ("duplicate", RelDuplicate)
+    , ("edit", RelEdit)
+    , ("edit-form", RelEditForm)
+    , ("edit-media", RelEditMedia)
+    , ("enclosure", RelEnclosure)
+    , ("first", RelFirst)
+    , ("glossary", RelGlossary)
+    , ("help", RelHelp)
+    , ("hosts", RelHosts)
+    , ("hub", RelHub)
+    , ("icon", RelIcon)
+    , ("index", RelIndex)
+    , ("item", RelItem)
+    , ("last", RelLast)
+    , ("latest-version", RelLatestVersion)
+    , ("license", RelLicense)
+    , ("lrdd", RelLrdd)
+    , ("memento", RelMemento)
+    , ("monitor", RelMonitor)
+    , ("monitor-group", RelMonitorGroup)
+    , ("next", RelNext)
+    , ("next-archive", RelNextArchive)
+    , ("nofollow", RelNofollow)
+    , ("noreferrer", RelNoreferrer)
+    , ("original", RelOriginal)
+    , ("payment", RelPayment)
+    , ("predecessor-version", RelPredecessorVersion)
+    , ("prefetch", RelPrefetch)
+    , ("prev", RelPrev)
+    , ("preview", RelPreview)
+    , ("previous", RelPrevious)
+    , ("prev-archive", RelPrevArchive)
+    , ("privacy-policy", RelPrivacyPolicy)
+    , ("profile", RelProfile)
+    , ("related", RelRelated)
+    , ("replies", RelReplies)
+    , ("search", RelSearch)
+    , ("section", RelSection)
+    , ("self", RelSelf)
+    , ("service", RelService)
+    , ("start", RelStart)
+    , ("stylesheet", RelStylesheet)
+    , ("subsection", RelSubsection)
+    , ("successor-version", RelSuccessorVersion)
+    , ("tag", RelTag)
+    , ("terms-of-service", RelTermsOfService)
+    , ("timegate", RelTimegate)
+    , ("timemap", RelTimemap)
+    , ("type", RelType)
+    , ("up", RelUp)
+    , ("version-history", RelVersionHistory)
+    , ("via", RelVia)
+    , ("working-copy", RelWorkingCopy)
+    , ("working-copy-of", RelWorkingCopyOf)
+    ]
+
+-- | Create a link relation type from a URI.
+fromURI :: URI -> LinkRelation
+fromURI = FromURI
+
+-- | Try to parse a string into a URI, returning a link relation if successful.
+fromURIStr :: B.ByteString -> Maybe LinkRelation
+fromURIStr s =
+    case parseURI laxURIParserOptions s of
+        Left _  -> Nothing
+        Right u -> Just $ fromURI u
+
+-- | Try to match a given link relation name against the registered names.
+-- Return the matching link relation if successful.
+fromName :: B.ByteString -> Maybe LinkRelation
+fromName s = M.lookup s hashmap
+
+-- | Try to parse the given string as a link relation, either as a registered
+-- name or as a URI. If both fail, return 'Nothing'.
+fromByteString :: B.ByteString -> Maybe LinkRelation
+fromByteString s = fromName s <|> fromURIStr s
+
+-- | Get the official registered name of a link relation (if it's a name) or
+-- its URI string (if it's a URI).
+toByteString :: LinkRelation -> B.ByteString
+toByteString lr =
+    case lr of
+        FromURI u -> serializeURI' u
+        RelAbout -> "about"
+        RelAlternate -> "alternate"
+        RelAppendix -> "appendix"
+        RelArchives -> "archives"
+        RelAuthor -> "author"
+        RelBookmark -> "bookmark"
+        RelCanonical -> "canonical"
+        RelChapter -> "chapter"
+        RelCollection -> "collection"
+        RelContents -> "contents"
+        RelCopyright -> "copyright"
+        RelCreateForm -> "create-form"
+        RelCurrent -> "current"
+        RelDerivedfrom -> "derivedfrom"
+        RelDescribedby -> "describedby"
+        RelDescribes -> "describes"
+        RelDisclosure -> "disclosure"
+        RelDuplicate -> "duplicate"
+        RelEdit -> "edit"
+        RelEditForm -> "edit-form"
+        RelEditMedia -> "edit-media"
+        RelEnclosure -> "enclosure"
+        RelFirst -> "first"
+        RelGlossary -> "glossary"
+        RelHelp -> "help"
+        RelHosts -> "hosts"
+        RelHub -> "hub"
+        RelIcon -> "icon"
+        RelIndex -> "index"
+        RelItem -> "item"
+        RelLast -> "last"
+        RelLatestVersion -> "latest-version"
+        RelLicense -> "license"
+        RelLrdd -> "lrdd"
+        RelMemento -> "memento"
+        RelMonitor -> "monitor"
+        RelMonitorGroup -> "monitor-group"
+        RelNext -> "next"
+        RelNextArchive -> "next-archive"
+        RelNofollow -> "nofollow"
+        RelNoreferrer -> "noreferrer"
+        RelOriginal -> "original"
+        RelPayment -> "payment"
+        RelPredecessorVersion -> "predecessor-version"
+        RelPrefetch -> "prefetch"
+        RelPrev -> "prev"
+        RelPreview -> "preview"
+        RelPrevious -> "previous"
+        RelPrevArchive -> "prev-archive"
+        RelPrivacyPolicy -> "privacy-policy"
+        RelProfile -> "profile"
+        RelRelated -> "related"
+        RelReplies -> "replies"
+        RelSearch -> "search"
+        RelSection -> "section"
+        RelSelf -> "self"
+        RelService -> "service"
+        RelStart -> "start"
+        RelStylesheet -> "stylesheet"
+        RelSubsection -> "subsection"
+        RelSuccessorVersion -> "successor-version"
+        RelTag -> "tag"
+        RelTermsOfService -> "terms-of-service"
+        RelTimegate -> "timegate"
+        RelTimemap -> "timemap"
+        RelType -> "type"
+        RelUp -> "up"
+        RelVersionHistory -> "version-history"
+        RelVia -> "via"
+        RelWorkingCopy -> "working-copy"
+        RelWorkingCopyOf -> "working-copy-of"
+
+-- | Check whether a link relation is represented by a URI, i.e. is not a link
+-- relation registered name.
+isURI :: LinkRelation -> Bool
+isURI (FromURI _) = True
+isURI _           = False
+
+-- | Check whether a link relation is represented by a registered name, i.e. is
+-- not a URI.
+isName :: LinkRelation -> Bool
+isName = not . isURI
+
+-- | Refers to a resource that is the subject of the link\'s context.
+--
+-- Reference: <https://www.iana.org/go/rfc6903 RFC 6903>, section 2
+relAbout :: LinkRelation
+relAbout = RelAbout
+
+-- | Refers to a substitute for this context
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-alternate>
+relAlternate :: LinkRelation
+relAlternate = RelAlternate
+
+-- | Refers to an appendix.
+--
+-- Reference: <http://www.w3.org/TR/1999/REC-html401-19991224>
+relAppendix :: LinkRelation
+relAppendix = RelAppendix
+
+-- | Refers to a collection of records, documents, or other materials of
+-- historical interest.
+--
+-- Reference:
+-- <http://www.w3.org/TR/2011/WD-html5-20110113/links.html#rel-archives>
+relArchives :: LinkRelation
+relArchives = RelArchives
+
+-- | Refers to the context\'s author.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-author>
+relAuthor :: LinkRelation
+relAuthor = RelAuthor
+
+-- | Gives a permanent link to use for bookmarking purposes.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-bookmark>
+relBookmark :: LinkRelation
+relBookmark = RelBookmark
+
+-- | Designates the preferred version of a resource (the IRI and its contents).
+--
+-- Reference: <https://www.iana.org/go/rfc6596 RFC 6596>
+relCanonical :: LinkRelation
+relCanonical = RelCanonical
+
+-- | Refers to a chapter in a collection of resources.
+--
+-- Reference: <http://www.w3.org/TR/1999/REC-html401-19991224>
+relChapter :: LinkRelation
+relChapter = RelChapter
+
+-- | The target IRI points to a resource which represents the collection
+-- resource for the context IRI.
+--
+-- Reference: <https://www.iana.org/go/rfc6573 RFC 6573>
+relCollection :: LinkRelation
+relCollection = RelCollection
+
+-- | Refers to a table of contents.
+--
+-- Reference: <http://www.w3.org/TR/1999/REC-html401-19991224>
+relContents :: LinkRelation
+relContents = RelContents
+
+-- | Refers to a copyright statement that applies to the link\'s context.
+--
+-- Reference: <http://www.w3.org/TR/1999/REC-html401-19991224>
+relCopyright :: LinkRelation
+relCopyright = RelCopyright
+
+-- | The target IRI points to a resource where a submission form can be
+-- obtained.
+--
+-- Reference: <https://www.iana.org/go/rfc6861 RFC 6861>
+relCreateForm :: LinkRelation
+relCreateForm = RelCreateForm
+
+-- | Refers to a resource containing the most recent item(s) in a collection of
+-- resources.
+--
+-- Reference: <https://www.iana.org/go/rfc5005 RFC 5005>
+relCurrent :: LinkRelation
+relCurrent = RelCurrent
+
+-- | The target IRI points to a resource from which this material was derived.
+--
+-- Reference:
+-- <https://www.iana.org/go/draft-hoffman-xml2rfc draft-hoffman-xml2rfc>
+relDerivedfrom :: LinkRelation
+relDerivedfrom = RelDerivedfrom
+
+-- | Refers to a resource providing information about the link\'s context.
+--
+-- Reference: <http://www.w3.org/TR/powder-dr/#assoc-linking>
+relDescribedby :: LinkRelation
+relDescribedby = RelDescribedby
+
+-- | The relationship A \'describes\' B asserts that resource A provides a
+-- description of resource B. There are no constraints on the format or
+-- representation of either A or B, neither are there any further constraints
+-- on either resource.
+--
+-- Reference: <https://www.iana.org/go/rfc6892 RFC 6892>
+--
+-- Note: This link relation type is the inverse of the \'describedby\' relation
+-- type. While \'describedby\' establishes a relation from the described
+-- resource back to the resource that describes it, \'describes\' established a
+-- relation from the describing resource to the resource it describes. If B is
+-- \'describedby\' A, then A \'describes\' B.
+relDescribes :: LinkRelation
+relDescribes = RelDescribes
+
+-- | Refers to a list of patent disclosures made with respect to material for
+-- which \'disclosure\' relation is specified.
+--
+-- Reference: <https://www.iana.org/go/rfc6579 RFC 6579>
+relDisclosure :: LinkRelation
+relDisclosure = RelDisclosure
+
+-- | Refers to a resource whose available representations are byte-for-byte
+-- identical with the corresponding representations of the context IRI.
+--
+-- Reference: <https://www.iana.org/go/rfc6249 RFC 6249>
+--
+-- Note: This relation is for static resources. That is, an HTTP GET request on
+-- any duplicate will return the same representation. It does not make sense
+-- for dynamic or POSTable resources and should not be used for them.
+relDuplicate :: LinkRelation
+relDuplicate = RelDuplicate
+
+-- | Refers to a resource that can be used to edit the link\'s context.
+--
+-- Reference: <https://www.iana.org/go/rfc5023 RFC 5023>
+relEdit :: LinkRelation
+relEdit = RelEdit
+
+-- | The target IRI points to a resource where a submission form for editing
+-- associated resource can be obtained.
+--
+-- Reference: <https://www.iana.org/go/rfc6861 RFC 6861>
+relEditForm :: LinkRelation
+relEditForm = RelEditForm
+
+-- | Refers to a resource that can be used to edit media associated with the
+-- link\'s context.
+--
+-- Reference: <https://www.iana.org/go/rfc5023 RFC 5023>
+relEditMedia :: LinkRelation
+relEditMedia = RelEditMedia
+
+-- | Identifies a related resource that is potentially large and might require
+-- special handling.
+--
+-- Reference: <https://www.iana.org/go/rfc4287 RFC 4287>
+relEnclosure :: LinkRelation
+relEnclosure = RelEnclosure
+
+-- | An IRI that refers to the furthest preceding resource in a series of
+-- resources.
+--
+-- Reference: <https://www.iana.org/go/rfc5988 RFC 5988>
+--
+-- Note: This relation type registration did not indicate a reference.
+-- Originally requested by Mark Nottingham in December 2004.
+relFirst :: LinkRelation
+relFirst = RelFirst
+
+-- | Refers to a glossary of terms.
+--
+-- Reference: <http://www.w3.org/TR/1999/REC-html401-19991224>
+relGlossary :: LinkRelation
+relGlossary = RelGlossary
+
+-- | Refers to context-sensitive help.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-help>
+relHelp :: LinkRelation
+relHelp = RelHelp
+
+-- | Refers to a resource hosted by the server indicated by the link context.
+--
+-- Reference: <https://www.iana.org/go/rfc6690 RFC 6690>
+--
+-- Note: This relation is used in CoRE where links are retrieved as a
+-- \"\/.well-known\/core\" resource representation, and is the default relation
+-- type in the CoRE Link Format.
+relHosts :: LinkRelation
+relHosts = RelHosts
+
+-- | Refers to a hub that enables registration for notification of updates to
+-- the context.
+--
+-- Reference: <http://pubsubhubbub.googlecode.com>
+--
+-- Note: This relation type was requested by Brett Slatkin.
+relHub :: LinkRelation
+relHub = RelHub
+
+-- | Refers to an icon representing the link\'s context.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-icon>
+relIcon :: LinkRelation
+relIcon = RelIcon
+
+-- | Refers to an index.
+--
+-- Reference: <http://www.w3.org/TR/1999/REC-html401-19991224>
+relIndex :: LinkRelation
+relIndex = RelIndex
+
+-- | The target IRI points to a resource that is a member of the collection
+-- represented by the context IRI.
+--
+-- Reference: <https://www.iana.org/go/rfc6573 RFC 6573>
+relItem :: LinkRelation
+relItem = RelItem
+
+-- | An IRI that refers to the furthest following resource in a series of
+-- resources.
+--
+-- Reference: <https://www.iana.org/go/rfc5988 RFC 5988>
+--
+-- Note: This relation type registration did not indicate a reference.
+-- Originally requested by Mark Nottingham in December 2004.
+relLast :: LinkRelation
+relLast = RelLast
+
+-- | Points to a resource containing the latest (e.g., current) version of the
+-- context.
+--
+-- Reference: <https://www.iana.org/go/rfc5829 RFC 5829>
+relLatestVersion :: LinkRelation
+relLatestVersion = RelLatestVersion
+
+-- | Refers to a license associated with this context.
+--
+-- Reference: <https://www.iana.org/go/rfc4946 RFC 4946>
+--
+-- Note: For implications of use in HTML, see:
+-- http:\/\/www.w3.org\/TR\/html5\/links.html#link-type-license
+relLicense :: LinkRelation
+relLicense = RelLicense
+
+-- | Refers to further information about the link\'s context, expressed as a
+-- LRDD (\"Link-based Resource Descriptor Document\") resource. See
+-- <https://www.iana.org/go/rfc6415 RFC 6415> for information about processing
+-- this relation type in host-meta documents. When used elsewhere, it refers to
+-- additional links and other metadata. Multiple instances indicate additional
+-- LRDD resources. LRDD resources MUST have an \"application\/xrd+xml\"
+-- representation, and MAY have others.
+--
+-- Reference: <https://www.iana.org/go/rfc6415 RFC 6415>
+relLrdd :: LinkRelation
+relLrdd = RelLrdd
+
+-- | The Target IRI points to a Memento, a fixed resource that will not change
+-- state anymore.
+--
+-- Reference: <https://www.iana.org/go/rfc7089 RFC 7089>
+--
+-- Note: A Memento for an Original Resource is a resource that encapsulates a
+-- prior state of the Original Resource.
+relMemento :: LinkRelation
+relMemento = RelMemento
+
+-- | Refers to a resource that can be used to monitor changes in an HTTP
+-- resource.
+--
+-- Reference: <https://www.iana.org/go/rfc5989 RFC 5989>
+relMonitor :: LinkRelation
+relMonitor = RelMonitor
+
+-- | Refers to a resource that can be used to monitor changes in a specified
+-- group of HTTP resources.
+--
+-- Reference: <https://www.iana.org/go/rfc5989 RFC 5989>
+relMonitorGroup :: LinkRelation
+relMonitorGroup = RelMonitorGroup
+
+-- | Indicates that the link\'s context is a part of a series, and that the
+-- next in the series is the link target.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-next>
+relNext :: LinkRelation
+relNext = RelNext
+
+-- | Refers to the immediately following archive resource.
+--
+-- Reference: <https://www.iana.org/go/rfc5005 RFC 5005>
+relNextArchive :: LinkRelation
+relNextArchive = RelNextArchive
+
+-- | Indicates that the context’s original author or publisher does not
+-- endorse the link target.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-nofollow>
+relNofollow :: LinkRelation
+relNofollow = RelNofollow
+
+-- | Indicates that no referrer information is to be leaked when following the
+-- link.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-noreferrer>
+relNoreferrer :: LinkRelation
+relNoreferrer = RelNoreferrer
+
+-- | The Target IRI points to an Original Resource.
+--
+-- Reference: <https://www.iana.org/go/rfc7089 RFC 7089>
+--
+-- Note: An Original Resource is a resource that exists or used to exist, and
+-- for which access to one of its prior states may be required.
+relOriginal :: LinkRelation
+relOriginal = RelOriginal
+
+-- | Indicates a resource where payment is accepted.
+--
+-- Reference: <https://www.iana.org/go/rfc5988 RFC 5988>
+--
+-- Note: This relation type registration did not indicate a reference.
+-- Requested by Joshua Kinberg and Robert Sayre. It is meant as a general way
+-- to facilitate acts of payment, and thus this specification makes no
+-- assumptions on the type of payment or transaction protocol. Examples may
+-- include a web page where donations are accepted or where goods and services
+-- are available for purchase. rel=\"payment\" is not intended to initiate an
+-- automated transaction. In Atom documents, a link element with a
+-- rel=\"payment\" attribute may exist at the feed\/channel level and\/or the
+-- entry\/item level. For example, a rel=\"payment\" link at the feed\/channel
+-- level may point to a \"tip jar\" URI, whereas an entry\/ item containing a
+-- book review may include a rel=\"payment\" link that points to the location
+-- where the book may be purchased through an online retailer.
+relPayment :: LinkRelation
+relPayment = RelPayment
+
+-- | Points to a resource containing the predecessor version in the version
+-- history.
+--
+-- Reference: <https://www.iana.org/go/rfc5829 RFC 5829>
+relPredecessorVersion :: LinkRelation
+relPredecessorVersion = RelPredecessorVersion
+
+-- | Indicates that the link target should be preemptively cached.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-prefetch>
+relPrefetch :: LinkRelation
+relPrefetch = RelPrefetch
+
+-- | Indicates that the link\'s context is a part of a series, and that the
+-- previous in the series is the link target.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-prev>
+relPrev :: LinkRelation
+relPrev = RelPrev
+
+-- | Refers to a resource that provides a preview of the link\'s context.
+--
+-- Reference: <https://www.iana.org/go/rfc6903 RFC 6903>, section 3
+relPreview :: LinkRelation
+relPreview = RelPreview
+
+-- | Refers to the previous resource in an ordered series of resources. Synonym
+-- for \"prev\".
+--
+-- Reference: <http://www.w3.org/TR/1999/REC-html401-19991224>
+relPrevious :: LinkRelation
+relPrevious = RelPrevious
+
+-- | Refers to the immediately preceding archive resource.
+--
+-- Reference: <https://www.iana.org/go/rfc5005 RFC 5005>
+relPrevArchive :: LinkRelation
+relPrevArchive = RelPrevArchive
+
+-- | Refers to a privacy policy associated with the link\'s context.
+--
+-- Reference: <https://www.iana.org/go/rfc6903 RFC 6903>, section 4
+relPrivacyPolicy :: LinkRelation
+relPrivacyPolicy = RelPrivacyPolicy
+
+-- | Identifying that a resource representation conforms to a certain profile,
+-- without affecting the non-profile semantics of the resource representation.
+--
+-- Reference: <https://www.iana.org/go/rfc6906 RFC 6906>
+--
+-- Note: Profile URIs are primarily intended to be used as identifiers, and
+-- thus clients SHOULD NOT indiscriminately access profile URIs.
+relProfile :: LinkRelation
+relProfile = RelProfile
+
+-- | Identifies a related resource.
+--
+-- Reference: <https://www.iana.org/go/rfc4287 RFC 4287>
+relRelated :: LinkRelation
+relRelated = RelRelated
+
+-- | Identifies a resource that is a reply to the context of the link.
+--
+-- Reference: <https://www.iana.org/go/rfc4685 RFC 4685>
+relReplies :: LinkRelation
+relReplies = RelReplies
+
+-- | Refers to a resource that can be used to search through the link\'s
+-- context and related resources.
+--
+-- Reference: <http://www.opensearch.org/Specifications/OpenSearch/1.1>
+relSearch :: LinkRelation
+relSearch = RelSearch
+
+-- | Refers to a section in a collection of resources.
+--
+-- Reference: <http://www.w3.org/TR/1999/REC-html401-19991224>
+relSection :: LinkRelation
+relSection = RelSection
+
+-- | Conveys an identifier for the link\'s context.
+--
+-- Reference: <https://www.iana.org/go/rfc4287 RFC 4287>
+relSelf :: LinkRelation
+relSelf = RelSelf
+
+-- | Indicates a URI that can be used to retrieve a service document.
+--
+-- Reference: <https://www.iana.org/go/rfc5023 RFC 5023>
+--
+-- Note: When used in an Atom document, this relation type specifies Atom
+-- Publishing Protocol service documents by default. Requested by James Snell.
+relService :: LinkRelation
+relService = RelService
+
+-- | Refers to the first resource in a collection of resources.
+--
+-- Reference: <http://www.w3.org/TR/1999/REC-html401-19991224>
+relStart :: LinkRelation
+relStart = RelStart
+
+-- | Refers to a stylesheet.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-stylesheet>
+relStylesheet :: LinkRelation
+relStylesheet = RelStylesheet
+
+-- | Refers to a resource serving as a subsection in a collection of resources.
+--
+-- Reference: <http://www.w3.org/TR/1999/REC-html401-19991224>
+relSubsection :: LinkRelation
+relSubsection = RelSubsection
+
+-- | Points to a resource containing the successor version in the version
+-- history.
+--
+-- Reference: <https://www.iana.org/go/rfc5829 RFC 5829>
+relSuccessorVersion :: LinkRelation
+relSuccessorVersion = RelSuccessorVersion
+
+-- | Gives a tag (identified by the given address) that applies to the current
+-- document.
+--
+-- Reference: <http://www.w3.org/TR/html5/links.html#link-type-tag>
+relTag :: LinkRelation
+relTag = RelTag
+
+-- | Refers to the terms of service associated with the link\'s context.
+--
+-- Reference: <https://www.iana.org/go/rfc6903 RFC 6903>, section 5
+relTermsOfService :: LinkRelation
+relTermsOfService = RelTermsOfService
+
+-- | The Target IRI points to a TimeGate for an Original Resource.
+--
+-- Reference: <https://www.iana.org/go/rfc7089 RFC 7089>
+--
+-- Note: A TimeGate for an Original Resource is a resource that is capable of
+-- datetime negotiation to support access to prior states of the Original
+-- Resource.
+relTimegate :: LinkRelation
+relTimegate = RelTimegate
+
+-- | The Target IRI points to a TimeMap for an Original Resource.
+--
+-- Reference: <https://www.iana.org/go/rfc7089 RFC 7089>
+--
+-- Note: A TimeMap for an Original Resource is a resource from which a list of
+-- URIs of Mementos of the Original Resource is available.
+relTimemap :: LinkRelation
+relTimemap = RelTimemap
+
+-- | Refers to a resource identifying the abstract semantic type of which the
+-- link\'s context is considered to be an instance.
+--
+-- Reference: <https://www.iana.org/go/rfc6903 RFC 6903>, section 6
+relType :: LinkRelation
+relType = RelType
+
+-- | Refers to a parent document in a hierarchy of documents.
+--
+-- Reference: <https://www.iana.org/go/rfc5988 RFC 5988>
+--
+-- Note: This relation type registration did not indicate a reference.
+-- Requested by Noah Slater.
+relUp :: LinkRelation
+relUp = RelUp
+
+-- | Points to a resource containing the version history for the context.
+--
+-- Reference: <https://www.iana.org/go/rfc5829 RFC 5829>
+relVersionHistory :: LinkRelation
+relVersionHistory = RelVersionHistory
+
+-- | Identifies a resource that is the source of the information in the link\'s
+-- context.
+--
+-- Reference: <https://www.iana.org/go/rfc4287 RFC 4287>
+relVia :: LinkRelation
+relVia = RelVia
+
+-- | Points to a working copy for this resource.
+--
+-- Reference: <https://www.iana.org/go/rfc5829 RFC 5829>
+relWorkingCopy :: LinkRelation
+relWorkingCopy = RelWorkingCopy
+
+-- | Points to the versioned resource from which this working copy was
+-- obtained.
+--
+-- Reference: <https://www.iana.org/go/rfc5829 RFC 5829>
+relWorkingCopyOf :: LinkRelation
+relWorkingCopyOf = RelWorkingCopyOf
diff --git a/util/generate.hs b/util/generate.hs
new file mode 100644
--- /dev/null
+++ b/util/generate.hs
@@ -0,0 +1,353 @@
+{- This file is part of link-relations.
+ -
+ - Written in 2015 by fr33domlover <fr33domlover@riseup.net>.
+ -
+ - ♡ Copying is an act of love. Please copy, reuse and share.
+ -
+ - The author(s) have dedicated all copyright and related and neighboring
+ - rights to this software to the public domain worldwide. This software is
+ - distributed without any warranty.
+ -
+ - You should have received a copy of the CC0 Public Domain Dedication along
+ - with this software. If not, see
+ - <http://creativecommons.org/publicdomain/zero/1.0/>.
+ -}
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import Control.Applicative
+import Control.Monad (guard, liftM)
+import Data.Char (toUpper)
+import Data.Csv
+import Data.Foldable (mapM_)
+import Data.Int (Int64)
+import Data.List (intercalate)
+import Data.Maybe (fromMaybe)
+import Data.Monoid ((<>), mconcat)
+import Prelude hiding (mapM_)
+
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.ByteString.Lazy.Char8 as BLC
+import qualified Data.Vector as V (length, toList)
+
+-------------------------------------------------------------------------------
+-- Configuration
+-------------------------------------------------------------------------------
+
+-- | The IANA link relations registry data.
+csvFile :: FilePath
+csvFile = "util/link-relations-1.csv"
+
+-- | Haskell source template into which the data needs to be inserted.
+hsTemplateFile :: FilePath
+hsTemplateFile = "util/template.hs"
+
+-- | Where to write the result of writing formatted data into the template.
+outputFile :: FilePath
+outputFile = "src/Web/LinkRelations.hs.new"
+
+-- | Y-M-D date specifying the time of the last update to the IANA registry.
+lastUpdated :: BL.ByteString
+lastUpdated = "2015-01-21"
+
+-------------------------------------------------------------------------------
+-- Types
+-------------------------------------------------------------------------------
+
+data LinkRelInfo = LinkRelInfo
+    { lrName        :: BL.ByteString
+    , lrDescription :: BL.ByteString
+    , lrReferences  :: BL.ByteString
+    , lrNotes       :: BL.ByteString
+    }
+    deriving (Show)
+
+instance FromRecord LinkRelInfo where
+    parseRecord v =
+        if V.length v == 4
+            then LinkRelInfo <$>
+                 v .! 0 <*>
+                 v .! 1 <*>
+                 v .! 2 <*>
+                 v .! 3
+            else empty
+
+data TemplateLine = TemplText BL.ByteString | TemplVar BL.ByteString
+
+-------------------------------------------------------------------------------
+-- Utilities
+-------------------------------------------------------------------------------
+
+-- | Like 'map', but applies one function to the first element and a different
+-- function to the rest of the elements.
+mapSep :: (a -> b) -> (a -> b) -> [a] -> [b]
+mapSep _ _ []     = []
+mapSep f g (x:xs) = f x : map g xs
+
+-- | Like 'BLC.map', but applies the function /only/ to the first byte.
+mapFstB :: (Char -> Char) -> BL.ByteString -> BL.ByteString
+mapFstB f s =
+    case BLC.uncons s of
+        Nothing     -> s
+        Just (c, r) -> f c `BLC.cons` r
+
+--mapTail :: (a -> a) -> [a] -> [a]
+--mapTail _ []     = []
+--mapTail f (x:xs) = x : map f xs
+
+-- | Convert @this-is-some-name@ to @ThisIsSomeName@.
+toCamel :: BL.ByteString -> BL.ByteString
+toCamel = mconcat . map (mapFstB toUpper) . BLC.split '-'
+
+-- | Apply a function to the first element.
+applyFst :: (a -> c) -> (a, b) -> (c, b)
+applyFst f (x, y) = (f x, y)
+
+-- | Break a list of words into two lists. The first is the maximal prefix
+-- whose length (including spaces between words) is at most N (unless the first
+-- word is longer than N, in which case that word is returned), and the second
+-- list is the rest of the word list.
+breakN :: Int64 -> [BL.ByteString] -> ([BL.ByteString], [BL.ByteString])
+breakN len ws = applyFst reverse $ f 0 [] ws
+    where
+    f _ xs []       = (xs, [])
+    f n [] (y:ys)   =
+        let l = n + BL.length y -- 'n' should be 0 here
+        in  if len <= l
+                then ([y], ys)
+                else f l [y] ys
+    f n xs r@(y:ys) =
+        let l = n + 1 + BL.length y
+        in  if len < l
+                then (xs, r)
+                else f l (y:xs) ys
+
+-- | Apply 'breakN' repeatedly on a word list, splitting it into a list of
+-- word-lists.
+splitN :: Int64 -> [BL.ByteString] -> [[BL.ByteString]]
+splitN len = f
+    where
+    f ws =
+        case breakN len ws of
+            (xs, []) -> [xs]
+            (xs, ys) -> xs : f ys
+
+-- | Read a text file into a list of lines.
+readLines :: FilePath -> IO [BL.ByteString]
+readLines = liftM BLC.lines . BL.readFile
+
+-------------------------------------------------------------------------------
+-- Haddock Comments
+-------------------------------------------------------------------------------
+
+-- | Try to detect a link by suffix. Return remaining prefix and a suffix to
+-- append after link conversion.
+stripLinkSuffix :: BL.ByteString -> Maybe (BL.ByteString, BL.ByteString)
+stripLinkSuffix w =
+    case BLC.unsnoc w of
+        Just (t, ']') -> Just (t, BL.empty)
+        Just (t, ',') ->
+            case BLC.unsnoc t of
+                Just (r, ']') -> Just (r, BLC.singleton ',')
+                _             -> Nothing
+        _             -> Nothing
+
+-- | If the given word is a link, convert to haddock format.
+detectLink :: BL.ByteString -> Maybe BL.ByteString
+detectLink w =
+    let rfcPrefix = "[RFC"
+        rfcDrop = 4
+        httpPrefix = "[http"
+        httpDrop = 1
+        otherPrefix = "["
+        otherDrop = 1
+        iana = "<https://www.iana.org/go/"
+    in  case stripLinkSuffix w of
+            Just (t, r)
+                | rfcPrefix `BL.isPrefixOf` t  -> Just $
+                    let num = BL.drop rfcDrop t
+                    in  iana <> "rfc" <> num <> " RFC " <> num <> ">" <> r
+                | httpPrefix `BL.isPrefixOf` t -> Just $
+                    "<" <> BL.drop httpDrop t <> ">" <> r
+                | otherPrefix `BL.isPrefixOf` t  -> Just $
+                    let name = BL.drop otherDrop t
+                    in  iana <> name <> " " <> name <> ">" <> r
+                | otherwise                    -> Nothing
+            Nothing -> Nothing
+
+-- | Escape some formatting characters in a non-link word.
+escapeWord :: BL.ByteString -> BL.ByteString
+escapeWord = BLC.foldr f BL.empty
+    where
+    f c s =
+        if c `elem` ['/', '"', '\'']
+            then '\\' `BLC.cons` c `BLC.cons` s
+            else c `BLC.cons` s
+
+-- | Convert links, espcape characters, do some comment text processing.
+processWords :: [BL.ByteString] -> [BL.ByteString]
+processWords = map $ \ s -> fromMaybe (escapeWord s) $ detectLink s
+
+-- | Format a paragraph as haddock identifier comment lines.
+toComment :: BL.ByteString -> [BL.ByteString]
+toComment s =
+    let mkFirst = ("-- | " <>) . BLC.unwords
+        mkRest = ("-- " <>) . BLC.unwords
+    in  case breakN 74 $ processWords $ BLC.words s of
+            (xs, []) -> [mkFirst xs]
+            (xs, ys) -> mkFirst xs : map mkRest (splitN 76 ys)
+
+-- | Format a paragraph as haddock identifier non-first comment lines.
+toCommentCont :: BL.ByteString -> [BL.ByteString]
+toCommentCont =
+    let mkline = ("-- " <>) . BLC.unwords
+    in  map mkline . splitN 76 . processWords . BLC.words
+
+-------------------------------------------------------------------------------
+-- Registry Data
+-------------------------------------------------------------------------------
+
+-- | Given the CSV file path, read it into a list of linkrel records.
+readData :: FilePath -> IO [LinkRelInfo]
+readData file = do
+    csv <- BL.readFile file
+    case decode HasHeader csv of
+        Left err  -> error $ "CSV parsing failed: " ++ err
+        Right lrs -> return $ V.toList lrs
+
+-------------------------------------------------------------------------------
+-- Template
+-------------------------------------------------------------------------------
+
+-- | Detect whether a template line is literal text or a variable.
+parseLine :: BL.ByteString -> TemplateLine
+parseLine s = fromMaybe (TemplText s) $ do
+    (h, t) <- BLC.uncons s
+    guard $ h == '$'
+    (h', t') <- BLC.uncons t
+    guard $ h' == '{'
+    (i, l) <- BLC.unsnoc t'
+    guard $ l == '}'
+    return $ TemplVar i
+
+-- | Interpolate the variable values into a template.
+render :: (BL.ByteString -> BL.ByteString) -> [TemplateLine] -> BL.ByteString
+render f =
+    let g (TemplText s) = s `BLC.snoc` '\n'
+        g (TemplVar v) = f v
+    in  mconcat . map g
+
+-- | Read and parse a template file.
+readTemplate :: FilePath -> IO [TemplateLine]
+readTemplate = liftM (map parseLine) . readLines
+
+-------------------------------------------------------------------------------
+-- Haskell Code Generation
+-------------------------------------------------------------------------------
+
+-- date -----------------------------------------------------------------------
+
+-- | Registry revision date to insert into haddock comment.
+varDate :: BL.ByteString
+varDate = "-- /" <> lastUpdated <> "/\n"
+
+-- exports --------------------------------------------------------------------
+
+-- | Given a linkrel name, get a matching Haskell variable name.
+toSymName :: BL.ByteString -> BL.ByteString
+toSymName = ("rel" <>) . toCamel
+
+-- | Given a linkrel name, format an export source line.
+formatExport :: BL.ByteString -> BL.ByteString
+formatExport = ("    , " <>) . toSymName
+
+-- | Given a list of linkrel names, get the exports text.
+mkVarExports :: [BL.ByteString] -> BL.ByteString
+mkVarExports = BLC.unlines . map formatExport
+
+-- ctors ----------------------------------------------------------------------
+
+-- | Given a linkrel name, get a matching data constructor name.
+toCtorName :: BL.ByteString -> BL.ByteString
+toCtorName = ("Rel" <>) . toCamel
+
+-- | Given a linkrel name, format a data constructor source line.
+formatCtor :: BL.ByteString -> BL.ByteString
+formatCtor = ("    | " <>) . toCtorName
+
+-- | Given a list of linkrel names, get the constructors text.
+mkVarCtors :: [BL.ByteString] -> BL.ByteString
+mkVarCtors = BLC.unlines . map formatCtor
+
+-- hashmap --------------------------------------------------------------------
+
+-- | Given a linkrel name, format a hashmap pair source line.
+formatPair :: Bool -> BL.ByteString -> BL.ByteString
+formatPair first name =
+    let c = if first then "[" else ","
+    in  "    " <> c <> " (\"" <> name <> "\", " <> toCtorName name <> ")"
+
+-- | Given a list of linkrel names, get the hashmap text.
+mkVarHashmap :: [BL.ByteString] -> BL.ByteString
+mkVarHashmap = BLC.unlines . mapSep (formatPair True) (formatPair False)
+
+-- tostr ----------------------------------------------------------------------
+
+-- | Given a linkrel name, format a to-string source line.
+formatMatch :: BL.ByteString -> BL.ByteString
+formatMatch name = "        " <> toCtorName name <> " -> \"" <> name <> "\""
+
+-- | Given a list of linkrel names, get the tostr text.
+mkVarTostr :: [BL.ByteString] -> BL.ByteString
+mkVarTostr = BLC.unlines . map formatMatch
+
+-- rels -----------------------------------------------------------------------
+
+-- | Given a linkrel, format a haddock comment.
+formatRelComment :: LinkRelInfo -> [BL.ByteString]
+formatRelComment lr =
+    let fmt pref s =
+            if BL.null s
+                then []
+                else "--" : toCommentCont (pref <> s)
+    in  toComment (lrDescription lr) ++
+        fmt "Reference: " (lrReferences lr) ++
+        fmt "Note: " (lrNotes lr)
+
+-- | Given a linkrel, format relation symbol lines.
+formatRel :: LinkRelInfo -> [BL.ByteString]
+formatRel lr =
+    let name = lrName lr
+        sym = toSymName name
+        ctor = toCtorName name
+    in  formatRelComment lr ++
+        [ sym <> " :: LinkRelation"
+        , sym <> " = " <> ctor
+        ]
+
+-- | Given a list of linkrels , get the rels text.
+mkVarRels :: [LinkRelInfo] -> BL.ByteString
+mkVarRels = BLC.unlines . intercalate [BL.empty] . map formatRel
+
+-------------------------------------------------------------------------------
+-- Main
+-------------------------------------------------------------------------------
+
+main :: IO ()
+main = do
+    lrs <- readData csvFile
+    templ <- readTemplate hsTemplateFile
+    let lrNames = map lrName lrs
+        getVar s
+            | s == "date"    = varDate
+            | s == "exports" = mkVarExports lrNames
+            | s == "ctors"   = mkVarCtors lrNames
+            | s == "hashmap" = mkVarHashmap lrNames
+            | s == "tostr"   = mkVarTostr lrNames
+            | s == "rels"    = mkVarRels lrs
+            | otherwise      =
+                error $ "No value defined for variable " ++ show s
+    BL.writeFile outputFile $ render getVar templ
+    putStrLn "Done."
diff --git a/util/link-relations-1.csv b/util/link-relations-1.csv
new file mode 100644
--- /dev/null
+++ b/util/link-relations-1.csv
@@ -0,0 +1,153 @@
+Relation Name,Description,Reference,Notes
+about,Refers to a resource that is the subject of the link's context.,"[RFC6903], section 2",
+alternate,Refers to a substitute for this context,[http://www.w3.org/TR/html5/links.html#link-type-alternate],
+appendix,Refers to an appendix.,[http://www.w3.org/TR/1999/REC-html401-19991224],
+archives,"Refers to a collection of records, documents, or other
+      materials of historical interest.",[http://www.w3.org/TR/2011/WD-html5-20110113/links.html#rel-archives],
+author,Refers to the context's author.,[http://www.w3.org/TR/html5/links.html#link-type-author],
+bookmark,Gives a permanent link to use for bookmarking purposes.,[http://www.w3.org/TR/html5/links.html#link-type-bookmark],
+canonical,Designates the preferred version of a resource (the IRI and its contents).,[RFC6596],
+chapter,Refers to a chapter in a collection of resources.,[http://www.w3.org/TR/1999/REC-html401-19991224],
+collection,The target IRI points to a resource which represents the collection resource for the context IRI.,[RFC6573],
+contents,Refers to a table of contents.,[http://www.w3.org/TR/1999/REC-html401-19991224],
+copyright,"Refers to a copyright statement that applies to the
+    link's context.",[http://www.w3.org/TR/1999/REC-html401-19991224],
+create-form,The target IRI points to a resource where a submission form can be obtained.,[RFC6861],
+current,"Refers to a resource containing the most recent
+      item(s) in a collection of resources.",[RFC5005],
+derivedfrom,The target IRI points to a resource from which this material was derived.,[draft-hoffman-xml2rfc],
+describedby,"Refers to a resource providing information about the
+      link's context.",[http://www.w3.org/TR/powder-dr/#assoc-linking],
+describes,"The relationship A 'describes' B asserts that
+      resource A provides a description of resource B. There are no
+      constraints on the format or representation of either A or B,
+      neither are there any further constraints on either resource.",[RFC6892],"This link relation type is the inverse of the 'describedby'
+        relation type.  While 'describedby' establishes a relation from
+        the described resource back to the resource that describes it,
+        'describes' established a relation from the describing resource to
+        the resource it describes.  If B is 'describedby' A, then A
+        'describes' B."
+disclosure,Refers to a list of patent disclosures made with respect to material for which 'disclosure' relation is specified.,[RFC6579],
+duplicate,"Refers to a resource whose available representations
+      are byte-for-byte identical with the corresponding representations of
+      the context IRI.",[RFC6249],"This relation is for static resources.  That is, an HTTP GET
+      request on any duplicate will return the same representation.  It
+      does not make sense for dynamic or POSTable resources and should not
+      be used for them."
+edit,"Refers to a resource that can be used to edit the
+      link's context.",[RFC5023],
+edit-form,"The target IRI points to a resource where a submission form for
+      editing associated resource can be obtained.",[RFC6861],
+edit-media,"Refers to a resource that can be used to edit media
+      associated with the link's context.",[RFC5023],
+enclosure,"Identifies a related resource that is potentially
+      large and might require special handling.",[RFC4287],
+first,"An IRI that refers to the furthest preceding resource
+    in a series of resources.",[RFC5988],"This relation type registration did not indicate a
+      reference.  Originally requested by Mark Nottingham in December
+      2004."
+glossary,Refers to a glossary of terms.,[http://www.w3.org/TR/1999/REC-html401-19991224],
+help,Refers to context-sensitive help.,[http://www.w3.org/TR/html5/links.html#link-type-help],
+hosts,"Refers to a resource hosted by the server indicated by
+      the link context.",[RFC6690],"This relation is used in CoRE where links are retrieved as a
+      ""/.well-known/core"" resource representation, and is the default
+      relation type in the CoRE Link Format."
+hub,"Refers to a hub that enables registration for
+    notification of updates to the context.",[http://pubsubhubbub.googlecode.com],This relation type was requested by Brett Slatkin.
+icon,Refers to an icon representing the link's context.,[http://www.w3.org/TR/html5/links.html#link-type-icon],
+index,Refers to an index.,[http://www.w3.org/TR/1999/REC-html401-19991224],
+item,The target IRI points to a resource that is a member of the collection represented by the context IRI.,[RFC6573],
+last,"An IRI that refers to the furthest following resource
+      in a series of resources.",[RFC5988],"This relation type registration did not indicate a
+      reference. Originally requested by Mark Nottingham in December
+      2004."
+latest-version,"Points to a resource containing the latest (e.g.,
+      current) version of the context.",[RFC5829],
+license,Refers to a license associated with this context.,[RFC4946],"For implications of use in HTML, see: 
+      http://www.w3.org/TR/html5/links.html#link-type-license"
+lrdd,"Refers to further information about the link's context,
+      expressed as a LRDD (""Link-based Resource Descriptor Document"")
+      resource.  See [RFC6415] for information about
+      processing this relation type in host-meta documents. When used
+      elsewhere, it refers to additional links and other metadata.
+      Multiple instances indicate additional LRDD resources. LRDD
+      resources MUST have an ""application/xrd+xml"" representation, and
+      MAY have others.",[RFC6415],
+memento,"The Target IRI points to a Memento, a fixed resource that will not change state anymore.",[RFC7089],"A Memento for an Original Resource is a resource that
+      encapsulates a prior state of the Original Resource."
+monitor,Refers to a resource that can be used to monitor changes in an HTTP resource.,[RFC5989],
+monitor-group,Refers to a resource that can be used to monitor changes in a specified group of HTTP resources.,[RFC5989],
+next,"Indicates that the link's context is a part of a series, and
+      that the next in the series is the link target.",[http://www.w3.org/TR/html5/links.html#link-type-next],
+next-archive,Refers to the immediately following archive resource.,[RFC5005],
+nofollow,Indicates that the context’s original author or publisher does not endorse the link target.,[http://www.w3.org/TR/html5/links.html#link-type-nofollow],
+noreferrer,Indicates that no referrer information is to be leaked when following the link.,[http://www.w3.org/TR/html5/links.html#link-type-noreferrer],
+original,The Target IRI points to an Original Resource.,[RFC7089],"An Original Resource is a resource that exists or used to
+      exist, and for which access to one of its prior states may be
+      required."
+payment,Indicates a resource where payment is accepted.,[RFC5988],"This relation type registration did not indicate a
+      reference.  Requested by Joshua Kinberg and Robert Sayre.  It is
+      meant as a general way to facilitate acts of payment, and thus
+      this specification makes no assumptions on the type of payment or
+      transaction protocol.  Examples may include a web page where
+      donations are accepted or where goods and services are available
+      for purchase. rel=""payment"" is not intended to initiate an
+      automated transaction.  In Atom documents, a link element with a
+      rel=""payment"" attribute may exist at the feed/channel level and/or
+      the entry/item level.  For example, a rel=""payment"" link at the
+      feed/channel level may point to a ""tip jar"" URI, whereas an entry/
+      item containing a book review may include a rel=""payment"" link
+      that points to the location where the book may be purchased
+      through an online retailer."
+predecessor-version,"Points to a resource containing the predecessor
+      version in the version history.",[RFC5829],
+prefetch,Indicates that the link target should be preemptively cached.,[http://www.w3.org/TR/html5/links.html#link-type-prefetch],
+prev,"Indicates that the link's context is a part of a series, and
+      that the previous in the series is the link target.",[http://www.w3.org/TR/html5/links.html#link-type-prev],
+preview,Refers to a resource that provides a preview of the link's context.,"[RFC6903], section 3",
+previous,"Refers to the previous resource in an ordered series
+      of resources.  Synonym for ""prev"".",[http://www.w3.org/TR/1999/REC-html401-19991224],
+prev-archive,Refers to the immediately preceding archive resource.,[RFC5005],
+privacy-policy,Refers to a privacy policy associated with the link's context.,"[RFC6903], section 4",
+profile,"Identifying that a resource representation conforms
+to a certain profile, without affecting the non-profile semantics
+of the resource representation.",[RFC6906],"Profile URIs are primarily intended to be used as
+identifiers, and thus clients SHOULD NOT indiscriminately access
+profile URIs."
+related,Identifies a related resource.,[RFC4287],
+replies,"Identifies a resource that is a reply to the context
+      of the link.",[RFC4685],
+search,"Refers to a resource that can be used to search through
+      the link's context and related resources.",[http://www.opensearch.org/Specifications/OpenSearch/1.1],
+section,Refers to a section in a collection of resources.,[http://www.w3.org/TR/1999/REC-html401-19991224],
+self,Conveys an identifier for the link's context.,[RFC4287],
+service,"Indicates a URI that can be used to retrieve a
+      service document.",[RFC5023],"When used in an Atom document, this relation type specifies
+      Atom Publishing Protocol service documents by default.  Requested
+      by James Snell."
+start,"Refers to the first resource in a collection of
+      resources.",[http://www.w3.org/TR/1999/REC-html401-19991224],
+stylesheet,Refers to a stylesheet.,[http://www.w3.org/TR/html5/links.html#link-type-stylesheet],
+subsection,"Refers to a resource serving as a subsection in a
+      collection of resources.",[http://www.w3.org/TR/1999/REC-html401-19991224],
+successor-version,"Points to a resource containing the successor version
+      in the version history.",[RFC5829],
+tag,"Gives a tag (identified by the given address) that applies to
+      the current document.",[http://www.w3.org/TR/html5/links.html#link-type-tag],
+terms-of-service,Refers to the terms of service associated with the link's context.,"[RFC6903], section 5",
+timegate,The Target IRI points to a TimeGate for an Original Resource.,[RFC7089],"A TimeGate for an Original Resource is a resource that is
+      capable of datetime negotiation to support access to prior states
+      of the Original Resource."
+timemap,The Target IRI points to a TimeMap for an Original Resource.,[RFC7089],"A TimeMap for an Original Resource is a resource from which
+      a list of URIs of Mementos of the Original Resource is available."
+type,Refers to a resource identifying the abstract semantic type of which the link's context is considered to be an instance.,"[RFC6903], section 6",
+up,"Refers to a parent document in a hierarchy of
+      documents.",[RFC5988],"This relation type registration did not indicate a
+      reference.  Requested by Noah Slater."
+version-history,"Points to a resource containing the version history
+      for the context.",[RFC5829],
+via,"Identifies a resource that is the source of the
+      information in the link's context.",[RFC4287],
+working-copy,Points to a working copy for this resource.,[RFC5829],
+working-copy-of,"Points to the versioned resource from which this
+      working copy was obtained.",[RFC5829],
diff --git a/util/template.hs b/util/template.hs
new file mode 100644
--- /dev/null
+++ b/util/template.hs
@@ -0,0 +1,111 @@
+{- This file is part of link-relations.
+ -
+ - Written in 2015 by fr33domlover <fr33domlover@riseup.net>.
+ - Also includes parts generated from the IANA link relation registry.
+ -
+ - ♡ Copying is an act of love. Please copy, reuse and share.
+ -
+ - The author(s) have dedicated all copyright and related and neighboring
+ - rights to this software to the public domain worldwide. This software is
+ - distributed without any warranty.
+ -
+ - You should have received a copy of the CC0 Public Domain Dedication along
+ - with this software. If not, see
+ - <http://creativecommons.org/publicdomain/zero/1.0/>.
+ -}
+
+{-# LANGUAGE OverloadedStrings #-}
+
+-- | A simple web link between two web resources, e.g. a hyperlink in a webpage
+-- referring to another page, suggests that the resources are somehow related,
+-- but it doesn't say /how/ they are related. For example, a blog post may link
+-- to the author's main page (relation: author of the post) but also link to
+-- the previous post (relation: previous post by chronological order).
+--
+-- Link relations provide a way to express the relation between resources
+-- linked by a web link.
+--
+-- A link relation type may be represented by a URI, or by a registered link
+-- relation name. IANA maintains a
+-- <https://www.iana.org/assignments/link-relations registry> of link
+-- relations. This module provides access to them through a dedicated
+-- 'LinkRelation' datatype.
+--
+-- This version of the package corresponds to the
+${date}
+-- version of the registry.
+module Web.LinkRelations
+    ( -- * Types and Conversions
+      LinkRelation
+    , fromURI
+    , fromURIStr
+    , fromName
+    , fromByteString
+    , isURI
+    , isName
+    , toByteString
+      -- * Link Relation Identifiers
+${exports}
+    )
+where
+
+import Control.Applicative ((<|>))
+import URI.ByteString
+
+import qualified Data.ByteString as B
+import qualified Data.HashMap.Strict as M
+
+-- | A web link relation type. Either a URI or a registered name.
+data LinkRelation
+    = FromURI URI
+${ctors}
+    deriving (Eq, Show)
+
+-- | A mapping from name string to identifiers, for parsing a link relation
+-- name from a string.
+hashmap :: M.HashMap B.ByteString LinkRelation
+hashmap = M.fromList
+${hashmap}
+    ]
+
+-- | Create a link relation type from a URI.
+fromURI :: URI -> LinkRelation
+fromURI = FromURI
+
+-- | Try to parse a string into a URI, returning a link relation if successful.
+fromURIStr :: B.ByteString -> Maybe LinkRelation
+fromURIStr s =
+    case parseURI laxURIParserOptions s of
+        Left _  -> Nothing
+        Right u -> Just $ fromURI u
+
+-- | Try to match a given link relation name against the registered names.
+-- Return the matching link relation if successful.
+fromName :: B.ByteString -> Maybe LinkRelation
+fromName s = M.lookup s hashmap
+
+-- | Try to parse the given string as a link relation, either as a registered
+-- name or as a URI. If both fail, return 'Nothing'.
+fromByteString :: B.ByteString -> Maybe LinkRelation
+fromByteString s = fromName s <|> fromURIStr s
+
+-- | Get the official registered name of a link relation (if it's a name) or
+-- its URI string (if it's a URI).
+toByteString :: LinkRelation -> B.ByteString
+toByteString lr =
+    case lr of
+        FromURI u -> serializeURI' u
+${tostr}
+
+-- | Check whether a link relation is represented by a URI, i.e. is not a link
+-- relation registered name.
+isURI :: LinkRelation -> Bool
+isURI (FromURI _) = True
+isURI _           = False
+
+-- | Check whether a link relation is represented by a registered name, i.e. is
+-- not a URI.
+isName :: LinkRelation -> Bool
+isName = not . isURI
+
+${rels}
