| /* |
| * relaxng.c : implementation of the Relax-NG handling and validity checking |
| * |
| * See Copyright for the status of this software. |
| * |
| * Daniel Veillard <veillard@redhat.com> |
| */ |
| |
| /** |
| * 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 "third_party/libxml/src/libxml.h" |
| |
| #ifdef LIBXML_SCHEMAS_ENABLED |
| |
| #include <string.h> |
| #include <stdio.h> |
| #include <stddef.h> |
| #include "third_party/libxml/src/include/libxml/xmlmemory.h" |
| #include "third_party/libxml/src/include/libxml/parser.h" |
| #include "third_party/libxml/src/include/libxml/parserInternals.h" |
| #include "third_party/libxml/src/include/libxml/hash.h" |
| #include "third_party/libxml/src/include/libxml/uri.h" |
| |
| #include "third_party/libxml/src/include/libxml/relaxng.h" |
| |
| #include "third_party/libxml/src/include/libxml/xmlschemastypes.h" |
| #include "third_party/libxml/src/include/libxml/xmlautomata.h" |
| #include "third_party/libxml/src/include/libxml/xmlregexp.h" |
| #include "third_party/libxml/src/include/libxml/xmlschemastypes.h" |
| |
| /* |
| * The Relax-NG namespace |
| */ |
| static const xmlChar *xmlRelaxNGNs = (const xmlChar *) |
| "http://relaxng.org/ns/structure/1.0"; |
| |
| #define IS_RELAXNG(node, typ) \ |
| ((node != NULL) && (node->ns != NULL) && \ |
| (node->type == XML_ELEMENT_NODE) && \ |
| (xmlStrEqual(node->name, (const xmlChar *) typ)) && \ |
| (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; /* <start> content */ |
| xmlRelaxNGCombine combine; /* the default combine value */ |
| xmlRelaxNGDefinePtr startList; /* list of <start> 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, /* external data type definition */ |
| XML_RELAXNG_PARAM, /* external data type parameter */ |
| XML_RELAXNG_VALUE, /* value from an external data type definition */ |
| XML_RELAXNG_LIST, /* a list of patterns */ |
| XML_RELAXNG_ATTRIBUTE, /* an attribute 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) |
| #define IS_EXTERNAL_REF (1 << 8) |
| |
| 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 allocated */ |
| 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 */ |
| int externalRef; /* 1 if an external ref */ |
| }; |
| |
| |
| /************************************************************************ |
| * * |
| * 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 LIBXML_ATTR_FORMAT(4,0) |
| 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 LIBXML_ATTR_FORMAT(4,0) |
| 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->freeStates != 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 || states == 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: the 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 equal, 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 <include> start from %s\n", URL); |
| else |
| xmlGenericError(xmlGenericErrorContext, |
| "Elimination of <include> 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, |
| xmlDocGetRootElement(inc->doc)->children, |
| name) == 1) { |
| found = 1; |
| } |
| #ifdef DEBUG_INCLUDE |
| if (href != NULL) |
| xmlFree(href); |
| #endif |
| } |
| } |
| if (xmlRelaxNGRemoveRedefine(ctxt, URL, tmp->children, name) == 1) { |
| found = 1; |
| } |
| } |
| 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); |
| } |
| } |
| if (IS_RELAXNG(cur, "div") && cur->children != NULL) { |
| cur = cur->children; |
| } else { |
| if (cur->next != NULL) { |
| cur = cur->next; |
| } else { |
| while (cur->parent != node && cur->parent->next == NULL) { |
| cur = cur->parent; |
| } |
| cur = cur->parent != node ? cur->parent->next : NULL; |
| } |
| } |
| } |
| |
| |
| 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; |
| ret->externalRef = 1; |
| 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]; |
| xmlChar *result; |
| |
| 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; |
| result = xmlCharStrdup(msg); |
| return (xmlEscapeFormatString(&result)); |
| } |
| |
| /** |
| * 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; |
| } |
| if ((node == NULL) && (seq == NULL)) { |
| node = ctxt->pnode; |
| } |
| 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 (res1 != (xmlSchemaValPtr) comp1) |
| xmlSchemaFreeValue(res1); |
| 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(void *payload, |
| const xmlChar * namespace ATTRIBUTE_UNUSED) |
| { |
| xmlRelaxNGTypeLibraryPtr lib = (xmlRelaxNGTypeLibraryPtr) payload; |
| 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: |
| * |
| * Initialize 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, 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 * |
| * * |
| ************************************************************************/ |
| |
| /* from automata.c but not exported */ |
| void xmlAutomataSetFlags(xmlAutomataPtr am, int flags); |
| |
| |
| static int xmlRelaxNGTryCompile(xmlRelaxNGParserCtxtPtr ctxt, |
| xmlRelaxNGDefinePtr def); |
| |
| /** |
| * xmlRelaxNGIsCompilable: |
| * @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 |
| xmlRelaxNGIsCompilable(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 = xmlRelaxNGIsCompilable(def->content); |
| break; |
| case XML_RELAXNG_TEXT: |
| case XML_RELAXNG_EMPTY: |
| ret = 1; |
| break; |
| case XML_RELAXNG_ELEMENT: |
| /* |
| * Check if the element content is compilable |
| */ |
| if (((def->dflags & IS_NOT_COMPILABLE) == 0) && |
| ((def->dflags & IS_COMPILABLE) == 0)) { |
| xmlRelaxNGDefinePtr list; |
| |
| list = def->content; |
| while (list != NULL) { |
| ret = xmlRelaxNGIsCompilable(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 RelaxNGIsCompilable for element %s\n", |
| def->name); |
| } |
| #endif |
| } |
| /* |
| * All elements return a compilable 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 = xmlRelaxNGIsCompilable(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 = xmlRelaxNGIsCompilable(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, |
| "RelaxNGIsCompilable %s : true\n", |
| xmlRelaxNGDefName(def)); |
| } else if (ret == 0) { |
| xmlGenericError(xmlGenericErrorContext, |
| "RelaxNGIsCompilable %s : false\n", |
| xmlRelaxNGDefName(def)); |
| } else { |
| xmlGenericError(xmlGenericErrorContext, |
| "Problem in RelaxNGIsCompilable %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 ((xmlRelaxNGIsCompilable(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); |
| |
| /* |
| * assume identical strings but not same pointer are different |
| * atoms, needed for non-determinism detection |
| * That way if 2 elements with the same name are in a choice |
| * branch the automata is found non-deterministic and |
| * we fallback to the normal validation which does the right |
| * thing of exploring both choices. |
| */ |
| xmlAutomataSetFlags(ctxt->am, 1); |
| |
| ctxt->state = xmlAutomataGetInitState(ctxt->am); |
| while (list != NULL) { |
| xmlRelaxNGCompile(ctxt, list); |
| list = list->next; |
| } |
| xmlAutomataSetFinalState(ctxt->am, ctxt->state); |
| if (xmlAutomataIsDeterminist(ctxt->am)) |
| def->contModel = xmlAutomataCompile(ctxt->am); |
| |
| 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); |
| xmlAutomataSetFlags(ctxt->am, 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)) { |
| #ifdef DEBUG_COMPILE |
| xmlGenericError(xmlGenericErrorContext, |
| "Content model not determinist %s\n", |
| def->name); |
| #endif |
| /* |
| * 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; |
| |
| list = def->content; |
| while (list != NULL) { |
| xmlRelaxNGCompile(ctxt, list); |
| list = list->next; |
| } |
| |