[PATCH][RFC] Generate corosync.conf.5{, .html} and corosync.aug from single file

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This file is conf/corosync.rnc that models set of valid (in relatively
weak sense) corosync configurations as captured in corosync.conf and
uses RELAX NG Compact Syntax notation for that purpose.

This was chosen as the most viable option (e.g., when compared
to standard XML encoding of RELAX NG, see below for more reasons)
for modifications and comprehension performed *by humans* and
its brevity is also importand from versioning system perspective.
For automated processing, it can be (and actually is being done
as one of the step towards a final man page/Augeas lens) easily
converted into computer-friendly XML notation, so the advantage
of both approaches can be utilized.  Big advantage of RELAX NG
is the simple extendability using various annotations and/or
tags from custom namespace.

As a side-effect, this schema can be directly used to validate
XML form of corosync.conf (see COROSYNC.XML(5)) if one prefers
it (and beside that, it is to be used in cman->pcmk cluster
conversion utility, which was a prime reason for this changset
originally).

Apparently, having single authoritative source/model of possible
configurations as a source for producing corosync.conf documentation
and Augeas lens for this file alleviates maintainer's effort and
indirectly leads to more up-to-date/up-to-reality reflection of
possible configuration items.  Systematic approach is usually
pretty reasonable...

The core of this changeset, corosync.rnc, is mostly a result
of copy-paste from original man page, analyzing coroparse.c
and few others for the expected types/sets of values for
particular options and some other annotation work (see
integration task 4/ below).

Technical details
-----------------

- generating corosync.conf documentation:

                  rnctree.py
-> [corosync.rnc] ---------> [corosync.full.rng] --------+
                                                         |
                                                     xsltproc
                                            simplification-wrapper.xsl
                          xsltproc                       |
[corosync.conf.asciidoc] <-------- [corosync.rng] <------+
       +            rng2asciidocman.xsl
       |                               xsltproc
    asciidoc                        +-----------> [corosync.conf.5]
       |                           / manpage.xsl
       +--> [corosync.conf.xml] --K
                                   \  xsltproc
                                    +-----------> [corosync.conf.5.html]
                                      xhtml.xsl

- generating corosync.aug Augeas lens:

                  rnctree.py
-> [corosync.rnc] ---------> [corosync.full.rng] --------+
                                                         |
                                                     xsltproc
                                                   rng2lens.xsl
                                                         |
                                   [corosync.aug] <------+

- apparently, new dependencies in build-time are following
  (checks also added into configure.ac):
  - xsltproc (or another XSLT 1.0 compliant processor)
  - DocBook 5+ stylesheets
  - AsciiDoc, and therefore
    - Python (version [2.6, 3.0] should be fine)
  - optionally trang, but custom rnc2rng (see below) logic is embedded
    (this requires Python as well)

- it might not be difficult to generate C parser code based on the
  schema described in corosync.rnc in a similar way in the future,
  however this is the next-level challenge

Licensing concerns and bundling files of other projects
-------------------------------------------------------

Not all the added files are authored by me, however they are either
licensed also under revised BSD (lex.py, rnc_tokenize.py, rnctree.py)
or I got approval from the original author as the original licensing
was unclear (simplification.xsl).  See LICENSE file modifications.

The paragraph above enumerated the files that were bundled due to
a lack of respective packages in RHEL or elsewhere and partly because
they are used solely for the purpose or producing a build/release.

The respective projects are:
- rnc2rng [1] (rnc_tokenize.py, rnctree.py)
  - this is a light-weight alternative to trang
  - originally public domain, I maintain the extended version licensed
    under revised BSD (with an awareness of the original author)
    and fined-tuned particularly for this purpose
- PLY [2] (lex.py)
  - lexical analyzer used in rnc2rng, which bundles it as well
    "since beginning"
- "relax-ng utitilites" [3] (simplification.xsl)

Integration tasks
-----------------

The changeset needs some more love in several aspects:

1/ which files are to be part of installation/distribution/tarball
   (see ascii-art schemes under Technical details)
   - corosync.aug, corosync.conf.5{,.html}?
   - corosync.rng?

2/ autotools integration
   I am not an expert here;  the dependencies based on modification
   time seem to work only occasinally, "make clear" misses some
   by-products, "make dist" and the like may forget some files
   (see also 1/)

3/ portability
   - will AsciiDoc always install XSLT wrappers in
     /etc/asciidoc/docbook-xsl?
   - and many others, also the variability in locations is connected
     to 2/

4/ reflect current state in corosync.rnc
   - I tried hard to make sure the schema corresponds to the reality
     (using original man page and Augeas lens, and the code itself)
     but it should be reviewed carefully by the corosync expert

[1] http://fedorapeople.org/cgit/jpokorny/public_git/rnc2rng.git/
    (real main project homepage is expected soon)
[2] http://www.dabeaz.com/ply/
[3] http://downloads.xmlschemata.org/relax-ng/utilities/

Signed-off-by: Jan Pokorný <jpokorny@xxxxxxxxxx>
---
 .gitignore                      |    6 +
 LICENSE                         |   22 +-
 conf/Makefile.am                |   42 +-
 conf/common.xsl                 |   77 +++
 conf/corosync.rnc               |  729 ++++++++++++++++++++++++++
 conf/lenses/corosync.aug        |  181 -------
 conf/lex.py                     | 1069 +++++++++++++++++++++++++++++++++++++++
 conf/rnc_tokenize.py            |  275 ++++++++++
 conf/rnctree.py                 |  609 ++++++++++++++++++++++
 conf/rng2asciidocman.xsl        |  490 ++++++++++++++++++
 conf/rng2lens.xsl               |  333 ++++++++++++
 conf/simplification-wrapper.xsl |   25 +
 conf/simplification.xsl         | 1037 +++++++++++++++++++++++++++++++++++++
 conf/xsddatatypes2lens.xsl      |  144 ++++++
 configure.ac                    |    5 +
 man/.gitignore                  |    1 +
 man/Makefile.am                 |   31 +-
 man/corosync.conf.5             |  684 -------------------------
 18 files changed, 4884 insertions(+), 876 deletions(-)
 create mode 100644 conf/common.xsl
 create mode 100644 conf/corosync.rnc
 delete mode 100644 conf/lenses/corosync.aug
 create mode 100644 conf/lex.py
 create mode 100644 conf/rnc_tokenize.py
 create mode 100755 conf/rnctree.py
 create mode 100644 conf/rng2asciidocman.xsl
 create mode 100644 conf/rng2lens.xsl
 create mode 100644 conf/simplification-wrapper.xsl
 create mode 100644 conf/simplification.xsl
 create mode 100644 conf/xsddatatypes2lens.xsl
 delete mode 100644 man/corosync.conf.5

diff --git a/.gitignore b/.gitignore
index 1cdac50..5a77dc7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@
 *.so*
 *.lo
 *.la
+*.pyc
+*.swp
 .libs
 .deps
 .version
@@ -28,3 +30,7 @@ missing
 tags
 ID
 Doxyfile
+/conf/corosync*.rng
+/conf/corosync.conf.asciidoc
+/conf/corosync.conf.xml
+/conf/lenses/corosync.aug
diff --git a/LICENSE b/LICENSE
index 8d32cd1..b3e3d94 100644
--- a/LICENSE
+++ b/LICENSE
@@ -2,17 +2,27 @@
 The following license applies to every file in this source distribution except
 for the files git-version-gen, and gitlog-to-changelog.
 
-The git* files, which are available under GPLv3 or later,  are only used by
+The git* files, which are available under GPLv3 or later, are only used by
 our release process to generate text file content and are not part of any
 generated binary.
+
+Some of the files, although licensed like the vast majority of corosync
+codebase under revised BSD license, the actual wording differs as
+the licensors are different.  The authoritative license text can be
+found within these files.
+
+Special exception is simplification.xsl transformation obtained verbatim
+from http://downloads.xmlschemata.org/relax-ng/utilities/simplification.xsl,
+author of which, Eric van der Vlist, agreed with effectively inheriting
+corosync's revised BSD license.
 -----------------------------------------------------------------------------
 
 Copyright (c) 2002-2004 MontaVista Software, Inc.
-Copyright (c) 2005-2010 Red Hat, Inc.
+Copyright (c) 2005-2013 Red Hat, Inc.
 
 All rights reserved.
 
-This software licensed under BSD license, the text of which follows:
+This software licensed under revised BSD license, the text of which follows:
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
@@ -39,10 +49,10 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 THE POSSIBILITY OF SUCH DAMAGE.
 
 -----------------------------------------------------------------------------
-The corosync project uses software for release processing which generates
+The corosync project uses software for release processing, which generates
 changelogs and version information for the software.  These programs are not
-used by the generated binaries or libraries These files are git-version-gen
-and gitlog-to-changelog.
+used by the generated binaries or libraries.
+These files are git-version-gen and gitlog-to-changelog.
 -----------------------------------------------------------------------------
 The license for these files is as follows:
 This program is free software: you can redistribute it and/or modify
diff --git a/conf/Makefile.am b/conf/Makefile.am
index 69f7ff9..d0771c5 100644
--- a/conf/Makefile.am
+++ b/conf/Makefile.am
@@ -1,4 +1,4 @@
-# Copyright (c) 2009 Red Hat, Inc.
+# Copyright (c) 2013 Red Hat, Inc.
 #
 # Authors: Andrew Beekhof
 #         Steven Dake (sdake@xxxxxxxxxx)
@@ -69,3 +69,43 @@ if INSTALL_DBUSCONF
 dbusdir			= $(sysconfdir)/dbus-1/system.d
 dbus_DATA		= corosync-signals.conf
 endif
+
+#
+# rnc -> man page DocBook, Augeas lens
+#
+
+CONF_NAME		= corosync.conf
+CONF_SCHEMA_ORIG	= corosync.rnc
+CONF_XSL_ASCIIDOCMAN	= rng2asciidocman.xsl
+CONF_XSL_LENS		= rng2lens.xsl
+CONF_XSLS_ASCIIDOCMAN	= $(CONF_XSL_ASCIIDOCMAN) common.xsl
+CONF_XSLS_LENS		= $(CONF_XSL_LENS) xsddatatypes2lens.xsl common.xsl
+CONF_SCRIPT		= rnctree.py
+CONF_SIMPLIFICATOR	= simplification-wrapper.xsl
+# derived
+CONF_ASCIIDOC		= $(CONF_NAME:=.asciidoc)  # fixed name
+CONF_DOCBOOK		= $(CONF_NAME:=.xml)       # fixed name
+
+CONF_SCHEMA		= $(CONF_SCHEMA_ORIG:.rnc=.rng)
+CONF_SCHEMA_ORIG_RNG	= $(CONF_SCHEMA_ORIG:.rnc=.orig.rng)
+
+noinst_DATA		= $(CONF_SCHEMA_ORIG) $(CONF_SCHEMA_ORIG_RNG) \
+			  $(CONF_ASCIIDOC) $(CONF_DOCBOOK)
+
+$(CONF_DOCBOOK): $(CONF_ASCIIDOC)
+	$(ASCIIDOC) --doctype=manpage -b docbook -d manpage $<
+
+$(CONF_ASCIIDOC): $(CONF_SCHEMA) $(CONF_XSLS_ASCIIDOCMAN)
+	$(XSLTPROC) $(CONF_XSL_ASCIIDOCMAN) $< > $@
+
+lenses/corosync.aug: $(CONF_SCHEMA_ORIG_RNG) $(CONF_XSLS_LENS)
+	$(XSLTPROC) $(CONF_XSL_LENS) $< > $@
+
+#trang -O rng $(CONF_SCHEMA_ORIG) $@
+$(CONF_SCHEMA_ORIG_RNG): $(CONF_SCHEMA_ORIG) $(CONF_SCRIPT)
+	cat $< | $(PYTHON) $(CONF_SCRIPT) > $@
+
+$(CONF_SCHEMA): $(CONF_SCHEMA_ORIG_RNG) $(CONF_SIMPLIFICATOR)
+	$(XSLTPROC) $(CONF_SIMPLIFICATOR) $< > $@
+
+all-local: lenses/corosync.aug
diff --git a/conf/common.xsl b/conf/common.xsl
new file mode 100644
index 0000000..0b75fc3
--- /dev/null
+++ b/conf/common.xsl
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";>
+
+<!--
+  Copyright 2013 Red Hat, Inc.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+  - Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+  - Neither the name of the Red Hat, Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+  THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!-- xsltdoc (http://www.kanzaki.com/parts/xsltdoc.xsl) header -->
+<xsl:template name="_doas_description">
+    <rdf:RDF xmlns="http://purl.org/net/ns/doas#";>
+        <rdf:Description rdf:about="">
+            <title>common.xsl XSLT stylesheet</title>
+            <description>
+                This stylesheet contains shared helper function-like
+                templates.
+            </description>
+            <author rdf:parseType="Resource">
+                <name>Jan Pokorný</name>
+                <mbox rdf:resource="jpokorny@xxxxxxxxxx"/>
+            </author>
+            <created>2013-02-04</created>
+            <release rdf:parseType="Resource">
+                <revision>0.1</revision>
+                <created>2013-02-04</created>
+            </release>
+            <rights>Copyright 2013 Red Hat, Inc.</rights>
+            <license rdf:resource="http://opensource.org/licenses/BSD-3-Clause"/>
+        </rdf:Description>
+    </rdf:RDF>
+</xsl:template>
+
+<xsl:template name="text-join">
+    <!--** Simple text join of 'items' using 'sep' (defaults to space). -->
+    <!-- TODO: normalize-space? -->
+    <xsl:param name="items"/>
+    <xsl:param name="sep" select="$SP"/>
+    <xsl:param name="markup" value="''"/>
+    <xsl:for-each select="$items">
+        <xsl:choose>
+            <xsl:when test="position() = 1">
+                <xsl:value-of select="concat($markup, ., $markup)"/>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="concat($sep, $markup, ., $markup)"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:for-each>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/conf/corosync.rnc b/conf/corosync.rnc
new file mode 100644
index 0000000..a5c9e52
--- /dev/null
+++ b/conf/corosync.rnc
@@ -0,0 +1,729 @@
+# style guide (RELAX NG Compact only):
+# * '##' comments are wrapped at 50/80 (first line/rest), 2-spaced sentence sep,
+#   AsciiDoc formatting is used within them
+# * sort everything (except for dependencies/nesting) to ease the lookup
+
+namespace a = "http://relaxng.org/ns/compatibility/annotations/1.0";
+namespace a4doc = "http://people.redhat.com/jpokorny/ns/a4doc";
+
+start = corosync
+
+corosync =
+  element corosync {
+    (logging?
+     & nodelist?
+     & resources?
+     & quorum?
+     & totem
+     & uidgid?)
+  }
+
+## LOGGING ####################################################################
+
+# only named set of attributes
+common_logging =
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies whether debug output is
+    ## logged for this particular logger.  Also can contain value 'trace',
+    ## which is highest level of debug informations.
+    [ a:defaultValue = "off" ]
+    attribute debug {"off"|"on"|"trace"}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## If the *to_logfile* option is set to
+    ## 'yes', this option specifies the pathname of the log file.
+    attribute logfile {text}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the logfile level for this particular subsystem.  Ignored
+    ## if *debug* is 'on'.  Note: 'debug' is the same as if *debug* is 'on'.
+    [ a:defaultValue = "info" ]
+    attribute logfile_priority {"alert"
+                               |"crit"
+                               |"debug"
+                               |"emerg"
+                               |"err"
+                               |"info"
+                               |"notice"
+                               |"warning"
+    }?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the syslog facility type
+    ## that will be used for any messages sent to syslog.
+    [ a:defaultValue = "daemon" ]
+    attribute syslog_facility {"daemon"
+                              |"local0"
+                              |"local1"
+                              |"local2"
+                              |"local3"
+                              |"local4"
+                              |"local5"
+                              |"local6"
+                              |"local7"
+    }?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the syslog level for this
+    ## particular subsystem.  Ignored if *debug* is 'on'.  Note: 'debug'
+    ## is the same as *debug* is 'on'.
+    [ a:defaultValue = "info" ]
+    attribute syslog_priority {"alert"
+                              |"crit"
+                              |"debug"
+                              |"emerg"
+                              |"err"
+                              |"info"
+                              |"notice"
+                              |"warning"
+    }?,
+
+    # XXX: undocumented
+    attribute tags {text}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies whether to use
+    ## the respective destination of logging output.
+    ##
+    ## Please note, if you are using *to_logfile* and want to rotate the file,
+    ## use `logrotate(8)` with the option `copytruncate`, e.g.
+    ##
+    ## ----
+    ## /var/log/corosync.log {
+    ##     missingok
+    ##     compress
+    ##     notifempty
+    ##     daily
+    ##     rotate 7
+    ##     copytruncate
+    ## }
+    ## ----
+    [ a:defaultValue = "no" ]
+    attribute to_logfile {"no"|"yes"}?,
+
+    # CFG: corosync.conf
+    ## This specifies whether to use
+    ## the respective destination of logging output.
+    [ a:defaultValue = "yes" ]
+    attribute to_stderr {"no"|"yes"}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies whether to use
+    ## the respective destination of logging output.
+    [ a:defaultValue = "yes" ]
+    attribute to_syslog {"no"|"yes"}?
+
+logging =
+  ## In this configuration section, one can
+  ## adjust logging.
+  element logging {
+    # CFG: corosync.conf
+    ## This specifies that file and line should
+    ## be printed.
+    [ a:defaultValue = "off" ]
+    attribute fileline {"off"|"on"}?,
+
+    # CFG: corosync.conf
+    ## This specifies that the code function name
+    ## should be printed.
+    [ a:defaultValue = "off" ]
+    attribute function_name {"off"|"on"}?,
+
+    # CFG: corosync.conf
+    ## This specifies that a timestamp is placed
+    ## on all log messages.
+    [ a:defaultValue = "off" ]
+    attribute timestamp {"off"|"on"}?,
+
+    common_logging,
+    logger_subsys*
+  }
+
+logger_subsys =
+  element logger_subsys {
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the subsystem identity
+    ## (name) for which logging is specified.  This is the name used by
+    ## a service in the `log_init` call, e.g., 'CPG'.
+    attribute subsys {text},
+
+    common_logging
+  }
+
+## NODELIST ###################################################################
+
+nodelist =
+  ## In this configuration section, one can
+  ## adjust nodes in the cluster.
+  element nodelist {
+    node*
+  }
+
+node =
+  element node {
+    # XXX: implied check
+    # CFG: corosync.conf, cluster.conf
+    ## This configuration option is optional when
+    ## using IPv4 and required when using IPv6.  This is a 32bit value
+    ## specifying the node identifier delivered to the cluster membership
+    ## service.  If this is not specified with IPv4, *nodeid* will be
+    ## determined from the 32bit IP address the system to which the system
+    ## is bound with ring identifier of 0.  The node identifier value of zero
+    ## is reserved and should not be used.
+    attribute nodeid {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf, cluster.conf
+    # NOTE: not a direct mapping in cluster.conf (clusternode/votes)
+    attribute quorum_votes {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies IP address of one of the nodes for particular ring
+    ## as denoted by its number (instead 0, there can be higher numbers).
+    attribute ring0_addr {text},
+    attribute ring1_addr {text}?,
+    attribute ring2_addr {text}?,
+    attribute ring3_addr {text}?,
+    attribute ring4_addr {text}?,
+    attribute ring5_addr {text}?,
+    attribute ring6_addr {text}?,
+    attribute ring7_addr {text}?,
+    attribute ring8_addr {text}?,
+    attribute ring9_addr {text}?
+    # NOTE: Augeas lens for corosync.conf counts on X = 0..9 only
+  }
+
+## QUORUM #####################################################################
+
+quorum =
+  ## In this configuration section, one can
+  ## adjust quorum.
+  element quorum {
+    # CFG: corosync.conf
+    ## This enables Downscale feature
+    ## (see `votequorum(5)`).
+    [ a:defaultValue = "0" ]
+    attribute allow_downscale {"0"|"1"}?,
+
+    # CFG: corosync.conf
+    ## This enables Auto Tie Breaker feature
+    ## (see `votequorum(5)`).
+    [ a:defaultValue = "0" ]
+    attribute auto_tie_breaker {"0"|"1"}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the number of expected votes, overriding the number
+    ## implied by the number of *node* items within *nodes*.
+    attribute expected_votes {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## This enables Last Man Standing feature
+    ## (see `votequorum(5)`).
+    [ a:defaultValue = "0" ]
+    attribute last_man_standing {"0"|"1"}?,
+
+    # CFG: corosync.conf
+    ## This specifies the tunable for Last Man
+    ## Standing feature (see `votequorum(5)`).
+    [ a:defaultValue = "0" ]
+    attribute last_man_standing_window {xsd:nonNegativeInteger}?,
+
+    # CFG: corosync.conf
+    ## This specifies the quorum algorithm to use.
+    ## As of now, only 'corosync_votequorum' is supported.
+    attribute provider {"corosync_votequorum"}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This enables two node cluster operations
+    ## (see `votequorum(5)`).
+    [ a:defaultValue = "0" ]
+    attribute two_node {"0"|"1"}?,
+
+    # CFG: corosync.conf
+    attribute votes {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## This enables Wait For All feature
+    ## (see `votequorum(5)`).
+    [ a:defaultValue = "0" ]
+    attribute wait_for_all {"0"|"1"}?
+  }
+
+## RESOURCES ##################################################################
+
+# only named set of attributes
+common_resource =
+  # XXX: undocumented
+  attribute max {xsd:decimal}?,
+
+  # XXX: undocumented
+  attribute poll_period {xsd:unsignedInt}?,
+
+  # XXX: undocumented
+  attribute recovery {"reboot"|"shutdown"|"watchdog"|"none"}?
+
+load_15min =
+  element load_15min {
+    common_resource?
+  }
+
+memory_used =
+  element memory_used {
+    common_resource?
+  }
+
+system =
+  element system {
+    (load_15min?
+     &memory_used?)
+  }
+
+resources =
+  element resources {
+    system?
+  }
+
+## TOTEM ######################################################################
+
+totem =
+  ## In this configuration section, one can
+  ## adjust totem protocol.
+  element totem {
+    # CFG: corosync.conf
+    ## This configuration option is only relevant
+    ## when no *nodeid* option within *nodelist* section is specified.  Some
+    ## corosync clients require a signed 32bit nodeid that is greater than
+    ## zero however, by default, corosync uses all 32 bits of the IPv4 address
+    ## space when generating a nodeid.
+    ## Set this option to 'yes' to force the high bit to be zero and therefor
+    ## ensure the nodeid is a positive signed 32bit integer.
+    [ a:defaultValue = "no"
+      a4doc:discretion-hint =
+        "The clusters behavior is undefined if this option is enabled"
+        ~ " on only a subset of the cluster (for example during a rolling"
+        ~ " upgrade)." ]
+    attribute clear_node_high_bit {"no"|"yes"}?,
+
+    # CFG: corosync.conf, cluster.conf
+    # NOTE: not a direct mapping in cluster.conf (top-level tag instead)
+    ## This specifies the name of cluster and it's
+    ## used for automatic generating of multicast address.
+    attribute cluster_name {text}?,
+
+    # XXX: implied check
+    # CFG: corosync.conf, cluster.conf
+    ## This timeout specifies in milliseconds how
+    ## long to wait for consensus to be achieved before starting a new round
+    ## of membership configuration.  The minimum value for *consensus* must be
+    ## 1.2 x *token*.
+    ##
+    ## This value will be automatically calculated at 1.2 x *token* if
+    ## the user doesn't specify a *consensus* value.
+    ##
+    ## For two node clusters, a *consensus* larger than the *join* timeout but
+    ## less than *token* is safe.  For three-node or larger clusters,
+    ## *consensus* should be larger than *token*.  There is an increasing risk
+    ## of odd membership changes, which still guarantee virtual synchrony,
+    ## as node count grows if *consensus* is less than *token*.
+    [ a:defaultValue = "1200" ]
+    attribute consensus {xsd:unsignedInt}?,
+
+    # XXX: missing nss?
+    # CFG: corosync.conf
+    ## This specifies which cipher should be used
+    ## to encrypt all messages.
+    [ a:defaultValue = "aes256" ]
+    attribute crypto_cipher {"3des"|"aes128"|"aes192"|"aes256"|"none"}?,
+
+    # XXX: undocumented
+    attribute crypto_compat {"2.0"|"2.2"}?,
+
+    # CFG: corosync.conf
+    ## This specifies which HMAC authentication
+    ## should be used to authenticate all messages.
+    [ a:defaultValue = "sha1" ]
+    attribute crypto_hash {"none"|"md5"|"sha1"|"sha256"|"sha384"|"sha512"}?,
+
+    # XXX: undocumented
+    attribute crypto_type {"3des"|"aes128"|"aes192"|"aes256"|"nss"}?,
+
+    # CFG: corosync.conf
+    ## This timeout specifies in milliseconds how
+    ## long to wait before checking that a network interface is back up after
+    ## it has been downed.
+    [ a:defaultValue = "1000" ]
+    attribute downcheck {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This constant specifies how many rotations
+    ## of the token without receiving any of the messages when messages should
+    ## be received may occur before a new configuration is formed.
+    [ a:defaultValue = "2500" ]
+    attribute fail_recv_const {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## Configures the optional HeartBeating
+    ## mechanism for faster failure detection. Keep in mind that engaging this
+    ## mechanism in lossy networks could cause faulty loss declaration as
+    ## the mechanism relies on the network for heartbeating.
+    ##
+    ## So as a rule of thumb use this mechanism if you require improved
+    ## failure in low to medium utilized networks.
+    ##
+    ## This constant specifies the number of heartbeat failures the system
+    ## should tolerate before declaring heartbeat failure, e.g., 3.
+    ## Also if this value is not set or is 0, the heartbeat mechanism is
+    ## not engaged in the system and token rotation is the method of failure
+    ## detection.  Zero disables the mechanism.
+    [ a:defaultValue = "0" ]
+    attribute heartbeat_failures_allowed {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## This timeout specifies in milliseconds
+    ## how long the token should be held by the representative when
+    ## the protocol is under low utilization.
+    [ a:defaultValue = "180"
+      a4doc:danger-hint =
+        "It is not recommended to override this value without guidance"
+        ~ " from the corosync community." ]
+    attribute hold {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This timeout specifies in milliseconds how
+    ## long to wait for join messages in the membership protocol.
+    [ a:defaultValue = "50" ]
+    attribute join {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This constant specifies the maximum number
+    ## of messages that may be sent by one processor on receipt of the token.
+    ## The *max_messages* parameter is limited to 256000 / *netmtu* to prevent
+    ## overflow of the kernel transmit buffers.
+    [ a:defaultValue = "17" ]
+    attribute max_messages {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## This constant specifies in milliseconds
+    ## the approximate delay that your network takes to transport one packet
+    ## from one machine to another.  This value is to be set by system engineers
+    ## and please don't change it if not sure as this effects the failure
+    ## detection mechanism using heartbeat.
+    [ a:defaultValue = "50" ]
+    attribute max_network_delay {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## This timeout specifies in milliseconds how
+    ## long to wait before checking for a partition when no multicast traffic
+    ## is being sent.  If multicast traffic is being sent, the merge detection
+    ## happens automatically as a function of the protocol.
+    [ a:defaultValue = "200" ]
+    attribute merge {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This constant defines the maximum number
+    ## of times on receipt of a token a message is checked for retransmission
+    ## before a retransmission occurs.  This parameter is useful to modify for
+    ## switches that delay multicast packets compared to unicast packets.
+    ## The default setting works well for nearly all modern switches.
+    [ a:defaultValue = "5" ]
+    attribute miss_count_const {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the network maximum transmit
+    ## unit.  To set this value beyond 1500, the regular frame MTU, requires
+    ## ethernet devices that support large, or also called jumbo, frames.
+    ## If any device in the network doesn't support large frames, the protocol
+    ## will not operate properly.  The hosts must also have their mtu size set
+    ## from 1500 to whatever frame size is specified here.
+    ##
+    ## Please note that while some NICs or
+    ## switches claim large frame support, they support '9000' MTU as
+    ## the maximum frame size including the IP header.  Setting the *netmtu*
+    ## and host MTUs to '9000' will cause totem to use the full 9000 bytes
+    ## of the frame.  Then Linux will add an 18byte header moving the full
+    ## frame size to 9018.  As a result some hardware will not operate properly
+    ## with this size of data.  A *netmtu* of '8982' seems to work for the few
+    ## large frame devices that have been tested.  Some manufacturers claim
+    ## large frame support when in fact they support frame sizes of 4500 bytes.
+    ##
+    ## When sending multicast traffic, if the network frequently reconfigures,
+    ## chances are that some device in the network doesn't support large frames.
+    ##
+    ## Choose hardware carefully if intending to use large frame support.
+    [ a:defaultValue = "1500" ]
+    attribute netmtu {xsd:unsignedInt}?,
+
+    # XXX: undocumented
+    attribute nodeid {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## This specifies the time in milliseconds
+    ## to check if the failed ring can be auto-recovered.
+    [ a:defaultValue = "1000" ]
+    attribute rrp_autorecovery_check_timeout {xsd:unsignedInt}?,
+
+    # XXX: implied check: count(interface) <= 4 (2 active + 2 passive)
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the mode of redundant ring.
+    ## Active replication ('active') offers slightly lower latency from
+    ## transmit to delivery in faulty network environments but with less
+    ## performance.  Passive replication ('passive') may nearly double
+    ## the speed of the totem protocol if it doesn't become CPU bound.
+    ## The remaining option is 'none', in which case only one network
+    ## interface will be used to operate the totem protocol.
+    ##
+    ## If only one *interface* section is specified, 'none' is automatically
+    ## chosen.  If multiple *interface* sections are specified, only 'active'
+    ## or 'passive' may be chosen.
+    ##
+    ## The maximum number of *interface* sections that is allowed for either
+    ## mode ('active' or 'passive') is 2.
+    attribute rrp_mode {"active"|"none"|"passive"}?,
+
+    # CFG: corosync.conf
+    ## This specifies the number of times
+    ## a problem is detected with multicast before setting the link faulty for
+    ## 'passive' *rrp_mode*.  This variable is unused in 'active' *rrp_mode*.
+    ##
+    ## The default is 10 x *rrp_problem_count_threshold*.
+    attribute rrp_problem_count_mcast_threshold {xsd:unsignedInt}?,
+
+    # XXX: implied check
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the number of times
+    ## a problem is detected with a link before setting the link faulty.
+    ## Once a link is set faulty, no more data is transmitted upon it.  Also,
+    ## the problem counter is no longer decremented when the problem count
+    ## timeout expires.
+    ##
+    ## A problem is detected whenever all tokens from the proceeding
+    ## processor have not been received within the *rrp_token_expired_timeout*.
+    ## The *rrp_problem_count_threshold* x *rrp_token_expired_timeout* should be
+    ## at least 50 milliseconds less than the *token* timeout, or a complete
+    ## reconfiguration may occur.
+    [ a:defaultValue = "10" ]
+    attribute rrp_problem_count_threshold {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## This specifies the time in milliseconds
+    ## to wait before decrementing the problem count by 1 for a particular ring
+    ## to ensure a link is not marked faulty for transient network failures.
+    [ a:defaultValue = "2000" ]
+    attribute rrp_problem_count_timeout {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## This specifies the time in milliseconds
+    ## to increment the problem counter for the redundant ring protocol after
+    ## not having received a token from all rings for a particular processor.
+    ##
+    ## This value will automatically be calculated from the *token* timeout
+    ## and *problem_count_threshold* but may be overridden.
+    [ a:defaultValue = "47"
+      a4doc:danger-hint =
+        "It is not recommended to override this value without guidance"
+        ~ " from the corosync community." ]
+    attribute rrp_token_expired_timeout {xsd:unsignedInt}?,
+
+    # XXX: implied check/migration to current items
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies that HMAC/SHA1 authentication should be used
+    ## to authenticate all messages.  It further specifies that all data
+    ## should be encrypted with the nss library and aes256 encryption
+    ## algorithm to protect data from eavesdropping.
+    ##
+    ## Enabling this option adds a encryption header to every message sent
+    ## by totem which reduces total throughput.  Also encryption and
+    ## authentication consume extra CPU cycles in corosync.
+    [ a:defaultValue = "on"
+      a4doc:deprecation-hint =
+      "It's recomended to use combination of *crypto_cipher* and *crypto_hash*."
+    ]
+    attribute secauth {"off"|"on"}?,
+
+    # CFG: corosync.conf
+    ## This timeout specifies in milliseconds
+    ## an upper range between 0 and *send_join* to wait before sending a join
+    ## message.  For configurations with less than 32 nodes, this parameter
+    ## is not necessary.  For larger rings, this parameter is necessary
+    ## to ensure the NIC is not overflowed with join messages on formation of
+    ## a new ring.  A reasonable value for large rings (128 nodes) would be
+    ## __80__msec.  Other timer values must also change if this value
+    ## is changed.
+    [ a:defaultValue = "0"
+      a4doc:danger-hint =
+        "Seek advice from the corosync mailing list if trying to run"
+        ~ " larger configurations." ]
+    attribute send_join {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This constant specifies how many rotations
+    ## of the token without any multicast traffic should occur before the hold
+    ## timer is started.
+    [ a:defaultValue = "30" ]
+    attribute seqno_unchanged_const {xsd:unsignedInt}?,
+
+    # XXX: undocumented
+    attribute threads {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This timeout specifies a period in
+    ## milliseconds until a token loss is declared after not receiving
+    ## a token.  This is the time spent detecting a failure of a processor
+    ## in the current configuration.  Reforming a new configuration takes
+    ## about 50 milliseconds in addition to this timeout.
+    [ a:defaultValue = "1000" ]
+    attribute token {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## This timeout specifies a period in
+    ## milliseconds without receiving a token after which the token is
+    ## retransmitted.  This will be automatically calculated if *token* is
+    ## modified.
+    [ a:defaultValue = "238"
+      a4doc:danger-hint =
+        "It is not recommended to override this value without guidance"
+        ~ " from the corosync community." ]
+    attribute token_retransmit {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This value identifies how many token
+    ## retransmits should be attempted before forming a new configuration.
+    ## If this value is set, retransmit and hold will be automatically
+    ## calculated from *retransmits_before_loss* and *token*.
+    [ a:defaultValue = "4" ]
+    attribute token_retransmits_before_loss_const {xsd:unsignedInt}?,
+
+    # CFG: corosync.conf
+    ## This option controls the transport
+    ## mechanism used.  If the interface to which corosync is binding is
+    ## an RDMA interface such as RoCEE or Infiniband, the 'iba' parameter
+    ## may be specified.  To avoid the use of multicast entirely, a unicast
+    ## transport parameter 'udpu' can be specified.  This requires specifying
+    ## the list of members that could potentially make up the membership
+    ## in *nodelist* section before deployment.
+    [ a:defaultValue = "udp" ]
+    attribute transport {"iba"|"udp"|"udpu"}?,
+
+    # CFG: corosync.conf
+    ## This specifies the version of
+    ## the configuration file.  Currently the only valid value for this
+    ## option is '2'.
+    attribute version {xsd:unsignedInt},
+
+    # CFG: corosync.conf
+    ## This option controls the virtual
+    ## synchrony filter type used to identify a primary component.
+    ## The preferred choice is YKD dynamic linear voting ('ykd'), however, for
+    ## clusters larger than 32 nodes YKD consumes a lot of memory.  For large
+    ## scale clusters that are created by changing the MAX_PROCESSORS_COUNT
+    ## #define in the C code totem.h file, the virtual synchrony filter 'none'
+    ## is recommended but then AMF and DLCK services (which are currently
+    ## experimental) are not safe for use.
+    [ a:defaultValue = "ykd" ]
+    attribute vsftype {"none"|"ykd"}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This constant specifies the maximum number
+    ## of messages that may be sent on one token rotation.  If all processors
+    ## perform equally well, this value could be large ('300'), which would
+    ## introduce higher latency from origination to delivery for very large
+    ## rings.  To reduce latency in large rings (16+), the default is a safe
+    ## compromise.  If 1 or more slow processor(s) are present among fast
+    ## processors, *window_size* should be no larger than 256000 / *netmtu*
+    ## to avoid overflow of the kernel receive buffers.  The user is notified
+    ## of this by the display of a retransmit list in the notification logs.
+    ## There is no loss of data, but performance is reduced when these errors
+    ## occur.
+    [ a:defaultValue = "50" ]
+    attribute window_size {xsd:unsignedInt}?,
+
+    interface*
+  }
+
+interface =
+  element interface {
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the network address
+    ## the corosync executive should bind to.
+    ## *bindnetaddr* should be an IP address configured on the system, or
+    ## a network address.
+    ##
+    ## For example, if the local interface is `192.168.5.92` with netmask
+    ## `255.255.255.0`, you should set *bindnetaddr* to `192.168.5.92` or
+    ## `192.168.5.0`.   If the local interface is `192.168.5.92` with netmask
+    ## `255.255.255.192`, set *bindnetaddr* to `192.168.5.92` or `192.168.5.64`,
+    ## and so forth.
+    ##
+    ## This may also be an IPv6 address, in which case IPv6 networking will be
+    ## used.  In this case, the exact address must be specified and there is no
+    ## automatic selection of the network interface within a specific subnet
+    ## as with IPv4.
+    ##
+    ## If IPv6 networking is used, *nodeid* options within *nodelist* section
+    ## must be specified.
+    attribute bindnetaddr {text}?,
+
+    # CFG: corosync.conf
+    ## If this is set to 'yes', the broadcast
+    ## address will be used for communication.  If this option is set,
+    ## *mcastaddr* should not be set.
+    [ a:defaultValue = "no" ]
+    attribute broadcast {"no"|"yes"}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This is the multicast address used
+    ## by corosync executive.  The default should work for most networks, but
+    ## the network administrator should be queried about a multicast address
+    ## to use.  Avoid `224.x.x.x` because this is a "config" multicast address.
+    ##
+    ## This may also be an IPv6 multicast address, in which case IPv6 networking
+    ## will be used.  If IPv6 networking is used, *nodeid* options within
+    ## *nodelist* section must be specified.
+    ##
+    ## It's not needed to use this option if *cluster_name* option in
+    ## *totem* section is used.  If both options are used, *mcastaddr* has
+    ## higher priority.
+    attribute mcastaddr {text}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the UDP port number.
+    ## It is possible to use the same multicast address on a network with
+    ## the corosync services configured for different UDP ports.  Please note
+    ## corosync uses two UDP ports *mcastport* (for mcast receives) and
+    ## *mcastport* - 1 (for mcast sends).  If you have multiple  clusters
+    ## on the same network using the same *mcastaddr*, please configure
+    ## the **mcastport**s with a gap.
+    attribute mcastport {xsd:unsignedShort}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the ring number for
+    ## the interface.  When using the redundant ring protocol, each interface
+    ## should specify separate ring numbers to uniquely identify to
+    ## the membership protocol which interface to use for which redundant ring.
+    ## The *ringnumber* must start at '0'.
+    attribute ringnumber {xsd:unsignedByte}?,
+
+    # CFG: corosync.conf, cluster.conf
+    ## This specifies the Time To Live (TTL).
+    ## If you run your cluster on a routed network, the default of '1' will
+    ## be too small.  This option provides a way to increase this up to '255'.
+    ## The valid range is '0..255'.  Note that this is only valid on multicast
+    ## transport types.
+    [ a:defaultValue = "1" ]
+    attribute ttl {xsd:unsignedByte}?
+  }
+
+## UIDGID #####################################################################
+
+uidgid =
+  element uidgid {
+    # CFG: corosync.conf, cluster.conf
+    # XXX: undocumented
+    attribute uid {text}?,
+
+    # CFG: corosync.conf, cluster.conf
+    # XXX: undocumented
+    attribute gid {text}?
+  }
+
+
+# vim: et:ts=2:sw=2
diff --git a/conf/lenses/corosync.aug b/conf/lenses/corosync.aug
deleted file mode 100644
index 1418c30..0000000
--- a/conf/lenses/corosync.aug
+++ /dev/null
@@ -1,181 +0,0 @@
-(* Process /etc/corosync/corosync.conf                             *)
-(* The lens is based on the corosync.conf(5) man page     *)
-module Corosync =
-
-autoload xfm
-
-let comment = Util.comment
-let empty = Util.empty
-let dels = Util.del_str
-let eol = Util.eol
-
-let ws = del /[ \t]+/ " "
-let wsc = del /:[ \t]+/ ": "
-let indent = del /[ \t]*/ ""
-(* We require that braces are always followed by a newline *)
-let obr = del /\{([ \t]*)\n/ "{\n"
-let cbr = del /[ \t]*}[ \t]*\n/ "}\n"
-
-let ikey (k:regexp) = indent . key k
-
-let section (n:regexp) (b:lens) =
-  [ ikey n . ws . obr . (b|empty|comment)* . cbr ]
-
-let kv (k:regexp) (v:regexp) =
-  [ ikey k .  wsc . store v . eol ]
-
-(* FIXME: it would be much more concise to write                       *)
-(* [ key k . ws . (bare | quoted) ]                                    *)
-(* but the typechecker trips over that                                 *)
-let qstr (k:regexp) =
-  let delq = del /['"]/ "\"" in
-  let bare = del /["']?/ "" . store /[^"' \t\n]+/ . del /["']?/ "" in
-  let quoted = delq . store /.*[ \t].*/ . delq in
-  [ ikey k . wsc . bare . eol ]
- |[ ikey k . wsc . quoted . eol ]
-
-(* A integer subsection *)
-let interface =
-  let setting =
-    kv "ringnumber" Rx.integer
-    |kv "mcastport" Rx.integer
-    |kv "ttl" Rx.integer
-    |qstr /bindnetaddr|mcastaddr/ in
-  section "interface" setting
-
-(* The totem section *)
-let totem =
-  let setting =
-    kv "clear_node_high_bit" /yes|no/
-    |kv "rrp_mode" /none|active|passive/
-    |kv "vsftype" /none|ykd/
-    |kv "secauth" /on|off/
-    |kv "crypto_type" /nss|aes256|aes192|aes128|3des/
-    |kv "crypto_cipher" /none|nss|aes256|aes192|aes128|3des/
-    |kv "crypto_hash" /none|md5|sha1|sha256|sha384|sha512/
-    |kv "transport" /udp|iba/
-    |kv "version" Rx.integer
-    |kv "nodeid" Rx.integer
-    |kv "threads" Rx.integer
-    |kv "netmtu" Rx.integer
-    |kv "token" Rx.integer
-    |kv "token_retransmit" Rx.integer
-    |kv "hold" Rx.integer
-    |kv "token_retransmits_before_loss_const" Rx.integer
-    |kv "join" Rx.integer
-    |kv "send_join" Rx.integer
-    |kv "consensus" Rx.integer
-    |kv "merge" Rx.integer
-    |kv "downcheck" Rx.integer
-    |kv "fail_to_recv_const" Rx.integer
-    |kv "seqno_unchanged_const" Rx.integer
-    |kv "heartbeat_failures_allowed" Rx.integer
-    |kv "max_network_delay" Rx.integer
-    |kv "max_messages" Rx.integer
-    |kv "window_size" Rx.integer
-    |kv "rrp_problem_count_timeout" Rx.integer
-    |kv "rrp_problem_count_threshold" Rx.integer
-    |kv "rrp_token_expired_timeout" Rx.integer
-    |interface in
-  section "totem" setting
-
-let common_logging =
-   kv "to_syslog" /yes|no|on|off/
-   |kv "to_stderr" /yes|no|on|off/
-   |kv "to_logfile" /yes|no|on|off/
-   |kv "debug" /yes|no|on|off|trace/
-   |kv "logfile_priority" /alert|crit|debug|emerg|err|info|notice|warning/
-   |kv "syslog_priority" /alert|crit|debug|emerg|err|info|notice|warning/
-   |kv "syslog_facility" /daemon|local0|local1|local2|local3|local4|local5|local6|local7/
-   |qstr /logfile|tags/
-
-(* A logger_subsys subsection *)
-let logger_subsys =
-  let setting =
-    qstr /subsys/
-   |common_logging in
-  section "logger_subsys" setting
-
-
-(* The logging section *)
-let logging =
-  let setting =
-   kv "fileline" /yes|no|on|off/
-   |kv "function_name" /yes|no|on|off/
-   |kv "timestamp" /yes|no|on|off/
-   |common_logging
-   |logger_subsys in
-  section "logging" setting
-
-
-(* The resource section *)
-let common_resource =
-   kv "max" Rx.decimal
-   |kv "poll_period" Rx.integer
-   |kv "recovery" /reboot|shutdown|watchdog|none/
-
-let memory_used =
-    let setting =
-    common_resource in
-  section "memory_used" setting
-
-
-let load_15min =
-    let setting =
-    common_resource in
-  section "load_15min" setting
-
-let system =
-    let setting =
-     load_15min
-     |memory_used in
-   section "system" setting
-
-(* The resources section *)
-let resources =
-  let setting =
-    system in
-  section "resources" setting
-
-(* The quorum section *)
-let quorum =
-  let setting =
-   qstr /provider/
-   |kv "expected_votes" Rx.integer
-   |kv "votes" Rx.integer
-   |kv "wait_for_all" Rx.integer
-   |kv "last_man_standing" Rx.integer
-   |kv "last_man_standing_window" Rx.integer
-   |kv "auto_tie_breaker" Rx.integer
-   |kv "two_node" Rx.integer in
-  section "quorum" setting
-
-(* The service section *)
-let service =
-  let setting =
-   qstr /name|ver/ in
-  section "service" setting
-
-(* The uidgid section *)
-let uidgid =
-  let setting =
-   qstr /uid|gid/ in
-  section "uidgid" setting
-
-(* The node section *)
-let node =
-  let setting =
-   qstr /ring[0-9]_addr/
-   |kv "nodeid" Rx.integer
-   |kv "quorum_votes" Rx.integer in
-  section "node" setting
-
-(* The nodelist section *)
-let nodelist =
-  let setting =
-    node in
-  section "nodelist" setting
-
-let lns = (comment|empty|totem|quorum|logging|resources|service|uidgid|nodelist)*
-
-let xfm = transform lns (incl "/etc/corosync/corosync.conf")
diff --git a/conf/lex.py b/conf/lex.py
new file mode 100644
index 0000000..137d28f
--- /dev/null
+++ b/conf/lex.py
@@ -0,0 +1,1069 @@
+# -----------------------------------------------------------------------------
+# ply: lex.py
+#
+# Copyright (C) 2001-2011,
+# David M. Beazley (Dabeaz LLC)
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+# 
+# * Redistributions of source code must retain the above copyright notice,
+#   this list of conditions and the following disclaimer.  
+# * Redistributions in binary form must reproduce the above copyright notice, 
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.  
+# * Neither the name of the David Beazley or Dabeaz LLC may be used to
+#   endorse or promote products derived from this software without
+#  specific prior written permission. 
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# -----------------------------------------------------------------------------
+
+__version__    = "3.5"
+__tabversion__ = "3.5"       # Version of table file used
+
+import re, sys, types, copy, os
+
+# This tuple contains known string types
+try:
+    # Python 2.6
+    StringTypes = (types.StringType, types.UnicodeType)
+except AttributeError:
+    # Python 3.0
+    StringTypes = (str, bytes)
+
+# Extract the code attribute of a function. Different implementations
+# are for Python 2/3 compatibility.
+
+if sys.version_info[0] < 3:
+    def func_code(f):
+        return f.func_code
+else:
+    def func_code(f):
+        return f.__code__
+
+# This regular expression is used to match valid token names
+_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$')
+
+# Exception thrown when invalid token encountered and no default error
+# handler is defined.
+
+class LexError(Exception):
+    def __init__(self,message,s):
+         self.args = (message,)
+         self.text = s
+
+# Token class.  This class is used to represent the tokens produced.
+class LexToken(object):
+    def __str__(self):
+        return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos)
+    def __repr__(self):
+        return str(self)
+
+# This object is a stand-in for a logging object created by the 
+# logging module.  
+
+class PlyLogger(object):
+    def __init__(self,f):
+        self.f = f
+    def critical(self,msg,*args,**kwargs):
+        self.f.write((msg % args) + "\n")
+
+    def warning(self,msg,*args,**kwargs):
+        self.f.write("WARNING: "+ (msg % args) + "\n")
+
+    def error(self,msg,*args,**kwargs):
+        self.f.write("ERROR: " + (msg % args) + "\n")
+
+    info = critical
+    debug = critical
+
+# Null logger is used when no output is generated. Does nothing.
+class NullLogger(object):
+    def __getattribute__(self,name):
+        return self
+    def __call__(self,*args,**kwargs):
+        return self
+
+# -----------------------------------------------------------------------------
+#                        === Lexing Engine ===
+#
+# The following Lexer class implements the lexer runtime.   There are only
+# a few public methods and attributes:
+#
+#    input()          -  Store a new string in the lexer
+#    token()          -  Get the next token
+#    clone()          -  Clone the lexer
+#
+#    lineno           -  Current line number
+#    lexpos           -  Current position in the input string
+# -----------------------------------------------------------------------------
+
+class Lexer:
+    def __init__(self):
+        self.lexre = None             # Master regular expression. This is a list of
+                                      # tuples (re,findex) where re is a compiled
+                                      # regular expression and findex is a list
+                                      # mapping regex group numbers to rules
+        self.lexretext = None         # Current regular expression strings
+        self.lexstatere = {}          # Dictionary mapping lexer states to master regexs
+        self.lexstateretext = {}      # Dictionary mapping lexer states to regex strings
+        self.lexstaterenames = {}     # Dictionary mapping lexer states to symbol names
+        self.lexstate = "INITIAL"     # Current lexer state
+        self.lexstatestack = []       # Stack of lexer states
+        self.lexstateinfo = None      # State information
+        self.lexstateignore = {}      # Dictionary of ignored characters for each state
+        self.lexstateerrorf = {}      # Dictionary of error functions for each state
+        self.lexreflags = 0           # Optional re compile flags
+        self.lexdata = None           # Actual input data (as a string)
+        self.lexpos = 0               # Current position in input text
+        self.lexlen = 0               # Length of the input text
+        self.lexerrorf = None         # Error rule (if any)
+        self.lextokens = None         # List of valid tokens
+        self.lexignore = ""           # Ignored characters
+        self.lexliterals = ""         # Literal characters that can be passed through
+        self.lexmodule = None         # Module
+        self.lineno = 1               # Current line number
+        self.lexoptimize = 0          # Optimized mode
+
+    def clone(self,object=None):
+        c = copy.copy(self)
+
+        # If the object parameter has been supplied, it means we are attaching the
+        # lexer to a new object.  In this case, we have to rebind all methods in
+        # the lexstatere and lexstateerrorf tables.
+
+        if object:
+            newtab = { }
+            for key, ritem in self.lexstatere.items():
+                newre = []
+                for cre, findex in ritem:
+                     newfindex = []
+                     for f in findex:
+                         if not f or not f[0]:
+                             newfindex.append(f)
+                             continue
+                         newfindex.append((getattr(object,f[0].__name__),f[1]))
+                newre.append((cre,newfindex))
+                newtab[key] = newre
+            c.lexstatere = newtab
+            c.lexstateerrorf = { }
+            for key, ef in self.lexstateerrorf.items():
+                c.lexstateerrorf[key] = getattr(object,ef.__name__)
+            c.lexmodule = object
+        return c
+
+    # ------------------------------------------------------------
+    # writetab() - Write lexer information to a table file
+    # ------------------------------------------------------------
+    def writetab(self,tabfile,outputdir=""):
+        if isinstance(tabfile,types.ModuleType):
+            return
+        basetabfilename = tabfile.split(".")[-1]
+        filename = os.path.join(outputdir,basetabfilename)+".py"
+        tf = open(filename,"w")
+        tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__))
+        tf.write("_tabversion   = %s\n" % repr(__tabversion__))
+        tf.write("_lextokens    = %s\n" % repr(self.lextokens))
+        tf.write("_lexreflags   = %s\n" % repr(self.lexreflags))
+        tf.write("_lexliterals  = %s\n" % repr(self.lexliterals))
+        tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo))
+
+        tabre = { }
+        # Collect all functions in the initial state
+        initial = self.lexstatere["INITIAL"]
+        initialfuncs = []
+        for part in initial:
+            for f in part[1]:
+                if f and f[0]:
+                    initialfuncs.append(f)
+
+        for key, lre in self.lexstatere.items():
+             titem = []
+             for i in range(len(lre)):
+                  titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i])))
+             tabre[key] = titem
+
+        tf.write("_lexstatere   = %s\n" % repr(tabre))
+        tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore))
+
+        taberr = { }
+        for key, ef in self.lexstateerrorf.items():
+             if ef:
+                  taberr[key] = ef.__name__
+             else:
+                  taberr[key] = None
+        tf.write("_lexstateerrorf = %s\n" % repr(taberr))
+        tf.close()
+
+    # ------------------------------------------------------------
+    # readtab() - Read lexer information from a tab file
+    # ------------------------------------------------------------
+    def readtab(self,tabfile,fdict):
+        if isinstance(tabfile,types.ModuleType):
+            lextab = tabfile
+        else:
+            if sys.version_info[0] < 3:
+                exec("import %s as lextab" % tabfile)
+            else:
+                env = { }
+                exec("import %s as lextab" % tabfile, env,env)
+                lextab = env['lextab']
+
+        if getattr(lextab,"_tabversion","0.0") != __tabversion__:
+            raise ImportError("Inconsistent PLY version")
+
+        self.lextokens      = lextab._lextokens
+        self.lexreflags     = lextab._lexreflags
+        self.lexliterals    = lextab._lexliterals
+        self.lexstateinfo   = lextab._lexstateinfo
+        self.lexstateignore = lextab._lexstateignore
+        self.lexstatere     = { }
+        self.lexstateretext = { }
+        for key,lre in lextab._lexstatere.items():
+             titem = []
+             txtitem = []
+             for i in range(len(lre)):
+                  titem.append((re.compile(lre[i][0],lextab._lexreflags | re.VERBOSE),_names_to_funcs(lre[i][1],fdict)))
+                  txtitem.append(lre[i][0])
+             self.lexstatere[key] = titem
+             self.lexstateretext[key] = txtitem
+        self.lexstateerrorf = { }
+        for key,ef in lextab._lexstateerrorf.items():
+             self.lexstateerrorf[key] = fdict[ef]
+        self.begin('INITIAL')
+
+    # ------------------------------------------------------------
+    # input() - Push a new string into the lexer
+    # ------------------------------------------------------------
+    def input(self,s):
+        # Pull off the first character to see if s looks like a string
+        c = s[:1]
+        if not isinstance(c,StringTypes):
+            raise ValueError("Expected a string")
+        self.lexdata = s
+        self.lexpos = 0
+        self.lexlen = len(s)
+
+    # ------------------------------------------------------------
+    # begin() - Changes the lexing state
+    # ------------------------------------------------------------
+    def begin(self,state):
+        if not state in self.lexstatere:
+            raise ValueError("Undefined state")
+        self.lexre = self.lexstatere[state]
+        self.lexretext = self.lexstateretext[state]
+        self.lexignore = self.lexstateignore.get(state,"")
+        self.lexerrorf = self.lexstateerrorf.get(state,None)
+        self.lexstate = state
+
+    # ------------------------------------------------------------
+    # push_state() - Changes the lexing state and saves old on stack
+    # ------------------------------------------------------------
+    def push_state(self,state):
+        self.lexstatestack.append(self.lexstate)
+        self.begin(state)
+
+    # ------------------------------------------------------------
+    # pop_state() - Restores the previous state
+    # ------------------------------------------------------------
+    def pop_state(self):
+        self.begin(self.lexstatestack.pop())
+
+    # ------------------------------------------------------------
+    # current_state() - Returns the current lexing state
+    # ------------------------------------------------------------
+    def current_state(self):
+        return self.lexstate
+
+    # ------------------------------------------------------------
+    # skip() - Skip ahead n characters
+    # ------------------------------------------------------------
+    def skip(self,n):
+        self.lexpos += n
+
+    # ------------------------------------------------------------
+    # opttoken() - Return the next token from the Lexer
+    #
+    # Note: This function has been carefully implemented to be as fast
+    # as possible.  Don't make changes unless you really know what
+    # you are doing
+    # ------------------------------------------------------------
+    def token(self):
+        # Make local copies of frequently referenced attributes
+        lexpos    = self.lexpos
+        lexlen    = self.lexlen
+        lexignore = self.lexignore
+        lexdata   = self.lexdata
+
+        while lexpos < lexlen:
+            # This code provides some short-circuit code for whitespace, tabs, and other ignored characters
+            if lexdata[lexpos] in lexignore:
+                lexpos += 1
+                continue
+
+            # Look for a regular expression match
+            for lexre,lexindexfunc in self.lexre:
+                m = lexre.match(lexdata,lexpos)
+                if not m: continue
+
+                # Create a token for return
+                tok = LexToken()
+                tok.value = m.group()
+                tok.lineno = self.lineno
+                tok.lexpos = lexpos
+
+                i = m.lastindex
+                func,tok.type = lexindexfunc[i]
+
+                if not func:
+                   # If no token type was set, it's an ignored token
+                   if tok.type:
+                      self.lexpos = m.end()
+                      return tok
+                   else:
+                      lexpos = m.end()
+                      break
+
+                lexpos = m.end()
+
+                # If token is processed by a function, call it
+
+                tok.lexer = self      # Set additional attributes useful in token rules
+                self.lexmatch = m
+                self.lexpos = lexpos
+
+                newtok = func(tok)
+
+                # Every function must return a token, if nothing, we just move to next token
+                if not newtok:
+                    lexpos    = self.lexpos         # This is here in case user has updated lexpos.
+                    lexignore = self.lexignore      # This is here in case there was a state change
+                    break
+
+                # Verify type of the token.  If not in the token map, raise an error
+                if not self.lexoptimize:
+                    if not newtok.type in self.lextokens:
+                        raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % (
+                            func_code(func).co_filename, func_code(func).co_firstlineno,
+                            func.__name__, newtok.type),lexdata[lexpos:])
+
+                return newtok
+            else:
+                # No match, see if in literals
+                if lexdata[lexpos] in self.lexliterals:
+                    tok = LexToken()
+                    tok.value = lexdata[lexpos]
+                    tok.lineno = self.lineno
+                    tok.type = tok.value
+                    tok.lexpos = lexpos
+                    self.lexpos = lexpos + 1
+                    return tok
+
+                # No match. Call t_error() if defined.
+                if self.lexerrorf:
+                    tok = LexToken()
+                    tok.value = self.lexdata[lexpos:]
+                    tok.lineno = self.lineno
+                    tok.type = "error"
+                    tok.lexer = self
+                    tok.lexpos = lexpos
+                    self.lexpos = lexpos
+                    newtok = self.lexerrorf(tok)
+                    if lexpos == self.lexpos:
+                        # Error method didn't change text position at all. This is an error.
+                        raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:])
+                    lexpos = self.lexpos
+                    if not newtok: continue
+                    return newtok
+
+                self.lexpos = lexpos
+                raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:])
+
+        self.lexpos = lexpos + 1
+        if self.lexdata is None:
+             raise RuntimeError("No input string given with input()")
+        return None
+
+    # Iterator interface
+    def __iter__(self):
+        return self
+
+    def next(self):
+        t = self.token()
+        if t is None:
+            raise StopIteration
+        return t
+
+    __next__ = next
+
+# -----------------------------------------------------------------------------
+#                           ==== Lex Builder ===
+#
+# The functions and classes below are used to collect lexing information
+# and build a Lexer object from it.
+# -----------------------------------------------------------------------------
+
+# -----------------------------------------------------------------------------
+# _get_regex(func)
+#
+# Returns the regular expression assigned to a function either as a doc string
+# or as a .regex attribute attached by the @TOKEN decorator.
+# -----------------------------------------------------------------------------
+
+def _get_regex(func):
+    return getattr(func,"regex",func.__doc__)
+
+# -----------------------------------------------------------------------------
+# get_caller_module_dict()
+#
+# This function returns a dictionary containing all of the symbols defined within
+# a caller further down the call stack.  This is used to get the environment
+# associated with the yacc() call if none was provided.
+# -----------------------------------------------------------------------------
+
+def get_caller_module_dict(levels):
+    try:
+        raise RuntimeError
+    except RuntimeError:
+        e,b,t = sys.exc_info()
+        f = t.tb_frame
+        while levels > 0:
+            f = f.f_back                   
+            levels -= 1
+        ldict = f.f_globals.copy()
+        if f.f_globals != f.f_locals:
+            ldict.update(f.f_locals)
+
+        return ldict
+
+# -----------------------------------------------------------------------------
+# _funcs_to_names()
+#
+# Given a list of regular expression functions, this converts it to a list
+# suitable for output to a table file
+# -----------------------------------------------------------------------------
+
+def _funcs_to_names(funclist,namelist):
+    result = []
+    for f,name in zip(funclist,namelist):
+         if f and f[0]:
+             result.append((name, f[1]))
+         else:
+             result.append(f)
+    return result
+
+# -----------------------------------------------------------------------------
+# _names_to_funcs()
+#
+# Given a list of regular expression function names, this converts it back to
+# functions.
+# -----------------------------------------------------------------------------
+
+def _names_to_funcs(namelist,fdict):
+     result = []
+     for n in namelist:
+          if n and n[0]:
+              result.append((fdict[n[0]],n[1]))
+          else:
+              result.append(n)
+     return result
+
+# -----------------------------------------------------------------------------
+# _form_master_re()
+#
+# This function takes a list of all of the regex components and attempts to
+# form the master regular expression.  Given limitations in the Python re
+# module, it may be necessary to break the master regex into separate expressions.
+# -----------------------------------------------------------------------------
+
+def _form_master_re(relist,reflags,ldict,toknames):
+    if not relist: return []
+    regex = "|".join(relist)
+    try:
+        lexre = re.compile(regex,re.VERBOSE | reflags)
+
+        # Build the index to function map for the matching engine
+        lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1)
+        lexindexnames = lexindexfunc[:]
+
+        for f,i in lexre.groupindex.items():
+            handle = ldict.get(f,None)
+            if type(handle) in (types.FunctionType, types.MethodType):
+                lexindexfunc[i] = (handle,toknames[f])
+                lexindexnames[i] = f
+            elif handle is not None:
+                lexindexnames[i] = f
+                if f.find("ignore_") > 0:
+                    lexindexfunc[i] = (None,None)
+                else:
+                    lexindexfunc[i] = (None, toknames[f])
+        
+        return [(lexre,lexindexfunc)],[regex],[lexindexnames]
+    except Exception:
+        m = int(len(relist)/2)
+        if m == 0: m = 1
+        llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames)
+        rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames)
+        return llist+rlist, lre+rre, lnames+rnames
+
+# -----------------------------------------------------------------------------
+# def _statetoken(s,names)
+#
+# Given a declaration name s of the form "t_" and a dictionary whose keys are
+# state names, this function returns a tuple (states,tokenname) where states
+# is a tuple of state names and tokenname is the name of the token.  For example,
+# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM')
+# -----------------------------------------------------------------------------
+
+def _statetoken(s,names):
+    nonstate = 1
+    parts = s.split("_")
+    for i in range(1,len(parts)):
+         if not parts[i] in names and parts[i] != 'ANY': break
+    if i > 1:
+       states = tuple(parts[1:i])
+    else:
+       states = ('INITIAL',)
+
+    if 'ANY' in states:
+       states = tuple(names)
+
+    tokenname = "_".join(parts[i:])
+    return (states,tokenname)
+
+
+# -----------------------------------------------------------------------------
+# LexerReflect()
+#
+# This class represents information needed to build a lexer as extracted from a
+# user's input file.
+# -----------------------------------------------------------------------------
+class LexerReflect(object):
+    def __init__(self,ldict,log=None,reflags=0):
+        self.ldict      = ldict
+        self.error_func = None
+        self.tokens     = []
+        self.reflags    = reflags
+        self.stateinfo  = { 'INITIAL' : 'inclusive'}
+        self.files      = {}
+        self.error      = 0
+
+        if log is None:
+            self.log = PlyLogger(sys.stderr)
+        else:
+            self.log = log
+
+    # Get all of the basic information
+    def get_all(self):
+        self.get_tokens()
+        self.get_literals()
+        self.get_states()
+        self.get_rules()
+        
+    # Validate all of the information
+    def validate_all(self):
+        self.validate_tokens()
+        self.validate_literals()
+        self.validate_rules()
+        return self.error
+
+    # Get the tokens map
+    def get_tokens(self):
+        tokens = self.ldict.get("tokens",None)
+        if not tokens:
+            self.log.error("No token list is defined")
+            self.error = 1
+            return
+
+        if not isinstance(tokens,(list, tuple)):
+            self.log.error("tokens must be a list or tuple")
+            self.error = 1
+            return
+        
+        if not tokens:
+            self.log.error("tokens is empty")
+            self.error = 1
+            return
+
+        self.tokens = tokens
+
+    # Validate the tokens
+    def validate_tokens(self):
+        terminals = {}
+        for n in self.tokens:
+            if not _is_identifier.match(n):
+                self.log.error("Bad token name '%s'",n)
+                self.error = 1
+            if n in terminals:
+                self.log.warning("Token '%s' multiply defined", n)
+            terminals[n] = 1
+
+    # Get the literals specifier
+    def get_literals(self):
+        self.literals = self.ldict.get("literals","")
+        if not self.literals:
+            self.literals = ""
+
+    # Validate literals
+    def validate_literals(self):
+        try:
+            for c in self.literals:
+                if not isinstance(c,StringTypes) or len(c) > 1:
+                    self.log.error("Invalid literal %s. Must be a single character", repr(c))
+                    self.error = 1
+
+        except TypeError:
+            self.log.error("Invalid literals specification. literals must be a sequence of characters")
+            self.error = 1
+
+    def get_states(self):
+        self.states = self.ldict.get("states",None)
+        # Build statemap
+        if self.states:
+             if not isinstance(self.states,(tuple,list)):
+                  self.log.error("states must be defined as a tuple or list")
+                  self.error = 1
+             else:
+                  for s in self.states:
+                        if not isinstance(s,tuple) or len(s) != 2:
+                               self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s))
+                               self.error = 1
+                               continue
+                        name, statetype = s
+                        if not isinstance(name,StringTypes):
+                               self.log.error("State name %s must be a string", repr(name))
+                               self.error = 1
+                               continue
+                        if not (statetype == 'inclusive' or statetype == 'exclusive'):
+                               self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name)
+                               self.error = 1
+                               continue
+                        if name in self.stateinfo:
+                               self.log.error("State '%s' already defined",name)
+                               self.error = 1
+                               continue
+                        self.stateinfo[name] = statetype
+
+    # Get all of the symbols with a t_ prefix and sort them into various
+    # categories (functions, strings, error functions, and ignore characters)
+
+    def get_rules(self):
+        tsymbols = [f for f in self.ldict if f[:2] == 't_' ]
+
+        # Now build up a list of functions and a list of strings
+
+        self.toknames = { }        # Mapping of symbols to token names
+        self.funcsym =  { }        # Symbols defined as functions
+        self.strsym =   { }        # Symbols defined as strings
+        self.ignore   = { }        # Ignore strings by state
+        self.errorf   = { }        # Error functions by state
+
+        for s in self.stateinfo:
+             self.funcsym[s] = []
+             self.strsym[s] = []
+
+        if len(tsymbols) == 0:
+            self.log.error("No rules of the form t_rulename are defined")
+            self.error = 1
+            return
+
+        for f in tsymbols:
+            t = self.ldict[f]
+            states, tokname = _statetoken(f,self.stateinfo)
+            self.toknames[f] = tokname
+
+            if hasattr(t,"__call__"):
+                if tokname == 'error':
+                    for s in states:
+                        self.errorf[s] = t
+                elif tokname == 'ignore':
+                    line = func_code(t).co_firstlineno
+                    file = func_code(t).co_filename
+                    self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__)
+                    self.error = 1
+                else:
+                    for s in states: 
+                        self.funcsym[s].append((f,t))
+            elif isinstance(t, StringTypes):
+                if tokname == 'ignore':
+                    for s in states:
+                        self.ignore[s] = t
+                    if "\\" in t:
+                        self.log.warning("%s contains a literal backslash '\\'",f)
+
+                elif tokname == 'error':
+                    self.log.error("Rule '%s' must be defined as a function", f)
+                    self.error = 1
+                else:
+                    for s in states: 
+                        self.strsym[s].append((f,t))
+            else:
+                self.log.error("%s not defined as a function or string", f)
+                self.error = 1
+
+        # Sort the functions by line number
+        for f in self.funcsym.values():
+            if sys.version_info[0] < 3:
+                f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno))
+            else:
+                # Python 3.0
+                f.sort(key=lambda x: func_code(x[1]).co_firstlineno)
+
+        # Sort the strings by regular expression length
+        for s in self.strsym.values():
+            if sys.version_info[0] < 3:
+                s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1])))
+            else:
+                # Python 3.0
+                s.sort(key=lambda x: len(x[1]),reverse=True)
+
+    # Validate all of the t_rules collected 
+    def validate_rules(self):
+        for state in self.stateinfo:
+            # Validate all rules defined by functions
+
+            
+
+            for fname, f in self.funcsym[state]:
+                line = func_code(f).co_firstlineno
+                file = func_code(f).co_filename
+                self.files[file] = 1
+
+                tokname = self.toknames[fname]
+                if isinstance(f, types.MethodType):
+                    reqargs = 2
+                else:
+                    reqargs = 1
+                nargs = func_code(f).co_argcount
+                if nargs > reqargs:
+                    self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
+                    self.error = 1
+                    continue
+
+                if nargs < reqargs:
+                    self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
+                    self.error = 1
+                    continue
+
+                if not _get_regex(f):
+                    self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__)
+                    self.error = 1
+                    continue
+
+                try:
+                    c = re.compile("(?P<%s>%s)" % (fname, _get_regex(f)), re.VERBOSE | self.reflags)
+                    if c.match(""):
+                        self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__)
+                        self.error = 1
+                except re.error:
+                    _etype, e, _etrace = sys.exc_info()
+                    self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e)
+                    if '#' in _get_regex(f):
+                        self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__)
+                    self.error = 1
+
+            # Validate all rules defined by strings
+            for name,r in self.strsym[state]:
+                tokname = self.toknames[name]
+                if tokname == 'error':
+                    self.log.error("Rule '%s' must be defined as a function", name)
+                    self.error = 1
+                    continue
+
+                if not tokname in self.tokens and tokname.find("ignore_") < 0:
+                    self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname)
+                    self.error = 1
+                    continue
+
+                try:
+                    c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags)
+                    if (c.match("")):
+                         self.log.error("Regular expression for rule '%s' matches empty string",name)
+                         self.error = 1
+                except re.error:
+                    _etype, e, _etrace = sys.exc_info()
+                    self.log.error("Invalid regular expression for rule '%s'. %s",name,e)
+                    if '#' in r:
+                         self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name)
+                    self.error = 1
+
+            if not self.funcsym[state] and not self.strsym[state]:
+                self.log.error("No rules defined for state '%s'",state)
+                self.error = 1
+
+            # Validate the error function
+            efunc = self.errorf.get(state,None)
+            if efunc:
+                f = efunc
+                line = func_code(f).co_firstlineno
+                file = func_code(f).co_filename
+                self.files[file] = 1
+
+                if isinstance(f, types.MethodType):
+                    reqargs = 2
+                else:
+                    reqargs = 1
+                nargs = func_code(f).co_argcount
+                if nargs > reqargs:
+                    self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__)
+                    self.error = 1
+
+                if nargs < reqargs:
+                    self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__)
+                    self.error = 1
+
+        for f in self.files:
+            self.validate_file(f)
+
+
+    # -----------------------------------------------------------------------------
+    # validate_file()
+    #
+    # This checks to see if there are duplicated t_rulename() functions or strings
+    # in the parser input file.  This is done using a simple regular expression
+    # match on each line in the given file.  
+    # -----------------------------------------------------------------------------
+
+    def validate_file(self,filename):
+        import os.path
+        base,ext = os.path.splitext(filename)
+        if ext != '.py': return         # No idea what the file is. Return OK
+
+        try:
+            f = open(filename)
+            lines = f.readlines()
+            f.close()
+        except IOError:
+            return                      # Couldn't find the file.  Don't worry about it
+
+        fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(')
+        sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=')
+
+        counthash = { }
+        linen = 1
+        for l in lines:
+            m = fre.match(l)
+            if not m:
+                m = sre.match(l)
+            if m:
+                name = m.group(1)
+                prev = counthash.get(name)
+                if not prev:
+                    counthash[name] = linen
+                else:
+                    self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev)
+                    self.error = 1
+            linen += 1
+            
+# -----------------------------------------------------------------------------
+# lex(module)
+#
+# Build all of the regular expression rules from definitions in the supplied module
+# -----------------------------------------------------------------------------
+def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None):
+    global lexer
+    ldict = None
+    stateinfo  = { 'INITIAL' : 'inclusive'}
+    lexobj = Lexer()
+    lexobj.lexoptimize = optimize
+    global token,input
+
+    if errorlog is None:
+        errorlog = PlyLogger(sys.stderr)
+
+    if debug:
+        if debuglog is None:
+            debuglog = PlyLogger(sys.stderr)
+
+    # Get the module dictionary used for the lexer
+    if object: module = object
+
+    if module:
+        _items = [(k,getattr(module,k)) for k in dir(module)]
+        ldict = dict(_items)
+    else:
+        ldict = get_caller_module_dict(2)
+
+    # Collect parser information from the dictionary
+    linfo = LexerReflect(ldict,log=errorlog,reflags=reflags)
+    linfo.get_all()
+    if not optimize:
+        if linfo.validate_all():
+            raise SyntaxError("Can't build lexer")
+
+    if optimize and lextab:
+        try:
+            lexobj.readtab(lextab,ldict)
+            token = lexobj.token
+            input = lexobj.input
+            lexer = lexobj
+            return lexobj
+
+        except ImportError:
+            pass
+
+    # Dump some basic debugging information
+    if debug:
+        debuglog.info("lex: tokens   = %r", linfo.tokens)
+        debuglog.info("lex: literals = %r", linfo.literals)
+        debuglog.info("lex: states   = %r", linfo.stateinfo)
+
+    # Build a dictionary of valid token names
+    lexobj.lextokens = { }
+    for n in linfo.tokens:
+        lexobj.lextokens[n] = 1
+
+    # Get literals specification
+    if isinstance(linfo.literals,(list,tuple)):
+        lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals)
+    else:
+        lexobj.lexliterals = linfo.literals
+
+    # Get the stateinfo dictionary
+    stateinfo = linfo.stateinfo
+
+    regexs = { }
+    # Build the master regular expressions
+    for state in stateinfo:
+        regex_list = []
+
+        # Add rules defined by functions first
+        for fname, f in linfo.funcsym[state]:
+            line = func_code(f).co_firstlineno
+            file = func_code(f).co_filename
+            regex_list.append("(?P<%s>%s)" % (fname,_get_regex(f)))
+            if debug:
+                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,_get_regex(f), state)
+
+        # Now add all of the simple rules
+        for name,r in linfo.strsym[state]:
+            regex_list.append("(?P<%s>%s)" % (name,r))
+            if debug:
+                debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state)
+
+        regexs[state] = regex_list
+
+    # Build the master regular expressions
+
+    if debug:
+        debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====")
+
+    for state in regexs:
+        lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames)
+        lexobj.lexstatere[state] = lexre
+        lexobj.lexstateretext[state] = re_text
+        lexobj.lexstaterenames[state] = re_names
+        if debug:
+            for i in range(len(re_text)):
+                debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i])
+
+    # For inclusive states, we need to add the regular expressions from the INITIAL state
+    for state,stype in stateinfo.items():
+        if state != "INITIAL" and stype == 'inclusive':
+             lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL'])
+             lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL'])
+             lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL'])
+
+    lexobj.lexstateinfo = stateinfo
+    lexobj.lexre = lexobj.lexstatere["INITIAL"]
+    lexobj.lexretext = lexobj.lexstateretext["INITIAL"]
+    lexobj.lexreflags = reflags
+
+    # Set up ignore variables
+    lexobj.lexstateignore = linfo.ignore
+    lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","")
+
+    # Set up error functions
+    lexobj.lexstateerrorf = linfo.errorf
+    lexobj.lexerrorf = linfo.errorf.get("INITIAL",None)
+    if not lexobj.lexerrorf:
+        errorlog.warning("No t_error rule is defined")
+
+    # Check state information for ignore and error rules
+    for s,stype in stateinfo.items():
+        if stype == 'exclusive':
+              if not s in linfo.errorf:
+                   errorlog.warning("No error rule is defined for exclusive state '%s'", s)
+              if not s in linfo.ignore and lexobj.lexignore:
+                   errorlog.warning("No ignore rule is defined for exclusive state '%s'", s)
+        elif stype == 'inclusive':
+              if not s in linfo.errorf:
+                   linfo.errorf[s] = linfo.errorf.get("INITIAL",None)
+              if not s in linfo.ignore:
+                   linfo.ignore[s] = linfo.ignore.get("INITIAL","")
+
+    # Create global versions of the token() and input() functions
+    token = lexobj.token
+    input = lexobj.input
+    lexer = lexobj
+
+    # If in optimize mode, we write the lextab
+    if lextab and optimize:
+        lexobj.writetab(lextab,outputdir)
+
+    return lexobj
+
+# -----------------------------------------------------------------------------
+# runmain()
+#
+# This runs the lexer as a main program
+# -----------------------------------------------------------------------------
+
+def runmain(lexer=None,data=None):
+    if not data:
+        try:
+            filename = sys.argv[1]
+            f = open(filename)
+            data = f.read()
+            f.close()
+        except IndexError:
+            sys.stdout.write("Reading from standard input (type EOF to end):\n")
+            data = sys.stdin.read()
+
+    if lexer:
+        _input = lexer.input
+    else:
+        _input = input
+    _input(data)
+    if lexer:
+        _token = lexer.token
+    else:
+        _token = token
+
+    while 1:
+        tok = _token()
+        if not tok: break
+        sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos))
+
+# -----------------------------------------------------------------------------
+# @TOKEN(regex)
+#
+# This decorator function can be used to set the regex expression on a function
+# when its docstring might need to be set in an alternative way
+# -----------------------------------------------------------------------------
+
+def TOKEN(r):
+    def set_regex(f):
+        if hasattr(r,"__call__"):
+            f.regex = _get_regex(r)
+        else:
+            f.regex = r
+        return f
+    return set_regex
+
+# Alternative spelling of the TOKEN decorator
+Token = TOKEN
+
diff --git a/conf/rnc_tokenize.py b/conf/rnc_tokenize.py
new file mode 100644
index 0000000..3620825
--- /dev/null
+++ b/conf/rnc_tokenize.py
@@ -0,0 +1,275 @@
+#!/usr/bin/env python
+# Define the tokenizer for RELAX NG compact syntax
+# This file released to the Public Domain by David Mertz
+#
+# Extended under revised BSD license by Jan Pokorny (jpokorny@xxxxxxxxxx)
+# Copyright 2013 Red Hat, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# - Redistributions of source code must retain the above copyright notice,
+#   this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright notice,
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.
+# - Neither the name of the Red Hat, Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+# TODO: update positions so they can be used for debugging
+
+import re
+try:
+    from ply import lex
+except ImportError:
+    import lex
+
+
+#
+# tokens declaration
+#
+
+# square (left + right delimiter) together with single-node tree replacement
+pair_rules = tuple(l.replace(' ', '').split(':') for l in '''
+  BEG_BODY  : END_BODY  : BODY
+  BEG_PAREN : END_PAREN : GROUP
+  BEG_ANNO  : END_ANNO  : NS_ANNOTATION
+  '''.strip().splitlines())
+pair_tokens = reduce(lambda a, b: a + tuple(b[:2]), pair_rules, ())
+
+quant_tokens = tuple('''
+  ANY
+  MAYBE
+  SOME
+  '''.split())
+
+immediate_tokens = tuple('''
+  ANNOTATION
+  CHOICE
+  COMMENT
+  DATATAG
+  DEFINE
+  EQUAL
+  INTERLEAVE
+  LITERAL
+  NAME
+  PATTERN
+  SEQ
+  WHITESPACE
+  '''.split()) + quant_tokens
+
+# http://relaxng.org/compact-20021121.html#syntax
+keywords = {
+    'attribute':  'ATTR',
+    'default':    'DEFAULT_NS',
+    'datatypes':  'DATATYPES',
+    'div':        'DIV',
+    'element':    'ELEM',
+    'empty':      'EMPTY',
+    'external':   'EXTERNAL',
+    'grammar':    'GRAMMAR',
+    'include':    'INCLUDE',
+    'inherit':    'INHERIT',
+    'list':       'LIST',
+    'mixed':      'MIXED',
+    'namespace':  'NS',
+    'notAllowed': 'NOTALLOWED',
+    'parent':     'PARENT',
+    'start':      'START',
+    'string':     'STRING',
+    'text':       'TEXT',
+    'token':      'TOKEN',
+}
+
+tokens = immediate_tokens + pair_tokens + tuple(keywords.values())
+
+
+#
+# tokens definition
+#
+
+# RELAX NG/XML datatype NCName is defined in
+#   http://books.xmlschemata.org/relaxng/ch19-77215.html
+#   http://www.w3.org/TR/REC-xml-names/#NT-NCName
+# which can be resolved as
+#   <NCName> ::= <Start> <NonStart>*
+# where
+#   <Start> ::= [A-Z] | "_" | [a-z] | [#xC0-#xD6] | ...
+#     (see http://www.w3.org/TR/REC-xml/#NT-NameStartChar)
+#   <NonStart> ::= <Start> | "-" | "." | [0-9] | #xB7 | ..
+#     (see http://www.w3.org/TR/REC-xml/#NT-NameChar)
+#
+# NOTE: skipping [\u10000-\uEFFFF] as it won't get lex'd (???)
+NCName_start = "(?:[A-Z_a-z]" \
+    u"|[\u00C0-\u00D6]" \
+    u"|[\u00D8-\u00F6]" \
+    u"|[\u00F8-\u02FF]" \
+    u"|[\u0370-\u037D]" \
+    u"|[\u037F-\u1FFF]" \
+    u"|[\u200C-\u200D]" \
+    u"|[\u2070-\u218F]" \
+    u"|[\u2C00-\u2FEF]" \
+    u"|[\u3001-\uD7FF]" \
+    u"|[\uF900-\uFDCF]" \
+    u"|[\uFDF0-\uFFFD]" \
+    ")"
+NCName_nonstart = NCName_start + "|(?:[-.0-9]" \
+    u"|\u00B7"          \
+    u"|[\u0300-\u036F]" \
+    u"|[\u203F-\u2040]" \
+    ")"
+NCName = NCName_start + "(?:" + NCName_nonstart + ")*"
+
+# lex internals
+
+t_ignore = " \t\n"
+
+
+def t_error(t):
+    try:
+        t.lexer.skip(1)
+    except AttributeError:
+        # works in historic version of PLY
+        t.skip(1)
+
+# immediate tokens
+
+t_ANY        = r'\*'
+t_CHOICE     = r'\|'
+t_EQUAL      = r'='
+t_INTERLEAVE = r'&'
+t_MAYBE      = r'\?'
+t_SEQ        = r','
+t_SOME       = r'\+'
+t_WHITESPACE = r'\s+'
+
+
+def t_ANNOTATION(t):
+    r"\#\#[ \t]?.*"
+    t.value = t.value.replace('# ', '#', 1).split('##', 1)[1].rstrip()
+    return t
+
+
+def t_COMMENT(t):
+    r"\#[ \t]?.*"
+    t.value = t.value.replace('# ', '#', 1).split('#', 1)[1].rstrip()
+    return t
+
+
+def t_DATATYPES(t):
+    r"datatypes\s+xsd\s*=\s*.*"
+    t.value = t.value.split('=', 1)[1].strip()
+    return t
+
+
+def t_DATATAG(t):
+    r"xsd:\w+"
+    t.value = t.value.split(':', 1)[1]
+    return t
+
+
+def t_DEFAULT_NS(t):
+    r"default\s+namespace\s*=\s*.*"
+    t.value = t.value.split('=', 1)[1].strip()
+    return t
+
+
+def t_INCLUDE(t):
+    t.value = t.value.split('"', 1)[1][:-1]
+    return t
+t_INCLUDE.__doc__ = r'include\s*"' + NCName + '"'
+
+
+def t_LITERAL(t):
+    r'".+?"(?:\s*[~]\s*".+?")*'
+    t.value = ' '.join(i.strip(' \n"') for i in t.value.split('~'))
+    return t
+
+
+def t_NAME(t):
+    # "In order to use a keyword as an identifier, it must be quoted with \."
+    t.value = t.value[2:]
+    return t
+t_NAME.__doc__ = r"\\[.]" + NCName
+
+
+def t_NS(t):
+    r"namespace\s+.*"
+    t.value = t.value.split(None, 1)[1]
+    return t
+
+
+def t_PATTERN(t):
+    r'{\s*pattern\s*=\s*".*"\s*}'
+    t.value = t.value[:-1].split('=', 1)[1].strip()[1:-1]
+    return t
+
+
+# these two last (and in this order) for reason
+def t_DEFINE(t):
+    t.value = t.value.split('=', 1)[0].strip()
+    return t
+t_DEFINE.__doc__ = NCName + "\s*="
+
+
+def t_ID(t):
+    t.type = keywords.get(t.value, 'NAME')    # Check for keywords
+    return t
+t_ID.__doc__ = NCName
+
+
+# pair tokens
+
+t_BEG_ANNO  = r'\['
+t_END_ANNO  = r'\]'
+t_BEG_BODY  = r'{'
+t_END_BODY  = r'}'
+t_BEG_PAREN = r'\('
+t_END_PAREN = r'\)'
+
+
+#
+# processing
+#
+
+def preprocess(rnc):
+    # 2.2. BOM stripping
+    if len(rnc) >= 2 and ord(rnc[0]) == 0xFE and ord(rnc[1]) == 0xFF:
+        rnc = rnc[2:]
+    # 2.3 Newline normalization
+    rnc = re.sub(r"(?:\n|\r\n?)", "\n", rnc, re.MULTILINE)
+    # TODO: 2.4 Escape interpretation
+    return rnc
+
+
+def token_list(rnc):
+    lex.lex()
+    lex.input(preprocess(rnc))
+    ts = []
+    while 1:
+        t = lex.token()
+        if t is None:
+            break
+        ts.append(t)
+    return ts
+
+
+if __name__ == '__main__':
+    import sys
+    del t_ignore
+    tokens = token_list(sys.stdin.read())
+    print '\n'.join(map(repr, tokens))
diff --git a/conf/rnctree.py b/conf/rnctree.py
new file mode 100755
index 0000000..f691a4f
--- /dev/null
+++ b/conf/rnctree.py
@@ -0,0 +1,609 @@
+#!/usr/bin/env python
+# Convert an RELAX NG compact syntax schema to a Node tree
+# This file released to the Public Domain by David Mertz
+#
+# Extended under revised BSD license by Jan Pokorny (jpokorny@xxxxxxxxxx)
+# Copyright 2013 Red Hat, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# - Redistributions of source code must retain the above copyright notice,
+#   this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright notice,
+#   this list of conditions and the following disclaimer in the documentation
+#   and/or other materials provided with the distribution.
+# - Neither the name of the Red Hat, Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGE.
+
+# Differences when compared to trang output
+#  1. comments placement
+#  2. sometimes superfluous <group>
+#  3. context-free dichotomy (diff conv08.rng.{expected,trang})
+#  plenty of others (it's not the primary goal to achieve 1:1 trang match)
+
+# XXX: each AST node has its own subclass, knows how to XMLize itself, ...?
+
+
+import sys
+from rnc_tokenize import tokens, pair_rules, keywords, token_list
+
+# ONE    ... default cardinality of one
+# DIRECT ... denotes that the usage of NAME is <name>, not <ref name=...>
+quant_tokens_aux = tuple('''
+  DIRECT
+  ONE
+  '''.split())
+
+# AST nodes not directly matching the tokens
+parse_constructs = tuple('''
+  ROOT
+  '''.split()) + tuple(r[2] for r in pair_rules)
+
+for t in tokens + quant_tokens_aux + parse_constructs:
+    globals()[t] = t
+
+keyword_list = keywords.values()
+
+PAIRS = {r[0]: tuple(r[1:]) for r in pair_rules}
+
+TAGS = {
+    ONE:   'group',
+    SOME:  'oneOrMore',
+    MAYBE: 'optional',
+    ANY:   'zeroOrMore',
+    ELEM:  'element',
+    ATTR:  'attribute',
+    NAME:  'ref',
+}
+
+URI_DATATYPES = "http://www.w3.org/2001/XMLSchema-datatypes";
+URI_ANNOTATIONS = "http://relaxng.org/ns/compatibility/annotations/1.0";
+
+DEFAULT_NAMESPACE = None
+DATATYPE_LIB = [0, '"' + URI_DATATYPES + '"']
+OTHER_NAMESPACE = {}
+CONTEXT_FREE = 0
+
+# debugging
+for i, n in enumerate("""
+    D_NOTHING
+    D_TO_NODES
+    D_MATCH_PAIR
+    D_TYPE_BODIES
+    D_NEST_DEFINES
+    D_SCAN_NS
+""".split()):
+    globals()[n] = i and 2 << (i - 1) or 0
+dlist = []
+#dlist.append(D_TO_NODES)
+#dlist.append(D_MATCH_PAIR)
+#dlist.append(D_TYPE_BODIES)
+#dlist.append(D_NEST_DEFINES)
+#dlist.append(D_SCAN_NS)
+debug = reduce(lambda a, b: a | b, dlist, D_NOTHING)
+
+
+def try_debug(what, nodes):
+    if debug & globals().get('D_' + what, D_NOTHING):
+        print what
+        for node in nodes:
+            print node.prettyprint()
+
+
+nodetypes = lambda nl: tuple(map(lambda n: n.type, nl))
+toNodes = lambda toks: map(lambda t: Node(t.type, t.value), toks)
+
+
+class ParseError(SyntaxError):
+    pass
+
+
+class Node(object):
+    __slots__ = ('type', 'value', 'name', 'quant')
+
+    def __iter__(self):
+        yield self
+    __len__ = lambda self: 1
+
+    def __init__(self, type='', value=None, name=None, quant=ONE):
+        self.type = type
+        self.value = value if value is not None else []
+        self.name = name
+        self.quant = quant
+
+    def format(self, indent=0):
+        out = ['  ' * indent + repr(self)]
+        write = out.append
+        if isinstance(self.value, str):
+            if self.type == COMMENT:
+                write('  ' * (1 + indent) + self.value)
+        else:
+            for node in self.value:
+                write(node.format(indent + 1))
+        return '\n'.join(out)
+
+    def prettyprint(self):
+        print self.format()
+
+    def toxml(self):
+        if CONTEXT_FREE:
+            out = []
+            write = out.append
+            write('<?xml version="1.0" encoding="UTF-8"?>')
+            write('<grammar>')
+            self.type = None
+            write(self.xmlnode(1))
+            write('</grammar>')
+            return self.add_ns('\n'.join(out))
+        else:
+            return self.add_ns(self.xmlnode())
+
+    def collect_annot(self, x):
+        ret = {}
+        if isinstance(x.value, basestring):
+            return ret
+
+        name, value = None, None
+        for node in x.value:
+            if node.type != NS_ANNOTATION:
+                break
+            for i, inner in enumerate(node.value):
+                if i % 3 == 0 and inner.type == NAME:
+                    name = inner.value
+                elif i % 3 == 1 and inner.type == DEFINE:
+                    name += ':' + inner.value
+                elif i % 3 == 2 and inner.type == LITERAL:
+                    value = inner.value
+                    if ret.setdefault(name, value) is not value:
+                        assert 0, "redefinition of %s" % name
+                    name, value = None, None
+                elif i % 3 == 0 and i > 0:
+                    break
+                else:
+                    assert 0, "NS_ANNOTATION body does not match"
+        return [n + '="' + v + '"' for n, v in ret.iteritems()]
+
+    def xmlnode(self, indent=0):
+        out = []
+        write = out.append
+        if self.type == ROOT:
+            write('<?xml version="1.0" encoding="UTF-8"?>')
+
+        for i, x in enumerate(self.value):
+            if not isinstance(x, Node):
+                raise TypeError("Unhappy Node.value: " + repr(x))
+            if x.type == START:
+                write('  ' * indent + '<start>')
+                if (x.name is not None):
+                    write('  ' * (indent + 1) + '<ref name="%s"/>' % x.name)
+                else:
+                    write(x.xmlnode(indent + 1))
+                write('  ' * indent + '</start>')
+            elif x.type == DEFINE:
+                write('  ' * indent + '<define name="%s">' % x.name)
+                write(x.xmlnode(indent + 1))
+                write('  ' * indent + '</define>')
+            elif x.type == COMMENT:
+                comments = x.value.split('\n')
+                if len(comments) == 1:
+                    c = ' ' + comments[0] + ' '
+                else:
+                    c = ('\n' + '  ' * (indent + 1)).join([''] + comments + [''])
+                write('  ' * indent + '<!--%s-->' % c)
+            elif x.type == LITERAL:
+                write('  ' * indent + '<value>%s</value>' % x.value)
+            elif x.type == ANNOTATION:
+                write('  ' * indent
+                      + '<a:documentation>%s</a:documentation>' % x.value)
+            elif x.type == INTERLEAVE:
+                write('  ' * indent + '<interleave>')
+                write(x.xmlnode(indent + 1))
+                write('  ' * indent + '</interleave>')
+            elif x.type == CHOICE:
+                write('  ' * indent + '<choice>')
+                write(x.xmlnode(indent + 1))
+                write('  ' * indent + '</choice>')
+            elif x.type in (GROUP, SEQ):
+                write('  ' * indent + '<group>')
+                write(x.xmlnode(indent + 1))
+                write('  ' * indent + '</group>')
+            elif x.type == TEXT:
+                write('  ' * indent + '<text/>')
+            elif x.type == EMPTY:
+                write('  ' * indent + '<empty/>')
+            elif x.type == DATATAG:
+                DATATYPE_LIB[0] = 1     # Use datatypes
+                if x.name is None:      # no paramaters
+                    write('  ' * indent + '<data type="%s"/>' % x.value)
+                else:
+                    write('  ' * indent + '<data type="%s">' % x.name)
+                    p = '<param name="pattern">%s</param>' % x.value
+                    write('  ' * (indent + 1) + p)
+                    write('  ' * indent + '</data>')
+            elif x.type == INCLUDE:
+                write('  ' * indent + '<include href="%s"/>' % x.value)
+            elif x.type == NAME and x.quant == DIRECT:
+                    assert x.type == NAME
+                    write('  ' * indent + '<name>%s</name>' % x.value)
+            elif x.type in (ATTR, ELEM, NAME):
+                a = ('\n' + '  ' * (indent + 3)).join(self.collect_annot(x))
+                name_n_annot = '%s' % (' ' + a).rstrip()
+                name = x.value if x.type == NAME else x.name
+                if name:
+                    name_n_annot = ' name="%s"' % name + name_n_annot
+
+                indent_inner = indent
+                if x.quant != ONE:
+                    write('  ' * indent_inner + '<%s>' % TAGS[x.quant])
+                    indent_inner += 1
+                tag, rest = TAGS[x.type], name_n_annot
+                if x.type == NAME or x.type == ATTR and x.value[0].type == TEXT:
+                    write('  ' * indent_inner + '<%s%s/>' % (tag, rest))
+                else:
+                    write('  ' * indent_inner + '<%s%s>' % (tag, rest))
+                    write(x.xmlnode(indent_inner + 1))
+                    write('  ' * indent_inner + '</%s>' % tag)
+                if x.quant != ONE:
+                    indent_inner -= 1
+                    write('  ' * indent_inner + '</%s>' % TAGS[x.quant])
+
+        return '\n'.join(out)
+
+    def __repr__(self):
+        return "Node(%s,%s,%s)[%d]" % (self.type, self.name,
+                                       self.quant, len(self.value))
+
+    def add_ns(self, xml):
+        "Add namespace attributes to top level element"
+        lines = xml.split('\n')
+        self.nest_annotations(lines)  # annots not allowed before root elem
+        for i, line in enumerate(lines):
+            ltpos = line.find('<')
+            if ltpos >= 0 and line[ltpos + 1] not in ('!', '?'):
+                # We've got an element tag, not PI or comment
+                tail = '>'
+                new = line[:line.find(tail)]
+                if new.endswith('/'):
+                    new = new[:-1]
+                    tail = '/' + tail
+                new += ' xmlns="http://relaxng.org/ns/structure/1.0";'
+                if DEFAULT_NAMESPACE is not None:
+                    new += '\n    ns=%s' % DEFAULT_NAMESPACE
+                if DATATYPE_LIB[0]:
+                    new += '\n    datatypeLibrary=%s' % DATATYPE_LIB[1]
+                for ns, url in OTHER_NAMESPACE.items():
+                    new += '\n    xmlns:%s=%s' % (ns, url)
+                new += tail
+                lines[i] = new
+                break
+        return '\n'.join(lines)
+
+    def nest_annotations(self, lines):
+        "Nest any top annotation within first element"
+        top_annotations = []
+        for i, line in enumerate(lines[:]):
+            if line.find('<a:') >= 0:
+                top_annotations.append(line)
+                del lines[i]
+            else:
+                ltpos = line.find('<')
+                if ltpos >= 0 and line[ltpos + 1] not in ('!', '?'):
+                    break
+        for line in top_annotations:
+            lines.insert(i, '  ' + line)
+
+
+def findmatch(beg, nodes, offset):
+    level = 1
+    end = PAIRS[beg][0]
+    for i, t in enumerate(nodes[offset:]):
+        if t.type == beg:
+            level += 1
+        elif t.type == end:
+            level -= 1
+        if level == 0:
+            return i + offset
+    raise EOFError("No closing token encountered for %s @ %d"
+                   % (beg, offset))
+
+
+#
+# 1st pass in the pipe
+#
+
+def match_pairs(nodes):
+    """<left paren., []> + <tokens> + <right paren., []>  -->  <ent., <tokens>>
+
+    Other effects:
+        - merge comments/annotations
+    """
+    newnodes = []
+    i = 0
+    while 1:
+        if i >= len(nodes):
+            break
+        node = nodes[i]
+        if node.type in PAIRS.keys():
+            # TOKEN, etc. -> NAME where suitable
+            # (keyword-like names do not need to be escaped in some cases)
+            if node.type == 'BEG_BODY' and newnodes[-1].type in keyword_list:
+                if newnodes[-2].type in (ELEM, ATTR):
+                    newnodes[-1].type = NAME
+            # Look for enclosing brackets
+            match = findmatch(node.type, nodes, i + 1)
+            matchtype = PAIRS[node.type][1]
+            node = Node(type=matchtype, value=nodes[i + 1:match])
+            node.value = match_pairs(node.value)
+            newnodes.append(node)
+            i = match + 1
+        elif (node.type in (COMMENT, ANNOTATION) and i > 0
+          and newnodes[-1].type == node.type):
+            # merge comments/annotations
+            newnodes[-1].value += "\n" + node.value
+            i += 1
+        else:
+            newnodes.append(node)
+            i += 1
+        if i >= len(nodes):
+            break
+        if nodes[i].type in (ANY, SOME, MAYBE):
+            newnodes[-1].quant = nodes[i].type
+            i += 1
+
+    nodes[:] = newnodes
+    return nodes
+
+
+#
+# 2nd pass in the pipe
+#
+
+def type_bodies(nodes):
+    """Another (main) de-linearization"""
+    newnodes = []
+    i = 0
+    while 1:
+        if i >= len(nodes):
+            break
+        if (nodetypes(nodes[i:i + 3]) == (ELEM, NAME, BODY)
+          or nodetypes(nodes[i:i + 3]) == (ATTR, NAME, BODY)):
+            name, body = nodes[i + 1].value, nodes[i + 2]
+            value, quant = type_bodies(body.value), body.quant
+            node = Node(nodes[i].type, value, name, quant)
+            newnodes.append(node)
+            if not name:
+                assert False
+            i += 3
+        # "element a|b" cases
+        elif (nodetypes(nodes[i:i + 3]) == (ELEM, NAME, CHOICE)
+          or nodetypes(nodes[i:i + 3]) == (ATTR, NAME, CHOICE)):
+            # see nameClass (choice of nameClass+)
+            # XXX: very simplified
+            if nodes[i].type == ATTR:
+                assert False
+            node_type = nodes[i].type
+            value = [nodes[i + 1]]
+            i += 2
+            while nodetypes(nodes[i:i + 2]) == (CHOICE, NAME):
+                value.extend(type_bodies(nodes[i:i + 2]))
+                i += 2
+            # re-mark quant as we do not want "ref" output here
+            for v in value:
+                if v.type == NAME:
+                    v.quant = DIRECT
+            assert len(nodes) >= i and nodes[i].type == BODY
+            value.extend(type_bodies(nodes[i].value))
+            node = Node(node_type, value, None, nodes[i].quant)
+            i += 1
+            newnodes.append(node)
+        elif nodetypes(nodes[i:i + 2]) == (DATATAG, PATTERN):
+            node = Node(DATATAG, nodes[i + 1].value, nodes[i].value)
+            newnodes.append(node)
+            i += 2
+        else:
+            n = nodes[i]
+            if n.type == GROUP:   # Recurse into groups
+                value = type_bodies(n.value)
+                if len(value) > 1 and n.type:
+                    n = Node(GROUP, value, None, n.quant)
+            newnodes.append(n)
+            i += 1
+    nodes[:] = newnodes
+    return nodes
+
+
+#
+# 3rd pass in the pipe
+#
+
+def _nest_annotations(nodes, mapping, delim=None):
+    """Helper to move comments/annotations down into attributes/elements
+
+    Uses non-tail recursion to proceed the tree bottom-up as
+    otherwise there would be confusion if the annotations are
+    newly added (and thus should be kept) or the original ones
+    to be moved.
+
+    Mapping is partially defined
+        token-type |-> accumulator-list for token-type
+    for token-types covering annotations (ANNOTATION, NS_ANNOTATION)
+    and is used to pass unconsumed annotations down the tree.
+
+    Returns triplet: number of consumed nodes, filtered nodes, mapping.
+
+    Note that mapping should contain empty lists only when the recursion
+    returns back to the initiator (XXX: little bit of sanity checking,
+    we cannot speak about proper validation here).
+    """
+    # XXX: unclean, yes
+    newnodes = []
+    for i, n in enumerate(nodes):
+        if delim and n.type == delim:
+            break
+
+        if not isinstance(n.value, str):  # no recurse to terminal str
+            if n.type in (ELEM, ATTR):
+                mapping_rec = {n: [] for n in
+                               (ANNOTATION, NS_ANNOTATION, COMMENT)}
+            else:
+                mapping_rec = mapping
+            _nest_annotations(n.value, mapping_rec)
+
+            if n.type in (ELEM, ATTR):  # annot. consumer (guarded in recursion)
+                n.value = (mapping['NS_ANNOTATION'] + mapping['ANNOTATION']
+                           + mapping['COMMENT'] + n.value)
+                mapping['NS_ANNOTATION'][:], mapping['ANNOTATION'][:] = [], []
+                mapping['COMMENT'][:] = []
+        elif i == len(nodes) - 1 and n.type == COMMENT and not delim:
+            # comment at the end of the nodelist, but only if not top-level
+            newnodes.append(n)
+            continue
+
+        mapping.get(n.type, newnodes).append(n)
+
+    nodes[:] = newnodes
+    return i, nodes, mapping
+
+
+def _intersperse(nodes):
+    """Look for interleaved, choice, or sequential nodes in groups/bodies"""
+    for node in nodes:
+        if node.type in (ELEM, ATTR, GROUP, LITERAL):  # XXX: literal?
+            val = node.value
+            ntypes = [n.type for n in val if not isinstance(val, str)]
+            inters = [t for t in ntypes if t in (INTERLEAVE, CHOICE, SEQ)]
+            inters = dict(zip(inters, [0] * len(inters)))
+            if len(inters) > 1:
+                raise ParseError("Ambiguity in sequencing: %s" % node)
+            if len(inters) > 0:
+                intertype = inters.keys()[0]
+                outer_items, last_ntype, internode = [], None, None
+                simplify = node.type == GROUP
+                for pat in node.value:
+                    if pat.type == intertype:
+                        if internode is None:
+                            internode = Node(intertype, [outer_items.pop()])
+                            outer_items.append(internode)
+                        # otherwise drop it
+                    elif last_ntype == intertype:
+                        internode.value.append(pat)
+                    else:
+                        outer_items.append(pat)
+                        if pat.type in (COMMENT, ANNOTATION):
+                            # these are not interesting wrt. last type
+                            continue
+                        elif pat.quant not in (ONE, MAYBE):
+                            simplify = False
+                    last_ntype = pat.type
+
+                if (simplify and len(outer_items) == 1
+                  and outer_items[0] is internode):
+                    node.type, node.value = internode.type, internode.value
+                else:
+                    node.value = outer_items
+        if not isinstance(node.value, str):  # No recurse to terminal str
+            _intersperse(node.value)
+    return nodes
+
+
+def nest_defines(nodes):
+    """Attach groups to named patterns
+
+    Other effects:
+        - annotations are properly nested
+        - comments are nested
+    """
+    newnodes = []
+    i = 0
+    group, annotations, ns_annotations, comments = [], [], [], []
+    mapping = dict(ANNOTATION=annotations, NS_ANNOTATION=ns_annotations,
+                   COMMENT=comments)
+    while i < len(nodes):
+        node = nodes[i]
+        newnodes.append(node)
+        group[:], annotations[:], ns_annotations[:], comments[:] = [], [], [], []
+        if node.type == DEFINE:
+            j, group[:], mapping = _nest_annotations(nodes[i + 1:], mapping, DEFINE)
+            i += j
+            node.name = node.value
+            grp = _intersperse([Node(GROUP, group[:])])[0]
+            if len(grp.value) > 1 and grp.type != SEQ:
+                node.value = [grp]
+            else:
+                node.value = grp.value[:]
+            # when _nest_annotations returned *not* due to reaching DEFINE,
+            # but trailing comments are tolerated
+            if i + 1 > len(nodes) or nodes[i + 1].type not in (DEFINE, COMMENT):
+                break
+        elif node.type == ELEM:
+            # top-level element
+            _intersperse(Node(GROUP, [node]))
+        i += 1
+    nodes[:] = newnodes
+    return nodes
+
+
+#
+# 4th pass in the pipe
+#
+
+def scan_NS(nodes):
+    """Look for any namespace configuration lines
+
+    Other effects:
+        - DEFINE(start) --> START
+    """
+    global DEFAULT_NAMESPACE, OTHER_NAMESPACE, CONTEXT_FREE
+    for node in nodes:
+        if node.type == DEFAULT_NS:
+            DEFAULT_NAMESPACE = node.value
+        elif node.type == NS:
+            ns, url = map(str.strip, node.value.split('=', 1))
+            OTHER_NAMESPACE[ns] = url
+        elif node.type == ANNOTATION and 'a' not in OTHER_NAMESPACE:
+            OTHER_NAMESPACE['a'] = '"' + URI_ANNOTATIONS + '"'
+        elif node.type == DATATYPES:
+            DATATYPE_LIB[:] = [1, node.value]
+        elif not CONTEXT_FREE and node.type == DEFINE and node.name == 'start':
+            CONTEXT_FREE = 1
+            node.type = START
+            node.name = None
+
+
+def make_nodetree(tokens):
+    """Wraps the pipe of conversion passes"""
+    nodes = toNodes(tokens)
+    try_debug('TO_NODES', nodes)
+
+    match_pairs(nodes)
+    try_debug('MATCH_PAIR', nodes)
+
+    type_bodies(nodes)
+    try_debug('TYPE_BODIES', nodes)
+
+    nest_defines(nodes)
+    try_debug('NEST_DEFINES', nodes)
+
+    scan_NS(nodes)
+    try_debug('SCAN_NS', nodes)
+
+    return Node(ROOT, nodes)
+
+
+if __name__ == '__main__':
+    print make_nodetree(token_list(sys.stdin.read())).toxml()
diff --git a/conf/rng2asciidocman.xsl b/conf/rng2asciidocman.xsl
new file mode 100644
index 0000000..719fe55
--- /dev/null
+++ b/conf/rng2asciidocman.xsl
@@ -0,0 +1,490 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+    xmlns:rng="http://relaxng.org/ns/structure/1.0";
+    xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0";
+    xmlns:a4doc="http://people.redhat.com/jpokorny/ns/a4doc";
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";>
+
+<!--
+  Copyright 2013 Red Hat, Inc.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+  - Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+  - Neither the name of the Red Hat, Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+  THE POSSIBILITY OF SUCH DAMAGE.
+ -->
+
+<!--
+  TODO: cross-references
+  TODO: support annotations of the sections
+  TODO: dependency on simplification can be removed using similar construction
+        as used in rng2lens.xsl, IOW. named set of options would be described
+        and further referred (wrapped with some wording like "see the named
+        set of options XY" ... wait, expanding it fully may be actually better)
+ -->
+
+<!-- prologue -->
+<xsl:import href="common.xsl"/>
+<xsl:output method="text"/>
+<xsl:strip-space elements="*"/>
+
+<!-- xsltdoc (http://www.kanzaki.com/parts/xsltdoc.xsl) header -->
+<xsl:template name="_doas_description">
+    <rdf:RDF xmlns="http://purl.org/net/ns/doas#";>
+        <rdf:Description rdf:about="">
+            <title>rng2asciidocman.xsl XSLT stylesheet</title>
+            <description>
+                This stylesheet generates AsciiDoc formatted man page from
+                RELAX NG schema modeling corosync configuration (as reified
+                in the form of corosync.conf).
+
+                INPUT:  corosync.rnc converted to non-compact syntax
+                        + simplified (step7.20)
+                OUTPUT: AsciiDoc document suitable for -> DocBook -> man page
+                        proceeding
+            </description>
+            <author rdf:parseType="Resource">
+                <name>Jan Pokorný</name>
+                <mbox rdf:resource="jpokorny@xxxxxxxxxx"/>
+            </author>
+            <created>2013-02-01</created>
+            <release rdf:parseType="Resource">
+                <revision>0.2</revision>
+                <created>2013-02-04</created>
+            </release>
+            <rights>Copyright 2013 Red Hat, Inc.</rights>
+            <license rdf:resource="http://opensource.org/licenses/BSD-3-Clause"/>
+            <acknowledgement>
+                Based on previous work of Steven Dake et al.
+            </acknowledgement>
+        </rdf:Description>
+    </rdf:RDF>
+</xsl:template>
+
+<!--
+     helpers
+ -->
+
+<xsl:variable name="bold" select="'*'"/>
+<xsl:variable name="underline" select='"&apos;"'/>
+<xsl:variable name="SP"><xsl:text> </xsl:text></xsl:variable>
+<xsl:variable name="NL"><xsl:text>&#xA;</xsl:text></xsl:variable>
+<xsl:variable name="NLNL"><xsl:text>&#xA;&#xA;</xsl:text></xsl:variable>
+<xsl:variable name="NLNLNL"><xsl:text>&#xA;&#xA;&#xA;</xsl:text></xsl:variable>
+
+<xsl:template name="string-replace-all">
+    <!--** Replaces each occurrence of 'replace' in 'string' with 'by'. -->
+    <xsl:param name="string"/>
+    <xsl:param name="replace"/>
+    <xsl:param name="by"/>
+    <xsl:choose>
+        <xsl:when test="contains($string, $replace)">
+            <xsl:value-of select="substring-before($string, $replace)"/>
+            <xsl:value-of select="$by"/>
+            <!--@Recursion.-->
+            <xsl:call-template name="string-replace-all">
+                <xsl:with-param name="string"
+                                select="substring-after($string, $replace)"/>
+                <xsl:with-param name="replace" select="$replace"/>
+                <xsl:with-param name="by" select="$by"/>
+            </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="$string"/>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+<xsl:template name="serialize-per-name">
+    <!--** Generates "A, B and C" with optional markup of items. -->
+    <xsl:param name="items"/>
+    <xsl:param name="markup" value="''"/>
+    <xsl:for-each select="$items">
+        <xsl:choose>
+            <xsl:when test="position() = 1">
+                <xsl:value-of select="concat($markup, @name, $markup)"/>
+            </xsl:when>
+            <xsl:when test="position() = last()">
+                <xsl:value-of select="concat(' and ', $markup, @name, $markup)"/>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="concat(', ', $markup, @name, $markup)"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="context-description">
+    <!--** Generates context description. -->
+    <xsl:param name="items"/>
+    <xsl:choose>
+        <xsl:when test='@name != $start-name'>
+            <xsl:choose>
+                <xsl:when test="count($items) &lt;= 1">
+                    <xsl:text>subsection of </xsl:text>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:text>subsections of </xsl:text>
+                </xsl:otherwise>
+            </xsl:choose>
+            <xsl:value-of select="concat($bold, @name, $bold)"/>
+            <xsl:text> section</xsl:text>
+            <xsl:choose>
+                <xsl:when test="count($items) = 0"/>
+                <xsl:when test="count($items) = 1">
+                    <xsl:text> is</xsl:text>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:text> are</xsl:text>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:when>
+        <xsl:otherwise>
+            <xsl:choose>
+                <xsl:when test="count($items) = 0">
+                    <xsl:text>top-level section</xsl:text>
+                </xsl:when>
+                <xsl:when test="count($items) = 1">
+                    <xsl:text>top-level section is</xsl:text>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:text>top-level sections are</xsl:text>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+<!--
+    configuration
+ -->
+
+<xsl:variable name="head">// generated, no point in editing this
+COROSYNC_CONF(5)
+================
+:doctype: manpage
+:man source: corosync Man Page
+//:man version:
+:man manual: Corosync Cluster Engine Programmer's Manual
+// workaround AsciiDoc not supporting glosslist as it's in DocBook5+
+:listtags-glossary.list: &lt;glosslist{id? id="{id}"}{role? role="{role}"}{reftext? xreflabel="{reftext}"}&gt;{title?&lt;title&gt;{title}&lt;/title&gt;}|&lt;/glosslist&gt;
+
+
+NAME
+----
+corosync.conf - corosync executive configuration file
+
+
+SYNOPSIS
+--------
+`/etc/corosync/corosync.conf`
+
+
+DESCRIPTION
+-----------
+The *`corosync.conf`* instructs the corosync executive about various parameters
+needed to control the corosync executive.
+
+The configuration file consists top level directives that mostly denote
+bracketed configuration sections (i.e., wrapped by a pair of '{', '}'
+characters and identified by the preceeding directive).  The content of
+a section consists of new-line separated configuration items, option-value
+pairs delimited by ':', interleaved with likewise formatted subsections.
+Empty lines and lines starting with '#' character are ignored.
+
+
+CONFIGURATION SECTIONS AND OPTIONS
+----------------------------------
+</xsl:variable>
+
+<xsl:variable name="tail">
+
+FILES
+-----
+[glossary]
+*`/etc/corosync/corosync.conf`*::
+    The corosync executive configuration file.
+
+
+SEE ALSO
+--------
+*corosync_overview*(8), *votequorum*(5), *logrotate*(8)
+</xsl:variable>
+
+<!--
+     declarative attitude
+ -->
+
+<xsl:variable name="start-name" select="/rng:grammar/rng:start/rng:ref/@name"/>
+<xsl:variable name="start"
+              select="/rng:grammar/rng:define[@name = $start-name]/rng:element"/>
+
+<xsl:template match="/">
+    <!--** Triggers template, wrapping processed top element with head/tail. -->
+    <xsl:value-of select="$head"/>
+    <xsl:apply-templates select="$start"/>
+    <xsl:value-of select="$tail"/>
+</xsl:template>
+
+<xsl:template match="rng:element">
+    <!--** Section handling. -->
+    <xsl:variable name="secs-required"
+                  select="/rng:grammar/rng:define[
+                          @name =
+                              (current()
+                              |current()/rng:group
+                              |current()/rng:interleave
+                              |current()/rng:group/rng:interleave
+                              )/rng:ref/@name
+                          ]/rng:element[
+                              a:documentation or current() != $start
+                          ]"/>
+    <xsl:variable name="secs-optional"
+                  select="/rng:grammar/rng:define[
+                          @name =
+                              (current()/rng:zeroOrMore
+                              |current()/rng:group/rng:zeroOrMore
+                              |current()/rng:interleave/rng:optional
+                              |current()/rng:group/rng:interleave/rng:optional
+                              |current()/rng:optional
+                              |current()/rng:group/rng:optional
+                              )/rng:ref/@name
+                          ]/rng:element[
+                              a:documentation or current() != $start
+                          ]"/>
+
+    <xsl:if test="a:documentation">
+        <xsl:call-template name="string-replace-all">
+            <xsl:with-param name="string" select="a:documentation"/>
+            <xsl:with-param name="replace" select="$NLNL"/>
+            <xsl:with-param name="by" select="concat($NL, '+', $NL)"/>
+        </xsl:call-template>
+        <xsl:value-of select="$NL"/>
+    </xsl:if>
+
+    <xsl:choose>
+        <xsl:when test="$secs-required or $secs-optional">
+            <xsl:if test="$secs-required">
+                <xsl:variable name="context-description">
+                    <xsl:call-template name="context-description">
+                        <xsl:with-param name="items" select="$secs-required"/>
+                    </xsl:call-template>
+                </xsl:variable>
+                <xsl:variable name="secs-required-line">
+                    <xsl:call-template name="serialize-per-name">
+                        <xsl:with-param name="items" select="$secs-required"/>
+                        <xsl:with-param name="markup" select="$bold"/>
+                    </xsl:call-template>
+                </xsl:variable>
+                <xsl:text>Required </xsl:text>
+                <xsl:value-of select="concat($context-description, $SP,
+                                             $secs-required-line)"/>
+                <xsl:choose>
+                    <xsl:when test="$secs-optional">
+                        <xsl:text>,</xsl:text>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:text>.</xsl:text>
+                    </xsl:otherwise>
+                </xsl:choose>
+                <xsl:value-of select="$NL"/>
+            </xsl:if>
+            <!-- sep -->
+            <xsl:if test="$secs-optional">
+                <xsl:variable name="context-description">
+                    <xsl:call-template name="context-description">
+                        <xsl:with-param name="items" select="$secs-optional"/>
+                    </xsl:call-template>
+                </xsl:variable>
+                <xsl:variable name="secs-optional-line">
+                    <xsl:call-template name="serialize-per-name">
+                        <xsl:with-param name="items" select="$secs-optional"/>
+                        <xsl:with-param name="markup" select="$bold"/>
+                    </xsl:call-template>
+                </xsl:variable>
+                <xsl:choose>
+                    <xsl:when test="$secs-required">
+                        <xsl:text>optional </xsl:text>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:text>Optional </xsl:text>
+                    </xsl:otherwise>
+                </xsl:choose>
+                <xsl:value-of select="concat($context-description, $SP,
+                                             $secs-optional-line, '.', $NL)"/>
+            </xsl:if>
+        </xsl:when>
+        <xsl:otherwise>
+            <xsl:variable name="context-description">
+                <xsl:call-template name="context-description">
+                    <xsl:with-param name="items" select="$secs-required"/>
+                </xsl:call-template>
+            </xsl:variable>
+            <xsl:text>There is no </xsl:text>
+            <xsl:value-of select="$context-description"/>
+            <xsl:text>.</xsl:text>
+            <xsl:value-of select="$NL"/>
+        </xsl:otherwise>
+    </xsl:choose>
+    <xsl:value-of select="$NL"/>
+
+    <xsl:call-template name="proceed-options">
+        <xsl:with-param name="opts-required"
+                        select="(.
+                                |rng:group
+                                )/rng:attribute[a:documentation]"/>
+        <xsl:with-param name="opts-optional"
+                        select="(.
+                                |rng:group
+                                )/rng:optional/rng:attribute[a:documentation]"/>
+    </xsl:call-template>
+
+    <xsl:call-template name="proceed-sections">
+        <xsl:with-param name="secs-required"
+                        select="$secs-required"/>
+        <xsl:with-param name="secs-optional"
+                        select="$secs-optional"/>
+    </xsl:call-template>
+</xsl:template>
+
+<xsl:template match="rng:attribute">
+    <!--** Option handling. -->
+    <xsl:variable name="content">
+        <xsl:call-template name="string-replace-all">
+            <xsl:with-param name="string" select="a:documentation"/>
+            <xsl:with-param name="replace" select="$NLNL"/>
+            <xsl:with-param name="by" select="concat($NL, '+', $NL)"/>
+        </xsl:call-template>
+    </xsl:variable>
+    <xsl:value-of select="$NL"/>
+    <xsl:value-of select="concat($bold, @name, $bold)"/>
+    <xsl:if test="@a:defaultValue">
+        <xsl:value-of select="concat(' (default: ', $underline, @a:defaultValue, $underline,')')"/>
+    </xsl:if>
+    <xsl:text>::</xsl:text>
+    <xsl:value-of select="concat($NLNL, $content)"/>
+    <xsl:if test="rng:choice">
+        <!--@Show enumerated values.-->
+        <xsl:value-of select="$NL"/>
+        <xsl:text>Possible values: </xsl:text>
+            <xsl:call-template name="text-join">
+                <xsl:with-param name="items" select="rng:choice/rng:value"/>
+                <xsl:with-param name="sep" select="', '"/>
+                <xsl:with-param name="markup" select="$underline"/>
+            </xsl:call-template>
+        <xsl:text>.</xsl:text>
+    </xsl:if>
+    <xsl:for-each select="@a4doc:*[contains(local-name(), '-hint')]">
+        <!--@Extra handling of per-option a4doc annotations.-->
+        <xsl:value-of select="$NLNL"/>
+        <xsl:choose>
+            <xsl:when test="local-name() = 'danger-hint'">
+                <xsl:text>CAUTION: </xsl:text>
+            </xsl:when>
+            <xsl:when test="local-name() = 'discretion-hint'">
+                <xsl:text>IMPORTANT: </xsl:text>
+            </xsl:when>
+            <xsl:when test="local-name() = 'deprecation-hint'">
+                <xsl:text>WARNING: This has been deprecated. </xsl:text>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:text>WARNING: </xsl:text>
+            </xsl:otherwise>
+        </xsl:choose>
+        <xsl:value-of select="normalize-space(.)"/>
+        <xsl:value-of select="$NL"/>
+    </xsl:for-each>
+    <xsl:value-of select="$NLNL"/>
+</xsl:template>
+
+<!--
+    procedural attitude
+ -->
+
+<xsl:template name="proceed-sections">
+    <!--** Required/optional sections handling. -->
+    <xsl:param name="secs-required" value="false()"/>
+    <xsl:param name="secs-optional" value="false()"/>
+
+    <!-- cannot use $secs-required|$secs-optional (we need explicit order) -->
+    <xsl:for-each select="$secs-required">
+        <xsl:value-of select="concat($NL, '=== ', @name, $NLNLNL)"/>
+        <xsl:apply-templates select="."/>
+    </xsl:for-each>
+
+    <xsl:for-each select="$secs-optional">
+        <xsl:value-of select="concat($NL, '=== ', @name, $NLNLNL)"/>
+        <xsl:apply-templates select="."/>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="proceed-options">
+    <!--** Required/optional options handling. -->
+    <xsl:param name="opts-required" value="false()"/>
+    <xsl:param name="opts-optional" value="false()"/>
+
+    <xsl:variable name="opts-required-line">
+        <xsl:call-template name="serialize-per-name">
+            <xsl:with-param name="items" select="$opts-required"/>
+            <xsl:with-param name="markup" select="$underline"/>
+        </xsl:call-template>
+    </xsl:variable>
+    <xsl:variable name="opts-optional-line">
+        <xsl:call-template name="serialize-per-name">
+            <xsl:with-param name="items" select="$opts-optional"/>
+            <xsl:with-param name="markup" select="$underline"/>
+        </xsl:call-template>
+    </xsl:variable>
+
+    <xsl:if test="$opts-required">
+        <xsl:value-of select="$NL"/>
+        <xsl:choose>
+            <xsl:when test="count($opts-required) = 1">
+                <xsl:text>==== Required option</xsl:text>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:text>==== Required options</xsl:text>
+            </xsl:otherwise>
+        </xsl:choose>
+        <xsl:value-of select="$NL"/>
+        <xsl:apply-templates select="$opts-required"/>
+    </xsl:if>
+
+    <xsl:if test="$opts-optional">
+        <xsl:value-of select="$NL"/>
+        <xsl:choose>
+            <xsl:when test="count($opts-optional) = 1">
+                <xsl:text>==== Optional or conditionally required option</xsl:text>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:text>==== Optional or conditionally required options</xsl:text>
+            </xsl:otherwise>
+        </xsl:choose>
+        <xsl:value-of select="$NL"/>
+        <xsl:apply-templates select="$opts-optional"/>
+    </xsl:if>
+</xsl:template>
+
+</xsl:stylesheet>
+<!-- vim: set et ts=4 sw=4: -->
diff --git a/conf/rng2lens.xsl b/conf/rng2lens.xsl
new file mode 100644
index 0000000..c205a6f
--- /dev/null
+++ b/conf/rng2lens.xsl
@@ -0,0 +1,333 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+    xmlns:rng="http://relaxng.org/ns/structure/1.0";
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";>
+
+<!--
+  Copyright 2013 Red Hat, Inc.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+  - Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+  - Neither the name of the Red Hat, Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+  THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!--
+  TODO: migrate to using Build Augeas "library"
+  -->
+
+<!-- prologue -->
+<xsl:import href="common.xsl"/>
+<xsl:import href="xsddatatypes2lens.xsl"/>
+<xsl:output method="text"/>
+<xsl:strip-space elements="*"/>
+
+<!-- xsltdoc (http://www.kanzaki.com/parts/xsltdoc.xsl) header -->
+<xsl:template name="_doas_description">
+    <rdf:RDF xmlns="http://purl.org/net/ns/doas#";>
+        <rdf:Description rdf:about="">
+            <title>rng2lens.xsl XSLT stylesheet</title>
+            <description>
+                This stylesheet generates Augeas lens from RELAX NG schema
+                modeling corosync configuration (as reified in the form
+                of corosync.conf).
+
+                INPUT:  corosync.rnc converted 1:1 to non-compact syntax
+                        (no simplification)
+                OUTPUT: Augeas lens for corosync.conf
+            </description>
+            <author rdf:parseType="Resource">
+                <name>Jan Pokorný</name>
+                <mbox rdf:resource="jpokorny@xxxxxxxxxx"/>
+            </author>
+            <created>2013-02-01</created>
+            <release rdf:parseType="Resource">
+                <revision>0.2</revision>
+                <created>2013-02-04</created>
+            </release>
+            <rights>Copyright 2013 Red Hat, Inc.</rights>
+            <license rdf:resource="http://opensource.org/licenses/BSD-3-Clause"/>
+            <acknowledgement>
+                Based on previous work of Angus Salkeld et al.
+            </acknowledgement>
+        </rdf:Description>
+    </rdf:RDF>
+</xsl:template>
+
+<!--
+     helpers
+ -->
+
+<xsl:variable name="SP"><xsl:text> </xsl:text></xsl:variable>
+<xsl:variable name="NL"><xsl:text>&#xA;</xsl:text></xsl:variable>
+<xsl:variable name="NLNL"><xsl:text>&#xA;&#xA;</xsl:text></xsl:variable>
+<xsl:variable name="QUOT"><xsl:text>"</xsl:text></xsl:variable>
+
+<xsl:template name="string-replace-all">
+    <!--** Replaces each occurrence of 'replace' in 'string' with 'by'. -->
+    <xsl:param name="string"/>
+    <xsl:param name="replace"/>
+    <xsl:param name="by"/>
+    <xsl:choose>
+        <xsl:when test="contains($string, $replace)">
+            <xsl:value-of select="substring-before($string, $replace)"/>
+            <xsl:value-of select="$by"/>
+            <!-- recursion -->
+            <xsl:call-template name="string-replace-all">
+                <xsl:with-param name="string"
+                                select="substring-after($string, $replace)"/>
+                <xsl:with-param name="replace" select="$replace"/>
+                <xsl:with-param name="by" select="$by"/>
+            </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="$string"/>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+<xsl:template name="serialize-per-name-plus">
+    <!--** Generates "A|B|C" with extra decorations via modes. -->
+    <xsl:param name="items"/>
+    <xsl:for-each select="$items">
+        <xsl:variable name="prefix">
+            <xsl:choose>
+                <xsl:when test="name() = 'attribute'">
+                    <xsl:apply-templates select="." mode="plus-prefix-option"/>
+                </xsl:when>
+            </xsl:choose>
+        </xsl:variable>
+        <xsl:variable name="suffix">
+            <xsl:choose>
+                <xsl:when test="name() = 'attribute'">
+                    <xsl:apply-templates select="." mode="plus-suffix-option"/>
+                </xsl:when>
+            </xsl:choose>
+        </xsl:variable>
+        <!--@Serialization itself, using obtained prefix/suffix.-->
+        <xsl:choose>
+            <xsl:when test="position() = 1">
+                <xsl:value-of select="concat($NL, '    ', $prefix, @name, $suffix)"/>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="concat($NL, '    |', $prefix, @name, $suffix)"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:for-each>
+</xsl:template>
+
+<!--
+    configuration
+ -->
+
+<xsl:variable name="toplevel-name">lns</xsl:variable>
+
+<!-- XXX: this can be overriden from autotools -->
+<xsl:param name="config-file"
+           select="concat($QUOT, '/etc/corosync/corosync.conf', $QUOT)"/>
+
+<xsl:variable name="head">(* generated, no point in editing this *)
+(* Process /etc/corosync/corosync.conf                             *)
+(* The lens is based on the corosync RELAX NG schema defining      *)
+(* corosync configuration (same base for corosync.conf man page)   *)
+module Corosync =
+
+autoload xfm
+
+let comment = Util.comment
+let empty = Util.empty
+let dels = Util.del_str
+let eol = Util.eol
+
+let ws = del /[ \t]+/ " "
+let wsc = del /:[ \t]+/ ": "
+let indent = del /[ \t]*/ ""
+(* We require that braces are always followed by a newline *)
+let obr = del /\{([ \t]*)\n/ "{\n"
+let cbr = del /[ \t]*}[ \t]*\n/ "}\n"
+
+let ikey (k:regexp) = indent . key k
+
+let section (n:regexp) (b:lens) =
+  [ ikey n . ws . obr . (b|empty|comment)* . cbr ]
+
+let kv (k:regexp) (v:regexp) =
+  [ ikey k .  wsc . store v . eol ]
+
+(* FIXME: it would be much more concise to write                       *)
+(* [ key k . ws . (bare | quoted) ]                                    *)
+(* but the typechecker trips over that                                 *)
+let qstr (k:regexp) =
+  let delq = del /['"]/ "\"" in
+  let bare = del /["']?/ "" . store /[^"' \t\n]+/ . del /["']?/ "" in
+  let quoted = delq . store /.*[ \t].*/ . delq in
+  [ ikey k . wsc . bare . eol ]
+ |[ ikey k . wsc . quoted . eol ]
+
+</xsl:variable>
+
+<!--
+     declarative attitude
+ -->
+
+<xsl:variable name="start-name" select="/rng:grammar/rng:start/rng:ref/@name"/>
+<xsl:variable name="start"
+              select="/rng:grammar/rng:define[@name = $start-name]"/>
+
+<xsl:key name="definekey" match="/rng:grammar/rng:define" use="@name"/>
+<xsl:key name="refkey" match="rng:ref" use="@name"/>
+
+<xsl:template match="/">
+    <!--** Triggers template, wrapping processed top element with head/tail. -->
+    <xsl:value-of select="$head"/>
+    <xsl:apply-templates select="$start"/>
+    <xsl:value-of select="concat('let xfm = transform ', $toplevel-name)"/>
+    <xsl:value-of select="concat(' (incl ', QUOT, $config-file, QUOT, ')')"/>
+</xsl:template>
+
+<xsl:template match="rng:define | rng:element">
+    <!--** Sections handling. -->
+    <xsl:choose>
+        <xsl:when test="name() = 'define' and rng:element[@name = current()/@name]">
+            <!--@Delegate to nested element with the same @name if suitable.-->
+            <xsl:apply-templates select="rng:element[@name = current()/@name]"/>
+        </xsl:when>
+        <xsl:otherwise>
+            <xsl:variable name="secs-required"
+                          select="(current()
+                                  |current()/rng:group
+                                  |current()/rng:interleave
+                                  |current()/rng:group/rng:interleave
+                                  )/rng:ref"/>
+            <xsl:variable name="secs-optional"
+                          select="(current()/rng:zeroOrMore
+                                  |current()/rng:group/rng:zeroOrMore
+                                  |current()/rng:interleave/rng:optional
+                                  |current()/rng:group/rng:interleave/rng:optional
+                                  |current()/rng:optional
+                                  |current()/rng:group/rng:optional
+                                  )/rng:ref"/>
+            <xsl:variable name="opts-required"
+                          select="(.
+                                  |rng:group
+                                  )/rng:attribute"/>
+            <xsl:variable name="opts-optional"
+                          select="(.
+                                  |rng:group
+                                  )/rng:optional/rng:attribute"/>
+
+            <xsl:if test="name() = 'element' and $start//rng:ref[@name = current()/@name]">
+                <xsl:value-of select="concat('(* The ', @name,
+                                             ' section *)', $NL)"/>
+            </xsl:if>
+
+            <!--@Emit non-duplicated definitions we depend on (-> postorder).-->
+            <xsl:for-each select="$secs-required|$secs-optional">
+                <xsl:if test="generate-id() = generate-id(key('refkey', @name)[1])">
+                    <xsl:apply-templates select="key('definekey', @name)"/>
+                </xsl:if>
+            </xsl:for-each>
+
+            <!--@Determine initial part.-->
+            <xsl:choose>
+                <xsl:when test="@name = $start-name">
+                    <xsl:value-of select="concat('let ', $toplevel-name,
+                                                 ' = (comment|empty|')"/>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:value-of select="concat('let ', @name, ' =')"/>
+                    <xsl:if test="name() = 'element'">
+                        <xsl:value-of select="concat($NL, '  let setting =')"/>
+                    </xsl:if>
+                </xsl:otherwise>
+            </xsl:choose>
+
+            <!--@Determine body.-->
+            <xsl:call-template name="serialize-per-name-plus">
+                <xsl:with-param name="items"
+                                select="$secs-required
+                                       |$secs-optional
+                                       |$opts-required
+                                       |$opts-optional"/>
+            </xsl:call-template>
+
+            <!--@Determine final part.-->
+            <xsl:choose>
+                <xsl:when test="@name = $start-name">
+                    <xsl:text>)*</xsl:text>
+                </xsl:when>
+                <xsl:when test="name() = 'element'">
+                    <xsl:value-of select="concat(' in', $NL, '  section ',
+                                                 $QUOT, @name, $QUOT,
+                                                 ' setting')"/>
+                </xsl:when>
+            </xsl:choose>
+            <xsl:value-of select="$NLNL"/>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+<xsl:template match="*" mode="plus-prefix-option">
+    <!--** Option has extra line prefix: 'kv' or 'qstr'. -->
+    <xsl:choose>
+        <xsl:when test="rng:choice or rng:data[@type != '']">
+            <!--@Start kv (finalized in 'plus-suffix-option' mode).-->
+            <xsl:value-of select="concat('kv', $SP, $QUOT)"/>
+        </xsl:when>
+        <xsl:otherwise>
+            <!--@Start qstr (finalized in 'plus-suffix-option' mode).-->
+            <xsl:value-of select="concat('qstr', $SP, '/')"/>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+<xsl:template match="*" mode="plus-suffix-option">
+    <!--** Option has extra line suffix: (type/enumerated values). -->
+    <xsl:choose>
+        <xsl:when test="rng:choice">
+            <!--@Finalize kv with enumerated values forming target regexp.-->
+            <xsl:value-of select="concat($QUOT, $SP)"/>
+            <xsl:text>/</xsl:text>
+                <xsl:call-template name="text-join">
+                    <xsl:with-param name="items" select="rng:choice/rng:value"/>
+                    <xsl:with-param name="sep" select="'|'"/>
+                </xsl:call-template>
+            <xsl:text>/</xsl:text>
+        </xsl:when>
+        <xsl:when test="rng:data[@type != '']">
+            <!--@Finalize kv with explicit type (see xsddatatypes2lens.xsl).-->
+            <xsl:value-of select="concat($QUOT, $SP)"/>
+            <xsl:call-template name="xsddatatype2lens">
+                <xsl:with-param name="type" select="rng:data/@type"/>
+            </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+            <!--@Finalize qstr.-->
+            <xsl:value-of select="'/'"/>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
+<!-- vim: set et ts=4 sw=4: -->
diff --git a/conf/simplification-wrapper.xsl b/conf/simplification-wrapper.xsl
new file mode 100644
index 0000000..c2b820a
--- /dev/null
+++ b/conf/simplification-wrapper.xsl
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
+<xsl:import href="simplification.xsl"/>
+
+<xsl:param name="step" select="'20'"/>
+<xsl:variable name="step_concat" select="concat('step7.', $step)"/>
+
+<xsl:template match="/">
+    <xsl:choose>
+        <xsl:when test="$step = '20'">
+            <xsl:apply-templates mode="step7.20">
+                <xsl:with-param name="out" select="0"/>
+                <xsl:with-param name="stop-after" select="$step_concat"/>
+            </xsl:apply-templates>
+        </xsl:when>
+        <xsl:otherwise>
+            <xsl:message terminate="yes">
+                <xsl:value-of select="concat('Not implemented: ', $step_concat)"/>
+            </xsl:message>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/conf/simplification.xsl b/conf/simplification.xsl
new file mode 100644
index 0000000..1dd0447
--- /dev/null
+++ b/conf/simplification.xsl
@@ -0,0 +1,1037 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; xmlns="http://relaxng.org/ns/structure/1.0"; xmlns:rng="http://relaxng.org/ns/structure/1.0"; xmlns:exsl="http://exslt.org/common"; extension-element-prefixes="exsl" 
+exclude-result-prefixes = "exsl rng">
+
+<xsl:output method="xml"/>
+
+<xsl:param name="out-name">simplified-</xsl:param>
+
+<!-- 7.2 -->
+
+<xsl:template match="/">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.2</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.2"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-2.rng" method="xml" indent="no">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.2'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.3"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="rng:*|text()|@*[namespace-uri()='']" mode="step7.2">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.2"/>
+		<xsl:apply-templates mode="step7.2"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="*|@*" mode="step7.2"/>
+
+<!-- 7.3 -->
+
+<xsl:template match="/" mode="step7.3">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.3</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.3"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-3.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.3'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.4">
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.3">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.3"/>
+		<xsl:apply-templates mode="step7.3"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="text()[normalize-space(.)='' and not(parent::rng:param or parent::rng:value)]" mode="step7.3"/>
+
+<xsl:template match="@name|@type|@combine" mode="step7.3">
+	<xsl:attribute name="{name()}">
+		<xsl:value-of select="normalize-space(.)"/>
+	</xsl:attribute>
+</xsl:template>
+
+<xsl:template match="rng:name/text()" mode="step7.3">
+	<xsl:value-of select="normalize-space(.)"/>
+</xsl:template>
+
+<!-- 7.4 -->
+
+<xsl:template match="/" mode="step7.4">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.4</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.4"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-4.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.4'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.5"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.4">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.4"/>
+		<xsl:apply-templates mode="step7.4"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="@datatypeLibrary" mode="step7.4"/>
+
+<xsl:template match="rng:data|rng:value" mode="step7.4">
+	<xsl:copy>
+		<xsl:attribute name="datatypeLibrary">
+			<xsl:value-of select="ancestor-or-self::*[@datatypeLibrary][1]/@datatypeLibrary"/>
+		</xsl:attribute>
+		<xsl:apply-templates select="@*" mode="step7.4"/>
+		<xsl:apply-templates mode="step7.4"/>
+	</xsl:copy>
+</xsl:template>
+
+<!-- 7.5 -->
+
+<xsl:template match="/" mode="step7.5">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.5</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.5"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-5.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.5'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.7"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.5">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.5"/>
+		<xsl:apply-templates mode="step7.5"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:value[not(@type)]/@datatypeLibrary" mode="step7.5"/>
+
+<xsl:template match="rng:value[not(@type)]" mode="step7.5">
+	<value type="token" datatypeLibrary="">
+		<xsl:apply-templates select="@*" mode="step7.5"/>
+		<xsl:apply-templates mode="step7.5"/>
+	</value>
+</xsl:template>
+
+<!-- 7.7 -->
+
+<xsl:template match="/" mode="step7.7">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.7</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.7"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-7.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.7'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.8"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.7">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.7"/>
+		<xsl:apply-templates mode="step7.7"/>
+	</xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="rng:externalRef" mode="step7.7">
+	<xsl:variable name="ref-rtf">
+		<xsl:apply-templates select="document(@href)">
+			<xsl:with-param name="out" select="0"/>
+				<xsl:with-param name="stop-after" select="'step7.7'"/>
+		</xsl:apply-templates>
+	</xsl:variable>
+	<xsl:variable name="ref" select="exsl:node-set($ref-rtf)"/>
+	<xsl:element name="{local-name($ref/*)}" namespace="http://relaxng.org/ns/structure/1.0";>
+		<xsl:if test="not($ref/*/@ns) and @ns">
+			<xsl:attribute name="ns">
+				<xsl:value-of select="@ns"/>
+			</xsl:attribute>
+		</xsl:if>
+		<xsl:copy-of select="$ref/*/@*"/>
+		<xsl:copy-of select="$ref/*/*|$ref/*/text()"/>
+	</xsl:element>
+</xsl:template>
+
+<!-- 7.8 -->
+
+<xsl:template match="/" mode="step7.8">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.8</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.8"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-8.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.8'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.9"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.8">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.8"/>
+		<xsl:apply-templates mode="step7.8"/>
+	</xsl:copy>
+</xsl:template>
+
+
+<xsl:template match="rng:include" mode="step7.8">
+	<xsl:variable name="ref-rtf">
+		<xsl:apply-templates select="document(@href)">
+			<xsl:with-param name="out" select="0"/>
+				<xsl:with-param name="stop-after" select="'step7.8'"/>
+		</xsl:apply-templates>
+	</xsl:variable>
+	<xsl:variable name="ref" select="exsl:node-set($ref-rtf)"/>
+	<div>
+		<xsl:copy-of select="@*[name() != 'href']"/>
+		<xsl:copy-of select="*"/>
+		<xsl:copy-of select="$ref/rng:grammar/rng:start[not(current()/rng:start)]"/>
+		<xsl:copy-of select="$ref/rng:grammar/rng:define[not(@name = current()/rng:define/@name)]"/>
+	</div>
+</xsl:template>
+
+<!-- 7.9 -->
+
+<xsl:template match="/" mode="step7.9">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.9</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.9"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-9.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.9'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.10"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.9">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.9"/>
+		<xsl:apply-templates mode="step7.9"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="@name[parent::rng:element|parent::rng:attribute]" mode="step7.9"/>
+
+<xsl:template match="rng:element[@name]|rng:attribute[@name]" mode="step7.9">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.9"/>
+		<xsl:if test="self::rng:attribute and not(@ns)">
+			<xsl:attribute name="ns"/>
+		</xsl:if>
+		<name>
+			<xsl:value-of select="@name"/>
+		</name>
+		<xsl:apply-templates mode="step7.9"/>
+	</xsl:copy>
+</xsl:template>
+
+<!-- 7.10 -->
+
+<xsl:template match="/" mode="step7.10">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.10</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.10"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-10.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.10'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.11"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.10">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.10"/>
+		<xsl:apply-templates mode="step7.10"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="@ns" mode="step7.10"/>
+
+<xsl:template match="rng:name|rng:nsName|rng:value" mode="step7.10">
+	<xsl:copy>
+		<xsl:attribute name="ns">
+			<xsl:value-of select="ancestor-or-self::*[@ns][1]/@ns"/>
+		</xsl:attribute>
+		<xsl:apply-templates select="@*" mode="step7.10"/>
+		<xsl:apply-templates mode="step7.10"/>
+	</xsl:copy>
+</xsl:template>
+
+<!-- 7.11 -->
+
+<xsl:template match="/" mode="step7.11">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.11</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.11"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-11.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.11'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.12"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.11">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.11"/>
+		<xsl:apply-templates mode="step7.11"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:name[contains(., ':')]" mode="step7.11">
+	<xsl:variable name="prefix" select="substring-before(., ':')"/>
+	<name>
+		<xsl:attribute name="ns">
+			<xsl:for-each select="namespace::*">
+				<xsl:if test="name()=$prefix">
+					<xsl:value-of select="."/>
+				</xsl:if>
+			</xsl:for-each>
+		</xsl:attribute>
+		<xsl:value-of select="substring-after(., ':')"/>
+	</name>
+</xsl:template>
+
+<!-- 7.12 -->
+
+<xsl:template match="/" mode="step7.12">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.12</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.12"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-12.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.12'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.13"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.12">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.12"/>
+		<xsl:apply-templates mode="step7.12"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:div" mode="step7.12">
+	<xsl:apply-templates mode="step7.12"/>
+</xsl:template>
+
+<!-- 7.13 -->
+
+<xsl:template match="/" mode="step7.13">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.13</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.13"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-13.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.13'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.14"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.13">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.13"/>
+		<xsl:apply-templates mode="step7.13"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:define[count(*)>1]|rng:oneOrMore[count(*)>1]|rng:zeroOrMore[count(*)>1]|rng:optional[count(*)>1]|rng:list[count(*)>1]|rng:mixed[count(*)>1]" mode="step7.13">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.13"/>
+		<xsl:call-template name="reduce7.13">
+			<xsl:with-param name="node-name" select="'group'"/>
+		</xsl:call-template>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:except[count(*)>1]" mode="step7.13">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.13"/>
+		<xsl:call-template name="reduce7.13">
+			<xsl:with-param name="node-name" select="'choice'"/>
+		</xsl:call-template>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:attribute[count(*) =1]" mode="step7.13">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.13"/>
+		<xsl:apply-templates select="*" mode="step7.13"/>
+		<text/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:element[count(*)>2]" mode="step7.13">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.13"/>
+		<xsl:apply-templates select="*[1]" mode="step7.13"/>
+		<xsl:call-template name="reduce7.13">
+			<xsl:with-param name="left" select="*[4]"/>
+			<xsl:with-param name="node-name" select="'group'"/>
+			<xsl:with-param name="out">
+				<group>
+					<xsl:apply-templates select="*[2]" mode="step7.13"/>
+					<xsl:apply-templates select="*[3]" mode="step7.13"/>
+				</group>
+			</xsl:with-param>
+		</xsl:call-template>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:group[count(*)=1]|rng:choice[count(*)=1]|rng:interleave[count(*)=1]" mode="step7.13">
+	<xsl:apply-templates select="*" mode="step7.13"/>
+</xsl:template>
+
+<xsl:template match="rng:group[count(*)>2]|rng:choice[count(*)>2]|rng:interleave[count(*)>2]" mode="step7.13" name="reduce7.13">
+	<xsl:param name="left" select="*[3]"/>
+	<xsl:param name="node-name" select="name()"/>
+	<xsl:param name="out">
+		<xsl:element name="{$node-name}">
+			<xsl:apply-templates select="*[1]" mode="step7.13"/>
+			<xsl:apply-templates select="*[2]" mode="step7.13"/>
+		</xsl:element>
+	</xsl:param>
+	<xsl:choose>
+		<xsl:when test="$left">
+			<xsl:variable name="newOut">
+				<xsl:element name="{$node-name}">
+					<xsl:copy-of select="$out"/>
+					<xsl:apply-templates select="$left" mode="step7.13"/>
+				</xsl:element>
+			</xsl:variable>
+			<xsl:call-template name="reduce7.13">
+				<xsl:with-param name="left" select="$left/following-sibling::*[1]"/>
+				<xsl:with-param name="out" select="$newOut"/>
+				<xsl:with-param name="node-name" select="$node-name"/>
+			</xsl:call-template>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:copy-of select="$out"/>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<!-- 7.14 -->
+
+<xsl:template match="/" mode="step7.14">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.14</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.14"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-14.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.14'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.15"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.14">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.14"/>
+		<xsl:apply-templates mode="step7.14"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:mixed" mode="step7.14">
+	<interleave>
+		<xsl:apply-templates mode="step7.14"/>
+		<text/>
+	</interleave>
+</xsl:template>
+
+<!-- 7.15 -->
+
+<xsl:template match="/" mode="step7.15">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.15</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.15"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-15.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.15'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.16"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.15">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.15"/>
+		<xsl:apply-templates mode="step7.15"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:optional" mode="step7.15">
+	<choice>
+		<xsl:apply-templates mode="step7.15"/>
+		<empty/>
+	</choice>
+</xsl:template>
+
+<!-- 7.16 -->
+
+<xsl:template match="/" mode="step7.16">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.16</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.16"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-16.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.16'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.18"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.16">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.16"/>
+		<xsl:apply-templates mode="step7.16"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:zeroOrMore" mode="step7.16">
+	<choice>
+		<oneOrMore>
+			<xsl:apply-templates mode="step7.16"/>
+		</oneOrMore>
+		<empty/>
+	</choice>
+</xsl:template>
+
+<!-- 7.18 -->
+
+<xsl:template match="/" mode="step7.18">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.18</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.18"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-18.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.18'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.19"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.18">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.18"/>
+		<xsl:apply-templates mode="step7.18"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="@combine" mode="step7.18"/>
+<xsl:template match="rng:start[preceding-sibling::rng:start]|rng:define[@name=preceding-sibling::rng:define/@name]" mode="step7.18"/>
+
+<xsl:template match="rng:start[not(preceding-sibling::rng:start) and following-sibling::rng:start]" mode="step7.18">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.18"/>
+		<xsl:element name="{parent::*/rng:start/@combine}">
+			<xsl:call-template name="start7.18"/>
+		</xsl:element>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template name="start7.18">
+	<xsl:param name="left" select="following-sibling::rng:start[2]"/>
+	<xsl:param name="node-name" select="parent::*/rng:start/@combine"/>
+	<xsl:param name="out">
+		<xsl:element name="{$node-name}">
+			<xsl:apply-templates select="*" mode="step7.18"/>
+			<xsl:apply-templates select="following-sibling::rng:start[1]/*" mode="step7.18"/>
+		</xsl:element>
+	</xsl:param>
+	<xsl:choose>
+		<xsl:when test="$left/*">
+			<xsl:variable name="newOut">
+				<xsl:element name="{$node-name}">
+					<xsl:copy-of select="$out"/>
+					<xsl:apply-templates select="$left/*" mode="step7.18"/>
+				</xsl:element>
+			</xsl:variable>
+			<xsl:call-template name="start7.18">
+				<xsl:with-param name="left" select="$left/following-sibling::rng:start[1]"/>
+				<xsl:with-param name="node-name" select="$node-name"/>
+				<xsl:with-param name="out" select="$newOut"/>
+			</xsl:call-template>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:copy-of select="$out"/>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="rng:define[not(@name=preceding-sibling::rng:define/@name) and @name=following-sibling::rng:define/@name]" mode="step7.18">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.18"/>
+		<xsl:call-template name="define7.18"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template name="define7.18">
+	<xsl:param name="left" select="following-sibling::rng:define[@name=current()/@name][2]"/>
+	<xsl:param name="node-name" select="parent::*/rng:define[@name=current()/@name]/@combine"/>
+	<xsl:param name="out">
+		<xsl:element name="{$node-name}">
+			<xsl:apply-templates select="*" mode="step7.18"/>
+			<xsl:apply-templates select="following-sibling::rng:define[@name=current()/@name][1]/*" mode="step7.18"/>
+		</xsl:element>
+	</xsl:param>
+	<xsl:choose>
+		<xsl:when test="$left/*">
+			<xsl:variable name="newOut">
+				<xsl:element name="{$node-name}">
+					<xsl:copy-of select="$out"/>
+					<xsl:apply-templates select="$left/*" mode="step7.18"/>
+				</xsl:element>
+			</xsl:variable>
+			<xsl:call-template name="define7.18">
+				<xsl:with-param name="left" select="$left/following-sibling::rng:define[@name=current()/@name][1]"/>
+				<xsl:with-param name="node-name" select="$node-name"/>
+				<xsl:with-param name="out" select="$newOut"/>
+			</xsl:call-template>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:copy-of select="$out"/>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<!-- 7.19 -->
+
+<xsl:template match="/" mode="step7.19">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.19</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.19"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-19.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.19'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.20"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.19">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.19"/>
+		<xsl:apply-templates mode="step7.19"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="/rng:grammar" mode="step7.19">
+	<grammar>
+		<xsl:apply-templates mode="step7.19"/>
+		<xsl:apply-templates select="//rng:define" mode="step7.19-define"/>
+	</grammar>
+</xsl:template>
+
+<xsl:template match="/*" mode="step7.19">
+	<grammar>
+		<start>
+			<xsl:copy>
+				<xsl:apply-templates select="@*" mode="step7.19"/>
+				<xsl:apply-templates mode="step7.19"/>
+			</xsl:copy>
+		</start>
+	</grammar>
+</xsl:template>
+
+<xsl:template match="rng:define|rng:define/@name|rng:ref/@name" mode="step7.19"/>
+
+<xsl:template match="rng:define" mode="step7.19-define">
+	<xsl:copy>
+		<xsl:attribute name="name">
+			<xsl:value-of select="concat(@name, '-', generate-id())"/>
+		</xsl:attribute>
+		<xsl:apply-templates select="@*" mode="step7.19"/>
+		<xsl:apply-templates mode="step7.19"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:grammar" mode="step7.19">
+	<xsl:apply-templates select="rng:start/*" mode="step7.19"/>
+</xsl:template>
+
+<xsl:template match="rng:ref" mode="step7.19">
+	<xsl:copy>
+		<xsl:attribute name="name">
+			<xsl:value-of select="concat(@name, '-', generate-id(ancestor::rng:grammar[1]/rng:define[@name=current()/@name]))"/>
+		</xsl:attribute>
+		<xsl:apply-templates select="@*" mode="step7.19"/>
+		<xsl:apply-templates mode="step7.19"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:parentRef" mode="step7.19">
+	<ref>
+		<xsl:attribute name="name">
+			<xsl:value-of select="concat(@name, '-', generate-id(ancestor::rng:grammar[2]/rng:define[@name=current()/@name]))"/>
+		</xsl:attribute>
+		<xsl:apply-templates select="@*" mode="step7.19"/>
+		<xsl:apply-templates mode="step7.19"/>
+	</ref>
+</xsl:template>
+
+<!-- 7.20 -->
+
+<xsl:template match="/" mode="step7.20">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.20</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.20"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-20.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.20'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.22"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.20">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.20"/>
+		<xsl:apply-templates mode="step7.20"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="/rng:grammar" mode="step7.20">
+	<xsl:copy>
+		<xsl:apply-templates select="@*" mode="step7.20"/>
+		<xsl:apply-templates mode="step7.20"/>
+		<xsl:apply-templates select="//rng:element[not(parent::rng:define)]" mode="step7.20-define"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:element" mode="step7.20-define">
+	<define name="__{rng:name}-elt-{generate-id()}">
+		<xsl:copy>
+			<xsl:apply-templates select="@*" mode="step7.20"/>
+			<xsl:apply-templates mode="step7.20"/>
+		</xsl:copy>
+	</define>
+</xsl:template>
+
+<xsl:template match="rng:element[not(parent::rng:define)]" mode="step7.20">
+	<ref name="__{rng:name}-elt-{generate-id()}"/>
+</xsl:template>
+
+<xsl:template match="rng:define[not(rng:element)]" mode="step7.20"/>
+
+<xsl:template match="rng:ref[@name=/*/rng:define[not(rng:element)]/@name]" mode="step7.20">
+	<xsl:apply-templates select="/*/rng:define[@name=current()/@name]/*" mode="step7.20"/>
+</xsl:template>
+
+<!-- 7.22 -->
+
+<xsl:template match="/" mode="step7.22">
+	<xsl:param name="out" select="1"/>
+	<xsl:param name="stop-after" select="''"/>
+	<xsl:comment>7.22</xsl:comment>
+	<xsl:variable name="step">
+		<xsl:apply-templates mode="step7.22"/>
+	</xsl:variable>
+	<xsl:if test="$out=1">
+		<exsl:document href="{$out-name}7-22.rng" method="xml" indent="yes">
+			<xsl:copy-of select="$step"/>
+		</exsl:document>
+	</xsl:if>
+	<xsl:choose>
+		<xsl:when test="$stop-after = 'step7.22'">
+			<xsl:copy-of select="$step"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:apply-templates select="exsl:node-set($step)" mode="step7.23"> 
+				<xsl:with-param name="out" select="$out"/>
+				<xsl:with-param name="stop-after" select="$stop-after"/>
+			</xsl:apply-templates>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="*|text()|@*" mode="step7.22">
+	<xsl:param name="updated" select="0"/>
+	<xsl:copy>
+		<xsl:if test="$updated != 0">
+			<xsl:attribute name="updated"><xsl:value-of select="$updated"/></xsl:attribute>
+		</xsl:if>
+		<xsl:apply-templates select="@*" mode="step7.22"/>
+		<xsl:apply-templates mode="step7.22"/>
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="@updated" mode="step7.22"/>
+
+<xsl:template match="/rng:grammar" mode="step7.22">
+	<xsl:variable name="thisIteration-rtf">
+		<xsl:copy>
+			<xsl:apply-templates select="@*" mode="step7.22"/>
+			<xsl:apply-templates mode="step7.22"/>
+		</xsl:copy>
+	</xsl:variable>
+	<xsl:variable name="thisIteration" select="exsl:node-set($thisIteration-rtf)"/>
+	<xsl:choose>
+		<xsl:when test="$thisIteration//@updated">
+			<xsl:apply-templates select="$thisIteration/rng:grammar" mode="step7.22"/>
+		</xsl:when>
+		<xsl:otherwise>
+			<xsl:copy-of select="$thisIteration-rtf"/>
+		</xsl:otherwise>
+	</xsl:choose>
+</xsl:template>
+
+<xsl:template match="rng:choice[*[1][not(self::rng:empty)] and *[2][self::rng:empty]]" mode="step7.22">
+	<xsl:copy>
+		<xsl:attribute name="updated">1</xsl:attribute>
+		<xsl:apply-templates select="*[2]" mode="step7.22" />
+		<xsl:apply-templates select="*[1]" mode="step7.22" />
+	</xsl:copy>
+</xsl:template>
+
+<xsl:template match="rng:group[count(rng:empty)=1]|rng:interleave[count(rng:empty)=1]" mode="step7.22">
+	<xsl:apply-templates select="*[not(self::rng:empty)]" mode="step7.22">
+		<xsl:with-param name="updated" select="1"/>
+	</xsl:apply-templates>
+</xsl:template>
+
+<xsl:template match="rng:group[count(rng:empty)=2]|rng:interleave[count(rng:empty)=2]|rng:choice[count(rng:empty)=2]|rng:oneOrMore[rng:empty]" mode="step7.22">
+	<rng:empty updated="1"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/conf/xsddatatypes2lens.xsl b/conf/xsddatatypes2lens.xsl
new file mode 100644
index 0000000..88ae304
--- /dev/null
+++ b/conf/xsddatatypes2lens.xsl
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
+    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";>
+
+<!--
+  Copyright 2013 Red Hat, Inc.
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  - Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+  - Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+  - Neither the name of the Red Hat, Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+  THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<!-- xsltdoc (http://www.kanzaki.com/parts/xsltdoc.xsl) header -->
+<xsl:template name="_doas_description">
+    <rdf:RDF xmlns="http://purl.org/net/ns/doas#";>
+        <rdf:Description rdf:about="">
+            <title>xsddatatypes2lens.xsl XSLT stylesheet</title>
+            <description>
+                This stylesheet contains a helper function-like template
+                xsddatatype2lens.
+            </description>
+            <author rdf:parseType="Resource">
+                <name>Jan Pokorný</name>
+                <mbox rdf:resource="jpokorny@xxxxxxxxxx"/>
+            </author>
+            <created>2013-02-01</created>
+            <release rdf:parseType="Resource">
+                <revision>0.2</revision>
+                <created>2013-02-04</created>
+            </release>
+            <rights>Copyright 2013 Red Hat, Inc.</rights>
+            <license rdf:resource="http://opensource.org/licenses/BSD-3-Clause"/>
+            <!-- acknowledgement>
+                XY added mapping for A and B.
+            </acknowledgement -->
+        </rdf:Description>
+    </rdf:RDF>
+</xsl:template>
+
+<xsl:template name="xsddatatype2lens">
+    <!--** Maps some XSD datatypes to respective primitives in Augeas lenses.
+
+        Note that there is a simple support for backward compatibility
+        through the compat-date parameter that, when explicitly set
+        as suitable (YYYYMMDD format), will (usually) bring more
+        fine-grained view for some datatypes.
+    -->
+    <xsl:param name="type"/>
+    <xsl:param name="compat-date" select="0"/>
+    <xsl:choose>
+        <!--
+            3.3 Primitive Datatypes
+         -->
+        <!-- TODO: string -->
+        <!-- TODO: boolean -->
+        <xsl:when test="$type = 'decimal'">Rx.reldecimal</xsl:when>
+        <xsl:when test="$type = 'float'">Rx.reldecimal</xsl:when>
+        <xsl:when test="$type = 'double'">Rx.reldecimal</xsl:when>
+        <!-- TODO: duration -->
+        <!-- TODO: dateTime -->
+        <!-- TODO: time -->
+        <!-- TODO: date -->
+        <!-- TODO: gYearMonth -->
+        <!-- TODO: gYear -->
+        <!-- TODO: gMonthDay -->
+        <!-- TODO: gDay -->
+        <!-- TODO: gMonth -->
+        <xsl:when test="$type = 'hexBinary'">Rx.hex</xsl:when>
+        <!-- TODO: base64Binary -->
+        <!-- TODO: anyURI -->
+        <!-- TODO: QName -->
+        <!-- TODO: NOTATION -->
+        <!--
+            3.4 Other Built-in Datatypes
+         -->
+        <!-- TODO: normalizedString -->
+        <!-- TODO: token -->
+        <!-- TODO: language -->
+        <xsl:when test="$type = 'NMTOKEN'">Xml.nmtoken</xsl:when>
+        <!-- TODO: NMTOKENS -->
+        <!-- TODO: Name -->
+        <!-- TODO: NCName -->
+        <!-- TODO: ID -->
+        <!-- TODO: IDREF -->
+        <!-- TODO: IDREFS -->
+        <!-- TODO: ENTITY -->
+        <!-- TODO: ENTITIES -->
+        <xsl:when test="$type = 'integer'">Rx.relinteger</xsl:when>
+        <xsl:when test="$type = 'nonPositiveInteger'">Rx.relinteger</xsl:when>
+        <xsl:when test="$type = 'negativeInteger'">Rx.relinteger</xsl:when>
+        <xsl:when test="$type = 'long'">Rx.relinteger</xsl:when>
+        <xsl:when test="$type = 'int'">Rx.relinteger</xsl:when>
+        <xsl:when test="$type = 'short'">Rx.relinteger</xsl:when>
+        <xsl:when test="$type = 'byte'">Rx.relinteger</xsl:when>
+        <xsl:when test="$type = 'nonNegativeInteger'">Rx.integer</xsl:when>
+        <xsl:when test="$type = 'unsignedLong'">Rx.integer</xsl:when>
+        <xsl:when test="$type = 'unsignedInt'">Rx.integer</xsl:when>
+        <xsl:when test="$type = 'unsignedShort'">Rx.integer</xsl:when>
+        <xsl:when test="$type = 'unsignedByte'">
+            <xsl:choose>
+                <xsl:when test="$compat-date >= 20130103">
+                    <xsl:text>Rx.byte</xsl:text>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:text>Rx.integer</xsl:text>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:when>
+        <xsl:when test="$type = 'positiveInteger'">Rx.integer</xsl:when>
+        <!-- TODO: yearMonthDuration -->
+        <!-- TODO: dayTimeDuration -->
+        <!-- TODO: dateTimeStamp -->
+        <xsl:otherwise>
+            <xsl:message>
+                <xsl:value-of select="concat('Unhandled XML datatype: ', $type)"/>
+            </xsl:message>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
+<!-- vim: set et ts=4 sw=4: -->
diff --git a/configure.ac b/configure.ac
index 5346279..bb79a76 100644
--- a/configure.ac
+++ b/configure.ac
@@ -95,6 +95,11 @@ AC_CHECK_PROGS([DOT], [dot])
 AC_CHECK_PROGS([DOXYGEN], [doxygen])
 AC_CHECK_PROGS([AWK], [awk])
 AC_CHECK_PROGS([SED], [sed])
+# these are for generated corosync.conf.5 and corosync.aug
+AC_CHECK_PROGS([XSLTPROC], [xsltproc])
+AC_CHECK_PROGS([ASCIIDOC], [asciidoc])
+AC_CHECK_PROGS([PYTHON], [python])
+
 AC_PATH_PROG([BASHPATH], [bash])
 
 # Checks for compiler characteristics.
diff --git a/man/.gitignore b/man/.gitignore
index 0315fcd..531c10e 100644
--- a/man/.gitignore
+++ b/man/.gitignore
@@ -1,2 +1,3 @@
 *.html
 *.3
+/corosync.conf.5
diff --git a/man/Makefile.am b/man/Makefile.am
index 9824524..c179859 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,5 +1,5 @@
 # Copyright (c) 2004 MontaVista Software, Inc.
-# Copyright (c) 2009, 2012 Red Hat, Inc.
+# Copyright (c) 2013 Red Hat, Inc.
 #
 # Authors: Steven Dake (sdake@xxxxxxxxxx)
 #          Fabio M. Di Nitto (fdinitto@xxxxxxxxxx)
@@ -39,7 +39,8 @@ xml_man			= corosync-xmlproc.8 \
 
 INDEX_HTML		= index.html
 
-autogen_man		= cpg_context_get.3 \
+autogen_man		= corosync.conf.5 \
+			  cpg_context_get.3 \
 			  cpg_context_set.3 \
 			  cpg_dispatch.3 \
 			  cpg_fd_get.3 \
@@ -115,8 +116,7 @@ EXTRA_DIST		= $(INDEX_HTML) \
 
 man_MANS		= $(autogen_man)
 
-dist_man_MANS 		= corosync.conf.5 \
-			  votequorum.5 \
+dist_man_MANS 		= votequorum.5 \
 			  corosync.8 \
 			  corosync-cmapctl.8 \
 			  corosync-blackbox.8 \
@@ -139,6 +139,18 @@ endif
 
 HTML_DOCS 		= $(dist_man_MANS:%=%.html) $(man_MANS:%=%.html)
 
+DOCBOOK_XSL_MAN		= /etc/asciidoc/docbook-xsl/manpage.xsl
+DOCBOOK_XSL_XHTML	= /etc/asciidoc/docbook-xsl/xhtml.xsl
+# verbatim from a2x
+DOCBOOK_XSLT_OPTS	= --stringparam callout.graphics 0 \
+			  --stringparam navig.graphics   0 \
+			  --stringparam admon.textlabel  1 \
+			  --stringparam admon.graphics   0 \
+			  --nonet
+# we need more space for topcenter text
+DOCBOOK_XSLT_OPTS	+= --param man.th.extra3.max.length 52
+
+
 # developer man page generation
 %.3: %.3.in $(autogen_common)
 	@echo Generating $@ man page && \
@@ -154,6 +166,17 @@ HTML_DOCS 		= $(dist_man_MANS:%=%.html) $(man_MANS:%=%.html)
 	rm -f $@-t-t && \
 	mv $@-t $@
 
+# corosync.conf.5{,.html}
+corosync.conf.5:
+	@echo Generating $@ man page && \
+	$(MAKE) $(AM_MAKEFLAGS) -C ../conf $(@:.5=.xml) && \
+	$(XSLTPROC) $(DOCBOOK_XSLT_OPTS) $(DOCBOOK_XSL_MAN) ../conf/$(@:.5=.xml)
+
+corosync.conf.5.html:
+	@echo Generating $@ HTMLized man page && \
+	$(MAKE) $(AM_MAKEFLAGS) -C ../conf $(@:.5.html=.xml) && \
+	$(XSLTPROC) $(DOCBOOK_XSLT_OPTS) $(DOCBOOK_XSL_XHTML) ../conf/$(@:.5.html=.xml) > $@
+
 clean-local:
 	rm -rf $(HTML_DOCS) $(autogen_man)
 
diff --git a/man/corosync.conf.5 b/man/corosync.conf.5
deleted file mode 100644
index 9da9dbb..0000000
--- a/man/corosync.conf.5
+++ /dev/null
@@ -1,684 +0,0 @@
-.\"/*
-.\" * Copyright (c) 2005 MontaVista Software, Inc.
-.\" * Copyright (c) 2006-2012 Red Hat, Inc.
-.\" *
-.\" * All rights reserved.
-.\" *
-.\" * Author: Steven Dake (sdake@xxxxxxxxxx)
-.\" *
-.\" * This software licensed under BSD license, the text of which follows:
-.\" *
-.\" * Redistribution and use in source and binary forms, with or without
-.\" * modification, are permitted provided that the following conditions are met:
-.\" *
-.\" * - Redistributions of source code must retain the above copyright notice,
-.\" *   this list of conditions and the following disclaimer.
-.\" * - Redistributions in binary form must reproduce the above copyright notice,
-.\" *   this list of conditions and the following disclaimer in the documentation
-.\" *   and/or other materials provided with the distribution.
-.\" * - Neither the name of the MontaVista Software, Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-.\" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-.\" * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-.\" * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-.\" * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-.\" * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-.\" * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-.\" * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-.\" * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-.\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
-.\" * THE POSSIBILITY OF SUCH DAMAGE.
-.\" */
-.TH COROSYNC_CONF 5 2012-10-10 "corosync Man Page" "Corosync Cluster Engine Programmer's Manual"
-.SH NAME
-corosync.conf - corosync executive configuration file
-
-.SH SYNOPSIS
-/etc/corosync/corosync.conf
-
-.SH DESCRIPTION
-The corosync.conf instructs the corosync executive about various parameters
-needed to control the corosync executive.  Empty lines and lines starting with
-# character are ignored.  The configuration file consists of bracketed top level
-directives.  The possible directive choices are:
-
-.TP
-totem { }
-This top level directive contains configuration options for the totem protocol.
-.TP
-logging { }
-This top level directive contains configuration options for logging.
-.TP
-quorum { }
-This top level directive contains configuration options for quorum.
-.TP
-nodelist { }
-This top level directive contains configuration options for nodes in cluster.
-.TP
-qb { }
-This top level directive contains configuration options related to libqb.
-
-.PP
-.PP
-Within the
-.B totem
-directive, an interface directive is required.  There is also one configuration
-option which is required:
-.PP
-.PP
-Within the
-.B interface
-sub-directive of totem there are four parameters which are required.  There is
-one parameter which is optional.
-
-.TP
-ringnumber
-This specifies the ring number for the interface.  When using the redundant
-ring protocol, each interface should specify separate ring numbers to uniquely
-identify to the membership protocol which interface to use for which redundant
-ring. The ringnumber must start at 0.
-
-.TP
-bindnetaddr
-This specifies the network address the corosync executive should bind
-to.
-
-bindnetaddr should be an IP address configured on the system, or a network
-address.
-
-For example, if the local interface is 192.168.5.92 with netmask
-255.255.255.0, you should set bindnetaddr to 192.168.5.92 or 192.168.5.0.
-If the local interface is 192.168.5.92 with netmask 255.255.255.192,
-set bindnetaddr to 192.168.5.92 or 192.168.5.64, and so forth.
-
-This may also be an IPV6 address, in which case IPV6 networking will be used.
-In this case, the exact address must be specified and there is no automatic
-selection of the network interface within a specific subnet as with IPv4.
-
-If IPv6 networking is used, the nodeid field in nodelist must be specified.
-
-.TP
-broadcast
-This is optional and can be set to yes.  If it is set to yes, the broadcast
-address will be used for communication.  If this option is set, mcastaddr
-should not be set.
-
-.TP
-mcastaddr
-This is the multicast address used by corosync executive.  The default
-should work for most networks, but the network administrator should be queried
-about a multicast address to use.  Avoid 224.x.x.x because this is a "config"
-multicast address.
-
-This may also be an IPV6 multicast address, in which case IPV6 networking
-will be used.  If IPv6 networking is used, the nodeid field in nodelist must
-be specified.
-
-It's not needed to use this option if cluster_name option is used. If both options
-are used, mcastaddr has higher priority.
-
-.TP
-mcastport
-This specifies the UDP port number.  It is possible to use the same multicast
-address on a network with the corosync services configured for different
-UDP ports.
-Please note corosync uses two UDP ports mcastport (for mcast receives) and 
-mcastport - 1 (for mcast sends).
-If you have multiple clusters on the same network using the same mcastaddr 
-please configure the mcastports with a gap.
-
-.TP
-ttl
-This specifies the Time To Live (TTL). If you run your cluster on a routed
-network then the default of "1" will be too small. This option provides
-a way to increase this up to 255. The valid range is 0..255.
-Note that this is only valid on multicast transport types.
-
-.PP
-.PP
-Within the
-.B totem
-directive, there are seven configuration options of which one is required,
-five are optional, and one is required when IPV6 is configured in the interface
-subdirective.  The required directive controls the version of the totem
-configuration.  The optional option unless using IPV6 directive controls
-identification of the processor.  The optional options control secrecy and
-authentication, the redundant ring mode of operation and maximum network MTU
-field.
-
-.TP
-version
-This specifies the version of the configuration file.  Currently the only
-valid version for this directive is 2.
-
-.PP
-clear_node_high_bit
-This configuration option is optional and is only relevant when no nodeid is
-specified.  Some corosync clients require a signed 32 bit nodeid that is greater
-than zero however by default corosync uses all 32 bits of the IPv4 address space
-when generating a nodeid.  Set this option to yes to force the high bit to be
-zero and therefor ensure the nodeid is a positive signed 32 bit integer.
-
-WARNING: The clusters behavior is undefined if this option is enabled on only
-a subset of the cluster (for example during a rolling upgrade).
-
-.TP
-crypto_hash
-This specifies which HMAC authentication should be used to authenticate all
-messages. Valid values are none (no authentication), md5, sha1, sha256,
-sha384 and sha512.
-
-The default is sha1.
-
-.TP
-crypto_cipher
-This specifies which cipher should be used to encrypt all messages.
-Valid values are none (no encryption), aes256, aes192, aes128 and 3des.
-Enabling crypto_cipher, requires also enabling of crypto_hash.
-
-The default is aes256.
-
-.TP
-secauth
-This specifies that HMAC/SHA1 authentication should be used to authenticate
-all messages.  It further specifies that all data should be encrypted with the
-nss library and aes256 encryption algorithm to protect data from eavesdropping.
-
-Enabling this option adds a encryption header to every message sent by totem which
-reduces total throughput. Also encryption and authentication consume extra CPU
-cycles in corosync.
-
-The default is on.
-
-WARNING: This parameter is deprecated. It's recomended to use combination of
-crypto_cipher and crypto_hash.
-
-.TP
-rrp_mode
-This specifies the mode of redundant ring, which may be none, active, or
-passive.  Active replication offers slightly lower latency from transmit
-to delivery in faulty network environments but with less performance.
-Passive replication may nearly double the speed of the totem protocol
-if the protocol doesn't become cpu bound.  The final option is none, in
-which case only one network interface will be used to operate the totem
-protocol.
-
-If only one interface directive is specified, none is automatically chosen.
-If multiple interface directives are specified, only active or passive may
-be chosen.
-
-The maximum number of interface directives that is allowed for either 
-modes (active or passive) is 2.
-
-.TP
-netmtu
-This specifies the network maximum transmit unit.  To set this value beyond
-1500, the regular frame MTU, requires ethernet devices that support large, or
-also called jumbo, frames.  If any device in the network doesn't support large
-frames, the protocol will not operate properly.  The hosts must also have their
-mtu size set from 1500 to whatever frame size is specified here.
-
-Please note while some NICs or switches claim large frame support, they support
-9000 MTU as the maximum frame size including the IP header.  Setting the netmtu
-and host MTUs to 9000 will cause totem to use the full 9000 bytes of the frame.
-Then Linux will add a 18 byte header moving the full frame size to 9018.  As a
-result some hardware will not operate properly with this size of data.  A netmtu
-of 8982 seems to work for the few large frame devices that have been tested.
-Some manufacturers claim large frame support when in fact they support frame
-sizes of 4500 bytes.
-
-When sending multicast traffic, if the network frequently reconfigures, chances are
-that some device in the network doesn't support large frames.
-
-Choose hardware carefully if intending to use large frame support.
-
-The default is 1500.
-
-.TP
-vsftype
-This directive controls the virtual synchrony filter type used to identify
-a primary component.  The preferred choice is YKD dynamic linear voting,
-however, for clusters larger then 32 nodes YKD consumes alot of memory.  For
-large scale clusters that are created by changing the MAX_PROCESSORS_COUNT
-#define in the C code totem.h file, the virtual synchrony filter "none" is
-recommended but then AMF and DLCK services (which are currently experimental)
-are not safe for use.
-
-The default is ykd.  The vsftype can also be set to none.
-
-.TP
-transport
-This directive controls the transport mechanism used.  If the interface to
-which corosync is binding is an RDMA interface such as RoCEE or Infiniband, the
-"iba" parameter may be specified.  To avoid the use of multicast entirely, a
-unicast transport parameter "udpu" can be specified.  This requires specifying
-the list of members in nodelist directive, that could potentially make up
-the membership before deployment.
-
-The default is udp.  The transport type can also be set to udpu or iba.
-
-.TP
-cluster_name
-This specifies the name of cluster and it's used for automatic generating
-of multicast address.
-
-.TP
-config_version
-This specifies version of config file. This is converted to unsigned 64-bit int.
-By default it's 0. Option is used to prevent joining old nodes with not
-up-to-date configuration. If value is not 0, and node is going for first time
-(only for first time, join after split doesn't follow this rules)
-from single-node membership to multiple nodes membership, other nodes
-config_versions are collected. If current node config_version is not
-equal to highest of collected versions, corosync is terminated.
-
-.TP
-ip_version
-Specifies version of IP to use for communication. Value can be one of
-ipv4 or ipv6. Default (if unspecified) is ipv4.
-
-
-Within the
-.B totem
-directive, there are several configuration options which are used to control
-the operation of the protocol.  It is generally not recommended to change any
-of these values without proper guidance and sufficient testing.  Some networks
-may require larger values if suffering from frequent reconfigurations.  Some
-applications may require faster failure detection times which can be achieved
-by reducing the token timeout.
-
-.TP
-token
-This timeout specifies in milliseconds until a token loss is declared after not
-receiving a token.  This is the time spent detecting a failure of a processor
-in the current configuration.  Reforming a new configuration takes about 50
-milliseconds in addition to this timeout.
-
-The default is 1000 milliseconds.
-
-.TP
-token_retransmit
-This timeout specifies in milliseconds after how long before receiving a token
-the token is retransmitted.  This will be automatically calculated if token
-is modified.  It is not recommended to alter this value without guidance from
-the corosync community.
-
-The default is 238 milliseconds.
-
-.TP
-hold
-This timeout specifies in milliseconds how long the token should be held by
-the representative when the protocol is under low utilization.   It is not
-recommended to alter this value without guidance from the corosync community.
-
-The default is 180 milliseconds.
-
-.TP
-token_retransmits_before_loss_const
-This value identifies how many token retransmits should be attempted before
-forming a new configuration.  If this value is set, retransmit and hold will
-be automatically calculated from retransmits_before_loss and token.
-
-The default is 4 retransmissions.
-
-.TP
-join
-This timeout specifies in milliseconds how long to wait for join messages in
-the membership protocol.
-
-The default is 50 milliseconds.
-
-.TP
-send_join
-This timeout specifies in milliseconds an upper range between 0 and send_join
-to wait before sending a join message.  For configurations with less then
-32 nodes, this parameter is not necessary.  For larger rings, this parameter
-is necessary to ensure the NIC is not overflowed with join messages on
-formation of a new ring.  A reasonable value for large rings (128 nodes) would
-be 80msec.  Other timer values must also change if this value is changed.  Seek
-advice from the corosync mailing list if trying to run larger configurations.
-
-The default is 0 milliseconds.
-
-.TP
-consensus
-This timeout specifies in milliseconds how long to wait for consensus to be
-achieved before starting a new round of membership configuration.  The minimum
-value for consensus must be 1.2 * token.  This value will be automatically
-calculated at 1.2 * token if the user doesn't specify a consensus value.
-
-For two node clusters, a consensus larger then the join timeout but less then
-token is safe.  For three node or larger clusters, consensus should be larger
-then token.  There is an increasing risk of odd membership changes, which stil
-guarantee virtual synchrony,  as node count grows if consensus is less than
-token.
-
-The default is 1200 milliseconds.
-
-.TP
-merge
-This timeout specifies in milliseconds how long to wait before checking for
-a partition when no multicast traffic is being sent.  If multicast traffic
-is being sent, the merge detection happens automatically as a function of
-the protocol.
-
-The default is 200 milliseconds.
-
-.TP
-downcheck
-This timeout specifies in milliseconds how long to wait before checking
-that a network interface is back up after it has been downed.
-
-The default is 1000 millseconds.
-
-.TP
-fail_recv_const
-This constant specifies how many rotations of the token without receiving any
-of the messages when messages should be received may occur before a new
-configuration is formed.
-
-The default is 2500 failures to receive a message.
-
-.TP
-seqno_unchanged_const
-This constant specifies how many rotations of the token without any multicast
-traffic should occur before the hold timer is started.
-
-The default is 30 rotations.
-
-.TP
-heartbeat_failures_allowed
-[HeartBeating mechanism]
-Configures the optional HeartBeating mechanism for faster failure detection. Keep in
-mind that engaging this mechanism in lossy networks could cause faulty loss declaration
-as the mechanism relies on the network for heartbeating.
-
-So as a rule of thumb use this mechanism if you require improved failure in low to
-medium utilized networks.
-
-This constant specifies the number of heartbeat failures the system should tolerate
-before declaring heartbeat failure e.g 3. Also if this value is not set or is 0 then the
-heartbeat mechanism is not engaged in the system and token rotation is the method
-of failure detection
-
-The default is 0 (disabled).
-
-.TP
-max_network_delay
-[HeartBeating mechanism]
-This constant specifies in milliseconds the approximate delay that your network takes
-to transport one packet from one machine to another. This value is to be set by system
-engineers and please dont change if not sure as this effects the failure detection
-mechanism using heartbeat.
-
-The default is 50 milliseconds.
-
-.TP
-window_size
-This constant specifies the maximum number of messages that may be sent on one
-token rotation.  If all processors perform equally well, this value could be
-large (300), which would introduce higher latency from origination to delivery
-for very large rings.  To reduce latency in large rings(16+), the defaults are
-a safe compromise.  If 1 or more slow processor(s) are present among fast
-processors, window_size should be no larger then 256000 / netmtu to avoid
-overflow of the kernel receive buffers.  The user is notified of this by
-the display of a retransmit list in the notification logs.  There is no loss
-of data, but performance is reduced when these errors occur.
-
-The default is 50 messages.
-
-.TP
-max_messages
-This constant specifies the maximum number of messages that may be sent by one
-processor on receipt of the token.  The max_messages parameter is limited to
-256000 / netmtu to prevent overflow of the kernel transmit buffers.
-
-The default is 17 messages.
-
-.TP
-miss_count_const
-This constant defines the maximum number of times on receipt of a token
-a message is checked for retransmission before a retransmission occurs.  This
-parameter is useful to modify for switches that delay multicast packets
-compared to unicast packets.  The default setting works well for nearly all
-modern switches.
-
-The default is 5 messages.
-
-.TP
-rrp_problem_count_timeout
-This specifies the time in milliseconds to wait before decrementing the
-problem count by 1 for a particular ring to ensure a link is not marked
-faulty for transient network failures.
-
-The default is 2000 milliseconds.
-
-.TP
-rrp_problem_count_threshold
-This specifies the number of times a problem is detected with a link before
-setting the link faulty.  Once a link is set faulty, no more data is
-transmitted upon it.  Also, the problem counter is no longer decremented when
-the problem count timeout expires.
-
-A problem is detected whenever all tokens from the proceeding processor have
-not been received within the rrp_token_expired_timeout.  The
-rrp_problem_count_threshold * rrp_token_expired_timeout should be atleast 50
-milliseconds less then the token timeout, or a complete reconfiguration
-may occur.
-
-The default is 10 problem counts.
-
-.TP
-rrp_problem_count_mcast_threshold
-This specifies the number of times a problem is detected with multicast before
-setting the link faulty for passive rrp mode. This variable is unused in active
-rrp mode.
-
-The default is 10 times rrp_problem_count_threshold.
-
-.TP
-rrp_token_expired_timeout
-This specifies the time in milliseconds to increment the problem counter for
-the redundant ring protocol after not having received a token from all rings
-for a particular processor.
-
-This value will automatically be calculated from the token timeout and
-problem_count_threshold but may be overridden.  It is not recommended to
-override this value without guidance from the corosync community.
-
-The default is 47 milliseconds.
-
-.TP
-rrp_autorecovery_check_timeout
-This specifies the time in milliseconds to check if the failed ring can be
-auto-recovered.
-
-The default is 1000 milliseconds.
-
-.PP
-Within the
-.B logging
-directive, there are several configuration options which are all optional.
-
-.PP
-The following 3 options are valid only for the top level logging directive:
-
-.TP
-timestamp
-This specifies that a timestamp is placed on all log messages.
-
-The default is off.
-
-.TP
-fileline
-This specifies that file and line should be printed.
-
-The default is off.
-
-.TP
-function_name
-This specifies that the code function name should be printed.
-
-The default is off.
-
-.PP
-The following options are valid both for top level logging directive
-and they can be overriden in logger_subsys entries.
-
-.TP
-to_stderr
-.TP
-to_logfile
-.TP
-to_syslog
-These specify the destination of logging output. Any combination of
-these options may be specified. Valid options are
-.B yes
-and
-.B no.
-
-The default is syslog and stderr.
-
-Please note, if you are using to_logfile and want to rotate the file, use logrotate(8)
-with the option 
-.B
-copytruncate.
-eg.
-.IP
-.RS
-.ne 18
-.nf
-.ta 4n 30n 33n
-/var/log/corosync.log {
-	missingok
-	compress
-	notifempty
-	daily
-	rotate 7
-	copytruncate
-}
-.ta
-.fi
-.RE
-.IP
-.PP
-
-
-.TP
-logfile
-If the
-.B to_logfile
-directive is set to
-.B yes
-, this option specifies the pathname of the log file.
-
-No default.
-
-.TP
-logfile_priority
-This specifies the logfile priority for this particular subsystem. Ignored if debug is on.
-Possible values are: alert, crit, debug (same as debug = on), emerg, err, info, notice, warning.
-
-The default is: info.
-
-.TP
-syslog_facility
-This specifies the syslog facility type that will be used for any messages
-sent to syslog. options are daemon, local0, local1, local2, local3, local4,
-local5, local6 & local7.
-
-The default is daemon.
-
-.TP
-syslog_priority
-This specifies the syslog level for this particular subsystem. Ignored if debug is on.
-Possible values are: alert, crit, debug (same as debug = on), emerg, err, info, notice, warning.
-
-The default is: info.
-
-.TP
-debug
-This specifies whether debug output is logged for this particular logger. Also can contain
-value trace, what is highest level of debug informations.
-
-The default is off.
-
-.PP
-Within the
-.B logging
-directive, logger_subsys directives are optional.
-
-.PP
-Within the
-.B logger_subsys
-sub-directive, all of the above logging configuration options are valid and
-can be used to override the default settings.
-The subsys entry, described below, is mandatory to identify the subsystem.
-
-.TP
-subsys
-This specifies the subsystem identity (name) for which logging is specified. This is the
-name used by a service in the log_init () call. E.g. 'CPG'. This directive is
-required.
-
-.PP
-Within the
-.B quorum
-directive it is possible to specify the quorum algorithm to use with the
-
-.TP
-provider
-directive. At the time of writing only corosync_votequorum is supported.
-See votequorum(5) for configuration options.
-
-.PP
-Within the
-.B nodelist
-directive it is possible to specify specific informations about nodes in cluster. Directive
-can contain only
-.B node
-sub-directive, which specifies every node that should be a member of the membership, and where
-non-default options are needed. Every node must have at least ring0_addr field filled.
-
-For UDPU, every node that should be a member of the membership must be specified.
-
-Possible options are:
-.TP
-ringX_addr
-This specifies ip address of one of the nodes. X is ring number.
-
-.TP
-nodeid
-This configuration option is optional when using IPv4 and required when using
-IPv6.  This is a 32 bit value specifying the node identifier delivered to the
-cluster membership service.  If this is not specified with IPv4, the node id
-will be determined from the 32 bit IP address the system to which the system
-is bound with ring identifier of 0.  The node identifier value of zero is
-reserved and should not be used.
-
-.PP
-Within the
-.B qb
-directive it is possible to specify options for libqb.
-
-Possible option is:
-.TP
-ipc_type
-This specifies type of IPC to use. Can be one of native (default), shm and socket.
-Native means one of shm or socket, depending on what is supported by OS. On systems
-with support for both, SHM is selected. SHM is generally faster, but need to allocate
-ring buffer file in /dev/shm.
-
-.SH "FILES"
-.TP
-/etc/corosync/corosync.conf
-The corosync executive configuration file.
-
-.SH "SEE ALSO"
-.BR corosync_overview (8),
-.BR votequorum (5),
-.BR logrotate (8)
-.PP
-- 
1.7.11.7

_______________________________________________
discuss mailing list
discuss@xxxxxxxxxxxx
http://lists.corosync.org/mailman/listinfo/discuss



[Index of Archives]     [Linux Clusters]     [Corosync Project]     [Linux USB Devel]     [Linux Audio Users]     [Photo]     [Yosemite News]    [Yosemite Photos]    [Linux Kernel]     [Linux SCSI]     [X.Org]

  Powered by Linux