Revert directory structure changes
diff --git a/valid.c b/valid.c
new file mode 100644
index 0000000..1b7e6f6
--- /dev/null
+++ b/valid.c
@@ -0,0 +1,4098 @@
+/*
+ * 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@w3.org
+ */
+
+#ifdef WIN32
+#include "win32config.h"
+#else
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <libxml/xmlmemory.h>
+#include <libxml/hash.h>
+#include <libxml/valid.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/xmlerror.h>
+#include <libxml/list.h>
+
+/*
+ * Generic function for accessing stacks in the Validity Context
+ */
+
+#define PUSH_AND_POP(scope, type, name) \
+scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
+ if (ctxt->name##Nr >= ctxt->name##Max) { \
+ ctxt->name##Max *= 2; \
+ ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
+ ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
+ if (ctxt->name##Tab == NULL) { \
+ xmlGenericError(xmlGenericErrorContext, \
+ "realloc failed !\n"); \
+ return(0); \
+ } \
+ } \
+ ctxt->name##Tab[ctxt->name##Nr] = value; \
+ ctxt->name = value; \
+ return(ctxt->name##Nr++); \
+} \
+scope type name##VPop(xmlValidCtxtPtr ctxt) { \
+ type ret; \
+ if (ctxt->name##Nr <= 0) return(0); \
+ ctxt->name##Nr--; \
+ if (ctxt->name##Nr > 0) \
+ ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
+ else \
+ ctxt->name = NULL; \
+ ret = ctxt->name##Tab[ctxt->name##Nr]; \
+ ctxt->name##Tab[ctxt->name##Nr] = 0; \
+ return(ret); \
+} \
+
+PUSH_AND_POP(static, xmlNodePtr, node)
+
+/* #define DEBUG_VALID_ALGO */
+
+#ifdef DEBUG_VALID_ALGO
+void xmlValidPrintNodeList(xmlNodePtr cur) {
+ if (cur == NULL)
+ xmlGenericError(xmlGenericErrorContext, "null ");
+ while (cur != NULL) {
+ 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;
+ 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;
+ }
+ cur = cur->next;
+ }
+}
+
+void xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
+ char expr[1000];
+
+ expr[0] = 0;
+ xmlGenericError(xmlGenericErrorContext, "valid: ");
+ xmlValidPrintNodeList(cur);
+ xmlGenericError(xmlGenericErrorContext, "against ");
+ xmlSprintfElementContent(expr, cont, 0);
+ xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
+}
+
+#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
+#else
+#define DEBUG_VALID_STATE(n,c)
+#endif
+
+/* TODO: use hash table for accesses to elem and attribute dedinitions */
+
+#define VERROR \
+ if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
+
+#define VWARNING \
+ if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
+
+#define CHECK_DTD \
+ if (doc == NULL) return(0); \
+ else if ((doc->intSubset == NULL) && \
+ (doc->extSubset == NULL)) return(0)
+
+xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
+xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
+
+/************************************************************************
+ * *
+ * QName handling helper *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlSplitQName2:
+ * @name: an XML parser context
+ * @prefix: a xmlChar **
+ *
+ * parse an XML qualified name string
+ *
+ * [NS 5] QName ::= (Prefix ':')? LocalPart
+ *
+ * [NS 6] Prefix ::= NCName
+ *
+ * [NS 7] LocalPart ::= NCName
+ *
+ * Returns NULL if not a QName, otherwise the local part, and prefix
+ * is updated to get the Prefix if any.
+ */
+
+xmlChar *
+xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
+ int len = 0;
+ xmlChar *ret = NULL;
+
+ *prefix = NULL;
+
+ /* xml: prefix is not really a namespace */
+ if ((name[0] == 'x') && (name[1] == 'm') &&
+ (name[2] == 'l') && (name[3] == ':'))
+ return(NULL);
+
+ /* nasty but valid */
+ if (name[0] == ':')
+ return(NULL);
+
+ /*
+ * we are not trying to validate but just to cut, and yes it will
+ * work even if this is as set of UTF-8 encoded chars
+ */
+ while ((name[len] != 0) && (name[len] != ':'))
+ len++;
+
+ if (name[len] == 0)
+ return(NULL);
+
+ *prefix = xmlStrndup(name, len);
+ ret = xmlStrdup(&name[len + 1]);
+
+ return(ret);
+}
+
+/****************************************************************
+ * *
+ * Util functions for data allocation/deallocation *
+ * *
+ ****************************************************************/
+
+/**
+ * xmlNewElementContent:
+ * @name: the subelement name or NULL
+ * @type: the type of element content decl
+ *
+ * Allocate an element content structure.
+ *
+ * Returns NULL if not, othervise the new element content structure
+ */
+xmlElementContentPtr
+xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
+ xmlElementContentPtr ret;
+
+ switch(type) {
+ case XML_ELEMENT_CONTENT_ELEMENT:
+ if (name == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlNewElementContent : name == NULL !\n");
+ }
+ break;
+ case XML_ELEMENT_CONTENT_PCDATA:
+ case XML_ELEMENT_CONTENT_SEQ:
+ case XML_ELEMENT_CONTENT_OR:
+ if (name != NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlNewElementContent : name != NULL !\n");
+ }
+ break;
+ default:
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlNewElementContent: unknown type %d\n", type);
+ return(NULL);
+ }
+ ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlNewElementContent : out of memory!\n");
+ return(NULL);
+ }
+ ret->type = type;
+ ret->ocur = XML_ELEMENT_CONTENT_ONCE;
+ if (name != NULL)
+ ret->name = xmlStrdup(name);
+ else
+ ret->name = NULL;
+ ret->c1 = ret->c2 = NULL;
+ return(ret);
+}
+
+/**
+ * xmlCopyElementContent:
+ * @content: An element content pointer.
+ *
+ * Build a copy of an element content description.
+ *
+ * Returns the new xmlElementContentPtr or NULL in case of error.
+ */
+xmlElementContentPtr
+xmlCopyElementContent(xmlElementContentPtr cur) {
+ xmlElementContentPtr ret;
+
+ if (cur == NULL) return(NULL);
+ ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlCopyElementContent : out of memory\n");
+ return(NULL);
+ }
+ ret->ocur = cur->ocur;
+ if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
+ if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
+ return(ret);
+}
+
+/**
+ * xmlFreeElementContent:
+ * @cur: the element content tree to free
+ *
+ * Free an element content structure. This is a recursive call !
+ */
+void
+xmlFreeElementContent(xmlElementContentPtr cur) {
+ if (cur == NULL) return;
+ 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:
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlFreeElementContent : type %d\n", cur->type);
+ return;
+ }
+ if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
+ if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
+ if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
+ memset(cur, -1, sizeof(xmlElementContent));
+ xmlFree(cur);
+}
+
+/**
+ * xmlDumpElementContent:
+ * @buf: An XML buffer
+ * @content: An element table
+ * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
+ *
+ * This will dump the content of the element table as an XML DTD definition
+ */
+void
+xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
+ if (content == NULL) return;
+
+ if (glob) xmlBufferWriteChar(buf, "(");
+ switch (content->type) {
+ case XML_ELEMENT_CONTENT_PCDATA:
+ xmlBufferWriteChar(buf, "#PCDATA");
+ break;
+ case XML_ELEMENT_CONTENT_ELEMENT:
+ xmlBufferWriteCHAR(buf, content->name);
+ break;
+ case XML_ELEMENT_CONTENT_SEQ:
+ if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
+ (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
+ xmlDumpElementContent(buf, content->c1, 1);
+ else
+ xmlDumpElementContent(buf, content->c1, 0);
+ xmlBufferWriteChar(buf, " , ");
+ if (content->c2->type == XML_ELEMENT_CONTENT_OR)
+ xmlDumpElementContent(buf, content->c2, 1);
+ else
+ xmlDumpElementContent(buf, content->c2, 0);
+ break;
+ case XML_ELEMENT_CONTENT_OR:
+ if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
+ (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
+ xmlDumpElementContent(buf, content->c1, 1);
+ else
+ xmlDumpElementContent(buf, content->c1, 0);
+ xmlBufferWriteChar(buf, " | ");
+ if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
+ xmlDumpElementContent(buf, content->c2, 1);
+ else
+ xmlDumpElementContent(buf, content->c2, 0);
+ break;
+ default:
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlDumpElementContent: unknown type %d\n",
+ content->type);
+ }
+ if (glob)
+ xmlBufferWriteChar(buf, ")");
+ switch (content->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;
+ }
+}
+
+/**
+ * xmlSprintfElementContent:
+ * @buf: an output buffer
+ * @content: An element table
+ * @glob: 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
+xmlSprintfElementContent(char *buf, xmlElementContentPtr content, int glob) {
+ if (content == NULL) return;
+ if (glob) strcat(buf, "(");
+ switch (content->type) {
+ case XML_ELEMENT_CONTENT_PCDATA:
+ strcat(buf, "#PCDATA");
+ break;
+ case XML_ELEMENT_CONTENT_ELEMENT:
+ 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))
+ xmlSprintfElementContent(buf, content->c1, 1);
+ else
+ xmlSprintfElementContent(buf, content->c1, 0);
+ strcat(buf, " , ");
+ if (content->c2->type == XML_ELEMENT_CONTENT_OR)
+ xmlSprintfElementContent(buf, content->c2, 1);
+ else
+ xmlSprintfElementContent(buf, content->c2, 0);
+ break;
+ case XML_ELEMENT_CONTENT_OR:
+ if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
+ (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
+ xmlSprintfElementContent(buf, content->c1, 1);
+ else
+ xmlSprintfElementContent(buf, content->c1, 0);
+ strcat(buf, " | ");
+ if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
+ xmlSprintfElementContent(buf, content->c2, 1);
+ else
+ xmlSprintfElementContent(buf, content->c2, 0);
+ break;
+ }
+ if (glob)
+ 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 *
+ * *
+ ****************************************************************/
+
+/**
+ * xmlCreateElementTable:
+ *
+ * create and initialize an empty element hash table.
+ *
+ * Returns the xmlElementTablePtr just created or NULL in case of error.
+ */
+xmlElementTablePtr
+xmlCreateElementTable(void) {
+ return(xmlHashCreate(0));
+}
+
+/**
+ * xmlFreeElement:
+ * @elem: An element
+ *
+ * Deallocate the memory used by an element definition
+ */
+void
+xmlFreeElement(xmlElementPtr elem) {
+ if (elem == NULL) return;
+ xmlUnlinkNode((xmlNodePtr) elem);
+ xmlFreeElementContent(elem->content);
+ if (elem->name != NULL)
+ xmlFree((xmlChar *) elem->name);
+ if (elem->prefix != NULL)
+ xmlFree((xmlChar *) elem->prefix);
+ memset(elem, -1, sizeof(xmlElement));
+ 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, othervise the entity
+ */
+xmlElementPtr
+xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
+ xmlElementTypeVal type,
+ xmlElementContentPtr content) {
+ xmlElementPtr ret;
+ xmlElementTablePtr table;
+ xmlChar *ns, *uqname;
+
+ if (dtd == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddElementDecl: dtd == NULL\n");
+ return(NULL);
+ }
+ if (name == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddElementDecl: name == NULL\n");
+ return(NULL);
+ }
+ switch (type) {
+ case XML_ELEMENT_TYPE_EMPTY:
+ if (content != NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddElementDecl: content != NULL for EMPTY\n");
+ return(NULL);
+ }
+ break;
+ case XML_ELEMENT_TYPE_ANY:
+ if (content != NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddElementDecl: content != NULL for ANY\n");
+ return(NULL);
+ }
+ break;
+ case XML_ELEMENT_TYPE_MIXED:
+ if (content == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddElementDecl: content == NULL for MIXED\n");
+ return(NULL);
+ }
+ break;
+ case XML_ELEMENT_TYPE_ELEMENT:
+ if (content == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddElementDecl: content == NULL for ELEMENT\n");
+ return(NULL);
+ }
+ break;
+ default:
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddElementDecl: unknown type %d\n", type);
+ 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) {
+ table = xmlCreateElementTable();
+ dtd->elements = (void *) table;
+ }
+ if (table == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddElementDecl: Table creation failed!\n");
+ return(NULL);
+ }
+
+ ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddElementDecl: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(xmlElement));
+ ret->type = XML_ELEMENT_DECL;
+
+ /*
+ * fill the structure.
+ */
+ ret->etype = type;
+ ret->name = xmlStrdup(name);
+ ret->prefix = ns;
+ ret->content = xmlCopyElementContent(content);
+ ret->attributes = xmlScanAttributeDecl(dtd, name);
+
+ /*
+ * Validity Check:
+ * Insertion must not fail
+ */
+ if (xmlHashAddEntry2(table, name, ns, ret)) {
+ /*
+ * The element is already defined in this Dtd.
+ */
+ VERROR(ctxt->userData, "Redefinition of element %s\n", name);
+ xmlFreeElement(ret);
+ if (uqname != NULL)
+ xmlFree(uqname);
+ return(NULL);
+ }
+
+ /*
+ * 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);
+}
+
+/**
+ * xmlFreeElementTable:
+ * @table: An element table
+ *
+ * Deallocate the memory used by an element hash table.
+ */
+void
+xmlFreeElementTable(xmlElementTablePtr table) {
+ xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
+}
+
+/**
+ * xmlCopyElement:
+ * @elem: An element
+ *
+ * Build a copy of an element.
+ *
+ * Returns the new xmlElementPtr or NULL in case of error.
+ */
+xmlElementPtr
+xmlCopyElement(xmlElementPtr elem) {
+ xmlElementPtr cur;
+
+ cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
+ if (cur == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlCopyElement: out of memory !\n");
+ 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,
+ (xmlHashCopier) xmlCopyElement));
+}
+
+/**
+ * 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) {
+ switch (elem->etype) {
+ case XML_ELEMENT_TYPE_EMPTY:
+ xmlBufferWriteChar(buf, "<!ELEMENT ");
+ xmlBufferWriteCHAR(buf, elem->name);
+ xmlBufferWriteChar(buf, " EMPTY>\n");
+ break;
+ case XML_ELEMENT_TYPE_ANY:
+ xmlBufferWriteChar(buf, "<!ELEMENT ");
+ xmlBufferWriteCHAR(buf, elem->name);
+ xmlBufferWriteChar(buf, " ANY>\n");
+ break;
+ case XML_ELEMENT_TYPE_MIXED:
+ xmlBufferWriteChar(buf, "<!ELEMENT ");
+ xmlBufferWriteCHAR(buf, elem->name);
+ xmlBufferWriteChar(buf, " ");
+ xmlDumpElementContent(buf, elem->content, 1);
+ xmlBufferWriteChar(buf, ">\n");
+ break;
+ case XML_ELEMENT_TYPE_ELEMENT:
+ xmlBufferWriteChar(buf, "<!ELEMENT ");
+ xmlBufferWriteCHAR(buf, elem->name);
+ xmlBufferWriteChar(buf, " ");
+ xmlDumpElementContent(buf, elem->content, 1);
+ xmlBufferWriteChar(buf, ">\n");
+ break;
+ default:
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlDumpElementDecl: internal: unknown type %d\n",
+ elem->etype);
+ }
+}
+
+/**
+ * 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) {
+ xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
+}
+
+/**
+ * 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(xmlChar *name) {
+ xmlEnumerationPtr ret;
+
+ ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
+ (long)sizeof(xmlEnumeration));
+ 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);
+ memset(cur, -1, sizeof(xmlEnumeration));
+ xmlFree(cur);
+}
+
+/**
+ * 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 (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
+ else ret->next = NULL;
+
+ return(ret);
+}
+
+/**
+ * xmlDumpEnumeration:
+ * @buf: the XML buffer output
+ * @enum: An enumeration
+ *
+ * This will dump the content of the enumeration
+ */
+void
+xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
+ if (cur == NULL) return;
+
+ xmlBufferWriteCHAR(buf, cur->name);
+ if (cur->next == NULL)
+ xmlBufferWriteChar(buf, ")");
+ else {
+ xmlBufferWriteChar(buf, " | ");
+ xmlDumpEnumeration(buf, cur->next);
+ }
+}
+
+/**
+ * xmlCreateAttributeTable:
+ *
+ * create and initialize an empty attribute hash table.
+ *
+ * Returns the xmlAttributeTablePtr just created or NULL in case
+ * of error.
+ */
+xmlAttributeTablePtr
+xmlCreateAttributeTable(void) {
+ return(xmlHashCreate(0));
+}
+
+/**
+ * xmlScanAttributeDeclCallback:
+ * @attr: the attribute decl
+ * @list: the list to update
+ *
+ * Callback called by xmlScanAttributeDecl when a new attribute
+ * has to be entered in the list.
+ */
+void
+xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
+ const xmlChar* name) {
+ attr->nexth = *list;
+ *list = attr;
+}
+
+/**
+ * xmlScanAttributeDecl:
+ * @dtd: pointer to the DTD
+ * @elem: the element name
+ *
+ * When inserting a new element scan the DtD for existing attributes
+ * for taht element and initialize the Attribute chain
+ *
+ * Returns the pointer to the first attribute decl in the chain,
+ * possibly NULL.
+ */
+xmlAttributePtr
+xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
+ xmlAttributePtr ret = NULL;
+ xmlAttributeTablePtr table;
+
+ if (dtd == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlScanAttributeDecl: dtd == NULL\n");
+ return(NULL);
+ }
+ if (elem == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlScanAttributeDecl: elem == NULL\n");
+ return(NULL);
+ }
+ table = (xmlAttributeTablePtr) dtd->attributes;
+ if (table == NULL)
+ return(NULL);
+
+ /* WRONG !!! */
+ xmlHashScan3(table, NULL, NULL, elem,
+ (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
+ return(ret);
+}
+
+/**
+ * xmlScanIDAttributeDecl:
+ * @ctxt: the validation context
+ * @elem: the element name
+ *
+ * Verify that the element don't have too many ID attributes
+ * declared.
+ *
+ * Returns the number of ID attributes found.
+ */
+int
+xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
+ 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)
+ VERROR(ctxt->userData,
+ "Element %s has too may ID attributes defined : %s\n",
+ elem->name, cur->name);
+ }
+ cur = cur->nexth;
+ }
+ return(ret);
+}
+
+/**
+ * xmlFreeAttribute:
+ * @elem: An attribute
+ *
+ * Deallocate the memory used by an attribute definition
+ */
+void
+xmlFreeAttribute(xmlAttributePtr attr) {
+ if (attr == NULL) return;
+ xmlUnlinkNode((xmlNodePtr) attr);
+ if (attr->tree != NULL)
+ xmlFreeEnumeration(attr->tree);
+ 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);
+ memset(attr, -1, sizeof(xmlAttribute));
+ 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, othervise 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;
+
+ if (dtd == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddAttributeDecl: dtd == NULL\n");
+ xmlFreeEnumeration(tree);
+ return(NULL);
+ }
+ if (name == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddAttributeDecl: name == NULL\n");
+ xmlFreeEnumeration(tree);
+ return(NULL);
+ }
+ if (elem == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddAttributeDecl: elem == NULL\n");
+ xmlFreeEnumeration(tree);
+ return(NULL);
+ }
+ /*
+ * 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:
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddAttributeDecl: unknown type %d\n", type);
+ xmlFreeEnumeration(tree);
+ return(NULL);
+ }
+ if ((defaultValue != NULL) &&
+ (!xmlValidateAttributeValue(type, defaultValue))) {
+ VERROR(ctxt->userData, "Attribute %s on %s: invalid default value\n",
+ elem, name, defaultValue);
+ defaultValue = NULL;
+ }
+
+ /*
+ * Create the Attribute table if needed.
+ */
+ table = (xmlAttributeTablePtr) dtd->attributes;
+ if (table == NULL) {
+ table = xmlCreateAttributeTable();
+ dtd->attributes = (void *) table;
+ }
+ if (table == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddAttributeDecl: Table creation failed!\n");
+ return(NULL);
+ }
+
+
+ ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddAttributeDecl: out of memory\n");
+ return(NULL);
+ }
+ memset(ret, 0, sizeof(xmlAttribute));
+ ret->type = XML_ATTRIBUTE_DECL;
+
+ /*
+ * fill the structure.
+ */
+ ret->atype = type;
+ ret->name = xmlStrdup(name);
+ ret->prefix = xmlStrdup(ns);
+ ret->elem = xmlStrdup(elem);
+ ret->def = def;
+ ret->tree = tree;
+ if (defaultValue != NULL)
+ ret->defaultValue = xmlStrdup(defaultValue);
+
+ /*
+ * Validity Check:
+ * Search the DTD for previous declarations of the ATTLIST
+ */
+ if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
+ /*
+ * The attribute is already defined in this Dtd.
+ */
+ VWARNING(ctxt->userData,
+ "Attribute %s on %s: already defined\n",
+ name, elem);
+ xmlFreeAttribute(ret);
+ return(NULL);
+ }
+
+ /*
+ * Validity Check:
+ * Multiple ID per element
+ */
+ elemDef = xmlGetDtdElementDesc(dtd, elem);
+ if (elemDef != NULL) {
+ if ((type == XML_ATTRIBUTE_ID) &&
+ (xmlScanIDAttributeDecl(NULL, elemDef) != 0))
+ VERROR(ctxt->userData,
+ "Element %s has too may ID attributes defined : %s\n",
+ elem, name);
+ ret->nexth = elemDef->attributes;
+ elemDef->attributes = ret;
+ }
+
+ /*
+ * 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;
+ }
+ return(ret);
+}
+
+/**
+ * xmlFreeAttributeTable:
+ * @table: An attribute table
+ *
+ * Deallocate the memory used by an entities hash table.
+ */
+void
+xmlFreeAttributeTable(xmlAttributeTablePtr table) {
+ xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
+}
+
+/**
+ * xmlCopyAttribute:
+ * @attr: An attribute
+ *
+ * Build a copy of an attribute.
+ *
+ * Returns the new xmlAttributePtr or NULL in case of error.
+ */
+xmlAttributePtr
+xmlCopyAttribute(xmlAttributePtr attr) {
+ xmlAttributePtr cur;
+
+ cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
+ if (cur == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlCopyAttribute: out of memory !\n");
+ return(NULL);
+ }
+ memset(cur, 0, sizeof(xmlAttribute));
+ 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->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,
+ (xmlHashCopier) xmlCopyAttribute));
+}
+
+/**
+ * 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) {
+ xmlBufferWriteChar(buf, "<!ATTLIST ");
+ xmlBufferWriteCHAR(buf, attr->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:
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlDumpAttributeTable: internal: unknown type %d\n",
+ attr->atype);
+ }
+ 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:
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlDumpAttributeTable: internal: unknown default %d\n",
+ attr->def);
+ }
+ if (attr->defaultValue != NULL) {
+ xmlBufferWriteChar(buf, " ");
+ xmlBufferWriteQuotedString(buf, attr->defaultValue);
+ }
+ xmlBufferWriteChar(buf, ">\n");
+}
+
+/**
+ * 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) {
+ xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
+}
+
+/************************************************************************
+ * *
+ * NOTATIONs *
+ * *
+ ************************************************************************/
+/**
+ * xmlCreateNotationTable:
+ *
+ * create and initialize an empty notation hash table.
+ *
+ * Returns the xmlNotationTablePtr just created or NULL in case
+ * of error.
+ */
+xmlNotationTablePtr
+xmlCreateNotationTable(void) {
+ return(xmlHashCreate(0));
+}
+
+/**
+ * xmlFreeNotation:
+ * @not: A notation
+ *
+ * Deallocate the memory used by an notation definition
+ */
+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);
+ memset(nota, -1, sizeof(xmlNotation));
+ 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, othervise the entity
+ */
+xmlNotationPtr
+xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
+ const xmlChar *PublicID, const xmlChar *SystemID) {
+ xmlNotationPtr ret;
+ xmlNotationTablePtr table;
+
+ if (dtd == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddNotationDecl: dtd == NULL\n");
+ return(NULL);
+ }
+ if (name == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddNotationDecl: name == NULL\n");
+ return(NULL);
+ }
+ if ((PublicID == NULL) && (SystemID == NULL)) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
+ }
+
+ /*
+ * Create the Notation table if needed.
+ */
+ table = (xmlNotationTablePtr) dtd->notations;
+ if (table == NULL)
+ dtd->notations = table = xmlCreateNotationTable();
+ if (table == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddNotationDecl: Table creation failed!\n");
+ return(NULL);
+ }
+
+ ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddNotationDecl: out of memory\n");
+ 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)) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddNotationDecl: %s already defined\n", name);
+ xmlFreeNotation(ret);
+ return(NULL);
+ }
+ return(ret);
+}
+
+/**
+ * xmlFreeNotationTable:
+ * @table: An notation table
+ *
+ * Deallocate the memory used by an entities hash table.
+ */
+void
+xmlFreeNotationTable(xmlNotationTablePtr table) {
+ xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
+}
+
+/**
+ * xmlCopyNotation:
+ * @nota: A notation
+ *
+ * Build a copy of a notation.
+ *
+ * Returns the new xmlNotationPtr or NULL in case of error.
+ */
+xmlNotationPtr
+xmlCopyNotation(xmlNotationPtr nota) {
+ xmlNotationPtr cur;
+
+ cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
+ if (cur == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlCopyNotation: out of memory !\n");
+ 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,
+ (xmlHashCopier) xmlCopyNotation));
+}
+
+/**
+ * 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) {
+ xmlBufferWriteChar(buf, "<!NOTATION ");
+ xmlBufferWriteCHAR(buf, nota->name);
+ if (nota->PublicID != NULL) {
+ xmlBufferWriteChar(buf, " PUBLIC ");
+ xmlBufferWriteQuotedString(buf, nota->PublicID);
+ if (nota->SystemID != NULL) {
+ xmlBufferWriteChar(buf, " ");
+ xmlBufferWriteCHAR(buf, nota->SystemID);
+ }
+ } else {
+ xmlBufferWriteChar(buf, " SYSTEM ");
+ xmlBufferWriteCHAR(buf, nota->SystemID);
+ }
+ xmlBufferWriteChar(buf, " >\n");
+}
+
+/**
+ * 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) {
+ xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
+}
+
+/************************************************************************
+ * *
+ * IDs *
+ * *
+ ************************************************************************/
+/**
+ * xmlCreateIDTable:
+ *
+ * create and initialize an empty id hash table.
+ *
+ * Returns the xmlIDTablePtr just created or NULL in case
+ * of error.
+ */
+xmlIDTablePtr
+xmlCreateIDTable(void) {
+ return(xmlHashCreate(0));
+}
+
+/**
+ * xmlFreeID:
+ * @not: A id
+ *
+ * Deallocate the memory used by an id definition
+ */
+void
+xmlFreeID(xmlIDPtr id) {
+ if (id == NULL) return;
+ if (id->value != NULL)
+ xmlFree((xmlChar *) id->value);
+ memset(id, -1, sizeof(xmlID));
+ 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, othervise the new xmlIDPtr
+ */
+xmlIDPtr
+xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+ xmlAttrPtr attr) {
+ xmlIDPtr ret;
+ xmlIDTablePtr table;
+
+ if (doc == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddIDDecl: doc == NULL\n");
+ return(NULL);
+ }
+ if (value == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddIDDecl: value == NULL\n");
+ return(NULL);
+ }
+ if (attr == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddIDDecl: attr == NULL\n");
+ return(NULL);
+ }
+
+ /*
+ * Create the ID table if needed.
+ */
+ table = (xmlIDTablePtr) doc->ids;
+ if (table == NULL)
+ doc->ids = table = xmlCreateIDTable();
+ if (table == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddID: Table creation failed!\n");
+ return(NULL);
+ }
+
+ ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddID: out of memory\n");
+ return(NULL);
+ }
+
+ /*
+ * fill the structure.
+ */
+ ret->value = xmlStrdup(value);
+ ret->attr = attr;
+
+ if (xmlHashAddEntry(table, value, ret) < 0) {
+ /*
+ * The id is already defined in this Dtd.
+ */
+ VERROR(ctxt->userData, "ID %s already defined\n", value);
+ xmlFreeID(ret);
+ return(NULL);
+ }
+ return(ret);
+}
+
+/**
+ * xmlFreeIDTable:
+ * @table: An id table
+ *
+ * Deallocate the memory used by an ID hash table.
+ */
+void
+xmlFreeIDTable(xmlIDTablePtr table) {
+ xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
+}
+
+/**
+ * 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 simple, otherwise we use an heuristic: name ID (upper
+ * or lowercase).
+ *
+ * Returns 0 or 1 depending on the lookup result
+ */
+int
+xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
+ if (doc == NULL) return(0);
+ if (attr == NULL) return(0);
+ if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
+ return(0);
+ } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
+ if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
+ (xmlStrEqual(BAD_CAST "name", attr->name)))
+ return(1);
+ 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_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) {
+ xmlAttrPtr cur;
+ xmlIDTablePtr table;
+ xmlChar *ID;
+
+ if (doc == NULL) return(-1);
+ if (attr == NULL) return(-1);
+ table = (xmlIDTablePtr) doc->ids;
+ if (table == NULL)
+ return(-1);
+
+ if (attr == NULL)
+ return(-1);
+ ID = xmlNodeListGetString(doc, attr->children, 1);
+ if (ID == NULL)
+ return(-1);
+ cur = xmlHashLookup(table, ID);
+ if (cur != attr) {
+ xmlFree(ID);
+ return(-1);
+ }
+ xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
+ xmlFree(ID);
+ 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) {
+ xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
+ return(NULL);
+ }
+
+ if (ID == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
+ return(NULL);
+ }
+
+ table = (xmlIDTablePtr) doc->ids;
+ if (table == NULL)
+ return(NULL);
+
+ id = xmlHashLookup(table, ID);
+ if (id == NULL)
+ return(NULL);
+ return(id->attr);
+}
+
+/************************************************************************
+ * *
+ * Refs *
+ * *
+ ************************************************************************/
+typedef struct xmlRemove_t
+{
+ xmlListPtr l;
+ xmlAttrPtr ap;
+} xmlRemove;
+
+/**
+ * xmlCreateRefTable:
+ *
+ * create and initialize an empty ref hash table.
+ *
+ * Returns the xmlRefTablePtr just created or NULL in case
+ * of error.
+ */
+xmlRefTablePtr
+xmlCreateRefTable(void) {
+ return(xmlHashCreate(0));
+}
+
+/**
+ * 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);
+ memset(ref, -1, sizeof(xmlRef));
+ xmlFree(ref);
+}
+
+/**
+ * xmlFreeRefList:
+ * @list_ref: A list of references.
+ *
+ * Deallocate the memory used by a list of references
+ */
+static void
+xmlFreeRefList(xmlListPtr list_ref) {
+ if (list_ref == NULL) return;
+ xmlListDelete(list_ref);
+}
+
+/**
+ * xmlWalkRemoveRef:
+ * @data: Contents of current link
+ * @user: Value supplied by the user
+ *
+ * Return 0 to abort the walk or 1 to continue
+ */
+static int
+xmlWalkRemoveRef(const void *data, const void *user)
+{
+ xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
+ xmlAttrPtr attr1 = ((xmlRemove *)user)->ap;
+ xmlListPtr ref_list = ((xmlRemove *)user)->l;
+
+ if (attr0 == attr1) { /* Matched: remove and terminate walk */
+ xmlListRemoveFirst(ref_list, (void *)data);
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * 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, othervise the new xmlRefPtr
+ */
+xmlRefPtr
+xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
+ xmlAttrPtr attr) {
+ xmlRefPtr ret;
+ xmlRefTablePtr table;
+ xmlListPtr ref_list;
+
+ if (doc == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddRefDecl: doc == NULL\n");
+ return(NULL);
+ }
+ if (value == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddRefDecl: value == NULL\n");
+ return(NULL);
+ }
+ if (attr == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddRefDecl: attr == NULL\n");
+ return(NULL);
+ }
+
+ /*
+ * Create the Ref table if needed.
+ */
+ table = (xmlRefTablePtr) doc->refs;
+ if (table == NULL)
+ doc->refs = table = xmlCreateRefTable();
+ if (table == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddRef: Table creation failed!\n");
+ return(NULL);
+ }
+
+ ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
+ if (ret == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddRef: out of memory\n");
+ return(NULL);
+ }
+
+ /*
+ * fill the structure.
+ */
+ ret->value = xmlStrdup(value);
+ ret->attr = attr;
+
+ /* 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, NULL))) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddRef: Reference list creation failed!\n");
+ return(NULL);
+ }
+ if (xmlHashAddEntry(table, value, ref_list) < 0) {
+ xmlListDelete(ref_list);
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlAddRef: Reference list insertion failed!\n");
+ return(NULL);
+ }
+ }
+ xmlListInsert(ref_list, ret);
+ return(ret);
+}
+
+/**
+ * xmlFreeRefTable:
+ * @table: An ref table
+ *
+ * Deallocate the memory used by an Ref hash table.
+ */
+void
+xmlFreeRefTable(xmlRefTablePtr table) {
+ xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
+}
+
+/**
+ * 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 ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
+ return(0);
+ } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
+ /* TODO @@@ */
+ return(0);
+ } else {
+ xmlAttributePtr attrDecl;
+
+ 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))
+ 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;
+ xmlRemove target;
+
+ if (doc == NULL) return(-1);
+ if (attr == NULL) return(-1);
+ table = (xmlRefTablePtr) doc->refs;
+ if (table == NULL)
+ return(-1);
+
+ if (attr == 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, (xmlHashDeallocator)
+ xmlFreeRefList);
+ 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) {
+ xmlGenericError(xmlGenericErrorContext, "xmlGetRef: doc == NULL\n");
+ return(NULL);
+ }
+
+ if (ID == NULL) {
+ xmlGenericError(xmlGenericErrorContext, "xmlGetRef: ID == NULL\n");
+ 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) return(NULL);
+ if (dtd->elements == NULL) return(NULL);
+ table = (xmlElementTablePtr) dtd->elements;
+
+ uqname = xmlSplitQName2(name, &prefix);
+ if (uqname != NULL) {
+ cur = xmlHashLookup2(table, uqname, prefix);
+ if (prefix != NULL) xmlFree(prefix);
+ if (uqname != NULL) xmlFree(uqname);
+ } else
+ cur = xmlHashLookup2(table, name, NULL);
+ 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));
+}
+
+/**
+ * xmlValidateNotationUse:
+ * @ctxt: the validation context
+ * @doc: the document
+ * @notationName: the notation name to check
+ *
+ * Validate that the given mame 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)) return(-1);
+
+ notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
+ if ((notaDecl == NULL) && (doc->extSubset != NULL))
+ notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
+
+ if (notaDecl == NULL) {
+ VERROR(ctxt->userData, "NOTATION %s is not declared\n",
+ notationName);
+ return(0);
+ }
+ return(1);
+}
+
+/**
+ * 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_ELEMENT:
+ return(0);
+ case XML_ELEMENT_TYPE_EMPTY:
+ /*
+ * return 1 for EMPTY since we want VC error to pop up
+ * on <empty> </empty> for example
+ */
+ case XML_ELEMENT_TYPE_ANY:
+ case XML_ELEMENT_TYPE_MIXED:
+ return(1);
+ }
+ 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) {
+ const xmlChar *cur;
+
+ if (value == NULL) return(0);
+ cur = value;
+
+ if (!IS_LETTER(*cur) && (*cur != '_') &&
+ (*cur != ':')) {
+ return(0);
+ }
+
+ while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
+ (*cur == '.') || (*cur == '-') ||
+ (*cur == '_') || (*cur == ':') ||
+ (IS_COMBINING(*cur)) ||
+ (IS_EXTENDER(*cur)))
+ cur++;
+
+ if (*cur != 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) {
+ const xmlChar *cur;
+
+ if (value == NULL) return(0);
+ cur = value;
+
+ if (!IS_LETTER(*cur) && (*cur != '_') &&
+ (*cur != ':')) {
+ return(0);
+ }
+
+ while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
+ (*cur == '.') || (*cur == '-') ||
+ (*cur == '_') || (*cur == ':') ||
+ (IS_COMBINING(*cur)) ||
+ (IS_EXTENDER(*cur)))
+ cur++;
+
+ while (IS_BLANK(*cur)) {
+ while (IS_BLANK(*cur)) cur++;
+
+ if (!IS_LETTER(*cur) && (*cur != '_') &&
+ (*cur != ':')) {
+ return(0);
+ }
+
+ while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
+ (*cur == '.') || (*cur == '-') ||
+ (*cur == '_') || (*cur == ':') ||
+ (IS_COMBINING(*cur)) ||
+ (IS_EXTENDER(*cur)))
+ cur++;
+ }
+
+ if (*cur != 0) return(0);
+
+ return(1);
+}
+
+/**
+ * xmlValidateNmtokenValue:
+ * @value: an Mntoken value
+ *
+ * Validate that the given value match Nmtoken production
+ *
+ * [ VC: Name Token ]
+ *
+ * returns 1 if valid or 0 otherwise
+ */
+
+int
+xmlValidateNmtokenValue(const xmlChar *value) {
+ const xmlChar *cur;
+
+ if (value == NULL) return(0);
+ cur = value;
+
+ if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
+ (*cur != '.') && (*cur != '-') &&
+ (*cur != '_') && (*cur != ':') &&
+ (!IS_COMBINING(*cur)) &&
+ (!IS_EXTENDER(*cur)))
+ return(0);
+
+ while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
+ (*cur == '.') || (*cur == '-') ||
+ (*cur == '_') || (*cur == ':') ||
+ (IS_COMBINING(*cur)) ||
+ (IS_EXTENDER(*cur)))
+ cur++;
+
+ if (*cur != 0) return(0);
+
+ return(1);
+}
+
+/**
+ * xmlValidateNmtokensValue:
+ * @value: an Mntokens value
+ *
+ * Validate that the given value match Nmtokens production
+ *
+ * [ VC: Name Token ]
+ *
+ * returns 1 if valid or 0 otherwise
+ */
+
+int
+xmlValidateNmtokensValue(const xmlChar *value) {
+ const xmlChar *cur;
+
+ if (value == NULL) return(0);
+ cur = value;
+
+ while (IS_BLANK(*cur)) cur++;
+ if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
+ (*cur != '.') && (*cur != '-') &&
+ (*cur != '_') && (*cur != ':') &&
+ (!IS_COMBINING(*cur)) &&
+ (!IS_EXTENDER(*cur)))
+ return(0);
+
+ while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
+ (*cur == '.') || (*cur == '-') ||
+ (*cur == '_') || (*cur == ':') ||
+ (IS_COMBINING(*cur)) ||
+ (IS_EXTENDER(*cur)))
+ cur++;
+
+ while (IS_BLANK(*cur)) {
+ while (IS_BLANK(*cur)) cur++;
+ if (*cur == 0) return(1);
+
+ if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
+ (*cur != '.') && (*cur != '-') &&
+ (*cur != '_') && (*cur != ':') &&
+ (!IS_COMBINING(*cur)) &&
+ (!IS_EXTENDER(*cur)))
+ return(0);
+
+ while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
+ (*cur == '.') || (*cur == '-') ||
+ (*cur == '_') || (*cur == ':') ||
+ (IS_COMBINING(*cur)) ||
+ (IS_EXTENDER(*cur)))
+ cur++;
+ }
+
+ if (*cur != 0) return(0);
+
+ return(1);
+}
+
+/**
+ * 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 constraing exist on notation declarations
+ * But this function get called anyway ...
+ *
+ * returns 1 if valid or 0 otherwise
+ */
+
+int
+xmlValidateNotationDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
+ xmlNotationPtr nota) {
+ int ret = 1;
+
+ return(ret);
+}
+
+/**
+ * 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) {
+ switch (type) {
+ case XML_ATTRIBUTE_ENTITIES:
+ case XML_ATTRIBUTE_IDREFS:
+ return(xmlValidateNamesValue(value));
+ case XML_ATTRIBUTE_ENTITY:
+ case XML_ATTRIBUTE_IDREF:
+ case XML_ATTRIBUTE_ID:
+ case XML_ATTRIBUTE_NOTATION:
+ return(xmlValidateNameValue(value));
+ case XML_ATTRIBUTE_NMTOKENS:
+ case XML_ATTRIBUTE_ENUMERATION:
+ return(xmlValidateNmtokensValue(value));
+ case XML_ATTRIBUTE_NMTOKEN:
+ return(xmlValidateNmtokenValue(value));
+ case XML_ATTRIBUTE_CDATA:
+ break;
+ }
+ return(1);
+}
+
+/**
+ * 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
+ */
+
+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);
+ if (ent == NULL) {
+ VERROR(ctxt->userData,
+ "ENTITY attribute %s reference an unknown entity \"%s\"\n",
+ name, value);
+ ret = 0;
+ } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
+ VERROR(ctxt->userData,
+ "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
+ name, value);
+ 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(*cur))) cur++;
+ save = *cur;
+ *cur = 0;
+ ent = xmlGetDocEntity(doc, nam);
+ if (ent == NULL) {
+ VERROR(ctxt->userData,
+ "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
+ name, nam);
+ ret = 0;
+ } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
+ VERROR(ctxt->userData,
+ "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
+ name, nam);
+ ret = 0;
+ }
+ if (save == 0)
+ break;
+ *cur = save;
+ while (IS_BLANK(*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) {
+ VERROR(ctxt->userData,
+ "NOTATION attribute %s reference an unknown notation \"%s\"\n",
+ name, value);
+ ret = 0;
+ }
+ break;
+ }
+ }
+ 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 qname[500];
+#ifdef HAVE_SNPRINTF
+ snprintf((char *) qname, sizeof(qname), "%s:%s",
+ elem->ns->prefix, elem->name);
+#else
+ sprintf((char *) qname, "%s:%s", elem->ns->prefix, elem->name);
+#endif
+ qname[sizeof(qname) - 1] = 0;
+ attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
+ if ((attrDecl == NULL) && (doc->extSubset != NULL))
+ attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
+ }
+ 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);
+}
+
+void
+xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
+ const xmlChar* name) {
+ 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 = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
+ if (val == 0) {
+ VERROR(ctxt->userData,
+ "Syntax of default value for attribute %s on %s is not valid\n",
+ attr->name, attr->elem);
+ }
+ ret &= val;
+ }
+
+ /* ID Attribute Default */
+ if ((attr->atype == XML_ATTRIBUTE_ID)&&
+ (attr->def != XML_ATTRIBUTE_IMPLIED) &&
+ (attr->def != XML_ATTRIBUTE_REQUIRED)) {
+ VERROR(ctxt->userData,
+ "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
+ attr->name, attr->elem);
+ ret = 0;
+ }
+
+ /* One ID per Element Type */
+ if (attr->atype == XML_ATTRIBUTE_ID) {
+ int nbId;
+
+ /* the trick is taht we parse DtD as their own internal subset */
+ xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
+ attr->elem);
+ if (elem != NULL) {
+ nbId = xmlScanIDAttributeDecl(NULL, elem);
+ } else {
+ xmlAttributeTablePtr table;
+
+ /*
+ * The attribute may be declared in the internal subset and the
+ * element in the external subset.
+ */
+ nbId = 0;
+ table = (xmlAttributeTablePtr) doc->intSubset->attributes;
+ xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
+ xmlValidateAttributeIdCallback, &nbId);
+ }
+ if (nbId > 1) {
+ VERROR(ctxt->userData,
+ "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);
+ }
+ if (extId > 1) {
+ VERROR(ctxt->userData,
+ "Element %s has %d ID attribute defined in the external subset : %s\n",
+ attr->elem, extId, attr->name);
+ } else if (extId + nbId > 1) {
+ VERROR(ctxt->userData,
+"Element %s has ID attributes defined in the internal and external subset : %s\n",
+ attr->elem, attr->name);
+ }
+ }
+ }
+
+ /* 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) {
+ VERROR(ctxt->userData,
+"Default value \"%s\" for attribute %s on %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);
+
+ /* 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)) {
+ VERROR(ctxt->userData,
+ "Definition of %s has duplicate references of %s\n",
+ elem->name, name);
+ ret = 0;
+ }
+ break;
+ }
+ if (next->c1 == NULL) break;
+ if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
+ if (xmlStrEqual(next->c1->name, name)) {
+ VERROR(ctxt->userData,
+ "Definition of %s has duplicate references of %s\n",
+ elem->name, 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)) {
+ VERROR(ctxt->userData, "Redefinition of element %s\n",
+ elem->name);
+ ret = 0;
+ }
+ tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
+ if ((tst != NULL ) && (tst != elem)) {
+ VERROR(ctxt->userData, "Redefinition of element %s\n",
+ elem->name);
+ ret = 0;
+ }
+
+ /* One ID per Element Type */
+ 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) {
+ /* xmlElementPtr elemDecl; */
+ 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 qname[500];
+#ifdef HAVE_SNPRINTF
+ snprintf((char *) qname, sizeof(qname), "%s:%s",
+ elem->ns->prefix, elem->name);
+#else
+ sprintf((char *) qname, "%s:%s", elem->ns->prefix, elem->name);
+#endif
+ qname[sizeof(qname) - 1] = 0;
+ if (attr->ns != NULL) {
+ attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
+ attr->name, attr->ns->prefix);
+ if ((attrDecl == NULL) && (doc->extSubset != NULL))
+ attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
+ attr->name, attr->ns->prefix);
+ } else {
+ attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
+ if ((attrDecl == NULL) && (doc->extSubset != NULL))
+ attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
+ qname, attr->name);
+ }
+ }
+ 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) {
+ VERROR(ctxt->userData,
+ "No declaration for attribute %s on element %s\n",
+ attr->name, elem->name);
+ return(0);
+ }
+ attr->atype = attrDecl->atype;
+
+ val = xmlValidateAttributeValue(attrDecl->atype, value);
+ if (val == 0) {
+ VERROR(ctxt->userData,
+ "Syntax of value for attribute %s on %s is not valid\n",
+ attr->name, elem->name);
+ ret = 0;
+ }
+
+ /* Validity constraint: Fixed Attribute Default */
+ if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
+ if (!xmlStrEqual(value, attrDecl->defaultValue)) {
+ VERROR(ctxt->userData,
+ "Value for attribute %s on %s is differnt 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) {
+ VERROR(ctxt->userData,
+ "Value \"%s\" for attribute %s on %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) {
+ VERROR(ctxt->userData,
+"Value \"%s\" for attribute %s on %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) {
+ VERROR(ctxt->userData,
+ "Value \"%s\" for attribute %s on %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))) {
+ VERROR(ctxt->userData,
+ "Value for attribute %s on %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);
+}
+
+/* Find the next XML_ELEMENT_NODE, subject to the content constraints.
+ * Return -1 if we found something unexpected, or 1 otherwise.
+ */
+
+static int
+xmlValidateFindNextElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
+ xmlElementContentPtr cont)
+{
+ while (*child && (*child)->type != XML_ELEMENT_NODE) {
+ switch ((*child)->type) {
+ /*
+ * If there is an entity declared and it's not empty
+ * Push the current node on the stack and process with the
+ * entity content.
+ */
+ case XML_ENTITY_REF_NODE:
+ if (((*child)->children != NULL) &&
+ ((*child)->children->children != NULL)) {
+ nodeVPush(ctxt, *child);
+ *child = (*child)->children->children;
+ continue;
+ }
+ break;
+
+ /* These things are ignored (skipped) during validation. */
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ case XML_XINCLUDE_START:
+ case XML_XINCLUDE_END:
+ break;
+
+ case XML_TEXT_NODE:
+ if (xmlIsBlankNode(*child)
+ && (cont->type == XML_ELEMENT_CONTENT_ELEMENT
+ || cont->type == XML_ELEMENT_CONTENT_SEQ
+ || cont->type == XML_ELEMENT_CONTENT_OR))
+ break;
+ return -1;
+
+ default:
+ return -1;
+ }
+ *child = (*child)->next;
+ }
+
+ return 1;
+}
+
+int xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
+ xmlElementContentPtr cont);
+
+/**
+ * xmlValidateElementTypeExpr:
+ * @ctxt: the validation context
+ * @child: pointer to the child list
+ * @cont: pointer to the content declaration
+ *
+ * Try to validate the content of an element of type element
+ * but don't handle the occurence factor
+ *
+ * returns 1 if valid or 0 and -1 if PCDATA stuff is found,
+ * also update child value in-situ.
+ */
+
+int
+xmlValidateElementTypeExpr(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
+ xmlElementContentPtr cont) {
+ xmlNodePtr cur;
+ int ret = 1;
+
+ if (cont == NULL) return(-1);
+ DEBUG_VALID_STATE(*child, cont)
+ ret = xmlValidateFindNextElement(ctxt, child, cont);
+ if (ret < 0)
+ return(-1);
+ DEBUG_VALID_STATE(*child, cont)
+ switch (cont->type) {
+ case XML_ELEMENT_CONTENT_PCDATA:
+ if (*child == NULL) return(0);
+ if ((*child)->type == XML_TEXT_NODE) return(1);
+ return(0);
+ case XML_ELEMENT_CONTENT_ELEMENT:
+ if (*child == NULL) return(0);
+ ret = (xmlStrEqual((*child)->name, cont->name));
+ if (ret == 1) {
+ while ((*child)->next == NULL) {
+ if (((*child)->parent != NULL) &&
+ ((*child)->parent->type == XML_ENTITY_DECL)) {
+ *child = nodeVPop(ctxt);
+ } else
+ break;
+ }
+ *child = (*child)->next;
+ }
+ return(ret);
+ case XML_ELEMENT_CONTENT_OR:
+ cur = *child;
+ ret = xmlValidateElementTypeElement(ctxt, child, cont->c1);
+ if (ret == -1) return(-1);
+ if (ret == 1) {
+ return(1);
+ }
+ /* rollback and retry the other path */
+ *child = cur;
+ ret = xmlValidateElementTypeElement(ctxt, child, cont->c2);
+ if (ret == -1) return(-1);
+ if (ret == 0) {
+ *child = cur;
+ return(0);
+ }
+ return(1);
+ case XML_ELEMENT_CONTENT_SEQ:
+ cur = *child;
+ ret = xmlValidateElementTypeElement(ctxt, child, cont->c1);
+ if (ret == -1) return(-1);
+ if (ret == 0) {
+ *child = cur;
+ return(0);
+ }
+ ret = xmlValidateElementTypeElement(ctxt, child, cont->c2);
+ if (ret == -1) return(-1);
+ if (ret == 0) {
+ *child = cur;
+ return(0);
+ }
+ return(1);
+ }
+ return(ret);
+}
+
+/**
+ * xmlValidateElementTypeElement:
+ * @ctxt: the validation context
+ * @child: pointer to the child list
+ * @cont: pointer to the content declaration
+ *
+ * Try to validate the content of an element of type element
+ * yeah, Yet Another Regexp Implementation, and recursive
+ *
+ * returns 1 if valid or 0 and -1 if PCDATA stuff is found,
+ * also update child and content values in-situ.
+ */
+
+int
+xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
+ xmlElementContentPtr cont) {
+ xmlNodePtr cur;
+ int ret;
+
+ if (cont == NULL) return(-1);
+
+ DEBUG_VALID_STATE(*child, cont)
+ ret = xmlValidateFindNextElement(ctxt, child, cont);
+ if (ret < 0)
+ return(-1);
+ DEBUG_VALID_STATE(*child, cont)
+ cur = *child;
+ ret = xmlValidateElementTypeExpr(ctxt, child, cont);
+ if (ret == -1) return(-1);
+ switch (cont->ocur) {
+ case XML_ELEMENT_CONTENT_ONCE:
+ if (ret == 1) {
+ /* skip ignorable elems */
+ while ((*child != NULL) &&
+ ((*child)->type == XML_PI_NODE
+ || (*child)->type == XML_COMMENT_NODE
+ || (*child)->type == XML_XINCLUDE_START
+ || (*child)->type == XML_XINCLUDE_END)) {
+ while ((*child)->next == NULL) {
+ if (((*child)->parent != NULL) &&
+ ((*child)->parent->type == XML_ENTITY_REF_NODE)) {
+ *child = (*child)->parent;
+ } else
+ break;
+ }
+ *child = (*child)->next;
+ }
+ return(1);
+ }
+ *child = cur;
+ return(0);
+ case XML_ELEMENT_CONTENT_OPT:
+ if (ret == 0) {
+ *child = cur;
+ return(1);
+ }
+ break;
+ case XML_ELEMENT_CONTENT_MULT:
+ if (ret == 0) {
+ *child = cur;
+ break;
+ }
+ /* no break on purpose */
+ case XML_ELEMENT_CONTENT_PLUS:
+ if (ret == 0) {
+ *child = cur;
+ return(0);
+ }
+ if (ret == -1) return(-1);
+ cur = *child;
+ do {
+ if (*child == NULL)
+ break; /* while */
+ if ((*child)->type == XML_TEXT_NODE
+ && xmlIsBlankNode(*child)) {
+ *child = (*child)->next;
+ continue;
+ }
+ ret = xmlValidateElementTypeExpr(ctxt, child, cont);
+ if (ret == 1)
+ cur = *child;
+ } while (ret == 1);
+ if (ret == -1) return(-1);
+ *child = cur;
+ break;
+ }
+
+ return xmlValidateFindNextElement(ctxt, child, cont);
+}
+
+/**
+ * xmlSprintfElementChilds:
+ * @buf: an output buffer
+ * @content: An element
+ * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
+ *
+ * This will dump the list of childs to the buffer
+ * Intended just for the debug routine
+ */
+void
+xmlSprintfElementChilds(char *buf, xmlNodePtr node, int glob) {
+ xmlNodePtr cur;
+
+ if (node == NULL) return;
+ if (glob) strcat(buf, "(");
+ cur = node->children;
+ while (cur != NULL) {
+ switch (cur->type) {
+ case XML_ELEMENT_NODE:
+ strcat(buf, (char *) cur->name);
+ if (cur->next != NULL)
+ strcat(buf, " ");
+ break;
+ case XML_TEXT_NODE:
+ if (xmlIsBlankNode(cur))
+ break;
+ 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_SGML_ENABLED
+ case XML_SGML_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, ")");
+}
+
+
+/**
+ * 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;
+ const xmlChar *name;
+
+ CHECK_DTD;
+
+ if (elem == NULL) return(0);
+ if (elem->type == XML_TEXT_NODE) {
+ }
+ switch (elem->type) {
+ case XML_ATTRIBUTE_NODE:
+ VERROR(ctxt->userData,
+ "Attribute element not expected here\n");
+ return(0);
+ case XML_TEXT_NODE:
+ if (elem->children != NULL) {
+ VERROR(ctxt->userData, "Text element has childs !\n");
+ return(0);
+ }
+ if (elem->properties != NULL) {
+ VERROR(ctxt->userData, "Text element has attributes !\n");
+ return(0);
+ }
+ if (elem->ns != NULL) {
+ VERROR(ctxt->userData, "Text element has namespace !\n");
+ return(0);
+ }
+ if (elem->nsDef != NULL) {
+ VERROR(ctxt->userData,
+ "Text element carries namespace definitions !\n");
+ return(0);
+ }
+ if (elem->content == NULL) {
+ VERROR(ctxt->userData,
+ "Text element has no content !\n");
+ 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:
+ VERROR(ctxt->userData,
+ "Entity element not expected here\n");
+ return(0);
+ case XML_NOTATION_NODE:
+ VERROR(ctxt->userData,
+ "Notation element not expected here\n");
+ return(0);
+ case XML_DOCUMENT_NODE:
+ case XML_DOCUMENT_TYPE_NODE:
+ case XML_DOCUMENT_FRAG_NODE:
+ VERROR(ctxt->userData,
+ "Document element not expected here\n");
+ return(0);
+ case XML_HTML_DOCUMENT_NODE:
+ VERROR(ctxt->userData,
+ "\n");
+ return(0);
+ case XML_ELEMENT_NODE:
+ break;
+ default:
+ VERROR(ctxt->userData,
+ "unknown element type %d\n", elem->type);
+ return(0);
+ }
+ if (elem->name == NULL) return(0);
+
+ /*
+ * Fetch the declaration for the qualified name
+ */
+ if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
+ elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
+ elem->name, elem->ns->prefix);
+ if ((elemDecl == NULL) && (doc->extSubset != NULL))
+ elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
+ elem->name, elem->ns->prefix);
+ }
+
+ /*
+ * Fetch the declaration for the non qualified name
+ */
+ if (elemDecl == NULL) {
+ elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
+ if ((elemDecl == NULL) && (doc->extSubset != NULL))
+ elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
+ }
+ if (elemDecl == NULL) {
+ VERROR(ctxt->userData, "No declaration for element %s\n",
+ elem->name);
+ return(0);
+ }
+
+ /* Check taht the element content matches the definition */
+ switch (elemDecl->etype) {
+ case XML_ELEMENT_TYPE_EMPTY:
+ if (elem->children != NULL) {
+ VERROR(ctxt->userData,
+ "Element %s was declared EMPTY this one has content\n",
+ elem->name);
+ ret = 0;
+ }
+ break;
+ case XML_ELEMENT_TYPE_ANY:
+ /* I don't think anything is required then */
+ break;
+ case XML_ELEMENT_TYPE_MIXED:
+ /* Hum, this start to get messy */
+ child = elem->children;
+ while (child != NULL) {
+ if (child->type == XML_ELEMENT_NODE) {
+ name = child->name;
+ if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
+ xmlChar qname[500];
+#ifdef HAVE_SNPRINTF
+ snprintf((char *) qname, sizeof(qname), "%s:%s",
+ child->ns->prefix, child->name);
+#else
+ sprintf((char *) qname, "%s:%s",
+ child->ns->prefix, child->name);
+#endif
+ qname[sizeof(qname) - 1] = 0;
+ cont = elemDecl->content;
+ while (cont != NULL) {
+ if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
+ if (xmlStrEqual(cont->name, qname)) break;
+ } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
+ (cont->c1 != NULL) &&
+ (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
+ if (xmlStrEqual(cont->c1->name, qname)) break;
+ } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
+ (cont->c1 == NULL) ||
+ (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
+ /* Internal error !!! */
+ xmlGenericError(xmlGenericErrorContext,
+ "Internal: MIXED struct bad\n");
+ break;
+ }
+ cont = cont->c2;
+ }
+ 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)) {
+ /* Internal error !!! */
+ xmlGenericError(xmlGenericErrorContext,
+ "Internal: MIXED struct bad\n");
+ break;
+ }
+ cont = cont->c2;
+ }
+ if (cont == NULL) {
+ VERROR(ctxt->userData,
+ "Element %s is not declared in %s list of possible childs\n",
+ name, elem->name);
+ ret = 0;
+ }
+ }
+child_ok:
+ child = child->next;
+ }
+ break;
+ case XML_ELEMENT_TYPE_ELEMENT:
+ child = elem->children;
+ cont = elemDecl->content;
+ ret = xmlValidateElementTypeElement(ctxt, &child, cont);
+ while ((child != NULL) && (child->type == XML_TEXT_NODE) &&
+ (xmlIsBlankNode(child))) {
+ child = child->next;
+ continue;
+ }
+ if ((ret == 0) || (child != NULL)) {
+ char expr[1000];
+ char list[2000];
+
+ expr[0] = 0;
+ xmlSprintfElementContent(expr, cont, 1);
+ list[0] = 0;
+ xmlSprintfElementChilds(list, elem, 1);
+
+ VERROR(ctxt->userData,
+ "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
+ elem->name, expr, list);
+ ret = 0;
+ }
+ break;
+ }
+
+ /* [ VC: Required Attribute ] */
+ attr = elemDecl->attributes;
+ while (attr != NULL) {
+ if (attr->def == XML_ATTRIBUTE_REQUIRED) {
+ xmlAttrPtr attrib;
+ int qualified = -1;
+
+ 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) {
+ VERROR(ctxt->userData,
+ "Element %s doesn't carry attribute %s\n",
+ elem->name, attr->name);
+ ret = 0;
+ } else {
+ VERROR(ctxt->userData,
+ "Element %s doesn't carry attribute %s:%s\n",
+ elem->name, attr->prefix,attr->name);
+ ret = 0;
+ }
+ } else if (qualified == 0) {
+ VWARNING(ctxt->userData,
+ "Element %s required attribute %s:%s has no prefix\n",
+ elem->name, attr->prefix,attr->name);
+ } else if (qualified == 1) {
+ VWARNING(ctxt->userData,
+ "Element %s required attribute %s:%s has different prefix\n",
+ elem->name, attr->prefix,attr->name);
+ }
+ }
+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;
+ if (doc == NULL) return(0);
+
+ root = xmlDocGetRootElement(doc);
+ if ((root == NULL) || (root->name == NULL)) {
+ VERROR(ctxt->userData, "Not valid: no root element\n");
+ 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 qname[500];
+#ifdef HAVE_SNPRINTF
+ snprintf((char *) qname, sizeof(qname), "%s:%s",
+ root->ns->prefix, root->name);
+#else
+ sprintf((char *) qname, "%s:%s", root->ns->prefix, root->name);
+#endif
+ qname[sizeof(qname) - 1] = 0;
+ if (xmlStrEqual(doc->intSubset->name, qname))
+ goto name_ok;
+ }
+ if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
+ (xmlStrEqual(root->name, BAD_CAST "html")))
+ goto name_ok;
+ VERROR(ctxt->userData,
+ "Not valid: root and DtD name do not match '%s' and '%s'\n",
+ root->name, doc->intSubset->name);
+ 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;
+ 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))
+ return(1);
+
+ CHECK_DTD;
+
+ ret &= xmlValidateOneElement(ctxt, doc, elem);
+ attr = elem->properties;
+ while(attr != NULL) {
+ value = xmlNodeListGetString(doc, attr->children, 0);
+ ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
+ if (value != NULL)
+ xmlFree(value);
+ attr= attr->next;
+ }
+ child = elem->children;
+ while (child != NULL) {
+ ret &= xmlValidateElement(ctxt, doc, child);
+ child = child->next;
+ }
+
+ return(ret);
+}
+
+
+void
+xmlValidateCheckRefCallback(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
+ const xmlChar *name) {
+ xmlAttrPtr id;
+ xmlAttrPtr attr;
+
+ if (ref == NULL)
+ return;
+ attr = ref->attr;
+ if (attr == NULL)
+ return;
+ if (attr->atype == XML_ATTRIBUTE_IDREF) {
+ id = xmlGetID(ctxt->doc, name);
+ if (id == NULL) {
+ VERROR(ctxt->userData,
+ "IDREF attribute %s reference an unknown ID \"%s\"\n",
+ attr->name, name);
+ ctxt->valid = 0;
+ }
+ } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
+ 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(*cur))) cur++;
+ save = *cur;
+ *cur = 0;
+ id = xmlGetID(ctxt->doc, str);
+ if (id == NULL) {
+ VERROR(ctxt->userData,
+ "IDREFS attribute %s reference an unknown ID \"%s\"\n",
+ attr->name, str);
+ ctxt->valid = 0;
+ }
+ if (save == 0)
+ break;
+ *cur = save;
+ while (IS_BLANK(*cur)) cur++;
+ }
+ xmlFree(dup);
+ }
+}
+
+/**
+ * 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
+ *
+ *
+ * returns 1 if valid or 0 otherwise
+ */
+
+int
+xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
+ xmlRefTablePtr table;
+
+ if (doc == NULL) {
+ xmlGenericError(xmlGenericErrorContext,
+ "xmlValidateDocumentFinal: doc == NULL\n");
+ return(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, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
+ 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.
+ *
+ * returns 1 if valid or 0 otherwise
+ */
+
+int
+xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
+ int ret;
+ xmlDtdPtr oldExt;
+ xmlNodePtr root;
+
+ if (dtd == NULL) return(0);
+ if (doc == NULL) return(0);
+ oldExt = doc->extSubset;
+ doc->extSubset = dtd;
+ ret = xmlValidateRoot(ctxt, doc);
+ if (ret == 0) {
+ doc->extSubset = oldExt;
+ 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;
+ return(ret);
+}
+
+void
+xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
+ const xmlChar *name) {
+ 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) {
+ ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
+ cur->name, cur->atype, cur->defaultValue);
+ }
+ if (cur->tree != NULL) {
+ xmlEnumerationPtr tree = cur->tree;
+ while (tree != NULL) {
+ ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
+ cur->name, cur->atype, tree->name);
+ tree = tree->next;
+ }
+ }
+ }
+}
+
+/**
+ * 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 otherwise
+ */
+
+int
+xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
+ int ret = 1;
+ xmlDtdPtr dtd;
+ xmlAttributeTablePtr table;
+
+ if (doc == NULL) return(0);
+ if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
+ return(0);
+ ctxt->doc = doc;
+ ctxt->valid = ret;
+ dtd = doc->intSubset;
+ if ((dtd != NULL) && (dtd->attributes != NULL)) {
+ table = (xmlAttributeTablePtr) dtd->attributes;
+ xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
+ }
+ dtd = doc->extSubset;
+ if ((dtd != NULL) && (dtd->attributes != NULL)) {
+ table = (xmlAttributeTablePtr) dtd->attributes;
+ xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, 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->intSubset == NULL) && (doc->extSubset == NULL))
+ return(0);
+ if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
+ (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
+ doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
+ doc->intSubset->SystemID);
+ if (doc->extSubset == NULL) {
+ if (doc->intSubset->SystemID != NULL) {
+ VERROR(ctxt->userData,
+ "Could not load the external subset \"%s\"\n",
+ doc->intSubset->SystemID);
+ } else {
+ VERROR(ctxt->userData,
+ "Could not load the external subset \"%s\"\n",
+ 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
+ * @list: 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 **list,
+ int *len, int max) {
+ int i;
+
+ if ((ctree == NULL) || (list == 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", list[i])) return(*len);
+ list[(*len)++] = BAD_CAST "#PCDATA";
+ break;
+ case XML_ELEMENT_CONTENT_ELEMENT:
+ for (i = 0; i < *len;i++)
+ if (xmlStrEqual(ctree->name, list[i])) return(*len);
+ list[(*len)++] = ctree->name;
+ break;
+ case XML_ELEMENT_CONTENT_SEQ:
+ xmlValidGetPotentialChildren(ctree->c1, list, len, max);
+ xmlValidGetPotentialChildren(ctree->c2, list, len, max);
+ break;
+ case XML_ELEMENT_CONTENT_OR:
+ xmlValidGetPotentialChildren(ctree->c1, list, len, max);
+ xmlValidGetPotentialChildren(ctree->c2, list, len, max);
+ break;
+ }
+
+ return(*len);
+}
+
+/**
+ * xmlValidGetValidElements:
+ * @prev: an element to insert after
+ * @next: an element to insert next
+ * @list: 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 **list,
+ int max) {
+ int nb_valid_elements = 0;
+ const xmlChar *elements[256];
+ int nb_elements = 0, i;
+
+ 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 (list == NULL) return(-1);
+ if (max <= 0) return(-1);
+
+ 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 = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
+ test_node->doc = ref_node->doc;
+ test_node->parent = parent;
+ test_node->prev = prev;
+ test_node->next = next;
+
+ 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(NULL, parent->doc, parent)) {
+ int j;
+
+ for (j = 0; j < nb_valid_elements;j++)
+ if (xmlStrEqual(elements[i], list[j])) break;
+ list[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;
+
+ return(nb_valid_elements);
+}