From 35a201cc8ef0c3f5b2df88d2e528aabee1048348 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 30 Apr 2021 18:47:09 +0200 Subject: Initial/Final commit --- libxml2-2.9.10/valid.c | 7139 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 7139 insertions(+) create mode 100644 libxml2-2.9.10/valid.c (limited to 'libxml2-2.9.10/valid.c') diff --git a/libxml2-2.9.10/valid.c b/libxml2-2.9.10/valid.c new file mode 100644 index 0000000..07963e7 --- /dev/null +++ b/libxml2-2.9.10/valid.c @@ -0,0 +1,7139 @@ +/* + * valid.c : part of the code use to do the DTD handling and the validity + * checking + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ + +#define IN_LIBXML +#include "libxml.h" + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, + int create); +/* #define DEBUG_VALID_ALGO */ +/* #define DEBUG_REGEXP_ALGO */ + +#define TODO \ + xmlGenericError(xmlGenericErrorContext, \ + "Unimplemented block at %s:%d\n", \ + __FILE__, __LINE__); + +#ifdef LIBXML_VALID_ENABLED +static int +xmlValidateAttributeValueInternal(xmlDocPtr doc, xmlAttributeType type, + const xmlChar *value); +#endif +/************************************************************************ + * * + * Error handling routines * + * * + ************************************************************************/ + +/** + * xmlVErrMemory: + * @ctxt: an XML validation parser context + * @extra: extra informations + * + * Handle an out of memory error + */ +static void +xmlVErrMemory(xmlValidCtxtPtr ctxt, const char *extra) +{ + xmlGenericErrorFunc channel = NULL; + xmlParserCtxtPtr pctxt = NULL; + void *data = NULL; + + if (ctxt != NULL) { + channel = ctxt->error; + data = ctxt->userData; + /* Use the special values to detect if it is part of a parsing + context */ + if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) || + (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) { + long delta = (char *) ctxt - (char *) ctxt->userData; + if ((delta > 0) && (delta < 250)) + pctxt = ctxt->userData; + } + } + if (extra) + __xmlRaiseError(NULL, channel, data, + pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY, + XML_ERR_FATAL, NULL, 0, extra, NULL, NULL, 0, 0, + "Memory allocation failed : %s\n", extra); + else + __xmlRaiseError(NULL, channel, data, + pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY, + XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0, + "Memory allocation failed\n"); +} + +/** + * xmlErrValid: + * @ctxt: an XML validation parser context + * @error: the error number + * @extra: extra informations + * + * Handle a validation error + */ +static void LIBXML_ATTR_FORMAT(3,0) +xmlErrValid(xmlValidCtxtPtr ctxt, xmlParserErrors error, + const char *msg, const char *extra) +{ + xmlGenericErrorFunc channel = NULL; + xmlParserCtxtPtr pctxt = NULL; + void *data = NULL; + + if (ctxt != NULL) { + channel = ctxt->error; + data = ctxt->userData; + /* Use the special values to detect if it is part of a parsing + context */ + if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) || + (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) { + long delta = (char *) ctxt - (char *) ctxt->userData; + if ((delta > 0) && (delta < 250)) + pctxt = ctxt->userData; + } + } + if (extra) + __xmlRaiseError(NULL, channel, data, + pctxt, NULL, XML_FROM_VALID, error, + XML_ERR_ERROR, NULL, 0, extra, NULL, NULL, 0, 0, + msg, extra); + else + __xmlRaiseError(NULL, channel, data, + pctxt, NULL, XML_FROM_VALID, error, + XML_ERR_ERROR, NULL, 0, NULL, NULL, NULL, 0, 0, + "%s", msg); +} + +#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) +/** + * xmlErrValidNode: + * @ctxt: an XML validation parser context + * @node: the node raising the error + * @error: the error number + * @str1: extra informations + * @str2: extra informations + * @str3: extra informations + * + * Handle a validation error, provide contextual informations + */ +static void LIBXML_ATTR_FORMAT(4,0) +xmlErrValidNode(xmlValidCtxtPtr ctxt, + xmlNodePtr node, xmlParserErrors error, + const char *msg, const xmlChar * str1, + const xmlChar * str2, const xmlChar * str3) +{ + xmlStructuredErrorFunc schannel = NULL; + xmlGenericErrorFunc channel = NULL; + xmlParserCtxtPtr pctxt = NULL; + void *data = NULL; + + if (ctxt != NULL) { + channel = ctxt->error; + data = ctxt->userData; + /* Use the special values to detect if it is part of a parsing + context */ + if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) || + (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) { + long delta = (char *) ctxt - (char *) ctxt->userData; + if ((delta > 0) && (delta < 250)) + pctxt = ctxt->userData; + } + } + __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, + XML_ERR_ERROR, NULL, 0, + (const char *) str1, + (const char *) str2, + (const char *) str3, 0, 0, msg, str1, str2, str3); +} +#endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */ + +#ifdef LIBXML_VALID_ENABLED +/** + * xmlErrValidNodeNr: + * @ctxt: an XML validation parser context + * @node: the node raising the error + * @error: the error number + * @str1: extra informations + * @int2: extra informations + * @str3: extra informations + * + * Handle a validation error, provide contextual informations + */ +static void LIBXML_ATTR_FORMAT(4,0) +xmlErrValidNodeNr(xmlValidCtxtPtr ctxt, + xmlNodePtr node, xmlParserErrors error, + const char *msg, const xmlChar * str1, + int int2, const xmlChar * str3) +{ + xmlStructuredErrorFunc schannel = NULL; + xmlGenericErrorFunc channel = NULL; + xmlParserCtxtPtr pctxt = NULL; + void *data = NULL; + + if (ctxt != NULL) { + channel = ctxt->error; + data = ctxt->userData; + /* Use the special values to detect if it is part of a parsing + context */ + if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) || + (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) { + long delta = (char *) ctxt - (char *) ctxt->userData; + if ((delta > 0) && (delta < 250)) + pctxt = ctxt->userData; + } + } + __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, + XML_ERR_ERROR, NULL, 0, + (const char *) str1, + (const char *) str3, + NULL, int2, 0, msg, str1, int2, str3); +} + +/** + * xmlErrValidWarning: + * @ctxt: an XML validation parser context + * @node: the node raising the error + * @error: the error number + * @str1: extra information + * @str2: extra information + * @str3: extra information + * + * Handle a validation error, provide contextual information + */ +static void LIBXML_ATTR_FORMAT(4,0) +xmlErrValidWarning(xmlValidCtxtPtr ctxt, + xmlNodePtr node, xmlParserErrors error, + const char *msg, const xmlChar * str1, + const xmlChar * str2, const xmlChar * str3) +{ + xmlStructuredErrorFunc schannel = NULL; + xmlGenericErrorFunc channel = NULL; + xmlParserCtxtPtr pctxt = NULL; + void *data = NULL; + + if (ctxt != NULL) { + channel = ctxt->warning; + data = ctxt->userData; + /* Use the special values to detect if it is part of a parsing + context */ + if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) || + (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) { + long delta = (char *) ctxt - (char *) ctxt->userData; + if ((delta > 0) && (delta < 250)) + pctxt = ctxt->userData; + } + } + __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, + XML_ERR_WARNING, NULL, 0, + (const char *) str1, + (const char *) str2, + (const char *) str3, 0, 0, msg, str1, str2, str3); +} + + + +#ifdef LIBXML_REGEXP_ENABLED +/* + * If regexp are enabled we can do continuous validation without the + * need of a tree to validate the content model. this is done in each + * callbacks. + * Each xmlValidState represent the validation state associated to the + * set of nodes currently open from the document root to the current element. + */ + + +typedef struct _xmlValidState { + xmlElementPtr elemDecl; /* pointer to the content model */ + xmlNodePtr node; /* pointer to the current node */ + xmlRegExecCtxtPtr exec; /* regexp runtime */ +} _xmlValidState; + + +static int +vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) { + if ((ctxt->vstateMax == 0) || (ctxt->vstateTab == NULL)) { + ctxt->vstateMax = 10; + ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax * + sizeof(ctxt->vstateTab[0])); + if (ctxt->vstateTab == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + return(-1); + } + } + + if (ctxt->vstateNr >= ctxt->vstateMax) { + xmlValidState *tmp; + + tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab, + 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); + if (tmp == NULL) { + xmlVErrMemory(ctxt, "realloc failed"); + return(-1); + } + ctxt->vstateMax *= 2; + ctxt->vstateTab = tmp; + } + ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr]; + ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl; + ctxt->vstateTab[ctxt->vstateNr].node = node; + if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) { + if (elemDecl->contModel == NULL) + xmlValidBuildContentModel(ctxt, elemDecl); + if (elemDecl->contModel != NULL) { + ctxt->vstateTab[ctxt->vstateNr].exec = + xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL); + } else { + ctxt->vstateTab[ctxt->vstateNr].exec = NULL; + xmlErrValidNode(ctxt, (xmlNodePtr) elemDecl, + XML_ERR_INTERNAL_ERROR, + "Failed to build content model regexp for %s\n", + node->name, NULL, NULL); + } + } + return(ctxt->vstateNr++); +} + +static int +vstateVPop(xmlValidCtxtPtr ctxt) { + xmlElementPtr elemDecl; + + if (ctxt->vstateNr < 1) return(-1); + ctxt->vstateNr--; + elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl; + ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL; + ctxt->vstateTab[ctxt->vstateNr].node = NULL; + if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) { + xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec); + } + ctxt->vstateTab[ctxt->vstateNr].exec = NULL; + if (ctxt->vstateNr >= 1) + ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1]; + else + ctxt->vstate = NULL; + return(ctxt->vstateNr); +} + +#else /* not LIBXML_REGEXP_ENABLED */ +/* + * If regexp are not enabled, it uses a home made algorithm less + * complex and easier to + * debug/maintain than a generic NFA -> DFA state based algo. The + * only restriction is on the deepness of the tree limited by the + * size of the occurs bitfield + * + * this is the content of a saved state for rollbacks + */ + +#define ROLLBACK_OR 0 +#define ROLLBACK_PARENT 1 + +typedef struct _xmlValidState { + xmlElementContentPtr cont; /* pointer to the content model subtree */ + xmlNodePtr node; /* pointer to the current node in the list */ + long occurs;/* bitfield for multiple occurrences */ + unsigned char depth; /* current depth in the overall tree */ + unsigned char state; /* ROLLBACK_XXX */ +} _xmlValidState; + +#define MAX_RECURSE 25000 +#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8) +#define CONT ctxt->vstate->cont +#define NODE ctxt->vstate->node +#define DEPTH ctxt->vstate->depth +#define OCCURS ctxt->vstate->occurs +#define STATE ctxt->vstate->state + +#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH)) +#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1)) + +#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH) +#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1) + +static int +vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont, + xmlNodePtr node, unsigned char depth, long occurs, + unsigned char state) { + int i = ctxt->vstateNr - 1; + + if (ctxt->vstateNr > MAX_RECURSE) { + return(-1); + } + if (ctxt->vstateTab == NULL) { + ctxt->vstateMax = 8; + ctxt->vstateTab = (xmlValidState *) xmlMalloc( + ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); + if (ctxt->vstateTab == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + return(-1); + } + } + if (ctxt->vstateNr >= ctxt->vstateMax) { + xmlValidState *tmp; + + tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab, + 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); + if (tmp == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + return(-1); + } + ctxt->vstateMax *= 2; + ctxt->vstateTab = tmp; + ctxt->vstate = &ctxt->vstateTab[0]; + } + /* + * Don't push on the stack a state already here + */ + if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) && + (ctxt->vstateTab[i].node == node) && + (ctxt->vstateTab[i].depth == depth) && + (ctxt->vstateTab[i].occurs == occurs) && + (ctxt->vstateTab[i].state == state)) + return(ctxt->vstateNr); + ctxt->vstateTab[ctxt->vstateNr].cont = cont; + ctxt->vstateTab[ctxt->vstateNr].node = node; + ctxt->vstateTab[ctxt->vstateNr].depth = depth; + ctxt->vstateTab[ctxt->vstateNr].occurs = occurs; + ctxt->vstateTab[ctxt->vstateNr].state = state; + return(ctxt->vstateNr++); +} + +static int +vstateVPop(xmlValidCtxtPtr ctxt) { + if (ctxt->vstateNr <= 1) return(-1); + ctxt->vstateNr--; + ctxt->vstate = &ctxt->vstateTab[0]; + ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont; + ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node; + ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth; + ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs; + ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state; + return(ctxt->vstateNr); +} + +#endif /* LIBXML_REGEXP_ENABLED */ + +static int +nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value) +{ + if (ctxt->nodeMax <= 0) { + ctxt->nodeMax = 4; + ctxt->nodeTab = + (xmlNodePtr *) xmlMalloc(ctxt->nodeMax * + sizeof(ctxt->nodeTab[0])); + if (ctxt->nodeTab == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + ctxt->nodeMax = 0; + return (0); + } + } + if (ctxt->nodeNr >= ctxt->nodeMax) { + xmlNodePtr *tmp; + tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab, + ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0])); + if (tmp == NULL) { + xmlVErrMemory(ctxt, "realloc failed"); + return (0); + } + ctxt->nodeMax *= 2; + ctxt->nodeTab = tmp; + } + ctxt->nodeTab[ctxt->nodeNr] = value; + ctxt->node = value; + return (ctxt->nodeNr++); +} +static xmlNodePtr +nodeVPop(xmlValidCtxtPtr ctxt) +{ + xmlNodePtr ret; + + if (ctxt->nodeNr <= 0) + return (NULL); + ctxt->nodeNr--; + if (ctxt->nodeNr > 0) + ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1]; + else + ctxt->node = NULL; + ret = ctxt->nodeTab[ctxt->nodeNr]; + ctxt->nodeTab[ctxt->nodeNr] = NULL; + return (ret); +} + +#ifdef DEBUG_VALID_ALGO +static void +xmlValidPrintNode(xmlNodePtr cur) { + if (cur == NULL) { + xmlGenericError(xmlGenericErrorContext, "null"); + return; + } + switch (cur->type) { + case XML_ELEMENT_NODE: + xmlGenericError(xmlGenericErrorContext, "%s ", cur->name); + break; + case XML_TEXT_NODE: + xmlGenericError(xmlGenericErrorContext, "text "); + break; + case XML_CDATA_SECTION_NODE: + xmlGenericError(xmlGenericErrorContext, "cdata "); + break; + case XML_ENTITY_REF_NODE: + xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name); + break; + case XML_PI_NODE: + xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name); + break; + case XML_COMMENT_NODE: + xmlGenericError(xmlGenericErrorContext, "comment "); + break; + case XML_ATTRIBUTE_NODE: + xmlGenericError(xmlGenericErrorContext, "?attr? "); + break; + case XML_ENTITY_NODE: + xmlGenericError(xmlGenericErrorContext, "?ent? "); + break; + case XML_DOCUMENT_NODE: + xmlGenericError(xmlGenericErrorContext, "?doc? "); + break; + case XML_DOCUMENT_TYPE_NODE: + xmlGenericError(xmlGenericErrorContext, "?doctype? "); + break; + case XML_DOCUMENT_FRAG_NODE: + xmlGenericError(xmlGenericErrorContext, "?frag? "); + break; + case XML_NOTATION_NODE: + xmlGenericError(xmlGenericErrorContext, "?nota? "); + break; + case XML_HTML_DOCUMENT_NODE: + xmlGenericError(xmlGenericErrorContext, "?html? "); + break; +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: + xmlGenericError(xmlGenericErrorContext, "?docb? "); + break; +#endif + case XML_DTD_NODE: + xmlGenericError(xmlGenericErrorContext, "?dtd? "); + break; + case XML_ELEMENT_DECL: + xmlGenericError(xmlGenericErrorContext, "?edecl? "); + break; + case XML_ATTRIBUTE_DECL: + xmlGenericError(xmlGenericErrorContext, "?adecl? "); + break; + case XML_ENTITY_DECL: + xmlGenericError(xmlGenericErrorContext, "?entdecl? "); + break; + case XML_NAMESPACE_DECL: + xmlGenericError(xmlGenericErrorContext, "?nsdecl? "); + break; + case XML_XINCLUDE_START: + xmlGenericError(xmlGenericErrorContext, "incstart "); + break; + case XML_XINCLUDE_END: + xmlGenericError(xmlGenericErrorContext, "incend "); + break; + } +} + +static void +xmlValidPrintNodeList(xmlNodePtr cur) { + if (cur == NULL) + xmlGenericError(xmlGenericErrorContext, "null "); + while (cur != NULL) { + xmlValidPrintNode(cur); + cur = cur->next; + } +} + +static void +xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) { + char expr[5000]; + + expr[0] = 0; + xmlGenericError(xmlGenericErrorContext, "valid: "); + xmlValidPrintNodeList(cur); + xmlGenericError(xmlGenericErrorContext, "against "); + xmlSnprintfElementContent(expr, 5000, cont, 1); + xmlGenericError(xmlGenericErrorContext, "%s\n", expr); +} + +static void +xmlValidDebugState(xmlValidStatePtr state) { + xmlGenericError(xmlGenericErrorContext, "("); + if (state->cont == NULL) + xmlGenericError(xmlGenericErrorContext, "null,"); + else + switch (state->cont->type) { + case XML_ELEMENT_CONTENT_PCDATA: + xmlGenericError(xmlGenericErrorContext, "pcdata,"); + break; + case XML_ELEMENT_CONTENT_ELEMENT: + xmlGenericError(xmlGenericErrorContext, "%s,", + state->cont->name); + break; + case XML_ELEMENT_CONTENT_SEQ: + xmlGenericError(xmlGenericErrorContext, "seq,"); + break; + case XML_ELEMENT_CONTENT_OR: + xmlGenericError(xmlGenericErrorContext, "or,"); + break; + } + xmlValidPrintNode(state->node); + xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)", + state->depth, state->occurs, state->state); +} + +static void +xmlValidStateDebug(xmlValidCtxtPtr ctxt) { + int i, j; + + xmlGenericError(xmlGenericErrorContext, "state: "); + xmlValidDebugState(ctxt->vstate); + xmlGenericError(xmlGenericErrorContext, " stack: %d ", + ctxt->vstateNr - 1); + for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--) + xmlValidDebugState(&ctxt->vstateTab[j]); + xmlGenericError(xmlGenericErrorContext, "\n"); +} + +/***** +#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c); + *****/ + +#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt); +#define DEBUG_VALID_MSG(m) \ + xmlGenericError(xmlGenericErrorContext, "%s\n", m); + +#else +#define DEBUG_VALID_STATE(n,c) +#define DEBUG_VALID_MSG(m) +#endif + +/* TODO: use hash table for accesses to elem and attribute definitions */ + + +#define CHECK_DTD \ + if (doc == NULL) return(0); \ + else if ((doc->intSubset == NULL) && \ + (doc->extSubset == NULL)) return(0) + +#ifdef LIBXML_REGEXP_ENABLED + +/************************************************************************ + * * + * Content model validation based on the regexps * + * * + ************************************************************************/ + +/** + * xmlValidBuildAContentModel: + * @content: the content model + * @ctxt: the schema parser context + * @name: the element name whose content is being built + * + * Generate the automata sequence needed for that type + * + * Returns 1 if successful or 0 in case of error. + */ +static int +xmlValidBuildAContentModel(xmlElementContentPtr content, + xmlValidCtxtPtr ctxt, + const xmlChar *name) { + if (content == NULL) { + xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR, + "Found NULL content in content model of %s\n", + name, NULL, NULL); + return(0); + } + switch (content->type) { + case XML_ELEMENT_CONTENT_PCDATA: + xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR, + "Found PCDATA in content model of %s\n", + name, NULL, NULL); + return(0); + break; + case XML_ELEMENT_CONTENT_ELEMENT: { + xmlAutomataStatePtr oldstate = ctxt->state; + xmlChar fn[50]; + xmlChar *fullname; + + fullname = xmlBuildQName(content->name, content->prefix, fn, 50); + if (fullname == NULL) { + xmlVErrMemory(ctxt, "Building content model"); + return(0); + } + + switch (content->ocur) { + case XML_ELEMENT_CONTENT_ONCE: + ctxt->state = xmlAutomataNewTransition(ctxt->am, + ctxt->state, NULL, fullname, NULL); + break; + case XML_ELEMENT_CONTENT_OPT: + ctxt->state = xmlAutomataNewTransition(ctxt->am, + ctxt->state, NULL, fullname, NULL); + xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); + break; + case XML_ELEMENT_CONTENT_PLUS: + ctxt->state = xmlAutomataNewTransition(ctxt->am, + ctxt->state, NULL, fullname, NULL); + xmlAutomataNewTransition(ctxt->am, ctxt->state, + ctxt->state, fullname, NULL); + break; + case XML_ELEMENT_CONTENT_MULT: + ctxt->state = xmlAutomataNewEpsilon(ctxt->am, + ctxt->state, NULL); + xmlAutomataNewTransition(ctxt->am, + ctxt->state, ctxt->state, fullname, NULL); + break; + } + if ((fullname != fn) && (fullname != content->name)) + xmlFree(fullname); + break; + } + case XML_ELEMENT_CONTENT_SEQ: { + xmlAutomataStatePtr oldstate, oldend; + xmlElementContentOccur ocur; + + /* + * Simply iterate over the content + */ + oldstate = ctxt->state; + ocur = content->ocur; + if (ocur != XML_ELEMENT_CONTENT_ONCE) { + ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL); + oldstate = ctxt->state; + } + do { + xmlValidBuildAContentModel(content->c1, ctxt, name); + content = content->c2; + } while ((content->type == XML_ELEMENT_CONTENT_SEQ) && + (content->ocur == XML_ELEMENT_CONTENT_ONCE)); + xmlValidBuildAContentModel(content, ctxt, name); + oldend = ctxt->state; + ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL); + switch (ocur) { + case XML_ELEMENT_CONTENT_ONCE: + break; + case XML_ELEMENT_CONTENT_OPT: + xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); + break; + case XML_ELEMENT_CONTENT_MULT: + xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); + xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate); + break; + case XML_ELEMENT_CONTENT_PLUS: + xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate); + break; + } + break; + } + case XML_ELEMENT_CONTENT_OR: { + xmlAutomataStatePtr oldstate, oldend; + xmlElementContentOccur ocur; + + ocur = content->ocur; + if ((ocur == XML_ELEMENT_CONTENT_PLUS) || + (ocur == XML_ELEMENT_CONTENT_MULT)) { + ctxt->state = xmlAutomataNewEpsilon(ctxt->am, + ctxt->state, NULL); + } + oldstate = ctxt->state; + oldend = xmlAutomataNewState(ctxt->am); + + /* + * iterate over the subtypes and remerge the end with an + * epsilon transition + */ + do { + ctxt->state = oldstate; + xmlValidBuildAContentModel(content->c1, ctxt, name); + xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend); + content = content->c2; + } while ((content->type == XML_ELEMENT_CONTENT_OR) && + (content->ocur == XML_ELEMENT_CONTENT_ONCE)); + ctxt->state = oldstate; + xmlValidBuildAContentModel(content, ctxt, name); + xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend); + ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL); + switch (ocur) { + case XML_ELEMENT_CONTENT_ONCE: + break; + case XML_ELEMENT_CONTENT_OPT: + xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); + break; + case XML_ELEMENT_CONTENT_MULT: + xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state); + xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate); + break; + case XML_ELEMENT_CONTENT_PLUS: + xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate); + break; + } + break; + } + default: + xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + "ContentModel broken for element %s\n", + (const char *) name); + return(0); + } + return(1); +} +/** + * xmlValidBuildContentModel: + * @ctxt: a validation context + * @elem: an element declaration node + * + * (Re)Build the automata associated to the content model of this + * element + * + * Returns 1 in case of success, 0 in case of error + */ +int +xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) { + + if ((ctxt == NULL) || (elem == NULL)) + return(0); + if (elem->type != XML_ELEMENT_DECL) + return(0); + if (elem->etype != XML_ELEMENT_TYPE_ELEMENT) + return(1); + /* TODO: should we rebuild in this case ? */ + if (elem->contModel != NULL) { + if (!xmlRegexpIsDeterminist(elem->contModel)) { + ctxt->valid = 0; + return(0); + } + return(1); + } + + ctxt->am = xmlNewAutomata(); + if (ctxt->am == NULL) { + xmlErrValidNode(ctxt, (xmlNodePtr) elem, + XML_ERR_INTERNAL_ERROR, + "Cannot create automata for element %s\n", + elem->name, NULL, NULL); + return(0); + } + ctxt->state = xmlAutomataGetInitState(ctxt->am); + xmlValidBuildAContentModel(elem->content, ctxt, elem->name); + xmlAutomataSetFinalState(ctxt->am, ctxt->state); + elem->contModel = xmlAutomataCompile(ctxt->am); + if (xmlRegexpIsDeterminist(elem->contModel) != 1) { + char expr[5000]; + expr[0] = 0; + xmlSnprintfElementContent(expr, 5000, elem->content, 1); + xmlErrValidNode(ctxt, (xmlNodePtr) elem, + XML_DTD_CONTENT_NOT_DETERMINIST, + "Content model of %s is not determinist: %s\n", + elem->name, BAD_CAST expr, NULL); +#ifdef DEBUG_REGEXP_ALGO + xmlRegexpPrint(stderr, elem->contModel); +#endif + ctxt->valid = 0; + ctxt->state = NULL; + xmlFreeAutomata(ctxt->am); + ctxt->am = NULL; + return(0); + } + ctxt->state = NULL; + xmlFreeAutomata(ctxt->am); + ctxt->am = NULL; + return(1); +} + +#endif /* LIBXML_REGEXP_ENABLED */ + +/**************************************************************** + * * + * Util functions for data allocation/deallocation * + * * + ****************************************************************/ + +/** + * xmlNewValidCtxt: + * + * Allocate a validation context structure. + * + * Returns NULL if not, otherwise the new validation context structure + */ +xmlValidCtxtPtr xmlNewValidCtxt(void) { + xmlValidCtxtPtr ret; + + if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL) { + xmlVErrMemory(NULL, "malloc failed"); + return (NULL); + } + + (void) memset(ret, 0, sizeof (xmlValidCtxt)); + + return (ret); +} + +/** + * xmlFreeValidCtxt: + * @cur: the validation context to free + * + * Free a validation context structure. + */ +void +xmlFreeValidCtxt(xmlValidCtxtPtr cur) { + if (cur->vstateTab != NULL) + xmlFree(cur->vstateTab); + if (cur->nodeTab != NULL) + xmlFree(cur->nodeTab); + xmlFree(cur); +} + +#endif /* LIBXML_VALID_ENABLED */ + +/** + * xmlNewDocElementContent: + * @doc: the document + * @name: the subelement name or NULL + * @type: the type of element content decl + * + * Allocate an element content structure for the document. + * + * Returns NULL if not, otherwise the new element content structure + */ +xmlElementContentPtr +xmlNewDocElementContent(xmlDocPtr doc, const xmlChar *name, + xmlElementContentType type) { + xmlElementContentPtr ret; + xmlDictPtr dict = NULL; + + if (doc != NULL) + dict = doc->dict; + + switch(type) { + case XML_ELEMENT_CONTENT_ELEMENT: + if (name == NULL) { + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "xmlNewElementContent : name == NULL !\n", + NULL); + } + break; + case XML_ELEMENT_CONTENT_PCDATA: + case XML_ELEMENT_CONTENT_SEQ: + case XML_ELEMENT_CONTENT_OR: + if (name != NULL) { + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "xmlNewElementContent : name != NULL !\n", + NULL); + } + break; + default: + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "Internal: ELEMENT content corrupted invalid type\n", + NULL); + return(NULL); + } + ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent)); + if (ret == NULL) { + xmlVErrMemory(NULL, "malloc failed"); + return(NULL); + } + memset(ret, 0, sizeof(xmlElementContent)); + ret->type = type; + ret->ocur = XML_ELEMENT_CONTENT_ONCE; + if (name != NULL) { + int l; + const xmlChar *tmp; + + tmp = xmlSplitQName3(name, &l); + if (tmp == NULL) { + if (dict == NULL) + ret->name = xmlStrdup(name); + else + ret->name = xmlDictLookup(dict, name, -1); + } else { + if (dict == NULL) { + ret->prefix = xmlStrndup(name, l); + ret->name = xmlStrdup(tmp); + } else { + ret->prefix = xmlDictLookup(dict, name, l); + ret->name = xmlDictLookup(dict, tmp, -1); + } + } + } + return(ret); +} + +/** + * xmlNewElementContent: + * @name: the subelement name or NULL + * @type: the type of element content decl + * + * Allocate an element content structure. + * Deprecated in favor of xmlNewDocElementContent + * + * Returns NULL if not, otherwise the new element content structure + */ +xmlElementContentPtr +xmlNewElementContent(const xmlChar *name, xmlElementContentType type) { + return(xmlNewDocElementContent(NULL, name, type)); +} + +/** + * xmlCopyDocElementContent: + * @doc: the document owning the element declaration + * @cur: An element content pointer. + * + * Build a copy of an element content description. + * + * Returns the new xmlElementContentPtr or NULL in case of error. + */ +xmlElementContentPtr +xmlCopyDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) { + xmlElementContentPtr ret = NULL, prev = NULL, tmp; + xmlDictPtr dict = NULL; + + if (cur == NULL) return(NULL); + + if (doc != NULL) + dict = doc->dict; + + ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent)); + if (ret == NULL) { + xmlVErrMemory(NULL, "malloc failed"); + return(NULL); + } + memset(ret, 0, sizeof(xmlElementContent)); + ret->type = cur->type; + ret->ocur = cur->ocur; + if (cur->name != NULL) { + if (dict) + ret->name = xmlDictLookup(dict, cur->name, -1); + else + ret->name = xmlStrdup(cur->name); + } + + if (cur->prefix != NULL) { + if (dict) + ret->prefix = xmlDictLookup(dict, cur->prefix, -1); + else + ret->prefix = xmlStrdup(cur->prefix); + } + if (cur->c1 != NULL) + ret->c1 = xmlCopyDocElementContent(doc, cur->c1); + if (ret->c1 != NULL) + ret->c1->parent = ret; + if (cur->c2 != NULL) { + prev = ret; + cur = cur->c2; + while (cur != NULL) { + tmp = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent)); + if (tmp == NULL) { + xmlVErrMemory(NULL, "malloc failed"); + return(ret); + } + memset(tmp, 0, sizeof(xmlElementContent)); + tmp->type = cur->type; + tmp->ocur = cur->ocur; + prev->c2 = tmp; + if (cur->name != NULL) { + if (dict) + tmp->name = xmlDictLookup(dict, cur->name, -1); + else + tmp->name = xmlStrdup(cur->name); + } + + if (cur->prefix != NULL) { + if (dict) + tmp->prefix = xmlDictLookup(dict, cur->prefix, -1); + else + tmp->prefix = xmlStrdup(cur->prefix); + } + if (cur->c1 != NULL) + tmp->c1 = xmlCopyDocElementContent(doc,cur->c1); + if (tmp->c1 != NULL) + tmp->c1->parent = ret; + prev = tmp; + cur = cur->c2; + } + } + return(ret); +} + +/** + * xmlCopyElementContent: + * @cur: An element content pointer. + * + * Build a copy of an element content description. + * Deprecated, use xmlCopyDocElementContent instead + * + * Returns the new xmlElementContentPtr or NULL in case of error. + */ +xmlElementContentPtr +xmlCopyElementContent(xmlElementContentPtr cur) { + return(xmlCopyDocElementContent(NULL, cur)); +} + +/** + * xmlFreeDocElementContent: + * @doc: the document owning the element declaration + * @cur: the element content tree to free + * + * Free an element content structure. The whole subtree is removed. + */ +void +xmlFreeDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) { + xmlDictPtr dict = NULL; + size_t depth = 0; + + if (cur == NULL) + return; + if (doc != NULL) + dict = doc->dict; + + while (1) { + xmlElementContentPtr parent; + + while ((cur->c1 != NULL) || (cur->c2 != NULL)) { + cur = (cur->c1 != NULL) ? cur->c1 : cur->c2; + depth += 1; + } + + switch (cur->type) { + case XML_ELEMENT_CONTENT_PCDATA: + case XML_ELEMENT_CONTENT_ELEMENT: + case XML_ELEMENT_CONTENT_SEQ: + case XML_ELEMENT_CONTENT_OR: + break; + default: + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "Internal: ELEMENT content corrupted invalid type\n", + NULL); + return; + } + if (dict) { + if ((cur->name != NULL) && (!xmlDictOwns(dict, cur->name))) + xmlFree((xmlChar *) cur->name); + if ((cur->prefix != NULL) && (!xmlDictOwns(dict, cur->prefix))) + xmlFree((xmlChar *) cur->prefix); + } else { + if (cur->name != NULL) xmlFree((xmlChar *) cur->name); + if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix); + } + parent = cur->parent; + if ((depth == 0) || (parent == NULL)) { + xmlFree(cur); + break; + } + if (cur == parent->c1) + parent->c1 = NULL; + else + parent->c2 = NULL; + xmlFree(cur); + + if (parent->c2 != NULL) { + cur = parent->c2; + } else { + depth -= 1; + cur = parent; + } + } +} + +/** + * xmlFreeElementContent: + * @cur: the element content tree to free + * + * Free an element content structure. The whole subtree is removed. + * Deprecated, use xmlFreeDocElementContent instead + */ +void +xmlFreeElementContent(xmlElementContentPtr cur) { + xmlFreeDocElementContent(NULL, cur); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlDumpElementOccur: + * @buf: An XML buffer + * @cur: An element table + * + * Dump the occurence operator of an element. + */ +static void +xmlDumpElementOccur(xmlBufferPtr buf, xmlElementContentPtr cur) { + switch (cur->ocur) { + case XML_ELEMENT_CONTENT_ONCE: + break; + case XML_ELEMENT_CONTENT_OPT: + xmlBufferWriteChar(buf, "?"); + break; + case XML_ELEMENT_CONTENT_MULT: + xmlBufferWriteChar(buf, "*"); + break; + case XML_ELEMENT_CONTENT_PLUS: + xmlBufferWriteChar(buf, "+"); + break; + } +} + +/** + * xmlDumpElementContent: + * @buf: An XML buffer + * @content: An element table + * + * This will dump the content of the element table as an XML DTD definition + */ +static void +xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content) { + xmlElementContentPtr cur; + + if (content == NULL) return; + + xmlBufferWriteChar(buf, "("); + cur = content; + + do { + if (cur == NULL) return; + + switch (cur->type) { + case XML_ELEMENT_CONTENT_PCDATA: + xmlBufferWriteChar(buf, "#PCDATA"); + break; + case XML_ELEMENT_CONTENT_ELEMENT: + if (cur->prefix != NULL) { + xmlBufferWriteCHAR(buf, cur->prefix); + xmlBufferWriteChar(buf, ":"); + } + xmlBufferWriteCHAR(buf, cur->name); + break; + case XML_ELEMENT_CONTENT_SEQ: + case XML_ELEMENT_CONTENT_OR: + if ((cur != content) && + (cur->parent != NULL) && + ((cur->type != cur->parent->type) || + (cur->ocur != XML_ELEMENT_CONTENT_ONCE))) + xmlBufferWriteChar(buf, "("); + cur = cur->c1; + continue; + default: + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "Internal: ELEMENT cur corrupted invalid type\n", + NULL); + } + + while (cur != content) { + xmlElementContentPtr parent = cur->parent; + + if (parent == NULL) return; + + if (((cur->type == XML_ELEMENT_CONTENT_OR) || + (cur->type == XML_ELEMENT_CONTENT_SEQ)) && + ((cur->type != parent->type) || + (cur->ocur != XML_ELEMENT_CONTENT_ONCE))) + xmlBufferWriteChar(buf, ")"); + xmlDumpElementOccur(buf, cur); + + if (cur == parent->c1) { + if (parent->type == XML_ELEMENT_CONTENT_SEQ) + xmlBufferWriteChar(buf, " , "); + else if (parent->type == XML_ELEMENT_CONTENT_OR) + xmlBufferWriteChar(buf, " | "); + + cur = parent->c2; + break; + } + + cur = parent; + } + } while (cur != content); + + xmlBufferWriteChar(buf, ")"); + xmlDumpElementOccur(buf, content); +} + +/** + * xmlSprintfElementContent: + * @buf: an output buffer + * @content: An element table + * @englob: 1 if one must print the englobing parenthesis, 0 otherwise + * + * Deprecated, unsafe, use xmlSnprintfElementContent + */ +void +xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED, + xmlElementContentPtr content ATTRIBUTE_UNUSED, + int englob ATTRIBUTE_UNUSED) { +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +/** + * xmlSnprintfElementContent: + * @buf: an output buffer + * @size: the buffer size + * @content: An element table + * @englob: 1 if one must print the englobing parenthesis, 0 otherwise + * + * This will dump the content of the element content definition + * Intended just for the debug routine + */ +void +xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int englob) { + int len; + + if (content == NULL) return; + len = strlen(buf); + if (size - len < 50) { + if ((size - len > 4) && (buf[len - 1] != '.')) + strcat(buf, " ..."); + return; + } + if (englob) strcat(buf, "("); + switch (content->type) { + case XML_ELEMENT_CONTENT_PCDATA: + strcat(buf, "#PCDATA"); + break; + case XML_ELEMENT_CONTENT_ELEMENT: { + int qnameLen = xmlStrlen(content->name); + + if (content->prefix != NULL) + qnameLen += xmlStrlen(content->prefix) + 1; + if (size - len < qnameLen + 10) { + strcat(buf, " ..."); + return; + } + if (content->prefix != NULL) { + strcat(buf, (char *) content->prefix); + strcat(buf, ":"); + } + if (content->name != NULL) + strcat(buf, (char *) content->name); + break; + } + case XML_ELEMENT_CONTENT_SEQ: + if ((content->c1->type == XML_ELEMENT_CONTENT_OR) || + (content->c1->type == XML_ELEMENT_CONTENT_SEQ)) + xmlSnprintfElementContent(buf, size, content->c1, 1); + else + xmlSnprintfElementContent(buf, size, content->c1, 0); + len = strlen(buf); + if (size - len < 50) { + if ((size - len > 4) && (buf[len - 1] != '.')) + strcat(buf, " ..."); + return; + } + strcat(buf, " , "); + if (((content->c2->type == XML_ELEMENT_CONTENT_OR) || + (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) && + (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT)) + xmlSnprintfElementContent(buf, size, content->c2, 1); + else + xmlSnprintfElementContent(buf, size, content->c2, 0); + break; + case XML_ELEMENT_CONTENT_OR: + if ((content->c1->type == XML_ELEMENT_CONTENT_OR) || + (content->c1->type == XML_ELEMENT_CONTENT_SEQ)) + xmlSnprintfElementContent(buf, size, content->c1, 1); + else + xmlSnprintfElementContent(buf, size, content->c1, 0); + len = strlen(buf); + if (size - len < 50) { + if ((size - len > 4) && (buf[len - 1] != '.')) + strcat(buf, " ..."); + return; + } + strcat(buf, " | "); + if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) || + (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) && + (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT)) + xmlSnprintfElementContent(buf, size, content->c2, 1); + else + xmlSnprintfElementContent(buf, size, content->c2, 0); + break; + } + if (size - strlen(buf) <= 2) return; + if (englob) + strcat(buf, ")"); + switch (content->ocur) { + case XML_ELEMENT_CONTENT_ONCE: + break; + case XML_ELEMENT_CONTENT_OPT: + strcat(buf, "?"); + break; + case XML_ELEMENT_CONTENT_MULT: + strcat(buf, "*"); + break; + case XML_ELEMENT_CONTENT_PLUS: + strcat(buf, "+"); + break; + } +} + +/**************************************************************** + * * + * Registration of DTD declarations * + * * + ****************************************************************/ + +/** + * xmlFreeElement: + * @elem: An element + * + * Deallocate the memory used by an element definition + */ +static void +xmlFreeElement(xmlElementPtr elem) { + if (elem == NULL) return; + xmlUnlinkNode((xmlNodePtr) elem); + xmlFreeDocElementContent(elem->doc, elem->content); + if (elem->name != NULL) + xmlFree((xmlChar *) elem->name); + if (elem->prefix != NULL) + xmlFree((xmlChar *) elem->prefix); +#ifdef LIBXML_REGEXP_ENABLED + if (elem->contModel != NULL) + xmlRegFreeRegexp(elem->contModel); +#endif + xmlFree(elem); +} + + +/** + * xmlAddElementDecl: + * @ctxt: the validation context + * @dtd: pointer to the DTD + * @name: the entity name + * @type: the element type + * @content: the element content tree or NULL + * + * Register a new element declaration + * + * Returns NULL if not, otherwise the entity + */ +xmlElementPtr +xmlAddElementDecl(xmlValidCtxtPtr ctxt, + xmlDtdPtr dtd, const xmlChar *name, + xmlElementTypeVal type, + xmlElementContentPtr content) { + xmlElementPtr ret; + xmlElementTablePtr table; + xmlAttributePtr oldAttributes = NULL; + xmlChar *ns, *uqname; + + if (dtd == NULL) { + return(NULL); + } + if (name == NULL) { + return(NULL); + } + + switch (type) { + case XML_ELEMENT_TYPE_EMPTY: + if (content != NULL) { + xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + "xmlAddElementDecl: content != NULL for EMPTY\n", + NULL); + return(NULL); + } + break; + case XML_ELEMENT_TYPE_ANY: + if (content != NULL) { + xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + "xmlAddElementDecl: content != NULL for ANY\n", + NULL); + return(NULL); + } + break; + case XML_ELEMENT_TYPE_MIXED: + if (content == NULL) { + xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + "xmlAddElementDecl: content == NULL for MIXED\n", + NULL); + return(NULL); + } + break; + case XML_ELEMENT_TYPE_ELEMENT: + if (content == NULL) { + xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + "xmlAddElementDecl: content == NULL for ELEMENT\n", + NULL); + return(NULL); + } + break; + default: + xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + "Internal: ELEMENT decl corrupted invalid type\n", + NULL); + return(NULL); + } + + /* + * check if name is a QName + */ + uqname = xmlSplitQName2(name, &ns); + if (uqname != NULL) + name = uqname; + + /* + * Create the Element table if needed. + */ + table = (xmlElementTablePtr) dtd->elements; + if (table == NULL) { + xmlDictPtr dict = NULL; + + if (dtd->doc != NULL) + dict = dtd->doc->dict; + table = xmlHashCreateDict(0, dict); + dtd->elements = (void *) table; + } + if (table == NULL) { + xmlVErrMemory(ctxt, + "xmlAddElementDecl: Table creation failed!\n"); + if (uqname != NULL) + xmlFree(uqname); + if (ns != NULL) + xmlFree(ns); + return(NULL); + } + + /* + * lookup old attributes inserted on an undefined element in the + * internal subset. + */ + if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) { + ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns); + if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) { + oldAttributes = ret->attributes; + ret->attributes = NULL; + xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL); + xmlFreeElement(ret); + } + } + + /* + * The element may already be present if one of its attribute + * was registered first + */ + ret = xmlHashLookup2(table, name, ns); + if (ret != NULL) { + if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) { +#ifdef LIBXML_VALID_ENABLED + /* + * The element is already defined in this DTD. + */ + xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED, + "Redefinition of element %s\n", + name, NULL, NULL); +#endif /* LIBXML_VALID_ENABLED */ + if (uqname != NULL) + xmlFree(uqname); + if (ns != NULL) + xmlFree(ns); + return(NULL); + } + if (ns != NULL) { + xmlFree(ns); + ns = NULL; + } + } else { + ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement)); + if (ret == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + if (uqname != NULL) + xmlFree(uqname); + if (ns != NULL) + xmlFree(ns); + return(NULL); + } + memset(ret, 0, sizeof(xmlElement)); + ret->type = XML_ELEMENT_DECL; + + /* + * fill the structure. + */ + ret->name = xmlStrdup(name); + if (ret->name == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + if (uqname != NULL) + xmlFree(uqname); + if (ns != NULL) + xmlFree(ns); + xmlFree(ret); + return(NULL); + } + ret->prefix = ns; + + /* + * Validity Check: + * Insertion must not fail + */ + if (xmlHashAddEntry2(table, name, ns, ret)) { +#ifdef LIBXML_VALID_ENABLED + /* + * The element is already defined in this DTD. + */ + xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED, + "Redefinition of element %s\n", + name, NULL, NULL); +#endif /* LIBXML_VALID_ENABLED */ + xmlFreeElement(ret); + if (uqname != NULL) + xmlFree(uqname); + return(NULL); + } + /* + * For new element, may have attributes from earlier + * definition in internal subset + */ + ret->attributes = oldAttributes; + } + + /* + * Finish to fill the structure. + */ + ret->etype = type; + /* + * Avoid a stupid copy when called by the parser + * and flag it by setting a special parent value + * so the parser doesn't unallocate it. + */ + if ((ctxt != NULL) && + ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) || + (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1))) { + ret->content = content; + if (content != NULL) + content->parent = (xmlElementContentPtr) 1; + } else { + ret->content = xmlCopyDocElementContent(dtd->doc, content); + } + + /* + * Link it to the DTD + */ + ret->parent = dtd; + ret->doc = dtd->doc; + if (dtd->last == NULL) { + dtd->children = dtd->last = (xmlNodePtr) ret; + } else { + dtd->last->next = (xmlNodePtr) ret; + ret->prev = dtd->last; + dtd->last = (xmlNodePtr) ret; + } + if (uqname != NULL) + xmlFree(uqname); + return(ret); +} + +static void +xmlFreeElementTableEntry(void *elem, const xmlChar *name ATTRIBUTE_UNUSED) { + xmlFreeElement((xmlElementPtr) elem); +} + +/** + * xmlFreeElementTable: + * @table: An element table + * + * Deallocate the memory used by an element hash table. + */ +void +xmlFreeElementTable(xmlElementTablePtr table) { + xmlHashFree(table, xmlFreeElementTableEntry); +} + +#ifdef LIBXML_TREE_ENABLED +/** + * xmlCopyElement: + * @elem: An element + * + * Build a copy of an element. + * + * Returns the new xmlElementPtr or NULL in case of error. + */ +static void * +xmlCopyElement(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { + xmlElementPtr elem = (xmlElementPtr) payload; + xmlElementPtr cur; + + cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement)); + if (cur == NULL) { + xmlVErrMemory(NULL, "malloc failed"); + return(NULL); + } + memset(cur, 0, sizeof(xmlElement)); + cur->type = XML_ELEMENT_DECL; + cur->etype = elem->etype; + if (elem->name != NULL) + cur->name = xmlStrdup(elem->name); + else + cur->name = NULL; + if (elem->prefix != NULL) + cur->prefix = xmlStrdup(elem->prefix); + else + cur->prefix = NULL; + cur->content = xmlCopyElementContent(elem->content); + /* TODO : rebuild the attribute list on the copy */ + cur->attributes = NULL; + return(cur); +} + +/** + * xmlCopyElementTable: + * @table: An element table + * + * Build a copy of an element table. + * + * Returns the new xmlElementTablePtr or NULL in case of error. + */ +xmlElementTablePtr +xmlCopyElementTable(xmlElementTablePtr table) { + return((xmlElementTablePtr) xmlHashCopy(table, xmlCopyElement)); +} +#endif /* LIBXML_TREE_ENABLED */ + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlDumpElementDecl: + * @buf: the XML buffer output + * @elem: An element table + * + * This will dump the content of the element declaration as an XML + * DTD definition + */ +void +xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) { + if ((buf == NULL) || (elem == NULL)) + return; + switch (elem->etype) { + case XML_ELEMENT_TYPE_EMPTY: + xmlBufferWriteChar(buf, "prefix != NULL) { + xmlBufferWriteCHAR(buf, elem->prefix); + xmlBufferWriteChar(buf, ":"); + } + xmlBufferWriteCHAR(buf, elem->name); + xmlBufferWriteChar(buf, " EMPTY>\n"); + break; + case XML_ELEMENT_TYPE_ANY: + xmlBufferWriteChar(buf, "prefix != NULL) { + xmlBufferWriteCHAR(buf, elem->prefix); + xmlBufferWriteChar(buf, ":"); + } + xmlBufferWriteCHAR(buf, elem->name); + xmlBufferWriteChar(buf, " ANY>\n"); + break; + case XML_ELEMENT_TYPE_MIXED: + xmlBufferWriteChar(buf, "prefix != NULL) { + xmlBufferWriteCHAR(buf, elem->prefix); + xmlBufferWriteChar(buf, ":"); + } + xmlBufferWriteCHAR(buf, elem->name); + xmlBufferWriteChar(buf, " "); + xmlDumpElementContent(buf, elem->content); + xmlBufferWriteChar(buf, ">\n"); + break; + case XML_ELEMENT_TYPE_ELEMENT: + xmlBufferWriteChar(buf, "prefix != NULL) { + xmlBufferWriteCHAR(buf, elem->prefix); + xmlBufferWriteChar(buf, ":"); + } + xmlBufferWriteCHAR(buf, elem->name); + xmlBufferWriteChar(buf, " "); + xmlDumpElementContent(buf, elem->content); + xmlBufferWriteChar(buf, ">\n"); + break; + default: + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "Internal: ELEMENT struct corrupted invalid type\n", + NULL); + } +} + +/** + * xmlDumpElementDeclScan: + * @elem: An element table + * @buf: the XML buffer output + * + * This routine is used by the hash scan function. It just reverses + * the arguments. + */ +static void +xmlDumpElementDeclScan(void *elem, void *buf, + const xmlChar *name ATTRIBUTE_UNUSED) { + xmlDumpElementDecl((xmlBufferPtr) buf, (xmlElementPtr) elem); +} + +/** + * xmlDumpElementTable: + * @buf: the XML buffer output + * @table: An element table + * + * This will dump the content of the element table as an XML DTD definition + */ +void +xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) { + if ((buf == NULL) || (table == NULL)) + return; + xmlHashScan(table, xmlDumpElementDeclScan, buf); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +/** + * xmlCreateEnumeration: + * @name: the enumeration name or NULL + * + * create and initialize an enumeration attribute node. + * + * Returns the xmlEnumerationPtr just created or NULL in case + * of error. + */ +xmlEnumerationPtr +xmlCreateEnumeration(const xmlChar *name) { + xmlEnumerationPtr ret; + + ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration)); + if (ret == NULL) { + xmlVErrMemory(NULL, "malloc failed"); + return(NULL); + } + memset(ret, 0, sizeof(xmlEnumeration)); + + if (name != NULL) + ret->name = xmlStrdup(name); + return(ret); +} + +/** + * xmlFreeEnumeration: + * @cur: the tree to free. + * + * free an enumeration attribute node (recursive). + */ +void +xmlFreeEnumeration(xmlEnumerationPtr cur) { + if (cur == NULL) return; + + if (cur->next != NULL) xmlFreeEnumeration(cur->next); + + if (cur->name != NULL) xmlFree((xmlChar *) cur->name); + xmlFree(cur); +} + +#ifdef LIBXML_TREE_ENABLED +/** + * xmlCopyEnumeration: + * @cur: the tree to copy. + * + * Copy an enumeration attribute node (recursive). + * + * Returns the xmlEnumerationPtr just created or NULL in case + * of error. + */ +xmlEnumerationPtr +xmlCopyEnumeration(xmlEnumerationPtr cur) { + xmlEnumerationPtr ret; + + if (cur == NULL) return(NULL); + ret = xmlCreateEnumeration((xmlChar *) cur->name); + if (ret == NULL) return(NULL); + + if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next); + else ret->next = NULL; + + return(ret); +} +#endif /* LIBXML_TREE_ENABLED */ + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlDumpEnumeration: + * @buf: the XML buffer output + * @enum: An enumeration + * + * This will dump the content of the enumeration + */ +static void +xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) { + if ((buf == NULL) || (cur == NULL)) + return; + + xmlBufferWriteCHAR(buf, cur->name); + if (cur->next == NULL) + xmlBufferWriteChar(buf, ")"); + else { + xmlBufferWriteChar(buf, " | "); + xmlDumpEnumeration(buf, cur->next); + } +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +#ifdef LIBXML_VALID_ENABLED +/** + * xmlScanIDAttributeDecl: + * @ctxt: the validation context + * @elem: the element name + * @err: whether to raise errors here + * + * Verify that the element don't have too many ID attributes + * declared. + * + * Returns the number of ID attributes found. + */ +static int +xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem, int err) { + xmlAttributePtr cur; + int ret = 0; + + if (elem == NULL) return(0); + cur = elem->attributes; + while (cur != NULL) { + if (cur->atype == XML_ATTRIBUTE_ID) { + ret ++; + if ((ret > 1) && (err)) + xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_MULTIPLE_ID, + "Element %s has too many ID attributes defined : %s\n", + elem->name, cur->name, NULL); + } + cur = cur->nexth; + } + return(ret); +} +#endif /* LIBXML_VALID_ENABLED */ + +/** + * xmlFreeAttribute: + * @elem: An attribute + * + * Deallocate the memory used by an attribute definition + */ +static void +xmlFreeAttribute(xmlAttributePtr attr) { + xmlDictPtr dict; + + if (attr == NULL) return; + if (attr->doc != NULL) + dict = attr->doc->dict; + else + dict = NULL; + xmlUnlinkNode((xmlNodePtr) attr); + if (attr->tree != NULL) + xmlFreeEnumeration(attr->tree); + if (dict) { + if ((attr->elem != NULL) && (!xmlDictOwns(dict, attr->elem))) + xmlFree((xmlChar *) attr->elem); + if ((attr->name != NULL) && (!xmlDictOwns(dict, attr->name))) + xmlFree((xmlChar *) attr->name); + if ((attr->prefix != NULL) && (!xmlDictOwns(dict, attr->prefix))) + xmlFree((xmlChar *) attr->prefix); + if ((attr->defaultValue != NULL) && + (!xmlDictOwns(dict, attr->defaultValue))) + xmlFree((xmlChar *) attr->defaultValue); + } else { + if (attr->elem != NULL) + xmlFree((xmlChar *) attr->elem); + if (attr->name != NULL) + xmlFree((xmlChar *) attr->name); + if (attr->defaultValue != NULL) + xmlFree((xmlChar *) attr->defaultValue); + if (attr->prefix != NULL) + xmlFree((xmlChar *) attr->prefix); + } + xmlFree(attr); +} + + +/** + * xmlAddAttributeDecl: + * @ctxt: the validation context + * @dtd: pointer to the DTD + * @elem: the element name + * @name: the attribute name + * @ns: the attribute namespace prefix + * @type: the attribute type + * @def: the attribute default type + * @defaultValue: the attribute default value + * @tree: if it's an enumeration, the associated list + * + * Register a new attribute declaration + * Note that @tree becomes the ownership of the DTD + * + * Returns NULL if not new, otherwise the attribute decl + */ +xmlAttributePtr +xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, + xmlDtdPtr dtd, const xmlChar *elem, + const xmlChar *name, const xmlChar *ns, + xmlAttributeType type, xmlAttributeDefault def, + const xmlChar *defaultValue, xmlEnumerationPtr tree) { + xmlAttributePtr ret; + xmlAttributeTablePtr table; + xmlElementPtr elemDef; + xmlDictPtr dict = NULL; + + if (dtd == NULL) { + xmlFreeEnumeration(tree); + return(NULL); + } + if (name == NULL) { + xmlFreeEnumeration(tree); + return(NULL); + } + if (elem == NULL) { + xmlFreeEnumeration(tree); + return(NULL); + } + if (dtd->doc != NULL) + dict = dtd->doc->dict; + +#ifdef LIBXML_VALID_ENABLED + /* + * Check the type and possibly the default value. + */ + switch (type) { + case XML_ATTRIBUTE_CDATA: + break; + case XML_ATTRIBUTE_ID: + break; + case XML_ATTRIBUTE_IDREF: + break; + case XML_ATTRIBUTE_IDREFS: + break; + case XML_ATTRIBUTE_ENTITY: + break; + case XML_ATTRIBUTE_ENTITIES: + break; + case XML_ATTRIBUTE_NMTOKEN: + break; + case XML_ATTRIBUTE_NMTOKENS: + break; + case XML_ATTRIBUTE_ENUMERATION: + break; + case XML_ATTRIBUTE_NOTATION: + break; + default: + xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + "Internal: ATTRIBUTE struct corrupted invalid type\n", + NULL); + xmlFreeEnumeration(tree); + return(NULL); + } + if ((defaultValue != NULL) && + (!xmlValidateAttributeValueInternal(dtd->doc, type, defaultValue))) { + xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_DEFAULT, + "Attribute %s of %s: invalid default value\n", + elem, name, defaultValue); + defaultValue = NULL; + if (ctxt != NULL) + ctxt->valid = 0; + } +#endif /* LIBXML_VALID_ENABLED */ + + /* + * Check first that an attribute defined in the external subset wasn't + * already defined in the internal subset + */ + if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) && + (dtd->doc->intSubset != NULL) && + (dtd->doc->intSubset->attributes != NULL)) { + ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem); + if (ret != NULL) { + xmlFreeEnumeration(tree); + return(NULL); + } + } + + /* + * Create the Attribute table if needed. + */ + table = (xmlAttributeTablePtr) dtd->attributes; + if (table == NULL) { + table = xmlHashCreateDict(0, dict); + dtd->attributes = (void *) table; + } + if (table == NULL) { + xmlVErrMemory(ctxt, + "xmlAddAttributeDecl: Table creation failed!\n"); + xmlFreeEnumeration(tree); + return(NULL); + } + + + ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute)); + if (ret == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + xmlFreeEnumeration(tree); + return(NULL); + } + memset(ret, 0, sizeof(xmlAttribute)); + ret->type = XML_ATTRIBUTE_DECL; + + /* + * fill the structure. + */ + ret->atype = type; + /* + * doc must be set before possible error causes call + * to xmlFreeAttribute (because it's used to check on + * dict use) + */ + ret->doc = dtd->doc; + if (dict) { + ret->name = xmlDictLookup(dict, name, -1); + ret->prefix = xmlDictLookup(dict, ns, -1); + ret->elem = xmlDictLookup(dict, elem, -1); + } else { + ret->name = xmlStrdup(name); + ret->prefix = xmlStrdup(ns); + ret->elem = xmlStrdup(elem); + } + ret->def = def; + ret->tree = tree; + if (defaultValue != NULL) { + if (dict) + ret->defaultValue = xmlDictLookup(dict, defaultValue, -1); + else + ret->defaultValue = xmlStrdup(defaultValue); + } + + /* + * Validity Check: + * Search the DTD for previous declarations of the ATTLIST + */ + if (xmlHashAddEntry3(table, ret->name, ret->prefix, ret->elem, ret) < 0) { +#ifdef LIBXML_VALID_ENABLED + /* + * The attribute is already defined in this DTD. + */ + xmlErrValidWarning(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_REDEFINED, + "Attribute %s of element %s: already defined\n", + name, elem, NULL); +#endif /* LIBXML_VALID_ENABLED */ + xmlFreeAttribute(ret); + return(NULL); + } + + /* + * Validity Check: + * Multiple ID per element + */ + elemDef = xmlGetDtdElementDesc2(dtd, elem, 1); + if (elemDef != NULL) { + +#ifdef LIBXML_VALID_ENABLED + if ((type == XML_ATTRIBUTE_ID) && + (xmlScanIDAttributeDecl(NULL, elemDef, 1) != 0)) { + xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID, + "Element %s has too may ID attributes defined : %s\n", + elem, name, NULL); + if (ctxt != NULL) + ctxt->valid = 0; + } +#endif /* LIBXML_VALID_ENABLED */ + + /* + * Insert namespace default def first they need to be + * processed first. + */ + if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) || + ((ret->prefix != NULL && + (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) { + ret->nexth = elemDef->attributes; + elemDef->attributes = ret; + } else { + xmlAttributePtr tmp = elemDef->attributes; + + while ((tmp != NULL) && + ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) || + ((ret->prefix != NULL && + (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) { + if (tmp->nexth == NULL) + break; + tmp = tmp->nexth; + } + if (tmp != NULL) { + ret->nexth = tmp->nexth; + tmp->nexth = ret; + } else { + ret->nexth = elemDef->attributes; + elemDef->attributes = ret; + } + } + } + + /* + * Link it to the DTD + */ + ret->parent = dtd; + if (dtd->last == NULL) { + dtd->children = dtd->last = (xmlNodePtr) ret; + } else { + dtd->last->next = (xmlNodePtr) ret; + ret->prev = dtd->last; + dtd->last = (xmlNodePtr) ret; + } + return(ret); +} + +static void +xmlFreeAttributeTableEntry(void *attr, const xmlChar *name ATTRIBUTE_UNUSED) { + xmlFreeAttribute((xmlAttributePtr) attr); +} + +/** + * xmlFreeAttributeTable: + * @table: An attribute table + * + * Deallocate the memory used by an entities hash table. + */ +void +xmlFreeAttributeTable(xmlAttributeTablePtr table) { + xmlHashFree(table, xmlFreeAttributeTableEntry); +} + +#ifdef LIBXML_TREE_ENABLED +/** + * xmlCopyAttribute: + * @attr: An attribute + * + * Build a copy of an attribute. + * + * Returns the new xmlAttributePtr or NULL in case of error. + */ +static void * +xmlCopyAttribute(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { + xmlAttributePtr attr = (xmlAttributePtr) payload; + xmlAttributePtr cur; + + cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute)); + if (cur == NULL) { + xmlVErrMemory(NULL, "malloc failed"); + return(NULL); + } + memset(cur, 0, sizeof(xmlAttribute)); + cur->type = XML_ATTRIBUTE_DECL; + cur->atype = attr->atype; + cur->def = attr->def; + cur->tree = xmlCopyEnumeration(attr->tree); + if (attr->elem != NULL) + cur->elem = xmlStrdup(attr->elem); + if (attr->name != NULL) + cur->name = xmlStrdup(attr->name); + if (attr->prefix != NULL) + cur->prefix = xmlStrdup(attr->prefix); + if (attr->defaultValue != NULL) + cur->defaultValue = xmlStrdup(attr->defaultValue); + return(cur); +} + +/** + * xmlCopyAttributeTable: + * @table: An attribute table + * + * Build a copy of an attribute table. + * + * Returns the new xmlAttributeTablePtr or NULL in case of error. + */ +xmlAttributeTablePtr +xmlCopyAttributeTable(xmlAttributeTablePtr table) { + return((xmlAttributeTablePtr) xmlHashCopy(table, xmlCopyAttribute)); +} +#endif /* LIBXML_TREE_ENABLED */ + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlDumpAttributeDecl: + * @buf: the XML buffer output + * @attr: An attribute declaration + * + * This will dump the content of the attribute declaration as an XML + * DTD definition + */ +void +xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) { + if ((buf == NULL) || (attr == NULL)) + return; + xmlBufferWriteChar(buf, "elem); + xmlBufferWriteChar(buf, " "); + if (attr->prefix != NULL) { + xmlBufferWriteCHAR(buf, attr->prefix); + xmlBufferWriteChar(buf, ":"); + } + xmlBufferWriteCHAR(buf, attr->name); + switch (attr->atype) { + case XML_ATTRIBUTE_CDATA: + xmlBufferWriteChar(buf, " CDATA"); + break; + case XML_ATTRIBUTE_ID: + xmlBufferWriteChar(buf, " ID"); + break; + case XML_ATTRIBUTE_IDREF: + xmlBufferWriteChar(buf, " IDREF"); + break; + case XML_ATTRIBUTE_IDREFS: + xmlBufferWriteChar(buf, " IDREFS"); + break; + case XML_ATTRIBUTE_ENTITY: + xmlBufferWriteChar(buf, " ENTITY"); + break; + case XML_ATTRIBUTE_ENTITIES: + xmlBufferWriteChar(buf, " ENTITIES"); + break; + case XML_ATTRIBUTE_NMTOKEN: + xmlBufferWriteChar(buf, " NMTOKEN"); + break; + case XML_ATTRIBUTE_NMTOKENS: + xmlBufferWriteChar(buf, " NMTOKENS"); + break; + case XML_ATTRIBUTE_ENUMERATION: + xmlBufferWriteChar(buf, " ("); + xmlDumpEnumeration(buf, attr->tree); + break; + case XML_ATTRIBUTE_NOTATION: + xmlBufferWriteChar(buf, " NOTATION ("); + xmlDumpEnumeration(buf, attr->tree); + break; + default: + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "Internal: ATTRIBUTE struct corrupted invalid type\n", + NULL); + } + switch (attr->def) { + case XML_ATTRIBUTE_NONE: + break; + case XML_ATTRIBUTE_REQUIRED: + xmlBufferWriteChar(buf, " #REQUIRED"); + break; + case XML_ATTRIBUTE_IMPLIED: + xmlBufferWriteChar(buf, " #IMPLIED"); + break; + case XML_ATTRIBUTE_FIXED: + xmlBufferWriteChar(buf, " #FIXED"); + break; + default: + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "Internal: ATTRIBUTE struct corrupted invalid def\n", + NULL); + } + if (attr->defaultValue != NULL) { + xmlBufferWriteChar(buf, " "); + xmlBufferWriteQuotedString(buf, attr->defaultValue); + } + xmlBufferWriteChar(buf, ">\n"); +} + +/** + * xmlDumpAttributeDeclScan: + * @attr: An attribute declaration + * @buf: the XML buffer output + * + * This is used with the hash scan function - just reverses arguments + */ +static void +xmlDumpAttributeDeclScan(void *attr, void *buf, + const xmlChar *name ATTRIBUTE_UNUSED) { + xmlDumpAttributeDecl((xmlBufferPtr) buf, (xmlAttributePtr) attr); +} + +/** + * xmlDumpAttributeTable: + * @buf: the XML buffer output + * @table: An attribute table + * + * This will dump the content of the attribute table as an XML DTD definition + */ +void +xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) { + if ((buf == NULL) || (table == NULL)) + return; + xmlHashScan(table, xmlDumpAttributeDeclScan, buf); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +/************************************************************************ + * * + * NOTATIONs * + * * + ************************************************************************/ +/** + * xmlFreeNotation: + * @not: A notation + * + * Deallocate the memory used by an notation definition + */ +static void +xmlFreeNotation(xmlNotationPtr nota) { + if (nota == NULL) return; + if (nota->name != NULL) + xmlFree((xmlChar *) nota->name); + if (nota->PublicID != NULL) + xmlFree((xmlChar *) nota->PublicID); + if (nota->SystemID != NULL) + xmlFree((xmlChar *) nota->SystemID); + xmlFree(nota); +} + + +/** + * xmlAddNotationDecl: + * @dtd: pointer to the DTD + * @ctxt: the validation context + * @name: the entity name + * @PublicID: the public identifier or NULL + * @SystemID: the system identifier or NULL + * + * Register a new notation declaration + * + * Returns NULL if not, otherwise the entity + */ +xmlNotationPtr +xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, + const xmlChar *name, + const xmlChar *PublicID, const xmlChar *SystemID) { + xmlNotationPtr ret; + xmlNotationTablePtr table; + + if (dtd == NULL) { + return(NULL); + } + if (name == NULL) { + return(NULL); + } + if ((PublicID == NULL) && (SystemID == NULL)) { + return(NULL); + } + + /* + * Create the Notation table if needed. + */ + table = (xmlNotationTablePtr) dtd->notations; + if (table == NULL) { + xmlDictPtr dict = NULL; + if (dtd->doc != NULL) + dict = dtd->doc->dict; + + dtd->notations = table = xmlHashCreateDict(0, dict); + } + if (table == NULL) { + xmlVErrMemory(ctxt, + "xmlAddNotationDecl: Table creation failed!\n"); + return(NULL); + } + + ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation)); + if (ret == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + return(NULL); + } + memset(ret, 0, sizeof(xmlNotation)); + + /* + * fill the structure. + */ + ret->name = xmlStrdup(name); + if (SystemID != NULL) + ret->SystemID = xmlStrdup(SystemID); + if (PublicID != NULL) + ret->PublicID = xmlStrdup(PublicID); + + /* + * Validity Check: + * Check the DTD for previous declarations of the ATTLIST + */ + if (xmlHashAddEntry(table, name, ret)) { +#ifdef LIBXML_VALID_ENABLED + xmlErrValid(NULL, XML_DTD_NOTATION_REDEFINED, + "xmlAddNotationDecl: %s already defined\n", + (const char *) name); +#endif /* LIBXML_VALID_ENABLED */ + xmlFreeNotation(ret); + return(NULL); + } + return(ret); +} + +static void +xmlFreeNotationTableEntry(void *nota, const xmlChar *name ATTRIBUTE_UNUSED) { + xmlFreeNotation((xmlNotationPtr) nota); +} + +/** + * xmlFreeNotationTable: + * @table: An notation table + * + * Deallocate the memory used by an entities hash table. + */ +void +xmlFreeNotationTable(xmlNotationTablePtr table) { + xmlHashFree(table, xmlFreeNotationTableEntry); +} + +#ifdef LIBXML_TREE_ENABLED +/** + * xmlCopyNotation: + * @nota: A notation + * + * Build a copy of a notation. + * + * Returns the new xmlNotationPtr or NULL in case of error. + */ +static void * +xmlCopyNotation(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { + xmlNotationPtr nota = (xmlNotationPtr) payload; + xmlNotationPtr cur; + + cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation)); + if (cur == NULL) { + xmlVErrMemory(NULL, "malloc failed"); + return(NULL); + } + if (nota->name != NULL) + cur->name = xmlStrdup(nota->name); + else + cur->name = NULL; + if (nota->PublicID != NULL) + cur->PublicID = xmlStrdup(nota->PublicID); + else + cur->PublicID = NULL; + if (nota->SystemID != NULL) + cur->SystemID = xmlStrdup(nota->SystemID); + else + cur->SystemID = NULL; + return(cur); +} + +/** + * xmlCopyNotationTable: + * @table: A notation table + * + * Build a copy of a notation table. + * + * Returns the new xmlNotationTablePtr or NULL in case of error. + */ +xmlNotationTablePtr +xmlCopyNotationTable(xmlNotationTablePtr table) { + return((xmlNotationTablePtr) xmlHashCopy(table, xmlCopyNotation)); +} +#endif /* LIBXML_TREE_ENABLED */ + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlDumpNotationDecl: + * @buf: the XML buffer output + * @nota: A notation declaration + * + * This will dump the content the notation declaration as an XML DTD definition + */ +void +xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) { + if ((buf == NULL) || (nota == NULL)) + return; + xmlBufferWriteChar(buf, "name); + if (nota->PublicID != NULL) { + xmlBufferWriteChar(buf, " PUBLIC "); + xmlBufferWriteQuotedString(buf, nota->PublicID); + if (nota->SystemID != NULL) { + xmlBufferWriteChar(buf, " "); + xmlBufferWriteQuotedString(buf, nota->SystemID); + } + } else { + xmlBufferWriteChar(buf, " SYSTEM "); + xmlBufferWriteQuotedString(buf, nota->SystemID); + } + xmlBufferWriteChar(buf, " >\n"); +} + +/** + * xmlDumpNotationDeclScan: + * @nota: A notation declaration + * @buf: the XML buffer output + * + * This is called with the hash scan function, and just reverses args + */ +static void +xmlDumpNotationDeclScan(void *nota, void *buf, + const xmlChar *name ATTRIBUTE_UNUSED) { + xmlDumpNotationDecl((xmlBufferPtr) buf, (xmlNotationPtr) nota); +} + +/** + * xmlDumpNotationTable: + * @buf: the XML buffer output + * @table: A notation table + * + * This will dump the content of the notation table as an XML DTD definition + */ +void +xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) { + if ((buf == NULL) || (table == NULL)) + return; + xmlHashScan(table, xmlDumpNotationDeclScan, buf); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +/************************************************************************ + * * + * IDs * + * * + ************************************************************************/ +/** + * DICT_FREE: + * @str: a string + * + * Free a string if it is not owned by the "dict" dictionary in the + * current scope + */ +#define DICT_FREE(str) \ + if ((str) && ((!dict) || \ + (xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \ + xmlFree((char *)(str)); + +/** + * xmlFreeID: + * @not: A id + * + * Deallocate the memory used by an id definition + */ +static void +xmlFreeID(xmlIDPtr id) { + xmlDictPtr dict = NULL; + + if (id == NULL) return; + + if (id->doc != NULL) + dict = id->doc->dict; + + if (id->value != NULL) + DICT_FREE(id->value) + if (id->name != NULL) + DICT_FREE(id->name) + xmlFree(id); +} + + +/** + * xmlAddID: + * @ctxt: the validation context + * @doc: pointer to the document + * @value: the value name + * @attr: the attribute holding the ID + * + * Register a new id declaration + * + * Returns NULL if not, otherwise the new xmlIDPtr + */ +xmlIDPtr +xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value, + xmlAttrPtr attr) { + xmlIDPtr ret; + xmlIDTablePtr table; + + if (doc == NULL) { + return(NULL); + } + if (value == NULL) { + return(NULL); + } + if (attr == NULL) { + return(NULL); + } + + /* + * Create the ID table if needed. + */ + table = (xmlIDTablePtr) doc->ids; + if (table == NULL) { + doc->ids = table = xmlHashCreateDict(0, doc->dict); + } + if (table == NULL) { + xmlVErrMemory(ctxt, + "xmlAddID: Table creation failed!\n"); + return(NULL); + } + + ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID)); + if (ret == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + return(NULL); + } + + /* + * fill the structure. + */ + ret->value = xmlStrdup(value); + ret->doc = doc; + if ((ctxt != NULL) && (ctxt->vstateNr != 0)) { + /* + * Operating in streaming mode, attr is gonna disappear + */ + if (doc->dict != NULL) + ret->name = xmlDictLookup(doc->dict, attr->name, -1); + else + ret->name = xmlStrdup(attr->name); + ret->attr = NULL; + } else { + ret->attr = attr; + ret->name = NULL; + } + ret->lineno = xmlGetLineNo(attr->parent); + + if (xmlHashAddEntry(table, value, ret) < 0) { +#ifdef LIBXML_VALID_ENABLED + /* + * The id is already defined in this DTD. + */ + if (ctxt != NULL) { + xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED, + "ID %s already defined\n", value, NULL, NULL); + } +#endif /* LIBXML_VALID_ENABLED */ + xmlFreeID(ret); + return(NULL); + } + if (attr != NULL) + attr->atype = XML_ATTRIBUTE_ID; + return(ret); +} + +static void +xmlFreeIDTableEntry(void *id, const xmlChar *name ATTRIBUTE_UNUSED) { + xmlFreeID((xmlIDPtr) id); +} + +/** + * xmlFreeIDTable: + * @table: An id table + * + * Deallocate the memory used by an ID hash table. + */ +void +xmlFreeIDTable(xmlIDTablePtr table) { + xmlHashFree(table, xmlFreeIDTableEntry); +} + +/** + * xmlIsID: + * @doc: the document + * @elem: the element carrying the attribute + * @attr: the attribute + * + * Determine whether an attribute is of type ID. In case we have DTD(s) + * then this is done if DTD loading has been requested. In the case + * of HTML documents parsed with the HTML parser, then ID detection is + * done systematically. + * + * Returns 0 or 1 depending on the lookup result + */ +int +xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) { + if ((attr == NULL) || (attr->name == NULL)) return(0); + if ((attr->ns != NULL) && (attr->ns->prefix != NULL) && + (!strcmp((char *) attr->name, "id")) && + (!strcmp((char *) attr->ns->prefix, "xml"))) + return(1); + if (doc == NULL) return(0); + if ((doc->intSubset == NULL) && (doc->extSubset == NULL) && + (doc->type != XML_HTML_DOCUMENT_NODE)) { + return(0); + } else if (doc->type == XML_HTML_DOCUMENT_NODE) { + if ((xmlStrEqual(BAD_CAST "id", attr->name)) || + ((xmlStrEqual(BAD_CAST "name", attr->name)) && + ((elem == NULL) || (xmlStrEqual(elem->name, BAD_CAST "a"))))) + return(1); + return(0); + } else if (elem == NULL) { + return(0); + } else { + xmlAttributePtr attrDecl = NULL; + + xmlChar felem[50], fattr[50]; + xmlChar *fullelemname, *fullattrname; + + fullelemname = (elem->ns != NULL && elem->ns->prefix != NULL) ? + xmlBuildQName(elem->name, elem->ns->prefix, felem, 50) : + (xmlChar *)elem->name; + + fullattrname = (attr->ns != NULL && attr->ns->prefix != NULL) ? + xmlBuildQName(attr->name, attr->ns->prefix, fattr, 50) : + (xmlChar *)attr->name; + + if (fullelemname != NULL && fullattrname != NULL) { + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullelemname, + fullattrname); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullelemname, + fullattrname); + } + + if ((fullattrname != fattr) && (fullattrname != attr->name)) + xmlFree(fullattrname); + if ((fullelemname != felem) && (fullelemname != elem->name)) + xmlFree(fullelemname); + + if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID)) + return(1); + } + return(0); +} + +/** + * xmlRemoveID: + * @doc: the document + * @attr: the attribute + * + * Remove the given attribute from the ID table maintained internally. + * + * Returns -1 if the lookup failed and 0 otherwise + */ +int +xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) { + xmlIDTablePtr table; + xmlIDPtr id; + xmlChar *ID; + + if (doc == NULL) return(-1); + if (attr == NULL) return(-1); + + table = (xmlIDTablePtr) doc->ids; + if (table == NULL) + return(-1); + + ID = xmlNodeListGetString(doc, attr->children, 1); + if (ID == NULL) + return(-1); + + id = xmlHashLookup(table, ID); + if (id == NULL || id->attr != attr) { + xmlFree(ID); + return(-1); + } + + xmlHashRemoveEntry(table, ID, xmlFreeIDTableEntry); + xmlFree(ID); + attr->atype = 0; + return(0); +} + +/** + * xmlGetID: + * @doc: pointer to the document + * @ID: the ID value + * + * Search the attribute declaring the given ID + * + * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID + */ +xmlAttrPtr +xmlGetID(xmlDocPtr doc, const xmlChar *ID) { + xmlIDTablePtr table; + xmlIDPtr id; + + if (doc == NULL) { + return(NULL); + } + + if (ID == NULL) { + return(NULL); + } + + table = (xmlIDTablePtr) doc->ids; + if (table == NULL) + return(NULL); + + id = xmlHashLookup(table, ID); + if (id == NULL) + return(NULL); + if (id->attr == NULL) { + /* + * We are operating on a stream, return a well known reference + * since the attribute node doesn't exist anymore + */ + return((xmlAttrPtr) doc); + } + return(id->attr); +} + +/************************************************************************ + * * + * Refs * + * * + ************************************************************************/ +typedef struct xmlRemoveMemo_t +{ + xmlListPtr l; + xmlAttrPtr ap; +} xmlRemoveMemo; + +typedef xmlRemoveMemo *xmlRemoveMemoPtr; + +typedef struct xmlValidateMemo_t +{ + xmlValidCtxtPtr ctxt; + const xmlChar *name; +} xmlValidateMemo; + +typedef xmlValidateMemo *xmlValidateMemoPtr; + +/** + * xmlFreeRef: + * @lk: A list link + * + * Deallocate the memory used by a ref definition + */ +static void +xmlFreeRef(xmlLinkPtr lk) { + xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk); + if (ref == NULL) return; + if (ref->value != NULL) + xmlFree((xmlChar *)ref->value); + if (ref->name != NULL) + xmlFree((xmlChar *)ref->name); + xmlFree(ref); +} + +/** + * xmlFreeRefTableEntry: + * @list_ref: A list of references. + * + * Deallocate the memory used by a list of references + */ +static void +xmlFreeRefTableEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { + xmlListPtr list_ref = (xmlListPtr) payload; + if (list_ref == NULL) return; + xmlListDelete(list_ref); +} + +/** + * xmlWalkRemoveRef: + * @data: Contents of current link + * @user: Value supplied by the user + * + * Returns 0 to abort the walk or 1 to continue + */ +static int +xmlWalkRemoveRef(const void *data, void *user) +{ + xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr; + xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap; + xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l; + + if (attr0 == attr1) { /* Matched: remove and terminate walk */ + xmlListRemoveFirst(ref_list, (void *)data); + return 0; + } + return 1; +} + +/** + * xmlDummyCompare + * @data0: Value supplied by the user + * @data1: Value supplied by the user + * + * Do nothing, return 0. Used to create unordered lists. + */ +static int +xmlDummyCompare(const void *data0 ATTRIBUTE_UNUSED, + const void *data1 ATTRIBUTE_UNUSED) +{ + return (0); +} + +/** + * xmlAddRef: + * @ctxt: the validation context + * @doc: pointer to the document + * @value: the value name + * @attr: the attribute holding the Ref + * + * Register a new ref declaration + * + * Returns NULL if not, otherwise the new xmlRefPtr + */ +xmlRefPtr +xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value, + xmlAttrPtr attr) { + xmlRefPtr ret; + xmlRefTablePtr table; + xmlListPtr ref_list; + + if (doc == NULL) { + return(NULL); + } + if (value == NULL) { + return(NULL); + } + if (attr == NULL) { + return(NULL); + } + + /* + * Create the Ref table if needed. + */ + table = (xmlRefTablePtr) doc->refs; + if (table == NULL) { + doc->refs = table = xmlHashCreateDict(0, doc->dict); + } + if (table == NULL) { + xmlVErrMemory(ctxt, + "xmlAddRef: Table creation failed!\n"); + return(NULL); + } + + ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef)); + if (ret == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + return(NULL); + } + + /* + * fill the structure. + */ + ret->value = xmlStrdup(value); + if ((ctxt != NULL) && (ctxt->vstateNr != 0)) { + /* + * Operating in streaming mode, attr is gonna disappear + */ + ret->name = xmlStrdup(attr->name); + ret->attr = NULL; + } else { + ret->name = NULL; + ret->attr = attr; + } + ret->lineno = xmlGetLineNo(attr->parent); + + /* To add a reference :- + * References are maintained as a list of references, + * Lookup the entry, if no entry create new nodelist + * Add the owning node to the NodeList + * Return the ref + */ + + if (NULL == (ref_list = xmlHashLookup(table, value))) { + if (NULL == (ref_list = xmlListCreate(xmlFreeRef, xmlDummyCompare))) { + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "xmlAddRef: Reference list creation failed!\n", + NULL); + goto failed; + } + if (xmlHashAddEntry(table, value, ref_list) < 0) { + xmlListDelete(ref_list); + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "xmlAddRef: Reference list insertion failed!\n", + NULL); + goto failed; + } + } + if (xmlListAppend(ref_list, ret) != 0) { + xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, + "xmlAddRef: Reference list insertion failed!\n", + NULL); + goto failed; + } + return(ret); +failed: + if (ret != NULL) { + if (ret->value != NULL) + xmlFree((char *)ret->value); + if (ret->name != NULL) + xmlFree((char *)ret->name); + xmlFree(ret); + } + return(NULL); +} + +/** + * xmlFreeRefTable: + * @table: An ref table + * + * Deallocate the memory used by an Ref hash table. + */ +void +xmlFreeRefTable(xmlRefTablePtr table) { + xmlHashFree(table, xmlFreeRefTableEntry); +} + +/** + * xmlIsRef: + * @doc: the document + * @elem: the element carrying the attribute + * @attr: the attribute + * + * Determine whether an attribute is of type Ref. In case we have DTD(s) + * then this is simple, otherwise we use an heuristic: name Ref (upper + * or lowercase). + * + * Returns 0 or 1 depending on the lookup result + */ +int +xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) { + if (attr == NULL) + return(0); + if (doc == NULL) { + doc = attr->doc; + if (doc == NULL) return(0); + } + + if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) { + return(0); + } else if (doc->type == XML_HTML_DOCUMENT_NODE) { + /* TODO @@@ */ + return(0); + } else { + xmlAttributePtr attrDecl; + + if (elem == NULL) return(0); + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, + elem->name, attr->name); + + if ((attrDecl != NULL) && + (attrDecl->atype == XML_ATTRIBUTE_IDREF || + attrDecl->atype == XML_ATTRIBUTE_IDREFS)) + return(1); + } + return(0); +} + +/** + * xmlRemoveRef: + * @doc: the document + * @attr: the attribute + * + * Remove the given attribute from the Ref table maintained internally. + * + * Returns -1 if the lookup failed and 0 otherwise + */ +int +xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) { + xmlListPtr ref_list; + xmlRefTablePtr table; + xmlChar *ID; + xmlRemoveMemo target; + + if (doc == NULL) return(-1); + if (attr == NULL) return(-1); + + table = (xmlRefTablePtr) doc->refs; + if (table == NULL) + return(-1); + + ID = xmlNodeListGetString(doc, attr->children, 1); + if (ID == NULL) + return(-1); + + ref_list = xmlHashLookup(table, ID); + if(ref_list == NULL) { + xmlFree(ID); + return (-1); + } + + /* At this point, ref_list refers to a list of references which + * have the same key as the supplied attr. Our list of references + * is ordered by reference address and we don't have that information + * here to use when removing. We'll have to walk the list and + * check for a matching attribute, when we find one stop the walk + * and remove the entry. + * The list is ordered by reference, so that means we don't have the + * key. Passing the list and the reference to the walker means we + * will have enough data to be able to remove the entry. + */ + target.l = ref_list; + target.ap = attr; + + /* Remove the supplied attr from our list */ + xmlListWalk(ref_list, xmlWalkRemoveRef, &target); + + /*If the list is empty then remove the list entry in the hash */ + if (xmlListEmpty(ref_list)) + xmlHashUpdateEntry(table, ID, NULL, xmlFreeRefTableEntry); + xmlFree(ID); + return(0); +} + +/** + * xmlGetRefs: + * @doc: pointer to the document + * @ID: the ID value + * + * Find the set of references for the supplied ID. + * + * Returns NULL if not found, otherwise node set for the ID. + */ +xmlListPtr +xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) { + xmlRefTablePtr table; + + if (doc == NULL) { + return(NULL); + } + + if (ID == NULL) { + return(NULL); + } + + table = (xmlRefTablePtr) doc->refs; + if (table == NULL) + return(NULL); + + return (xmlHashLookup(table, ID)); +} + +/************************************************************************ + * * + * Routines for validity checking * + * * + ************************************************************************/ + +/** + * xmlGetDtdElementDesc: + * @dtd: a pointer to the DtD to search + * @name: the element name + * + * Search the DTD for the description of this element + * + * returns the xmlElementPtr if found or NULL + */ + +xmlElementPtr +xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) { + xmlElementTablePtr table; + xmlElementPtr cur; + xmlChar *uqname = NULL, *prefix = NULL; + + if ((dtd == NULL) || (name == NULL)) return(NULL); + if (dtd->elements == NULL) + return(NULL); + table = (xmlElementTablePtr) dtd->elements; + + uqname = xmlSplitQName2(name, &prefix); + if (uqname != NULL) + name = uqname; + cur = xmlHashLookup2(table, name, prefix); + if (prefix != NULL) xmlFree(prefix); + if (uqname != NULL) xmlFree(uqname); + return(cur); +} +/** + * xmlGetDtdElementDesc2: + * @dtd: a pointer to the DtD to search + * @name: the element name + * @create: create an empty description if not found + * + * Search the DTD for the description of this element + * + * returns the xmlElementPtr if found or NULL + */ + +static xmlElementPtr +xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) { + xmlElementTablePtr table; + xmlElementPtr cur; + xmlChar *uqname = NULL, *prefix = NULL; + + if (dtd == NULL) return(NULL); + if (dtd->elements == NULL) { + xmlDictPtr dict = NULL; + + if (dtd->doc != NULL) + dict = dtd->doc->dict; + + if (!create) + return(NULL); + /* + * Create the Element table if needed. + */ + table = (xmlElementTablePtr) dtd->elements; + if (table == NULL) { + table = xmlHashCreateDict(0, dict); + dtd->elements = (void *) table; + } + if (table == NULL) { + xmlVErrMemory(NULL, "element table allocation failed"); + return(NULL); + } + } + table = (xmlElementTablePtr) dtd->elements; + + uqname = xmlSplitQName2(name, &prefix); + if (uqname != NULL) + name = uqname; + cur = xmlHashLookup2(table, name, prefix); + if ((cur == NULL) && (create)) { + cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement)); + if (cur == NULL) { + xmlVErrMemory(NULL, "malloc failed"); + return(NULL); + } + memset(cur, 0, sizeof(xmlElement)); + cur->type = XML_ELEMENT_DECL; + + /* + * fill the structure. + */ + cur->name = xmlStrdup(name); + cur->prefix = xmlStrdup(prefix); + cur->etype = XML_ELEMENT_TYPE_UNDEFINED; + + xmlHashAddEntry2(table, name, prefix, cur); + } + if (prefix != NULL) xmlFree(prefix); + if (uqname != NULL) xmlFree(uqname); + return(cur); +} + +/** + * xmlGetDtdQElementDesc: + * @dtd: a pointer to the DtD to search + * @name: the element name + * @prefix: the element namespace prefix + * + * Search the DTD for the description of this element + * + * returns the xmlElementPtr if found or NULL + */ + +xmlElementPtr +xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name, + const xmlChar *prefix) { + xmlElementTablePtr table; + + if (dtd == NULL) return(NULL); + if (dtd->elements == NULL) return(NULL); + table = (xmlElementTablePtr) dtd->elements; + + return(xmlHashLookup2(table, name, prefix)); +} + +/** + * xmlGetDtdAttrDesc: + * @dtd: a pointer to the DtD to search + * @elem: the element name + * @name: the attribute name + * + * Search the DTD for the description of this attribute on + * this element. + * + * returns the xmlAttributePtr if found or NULL + */ + +xmlAttributePtr +xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) { + xmlAttributeTablePtr table; + xmlAttributePtr cur; + xmlChar *uqname = NULL, *prefix = NULL; + + if (dtd == NULL) return(NULL); + if (dtd->attributes == NULL) return(NULL); + + table = (xmlAttributeTablePtr) dtd->attributes; + if (table == NULL) + return(NULL); + + uqname = xmlSplitQName2(name, &prefix); + + if (uqname != NULL) { + cur = xmlHashLookup3(table, uqname, prefix, elem); + if (prefix != NULL) xmlFree(prefix); + if (uqname != NULL) xmlFree(uqname); + } else + cur = xmlHashLookup3(table, name, NULL, elem); + return(cur); +} + +/** + * xmlGetDtdQAttrDesc: + * @dtd: a pointer to the DtD to search + * @elem: the element name + * @name: the attribute name + * @prefix: the attribute namespace prefix + * + * Search the DTD for the description of this qualified attribute on + * this element. + * + * returns the xmlAttributePtr if found or NULL + */ + +xmlAttributePtr +xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name, + const xmlChar *prefix) { + xmlAttributeTablePtr table; + + if (dtd == NULL) return(NULL); + if (dtd->attributes == NULL) return(NULL); + table = (xmlAttributeTablePtr) dtd->attributes; + + return(xmlHashLookup3(table, name, prefix, elem)); +} + +/** + * xmlGetDtdNotationDesc: + * @dtd: a pointer to the DtD to search + * @name: the notation name + * + * Search the DTD for the description of this notation + * + * returns the xmlNotationPtr if found or NULL + */ + +xmlNotationPtr +xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) { + xmlNotationTablePtr table; + + if (dtd == NULL) return(NULL); + if (dtd->notations == NULL) return(NULL); + table = (xmlNotationTablePtr) dtd->notations; + + return(xmlHashLookup(table, name)); +} + +#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) +/** + * xmlValidateNotationUse: + * @ctxt: the validation context + * @doc: the document + * @notationName: the notation name to check + * + * Validate that the given name match a notation declaration. + * - [ VC: Notation Declared ] + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc, + const xmlChar *notationName) { + xmlNotationPtr notaDecl; + if ((doc == NULL) || (doc->intSubset == NULL) || + (notationName == NULL)) return(-1); + + notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName); + if ((notaDecl == NULL) && (doc->extSubset != NULL)) + notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName); + + if ((notaDecl == NULL) && (ctxt != NULL)) { + xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION, + "NOTATION %s is not declared\n", + notationName, NULL, NULL); + return(0); + } + return(1); +} +#endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */ + +/** + * xmlIsMixedElement: + * @doc: the document + * @name: the element name + * + * Search in the DtDs whether an element accept Mixed content (or ANY) + * basically if it is supposed to accept text childs + * + * returns 0 if no, 1 if yes, and -1 if no element description is available + */ + +int +xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) { + xmlElementPtr elemDecl; + + if ((doc == NULL) || (doc->intSubset == NULL)) return(-1); + + elemDecl = xmlGetDtdElementDesc(doc->intSubset, name); + if ((elemDecl == NULL) && (doc->extSubset != NULL)) + elemDecl = xmlGetDtdElementDesc(doc->extSubset, name); + if (elemDecl == NULL) return(-1); + switch (elemDecl->etype) { + case XML_ELEMENT_TYPE_UNDEFINED: + return(-1); + case XML_ELEMENT_TYPE_ELEMENT: + return(0); + case XML_ELEMENT_TYPE_EMPTY: + /* + * return 1 for EMPTY since we want VC error to pop up + * on for example + */ + case XML_ELEMENT_TYPE_ANY: + case XML_ELEMENT_TYPE_MIXED: + return(1); + } + return(1); +} + +#ifdef LIBXML_VALID_ENABLED + +static int +xmlIsDocNameStartChar(xmlDocPtr doc, int c) { + if ((doc == NULL) || (doc->properties & XML_DOC_OLD10) == 0) { + /* + * Use the new checks of production [4] [4a] amd [5] of the + * Update 5 of XML-1.0 + */ + if (((c >= 'a') && (c <= 'z')) || + ((c >= 'A') && (c <= 'Z')) || + (c == '_') || (c == ':') || + ((c >= 0xC0) && (c <= 0xD6)) || + ((c >= 0xD8) && (c <= 0xF6)) || + ((c >= 0xF8) && (c <= 0x2FF)) || + ((c >= 0x370) && (c <= 0x37D)) || + ((c >= 0x37F) && (c <= 0x1FFF)) || + ((c >= 0x200C) && (c <= 0x200D)) || + ((c >= 0x2070) && (c <= 0x218F)) || + ((c >= 0x2C00) && (c <= 0x2FEF)) || + ((c >= 0x3001) && (c <= 0xD7FF)) || + ((c >= 0xF900) && (c <= 0xFDCF)) || + ((c >= 0xFDF0) && (c <= 0xFFFD)) || + ((c >= 0x10000) && (c <= 0xEFFFF))) + return(1); + } else { + if (IS_LETTER(c) || (c == '_') || (c == ':')) + return(1); + } + return(0); +} + +static int +xmlIsDocNameChar(xmlDocPtr doc, int c) { + if ((doc == NULL) || (doc->properties & XML_DOC_OLD10) == 0) { + /* + * Use the new checks of production [4] [4a] amd [5] of the + * Update 5 of XML-1.0 + */ + if (((c >= 'a') && (c <= 'z')) || + ((c >= 'A') && (c <= 'Z')) || + ((c >= '0') && (c <= '9')) || /* !start */ + (c == '_') || (c == ':') || + (c == '-') || (c == '.') || (c == 0xB7) || /* !start */ + ((c >= 0xC0) && (c <= 0xD6)) || + ((c >= 0xD8) && (c <= 0xF6)) || + ((c >= 0xF8) && (c <= 0x2FF)) || + ((c >= 0x300) && (c <= 0x36F)) || /* !start */ + ((c >= 0x370) && (c <= 0x37D)) || + ((c >= 0x37F) && (c <= 0x1FFF)) || + ((c >= 0x200C) && (c <= 0x200D)) || + ((c >= 0x203F) && (c <= 0x2040)) || /* !start */ + ((c >= 0x2070) && (c <= 0x218F)) || + ((c >= 0x2C00) && (c <= 0x2FEF)) || + ((c >= 0x3001) && (c <= 0xD7FF)) || + ((c >= 0xF900) && (c <= 0xFDCF)) || + ((c >= 0xFDF0) && (c <= 0xFFFD)) || + ((c >= 0x10000) && (c <= 0xEFFFF))) + return(1); + } else { + if ((IS_LETTER(c)) || (IS_DIGIT(c)) || + (c == '.') || (c == '-') || + (c == '_') || (c == ':') || + (IS_COMBINING(c)) || + (IS_EXTENDER(c))) + return(1); + } + return(0); +} + +/** + * xmlValidateNameValue: + * @doc: pointer to the document or NULL + * @value: an Name value + * + * Validate that the given value match Name production + * + * returns 1 if valid or 0 otherwise + */ + +static int +xmlValidateNameValueInternal(xmlDocPtr doc, const xmlChar *value) { + const xmlChar *cur; + int val, len; + + if (value == NULL) return(0); + cur = value; + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + if (!xmlIsDocNameStartChar(doc, val)) + return(0); + + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + while (xmlIsDocNameChar(doc, val)) { + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + } + + if (val != 0) return(0); + + return(1); +} + +/** + * xmlValidateNameValue: + * @value: an Name value + * + * Validate that the given value match Name production + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateNameValue(const xmlChar *value) { + return(xmlValidateNameValueInternal(NULL, value)); +} + +/** + * xmlValidateNamesValueInternal: + * @doc: pointer to the document or NULL + * @value: an Names value + * + * Validate that the given value match Names production + * + * returns 1 if valid or 0 otherwise + */ + +static int +xmlValidateNamesValueInternal(xmlDocPtr doc, const xmlChar *value) { + const xmlChar *cur; + int val, len; + + if (value == NULL) return(0); + cur = value; + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + + if (!xmlIsDocNameStartChar(doc, val)) + return(0); + + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + while (xmlIsDocNameChar(doc, val)) { + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + } + + /* Should not test IS_BLANK(val) here -- see erratum E20*/ + while (val == 0x20) { + while (val == 0x20) { + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + } + + if (!xmlIsDocNameStartChar(doc, val)) + return(0); + + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + + while (xmlIsDocNameChar(doc, val)) { + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + } + } + + if (val != 0) return(0); + + return(1); +} + +/** + * xmlValidateNamesValue: + * @value: an Names value + * + * Validate that the given value match Names production + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateNamesValue(const xmlChar *value) { + return(xmlValidateNamesValueInternal(NULL, value)); +} + +/** + * xmlValidateNmtokenValueInternal: + * @doc: pointer to the document or NULL + * @value: an Nmtoken value + * + * Validate that the given value match Nmtoken production + * + * [ VC: Name Token ] + * + * returns 1 if valid or 0 otherwise + */ + +static int +xmlValidateNmtokenValueInternal(xmlDocPtr doc, const xmlChar *value) { + const xmlChar *cur; + int val, len; + + if (value == NULL) return(0); + cur = value; + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + + if (!xmlIsDocNameChar(doc, val)) + return(0); + + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + while (xmlIsDocNameChar(doc, val)) { + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + } + + if (val != 0) return(0); + + return(1); +} + +/** + * xmlValidateNmtokenValue: + * @value: an Nmtoken value + * + * Validate that the given value match Nmtoken production + * + * [ VC: Name Token ] + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateNmtokenValue(const xmlChar *value) { + return(xmlValidateNmtokenValueInternal(NULL, value)); +} + +/** + * xmlValidateNmtokensValueInternal: + * @doc: pointer to the document or NULL + * @value: an Nmtokens value + * + * Validate that the given value match Nmtokens production + * + * [ VC: Name Token ] + * + * returns 1 if valid or 0 otherwise + */ + +static int +xmlValidateNmtokensValueInternal(xmlDocPtr doc, const xmlChar *value) { + const xmlChar *cur; + int val, len; + + if (value == NULL) return(0); + cur = value; + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + + while (IS_BLANK(val)) { + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + } + + if (!xmlIsDocNameChar(doc, val)) + return(0); + + while (xmlIsDocNameChar(doc, val)) { + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + } + + /* Should not test IS_BLANK(val) here -- see erratum E20*/ + while (val == 0x20) { + while (val == 0x20) { + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + } + if (val == 0) return(1); + + if (!xmlIsDocNameChar(doc, val)) + return(0); + + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + + while (xmlIsDocNameChar(doc, val)) { + val = xmlStringCurrentChar(NULL, cur, &len); + cur += len; + } + } + + if (val != 0) return(0); + + return(1); +} + +/** + * xmlValidateNmtokensValue: + * @value: an Nmtokens value + * + * Validate that the given value match Nmtokens production + * + * [ VC: Name Token ] + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateNmtokensValue(const xmlChar *value) { + return(xmlValidateNmtokensValueInternal(NULL, value)); +} + +/** + * xmlValidateNotationDecl: + * @ctxt: the validation context + * @doc: a document instance + * @nota: a notation definition + * + * Try to validate a single notation definition + * basically it does the following checks as described by the + * XML-1.0 recommendation: + * - it seems that no validity constraint exists on notation declarations + * But this function get called anyway ... + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED, + xmlNotationPtr nota ATTRIBUTE_UNUSED) { + int ret = 1; + + return(ret); +} + +/** + * xmlValidateAttributeValueInternal: + * @doc: the document + * @type: an attribute type + * @value: an attribute value + * + * Validate that the given attribute value match the proper production + * + * returns 1 if valid or 0 otherwise + */ + +static int +xmlValidateAttributeValueInternal(xmlDocPtr doc, xmlAttributeType type, + const xmlChar *value) { + switch (type) { + case XML_ATTRIBUTE_ENTITIES: + case XML_ATTRIBUTE_IDREFS: + return(xmlValidateNamesValueInternal(doc, value)); + case XML_ATTRIBUTE_ENTITY: + case XML_ATTRIBUTE_IDREF: + case XML_ATTRIBUTE_ID: + case XML_ATTRIBUTE_NOTATION: + return(xmlValidateNameValueInternal(doc, value)); + case XML_ATTRIBUTE_NMTOKENS: + case XML_ATTRIBUTE_ENUMERATION: + return(xmlValidateNmtokensValueInternal(doc, value)); + case XML_ATTRIBUTE_NMTOKEN: + return(xmlValidateNmtokenValueInternal(doc, value)); + case XML_ATTRIBUTE_CDATA: + break; + } + return(1); +} + +/** + * xmlValidateAttributeValue: + * @type: an attribute type + * @value: an attribute value + * + * Validate that the given attribute value match the proper production + * + * [ VC: ID ] + * Values of type ID must match the Name production.... + * + * [ VC: IDREF ] + * Values of type IDREF must match the Name production, and values + * of type IDREFS must match Names ... + * + * [ VC: Entity Name ] + * Values of type ENTITY must match the Name production, values + * of type ENTITIES must match Names ... + * + * [ VC: Name Token ] + * Values of type NMTOKEN must match the Nmtoken production; values + * of type NMTOKENS must match Nmtokens. + * + * returns 1 if valid or 0 otherwise + */ +int +xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) { + return(xmlValidateAttributeValueInternal(NULL, type, value)); +} + +/** + * xmlValidateAttributeValue2: + * @ctxt: the validation context + * @doc: the document + * @name: the attribute name (used for error reporting only) + * @type: the attribute type + * @value: the attribute value + * + * Validate that the given attribute value match a given type. + * This typically cannot be done before having finished parsing + * the subsets. + * + * [ VC: IDREF ] + * Values of type IDREF must match one of the declared IDs + * Values of type IDREFS must match a sequence of the declared IDs + * each Name must match the value of an ID attribute on some element + * in the XML document; i.e. IDREF values must match the value of + * some ID attribute + * + * [ VC: Entity Name ] + * Values of type ENTITY must match one declared entity + * Values of type ENTITIES must match a sequence of declared entities + * + * [ VC: Notation Attributes ] + * all notation names in the declaration must be declared. + * + * returns 1 if valid or 0 otherwise + */ + +static int +xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc, + const xmlChar *name, xmlAttributeType type, const xmlChar *value) { + int ret = 1; + switch (type) { + case XML_ATTRIBUTE_IDREFS: + case XML_ATTRIBUTE_IDREF: + case XML_ATTRIBUTE_ID: + case XML_ATTRIBUTE_NMTOKENS: + case XML_ATTRIBUTE_ENUMERATION: + case XML_ATTRIBUTE_NMTOKEN: + case XML_ATTRIBUTE_CDATA: + break; + case XML_ATTRIBUTE_ENTITY: { + xmlEntityPtr ent; + + ent = xmlGetDocEntity(doc, value); + /* yeah it's a bit messy... */ + if ((ent == NULL) && (doc->standalone == 1)) { + doc->standalone = 0; + ent = xmlGetDocEntity(doc, value); + } + if (ent == NULL) { + xmlErrValidNode(ctxt, (xmlNodePtr) doc, + XML_DTD_UNKNOWN_ENTITY, + "ENTITY attribute %s reference an unknown entity \"%s\"\n", + name, value, NULL); + ret = 0; + } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) { + xmlErrValidNode(ctxt, (xmlNodePtr) doc, + XML_DTD_ENTITY_TYPE, + "ENTITY attribute %s reference an entity \"%s\" of wrong type\n", + name, value, NULL); + ret = 0; + } + break; + } + case XML_ATTRIBUTE_ENTITIES: { + xmlChar *dup, *nam = NULL, *cur, save; + xmlEntityPtr ent; + + dup = xmlStrdup(value); + if (dup == NULL) + return(0); + cur = dup; + while (*cur != 0) { + nam = cur; + while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++; + save = *cur; + *cur = 0; + ent = xmlGetDocEntity(doc, nam); + if (ent == NULL) { + xmlErrValidNode(ctxt, (xmlNodePtr) doc, + XML_DTD_UNKNOWN_ENTITY, + "ENTITIES attribute %s reference an unknown entity \"%s\"\n", + name, nam, NULL); + ret = 0; + } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) { + xmlErrValidNode(ctxt, (xmlNodePtr) doc, + XML_DTD_ENTITY_TYPE, + "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n", + name, nam, NULL); + ret = 0; + } + if (save == 0) + break; + *cur = save; + while (IS_BLANK_CH(*cur)) cur++; + } + xmlFree(dup); + break; + } + case XML_ATTRIBUTE_NOTATION: { + xmlNotationPtr nota; + + nota = xmlGetDtdNotationDesc(doc->intSubset, value); + if ((nota == NULL) && (doc->extSubset != NULL)) + nota = xmlGetDtdNotationDesc(doc->extSubset, value); + + if (nota == NULL) { + xmlErrValidNode(ctxt, (xmlNodePtr) doc, + XML_DTD_UNKNOWN_NOTATION, + "NOTATION attribute %s reference an unknown notation \"%s\"\n", + name, value, NULL); + ret = 0; + } + break; + } + } + return(ret); +} + +/** + * xmlValidCtxtNormalizeAttributeValue: + * @ctxt: the validation context + * @doc: the document + * @elem: the parent + * @name: the attribute name + * @value: the attribute value + * @ctxt: the validation context or NULL + * + * Does the validation related extra step of the normalization of attribute + * values: + * + * If the declared value is not CDATA, then the XML processor must further + * process the normalized attribute value by discarding any leading and + * trailing space (#x20) characters, and by replacing sequences of space + * (#x20) characters by single space (#x20) character. + * + * Also check VC: Standalone Document Declaration in P32, and update + * ctxt->valid accordingly + * + * returns a new normalized string if normalization is needed, NULL otherwise + * the caller must free the returned value. + */ + +xmlChar * +xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc, + xmlNodePtr elem, const xmlChar *name, const xmlChar *value) { + xmlChar *ret, *dst; + const xmlChar *src; + xmlAttributePtr attrDecl = NULL; + int extsubset = 0; + + if (doc == NULL) return(NULL); + if (elem == NULL) return(NULL); + if (name == NULL) return(NULL); + if (value == NULL) return(NULL); + + if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { + xmlChar fn[50]; + xmlChar *fullname; + + fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); + if (fullname == NULL) + return(NULL); + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) { + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name); + if (attrDecl != NULL) + extsubset = 1; + } + if ((fullname != fn) && (fullname != elem->name)) + xmlFree(fullname); + } + if ((attrDecl == NULL) && (doc->intSubset != NULL)) + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) { + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name); + if (attrDecl != NULL) + extsubset = 1; + } + + if (attrDecl == NULL) + return(NULL); + if (attrDecl->atype == XML_ATTRIBUTE_CDATA) + return(NULL); + + ret = xmlStrdup(value); + if (ret == NULL) + return(NULL); + src = value; + dst = ret; + while (*src == 0x20) src++; + while (*src != 0) { + if (*src == 0x20) { + while (*src == 0x20) src++; + if (*src != 0) + *dst++ = 0x20; + } else { + *dst++ = *src++; + } + } + *dst = 0; + if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) { + xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE, +"standalone: %s on %s value had to be normalized based on external subset declaration\n", + name, elem->name, NULL); + ctxt->valid = 0; + } + return(ret); +} + +/** + * xmlValidNormalizeAttributeValue: + * @doc: the document + * @elem: the parent + * @name: the attribute name + * @value: the attribute value + * + * Does the validation related extra step of the normalization of attribute + * values: + * + * If the declared value is not CDATA, then the XML processor must further + * process the normalized attribute value by discarding any leading and + * trailing space (#x20) characters, and by replacing sequences of space + * (#x20) characters by single space (#x20) character. + * + * Returns a new normalized string if normalization is needed, NULL otherwise + * the caller must free the returned value. + */ + +xmlChar * +xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem, + const xmlChar *name, const xmlChar *value) { + xmlChar *ret, *dst; + const xmlChar *src; + xmlAttributePtr attrDecl = NULL; + + if (doc == NULL) return(NULL); + if (elem == NULL) return(NULL); + if (name == NULL) return(NULL); + if (value == NULL) return(NULL); + + if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { + xmlChar fn[50]; + xmlChar *fullname; + + fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); + if (fullname == NULL) + return(NULL); + if ((fullname != fn) && (fullname != elem->name)) + xmlFree(fullname); + } + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name); + + if (attrDecl == NULL) + return(NULL); + if (attrDecl->atype == XML_ATTRIBUTE_CDATA) + return(NULL); + + ret = xmlStrdup(value); + if (ret == NULL) + return(NULL); + src = value; + dst = ret; + while (*src == 0x20) src++; + while (*src != 0) { + if (*src == 0x20) { + while (*src == 0x20) src++; + if (*src != 0) + *dst++ = 0x20; + } else { + *dst++ = *src++; + } + } + *dst = 0; + return(ret); +} + +static void +xmlValidateAttributeIdCallback(void *payload, void *data, + const xmlChar *name ATTRIBUTE_UNUSED) { + xmlAttributePtr attr = (xmlAttributePtr) payload; + int *count = (int *) data; + if (attr->atype == XML_ATTRIBUTE_ID) (*count)++; +} + +/** + * xmlValidateAttributeDecl: + * @ctxt: the validation context + * @doc: a document instance + * @attr: an attribute definition + * + * Try to validate a single attribute definition + * basically it does the following checks as described by the + * XML-1.0 recommendation: + * - [ VC: Attribute Default Legal ] + * - [ VC: Enumeration ] + * - [ VC: ID Attribute Default ] + * + * The ID/IDREF uniqueness and matching are done separately + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, + xmlAttributePtr attr) { + int ret = 1; + int val; + CHECK_DTD; + if(attr == NULL) return(1); + + /* Attribute Default Legal */ + /* Enumeration */ + if (attr->defaultValue != NULL) { + val = xmlValidateAttributeValueInternal(doc, attr->atype, + attr->defaultValue); + if (val == 0) { + xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_DEFAULT, + "Syntax of default value for attribute %s of %s is not valid\n", + attr->name, attr->elem, NULL); + } + ret &= val; + } + + /* ID Attribute Default */ + if ((attr->atype == XML_ATTRIBUTE_ID)&& + (attr->def != XML_ATTRIBUTE_IMPLIED) && + (attr->def != XML_ATTRIBUTE_REQUIRED)) { + xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_FIXED, + "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n", + attr->name, attr->elem, NULL); + ret = 0; + } + + /* One ID per Element Type */ + if (attr->atype == XML_ATTRIBUTE_ID) { + int nbId; + + /* the trick is that we parse DtD as their own internal subset */ + xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset, + attr->elem); + if (elem != NULL) { + nbId = xmlScanIDAttributeDecl(NULL, elem, 0); + } else { + xmlAttributeTablePtr table; + + /* + * The attribute may be declared in the internal subset and the + * element in the external subset. + */ + nbId = 0; + if (doc->intSubset != NULL) { + table = (xmlAttributeTablePtr) doc->intSubset->attributes; + xmlHashScan3(table, NULL, NULL, attr->elem, + xmlValidateAttributeIdCallback, &nbId); + } + } + if (nbId > 1) { + + xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, + "Element %s has %d ID attribute defined in the internal subset : %s\n", + attr->elem, nbId, attr->name); + } else if (doc->extSubset != NULL) { + int extId = 0; + elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem); + if (elem != NULL) { + extId = xmlScanIDAttributeDecl(NULL, elem, 0); + } + if (extId > 1) { + xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, + "Element %s has %d ID attribute defined in the external subset : %s\n", + attr->elem, extId, attr->name); + } else if (extId + nbId > 1) { + xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, +"Element %s has ID attributes defined in the internal and external subset : %s\n", + attr->elem, attr->name, NULL); + } + } + } + + /* Validity Constraint: Enumeration */ + if ((attr->defaultValue != NULL) && (attr->tree != NULL)) { + xmlEnumerationPtr tree = attr->tree; + while (tree != NULL) { + if (xmlStrEqual(tree->name, attr->defaultValue)) break; + tree = tree->next; + } + if (tree == NULL) { + xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_VALUE, +"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n", + attr->defaultValue, attr->name, attr->elem); + ret = 0; + } + } + + return(ret); +} + +/** + * xmlValidateElementDecl: + * @ctxt: the validation context + * @doc: a document instance + * @elem: an element definition + * + * Try to validate a single element definition + * basically it does the following checks as described by the + * XML-1.0 recommendation: + * - [ VC: One ID per Element Type ] + * - [ VC: No Duplicate Types ] + * - [ VC: Unique Element Type Declaration ] + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, + xmlElementPtr elem) { + int ret = 1; + xmlElementPtr tst; + + CHECK_DTD; + + if (elem == NULL) return(1); + +#if 0 +#ifdef LIBXML_REGEXP_ENABLED + /* Build the regexp associated to the content model */ + ret = xmlValidBuildContentModel(ctxt, elem); +#endif +#endif + + /* No Duplicate Types */ + if (elem->etype == XML_ELEMENT_TYPE_MIXED) { + xmlElementContentPtr cur, next; + const xmlChar *name; + + cur = elem->content; + while (cur != NULL) { + if (cur->type != XML_ELEMENT_CONTENT_OR) break; + if (cur->c1 == NULL) break; + if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) { + name = cur->c1->name; + next = cur->c2; + while (next != NULL) { + if (next->type == XML_ELEMENT_CONTENT_ELEMENT) { + if ((xmlStrEqual(next->name, name)) && + (xmlStrEqual(next->prefix, cur->c1->prefix))) { + if (cur->c1->prefix == NULL) { + xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, + "Definition of %s has duplicate references of %s\n", + elem->name, name, NULL); + } else { + xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, + "Definition of %s has duplicate references of %s:%s\n", + elem->name, cur->c1->prefix, name); + } + ret = 0; + } + break; + } + if (next->c1 == NULL) break; + if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break; + if ((xmlStrEqual(next->c1->name, name)) && + (xmlStrEqual(next->c1->prefix, cur->c1->prefix))) { + if (cur->c1->prefix == NULL) { + xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, + "Definition of %s has duplicate references to %s\n", + elem->name, name, NULL); + } else { + xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, + "Definition of %s has duplicate references to %s:%s\n", + elem->name, cur->c1->prefix, name); + } + ret = 0; + } + next = next->c2; + } + } + cur = cur->c2; + } + } + + /* VC: Unique Element Type Declaration */ + tst = xmlGetDtdElementDesc(doc->intSubset, elem->name); + if ((tst != NULL ) && (tst != elem) && + ((tst->prefix == elem->prefix) || + (xmlStrEqual(tst->prefix, elem->prefix))) && + (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) { + xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED, + "Redefinition of element %s\n", + elem->name, NULL, NULL); + ret = 0; + } + tst = xmlGetDtdElementDesc(doc->extSubset, elem->name); + if ((tst != NULL ) && (tst != elem) && + ((tst->prefix == elem->prefix) || + (xmlStrEqual(tst->prefix, elem->prefix))) && + (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) { + xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED, + "Redefinition of element %s\n", + elem->name, NULL, NULL); + ret = 0; + } + /* One ID per Element Type + * already done when registering the attribute + if (xmlScanIDAttributeDecl(ctxt, elem) > 1) { + ret = 0; + } */ + return(ret); +} + +/** + * xmlValidateOneAttribute: + * @ctxt: the validation context + * @doc: a document instance + * @elem: an element instance + * @attr: an attribute instance + * @value: the attribute value (without entities processing) + * + * Try to validate a single attribute for an element + * basically it does the following checks as described by the + * XML-1.0 recommendation: + * - [ VC: Attribute Value Type ] + * - [ VC: Fixed Attribute Default ] + * - [ VC: Entity Name ] + * - [ VC: Name Token ] + * - [ VC: ID ] + * - [ VC: IDREF ] + * - [ VC: Entity Name ] + * - [ VC: Notation Attributes ] + * + * The ID/IDREF uniqueness and matching are done separately + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc, + xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) +{ + xmlAttributePtr attrDecl = NULL; + int val; + int ret = 1; + + CHECK_DTD; + if ((elem == NULL) || (elem->name == NULL)) return(0); + if ((attr == NULL) || (attr->name == NULL)) return(0); + + if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { + xmlChar fn[50]; + xmlChar *fullname; + + fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); + if (fullname == NULL) + return(0); + if (attr->ns != NULL) { + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname, + attr->name, attr->ns->prefix); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname, + attr->name, attr->ns->prefix); + } else { + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, + fullname, attr->name); + } + if ((fullname != fn) && (fullname != elem->name)) + xmlFree(fullname); + } + if (attrDecl == NULL) { + if (attr->ns != NULL) { + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, + attr->name, attr->ns->prefix); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, + attr->name, attr->ns->prefix); + } else { + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, + elem->name, attr->name); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, + elem->name, attr->name); + } + } + + + /* Validity Constraint: Attribute Value Type */ + if (attrDecl == NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE, + "No declaration for attribute %s of element %s\n", + attr->name, elem->name, NULL); + return(0); + } + attr->atype = attrDecl->atype; + + val = xmlValidateAttributeValueInternal(doc, attrDecl->atype, value); + if (val == 0) { + xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, + "Syntax of value for attribute %s of %s is not valid\n", + attr->name, elem->name, NULL); + ret = 0; + } + + /* Validity constraint: Fixed Attribute Default */ + if (attrDecl->def == XML_ATTRIBUTE_FIXED) { + if (!xmlStrEqual(value, attrDecl->defaultValue)) { + xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT, + "Value for attribute %s of %s is different from default \"%s\"\n", + attr->name, elem->name, attrDecl->defaultValue); + ret = 0; + } + } + + /* Validity Constraint: ID uniqueness */ + if (attrDecl->atype == XML_ATTRIBUTE_ID) { + if (xmlAddID(ctxt, doc, value, attr) == NULL) + ret = 0; + } + + if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) || + (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) { + if (xmlAddRef(ctxt, doc, value, attr) == NULL) + ret = 0; + } + + /* Validity Constraint: Notation Attributes */ + if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) { + xmlEnumerationPtr tree = attrDecl->tree; + xmlNotationPtr nota; + + /* First check that the given NOTATION was declared */ + nota = xmlGetDtdNotationDesc(doc->intSubset, value); + if (nota == NULL) + nota = xmlGetDtdNotationDesc(doc->extSubset, value); + + if (nota == NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION, + "Value \"%s\" for attribute %s of %s is not a declared Notation\n", + value, attr->name, elem->name); + ret = 0; + } + + /* Second, verify that it's among the list */ + while (tree != NULL) { + if (xmlStrEqual(tree->name, value)) break; + tree = tree->next; + } + if (tree == NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE, +"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n", + value, attr->name, elem->name); + ret = 0; + } + } + + /* Validity Constraint: Enumeration */ + if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) { + xmlEnumerationPtr tree = attrDecl->tree; + while (tree != NULL) { + if (xmlStrEqual(tree->name, value)) break; + tree = tree->next; + } + if (tree == NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, + "Value \"%s\" for attribute %s of %s is not among the enumerated set\n", + value, attr->name, elem->name); + ret = 0; + } + } + + /* Fixed Attribute Default */ + if ((attrDecl->def == XML_ATTRIBUTE_FIXED) && + (!xmlStrEqual(attrDecl->defaultValue, value))) { + xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, + "Value for attribute %s of %s must be \"%s\"\n", + attr->name, elem->name, attrDecl->defaultValue); + ret = 0; + } + + /* Extra check for the attribute value */ + ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name, + attrDecl->atype, value); + + return(ret); +} + +/** + * xmlValidateOneNamespace: + * @ctxt: the validation context + * @doc: a document instance + * @elem: an element instance + * @prefix: the namespace prefix + * @ns: an namespace declaration instance + * @value: the attribute value (without entities processing) + * + * Try to validate a single namespace declaration for an element + * basically it does the following checks as described by the + * XML-1.0 recommendation: + * - [ VC: Attribute Value Type ] + * - [ VC: Fixed Attribute Default ] + * - [ VC: Entity Name ] + * - [ VC: Name Token ] + * - [ VC: ID ] + * - [ VC: IDREF ] + * - [ VC: Entity Name ] + * - [ VC: Notation Attributes ] + * + * The ID/IDREF uniqueness and matching are done separately + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc, +xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) { + /* xmlElementPtr elemDecl; */ + xmlAttributePtr attrDecl = NULL; + int val; + int ret = 1; + + CHECK_DTD; + if ((elem == NULL) || (elem->name == NULL)) return(0); + if ((ns == NULL) || (ns->href == NULL)) return(0); + + if (prefix != NULL) { + xmlChar fn[50]; + xmlChar *fullname; + + fullname = xmlBuildQName(elem->name, prefix, fn, 50); + if (fullname == NULL) { + xmlVErrMemory(ctxt, "Validating namespace"); + return(0); + } + if (ns->prefix != NULL) { + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname, + ns->prefix, BAD_CAST "xmlns"); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname, + ns->prefix, BAD_CAST "xmlns"); + } else { + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, + BAD_CAST "xmlns"); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, + BAD_CAST "xmlns"); + } + if ((fullname != fn) && (fullname != elem->name)) + xmlFree(fullname); + } + if (attrDecl == NULL) { + if (ns->prefix != NULL) { + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, + ns->prefix, BAD_CAST "xmlns"); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, + ns->prefix, BAD_CAST "xmlns"); + } else { + attrDecl = xmlGetDtdAttrDesc(doc->intSubset, + elem->name, BAD_CAST "xmlns"); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdAttrDesc(doc->extSubset, + elem->name, BAD_CAST "xmlns"); + } + } + + + /* Validity Constraint: Attribute Value Type */ + if (attrDecl == NULL) { + if (ns->prefix != NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE, + "No declaration for attribute xmlns:%s of element %s\n", + ns->prefix, elem->name, NULL); + } else { + xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE, + "No declaration for attribute xmlns of element %s\n", + elem->name, NULL, NULL); + } + return(0); + } + + val = xmlValidateAttributeValueInternal(doc, attrDecl->atype, value); + if (val == 0) { + if (ns->prefix != NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT, + "Syntax of value for attribute xmlns:%s of %s is not valid\n", + ns->prefix, elem->name, NULL); + } else { + xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT, + "Syntax of value for attribute xmlns of %s is not valid\n", + elem->name, NULL, NULL); + } + ret = 0; + } + + /* Validity constraint: Fixed Attribute Default */ + if (attrDecl->def == XML_ATTRIBUTE_FIXED) { + if (!xmlStrEqual(value, attrDecl->defaultValue)) { + if (ns->prefix != NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT, + "Value for attribute xmlns:%s of %s is different from default \"%s\"\n", + ns->prefix, elem->name, attrDecl->defaultValue); + } else { + xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT, + "Value for attribute xmlns of %s is different from default \"%s\"\n", + elem->name, attrDecl->defaultValue, NULL); + } + ret = 0; + } + } + + /* + * Casting ns to xmlAttrPtr is wrong. We'd need separate functions + * xmlAddID and xmlAddRef for namespace declarations, but it makes + * no practical sense to use ID types anyway. + */ +#if 0 + /* Validity Constraint: ID uniqueness */ + if (attrDecl->atype == XML_ATTRIBUTE_ID) { + if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL) + ret = 0; + } + + if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) || + (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) { + if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL) + ret = 0; + } +#endif + + /* Validity Constraint: Notation Attributes */ + if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) { + xmlEnumerationPtr tree = attrDecl->tree; + xmlNotationPtr nota; + + /* First check that the given NOTATION was declared */ + nota = xmlGetDtdNotationDesc(doc->intSubset, value); + if (nota == NULL) + nota = xmlGetDtdNotationDesc(doc->extSubset, value); + + if (nota == NULL) { + if (ns->prefix != NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION, + "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n", + value, ns->prefix, elem->name); + } else { + xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION, + "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n", + value, elem->name, NULL); + } + ret = 0; + } + + /* Second, verify that it's among the list */ + while (tree != NULL) { + if (xmlStrEqual(tree->name, value)) break; + tree = tree->next; + } + if (tree == NULL) { + if (ns->prefix != NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE, +"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n", + value, ns->prefix, elem->name); + } else { + xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE, +"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n", + value, elem->name, NULL); + } + ret = 0; + } + } + + /* Validity Constraint: Enumeration */ + if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) { + xmlEnumerationPtr tree = attrDecl->tree; + while (tree != NULL) { + if (xmlStrEqual(tree->name, value)) break; + tree = tree->next; + } + if (tree == NULL) { + if (ns->prefix != NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, +"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n", + value, ns->prefix, elem->name); + } else { + xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, +"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n", + value, elem->name, NULL); + } + ret = 0; + } + } + + /* Fixed Attribute Default */ + if ((attrDecl->def == XML_ATTRIBUTE_FIXED) && + (!xmlStrEqual(attrDecl->defaultValue, value))) { + if (ns->prefix != NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE, + "Value for attribute xmlns:%s of %s must be \"%s\"\n", + ns->prefix, elem->name, attrDecl->defaultValue); + } else { + xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE, + "Value for attribute xmlns of %s must be \"%s\"\n", + elem->name, attrDecl->defaultValue, NULL); + } + ret = 0; + } + + /* Extra check for the attribute value */ + if (ns->prefix != NULL) { + ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix, + attrDecl->atype, value); + } else { + ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns", + attrDecl->atype, value); + } + + return(ret); +} + +#ifndef LIBXML_REGEXP_ENABLED +/** + * xmlValidateSkipIgnorable: + * @ctxt: the validation context + * @child: the child list + * + * Skip ignorable elements w.r.t. the validation process + * + * returns the first element to consider for validation of the content model + */ + +static xmlNodePtr +xmlValidateSkipIgnorable(xmlNodePtr child) { + while (child != NULL) { + switch (child->type) { + /* These things are ignored (skipped) during validation. */ + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + child = child->next; + break; + case XML_TEXT_NODE: + if (xmlIsBlankNode(child)) + child = child->next; + else + return(child); + break; + /* keep current node */ + default: + return(child); + } + } + return(child); +} + +/** + * xmlValidateElementType: + * @ctxt: the validation context + * + * Try to validate the content model of an element internal function + * + * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity + * reference is found and -3 if the validation succeeded but + * the content model is not determinist. + */ + +static int +xmlValidateElementType(xmlValidCtxtPtr ctxt) { + int ret = -1; + int determinist = 1; + + NODE = xmlValidateSkipIgnorable(NODE); + if ((NODE == NULL) && (CONT == NULL)) + return(1); + if ((NODE == NULL) && + ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) || + (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) { + return(1); + } + if (CONT == NULL) return(-1); + if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE)) + return(-2); + + /* + * We arrive here when more states need to be examined + */ +cont: + + /* + * We just recovered from a rollback generated by a possible + * epsilon transition, go directly to the analysis phase + */ + if (STATE == ROLLBACK_PARENT) { + DEBUG_VALID_MSG("restored parent branch"); + DEBUG_VALID_STATE(NODE, CONT) + ret = 1; + goto analyze; + } + + DEBUG_VALID_STATE(NODE, CONT) + /* + * we may have to save a backup state here. This is the equivalent + * of handling epsilon transition in NFAs. + */ + if ((CONT != NULL) && + ((CONT->parent == NULL) || + (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) && + ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) || + (CONT->ocur == XML_ELEMENT_CONTENT_OPT) || + ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) { + DEBUG_VALID_MSG("saving parent branch"); + if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0) + return(0); + } + + + /* + * Check first if the content matches + */ + switch (CONT->type) { + case XML_ELEMENT_CONTENT_PCDATA: + if (NODE == NULL) { + DEBUG_VALID_MSG("pcdata failed no node"); + ret = 0; + break; + } + if (NODE->type == XML_TEXT_NODE) { + DEBUG_VALID_MSG("pcdata found, skip to next"); + /* + * go to next element in the content model + * skipping ignorable elems + */ + do { + NODE = NODE->next; + NODE = xmlValidateSkipIgnorable(NODE); + if ((NODE != NULL) && + (NODE->type == XML_ENTITY_REF_NODE)) + return(-2); + } while ((NODE != NULL) && + ((NODE->type != XML_ELEMENT_NODE) && + (NODE->type != XML_TEXT_NODE) && + (NODE->type != XML_CDATA_SECTION_NODE))); + ret = 1; + break; + } else { + DEBUG_VALID_MSG("pcdata failed"); + ret = 0; + break; + } + break; + case XML_ELEMENT_CONTENT_ELEMENT: + if (NODE == NULL) { + DEBUG_VALID_MSG("element failed no node"); + ret = 0; + break; + } + ret = ((NODE->type == XML_ELEMENT_NODE) && + (xmlStrEqual(NODE->name, CONT->name))); + if (ret == 1) { + if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) { + ret = (CONT->prefix == NULL); + } else if (CONT->prefix == NULL) { + ret = 0; + } else { + ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix); + } + } + if (ret == 1) { + DEBUG_VALID_MSG("element found, skip to next"); + /* + * go to next element in the content model + * skipping ignorable elems + */ + do { + NODE = NODE->next; + NODE = xmlValidateSkipIgnorable(NODE); + if ((NODE != NULL) && + (NODE->type == XML_ENTITY_REF_NODE)) + return(-2); + } while ((NODE != NULL) && + ((NODE->type != XML_ELEMENT_NODE) && + (NODE->type != XML_TEXT_NODE) && + (NODE->type != XML_CDATA_SECTION_NODE))); + } else { + DEBUG_VALID_MSG("element failed"); + ret = 0; + break; + } + break; + case XML_ELEMENT_CONTENT_OR: + /* + * Small optimization. + */ + if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) { + if ((NODE == NULL) || + (!xmlStrEqual(NODE->name, CONT->c1->name))) { + DEPTH++; + CONT = CONT->c2; + goto cont; + } + if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) { + ret = (CONT->c1->prefix == NULL); + } else if (CONT->c1->prefix == NULL) { + ret = 0; + } else { + ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix); + } + if (ret == 0) { + DEPTH++; + CONT = CONT->c2; + goto cont; + } + } + + /* + * save the second branch 'or' branch + */ + DEBUG_VALID_MSG("saving 'or' branch"); + if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1), + OCCURS, ROLLBACK_OR) < 0) + return(-1); + DEPTH++; + CONT = CONT->c1; + goto cont; + case XML_ELEMENT_CONTENT_SEQ: + /* + * Small optimization. + */ + if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) && + ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) || + (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) { + if ((NODE == NULL) || + (!xmlStrEqual(NODE->name, CONT->c1->name))) { + DEPTH++; + CONT = CONT->c2; + goto cont; + } + if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) { + ret = (CONT->c1->prefix == NULL); + } else if (CONT->c1->prefix == NULL) { + ret = 0; + } else { + ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix); + } + if (ret == 0) { + DEPTH++; + CONT = CONT->c2; + goto cont; + } + } + DEPTH++; + CONT = CONT->c1; + goto cont; + } + + /* + * At this point handle going up in the tree + */ + if (ret == -1) { + DEBUG_VALID_MSG("error found returning"); + return(ret); + } +analyze: + while (CONT != NULL) { + /* + * First do the analysis depending on the occurrence model at + * this level. + */ + if (ret == 0) { + switch (CONT->ocur) { + xmlNodePtr cur; + + case XML_ELEMENT_CONTENT_ONCE: + cur = ctxt->vstate->node; + DEBUG_VALID_MSG("Once branch failed, rollback"); + if (vstateVPop(ctxt) < 0 ) { + DEBUG_VALID_MSG("exhaustion, failed"); + return(0); + } + if (cur != ctxt->vstate->node) + determinist = -3; + goto cont; + case XML_ELEMENT_CONTENT_PLUS: + if (OCCURRENCE == 0) { + cur = ctxt->vstate->node; + DEBUG_VALID_MSG("Plus branch failed, rollback"); + if (vstateVPop(ctxt) < 0 ) { + DEBUG_VALID_MSG("exhaustion, failed"); + return(0); + } + if (cur != ctxt->vstate->node) + determinist = -3; + goto cont; + } + DEBUG_VALID_MSG("Plus branch found"); + ret = 1; + break; + case XML_ELEMENT_CONTENT_MULT: +#ifdef DEBUG_VALID_ALGO + if (OCCURRENCE == 0) { + DEBUG_VALID_MSG("Mult branch failed"); + } else { + DEBUG_VALID_MSG("Mult branch found"); + } +#endif + ret = 1; + break; + case XML_ELEMENT_CONTENT_OPT: + DEBUG_VALID_MSG("Option branch failed"); + ret = 1; + break; + } + } else { + switch (CONT->ocur) { + case XML_ELEMENT_CONTENT_OPT: + DEBUG_VALID_MSG("Option branch succeeded"); + ret = 1; + break; + case XML_ELEMENT_CONTENT_ONCE: + DEBUG_VALID_MSG("Once branch succeeded"); + ret = 1; + break; + case XML_ELEMENT_CONTENT_PLUS: + if (STATE == ROLLBACK_PARENT) { + DEBUG_VALID_MSG("Plus branch rollback"); + ret = 1; + break; + } + if (NODE == NULL) { + DEBUG_VALID_MSG("Plus branch exhausted"); + ret = 1; + break; + } + DEBUG_VALID_MSG("Plus branch succeeded, continuing"); + SET_OCCURRENCE; + goto cont; + case XML_ELEMENT_CONTENT_MULT: + if (STATE == ROLLBACK_PARENT) { + DEBUG_VALID_MSG("Mult branch rollback"); + ret = 1; + break; + } + if (NODE == NULL) { + DEBUG_VALID_MSG("Mult branch exhausted"); + ret = 1; + break; + } + DEBUG_VALID_MSG("Mult branch succeeded, continuing"); + /* SET_OCCURRENCE; */ + goto cont; + } + } + STATE = 0; + + /* + * Then act accordingly at the parent level + */ + RESET_OCCURRENCE; + if (CONT->parent == NULL) + break; + + switch (CONT->parent->type) { + case XML_ELEMENT_CONTENT_PCDATA: + DEBUG_VALID_MSG("Error: parent pcdata"); + return(-1); + case XML_ELEMENT_CONTENT_ELEMENT: + DEBUG_VALID_MSG("Error: parent element"); + return(-1); + case XML_ELEMENT_CONTENT_OR: + if (ret == 1) { + DEBUG_VALID_MSG("Or succeeded"); + CONT = CONT->parent; + DEPTH--; + } else { + DEBUG_VALID_MSG("Or failed"); + CONT = CONT->parent; + DEPTH--; + } + break; + case XML_ELEMENT_CONTENT_SEQ: + if (ret == 0) { + DEBUG_VALID_MSG("Sequence failed"); + CONT = CONT->parent; + DEPTH--; + } else if (CONT == CONT->parent->c1) { + DEBUG_VALID_MSG("Sequence testing 2nd branch"); + CONT = CONT->parent->c2; + goto cont; + } else { + DEBUG_VALID_MSG("Sequence succeeded"); + CONT = CONT->parent; + DEPTH--; + } + } + } + if (NODE != NULL) { + xmlNodePtr cur; + + cur = ctxt->vstate->node; + DEBUG_VALID_MSG("Failed, remaining input, rollback"); + if (vstateVPop(ctxt) < 0 ) { + DEBUG_VALID_MSG("exhaustion, failed"); + return(0); + } + if (cur != ctxt->vstate->node) + determinist = -3; + goto cont; + } + if (ret == 0) { + xmlNodePtr cur; + + cur = ctxt->vstate->node; + DEBUG_VALID_MSG("Failure, rollback"); + if (vstateVPop(ctxt) < 0 ) { + DEBUG_VALID_MSG("exhaustion, failed"); + return(0); + } + if (cur != ctxt->vstate->node) + determinist = -3; + goto cont; + } + return(determinist); +} +#endif + +/** + * xmlSnprintfElements: + * @buf: an output buffer + * @size: the size of the buffer + * @content: An element + * @glob: 1 if one must print the englobing parenthesis, 0 otherwise + * + * This will dump the list of elements to the buffer + * Intended just for the debug routine + */ +static void +xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) { + xmlNodePtr cur; + int len; + + if (node == NULL) return; + if (glob) strcat(buf, "("); + cur = node; + while (cur != NULL) { + len = strlen(buf); + if (size - len < 50) { + if ((size - len > 4) && (buf[len - 1] != '.')) + strcat(buf, " ..."); + return; + } + switch (cur->type) { + case XML_ELEMENT_NODE: + if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { + if (size - len < xmlStrlen(cur->ns->prefix) + 10) { + if ((size - len > 4) && (buf[len - 1] != '.')) + strcat(buf, " ..."); + return; + } + strcat(buf, (char *) cur->ns->prefix); + strcat(buf, ":"); + } + if (size - len < xmlStrlen(cur->name) + 10) { + if ((size - len > 4) && (buf[len - 1] != '.')) + strcat(buf, " ..."); + return; + } + strcat(buf, (char *) cur->name); + if (cur->next != NULL) + strcat(buf, " "); + break; + case XML_TEXT_NODE: + if (xmlIsBlankNode(cur)) + break; + /* Falls through. */ + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + strcat(buf, "CDATA"); + if (cur->next != NULL) + strcat(buf, " "); + break; + case XML_ATTRIBUTE_NODE: + case XML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_NOTATION_NODE: + case XML_NAMESPACE_DECL: + strcat(buf, "???"); + if (cur->next != NULL) + strcat(buf, " "); + break; + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_DTD_NODE: + case XML_COMMENT_NODE: + case XML_ELEMENT_DECL: + case XML_ATTRIBUTE_DECL: + case XML_ENTITY_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + break; + } + cur = cur->next; + } + if (glob) strcat(buf, ")"); +} + +/** + * xmlValidateElementContent: + * @ctxt: the validation context + * @child: the child list + * @elemDecl: pointer to the element declaration + * @warn: emit the error message + * @parent: the parent element (for error reporting) + * + * Try to validate the content model of an element + * + * returns 1 if valid or 0 if not and -1 in case of error + */ + +static int +xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child, + xmlElementPtr elemDecl, int warn, xmlNodePtr parent) { + int ret = 1; +#ifndef LIBXML_REGEXP_ENABLED + xmlNodePtr repl = NULL, last = NULL, tmp; +#endif + xmlNodePtr cur; + xmlElementContentPtr cont; + const xmlChar *name; + + if ((elemDecl == NULL) || (parent == NULL) || (ctxt == NULL)) + return(-1); + cont = elemDecl->content; + name = elemDecl->name; + +#ifdef LIBXML_REGEXP_ENABLED + /* Build the regexp associated to the content model */ + if (elemDecl->contModel == NULL) + ret = xmlValidBuildContentModel(ctxt, elemDecl); + if (elemDecl->contModel == NULL) { + return(-1); + } else { + xmlRegExecCtxtPtr exec; + + if (!xmlRegexpIsDeterminist(elemDecl->contModel)) { + return(-1); + } + ctxt->nodeMax = 0; + ctxt->nodeNr = 0; + ctxt->nodeTab = NULL; + exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL); + if (exec != NULL) { + cur = child; + while (cur != NULL) { + switch (cur->type) { + case XML_ENTITY_REF_NODE: + /* + * Push the current node to be able to roll back + * and process within the entity + */ + if ((cur->children != NULL) && + (cur->children->children != NULL)) { + nodeVPush(ctxt, cur); + cur = cur->children->children; + continue; + } + break; + case XML_TEXT_NODE: + if (xmlIsBlankNode(cur)) + break; + ret = 0; + goto fail; + case XML_CDATA_SECTION_NODE: + /* TODO */ + ret = 0; + goto fail; + case XML_ELEMENT_NODE: + if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { + xmlChar fn[50]; + xmlChar *fullname; + + fullname = xmlBuildQName(cur->name, + cur->ns->prefix, fn, 50); + if (fullname == NULL) { + ret = -1; + goto fail; + } + ret = xmlRegExecPushString(exec, fullname, NULL); + if ((fullname != fn) && (fullname != cur->name)) + xmlFree(fullname); + } else { + ret = xmlRegExecPushString(exec, cur->name, NULL); + } + break; + default: + break; + } + /* + * Switch to next element + */ + cur = cur->next; + while (cur == NULL) { + cur = nodeVPop(ctxt); + if (cur == NULL) + break; + cur = cur->next; + } + } + ret = xmlRegExecPushString(exec, NULL, NULL); +fail: + xmlRegFreeExecCtxt(exec); + } + } +#else /* LIBXML_REGEXP_ENABLED */ + /* + * Allocate the stack + */ + ctxt->vstateMax = 8; + ctxt->vstateTab = (xmlValidState *) xmlMalloc( + ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); + if (ctxt->vstateTab == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + return(-1); + } + /* + * The first entry in the stack is reserved to the current state + */ + ctxt->nodeMax = 0; + ctxt->nodeNr = 0; + ctxt->nodeTab = NULL; + ctxt->vstate = &ctxt->vstateTab[0]; + ctxt->vstateNr = 1; + CONT = cont; + NODE = child; + DEPTH = 0; + OCCURS = 0; + STATE = 0; + ret = xmlValidateElementType(ctxt); + if ((ret == -3) && (warn)) { + xmlErrValidWarning(ctxt, child, XML_DTD_CONTENT_NOT_DETERMINIST, + "Content model for Element %s is ambiguous\n", + name, NULL, NULL); + } else if (ret == -2) { + /* + * An entities reference appeared at this level. + * Buid a minimal representation of this node content + * sufficient to run the validation process on it + */ + DEBUG_VALID_MSG("Found an entity reference, linearizing"); + cur = child; + while (cur != NULL) { + switch (cur->type) { + case XML_ENTITY_REF_NODE: + /* + * Push the current node to be able to roll back + * and process within the entity + */ + if ((cur->children != NULL) && + (cur->children->children != NULL)) { + nodeVPush(ctxt, cur); + cur = cur->children->children; + continue; + } + break; + case XML_TEXT_NODE: + if (xmlIsBlankNode(cur)) + break; + /* no break on purpose */ + case XML_CDATA_SECTION_NODE: + /* no break on purpose */ + case XML_ELEMENT_NODE: + /* + * Allocate a new node and minimally fills in + * what's required + */ + tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); + if (tmp == NULL) { + xmlVErrMemory(ctxt, "malloc failed"); + xmlFreeNodeList(repl); + ret = -1; + goto done; + } + tmp->type = cur->type; + tmp->name = cur->name; + tmp->ns = cur->ns; + tmp->next = NULL; + tmp->content = NULL; + if (repl == NULL) + repl = last = tmp; + else { + last->next = tmp; + last = tmp; + } + if (cur->type == XML_CDATA_SECTION_NODE) { + /* + * E59 spaces in CDATA does not match the + * nonterminal S + */ + tmp->content = xmlStrdup(BAD_CAST "CDATA"); + } + break; + default: + break; + } + /* + * Switch to next element + */ + cur = cur->next; + while (cur == NULL) { + cur = nodeVPop(ctxt); + if (cur == NULL) + break; + cur = cur->next; + } + } + + /* + * Relaunch the validation + */ + ctxt->vstate = &ctxt->vstateTab[0]; + ctxt->vstateNr = 1; + CONT = cont; + NODE = repl; + DEPTH = 0; + OCCURS = 0; + STATE = 0; + ret = xmlValidateElementType(ctxt); + } +#endif /* LIBXML_REGEXP_ENABLED */ + if ((warn) && ((ret != 1) && (ret != -3))) { + if (ctxt != NULL) { + char expr[5000]; + char list[5000]; + + expr[0] = 0; + xmlSnprintfElementContent(&expr[0], 5000, cont, 1); + list[0] = 0; +#ifndef LIBXML_REGEXP_ENABLED + if (repl != NULL) + xmlSnprintfElements(&list[0], 5000, repl, 1); + else +#endif /* LIBXML_REGEXP_ENABLED */ + xmlSnprintfElements(&list[0], 5000, child, 1); + + if (name != NULL) { + xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, + "Element %s content does not follow the DTD, expecting %s, got %s\n", + name, BAD_CAST expr, BAD_CAST list); + } else { + xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, + "Element content does not follow the DTD, expecting %s, got %s\n", + BAD_CAST expr, BAD_CAST list, NULL); + } + } else { + if (name != NULL) { + xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, + "Element %s content does not follow the DTD\n", + name, NULL, NULL); + } else { + xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, + "Element content does not follow the DTD\n", + NULL, NULL, NULL); + } + } + ret = 0; + } + if (ret == -3) + ret = 1; + +#ifndef LIBXML_REGEXP_ENABLED +done: + /* + * Deallocate the copy if done, and free up the validation stack + */ + while (repl != NULL) { + tmp = repl->next; + xmlFree(repl); + repl = tmp; + } + ctxt->vstateMax = 0; + if (ctxt->vstateTab != NULL) { + xmlFree(ctxt->vstateTab); + ctxt->vstateTab = NULL; + } +#endif + ctxt->nodeMax = 0; + ctxt->nodeNr = 0; + if (ctxt->nodeTab != NULL) { + xmlFree(ctxt->nodeTab); + ctxt->nodeTab = NULL; + } + return(ret); + +} + +/** + * xmlValidateCdataElement: + * @ctxt: the validation context + * @doc: a document instance + * @elem: an element instance + * + * Check that an element follows #CDATA + * + * returns 1 if valid or 0 otherwise + */ +static int +xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, + xmlNodePtr elem) { + int ret = 1; + xmlNodePtr cur, child; + + if ((ctxt == NULL) || (doc == NULL) || (elem == NULL) || + (elem->type != XML_ELEMENT_NODE)) + return(0); + + child = elem->children; + + cur = child; + while (cur != NULL) { + switch (cur->type) { + case XML_ENTITY_REF_NODE: + /* + * Push the current node to be able to roll back + * and process within the entity + */ + if ((cur->children != NULL) && + (cur->children->children != NULL)) { + nodeVPush(ctxt, cur); + cur = cur->children->children; + continue; + } + break; + case XML_COMMENT_NODE: + case XML_PI_NODE: + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + break; + default: + ret = 0; + goto done; + } + /* + * Switch to next element + */ + cur = cur->next; + while (cur == NULL) { + cur = nodeVPop(ctxt); + if (cur == NULL) + break; + cur = cur->next; + } + } +done: + ctxt->nodeMax = 0; + ctxt->nodeNr = 0; + if (ctxt->nodeTab != NULL) { + xmlFree(ctxt->nodeTab); + ctxt->nodeTab = NULL; + } + return(ret); +} + +/** + * xmlValidateCheckMixed: + * @ctxt: the validation context + * @cont: the mixed content model + * @qname: the qualified name as appearing in the serialization + * + * Check if the given node is part of the content model. + * + * Returns 1 if yes, 0 if no, -1 in case of error + */ +static int +xmlValidateCheckMixed(xmlValidCtxtPtr ctxt, + xmlElementContentPtr cont, const xmlChar *qname) { + const xmlChar *name; + int plen; + name = xmlSplitQName3(qname, &plen); + + if (name == NULL) { + while (cont != NULL) { + if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { + if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname))) + return(1); + } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && + (cont->c1 != NULL) && + (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){ + if ((cont->c1->prefix == NULL) && + (xmlStrEqual(cont->c1->name, qname))) + return(1); + } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || + (cont->c1 == NULL) || + (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){ + xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT, + "Internal: MIXED struct corrupted\n", + NULL); + break; + } + cont = cont->c2; + } + } else { + while (cont != NULL) { + if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { + if ((cont->prefix != NULL) && + (xmlStrncmp(cont->prefix, qname, plen) == 0) && + (xmlStrEqual(cont->name, name))) + return(1); + } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && + (cont->c1 != NULL) && + (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){ + if ((cont->c1->prefix != NULL) && + (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) && + (xmlStrEqual(cont->c1->name, name))) + return(1); + } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || + (cont->c1 == NULL) || + (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){ + xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT, + "Internal: MIXED struct corrupted\n", + NULL); + break; + } + cont = cont->c2; + } + } + return(0); +} + +/** + * xmlValidGetElemDecl: + * @ctxt: the validation context + * @doc: a document instance + * @elem: an element instance + * @extsubset: pointer, (out) indicate if the declaration was found + * in the external subset. + * + * Finds a declaration associated to an element in the document. + * + * returns the pointer to the declaration or NULL if not found. + */ +static xmlElementPtr +xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, + xmlNodePtr elem, int *extsubset) { + xmlElementPtr elemDecl = NULL; + const xmlChar *prefix = NULL; + + if ((ctxt == NULL) || (doc == NULL) || + (elem == NULL) || (elem->name == NULL)) + return(NULL); + if (extsubset != NULL) + *extsubset = 0; + + /* + * Fetch the declaration for the qualified name + */ + if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) + prefix = elem->ns->prefix; + + if (prefix != NULL) { + elemDecl = xmlGetDtdQElementDesc(doc->intSubset, + elem->name, prefix); + if ((elemDecl == NULL) && (doc->extSubset != NULL)) { + elemDecl = xmlGetDtdQElementDesc(doc->extSubset, + elem->name, prefix); + if ((elemDecl != NULL) && (extsubset != NULL)) + *extsubset = 1; + } + } + + /* + * Fetch the declaration for the non qualified name + * This is "non-strict" validation should be done on the + * full QName but in that case being flexible makes sense. + */ + if (elemDecl == NULL) { + elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name); + if ((elemDecl == NULL) && (doc->extSubset != NULL)) { + elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name); + if ((elemDecl != NULL) && (extsubset != NULL)) + *extsubset = 1; + } + } + if (elemDecl == NULL) { + xmlErrValidNode(ctxt, elem, + XML_DTD_UNKNOWN_ELEM, + "No declaration for element %s\n", + elem->name, NULL, NULL); + } + return(elemDecl); +} + +#ifdef LIBXML_REGEXP_ENABLED +/** + * xmlValidatePushElement: + * @ctxt: the validation context + * @doc: a document instance + * @elem: an element instance + * @qname: the qualified name as appearing in the serialization + * + * Push a new element start on the validation stack. + * + * returns 1 if no validation problem was found or 0 otherwise + */ +int +xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, + xmlNodePtr elem, const xmlChar *qname) { + int ret = 1; + xmlElementPtr eDecl; + int extsubset = 0; + + if (ctxt == NULL) + return(0); +/* printf("PushElem %s\n", qname); */ + if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) { + xmlValidStatePtr state = ctxt->vstate; + xmlElementPtr elemDecl; + + /* + * Check the new element against the content model of the new elem. + */ + if (state->elemDecl != NULL) { + elemDecl = state->elemDecl; + + switch(elemDecl->etype) { + case XML_ELEMENT_TYPE_UNDEFINED: + ret = 0; + break; + case XML_ELEMENT_TYPE_EMPTY: + xmlErrValidNode(ctxt, state->node, + XML_DTD_NOT_EMPTY, + "Element %s was declared EMPTY this one has content\n", + state->node->name, NULL, NULL); + ret = 0; + break; + case XML_ELEMENT_TYPE_ANY: + /* I don't think anything is required then */ + break; + case XML_ELEMENT_TYPE_MIXED: + /* simple case of declared as #PCDATA */ + if ((elemDecl->content != NULL) && + (elemDecl->content->type == + XML_ELEMENT_CONTENT_PCDATA)) { + xmlErrValidNode(ctxt, state->node, + XML_DTD_NOT_PCDATA, + "Element %s was declared #PCDATA but contains non text nodes\n", + state->node->name, NULL, NULL); + ret = 0; + } else { + ret = xmlValidateCheckMixed(ctxt, elemDecl->content, + qname); + if (ret != 1) { + xmlErrValidNode(ctxt, state->node, + XML_DTD_INVALID_CHILD, + "Element %s is not declared in %s list of possible children\n", + qname, state->node->name, NULL); + } + } + break; + case XML_ELEMENT_TYPE_ELEMENT: + /* + * TODO: + * VC: Standalone Document Declaration + * - element types with element content, if white space + * occurs directly within any instance of those types. + */ + if (state->exec != NULL) { + ret = xmlRegExecPushString(state->exec, qname, NULL); + if (ret < 0) { + xmlErrValidNode(ctxt, state->node, + XML_DTD_CONTENT_MODEL, + "Element %s content does not follow the DTD, Misplaced %s\n", + state->node->name, qname, NULL); + ret = 0; + } else { + ret = 1; + } + } + break; + } + } + } + eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset); + vstateVPush(ctxt, eDecl, elem); + return(ret); +} + +/** + * xmlValidatePushCData: + * @ctxt: the validation context + * @data: some character data read + * @len: the length of the data + * + * check the CData parsed for validation in the current stack + * + * returns 1 if no validation problem was found or 0 otherwise + */ +int +xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) { + int ret = 1; + +/* printf("CDATA %s %d\n", data, len); */ + if (ctxt == NULL) + return(0); + if (len <= 0) + return(ret); + if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) { + xmlValidStatePtr state = ctxt->vstate; + xmlElementPtr elemDecl; + + /* + * Check the new element against the content model of the new elem. + */ + if (state->elemDecl != NULL) { + elemDecl = state->elemDecl; + + switch(elemDecl->etype) { + case XML_ELEMENT_TYPE_UNDEFINED: + ret = 0; + break; + case XML_ELEMENT_TYPE_EMPTY: + xmlErrValidNode(ctxt, state->node, + XML_DTD_NOT_EMPTY, + "Element %s was declared EMPTY this one has content\n", + state->node->name, NULL, NULL); + ret = 0; + break; + case XML_ELEMENT_TYPE_ANY: + break; + case XML_ELEMENT_TYPE_MIXED: + break; + case XML_ELEMENT_TYPE_ELEMENT: + if (len > 0) { + int i; + + for (i = 0;i < len;i++) { + if (!IS_BLANK_CH(data[i])) { + xmlErrValidNode(ctxt, state->node, + XML_DTD_CONTENT_MODEL, + "Element %s content does not follow the DTD, Text not allowed\n", + state->node->name, NULL, NULL); + ret = 0; + goto done; + } + } + /* + * TODO: + * VC: Standalone Document Declaration + * element types with element content, if white space + * occurs directly within any instance of those types. + */ + } + break; + } + } + } +done: + return(ret); +} + +/** + * xmlValidatePopElement: + * @ctxt: the validation context + * @doc: a document instance + * @elem: an element instance + * @qname: the qualified name as appearing in the serialization + * + * Pop the element end from the validation stack. + * + * returns 1 if no validation problem was found or 0 otherwise + */ +int +xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED, + xmlNodePtr elem ATTRIBUTE_UNUSED, + const xmlChar *qname ATTRIBUTE_UNUSED) { + int ret = 1; + + if (ctxt == NULL) + return(0); +/* printf("PopElem %s\n", qname); */ + if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) { + xmlValidStatePtr state = ctxt->vstate; + xmlElementPtr elemDecl; + + /* + * Check the new element against the content model of the new elem. + */ + if (state->elemDecl != NULL) { + elemDecl = state->elemDecl; + + if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) { + if (state->exec != NULL) { + ret = xmlRegExecPushString(state->exec, NULL, NULL); + if (ret == 0) { + xmlErrValidNode(ctxt, state->node, + XML_DTD_CONTENT_MODEL, + "Element %s content does not follow the DTD, Expecting more child\n", + state->node->name, NULL,NULL); + } else { + /* + * previous validation errors should not generate + * a new one here + */ + ret = 1; + } + } + } + } + vstateVPop(ctxt); + } + return(ret); +} +#endif /* LIBXML_REGEXP_ENABLED */ + +/** + * xmlValidateOneElement: + * @ctxt: the validation context + * @doc: a document instance + * @elem: an element instance + * + * Try to validate a single element and it's attributes, + * basically it does the following checks as described by the + * XML-1.0 recommendation: + * - [ VC: Element Valid ] + * - [ VC: Required Attribute ] + * Then call xmlValidateOneAttribute() for each attribute present. + * + * The ID/IDREF checkings are done separately + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, + xmlNodePtr elem) { + xmlElementPtr elemDecl = NULL; + xmlElementContentPtr cont; + xmlAttributePtr attr; + xmlNodePtr child; + int ret = 1, tmp; + const xmlChar *name; + int extsubset = 0; + + CHECK_DTD; + + if (elem == NULL) return(0); + switch (elem->type) { + case XML_ATTRIBUTE_NODE: + xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, + "Attribute element not expected\n", NULL, NULL ,NULL); + return(0); + case XML_TEXT_NODE: + if (elem->children != NULL) { + xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, + "Text element has children !\n", + NULL,NULL,NULL); + return(0); + } + if (elem->ns != NULL) { + xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, + "Text element has namespace !\n", + NULL,NULL,NULL); + return(0); + } + if (elem->content == NULL) { + xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, + "Text element has no content !\n", + NULL,NULL,NULL); + return(0); + } + return(1); + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + return(1); + case XML_CDATA_SECTION_NODE: + case XML_ENTITY_REF_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + return(1); + case XML_ENTITY_NODE: + xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, + "Entity element not expected\n", NULL, NULL ,NULL); + return(0); + case XML_NOTATION_NODE: + xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, + "Notation element not expected\n", NULL, NULL ,NULL); + return(0); + case XML_DOCUMENT_NODE: + case XML_DOCUMENT_TYPE_NODE: + case XML_DOCUMENT_FRAG_NODE: + xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, + "Document element not expected\n", NULL, NULL ,NULL); + return(0); + case XML_HTML_DOCUMENT_NODE: + xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, + "HTML Document not expected\n", NULL, NULL ,NULL); + return(0); + case XML_ELEMENT_NODE: + break; + default: + xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, + "unknown element type\n", NULL, NULL ,NULL); + return(0); + } + + /* + * Fetch the declaration + */ + elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset); + if (elemDecl == NULL) + return(0); + + /* + * If vstateNr is not zero that means continuous validation is + * activated, do not try to check the content model at that level. + */ + if (ctxt->vstateNr == 0) { + /* Check that the element content matches the definition */ + switch (elemDecl->etype) { + case XML_ELEMENT_TYPE_UNDEFINED: + xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM, + "No declaration for element %s\n", + elem->name, NULL, NULL); + return(0); + case XML_ELEMENT_TYPE_EMPTY: + if (elem->children != NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_NOT_EMPTY, + "Element %s was declared EMPTY this one has content\n", + elem->name, NULL, NULL); + ret = 0; + } + break; + case XML_ELEMENT_TYPE_ANY: + /* I don't think anything is required then */ + break; + case XML_ELEMENT_TYPE_MIXED: + + /* simple case of declared as #PCDATA */ + if ((elemDecl->content != NULL) && + (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) { + ret = xmlValidateOneCdataElement(ctxt, doc, elem); + if (!ret) { + xmlErrValidNode(ctxt, elem, XML_DTD_NOT_PCDATA, + "Element %s was declared #PCDATA but contains non text nodes\n", + elem->name, NULL, NULL); + } + break; + } + child = elem->children; + /* Hum, this start to get messy */ + while (child != NULL) { + if (child->type == XML_ELEMENT_NODE) { + name = child->name; + if ((child->ns != NULL) && (child->ns->prefix != NULL)) { + xmlChar fn[50]; + xmlChar *fullname; + + fullname = xmlBuildQName(child->name, child->ns->prefix, + fn, 50); + if (fullname == NULL) + return(0); + cont = elemDecl->content; + while (cont != NULL) { + if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { + if (xmlStrEqual(cont->name, fullname)) + break; + } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && + (cont->c1 != NULL) && + (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){ + if (xmlStrEqual(cont->c1->name, fullname)) + break; + } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || + (cont->c1 == NULL) || + (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){ + xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT, + "Internal: MIXED struct corrupted\n", + NULL); + break; + } + cont = cont->c2; + } + if ((fullname != fn) && (fullname != child->name)) + xmlFree(fullname); + if (cont != NULL) + goto child_ok; + } + cont = elemDecl->content; + while (cont != NULL) { + if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { + if (xmlStrEqual(cont->name, name)) break; + } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && + (cont->c1 != NULL) && + (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) { + if (xmlStrEqual(cont->c1->name, name)) break; + } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || + (cont->c1 == NULL) || + (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) { + xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT, + "Internal: MIXED struct corrupted\n", + NULL); + break; + } + cont = cont->c2; + } + if (cont == NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_CHILD, + "Element %s is not declared in %s list of possible children\n", + name, elem->name, NULL); + ret = 0; + } + } +child_ok: + child = child->next; + } + break; + case XML_ELEMENT_TYPE_ELEMENT: + if ((doc->standalone == 1) && (extsubset == 1)) { + /* + * VC: Standalone Document Declaration + * - element types with element content, if white space + * occurs directly within any instance of those types. + */ + child = elem->children; + while (child != NULL) { + if (child->type == XML_TEXT_NODE) { + const xmlChar *content = child->content; + + while (IS_BLANK_CH(*content)) + content++; + if (*content == 0) { + xmlErrValidNode(ctxt, elem, + XML_DTD_STANDALONE_WHITE_SPACE, +"standalone: %s declared in the external subset contains white spaces nodes\n", + elem->name, NULL, NULL); + ret = 0; + break; + } + } + child =child->next; + } + } + child = elem->children; + cont = elemDecl->content; + tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem); + if (tmp <= 0) + ret = tmp; + break; + } + } /* not continuous */ + + /* [ VC: Required Attribute ] */ + attr = elemDecl->attributes; + while (attr != NULL) { + if (attr->def == XML_ATTRIBUTE_REQUIRED) { + int qualified = -1; + + if ((attr->prefix == NULL) && + (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) { + xmlNsPtr ns; + + ns = elem->nsDef; + while (ns != NULL) { + if (ns->prefix == NULL) + goto found; + ns = ns->next; + } + } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) { + xmlNsPtr ns; + + ns = elem->nsDef; + while (ns != NULL) { + if (xmlStrEqual(attr->name, ns->prefix)) + goto found; + ns = ns->next; + } + } else { + xmlAttrPtr attrib; + + attrib = elem->properties; + while (attrib != NULL) { + if (xmlStrEqual(attrib->name, attr->name)) { + if (attr->prefix != NULL) { + xmlNsPtr nameSpace = attrib->ns; + + if (nameSpace == NULL) + nameSpace = elem->ns; + /* + * qualified names handling is problematic, having a + * different prefix should be possible but DTDs don't + * allow to define the URI instead of the prefix :-( + */ + if (nameSpace == NULL) { + if (qualified < 0) + qualified = 0; + } else if (!xmlStrEqual(nameSpace->prefix, + attr->prefix)) { + if (qualified < 1) + qualified = 1; + } else + goto found; + } else { + /* + * We should allow applications to define namespaces + * for their application even if the DTD doesn't + * carry one, otherwise, basically we would always + * break. + */ + goto found; + } + } + attrib = attrib->next; + } + } + if (qualified == -1) { + if (attr->prefix == NULL) { + xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE, + "Element %s does not carry attribute %s\n", + elem->name, attr->name, NULL); + ret = 0; + } else { + xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE, + "Element %s does not carry attribute %s:%s\n", + elem->name, attr->prefix,attr->name); + ret = 0; + } + } else if (qualified == 0) { + xmlErrValidWarning(ctxt, elem, XML_DTD_NO_PREFIX, + "Element %s required attribute %s:%s has no prefix\n", + elem->name, attr->prefix, attr->name); + } else if (qualified == 1) { + xmlErrValidWarning(ctxt, elem, XML_DTD_DIFFERENT_PREFIX, + "Element %s required attribute %s:%s has different prefix\n", + elem->name, attr->prefix, attr->name); + } + } else if (attr->def == XML_ATTRIBUTE_FIXED) { + /* + * Special tests checking #FIXED namespace declarations + * have the right value since this is not done as an + * attribute checking + */ + if ((attr->prefix == NULL) && + (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) { + xmlNsPtr ns; + + ns = elem->nsDef; + while (ns != NULL) { + if (ns->prefix == NULL) { + if (!xmlStrEqual(attr->defaultValue, ns->href)) { + xmlErrValidNode(ctxt, elem, + XML_DTD_ELEM_DEFAULT_NAMESPACE, + "Element %s namespace name for default namespace does not match the DTD\n", + elem->name, NULL, NULL); + ret = 0; + } + goto found; + } + ns = ns->next; + } + } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) { + xmlNsPtr ns; + + ns = elem->nsDef; + while (ns != NULL) { + if (xmlStrEqual(attr->name, ns->prefix)) { + if (!xmlStrEqual(attr->defaultValue, ns->href)) { + xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE, + "Element %s namespace name for %s does not match the DTD\n", + elem->name, ns->prefix, NULL); + ret = 0; + } + goto found; + } + ns = ns->next; + } + } + } +found: + attr = attr->nexth; + } + return(ret); +} + +/** + * xmlValidateRoot: + * @ctxt: the validation context + * @doc: a document instance + * + * Try to validate a the root element + * basically it does the following check as described by the + * XML-1.0 recommendation: + * - [ VC: Root Element Type ] + * it doesn't try to recurse or apply other check to the element + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { + xmlNodePtr root; + int ret; + + if (doc == NULL) return(0); + + root = xmlDocGetRootElement(doc); + if ((root == NULL) || (root->name == NULL)) { + xmlErrValid(ctxt, XML_DTD_NO_ROOT, + "no root element\n", NULL); + return(0); + } + + /* + * When doing post validation against a separate DTD, those may + * no internal subset has been generated + */ + if ((doc->intSubset != NULL) && + (doc->intSubset->name != NULL)) { + /* + * Check first the document root against the NQName + */ + if (!xmlStrEqual(doc->intSubset->name, root->name)) { + if ((root->ns != NULL) && (root->ns->prefix != NULL)) { + xmlChar fn[50]; + xmlChar *fullname; + + fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50); + if (fullname == NULL) { + xmlVErrMemory(ctxt, NULL); + return(0); + } + ret = xmlStrEqual(doc->intSubset->name, fullname); + if ((fullname != fn) && (fullname != root->name)) + xmlFree(fullname); + if (ret == 1) + goto name_ok; + } + if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) && + (xmlStrEqual(root->name, BAD_CAST "html"))) + goto name_ok; + xmlErrValidNode(ctxt, root, XML_DTD_ROOT_NAME, + "root and DTD name do not match '%s' and '%s'\n", + root->name, doc->intSubset->name, NULL); + return(0); + } + } +name_ok: + return(1); +} + + +/** + * xmlValidateElement: + * @ctxt: the validation context + * @doc: a document instance + * @elem: an element instance + * + * Try to validate the subtree under an element + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) { + xmlNodePtr child; + xmlAttrPtr attr; + xmlNsPtr ns; + const xmlChar *value; + int ret = 1; + + if (elem == NULL) return(0); + + /* + * XInclude elements were added after parsing in the infoset, + * they don't really mean anything validation wise. + */ + if ((elem->type == XML_XINCLUDE_START) || + (elem->type == XML_XINCLUDE_END) || + (elem->type == XML_NAMESPACE_DECL)) + return(1); + + CHECK_DTD; + + /* + * Entities references have to be handled separately + */ + if (elem->type == XML_ENTITY_REF_NODE) { + return(1); + } + + ret &= xmlValidateOneElement(ctxt, doc, elem); + if (elem->type == XML_ELEMENT_NODE) { + attr = elem->properties; + while (attr != NULL) { + value = xmlNodeListGetString(doc, attr->children, 0); + ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value); + if (value != NULL) + xmlFree((char *)value); + attr= attr->next; + } + ns = elem->nsDef; + while (ns != NULL) { + if (elem->ns == NULL) + ret &= xmlValidateOneNamespace(ctxt, doc, elem, NULL, + ns, ns->href); + else + ret &= xmlValidateOneNamespace(ctxt, doc, elem, + elem->ns->prefix, ns, ns->href); + ns = ns->next; + } + } + child = elem->children; + while (child != NULL) { + ret &= xmlValidateElement(ctxt, doc, child); + child = child->next; + } + + return(ret); +} + +/** + * xmlValidateRef: + * @ref: A reference to be validated + * @ctxt: Validation context + * @name: Name of ID we are searching for + * + */ +static void +xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt, + const xmlChar *name) { + xmlAttrPtr id; + xmlAttrPtr attr; + + if (ref == NULL) + return; + if ((ref->attr == NULL) && (ref->name == NULL)) + return; + attr = ref->attr; + if (attr == NULL) { + xmlChar *dup, *str = NULL, *cur, save; + + dup = xmlStrdup(name); + if (dup == NULL) { + ctxt->valid = 0; + return; + } + cur = dup; + while (*cur != 0) { + str = cur; + while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++; + save = *cur; + *cur = 0; + id = xmlGetID(ctxt->doc, str); + if (id == NULL) { + xmlErrValidNodeNr(ctxt, NULL, XML_DTD_UNKNOWN_ID, + "attribute %s line %d references an unknown ID \"%s\"\n", + ref->name, ref->lineno, str); + ctxt->valid = 0; + } + if (save == 0) + break; + *cur = save; + while (IS_BLANK_CH(*cur)) cur++; + } + xmlFree(dup); + } else if (attr->atype == XML_ATTRIBUTE_IDREF) { + id = xmlGetID(ctxt->doc, name); + if (id == NULL) { + xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID, + "IDREF attribute %s references an unknown ID \"%s\"\n", + attr->name, name, NULL); + ctxt->valid = 0; + } + } else if (attr->atype == XML_ATTRIBUTE_IDREFS) { + xmlChar *dup, *str = NULL, *cur, save; + + dup = xmlStrdup(name); + if (dup == NULL) { + xmlVErrMemory(ctxt, "IDREFS split"); + ctxt->valid = 0; + return; + } + cur = dup; + while (*cur != 0) { + str = cur; + while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++; + save = *cur; + *cur = 0; + id = xmlGetID(ctxt->doc, str); + if (id == NULL) { + xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID, + "IDREFS attribute %s references an unknown ID \"%s\"\n", + attr->name, str, NULL); + ctxt->valid = 0; + } + if (save == 0) + break; + *cur = save; + while (IS_BLANK_CH(*cur)) cur++; + } + xmlFree(dup); + } +} + +/** + * xmlWalkValidateList: + * @data: Contents of current link + * @user: Value supplied by the user + * + * Returns 0 to abort the walk or 1 to continue + */ +static int +xmlWalkValidateList(const void *data, void *user) +{ + xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user; + xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name); + return 1; +} + +/** + * xmlValidateCheckRefCallback: + * @ref_list: List of references + * @ctxt: Validation context + * @name: Name of ID we are searching for + * + */ +static void +xmlValidateCheckRefCallback(void *payload, void *data, const xmlChar *name) { + xmlListPtr ref_list = (xmlListPtr) payload; + xmlValidCtxtPtr ctxt = (xmlValidCtxtPtr) data; + xmlValidateMemo memo; + + if (ref_list == NULL) + return; + memo.ctxt = ctxt; + memo.name = name; + + xmlListWalk(ref_list, xmlWalkValidateList, &memo); + +} + +/** + * xmlValidateDocumentFinal: + * @ctxt: the validation context + * @doc: a document instance + * + * Does the final step for the document validation once all the + * incremental validation steps have been completed + * + * basically it does the following checks described by the XML Rec + * + * Check all the IDREF/IDREFS attributes definition for validity + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { + xmlRefTablePtr table; + unsigned int save; + + if (ctxt == NULL) + return(0); + if (doc == NULL) { + xmlErrValid(ctxt, XML_DTD_NO_DOC, + "xmlValidateDocumentFinal: doc == NULL\n", NULL); + return(0); + } + + /* trick to get correct line id report */ + save = ctxt->finishDtd; + ctxt->finishDtd = 0; + + /* + * Check all the NOTATION/NOTATIONS attributes + */ + /* + * Check all the ENTITY/ENTITIES attributes definition for validity + */ + /* + * Check all the IDREF/IDREFS attributes definition for validity + */ + table = (xmlRefTablePtr) doc->refs; + ctxt->doc = doc; + ctxt->valid = 1; + xmlHashScan(table, xmlValidateCheckRefCallback, ctxt); + + ctxt->finishDtd = save; + return(ctxt->valid); +} + +/** + * xmlValidateDtd: + * @ctxt: the validation context + * @doc: a document instance + * @dtd: a dtd instance + * + * Try to validate the document against the dtd instance + * + * Basically it does check all the definitions in the DtD. + * Note the the internal subset (if present) is de-coupled + * (i.e. not used), which could give problems if ID or IDREF + * is present. + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) { + int ret; + xmlDtdPtr oldExt, oldInt; + xmlNodePtr root; + + if (dtd == NULL) return(0); + if (doc == NULL) return(0); + oldExt = doc->extSubset; + oldInt = doc->intSubset; + doc->extSubset = dtd; + doc->intSubset = NULL; + ret = xmlValidateRoot(ctxt, doc); + if (ret == 0) { + doc->extSubset = oldExt; + doc->intSubset = oldInt; + return(ret); + } + if (doc->ids != NULL) { + xmlFreeIDTable(doc->ids); + doc->ids = NULL; + } + if (doc->refs != NULL) { + xmlFreeRefTable(doc->refs); + doc->refs = NULL; + } + root = xmlDocGetRootElement(doc); + ret = xmlValidateElement(ctxt, doc, root); + ret &= xmlValidateDocumentFinal(ctxt, doc); + doc->extSubset = oldExt; + doc->intSubset = oldInt; + return(ret); +} + +static void +xmlValidateNotationCallback(void *payload, void *data, + const xmlChar *name ATTRIBUTE_UNUSED) { + xmlEntityPtr cur = (xmlEntityPtr) payload; + xmlValidCtxtPtr ctxt = (xmlValidCtxtPtr) data; + if (cur == NULL) + return; + if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) { + xmlChar *notation = cur->content; + + if (notation != NULL) { + int ret; + + ret = xmlValidateNotationUse(ctxt, cur->doc, notation); + if (ret != 1) { + ctxt->valid = 0; + } + } + } +} + +static void +xmlValidateAttributeCallback(void *payload, void *data, + const xmlChar *name ATTRIBUTE_UNUSED) { + xmlAttributePtr cur = (xmlAttributePtr) payload; + xmlValidCtxtPtr ctxt = (xmlValidCtxtPtr) data; + int ret; + xmlDocPtr doc; + xmlElementPtr elem = NULL; + + if (cur == NULL) + return; + switch (cur->atype) { + case XML_ATTRIBUTE_CDATA: + case XML_ATTRIBUTE_ID: + case XML_ATTRIBUTE_IDREF : + case XML_ATTRIBUTE_IDREFS: + case XML_ATTRIBUTE_NMTOKEN: + case XML_ATTRIBUTE_NMTOKENS: + case XML_ATTRIBUTE_ENUMERATION: + break; + case XML_ATTRIBUTE_ENTITY: + case XML_ATTRIBUTE_ENTITIES: + case XML_ATTRIBUTE_NOTATION: + if (cur->defaultValue != NULL) { + + ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name, + cur->atype, cur->defaultValue); + if ((ret == 0) && (ctxt->valid == 1)) + ctxt->valid = 0; + } + if (cur->tree != NULL) { + xmlEnumerationPtr tree = cur->tree; + while (tree != NULL) { + ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, + cur->name, cur->atype, tree->name); + if ((ret == 0) && (ctxt->valid == 1)) + ctxt->valid = 0; + tree = tree->next; + } + } + } + if (cur->atype == XML_ATTRIBUTE_NOTATION) { + doc = cur->doc; + if (cur->elem == NULL) { + xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + "xmlValidateAttributeCallback(%s): internal error\n", + (const char *) cur->name); + return; + } + + if (doc != NULL) + elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem); + if ((elem == NULL) && (doc != NULL)) + elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem); + if ((elem == NULL) && (cur->parent != NULL) && + (cur->parent->type == XML_DTD_NODE)) + elem = xmlGetDtdElementDesc((xmlDtdPtr) cur->parent, cur->elem); + if (elem == NULL) { + xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM, + "attribute %s: could not find decl for element %s\n", + cur->name, cur->elem, NULL); + return; + } + if (elem->etype == XML_ELEMENT_TYPE_EMPTY) { + xmlErrValidNode(ctxt, NULL, XML_DTD_EMPTY_NOTATION, + "NOTATION attribute %s declared for EMPTY element %s\n", + cur->name, cur->elem, NULL); + ctxt->valid = 0; + } + } +} + +/** + * xmlValidateDtdFinal: + * @ctxt: the validation context + * @doc: a document instance + * + * Does the final step for the dtds validation once all the + * subsets have been parsed + * + * basically it does the following checks described by the XML Rec + * - check that ENTITY and ENTITIES type attributes default or + * possible values matches one of the defined entities. + * - check that NOTATION type attributes default or + * possible values matches one of the defined notations. + * + * returns 1 if valid or 0 if invalid and -1 if not well-formed + */ + +int +xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { + xmlDtdPtr dtd; + xmlAttributeTablePtr table; + xmlEntitiesTablePtr entities; + + if ((doc == NULL) || (ctxt == NULL)) return(0); + if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) + return(0); + ctxt->doc = doc; + ctxt->valid = 1; + dtd = doc->intSubset; + if ((dtd != NULL) && (dtd->attributes != NULL)) { + table = (xmlAttributeTablePtr) dtd->attributes; + xmlHashScan(table, xmlValidateAttributeCallback, ctxt); + } + if ((dtd != NULL) && (dtd->entities != NULL)) { + entities = (xmlEntitiesTablePtr) dtd->entities; + xmlHashScan(entities, xmlValidateNotationCallback, ctxt); + } + dtd = doc->extSubset; + if ((dtd != NULL) && (dtd->attributes != NULL)) { + table = (xmlAttributeTablePtr) dtd->attributes; + xmlHashScan(table, xmlValidateAttributeCallback, ctxt); + } + if ((dtd != NULL) && (dtd->entities != NULL)) { + entities = (xmlEntitiesTablePtr) dtd->entities; + xmlHashScan(entities, xmlValidateNotationCallback, ctxt); + } + return(ctxt->valid); +} + +/** + * xmlValidateDocument: + * @ctxt: the validation context + * @doc: a document instance + * + * Try to validate the document instance + * + * basically it does the all the checks described by the XML Rec + * i.e. validates the internal and external subset (if present) + * and validate the document tree. + * + * returns 1 if valid or 0 otherwise + */ + +int +xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { + int ret; + xmlNodePtr root; + + if (doc == NULL) + return(0); + if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) { + xmlErrValid(ctxt, XML_DTD_NO_DTD, + "no DTD found!\n", NULL); + return(0); + } + if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) || + (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) { + xmlChar *sysID; + if (doc->intSubset->SystemID != NULL) { + sysID = xmlBuildURI(doc->intSubset->SystemID, + doc->URL); + if (sysID == NULL) { + xmlErrValid(ctxt, XML_DTD_LOAD_ERROR, + "Could not build URI for external subset \"%s\"\n", + (const char *) doc->intSubset->SystemID); + return 0; + } + } else + sysID = NULL; + doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID, + (const xmlChar *)sysID); + if (sysID != NULL) + xmlFree(sysID); + if (doc->extSubset == NULL) { + if (doc->intSubset->SystemID != NULL) { + xmlErrValid(ctxt, XML_DTD_LOAD_ERROR, + "Could not load the external subset \"%s\"\n", + (const char *) doc->intSubset->SystemID); + } else { + xmlErrValid(ctxt, XML_DTD_LOAD_ERROR, + "Could not load the external subset \"%s\"\n", + (const char *) doc->intSubset->ExternalID); + } + return(0); + } + } + + if (doc->ids != NULL) { + xmlFreeIDTable(doc->ids); + doc->ids = NULL; + } + if (doc->refs != NULL) { + xmlFreeRefTable(doc->refs); + doc->refs = NULL; + } + ret = xmlValidateDtdFinal(ctxt, doc); + if (!xmlValidateRoot(ctxt, doc)) return(0); + + root = xmlDocGetRootElement(doc); + ret &= xmlValidateElement(ctxt, doc, root); + ret &= xmlValidateDocumentFinal(ctxt, doc); + return(ret); +} + +/************************************************************************ + * * + * Routines for dynamic validation editing * + * * + ************************************************************************/ + +/** + * xmlValidGetPotentialChildren: + * @ctree: an element content tree + * @names: an array to store the list of child names + * @len: a pointer to the number of element in the list + * @max: the size of the array + * + * Build/extend a list of potential children allowed by the content tree + * + * returns the number of element in the list, or -1 in case of error. + */ + +int +xmlValidGetPotentialChildren(xmlElementContent *ctree, + const xmlChar **names, + int *len, int max) { + int i; + + if ((ctree == NULL) || (names == NULL) || (len == NULL)) + return(-1); + if (*len >= max) return(*len); + + switch (ctree->type) { + case XML_ELEMENT_CONTENT_PCDATA: + for (i = 0; i < *len;i++) + if (xmlStrEqual(BAD_CAST "#PCDATA", names[i])) return(*len); + names[(*len)++] = BAD_CAST "#PCDATA"; + break; + case XML_ELEMENT_CONTENT_ELEMENT: + for (i = 0; i < *len;i++) + if (xmlStrEqual(ctree->name, names[i])) return(*len); + names[(*len)++] = ctree->name; + break; + case XML_ELEMENT_CONTENT_SEQ: + xmlValidGetPotentialChildren(ctree->c1, names, len, max); + xmlValidGetPotentialChildren(ctree->c2, names, len, max); + break; + case XML_ELEMENT_CONTENT_OR: + xmlValidGetPotentialChildren(ctree->c1, names, len, max); + xmlValidGetPotentialChildren(ctree->c2, names, len, max); + break; + } + + return(*len); +} + +/* + * Dummy function to suppress messages while we try out valid elements + */ +static void XMLCDECL xmlNoValidityErr(void *ctx ATTRIBUTE_UNUSED, + const char *msg ATTRIBUTE_UNUSED, ...) { + return; +} + +/** + * xmlValidGetValidElements: + * @prev: an element to insert after + * @next: an element to insert next + * @names: an array to store the list of child names + * @max: the size of the array + * + * This function returns the list of authorized children to insert + * within an existing tree while respecting the validity constraints + * forced by the Dtd. The insertion point is defined using @prev and + * @next in the following ways: + * to insert before 'node': xmlValidGetValidElements(node->prev, node, ... + * to insert next 'node': xmlValidGetValidElements(node, node->next, ... + * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ... + * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs, + * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ... + * + * pointers to the element names are inserted at the beginning of the array + * and do not need to be freed. + * + * returns the number of element in the list, or -1 in case of error. If + * the function returns the value @max the caller is invited to grow the + * receiving array and retry. + */ + +int +xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **names, + int max) { + xmlValidCtxt vctxt; + int nb_valid_elements = 0; + const xmlChar *elements[256]={0}; + int nb_elements = 0, i; + const xmlChar *name; + + xmlNode *ref_node; + xmlNode *parent; + xmlNode *test_node; + + xmlNode *prev_next; + xmlNode *next_prev; + xmlNode *parent_childs; + xmlNode *parent_last; + + xmlElement *element_desc; + + if (prev == NULL && next == NULL) + return(-1); + + if (names == NULL) return(-1); + if (max <= 0) return(-1); + + memset(&vctxt, 0, sizeof (xmlValidCtxt)); + vctxt.error = xmlNoValidityErr; /* this suppresses err/warn output */ + + nb_valid_elements = 0; + ref_node = prev ? prev : next; + parent = ref_node->parent; + + /* + * Retrieves the parent element declaration + */ + element_desc = xmlGetDtdElementDesc(parent->doc->intSubset, + parent->name); + if ((element_desc == NULL) && (parent->doc->extSubset != NULL)) + element_desc = xmlGetDtdElementDesc(parent->doc->extSubset, + parent->name); + if (element_desc == NULL) return(-1); + + /* + * Do a backup of the current tree structure + */ + prev_next = prev ? prev->next : NULL; + next_prev = next ? next->prev : NULL; + parent_childs = parent->children; + parent_last = parent->last; + + /* + * Creates a dummy node and insert it into the tree + */ + test_node = xmlNewDocNode (ref_node->doc, NULL, BAD_CAST "", NULL); + if (test_node == NULL) + return(-1); + + test_node->parent = parent; + test_node->prev = prev; + test_node->next = next; + name = test_node->name; + + if (prev) prev->next = test_node; + else parent->children = test_node; + + if (next) next->prev = test_node; + else parent->last = test_node; + + /* + * Insert each potential child node and check if the parent is + * still valid + */ + nb_elements = xmlValidGetPotentialChildren(element_desc->content, + elements, &nb_elements, 256); + + for (i = 0;i < nb_elements;i++) { + test_node->name = elements[i]; + if (xmlValidateOneElement(&vctxt, parent->doc, parent)) { + int j; + + for (j = 0; j < nb_valid_elements;j++) + if (xmlStrEqual(elements[i], names[j])) break; + names[nb_valid_elements++] = elements[i]; + if (nb_valid_elements >= max) break; + } + } + + /* + * Restore the tree structure + */ + if (prev) prev->next = prev_next; + if (next) next->prev = next_prev; + parent->children = parent_childs; + parent->last = parent_last; + + /* + * Free up the dummy node + */ + test_node->name = name; + xmlFreeNode(test_node); + + return(nb_valid_elements); +} +#endif /* LIBXML_VALID_ENABLED */ + +#define bottom_valid +#include "elfgcchack.h" -- cgit v1.2.3