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