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