Home | History | Annotate | Download | only in libxml2
      1 /*
      2  * schematron.c : implementation of the Schematron schema validity checking
      3  *
      4  * See Copyright for the status of this software.
      5  *
      6  * Daniel Veillard <daniel (at) veillard.com>
      7  */
      8 
      9 /*
     10  * TODO:
     11  * + double check the semantic, especially
     12  *        - multiple rules applying in a single pattern/node
     13  *        - the semantic of libxml2 patterns vs. XSLT production referenced
     14  *          by the spec.
     15  * + export of results in SVRL
     16  * + full parsing and coverage of the spec, conformance of the input to the
     17  *   spec
     18  * + divergences between the draft and the ISO proposed standard :-(
     19  * + hook and test include
     20  * + try and compare with the XSLT version
     21  */
     22 
     23 #define IN_LIBXML
     24 #include "libxml.h"
     25 
     26 #ifdef LIBXML_SCHEMATRON_ENABLED
     27 
     28 #include <string.h>
     29 #include <libxml/parser.h>
     30 #include <libxml/tree.h>
     31 #include <libxml/uri.h>
     32 #include <libxml/xpath.h>
     33 #include <libxml/xpathInternals.h>
     34 #include <libxml/pattern.h>
     35 #include <libxml/schematron.h>
     36 
     37 #define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT
     38 
     39 #define SCT_OLD_NS BAD_CAST "http://www.ascc.net/xml/schematron"
     40 
     41 #define XML_SCHEMATRON_NS BAD_CAST "http://purl.oclc.org/dsdl/schematron"
     42 
     43 
     44 static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS;
     45 static const xmlChar *xmlOldSchematronNs = SCT_OLD_NS;
     46 
     47 #define IS_SCHEMATRON(node, elem)					\
     48    ((node != NULL) && (node->type == XML_ELEMENT_NODE ) &&		\
     49     (node->ns != NULL) &&						\
     50     (xmlStrEqual(node->name, (const xmlChar *) elem)) &&		\
     51     ((xmlStrEqual(node->ns->href, xmlSchematronNs)) ||			\
     52      (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))
     53 
     54 #define NEXT_SCHEMATRON(node)						\
     55    while (node != NULL) {						\
     56        if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) && 	\
     57            ((xmlStrEqual(node->ns->href, xmlSchematronNs)) ||		\
     58 	    (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))		\
     59 	   break;							\
     60        node = node->next;						\
     61    }
     62 
     63 /**
     64  * TODO:
     65  *
     66  * macro to flag unimplemented blocks
     67  */
     68 #define TODO 								\
     69     xmlGenericError(xmlGenericErrorContext,				\
     70 	    "Unimplemented block at %s:%d\n",				\
     71             __FILE__, __LINE__);
     72 
     73 typedef enum {
     74     XML_SCHEMATRON_ASSERT=1,
     75     XML_SCHEMATRON_REPORT=2
     76 } xmlSchematronTestType;
     77 
     78 /**
     79  * _xmlSchematronTest:
     80  *
     81  * A Schematrons test, either an assert or a report
     82  */
     83 typedef struct _xmlSchematronTest xmlSchematronTest;
     84 typedef xmlSchematronTest *xmlSchematronTestPtr;
     85 struct _xmlSchematronTest {
     86     xmlSchematronTestPtr next;	/* the next test in the list */
     87     xmlSchematronTestType type;	/* the test type */
     88     xmlNodePtr node;		/* the node in the tree */
     89     xmlChar *test;		/* the expression to test */
     90     xmlXPathCompExprPtr comp;	/* the compiled expression */
     91     xmlChar *report;		/* the message to report */
     92 };
     93 
     94 /**
     95  * _xmlSchematronRule:
     96  *
     97  * A Schematrons rule
     98  */
     99 typedef struct _xmlSchematronRule xmlSchematronRule;
    100 typedef xmlSchematronRule *xmlSchematronRulePtr;
    101 struct _xmlSchematronRule {
    102     xmlSchematronRulePtr next;	/* the next rule in the list */
    103     xmlSchematronRulePtr patnext;/* the next rule in the pattern list */
    104     xmlNodePtr node;		/* the node in the tree */
    105     xmlChar *context;		/* the context evaluation rule */
    106     xmlSchematronTestPtr tests;	/* the list of tests */
    107     xmlPatternPtr pattern;	/* the compiled pattern associated */
    108     xmlChar *report;		/* the message to report */
    109 };
    110 
    111 /**
    112  * _xmlSchematronPattern:
    113  *
    114  * A Schematrons pattern
    115  */
    116 typedef struct _xmlSchematronPattern xmlSchematronPattern;
    117 typedef xmlSchematronPattern *xmlSchematronPatternPtr;
    118 struct _xmlSchematronPattern {
    119     xmlSchematronPatternPtr next;/* the next pattern in the list */
    120     xmlSchematronRulePtr rules;	/* the list of rules */
    121     xmlChar *name;		/* the name of the pattern */
    122 };
    123 
    124 /**
    125  * _xmlSchematron:
    126  *
    127  * A Schematrons definition
    128  */
    129 struct _xmlSchematron {
    130     const xmlChar *name;	/* schema name */
    131     int preserve;		/* was the document passed by the user */
    132     xmlDocPtr doc;		/* pointer to the parsed document */
    133     int flags;			/* specific to this schematron */
    134 
    135     void *_private;		/* unused by the library */
    136     xmlDictPtr dict;		/* the dictionnary used internally */
    137 
    138     const xmlChar *title;	/* the title if any */
    139 
    140     int nbNs;			/* the number of namespaces */
    141 
    142     int nbPattern;		/* the number of patterns */
    143     xmlSchematronPatternPtr patterns;/* the patterns found */
    144     xmlSchematronRulePtr rules;	/* the rules gathered */
    145     int nbNamespaces;		/* number of namespaces in the array */
    146     int maxNamespaces;		/* size of the array */
    147     const xmlChar **namespaces;	/* the array of namespaces */
    148 };
    149 
    150 /**
    151  * xmlSchematronValidCtxt:
    152  *
    153  * A Schematrons validation context
    154  */
    155 struct _xmlSchematronValidCtxt {
    156     int type;
    157     int flags;			/* an or of xmlSchematronValidOptions */
    158 
    159     xmlDictPtr dict;
    160     int nberrors;
    161     int err;
    162 
    163     xmlSchematronPtr schema;
    164     xmlXPathContextPtr xctxt;
    165 
    166     FILE *outputFile;		/* if using XML_SCHEMATRON_OUT_FILE */
    167     xmlBufferPtr outputBuffer;	/* if using XML_SCHEMATRON_OUT_BUFFER */
    168     xmlOutputWriteCallback iowrite; /* if using XML_SCHEMATRON_OUT_IO */
    169     xmlOutputCloseCallback  ioclose;
    170     void *ioctx;
    171 
    172     /* error reporting data */
    173     void *userData;                      /* user specific data block */
    174     xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
    175     xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
    176     xmlStructuredErrorFunc serror;       /* the structured function */
    177 };
    178 
    179 struct _xmlSchematronParserCtxt {
    180     int type;
    181     const xmlChar *URL;
    182     xmlDocPtr doc;
    183     int preserve;               /* Whether the doc should be freed  */
    184     const char *buffer;
    185     int size;
    186 
    187     xmlDictPtr dict;            /* dictionnary for interned string names */
    188 
    189     int nberrors;
    190     int err;
    191     xmlXPathContextPtr xctxt;	/* the XPath context used for compilation */
    192     xmlSchematronPtr schema;
    193 
    194     int nbNamespaces;		/* number of namespaces in the array */
    195     int maxNamespaces;		/* size of the array */
    196     const xmlChar **namespaces;	/* the array of namespaces */
    197 
    198     int nbIncludes;		/* number of includes in the array */
    199     int maxIncludes;		/* size of the array */
    200     xmlNodePtr *includes;	/* the array of includes */
    201 
    202     /* error reporting data */
    203     void *userData;                      /* user specific data block */
    204     xmlSchematronValidityErrorFunc error;/* the callback in case of errors */
    205     xmlSchematronValidityWarningFunc warning;/* callback in case of warning */
    206     xmlStructuredErrorFunc serror;       /* the structured function */
    207 };
    208 
    209 #define XML_STRON_CTXT_PARSER 1
    210 #define XML_STRON_CTXT_VALIDATOR 2
    211 
    212 /************************************************************************
    213  *									*
    214  *			Error reporting					*
    215  *									*
    216  ************************************************************************/
    217 
    218 /**
    219  * xmlSchematronPErrMemory:
    220  * @node: a context node
    221  * @extra:  extra informations
    222  *
    223  * Handle an out of memory condition
    224  */
    225 static void
    226 xmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt,
    227                         const char *extra, xmlNodePtr node)
    228 {
    229     if (ctxt != NULL)
    230         ctxt->nberrors++;
    231     __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL,
    232                      extra);
    233 }
    234 
    235 /**
    236  * xmlSchematronPErr:
    237  * @ctxt: the parsing context
    238  * @node: the context node
    239  * @error: the error code
    240  * @msg: the error message
    241  * @str1: extra data
    242  * @str2: extra data
    243  *
    244  * Handle a parser error
    245  */
    246 static void
    247 xmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,
    248               const char *msg, const xmlChar * str1, const xmlChar * str2)
    249 {
    250     xmlGenericErrorFunc channel = NULL;
    251     xmlStructuredErrorFunc schannel = NULL;
    252     void *data = NULL;
    253 
    254     if (ctxt != NULL) {
    255         ctxt->nberrors++;
    256         channel = ctxt->error;
    257         data = ctxt->userData;
    258 	schannel = ctxt->serror;
    259     }
    260     __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP,
    261                     error, XML_ERR_ERROR, NULL, 0,
    262                     (const char *) str1, (const char *) str2, NULL, 0, 0,
    263                     msg, str1, str2);
    264 }
    265 
    266 /**
    267  * xmlSchematronVTypeErrMemory:
    268  * @node: a context node
    269  * @extra:  extra informations
    270  *
    271  * Handle an out of memory condition
    272  */
    273 static void
    274 xmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt,
    275                         const char *extra, xmlNodePtr node)
    276 {
    277     if (ctxt != NULL) {
    278         ctxt->nberrors++;
    279         ctxt->err = XML_SCHEMAV_INTERNAL;
    280     }
    281     __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL,
    282                      extra);
    283 }
    284 
    285 /************************************************************************
    286  *									*
    287  *		Parsing and compilation of the Schematrontrons		*
    288  *									*
    289  ************************************************************************/
    290 
    291 /**
    292  * xmlSchematronAddTest:
    293  * @ctxt: the schema parsing context
    294  * @type:  the type of test
    295  * @rule:  the parent rule
    296  * @node:  the node hosting the test
    297  * @test: the associated test
    298  * @report: the associated report string
    299  *
    300  * Add a test to a schematron
    301  *
    302  * Returns the new pointer or NULL in case of error
    303  */
    304 static xmlSchematronTestPtr
    305 xmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,
    306                      xmlSchematronTestType type,
    307                      xmlSchematronRulePtr rule,
    308                      xmlNodePtr node, xmlChar *test, xmlChar *report)
    309 {
    310     xmlSchematronTestPtr ret;
    311     xmlXPathCompExprPtr comp;
    312 
    313     if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||
    314         (test == NULL))
    315         return(NULL);
    316 
    317     /*
    318      * try first to compile the test expression
    319      */
    320     comp = xmlXPathCtxtCompile(ctxt->xctxt, test);
    321     if (comp == NULL) {
    322 	xmlSchematronPErr(ctxt, node,
    323 	    XML_SCHEMAP_NOROOT,
    324 	    "Failed to compile test expression %s",
    325 	    test, NULL);
    326 	return(NULL);
    327     }
    328 
    329     ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));
    330     if (ret == NULL) {
    331         xmlSchematronPErrMemory(ctxt, "allocating schema test", node);
    332         return (NULL);
    333     }
    334     memset(ret, 0, sizeof(xmlSchematronTest));
    335     ret->type = type;
    336     ret->node = node;
    337     ret->test = test;
    338     ret->comp = comp;
    339     ret->report = report;
    340     ret->next = NULL;
    341     if (rule->tests == NULL) {
    342 	rule->tests = ret;
    343     } else {
    344         xmlSchematronTestPtr prev = rule->tests;
    345 
    346 	while (prev->next != NULL)
    347 	     prev = prev->next;
    348         prev->next = ret;
    349     }
    350     return (ret);
    351 }
    352 
    353 /**
    354  * xmlSchematronFreeTests:
    355  * @tests:  a list of tests
    356  *
    357  * Free a list of tests.
    358  */
    359 static void
    360 xmlSchematronFreeTests(xmlSchematronTestPtr tests) {
    361     xmlSchematronTestPtr next;
    362 
    363     while (tests != NULL) {
    364         next = tests->next;
    365 	if (tests->test != NULL)
    366 	    xmlFree(tests->test);
    367 	if (tests->comp != NULL)
    368 	    xmlXPathFreeCompExpr(tests->comp);
    369 	if (tests->report != NULL)
    370 	    xmlFree(tests->report);
    371 	xmlFree(tests);
    372 	tests = next;
    373     }
    374 }
    375 
    376 /**
    377  * xmlSchematronAddRule:
    378  * @ctxt: the schema parsing context
    379  * @schema:  a schema structure
    380  * @node:  the node hosting the rule
    381  * @context: the associated context string
    382  * @report: the associated report string
    383  *
    384  * Add a rule to a schematron
    385  *
    386  * Returns the new pointer or NULL in case of error
    387  */
    388 static xmlSchematronRulePtr
    389 xmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,
    390                      xmlSchematronPatternPtr pat, xmlNodePtr node,
    391 		     xmlChar *context, xmlChar *report)
    392 {
    393     xmlSchematronRulePtr ret;
    394     xmlPatternPtr pattern;
    395 
    396     if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||
    397         (context == NULL))
    398         return(NULL);
    399 
    400     /*
    401      * Try first to compile the pattern
    402      */
    403     pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,
    404                                 ctxt->namespaces);
    405     if (pattern == NULL) {
    406 	xmlSchematronPErr(ctxt, node,
    407 	    XML_SCHEMAP_NOROOT,
    408 	    "Failed to compile context expression %s",
    409 	    context, NULL);
    410     }
    411 
    412     ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));
    413     if (ret == NULL) {
    414         xmlSchematronPErrMemory(ctxt, "allocating schema rule", node);
    415         return (NULL);
    416     }
    417     memset(ret, 0, sizeof(xmlSchematronRule));
    418     ret->node = node;
    419     ret->context = context;
    420     ret->pattern = pattern;
    421     ret->report = report;
    422     ret->next = NULL;
    423     if (schema->rules == NULL) {
    424 	schema->rules = ret;
    425     } else {
    426         xmlSchematronRulePtr prev = schema->rules;
    427 
    428 	while (prev->next != NULL)
    429 	     prev = prev->next;
    430         prev->next = ret;
    431     }
    432     ret->patnext = NULL;
    433     if (pat->rules == NULL) {
    434 	pat->rules = ret;
    435     } else {
    436         xmlSchematronRulePtr prev = pat->rules;
    437 
    438 	while (prev->patnext != NULL)
    439 	     prev = prev->patnext;
    440         prev->patnext = ret;
    441     }
    442     return (ret);
    443 }
    444 
    445 /**
    446  * xmlSchematronFreeRules:
    447  * @rules:  a list of rules
    448  *
    449  * Free a list of rules.
    450  */
    451 static void
    452 xmlSchematronFreeRules(xmlSchematronRulePtr rules) {
    453     xmlSchematronRulePtr next;
    454 
    455     while (rules != NULL) {
    456         next = rules->next;
    457 	if (rules->tests)
    458 	    xmlSchematronFreeTests(rules->tests);
    459 	if (rules->context != NULL)
    460 	    xmlFree(rules->context);
    461 	if (rules->pattern)
    462 	    xmlFreePattern(rules->pattern);
    463 	if (rules->report != NULL)
    464 	    xmlFree(rules->report);
    465 	xmlFree(rules);
    466 	rules = next;
    467     }
    468 }
    469 
    470 /**
    471  * xmlSchematronAddPattern:
    472  * @ctxt: the schema parsing context
    473  * @schema:  a schema structure
    474  * @node:  the node hosting the pattern
    475  * @id: the id or name of the pattern
    476  *
    477  * Add a pattern to a schematron
    478  *
    479  * Returns the new pointer or NULL in case of error
    480  */
    481 static xmlSchematronPatternPtr
    482 xmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,
    483                      xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name)
    484 {
    485     xmlSchematronPatternPtr ret;
    486 
    487     if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL))
    488         return(NULL);
    489 
    490     ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern));
    491     if (ret == NULL) {
    492         xmlSchematronPErrMemory(ctxt, "allocating schema pattern", node);
    493         return (NULL);
    494     }
    495     memset(ret, 0, sizeof(xmlSchematronPattern));
    496     ret->name = name;
    497     ret->next = NULL;
    498     if (schema->patterns == NULL) {
    499 	schema->patterns = ret;
    500     } else {
    501         xmlSchematronPatternPtr prev = schema->patterns;
    502 
    503 	while (prev->next != NULL)
    504 	     prev = prev->next;
    505         prev->next = ret;
    506     }
    507     return (ret);
    508 }
    509 
    510 /**
    511  * xmlSchematronFreePatterns:
    512  * @patterns:  a list of patterns
    513  *
    514  * Free a list of patterns.
    515  */
    516 static void
    517 xmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {
    518     xmlSchematronPatternPtr next;
    519 
    520     while (patterns != NULL) {
    521         next = patterns->next;
    522 	if (patterns->name != NULL)
    523 	    xmlFree(patterns->name);
    524 	xmlFree(patterns);
    525 	patterns = next;
    526     }
    527 }
    528 
    529 /**
    530  * xmlSchematronNewSchematron:
    531  * @ctxt:  a schema validation context
    532  *
    533  * Allocate a new Schematron structure.
    534  *
    535  * Returns the newly allocated structure or NULL in case or error
    536  */
    537 static xmlSchematronPtr
    538 xmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt)
    539 {
    540     xmlSchematronPtr ret;
    541 
    542     ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));
    543     if (ret == NULL) {
    544         xmlSchematronPErrMemory(ctxt, "allocating schema", NULL);
    545         return (NULL);
    546     }
    547     memset(ret, 0, sizeof(xmlSchematron));
    548     ret->dict = ctxt->dict;
    549     xmlDictReference(ret->dict);
    550 
    551     return (ret);
    552 }
    553 
    554 /**
    555  * xmlSchematronFree:
    556  * @schema:  a schema structure
    557  *
    558  * Deallocate a Schematron structure.
    559  */
    560 void
    561 xmlSchematronFree(xmlSchematronPtr schema)
    562 {
    563     if (schema == NULL)
    564         return;
    565 
    566     if ((schema->doc != NULL) && (!(schema->preserve)))
    567         xmlFreeDoc(schema->doc);
    568 
    569     if (schema->namespaces != NULL)
    570         xmlFree((char **) schema->namespaces);
    571 
    572     xmlSchematronFreeRules(schema->rules);
    573     xmlSchematronFreePatterns(schema->patterns);
    574     xmlDictFree(schema->dict);
    575     xmlFree(schema);
    576 }
    577 
    578 /**
    579  * xmlSchematronNewParserCtxt:
    580  * @URL:  the location of the schema
    581  *
    582  * Create an XML Schematrons parse context for that file/resource expected
    583  * to contain an XML Schematrons file.
    584  *
    585  * Returns the parser context or NULL in case of error
    586  */
    587 xmlSchematronParserCtxtPtr
    588 xmlSchematronNewParserCtxt(const char *URL)
    589 {
    590     xmlSchematronParserCtxtPtr ret;
    591 
    592     if (URL == NULL)
    593         return (NULL);
    594 
    595     ret =
    596         (xmlSchematronParserCtxtPtr)
    597         xmlMalloc(sizeof(xmlSchematronParserCtxt));
    598     if (ret == NULL) {
    599         xmlSchematronPErrMemory(NULL, "allocating schema parser context",
    600                                 NULL);
    601         return (NULL);
    602     }
    603     memset(ret, 0, sizeof(xmlSchematronParserCtxt));
    604     ret->type = XML_STRON_CTXT_PARSER;
    605     ret->dict = xmlDictCreate();
    606     ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1);
    607     ret->includes = NULL;
    608     ret->xctxt = xmlXPathNewContext(NULL);
    609     if (ret->xctxt == NULL) {
    610         xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
    611                                 NULL);
    612 	xmlSchematronFreeParserCtxt(ret);
    613         return (NULL);
    614     }
    615     ret->xctxt->flags = XML_XPATH_CHECKNS;
    616     return (ret);
    617 }
    618 
    619 /**
    620  * xmlSchematronNewMemParserCtxt:
    621  * @buffer:  a pointer to a char array containing the schemas
    622  * @size:  the size of the array
    623  *
    624  * Create an XML Schematrons parse context for that memory buffer expected
    625  * to contain an XML Schematrons file.
    626  *
    627  * Returns the parser context or NULL in case of error
    628  */
    629 xmlSchematronParserCtxtPtr
    630 xmlSchematronNewMemParserCtxt(const char *buffer, int size)
    631 {
    632     xmlSchematronParserCtxtPtr ret;
    633 
    634     if ((buffer == NULL) || (size <= 0))
    635         return (NULL);
    636 
    637     ret =
    638         (xmlSchematronParserCtxtPtr)
    639         xmlMalloc(sizeof(xmlSchematronParserCtxt));
    640     if (ret == NULL) {
    641         xmlSchematronPErrMemory(NULL, "allocating schema parser context",
    642                                 NULL);
    643         return (NULL);
    644     }
    645     memset(ret, 0, sizeof(xmlSchematronParserCtxt));
    646     ret->buffer = buffer;
    647     ret->size = size;
    648     ret->dict = xmlDictCreate();
    649     ret->xctxt = xmlXPathNewContext(NULL);
    650     if (ret->xctxt == NULL) {
    651         xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
    652                                 NULL);
    653 	xmlSchematronFreeParserCtxt(ret);
    654         return (NULL);
    655     }
    656     return (ret);
    657 }
    658 
    659 /**
    660  * xmlSchematronNewDocParserCtxt:
    661  * @doc:  a preparsed document tree
    662  *
    663  * Create an XML Schematrons parse context for that document.
    664  * NB. The document may be modified during the parsing process.
    665  *
    666  * Returns the parser context or NULL in case of error
    667  */
    668 xmlSchematronParserCtxtPtr
    669 xmlSchematronNewDocParserCtxt(xmlDocPtr doc)
    670 {
    671     xmlSchematronParserCtxtPtr ret;
    672 
    673     if (doc == NULL)
    674         return (NULL);
    675 
    676     ret =
    677         (xmlSchematronParserCtxtPtr)
    678         xmlMalloc(sizeof(xmlSchematronParserCtxt));
    679     if (ret == NULL) {
    680         xmlSchematronPErrMemory(NULL, "allocating schema parser context",
    681                                 NULL);
    682         return (NULL);
    683     }
    684     memset(ret, 0, sizeof(xmlSchematronParserCtxt));
    685     ret->doc = doc;
    686     ret->dict = xmlDictCreate();
    687     /* The application has responsibility for the document */
    688     ret->preserve = 1;
    689     ret->xctxt = xmlXPathNewContext(doc);
    690     if (ret->xctxt == NULL) {
    691         xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
    692                                 NULL);
    693 	xmlSchematronFreeParserCtxt(ret);
    694         return (NULL);
    695     }
    696 
    697     return (ret);
    698 }
    699 
    700 /**
    701  * xmlSchematronFreeParserCtxt:
    702  * @ctxt:  the schema parser context
    703  *
    704  * Free the resources associated to the schema parser context
    705  */
    706 void
    707 xmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt)
    708 {
    709     if (ctxt == NULL)
    710         return;
    711     if (ctxt->doc != NULL && !ctxt->preserve)
    712         xmlFreeDoc(ctxt->doc);
    713     if (ctxt->xctxt != NULL) {
    714         xmlXPathFreeContext(ctxt->xctxt);
    715     }
    716     if (ctxt->namespaces != NULL)
    717         xmlFree((char **) ctxt->namespaces);
    718     xmlDictFree(ctxt->dict);
    719     xmlFree(ctxt);
    720 }
    721 
    722 #if 0
    723 /**
    724  * xmlSchematronPushInclude:
    725  * @ctxt:  the schema parser context
    726  * @doc:  the included document
    727  * @cur:  the current include node
    728  *
    729  * Add an included document
    730  */
    731 static void
    732 xmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,
    733                         xmlDocPtr doc, xmlNodePtr cur)
    734 {
    735     if (ctxt->includes == NULL) {
    736         ctxt->maxIncludes = 10;
    737         ctxt->includes = (xmlNodePtr *)
    738 	    xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr));
    739 	if (ctxt->includes == NULL) {
    740 	    xmlSchematronPErrMemory(NULL, "allocating parser includes",
    741 				    NULL);
    742 	    return;
    743 	}
    744         ctxt->nbIncludes = 0;
    745     } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {
    746         xmlNodePtr *tmp;
    747 
    748 	tmp = (xmlNodePtr *)
    749 	    xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *
    750 	               sizeof(xmlNodePtr));
    751 	if (tmp == NULL) {
    752 	    xmlSchematronPErrMemory(NULL, "allocating parser includes",
    753 				    NULL);
    754 	    return;
    755 	}
    756         ctxt->includes = tmp;
    757 	ctxt->maxIncludes *= 2;
    758     }
    759     ctxt->includes[2 * ctxt->nbIncludes] = cur;
    760     ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;
    761     ctxt->nbIncludes++;
    762 }
    763 
    764 /**
    765  * xmlSchematronPopInclude:
    766  * @ctxt:  the schema parser context
    767  *
    768  * Pop an include level. The included document is being freed
    769  *
    770  * Returns the node immediately following the include or NULL if the
    771  *         include list was empty.
    772  */
    773 static xmlNodePtr
    774 xmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt)
    775 {
    776     xmlDocPtr doc;
    777     xmlNodePtr ret;
    778 
    779     if (ctxt->nbIncludes <= 0)
    780         return(NULL);
    781     ctxt->nbIncludes--;
    782     doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];
    783     ret = ctxt->includes[2 * ctxt->nbIncludes];
    784     xmlFreeDoc(doc);
    785     if (ret != NULL)
    786 	ret = ret->next;
    787     if (ret == NULL)
    788         return(xmlSchematronPopInclude(ctxt));
    789     return(ret);
    790 }
    791 #endif
    792 
    793 /**
    794  * xmlSchematronAddNamespace:
    795  * @ctxt:  the schema parser context
    796  * @prefix:  the namespace prefix
    797  * @ns:  the namespace name
    798  *
    799  * Add a namespace definition in the context
    800  */
    801 static void
    802 xmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,
    803                           const xmlChar *prefix, const xmlChar *ns)
    804 {
    805     if (ctxt->namespaces == NULL) {
    806         ctxt->maxNamespaces = 10;
    807         ctxt->namespaces = (const xmlChar **)
    808 	    xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *));
    809 	if (ctxt->namespaces == NULL) {
    810 	    xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
    811 				    NULL);
    812 	    return;
    813 	}
    814         ctxt->nbNamespaces = 0;
    815     } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {
    816         const xmlChar **tmp;
    817 
    818 	tmp = (const xmlChar **)
    819 	    xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *
    820 	               sizeof(const xmlChar *));
    821 	if (tmp == NULL) {
    822 	    xmlSchematronPErrMemory(NULL, "allocating parser namespaces",
    823 				    NULL);
    824 	    return;
    825 	}
    826         ctxt->namespaces = tmp;
    827 	ctxt->maxNamespaces *= 2;
    828     }
    829     ctxt->namespaces[2 * ctxt->nbNamespaces] =
    830         xmlDictLookup(ctxt->dict, ns, -1);
    831     ctxt->namespaces[2 * ctxt->nbNamespaces + 1] =
    832         xmlDictLookup(ctxt->dict, prefix, -1);
    833     ctxt->nbNamespaces++;
    834     ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL;
    835     ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL;
    836 
    837 }
    838 
    839 /**
    840  * xmlSchematronParseRule:
    841  * @ctxt:  a schema validation context
    842  * @rule:  the rule node
    843  *
    844  * parse a rule element
    845  */
    846 static void
    847 xmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,
    848                        xmlSchematronPatternPtr pattern,
    849 		       xmlNodePtr rule)
    850 {
    851     xmlNodePtr cur;
    852     int nbChecks = 0;
    853     xmlChar *test;
    854     xmlChar *context;
    855     xmlChar *report;
    856     xmlSchematronRulePtr ruleptr;
    857     xmlSchematronTestPtr testptr;
    858 
    859     if ((ctxt == NULL) || (rule == NULL)) return;
    860 
    861     context = xmlGetNoNsProp(rule, BAD_CAST "context");
    862     if (context == NULL) {
    863 	xmlSchematronPErr(ctxt, rule,
    864 	    XML_SCHEMAP_NOROOT,
    865 	    "rule has no context attribute",
    866 	    NULL, NULL);
    867 	return;
    868     } else if (context[0] == 0) {
    869 	xmlSchematronPErr(ctxt, rule,
    870 	    XML_SCHEMAP_NOROOT,
    871 	    "rule has an empty context attribute",
    872 	    NULL, NULL);
    873 	xmlFree(context);
    874 	return;
    875     } else {
    876 	ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,
    877 	                               rule, context, NULL);
    878 	if (ruleptr == NULL) {
    879 	    xmlFree(context);
    880 	    return;
    881 	}
    882     }
    883 
    884     cur = rule->children;
    885     NEXT_SCHEMATRON(cur);
    886     while (cur != NULL) {
    887 	if (IS_SCHEMATRON(cur, "assert")) {
    888 	    nbChecks++;
    889 	    test = xmlGetNoNsProp(cur, BAD_CAST "test");
    890 	    if (test == NULL) {
    891 		xmlSchematronPErr(ctxt, cur,
    892 		    XML_SCHEMAP_NOROOT,
    893 		    "assert has no test attribute",
    894 		    NULL, NULL);
    895 	    } else if (test[0] == 0) {
    896 		xmlSchematronPErr(ctxt, cur,
    897 		    XML_SCHEMAP_NOROOT,
    898 		    "assert has an empty test attribute",
    899 		    NULL, NULL);
    900 		xmlFree(test);
    901 	    } else {
    902 		/* TODO will need dynamic processing instead */
    903 		report = xmlNodeGetContent(cur);
    904 
    905 		testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,
    906 		                               ruleptr, cur, test, report);
    907 		if (testptr == NULL)
    908 		    xmlFree(test);
    909 	    }
    910 	} else if (IS_SCHEMATRON(cur, "report")) {
    911 	    nbChecks++;
    912 	    test = xmlGetNoNsProp(cur, BAD_CAST "test");
    913 	    if (test == NULL) {
    914 		xmlSchematronPErr(ctxt, cur,
    915 		    XML_SCHEMAP_NOROOT,
    916 		    "assert has no test attribute",
    917 		    NULL, NULL);
    918 	    } else if (test[0] == 0) {
    919 		xmlSchematronPErr(ctxt, cur,
    920 		    XML_SCHEMAP_NOROOT,
    921 		    "assert has an empty test attribute",
    922 		    NULL, NULL);
    923 		xmlFree(test);
    924 	    } else {
    925 		/* TODO will need dynamic processing instead */
    926 		report = xmlNodeGetContent(cur);
    927 
    928 		testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,
    929 		                               ruleptr, cur, test, report);
    930 		if (testptr == NULL)
    931 		    xmlFree(test);
    932 	    }
    933 	} else {
    934 	    xmlSchematronPErr(ctxt, cur,
    935 		XML_SCHEMAP_NOROOT,
    936 		"Expecting an assert or a report element instead of %s",
    937 		cur->name, NULL);
    938 	}
    939 	cur = cur->next;
    940 	NEXT_SCHEMATRON(cur);
    941     }
    942     if (nbChecks == 0) {
    943 	xmlSchematronPErr(ctxt, rule,
    944 	    XML_SCHEMAP_NOROOT,
    945 	    "rule has no assert nor report element", NULL, NULL);
    946     }
    947 }
    948 
    949 /**
    950  * xmlSchematronParsePattern:
    951  * @ctxt:  a schema validation context
    952  * @pat:  the pattern node
    953  *
    954  * parse a pattern element
    955  */
    956 static void
    957 xmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat)
    958 {
    959     xmlNodePtr cur;
    960     xmlSchematronPatternPtr pattern;
    961     int nbRules = 0;
    962     xmlChar *id;
    963 
    964     if ((ctxt == NULL) || (pat == NULL)) return;
    965 
    966     id = xmlGetNoNsProp(pat, BAD_CAST "id");
    967     if (id == NULL) {
    968 	id = xmlGetNoNsProp(pat, BAD_CAST "name");
    969     }
    970     pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);
    971     if (pattern == NULL) {
    972 	if (id != NULL)
    973 	    xmlFree(id);
    974         return;
    975     }
    976     cur = pat->children;
    977     NEXT_SCHEMATRON(cur);
    978     while (cur != NULL) {
    979 	if (IS_SCHEMATRON(cur, "rule")) {
    980 	    xmlSchematronParseRule(ctxt, pattern, cur);
    981 	    nbRules++;
    982 	} else {
    983 	    xmlSchematronPErr(ctxt, cur,
    984 		XML_SCHEMAP_NOROOT,
    985 		"Expecting a rule element instead of %s", cur->name, NULL);
    986 	}
    987 	cur = cur->next;
    988 	NEXT_SCHEMATRON(cur);
    989     }
    990     if (nbRules == 0) {
    991 	xmlSchematronPErr(ctxt, pat,
    992 	    XML_SCHEMAP_NOROOT,
    993 	    "Pattern has no rule element", NULL, NULL);
    994     }
    995 }
    996 
    997 #if 0
    998 /**
    999  * xmlSchematronLoadInclude:
   1000  * @ctxt:  a schema validation context
   1001  * @cur:  the include element
   1002  *
   1003  * Load the include document, Push the current pointer
   1004  *
   1005  * Returns the updated node pointer
   1006  */
   1007 static xmlNodePtr
   1008 xmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur)
   1009 {
   1010     xmlNodePtr ret = NULL;
   1011     xmlDocPtr doc = NULL;
   1012     xmlChar *href = NULL;
   1013     xmlChar *base = NULL;
   1014     xmlChar *URI = NULL;
   1015 
   1016     if ((ctxt == NULL) || (cur == NULL))
   1017         return(NULL);
   1018 
   1019     href = xmlGetNoNsProp(cur, BAD_CAST "href");
   1020     if (href == NULL) {
   1021 	xmlSchematronPErr(ctxt, cur,
   1022 	    XML_SCHEMAP_NOROOT,
   1023 	    "Include has no href attribute", NULL, NULL);
   1024 	return(cur->next);
   1025     }
   1026 
   1027     /* do the URI base composition, load and find the root */
   1028     base = xmlNodeGetBase(cur->doc, cur);
   1029     URI = xmlBuildURI(href, base);
   1030     doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);
   1031     if (doc == NULL) {
   1032 	xmlSchematronPErr(ctxt, cur,
   1033 		      XML_SCHEMAP_FAILED_LOAD,
   1034 		      "could not load include '%s'.\n",
   1035 		      URI, NULL);
   1036 	goto done;
   1037     }
   1038     ret = xmlDocGetRootElement(doc);
   1039     if (ret == NULL) {
   1040 	xmlSchematronPErr(ctxt, cur,
   1041 		      XML_SCHEMAP_FAILED_LOAD,
   1042 		      "could not find root from include '%s'.\n",
   1043 		      URI, NULL);
   1044 	goto done;
   1045     }
   1046 
   1047     /* Success, push the include for rollback on exit */
   1048     xmlSchematronPushInclude(ctxt, doc, cur);
   1049 
   1050 done:
   1051     if (ret == NULL) {
   1052         if (doc != NULL)
   1053 	    xmlFreeDoc(doc);
   1054     }
   1055     xmlFree(href);
   1056     if (base != NULL)
   1057         xmlFree(base);
   1058     if (URI != NULL)
   1059         xmlFree(URI);
   1060     return(ret);
   1061 }
   1062 #endif
   1063 
   1064 /**
   1065  * xmlSchematronParse:
   1066  * @ctxt:  a schema validation context
   1067  *
   1068  * parse a schema definition resource and build an internal
   1069  * XML Shema struture which can be used to validate instances.
   1070  *
   1071  * Returns the internal XML Schematron structure built from the resource or
   1072  *         NULL in case of error
   1073  */
   1074 xmlSchematronPtr
   1075 xmlSchematronParse(xmlSchematronParserCtxtPtr ctxt)
   1076 {
   1077     xmlSchematronPtr ret = NULL;
   1078     xmlDocPtr doc;
   1079     xmlNodePtr root, cur;
   1080     int preserve = 0;
   1081 
   1082     if (ctxt == NULL)
   1083         return (NULL);
   1084 
   1085     ctxt->nberrors = 0;
   1086 
   1087     /*
   1088      * First step is to parse the input document into an DOM/Infoset
   1089      */
   1090     if (ctxt->URL != NULL) {
   1091         doc = xmlReadFile((const char *) ctxt->URL, NULL,
   1092 	                  SCHEMATRON_PARSE_OPTIONS);
   1093         if (doc == NULL) {
   1094 	    xmlSchematronPErr(ctxt, NULL,
   1095 			  XML_SCHEMAP_FAILED_LOAD,
   1096                           "xmlSchematronParse: could not load '%s'.\n",
   1097                           ctxt->URL, NULL);
   1098             return (NULL);
   1099         }
   1100 	ctxt->preserve = 0;
   1101     } else if (ctxt->buffer != NULL) {
   1102         doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,
   1103 	                    SCHEMATRON_PARSE_OPTIONS);
   1104         if (doc == NULL) {
   1105 	    xmlSchematronPErr(ctxt, NULL,
   1106 			  XML_SCHEMAP_FAILED_PARSE,
   1107                           "xmlSchematronParse: could not parse.\n",
   1108                           NULL, NULL);
   1109             return (NULL);
   1110         }
   1111         doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
   1112         ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);
   1113 	ctxt->preserve = 0;
   1114     } else if (ctxt->doc != NULL) {
   1115         doc = ctxt->doc;
   1116 	preserve = 1;
   1117 	ctxt->preserve = 1;
   1118     } else {
   1119 	xmlSchematronPErr(ctxt, NULL,
   1120 		      XML_SCHEMAP_NOTHING_TO_PARSE,
   1121 		      "xmlSchematronParse: could not parse.\n",
   1122 		      NULL, NULL);
   1123         return (NULL);
   1124     }
   1125 
   1126     /*
   1127      * Then extract the root and Schematron parse it
   1128      */
   1129     root = xmlDocGetRootElement(doc);
   1130     if (root == NULL) {
   1131 	xmlSchematronPErr(ctxt, (xmlNodePtr) doc,
   1132 		      XML_SCHEMAP_NOROOT,
   1133 		      "The schema has no document element.\n", NULL, NULL);
   1134 	if (!preserve) {
   1135 	    xmlFreeDoc(doc);
   1136 	}
   1137         return (NULL);
   1138     }
   1139 
   1140     if (!IS_SCHEMATRON(root, "schema")) {
   1141 	xmlSchematronPErr(ctxt, root,
   1142 	    XML_SCHEMAP_NOROOT,
   1143 	    "The XML document '%s' is not a XML schematron document",
   1144 	    ctxt->URL, NULL);
   1145 	goto exit;
   1146     }
   1147     ret = xmlSchematronNewSchematron(ctxt);
   1148     if (ret == NULL)
   1149         goto exit;
   1150     ctxt->schema = ret;
   1151 
   1152     /*
   1153      * scan the schema elements
   1154      */
   1155     cur = root->children;
   1156     NEXT_SCHEMATRON(cur);
   1157     if (IS_SCHEMATRON(cur, "title")) {
   1158         xmlChar *title = xmlNodeGetContent(cur);
   1159 	if (title != NULL) {
   1160 	    ret->title = xmlDictLookup(ret->dict, title, -1);
   1161 	    xmlFree(title);
   1162 	}
   1163 	cur = cur->next;
   1164 	NEXT_SCHEMATRON(cur);
   1165     }
   1166     while (IS_SCHEMATRON(cur, "ns")) {
   1167         xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");
   1168         xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");
   1169 	if ((uri == NULL) || (uri[0] == 0)) {
   1170 	    xmlSchematronPErr(ctxt, cur,
   1171 		XML_SCHEMAP_NOROOT,
   1172 		"ns element has no uri", NULL, NULL);
   1173 	}
   1174 	if ((prefix == NULL) || (prefix[0] == 0)) {
   1175 	    xmlSchematronPErr(ctxt, cur,
   1176 		XML_SCHEMAP_NOROOT,
   1177 		"ns element has no prefix", NULL, NULL);
   1178 	}
   1179 	if ((prefix) && (uri)) {
   1180 	    xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);
   1181 	    xmlSchematronAddNamespace(ctxt, prefix, uri);
   1182 	    ret->nbNs++;
   1183 	}
   1184 	if (uri)
   1185 	    xmlFree(uri);
   1186 	if (prefix)
   1187 	    xmlFree(prefix);
   1188 	cur = cur->next;
   1189 	NEXT_SCHEMATRON(cur);
   1190     }
   1191     while (cur != NULL) {
   1192 	if (IS_SCHEMATRON(cur, "pattern")) {
   1193 	    xmlSchematronParsePattern(ctxt, cur);
   1194 	    ret->nbPattern++;
   1195 	} else {
   1196 	    xmlSchematronPErr(ctxt, cur,
   1197 		XML_SCHEMAP_NOROOT,
   1198 		"Expecting a pattern element instead of %s", cur->name, NULL);
   1199 	}
   1200 	cur = cur->next;
   1201 	NEXT_SCHEMATRON(cur);
   1202     }
   1203     if (ret->nbPattern == 0) {
   1204 	xmlSchematronPErr(ctxt, root,
   1205 	    XML_SCHEMAP_NOROOT,
   1206 	    "The schematron document '%s' has no pattern",
   1207 	    ctxt->URL, NULL);
   1208 	goto exit;
   1209     }
   1210     /* the original document must be kept for reporting */
   1211     ret->doc = doc;
   1212     if (preserve) {
   1213 	    ret->preserve = 1;
   1214     }
   1215     preserve = 1;
   1216 
   1217 exit:
   1218     if (!preserve) {
   1219 	xmlFreeDoc(doc);
   1220     }
   1221     if (ret != NULL) {
   1222 	if (ctxt->nberrors != 0) {
   1223 	    xmlSchematronFree(ret);
   1224 	    ret = NULL;
   1225 	} else {
   1226 	    ret->namespaces = ctxt->namespaces;
   1227 	    ret->nbNamespaces = ctxt->nbNamespaces;
   1228 	    ctxt->namespaces = NULL;
   1229 	}
   1230     }
   1231     return (ret);
   1232 }
   1233 
   1234 /************************************************************************
   1235  *									*
   1236  *		Schematrontron Reports handler				*
   1237  *									*
   1238  ************************************************************************/
   1239 
   1240 static xmlNodePtr
   1241 xmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,
   1242                      xmlNodePtr cur, const xmlChar *xpath) {
   1243     xmlNodePtr node = NULL;
   1244     xmlXPathObjectPtr ret;
   1245 
   1246     if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))
   1247         return(NULL);
   1248 
   1249     ctxt->xctxt->doc = cur->doc;
   1250     ctxt->xctxt->node = cur;
   1251     ret = xmlXPathEval(xpath, ctxt->xctxt);
   1252     if (ret == NULL)
   1253         return(NULL);
   1254 
   1255     if ((ret->type == XPATH_NODESET) &&
   1256         (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))
   1257 	node = ret->nodesetval->nodeTab[0];
   1258 
   1259     xmlXPathFreeObject(ret);
   1260     return(node);
   1261 }
   1262 
   1263 /**
   1264  * xmlSchematronReportOutput:
   1265  * @ctxt: the validation context
   1266  * @cur: the current node tested
   1267  * @msg: the message output
   1268  *
   1269  * Output part of the report to whatever channel the user selected
   1270  */
   1271 static void
   1272 xmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
   1273                           xmlNodePtr cur ATTRIBUTE_UNUSED,
   1274                           const char *msg) {
   1275     /* TODO */
   1276     fprintf(stderr, "%s", msg);
   1277 }
   1278 
   1279 /**
   1280  * xmlSchematronFormatReport:
   1281  * @ctxt:  the validation context
   1282  * @test: the test node
   1283  * @cur: the current node tested
   1284  *
   1285  * Build the string being reported to the user.
   1286  *
   1287  * Returns a report string or NULL in case of error. The string needs
   1288  *         to be deallocated by teh caller
   1289  */
   1290 static xmlChar *
   1291 xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt,
   1292 			  xmlNodePtr test, xmlNodePtr cur) {
   1293     xmlChar *ret = NULL;
   1294     xmlNodePtr child, node;
   1295 
   1296     if ((test == NULL) || (cur == NULL))
   1297         return(ret);
   1298 
   1299     child = test->children;
   1300     while (child != NULL) {
   1301         if ((child->type == XML_TEXT_NODE) ||
   1302 	    (child->type == XML_CDATA_SECTION_NODE))
   1303 	    ret = xmlStrcat(ret, child->content);
   1304 	else if (IS_SCHEMATRON(child, "name")) {
   1305 	    xmlChar *path;
   1306 
   1307 	    path = xmlGetNoNsProp(child, BAD_CAST "path");
   1308 
   1309             node = cur;
   1310 	    if (path != NULL) {
   1311 	        node = xmlSchematronGetNode(ctxt, cur, path);
   1312 		if (node == NULL)
   1313 		    node = cur;
   1314 		xmlFree(path);
   1315 	    }
   1316 
   1317 	    if ((node->ns == NULL) || (node->ns->prefix == NULL))
   1318 	        ret = xmlStrcat(ret, node->name);
   1319 	    else {
   1320 	        ret = xmlStrcat(ret, node->ns->prefix);
   1321 	        ret = xmlStrcat(ret, BAD_CAST ":");
   1322 	        ret = xmlStrcat(ret, node->name);
   1323 	    }
   1324 	} else {
   1325 	    child = child->next;
   1326 	    continue;
   1327 	}
   1328 
   1329 	/*
   1330 	 * remove superfluous \n
   1331 	 */
   1332 	if (ret != NULL) {
   1333 	    int len = xmlStrlen(ret);
   1334 	    xmlChar c;
   1335 
   1336 	    if (len > 0) {
   1337 		c = ret[len - 1];
   1338 		if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {
   1339 		    while ((c == ' ') || (c == '\n') ||
   1340 		           (c == '\r') || (c == '\t')) {
   1341 			len--;
   1342 			if (len == 0)
   1343 			    break;
   1344 			c = ret[len - 1];
   1345 		    }
   1346 		    ret[len] = ' ';
   1347 		    ret[len + 1] = 0;
   1348 		}
   1349 	    }
   1350 	}
   1351 
   1352         child = child->next;
   1353     }
   1354     return(ret);
   1355 }
   1356 
   1357 /**
   1358  * xmlSchematronReportSuccess:
   1359  * @ctxt:  the validation context
   1360  * @test: the compiled test
   1361  * @cur: the current node tested
   1362  * @success: boolean value for the result
   1363  *
   1364  * called from the validation engine when an assert or report test have
   1365  * been done.
   1366  */
   1367 static void
   1368 xmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt,
   1369 		   xmlSchematronTestPtr test, xmlNodePtr cur, xmlSchematronPatternPtr pattern, int success) {
   1370     if ((ctxt == NULL) || (cur == NULL) || (test == NULL))
   1371         return;
   1372     /* if quiet and not SVRL report only failures */
   1373     if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&
   1374         ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&
   1375 	(test->type == XML_SCHEMATRON_REPORT))
   1376         return;
   1377     if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
   1378         TODO
   1379     } else {
   1380         xmlChar *path;
   1381 	char msg[1000];
   1382 	long line;
   1383 	const xmlChar *report = NULL;
   1384 
   1385         if (((test->type == XML_SCHEMATRON_REPORT) & (!success)) ||
   1386 	    ((test->type == XML_SCHEMATRON_ASSERT) & (success)))
   1387 	    return;
   1388 	line = xmlGetLineNo(cur);
   1389 	path = xmlGetNodePath(cur);
   1390 	if (path == NULL)
   1391 	    path = (xmlChar *) cur->name;
   1392 #if 0
   1393 	if ((test->report != NULL) && (test->report[0] != 0))
   1394 	    report = test->report;
   1395 #endif
   1396 	if (test->node != NULL)
   1397             report = xmlSchematronFormatReport(ctxt, test->node, cur);
   1398 	if (report == NULL) {
   1399 	    if (test->type == XML_SCHEMATRON_ASSERT) {
   1400             report = xmlStrdup((const xmlChar *) "node failed assert");
   1401 	    } else {
   1402             report = xmlStrdup((const xmlChar *) "node failed report");
   1403 	    }
   1404 	    }
   1405 	    snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,
   1406 		     line, (const char *) report);
   1407 
   1408     if (ctxt->flags & XML_SCHEMATRON_OUT_ERROR) {
   1409         xmlStructuredErrorFunc schannel = NULL;
   1410         xmlGenericErrorFunc channel = NULL;
   1411         void *data = NULL;
   1412 
   1413         if (ctxt != NULL) {
   1414             if (ctxt->serror != NULL)
   1415                 schannel = ctxt->serror;
   1416             else
   1417                 channel = ctxt->error;
   1418             data = ctxt->userData;
   1419 	}
   1420 
   1421         __xmlRaiseError(schannel, channel, data,
   1422                         NULL, cur, XML_FROM_SCHEMATRONV,
   1423                         (test->type == XML_SCHEMATRON_ASSERT)?XML_SCHEMATRONV_ASSERT:XML_SCHEMATRONV_REPORT,
   1424                         XML_ERR_ERROR, NULL, line,
   1425                         (pattern == NULL)?NULL:((const char *) pattern->name),
   1426                         (const char *) path,
   1427                         (const char *) report, 0, 0,
   1428                         "%s", msg);
   1429     } else {
   1430 	xmlSchematronReportOutput(ctxt, cur, &msg[0]);
   1431     }
   1432 
   1433     xmlFree((char *) report);
   1434 
   1435 	if ((path != NULL) && (path != (xmlChar *) cur->name))
   1436 	    xmlFree(path);
   1437     }
   1438 }
   1439 
   1440 /**
   1441  * xmlSchematronReportPattern:
   1442  * @ctxt:  the validation context
   1443  * @pattern: the current pattern
   1444  *
   1445  * called from the validation engine when starting to check a pattern
   1446  */
   1447 static void
   1448 xmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt,
   1449 			   xmlSchematronPatternPtr pattern) {
   1450     if ((ctxt == NULL) || (pattern == NULL))
   1451         return;
   1452     if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) || (ctxt->flags & XML_SCHEMATRON_OUT_ERROR)) /* Error gives pattern name as part of error */
   1453         return;
   1454     if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {
   1455         TODO
   1456     } else {
   1457 	char msg[1000];
   1458 
   1459 	if (pattern->name == NULL)
   1460 	    return;
   1461 	snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);
   1462 	xmlSchematronReportOutput(ctxt, NULL, &msg[0]);
   1463     }
   1464 }
   1465 
   1466 
   1467 /************************************************************************
   1468  *									*
   1469  *		Validation against a Schematrontron				*
   1470  *									*
   1471  ************************************************************************/
   1472 
   1473 /**
   1474  * xmlSchematronSetValidStructuredErrors:
   1475  * @ctxt:  a Schematron validation context
   1476  * @serror:  the structured error function
   1477  * @ctx: the functions context
   1478  *
   1479  * Set the structured error callback
   1480  */
   1481 void
   1482 xmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,
   1483                                       xmlStructuredErrorFunc serror, void *ctx)
   1484 {
   1485     if (ctxt == NULL)
   1486         return;
   1487     ctxt->serror = serror;
   1488     ctxt->error = NULL;
   1489     ctxt->warning = NULL;
   1490     ctxt->userData = ctx;
   1491 }
   1492 
   1493 /**
   1494  * xmlSchematronNewValidCtxt:
   1495  * @schema:  a precompiled XML Schematrons
   1496  * @options: a set of xmlSchematronValidOptions
   1497  *
   1498  * Create an XML Schematrons validation context based on the given schema.
   1499  *
   1500  * Returns the validation context or NULL in case of error
   1501  */
   1502 xmlSchematronValidCtxtPtr
   1503 xmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options)
   1504 {
   1505     int i;
   1506     xmlSchematronValidCtxtPtr ret;
   1507 
   1508     ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));
   1509     if (ret == NULL) {
   1510         xmlSchematronVErrMemory(NULL, "allocating validation context",
   1511                                 NULL);
   1512         return (NULL);
   1513     }
   1514     memset(ret, 0, sizeof(xmlSchematronValidCtxt));
   1515     ret->type = XML_STRON_CTXT_VALIDATOR;
   1516     ret->schema = schema;
   1517     ret->xctxt = xmlXPathNewContext(NULL);
   1518     ret->flags = options;
   1519     if (ret->xctxt == NULL) {
   1520         xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",
   1521                                 NULL);
   1522 	xmlSchematronFreeValidCtxt(ret);
   1523         return (NULL);
   1524     }
   1525     for (i = 0;i < schema->nbNamespaces;i++) {
   1526         if ((schema->namespaces[2 * i] == NULL) ||
   1527             (schema->namespaces[2 * i + 1] == NULL))
   1528 	    break;
   1529 	xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],
   1530 	                   schema->namespaces[2 * i]);
   1531     }
   1532     return (ret);
   1533 }
   1534 
   1535 /**
   1536  * xmlSchematronFreeValidCtxt:
   1537  * @ctxt:  the schema validation context
   1538  *
   1539  * Free the resources associated to the schema validation context
   1540  */
   1541 void
   1542 xmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt)
   1543 {
   1544     if (ctxt == NULL)
   1545         return;
   1546     if (ctxt->xctxt != NULL)
   1547         xmlXPathFreeContext(ctxt->xctxt);
   1548     if (ctxt->dict != NULL)
   1549         xmlDictFree(ctxt->dict);
   1550     xmlFree(ctxt);
   1551 }
   1552 
   1553 static xmlNodePtr
   1554 xmlSchematronNextNode(xmlNodePtr cur) {
   1555     if (cur->children != NULL) {
   1556 	/*
   1557 	 * Do not descend on entities declarations
   1558 	 */
   1559 	if (cur->children->type != XML_ENTITY_DECL) {
   1560 	    cur = cur->children;
   1561 	    /*
   1562 	     * Skip DTDs
   1563 	     */
   1564 	    if (cur->type != XML_DTD_NODE)
   1565 		return(cur);
   1566 	}
   1567     }
   1568 
   1569     while (cur->next != NULL) {
   1570 	cur = cur->next;
   1571 	if ((cur->type != XML_ENTITY_DECL) &&
   1572 	    (cur->type != XML_DTD_NODE))
   1573 	    return(cur);
   1574     }
   1575 
   1576     do {
   1577 	cur = cur->parent;
   1578 	if (cur == NULL) break;
   1579 	if (cur->type == XML_DOCUMENT_NODE) return(NULL);
   1580 	if (cur->next != NULL) {
   1581 	    cur = cur->next;
   1582 	    return(cur);
   1583 	}
   1584     } while (cur != NULL);
   1585     return(cur);
   1586 }
   1587 
   1588 /**
   1589  * xmlSchematronRunTest:
   1590  * @ctxt:  the schema validation context
   1591  * @test:  the current test
   1592  * @instance:  the document instace tree
   1593  * @cur:  the current node in the instance
   1594  *
   1595  * Validate a rule against a tree instance at a given position
   1596  *
   1597  * Returns 1 in case of success, 0 if error and -1 in case of internal error
   1598  */
   1599 static int
   1600 xmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,
   1601      xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur, xmlSchematronPatternPtr pattern)
   1602 {
   1603     xmlXPathObjectPtr ret;
   1604     int failed;
   1605 
   1606     failed = 0;
   1607     ctxt->xctxt->doc = instance;
   1608     ctxt->xctxt->node = cur;
   1609     ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);
   1610     if (ret == NULL) {
   1611 	failed = 1;
   1612     } else {
   1613         switch (ret->type) {
   1614 	    case XPATH_XSLT_TREE:
   1615 	    case XPATH_NODESET:
   1616 		if ((ret->nodesetval == NULL) ||
   1617 		    (ret->nodesetval->nodeNr == 0))
   1618 		    failed = 1;
   1619 		break;
   1620 	    case XPATH_BOOLEAN:
   1621 		failed = !ret->boolval;
   1622 		break;
   1623 	    case XPATH_NUMBER:
   1624 		if ((xmlXPathIsNaN(ret->floatval)) ||
   1625 		    (ret->floatval == 0.0))
   1626 		    failed = 1;
   1627 		break;
   1628 	    case XPATH_STRING:
   1629 		if ((ret->stringval == NULL) ||
   1630 		    (ret->stringval[0] == 0))
   1631 		    failed = 1;
   1632 		break;
   1633 	    case XPATH_UNDEFINED:
   1634 	    case XPATH_POINT:
   1635 	    case XPATH_RANGE:
   1636 	    case XPATH_LOCATIONSET:
   1637 	    case XPATH_USERS:
   1638 		failed = 1;
   1639 		break;
   1640 	}
   1641 	xmlXPathFreeObject(ret);
   1642     }
   1643     if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))
   1644         ctxt->nberrors++;
   1645     else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))
   1646         ctxt->nberrors++;
   1647 
   1648     xmlSchematronReportSuccess(ctxt, test, cur, pattern, !failed);
   1649 
   1650     return(!failed);
   1651 }
   1652 
   1653 /**
   1654  * xmlSchematronValidateDoc:
   1655  * @ctxt:  the schema validation context
   1656  * @instance:  the document instace tree
   1657  *
   1658  * Validate a tree instance against the schematron
   1659  *
   1660  * Returns 0 in case of success, -1 in case of internal error
   1661  *         and an error count otherwise.
   1662  */
   1663 int
   1664 xmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance)
   1665 {
   1666     xmlNodePtr cur, root;
   1667     xmlSchematronPatternPtr pattern;
   1668     xmlSchematronRulePtr rule;
   1669     xmlSchematronTestPtr test;
   1670 
   1671     if ((ctxt == NULL) || (ctxt->schema == NULL) ||
   1672         (ctxt->schema->rules == NULL) || (instance == NULL))
   1673         return(-1);
   1674     ctxt->nberrors = 0;
   1675     root = xmlDocGetRootElement(instance);
   1676     if (root == NULL) {
   1677         TODO
   1678 	ctxt->nberrors++;
   1679 	return(1);
   1680     }
   1681     if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||
   1682         (ctxt->flags == 0)) {
   1683 	/*
   1684 	 * we are just trying to assert the validity of the document,
   1685 	 * speed primes over the output, run in a single pass
   1686 	 */
   1687 	cur = root;
   1688 	while (cur != NULL) {
   1689 	    rule = ctxt->schema->rules;
   1690 	    while (rule != NULL) {
   1691 		if (xmlPatternMatch(rule->pattern, cur) == 1) {
   1692 		    test = rule->tests;
   1693 		    while (test != NULL) {
   1694 			xmlSchematronRunTest(ctxt, test, instance, cur, (xmlSchematronPatternPtr)rule->pattern);
   1695 			test = test->next;
   1696 		    }
   1697 		}
   1698 		rule = rule->next;
   1699 	    }
   1700 
   1701 	    cur = xmlSchematronNextNode(cur);
   1702 	}
   1703     } else {
   1704         /*
   1705 	 * Process all contexts one at a time
   1706 	 */
   1707 	pattern = ctxt->schema->patterns;
   1708 
   1709 	while (pattern != NULL) {
   1710 	    xmlSchematronReportPattern(ctxt, pattern);
   1711 
   1712 	    /*
   1713 	     * TODO convert the pattern rule to a direct XPath and
   1714 	     * compute directly instead of using the pattern matching
   1715 	     * over the full document...
   1716 	     * Check the exact semantic
   1717 	     */
   1718 	    cur = root;
   1719 	    while (cur != NULL) {
   1720 		rule = pattern->rules;
   1721 		while (rule != NULL) {
   1722 		    if (xmlPatternMatch(rule->pattern, cur) == 1) {
   1723 			test = rule->tests;
   1724 			while (test != NULL) {
   1725 			    xmlSchematronRunTest(ctxt, test, instance, cur, pattern);
   1726 			    test = test->next;
   1727 			}
   1728 		    }
   1729 		    rule = rule->patnext;
   1730 		}
   1731 
   1732 		cur = xmlSchematronNextNode(cur);
   1733 	    }
   1734 	    pattern = pattern->next;
   1735 	}
   1736     }
   1737     return(ctxt->nberrors);
   1738 }
   1739 
   1740 #ifdef STANDALONE
   1741 int
   1742 main(void)
   1743 {
   1744     int ret;
   1745     xmlDocPtr instance;
   1746     xmlSchematronParserCtxtPtr pctxt;
   1747     xmlSchematronValidCtxtPtr vctxt;
   1748     xmlSchematronPtr schema = NULL;
   1749 
   1750     pctxt = xmlSchematronNewParserCtxt("tst.sct");
   1751     if (pctxt == NULL) {
   1752         fprintf(stderr, "failed to build schematron parser\n");
   1753     } else {
   1754         schema = xmlSchematronParse(pctxt);
   1755 	if (schema == NULL) {
   1756 	    fprintf(stderr, "failed to compile schematron\n");
   1757 	}
   1758 	xmlSchematronFreeParserCtxt(pctxt);
   1759     }
   1760     instance = xmlReadFile("tst.sct", NULL,
   1761                            XML_PARSE_NOENT | XML_PARSE_NOCDATA);
   1762     if (instance == NULL) {
   1763 	fprintf(stderr, "failed to parse instance\n");
   1764     }
   1765     if ((schema != NULL) && (instance != NULL)) {
   1766         vctxt = xmlSchematronNewValidCtxt(schema);
   1767 	if (vctxt == NULL) {
   1768 	    fprintf(stderr, "failed to build schematron validator\n");
   1769 	} else {
   1770 	    ret = xmlSchematronValidateDoc(vctxt, instance);
   1771 	    xmlSchematronFreeValidCtxt(vctxt);
   1772 	}
   1773     }
   1774     xmlSchematronFree(schema);
   1775     xmlFreeDoc(instance);
   1776 
   1777     xmlCleanupParser();
   1778     xmlMemoryDump();
   1779 
   1780     return (0);
   1781 }
   1782 #endif
   1783 #define bottom_schematron
   1784 #include "elfgcchack.h"
   1785 #endif /* LIBXML_SCHEMATRON_ENABLED */
   1786