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