| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785 | /* * schematron.c : implementation of the Schematron schema validity checking * * See Copyright for the status of this software. * * Daniel Veillard <daniel@veillard.com> *//* * TODO: * + double check the semantic, especially *        - multiple rules applying in a single pattern/node *        - the semantic of libxml2 patterns vs. XSLT production referenced *          by the spec. * + export of results in SVRL * + full parsing and coverage of the spec, conformance of the input to the *   spec * + divergences between the draft and the ISO proposed standard :-( * + hook and test include * + try and compare with the XSLT version */#define IN_LIBXML#include "libxml.h"#ifdef LIBXML_SCHEMATRON_ENABLED#include <string.h>#include <libxml/parser.h>#include <libxml/tree.h>#include <libxml/uri.h>#include <libxml/xpath.h>#include <libxml/xpathInternals.h>#include <libxml/pattern.h>#include <libxml/schematron.h>#define SCHEMATRON_PARSE_OPTIONS XML_PARSE_NOENT#define SCT_OLD_NS BAD_CAST "http://www.ascc.net/xml/schematron"#define XML_SCHEMATRON_NS BAD_CAST "http://purl.oclc.org/dsdl/schematron"static const xmlChar *xmlSchematronNs = XML_SCHEMATRON_NS;static const xmlChar *xmlOldSchematronNs = SCT_OLD_NS;#define IS_SCHEMATRON(node, elem)					\   ((node != NULL) && (node->type == XML_ELEMENT_NODE ) &&		\    (node->ns != NULL) &&						\    (xmlStrEqual(node->name, (const xmlChar *) elem)) &&		\    ((xmlStrEqual(node->ns->href, xmlSchematronNs)) ||			\     (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))#define NEXT_SCHEMATRON(node)						\   while (node != NULL) {						\       if ((node->type == XML_ELEMENT_NODE ) && (node->ns != NULL) && 	\           ((xmlStrEqual(node->ns->href, xmlSchematronNs)) ||		\	    (xmlStrEqual(node->ns->href, xmlOldSchematronNs))))		\	   break;							\       node = node->next;						\   }/** * TODO: * * macro to flag unimplemented blocks */#define TODO 								\    xmlGenericError(xmlGenericErrorContext,				\	    "Unimplemented block at %s:%d\n",				\            __FILE__, __LINE__);typedef enum {    XML_SCHEMATRON_ASSERT=1,    XML_SCHEMATRON_REPORT=2} xmlSchematronTestType;/** * _xmlSchematronTest: * * A Schematrons test, either an assert or a report */typedef struct _xmlSchematronTest xmlSchematronTest;typedef xmlSchematronTest *xmlSchematronTestPtr;struct _xmlSchematronTest {    xmlSchematronTestPtr next;	/* the next test in the list */    xmlSchematronTestType type;	/* the test type */    xmlNodePtr node;		/* the node in the tree */    xmlChar *test;		/* the expression to test */    xmlXPathCompExprPtr comp;	/* the compiled expression */    xmlChar *report;		/* the message to report */};/** * _xmlSchematronRule: * * A Schematrons rule */typedef struct _xmlSchematronRule xmlSchematronRule;typedef xmlSchematronRule *xmlSchematronRulePtr;struct _xmlSchematronRule {    xmlSchematronRulePtr next;	/* the next rule in the list */    xmlSchematronRulePtr patnext;/* the next rule in the pattern list */    xmlNodePtr node;		/* the node in the tree */    xmlChar *context;		/* the context evaluation rule */    xmlSchematronTestPtr tests;	/* the list of tests */    xmlPatternPtr pattern;	/* the compiled pattern associated */    xmlChar *report;		/* the message to report */};/** * _xmlSchematronPattern: * * A Schematrons pattern */typedef struct _xmlSchematronPattern xmlSchematronPattern;typedef xmlSchematronPattern *xmlSchematronPatternPtr;struct _xmlSchematronPattern {    xmlSchematronPatternPtr next;/* the next pattern in the list */    xmlSchematronRulePtr rules;	/* the list of rules */    xmlChar *name;		/* the name of the pattern */};/** * _xmlSchematron: * * A Schematrons definition */struct _xmlSchematron {    const xmlChar *name;	/* schema name */    int preserve;		/* was the document passed by the user */    xmlDocPtr doc;		/* pointer to the parsed document */    int flags;			/* specific to this schematron */    void *_private;		/* unused by the library */    xmlDictPtr dict;		/* the dictionnary used internally */    const xmlChar *title;	/* the title if any */    int nbNs;			/* the number of namespaces */    int nbPattern;		/* the number of patterns */    xmlSchematronPatternPtr patterns;/* the patterns found */    xmlSchematronRulePtr rules;	/* the rules gathered */    int nbNamespaces;		/* number of namespaces in the array */    int maxNamespaces;		/* size of the array */    const xmlChar **namespaces;	/* the array of namespaces */};/** * xmlSchematronValidCtxt: * * A Schematrons validation context */struct _xmlSchematronValidCtxt {    int type;    int flags;			/* an or of xmlSchematronValidOptions */    xmlDictPtr dict;    int nberrors;    int err;    xmlSchematronPtr schema;    xmlXPathContextPtr xctxt;    FILE *outputFile;		/* if using XML_SCHEMATRON_OUT_FILE */    xmlBufferPtr outputBuffer;	/* if using XML_SCHEMATRON_OUT_BUFFER */    xmlOutputWriteCallback iowrite; /* if using XML_SCHEMATRON_OUT_IO */    xmlOutputCloseCallback  ioclose;    void *ioctx;    /* error reporting data */    void *userData;                      /* user specific data block */    xmlSchematronValidityErrorFunc error;/* the callback in case of errors */    xmlSchematronValidityWarningFunc warning;/* callback in case of warning */    xmlStructuredErrorFunc serror;       /* the structured function */};struct _xmlSchematronParserCtxt {    int type;    const xmlChar *URL;    xmlDocPtr doc;    int preserve;               /* Whether the doc should be freed  */    const char *buffer;    int size;    xmlDictPtr dict;            /* dictionnary for interned string names */    int nberrors;    int err;    xmlXPathContextPtr xctxt;	/* the XPath context used for compilation */    xmlSchematronPtr schema;    int nbNamespaces;		/* number of namespaces in the array */    int maxNamespaces;		/* size of the array */    const xmlChar **namespaces;	/* the array of namespaces */    int nbIncludes;		/* number of includes in the array */    int maxIncludes;		/* size of the array */    xmlNodePtr *includes;	/* the array of includes */    /* error reporting data */    void *userData;                      /* user specific data block */    xmlSchematronValidityErrorFunc error;/* the callback in case of errors */    xmlSchematronValidityWarningFunc warning;/* callback in case of warning */    xmlStructuredErrorFunc serror;       /* the structured function */};#define XML_STRON_CTXT_PARSER 1#define XML_STRON_CTXT_VALIDATOR 2/************************************************************************ *									* *			Error reporting					* *									* ************************************************************************//** * xmlSchematronPErrMemory: * @node: a context node * @extra:  extra informations * * Handle an out of memory condition */static voidxmlSchematronPErrMemory(xmlSchematronParserCtxtPtr ctxt,                        const char *extra, xmlNodePtr node){    if (ctxt != NULL)        ctxt->nberrors++;    __xmlSimpleError(XML_FROM_SCHEMASP, XML_ERR_NO_MEMORY, node, NULL,                     extra);}/** * xmlSchematronPErr: * @ctxt: the parsing context * @node: the context node * @error: the error code * @msg: the error message * @str1: extra data * @str2: extra data *  * Handle a parser error */static voidxmlSchematronPErr(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr node, int error,              const char *msg, const xmlChar * str1, const xmlChar * str2){    xmlGenericErrorFunc channel = NULL;    xmlStructuredErrorFunc schannel = NULL;    void *data = NULL;    if (ctxt != NULL) {        ctxt->nberrors++;        channel = ctxt->error;        data = ctxt->userData;	schannel = ctxt->serror;    }    __xmlRaiseError(schannel, channel, data, ctxt, node, XML_FROM_SCHEMASP,                    error, XML_ERR_ERROR, NULL, 0,                    (const char *) str1, (const char *) str2, NULL, 0, 0,                    msg, str1, str2);}/** * xmlSchematronVTypeErrMemory: * @node: a context node * @extra:  extra informations * * Handle an out of memory condition */static voidxmlSchematronVErrMemory(xmlSchematronValidCtxtPtr ctxt,                        const char *extra, xmlNodePtr node){    if (ctxt != NULL) {        ctxt->nberrors++;        ctxt->err = XML_SCHEMAV_INTERNAL;    }    __xmlSimpleError(XML_FROM_SCHEMASV, XML_ERR_NO_MEMORY, node, NULL,                     extra);}/************************************************************************ *									* *		Parsing and compilation of the Schematrontrons		* *									* ************************************************************************//** * xmlSchematronAddTest: * @ctxt: the schema parsing context * @type:  the type of test * @rule:  the parent rule * @node:  the node hosting the test * @test: the associated test * @report: the associated report string * * Add a test to a schematron * * Returns the new pointer or NULL in case of error */static xmlSchematronTestPtrxmlSchematronAddTest(xmlSchematronParserCtxtPtr ctxt,                     xmlSchematronTestType type,                     xmlSchematronRulePtr rule,                     xmlNodePtr node, xmlChar *test, xmlChar *report){    xmlSchematronTestPtr ret;    xmlXPathCompExprPtr comp;    if ((ctxt == NULL) || (rule == NULL) || (node == NULL) ||        (test == NULL))        return(NULL);    /*     * try first to compile the test expression     */    comp = xmlXPathCtxtCompile(ctxt->xctxt, test);    if (comp == NULL) {	xmlSchematronPErr(ctxt, node,	    XML_SCHEMAP_NOROOT,	    "Failed to compile test expression %s",	    test, NULL);	return(NULL);    }    ret = (xmlSchematronTestPtr) xmlMalloc(sizeof(xmlSchematronTest));    if (ret == NULL) {        xmlSchematronPErrMemory(ctxt, "allocating schema test", node);        return (NULL);    }    memset(ret, 0, sizeof(xmlSchematronTest));    ret->type = type;    ret->node = node;    ret->test = test;    ret->comp = comp;    ret->report = report;    ret->next = NULL;    if (rule->tests == NULL) {	rule->tests = ret;    } else {        xmlSchematronTestPtr prev = rule->tests;	while (prev->next != NULL)	     prev = prev->next;        prev->next = ret;    }    return (ret);}/** * xmlSchematronFreeTests: * @tests:  a list of tests * * Free a list of tests. */static voidxmlSchematronFreeTests(xmlSchematronTestPtr tests) {    xmlSchematronTestPtr next;    while (tests != NULL) {        next = tests->next;	if (tests->test != NULL)	    xmlFree(tests->test);	if (tests->comp != NULL)	    xmlXPathFreeCompExpr(tests->comp);	if (tests->report != NULL)	    xmlFree(tests->report);	xmlFree(tests);	tests = next;    }}/** * xmlSchematronAddRule: * @ctxt: the schema parsing context * @schema:  a schema structure * @node:  the node hosting the rule * @context: the associated context string * @report: the associated report string * * Add a rule to a schematron * * Returns the new pointer or NULL in case of error */static xmlSchematronRulePtrxmlSchematronAddRule(xmlSchematronParserCtxtPtr ctxt, xmlSchematronPtr schema,                     xmlSchematronPatternPtr pat, xmlNodePtr node,		     xmlChar *context, xmlChar *report){    xmlSchematronRulePtr ret;    xmlPatternPtr pattern;    if ((ctxt == NULL) || (schema == NULL) || (node == NULL) ||        (context == NULL))        return(NULL);    /*     * Try first to compile the pattern     */    pattern = xmlPatterncompile(context, ctxt->dict, XML_PATTERN_XPATH,                                ctxt->namespaces);    if (pattern == NULL) {	xmlSchematronPErr(ctxt, node,	    XML_SCHEMAP_NOROOT,	    "Failed to compile context expression %s",	    context, NULL);    }    ret = (xmlSchematronRulePtr) xmlMalloc(sizeof(xmlSchematronRule));    if (ret == NULL) {        xmlSchematronPErrMemory(ctxt, "allocating schema rule", node);        return (NULL);    }    memset(ret, 0, sizeof(xmlSchematronRule));    ret->node = node;    ret->context = context;    ret->pattern = pattern;    ret->report = report;    ret->next = NULL;    if (schema->rules == NULL) {	schema->rules = ret;    } else {        xmlSchematronRulePtr prev = schema->rules;	while (prev->next != NULL)	     prev = prev->next;        prev->next = ret;    }    ret->patnext = NULL;    if (pat->rules == NULL) {	pat->rules = ret;    } else {        xmlSchematronRulePtr prev = pat->rules;	while (prev->patnext != NULL)	     prev = prev->patnext;        prev->patnext = ret;    }    return (ret);}/** * xmlSchematronFreeRules: * @rules:  a list of rules * * Free a list of rules. */static voidxmlSchematronFreeRules(xmlSchematronRulePtr rules) {    xmlSchematronRulePtr next;    while (rules != NULL) {        next = rules->next;	if (rules->tests)	    xmlSchematronFreeTests(rules->tests);	if (rules->context != NULL)	    xmlFree(rules->context);	if (rules->pattern)	    xmlFreePattern(rules->pattern);	if (rules->report != NULL)	    xmlFree(rules->report);	xmlFree(rules);	rules = next;    }}/** * xmlSchematronAddPattern: * @ctxt: the schema parsing context * @schema:  a schema structure * @node:  the node hosting the pattern * @id: the id or name of the pattern * * Add a pattern to a schematron * * Returns the new pointer or NULL in case of error */static xmlSchematronPatternPtrxmlSchematronAddPattern(xmlSchematronParserCtxtPtr ctxt,                     xmlSchematronPtr schema, xmlNodePtr node, xmlChar *name){    xmlSchematronPatternPtr ret;    if ((ctxt == NULL) || (schema == NULL) || (node == NULL) || (name == NULL))        return(NULL);    ret = (xmlSchematronPatternPtr) xmlMalloc(sizeof(xmlSchematronPattern));    if (ret == NULL) {        xmlSchematronPErrMemory(ctxt, "allocating schema pattern", node);        return (NULL);    }    memset(ret, 0, sizeof(xmlSchematronPattern));    ret->name = name;    ret->next = NULL;    if (schema->patterns == NULL) {	schema->patterns = ret;    } else {        xmlSchematronPatternPtr prev = schema->patterns;	while (prev->next != NULL)	     prev = prev->next;        prev->next = ret;    }    return (ret);}/** * xmlSchematronFreePatterns: * @patterns:  a list of patterns * * Free a list of patterns. */static voidxmlSchematronFreePatterns(xmlSchematronPatternPtr patterns) {    xmlSchematronPatternPtr next;    while (patterns != NULL) {        next = patterns->next;	if (patterns->name != NULL)	    xmlFree(patterns->name);	xmlFree(patterns);	patterns = next;    }}/** * xmlSchematronNewSchematron: * @ctxt:  a schema validation context * * Allocate a new Schematron structure. * * Returns the newly allocated structure or NULL in case or error */static xmlSchematronPtrxmlSchematronNewSchematron(xmlSchematronParserCtxtPtr ctxt){    xmlSchematronPtr ret;    ret = (xmlSchematronPtr) xmlMalloc(sizeof(xmlSchematron));    if (ret == NULL) {        xmlSchematronPErrMemory(ctxt, "allocating schema", NULL);        return (NULL);    }    memset(ret, 0, sizeof(xmlSchematron));    ret->dict = ctxt->dict;    xmlDictReference(ret->dict);    return (ret);}/** * xmlSchematronFree: * @schema:  a schema structure * * Deallocate a Schematron structure. */voidxmlSchematronFree(xmlSchematronPtr schema){    if (schema == NULL)        return;    if ((schema->doc != NULL) && (!(schema->preserve)))        xmlFreeDoc(schema->doc);    if (schema->namespaces != NULL)        xmlFree((char **) schema->namespaces);        xmlSchematronFreeRules(schema->rules);    xmlSchematronFreePatterns(schema->patterns);    xmlDictFree(schema->dict);    xmlFree(schema);}/** * xmlSchematronNewParserCtxt: * @URL:  the location of the schema * * Create an XML Schematrons parse context for that file/resource expected * to contain an XML Schematrons file. * * Returns the parser context or NULL in case of error */xmlSchematronParserCtxtPtrxmlSchematronNewParserCtxt(const char *URL){    xmlSchematronParserCtxtPtr ret;    if (URL == NULL)        return (NULL);    ret =        (xmlSchematronParserCtxtPtr)        xmlMalloc(sizeof(xmlSchematronParserCtxt));    if (ret == NULL) {        xmlSchematronPErrMemory(NULL, "allocating schema parser context",                                NULL);        return (NULL);    }    memset(ret, 0, sizeof(xmlSchematronParserCtxt));    ret->type = XML_STRON_CTXT_PARSER;    ret->dict = xmlDictCreate();    ret->URL = xmlDictLookup(ret->dict, (const xmlChar *) URL, -1);    ret->includes = NULL;    ret->xctxt = xmlXPathNewContext(NULL);    if (ret->xctxt == NULL) {        xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",                                NULL);	xmlSchematronFreeParserCtxt(ret);        return (NULL);    }    ret->xctxt->flags = XML_XPATH_CHECKNS;    return (ret);}/** * xmlSchematronNewMemParserCtxt: * @buffer:  a pointer to a char array containing the schemas * @size:  the size of the array * * Create an XML Schematrons parse context for that memory buffer expected * to contain an XML Schematrons file. * * Returns the parser context or NULL in case of error */xmlSchematronParserCtxtPtrxmlSchematronNewMemParserCtxt(const char *buffer, int size){    xmlSchematronParserCtxtPtr ret;    if ((buffer == NULL) || (size <= 0))        return (NULL);    ret =        (xmlSchematronParserCtxtPtr)        xmlMalloc(sizeof(xmlSchematronParserCtxt));    if (ret == NULL) {        xmlSchematronPErrMemory(NULL, "allocating schema parser context",                                NULL);        return (NULL);    }    memset(ret, 0, sizeof(xmlSchematronParserCtxt));    ret->buffer = buffer;    ret->size = size;    ret->dict = xmlDictCreate();    ret->xctxt = xmlXPathNewContext(NULL);    if (ret->xctxt == NULL) {        xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",                                NULL);	xmlSchematronFreeParserCtxt(ret);        return (NULL);    }    return (ret);}/** * xmlSchematronNewDocParserCtxt: * @doc:  a preparsed document tree * * Create an XML Schematrons parse context for that document. * NB. The document may be modified during the parsing process. * * Returns the parser context or NULL in case of error */xmlSchematronParserCtxtPtrxmlSchematronNewDocParserCtxt(xmlDocPtr doc){    xmlSchematronParserCtxtPtr ret;    if (doc == NULL)        return (NULL);    ret =        (xmlSchematronParserCtxtPtr)        xmlMalloc(sizeof(xmlSchematronParserCtxt));    if (ret == NULL) {        xmlSchematronPErrMemory(NULL, "allocating schema parser context",                                NULL);        return (NULL);    }    memset(ret, 0, sizeof(xmlSchematronParserCtxt));    ret->doc = doc;    ret->dict = xmlDictCreate();    /* The application has responsibility for the document */    ret->preserve = 1;    ret->xctxt = xmlXPathNewContext(doc);    if (ret->xctxt == NULL) {        xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",                                NULL);	xmlSchematronFreeParserCtxt(ret);        return (NULL);    }    return (ret);}/** * xmlSchematronFreeParserCtxt: * @ctxt:  the schema parser context * * Free the resources associated to the schema parser context */voidxmlSchematronFreeParserCtxt(xmlSchematronParserCtxtPtr ctxt){    if (ctxt == NULL)        return;    if (ctxt->doc != NULL && !ctxt->preserve)        xmlFreeDoc(ctxt->doc);    if (ctxt->xctxt != NULL) {        xmlXPathFreeContext(ctxt->xctxt);    }    if (ctxt->namespaces != NULL)        xmlFree((char **) ctxt->namespaces);    xmlDictFree(ctxt->dict);    xmlFree(ctxt);}#if 0/** * xmlSchematronPushInclude: * @ctxt:  the schema parser context * @doc:  the included document * @cur:  the current include node * * Add an included document */static voidxmlSchematronPushInclude(xmlSchematronParserCtxtPtr ctxt,                        xmlDocPtr doc, xmlNodePtr cur){    if (ctxt->includes == NULL) {        ctxt->maxIncludes = 10;        ctxt->includes = (xmlNodePtr *)	    xmlMalloc(ctxt->maxIncludes * 2 * sizeof(xmlNodePtr));	if (ctxt->includes == NULL) {	    xmlSchematronPErrMemory(NULL, "allocating parser includes",				    NULL);	    return;	}        ctxt->nbIncludes = 0;    } else if (ctxt->nbIncludes + 2 >= ctxt->maxIncludes) {        xmlNodePtr *tmp;	tmp = (xmlNodePtr *)	    xmlRealloc(ctxt->includes, ctxt->maxIncludes * 4 *	               sizeof(xmlNodePtr));	if (tmp == NULL) {	    xmlSchematronPErrMemory(NULL, "allocating parser includes",				    NULL);	    return;	}        ctxt->includes = tmp;	ctxt->maxIncludes *= 2;    }    ctxt->includes[2 * ctxt->nbIncludes] = cur;    ctxt->includes[2 * ctxt->nbIncludes + 1] = (xmlNodePtr) doc;    ctxt->nbIncludes++;}/** * xmlSchematronPopInclude: * @ctxt:  the schema parser context * * Pop an include level. The included document is being freed * * Returns the node immediately following the include or NULL if the *         include list was empty. */static xmlNodePtrxmlSchematronPopInclude(xmlSchematronParserCtxtPtr ctxt){    xmlDocPtr doc;    xmlNodePtr ret;    if (ctxt->nbIncludes <= 0)        return(NULL);    ctxt->nbIncludes--;    doc = (xmlDocPtr) ctxt->includes[2 * ctxt->nbIncludes + 1];    ret = ctxt->includes[2 * ctxt->nbIncludes];    xmlFreeDoc(doc);    if (ret != NULL)	ret = ret->next;    if (ret == NULL)        return(xmlSchematronPopInclude(ctxt));    return(ret);}#endif/** * xmlSchematronAddNamespace: * @ctxt:  the schema parser context * @prefix:  the namespace prefix * @ns:  the namespace name * * Add a namespace definition in the context */static voidxmlSchematronAddNamespace(xmlSchematronParserCtxtPtr ctxt,                          const xmlChar *prefix, const xmlChar *ns){    if (ctxt->namespaces == NULL) {        ctxt->maxNamespaces = 10;        ctxt->namespaces = (const xmlChar **)	    xmlMalloc(ctxt->maxNamespaces * 2 * sizeof(const xmlChar *));	if (ctxt->namespaces == NULL) {	    xmlSchematronPErrMemory(NULL, "allocating parser namespaces",				    NULL);	    return;	}        ctxt->nbNamespaces = 0;    } else if (ctxt->nbNamespaces + 2 >= ctxt->maxNamespaces) {        const xmlChar **tmp;	tmp = (const xmlChar **)	    xmlRealloc((xmlChar **) ctxt->namespaces, ctxt->maxNamespaces * 4 *	               sizeof(const xmlChar *));	if (tmp == NULL) {	    xmlSchematronPErrMemory(NULL, "allocating parser namespaces",				    NULL);	    return;	}        ctxt->namespaces = tmp;	ctxt->maxNamespaces *= 2;    }    ctxt->namespaces[2 * ctxt->nbNamespaces] =         xmlDictLookup(ctxt->dict, ns, -1);    ctxt->namespaces[2 * ctxt->nbNamespaces + 1] =         xmlDictLookup(ctxt->dict, prefix, -1);    ctxt->nbNamespaces++;    ctxt->namespaces[2 * ctxt->nbNamespaces] = NULL;    ctxt->namespaces[2 * ctxt->nbNamespaces + 1] = NULL;}/** * xmlSchematronParseRule: * @ctxt:  a schema validation context * @rule:  the rule node * * parse a rule element */static voidxmlSchematronParseRule(xmlSchematronParserCtxtPtr ctxt,                       xmlSchematronPatternPtr pattern,		       xmlNodePtr rule){    xmlNodePtr cur;    int nbChecks = 0;    xmlChar *test;    xmlChar *context;    xmlChar *report;    xmlSchematronRulePtr ruleptr;    xmlSchematronTestPtr testptr;    if ((ctxt == NULL) || (rule == NULL)) return;    context = xmlGetNoNsProp(rule, BAD_CAST "context");    if (context == NULL) {	xmlSchematronPErr(ctxt, rule,	    XML_SCHEMAP_NOROOT,	    "rule has no context attribute",	    NULL, NULL);	return;    } else if (context[0] == 0) {	xmlSchematronPErr(ctxt, rule,	    XML_SCHEMAP_NOROOT,	    "rule has an empty context attribute",	    NULL, NULL);	xmlFree(context);	return;    } else {	ruleptr = xmlSchematronAddRule(ctxt, ctxt->schema, pattern,	                               rule, context, NULL);	if (ruleptr == NULL) {	    xmlFree(context);	    return;	}    }    cur = rule->children;    NEXT_SCHEMATRON(cur);    while (cur != NULL) {	if (IS_SCHEMATRON(cur, "assert")) {	    nbChecks++;	    test = xmlGetNoNsProp(cur, BAD_CAST "test");	    if (test == NULL) {		xmlSchematronPErr(ctxt, cur,		    XML_SCHEMAP_NOROOT,		    "assert has no test attribute",		    NULL, NULL);	    } else if (test[0] == 0) {		xmlSchematronPErr(ctxt, cur,		    XML_SCHEMAP_NOROOT,		    "assert has an empty test attribute",		    NULL, NULL);		xmlFree(test);	    } else {		/* TODO will need dynamic processing instead */		report = xmlNodeGetContent(cur);		testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_ASSERT,		                               ruleptr, cur, test, report);		if (testptr == NULL)		    xmlFree(test);	    }	} else if (IS_SCHEMATRON(cur, "report")) {	    nbChecks++;	    test = xmlGetNoNsProp(cur, BAD_CAST "test");	    if (test == NULL) {		xmlSchematronPErr(ctxt, cur,		    XML_SCHEMAP_NOROOT,		    "assert has no test attribute",		    NULL, NULL);	    } else if (test[0] == 0) {		xmlSchematronPErr(ctxt, cur,		    XML_SCHEMAP_NOROOT,		    "assert has an empty test attribute",		    NULL, NULL);		xmlFree(test);	    } else {		/* TODO will need dynamic processing instead */		report = xmlNodeGetContent(cur);		testptr = xmlSchematronAddTest(ctxt, XML_SCHEMATRON_REPORT,		                               ruleptr, cur, test, report);		if (testptr == NULL)		    xmlFree(test);	    }	} else {	    xmlSchematronPErr(ctxt, cur,		XML_SCHEMAP_NOROOT,		"Expecting an assert or a report element instead of %s",		cur->name, NULL);	}	cur = cur->next;	NEXT_SCHEMATRON(cur);    }    if (nbChecks == 0) {	xmlSchematronPErr(ctxt, rule,	    XML_SCHEMAP_NOROOT,	    "rule has no assert nor report element", NULL, NULL);    }}/** * xmlSchematronParsePattern: * @ctxt:  a schema validation context * @pat:  the pattern node * * parse a pattern element */static voidxmlSchematronParsePattern(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr pat){    xmlNodePtr cur;    xmlSchematronPatternPtr pattern;    int nbRules = 0;    xmlChar *id;    if ((ctxt == NULL) || (pat == NULL)) return;    id = xmlGetNoNsProp(pat, BAD_CAST "id");    if (id == NULL) {	id = xmlGetNoNsProp(pat, BAD_CAST "name");    }    pattern = xmlSchematronAddPattern(ctxt, ctxt->schema, pat, id);    if (pattern == NULL) {	if (id != NULL)	    xmlFree(id);        return;    }    cur = pat->children;    NEXT_SCHEMATRON(cur);    while (cur != NULL) {	if (IS_SCHEMATRON(cur, "rule")) {	    xmlSchematronParseRule(ctxt, pattern, cur);	    nbRules++;	} else {	    xmlSchematronPErr(ctxt, cur,		XML_SCHEMAP_NOROOT,		"Expecting a rule element instead of %s", cur->name, NULL);	}	cur = cur->next;	NEXT_SCHEMATRON(cur);    }    if (nbRules == 0) {	xmlSchematronPErr(ctxt, pat,	    XML_SCHEMAP_NOROOT,	    "Pattern has no rule element", NULL, NULL);    }}#if 0/** * xmlSchematronLoadInclude: * @ctxt:  a schema validation context * @cur:  the include element * * Load the include document, Push the current pointer * * Returns the updated node pointer */static xmlNodePtrxmlSchematronLoadInclude(xmlSchematronParserCtxtPtr ctxt, xmlNodePtr cur){    xmlNodePtr ret = NULL;    xmlDocPtr doc = NULL;    xmlChar *href = NULL;    xmlChar *base = NULL;    xmlChar *URI = NULL;    if ((ctxt == NULL) || (cur == NULL))        return(NULL);    href = xmlGetNoNsProp(cur, BAD_CAST "href");    if (href == NULL) {	xmlSchematronPErr(ctxt, cur,	    XML_SCHEMAP_NOROOT,	    "Include has no href attribute", NULL, NULL);	return(cur->next);    }    /* do the URI base composition, load and find the root */    base = xmlNodeGetBase(cur->doc, cur);    URI = xmlBuildURI(href, base);    doc = xmlReadFile((const char *) URI, NULL, SCHEMATRON_PARSE_OPTIONS);    if (doc == NULL) {	xmlSchematronPErr(ctxt, cur,		      XML_SCHEMAP_FAILED_LOAD,		      "could not load include '%s'.\n",		      URI, NULL);	goto done;    }    ret = xmlDocGetRootElement(doc);    if (ret == NULL) {	xmlSchematronPErr(ctxt, cur,		      XML_SCHEMAP_FAILED_LOAD,		      "could not find root from include '%s'.\n",		      URI, NULL);	goto done;    }    /* Success, push the include for rollback on exit */    xmlSchematronPushInclude(ctxt, doc, cur);done:    if (ret == NULL) {        if (doc != NULL)	    xmlFreeDoc(doc);    }    xmlFree(href);    if (base != NULL)        xmlFree(base);    if (URI != NULL)        xmlFree(URI);    return(ret);}#endif/** * xmlSchematronParse: * @ctxt:  a schema validation context * * parse a schema definition resource and build an internal * XML Shema struture which can be used to validate instances. * * Returns the internal XML Schematron structure built from the resource or *         NULL in case of error */xmlSchematronPtrxmlSchematronParse(xmlSchematronParserCtxtPtr ctxt){    xmlSchematronPtr ret = NULL;    xmlDocPtr doc;    xmlNodePtr root, cur;    int preserve = 0;    if (ctxt == NULL)        return (NULL);    ctxt->nberrors = 0;    /*     * First step is to parse the input document into an DOM/Infoset     */    if (ctxt->URL != NULL) {        doc = xmlReadFile((const char *) ctxt->URL, NULL,	                  SCHEMATRON_PARSE_OPTIONS);        if (doc == NULL) {	    xmlSchematronPErr(ctxt, NULL,			  XML_SCHEMAP_FAILED_LOAD,                          "xmlSchematronParse: could not load '%s'.\n",                          ctxt->URL, NULL);            return (NULL);        }	ctxt->preserve = 0;    } else if (ctxt->buffer != NULL) {        doc = xmlReadMemory(ctxt->buffer, ctxt->size, NULL, NULL,	                    SCHEMATRON_PARSE_OPTIONS);        if (doc == NULL) {	    xmlSchematronPErr(ctxt, NULL,			  XML_SCHEMAP_FAILED_PARSE,                          "xmlSchematronParse: could not parse.\n",                          NULL, NULL);            return (NULL);        }        doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");        ctxt->URL = xmlDictLookup(ctxt->dict, BAD_CAST "in_memory_buffer", -1);	ctxt->preserve = 0;    } else if (ctxt->doc != NULL) {        doc = ctxt->doc;	preserve = 1;	ctxt->preserve = 1;    } else {	xmlSchematronPErr(ctxt, NULL,		      XML_SCHEMAP_NOTHING_TO_PARSE,		      "xmlSchematronParse: could not parse.\n",		      NULL, NULL);        return (NULL);    }    /*     * Then extract the root and Schematron parse it     */    root = xmlDocGetRootElement(doc);    if (root == NULL) {	xmlSchematronPErr(ctxt, (xmlNodePtr) doc,		      XML_SCHEMAP_NOROOT,		      "The schema has no document element.\n", NULL, NULL);	if (!preserve) {	    xmlFreeDoc(doc);	}        return (NULL);    }    if (!IS_SCHEMATRON(root, "schema")) {	xmlSchematronPErr(ctxt, root,	    XML_SCHEMAP_NOROOT,	    "The XML document '%s' is not a XML schematron document",	    ctxt->URL, NULL);	goto exit;    }    ret = xmlSchematronNewSchematron(ctxt);    if (ret == NULL)        goto exit;    ctxt->schema = ret;    /*     * scan the schema elements     */    cur = root->children;    NEXT_SCHEMATRON(cur);    if (IS_SCHEMATRON(cur, "title")) {        xmlChar *title = xmlNodeGetContent(cur);	if (title != NULL) {	    ret->title = xmlDictLookup(ret->dict, title, -1);	    xmlFree(title);	}	cur = cur->next;	NEXT_SCHEMATRON(cur);    }    while (IS_SCHEMATRON(cur, "ns")) {        xmlChar *prefix = xmlGetNoNsProp(cur, BAD_CAST "prefix");        xmlChar *uri = xmlGetNoNsProp(cur, BAD_CAST "uri");	if ((uri == NULL) || (uri[0] == 0)) {	    xmlSchematronPErr(ctxt, cur,		XML_SCHEMAP_NOROOT,		"ns element has no uri", NULL, NULL);	}	if ((prefix == NULL) || (prefix[0] == 0)) {	    xmlSchematronPErr(ctxt, cur,		XML_SCHEMAP_NOROOT,		"ns element has no prefix", NULL, NULL);	}	if ((prefix) && (uri)) {	    xmlXPathRegisterNs(ctxt->xctxt, prefix, uri);	    xmlSchematronAddNamespace(ctxt, prefix, uri);	    ret->nbNs++;	}	if (uri)	    xmlFree(uri);	if (prefix)	    xmlFree(prefix);	cur = cur->next;	NEXT_SCHEMATRON(cur);    }    while (cur != NULL) {	if (IS_SCHEMATRON(cur, "pattern")) {	    xmlSchematronParsePattern(ctxt, cur);	    ret->nbPattern++;	} else {	    xmlSchematronPErr(ctxt, cur,		XML_SCHEMAP_NOROOT,		"Expecting a pattern element instead of %s", cur->name, NULL);	}	cur = cur->next;	NEXT_SCHEMATRON(cur);    }    if (ret->nbPattern == 0) {	xmlSchematronPErr(ctxt, root,	    XML_SCHEMAP_NOROOT,	    "The schematron document '%s' has no pattern",	    ctxt->URL, NULL);	goto exit;    }    /* the original document must be kept for reporting */    ret->doc = doc;    if (preserve) {	    ret->preserve = 1;    }    preserve = 1;exit:    if (!preserve) {	xmlFreeDoc(doc);    }    if (ret != NULL) {	if (ctxt->nberrors != 0) {	    xmlSchematronFree(ret);	    ret = NULL;	} else {	    ret->namespaces = ctxt->namespaces;	    ret->nbNamespaces = ctxt->nbNamespaces;	    ctxt->namespaces = NULL;	}    }    return (ret);}/************************************************************************ *									* *		Schematrontron Reports handler				* *									* ************************************************************************/static xmlNodePtrxmlSchematronGetNode(xmlSchematronValidCtxtPtr ctxt,                     xmlNodePtr cur, const xmlChar *xpath) {    xmlNodePtr node = NULL;    xmlXPathObjectPtr ret;    if ((ctxt == NULL) || (cur == NULL) || (xpath == NULL))        return(NULL);    ctxt->xctxt->doc = cur->doc;    ctxt->xctxt->node = cur;    ret = xmlXPathEval(xpath, ctxt->xctxt);    if (ret == NULL)        return(NULL);    if ((ret->type == XPATH_NODESET) &&        (ret->nodesetval != NULL) && (ret->nodesetval->nodeNr > 0))	node = ret->nodesetval->nodeTab[0];    xmlXPathFreeObject(ret);    return(node);}/** * xmlSchematronReportOutput: * @ctxt: the validation context * @cur: the current node tested * @msg: the message output * * Output part of the report to whatever channel the user selected */static voidxmlSchematronReportOutput(xmlSchematronValidCtxtPtr ctxt ATTRIBUTE_UNUSED,                          xmlNodePtr cur ATTRIBUTE_UNUSED,                          const char *msg) {    /* TODO */    fprintf(stderr, "%s", msg);}/** * xmlSchematronFormatReport: * @ctxt:  the validation context * @test: the test node * @cur: the current node tested * * Build the string being reported to the user. * * Returns a report string or NULL in case of error. The string needs *         to be deallocated by teh caller */static xmlChar *xmlSchematronFormatReport(xmlSchematronValidCtxtPtr ctxt, 			  xmlNodePtr test, xmlNodePtr cur) {    xmlChar *ret = NULL;    xmlNodePtr child, node;    if ((test == NULL) || (cur == NULL))        return(ret);    child = test->children;    while (child != NULL) {        if ((child->type == XML_TEXT_NODE) ||	    (child->type == XML_CDATA_SECTION_NODE))	    ret = xmlStrcat(ret, child->content);	else if (IS_SCHEMATRON(child, "name")) {	    xmlChar *path;	    path = xmlGetNoNsProp(child, BAD_CAST "path");            node = cur;	    if (path != NULL) {	        node = xmlSchematronGetNode(ctxt, cur, path);		if (node == NULL)		    node = cur;		xmlFree(path);	    }	    if ((node->ns == NULL) || (node->ns->prefix == NULL)) 	        ret = xmlStrcat(ret, node->name);	    else {	        ret = xmlStrcat(ret, node->ns->prefix);	        ret = xmlStrcat(ret, BAD_CAST ":");	        ret = xmlStrcat(ret, node->name);	    }	} else {	    child = child->next;	    continue;	}	/*	 * remove superfluous \n	 */	if (ret != NULL) {	    int len = xmlStrlen(ret);	    xmlChar c;	    if (len > 0) {		c = ret[len - 1];		if ((c == ' ') || (c == '\n') || (c == '\r') || (c == '\t')) {		    while ((c == ' ') || (c == '\n') ||		           (c == '\r') || (c == '\t')) {			len--;			if (len == 0)			    break;			c = ret[len - 1];		    }		    ret[len] = ' ';		    ret[len + 1] = 0;		}	    }	}        child = child->next;    }    return(ret);}/** * xmlSchematronReportSuccess: * @ctxt:  the validation context * @test: the compiled test * @cur: the current node tested * @success: boolean value for the result * * called from the validation engine when an assert or report test have * been done. */static voidxmlSchematronReportSuccess(xmlSchematronValidCtxtPtr ctxt, 		   xmlSchematronTestPtr test, xmlNodePtr cur, xmlSchematronPatternPtr pattern, int success) {    if ((ctxt == NULL) || (cur == NULL) || (test == NULL))        return;    /* if quiet and not SVRL report only failures */    if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) &&        ((ctxt->flags & XML_SCHEMATRON_OUT_XML) == 0) &&	(test->type == XML_SCHEMATRON_REPORT))        return;    if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {        TODO    } else {        xmlChar *path;	char msg[1000];	long line;	const xmlChar *report = NULL;        if (((test->type == XML_SCHEMATRON_REPORT) & (!success)) ||	    ((test->type == XML_SCHEMATRON_ASSERT) & (success)))	    return;	line = xmlGetLineNo(cur);	path = xmlGetNodePath(cur);	if (path == NULL)	    path = (xmlChar *) cur->name;#if 0	if ((test->report != NULL) && (test->report[0] != 0))	    report = test->report;#endif	if (test->node != NULL)            report = xmlSchematronFormatReport(ctxt, test->node, cur);	if (report == NULL) {	    if (test->type == XML_SCHEMATRON_ASSERT) {            report = xmlStrdup((const xmlChar *) "node failed assert");	    } else {            report = xmlStrdup((const xmlChar *) "node failed report");	    }	    }	    snprintf(msg, 999, "%s line %ld: %s\n", (const char *) path,		     line, (const char *) report);    if (ctxt->flags & XML_SCHEMATRON_OUT_ERROR) {        xmlStructuredErrorFunc schannel = NULL;        xmlGenericErrorFunc channel = NULL;        void *data = NULL;        if (ctxt != NULL) {            if (ctxt->serror != NULL)                schannel = ctxt->serror;            else                channel = ctxt->error;            data = ctxt->userData;	}        __xmlRaiseError(schannel, channel, data,                        NULL, cur, XML_FROM_SCHEMATRONV,                        (test->type == XML_SCHEMATRON_ASSERT)?XML_SCHEMATRONV_ASSERT:XML_SCHEMATRONV_REPORT,                        XML_ERR_ERROR, NULL, line,                        (pattern == NULL)?NULL:((const char *) pattern->name),                        (const char *) path,                        (const char *) report, 0, 0,                        "%s", msg);    } else {	xmlSchematronReportOutput(ctxt, cur, &msg[0]);    }    xmlFree((char *) report);	if ((path != NULL) && (path != (xmlChar *) cur->name))	    xmlFree(path);    }}/** * xmlSchematronReportPattern: * @ctxt:  the validation context * @pattern: the current pattern * * called from the validation engine when starting to check a pattern */static voidxmlSchematronReportPattern(xmlSchematronValidCtxtPtr ctxt, 			   xmlSchematronPatternPtr pattern) {    if ((ctxt == NULL) || (pattern == NULL))        return;    if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) || (ctxt->flags & XML_SCHEMATRON_OUT_ERROR)) /* Error gives pattern name as part of error */        return;    if (ctxt->flags & XML_SCHEMATRON_OUT_XML) {        TODO    } else {	char msg[1000];	if (pattern->name == NULL)	    return;	snprintf(msg, 999, "Pattern: %s\n", (const char *) pattern->name);	xmlSchematronReportOutput(ctxt, NULL, &msg[0]);    }}/************************************************************************ *									* *		Validation against a Schematrontron				* *									* ************************************************************************//** * xmlSchematronSetValidStructuredErrors: * @ctxt:  a Schematron validation context * @serror:  the structured error function * @ctx: the functions context * * Set the structured error callback */voidxmlSchematronSetValidStructuredErrors(xmlSchematronValidCtxtPtr ctxt,                                      xmlStructuredErrorFunc serror, void *ctx){    if (ctxt == NULL)        return;    ctxt->serror = serror;    ctxt->error = NULL;    ctxt->warning = NULL;    ctxt->userData = ctx;}/** * xmlSchematronNewValidCtxt: * @schema:  a precompiled XML Schematrons * @options: a set of xmlSchematronValidOptions * * Create an XML Schematrons validation context based on the given schema. * * Returns the validation context or NULL in case of error */xmlSchematronValidCtxtPtrxmlSchematronNewValidCtxt(xmlSchematronPtr schema, int options){    int i;    xmlSchematronValidCtxtPtr ret;    ret = (xmlSchematronValidCtxtPtr) xmlMalloc(sizeof(xmlSchematronValidCtxt));    if (ret == NULL) {        xmlSchematronVErrMemory(NULL, "allocating validation context",                                NULL);        return (NULL);    }    memset(ret, 0, sizeof(xmlSchematronValidCtxt));    ret->type = XML_STRON_CTXT_VALIDATOR;    ret->schema = schema;    ret->xctxt = xmlXPathNewContext(NULL);    ret->flags = options;    if (ret->xctxt == NULL) {        xmlSchematronPErrMemory(NULL, "allocating schema parser XPath context",                                NULL);	xmlSchematronFreeValidCtxt(ret);        return (NULL);    }    for (i = 0;i < schema->nbNamespaces;i++) {        if ((schema->namespaces[2 * i] == NULL) ||            (schema->namespaces[2 * i + 1] == NULL))	    break;	xmlXPathRegisterNs(ret->xctxt, schema->namespaces[2 * i + 1],	                   schema->namespaces[2 * i]);    }    return (ret);}/** * xmlSchematronFreeValidCtxt: * @ctxt:  the schema validation context * * Free the resources associated to the schema validation context */voidxmlSchematronFreeValidCtxt(xmlSchematronValidCtxtPtr ctxt){    if (ctxt == NULL)        return;    if (ctxt->xctxt != NULL)        xmlXPathFreeContext(ctxt->xctxt);    if (ctxt->dict != NULL)        xmlDictFree(ctxt->dict);    xmlFree(ctxt);}static xmlNodePtrxmlSchematronNextNode(xmlNodePtr cur) {    if (cur->children != NULL) {	/*	 * Do not descend on entities declarations	 */	if (cur->children->type != XML_ENTITY_DECL) {	    cur = cur->children;	    /*	     * Skip DTDs	     */	    if (cur->type != XML_DTD_NODE)		return(cur);	}    }    while (cur->next != NULL) {	cur = cur->next;	if ((cur->type != XML_ENTITY_DECL) &&	    (cur->type != XML_DTD_NODE))	    return(cur);    }        do {	cur = cur->parent;	if (cur == NULL) break;	if (cur->type == XML_DOCUMENT_NODE) return(NULL);	if (cur->next != NULL) {	    cur = cur->next;	    return(cur);	}    } while (cur != NULL);    return(cur);}/** * xmlSchematronRunTest: * @ctxt:  the schema validation context * @test:  the current test * @instance:  the document instace tree  * @cur:  the current node in the instance * * Validate a rule against a tree instance at a given position * * Returns 1 in case of success, 0 if error and -1 in case of internal error */static intxmlSchematronRunTest(xmlSchematronValidCtxtPtr ctxt,     xmlSchematronTestPtr test, xmlDocPtr instance, xmlNodePtr cur, xmlSchematronPatternPtr pattern){    xmlXPathObjectPtr ret;    int failed;    failed = 0;    ctxt->xctxt->doc = instance;    ctxt->xctxt->node = cur;    ret = xmlXPathCompiledEval(test->comp, ctxt->xctxt);    if (ret == NULL) {	failed = 1;    } else {        switch (ret->type) {	    case XPATH_XSLT_TREE:	    case XPATH_NODESET:		if ((ret->nodesetval == NULL) ||		    (ret->nodesetval->nodeNr == 0))		    failed = 1;		break;	    case XPATH_BOOLEAN:		failed = !ret->boolval;		break;	    case XPATH_NUMBER:		if ((xmlXPathIsNaN(ret->floatval)) ||		    (ret->floatval == 0.0))		    failed = 1;		break;	    case XPATH_STRING:		if ((ret->stringval == NULL) ||		    (ret->stringval[0] == 0))		    failed = 1;		break;	    case XPATH_UNDEFINED:	    case XPATH_POINT:	    case XPATH_RANGE:	    case XPATH_LOCATIONSET:	    case XPATH_USERS:		failed = 1;		break;	}	xmlXPathFreeObject(ret);    }    if ((failed) && (test->type == XML_SCHEMATRON_ASSERT))        ctxt->nberrors++;    else if ((!failed) && (test->type == XML_SCHEMATRON_REPORT))        ctxt->nberrors++;    xmlSchematronReportSuccess(ctxt, test, cur, pattern, !failed);    return(!failed);}/** * xmlSchematronValidateDoc: * @ctxt:  the schema validation context * @instance:  the document instace tree  * * Validate a tree instance against the schematron * * Returns 0 in case of success, -1 in case of internal error *         and an error count otherwise. */intxmlSchematronValidateDoc(xmlSchematronValidCtxtPtr ctxt, xmlDocPtr instance){    xmlNodePtr cur, root;    xmlSchematronPatternPtr pattern;    xmlSchematronRulePtr rule;    xmlSchematronTestPtr test;    if ((ctxt == NULL) || (ctxt->schema == NULL) ||        (ctxt->schema->rules == NULL) || (instance == NULL))        return(-1);    ctxt->nberrors = 0;    root = xmlDocGetRootElement(instance);    if (root == NULL) {        TODO	ctxt->nberrors++;	return(1);    }    if ((ctxt->flags & XML_SCHEMATRON_OUT_QUIET) ||        (ctxt->flags == 0)) {	/*	 * we are just trying to assert the validity of the document,	 * speed primes over the output, run in a single pass	 */	cur = root;	while (cur != NULL) {	    rule = ctxt->schema->rules;	    while (rule != NULL) {		if (xmlPatternMatch(rule->pattern, cur) == 1) {		    test = rule->tests;		    while (test != NULL) {			xmlSchematronRunTest(ctxt, test, instance, cur, (xmlSchematronPatternPtr)rule->pattern);			test = test->next;		    }		}		rule = rule->next;	    }	    	    cur = xmlSchematronNextNode(cur);	}    } else {        /*	 * Process all contexts one at a time	 */	pattern = ctxt->schema->patterns;		while (pattern != NULL) {	    xmlSchematronReportPattern(ctxt, pattern);	    /*	     * TODO convert the pattern rule to a direct XPath and	     * compute directly instead of using the pattern matching	     * over the full document... 	     * Check the exact semantic	     */	    cur = root;	    while (cur != NULL) {		rule = pattern->rules;		while (rule != NULL) {		    if (xmlPatternMatch(rule->pattern, cur) == 1) {			test = rule->tests;			while (test != NULL) {			    xmlSchematronRunTest(ctxt, test, instance, cur, pattern);			    test = test->next;			}		    }		    rule = rule->patnext;		}				cur = xmlSchematronNextNode(cur);	    }	    pattern = pattern->next;	}    }    return(ctxt->nberrors);}#ifdef STANDALONEintmain(void){    int ret;    xmlDocPtr instance;    xmlSchematronParserCtxtPtr pctxt;    xmlSchematronValidCtxtPtr vctxt;    xmlSchematronPtr schema = NULL;    pctxt = xmlSchematronNewParserCtxt("tst.sct");    if (pctxt == NULL) {        fprintf(stderr, "failed to build schematron parser\n");    } else {        schema = xmlSchematronParse(pctxt);	if (schema == NULL) {	    fprintf(stderr, "failed to compile schematron\n");	}	xmlSchematronFreeParserCtxt(pctxt);    }    instance = xmlReadFile("tst.sct", NULL,                           XML_PARSE_NOENT | XML_PARSE_NOCDATA);    if (instance == NULL) {	fprintf(stderr, "failed to parse instance\n");    }    if ((schema != NULL) && (instance != NULL)) {        vctxt = xmlSchematronNewValidCtxt(schema);	if (vctxt == NULL) {	    fprintf(stderr, "failed to build schematron validator\n");	} else {	    ret = xmlSchematronValidateDoc(vctxt, instance);	    xmlSchematronFreeValidCtxt(vctxt);	}    }    xmlSchematronFree(schema);    xmlFreeDoc(instance);    xmlCleanupParser();    xmlMemoryDump();    return (0);}#endif#define bottom_schematron#include "elfgcchack.h"#endif /* LIBXML_SCHEMATRON_ENABLED */
 |