2013-10-08 Keith Marshall <keithmarshall@users.sourceforge.net>
+ Implement an XML validation test suite.
+
+ * tests: New directory.
+ * tests/configure.ac tests/Makefile.in: New files.
+ * tests/xmlcheck.cpp tests/pkgspec.xsd: Likewise.
+
+2013-10-08 Keith Marshall <keithmarshall@users.sourceforge.net>
+
Delegate version assignment to VERSION.m4 auxiliary file.
* VERSION.m4: New file.
--- /dev/null
+# @configure_input@
+#
+# $Id$
+#
+# Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+# Copyright (C) 2013, MinGW.org Project
+#
+#
+# Makefile template for generating mingw-get distribution manifests.
+#
+# Project: @PACKAGE_TARNAME@
+# Version: @PACKAGE_VERSION@
+#
+#
+# This is free software. Permission is granted to copy, modify and
+# redistribute this software, under the provisions of the GNU General
+# Public License, Version 3, (or, at your option, any later version),
+# as published by the Free Software Foundation; see the file COPYING
+# for licensing details.
+#
+# Note, in particular, that this software is provided "as is", in the
+# hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+# even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+# PARTICULAR PURPOSE. Under no circumstances will the author, or the
+# MinGW Project, accept liability for any damages, however caused,
+# arising from the use of this software.
+#
+VPATH = @srcdir@
+vpath VERSION.m4 @srcdir@/..
+
+CXX = @CXX@
+CXXFLAGS = @CXXFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+
+EXEEXT = @EXEEXT@
+OBJEXT = @OBJEXT@
+
+srcdir = @srcdir@
+xml_datarootdir = ${srcdir}/..
+
+XMLCHECK =
+
+check: @XERCES_C_TOOLS@
+ ./xmlcheck$(EXEEXT) ${srcdir}/pkgspec.xsd ${xml_datarootdir}/*/*.xml \
+ 2> xmlcheck.log && rm -f xmlcheck.log \
+ || { cat xmlcheck.log; exit 1; }
+
+no-xerces-c-tools:
+ @for msg in $(NO_XERCES_C_TOOLS_MSG); do echo $$msg; done; false
+
+NO_XERCES_C_TOOLS_MSG = "" \
+ "This test suite requires the Xerces-C++ SDK, but it appears that" \
+ "this has not been installed on your system." "" \
+ "Please install a copy of this SDK, which is compatible with your" \
+ "system C++ compiler, if you wish to run this test suite." ""
+
+xerces-c-tools: xmlcheck$(EXEEXT)
+xmlcheck$(EXEEXT): xmlcheck.$(OBJEXT)
+ $(CXX) $(CXXFLAGS) -o $@ $(LDFLAGS) $^ $(LIBS)
+
+sinclude *.d
+%.$(OBJEXT): %.cpp
+ $(CXX) -MMD -MP -c $(CPPFLAGS) $(CXXFLAGS) -o $@ $<
+
+configure: configure.ac VERSION.m4
+ cd ${srcdir}; autoconf
+
+config.status: configure
+ ./config.status --recheck
+
+Makefile: config.status Makefile.in
+ ./config.status
+
+clean:
+ rm -f *.$(OBJEXT) xmlcheck$(EXEEXT)
+
+# $RCSfile$: end of file
--- /dev/null
+# configure.ac -*- autoconf -*- vim: filetype=config
+#
+# $Id$
+#
+# Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+# Copyright (C) 2013, MinGW.org Project
+#
+#
+# Configuration script for mingw-dist validation suite
+#
+#
+# This is free software. Permission is granted to copy, modify and
+# redistribute this software, under the provisions of the GNU General
+# Public License, Version 3, (or, at your option, any later version),
+# as published by the Free Software Foundation; see the file COPYING
+# for licensing details.
+#
+# Note, in particular, that this software is provided "as is", in the
+# hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+# even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+# PARTICULAR PURPOSE. Under no circumstances will the author, or the
+# MinGW Project, accept liability for any damages, however caused,
+# arising from the use of this software.
+#
+ m4_include([../VERSION.m4])
+ AC_INIT([mingw-dist-tests],__VERSION__,[http://mingw.org/reporting_bugs])
+#
+# Building the test suite driver program requires a C++ compiler,
+# (which must emit binary code to run on the build platform).
+#
+ AC_PROG_CXX
+#
+# The test suite is heavily dependent on the Xerces-C++ SDK; check, to
+# ensure that this is installed.
+#
+ AC_CHECK_LIB([xerces-c],[main])
+ AS_IF([test x"$ac_cv_lib_xerces_c_main" = xyes],dnl
+ [XERCES_C_TOOLS=xerces-c-tools],[XERCES_C_TOOLS=no-xerces-c-tools])dnl
+ AC_SUBST([XERCES_C_TOOLS])
+#
+# Configure output comprises a simple makefile.
+#
+ AC_CONFIG_FILES([Makefile])
+ AC_OUTPUT
+#
+# $RCSfile$: end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <!--
+ $Id$
+
+ XSDL schema for validation of mingw-get package specifications.
+
+ Written by Keith Marshall <keithmarshall@users.sourceforge.net>
+ Copyright (C) 2013, MinGW.org Project
+
+
+ This file is part of the mingw-dist catalogue validation suite.
+
+ This is free software. Permission is granted to copy, modify and
+ redistribute this software, under the provisions of the GNU General
+ Public License, Version 3, (or, at your option, any later version),
+ as published by the Free Software Foundation; see the file COPYING
+ for licensing details.
+
+ Note, in particular, that this software is provided "as is", in the
+ hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+ even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+ PARTICULAR PURPOSE. Under no circumstances will the author, or the
+ MinGW Project, accept liability for any damages, however caused,
+ arising from the use of this software.
+
+
+ Note to maintainers, (because this may seem counter intuitive, and
+ thus may be eminently forgettable): whilst the structure of any XML
+ document demands that attributes be specified in the opening tag of
+ their associated element, and thus they PRECEDE the element content,
+ XSDL requires that they be declared AFTER the corresponding content
+ model; please ensure that their declarations are correctly placed
+ within this XSDL schema.
+ -->
+
+ <xs:element name="software-distribution" type="specification-document" />
+ <!--
+ The package specification document root is always an XML element
+ named "software-distribution", which must conform to the following
+ type definition
+ -->
+ <xs:complexType name="specification-document">
+ <xs:sequence>
+ <!--
+ The entire specification comprises a sequence of at most three
+ optionally repeating section types; unless it is omitted...
+ -->
+ <xs:element name="package-group-hierarchy" minOccurs="0">
+ <!--
+ ...this must appear as the first section; only one instance
+ is permitted.
+ -->
+ <xs:complexType>
+ <xs:sequence maxOccurs="unbounded">
+ <!--
+ Within the package group hierarchy, at least one package
+ group must be specified. Additional group specifications
+ may be added, without limit; all must conform to the type
+ definition, as specified below.
+ -->
+ <xs:element name="package-group" type="package-group-definition" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <!--
+ When present, a package group hierarchy specification is typically
+ followed by one or more instances of...
+ -->
+ <xs:element name="package-list" minOccurs="0" maxOccurs="unbounded">
+ <!--
+ ...this; it is optional, but when present, all instances must
+ follow the package group hierarchy specification, (if any), and
+ must precede any "package collection" specification.
+ -->
+ <xs:complexType>
+ <!--
+ This is an "attribute only" element. The "catalogue" attribute
+ must be specified; it represents the name of a further XML file
+ to be included within the specification document, omitting both
+ any directory path, and the ".xml" file name suffix.
+ -->
+ <xs:attribute name="catalogue" type="xs:string" use="required" />
+ <!--
+ The specification author typically does NOT specify the "issue"
+ attribute; it is inserted automatically by the "mingw-dist" build
+ system, as an indicator to mingw-get when the catalogue named by
+ the "catalogue" attribute should be updated.
+ -->
+ <xs:attribute name="issue" type="serial-number" />
+ </xs:complexType>
+ </xs:element>
+ <!--
+ Typically specified in isolation, (i.e. entirely separated from any
+ package group hierarchy or package list specification), the final
+ section which may appear in the specification document is...
+ -->
+ <xs:element name="package-collection" minOccurs="0" maxOccurs="unbounded">
+ <!--
+ ...this. Although any number of instances may appear in a single
+ specification document, most commonly, each document will specify
+ only one package collection, which itself comprises...
+ -->
+ <xs:complexType>
+ <!--
+ ...a group of child elements, as defined below, specifying the
+ set of packages which are included in this collection.
+ -->
+ <xs:group ref="package-collection-specification" />
+ <!--
+ All of the individual packages in any one collection MUST
+ be assigned to a common installation "subsystem", which is
+ identified by this required attribute.
+ -->
+ <xs:attribute name="subsystem" type="xs:string" use="required" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <!--
+ The specification document also supports three attributes: the "project"
+ and "host" attributes are optional; they are provided primarily as an aid
+ to documenting the origin of the specified package collection, but they
+ are not actually used by mingw-get.
+ -->
+ <xs:attribute name="project" type="xs:string" default="MinGW" />
+ <xs:attribute name="home" type="xs:anyURI" default="http://www.mingw.org" />
+ <!--
+ On the other hand, the "issue" attribute is required; it takes a string
+ value, conforming to the type definition below; in the source context of
+ a mingw-dist catalogue build, it should be specified using the template
+ form for the value; other contexts should use the YYYYMMDDNN form.
+ -->
+ <xs:attribute name="issue" type="serial-number" use="required" />
+ </xs:complexType>
+
+ <xs:simpleType name="serial-number">
+ <!--
+ This specialization of the XSDL string type is to be used to specify the
+ value for the "issue" attribute, which is required in every specification
+ document. It may be either be the explicit "@YYYYMMDDNN@" substitution
+ template, as used in mingw-dist source documents, or any ten character
+ alpha-numeric pattern derived from this template.
+ -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="(@YYYYMMDDNN@)|([0-9A-Z]{10})" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="package-group-definition">
+ <!--
+ Defines the structure of the elements used to specify package groups,
+ within the package group hierarchy section of the specification document.
+ Elements of this type may repeat any number of times, both within this
+ context, and recursively nested within other package groups.
+ -->
+ <xs:sequence minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="package-group" type="package-group-definition" />
+ </xs:sequence>
+ <!--
+ Every package group must be named; mingw-get uses the value of the "name"
+ attribute as a label, to create a tree-view display representation of the
+ package group hierarchy, in the left hand pane of the GUI client window.
+ -->
+ <xs:attribute name="name" type="xs:string" use="required" />
+ <!--
+ The optional "expand" attribute provides a mechanism for specifying those
+ levels of a nested package group hierarchy which are to be shown expanded,
+ in the initial rendition of the tree-view display.
+ -->
+ <xs:attribute name="expand" type="xs:boolean" />
+ </xs:complexType>
+
+ <xs:group name="package-group-association-and-description">
+ <!--
+ Provides the mechanism for defining package to package group associations,
+ allowing multiple associations to be specified, and intermingled with any
+ number of package description elements, at the beginning of any individual
+ package specification.
+ -->
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="affiliate">
+ <!--
+ Defines a single package to package group association, in terms of
+ a single attribute only specification element...
+ -->
+ <xs:complexType>
+ <xs:attribute name="group" type="xs:string" />
+ </xs:complexType>
+ </xs:element>
+ <!--
+ ...whilst this allows "description" blocks to be interspersed among
+ repeating "affiliate" specifications.
+ -->
+ <xs:element name="description" type="package-description" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:group>
+
+ <xs:complexType name="package-description">
+ <!--
+ Defines the structure of a "description" block as a container for a
+ sequence of one or more "paragraph" blocks...
+ -->
+ <xs:sequence>
+ <xs:element name="paragraph" maxOccurs="unbounded" />
+ </xs:sequence>
+ <!--
+ ...optionally qualified by "lang" and/or "title" attributes.
+ -->
+ <xs:attribute name="lang" type="xs:string" default="en" />
+ <xs:attribute name="title" type="xs:string" />
+ </xs:complexType>
+
+ <xs:group name="package-collection-specification">
+ <xs:sequence>
+ <!--
+ Defines the sequence of XML elements which are required to properly
+ specify any package group collection.
+ -->
+ <xs:element name="download-host">
+ <xs:complexType>
+ <!--
+ The first is always a specification for the URL whence all packages
+ in the collection may be downloaded; it is declared as a string type
+ because it is specified in a template format, which may violate the
+ constraints of the XSDL anyURI type, e.g.:
+
+ <download-host uri="http://host.domain.com/packages/%F" />
+
+ where "%F" is substituted, at download time, by the actual name of
+ the archive file wich is to be downloaded.
+
+ Note: only one template may be specified for any package collection;
+ thus all packages in any collection MUST be served from one common
+ hosting domain.
+ -->
+ <xs:attribute name="uri" type="xs:string" use="required" />
+ </xs:complexType>
+ </xs:element>
+ <!--
+ The download host specification may be followed by an optional sequence
+ of package group association specifications. These must conform to the
+ format defined above, where it may be seen that they comprise any number
+ of "affiliate" and "description" elements, in arbitrary order. If any
+ of these are specified here, they apply to every package subsequently
+ included within the collection.
+ -->
+ <xs:group ref="package-group-association-and-description" />
+ <xs:choice maxOccurs="unbounded">
+ <!--
+ The principal content of any package collection comprises an arbitrary
+ number of "package" specification elements.
+ -->
+ <xs:element name="package">
+ <xs:complexType>
+ <xs:sequence>
+ <!--
+ Each package specification begins with an optional description,
+ and an arbitrary set of package group association declarations.
+ -->
+ <xs:group ref="package-group-association-and-description" />
+ <!--
+ This must be followed by an arbitray sequence of one or more
+ "source", "licence", "component", or "action" specifications.
+ -->
+ <xs:choice maxOccurs="unbounded">
+ <xs:group ref="source-or-licence-archive-reference" />
+ <xs:element name="component" type="component-specification" />
+ <xs:element name="action" type="action-script" />
+ </xs:choice>
+ </xs:sequence>
+ <!--
+ Each package MUST be named; it may also be assigned to the class
+ of "virtual" packages, so identifying it as a meta-package, and it
+ may also be identified by a list of aliases.
+ -->
+ <xs:attribute name="name" type="xs:string" use="required" />
+ <xs:attribute name="class" type="package-class" />
+ <xs:attribute name="alias" type="xs:string" />
+ </xs:complexType>
+ </xs:element>
+ <!--
+ Actions may also be specified within the package-collection scope, in
+ which case they apply to all packages, and component packages, which
+ are declared within the collection.
+ -->
+ <xs:element name="action" type="action-script" />
+ </xs:choice>
+ </xs:sequence>
+ </xs:group>
+
+ <xs:simpleType name="package-class">
+ <!--
+ Any package may be specified as a meta-package, by assigning it a "class"
+ attribute value of "virtual"; specification of this attribute is optional,
+ but no other value is permitted.
+ -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="virtual" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:complexType name="component-specification">
+ <!--
+ Each "package" should be subdivided into one or more "component" packages,
+ each of which may bear its own individual description and/or package group
+ associations. Each "component" package should also define a sequence of
+ "release" specifications, optionally qualified by specifications of any
+ dependencies which are common to all releases of the "component" package,
+ and any actions which should be performed when installing or removing any
+ release of the "component" package.
+ -->
+ <xs:choice maxOccurs="unbounded">
+ <xs:group ref="package-group-association-and-description" />
+ <xs:element name="release" type="release-specification" />
+ <xs:element name="requires" type="dependency-specification" />
+ <xs:element name="action" type="action-script" />
+ </xs:choice>
+ <!--
+ Every "component" package MUST be classified; however, the choice of
+ "class" name is unrestricted.
+ -->
+ <xs:attribute name="class" type="xs:string" use="required" />
+ </xs:complexType>
+
+ <xs:complexType name="release-specification">
+ <!--
+ Each individual release of any component package must be specified by its
+ own "release" specification element; this may be qualified by a "download"
+ specification, (for cases where the actual archive file name differs from
+ the canonical "tarname", or is "none" in the case of a meta-release, for
+ which the container "package" element cannot be declared as "virtual");
+ each "release" may also specify its own set of dependencies, and/or its
+ own specific "source" and/or "licence" archive associations.
+ -->
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="download" type="tarname-reference" />
+ <xs:element name="requires" type="dependency-specification" />
+ <xs:group ref="source-or-licence-archive-reference" />
+ </xs:choice>
+ <!--
+ Every "release" MUST be identified by a canonical "tarname" attribute.
+ -->
+ <xs:attribute ref="tarname" use="required" />
+ </xs:complexType>
+
+ <xs:complexType name="action-script" mixed="true">
+ <!--
+ Any action is specified as Lua script, appearing as textual content within
+ the body of the "action" element; it also requires a specification for its
+ "class" attribute, to identify the context in which it is to be executed.
+ -->
+ <xs:attribute name="class" type="action-class" use="required" />
+ </xs:complexType>
+
+ <xs:simpleType name="action-class">
+ <!--
+ The context in which any specified action is to be executed must be one of
+ "pre-install", "post-install", "pre-remove", or "post-remove".
+ -->
+ <xs:restriction base="xs:string">
+ <xs:pattern value="(pre|post)-(install|remove)" />
+ </xs:restriction>
+ </xs:simpleType>
+
+ <xs:group name="source-or-licence-archive-reference">
+ <!--
+ "source" and "licence" elements are simple "attribute only" elements, each
+ of which requires only a "tarname" attribute; note that the spelling which
+ is required for the "licence" element uses the noun form, as prescribed by
+ the Oxford Dictionary of World English; use of the US English form, where
+ noun and verb are both spelled as "license" is NOT supported.
+ -->
+ <xs:choice>
+ <xs:element name="source" type="tarname-reference" />
+ <xs:element name="licence" type="tarname-reference" />
+ </xs:choice>
+ </xs:group>
+
+ <xs:complexType name="tarname-reference">
+ <!--
+ When any element accepts a "tarname" attribute, then specification of that
+ attribute is mandatory.
+ -->
+ <xs:attribute ref="tarname" use="required" />
+ </xs:complexType>
+ <!--
+ The "tarname" attribute is a simple string type; it should match a specific
+ pattern, but for the time being, we do not attempt to verify it.
+ -->
+ <xs:attribute name="tarname" type="xs:string" />
+
+ <xs:complexType name="dependency-specification">
+ <!--
+ Any "requires" specification MUST include one or more of the following
+ attributes, to declare an equality or a range dependency. It may be noted
+ that, while a range specification requires two such attributes, there will
+ be some combinations which will be mutually inconsistent; unfortunately,
+ XSDL provides no mechanism to filter out such combinations.
+ -->
+ <xs:attribute name="lt" type="xs:string" />
+ <xs:attribute name="le" type="xs:string" />
+ <xs:attribute name="eq" type="xs:string" />
+ <xs:attribute name="ge" type="xs:string" />
+ <xs:attribute name="gt" type="xs:string" />
+ </xs:complexType>
+
+</xs:schema>
+
+<!-- vim: set nocompatible fileformat=unix: -->
+<!-- vim: set smartindent shiftwidth=2 expandtab textwidth=80 wrap: -->
+<!-- $RCSfile$: end of file -->
--- /dev/null
+/*
+ * xmlcheck.cpp
+ *
+ * $Id$
+ *
+ * Adapted from load-grammar-dom.cxx
+ * Written by Boris Kolpackov <boris@codesynthesis.com>
+ * Assigned, by the author, to the public domain
+ *
+ * This program uses Xerces-C++ DOM parser to load a set of schema files
+ * and then to validate a set of XML documents against these schemas. To
+ * build this program you will need Xerces-C++ 3.0.0 or later. For more
+ * information, see:
+ *
+ * http: *www.codesynthesis.com/~boris/blog/2010/03/15/validating-external-schemas-xerces-cxx/
+ *
+ *
+ * Adaptation by Keith Marshall <keithmarshall@users.sourceforge.net>
+ * Copyright (C) 2013, MinGW.org Project
+ *
+ * This is free software. Permission is granted to copy, modify and
+ * redistribute this software, under the provisions of the GNU General
+ * Public License, Version 3, (or, at your option, any later version),
+ * as published by the Free Software Foundation; see the file COPYING
+ * for licensing details.
+ *
+ * Note, in particular, that this software is provided "as is", in the
+ * hope that it may prove useful, but WITHOUT WARRANTY OF ANY KIND; not
+ * even an implied WARRANTY OF MERCHANTABILITY, nor of FITNESS FOR ANY
+ * PARTICULAR PURPOSE. Under no circumstances will the author, or the
+ * MinGW Project, accept liability for any damages, however caused,
+ * arising from the use of this software.
+ *
+ */
+#include <cstdio>
+#include <string>
+#include <memory> /* for std::auto_ptr */
+#include <cstddef> /* for std::size_t */
+
+#include <libgen.h> /* for basename() */
+
+#include <xercesc/util/XMLUni.hpp>
+#include <xercesc/util/XMLString.hpp>
+#include <xercesc/util/PlatformUtils.hpp>
+
+#include <xercesc/dom/DOM.hpp>
+
+#include <xercesc/validators/common/Grammar.hpp>
+#include <xercesc/framework/XMLGrammarPoolImpl.hpp>
+
+using namespace std;
+using namespace xercesc;
+
+#if _XERCES_VERSION < 30000
+/* We need at least Xerces-C++ version 3.0.0
+ */
+# error Xerces-C++ version >= 3.0.0 is required!
+
+#elif _XERCES_VERSION >= 30100
+/* We may wish to exploit some features which were not introduced
+ * until Xerces-C++ version 3.1.0
+ */
+# define IF_XERCES_30100_PLUS( STATEMENT ) STATEMENT
+
+#else
+/* We cannot use Xerces-C++ version 3.1.0 features; make them no-op.
+ */
+# define IF_XERCES_30100_PLUS( STATEMENT )
+#endif
+
+class error_handler: public DOMErrorHandler
+{
+ /* A locally defined class for capture of fault conditions, as
+ * reported by our DOM parsers.
+ */
+ public:
+ /* Constructor.
+ */
+ error_handler( const char *rel): source(rel),
+ first_report(true), new_document(true), failed(false){}
+
+ /* Method to access recorded error condition status.
+ */
+ bool has_failed() const { return failed; }
+
+ /* Method to reset recorded status, in preparation for
+ * parsing a new document.
+ */
+ void reset(){ new_document = true; failed = false; }
+
+ /* Method to handle error conditions, on behalf of our
+ * DOM parsers.
+ */
+ virtual bool handleError( const xercesc::DOMError& );
+
+ private:
+ /* The type of XML input being parsed, recorded when we
+ * consturct the error handler for binding to a particular
+ * DOM parser.
+ */
+ const char *source;
+
+ /* Recording for error condition status.
+ */
+ bool first_report, new_document, failed;
+};
+
+bool
+error_handler::handleError( const xercesc::DOMError& condition )
+{
+ /* Implementation of the error handler, which we will use to capture
+ * status, and report abnormal conditions detected by our DOM parsers.
+ */
+ bool warn = condition.getSeverity() == DOMError::DOM_SEVERITY_WARNING;
+
+ /* Record detection of any condition which is more severe than
+ * a simple warning.
+ */
+ if( ! warn ) failed = true;
+
+ /* Identify the location, within the current XML schema or document
+ * file, where the abnormality has been detected.
+ */
+ DOMLocator* loc( condition.getLocation() );
+
+ /* When this is the first abnormality detected within the current
+ * XML schema or document file...
+ */
+ if( new_document )
+ {
+ /* ...but we've previously reported abnormalities within another
+ * input file, then separate the current report from diagnostics
+ * relating to that other file...
+ */
+ if( ! first_report ) fputc( '\n', stderr );
+
+ /* ...then, regardless of whatever may have gone before, format
+ * and emit a report header to identify the current file.
+ */
+ char *uri = XMLString::transcode( loc->getURI() );
+ fprintf( stderr, "Problem Report:\n%s: %s\n", source, uri );
+ XMLString::release( &uri );
+
+ /* Record that we've now emitted a report header and diagnostic
+ * for the current XML input file.
+ */
+ first_report = new_document = false;
+ }
+
+ /* Whether we added a new report header, or not, we still have a
+ * diagnostic message to emit.
+ */
+ char* msg = XMLString::transcode( condition.getMessage() );
+ fprintf( stderr, "%d:%d: %s: %s\n", loc->getLineNumber(),
+ loc->getColumnNumber(), warn ? "WARNING" : "ERROR", msg
+ );
+ XMLString::release( &msg );
+
+ /* Finally, we return "true" to tell the DOM parser that we've
+ * handled the error, and that it should continue parsing.
+ */
+ return true;
+}
+
+static bool
+insufficient_arguments( bool status, const char *program_pathname )
+{
+ /* Diagnostic routine to report a lack of any command arguments
+ * to specify the XML documents which are to be validated.
+ */
+ if( status )
+ {
+ /* The "status" flag indicates an abnormal condition...
+ *
+ * We want to call "basename()" on the passed "program_pathname";
+ * while this is likely safe, it MAY try to modify the input string,
+ * so create a temporary working copy...
+ */
+ char progname[1 + strlen( program_pathname )];
+
+ /* ...then format and emit an appropriate diagnostic message.
+ */
+ strcpy( progname, program_pathname );
+ fprintf( stderr, "%s: no XML documents specified for validation\n"
+ "usage: %s [schema.xsd ...] document.xml ...\n", basename( progname ),
+ program_pathname
+ );
+ }
+ /* Irrespective of condition, we echo back the input state.
+ */
+ return status;
+}
+
+DOMLSParser*
+create_parser( XMLGrammarPool* pool )
+{
+ /* Helper function, to instantiate a DOM parser with "LS", (load and
+ * save), capability, (although we intend to use only "load").
+ */
+ const XMLCh ls_id[] = { chLatin_L, chLatin_S, chNull };
+
+ /* Locate a DOM implementation, providing the requisite "LS" feature.
+ */
+ DOMImplementation* impl(
+ DOMImplementationRegistry::getDOMImplementation( ls_id ) );
+
+ /* Instantiate a parser, based on this DOM implementation.
+ */
+ DOMLSParser* parser(
+ impl->createLSParser(
+ DOMImplementationLS::MODE_SYNCHRONOUS,
+ 0,
+ XMLPlatformUtils::fgMemoryManager,
+ pool ) );
+
+ /* Retrieve a pointer to its configuration data...
+ */
+ DOMConfiguration* conf( parser->getDomConfig() );
+
+ /* ...so we may apply this commonly useful configuration.
+ */
+ conf->setParameter( XMLUni::fgDOMComments, false );
+ conf->setParameter( XMLUni::fgDOMDatatypeNormalization, true );
+ conf->setParameter( XMLUni::fgDOMElementContentWhitespace, false );
+ conf->setParameter( XMLUni::fgDOMNamespaces, true );
+ conf->setParameter( XMLUni::fgDOMEntities, false );
+
+ /* Enable validation.
+ */
+ conf->setParameter( XMLUni::fgDOMValidate, true );
+ conf->setParameter( XMLUni::fgXercesSchema, true );
+ conf->setParameter( XMLUni::fgXercesSchemaFullChecking, false );
+
+ /* Use the loaded grammar during parsing.
+ */
+ conf->setParameter( XMLUni::fgXercesUseCachedGrammarInParse, true );
+
+ /* Don't load schemas from any other source (e.g., from XML document's
+ * xsi:schemaLocation attributes).
+ */
+ conf->setParameter( XMLUni::fgXercesLoadSchema, false );
+
+ /* Xerces-C++ 3.1.0 is the first version with working support for
+ * multiple import.
+ */
+ IF_XERCES_30100_PLUS(
+ conf->setParameter( XMLUni::fgXercesHandleMultipleImports, true )
+ );
+
+ /* We will release the DOM document ourselves.
+ */
+ conf->setParameter( XMLUni::fgXercesUserAdoptsDOMDocument, true );
+
+ /* Return a pointer to the instantiated parser.
+ */
+ return parser;
+}
+
+static inline int
+validation_status( int argc, char **argv )
+{
+ int retcode = 0;
+
+ /* Initialize a grammer pool, for use by our parser instances.
+ */
+ MemoryManager* mm( XMLPlatformUtils::fgMemoryManager );
+ auto_ptr<XMLGrammarPool> gp( new XMLGrammarPoolImpl( mm ) );
+
+ /* Load the schema definitions into the grammar pool.
+ */
+ int argind = 1;
+ {
+ /* Instantiate a parser for the schema definition file(s).
+ */
+ DOMLSParser* parser( create_parser( gp.get() ) );
+
+ /* Initialize an error handler for the schema context,
+ * and bind it to the schema file parser.
+ */
+ error_handler eh( "XML Schema" );
+ parser->getDomConfig()->setParameter( XMLUni::fgDOMErrorHandler, &eh );
+
+ /* Scan command arguments, left to right, to identify any XML schema
+ * files which we are expected to interpret.
+ */
+ do { const char *source = argv[argind]; size_t extent = strlen( source );
+ if( (extent > 4) && (strcasecmp( source + extent - 4, ".xsd" ) == 0) )
+ {
+ /* We have a "*.xsd" file to parse; do so, loading the grammar...
+ */
+ if( !parser->loadGrammar( source, Grammar::SchemaGrammarType, true ) )
+ {
+ /* ...but complain, and bail out, if loading fails...
+ */
+ fprintf( stderr, "%s: error: unable to load\n", source );
+ retcode = 1;
+ }
+ if( eh.has_failed() )
+ /*
+ * ...or if any schema parsing error was encountered.
+ */
+ retcode = 1;
+ }
+ else
+ /* We've exhausted the "*.xsd" file references; break out of
+ * the scanning loop, without further ceremony.
+ */
+ break;
+
+ /* Continue for the next "*.xsd" file, if any, provided there
+ * have been no schema abormalities detected thus far.
+ */
+ } while( (retcode == 0) && (++argind < argc) );
+
+ /* We're finished with our schema parser; release its resource pool.
+ */
+ parser->release();
+ }
+
+ /* Before proceeding to parse any XML documents, check that any
+ * specified XML schemas have been loaded successfully.
+ */
+ if( retcode == 0 )
+ {
+ /* It's okay to proceed, but it would be pointless to do so...
+ */
+ if( insufficient_arguments( argind >= argc, *argv ) )
+ /*
+ * ...when there are no remaining arguments to specify any
+ * XML documents for checking; in this case, bail out.
+ */
+ return 1;
+
+ /* Lock the grammar pool. This is necessary if we plan to use the
+ * same grammar pool in multiple threads (this way we can reuse the
+ * same grammar in multiple parsers). Locking the pool disallows any
+ * modifications to the pool, such as an attempt by one of the threads
+ * to cache additional schemas.
+ */
+ gp->lockPool();
+
+ /* Instantiate a new parser, to process the XML documents.
+ */
+ DOMLSParser* parser( create_parser( gp.get() ) );
+
+ /* Initialize an error handler for the XML document context,
+ * and bind it to the new parser.
+ */
+ error_handler eh( "XML Document" );
+ parser->getDomConfig()->setParameter( XMLUni::fgDOMErrorHandler, &eh );
+
+ /* Process all remaining arguments, as references to XML documents.
+ */
+ while( argind < argc )
+ {
+ /* Reset the error handler state, prior to loading each document.
+ */
+ eh.reset();
+ DOMDocument* doc( parser->parseURI( argv[argind++] ) );
+
+ /* In this application, all we care about is that the document
+ * can be successfully read by our validating parser; if we did
+ * read it successfully, we have no further use for it, se we
+ * may simply set it aside.
+ */
+ if( doc ) doc->release();
+
+ /* If any error occurred, while parsing the current document,
+ * the error handler will have recorded it; we need to capture
+ * that state here, for our eventual return code.
+ */
+ if( eh.has_failed() ) retcode = 1;
+ }
+ /* When all specified documents have been validated, we are done
+ * with our parser, so we may release its resource pool.
+ */
+ parser->release();
+ }
+ /* Report back, with the cumulative status from XML document parsing.
+ */
+ return retcode;
+}
+
+int
+main( int argc, char **argv )
+{
+ /* Fewer than one argument, after the command verb itself,
+ * is not useful; complain, and bail out.
+ */
+ if( insufficient_arguments( argc < 2, *argv ) )
+ return 1;
+
+ /* We must initialize Xerces-C++, before we can use it.
+ */
+ XMLPlatformUtils::Initialize();
+
+ /* Determine the validation status for all specified XML documents,
+ * with respect to any specified XML schema definitions.
+ */
+ int retcode = validation_status( argc, argv );
+
+ /* Shut down the Xerces-C++ subsystem, before returning the resultant
+ * validation status code to the operating system.
+ */
+ XMLPlatformUtils::Terminate();
+ return retcode;
+}
+
+/* $RCSfile$: end of file */