Re: [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]

 



As quickly discussed on IRC, the idea of autogeneration is nice.

Please push to a branch so that we can smooth the edges and integration
with autotools, and then we can re-post the final patch set.

Fabio

On 2/5/2013 4:07 PM, Jan Pokorný wrote:
> 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
> 

_______________________________________________
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