DriveHQ Start Menu
Cloud Drive Mapping
Folder Sync
Cloud Backup
True Drop Box
FTP/SFTP Hosting
Group Account
DriveHQ Start Menu
Online File Server
My Storage
|
Manage Shares
|
Publishes
|
Drop Boxes
|
Group Account
WebDAV Drive Mapping
Cloud Drive Home
|
WebDAV Guide
|
Drive Mapping Tool
|
Drive Mapping URL
Complete Data Backup
Backup Guide
|
Online Backup Tool
|
Cloud-to-Cloud Backup
FTP, Email & Web Service
FTP Home
|
FTP Hosting FAQ
|
Email Hosting
|
EmailManager
|
Web Hosting
Help & Resources
About
|
Enterprise Service
|
Partnership
|
Comparisons
|
Support
Quick Links
Security and Privacy
Download Software
Service Manual
Use Cases
Group Account
Online Help
Blog
Contact
Cloud Surveillance
Sign Up
Login
Features
Business Features
Online File Server
FTP Hosting
Cloud Drive Mapping
Cloud File Backup
Email Backup & Hosting
Cloud File Sharing
Folder Synchronization
Group Management
True Drop Box
Full-text Search
AD Integration/SSO
Mobile Access
IP Camera & DVR Solution
More...
Personal Features
Personal Cloud Drive
Backup All Devices
Mobile APPs
Personal Web Hosting
Sub-Account (for Kids)
Home/PC/Kids Monitoring
More...
Software
DriveHQ Drive Mapping Tool
DriveHQ FileManager
DriveHQ Online Backup
DriveHQ Mobile Apps
Pricing
Business Plans & Pricing
Personal Plans & Pricing
Price Comparison with Others
Feature Comparison with Others
Install Mobile App
Sign up
Creating account...
Invalid character in username! Only 0-9, a-z, A-Z, _, -, . allowed.
Username is required!
Invalid email address!
E-mail is required!
Password is required!
Password is invalid!
Password and confirmation do not match.
Confirm password is required!
I accept
Membership Agreement
Please read the Membership Agreement and check "I accept"!
Free Quick Sign-up
Sign-up Page
Log in
Signing in...
Username or e-mail address is required!
Password is required!
Keep me logged in
Quick Login
Forgot Password
Up
Upload
Download
Share
Publish
New Folder
New File
Copy
Cut
Delete
Paste
Rate
Upgrade
Rotate
Effect
Edit
Slide
History
/* * relaxng.c : implementation of the Relax-NG handling and validity checking * * See Copyright for the status of this software. * * Daniel Veillard
*/ /** * TODO: * - add support for DTD compatibility spec * http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html * - report better mem allocations pbms at runtime and abort immediately. */ #define IN_LIBXML #include "libxml.h" #ifdef LIBXML_SCHEMAS_ENABLED #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* * The Relax-NG namespace */ static const xmlChar *xmlRelaxNGNs = (const xmlChar *) "http://relaxng.org/ns/structure/1.0"; #define IS_RELAXNG(node, type) \ ((node != NULL) && (node->ns != NULL) && \ (xmlStrEqual(node->name, (const xmlChar *) type)) && \ (xmlStrEqual(node->ns->href, xmlRelaxNGNs))) #if 0 #define DEBUG 1 #define DEBUG_GRAMMAR 1 #define DEBUG_CONTENT 1 #define DEBUG_TYPE 1 #define DEBUG_VALID 1 #define DEBUG_INTERLEAVE 1 #define DEBUG_LIST 1 #define DEBUG_INCLUDE 1 #define DEBUG_ERROR 1 #define DEBUG_COMPILE 1 #define DEBUG_PROGRESSIVE 1 #endif #define MAX_ERROR 5 #define TODO \ xmlGenericError(xmlGenericErrorContext, \ "Unimplemented block at %s:%d\n", \ __FILE__, __LINE__); typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema; typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr; typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine; typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr; typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument; typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr; typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude; typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr; typedef enum { XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */ XML_RELAXNG_COMBINE_CHOICE, /* choice */ XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */ } xmlRelaxNGCombine; typedef enum { XML_RELAXNG_CONTENT_ERROR = -1, XML_RELAXNG_CONTENT_EMPTY = 0, XML_RELAXNG_CONTENT_SIMPLE, XML_RELAXNG_CONTENT_COMPLEX } xmlRelaxNGContentType; typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar; typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr; struct _xmlRelaxNGGrammar { xmlRelaxNGGrammarPtr parent; /* the parent grammar if any */ xmlRelaxNGGrammarPtr children; /* the children grammar if any */ xmlRelaxNGGrammarPtr next; /* the next grammar if any */ xmlRelaxNGDefinePtr start; /*
content */ xmlRelaxNGCombine combine; /* the default combine value */ xmlRelaxNGDefinePtr startList; /* list of
definitions */ xmlHashTablePtr defs; /* define* */ xmlHashTablePtr refs; /* references */ }; typedef enum { XML_RELAXNG_NOOP = -1, /* a no operation from simplification */ XML_RELAXNG_EMPTY = 0, /* an empty pattern */ XML_RELAXNG_NOT_ALLOWED, /* not allowed top */ XML_RELAXNG_EXCEPT, /* except present in nameclass defs */ XML_RELAXNG_TEXT, /* textual content */ XML_RELAXNG_ELEMENT, /* an element */ XML_RELAXNG_DATATYPE, /* extenal data type definition */ XML_RELAXNG_PARAM, /* extenal data type parameter */ XML_RELAXNG_VALUE, /* value from an extenal data type definition */ XML_RELAXNG_LIST, /* a list of patterns */ XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */ XML_RELAXNG_DEF, /* a definition */ XML_RELAXNG_REF, /* reference to a definition */ XML_RELAXNG_EXTERNALREF, /* reference to an external def */ XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */ XML_RELAXNG_OPTIONAL, /* optional patterns */ XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */ XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */ XML_RELAXNG_CHOICE, /* a choice between non empty patterns */ XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */ XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */ XML_RELAXNG_START /* Used to keep track of starts on grammars */ } xmlRelaxNGType; #define IS_NULLABLE (1 << 0) #define IS_NOT_NULLABLE (1 << 1) #define IS_INDETERMINIST (1 << 2) #define IS_MIXED (1 << 3) #define IS_TRIABLE (1 << 4) #define IS_PROCESSED (1 << 5) #define IS_COMPILABLE (1 << 6) #define IS_NOT_COMPILABLE (1 << 7) struct _xmlRelaxNGDefine { xmlRelaxNGType type; /* the type of definition */ xmlNodePtr node; /* the node in the source */ xmlChar *name; /* the element local name if present */ xmlChar *ns; /* the namespace local name if present */ xmlChar *value; /* value when available */ void *data; /* data lib or specific pointer */ xmlRelaxNGDefinePtr content; /* the expected content */ xmlRelaxNGDefinePtr parent; /* the parent definition, if any */ xmlRelaxNGDefinePtr next; /* list within grouping sequences */ xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */ xmlRelaxNGDefinePtr nameClass; /* the nameClass definition if any */ xmlRelaxNGDefinePtr nextHash; /* next define in defs/refs hash tables */ short depth; /* used for the cycle detection */ short dflags; /* define related flags */ xmlRegexpPtr contModel; /* a compiled content model if available */ }; /** * _xmlRelaxNG: * * A RelaxNGs definition */ struct _xmlRelaxNG { void *_private; /* unused by the library for users or bindings */ xmlRelaxNGGrammarPtr topgrammar; xmlDocPtr doc; int idref; /* requires idref checking */ xmlHashTablePtr defs; /* define */ xmlHashTablePtr refs; /* references */ xmlRelaxNGDocumentPtr documents; /* all the documents loaded */ xmlRelaxNGIncludePtr includes; /* all the includes loaded */ int defNr; /* number of defines used */ xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */ }; #define XML_RELAXNG_IN_ATTRIBUTE (1 << 0) #define XML_RELAXNG_IN_ONEORMORE (1 << 1) #define XML_RELAXNG_IN_LIST (1 << 2) #define XML_RELAXNG_IN_DATAEXCEPT (1 << 3) #define XML_RELAXNG_IN_START (1 << 4) #define XML_RELAXNG_IN_OOMGROUP (1 << 5) #define XML_RELAXNG_IN_OOMINTERLEAVE (1 << 6) #define XML_RELAXNG_IN_EXTERNALREF (1 << 7) #define XML_RELAXNG_IN_ANYEXCEPT (1 << 8) #define XML_RELAXNG_IN_NSEXCEPT (1 << 9) struct _xmlRelaxNGParserCtxt { void *userData; /* user specific data block */ xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */ xmlRelaxNGValidityWarningFunc warning; /* the callback in case of warning */ xmlStructuredErrorFunc serror; xmlRelaxNGValidErr err; xmlRelaxNGPtr schema; /* The schema in use */ xmlRelaxNGGrammarPtr grammar; /* the current grammar */ xmlRelaxNGGrammarPtr parentgrammar; /* the parent grammar */ int flags; /* parser flags */ int nbErrors; /* number of errors at parse time */ int nbWarnings; /* number of warnings at parse time */ const xmlChar *define; /* the current define scope */ xmlRelaxNGDefinePtr def; /* the current define */ int nbInterleaves; xmlHashTablePtr interleaves; /* keep track of all the interleaves */ xmlRelaxNGDocumentPtr documents; /* all the documents loaded */ xmlRelaxNGIncludePtr includes; /* all the includes loaded */ xmlChar *URL; xmlDocPtr document; int defNr; /* number of defines used */ int defMax; /* number of defines aloocated */ xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */ const char *buffer; int size; /* the document stack */ xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */ int docNr; /* Depth of the parsing stack */ int docMax; /* Max depth of the parsing stack */ xmlRelaxNGDocumentPtr *docTab; /* array of docs */ /* the include stack */ xmlRelaxNGIncludePtr inc; /* Current parsed include */ int incNr; /* Depth of the include parsing stack */ int incMax; /* Max depth of the parsing stack */ xmlRelaxNGIncludePtr *incTab; /* array of incs */ int idref; /* requires idref checking */ /* used to compile content models */ xmlAutomataPtr am; /* the automata */ xmlAutomataStatePtr state; /* used to build the automata */ int crng; /* compact syntax and other flags */ int freedoc; /* need to free the document */ }; #define FLAGS_IGNORABLE 1 #define FLAGS_NEGATIVE 2 #define FLAGS_MIXED_CONTENT 4 #define FLAGS_NOERROR 8 /** * xmlRelaxNGInterleaveGroup: * * A RelaxNGs partition set associated to lists of definitions */ typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup; typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr; struct _xmlRelaxNGInterleaveGroup { xmlRelaxNGDefinePtr rule; /* the rule to satisfy */ xmlRelaxNGDefinePtr *defs; /* the array of element definitions */ xmlRelaxNGDefinePtr *attrs; /* the array of attributes definitions */ }; #define IS_DETERMINIST 1 #define IS_NEEDCHECK 2 /** * xmlRelaxNGPartitions: * * A RelaxNGs partition associated to an interleave group */ typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition; typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr; struct _xmlRelaxNGPartition { int nbgroups; /* number of groups in the partitions */ xmlHashTablePtr triage; /* hash table used to direct nodes to the * right group when possible */ int flags; /* determinist ? */ xmlRelaxNGInterleaveGroupPtr *groups; }; /** * xmlRelaxNGValidState: * * A RelaxNGs validation state */ #define MAX_ATTR 20 typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState; typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr; struct _xmlRelaxNGValidState { xmlNodePtr node; /* the current node */ xmlNodePtr seq; /* the sequence of children left to validate */ int nbAttrs; /* the number of attributes */ int maxAttrs; /* the size of attrs */ int nbAttrLeft; /* the number of attributes left to validate */ xmlChar *value; /* the value when operating on string */ xmlChar *endvalue; /* the end value when operating on string */ xmlAttrPtr *attrs; /* the array of attributes */ }; /** * xmlRelaxNGStates: * * A RelaxNGs container for validation state */ typedef struct _xmlRelaxNGStates xmlRelaxNGStates; typedef xmlRelaxNGStates *xmlRelaxNGStatesPtr; struct _xmlRelaxNGStates { int nbState; /* the number of states */ int maxState; /* the size of the array */ xmlRelaxNGValidStatePtr *tabState; }; #define ERROR_IS_DUP 1 /** * xmlRelaxNGValidError: * * A RelaxNGs validation error */ typedef struct _xmlRelaxNGValidError xmlRelaxNGValidError; typedef xmlRelaxNGValidError *xmlRelaxNGValidErrorPtr; struct _xmlRelaxNGValidError { xmlRelaxNGValidErr err; /* the error number */ int flags; /* flags */ xmlNodePtr node; /* the current node */ xmlNodePtr seq; /* the current child */ const xmlChar *arg1; /* first arg */ const xmlChar *arg2; /* second arg */ }; /** * xmlRelaxNGValidCtxt: * * A RelaxNGs validation context */ struct _xmlRelaxNGValidCtxt { void *userData; /* user specific data block */ xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */ xmlRelaxNGValidityWarningFunc warning; /* the callback in case of warning */ xmlStructuredErrorFunc serror; int nbErrors; /* number of errors in validation */ xmlRelaxNGPtr schema; /* The schema in use */ xmlDocPtr doc; /* the document being validated */ int flags; /* validation flags */ int depth; /* validation depth */ int idref; /* requires idref checking */ int errNo; /* the first error found */ /* * Errors accumulated in branches may have to be stacked to be * provided back when it's sure they affect validation. */ xmlRelaxNGValidErrorPtr err; /* Last error */ int errNr; /* Depth of the error stack */ int errMax; /* Max depth of the error stack */ xmlRelaxNGValidErrorPtr errTab; /* stack of errors */ xmlRelaxNGValidStatePtr state; /* the current validation state */ xmlRelaxNGStatesPtr states; /* the accumulated state list */ xmlRelaxNGStatesPtr freeState; /* the pool of free valid states */ int freeStatesNr; int freeStatesMax; xmlRelaxNGStatesPtr *freeStates; /* the pool of free state groups */ /* * This is used for "progressive" validation */ xmlRegExecCtxtPtr elem; /* the current element regexp */ int elemNr; /* the number of element validated */ int elemMax; /* the max depth of elements */ xmlRegExecCtxtPtr *elemTab; /* the stack of regexp runtime */ int pstate; /* progressive state */ xmlNodePtr pnode; /* the current node */ xmlRelaxNGDefinePtr pdef; /* the non-streamable definition */ int perr; /* signal error in content model * outside the regexp */ }; /** * xmlRelaxNGInclude: * * Structure associated to a RelaxNGs document element */ struct _xmlRelaxNGInclude { xmlRelaxNGIncludePtr next; /* keep a chain of includes */ xmlChar *href; /* the normalized href value */ xmlDocPtr doc; /* the associated XML document */ xmlRelaxNGDefinePtr content; /* the definitions */ xmlRelaxNGPtr schema; /* the schema */ }; /** * xmlRelaxNGDocument: * * Structure associated to a RelaxNGs document element */ struct _xmlRelaxNGDocument { xmlRelaxNGDocumentPtr next; /* keep a chain of documents */ xmlChar *href; /* the normalized href value */ xmlDocPtr doc; /* the associated XML document */ xmlRelaxNGDefinePtr content; /* the definitions */ xmlRelaxNGPtr schema; /* the schema */ }; /************************************************************************ * * * Some factorized error routines * * * ************************************************************************/ /** * xmlRngPErrMemory: * @ctxt: an Relax-NG parser context * @extra: extra informations * * Handle a redefinition of attribute error */ static void xmlRngPErrMemory(xmlRelaxNGParserCtxtPtr ctxt, const char *extra) { 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; ctxt->nbErrors++; } if (extra) __xmlRaiseError(schannel, channel, data, NULL, NULL, XML_FROM_RELAXNGP, XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, extra, NULL, NULL, 0, 0, "Memory allocation failed : %s\n", extra); else __xmlRaiseError(schannel, channel, data, NULL, NULL, XML_FROM_RELAXNGP, XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0, "Memory allocation failed\n"); } /** * xmlRngVErrMemory: * @ctxt: a Relax-NG validation context * @extra: extra informations * * Handle a redefinition of attribute error */ static void xmlRngVErrMemory(xmlRelaxNGValidCtxtPtr ctxt, const char *extra) { 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; ctxt->nbErrors++; } if (extra) __xmlRaiseError(schannel, channel, data, NULL, NULL, XML_FROM_RELAXNGV, XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, extra, NULL, NULL, 0, 0, "Memory allocation failed : %s\n", extra); else __xmlRaiseError(schannel, channel, data, NULL, NULL, XML_FROM_RELAXNGV, XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0, "Memory allocation failed\n"); } /** * xmlRngPErr: * @ctxt: a Relax-NG parser context * @node: the node raising the error * @error: the error code * @msg: message * @str1: extra info * @str2: extra info * * Handle a Relax NG Parsing error */ static void xmlRngPErr(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node, int error, const char *msg, const xmlChar * str1, const xmlChar * str2) { 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; ctxt->nbErrors++; } __xmlRaiseError(schannel, channel, data, NULL, node, XML_FROM_RELAXNGP, error, XML_ERR_ERROR, NULL, 0, (const char *) str1, (const char *) str2, NULL, 0, 0, msg, str1, str2); } /** * xmlRngVErr: * @ctxt: a Relax-NG validation context * @node: the node raising the error * @error: the error code * @msg: message * @str1: extra info * @str2: extra info * * Handle a Relax NG Validation error */ static void xmlRngVErr(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node, int error, const char *msg, const xmlChar * str1, const xmlChar * str2) { 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; ctxt->nbErrors++; } __xmlRaiseError(schannel, channel, data, NULL, node, XML_FROM_RELAXNGV, error, XML_ERR_ERROR, NULL, 0, (const char *) str1, (const char *) str2, NULL, 0, 0, msg, str1, str2); } /************************************************************************ * * * Preliminary type checking interfaces * * * ************************************************************************/ /** * xmlRelaxNGTypeHave: * @data: data needed for the library * @type: the type name * @value: the value to check * * Function provided by a type library to check if a type is exported * * Returns 1 if yes, 0 if no and -1 in case of error. */ typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar * type); /** * xmlRelaxNGTypeCheck: * @data: data needed for the library * @type: the type name * @value: the value to check * @result: place to store the result if needed * * Function provided by a type library to check if a value match a type * * Returns 1 if yes, 0 if no and -1 in case of error. */ typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar * type, const xmlChar * value, void **result, xmlNodePtr node); /** * xmlRelaxNGFacetCheck: * @data: data needed for the library * @type: the type name * @facet: the facet name * @val: the facet value * @strval: the string value * @value: the value to check * * Function provided by a type library to check a value facet * * Returns 1 if yes, 0 if no and -1 in case of error. */ typedef int (*xmlRelaxNGFacetCheck) (void *data, const xmlChar * type, const xmlChar * facet, const xmlChar * val, const xmlChar * strval, void *value); /** * xmlRelaxNGTypeFree: * @data: data needed for the library * @result: the value to free * * Function provided by a type library to free a returned result */ typedef void (*xmlRelaxNGTypeFree) (void *data, void *result); /** * xmlRelaxNGTypeCompare: * @data: data needed for the library * @type: the type name * @value1: the first value * @value2: the second value * * Function provided by a type library to compare two values accordingly * to a type. * * Returns 1 if yes, 0 if no and -1 in case of error. */ typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar * type, const xmlChar * value1, xmlNodePtr ctxt1, void *comp1, const xmlChar * value2, xmlNodePtr ctxt2); typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary; typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr; struct _xmlRelaxNGTypeLibrary { const xmlChar *namespace; /* the datatypeLibrary value */ void *data; /* data needed for the library */ xmlRelaxNGTypeHave have; /* the export function */ xmlRelaxNGTypeCheck check; /* the checking function */ xmlRelaxNGTypeCompare comp; /* the compare function */ xmlRelaxNGFacetCheck facet; /* the facet check function */ xmlRelaxNGTypeFree freef; /* the freeing function */ }; /************************************************************************ * * * Allocation functions * * * ************************************************************************/ static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar); static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define); static void xmlRelaxNGNormExtSpace(xmlChar * value); static void xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema); static int xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlRelaxNGValidStatePtr state1, xmlRelaxNGValidStatePtr state2); static void xmlRelaxNGFreeValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidStatePtr state); /** * xmlRelaxNGFreeDocument: * @docu: a document structure * * Deallocate a RelaxNG document structure. */ static void xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu) { if (docu == NULL) return; if (docu->href != NULL) xmlFree(docu->href); if (docu->doc != NULL) xmlFreeDoc(docu->doc); if (docu->schema != NULL) xmlRelaxNGFreeInnerSchema(docu->schema); xmlFree(docu); } /** * xmlRelaxNGFreeDocumentList: * @docu: a list of document structure * * Deallocate a RelaxNG document structures. */ static void xmlRelaxNGFreeDocumentList(xmlRelaxNGDocumentPtr docu) { xmlRelaxNGDocumentPtr next; while (docu != NULL) { next = docu->next; xmlRelaxNGFreeDocument(docu); docu = next; } } /** * xmlRelaxNGFreeInclude: * @incl: a include structure * * Deallocate a RelaxNG include structure. */ static void xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl) { if (incl == NULL) return; if (incl->href != NULL) xmlFree(incl->href); if (incl->doc != NULL) xmlFreeDoc(incl->doc); if (incl->schema != NULL) xmlRelaxNGFree(incl->schema); xmlFree(incl); } /** * xmlRelaxNGFreeIncludeList: * @incl: a include structure list * * Deallocate a RelaxNG include structure. */ static void xmlRelaxNGFreeIncludeList(xmlRelaxNGIncludePtr incl) { xmlRelaxNGIncludePtr next; while (incl != NULL) { next = incl->next; xmlRelaxNGFreeInclude(incl); incl = next; } } /** * xmlRelaxNGNewRelaxNG: * @ctxt: a Relax-NG validation context (optional) * * Allocate a new RelaxNG structure. * * Returns the newly allocated structure or NULL in case or error */ static xmlRelaxNGPtr xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt) { xmlRelaxNGPtr ret; ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG)); if (ret == NULL) { xmlRngPErrMemory(ctxt, NULL); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNG)); return (ret); } /** * xmlRelaxNGFreeInnerSchema: * @schema: a schema structure * * Deallocate a RelaxNG schema structure. */ static void xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema) { if (schema == NULL) return; if (schema->doc != NULL) xmlFreeDoc(schema->doc); if (schema->defTab != NULL) { int i; for (i = 0; i < schema->defNr; i++) xmlRelaxNGFreeDefine(schema->defTab[i]); xmlFree(schema->defTab); } xmlFree(schema); } /** * xmlRelaxNGFree: * @schema: a schema structure * * Deallocate a RelaxNG structure. */ void xmlRelaxNGFree(xmlRelaxNGPtr schema) { if (schema == NULL) return; if (schema->topgrammar != NULL) xmlRelaxNGFreeGrammar(schema->topgrammar); if (schema->doc != NULL) xmlFreeDoc(schema->doc); if (schema->documents != NULL) xmlRelaxNGFreeDocumentList(schema->documents); if (schema->includes != NULL) xmlRelaxNGFreeIncludeList(schema->includes); if (schema->defTab != NULL) { int i; for (i = 0; i < schema->defNr; i++) xmlRelaxNGFreeDefine(schema->defTab[i]); xmlFree(schema->defTab); } xmlFree(schema); } /** * xmlRelaxNGNewGrammar: * @ctxt: a Relax-NG validation context (optional) * * Allocate a new RelaxNG grammar. * * Returns the newly allocated structure or NULL in case or error */ static xmlRelaxNGGrammarPtr xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt) { xmlRelaxNGGrammarPtr ret; ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar)); if (ret == NULL) { xmlRngPErrMemory(ctxt, NULL); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNGGrammar)); return (ret); } /** * xmlRelaxNGFreeGrammar: * @grammar: a grammar structure * * Deallocate a RelaxNG grammar structure. */ static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar) { if (grammar == NULL) return; if (grammar->children != NULL) { xmlRelaxNGFreeGrammar(grammar->children); } if (grammar->next != NULL) { xmlRelaxNGFreeGrammar(grammar->next); } if (grammar->refs != NULL) { xmlHashFree(grammar->refs, NULL); } if (grammar->defs != NULL) { xmlHashFree(grammar->defs, NULL); } xmlFree(grammar); } /** * xmlRelaxNGNewDefine: * @ctxt: a Relax-NG validation context * @node: the node in the input document. * * Allocate a new RelaxNG define. * * Returns the newly allocated structure or NULL in case or error */ static xmlRelaxNGDefinePtr xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGDefinePtr ret; if (ctxt->defMax == 0) { ctxt->defMax = 16; ctxt->defNr = 0; ctxt->defTab = (xmlRelaxNGDefinePtr *) xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr)); if (ctxt->defTab == NULL) { xmlRngPErrMemory(ctxt, "allocating define\n"); return (NULL); } } else if (ctxt->defMax <= ctxt->defNr) { xmlRelaxNGDefinePtr *tmp; ctxt->defMax *= 2; tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab, ctxt->defMax * sizeof (xmlRelaxNGDefinePtr)); if (tmp == NULL) { xmlRngPErrMemory(ctxt, "allocating define\n"); return (NULL); } ctxt->defTab = tmp; } ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine)); if (ret == NULL) { xmlRngPErrMemory(ctxt, "allocating define\n"); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNGDefine)); ctxt->defTab[ctxt->defNr++] = ret; ret->node = node; ret->depth = -1; return (ret); } /** * xmlRelaxNGFreePartition: * @partitions: a partition set structure * * Deallocate RelaxNG partition set structures. */ static void xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) { xmlRelaxNGInterleaveGroupPtr group; int j; if (partitions != NULL) { if (partitions->groups != NULL) { for (j = 0; j < partitions->nbgroups; j++) { group = partitions->groups[j]; if (group != NULL) { if (group->defs != NULL) xmlFree(group->defs); if (group->attrs != NULL) xmlFree(group->attrs); xmlFree(group); } } xmlFree(partitions->groups); } if (partitions->triage != NULL) { xmlHashFree(partitions->triage, NULL); } xmlFree(partitions); } } /** * xmlRelaxNGFreeDefine: * @define: a define structure * * Deallocate a RelaxNG define structure. */ static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define) { if (define == NULL) return; if ((define->type == XML_RELAXNG_VALUE) && (define->attrs != NULL)) { xmlRelaxNGTypeLibraryPtr lib; lib = (xmlRelaxNGTypeLibraryPtr) define->data; if ((lib != NULL) && (lib->freef != NULL)) lib->freef(lib->data, (void *) define->attrs); } if ((define->data != NULL) && (define->type == XML_RELAXNG_INTERLEAVE)) xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data); if ((define->data != NULL) && (define->type == XML_RELAXNG_CHOICE)) xmlHashFree((xmlHashTablePtr) define->data, NULL); if (define->name != NULL) xmlFree(define->name); if (define->ns != NULL) xmlFree(define->ns); if (define->value != NULL) xmlFree(define->value); if (define->contModel != NULL) xmlRegFreeRegexp(define->contModel); xmlFree(define); } /** * xmlRelaxNGNewStates: * @ctxt: a Relax-NG validation context * @size: the default size for the container * * Allocate a new RelaxNG validation state container * * Returns the newly allocated structure or NULL in case or error */ static xmlRelaxNGStatesPtr xmlRelaxNGNewStates(xmlRelaxNGValidCtxtPtr ctxt, int size) { xmlRelaxNGStatesPtr ret; if ((ctxt != NULL) && (ctxt->freeState != NULL) && (ctxt->freeStatesNr > 0)) { ctxt->freeStatesNr--; ret = ctxt->freeStates[ctxt->freeStatesNr]; ret->nbState = 0; return (ret); } if (size < 16) size = 16; ret = (xmlRelaxNGStatesPtr) xmlMalloc(sizeof(xmlRelaxNGStates) + (size - 1) * sizeof(xmlRelaxNGValidStatePtr)); if (ret == NULL) { xmlRngVErrMemory(ctxt, "allocating states\n"); return (NULL); } ret->nbState = 0; ret->maxState = size; ret->tabState = (xmlRelaxNGValidStatePtr *) xmlMalloc((size) * sizeof (xmlRelaxNGValidStatePtr)); if (ret->tabState == NULL) { xmlRngVErrMemory(ctxt, "allocating states\n"); xmlFree(ret); return (NULL); } return (ret); } /** * xmlRelaxNGAddStateUniq: * @ctxt: a Relax-NG validation context * @states: the states container * @state: the validation state * * Add a RelaxNG validation state to the container without checking * for unicity. * * Return 1 in case of success and 0 if this is a duplicate and -1 on error */ static int xmlRelaxNGAddStatesUniq(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGStatesPtr states, xmlRelaxNGValidStatePtr state) { if (state == NULL) { return (-1); } if (states->nbState >= states->maxState) { xmlRelaxNGValidStatePtr *tmp; int size; size = states->maxState * 2; tmp = (xmlRelaxNGValidStatePtr *) xmlRealloc(states->tabState, (size) * sizeof (xmlRelaxNGValidStatePtr)); if (tmp == NULL) { xmlRngVErrMemory(ctxt, "adding states\n"); return (-1); } states->tabState = tmp; states->maxState = size; } states->tabState[states->nbState++] = state; return (1); } /** * xmlRelaxNGAddState: * @ctxt: a Relax-NG validation context * @states: the states container * @state: the validation state * * Add a RelaxNG validation state to the container * * Return 1 in case of success and 0 if this is a duplicate and -1 on error */ static int xmlRelaxNGAddStates(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGStatesPtr states, xmlRelaxNGValidStatePtr state) { int i; if (state == NULL) { return (-1); } if (states->nbState >= states->maxState) { xmlRelaxNGValidStatePtr *tmp; int size; size = states->maxState * 2; tmp = (xmlRelaxNGValidStatePtr *) xmlRealloc(states->tabState, (size) * sizeof (xmlRelaxNGValidStatePtr)); if (tmp == NULL) { xmlRngVErrMemory(ctxt, "adding states\n"); return (-1); } states->tabState = tmp; states->maxState = size; } for (i = 0; i < states->nbState; i++) { if (xmlRelaxNGEqualValidState(ctxt, state, states->tabState[i])) { xmlRelaxNGFreeValidState(ctxt, state); return (0); } } states->tabState[states->nbState++] = state; return (1); } /** * xmlRelaxNGFreeStates: * @ctxt: a Relax-NG validation context * @states: teh container * * Free a RelaxNG validation state container */ static void xmlRelaxNGFreeStates(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGStatesPtr states) { if (states == NULL) return; if ((ctxt != NULL) && (ctxt->freeStates == NULL)) { ctxt->freeStatesMax = 40; ctxt->freeStatesNr = 0; ctxt->freeStates = (xmlRelaxNGStatesPtr *) xmlMalloc(ctxt->freeStatesMax * sizeof(xmlRelaxNGStatesPtr)); if (ctxt->freeStates == NULL) { xmlRngVErrMemory(ctxt, "storing states\n"); } } else if ((ctxt != NULL) && (ctxt->freeStatesNr >= ctxt->freeStatesMax)) { xmlRelaxNGStatesPtr *tmp; tmp = (xmlRelaxNGStatesPtr *) xmlRealloc(ctxt->freeStates, 2 * ctxt->freeStatesMax * sizeof (xmlRelaxNGStatesPtr)); if (tmp == NULL) { xmlRngVErrMemory(ctxt, "storing states\n"); xmlFree(states->tabState); xmlFree(states); return; } ctxt->freeStates = tmp; ctxt->freeStatesMax *= 2; } if ((ctxt == NULL) || (ctxt->freeStates == NULL)) { xmlFree(states->tabState); xmlFree(states); } else { ctxt->freeStates[ctxt->freeStatesNr++] = states; } } /** * xmlRelaxNGNewValidState: * @ctxt: a Relax-NG validation context * @node: the current node or NULL for the document * * Allocate a new RelaxNG validation state * * Returns the newly allocated structure or NULL in case or error */ static xmlRelaxNGValidStatePtr xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGValidStatePtr ret; xmlAttrPtr attr; xmlAttrPtr attrs[MAX_ATTR]; int nbAttrs = 0; xmlNodePtr root = NULL; if (node == NULL) { root = xmlDocGetRootElement(ctxt->doc); if (root == NULL) return (NULL); } else { attr = node->properties; while (attr != NULL) { if (nbAttrs < MAX_ATTR) attrs[nbAttrs++] = attr; else nbAttrs++; attr = attr->next; } } if ((ctxt->freeState != NULL) && (ctxt->freeState->nbState > 0)) { ctxt->freeState->nbState--; ret = ctxt->freeState->tabState[ctxt->freeState->nbState]; } else { ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState)); if (ret == NULL) { xmlRngVErrMemory(ctxt, "allocating states\n"); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNGValidState)); } ret->value = NULL; ret->endvalue = NULL; if (node == NULL) { ret->node = (xmlNodePtr) ctxt->doc; ret->seq = root; } else { ret->node = node; ret->seq = node->children; } ret->nbAttrs = 0; if (nbAttrs > 0) { if (ret->attrs == NULL) { if (nbAttrs < 4) ret->maxAttrs = 4; else ret->maxAttrs = nbAttrs; ret->attrs = (xmlAttrPtr *) xmlMalloc(ret->maxAttrs * sizeof(xmlAttrPtr)); if (ret->attrs == NULL) { xmlRngVErrMemory(ctxt, "allocating states\n"); return (ret); } } else if (ret->maxAttrs < nbAttrs) { xmlAttrPtr *tmp; tmp = (xmlAttrPtr *) xmlRealloc(ret->attrs, nbAttrs * sizeof(xmlAttrPtr)); if (tmp == NULL) { xmlRngVErrMemory(ctxt, "allocating states\n"); return (ret); } ret->attrs = tmp; ret->maxAttrs = nbAttrs; } ret->nbAttrs = nbAttrs; if (nbAttrs < MAX_ATTR) { memcpy(ret->attrs, attrs, sizeof(xmlAttrPtr) * nbAttrs); } else { attr = node->properties; nbAttrs = 0; while (attr != NULL) { ret->attrs[nbAttrs++] = attr; attr = attr->next; } } } ret->nbAttrLeft = ret->nbAttrs; return (ret); } /** * xmlRelaxNGCopyValidState: * @ctxt: a Relax-NG validation context * @state: a validation state * * Copy the validation state * * Returns the newly allocated structure or NULL in case or error */ static xmlRelaxNGValidStatePtr xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidStatePtr state) { xmlRelaxNGValidStatePtr ret; unsigned int maxAttrs; xmlAttrPtr *attrs; if (state == NULL) return (NULL); if ((ctxt->freeState != NULL) && (ctxt->freeState->nbState > 0)) { ctxt->freeState->nbState--; ret = ctxt->freeState->tabState[ctxt->freeState->nbState]; } else { ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState)); if (ret == NULL) { xmlRngVErrMemory(ctxt, "allocating states\n"); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNGValidState)); } attrs = ret->attrs; maxAttrs = ret->maxAttrs; memcpy(ret, state, sizeof(xmlRelaxNGValidState)); ret->attrs = attrs; ret->maxAttrs = maxAttrs; if (state->nbAttrs > 0) { if (ret->attrs == NULL) { ret->maxAttrs = state->maxAttrs; ret->attrs = (xmlAttrPtr *) xmlMalloc(ret->maxAttrs * sizeof(xmlAttrPtr)); if (ret->attrs == NULL) { xmlRngVErrMemory(ctxt, "allocating states\n"); ret->nbAttrs = 0; return (ret); } } else if (ret->maxAttrs < state->nbAttrs) { xmlAttrPtr *tmp; tmp = (xmlAttrPtr *) xmlRealloc(ret->attrs, state->maxAttrs * sizeof(xmlAttrPtr)); if (tmp == NULL) { xmlRngVErrMemory(ctxt, "allocating states\n"); ret->nbAttrs = 0; return (ret); } ret->maxAttrs = state->maxAttrs; ret->attrs = tmp; } memcpy(ret->attrs, state->attrs, state->nbAttrs * sizeof(xmlAttrPtr)); } return (ret); } /** * xmlRelaxNGEqualValidState: * @ctxt: a Relax-NG validation context * @state1: a validation state * @state2: a validation state * * Compare the validation states for equality * * Returns 1 if equald, 0 otherwise */ static int xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlRelaxNGValidStatePtr state1, xmlRelaxNGValidStatePtr state2) { int i; if ((state1 == NULL) || (state2 == NULL)) return (0); if (state1 == state2) return (1); if (state1->node != state2->node) return (0); if (state1->seq != state2->seq) return (0); if (state1->nbAttrLeft != state2->nbAttrLeft) return (0); if (state1->nbAttrs != state2->nbAttrs) return (0); if (state1->endvalue != state2->endvalue) return (0); if ((state1->value != state2->value) && (!xmlStrEqual(state1->value, state2->value))) return (0); for (i = 0; i < state1->nbAttrs; i++) { if (state1->attrs[i] != state2->attrs[i]) return (0); } return (1); } /** * xmlRelaxNGFreeValidState: * @state: a validation state structure * * Deallocate a RelaxNG validation state structure. */ static void xmlRelaxNGFreeValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidStatePtr state) { if (state == NULL) return; if ((ctxt != NULL) && (ctxt->freeState == NULL)) { ctxt->freeState = xmlRelaxNGNewStates(ctxt, 40); } if ((ctxt == NULL) || (ctxt->freeState == NULL)) { if (state->attrs != NULL) xmlFree(state->attrs); xmlFree(state); } else { xmlRelaxNGAddStatesUniq(ctxt, ctxt->freeState, state); } } /************************************************************************ * * * Semi internal functions * * * ************************************************************************/ /** * xmlRelaxParserSetFlag: * @ctxt: a RelaxNG parser context * @flags: a set of flags values * * Semi private function used to pass informations to a parser context * which are a combination of xmlRelaxNGParserFlag . * * Returns 0 if success and -1 in case of error */ int xmlRelaxParserSetFlag(xmlRelaxNGParserCtxtPtr ctxt, int flags) { if (ctxt == NULL) return(-1); if (flags & XML_RELAXNGP_FREE_DOC) { ctxt->crng |= XML_RELAXNGP_FREE_DOC; flags -= XML_RELAXNGP_FREE_DOC; } if (flags & XML_RELAXNGP_CRNG) { ctxt->crng |= XML_RELAXNGP_CRNG; flags -= XML_RELAXNGP_CRNG; } if (flags != 0) return(-1); return(0); } /************************************************************************ * * * Document functions * * * ************************************************************************/ static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc); /** * xmlRelaxNGIncludePush: * @ctxt: the parser context * @value: the element doc * * Pushes a new include on top of the include stack * * Returns 0 in case of error, the index in the stack otherwise */ static int xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGIncludePtr value) { if (ctxt->incTab == NULL) { ctxt->incMax = 4; ctxt->incNr = 0; ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(ctxt->incMax * sizeof(ctxt->incTab[0])); if (ctxt->incTab == NULL) { xmlRngPErrMemory(ctxt, "allocating include\n"); return (0); } } if (ctxt->incNr >= ctxt->incMax) { ctxt->incMax *= 2; ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab, ctxt->incMax * sizeof(ctxt->incTab[0])); if (ctxt->incTab == NULL) { xmlRngPErrMemory(ctxt, "allocating include\n"); return (0); } } ctxt->incTab[ctxt->incNr] = value; ctxt->inc = value; return (ctxt->incNr++); } /** * xmlRelaxNGIncludePop: * @ctxt: the parser context * * Pops the top include from the include stack * * Returns the include just removed */ static xmlRelaxNGIncludePtr xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt) { xmlRelaxNGIncludePtr ret; if (ctxt->incNr <= 0) return (NULL); ctxt->incNr--; if (ctxt->incNr > 0) ctxt->inc = ctxt->incTab[ctxt->incNr - 1]; else ctxt->inc = NULL; ret = ctxt->incTab[ctxt->incNr]; ctxt->incTab[ctxt->incNr] = NULL; return (ret); } /** * xmlRelaxNGRemoveRedefine: * @ctxt: the parser context * @URL: the normalized URL * @target: the included target * @name: the define name to eliminate * * Applies the elimination algorithm of 4.7 * * Returns 0 in case of error, 1 in case of success. */ static int xmlRelaxNGRemoveRedefine(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar * URL ATTRIBUTE_UNUSED, xmlNodePtr target, const xmlChar * name) { int found = 0; xmlNodePtr tmp, tmp2; xmlChar *name2; #ifdef DEBUG_INCLUDE if (name == NULL) xmlGenericError(xmlGenericErrorContext, "Elimination of
start from %s\n", URL); else xmlGenericError(xmlGenericErrorContext, "Elimination of
define %s from %s\n", name, URL); #endif tmp = target; while (tmp != NULL) { tmp2 = tmp->next; if ((name == NULL) && (IS_RELAXNG(tmp, "start"))) { found = 1; xmlUnlinkNode(tmp); xmlFreeNode(tmp); } else if ((name != NULL) && (IS_RELAXNG(tmp, "define"))) { name2 = xmlGetProp(tmp, BAD_CAST "name"); xmlRelaxNGNormExtSpace(name2); if (name2 != NULL) { if (xmlStrEqual(name, name2)) { found = 1; xmlUnlinkNode(tmp); xmlFreeNode(tmp); } xmlFree(name2); } } else if (IS_RELAXNG(tmp, "include")) { xmlChar *href = NULL; xmlRelaxNGDocumentPtr inc = tmp->psvi; if ((inc != NULL) && (inc->doc != NULL) && (inc->doc->children != NULL)) { if (xmlStrEqual (inc->doc->children->name, BAD_CAST "grammar")) { #ifdef DEBUG_INCLUDE href = xmlGetProp(tmp, BAD_CAST "href"); #endif if (xmlRelaxNGRemoveRedefine(ctxt, href, inc->doc->children-> children, name) == 1) { found = 1; } #ifdef DEBUG_INCLUDE if (href != NULL) xmlFree(href); #endif } } } tmp = tmp2; } return (found); } /** * xmlRelaxNGLoadInclude: * @ctxt: the parser context * @URL: the normalized URL * @node: the include node. * @ns: the namespace passed from the context. * * First lookup if the document is already loaded into the parser context, * check against recursion. If not found the resource is loaded and * the content is preprocessed before being returned back to the caller. * * Returns the xmlRelaxNGIncludePtr or NULL in case of error */ static xmlRelaxNGIncludePtr xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar * URL, xmlNodePtr node, const xmlChar * ns) { xmlRelaxNGIncludePtr ret = NULL; xmlDocPtr doc; int i; xmlNodePtr root, cur; #ifdef DEBUG_INCLUDE xmlGenericError(xmlGenericErrorContext, "xmlRelaxNGLoadInclude(%s)\n", URL); #endif /* * check against recursion in the stack */ for (i = 0; i < ctxt->incNr; i++) { if (xmlStrEqual(ctxt->incTab[i]->href, URL)) { xmlRngPErr(ctxt, NULL, XML_RNGP_INCLUDE_RECURSE, "Detected an Include recursion for %s\n", URL, NULL); return (NULL); } } /* * load the document */ doc = xmlReadFile((const char *) URL,NULL,0); if (doc == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_PARSE_ERROR, "xmlRelaxNG: could not load %s\n", URL, NULL); return (NULL); } #ifdef DEBUG_INCLUDE xmlGenericError(xmlGenericErrorContext, "Parsed %s Okay\n", URL); #endif /* * Allocate the document structures and register it first. */ ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude)); if (ret == NULL) { xmlRngPErrMemory(ctxt, "allocating include\n"); xmlFreeDoc(doc); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNGInclude)); ret->doc = doc; ret->href = xmlStrdup(URL); ret->next = ctxt->includes; ctxt->includes = ret; /* * transmit the ns if needed */ if (ns != NULL) { root = xmlDocGetRootElement(doc); if (root != NULL) { if (xmlHasProp(root, BAD_CAST "ns") == NULL) { xmlSetProp(root, BAD_CAST "ns", ns); } } } /* * push it on the stack */ xmlRelaxNGIncludePush(ctxt, ret); /* * Some preprocessing of the document content, this include recursing * in the include stack. */ #ifdef DEBUG_INCLUDE xmlGenericError(xmlGenericErrorContext, "cleanup of %s\n", URL); #endif doc = xmlRelaxNGCleanupDoc(ctxt, doc); if (doc == NULL) { ctxt->inc = NULL; return (NULL); } /* * Pop up the include from the stack */ xmlRelaxNGIncludePop(ctxt); #ifdef DEBUG_INCLUDE xmlGenericError(xmlGenericErrorContext, "Checking of %s\n", URL); #endif /* * Check that the top element is a grammar */ root = xmlDocGetRootElement(doc); if (root == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EMPTY, "xmlRelaxNG: included document is empty %s\n", URL, NULL); return (NULL); } if (!IS_RELAXNG(root, "grammar")) { xmlRngPErr(ctxt, node, XML_RNGP_GRAMMAR_MISSING, "xmlRelaxNG: included document %s root is not a grammar\n", URL, NULL); return (NULL); } /* * Elimination of redefined rules in the include. */ cur = node->children; while (cur != NULL) { if (IS_RELAXNG(cur, "start")) { int found = 0; found = xmlRelaxNGRemoveRedefine(ctxt, URL, root->children, NULL); if (!found) { xmlRngPErr(ctxt, node, XML_RNGP_START_MISSING, "xmlRelaxNG: include %s has a start but not the included grammar\n", URL, NULL); } } else if (IS_RELAXNG(cur, "define")) { xmlChar *name; name = xmlGetProp(cur, BAD_CAST "name"); if (name == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_NAME_MISSING, "xmlRelaxNG: include %s has define without name\n", URL, NULL); } else { int found; xmlRelaxNGNormExtSpace(name); found = xmlRelaxNGRemoveRedefine(ctxt, URL, root->children, name); if (!found) { xmlRngPErr(ctxt, node, XML_RNGP_DEFINE_MISSING, "xmlRelaxNG: include %s has a define %s but not the included grammar\n", URL, name); } xmlFree(name); } } cur = cur->next; } return (ret); } /** * xmlRelaxNGValidErrorPush: * @ctxt: the validation context * @err: the error code * @arg1: the first string argument * @arg2: the second string argument * @dup: arg need to be duplicated * * Pushes a new error on top of the error stack * * Returns 0 in case of error, the index in the stack otherwise */ static int xmlRelaxNGValidErrorPush(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err, const xmlChar * arg1, const xmlChar * arg2, int dup) { xmlRelaxNGValidErrorPtr cur; #ifdef DEBUG_ERROR xmlGenericError(xmlGenericErrorContext, "Pushing error %d at %d on stack\n", err, ctxt->errNr); #endif if (ctxt->errTab == NULL) { ctxt->errMax = 8; ctxt->errNr = 0; ctxt->errTab = (xmlRelaxNGValidErrorPtr) xmlMalloc(ctxt->errMax * sizeof (xmlRelaxNGValidError)); if (ctxt->errTab == NULL) { xmlRngVErrMemory(ctxt, "pushing error\n"); return (0); } ctxt->err = NULL; } if (ctxt->errNr >= ctxt->errMax) { ctxt->errMax *= 2; ctxt->errTab = (xmlRelaxNGValidErrorPtr) xmlRealloc(ctxt->errTab, ctxt->errMax * sizeof (xmlRelaxNGValidError)); if (ctxt->errTab == NULL) { xmlRngVErrMemory(ctxt, "pushing error\n"); return (0); } ctxt->err = &ctxt->errTab[ctxt->errNr - 1]; } if ((ctxt->err != NULL) && (ctxt->state != NULL) && (ctxt->err->node == ctxt->state->node) && (ctxt->err->err == err)) return (ctxt->errNr); cur = &ctxt->errTab[ctxt->errNr]; cur->err = err; if (dup) { cur->arg1 = xmlStrdup(arg1); cur->arg2 = xmlStrdup(arg2); cur->flags = ERROR_IS_DUP; } else { cur->arg1 = arg1; cur->arg2 = arg2; cur->flags = 0; } if (ctxt->state != NULL) { cur->node = ctxt->state->node; cur->seq = ctxt->state->seq; } else { cur->node = NULL; cur->seq = NULL; } ctxt->err = cur; return (ctxt->errNr++); } /** * xmlRelaxNGValidErrorPop: * @ctxt: the validation context * * Pops the top error from the error stack */ static void xmlRelaxNGValidErrorPop(xmlRelaxNGValidCtxtPtr ctxt) { xmlRelaxNGValidErrorPtr cur; if (ctxt->errNr <= 0) { ctxt->err = NULL; return; } ctxt->errNr--; if (ctxt->errNr > 0) ctxt->err = &ctxt->errTab[ctxt->errNr - 1]; else ctxt->err = NULL; cur = &ctxt->errTab[ctxt->errNr]; if (cur->flags & ERROR_IS_DUP) { if (cur->arg1 != NULL) xmlFree((xmlChar *) cur->arg1); cur->arg1 = NULL; if (cur->arg2 != NULL) xmlFree((xmlChar *) cur->arg2); cur->arg2 = NULL; cur->flags = 0; } } /** * xmlRelaxNGDocumentPush: * @ctxt: the parser context * @value: the element doc * * Pushes a new doc on top of the doc stack * * Returns 0 in case of error, the index in the stack otherwise */ static int xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDocumentPtr value) { if (ctxt->docTab == NULL) { ctxt->docMax = 4; ctxt->docNr = 0; ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(ctxt->docMax * sizeof(ctxt->docTab[0])); if (ctxt->docTab == NULL) { xmlRngPErrMemory(ctxt, "adding document\n"); return (0); } } if (ctxt->docNr >= ctxt->docMax) { ctxt->docMax *= 2; ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab, ctxt->docMax * sizeof(ctxt->docTab[0])); if (ctxt->docTab == NULL) { xmlRngPErrMemory(ctxt, "adding document\n"); return (0); } } ctxt->docTab[ctxt->docNr] = value; ctxt->doc = value; return (ctxt->docNr++); } /** * xmlRelaxNGDocumentPop: * @ctxt: the parser context * * Pops the top doc from the doc stack * * Returns the doc just removed */ static xmlRelaxNGDocumentPtr xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt) { xmlRelaxNGDocumentPtr ret; if (ctxt->docNr <= 0) return (NULL); ctxt->docNr--; if (ctxt->docNr > 0) ctxt->doc = ctxt->docTab[ctxt->docNr - 1]; else ctxt->doc = NULL; ret = ctxt->docTab[ctxt->docNr]; ctxt->docTab[ctxt->docNr] = NULL; return (ret); } /** * xmlRelaxNGLoadExternalRef: * @ctxt: the parser context * @URL: the normalized URL * @ns: the inherited ns if any * * First lookup if the document is already loaded into the parser context, * check against recursion. If not found the resource is loaded and * the content is preprocessed before being returned back to the caller. * * Returns the xmlRelaxNGDocumentPtr or NULL in case of error */ static xmlRelaxNGDocumentPtr xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar * URL, const xmlChar * ns) { xmlRelaxNGDocumentPtr ret = NULL; xmlDocPtr doc; xmlNodePtr root; int i; /* * check against recursion in the stack */ for (i = 0; i < ctxt->docNr; i++) { if (xmlStrEqual(ctxt->docTab[i]->href, URL)) { xmlRngPErr(ctxt, NULL, XML_RNGP_EXTERNALREF_RECURSE, "Detected an externalRef recursion for %s\n", URL, NULL); return (NULL); } } /* * load the document */ doc = xmlReadFile((const char *) URL,NULL,0); if (doc == NULL) { xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR, "xmlRelaxNG: could not load %s\n", URL, NULL); return (NULL); } /* * Allocate the document structures and register it first. */ ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument)); if (ret == NULL) { xmlRngPErr(ctxt, (xmlNodePtr) doc, XML_ERR_NO_MEMORY, "xmlRelaxNG: allocate memory for doc %s\n", URL, NULL); xmlFreeDoc(doc); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNGDocument)); ret->doc = doc; ret->href = xmlStrdup(URL); ret->next = ctxt->documents; ctxt->documents = ret; /* * transmit the ns if needed */ if (ns != NULL) { root = xmlDocGetRootElement(doc); if (root != NULL) { if (xmlHasProp(root, BAD_CAST "ns") == NULL) { xmlSetProp(root, BAD_CAST "ns", ns); } } } /* * push it on the stack and register it in the hash table */ xmlRelaxNGDocumentPush(ctxt, ret); /* * Some preprocessing of the document content */ doc = xmlRelaxNGCleanupDoc(ctxt, doc); if (doc == NULL) { ctxt->doc = NULL; return (NULL); } xmlRelaxNGDocumentPop(ctxt); return (ret); } /************************************************************************ * * * Error functions * * * ************************************************************************/ #define VALID_ERR(a) xmlRelaxNGAddValidError(ctxt, a, NULL, NULL, 0); #define VALID_ERR2(a, b) xmlRelaxNGAddValidError(ctxt, a, b, NULL, 0); #define VALID_ERR3(a, b, c) xmlRelaxNGAddValidError(ctxt, a, b, c, 0); #define VALID_ERR2P(a, b) xmlRelaxNGAddValidError(ctxt, a, b, NULL, 1); #define VALID_ERR3P(a, b, c) xmlRelaxNGAddValidError(ctxt, a, b, c, 1); static const char * xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) { if (def == NULL) return ("none"); switch (def->type) { case XML_RELAXNG_EMPTY: return ("empty"); case XML_RELAXNG_NOT_ALLOWED: return ("notAllowed"); case XML_RELAXNG_EXCEPT: return ("except"); case XML_RELAXNG_TEXT: return ("text"); case XML_RELAXNG_ELEMENT: return ("element"); case XML_RELAXNG_DATATYPE: return ("datatype"); case XML_RELAXNG_VALUE: return ("value"); case XML_RELAXNG_LIST: return ("list"); case XML_RELAXNG_ATTRIBUTE: return ("attribute"); case XML_RELAXNG_DEF: return ("def"); case XML_RELAXNG_REF: return ("ref"); case XML_RELAXNG_EXTERNALREF: return ("externalRef"); case XML_RELAXNG_PARENTREF: return ("parentRef"); case XML_RELAXNG_OPTIONAL: return ("optional"); case XML_RELAXNG_ZEROORMORE: return ("zeroOrMore"); case XML_RELAXNG_ONEORMORE: return ("oneOrMore"); case XML_RELAXNG_CHOICE: return ("choice"); case XML_RELAXNG_GROUP: return ("group"); case XML_RELAXNG_INTERLEAVE: return ("interleave"); case XML_RELAXNG_START: return ("start"); case XML_RELAXNG_NOOP: return ("noop"); case XML_RELAXNG_PARAM: return ("param"); } return ("unknown"); } /** * xmlRelaxNGGetErrorString: * @err: the error code * @arg1: the first string argument * @arg2: the second string argument * * computes a formatted error string for the given error code and args * * Returns the error string, it must be deallocated by the caller */ static xmlChar * xmlRelaxNGGetErrorString(xmlRelaxNGValidErr err, const xmlChar * arg1, const xmlChar * arg2) { char msg[1000]; if (arg1 == NULL) arg1 = BAD_CAST ""; if (arg2 == NULL) arg2 = BAD_CAST ""; msg[0] = 0; switch (err) { case XML_RELAXNG_OK: return (NULL); case XML_RELAXNG_ERR_MEMORY: return (xmlCharStrdup("out of memory\n")); case XML_RELAXNG_ERR_TYPE: snprintf(msg, 1000, "failed to validate type %s\n", arg1); break; case XML_RELAXNG_ERR_TYPEVAL: snprintf(msg, 1000, "Type %s doesn't allow value '%s'\n", arg1, arg2); break; case XML_RELAXNG_ERR_DUPID: snprintf(msg, 1000, "ID %s redefined\n", arg1); break; case XML_RELAXNG_ERR_TYPECMP: snprintf(msg, 1000, "failed to compare type %s\n", arg1); break; case XML_RELAXNG_ERR_NOSTATE: return (xmlCharStrdup("Internal error: no state\n")); case XML_RELAXNG_ERR_NODEFINE: return (xmlCharStrdup("Internal error: no define\n")); case XML_RELAXNG_ERR_INTERNAL: snprintf(msg, 1000, "Internal error: %s\n", arg1); break; case XML_RELAXNG_ERR_LISTEXTRA: snprintf(msg, 1000, "Extra data in list: %s\n", arg1); break; case XML_RELAXNG_ERR_INTERNODATA: return (xmlCharStrdup ("Internal: interleave block has no data\n")); case XML_RELAXNG_ERR_INTERSEQ: return (xmlCharStrdup("Invalid sequence in interleave\n")); case XML_RELAXNG_ERR_INTEREXTRA: snprintf(msg, 1000, "Extra element %s in interleave\n", arg1); break; case XML_RELAXNG_ERR_ELEMNAME: snprintf(msg, 1000, "Expecting element %s, got %s\n", arg1, arg2); break; case XML_RELAXNG_ERR_ELEMNONS: snprintf(msg, 1000, "Expecting a namespace for element %s\n", arg1); break; case XML_RELAXNG_ERR_ELEMWRONGNS: snprintf(msg, 1000, "Element %s has wrong namespace: expecting %s\n", arg1, arg2); break; case XML_RELAXNG_ERR_ELEMWRONG: snprintf(msg, 1000, "Did not expect element %s there\n", arg1); break; case XML_RELAXNG_ERR_TEXTWRONG: snprintf(msg, 1000, "Did not expect text in element %s content\n", arg1); break; case XML_RELAXNG_ERR_ELEMEXTRANS: snprintf(msg, 1000, "Expecting no namespace for element %s\n", arg1); break; case XML_RELAXNG_ERR_ELEMNOTEMPTY: snprintf(msg, 1000, "Expecting element %s to be empty\n", arg1); break; case XML_RELAXNG_ERR_NOELEM: snprintf(msg, 1000, "Expecting an element %s, got nothing\n", arg1); break; case XML_RELAXNG_ERR_NOTELEM: return (xmlCharStrdup("Expecting an element got text\n")); case XML_RELAXNG_ERR_ATTRVALID: snprintf(msg, 1000, "Element %s failed to validate attributes\n", arg1); break; case XML_RELAXNG_ERR_CONTENTVALID: snprintf(msg, 1000, "Element %s failed to validate content\n", arg1); break; case XML_RELAXNG_ERR_EXTRACONTENT: snprintf(msg, 1000, "Element %s has extra content: %s\n", arg1, arg2); break; case XML_RELAXNG_ERR_INVALIDATTR: snprintf(msg, 1000, "Invalid attribute %s for element %s\n", arg1, arg2); break; case XML_RELAXNG_ERR_LACKDATA: snprintf(msg, 1000, "Datatype element %s contains no data\n", arg1); break; case XML_RELAXNG_ERR_DATAELEM: snprintf(msg, 1000, "Datatype element %s has child elements\n", arg1); break; case XML_RELAXNG_ERR_VALELEM: snprintf(msg, 1000, "Value element %s has child elements\n", arg1); break; case XML_RELAXNG_ERR_LISTELEM: snprintf(msg, 1000, "List element %s has child elements\n", arg1); break; case XML_RELAXNG_ERR_DATATYPE: snprintf(msg, 1000, "Error validating datatype %s\n", arg1); break; case XML_RELAXNG_ERR_VALUE: snprintf(msg, 1000, "Error validating value %s\n", arg1); break; case XML_RELAXNG_ERR_LIST: return (xmlCharStrdup("Error validating list\n")); case XML_RELAXNG_ERR_NOGRAMMAR: return (xmlCharStrdup("No top grammar defined\n")); case XML_RELAXNG_ERR_EXTRADATA: return (xmlCharStrdup("Extra data in the document\n")); default: return (xmlCharStrdup("Unknown error !\n")); } if (msg[0] == 0) { snprintf(msg, 1000, "Unknown error code %d\n", err); } msg[1000 - 1] = 0; return (xmlStrdup((xmlChar *) msg)); } /** * xmlRelaxNGShowValidError: * @ctxt: the validation context * @err: the error number * @node: the node * @child: the node child generating the problem. * @arg1: the first argument * @arg2: the second argument * * Show a validation error. */ static void xmlRelaxNGShowValidError(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err, xmlNodePtr node, xmlNodePtr child, const xmlChar * arg1, const xmlChar * arg2) { xmlChar *msg; if (ctxt->flags & FLAGS_NOERROR) return; #ifdef DEBUG_ERROR xmlGenericError(xmlGenericErrorContext, "Show error %d\n", err); #endif msg = xmlRelaxNGGetErrorString(err, arg1, arg2); if (msg == NULL) return; if (ctxt->errNo == XML_RELAXNG_OK) ctxt->errNo = err; xmlRngVErr(ctxt, (child == NULL ? node : child), err, (const char *) msg, arg1, arg2); xmlFree(msg); } /** * xmlRelaxNGPopErrors: * @ctxt: the validation context * @level: the error level in the stack * * pop and discard all errors until the given level is reached */ static void xmlRelaxNGPopErrors(xmlRelaxNGValidCtxtPtr ctxt, int level) { int i; xmlRelaxNGValidErrorPtr err; #ifdef DEBUG_ERROR xmlGenericError(xmlGenericErrorContext, "Pop errors till level %d\n", level); #endif for (i = level; i < ctxt->errNr; i++) { err = &ctxt->errTab[i]; if (err->flags & ERROR_IS_DUP) { if (err->arg1 != NULL) xmlFree((xmlChar *) err->arg1); err->arg1 = NULL; if (err->arg2 != NULL) xmlFree((xmlChar *) err->arg2); err->arg2 = NULL; err->flags = 0; } } ctxt->errNr = level; if (ctxt->errNr <= 0) ctxt->err = NULL; } /** * xmlRelaxNGDumpValidError: * @ctxt: the validation context * * Show all validation error over a given index. */ static void xmlRelaxNGDumpValidError(xmlRelaxNGValidCtxtPtr ctxt) { int i, j, k; xmlRelaxNGValidErrorPtr err, dup; #ifdef DEBUG_ERROR xmlGenericError(xmlGenericErrorContext, "Dumping error stack %d errors\n", ctxt->errNr); #endif for (i = 0, k = 0; i < ctxt->errNr; i++) { err = &ctxt->errTab[i]; if (k < MAX_ERROR) { for (j = 0; j < i; j++) { dup = &ctxt->errTab[j]; if ((err->err == dup->err) && (err->node == dup->node) && (xmlStrEqual(err->arg1, dup->arg1)) && (xmlStrEqual(err->arg2, dup->arg2))) { goto skip; } } xmlRelaxNGShowValidError(ctxt, err->err, err->node, err->seq, err->arg1, err->arg2); k++; } skip: if (err->flags & ERROR_IS_DUP) { if (err->arg1 != NULL) xmlFree((xmlChar *) err->arg1); err->arg1 = NULL; if (err->arg2 != NULL) xmlFree((xmlChar *) err->arg2); err->arg2 = NULL; err->flags = 0; } } ctxt->errNr = 0; } /** * xmlRelaxNGAddValidError: * @ctxt: the validation context * @err: the error number * @arg1: the first argument * @arg2: the second argument * @dup: need to dup the args * * Register a validation error, either generating it if it's sure * or stacking it for later handling if unsure. */ static void xmlRelaxNGAddValidError(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err, const xmlChar * arg1, const xmlChar * arg2, int dup) { if (ctxt == NULL) return; if (ctxt->flags & FLAGS_NOERROR) return; #ifdef DEBUG_ERROR xmlGenericError(xmlGenericErrorContext, "Adding error %d\n", err); #endif /* * generate the error directly */ if (((ctxt->flags & FLAGS_IGNORABLE) == 0) || (ctxt->flags & FLAGS_NEGATIVE)) { xmlNodePtr node, seq; /* * Flush first any stacked error which might be the * real cause of the problem. */ if (ctxt->errNr != 0) xmlRelaxNGDumpValidError(ctxt); if (ctxt->state != NULL) { node = ctxt->state->node; seq = ctxt->state->seq; } else { node = seq = NULL; } xmlRelaxNGShowValidError(ctxt, err, node, seq, arg1, arg2); } /* * Stack the error for later processing if needed */ else { xmlRelaxNGValidErrorPush(ctxt, err, arg1, arg2, dup); } } /************************************************************************ * * * Type library hooks * * * ************************************************************************/ static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar * str); /** * xmlRelaxNGSchemaTypeHave: * @data: data needed for the library * @type: the type name * * Check if the given type is provided by * the W3C XMLSchema Datatype library. * * Returns 1 if yes, 0 if no and -1 in case of error. */ static int xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar * type) { xmlSchemaTypePtr typ; if (type == NULL) return (-1); typ = xmlSchemaGetPredefinedType(type, BAD_CAST "http://www.w3.org/2001/XMLSchema"); if (typ == NULL) return (0); return (1); } /** * xmlRelaxNGSchemaTypeCheck: * @data: data needed for the library * @type: the type name * @value: the value to check * @node: the node * * Check if the given type and value are validated by * the W3C XMLSchema Datatype library. * * Returns 1 if yes, 0 if no and -1 in case of error. */ static int xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED, const xmlChar * type, const xmlChar * value, void **result, xmlNodePtr node) { xmlSchemaTypePtr typ; int ret; if ((type == NULL) || (value == NULL)) return (-1); typ = xmlSchemaGetPredefinedType(type, BAD_CAST "http://www.w3.org/2001/XMLSchema"); if (typ == NULL) return (-1); ret = xmlSchemaValPredefTypeNode(typ, value, (xmlSchemaValPtr *) result, node); if (ret == 2) /* special ID error code */ return (2); if (ret == 0) return (1); if (ret > 0) return (0); return (-1); } /** * xmlRelaxNGSchemaFacetCheck: * @data: data needed for the library * @type: the type name * @facet: the facet name * @val: the facet value * @strval: the string value * @value: the value to check * * Function provided by a type library to check a value facet * * Returns 1 if yes, 0 if no and -1 in case of error. */ static int xmlRelaxNGSchemaFacetCheck(void *data ATTRIBUTE_UNUSED, const xmlChar * type, const xmlChar * facetname, const xmlChar * val, const xmlChar * strval, void *value) { xmlSchemaFacetPtr facet; xmlSchemaTypePtr typ; int ret; if ((type == NULL) || (strval == NULL)) return (-1); typ = xmlSchemaGetPredefinedType(type, BAD_CAST "http://www.w3.org/2001/XMLSchema"); if (typ == NULL) return (-1); facet = xmlSchemaNewFacet(); if (facet == NULL) return (-1); if (xmlStrEqual(facetname, BAD_CAST "minInclusive")) { facet->type = XML_SCHEMA_FACET_MININCLUSIVE; } else if (xmlStrEqual(facetname, BAD_CAST "minExclusive")) { facet->type = XML_SCHEMA_FACET_MINEXCLUSIVE; } else if (xmlStrEqual(facetname, BAD_CAST "maxInclusive")) { facet->type = XML_SCHEMA_FACET_MAXINCLUSIVE; } else if (xmlStrEqual(facetname, BAD_CAST "maxExclusive")) { facet->type = XML_SCHEMA_FACET_MAXEXCLUSIVE; } else if (xmlStrEqual(facetname, BAD_CAST "totalDigits")) { facet->type = XML_SCHEMA_FACET_TOTALDIGITS; } else if (xmlStrEqual(facetname, BAD_CAST "fractionDigits")) { facet->type = XML_SCHEMA_FACET_FRACTIONDIGITS; } else if (xmlStrEqual(facetname, BAD_CAST "pattern")) { facet->type = XML_SCHEMA_FACET_PATTERN; } else if (xmlStrEqual(facetname, BAD_CAST "enumeration")) { facet->type = XML_SCHEMA_FACET_ENUMERATION; } else if (xmlStrEqual(facetname, BAD_CAST "whiteSpace")) { facet->type = XML_SCHEMA_FACET_WHITESPACE; } else if (xmlStrEqual(facetname, BAD_CAST "length")) { facet->type = XML_SCHEMA_FACET_LENGTH; } else if (xmlStrEqual(facetname, BAD_CAST "maxLength")) { facet->type = XML_SCHEMA_FACET_MAXLENGTH; } else if (xmlStrEqual(facetname, BAD_CAST "minLength")) { facet->type = XML_SCHEMA_FACET_MINLENGTH; } else { xmlSchemaFreeFacet(facet); return (-1); } facet->value = val; ret = xmlSchemaCheckFacet(facet, typ, NULL, type); if (ret != 0) { xmlSchemaFreeFacet(facet); return (-1); } ret = xmlSchemaValidateFacet(typ, facet, strval, value); xmlSchemaFreeFacet(facet); if (ret != 0) return (-1); return (0); } /** * xmlRelaxNGSchemaFreeValue: * @data: data needed for the library * @value: the value to free * * Function provided by a type library to free a Schemas value * * Returns 1 if yes, 0 if no and -1 in case of error. */ static void xmlRelaxNGSchemaFreeValue(void *data ATTRIBUTE_UNUSED, void *value) { xmlSchemaFreeValue(value); } /** * xmlRelaxNGSchemaTypeCompare: * @data: data needed for the library * @type: the type name * @value1: the first value * @value2: the second value * * Compare two values for equality accordingly a type from the W3C XMLSchema * Datatype library. * * Returns 1 if equal, 0 if no and -1 in case of error. */ static int xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED, const xmlChar * type, const xmlChar * value1, xmlNodePtr ctxt1, void *comp1, const xmlChar * value2, xmlNodePtr ctxt2) { int ret; xmlSchemaTypePtr typ; xmlSchemaValPtr res1 = NULL, res2 = NULL; if ((type == NULL) || (value1 == NULL) || (value2 == NULL)) return (-1); typ = xmlSchemaGetPredefinedType(type, BAD_CAST "http://www.w3.org/2001/XMLSchema"); if (typ == NULL) return (-1); if (comp1 == NULL) { ret = xmlSchemaValPredefTypeNode(typ, value1, &res1, ctxt1); if (ret != 0) return (-1); if (res1 == NULL) return (-1); } else { res1 = (xmlSchemaValPtr) comp1; } ret = xmlSchemaValPredefTypeNode(typ, value2, &res2, ctxt2); if (ret != 0) { if ((comp1 == NULL) && (res1 != NULL)) xmlSchemaFreeValue(res1); return (-1); } if (res1 == NULL) { return (-1); } ret = xmlSchemaCompareValues(res1, res2); if (res1 != (xmlSchemaValPtr) comp1) xmlSchemaFreeValue(res1); xmlSchemaFreeValue(res2); if (ret == -2) return (-1); if (ret == 0) return (1); return (0); } /** * xmlRelaxNGDefaultTypeHave: * @data: data needed for the library * @type: the type name * * Check if the given type is provided by * the default datatype library. * * Returns 1 if yes, 0 if no and -1 in case of error. */ static int xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar * type) { if (type == NULL) return (-1); if (xmlStrEqual(type, BAD_CAST "string")) return (1); if (xmlStrEqual(type, BAD_CAST "token")) return (1); return (0); } /** * xmlRelaxNGDefaultTypeCheck: * @data: data needed for the library * @type: the type name * @value: the value to check * @node: the node * * Check if the given type and value are validated by * the default datatype library. * * Returns 1 if yes, 0 if no and -1 in case of error. */ static int xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED, const xmlChar * type ATTRIBUTE_UNUSED, const xmlChar * value ATTRIBUTE_UNUSED, void **result ATTRIBUTE_UNUSED, xmlNodePtr node ATTRIBUTE_UNUSED) { if (value == NULL) return (-1); if (xmlStrEqual(type, BAD_CAST "string")) return (1); if (xmlStrEqual(type, BAD_CAST "token")) { return (1); } return (0); } /** * xmlRelaxNGDefaultTypeCompare: * @data: data needed for the library * @type: the type name * @value1: the first value * @value2: the second value * * Compare two values accordingly a type from the default * datatype library. * * Returns 1 if yes, 0 if no and -1 in case of error. */ static int xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED, const xmlChar * type, const xmlChar * value1, xmlNodePtr ctxt1 ATTRIBUTE_UNUSED, void *comp1 ATTRIBUTE_UNUSED, const xmlChar * value2, xmlNodePtr ctxt2 ATTRIBUTE_UNUSED) { int ret = -1; if (xmlStrEqual(type, BAD_CAST "string")) { ret = xmlStrEqual(value1, value2); } else if (xmlStrEqual(type, BAD_CAST "token")) { if (!xmlStrEqual(value1, value2)) { xmlChar *nval, *nvalue; /* * TODO: trivial optimizations are possible by * computing at compile-time */ nval = xmlRelaxNGNormalize(NULL, value1); nvalue = xmlRelaxNGNormalize(NULL, value2); if ((nval == NULL) || (nvalue == NULL)) ret = -1; else if (xmlStrEqual(nval, nvalue)) ret = 1; else ret = 0; if (nval != NULL) xmlFree(nval); if (nvalue != NULL) xmlFree(nvalue); } else ret = 1; } return (ret); } static int xmlRelaxNGTypeInitialized = 0; static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL; /** * xmlRelaxNGFreeTypeLibrary: * @lib: the type library structure * @namespace: the URI bound to the library * * Free the structure associated to the type library */ static void xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib, const xmlChar * namespace ATTRIBUTE_UNUSED) { if (lib == NULL) return; if (lib->namespace != NULL) xmlFree((xmlChar *) lib->namespace); xmlFree(lib); } /** * xmlRelaxNGRegisterTypeLibrary: * @namespace: the URI bound to the library * @data: data associated to the library * @have: the provide function * @check: the checking function * @comp: the comparison function * * Register a new type library * * Returns 0 in case of success and -1 in case of error. */ static int xmlRelaxNGRegisterTypeLibrary(const xmlChar * namespace, void *data, xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check, xmlRelaxNGTypeCompare comp, xmlRelaxNGFacetCheck facet, xmlRelaxNGTypeFree freef) { xmlRelaxNGTypeLibraryPtr lib; int ret; if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) || (check == NULL) || (comp == NULL)) return (-1); if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) { xmlGenericError(xmlGenericErrorContext, "Relax-NG types library '%s' already registered\n", namespace); return (-1); } lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary)); if (lib == NULL) { xmlRngVErrMemory(NULL, "adding types library\n"); return (-1); } memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary)); lib->namespace = xmlStrdup(namespace); lib->data = data; lib->have = have; lib->comp = comp; lib->check = check; lib->facet = facet; lib->freef = freef; ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib); if (ret < 0) { xmlGenericError(xmlGenericErrorContext, "Relax-NG types library failed to register '%s'\n", namespace); xmlRelaxNGFreeTypeLibrary(lib, namespace); return (-1); } return (0); } /** * xmlRelaxNGInitTypes: * * Initilize the default type libraries. * * Returns 0 in case of success and -1 in case of error. */ int xmlRelaxNGInitTypes(void) { if (xmlRelaxNGTypeInitialized != 0) return (0); xmlRelaxNGRegisteredTypes = xmlHashCreate(10); if (xmlRelaxNGRegisteredTypes == NULL) { xmlGenericError(xmlGenericErrorContext, "Failed to allocate sh table for Relax-NG types\n"); return (-1); } xmlRelaxNGRegisterTypeLibrary(BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes", NULL, xmlRelaxNGSchemaTypeHave, xmlRelaxNGSchemaTypeCheck, xmlRelaxNGSchemaTypeCompare, xmlRelaxNGSchemaFacetCheck, xmlRelaxNGSchemaFreeValue); xmlRelaxNGRegisterTypeLibrary(xmlRelaxNGNs, NULL, xmlRelaxNGDefaultTypeHave, xmlRelaxNGDefaultTypeCheck, xmlRelaxNGDefaultTypeCompare, NULL, NULL); xmlRelaxNGTypeInitialized = 1; return (0); } /** * xmlRelaxNGCleanupTypes: * * Cleanup the default Schemas type library associated to RelaxNG */ void xmlRelaxNGCleanupTypes(void) { xmlSchemaCleanupTypes(); if (xmlRelaxNGTypeInitialized == 0) return; xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator) xmlRelaxNGFreeTypeLibrary); xmlRelaxNGTypeInitialized = 0; } /************************************************************************ * * * Compiling element content into regexp * * * * Sometime the element content can be compiled into a pure regexp, * * This allows a faster execution and streamability at that level * * * ************************************************************************/ static int xmlRelaxNGTryCompile(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def); /** * xmlRelaxNGIsCompileable: * @define: the definition to check * * Check if a definition is nullable. * * Returns 1 if yes, 0 if no and -1 in case of error */ static int xmlRelaxNGIsCompileable(xmlRelaxNGDefinePtr def) { int ret = -1; if (def == NULL) { return (-1); } if ((def->type != XML_RELAXNG_ELEMENT) && (def->dflags & IS_COMPILABLE)) return (1); if ((def->type != XML_RELAXNG_ELEMENT) && (def->dflags & IS_NOT_COMPILABLE)) return (0); switch (def->type) { case XML_RELAXNG_NOOP: ret = xmlRelaxNGIsCompileable(def->content); break; case XML_RELAXNG_TEXT: case XML_RELAXNG_EMPTY: ret = 1; break; case XML_RELAXNG_ELEMENT: /* * Check if the element content is compileable */ if (((def->dflags & IS_NOT_COMPILABLE) == 0) && ((def->dflags & IS_COMPILABLE) == 0)) { xmlRelaxNGDefinePtr list; list = def->content; while (list != NULL) { ret = xmlRelaxNGIsCompileable(list); if (ret != 1) break; list = list->next; } /* * Because the routine is recursive, we must guard against * discovering both COMPILABLE and NOT_COMPILABLE */ if (ret == 0) { def->dflags &= ~IS_COMPILABLE; def->dflags |= IS_NOT_COMPILABLE; } if ((ret == 1) && !(def->dflags &= IS_NOT_COMPILABLE)) def->dflags |= IS_COMPILABLE; #ifdef DEBUG_COMPILE if (ret == 1) { xmlGenericError(xmlGenericErrorContext, "element content for %s is compilable\n", def->name); } else if (ret == 0) { xmlGenericError(xmlGenericErrorContext, "element content for %s is not compilable\n", def->name); } else { xmlGenericError(xmlGenericErrorContext, "Problem in RelaxNGIsCompileable for element %s\n", def->name); } #endif } /* * All elements return a compileable status unless they * are generic like anyName */ if ((def->nameClass != NULL) || (def->name == NULL)) ret = 0; else ret = 1; return (ret); case XML_RELAXNG_REF: case XML_RELAXNG_EXTERNALREF: case XML_RELAXNG_PARENTREF: if (def->depth == -20) { return (1); } else { xmlRelaxNGDefinePtr list; def->depth = -20; list = def->content; while (list != NULL) { ret = xmlRelaxNGIsCompileable(list); if (ret != 1) break; list = list->next; } } break; case XML_RELAXNG_START: case XML_RELAXNG_OPTIONAL: case XML_RELAXNG_ZEROORMORE: case XML_RELAXNG_ONEORMORE: case XML_RELAXNG_CHOICE: case XML_RELAXNG_GROUP: case XML_RELAXNG_DEF:{ xmlRelaxNGDefinePtr list; list = def->content; while (list != NULL) { ret = xmlRelaxNGIsCompileable(list); if (ret != 1) break; list = list->next; } break; } case XML_RELAXNG_EXCEPT: case XML_RELAXNG_ATTRIBUTE: case XML_RELAXNG_INTERLEAVE: case XML_RELAXNG_DATATYPE: case XML_RELAXNG_LIST: case XML_RELAXNG_PARAM: case XML_RELAXNG_VALUE: case XML_RELAXNG_NOT_ALLOWED: ret = 0; break; } if (ret == 0) def->dflags |= IS_NOT_COMPILABLE; if (ret == 1) def->dflags |= IS_COMPILABLE; #ifdef DEBUG_COMPILE if (ret == 1) { xmlGenericError(xmlGenericErrorContext, "RelaxNGIsCompileable %s : true\n", xmlRelaxNGDefName(def)); } else if (ret == 0) { xmlGenericError(xmlGenericErrorContext, "RelaxNGIsCompileable %s : false\n", xmlRelaxNGDefName(def)); } else { xmlGenericError(xmlGenericErrorContext, "Problem in RelaxNGIsCompileable %s\n", xmlRelaxNGDefName(def)); } #endif return (ret); } /** * xmlRelaxNGCompile: * ctxt: the RelaxNG parser context * @define: the definition tree to compile * * Compile the set of definitions, it works recursively, till the * element boundaries, where it tries to compile the content if possible * * Returns 0 if success and -1 in case of error */ static int xmlRelaxNGCompile(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def) { int ret = 0; xmlRelaxNGDefinePtr list; if ((ctxt == NULL) || (def == NULL)) return (-1); switch (def->type) { case XML_RELAXNG_START: if ((xmlRelaxNGIsCompileable(def) == 1) && (def->depth != -25)) { xmlAutomataPtr oldam = ctxt->am; xmlAutomataStatePtr oldstate = ctxt->state; def->depth = -25; list = def->content; ctxt->am = xmlNewAutomata(); if (ctxt->am == NULL) return (-1); ctxt->state = xmlAutomataGetInitState(ctxt->am); while (list != NULL) { xmlRelaxNGCompile(ctxt, list); list = list->next; } xmlAutomataSetFinalState(ctxt->am, ctxt->state); def->contModel = xmlAutomataCompile(ctxt->am); xmlRegexpIsDeterminist(def->contModel); xmlFreeAutomata(ctxt->am); ctxt->state = oldstate; ctxt->am = oldam; } break; case XML_RELAXNG_ELEMENT: if ((ctxt->am != NULL) && (def->name != NULL)) { ctxt->state = xmlAutomataNewTransition2(ctxt->am, ctxt->state, NULL, def->name, def->ns, def); } if ((def->dflags & IS_COMPILABLE) && (def->depth != -25)) { xmlAutomataPtr oldam = ctxt->am; xmlAutomataStatePtr oldstate = ctxt->state; def->depth = -25; list = def->content; ctxt->am = xmlNewAutomata(); if (ctxt->am == NULL) return (-1); ctxt->state = xmlAutomataGetInitState(ctxt->am); while (list != NULL) { xmlRelaxNGCompile(ctxt, list); list = list->next; } xmlAutomataSetFinalState(ctxt->am, ctxt->state); def->contModel = xmlAutomataCompile(ctxt->am); if (!xmlRegexpIsDeterminist(def->contModel)) { /* * we can only use the automata if it is determinist */ xmlRegFreeRegexp(def->contModel); def->contModel = NULL; } xmlFreeAutomata(ctxt->am); ctxt->state = oldstate; ctxt->am = oldam; } else { xmlAutomataPtr oldam = ctxt->am; /* * we can't build the content model for this element content * but it still might be possible to build it for some of its * children, recurse. */ ret = xmlRelaxNGTryCompile(ctxt, def); ctxt->am = oldam; } break; case XML_RELAXNG_NOOP: ret = xmlRelaxNGCompile(ctxt, def->content); break; case XML_RELAXNG_OPTIONAL:{ xmlAutomataStatePtr oldstate = ctxt->state; xmlRelaxNGCompile(ctxt, def->content); xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); break; } case XML_RELAXNG_ZEROORMORE:{ xmlAutomataStatePtr oldstate; ctxt->state = xmlAutomataNewEpsilon(ctxt->am, ctxt->state, NULL); oldstate = ctxt->state; list = def->content; while (list != NULL) { xmlRelaxNGCompile(ctxt, list); list = list->next; } xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldstate); ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL); break; } case XML_RELAXNG_ONEORMORE:{ xmlAutomataStatePtr oldstate; list = def->content; while (list != NULL) { xmlRelaxNGCompile(ctxt, list); list = list->next; } oldstate = ctxt->state; list = def->content; while (list != NULL) { xmlRelaxNGCompile(ctxt, list); list = list->next; } xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldstate); ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL); break; } case XML_RELAXNG_CHOICE:{ xmlAutomataStatePtr target = NULL; xmlAutomataStatePtr oldstate = ctxt->state; list = def->content; while (list != NULL) { ctxt->state = oldstate; ret = xmlRelaxNGCompile(ctxt, list); if (ret != 0) break; if (target == NULL) target = ctxt->state; else { xmlAutomataNewEpsilon(ctxt->am, ctxt->state, target); } list = list->next; } ctxt->state = target; break; } case XML_RELAXNG_REF: case XML_RELAXNG_EXTERNALREF: case XML_RELAXNG_PARENTREF: case XML_RELAXNG_GROUP: case XML_RELAXNG_DEF: list = def->content; while (list != NULL) { ret = xmlRelaxNGCompile(ctxt, list); if (ret != 0) break; list = list->next; } break; case XML_RELAXNG_TEXT:{ xmlAutomataStatePtr oldstate; ctxt->state = xmlAutomataNewEpsilon(ctxt->am, ctxt->state, NULL); oldstate = ctxt->state; xmlRelaxNGCompile(ctxt, def->content); xmlAutomataNewTransition(ctxt->am, ctxt->state, ctxt->state, BAD_CAST "#text", NULL); ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL); break; } case XML_RELAXNG_EMPTY: ctxt->state = xmlAutomataNewEpsilon(ctxt->am, ctxt->state, NULL); break; case XML_RELAXNG_EXCEPT: case XML_RELAXNG_ATTRIBUTE: case XML_RELAXNG_INTERLEAVE: case XML_RELAXNG_NOT_ALLOWED: case XML_RELAXNG_DATATYPE: case XML_RELAXNG_LIST: case XML_RELAXNG_PARAM: case XML_RELAXNG_VALUE: /* This should not happen and generate an internal error */ fprintf(stderr, "RNG internal error trying to compile %s\n", xmlRelaxNGDefName(def)); break; } return (ret); } /** * xmlRelaxNGTryCompile: * ctxt: the RelaxNG parser context * @define: the definition tree to compile * * Try to compile the set of definitions, it works recursively, * possibly ignoring parts which cannot be compiled. * * Returns 0 if success and -1 in case of error */ static int xmlRelaxNGTryCompile(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def) { int ret = 0; xmlRelaxNGDefinePtr list; if ((ctxt == NULL) || (def == NULL)) return (-1); if ((def->type == XML_RELAXNG_START) || (def->type == XML_RELAXNG_ELEMENT)) { ret = xmlRelaxNGIsCompileable(def); if ((def->dflags & IS_COMPILABLE) && (def->depth != -25)) { ctxt->am = NULL; ret = xmlRelaxNGCompile(ctxt, def); #ifdef DEBUG_PROGRESSIVE if (ret == 0) { if (def->type == XML_RELAXNG_START) xmlGenericError(xmlGenericErrorContext, "compiled the start\n"); else xmlGenericError(xmlGenericErrorContext, "compiled element %s\n", def->name); } else { if (def->type == XML_RELAXNG_START) xmlGenericError(xmlGenericErrorContext, "failed to compile the start\n"); else xmlGenericError(xmlGenericErrorContext, "failed to compile element %s\n", def->name); } #endif return (ret); } } switch (def->type) { case XML_RELAXNG_NOOP: ret = xmlRelaxNGTryCompile(ctxt, def->content); break; case XML_RELAXNG_TEXT: case XML_RELAXNG_DATATYPE: case XML_RELAXNG_LIST: case XML_RELAXNG_PARAM: case XML_RELAXNG_VALUE: case XML_RELAXNG_EMPTY: case XML_RELAXNG_ELEMENT: ret = 0; break; case XML_RELAXNG_OPTIONAL: case XML_RELAXNG_ZEROORMORE: case XML_RELAXNG_ONEORMORE: case XML_RELAXNG_CHOICE: case XML_RELAXNG_GROUP: case XML_RELAXNG_DEF: case XML_RELAXNG_START: case XML_RELAXNG_REF: case XML_RELAXNG_EXTERNALREF: case XML_RELAXNG_PARENTREF: list = def->content; while (list != NULL) { ret = xmlRelaxNGTryCompile(ctxt, list); if (ret != 0) break; list = list->next; } break; case XML_RELAXNG_EXCEPT: case XML_RELAXNG_ATTRIBUTE: case XML_RELAXNG_INTERLEAVE: case XML_RELAXNG_NOT_ALLOWED: ret = 0; break; } return (ret); } /************************************************************************ * * * Parsing functions * * * ************************************************************************/ static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node); static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node); static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group); static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node); static xmlRelaxNGPtr xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node); static int xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes); static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node, xmlRelaxNGDefinePtr def); static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes); static int xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr define, xmlNodePtr elem); #define IS_BLANK_NODE(n) (xmlRelaxNGIsBlank((n)->content)) /** * xmlRelaxNGIsNullable: * @define: the definition to verify * * Check if a definition is nullable. * * Returns 1 if yes, 0 if no and -1 in case of error */ static int xmlRelaxNGIsNullable(xmlRelaxNGDefinePtr define) { int ret; if (define == NULL) return (-1); if (define->dflags & IS_NULLABLE) return (1); if (define->dflags & IS_NOT_NULLABLE) return (0); switch (define->type) { case XML_RELAXNG_EMPTY: case XML_RELAXNG_TEXT: ret = 1; break; case XML_RELAXNG_NOOP: case XML_RELAXNG_DEF: case XML_RELAXNG_REF: case XML_RELAXNG_EXTERNALREF: case XML_RELAXNG_PARENTREF: case XML_RELAXNG_ONEORMORE: ret = xmlRelaxNGIsNullable(define->content); break; case XML_RELAXNG_EXCEPT: case XML_RELAXNG_NOT_ALLOWED: case XML_RELAXNG_ELEMENT: case XML_RELAXNG_DATATYPE: case XML_RELAXNG_PARAM: case XML_RELAXNG_VALUE: case XML_RELAXNG_LIST: case XML_RELAXNG_ATTRIBUTE: ret = 0; break; case XML_RELAXNG_CHOICE:{ xmlRelaxNGDefinePtr list = define->content; while (list != NULL) { ret = xmlRelaxNGIsNullable(list); if (ret != 0) goto done; list = list->next; } ret = 0; break; } case XML_RELAXNG_START: case XML_RELAXNG_INTERLEAVE: case XML_RELAXNG_GROUP:{ xmlRelaxNGDefinePtr list = define->content; while (list != NULL) { ret = xmlRelaxNGIsNullable(list); if (ret != 1) goto done; list = list->next; } return (1); } default: return (-1); } done: if (ret == 0) define->dflags |= IS_NOT_NULLABLE; if (ret == 1) define->dflags |= IS_NULLABLE; return (ret); } /** * xmlRelaxNGIsBlank: * @str: a string * * Check if a string is ignorable c.f. 4.2. Whitespace * * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise */ static int xmlRelaxNGIsBlank(xmlChar * str) { if (str == NULL) return (1); while (*str != 0) { if (!(IS_BLANK_CH(*str))) return (0); str++; } return (1); } /** * xmlRelaxNGGetDataTypeLibrary: * @ctxt: a Relax-NG parser context * @node: the current data or value element * * Applies algorithm from 4.3. datatypeLibrary attribute * * Returns the datatypeLibary value or NULL if not found */ static xmlChar * xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) { xmlChar *ret, *escape; if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) { ret = xmlGetProp(node, BAD_CAST "datatypeLibrary"); if (ret != NULL) { if (ret[0] == 0) { xmlFree(ret); return (NULL); } escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?"); if (escape == NULL) { return (ret); } xmlFree(ret); return (escape); } } node = node->parent; while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) { ret = xmlGetProp(node, BAD_CAST "datatypeLibrary"); if (ret != NULL) { if (ret[0] == 0) { xmlFree(ret); return (NULL); } escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?"); if (escape == NULL) { return (ret); } xmlFree(ret); return (escape); } node = node->parent; } return (NULL); } /** * xmlRelaxNGParseValue: * @ctxt: a Relax-NG parser context * @node: the data node. * * parse the content of a RelaxNG value node. * * Returns the definition pointer or NULL in case of error */ static xmlRelaxNGDefinePtr xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGDefinePtr def = NULL; xmlRelaxNGTypeLibraryPtr lib = NULL; xmlChar *type; xmlChar *library; int success = 0; def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_VALUE; type = xmlGetProp(node, BAD_CAST "type"); if (type != NULL) { xmlRelaxNGNormExtSpace(type); if (xmlValidateNCName(type, 0)) { xmlRngPErr(ctxt, node, XML_RNGP_TYPE_VALUE, "value type '%s' is not an NCName\n", type, NULL); } library = xmlRelaxNGGetDataTypeLibrary(ctxt, node); if (library == NULL) library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0"); def->name = type; def->ns = library; lib = (xmlRelaxNGTypeLibraryPtr) xmlHashLookup(xmlRelaxNGRegisteredTypes, library); if (lib == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_UNKNOWN_TYPE_LIB, "Use of unregistered type library '%s'\n", library, NULL); def->data = NULL; } else { def->data = lib; if (lib->have == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_ERROR_TYPE_LIB, "Internal error with type library '%s': no 'have'\n", library, NULL); } else { success = lib->have(lib->data, def->name); if (success != 1) { xmlRngPErr(ctxt, node, XML_RNGP_TYPE_NOT_FOUND, "Error type '%s' is not exported by type library '%s'\n", def->name, library); } } } } if (node->children == NULL) { def->value = xmlStrdup(BAD_CAST ""); } else if (((node->children->type != XML_TEXT_NODE) && (node->children->type != XML_CDATA_SECTION_NODE)) || (node->children->next != NULL)) { xmlRngPErr(ctxt, node, XML_RNGP_TEXT_EXPECTED, "Expecting a single text value for
content\n", NULL, NULL); } else if (def != NULL) { def->value = xmlNodeGetContent(node); if (def->value == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_VALUE_NO_CONTENT, "Element
has no content\n", NULL, NULL); } else if ((lib != NULL) && (lib->check != NULL) && (success == 1)) { void *val = NULL; success = lib->check(lib->data, def->name, def->value, &val, node); if (success != 1) { xmlRngPErr(ctxt, node, XML_RNGP_INVALID_VALUE, "Value '%s' is not acceptable for type '%s'\n", def->value, def->name); } else { if (val != NULL) def->attrs = val; } } } return (def); } /** * xmlRelaxNGParseData: * @ctxt: a Relax-NG parser context * @node: the data node. * * parse the content of a RelaxNG data node. * * Returns the definition pointer or NULL in case of error */ static xmlRelaxNGDefinePtr xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGDefinePtr def = NULL, except; xmlRelaxNGDefinePtr param, lastparam = NULL; xmlRelaxNGTypeLibraryPtr lib; xmlChar *type; xmlChar *library; xmlNodePtr content; int tmp; type = xmlGetProp(node, BAD_CAST "type"); if (type == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_TYPE_MISSING, "data has no type\n", NULL, NULL); return (NULL); } xmlRelaxNGNormExtSpace(type); if (xmlValidateNCName(type, 0)) { xmlRngPErr(ctxt, node, XML_RNGP_TYPE_VALUE, "data type '%s' is not an NCName\n", type, NULL); } library = xmlRelaxNGGetDataTypeLibrary(ctxt, node); if (library == NULL) library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0"); def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) { xmlFree(type); return (NULL); } def->type = XML_RELAXNG_DATATYPE; def->name = type; def->ns = library; lib = (xmlRelaxNGTypeLibraryPtr) xmlHashLookup(xmlRelaxNGRegisteredTypes, library); if (lib == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_UNKNOWN_TYPE_LIB, "Use of unregistered type library '%s'\n", library, NULL); def->data = NULL; } else { def->data = lib; if (lib->have == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_ERROR_TYPE_LIB, "Internal error with type library '%s': no 'have'\n", library, NULL); } else { tmp = lib->have(lib->data, def->name); if (tmp != 1) { xmlRngPErr(ctxt, node, XML_RNGP_TYPE_NOT_FOUND, "Error type '%s' is not exported by type library '%s'\n", def->name, library); } else if ((xmlStrEqual (library, BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes")) && ((xmlStrEqual(def->name, BAD_CAST "IDREF")) || (xmlStrEqual(def->name, BAD_CAST "IDREFS")))) { ctxt->idref = 1; } } } content = node->children; /* * Handle optional params */ while (content != NULL) { if (!xmlStrEqual(content->name, BAD_CAST "param")) break; if (xmlStrEqual(library, BAD_CAST "http://relaxng.org/ns/structure/1.0")) { xmlRngPErr(ctxt, node, XML_RNGP_PARAM_FORBIDDEN, "Type library '%s' does not allow type parameters\n", library, NULL); content = content->next; while ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "param"))) content = content->next; } else { param = xmlRelaxNGNewDefine(ctxt, node); if (param != NULL) { param->type = XML_RELAXNG_PARAM; param->name = xmlGetProp(content, BAD_CAST "name"); if (param->name == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_PARAM_NAME_MISSING, "param has no name\n", NULL, NULL); } param->value = xmlNodeGetContent(content); if (lastparam == NULL) { def->attrs = lastparam = param; } else { lastparam->next = param; lastparam = param; } if (lib != NULL) { } } content = content->next; } } /* * Handle optional except */ if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) { xmlNodePtr child; xmlRelaxNGDefinePtr tmp2, last = NULL; except = xmlRelaxNGNewDefine(ctxt, node); if (except == NULL) { return (def); } except->type = XML_RELAXNG_EXCEPT; child = content->children; def->content = except; if (child == NULL) { xmlRngPErr(ctxt, content, XML_RNGP_EXCEPT_NO_CONTENT, "except has no content\n", NULL, NULL); } while (child != NULL) { tmp2 = xmlRelaxNGParsePattern(ctxt, child); if (tmp2 != NULL) { if (last == NULL) { except->content = last = tmp2; } else { last->next = tmp2; last = tmp2; } } child = child->next; } content = content->next; } /* * Check there is no unhandled data */ if (content != NULL) { xmlRngPErr(ctxt, content, XML_RNGP_DATA_CONTENT, "Element data has unexpected content %s\n", content->name, NULL); } return (def); } static const xmlChar *invalidName = BAD_CAST "\1"; /** * xmlRelaxNGCompareNameClasses: * @defs1: the first element/attribute defs * @defs2: the second element/attribute defs * @name: the restriction on the name * @ns: the restriction on the namespace * * Compare the 2 lists of element definitions. The comparison is * that if both lists do not accept the same QNames, it returns 1 * If the 2 lists can accept the same QName the comparison returns 0 * * Returns 1 disttinct, 0 if equal */ static int xmlRelaxNGCompareNameClasses(xmlRelaxNGDefinePtr def1, xmlRelaxNGDefinePtr def2) { int ret = 1; xmlNode node; xmlNs ns; xmlRelaxNGValidCtxt ctxt; memset(&ctxt, 0, sizeof(xmlRelaxNGValidCtxt)); ctxt.flags = FLAGS_IGNORABLE | FLAGS_NOERROR; if ((def1->type == XML_RELAXNG_ELEMENT) || (def1->type == XML_RELAXNG_ATTRIBUTE)) { if (def2->type == XML_RELAXNG_TEXT) return (1); if (def1->name != NULL) { node.name = def1->name; } else { node.name = invalidName; } if (def1->ns != NULL) { if (def1->ns[0] == 0) { node.ns = NULL; } else { node.ns = &ns; ns.href = def1->ns; } } else { node.ns = NULL; } if (xmlRelaxNGElementMatch(&ctxt, def2, &node)) { if (def1->nameClass != NULL) { ret = xmlRelaxNGCompareNameClasses(def1->nameClass, def2); } else { ret = 0; } } else { ret = 1; } } else if (def1->type == XML_RELAXNG_TEXT) { if (def2->type == XML_RELAXNG_TEXT) return (0); return (1); } else if (def1->type == XML_RELAXNG_EXCEPT) { TODO ret = 0; } else { TODO ret = 0; } if (ret == 0) return (ret); if ((def2->type == XML_RELAXNG_ELEMENT) || (def2->type == XML_RELAXNG_ATTRIBUTE)) { if (def2->name != NULL) { node.name = def2->name; } else { node.name = invalidName; } node.ns = &ns; if (def2->ns != NULL) { if (def2->ns[0] == 0) { node.ns = NULL; } else { ns.href = def2->ns; } } else { ns.href = invalidName; } if (xmlRelaxNGElementMatch(&ctxt, def1, &node)) { if (def2->nameClass != NULL) { ret = xmlRelaxNGCompareNameClasses(def2->nameClass, def1); } else { ret = 0; } } else { ret = 1; } } else { TODO ret = 0; } return (ret); } /** * xmlRelaxNGCompareElemDefLists: * @ctxt: a Relax-NG parser context * @defs1: the first list of element/attribute defs * @defs2: the second list of element/attribute defs * * Compare the 2 lists of element or attribute definitions. The comparison * is that if both lists do not accept the same QNames, it returns 1 * If the 2 lists can accept the same QName the comparison returns 0 * * Returns 1 disttinct, 0 if equal */ static int xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlRelaxNGDefinePtr * def1, xmlRelaxNGDefinePtr * def2) { xmlRelaxNGDefinePtr *basedef2 = def2; if ((def1 == NULL) || (def2 == NULL)) return (1); if ((*def1 == NULL) || (*def2 == NULL)) return (1); while (*def1 != NULL) { while ((*def2) != NULL) { if (xmlRelaxNGCompareNameClasses(*def1, *def2) == 0) return (0); def2++; } def2 = basedef2; def1++; } return (1); } /** * xmlRelaxNGGenerateAttributes: * @ctxt: a Relax-NG parser context * @def: the definition definition * * Check if the definition can only generate attributes * * Returns 1 if yes, 0 if no and -1 in case of error. */ static int xmlRelaxNGGenerateAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def) { xmlRelaxNGDefinePtr parent, cur, tmp; /* * Don't run that check in case of error. Infinite recursion * becomes possible. */ if (ctxt->nbErrors != 0) return (-1); parent = NULL; cur = def; while (cur != NULL) { if ((cur->type == XML_RELAXNG_ELEMENT) || (cur->type == XML_RELAXNG_TEXT) || (cur->type == XML_RELAXNG_DATATYPE) || (cur->type == XML_RELAXNG_PARAM) || (cur->type == XML_RELAXNG_LIST) || (cur->type == XML_RELAXNG_VALUE) || (cur->type == XML_RELAXNG_EMPTY)) return (0); if ((cur->type == XML_RELAXNG_CHOICE) || (cur->type == XML_RELAXNG_INTERLEAVE) || (cur->type == XML_RELAXNG_GROUP) || (cur->type == XML_RELAXNG_ONEORMORE) || (cur->type == XML_RELAXNG_ZEROORMORE) || (cur->type == XML_RELAXNG_OPTIONAL) || (cur->type == XML_RELAXNG_PARENTREF) || (cur->type == XML_RELAXNG_EXTERNALREF) || (cur->type == XML_RELAXNG_REF) || (cur->type == XML_RELAXNG_DEF)) { if (cur->content != NULL) { parent = cur; cur = cur->content; tmp = cur; while (tmp != NULL) { tmp->parent = parent; tmp = tmp->next; } continue; } } if (cur == def) break; if (cur->next != NULL) { cur = cur->next; continue; } do { cur = cur->parent; if (cur == NULL) break; if (cur == def) return (1); if (cur->next != NULL) { cur = cur->next; break; } } while (cur != NULL); } return (1); } /** * xmlRelaxNGGetElements: * @ctxt: a Relax-NG parser context * @def: the definition definition * @eora: gather elements (0) or attributes (1) * * Compute the list of top elements a definition can generate * * Returns a list of elements or NULL if none was found. */ static xmlRelaxNGDefinePtr * xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def, int eora) { xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp; int len = 0; int max = 0; /* * Don't run that check in case of error. Infinite recursion * becomes possible. */ if (ctxt->nbErrors != 0) return (NULL); parent = NULL; cur = def; while (cur != NULL) { if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) || (cur->type == XML_RELAXNG_TEXT))) || ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE))) { if (ret == NULL) { max = 10; ret = (xmlRelaxNGDefinePtr *) xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr)); if (ret == NULL) { xmlRngPErrMemory(ctxt, "getting element list\n"); return (NULL); } } else if (max <= len) { xmlRelaxNGDefinePtr *temp; max *= 2; temp = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr)); if (temp == NULL) { xmlRngPErrMemory(ctxt, "getting element list\n"); xmlFree(ret); return (NULL); } ret = temp; } ret[len++] = cur; ret[len] = NULL; } else if ((cur->type == XML_RELAXNG_CHOICE) || (cur->type == XML_RELAXNG_INTERLEAVE) || (cur->type == XML_RELAXNG_GROUP) || (cur->type == XML_RELAXNG_ONEORMORE) || (cur->type == XML_RELAXNG_ZEROORMORE) || (cur->type == XML_RELAXNG_OPTIONAL) || (cur->type == XML_RELAXNG_PARENTREF) || (cur->type == XML_RELAXNG_REF) || (cur->type == XML_RELAXNG_DEF) || (cur->type == XML_RELAXNG_EXTERNALREF)) { /* * Don't go within elements or attributes or string values. * Just gather the element top list */ if (cur->content != NULL) { parent = cur; cur = cur->content; tmp = cur; while (tmp != NULL) { tmp->parent = parent; tmp = tmp->next; } continue; } } if (cur == def) break; if (cur->next != NULL) { cur = cur->next; continue; } do { cur = cur->parent; if (cur == NULL) break; if (cur == def) return (ret); if (cur->next != NULL) { cur = cur->next; break; } } while (cur != NULL); } return (ret); } /** * xmlRelaxNGCheckChoiceDeterminism: * @ctxt: a Relax-NG parser context * @def: the choice definition * * Also used to find indeterministic pattern in choice */ static void xmlRelaxNGCheckChoiceDeterminism(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def) { xmlRelaxNGDefinePtr **list; xmlRelaxNGDefinePtr cur; int nbchild = 0, i, j, ret; int is_nullable = 0; int is_indeterminist = 0; xmlHashTablePtr triage = NULL; int is_triable = 1; if ((def == NULL) || (def->type != XML_RELAXNG_CHOICE)) return; if (def->dflags & IS_PROCESSED) return; /* * Don't run that check in case of error. Infinite recursion * becomes possible. */ if (ctxt->nbErrors != 0) return; is_nullable = xmlRelaxNGIsNullable(def); cur = def->content; while (cur != NULL) { nbchild++; cur = cur->next; } list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild * sizeof(xmlRelaxNGDefinePtr *)); if (list == NULL) { xmlRngPErrMemory(ctxt, "building choice\n"); return; } i = 0; /* * a bit strong but safe */ if (is_nullable == 0) { triage = xmlHashCreate(10); } else { is_triable = 0; } cur = def->content; while (cur != NULL) { list[i] = xmlRelaxNGGetElements(ctxt, cur, 0); if ((list[i] == NULL) || (list[i][0] == NULL)) { is_triable = 0; } else if (is_triable == 1) { xmlRelaxNGDefinePtr *tmp; int res; tmp = list[i]; while ((*tmp != NULL) && (is_triable == 1)) { if ((*tmp)->type == XML_RELAXNG_TEXT) { res = xmlHashAddEntry2(triage, BAD_CAST "#text", NULL, (void *) cur); if (res != 0) is_triable = -1; } else if (((*tmp)->type == XML_RELAXNG_ELEMENT) && ((*tmp)->name != NULL)) { if (((*tmp)->ns == NULL) || ((*tmp)->ns[0] == 0)) res = xmlHashAddEntry2(triage, (*tmp)->name, NULL, (void *) cur); else res = xmlHashAddEntry2(triage, (*tmp)->name, (*tmp)->ns, (void *) cur); if (res != 0) is_triable = -1; } else if ((*tmp)->type == XML_RELAXNG_ELEMENT) { if (((*tmp)->ns == NULL) || ((*tmp)->ns[0] == 0)) res = xmlHashAddEntry2(triage, BAD_CAST "#any", NULL, (void *) cur); else res = xmlHashAddEntry2(triage, BAD_CAST "#any", (*tmp)->ns, (void *) cur); if (res != 0) is_triable = -1; } else { is_triable = -1; } tmp++; } } i++; cur = cur->next; } for (i = 0; i < nbchild; i++) { if (list[i] == NULL) continue; for (j = 0; j < i; j++) { if (list[j] == NULL) continue; ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]); if (ret == 0) { is_indeterminist = 1; } } } for (i = 0; i < nbchild; i++) { if (list[i] != NULL) xmlFree(list[i]); } xmlFree(list); if (is_indeterminist) { def->dflags |= IS_INDETERMINIST; } if (is_triable == 1) { def->dflags |= IS_TRIABLE; def->data = triage; } else if (triage != NULL) { xmlHashFree(triage, NULL); } def->dflags |= IS_PROCESSED; } /** * xmlRelaxNGCheckGroupAttrs: * @ctxt: a Relax-NG parser context * @def: the group definition * * Detects violations of rule 7.3 */ static void xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr def) { xmlRelaxNGDefinePtr **list; xmlRelaxNGDefinePtr cur; int nbchild = 0, i, j, ret; if ((def == NULL) || ((def->type != XML_RELAXNG_GROUP) && (def->type != XML_RELAXNG_ELEMENT))) return; if (def->dflags & IS_PROCESSED) return; /* * Don't run that check in case of error. Infinite recursion * becomes possible. */ if (ctxt->nbErrors != 0) return; cur = def->attrs; while (cur != NULL) { nbchild++; cur = cur->next; } cur = def->content; while (cur != NULL) { nbchild++; cur = cur->next; } list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild * sizeof(xmlRelaxNGDefinePtr *)); if (list == NULL) { xmlRngPErrMemory(ctxt, "building group\n"); return; } i = 0; cur = def->attrs; while (cur != NULL) { list[i] = xmlRelaxNGGetElements(ctxt, cur, 1); i++; cur = cur->next; } cur = def->content; while (cur != NULL) { list[i] = xmlRelaxNGGetElements(ctxt, cur, 1); i++; cur = cur->next; } for (i = 0; i < nbchild; i++) { if (list[i] == NULL) continue; for (j = 0; j < i; j++) { if (list[j] == NULL) continue; ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]); if (ret == 0) { xmlRngPErr(ctxt, def->node, XML_RNGP_GROUP_ATTR_CONFLICT, "Attributes conflicts in group\n", NULL, NULL); } } } for (i = 0; i < nbchild; i++) { if (list[i] != NULL) xmlFree(list[i]); } xmlFree(list); def->dflags |= IS_PROCESSED; } /** * xmlRelaxNGComputeInterleaves: * @def: the interleave definition * @ctxt: a Relax-NG parser context * @name: the definition name * * A lot of work for preprocessing interleave definitions * is potentially needed to get a decent execution speed at runtime * - trying to get a total order on the element nodes generated * by the interleaves, order the list of interleave definitions * following that order. * - if
is used to handle mixed content, it is better to * flag this in the define and simplify the runtime checking * algorithm */ static void xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def, xmlRelaxNGParserCtxtPtr ctxt, xmlChar * name ATTRIBUTE_UNUSED) { xmlRelaxNGDefinePtr cur, *tmp; xmlRelaxNGPartitionPtr partitions = NULL; xmlRelaxNGInterleaveGroupPtr *groups = NULL; xmlRelaxNGInterleaveGroupPtr group; int i, j, ret, res; int nbgroups = 0; int nbchild = 0; int is_mixed = 0; int is_determinist = 1; /* * Don't run that check in case of error. Infinite recursion * becomes possible. */ if (ctxt->nbErrors != 0) return; #ifdef DEBUG_INTERLEAVE xmlGenericError(xmlGenericErrorContext, "xmlRelaxNGComputeInterleaves(%s)\n", name); #endif cur = def->content; while (cur != NULL) { nbchild++; cur = cur->next; } #ifdef DEBUG_INTERLEAVE xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild); #endif groups = (xmlRelaxNGInterleaveGroupPtr *) xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr)); if (groups == NULL) goto error; cur = def->content; while (cur != NULL) { groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr) xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup)); if (groups[nbgroups] == NULL) goto error; if (cur->type == XML_RELAXNG_TEXT) is_mixed++; groups[nbgroups]->rule = cur; groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 0); groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1); nbgroups++; cur = cur->next; } #ifdef DEBUG_INTERLEAVE xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups); #endif /* * Let's check that all rules makes a partitions according to 7.4 */ partitions = (xmlRelaxNGPartitionPtr) xmlMalloc(sizeof(xmlRelaxNGPartition)); if (partitions == NULL) goto error; memset(partitions, 0, sizeof(xmlRelaxNGPartition)); partitions->nbgroups = nbgroups; partitions->triage = xmlHashCreate(nbgroups); for (i = 0; i < nbgroups; i++) { group = groups[i]; for (j = i + 1; j < nbgroups; j++) { if (groups[j] == NULL) continue; ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs, groups[j]->defs); if (ret == 0) { xmlRngPErr(ctxt, def->node, XML_RNGP_ELEM_TEXT_CONFLICT, "Element or text conflicts in interleave\n", NULL, NULL); } ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs, groups[j]->attrs); if (ret == 0) { xmlRngPErr(ctxt, def->node, XML_RNGP_ATTR_CONFLICT, "Attributes conflicts in interleave\n", NULL, NULL); } } tmp = group->defs; if ((tmp != NULL) && (*tmp != NULL)) { while (*tmp != NULL) { if ((*tmp)->type == XML_RELAXNG_TEXT) { res = xmlHashAddEntry2(partitions->triage, BAD_CAST "#text", NULL, (void *) (long) (i + 1)); if (res != 0) is_determinist = -1; } else if (((*tmp)->type == XML_RELAXNG_ELEMENT) && ((*tmp)->name != NULL)) { if (((*tmp)->ns == NULL) || ((*tmp)->ns[0] == 0)) res = xmlHashAddEntry2(partitions->triage, (*tmp)->name, NULL, (void *) (long) (i + 1)); else res = xmlHashAddEntry2(partitions->triage, (*tmp)->name, (*tmp)->ns, (void *) (long) (i + 1)); if (res != 0) is_determinist = -1; } else if ((*tmp)->type == XML_RELAXNG_ELEMENT) { if (((*tmp)->ns == NULL) || ((*tmp)->ns[0] == 0)) res = xmlHashAddEntry2(partitions->triage, BAD_CAST "#any", NULL, (void *) (long) (i + 1)); else res = xmlHashAddEntry2(partitions->triage, BAD_CAST "#any", (*tmp)->ns, (void *) (long) (i + 1)); if ((*tmp)->nameClass != NULL) is_determinist = 2; if (res != 0) is_determinist = -1; } else { is_determinist = -1; } tmp++; } } else { is_determinist = 0; } } partitions->groups = groups; /* * and save the partition list back in the def */ def->data = partitions; if (is_mixed != 0) def->dflags |= IS_MIXED; if (is_determinist == 1) partitions->flags = IS_DETERMINIST; if (is_determinist == 2) partitions->flags = IS_DETERMINIST | IS_NEEDCHECK; return; error: xmlRngPErrMemory(ctxt, "in interleave computation\n"); if (groups != NULL) { for (i = 0; i < nbgroups; i++) if (groups[i] != NULL) { if (groups[i]->defs != NULL) xmlFree(groups[i]->defs); xmlFree(groups[i]); } xmlFree(groups); } xmlRelaxNGFreePartition(partitions); } /** * xmlRelaxNGParseInterleave: * @ctxt: a Relax-NG parser context * @node: the data node. * * parse the content of a RelaxNG interleave node. * * Returns the definition pointer or NULL in case of error */ static xmlRelaxNGDefinePtr xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGDefinePtr def = NULL; xmlRelaxNGDefinePtr last = NULL, cur; xmlNodePtr child; def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) { return (NULL); } def->type = XML_RELAXNG_INTERLEAVE; if (ctxt->interleaves == NULL) ctxt->interleaves = xmlHashCreate(10); if (ctxt->interleaves == NULL) { xmlRngPErrMemory(ctxt, "create interleaves\n"); } else { char name[32]; snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++); if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) { xmlRngPErr(ctxt, node, XML_RNGP_INTERLEAVE_ADD, "Failed to add %s to hash table\n", (const xmlChar *) name, NULL); } } child = node->children; if (child == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_INTERLEAVE_NO_CONTENT, "Element interleave is empty\n", NULL, NULL); } while (child != NULL) { if (IS_RELAXNG(child, "element")) { cur = xmlRelaxNGParseElement(ctxt, child); } else { cur = xmlRelaxNGParsePattern(ctxt, child); } if (cur != NULL) { cur->parent = def; if (last == NULL) { def->content = last = cur; } else { last->next = cur; last = cur; } } child = child->next; } return (def); } /** * xmlRelaxNGParseInclude: * @ctxt: a Relax-NG parser context * @node: the include node * * Integrate the content of an include node in the current grammar * * Returns 0 in case of success or -1 in case of error */ static int xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGIncludePtr incl; xmlNodePtr root; int ret = 0, tmp; incl = node->psvi; if (incl == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_INCLUDE_EMPTY, "Include node has no data\n", NULL, NULL); return (-1); } root = xmlDocGetRootElement(incl->doc); if (root == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EMPTY, "Include document is empty\n", NULL, NULL); return (-1); } if (!xmlStrEqual(root->name, BAD_CAST "grammar")) { xmlRngPErr(ctxt, node, XML_RNGP_GRAMMAR_MISSING, "Include document root is not a grammar\n", NULL, NULL); return (-1); } /* * Merge the definition from both the include and the internal list */ if (root->children != NULL) { tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children); if (tmp != 0) ret = -1; } if (node->children != NULL) { tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children); if (tmp != 0) ret = -1; } return (ret); } /** * xmlRelaxNGParseDefine: * @ctxt: a Relax-NG parser context * @node: the define node * * parse the content of a RelaxNG define element node. * * Returns 0 in case of success or -1 in case of error */ static int xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlChar *name; int ret = 0, tmp; xmlRelaxNGDefinePtr def; const xmlChar *olddefine; name = xmlGetProp(node, BAD_CAST "name"); if (name == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_DEFINE_NAME_MISSING, "define has no name\n", NULL, NULL); } else { xmlRelaxNGNormExtSpace(name); if (xmlValidateNCName(name, 0)) { xmlRngPErr(ctxt, node, XML_RNGP_INVALID_DEFINE_NAME, "define name '%s' is not an NCName\n", name, NULL); } def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) { xmlFree(name); return (-1); } def->type = XML_RELAXNG_DEF; def->name = name; if (node->children == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_DEFINE_EMPTY, "define has no children\n", NULL, NULL); } else { olddefine = ctxt->define; ctxt->define = name; def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0); ctxt->define = olddefine; } if (ctxt->grammar->defs == NULL) ctxt->grammar->defs = xmlHashCreate(10); if (ctxt->grammar->defs == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_DEFINE_CREATE_FAILED, "Could not create definition hash\n", NULL, NULL); ret = -1; } else { tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def); if (tmp < 0) { xmlRelaxNGDefinePtr prev; prev = xmlHashLookup(ctxt->grammar->defs, name); if (prev == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_DEFINE_CREATE_FAILED, "Internal error on define aggregation of %s\n", name, NULL); ret = -1; } else { while (prev->nextHash != NULL) prev = prev->nextHash; prev->nextHash = def; } } } } return (ret); } /** * xmlRelaxNGProcessExternalRef: * @ctxt: the parser context * @node: the externlRef node * * Process and compile an externlRef node * * Returns the xmlRelaxNGDefinePtr or NULL in case of error */ static xmlRelaxNGDefinePtr xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGDocumentPtr docu; xmlNodePtr root, tmp; xmlChar *ns; int newNs = 0, oldflags; xmlRelaxNGDefinePtr def; docu = node->psvi; if (docu != NULL) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_EXTERNALREF; if (docu->content == NULL) { /* * Then do the parsing for good */ root = xmlDocGetRootElement(docu->doc); if (root == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EXTERNALREF_EMTPY, "xmlRelaxNGParse: %s is empty\n", ctxt->URL, NULL); return (NULL); } /* * ns transmission rules */ ns = xmlGetProp(root, BAD_CAST "ns"); if (ns == NULL) { tmp = node; while ((tmp != NULL) && (tmp->type == XML_ELEMENT_NODE)) { ns = xmlGetProp(tmp, BAD_CAST "ns"); if (ns != NULL) { break; } tmp = tmp->parent; } if (ns != NULL) { xmlSetProp(root, BAD_CAST "ns", ns); newNs = 1; xmlFree(ns); } } else { xmlFree(ns); } /* * Parsing to get a precompiled schemas. */ oldflags = ctxt->flags; ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF; docu->schema = xmlRelaxNGParseDocument(ctxt, root); ctxt->flags = oldflags; if ((docu->schema != NULL) && (docu->schema->topgrammar != NULL)) { docu->content = docu->schema->topgrammar->start; } /* * the externalRef may be reused in a different ns context */ if (newNs == 1) { xmlUnsetProp(root, BAD_CAST "ns"); } } def->content = docu->content; } else { def = NULL; } return (def); } /** * xmlRelaxNGParsePattern: * @ctxt: a Relax-NG parser context * @node: the pattern node. * * parse the content of a RelaxNG pattern node. * * Returns the definition pointer or NULL in case of error or if no * pattern is generated. */ static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGDefinePtr def = NULL; if (node == NULL) { return (NULL); } if (IS_RELAXNG(node, "element")) { def = xmlRelaxNGParseElement(ctxt, node); } else if (IS_RELAXNG(node, "attribute")) { def = xmlRelaxNGParseAttribute(ctxt, node); } else if (IS_RELAXNG(node, "empty")) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_EMPTY; if (node->children != NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_NOT_EMPTY, "empty: had a child node\n", NULL, NULL); } } else if (IS_RELAXNG(node, "text")) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_TEXT; if (node->children != NULL) { xmlRngPErr(ctxt, node, XML_RNGP_TEXT_HAS_CHILD, "text: had a child node\n", NULL, NULL); } } else if (IS_RELAXNG(node, "zeroOrMore")) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_ZEROORMORE; if (node->children == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT, "Element %s is empty\n", node->name, NULL); } else { def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1); } } else if (IS_RELAXNG(node, "oneOrMore")) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_ONEORMORE; if (node->children == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT, "Element %s is empty\n", node->name, NULL); } else { def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1); } } else if (IS_RELAXNG(node, "optional")) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_OPTIONAL; if (node->children == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT, "Element %s is empty\n", node->name, NULL); } else { def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1); } } else if (IS_RELAXNG(node, "choice")) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_CHOICE; if (node->children == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT, "Element %s is empty\n", node->name, NULL); } else { def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0); } } else if (IS_RELAXNG(node, "group")) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_GROUP; if (node->children == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT, "Element %s is empty\n", node->name, NULL); } else { def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0); } } else if (IS_RELAXNG(node, "ref")) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_REF; def->name = xmlGetProp(node, BAD_CAST "name"); if (def->name == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_REF_NO_NAME, "ref has no name\n", NULL, NULL); } else { xmlRelaxNGNormExtSpace(def->name); if (xmlValidateNCName(def->name, 0)) { xmlRngPErr(ctxt, node, XML_RNGP_REF_NAME_INVALID, "ref name '%s' is not an NCName\n", def->name, NULL); } } if (node->children != NULL) { xmlRngPErr(ctxt, node, XML_RNGP_REF_NOT_EMPTY, "ref is not empty\n", NULL, NULL); } if (ctxt->grammar->refs == NULL) ctxt->grammar->refs = xmlHashCreate(10); if (ctxt->grammar->refs == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_REF_CREATE_FAILED, "Could not create references hash\n", NULL, NULL); def = NULL; } else { int tmp; tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def); if (tmp < 0) { xmlRelaxNGDefinePtr prev; prev = (xmlRelaxNGDefinePtr) xmlHashLookup(ctxt->grammar->refs, def->name); if (prev == NULL) { if (def->name != NULL) { xmlRngPErr(ctxt, node, XML_RNGP_REF_CREATE_FAILED, "Error refs definitions '%s'\n", def->name, NULL); } else { xmlRngPErr(ctxt, node, XML_RNGP_REF_CREATE_FAILED, "Error refs definitions\n", NULL, NULL); } def = NULL; } else { def->nextHash = prev->nextHash; prev->nextHash = def; } } } } else if (IS_RELAXNG(node, "data")) { def = xmlRelaxNGParseData(ctxt, node); } else if (IS_RELAXNG(node, "value")) { def = xmlRelaxNGParseValue(ctxt, node); } else if (IS_RELAXNG(node, "list")) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_LIST; if (node->children == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT, "Element %s is empty\n", node->name, NULL); } else { def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0); } } else if (IS_RELAXNG(node, "interleave")) { def = xmlRelaxNGParseInterleave(ctxt, node); } else if (IS_RELAXNG(node, "externalRef")) { def = xmlRelaxNGProcessExternalRef(ctxt, node); } else if (IS_RELAXNG(node, "notAllowed")) { def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_NOT_ALLOWED; if (node->children != NULL) { xmlRngPErr(ctxt, node, XML_RNGP_NOTALLOWED_NOT_EMPTY, "xmlRelaxNGParse: notAllowed element is not empty\n", NULL, NULL); } } else if (IS_RELAXNG(node, "grammar")) { xmlRelaxNGGrammarPtr grammar, old; xmlRelaxNGGrammarPtr oldparent; #ifdef DEBUG_GRAMMAR xmlGenericError(xmlGenericErrorContext, "Found
pattern\n"); #endif oldparent = ctxt->parentgrammar; old = ctxt->grammar; ctxt->parentgrammar = old; grammar = xmlRelaxNGParseGrammar(ctxt, node->children); if (old != NULL) { ctxt->grammar = old; ctxt->parentgrammar = oldparent; #if 0 if (grammar != NULL) { grammar->next = old->next; old->next = grammar; } #endif } if (grammar != NULL) def = grammar->start; else def = NULL; } else if (IS_RELAXNG(node, "parentRef")) { if (ctxt->parentgrammar == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NO_PARENT, "Use of parentRef without a parent grammar\n", NULL, NULL); return (NULL); } def = xmlRelaxNGNewDefine(ctxt, node); if (def == NULL) return (NULL); def->type = XML_RELAXNG_PARENTREF; def->name = xmlGetProp(node, BAD_CAST "name"); if (def->name == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NO_NAME, "parentRef has no name\n", NULL, NULL); } else { xmlRelaxNGNormExtSpace(def->name); if (xmlValidateNCName(def->name, 0)) { xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NAME_INVALID, "parentRef name '%s' is not an NCName\n", def->name, NULL); } } if (node->children != NULL) { xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_NOT_EMPTY, "parentRef is not empty\n", NULL, NULL); } if (ctxt->parentgrammar->refs == NULL) ctxt->parentgrammar->refs = xmlHashCreate(10); if (ctxt->parentgrammar->refs == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_CREATE_FAILED, "Could not create references hash\n", NULL, NULL); def = NULL; } else if (def->name != NULL) { int tmp; tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def); if (tmp < 0) { xmlRelaxNGDefinePtr prev; prev = (xmlRelaxNGDefinePtr) xmlHashLookup(ctxt->parentgrammar->refs, def->name); if (prev == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_PARENTREF_CREATE_FAILED, "Internal error parentRef definitions '%s'\n", def->name, NULL); def = NULL; } else { def->nextHash = prev->nextHash; prev->nextHash = def; } } } } else if (IS_RELAXNG(node, "mixed")) { if (node->children == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EMPTY_CONSTRUCT, "Mixed is empty\n", NULL, NULL); def = NULL; } else { def = xmlRelaxNGParseInterleave(ctxt, node); if (def != NULL) { xmlRelaxNGDefinePtr tmp; if ((def->content != NULL) && (def->content->next != NULL)) { tmp = xmlRelaxNGNewDefine(ctxt, node); if (tmp != NULL) { tmp->type = XML_RELAXNG_GROUP; tmp->content = def->content; def->content = tmp; } } tmp = xmlRelaxNGNewDefine(ctxt, node); if (tmp == NULL) return (def); tmp->type = XML_RELAXNG_TEXT; tmp->next = def->content; def->content = tmp; } } } else { xmlRngPErr(ctxt, node, XML_RNGP_UNKNOWN_CONSTRUCT, "Unexpected node %s is not a pattern\n", node->name, NULL); def = NULL; } return (def); } /** * xmlRelaxNGParseAttribute: * @ctxt: a Relax-NG parser context * @node: the element node * * parse the content of a RelaxNG attribute node. * * Returns the definition pointer or NULL in case of error. */ static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGDefinePtr ret, cur; xmlNodePtr child; int old_flags; ret = xmlRelaxNGNewDefine(ctxt, node); if (ret == NULL) return (NULL); ret->type = XML_RELAXNG_ATTRIBUTE; ret->parent = ctxt->def; child = node->children; if (child == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_ATTRIBUTE_EMPTY, "xmlRelaxNGParseattribute: attribute has no children\n", NULL, NULL); return (ret); } old_flags = ctxt->flags; ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE; cur = xmlRelaxNGParseNameClass(ctxt, child, ret); if (cur != NULL) child = child->next; if (child != NULL) { cur = xmlRelaxNGParsePattern(ctxt, child); if (cur != NULL) { switch (cur->type) { case XML_RELAXNG_EMPTY: case XML_RELAXNG_NOT_ALLOWED: case XML_RELAXNG_TEXT: case XML_RELAXNG_ELEMENT: case XML_RELAXNG_DATATYPE: case XML_RELAXNG_VALUE: case XML_RELAXNG_LIST: case XML_RELAXNG_REF: case XML_RELAXNG_PARENTREF: case XML_RELAXNG_EXTERNALREF: case XML_RELAXNG_DEF: case XML_RELAXNG_ONEORMORE: case XML_RELAXNG_ZEROORMORE: case XML_RELAXNG_OPTIONAL: case XML_RELAXNG_CHOICE: case XML_RELAXNG_GROUP: case XML_RELAXNG_INTERLEAVE: case XML_RELAXNG_ATTRIBUTE: ret->content = cur; cur->parent = ret; break; case XML_RELAXNG_START: case XML_RELAXNG_PARAM: case XML_RELAXNG_EXCEPT: xmlRngPErr(ctxt, node, XML_RNGP_ATTRIBUTE_CONTENT, "attribute has invalid content\n", NULL, NULL); break; case XML_RELAXNG_NOOP: xmlRngPErr(ctxt, node, XML_RNGP_ATTRIBUTE_NOOP, "RNG Internal error, noop found in attribute\n", NULL, NULL); break; } } child = child->next; } if (child != NULL) { xmlRngPErr(ctxt, node, XML_RNGP_ATTRIBUTE_CHILDREN, "attribute has multiple children\n", NULL, NULL); } ctxt->flags = old_flags; return (ret); } /** * xmlRelaxNGParseExceptNameClass: * @ctxt: a Relax-NG parser context * @node: the except node * @attr: 1 if within an attribute, 0 if within an element * * parse the content of a RelaxNG nameClass node. * * Returns the definition pointer or NULL in case of error. */ static xmlRelaxNGDefinePtr xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node, int attr) { xmlRelaxNGDefinePtr ret, cur, last = NULL; xmlNodePtr child; if (!IS_RELAXNG(node, "except")) { xmlRngPErr(ctxt, node, XML_RNGP_EXCEPT_MISSING, "Expecting an except node\n", NULL, NULL); return (NULL); } if (node->next != NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EXCEPT_MULTIPLE, "exceptNameClass allows only a single except node\n", NULL, NULL); } if (node->children == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_EXCEPT_EMPTY, "except has no content\n", NULL, NULL); return (NULL); } ret = xmlRelaxNGNewDefine(ctxt, node); if (ret == NULL) return (NULL); ret->type = XML_RELAXNG_EXCEPT; child = node->children; while (child != NULL) { cur = xmlRelaxNGNewDefine(ctxt, child); if (cur == NULL) break; if (attr) cur->type = XML_RELAXNG_ATTRIBUTE; else cur->type = XML_RELAXNG_ELEMENT; if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) { if (last == NULL) { ret->content = cur; } else { last->next = cur; } last = cur; } child = child->next; } return (ret); } /** * xmlRelaxNGParseNameClass: * @ctxt: a Relax-NG parser context * @node: the nameClass node * @def: the current definition * * parse the content of a RelaxNG nameClass node. * * Returns the definition pointer or NULL in case of error. */ static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node, xmlRelaxNGDefinePtr def) { xmlRelaxNGDefinePtr ret, tmp; xmlChar *val; ret = def; if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) || (IS_RELAXNG(node, "nsName"))) { if ((def->type != XML_RELAXNG_ELEMENT) && (def->type != XML_RELAXNG_ATTRIBUTE)) { ret = xmlRelaxNGNewDefine(ctxt, node); if (ret == NULL) return (NULL); ret->parent = def; if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) ret->type = XML_RELAXNG_ATTRIBUTE; else ret->type = XML_RELAXNG_ELEMENT; } } if (IS_RELAXNG(node, "name")) { val = xmlNodeGetContent(node); xmlRelaxNGNormExtSpace(val); if (xmlValidateNCName(val, 0)) { if (node->parent != NULL) xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_NAME, "Element %s name '%s' is not an NCName\n", node->parent->name, val); else xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_NAME, "name '%s' is not an NCName\n", val, NULL); } ret->name = val; val = xmlGetProp(node, BAD_CAST "ns"); ret->ns = val; if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) && (val != NULL) && (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) { xmlRngPErr(ctxt, node, XML_RNGP_XML_NS, "Attribute with namespace '%s' is not allowed\n", val, NULL); } if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) && (val != NULL) && (val[0] == 0) && (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) { xmlRngPErr(ctxt, node, XML_RNGP_XMLNS_NAME, "Attribute with QName 'xmlns' is not allowed\n", val, NULL); } } else if (IS_RELAXNG(node, "anyName")) { ret->name = NULL; ret->ns = NULL; if (node->children != NULL) { ret->nameClass = xmlRelaxNGParseExceptNameClass(ctxt, node->children, (def->type == XML_RELAXNG_ATTRIBUTE)); } } else if (IS_RELAXNG(node, "nsName")) { ret->name = NULL; ret->ns = xmlGetProp(node, BAD_CAST "ns"); if (ret->ns == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_NSNAME_NO_NS, "nsName has no ns attribute\n", NULL, NULL); } if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) && (ret->ns != NULL) && (xmlStrEqual (ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) { xmlRngPErr(ctxt, node, XML_RNGP_XML_NS, "Attribute with namespace '%s' is not allowed\n", ret->ns, NULL); } if (node->children != NULL) { ret->nameClass = xmlRelaxNGParseExceptNameClass(ctxt, node->children, (def->type == XML_RELAXNG_ATTRIBUTE)); } } else if (IS_RELAXNG(node, "choice")) { xmlNodePtr child; xmlRelaxNGDefinePtr last = NULL; ret = xmlRelaxNGNewDefine(ctxt, node); if (ret == NULL) return (NULL); ret->parent = def; ret->type = XML_RELAXNG_CHOICE; if (node->children == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_CHOICE_EMPTY, "Element choice is empty\n", NULL, NULL); } else { child = node->children; while (child != NULL) { tmp = xmlRelaxNGParseNameClass(ctxt, child, ret); if (tmp != NULL) { if (last == NULL) { last = ret->nameClass = tmp; } else { last->next = tmp; last = tmp; } } child = child->next; } } } else { xmlRngPErr(ctxt, node, XML_RNGP_CHOICE_CONTENT, "expecting name, anyName, nsName or choice : got %s\n", node->name, NULL); return (NULL); } if (ret != def) { if (def->nameClass == NULL) { def->nameClass = ret; } else { tmp = def->nameClass; while (tmp->next != NULL) { tmp = tmp->next; } tmp->next = ret; } } return (ret); } /** * xmlRelaxNGParseElement: * @ctxt: a Relax-NG parser context * @node: the element node * * parse the content of a RelaxNG element node. * * Returns the definition pointer or NULL in case of error. */ static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGDefinePtr ret, cur, last; xmlNodePtr child; const xmlChar *olddefine; ret = xmlRelaxNGNewDefine(ctxt, node); if (ret == NULL) return (NULL); ret->type = XML_RELAXNG_ELEMENT; ret->parent = ctxt->def; child = node->children; if (child == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_EMPTY, "xmlRelaxNGParseElement: element has no children\n", NULL, NULL); return (ret); } cur = xmlRelaxNGParseNameClass(ctxt, child, ret); if (cur != NULL) child = child->next; if (child == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_NO_CONTENT, "xmlRelaxNGParseElement: element has no content\n", NULL, NULL); return (ret); } olddefine = ctxt->define; ctxt->define = NULL; last = NULL; while (child != NULL) { cur = xmlRelaxNGParsePattern(ctxt, child); if (cur != NULL) { cur->parent = ret; switch (cur->type) { case XML_RELAXNG_EMPTY: case XML_RELAXNG_NOT_ALLOWED: case XML_RELAXNG_TEXT: case XML_RELAXNG_ELEMENT: case XML_RELAXNG_DATATYPE: case XML_RELAXNG_VALUE: case XML_RELAXNG_LIST: case XML_RELAXNG_REF: case XML_RELAXNG_PARENTREF: case XML_RELAXNG_EXTERNALREF: case XML_RELAXNG_DEF: case XML_RELAXNG_ZEROORMORE: case XML_RELAXNG_ONEORMORE: case XML_RELAXNG_OPTIONAL: case XML_RELAXNG_CHOICE: case XML_RELAXNG_GROUP: case XML_RELAXNG_INTERLEAVE: if (last == NULL) { ret->content = last = cur; } else { if ((last->type == XML_RELAXNG_ELEMENT) && (ret->content == last)) { ret->content = xmlRelaxNGNewDefine(ctxt, node); if (ret->content != NULL) { ret->content->type = XML_RELAXNG_GROUP; ret->content->content = last; } else { ret->content = last; } } last->next = cur; last = cur; } break; case XML_RELAXNG_ATTRIBUTE: cur->next = ret->attrs; ret->attrs = cur; break; case XML_RELAXNG_START: xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_CONTENT, "RNG Internal error, start found in element\n", NULL, NULL); break; case XML_RELAXNG_PARAM: xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_CONTENT, "RNG Internal error, param found in element\n", NULL, NULL); break; case XML_RELAXNG_EXCEPT: xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_CONTENT, "RNG Internal error, except found in element\n", NULL, NULL); break; case XML_RELAXNG_NOOP: xmlRngPErr(ctxt, node, XML_RNGP_ELEMENT_CONTENT, "RNG Internal error, noop found in element\n", NULL, NULL); break; } } child = child->next; } ctxt->define = olddefine; return (ret); } /** * xmlRelaxNGParsePatterns: * @ctxt: a Relax-NG parser context * @nodes: list of nodes * @group: use an implicit
for elements * * parse the content of a RelaxNG start node. * * Returns the definition pointer or NULL in case of error. */ static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group) { xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent; parent = ctxt->def; while (nodes != NULL) { if (IS_RELAXNG(nodes, "element")) { cur = xmlRelaxNGParseElement(ctxt, nodes); if (def == NULL) { def = last = cur; } else { if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) && (def == last)) { def = xmlRelaxNGNewDefine(ctxt, nodes); def->type = XML_RELAXNG_GROUP; def->content = last; } last->next = cur; last = cur; } cur->parent = parent; } else { cur = xmlRelaxNGParsePattern(ctxt, nodes); if (cur != NULL) { if (def == NULL) { def = last = cur; } else { last->next = cur; last = cur; } } } nodes = nodes->next; } return (def); } /** * xmlRelaxNGParseStart: * @ctxt: a Relax-NG parser context * @nodes: start children nodes * * parse the content of a RelaxNG start node. * * Returns 0 in case of success, -1 in case of error */ static int xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) { int ret = 0; xmlRelaxNGDefinePtr def = NULL, last; if (nodes == NULL) { xmlRngPErr(ctxt, nodes, XML_RNGP_START_EMPTY, "start has no children\n", NULL, NULL); return (-1); } if (IS_RELAXNG(nodes, "empty")) { def = xmlRelaxNGNewDefine(ctxt, nodes); if (def == NULL) return (-1); def->type = XML_RELAXNG_EMPTY; if (nodes->children != NULL) { xmlRngPErr(ctxt, nodes, XML_RNGP_EMPTY_CONTENT, "element empty is not empty\n", NULL, NULL); } } else if (IS_RELAXNG(nodes, "notAllowed")) { def = xmlRelaxNGNewDefine(ctxt, nodes); if (def == NULL) return (-1); def->type = XML_RELAXNG_NOT_ALLOWED; if (nodes->children != NULL) { xmlRngPErr(ctxt, nodes, XML_RNGP_NOTALLOWED_NOT_EMPTY, "element notAllowed is not empty\n", NULL, NULL); } } else { def = xmlRelaxNGParsePatterns(ctxt, nodes, 1); } if (ctxt->grammar->start != NULL) { last = ctxt->grammar->start; while (last->next != NULL) last = last->next; last->next = def; } else { ctxt->grammar->start = def; } nodes = nodes->next; if (nodes != NULL) { xmlRngPErr(ctxt, nodes, XML_RNGP_START_CONTENT, "start more than one children\n", NULL, NULL); return (-1); } return (ret); } /** * xmlRelaxNGParseGrammarContent: * @ctxt: a Relax-NG parser context * @nodes: grammar children nodes * * parse the content of a RelaxNG grammar node. * * Returns 0 in case of success, -1 in case of error */ static int xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) { int ret = 0, tmp; if (nodes == NULL) { xmlRngPErr(ctxt, nodes, XML_RNGP_GRAMMAR_EMPTY, "grammar has no children\n", NULL, NULL); return (-1); } while (nodes != NULL) { if (IS_RELAXNG(nodes, "start")) { if (nodes->children == NULL) { xmlRngPErr(ctxt, nodes, XML_RNGP_START_EMPTY, "start has no children\n", NULL, NULL); } else { tmp = xmlRelaxNGParseStart(ctxt, nodes->children); if (tmp != 0) ret = -1; } } else if (IS_RELAXNG(nodes, "define")) { tmp = xmlRelaxNGParseDefine(ctxt, nodes); if (tmp != 0) ret = -1; } else if (IS_RELAXNG(nodes, "include")) { tmp = xmlRelaxNGParseInclude(ctxt, nodes); if (tmp != 0) ret = -1; } else { xmlRngPErr(ctxt, nodes, XML_RNGP_GRAMMAR_CONTENT, "grammar has unexpected child %s\n", nodes->name, NULL); ret = -1; } nodes = nodes->next; } return (ret); } /** * xmlRelaxNGCheckReference: * @ref: the ref * @ctxt: a Relax-NG parser context * @name: the name associated to the defines * * Applies the 4.17. combine attribute rule for all the define * element of a given grammar using the same name. */ static void xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref, xmlRelaxNGParserCtxtPtr ctxt, const xmlChar * name) { xmlRelaxNGGrammarPtr grammar; xmlRelaxNGDefinePtr def, cur; grammar = ctxt->grammar; if (grammar == NULL) { xmlRngPErr(ctxt, ref->node, XML_ERR_INTERNAL_ERROR, "Internal error: no grammar in CheckReference %s\n", name, NULL); return; } if (ref->content != NULL) { xmlRngPErr(ctxt, ref->node, XML_ERR_INTERNAL_ERROR, "Internal error: reference has content in CheckReference %s\n", name, NULL); return; } if (grammar->defs != NULL) { def = xmlHashLookup(grammar->defs, name); if (def != NULL) { cur = ref; while (cur != NULL) { cur->content = def; cur = cur->nextHash; } } else { xmlRngPErr(ctxt, ref->node, XML_RNGP_REF_NO_DEF, "Reference %s has no matching definition\n", name, NULL); } } else { xmlRngPErr(ctxt, ref->node, XML_RNGP_REF_NO_DEF, "Reference %s has no matching definition\n", name, NULL); } } /** * xmlRelaxNGCheckCombine: * @define: the define(s) list * @ctxt: a Relax-NG parser context * @name: the name associated to the defines * * Applies the 4.17. combine attribute rule for all the define * element of a given grammar using the same name. */ static void xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define, xmlRelaxNGParserCtxtPtr ctxt, const xmlChar * name) { xmlChar *combine; int choiceOrInterleave = -1; int missing = 0; xmlRelaxNGDefinePtr cur, last, tmp, tmp2; if (define->nextHash == NULL) return; cur = define; while (cur != NULL) { combine = xmlGetProp(cur->node, BAD_CAST "combine"); if (combine != NULL) { if (xmlStrEqual(combine, BAD_CAST "choice")) { if (choiceOrInterleave == -1) choiceOrInterleave = 1; else if (choiceOrInterleave == 0) { xmlRngPErr(ctxt, define->node, XML_RNGP_DEF_CHOICE_AND_INTERLEAVE, "Defines for %s use both 'choice' and 'interleave'\n", name, NULL); } } else if (xmlStrEqual(combine, BAD_CAST "interleave")) { if (choiceOrInterleave == -1) choiceOrInterleave = 0; else if (choiceOrInterleave == 1) { xmlRngPErr(ctxt, define->node, XML_RNGP_DEF_CHOICE_AND_INTERLEAVE, "Defines for %s use both 'choice' and 'interleave'\n", name, NULL); } } else { xmlRngPErr(ctxt, define->node, XML_RNGP_UNKNOWN_COMBINE, "Defines for %s use unknown combine value '%s''\n", name, combine); } xmlFree(combine); } else { if (missing == 0) missing = 1; else { xmlRngPErr(ctxt, define->node, XML_RNGP_NEED_COMBINE, "Some defines for %s needs the combine attribute\n", name, NULL); } } cur = cur->nextHash; } #ifdef DEBUG xmlGenericError(xmlGenericErrorContext, "xmlRelaxNGCheckCombine(): merging %s defines: %d\n", name, choiceOrInterleave); #endif if (choiceOrInterleave == -1) choiceOrInterleave = 0; cur = xmlRelaxNGNewDefine(ctxt, define->node); if (cur == NULL) return; if (choiceOrInterleave == 0) cur->type = XML_RELAXNG_INTERLEAVE; else cur->type = XML_RELAXNG_CHOICE; tmp = define; last = NULL; while (tmp != NULL) { if (tmp->content != NULL) { if (tmp->content->next != NULL) { /* * we need first to create a wrapper. */ tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node); if (tmp2 == NULL) break; tmp2->type = XML_RELAXNG_GROUP; tmp2->content = tmp->content; } else { tmp2 = tmp->content; } if (last == NULL) { cur->content = tmp2; } else { last->next = tmp2; } last = tmp2; } tmp->content = cur; tmp = tmp->nextHash; } define->content = cur; if (choiceOrInterleave == 0) { if (ctxt->interleaves == NULL) ctxt->interleaves = xmlHashCreate(10); if (ctxt->interleaves == NULL) { xmlRngPErr(ctxt, define->node, XML_RNGP_INTERLEAVE_CREATE_FAILED, "Failed to create interleaves hash table\n", NULL, NULL); } else { char tmpname[32]; snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++); if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) { xmlRngPErr(ctxt, define->node, XML_RNGP_INTERLEAVE_CREATE_FAILED, "Failed to add %s to hash table\n", (const xmlChar *) tmpname, NULL); } } } } /** * xmlRelaxNGCombineStart: * @ctxt: a Relax-NG parser context * @grammar: the grammar * * Applies the 4.17. combine rule for all the start * element of a given grammar. */ static void xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGGrammarPtr grammar) { xmlRelaxNGDefinePtr starts; xmlChar *combine; int choiceOrInterleave = -1; int missing = 0; xmlRelaxNGDefinePtr cur; starts = grammar->start; if ((starts == NULL) || (starts->next == NULL)) return; cur = starts; while (cur != NULL) { if ((cur->node == NULL) || (cur->node->parent == NULL) || (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) { combine = NULL; xmlRngPErr(ctxt, cur->node, XML_RNGP_START_MISSING, "Internal error: start element not found\n", NULL, NULL); } else { combine = xmlGetProp(cur->node->parent, BAD_CAST "combine"); } if (combine != NULL) { if (xmlStrEqual(combine, BAD_CAST "choice")) { if (choiceOrInterleave == -1) choiceOrInterleave = 1; else if (choiceOrInterleave == 0) { xmlRngPErr(ctxt, cur->node, XML_RNGP_START_CHOICE_AND_INTERLEAVE, "
use both 'choice' and 'interleave'\n", NULL, NULL); } } else if (xmlStrEqual(combine, BAD_CAST "interleave")) { if (choiceOrInterleave == -1) choiceOrInterleave = 0; else if (choiceOrInterleave == 1) { xmlRngPErr(ctxt, cur->node, XML_RNGP_START_CHOICE_AND_INTERLEAVE, "
use both 'choice' and 'interleave'\n", NULL, NULL); } } else { xmlRngPErr(ctxt, cur->node, XML_RNGP_UNKNOWN_COMBINE, "
uses unknown combine value '%s''\n", combine, NULL); } xmlFree(combine); } else { if (missing == 0) missing = 1; else { xmlRngPErr(ctxt, cur->node, XML_RNGP_NEED_COMBINE, "Some
element miss the combine attribute\n", NULL, NULL); } } cur = cur->next; } #ifdef DEBUG xmlGenericError(xmlGenericErrorContext, "xmlRelaxNGCombineStart(): merging
: %d\n", choiceOrInterleave); #endif if (choiceOrInterleave == -1) choiceOrInterleave = 0; cur = xmlRelaxNGNewDefine(ctxt, starts->node); if (cur == NULL) return; if (choiceOrInterleave == 0) cur->type = XML_RELAXNG_INTERLEAVE; else cur->type = XML_RELAXNG_CHOICE; cur->content = grammar->start; grammar->start = cur; if (choiceOrInterleave == 0) { if (ctxt->interleaves == NULL) ctxt->interleaves = xmlHashCreate(10); if (ctxt->interleaves == NULL) { xmlRngPErr(ctxt, cur->node, XML_RNGP_INTERLEAVE_CREATE_FAILED, "Failed to create interleaves hash table\n", NULL, NULL); } else { char tmpname[32]; snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++); if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) { xmlRngPErr(ctxt, cur->node, XML_RNGP_INTERLEAVE_CREATE_FAILED, "Failed to add %s to hash table\n", (const xmlChar *) tmpname, NULL); } } } } /** * xmlRelaxNGCheckCycles: * @ctxt: a Relax-NG parser context * @nodes: grammar children nodes * @depth: the counter * * Check for cycles. * * Returns 0 if check passed, and -1 in case of error */ static int xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr cur, int depth) { int ret = 0; while ((ret == 0) && (cur != NULL)) { if ((cur->type == XML_RELAXNG_REF) || (cur->type == XML_RELAXNG_PARENTREF)) { if (cur->depth == -1) { cur->depth = depth; ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth); cur->depth = -2; } else if (depth == cur->depth) { xmlRngPErr(ctxt, cur->node, XML_RNGP_REF_CYCLE, "Detected a cycle in %s references\n", cur->name, NULL); return (-1); } } else if (cur->type == XML_RELAXNG_ELEMENT) { ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1); } else { ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth); } cur = cur->next; } return (ret); } /** * xmlRelaxNGTryUnlink: * @ctxt: a Relax-NG parser context * @cur: the definition to unlink * @parent: the parent definition * @prev: the previous sibling definition * * Try to unlink a definition. If not possble make it a NOOP * * Returns the new prev definition */ static xmlRelaxNGDefinePtr xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlRelaxNGDefinePtr cur, xmlRelaxNGDefinePtr parent, xmlRelaxNGDefinePtr prev) { if (prev != NULL) { prev->next = cur->next; } else { if (parent != NULL) { if (parent->content == cur) parent->content = cur->next; else if (parent->attrs == cur) parent->attrs = cur->next; else if (parent->nameClass == cur) parent->nameClass = cur->next; } else { cur->type = XML_RELAXNG_NOOP; prev = cur; } } return (prev); } /** * xmlRelaxNGSimplify: * @ctxt: a Relax-NG parser context * @nodes: grammar children nodes * * Check for simplification of empty and notAllowed */ static void xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr cur, xmlRelaxNGDefinePtr parent) { xmlRelaxNGDefinePtr prev = NULL; while (cur != NULL) { if ((cur->type == XML_RELAXNG_REF) || (cur->type == XML_RELAXNG_PARENTREF)) { if (cur->depth != -3) { cur->depth = -3; xmlRelaxNGSimplify(ctxt, cur->content, cur); } } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) { cur->parent = parent; if ((parent != NULL) && ((parent->type == XML_RELAXNG_ATTRIBUTE) || (parent->type == XML_RELAXNG_LIST) || (parent->type == XML_RELAXNG_GROUP) || (parent->type == XML_RELAXNG_INTERLEAVE) || (parent->type == XML_RELAXNG_ONEORMORE) || (parent->type == XML_RELAXNG_ZEROORMORE))) { parent->type = XML_RELAXNG_NOT_ALLOWED; break; } if ((parent != NULL) && (parent->type == XML_RELAXNG_CHOICE)) { prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev); } else prev = cur; } else if (cur->type == XML_RELAXNG_EMPTY) { cur->parent = parent; if ((parent != NULL) && ((parent->type == XML_RELAXNG_ONEORMORE) || (parent->type == XML_RELAXNG_ZEROORMORE))) { parent->type = XML_RELAXNG_EMPTY; break; } if ((parent != NULL) && ((parent->type == XML_RELAXNG_GROUP) || (parent->type == XML_RELAXNG_INTERLEAVE))) { prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev); } else prev = cur; } else { cur->parent = parent; if (cur->content != NULL) xmlRelaxNGSimplify(ctxt, cur->content, cur); if ((cur->type != XML_RELAXNG_VALUE) && (cur->attrs != NULL)) xmlRelaxNGSimplify(ctxt, cur->attrs, cur); if (cur->nameClass != NULL) xmlRelaxNGSimplify(ctxt, cur->nameClass, cur); /* * On Elements, try to move attribute only generating rules on * the attrs rules. */ if (cur->type == XML_RELAXNG_ELEMENT) { int attronly; xmlRelaxNGDefinePtr tmp, pre; while (cur->content != NULL) { attronly = xmlRelaxNGGenerateAttributes(ctxt, cur->content); if (attronly == 1) { /* * migrate cur->content to attrs */ tmp = cur->content; cur->content = tmp->next; tmp->next = cur->attrs; cur->attrs = tmp; } else { /* * cur->content can generate elements or text */ break; } } pre = cur->content; while ((pre != NULL) && (pre->next != NULL)) { tmp = pre->next; attronly = xmlRelaxNGGenerateAttributes(ctxt, tmp); if (attronly == 1) { /* * migrate tmp to attrs */ pre->next = tmp->next; tmp->next = cur->attrs; cur->attrs = tmp; } else { pre = tmp; } } } /* * This may result in a simplification */ if ((cur->type == XML_RELAXNG_GROUP) || (cur->type == XML_RELAXNG_INTERLEAVE)) { if (cur->content == NULL) cur->type = XML_RELAXNG_EMPTY; else if (cur->content->next == NULL) { if ((parent == NULL) && (prev == NULL)) { cur->type = XML_RELAXNG_NOOP; } else if (prev == NULL) { parent->content = cur->content; cur->content->next = cur->next; cur = cur->content; } else { cur->content->next = cur->next; prev->next = cur->content; cur = cur->content; } } } /* * the current node may have been transformed back */ if ((cur->type == XML_RELAXNG_EXCEPT) && (cur->content != NULL) && (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) { prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev); } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) { if ((parent != NULL) && ((parent->type == XML_RELAXNG_ATTRIBUTE) || (parent->type == XML_RELAXNG_LIST) || (parent->type == XML_RELAXNG_GROUP) || (parent->type == XML_RELAXNG_INTERLEAVE) || (parent->type == XML_RELAXNG_ONEORMORE) || (parent->type == XML_RELAXNG_ZEROORMORE))) { parent->type = XML_RELAXNG_NOT_ALLOWED; break; } if ((parent != NULL) && (parent->type == XML_RELAXNG_CHOICE)) { prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev); } else prev = cur; } else if (cur->type == XML_RELAXNG_EMPTY) { if ((parent != NULL) && ((parent->type == XML_RELAXNG_ONEORMORE) || (parent->type == XML_RELAXNG_ZEROORMORE))) { parent->type = XML_RELAXNG_EMPTY; break; } if ((parent != NULL) && ((parent->type == XML_RELAXNG_GROUP) || (parent->type == XML_RELAXNG_INTERLEAVE) || (parent->type == XML_RELAXNG_CHOICE))) { prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev); } else prev = cur; } else { prev = cur; } } cur = cur->next; } } /** * xmlRelaxNGGroupContentType: * @ct1: the first content type * @ct2: the second content type * * Try to group 2 content types * * Returns the content type */ static xmlRelaxNGContentType xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1, xmlRelaxNGContentType ct2) { if ((ct1 == XML_RELAXNG_CONTENT_ERROR) || (ct2 == XML_RELAXNG_CONTENT_ERROR)) return (XML_RELAXNG_CONTENT_ERROR); if (ct1 == XML_RELAXNG_CONTENT_EMPTY) return (ct2); if (ct2 == XML_RELAXNG_CONTENT_EMPTY) return (ct1); if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) && (ct2 == XML_RELAXNG_CONTENT_COMPLEX)) return (XML_RELAXNG_CONTENT_COMPLEX); return (XML_RELAXNG_CONTENT_ERROR); } /** * xmlRelaxNGMaxContentType: * @ct1: the first content type * @ct2: the second content type * * Compute the max content-type * * Returns the content type */ static xmlRelaxNGContentType xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1, xmlRelaxNGContentType ct2) { if ((ct1 == XML_RELAXNG_CONTENT_ERROR) || (ct2 == XML_RELAXNG_CONTENT_ERROR)) return (XML_RELAXNG_CONTENT_ERROR); if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) || (ct2 == XML_RELAXNG_CONTENT_SIMPLE)) return (XML_RELAXNG_CONTENT_SIMPLE); if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) || (ct2 == XML_RELAXNG_CONTENT_COMPLEX)) return (XML_RELAXNG_CONTENT_COMPLEX); return (XML_RELAXNG_CONTENT_EMPTY); } /** * xmlRelaxNGCheckRules: * @ctxt: a Relax-NG parser context * @cur: the current definition * @flags: some accumulated flags * @ptype: the parent type * * Check for rules in section 7.1 and 7.2 * * Returns the content type of @cur */ static xmlRelaxNGContentType xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGDefinePtr cur, int flags, xmlRelaxNGType ptype) { int nflags = flags; xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY; while (cur != NULL) { ret = XML_RELAXNG_CONTENT_EMPTY; if ((cur->type == XML_RELAXNG_REF) || (cur->type == XML_RELAXNG_PARENTREF)) { /* * This should actually be caught by list//element(ref) at the * element boundaries, c.f. Bug #159968 local refs are dropped * in step 4.19. */ #if 0 if (flags & XML_RELAXNG_IN_LIST) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_REF, "Found forbidden pattern list//ref\n", NULL, NULL); } #endif if (flags & XML_RELAXNG_IN_DATAEXCEPT) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_REF, "Found forbidden pattern data/except//ref\n", NULL, NULL); } if (cur->depth > -4) { cur->depth = -4; ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type); cur->depth = ret - 15; } else if (cur->depth == -4) { ret = XML_RELAXNG_CONTENT_COMPLEX; } else { ret = (xmlRelaxNGContentType) (cur->depth + 15); } } else if (cur->type == XML_RELAXNG_ELEMENT) { /* * The 7.3 Attribute derivation rule for groups is plugged there */ xmlRelaxNGCheckGroupAttrs(ctxt, cur); if (flags & XML_RELAXNG_IN_DATAEXCEPT) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_ELEM, "Found forbidden pattern data/except//element(ref)\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_LIST) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_ELEM, "Found forbidden pattern list//element(ref)\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_ATTRIBUTE) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_ATTR_ELEM, "Found forbidden pattern attribute//element(ref)\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_ATTRIBUTE) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_ATTR_ELEM, "Found forbidden pattern attribute//element(ref)\n", NULL, NULL); } /* * reset since in the simple form elements are only child * of grammar/define */ nflags = 0; ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type); if (ret != XML_RELAXNG_CONTENT_EMPTY) { xmlRngPErr(ctxt, cur->node, XML_RNGP_ELEM_CONTENT_EMPTY, "Element %s attributes have a content type error\n", cur->name, NULL); } ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type); if (ret == XML_RELAXNG_CONTENT_ERROR) { xmlRngPErr(ctxt, cur->node, XML_RNGP_ELEM_CONTENT_ERROR, "Element %s has a content type error\n", cur->name, NULL); } else { ret = XML_RELAXNG_CONTENT_COMPLEX; } } else if (cur->type == XML_RELAXNG_ATTRIBUTE) { if (flags & XML_RELAXNG_IN_ATTRIBUTE) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_ATTR_ATTR, "Found forbidden pattern attribute//attribute\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_LIST) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_ATTR, "Found forbidden pattern list//attribute\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_OOMGROUP) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_ONEMORE_GROUP_ATTR, "Found forbidden pattern oneOrMore//group//attribute\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_ONEMORE_INTERLEAVE_ATTR, "Found forbidden pattern oneOrMore//interleave//attribute\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_DATAEXCEPT) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_ATTR, "Found forbidden pattern data/except//attribute\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_START) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_ATTR, "Found forbidden pattern start//attribute\n", NULL, NULL); } if ((!(flags & XML_RELAXNG_IN_ONEORMORE)) && (cur->name == NULL)) { if (cur->ns == NULL) { xmlRngPErr(ctxt, cur->node, XML_RNGP_ANYNAME_ATTR_ANCESTOR, "Found anyName attribute without oneOrMore ancestor\n", NULL, NULL); } else { xmlRngPErr(ctxt, cur->node, XML_RNGP_NSNAME_ATTR_ANCESTOR, "Found nsName attribute without oneOrMore ancestor\n", NULL, NULL); } } nflags = flags | XML_RELAXNG_IN_ATTRIBUTE; xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type); ret = XML_RELAXNG_CONTENT_EMPTY; } else if ((cur->type == XML_RELAXNG_ONEORMORE) || (cur->type == XML_RELAXNG_ZEROORMORE)) { if (flags & XML_RELAXNG_IN_DATAEXCEPT) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_ONEMORE, "Found forbidden pattern data/except//oneOrMore\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_START) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_ONEMORE, "Found forbidden pattern start//oneOrMore\n", NULL, NULL); } nflags = flags | XML_RELAXNG_IN_ONEORMORE; ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type); ret = xmlRelaxNGGroupContentType(ret, ret); } else if (cur->type == XML_RELAXNG_LIST) { if (flags & XML_RELAXNG_IN_LIST) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_LIST, "Found forbidden pattern list//list\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_DATAEXCEPT) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_LIST, "Found forbidden pattern data/except//list\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_START) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_LIST, "Found forbidden pattern start//list\n", NULL, NULL); } nflags = flags | XML_RELAXNG_IN_LIST; ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type); } else if (cur->type == XML_RELAXNG_GROUP) { if (flags & XML_RELAXNG_IN_DATAEXCEPT) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_GROUP, "Found forbidden pattern data/except//group\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_START) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_GROUP, "Found forbidden pattern start//group\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_ONEORMORE) nflags = flags | XML_RELAXNG_IN_OOMGROUP; else nflags = flags; ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type); /* * The 7.3 Attribute derivation rule for groups is plugged there */ xmlRelaxNGCheckGroupAttrs(ctxt, cur); } else if (cur->type == XML_RELAXNG_INTERLEAVE) { if (flags & XML_RELAXNG_IN_LIST) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_INTERLEAVE, "Found forbidden pattern list//interleave\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_DATAEXCEPT) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_INTERLEAVE, "Found forbidden pattern data/except//interleave\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_START) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_INTERLEAVE, "Found forbidden pattern start//interleave\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_ONEORMORE) nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE; else nflags = flags; ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type); } else if (cur->type == XML_RELAXNG_EXCEPT) { if ((cur->parent != NULL) && (cur->parent->type == XML_RELAXNG_DATATYPE)) nflags = flags | XML_RELAXNG_IN_DATAEXCEPT; else nflags = flags; ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type); } else if (cur->type == XML_RELAXNG_DATATYPE) { if (flags & XML_RELAXNG_IN_START) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_DATA, "Found forbidden pattern start//data\n", NULL, NULL); } xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type); ret = XML_RELAXNG_CONTENT_SIMPLE; } else if (cur->type == XML_RELAXNG_VALUE) { if (flags & XML_RELAXNG_IN_START) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_VALUE, "Found forbidden pattern start//value\n", NULL, NULL); } xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type); ret = XML_RELAXNG_CONTENT_SIMPLE; } else if (cur->type == XML_RELAXNG_TEXT) { if (flags & XML_RELAXNG_IN_LIST) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_LIST_TEXT, "Found forbidden pattern list//text\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_DATAEXCEPT) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_TEXT, "Found forbidden pattern data/except//text\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_START) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_TEXT, "Found forbidden pattern start//text\n", NULL, NULL); } ret = XML_RELAXNG_CONTENT_COMPLEX; } else if (cur->type == XML_RELAXNG_EMPTY) { if (flags & XML_RELAXNG_IN_DATAEXCEPT) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_DATA_EXCEPT_EMPTY, "Found forbidden pattern data/except//empty\n", NULL, NULL); } if (flags & XML_RELAXNG_IN_START) { xmlRngPErr(ctxt, cur->node, XML_RNGP_PAT_START_EMPTY, "Found forbidden pattern start//empty\n", NULL, NULL); } ret = XML_RELAXNG_CONTENT_EMPTY; } else if (cur->type == XML_RELAXNG_CHOICE) { xmlRelaxNGCheckChoiceDeterminism(ctxt, cur); ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type); } else { ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type); } cur = cur->next; if (ptype == XML_RELAXNG_GROUP) { val = xmlRelaxNGGroupContentType(val, ret); } else if (ptype == XML_RELAXNG_INTERLEAVE) { tmp = xmlRelaxNGGroupContentType(val, ret); if (tmp != XML_RELAXNG_CONTENT_ERROR) tmp = xmlRelaxNGMaxContentType(val, ret); } else if (ptype == XML_RELAXNG_CHOICE) { val = xmlRelaxNGMaxContentType(val, ret); } else if (ptype == XML_RELAXNG_LIST) { val = XML_RELAXNG_CONTENT_SIMPLE; } else if (ptype == XML_RELAXNG_EXCEPT) { if (ret == XML_RELAXNG_CONTENT_ERROR) val = XML_RELAXNG_CONTENT_ERROR; else val = XML_RELAXNG_CONTENT_SIMPLE; } else { val = xmlRelaxNGGroupContentType(val, ret); } } return (val); } /** * xmlRelaxNGParseGrammar: * @ctxt: a Relax-NG parser context * @nodes: grammar children nodes * * parse a Relax-NG
node * * Returns the internal xmlRelaxNGGrammarPtr built or * NULL in case of error */ static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) { xmlRelaxNGGrammarPtr ret, tmp, old; #ifdef DEBUG_GRAMMAR xmlGenericError(xmlGenericErrorContext, "Parsing a new grammar\n"); #endif ret = xmlRelaxNGNewGrammar(ctxt); if (ret == NULL) return (NULL); /* * Link the new grammar in the tree */ ret->parent = ctxt->grammar; if (ctxt->grammar != NULL) { tmp = ctxt->grammar->children; if (tmp == NULL) { ctxt->grammar->children = ret; } else { while (tmp->next != NULL) tmp = tmp->next; tmp->next = ret; } } old = ctxt->grammar; ctxt->grammar = ret; xmlRelaxNGParseGrammarContent(ctxt, nodes); ctxt->grammar = ret; if (ctxt->grammar == NULL) { xmlRngPErr(ctxt, nodes, XML_RNGP_GRAMMAR_CONTENT, "Failed to parse
content\n", NULL, NULL); } else if (ctxt->grammar->start == NULL) { xmlRngPErr(ctxt, nodes, XML_RNGP_GRAMMAR_NO_START, "Element
has no
\n", NULL, NULL); } /* * Apply 4.17 mergingd rules to defines and starts */ xmlRelaxNGCombineStart(ctxt, ret); if (ret->defs != NULL) { xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine, ctxt); } /* * link together defines and refs in this grammar */ if (ret->refs != NULL) { xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference, ctxt); } ctxt->grammar = old; return (ret); } /** * xmlRelaxNGParseDocument: * @ctxt: a Relax-NG parser context * @node: the root node of the RelaxNG schema * * parse a Relax-NG definition resource and build an internal * xmlRelaxNG struture which can be used to validate instances. * * Returns the internal XML RelaxNG structure built or * NULL in case of error */ static xmlRelaxNGPtr xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlRelaxNGPtr schema = NULL; const xmlChar *olddefine; xmlRelaxNGGrammarPtr old; if ((ctxt == NULL) || (node == NULL)) return (NULL); schema = xmlRelaxNGNewRelaxNG(ctxt); if (schema == NULL) return (NULL); olddefine = ctxt->define; ctxt->define = NULL; if (IS_RELAXNG(node, "grammar")) { schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children); } else { xmlRelaxNGGrammarPtr tmp, ret; schema->topgrammar = ret = xmlRelaxNGNewGrammar(ctxt); if (schema->topgrammar == NULL) { return (schema); } /* * Link the new grammar in the tree */ ret->parent = ctxt->grammar; if (ctxt->grammar != NULL) { tmp = ctxt->grammar->children; if (tmp == NULL) { ctxt->grammar->children = ret; } else { while (tmp->next != NULL) tmp = tmp->next; tmp->next = ret; } } old = ctxt->grammar; ctxt->grammar = ret; xmlRelaxNGParseStart(ctxt, node); if (old != NULL) ctxt->grammar = old; } ctxt->define = olddefine; if (schema->topgrammar->start != NULL) { xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0); if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) { xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL); while ((schema->topgrammar->start != NULL) && (schema->topgrammar->start->type == XML_RELAXNG_NOOP) && (schema->topgrammar->start->next != NULL)) schema->topgrammar->start = schema->topgrammar->start->content; xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start, XML_RELAXNG_IN_START, XML_RELAXNG_NOOP); } } #ifdef DEBUG if (schema == NULL) xmlGenericError(xmlGenericErrorContext, "xmlRelaxNGParseDocument() failed\n"); #endif return (schema); } /************************************************************************ * * * Reading RelaxNGs * * * ************************************************************************/ /** * xmlRelaxNGNewParserCtxt: * @URL: the location of the schema * * Create an XML RelaxNGs parse context for that file/resource expected * to contain an XML RelaxNGs file. * * Returns the parser context or NULL in case of error */ xmlRelaxNGParserCtxtPtr xmlRelaxNGNewParserCtxt(const char *URL) { xmlRelaxNGParserCtxtPtr ret; if (URL == NULL) return (NULL); ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt)); if (ret == NULL) { xmlRngPErrMemory(NULL, "building parser\n"); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNGParserCtxt)); ret->URL = xmlStrdup((const xmlChar *) URL); ret->error = xmlGenericError; ret->userData = xmlGenericErrorContext; return (ret); } /** * xmlRelaxNGNewMemParserCtxt: * @buffer: a pointer to a char array containing the schemas * @size: the size of the array * * Create an XML RelaxNGs parse context for that memory buffer expected * to contain an XML RelaxNGs file. * * Returns the parser context or NULL in case of error */ xmlRelaxNGParserCtxtPtr xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) { xmlRelaxNGParserCtxtPtr ret; if ((buffer == NULL) || (size <= 0)) return (NULL); ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt)); if (ret == NULL) { xmlRngPErrMemory(NULL, "building parser\n"); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNGParserCtxt)); ret->buffer = buffer; ret->size = size; ret->error = xmlGenericError; ret->userData = xmlGenericErrorContext; return (ret); } /** * xmlRelaxNGNewDocParserCtxt: * @doc: a preparsed document tree * * Create an XML RelaxNGs parser context for that document. * Note: since the process of compiling a RelaxNG schemas modifies the * document, the @doc parameter is duplicated internally. * * Returns the parser context or NULL in case of error */ xmlRelaxNGParserCtxtPtr xmlRelaxNGNewDocParserCtxt(xmlDocPtr doc) { xmlRelaxNGParserCtxtPtr ret; xmlDocPtr copy; if (doc == NULL) return (NULL); copy = xmlCopyDoc(doc, 1); if (copy == NULL) return (NULL); ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt)); if (ret == NULL) { xmlRngPErrMemory(NULL, "building parser\n"); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNGParserCtxt)); ret->document = copy; ret->freedoc = 1; ret->userData = xmlGenericErrorContext; return (ret); } /** * xmlRelaxNGFreeParserCtxt: * @ctxt: the schema parser context * * Free the resources associated to the schema parser context */ void xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) { if (ctxt == NULL) return; if (ctxt->URL != NULL) xmlFree(ctxt->URL); if (ctxt->doc != NULL) xmlRelaxNGFreeDocument(ctxt->doc); if (ctxt->interleaves != NULL) xmlHashFree(ctxt->interleaves, NULL); if (ctxt->documents != NULL) xmlRelaxNGFreeDocumentList(ctxt->documents); if (ctxt->includes != NULL) xmlRelaxNGFreeIncludeList(ctxt->includes); if (ctxt->docTab != NULL) xmlFree(ctxt->docTab); if (ctxt->incTab != NULL) xmlFree(ctxt->incTab); if (ctxt->defTab != NULL) { int i; for (i = 0; i < ctxt->defNr; i++) xmlRelaxNGFreeDefine(ctxt->defTab[i]); xmlFree(ctxt->defTab); } if ((ctxt->document != NULL) && (ctxt->freedoc)) xmlFreeDoc(ctxt->document); xmlFree(ctxt); } /** * xmlRelaxNGNormExtSpace: * @value: a value * * Removes the leading and ending spaces of the value * The string is modified "in situ" */ static void xmlRelaxNGNormExtSpace(xmlChar * value) { xmlChar *start = value; xmlChar *cur = value; if (value == NULL) return; while (IS_BLANK_CH(*cur)) cur++; if (cur == start) { do { while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++; if (*cur == 0) return; start = cur; while (IS_BLANK_CH(*cur)) cur++; if (*cur == 0) { *start = 0; return; } } while (1); } else { do { while ((*cur != 0) && (!IS_BLANK_CH(*cur))) *start++ = *cur++; if (*cur == 0) { *start = 0; return; } /* don't try to normalize the inner spaces */ while (IS_BLANK_CH(*cur)) cur++; if (*cur == 0) { *start = 0; return; } *start++ = *cur++; } while (1); } } /** * xmlRelaxNGCleanupAttributes: * @ctxt: a Relax-NG parser context * @node: a Relax-NG node * * Check all the attributes on the given node */ static void xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) { xmlAttrPtr cur, next; cur = node->properties; while (cur != NULL) { next = cur->next; if ((cur->ns == NULL) || (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) { if (xmlStrEqual(cur->name, BAD_CAST "name")) { if ((!xmlStrEqual(node->name, BAD_CAST "element")) && (!xmlStrEqual(node->name, BAD_CAST "attribute")) && (!xmlStrEqual(node->name, BAD_CAST "ref")) && (!xmlStrEqual(node->name, BAD_CAST "parentRef")) && (!xmlStrEqual(node->name, BAD_CAST "param")) && (!xmlStrEqual(node->name, BAD_CAST "define"))) { xmlRngPErr(ctxt, node, XML_RNGP_FORBIDDEN_ATTRIBUTE, "Attribute %s is not allowed on %s\n", cur->name, node->name); } } else if (xmlStrEqual(cur->name, BAD_CAST "type")) { if ((!xmlStrEqual(node->name, BAD_CAST "value")) && (!xmlStrEqual(node->name, BAD_CAST "data"))) { xmlRngPErr(ctxt, node, XML_RNGP_FORBIDDEN_ATTRIBUTE, "Attribute %s is not allowed on %s\n", cur->name, node->name); } } else if (xmlStrEqual(cur->name, BAD_CAST "href")) { if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) && (!xmlStrEqual(node->name, BAD_CAST "include"))) { xmlRngPErr(ctxt, node, XML_RNGP_FORBIDDEN_ATTRIBUTE, "Attribute %s is not allowed on %s\n", cur->name, node->name); } } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) { if ((!xmlStrEqual(node->name, BAD_CAST "start")) && (!xmlStrEqual(node->name, BAD_CAST "define"))) { xmlRngPErr(ctxt, node, XML_RNGP_FORBIDDEN_ATTRIBUTE, "Attribute %s is not allowed on %s\n", cur->name, node->name); } } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) { xmlChar *val; xmlURIPtr uri; val = xmlNodeListGetString(node->doc, cur->children, 1); if (val != NULL) { if (val[0] != 0) { uri = xmlParseURI((const char *) val); if (uri == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_INVALID_URI, "Attribute %s contains invalid URI %s\n", cur->name, val); } else { if (uri->scheme == NULL) { xmlRngPErr(ctxt, node, XML_RNGP_URI_NOT_ABSOLUTE, "Attribute %s URI %s is not absolute\n", cur->name, val); } if (uri->fragment != NULL) { xmlRngPErr(ctxt, node, XML_RNGP_URI_FRAGMENT, "Attribute %s URI %s has a fragment ID\n", cur->name, val); } xmlFreeURI(uri); } } xmlFree(val); } } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) { xmlRngPErr(ctxt, node, XML_RNGP_UNKNOWN_ATTRIBUTE, "Unknown attribute %s on %s\n", cur->name, node->name); } } cur = next; } } /** * xmlRelaxNGCleanupTree: * @ctxt: a Relax-NG parser context * @root: an xmlNodePtr subtree * * Cleanup the subtree from unwanted nodes for parsing, resolve * Include and externalRef lookups. */ static void xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root) { xmlNodePtr cur, delete; delete = NULL; cur = root; while (cur != NULL) { if (delete != NULL) { xmlUnlinkNode(delete); xmlFreeNode(delete); delete = NULL; } if (cur->type == XML_ELEMENT_NODE) { /* * Simplification 4.1. Annotations */ if ((cur->ns == NULL) || (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) { if ((cur->parent != NULL) && (cur->parent->type == XML_ELEMENT_NODE) && ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) || (xmlStrEqual(cur->parent->name, BAD_CAST "value")) || (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) { xmlRngPErr(ctxt, cur, XML_RNGP_FOREIGN_ELEMENT, "element %s doesn't allow foreign elements\n", cur->parent->name, NULL); } delete = cur; goto skip_children; } else { xmlRelaxNGCleanupAttributes(ctxt, cur); if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) { xmlChar *href, *ns, *base, *URL; xmlRelaxNGDocumentPtr docu; xmlNodePtr tmp; xmlURIPtr uri; ns = xmlGetProp(cur, BAD_CAST "ns"); if (ns == NULL) { tmp = cur->parent; while ((tmp != NULL) && (tmp->type == XML_ELEMENT_NODE)) { ns = xmlGetProp(tmp, BAD_CAST "ns"); if (ns != NULL) break; tmp = tmp->parent; } } href = xmlGetProp(cur, BAD_CAST "href"); if (href == NULL) { xmlRngPErr(ctxt, cur, XML_RNGP_MISSING_HREF, "xmlRelaxNGParse: externalRef has no href attribute\n", NULL, NULL); if (ns != NULL) xmlFree(ns); delete = cur; goto skip_children; } uri = xmlParseURI((const char *) href); if (uri == NULL) { xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR, "Incorrect URI for externalRef %s\n", href, NULL); if (ns != NULL) xmlFree(ns); if (href != NULL) xmlFree(href); delete = cur; goto skip_children; } if (uri->fragment != NULL) { xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR, "Fragment forbidden in URI for externalRef %s\n", href, NULL); if (ns != NULL) xmlFree(ns); xmlFreeURI(uri); if (href != NULL) xmlFree(href); delete = cur; goto skip_children; } xmlFreeURI(uri); base = xmlNodeGetBase(cur->doc, cur); URL = xmlBuildURI(href, base); if (URL == NULL) { xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR, "Failed to compute URL for externalRef %s\n", href, NULL); if (ns != NULL) xmlFree(ns); if (href != NULL) xmlFree(href); if (base != NULL) xmlFree(base); delete = cur; goto skip_children; } if (href != NULL) xmlFree(href); if (base != NULL) xmlFree(base); docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns); if (docu == NULL) { xmlRngPErr(ctxt, cur, XML_RNGP_EXTERNAL_REF_FAILURE, "Failed to load externalRef %s\n", URL, NULL); if (ns != NULL) xmlFree(ns); xmlFree(URL); delete = cur; goto skip_children; } if (ns != NULL) xmlFree(ns); xmlFree(URL); cur->psvi = docu; } else if (xmlStrEqual(cur->name, BAD_CAST "include")) { xmlChar *href, *ns, *base, *URL; xmlRelaxNGIncludePtr incl; xmlNodePtr tmp; href = xmlGetProp(cur, BAD_CAST "href"); if (href == NULL) { xmlRngPErr(ctxt, cur, XML_RNGP_MISSING_HREF, "xmlRelaxNGParse: include has no href attribute\n", NULL, NULL); delete = cur; goto skip_children; } base = xmlNodeGetBase(cur->doc, cur); URL = xmlBuildURI(href, base); if (URL == NULL) { xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR, "Failed to compute URL for include %s\n", href, NULL); if (href != NULL) xmlFree(href); if (base != NULL) xmlFree(base); delete = cur; goto skip_children; } if (href != NULL) xmlFree(href); if (base != NULL) xmlFree(base); ns = xmlGetProp(cur, BAD_CAST "ns"); if (ns == NULL) { tmp = cur->parent; while ((tmp != NULL) && (tmp->type == XML_ELEMENT_NODE)) { ns = xmlGetProp(tmp, BAD_CAST "ns"); if (ns != NULL) break; tmp = tmp->parent; } } incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns); if (ns != NULL) xmlFree(ns); if (incl == NULL) { xmlRngPErr(ctxt, cur, XML_RNGP_INCLUDE_FAILURE, "Failed to load include %s\n", URL, NULL); xmlFree(URL); delete = cur; goto skip_children; } xmlFree(URL); cur->psvi = incl; } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) || (xmlStrEqual(cur->name, BAD_CAST "attribute"))) { xmlChar *name, *ns; xmlNodePtr text = NULL; /* * Simplification 4.8. name attribute of element * and attribute elements */ name = xmlGetProp(cur, BAD_CAST "name"); if (name != NULL) { if (cur->children == NULL) { text = xmlNewChild(cur, cur->ns, BAD_CAST "name", name); } else { xmlNodePtr node; node = xmlNewDocNode(cur->doc, cur->ns, BAD_CAST "name", NULL); if (node != NULL) { xmlAddPrevSibling(cur->children, node); text = xmlNewText(name); xmlAddChild(node, text); text = node; } } if (text == NULL) { xmlRngPErr(ctxt, cur, XML_RNGP_CREATE_FAILURE, "Failed to create a name %s element\n", name, NULL); } xmlUnsetProp(cur, BAD_CAST "name"); xmlFree(name); ns = xmlGetProp(cur, BAD_CAST "ns"); if (ns != NULL) { if (text != NULL) { xmlSetProp(text, BAD_CAST "ns", ns); /* xmlUnsetProp(cur, BAD_CAST "ns"); */ } xmlFree(ns); } else if (xmlStrEqual(cur->name, BAD_CAST "attribute")) { xmlSetProp(text, BAD_CAST "ns", BAD_CAST ""); } } } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) || (xmlStrEqual(cur->name, BAD_CAST "nsName")) || (xmlStrEqual(cur->name, BAD_CAST "value"))) { /* * Simplification 4.8. name attribute of element * and attribute elements */ if (xmlHasProp(cur, BAD_CAST "ns") == NULL) { xmlNodePtr node; xmlChar *ns = NULL; node = cur->parent; while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) { ns = xmlGetProp(node, BAD_CAST "ns"); if (ns != NULL) { break; } node = node->parent; } if (ns == NULL) { xmlSetProp(cur, BAD_CAST "ns", BAD_CAST ""); } else { xmlSetProp(cur, BAD_CAST "ns", ns); xmlFree(ns); } } if (xmlStrEqual(cur->name, BAD_CAST "name")) { xmlChar *name, *local, *prefix; /* * Simplification: 4.10. QNames */ name = xmlNodeGetContent(cur); if (name != NULL) { local = xmlSplitQName2(name, &prefix); if (local != NULL) { xmlNsPtr ns; ns = xmlSearchNs(cur->doc, cur, prefix); if (ns == NULL) { xmlRngPErr(ctxt, cur, XML_RNGP_PREFIX_UNDEFINED, "xmlRelaxNGParse: no namespace for prefix %s\n", prefix, NULL); } else { xmlSetProp(cur, BAD_CAST "ns", ns->href); xmlNodeSetContent(cur, local); } xmlFree(local); xmlFree(prefix); } xmlFree(name); } } /* * 4.16 */ if (xmlStrEqual(cur->name, BAD_CAST "nsName")) { if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) { xmlRngPErr(ctxt, cur, XML_RNGP_PAT_NSNAME_EXCEPT_NSNAME, "Found nsName/except//nsName forbidden construct\n", NULL, NULL); } } } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) && (cur != root)) { int oldflags = ctxt->flags; /* * 4.16 */ if ((cur->parent != NULL) && (xmlStrEqual (cur->parent->name, BAD_CAST "anyName"))) { ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT; xmlRelaxNGCleanupTree(ctxt, cur); ctxt->flags = oldflags; goto skip_children; } else if ((cur->parent != NULL) && (xmlStrEqual (cur->parent->name, BAD_CAST "nsName"))) { ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT; xmlRelaxNGCleanupTree(ctxt, cur); ctxt->flags = oldflags; goto skip_children; } } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) { /* * 4.16 */ if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) { xmlRngPErr(ctxt, cur, XML_RNGP_PAT_ANYNAME_EXCEPT_ANYNAME, "Found anyName/except//anyName forbidden construct\n", NULL, NULL); } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) { xmlRngPErr(ctxt, cur, XML_RNGP_PAT_NSNAME_EXCEPT_ANYNAME, "Found nsName/except//anyName forbidden construct\n", NULL, NULL); } } /* * Thisd is not an else since "include" is transformed * into a div */ if (xmlStrEqual(cur->name, BAD_CAST "div")) { xmlChar *ns; xmlNodePtr child, ins, tmp; /* * implements rule 4.11 */ ns = xmlGetProp(cur, BAD_CAST "ns"); child = cur->children; ins = cur; while (child != NULL) { if (ns != NULL) { if (!xmlHasProp(child, BAD_CAST "ns")) { xmlSetProp(child, BAD_CAST "ns", ns); } } tmp = child->next; xmlUnlinkNode(child); ins = xmlAddNextSibling(ins, child); child = tmp; } if (ns != NULL) xmlFree(ns); /* * Since we are about to delete cur, if it's nsDef is non-NULL we * need to preserve it (it contains the ns definitions for the * children we just moved). We'll just stick it on to the end * of cur->parent's list, since it's never going to be re-serialized * (bug 143738). */ if (cur->nsDef != NULL) { xmlNsPtr parDef = (xmlNsPtr)&cur->parent->nsDef; while (parDef->next != NULL) parDef = parDef->next; parDef->next = cur->nsDef; cur->nsDef = NULL; } delete = cur; goto skip_children; } } } /* * Simplification 4.2 whitespaces */ else if ((cur->type == XML_TEXT_NODE) || (cur->type == XML_CDATA_SECTION_NODE)) { if (IS_BLANK_NODE(cur)) { if (cur->parent->type == XML_ELEMENT_NODE) { if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) && (!xmlStrEqual (cur->parent->name, BAD_CAST "param"))) delete = cur; } else { delete = cur; goto skip_children; } } } else { delete = cur; goto skip_children; } /* * Skip to next node */ if (cur->children != NULL) { if ((cur->children->type != XML_ENTITY_DECL) && (cur->children->type != XML_ENTITY_REF_NODE) && (cur->children->type != XML_ENTITY_NODE)) { cur = cur->children; continue; } } skip_children: if (cur->next != NULL) { cur = cur->next; continue; } do { cur = cur->parent; if (cur == NULL) break; if (cur == root) { cur = NULL; break; } if (cur->next != NULL) { cur = cur->next; break; } } while (cur != NULL); } if (delete != NULL) { xmlUnlinkNode(delete); xmlFreeNode(delete); delete = NULL; } } /** * xmlRelaxNGCleanupDoc: * @ctxt: a Relax-NG parser context * @doc: an xmldocPtr document pointer * * Cleanup the document from unwanted nodes for parsing, resolve * Include and externalRef lookups. * * Returns the cleaned up document or NULL in case of error */ static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) { xmlNodePtr root; /* * Extract the root */ root = xmlDocGetRootElement(doc); if (root == NULL) { xmlRngPErr(ctxt, (xmlNodePtr) doc, XML_RNGP_EMPTY, "xmlRelaxNGParse: %s is empty\n", ctxt->URL, NULL); return (NULL); } xmlRelaxNGCleanupTree(ctxt, root); return (doc); } /** * xmlRelaxNGParse: * @ctxt: a Relax-NG parser context * * parse a schema definition resource and build an internal * XML Shema struture which can be used to validate instances. * * Returns the internal XML RelaxNG structure built from the resource or * NULL in case of error */ xmlRelaxNGPtr xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt) { xmlRelaxNGPtr ret = NULL; xmlDocPtr doc; xmlNodePtr root; xmlRelaxNGInitTypes(); if (ctxt == NULL) return (NULL); /* * First step is to parse the input document into an DOM/Infoset */ if (ctxt->URL != NULL) { doc = xmlReadFile((const char *) ctxt->URL,NULL,0); if (doc == NULL) { xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR, "xmlRelaxNGParse: could not load %s\n", ctxt->URL, NULL); return (NULL); } } else if (ctxt->buffer != NULL) { doc = xmlReadMemory(ctxt->buffer, ctxt->size,NULL,NULL,0); if (doc == NULL) { xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR, "xmlRelaxNGParse: could not parse schemas\n", NULL, NULL); return (NULL); } doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer"); ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer"); } else if (ctxt->document != NULL) { doc = ctxt->document; } else { xmlRngPErr(ctxt, NULL, XML_RNGP_EMPTY, "xmlRelaxNGParse: nothing to parse\n", NULL, NULL); return (NULL); } ctxt->document = doc; /* * Some preprocessing of the document content */ doc = xmlRelaxNGCleanupDoc(ctxt, doc); if (doc == NULL) { xmlFreeDoc(ctxt->document); ctxt->document = NULL; return (NULL); } /* * Then do the parsing for good */ root = xmlDocGetRootElement(doc); if (root == NULL) { xmlRngPErr(ctxt, (xmlNodePtr) doc, XML_RNGP_EMPTY, "xmlRelaxNGParse: %s is empty\n", (ctxt->URL ? ctxt->URL : BAD_CAST "schemas"), NULL); xmlFreeDoc(ctxt->document); ctxt->document = NULL; return (NULL); } ret = xmlRelaxNGParseDocument(ctxt, root); if (ret == NULL) { xmlFreeDoc(ctxt->document); ctxt->document = NULL; return (NULL); } /* * Check the ref/defines links */ /* * try to preprocess interleaves */ if (ctxt->interleaves != NULL) { xmlHashScan(ctxt->interleaves, (xmlHashScanner) xmlRelaxNGComputeInterleaves, ctxt); } /* * if there was a parsing error return NULL */ if (ctxt->nbErrors > 0) { xmlRelaxNGFree(ret); ctxt->document = NULL; xmlFreeDoc(doc); return (NULL); } /* * try to compile (parts of) the schemas */ if ((ret->topgrammar != NULL) && (ret->topgrammar->start != NULL)) { if (ret->topgrammar->start->type != XML_RELAXNG_START) { xmlRelaxNGDefinePtr def; def = xmlRelaxNGNewDefine(ctxt, NULL); if (def != NULL) { def->type = XML_RELAXNG_START; def->content = ret->topgrammar->start; ret->topgrammar->start = def; } } xmlRelaxNGTryCompile(ctxt, ret->topgrammar->start); } /* * Transfer the pointer for cleanup at the schema level. */ ret->doc = doc; ctxt->document = NULL; ret->documents = ctxt->documents; ctxt->documents = NULL; ret->includes = ctxt->includes; ctxt->includes = NULL; ret->defNr = ctxt->defNr; ret->defTab = ctxt->defTab; ctxt->defTab = NULL; if (ctxt->idref == 1) ret->idref = 1; return (ret); } /** * xmlRelaxNGSetParserErrors: * @ctxt: a Relax-NG validation context * @err: the error callback * @warn: the warning callback * @ctx: contextual data for the callbacks * * Set the callback functions used to handle errors for a validation context */ void xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGValidityErrorFunc err, xmlRelaxNGValidityWarningFunc warn, void *ctx) { if (ctxt == NULL) return; ctxt->error = err; ctxt->warning = warn; ctxt->serror = NULL; ctxt->userData = ctx; } /** * xmlRelaxNGGetParserErrors: * @ctxt: a Relax-NG validation context * @err: the error callback result * @warn: the warning callback result * @ctx: contextual data for the callbacks result * * Get the callback information used to handle errors for a validation context * * Returns -1 in case of failure, 0 otherwise. */ int xmlRelaxNGGetParserErrors(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGValidityErrorFunc * err, xmlRelaxNGValidityWarningFunc * warn, void **ctx) { if (ctxt == NULL) return (-1); if (err != NULL) *err = ctxt->error; if (warn != NULL) *warn = ctxt->warning; if (ctx != NULL) *ctx = ctxt->userData; return (0); } /** * xmlRelaxNGSetParserStructuredErrors: * @ctxt: a Relax-NG parser context * @serror: the error callback * @ctx: contextual data for the callbacks * * Set the callback functions used to handle errors for a parsing context */ void xmlRelaxNGSetParserStructuredErrors(xmlRelaxNGParserCtxtPtr ctxt, xmlStructuredErrorFunc serror, void *ctx) { if (ctxt == NULL) return; ctxt->serror = serror; ctxt->error = NULL; ctxt->warning = NULL; ctxt->userData = ctx; } #ifdef LIBXML_OUTPUT_ENABLED /************************************************************************ * * * Dump back a compiled form * * * ************************************************************************/ static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define); /** * xmlRelaxNGDumpDefines: * @output: the file output * @defines: a list of define structures * * Dump a RelaxNG structure back */ static void xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) { while (defines != NULL) { xmlRelaxNGDumpDefine(output, defines); defines = defines->next; } } /** * xmlRelaxNGDumpDefine: * @output: the file output * @define: a define structure * * Dump a RelaxNG structure back */ static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) { if (define == NULL) return; switch (define->type) { case XML_RELAXNG_EMPTY: fprintf(output, "
\n"); break; case XML_RELAXNG_NOT_ALLOWED: fprintf(output, "
\n"); break; case XML_RELAXNG_TEXT: fprintf(output, "
\n"); break; case XML_RELAXNG_ELEMENT: fprintf(output, "
\n"); if (define->name != NULL) { fprintf(output, "
ns != NULL) fprintf(output, " ns=\"%s\"", define->ns); fprintf(output, ">%s
\n", define->name); } xmlRelaxNGDumpDefines(output, define->attrs); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_LIST: fprintf(output, "
\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_ONEORMORE: fprintf(output, "
\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_ZEROORMORE: fprintf(output, "
\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_CHOICE: fprintf(output, "
\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_GROUP: fprintf(output, "
\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_INTERLEAVE: fprintf(output, "
\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_OPTIONAL: fprintf(output, "
\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_ATTRIBUTE: fprintf(output, "
\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_DEF: fprintf(output, "
name != NULL) fprintf(output, " name=\"%s\"", define->name); fprintf(output, ">\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_REF: fprintf(output, "
name != NULL) fprintf(output, " name=\"%s\"", define->name); fprintf(output, ">\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_PARENTREF: fprintf(output, "
name != NULL) fprintf(output, " name=\"%s\"", define->name); fprintf(output, ">\n"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_EXTERNALREF: fprintf(output, "
"); xmlRelaxNGDumpDefines(output, define->content); fprintf(output, "
\n"); break; case XML_RELAXNG_DATATYPE: case XML_RELAXNG_VALUE: TODO break; case XML_RELAXNG_START: case XML_RELAXNG_EXCEPT: case XML_RELAXNG_PARAM: TODO break; case XML_RELAXNG_NOOP: xmlRelaxNGDumpDefines(output, define->content); break; } } /** * xmlRelaxNGDumpGrammar: * @output: the file output * @grammar: a grammar structure * @top: is this a top grammar * * Dump a RelaxNG structure back */ static void xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top) { if (grammar == NULL) return; fprintf(output, "
combine) { case XML_RELAXNG_COMBINE_UNDEFINED: break; case XML_RELAXNG_COMBINE_CHOICE: fprintf(output, " combine=\"choice\""); break; case XML_RELAXNG_COMBINE_INTERLEAVE: fprintf(output, " combine=\"interleave\""); break; default: fprintf(output, " "); } fprintf(output, ">\n"); if (grammar->start == NULL) { fprintf(output, " "); } else { fprintf(output, "
\n"); xmlRelaxNGDumpDefine(output, grammar->start); fprintf(output, "
\n"); } /* TODO ? Dump the defines ? */ fprintf(output, "
\n"); } /** * xmlRelaxNGDump: * @output: the file output * @schema: a schema structure * * Dump a RelaxNG structure back */ void xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema) { if (output == NULL) return; if (schema == NULL) { fprintf(output, "RelaxNG empty or failed to compile\n"); return; } fprintf(output, "RelaxNG: "); if (schema->doc == NULL) { fprintf(output, "no document\n"); } else if (schema->doc->URL != NULL) { fprintf(output, "%s\n", schema->doc->URL); } else { fprintf(output, "\n"); } if (schema->topgrammar == NULL) { fprintf(output, "RelaxNG has no top grammar\n"); return; } xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1); } /** * xmlRelaxNGDumpTree: * @output: the file output * @schema: a schema structure * * Dump the transformed RelaxNG tree. */ void xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema) { if (output == NULL) return; if (schema == NULL) { fprintf(output, "RelaxNG empty or failed to compile\n"); return; } if (schema->doc == NULL) { fprintf(output, "no document\n"); } else { xmlDocDump(output, schema->doc); } } #endif /* LIBXML_OUTPUT_ENABLED */ /************************************************************************ * * * Validation of compiled content * * * ************************************************************************/ static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr define); /** * xmlRelaxNGValidateCompiledCallback: * @exec: the regular expression instance * @token: the token which matched * @transdata: callback data, the define for the subelement if available @ @inputdata: callback data, the Relax NG validation context * * Handle the callback and if needed validate the element children. */ static void xmlRelaxNGValidateCompiledCallback(xmlRegExecCtxtPtr exec ATTRIBUTE_UNUSED, const xmlChar * token, void *transdata, void *inputdata) { xmlRelaxNGValidCtxtPtr ctxt = (xmlRelaxNGValidCtxtPtr) inputdata; xmlRelaxNGDefinePtr define = (xmlRelaxNGDefinePtr) transdata; int ret; #ifdef DEBUG_COMPILE xmlGenericError(xmlGenericErrorContext, "Compiled callback for: '%s'\n", token); #endif if (ctxt == NULL) { fprintf(stderr, "callback on %s missing context\n", token); return; } if (define == NULL) { if (token[0] == '#') return; fprintf(stderr, "callback on %s missing define\n", token); if ((ctxt != NULL) && (ctxt->errNo == XML_RELAXNG_OK)) ctxt->errNo = XML_RELAXNG_ERR_INTERNAL; return; } if ((ctxt == NULL) || (define == NULL)) { fprintf(stderr, "callback on %s missing info\n", token); if ((ctxt != NULL) && (ctxt->errNo == XML_RELAXNG_OK)) ctxt->errNo = XML_RELAXNG_ERR_INTERNAL; return; } else if (define->type != XML_RELAXNG_ELEMENT) { fprintf(stderr, "callback on %s define is not element\n", token); if (ctxt->errNo == XML_RELAXNG_OK) ctxt->errNo = XML_RELAXNG_ERR_INTERNAL; return; } ret = xmlRelaxNGValidateDefinition(ctxt, define); if (ret != 0) ctxt->perr = ret; } /** * xmlRelaxNGValidateCompiledContent: * @ctxt: the RelaxNG validation context * @regexp: the regular expression as compiled * @content: list of children to test against the regexp * * Validate the content model of an element or start using the regexp * * Returns 0 in case of success, -1 in case of error. */ static int xmlRelaxNGValidateCompiledContent(xmlRelaxNGValidCtxtPtr ctxt, xmlRegexpPtr regexp, xmlNodePtr content) { xmlRegExecCtxtPtr exec; xmlNodePtr cur; int ret = 0; int oldperr; if ((ctxt == NULL) || (regexp == NULL)) return (-1); oldperr = ctxt->perr; exec = xmlRegNewExecCtxt(regexp, xmlRelaxNGValidateCompiledCallback, ctxt); ctxt->perr = 0; cur = content; while (cur != NULL) { ctxt->state->seq = cur; switch (cur->type) { case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: if (xmlIsBlankNode(cur)) break; ret = xmlRegExecPushString(exec, BAD_CAST "#text", ctxt); if (ret < 0) { VALID_ERR2(XML_RELAXNG_ERR_TEXTWRONG, cur->parent->name); } break; case XML_ELEMENT_NODE: if (cur->ns != NULL) { ret = xmlRegExecPushString2(exec, cur->name, cur->ns->href, ctxt); } else { ret = xmlRegExecPushString(exec, cur->name, ctxt); } if (ret < 0) { VALID_ERR2(XML_RELAXNG_ERR_ELEMWRONG, cur->name); } break; default: break; } if (ret < 0) break; /* * Switch to next element */ cur = cur->next; } ret = xmlRegExecPushString(exec, NULL, NULL); if (ret == 1) { ret = 0; ctxt->state->seq = NULL; } else if (ret == 0) { /* * TODO: get some of the names needed to exit the current state of exec */ VALID_ERR2(XML_RELAXNG_ERR_NOELEM, BAD_CAST ""); ret = -1; if ((ctxt->flags & FLAGS_IGNORABLE) == 0) xmlRelaxNGDumpValidError(ctxt); } else { ret = -1; } xmlRegFreeExecCtxt(exec); /* * There might be content model errors outside of the pure * regexp validation, e.g. for attribute values. */ if ((ret == 0) && (ctxt->perr != 0)) { ret = ctxt->perr; } ctxt->perr = oldperr; return (ret); } /************************************************************************ * * * Progressive validation of when possible * * * ************************************************************************/ static int xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr defines); static int xmlRelaxNGValidateElementEnd(xmlRelaxNGValidCtxtPtr ctxt, int dolog); static void xmlRelaxNGLogBestError(xmlRelaxNGValidCtxtPtr ctxt); /** * xmlRelaxNGElemPush: * @ctxt: the validation context * @exec: the regexp runtime for the new content model * * Push a new regexp for the current node content model on the stack * * Returns 0 in case of success and -1 in case of error. */ static int xmlRelaxNGElemPush(xmlRelaxNGValidCtxtPtr ctxt, xmlRegExecCtxtPtr exec) { if (ctxt->elemTab == NULL) { ctxt->elemMax = 10; ctxt->elemTab = (xmlRegExecCtxtPtr *) xmlMalloc(ctxt->elemMax * sizeof (xmlRegExecCtxtPtr)); if (ctxt->elemTab == NULL) { xmlRngVErrMemory(ctxt, "validating\n"); return (-1); } } if (ctxt->elemNr >= ctxt->elemMax) { ctxt->elemMax *= 2; ctxt->elemTab = (xmlRegExecCtxtPtr *) xmlRealloc(ctxt->elemTab, ctxt->elemMax * sizeof (xmlRegExecCtxtPtr)); if (ctxt->elemTab == NULL) { xmlRngVErrMemory(ctxt, "validating\n"); return (-1); } } ctxt->elemTab[ctxt->elemNr++] = exec; ctxt->elem = exec; return (0); } /** * xmlRelaxNGElemPop: * @ctxt: the validation context * * Pop the regexp of the current node content model from the stack * * Returns the exec or NULL if empty */ static xmlRegExecCtxtPtr xmlRelaxNGElemPop(xmlRelaxNGValidCtxtPtr ctxt) { xmlRegExecCtxtPtr ret; if (ctxt->elemNr <= 0) return (NULL); ctxt->elemNr--; ret = ctxt->elemTab[ctxt->elemNr]; ctxt->elemTab[ctxt->elemNr] = NULL; if (ctxt->elemNr > 0) ctxt->elem = ctxt->elemTab[ctxt->elemNr - 1]; else ctxt->elem = NULL; return (ret); } /** * xmlRelaxNGValidateProgressiveCallback: * @exec: the regular expression instance * @token: the token which matched * @transdata: callback data, the define for the subelement if available @ @inputdata: callback data, the Relax NG validation context * * Handle the callback and if needed validate the element children. * some of the in/out informations are passed via the context in @inputdata. */ static void xmlRelaxNGValidateProgressiveCallback(xmlRegExecCtxtPtr exec ATTRIBUTE_UNUSED, const xmlChar * token, void *transdata, void *inputdata) { xmlRelaxNGValidCtxtPtr ctxt = (xmlRelaxNGValidCtxtPtr) inputdata; xmlRelaxNGDefinePtr define = (xmlRelaxNGDefinePtr) transdata; xmlRelaxNGValidStatePtr state, oldstate; xmlNodePtr node; int ret = 0, oldflags; #ifdef DEBUG_PROGRESSIVE xmlGenericError(xmlGenericErrorContext, "Progressive callback for: '%s'\n", token); #endif if (ctxt == NULL) { fprintf(stderr, "callback on %s missing context\n", token); return; } node = ctxt->pnode; ctxt->pstate = 1; if (define == NULL) { if (token[0] == '#') return; fprintf(stderr, "callback on %s missing define\n", token); if ((ctxt != NULL) && (ctxt->errNo == XML_RELAXNG_OK)) ctxt->errNo = XML_RELAXNG_ERR_INTERNAL; ctxt->pstate = -1; return; } if ((ctxt == NULL) || (define == NULL)) { fprintf(stderr, "callback on %s missing info\n", token); if ((ctxt != NULL) && (ctxt->errNo == XML_RELAXNG_OK)) ctxt->errNo = XML_RELAXNG_ERR_INTERNAL; ctxt->pstate = -1; return; } else if (define->type != XML_RELAXNG_ELEMENT) { fprintf(stderr, "callback on %s define is not element\n", token); if (ctxt->errNo == XML_RELAXNG_OK) ctxt->errNo = XML_RELAXNG_ERR_INTERNAL; ctxt->pstate = -1; return; } if (node->type != XML_ELEMENT_NODE) { VALID_ERR(XML_RELAXNG_ERR_NOTELEM); if ((ctxt->flags & FLAGS_IGNORABLE) == 0) xmlRelaxNGDumpValidError(ctxt); ctxt->pstate = -1; return; } if (define->contModel == NULL) { /* * this node cannot be validated in a streamable fashion */ #ifdef DEBUG_PROGRESSIVE xmlGenericError(xmlGenericErrorContext, "Element '%s' validation is not streamable\n", token); #endif ctxt->pstate = 0; ctxt->pdef = define; return; } exec = xmlRegNewExecCtxt(define->contModel, xmlRelaxNGValidateProgressiveCallback, ctxt); if (exec == NULL) { ctxt->pstate = -1; return; } xmlRelaxNGElemPush(ctxt, exec); /* * Validate the attributes part of the content. */ state = xmlRelaxNGNewValidState(ctxt, node); if (state == NULL) { ctxt->pstate = -1; return; } oldstate = ctxt->state; ctxt->state = state; if (define->attrs != NULL) { ret = xmlRelaxNGValidateAttributeList(ctxt, define->attrs); if (ret != 0) { ctxt->pstate = -1; VALID_ERR2(XML_RELAXNG_ERR_ATTRVALID, node->name); } } if (ctxt->state != NULL) { ctxt->state->seq = NULL; ret = xmlRelaxNGValidateElementEnd(ctxt, 1); if (ret != 0) { ctxt->pstate = -1; } xmlRelaxNGFreeValidState(ctxt, ctxt->state); } else if (ctxt->states != NULL) { int tmp = -1, i; oldflags = ctxt->flags; for (i = 0; i < ctxt->states->nbState; i++) { state = ctxt->states->tabState[i]; ctxt->state = state; ctxt->state->seq = NULL; if (xmlRelaxNGValidateElementEnd(ctxt, 0) == 0) { tmp = 0; break; } } if (tmp != 0) { /* * validation error, log the message for the "best" one */ ctxt->flags |= FLAGS_IGNORABLE; xmlRelaxNGLogBestError(ctxt); } for (i = 0; i < ctxt->states->nbState; i++) { xmlRelaxNGFreeValidState(ctxt, ctxt->states->tabState[i]); } xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->states = NULL; if ((ret == 0) && (tmp == -1)) ctxt->pstate = -1; ctxt->flags = oldflags; } if (ctxt->pstate == -1) { if ((ctxt->flags & FLAGS_IGNORABLE) == 0) { xmlRelaxNGDumpValidError(ctxt); } } ctxt->state = oldstate; } /** * xmlRelaxNGValidatePushElement: * @ctxt: the validation context * @doc: a document instance * @elem: an element instance * * Push a new element start on the RelaxNG validation stack. * * returns 1 if no validation problem was found or 0 if validating the * element requires a full node, and -1 in case of error. */ int xmlRelaxNGValidatePushElement(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr elem) { int ret = 1; if ((ctxt == NULL) || (elem == NULL)) return (-1); #ifdef DEBUG_PROGRESSIVE xmlGenericError(xmlGenericErrorContext, "PushElem %s\n", elem->name); #endif if (ctxt->elem == 0) { xmlRelaxNGPtr schema; xmlRelaxNGGrammarPtr grammar; xmlRegExecCtxtPtr exec; xmlRelaxNGDefinePtr define; schema = ctxt->schema; if (schema == NULL) { VALID_ERR(XML_RELAXNG_ERR_NOGRAMMAR); return (-1); } grammar = schema->topgrammar; if ((grammar == NULL) || (grammar->start == NULL)) { VALID_ERR(XML_RELAXNG_ERR_NOGRAMMAR); return (-1); } define = grammar->start; if (define->contModel == NULL) { ctxt->pdef = define; return (0); } exec = xmlRegNewExecCtxt(define->contModel, xmlRelaxNGValidateProgressiveCallback, ctxt); if (exec == NULL) { return (-1); } xmlRelaxNGElemPush(ctxt, exec); } ctxt->pnode = elem; ctxt->pstate = 0; if (elem->ns != NULL) { ret = xmlRegExecPushString2(ctxt->elem, elem->name, elem->ns->href, ctxt); } else { ret = xmlRegExecPushString(ctxt->elem, elem->name, ctxt); } if (ret < 0) { VALID_ERR2(XML_RELAXNG_ERR_ELEMWRONG, elem->name); } else { if (ctxt->pstate == 0) ret = 0; else if (ctxt->pstate < 0) ret = -1; else ret = 1; } #ifdef DEBUG_PROGRESSIVE if (ret < 0) xmlGenericError(xmlGenericErrorContext, "PushElem %s failed\n", elem->name); #endif return (ret); } /** * xmlRelaxNGValidatePushCData: * @ctxt: the RelaxNG validation context * @data: some character data read * @len: the lenght of the data * * check the CData parsed for validation in the current stack * * returns 1 if no validation problem was found or -1 otherwise */ int xmlRelaxNGValidatePushCData(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar * data, int len ATTRIBUTE_UNUSED) { int ret = 1; if ((ctxt == NULL) || (ctxt->elem == NULL) || (data == NULL)) return (-1); #ifdef DEBUG_PROGRESSIVE xmlGenericError(xmlGenericErrorContext, "CDATA %s %d\n", data, len); #endif while (*data != 0) { if (!IS_BLANK_CH(*data)) break; data++; } if (*data == 0) return (1); ret = xmlRegExecPushString(ctxt->elem, BAD_CAST "#text", ctxt); if (ret < 0) { VALID_ERR2(XML_RELAXNG_ERR_TEXTWRONG, BAD_CAST " TODO "); #ifdef DEBUG_PROGRESSIVE xmlGenericError(xmlGenericErrorContext, "CDATA failed\n"); #endif return (-1); } return (1); } /** * xmlRelaxNGValidatePopElement: * @ctxt: the RelaxNG validation context * @doc: a document instance * @elem: an element instance * * Pop the element end from the RelaxNG validation stack. * * returns 1 if no validation problem was found or 0 otherwise */ int xmlRelaxNGValidatePopElement(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr elem) { int ret; xmlRegExecCtxtPtr exec; if ((ctxt == NULL) || (ctxt->elem == NULL) || (elem == NULL)) return (-1); #ifdef DEBUG_PROGRESSIVE xmlGenericError(xmlGenericErrorContext, "PopElem %s\n", elem->name); #endif /* * verify that we reached a terminal state of the content model. */ exec = xmlRelaxNGElemPop(ctxt); ret = xmlRegExecPushString(exec, NULL, NULL); if (ret == 0) { /* * TODO: get some of the names needed to exit the current state of exec */ VALID_ERR2(XML_RELAXNG_ERR_NOELEM, BAD_CAST ""); ret = -1; } else if (ret < 0) { ret = -1; } else { ret = 1; } xmlRegFreeExecCtxt(exec); #ifdef DEBUG_PROGRESSIVE if (ret < 0) xmlGenericError(xmlGenericErrorContext, "PopElem %s failed\n", elem->name); #endif return (ret); } /** * xmlRelaxNGValidateFullElement: * @ctxt: the validation context * @doc: a document instance * @elem: an element instance * * Validate a full subtree when xmlRelaxNGValidatePushElement() returned * 0 and the content of the node has been expanded. * * returns 1 if no validation problem was found or -1 in case of error. */ int xmlRelaxNGValidateFullElement(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr elem) { int ret; xmlRelaxNGValidStatePtr state; if ((ctxt == NULL) || (ctxt->pdef == NULL) || (elem == NULL)) return (-1); #ifdef DEBUG_PROGRESSIVE xmlGenericError(xmlGenericErrorContext, "FullElem %s\n", elem->name); #endif state = xmlRelaxNGNewValidState(ctxt, elem->parent); if (state == NULL) { return (-1); } state->seq = elem; ctxt->state = state; ctxt->errNo = XML_RELAXNG_OK; ret = xmlRelaxNGValidateDefinition(ctxt, ctxt->pdef); if ((ret != 0) || (ctxt->errNo != XML_RELAXNG_OK)) ret = -1; else ret = 1; xmlRelaxNGFreeValidState(ctxt, state); ctxt->state = NULL; #ifdef DEBUG_PROGRESSIVE if (ret < 0) xmlGenericError(xmlGenericErrorContext, "FullElem %s failed\n", elem->name); #endif return (ret); } /************************************************************************ * * * Generic interpreted validation implementation * * * ************************************************************************/ static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr define); /** * xmlRelaxNGSkipIgnored: * @ctxt: a schema validation context * @node: the top node. * * Skip ignorable nodes in that context * * Returns the new sibling or NULL in case of error. */ static xmlNodePtr xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr node) { /* * TODO complete and handle entities */ while ((node != NULL) && ((node->type == XML_COMMENT_NODE) || (node->type == XML_PI_NODE) || (node->type == XML_XINCLUDE_START) || (node->type == XML_XINCLUDE_END) || (((node->type == XML_TEXT_NODE) || (node->type == XML_CDATA_SECTION_NODE)) && ((ctxt->flags & FLAGS_MIXED_CONTENT) || (IS_BLANK_NODE(node)))))) { node = node->next; } return (node); } /** * xmlRelaxNGNormalize: * @ctxt: a schema validation context * @str: the string to normalize * * Implements the normalizeWhiteSpace( s ) function from * section 6.2.9 of the spec * * Returns the new string or NULL in case of error. */ static xmlChar * xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar * str) { xmlChar *ret, *p; const xmlChar *tmp; int len; if (str == NULL) return (NULL); tmp = str; while (*tmp != 0) tmp++; len = tmp - str; ret = (xmlChar *) xmlMallocAtomic((len + 1) * sizeof(xmlChar)); if (ret == NULL) { xmlRngVErrMemory(ctxt, "validating\n"); return (NULL); } p = ret; while (IS_BLANK_CH(*str)) str++; while (*str != 0) { if (IS_BLANK_CH(*str)) { while (IS_BLANK_CH(*str)) str++; if (*str == 0) break; *p++ = ' '; } else *p++ = *str++; } *p = 0; return (ret); } /** * xmlRelaxNGValidateDatatype: * @ctxt: a Relax-NG validation context * @value: the string value * @type: the datatype definition * @node: the node * * Validate the given value against the dataype * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar * value, xmlRelaxNGDefinePtr define, xmlNodePtr node) { int ret, tmp; xmlRelaxNGTypeLibraryPtr lib; void *result = NULL; xmlRelaxNGDefinePtr cur; if ((define == NULL) || (define->data == NULL)) { return (-1); } lib = (xmlRelaxNGTypeLibraryPtr) define->data; if (lib->check != NULL) { if ((define->attrs != NULL) && (define->attrs->type == XML_RELAXNG_PARAM)) { ret = lib->check(lib->data, define->name, value, &result, node); } else { ret = lib->check(lib->data, define->name, value, NULL, node); } } else ret = -1; if (ret < 0) { VALID_ERR2(XML_RELAXNG_ERR_TYPE, define->name); if ((result != NULL) && (lib != NULL) && (lib->freef != NULL)) lib->freef(lib->data, result); return (-1); } else if (ret == 1) { ret = 0; } else if (ret == 2) { VALID_ERR2P(XML_RELAXNG_ERR_DUPID, value); } else { VALID_ERR3P(XML_RELAXNG_ERR_TYPEVAL, define->name, value); ret = -1; } cur = define->attrs; while ((ret == 0) && (cur != NULL) && (cur->type == XML_RELAXNG_PARAM)) { if (lib->facet != NULL) { tmp = lib->facet(lib->data, define->name, cur->name, cur->value, value, result); if (tmp != 0) ret = -1; } cur = cur->next; } if ((ret == 0) && (define->content != NULL)) { const xmlChar *oldvalue, *oldendvalue; oldvalue = ctxt->state->value; oldendvalue = ctxt->state->endvalue; ctxt->state->value = (xmlChar *) value; ctxt->state->endvalue = NULL; ret = xmlRelaxNGValidateValue(ctxt, define->content); ctxt->state->value = (xmlChar *) oldvalue; ctxt->state->endvalue = (xmlChar *) oldendvalue; } if ((result != NULL) && (lib != NULL) && (lib->freef != NULL)) lib->freef(lib->data, result); return (ret); } /** * xmlRelaxNGNextValue: * @ctxt: a Relax-NG validation context * * Skip to the next value when validating within a list * * Returns 0 if the operation succeeded or an error code. */ static int xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) { xmlChar *cur; cur = ctxt->state->value; if ((cur == NULL) || (ctxt->state->endvalue == NULL)) { ctxt->state->value = NULL; ctxt->state->endvalue = NULL; return (0); } while (*cur != 0) cur++; while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++; if (cur == ctxt->state->endvalue) ctxt->state->value = NULL; else ctxt->state->value = cur; return (0); } /** * xmlRelaxNGValidateValueList: * @ctxt: a Relax-NG validation context * @defines: the list of definitions to verify * * Validate the given set of definitions for the current value * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr defines) { int ret = 0; while (defines != NULL) { ret = xmlRelaxNGValidateValue(ctxt, defines); if (ret != 0) break; defines = defines->next; } return (ret); } /** * xmlRelaxNGValidateValue: * @ctxt: a Relax-NG validation context * @define: the definition to verify * * Validate the given definition for the current value * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr define) { int ret = 0, oldflags; xmlChar *value; value = ctxt->state->value; switch (define->type) { case XML_RELAXNG_EMPTY:{ if ((value != NULL) && (value[0] != 0)) { int idx = 0; while (IS_BLANK_CH(value[idx])) idx++; if (value[idx] != 0) ret = -1; } break; } case XML_RELAXNG_TEXT: break; case XML_RELAXNG_VALUE:{ if (!xmlStrEqual(value, define->value)) { if (define->name != NULL) { xmlRelaxNGTypeLibraryPtr lib; lib = (xmlRelaxNGTypeLibraryPtr) define->data; if ((lib != NULL) && (lib->comp != NULL)) { ret = lib->comp(lib->data, define->name, define->value, define->node, (void *) define->attrs, value, ctxt->state->node); } else ret = -1; if (ret < 0) { VALID_ERR2(XML_RELAXNG_ERR_TYPECMP, define->name); return (-1); } else if (ret == 1) { ret = 0; } else { ret = -1; } } else { xmlChar *nval, *nvalue; /* * TODO: trivial optimizations are possible by * computing at compile-time */ nval = xmlRelaxNGNormalize(ctxt, define->value); nvalue = xmlRelaxNGNormalize(ctxt, value); if ((nval == NULL) || (nvalue == NULL) || (!xmlStrEqual(nval, nvalue))) ret = -1; if (nval != NULL) xmlFree(nval); if (nvalue != NULL) xmlFree(nvalue); } } if (ret == 0) xmlRelaxNGNextValue(ctxt); break; } case XML_RELAXNG_DATATYPE:{ ret = xmlRelaxNGValidateDatatype(ctxt, value, define, ctxt->state->seq); if (ret == 0) xmlRelaxNGNextValue(ctxt); break; } case XML_RELAXNG_CHOICE:{ xmlRelaxNGDefinePtr list = define->content; xmlChar *oldvalue; oldflags = ctxt->flags; ctxt->flags |= FLAGS_IGNORABLE; oldvalue = ctxt->state->value; while (list != NULL) { ret = xmlRelaxNGValidateValue(ctxt, list); if (ret == 0) { break; } ctxt->state->value = oldvalue; list = list->next; } ctxt->flags = oldflags; if (ret != 0) { if ((ctxt->flags & FLAGS_IGNORABLE) == 0) xmlRelaxNGDumpValidError(ctxt); } else { if (ctxt->errNr > 0) xmlRelaxNGPopErrors(ctxt, 0); } break; } case XML_RELAXNG_LIST:{ xmlRelaxNGDefinePtr list = define->content; xmlChar *oldvalue, *oldend, *val, *cur; #ifdef DEBUG_LIST int nb_values = 0; #endif oldvalue = ctxt->state->value; oldend = ctxt->state->endvalue; val = xmlStrdup(oldvalue); if (val == NULL) { val = xmlStrdup(BAD_CAST ""); } if (val == NULL) { VALID_ERR(XML_RELAXNG_ERR_NOSTATE); return (-1); } cur = val; while (*cur != 0) { if (IS_BLANK_CH(*cur)) { *cur = 0; cur++; #ifdef DEBUG_LIST nb_values++; #endif while (IS_BLANK_CH(*cur)) *cur++ = 0; } else cur++; } #ifdef DEBUG_LIST xmlGenericError(xmlGenericErrorContext, "list value: '%s' found %d items\n", oldvalue, nb_values); nb_values = 0; #endif ctxt->state->endvalue = cur; cur = val; while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++; ctxt->state->value = cur; while (list != NULL) { if (ctxt->state->value == ctxt->state->endvalue) ctxt->state->value = NULL; ret = xmlRelaxNGValidateValue(ctxt, list); if (ret != 0) { #ifdef DEBUG_LIST xmlGenericError(xmlGenericErrorContext, "Failed to validate value: '%s' with %d rule\n", ctxt->state->value, nb_values); #endif break; } #ifdef DEBUG_LIST nb_values++; #endif list = list->next; } if ((ret == 0) && (ctxt->state->value != NULL) && (ctxt->state->value != ctxt->state->endvalue)) { VALID_ERR2(XML_RELAXNG_ERR_LISTEXTRA, ctxt->state->value); ret = -1; } xmlFree(val); ctxt->state->value = oldvalue; ctxt->state->endvalue = oldend; break; } case XML_RELAXNG_ONEORMORE: ret = xmlRelaxNGValidateValueList(ctxt, define->content); if (ret != 0) { break; } /* no break on purpose */ case XML_RELAXNG_ZEROORMORE:{ xmlChar *cur, *temp; oldflags = ctxt->flags; ctxt->flags |= FLAGS_IGNORABLE; cur = ctxt->state->value; temp = NULL; while ((cur != NULL) && (cur != ctxt->state->endvalue) && (temp != cur)) { temp = cur; ret = xmlRelaxNGValidateValueList(ctxt, define->content); if (ret != 0) { ctxt->state->value = temp; ret = 0; break; } cur = ctxt->state->value; } ctxt->flags = oldflags; if (ctxt->errNr > 0) xmlRelaxNGPopErrors(ctxt, 0); break; } case XML_RELAXNG_EXCEPT:{ xmlRelaxNGDefinePtr list; list = define->content; while (list != NULL) { ret = xmlRelaxNGValidateValue(ctxt, list); if (ret == 0) { ret = -1; break; } else ret = 0; list = list->next; } break; } case XML_RELAXNG_DEF: case XML_RELAXNG_GROUP:{ xmlRelaxNGDefinePtr list; list = define->content; while (list != NULL) { ret = xmlRelaxNGValidateValue(ctxt, list); if (ret != 0) { ret = -1; break; } else ret = 0; list = list->next; } break; } case XML_RELAXNG_REF: case XML_RELAXNG_PARENTREF: ret = xmlRelaxNGValidateValue(ctxt, define->content); break; default: TODO ret = -1; } return (ret); } /** * xmlRelaxNGValidateValueContent: * @ctxt: a Relax-NG validation context * @defines: the list of definitions to verify * * Validate the given definitions for the current value * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr defines) { int ret = 0; while (defines != NULL) { ret = xmlRelaxNGValidateValue(ctxt, defines); if (ret != 0) break; defines = defines->next; } return (ret); } /** * xmlRelaxNGAttributeMatch: * @ctxt: a Relax-NG validation context * @define: the definition to check * @prop: the attribute * * Check if the attribute matches the definition nameClass * * Returns 1 if the attribute matches, 0 if no, or -1 in case of error */ static int xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr define, xmlAttrPtr prop) { int ret; if (define->name != NULL) { if (!xmlStrEqual(define->name, prop->name)) return (0); } if (define->ns != NULL) { if (define->ns[0] == 0) { if (prop->ns != NULL) return (0); } else { if ((prop->ns == NULL) || (!xmlStrEqual(define->ns, prop->ns->href))) return (0); } } if (define->nameClass == NULL) return (1); define = define->nameClass; if (define->type == XML_RELAXNG_EXCEPT) { xmlRelaxNGDefinePtr list; list = define->content; while (list != NULL) { ret = xmlRelaxNGAttributeMatch(ctxt, list, prop); if (ret == 1) return (0); if (ret < 0) return (ret); list = list->next; } } else { TODO} return (1); } /** * xmlRelaxNGValidateAttribute: * @ctxt: a Relax-NG validation context * @define: the definition to verify * * Validate the given attribute definition for that node * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr define) { int ret = 0, i; xmlChar *value, *oldvalue; xmlAttrPtr prop = NULL, tmp; xmlNodePtr oldseq; if (ctxt->state->nbAttrLeft <= 0) return (-1); if (define->name != NULL) { for (i = 0; i < ctxt->state->nbAttrs; i++) { tmp = ctxt->state->attrs[i]; if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) { if ((((define->ns == NULL) || (define->ns[0] == 0)) && (tmp->ns == NULL)) || ((tmp->ns != NULL) && (xmlStrEqual(define->ns, tmp->ns->href)))) { prop = tmp; break; } } } if (prop != NULL) { value = xmlNodeListGetString(prop->doc, prop->children, 1); oldvalue = ctxt->state->value; oldseq = ctxt->state->seq; ctxt->state->seq = (xmlNodePtr) prop; ctxt->state->value = value; ctxt->state->endvalue = NULL; ret = xmlRelaxNGValidateValueContent(ctxt, define->content); if (ctxt->state->value != NULL) value = ctxt->state->value; if (value != NULL) xmlFree(value); ctxt->state->value = oldvalue; ctxt->state->seq = oldseq; if (ret == 0) { /* * flag the attribute as processed */ ctxt->state->attrs[i] = NULL; ctxt->state->nbAttrLeft--; } } else { ret = -1; } #ifdef DEBUG xmlGenericError(xmlGenericErrorContext, "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret); #endif } else { for (i = 0; i < ctxt->state->nbAttrs; i++) { tmp = ctxt->state->attrs[i]; if ((tmp != NULL) && (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) { prop = tmp; break; } } if (prop != NULL) { value = xmlNodeListGetString(prop->doc, prop->children, 1); oldvalue = ctxt->state->value; oldseq = ctxt->state->seq; ctxt->state->seq = (xmlNodePtr) prop; ctxt->state->value = value; ret = xmlRelaxNGValidateValueContent(ctxt, define->content); if (ctxt->state->value != NULL) value = ctxt->state->value; if (value != NULL) xmlFree(value); ctxt->state->value = oldvalue; ctxt->state->seq = oldseq; if (ret == 0) { /* * flag the attribute as processed */ ctxt->state->attrs[i] = NULL; ctxt->state->nbAttrLeft--; } } else { ret = -1; } #ifdef DEBUG if (define->ns != NULL) { xmlGenericError(xmlGenericErrorContext, "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n", define->ns, ret); } else { xmlGenericError(xmlGenericErrorContext, "xmlRelaxNGValidateAttribute(anyName): %d\n", ret); } #endif } return (ret); } /** * xmlRelaxNGValidateAttributeList: * @ctxt: a Relax-NG validation context * @define: the list of definition to verify * * Validate the given node against the list of attribute definitions * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr defines) { int ret = 0, res; int needmore = 0; xmlRelaxNGDefinePtr cur; cur = defines; while (cur != NULL) { if (cur->type == XML_RELAXNG_ATTRIBUTE) { if (xmlRelaxNGValidateAttribute(ctxt, cur) != 0) ret = -1; } else needmore = 1; cur = cur->next; } if (!needmore) return (ret); cur = defines; while (cur != NULL) { if (cur->type != XML_RELAXNG_ATTRIBUTE) { if ((ctxt->state != NULL) || (ctxt->states != NULL)) { res = xmlRelaxNGValidateDefinition(ctxt, cur); if (res < 0) ret = -1; } else { VALID_ERR(XML_RELAXNG_ERR_NOSTATE); return (-1); } if (res == -1) /* continues on -2 */ break; } cur = cur->next; } return (ret); } /** * xmlRelaxNGNodeMatchesList: * @node: the node * @list: a NULL terminated array of definitions * * Check if a node can be matched by one of the definitions * * Returns 1 if matches 0 otherwise */ static int xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr * list) { xmlRelaxNGDefinePtr cur; int i = 0, tmp; if ((node == NULL) || (list == NULL)) return (0); cur = list[i++]; while (cur != NULL) { if ((node->type == XML_ELEMENT_NODE) && (cur->type == XML_RELAXNG_ELEMENT)) { tmp = xmlRelaxNGElementMatch(NULL, cur, node); if (tmp == 1) return (1); } else if (((node->type == XML_TEXT_NODE) || (node->type == XML_CDATA_SECTION_NODE)) && (cur->type == XML_RELAXNG_TEXT)) { return (1); } cur = list[i++]; } return (0); } /** * xmlRelaxNGValidateInterleave: * @ctxt: a Relax-NG validation context * @define: the definition to verify * * Validate an interleave definition for a node. * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr define) { int ret = 0, i, nbgroups; int errNr = ctxt->errNr; int oldflags; xmlRelaxNGValidStatePtr oldstate; xmlRelaxNGPartitionPtr partitions; xmlRelaxNGInterleaveGroupPtr group = NULL; xmlNodePtr cur, start, last = NULL, lastchg = NULL, lastelem; xmlNodePtr *list = NULL, *lasts = NULL; if (define->data != NULL) { partitions = (xmlRelaxNGPartitionPtr) define->data; nbgroups = partitions->nbgroups; } else { VALID_ERR(XML_RELAXNG_ERR_INTERNODATA); return (-1); } /* * Optimizations for MIXED */ oldflags = ctxt->flags; if (define->dflags & IS_MIXED) { ctxt->flags |= FLAGS_MIXED_CONTENT; if (nbgroups == 2) { /* * this is a pure
case */ if (ctxt->state != NULL) ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, ctxt->state->seq); if (partitions->groups[0]->rule->type == XML_RELAXNG_TEXT) ret = xmlRelaxNGValidateDefinition(ctxt, partitions->groups[1]-> rule); else ret = xmlRelaxNGValidateDefinition(ctxt, partitions->groups[0]-> rule); if (ret == 0) { if (ctxt->state != NULL) ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, ctxt->state-> seq); } ctxt->flags = oldflags; return (ret); } } /* * Build arrays to store the first and last node of the chain * pertaining to each group */ list = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr)); if (list == NULL) { xmlRngVErrMemory(ctxt, "validating\n"); return (-1); } memset(list, 0, nbgroups * sizeof(xmlNodePtr)); lasts = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr)); if (lasts == NULL) { xmlRngVErrMemory(ctxt, "validating\n"); return (-1); } memset(lasts, 0, nbgroups * sizeof(xmlNodePtr)); /* * Walk the sequence of children finding the right group and * sorting them in sequences. */ cur = ctxt->state->seq; cur = xmlRelaxNGSkipIgnored(ctxt, cur); start = cur; while (cur != NULL) { ctxt->state->seq = cur; if ((partitions->triage != NULL) && (partitions->flags & IS_DETERMINIST)) { void *tmp = NULL; if ((cur->type == XML_TEXT_NODE) || (cur->type == XML_CDATA_SECTION_NODE)) { tmp = xmlHashLookup2(partitions->triage, BAD_CAST "#text", NULL); } else if (cur->type == XML_ELEMENT_NODE) { if (cur->ns != NULL) { tmp = xmlHashLookup2(partitions->triage, cur->name, cur->ns->href); if (tmp == NULL) tmp = xmlHashLookup2(partitions->triage, BAD_CAST "#any", cur->ns->href); } else tmp = xmlHashLookup2(partitions->triage, cur->name, NULL); if (tmp == NULL) tmp = xmlHashLookup2(partitions->triage, BAD_CAST "#any", NULL); } if (tmp == NULL) { i = nbgroups; } else { i = ((long) tmp) - 1; if (partitions->flags & IS_NEEDCHECK) { group = partitions->groups[i]; if (!xmlRelaxNGNodeMatchesList(cur, group->defs)) i = nbgroups; } } } else { for (i = 0; i < nbgroups; i++) { group = partitions->groups[i]; if (group == NULL) continue; if (xmlRelaxNGNodeMatchesList(cur, group->defs)) break; } } /* * We break as soon as an element not matched is found */ if (i >= nbgroups) { break; } if (lasts[i] != NULL) { lasts[i]->next = cur; lasts[i] = cur; } else { list[i] = cur; lasts[i] = cur; } if (cur->next != NULL) lastchg = cur->next; else lastchg = cur; cur = xmlRelaxNGSkipIgnored(ctxt, cur->next); } if (ret != 0) { VALID_ERR(XML_RELAXNG_ERR_INTERSEQ); ret = -1; goto done; } lastelem = cur; oldstate = ctxt->state; for (i = 0; i < nbgroups; i++) { ctxt->state = xmlRelaxNGCopyValidState(ctxt, oldstate); group = partitions->groups[i]; if (lasts[i] != NULL) { last = lasts[i]->next; lasts[i]->next = NULL; } ctxt->state->seq = list[i]; ret = xmlRelaxNGValidateDefinition(ctxt, group->rule); if (ret != 0) break; if (ctxt->state != NULL) { cur = ctxt->state->seq; cur = xmlRelaxNGSkipIgnored(ctxt, cur); xmlRelaxNGFreeValidState(ctxt, oldstate); oldstate = ctxt->state; ctxt->state = NULL; if (cur != NULL) { VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA, cur->name); ret = -1; ctxt->state = oldstate; goto done; } } else if (ctxt->states != NULL) { int j; int found = 0; int best = -1; int lowattr = -1; /* * PBM: what happen if there is attributes checks in the interleaves */ for (j = 0; j < ctxt->states->nbState; j++) { cur = ctxt->states->tabState[j]->seq; cur = xmlRelaxNGSkipIgnored(ctxt, cur); if (cur == NULL) { if (found == 0) { lowattr = ctxt->states->tabState[j]->nbAttrLeft; best = j; } found = 1; if (ctxt->states->tabState[j]->nbAttrLeft <= lowattr) { /* try to keep the latest one to mach old heuristic */ lowattr = ctxt->states->tabState[j]->nbAttrLeft; best = j; } if (lowattr == 0) break; } else if (found == 0) { if (lowattr == -1) { lowattr = ctxt->states->tabState[j]->nbAttrLeft; best = j; } else if (ctxt->states->tabState[j]->nbAttrLeft <= lowattr) { /* try to keep the latest one to mach old heuristic */ lowattr = ctxt->states->tabState[j]->nbAttrLeft; best = j; } } } /* * BIG PBM: here we pick only one restarting point :-( */ if (ctxt->states->nbState > 0) { xmlRelaxNGFreeValidState(ctxt, oldstate); if (best != -1) { oldstate = ctxt->states->tabState[best]; ctxt->states->tabState[best] = NULL; } else { oldstate = ctxt->states->tabState[ctxt->states->nbState - 1]; ctxt->states->tabState[ctxt->states->nbState - 1] = NULL; } } for (j = 0; j < ctxt->states->nbState ; j++) { xmlRelaxNGFreeValidState(ctxt, ctxt->states->tabState[j]); } xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->states = NULL; if (found == 0) { VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA, cur->name); ret = -1; ctxt->state = oldstate; goto done; } } else { ret = -1; break; } if (lasts[i] != NULL) { lasts[i]->next = last; } } if (ctxt->state != NULL) xmlRelaxNGFreeValidState(ctxt, ctxt->state); ctxt->state = oldstate; ctxt->state->seq = lastelem; if (ret != 0) { VALID_ERR(XML_RELAXNG_ERR_INTERSEQ); ret = -1; goto done; } done: ctxt->flags = oldflags; /* * builds the next links chain from the prev one */ cur = lastchg; while (cur != NULL) { if ((cur == start) || (cur->prev == NULL)) break; cur->prev->next = cur; cur = cur->prev; } if (ret == 0) { if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr); } xmlFree(list); xmlFree(lasts); return (ret); } /** * xmlRelaxNGValidateDefinitionList: * @ctxt: a Relax-NG validation context * @define: the list of definition to verify * * Validate the given node content against the (list) of definitions * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateDefinitionList(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr defines) { int ret = 0, res; if (defines == NULL) { VALID_ERR2(XML_RELAXNG_ERR_INTERNAL, BAD_CAST "NULL definition list"); return (-1); } while (defines != NULL) { if ((ctxt->state != NULL) || (ctxt->states != NULL)) { res = xmlRelaxNGValidateDefinition(ctxt, defines); if (res < 0) ret = -1; } else { VALID_ERR(XML_RELAXNG_ERR_NOSTATE); return (-1); } if (res == -1) /* continues on -2 */ break; defines = defines->next; } return (ret); } /** * xmlRelaxNGElementMatch: * @ctxt: a Relax-NG validation context * @define: the definition to check * @elem: the element * * Check if the element matches the definition nameClass * * Returns 1 if the element matches, 0 if no, or -1 in case of error */ static int xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr define, xmlNodePtr elem) { int ret = 0, oldflags = 0; if (define->name != NULL) { if (!xmlStrEqual(elem->name, define->name)) { VALID_ERR3(XML_RELAXNG_ERR_ELEMNAME, define->name, elem->name); return (0); } } if ((define->ns != NULL) && (define->ns[0] != 0)) { if (elem->ns == NULL) { VALID_ERR2(XML_RELAXNG_ERR_ELEMNONS, elem->name); return (0); } else if (!xmlStrEqual(elem->ns->href, define->ns)) { VALID_ERR3(XML_RELAXNG_ERR_ELEMWRONGNS, elem->name, define->ns); return (0); } } else if ((elem->ns != NULL) && (define->ns != NULL) && (define->name == NULL)) { VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS, elem->name); return (0); } else if ((elem->ns != NULL) && (define->name != NULL)) { VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS, define->name); return (0); } if (define->nameClass == NULL) return (1); define = define->nameClass; if (define->type == XML_RELAXNG_EXCEPT) { xmlRelaxNGDefinePtr list; if (ctxt != NULL) { oldflags = ctxt->flags; ctxt->flags |= FLAGS_IGNORABLE; } list = define->content; while (list != NULL) { ret = xmlRelaxNGElementMatch(ctxt, list, elem); if (ret == 1) { if (ctxt != NULL) ctxt->flags = oldflags; return (0); } if (ret < 0) { if (ctxt != NULL) ctxt->flags = oldflags; return (ret); } list = list->next; } ret = 1; if (ctxt != NULL) { ctxt->flags = oldflags; } } else if (define->type == XML_RELAXNG_CHOICE) { xmlRelaxNGDefinePtr list; if (ctxt != NULL) { oldflags = ctxt->flags; ctxt->flags |= FLAGS_IGNORABLE; } list = define->nameClass; while (list != NULL) { ret = xmlRelaxNGElementMatch(ctxt, list, elem); if (ret == 1) { if (ctxt != NULL) ctxt->flags = oldflags; return (1); } if (ret < 0) { if (ctxt != NULL) ctxt->flags = oldflags; return (ret); } list = list->next; } if (ctxt != NULL) { if (ret != 0) { if ((ctxt->flags & FLAGS_IGNORABLE) == 0) xmlRelaxNGDumpValidError(ctxt); } else { if (ctxt->errNr > 0) xmlRelaxNGPopErrors(ctxt, 0); } } ret = 0; if (ctxt != NULL) { ctxt->flags = oldflags; } } else { TODO ret = -1; } return (ret); } /** * xmlRelaxNGBestState: * @ctxt: a Relax-NG validation context * * Find the "best" state in the ctxt->states list of states to report * errors about. I.e. a state with no element left in the child list * or the one with the less attributes left. * This is called only if a falidation error was detected * * Returns the index of the "best" state or -1 in case of error */ static int xmlRelaxNGBestState(xmlRelaxNGValidCtxtPtr ctxt) { xmlRelaxNGValidStatePtr state; int i, tmp; int best = -1; int value = 1000000; if ((ctxt == NULL) || (ctxt->states == NULL) || (ctxt->states->nbState <= 0)) return (-1); for (i = 0; i < ctxt->states->nbState; i++) { state = ctxt->states->tabState[i]; if (state == NULL) continue; if (state->seq != NULL) { if ((best == -1) || (value > 100000)) { value = 100000; best = i; } } else { tmp = state->nbAttrLeft; if ((best == -1) || (value > tmp)) { value = tmp; best = i; } } } return (best); } /** * xmlRelaxNGLogBestError: * @ctxt: a Relax-NG validation context * * Find the "best" state in the ctxt->states list of states to report * errors about and log it. */ static void xmlRelaxNGLogBestError(xmlRelaxNGValidCtxtPtr ctxt) { int best; if ((ctxt == NULL) || (ctxt->states == NULL) || (ctxt->states->nbState <= 0)) return; best = xmlRelaxNGBestState(ctxt); if ((best >= 0) && (best < ctxt->states->nbState)) { ctxt->state = ctxt->states->tabState[best]; xmlRelaxNGValidateElementEnd(ctxt, 1); } } /** * xmlRelaxNGValidateElementEnd: * @ctxt: a Relax-NG validation context * @dolog: indicate that error logging should be done * * Validate the end of the element, implements check that * there is nothing left not consumed in the element content * or in the attribute list. * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateElementEnd(xmlRelaxNGValidCtxtPtr ctxt, int dolog) { int i; xmlRelaxNGValidStatePtr state; state = ctxt->state; if (state->seq != NULL) { state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq); if (state->seq != NULL) { if (dolog) { VALID_ERR3(XML_RELAXNG_ERR_EXTRACONTENT, state->node->name, state->seq->name); } return (-1); } } for (i = 0; i < state->nbAttrs; i++) { if (state->attrs[i] != NULL) { if (dolog) { VALID_ERR3(XML_RELAXNG_ERR_INVALIDATTR, state->attrs[i]->name, state->node->name); } return (-1 - i); } } return (0); } /** * xmlRelaxNGValidateState: * @ctxt: a Relax-NG validation context * @define: the definition to verify * * Validate the current state against the definition * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateState(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr define) { xmlNodePtr node; int ret = 0, i, tmp, oldflags, errNr; xmlRelaxNGValidStatePtr oldstate = NULL, state; if (define == NULL) { VALID_ERR(XML_RELAXNG_ERR_NODEFINE); return (-1); } if (ctxt->state != NULL) { node = ctxt->state->seq; } else { node = NULL; } #ifdef DEBUG for (i = 0; i < ctxt->depth; i++) xmlGenericError(xmlGenericErrorContext, " "); xmlGenericError(xmlGenericErrorContext, "Start validating %s ", xmlRelaxNGDefName(define)); if (define->name != NULL) xmlGenericError(xmlGenericErrorContext, "%s ", define->name); if ((node != NULL) && (node->name != NULL)) xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name); else xmlGenericError(xmlGenericErrorContext, "\n"); #endif ctxt->depth++; switch (define->type) { case XML_RELAXNG_EMPTY: node = xmlRelaxNGSkipIgnored(ctxt, node); ret = 0; break; case XML_RELAXNG_NOT_ALLOWED: ret = -1; break; case XML_RELAXNG_TEXT: while ((node != NULL) && ((node->type == XML_TEXT_NODE) || (node->type == XML_COMMENT_NODE) || (node->type == XML_PI_NODE) || (node->type == XML_CDATA_SECTION_NODE))) node = node->next; ctxt->state->seq = node; break; case XML_RELAXNG_ELEMENT: errNr = ctxt->errNr; node = xmlRelaxNGSkipIgnored(ctxt, node); if (node == NULL) { VALID_ERR2(XML_RELAXNG_ERR_NOELEM, define->name); ret = -1; if ((ctxt->flags & FLAGS_IGNORABLE) == 0) xmlRelaxNGDumpValidError(ctxt); break; } if (node->type != XML_ELEMENT_NODE) { VALID_ERR(XML_RELAXNG_ERR_NOTELEM); ret = -1; if ((ctxt->flags & FLAGS_IGNORABLE) == 0) xmlRelaxNGDumpValidError(ctxt); break; } /* * This node was already validated successfully against * this definition. */ if (node->psvi == define) { ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, node->next); if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr); if (ctxt->errNr != 0) { while ((ctxt->err != NULL) && (((ctxt->err->err == XML_RELAXNG_ERR_ELEMNAME) && (xmlStrEqual(ctxt->err->arg2, node->name))) || ((ctxt->err->err == XML_RELAXNG_ERR_ELEMEXTRANS) && (xmlStrEqual(ctxt->err->arg1, node->name))) || (ctxt->err->err == XML_RELAXNG_ERR_NOELEM) || (ctxt->err->err == XML_RELAXNG_ERR_NOTELEM))) xmlRelaxNGValidErrorPop(ctxt); } break; } ret = xmlRelaxNGElementMatch(ctxt, define, node); if (ret <= 0) { ret = -1; if ((ctxt->flags & FLAGS_IGNORABLE) == 0) xmlRelaxNGDumpValidError(ctxt); break; } ret = 0; if (ctxt->errNr != 0) { if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr); while ((ctxt->err != NULL) && (((ctxt->err->err == XML_RELAXNG_ERR_ELEMNAME) && (xmlStrEqual(ctxt->err->arg2, node->name))) || ((ctxt->err->err == XML_RELAXNG_ERR_ELEMEXTRANS) && (xmlStrEqual(ctxt->err->arg1, node->name))) || (ctxt->err->err == XML_RELAXNG_ERR_NOELEM) || (ctxt->err->err == XML_RELAXNG_ERR_NOTELEM))) xmlRelaxNGValidErrorPop(ctxt); } errNr = ctxt->errNr; oldflags = ctxt->flags; if (ctxt->flags & FLAGS_MIXED_CONTENT) { ctxt->flags -= FLAGS_MIXED_CONTENT; } state = xmlRelaxNGNewValidState(ctxt, node); if (state == NULL) { ret = -1; if ((ctxt->flags & FLAGS_IGNORABLE) == 0) xmlRelaxNGDumpValidError(ctxt); break; } oldstate = ctxt->state; ctxt->state = state; if (define->attrs != NULL) { tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs); if (tmp != 0) { ret = -1; VALID_ERR2(XML_RELAXNG_ERR_ATTRVALID, node->name); } } if (define->contModel != NULL) { xmlRelaxNGValidStatePtr nstate, tmpstate = ctxt->state; xmlRelaxNGStatesPtr tmpstates = ctxt->states; xmlNodePtr nseq; nstate = xmlRelaxNGNewValidState(ctxt, node); ctxt->state = nstate; ctxt->states = NULL; tmp = xmlRelaxNGValidateCompiledContent(ctxt, define->contModel, ctxt->state->seq); nseq = ctxt->state->seq; ctxt->state = tmpstate; ctxt->states = tmpstates; xmlRelaxNGFreeValidState(ctxt, nstate); #ifdef DEBUG_COMPILE xmlGenericError(xmlGenericErrorContext, "Validating content of '%s' : %d\n", define->name, tmp); #endif if (tmp != 0) ret = -1; if (ctxt->states != NULL) { tmp = -1; for (i = 0; i < ctxt->states->nbState; i++) { state = ctxt->states->tabState[i]; ctxt->state = state; ctxt->state->seq = nseq; if (xmlRelaxNGValidateElementEnd(ctxt, 0) == 0) { tmp = 0; break; } } if (tmp != 0) { /* * validation error, log the message for the "best" one */ ctxt->flags |= FLAGS_IGNORABLE; xmlRelaxNGLogBestError(ctxt); } for (i = 0; i < ctxt->states->nbState; i++) { xmlRelaxNGFreeValidState(ctxt, ctxt->states-> tabState[i]); } xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->flags = oldflags; ctxt->states = NULL; if ((ret == 0) && (tmp == -1)) ret = -1; } else { state = ctxt->state; ctxt->state->seq = nseq; if (ret == 0) ret = xmlRelaxNGValidateElementEnd(ctxt, 1); xmlRelaxNGFreeValidState(ctxt, state); } } else { if (define->content != NULL) { tmp = xmlRelaxNGValidateDefinitionList(ctxt, define-> content); if (tmp != 0) { ret = -1; if (ctxt->state == NULL) { ctxt->state = oldstate; VALID_ERR2(XML_RELAXNG_ERR_CONTENTVALID, node->name); ctxt->state = NULL; } else { VALID_ERR2(XML_RELAXNG_ERR_CONTENTVALID, node->name); } } } if (ctxt->states != NULL) { tmp = -1; for (i = 0; i < ctxt->states->nbState; i++) { state = ctxt->states->tabState[i]; ctxt->state = state; if (xmlRelaxNGValidateElementEnd(ctxt, 0) == 0) { tmp = 0; break; } } if (tmp != 0) { /* * validation error, log the message for the "best" one */ ctxt->flags |= FLAGS_IGNORABLE; xmlRelaxNGLogBestError(ctxt); } for (i = 0; i < ctxt->states->nbState; i++) { xmlRelaxNGFreeValidState(ctxt, ctxt->states-> tabState[i]); } xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->flags = oldflags; ctxt->states = NULL; if ((ret == 0) && (tmp == -1)) ret = -1; } else { state = ctxt->state; if (ret == 0) ret = xmlRelaxNGValidateElementEnd(ctxt, 1); xmlRelaxNGFreeValidState(ctxt, state); } } if (ret == 0) { node->psvi = define; } ctxt->flags = oldflags; ctxt->state = oldstate; if (oldstate != NULL) oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next); if (ret != 0) { if ((ctxt->flags & FLAGS_IGNORABLE) == 0) { xmlRelaxNGDumpValidError(ctxt); ret = 0; } else { ret = -2; } } else { if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr); } #ifdef DEBUG xmlGenericError(xmlGenericErrorContext, "xmlRelaxNGValidateDefinition(): validated %s : %d", node->name, ret); if (oldstate == NULL) xmlGenericError(xmlGenericErrorContext, ": no state\n"); else if (oldstate->seq == NULL) xmlGenericError(xmlGenericErrorContext, ": done\n"); else if (oldstate->seq->type == XML_ELEMENT_NODE) xmlGenericError(xmlGenericErrorContext, ": next elem %s\n", oldstate->seq->name); else xmlGenericError(xmlGenericErrorContext, ": next %s %d\n", oldstate->seq->name, oldstate->seq->type); #endif break; case XML_RELAXNG_OPTIONAL:{ errNr = ctxt->errNr; oldflags = ctxt->flags; ctxt->flags |= FLAGS_IGNORABLE; oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state); ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content); if (ret != 0) { if (ctxt->state != NULL) xmlRelaxNGFreeValidState(ctxt, ctxt->state); ctxt->state = oldstate; ctxt->flags = oldflags; ret = 0; if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr); break; } if (ctxt->states != NULL) { xmlRelaxNGAddStates(ctxt, ctxt->states, oldstate); } else { ctxt->states = xmlRelaxNGNewStates(ctxt, 1); if (ctxt->states == NULL) { xmlRelaxNGFreeValidState(ctxt, oldstate); ctxt->flags = oldflags; ret = -1; if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr); break; } xmlRelaxNGAddStates(ctxt, ctxt->states, oldstate); xmlRelaxNGAddStates(ctxt, ctxt->states, ctxt->state); ctxt->state = NULL; } ctxt->flags = oldflags; ret = 0; if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr); break; } case XML_RELAXNG_ONEORMORE: errNr = ctxt->errNr; ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content); if (ret != 0) { break; } if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr); /* no break on purpose */ case XML_RELAXNG_ZEROORMORE:{ int progress; xmlRelaxNGStatesPtr states = NULL, res = NULL; int base, j; errNr = ctxt->errNr; res = xmlRelaxNGNewStates(ctxt, 1); if (res == NULL) { ret = -1; break; } /* * All the input states are also exit states */ if (ctxt->state != NULL) { xmlRelaxNGAddStates(ctxt, res, xmlRelaxNGCopyValidState(ctxt, ctxt-> state)); } else { for (j = 0; j < ctxt->states->nbState; j++) { xmlRelaxNGAddStates(ctxt, res, xmlRelaxNGCopyValidState(ctxt, ctxt-> states-> tabState [j])); } } oldflags = ctxt->flags; ctxt->flags |= FLAGS_IGNORABLE; do { progress = 0; base = res->nbState; if (ctxt->states != NULL) { states = ctxt->states; for (i = 0; i < states->nbState; i++) { ctxt->state = states->tabState[i]; ctxt->states = NULL; ret = xmlRelaxNGValidateDefinitionList(ctxt, define-> content); if (ret == 0) { if (ctxt->state != NULL) { tmp = xmlRelaxNGAddStates(ctxt, res, ctxt->state); ctxt->state = NULL; if (tmp == 1) progress = 1; } else if (ctxt->states != NULL) { for (j = 0; j < ctxt->states->nbState; j++) { tmp = xmlRelaxNGAddStates(ctxt, res, ctxt-> states-> tabState [j]); if (tmp == 1) progress = 1; } xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->states = NULL; } } else { if (ctxt->state != NULL) { xmlRelaxNGFreeValidState(ctxt, ctxt->state); ctxt->state = NULL; } } } } else { ret = xmlRelaxNGValidateDefinitionList(ctxt, define-> content); if (ret != 0) { xmlRelaxNGFreeValidState(ctxt, ctxt->state); ctxt->state = NULL; } else { base = res->nbState; if (ctxt->state != NULL) { tmp = xmlRelaxNGAddStates(ctxt, res, ctxt->state); ctxt->state = NULL; if (tmp == 1) progress = 1; } else if (ctxt->states != NULL) { for (j = 0; j < ctxt->states->nbState; j++) { tmp = xmlRelaxNGAddStates(ctxt, res, ctxt-> states-> tabState[j]); if (tmp == 1) progress = 1; } if (states == NULL) { states = ctxt->states; } else { xmlRelaxNGFreeStates(ctxt, ctxt->states); } ctxt->states = NULL; } } } if (progress) { /* * Collect all the new nodes added at that step * and make them the new node set */ if (res->nbState - base == 1) { ctxt->state = xmlRelaxNGCopyValidState(ctxt, res-> tabState [base]); } else { if (states == NULL) { xmlRelaxNGNewStates(ctxt, res->nbState - base); states = ctxt->states; if (states == NULL) { progress = 0; break; } } states->nbState = 0; for (i = base; i < res->nbState; i++) xmlRelaxNGAddStates(ctxt, states, xmlRelaxNGCopyValidState (ctxt, res->tabState[i])); ctxt->states = states; } } } while (progress == 1); if (states != NULL) { xmlRelaxNGFreeStates(ctxt, states); } ctxt->states = res; ctxt->flags = oldflags; #if 0 /* * errors may have to be propagated back... */ if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr); #endif ret = 0; break; } case XML_RELAXNG_CHOICE:{ xmlRelaxNGDefinePtr list = NULL; xmlRelaxNGStatesPtr states = NULL; node = xmlRelaxNGSkipIgnored(ctxt, node); errNr = ctxt->errNr; if ((define->dflags & IS_TRIABLE) && (define->data != NULL) && (node != NULL)) { /* * node == NULL can't be optimized since IS_TRIABLE * doesn't account for choice which may lead to * only attributes. */ xmlHashTablePtr triage = (xmlHashTablePtr) define->data; /* * Something we can optimize cleanly there is only one * possble branch out ! */ if ((node->type == XML_TEXT_NODE) || (node->type == XML_CDATA_SECTION_NODE)) { list = xmlHashLookup2(triage, BAD_CAST "#text", NULL); } else if (node->type == XML_ELEMENT_NODE) { if (node->ns != NULL) { list = xmlHashLookup2(triage, node->name, node->ns->href); if (list == NULL) list = xmlHashLookup2(triage, BAD_CAST "#any", node->ns->href); } else list = xmlHashLookup2(triage, node->name, NULL); if (list == NULL) list = xmlHashLookup2(triage, BAD_CAST "#any", NULL); } if (list == NULL) { ret = -1; VALID_ERR2(XML_RELAXNG_ERR_ELEMWRONG, node->name); break; } ret = xmlRelaxNGValidateDefinition(ctxt, list); if (ret == 0) { } break; } list = define->content; oldflags = ctxt->flags; ctxt->flags |= FLAGS_IGNORABLE; while (list != NULL) { oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state); ret = xmlRelaxNGValidateDefinition(ctxt, list); if (ret == 0) { if (states == NULL) { states = xmlRelaxNGNewStates(ctxt, 1); } if (ctxt->state != NULL) { xmlRelaxNGAddStates(ctxt, states, ctxt->state); } else if (ctxt->states != NULL) { for (i = 0; i < ctxt->states->nbState; i++) { xmlRelaxNGAddStates(ctxt, states, ctxt->states-> tabState[i]); } xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->states = NULL; } } else { xmlRelaxNGFreeValidState(ctxt, ctxt->state); } ctxt->state = oldstate; list = list->next; } if (states != NULL) { xmlRelaxNGFreeValidState(ctxt, oldstate); ctxt->states = states; ctxt->state = NULL; ret = 0; } else { ctxt->states = NULL; } ctxt->flags = oldflags; if (ret != 0) { if ((ctxt->flags & FLAGS_IGNORABLE) == 0) { xmlRelaxNGDumpValidError(ctxt); } } else { if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr); } break; } case XML_RELAXNG_DEF: case XML_RELAXNG_GROUP: ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content); break; case XML_RELAXNG_INTERLEAVE: ret = xmlRelaxNGValidateInterleave(ctxt, define); break; case XML_RELAXNG_ATTRIBUTE: ret = xmlRelaxNGValidateAttribute(ctxt, define); break; case XML_RELAXNG_START: case XML_RELAXNG_NOOP: case XML_RELAXNG_REF: case XML_RELAXNG_EXTERNALREF: case XML_RELAXNG_PARENTREF: ret = xmlRelaxNGValidateDefinition(ctxt, define->content); break; case XML_RELAXNG_DATATYPE:{ xmlNodePtr child; xmlChar *content = NULL; child = node; while (child != NULL) { if (child->type == XML_ELEMENT_NODE) { VALID_ERR2(XML_RELAXNG_ERR_DATAELEM, node->parent->name); ret = -1; break; } else if ((child->type == XML_TEXT_NODE) || (child->type == XML_CDATA_SECTION_NODE)) { content = xmlStrcat(content, child->content); } /* TODO: handle entities ... */ child = child->next; } if (ret == -1) { if (content != NULL) xmlFree(content); break; } if (content == NULL) { content = xmlStrdup(BAD_CAST ""); if (content == NULL) { xmlRngVErrMemory(ctxt, "validating\n"); ret = -1; break; } } ret = xmlRelaxNGValidateDatatype(ctxt, content, define, ctxt->state->seq); if (ret == -1) { VALID_ERR2(XML_RELAXNG_ERR_DATATYPE, define->name); } else if (ret == 0) { ctxt->state->seq = NULL; } if (content != NULL) xmlFree(content); break; } case XML_RELAXNG_VALUE:{ xmlChar *content = NULL; xmlChar *oldvalue; xmlNodePtr child; child = node; while (child != NULL) { if (child->type == XML_ELEMENT_NODE) { VALID_ERR2(XML_RELAXNG_ERR_VALELEM, node->parent->name); ret = -1; break; } else if ((child->type == XML_TEXT_NODE) || (child->type == XML_CDATA_SECTION_NODE)) { content = xmlStrcat(content, child->content); } /* TODO: handle entities ... */ child = child->next; } if (ret == -1) { if (content != NULL) xmlFree(content); break; } if (content == NULL) { content = xmlStrdup(BAD_CAST ""); if (content == NULL) { xmlRngVErrMemory(ctxt, "validating\n"); ret = -1; break; } } oldvalue = ctxt->state->value; ctxt->state->value = content; ret = xmlRelaxNGValidateValue(ctxt, define); ctxt->state->value = oldvalue; if (ret == -1) { VALID_ERR2(XML_RELAXNG_ERR_VALUE, define->name); } else if (ret == 0) { ctxt->state->seq = NULL; } if (content != NULL) xmlFree(content); break; } case XML_RELAXNG_LIST:{ xmlChar *content; xmlNodePtr child; xmlChar *oldvalue, *oldendvalue; int len; /* * Make sure it's only text nodes */ content = NULL; child = node; while (child != NULL) { if (child->type == XML_ELEMENT_NODE) { VALID_ERR2(XML_RELAXNG_ERR_LISTELEM, node->parent->name); ret = -1; break; } else if ((child->type == XML_TEXT_NODE) || (child->type == XML_CDATA_SECTION_NODE)) { content = xmlStrcat(content, child->content); } /* TODO: handle entities ... */ child = child->next; } if (ret == -1) { if (content != NULL) xmlFree(content); break; } if (content == NULL) { content = xmlStrdup(BAD_CAST ""); if (content == NULL) { xmlRngVErrMemory(ctxt, "validating\n"); ret = -1; break; } } len = xmlStrlen(content); oldvalue = ctxt->state->value; oldendvalue = ctxt->state->endvalue; ctxt->state->value = content; ctxt->state->endvalue = content + len; ret = xmlRelaxNGValidateValue(ctxt, define); ctxt->state->value = oldvalue; ctxt->state->endvalue = oldendvalue; if (ret == -1) { VALID_ERR(XML_RELAXNG_ERR_LIST); } else if ((ret == 0) && (node != NULL)) { ctxt->state->seq = node->next; } if (content != NULL) xmlFree(content); break; } case XML_RELAXNG_EXCEPT: case XML_RELAXNG_PARAM: TODO ret = -1; break; } ctxt->depth--; #ifdef DEBUG for (i = 0; i < ctxt->depth; i++) xmlGenericError(xmlGenericErrorContext, " "); xmlGenericError(xmlGenericErrorContext, "Validating %s ", xmlRelaxNGDefName(define)); if (define->name != NULL) xmlGenericError(xmlGenericErrorContext, "%s ", define->name); if (ret == 0) xmlGenericError(xmlGenericErrorContext, "suceeded\n"); else xmlGenericError(xmlGenericErrorContext, "failed\n"); #endif return (ret); } /** * xmlRelaxNGValidateDefinition: * @ctxt: a Relax-NG validation context * @define: the definition to verify * * Validate the current node lists against the definition * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGDefinePtr define) { xmlRelaxNGStatesPtr states, res; int i, j, k, ret, oldflags; /* * We should NOT have both ctxt->state and ctxt->states */ if ((ctxt->state != NULL) && (ctxt->states != NULL)) { TODO xmlRelaxNGFreeValidState(ctxt, ctxt->state); ctxt->state = NULL; } if ((ctxt->states == NULL) || (ctxt->states->nbState == 1)) { if (ctxt->states != NULL) { ctxt->state = ctxt->states->tabState[0]; xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->states = NULL; } ret = xmlRelaxNGValidateState(ctxt, define); if ((ctxt->state != NULL) && (ctxt->states != NULL)) { TODO xmlRelaxNGFreeValidState(ctxt, ctxt->state); ctxt->state = NULL; } if ((ctxt->states != NULL) && (ctxt->states->nbState == 1)) { ctxt->state = ctxt->states->tabState[0]; xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->states = NULL; } return (ret); } states = ctxt->states; ctxt->states = NULL; res = NULL; j = 0; oldflags = ctxt->flags; ctxt->flags |= FLAGS_IGNORABLE; for (i = 0; i < states->nbState; i++) { ctxt->state = states->tabState[i]; ctxt->states = NULL; ret = xmlRelaxNGValidateState(ctxt, define); /* * We should NOT have both ctxt->state and ctxt->states */ if ((ctxt->state != NULL) && (ctxt->states != NULL)) { TODO xmlRelaxNGFreeValidState(ctxt, ctxt->state); ctxt->state = NULL; } if (ret == 0) { if (ctxt->states == NULL) { if (res != NULL) { /* add the state to the container */ xmlRelaxNGAddStates(ctxt, res, ctxt->state); ctxt->state = NULL; } else { /* add the state directly in states */ states->tabState[j++] = ctxt->state; ctxt->state = NULL; } } else { if (res == NULL) { /* make it the new container and copy other results */ res = ctxt->states; ctxt->states = NULL; for (k = 0; k < j; k++) xmlRelaxNGAddStates(ctxt, res, states->tabState[k]); } else { /* add all the new results to res and reff the container */ for (k = 0; k < ctxt->states->nbState; k++) xmlRelaxNGAddStates(ctxt, res, ctxt->states->tabState[k]); xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->states = NULL; } } } else { if (ctxt->state != NULL) { xmlRelaxNGFreeValidState(ctxt, ctxt->state); ctxt->state = NULL; } else if (ctxt->states != NULL) { for (k = 0; k < ctxt->states->nbState; k++) xmlRelaxNGFreeValidState(ctxt, ctxt->states->tabState[k]); xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->states = NULL; } } } ctxt->flags = oldflags; if (res != NULL) { xmlRelaxNGFreeStates(ctxt, states); ctxt->states = res; ret = 0; } else if (j > 1) { states->nbState = j; ctxt->states = states; ret = 0; } else if (j == 1) { ctxt->state = states->tabState[0]; xmlRelaxNGFreeStates(ctxt, states); ret = 0; } else { ret = -1; xmlRelaxNGFreeStates(ctxt, states); if (ctxt->states != NULL) { xmlRelaxNGFreeStates(ctxt, ctxt->states); ctxt->states = NULL; } } if ((ctxt->state != NULL) && (ctxt->states != NULL)) { TODO xmlRelaxNGFreeValidState(ctxt, ctxt->state); ctxt->state = NULL; } return (ret); } /** * xmlRelaxNGValidateDocument: * @ctxt: a Relax-NG validation context * @doc: the document * * Validate the given document * * Returns 0 if the validation succeeded or an error code. */ static int xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) { int ret; xmlRelaxNGPtr schema; xmlRelaxNGGrammarPtr grammar; xmlRelaxNGValidStatePtr state; xmlNodePtr node; if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL)) return (-1); ctxt->errNo = XML_RELAXNG_OK; schema = ctxt->schema; grammar = schema->topgrammar; if (grammar == NULL) { VALID_ERR(XML_RELAXNG_ERR_NOGRAMMAR); return (-1); } state = xmlRelaxNGNewValidState(ctxt, NULL); ctxt->state = state; ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start); if ((ctxt->state != NULL) && (state->seq != NULL)) { state = ctxt->state; node = state->seq; node = xmlRelaxNGSkipIgnored(ctxt, node); if (node != NULL) { if (ret != -1) { VALID_ERR(XML_RELAXNG_ERR_EXTRADATA); ret = -1; } } } else if (ctxt->states != NULL) { int i; int tmp = -1; for (i = 0; i < ctxt->states->nbState; i++) { state = ctxt->states->tabState[i]; node = state->seq; node = xmlRelaxNGSkipIgnored(ctxt, node); if (node == NULL) tmp = 0; xmlRelaxNGFreeValidState(ctxt, state); } if (tmp == -1) { if (ret != -1) { VALID_ERR(XML_RELAXNG_ERR_EXTRADATA); ret = -1; } } } if (ctxt->state != NULL) { xmlRelaxNGFreeValidState(ctxt, ctxt->state); ctxt->state = NULL; } if (ret != 0) xmlRelaxNGDumpValidError(ctxt); #ifdef DEBUG else if (ctxt->errNr != 0) { ctxt->error(ctxt->userData, "%d Extra error messages left on stack !\n", ctxt->errNr); xmlRelaxNGDumpValidError(ctxt); } #endif #ifdef LIBXML_VALID_ENABLED if (ctxt->idref == 1) { xmlValidCtxt vctxt; memset(&vctxt, 0, sizeof(xmlValidCtxt)); vctxt.valid = 1; vctxt.error = ctxt->error; vctxt.warning = ctxt->warning; vctxt.userData = ctxt->userData; if (xmlValidateDocumentFinal(&vctxt, doc) != 1) ret = -1; } #endif /* LIBXML_VALID_ENABLED */ if ((ret == 0) && (ctxt->errNo != XML_RELAXNG_OK)) ret = -1; return (ret); } /************************************************************************ * * * Validation interfaces * * * ************************************************************************/ /** * xmlRelaxNGNewValidCtxt: * @schema: a precompiled XML RelaxNGs * * Create an XML RelaxNGs validation context based on the given schema * * Returns the validation context or NULL in case of error */ xmlRelaxNGValidCtxtPtr xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) { xmlRelaxNGValidCtxtPtr ret; ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt)); if (ret == NULL) { xmlRngVErrMemory(NULL, "building context\n"); return (NULL); } memset(ret, 0, sizeof(xmlRelaxNGValidCtxt)); ret->schema = schema; ret->error = xmlGenericError; ret->userData = xmlGenericErrorContext; ret->errNr = 0; ret->errMax = 0; ret->err = NULL; ret->errTab = NULL; if (schema != NULL) ret->idref = schema->idref; ret->states = NULL; ret->freeState = NULL; ret->freeStates = NULL; ret->errNo = XML_RELAXNG_OK; return (ret); } /** * xmlRelaxNGFreeValidCtxt: * @ctxt: the schema validation context * * Free the resources associated to the schema validation context */ void xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) { int k; if (ctxt == NULL) return; if (ctxt->states != NULL) xmlRelaxNGFreeStates(NULL, ctxt->states); if (ctxt->freeState != NULL) { for (k = 0; k < ctxt->freeState->nbState; k++) { xmlRelaxNGFreeValidState(NULL, ctxt->freeState->tabState[k]); } xmlRelaxNGFreeStates(NULL, ctxt->freeState); } if (ctxt->freeStates != NULL) { for (k = 0; k < ctxt->freeStatesNr; k++) { xmlRelaxNGFreeStates(NULL, ctxt->freeStates[k]); } xmlFree(ctxt->freeStates); } if (ctxt->errTab != NULL) xmlFree(ctxt->errTab); if (ctxt->elemTab != NULL) { xmlRegExecCtxtPtr exec; exec = xmlRelaxNGElemPop(ctxt); while (exec != NULL) { xmlRegFreeExecCtxt(exec); exec = xmlRelaxNGElemPop(ctxt); } xmlFree(ctxt->elemTab); } xmlFree(ctxt); } /** * xmlRelaxNGSetValidErrors: * @ctxt: a Relax-NG validation context * @err: the error function * @warn: the warning function * @ctx: the functions context * * Set the error and warning callback informations */ void xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidityErrorFunc err, xmlRelaxNGValidityWarningFunc warn, void *ctx) { if (ctxt == NULL) return; ctxt->error = err; ctxt->warning = warn; ctxt->userData = ctx; ctxt->serror = NULL; } /** * xmlRelaxNGSetValidStructuredErrors: * @ctxt: a Relax-NG validation context * @serror: the structured error function * @ctx: the functions context * * Set the structured error callback */ void xmlRelaxNGSetValidStructuredErrors(xmlRelaxNGValidCtxtPtr ctxt, xmlStructuredErrorFunc serror, void *ctx) { if (ctxt == NULL) return; ctxt->serror = serror; ctxt->error = NULL; ctxt->warning = NULL; ctxt->userData = ctx; } /** * xmlRelaxNGGetValidErrors: * @ctxt: a Relax-NG validation context * @err: the error function result * @warn: the warning function result * @ctx: the functions context result * * Get the error and warning callback informations * * Returns -1 in case of error and 0 otherwise */ int xmlRelaxNGGetValidErrors(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidityErrorFunc * err, xmlRelaxNGValidityWarningFunc * warn, void **ctx) { if (ctxt == NULL) return (-1); if (err != NULL) *err = ctxt->error; if (warn != NULL) *warn = ctxt->warning; if (ctx != NULL) *ctx = ctxt->userData; return (0); } /** * xmlRelaxNGValidateDoc: * @ctxt: a Relax-NG validation context * @doc: a parsed document tree * * Validate a document tree in memory. * * Returns 0 if the document is valid, a positive error code * number otherwise and -1 in case of internal or API error. */ int xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) { int ret; if ((ctxt == NULL) || (doc == NULL)) return (-1); ctxt->doc = doc; ret = xmlRelaxNGValidateDocument(ctxt, doc); /* * TODO: build error codes */ if (ret == -1) return (1); return (ret); } #define bottom_relaxng #include "elfgcchack.h" #endif /* LIBXML_SCHEMAS_ENABLED */
relaxng.c
Page URL
File URL
Prev
77/114
Next
Download
( 360 KB )
Note: The DriveHQ service banners will NOT be displayed if the file owner is a paid member.
Comments
Total ratings:
0
Average rating:
Not Rated
Would you like to comment?
Join DriveHQ
for a free account, or
Logon
if you are already a member.