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