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