mirror of
https://gitlab.gnome.org/GNOME/libxml2
synced 2025-03-28 21:33:13 +00:00

The best way to solve the I18N problen is unfortunately to fix libxml1 parser to at least deal correctly with UTF8 and ISO-Latin-1 encodings. I have plugged in the 2.3.5 (or what will be it's not released yet) core XML parser in the libxml1 framework. No changes API wise. The only changes will be backward binary compatible extensions of some of the parser structures and the number of parser states. - configure.in: version will be 1.8.12 - Makefile.am: added .memdump tests - SAX.c entities.[ch] parser.[ch] tree.c valid.c xml-error.h xmlIO.[ch]: plugged the 2.3.5 libxml2 XML parser in, while preserving binary compatibility - uri.[ch]: the parser code really requires URI manipulation add this from 2.3.5 too - results/* : of course this changed the output of a number of tests - test/dtd12 : this test was actually not wellformed, the new parser pukes at it, fixed ... Daniel
3401 lines
86 KiB
C
3401 lines
86 KiB
C
/*
|
|
* 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 "xmlmemory.h"
|
|
#include "valid.h"
|
|
#include "parser.h"
|
|
#include "parserInternals.h"
|
|
|
|
/* 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) return(0)
|
|
|
|
xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
|
|
xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
|
|
|
|
/****************************************************************
|
|
* *
|
|
* 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) {
|
|
fprintf(stderr, "xmlNewElementContent : name == NULL !\n");
|
|
}
|
|
break;
|
|
case XML_ELEMENT_CONTENT_PCDATA:
|
|
case XML_ELEMENT_CONTENT_SEQ:
|
|
case XML_ELEMENT_CONTENT_OR:
|
|
if (name != NULL) {
|
|
fprintf(stderr, "xmlNewElementContent : name != NULL !\n");
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "xmlNewElementContent: unknown type %d\n", type);
|
|
return(NULL);
|
|
}
|
|
ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "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) {
|
|
fprintf(stderr, "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;
|
|
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:
|
|
fprintf(stderr, "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) {
|
|
xmlElementTablePtr ret;
|
|
|
|
ret = (xmlElementTablePtr)
|
|
xmlMalloc(sizeof(xmlElementTable));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCreateElementTable : xmlMalloc(%ld) failed\n",
|
|
(long)sizeof(xmlElementTable));
|
|
return(NULL);
|
|
}
|
|
ret->max_elements = XML_MIN_ELEMENT_TABLE;
|
|
ret->nb_elements = 0;
|
|
ret->table = (xmlElementPtr *)
|
|
xmlMalloc(ret->max_elements * sizeof(xmlElementPtr));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCreateElementTable : xmlMalloc(%ld) failed\n",
|
|
ret->max_elements * (long)sizeof(xmlElement));
|
|
xmlFree(ret);
|
|
return(NULL);
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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, cur;
|
|
xmlElementTablePtr table;
|
|
int i;
|
|
|
|
if (dtd == NULL) {
|
|
fprintf(stderr, "xmlAddElementDecl: dtd == NULL\n");
|
|
return(NULL);
|
|
}
|
|
if (name == NULL) {
|
|
fprintf(stderr, "xmlAddElementDecl: name == NULL\n");
|
|
return(NULL);
|
|
}
|
|
switch (type) {
|
|
case XML_ELEMENT_TYPE_EMPTY:
|
|
if (content != NULL) {
|
|
fprintf(stderr,
|
|
"xmlAddElementDecl: content != NULL for EMPTY\n");
|
|
return(NULL);
|
|
}
|
|
break;
|
|
case XML_ELEMENT_TYPE_ANY:
|
|
if (content != NULL) {
|
|
fprintf(stderr,
|
|
"xmlAddElementDecl: content != NULL for ANY\n");
|
|
return(NULL);
|
|
}
|
|
break;
|
|
case XML_ELEMENT_TYPE_MIXED:
|
|
if (content == NULL) {
|
|
fprintf(stderr,
|
|
"xmlAddElementDecl: content == NULL for MIXED\n");
|
|
return(NULL);
|
|
}
|
|
break;
|
|
case XML_ELEMENT_TYPE_ELEMENT:
|
|
if (content == NULL) {
|
|
fprintf(stderr,
|
|
"xmlAddElementDecl: content == NULL for ELEMENT\n");
|
|
return(NULL);
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "xmlAddElementDecl: unknown type %d\n", type);
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Create the Element table if needed.
|
|
*/
|
|
table = dtd->elements;
|
|
if (table == NULL)
|
|
table = dtd->elements = xmlCreateElementTable();
|
|
if (table == NULL) {
|
|
fprintf(stderr, "xmlAddElementDecl: Table creation failed!\n");
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Validity Check:
|
|
* Search the DTD for previous declarations of the ELEMENT
|
|
*/
|
|
for (i = 0;i < table->nb_elements;i++) {
|
|
cur = table->table[i];
|
|
if (!xmlStrcmp(cur->name, name)) {
|
|
/*
|
|
* The element is already defined in this Dtd.
|
|
*/
|
|
VERROR(ctxt->userData, "Redefinition of element %s\n", name);
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Grow the table, if needed.
|
|
*/
|
|
if (table->nb_elements >= table->max_elements) {
|
|
/*
|
|
* need more elements.
|
|
*/
|
|
table->max_elements *= 2;
|
|
table->table = (xmlElementPtr *)
|
|
xmlRealloc(table->table, table->max_elements * sizeof(xmlElementPtr));
|
|
if (table->table == NULL) {
|
|
fprintf(stderr, "xmlAddElementDecl: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
}
|
|
ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlAddElementDecl: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
table->table[table->nb_elements] = ret;
|
|
|
|
/*
|
|
* fill the structure.
|
|
*/
|
|
ret->type = type;
|
|
ret->name = xmlStrdup(name);
|
|
ret->content = xmlCopyElementContent(content);
|
|
ret->attributes = xmlScanAttributeDecl(dtd, name);
|
|
table->nb_elements++;
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlFreeElement:
|
|
* @elem: An element
|
|
*
|
|
* Deallocate the memory used by an element definition
|
|
*/
|
|
void
|
|
xmlFreeElement(xmlElementPtr elem) {
|
|
if (elem == NULL) return;
|
|
xmlFreeElementContent(elem->content);
|
|
if (elem->name != NULL)
|
|
xmlFree((xmlChar *) elem->name);
|
|
memset(elem, -1, sizeof(xmlElement));
|
|
xmlFree(elem);
|
|
}
|
|
|
|
/**
|
|
* xmlFreeElementTable:
|
|
* @table: An element table
|
|
*
|
|
* Deallocate the memory used by an element hash table.
|
|
*/
|
|
void
|
|
xmlFreeElementTable(xmlElementTablePtr table) {
|
|
int i;
|
|
|
|
if (table == NULL) return;
|
|
|
|
for (i = 0;i < table->nb_elements;i++) {
|
|
xmlFreeElement(table->table[i]);
|
|
}
|
|
xmlFree(table->table);
|
|
xmlFree(table);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
xmlElementTablePtr ret;
|
|
xmlElementPtr cur, ent;
|
|
int i;
|
|
|
|
ret = (xmlElementTablePtr) xmlMalloc(sizeof(xmlElementTable));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCopyElementTable: out of memory !\n");
|
|
return(NULL);
|
|
}
|
|
ret->table = (xmlElementPtr *) xmlMalloc(table->max_elements *
|
|
sizeof(xmlElementPtr));
|
|
if (ret->table == NULL) {
|
|
fprintf(stderr, "xmlCopyElementTable: out of memory !\n");
|
|
xmlFree(ret);
|
|
return(NULL);
|
|
}
|
|
ret->max_elements = table->max_elements;
|
|
ret->nb_elements = table->nb_elements;
|
|
for (i = 0;i < ret->nb_elements;i++) {
|
|
cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
|
|
if (cur == NULL) {
|
|
fprintf(stderr, "xmlCopyElementTable: out of memory !\n");
|
|
xmlFree(ret);
|
|
xmlFree(ret->table);
|
|
return(NULL);
|
|
}
|
|
ret->table[i] = cur;
|
|
ent = table->table[i];
|
|
cur->type = ent->type;
|
|
if (ent->name != NULL)
|
|
cur->name = xmlStrdup(ent->name);
|
|
else
|
|
cur->name = NULL;
|
|
cur->content = xmlCopyElementContent(ent->content);
|
|
/* TODO : rebuild the attribute list on the copy */
|
|
cur->attributes = NULL;
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
int i;
|
|
xmlElementPtr cur;
|
|
|
|
if (table == NULL) return;
|
|
|
|
for (i = 0;i < table->nb_elements;i++) {
|
|
cur = table->table[i];
|
|
switch (cur->type) {
|
|
case XML_ELEMENT_TYPE_EMPTY:
|
|
xmlBufferWriteChar(buf, "<!ELEMENT ");
|
|
xmlBufferWriteCHAR(buf, cur->name);
|
|
xmlBufferWriteChar(buf, " EMPTY>\n");
|
|
break;
|
|
case XML_ELEMENT_TYPE_ANY:
|
|
xmlBufferWriteChar(buf, "<!ELEMENT ");
|
|
xmlBufferWriteCHAR(buf, cur->name);
|
|
xmlBufferWriteChar(buf, " ANY>\n");
|
|
break;
|
|
case XML_ELEMENT_TYPE_MIXED:
|
|
xmlBufferWriteChar(buf, "<!ELEMENT ");
|
|
xmlBufferWriteCHAR(buf, cur->name);
|
|
xmlBufferWriteChar(buf, " ");
|
|
xmlDumpElementContent(buf, cur->content, 1);
|
|
xmlBufferWriteChar(buf, ">\n");
|
|
break;
|
|
case XML_ELEMENT_TYPE_ELEMENT:
|
|
xmlBufferWriteChar(buf, "<!ELEMENT ");
|
|
xmlBufferWriteCHAR(buf, cur->name);
|
|
xmlBufferWriteChar(buf, " ");
|
|
xmlDumpElementContent(buf, cur->content, 1);
|
|
xmlBufferWriteChar(buf, ">\n");
|
|
break;
|
|
default:
|
|
fprintf(stderr,
|
|
"xmlDumpElementTable: internal: unknown type %d\n",
|
|
cur->type);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
fprintf(stderr, "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
|
|
(long)sizeof(xmlEnumeration));
|
|
return(NULL);
|
|
}
|
|
|
|
if (name != NULL)
|
|
ret->name = xmlStrdup(name);
|
|
else
|
|
ret->name = NULL;
|
|
ret->next = NULL;
|
|
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) {
|
|
xmlAttributeTablePtr ret;
|
|
|
|
ret = (xmlAttributeTablePtr)
|
|
xmlMalloc(sizeof(xmlAttributeTable));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCreateAttributeTable : xmlMalloc(%ld) failed\n",
|
|
(long)sizeof(xmlAttributeTable));
|
|
return(NULL);
|
|
}
|
|
ret->max_attributes = XML_MIN_ATTRIBUTE_TABLE;
|
|
ret->nb_attributes = 0;
|
|
ret->table = (xmlAttributePtr *)
|
|
xmlMalloc(ret->max_attributes * sizeof(xmlAttributePtr));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCreateAttributeTable : xmlMalloc(%ld) failed\n",
|
|
ret->max_attributes * (long)sizeof(xmlAttributePtr));
|
|
xmlFree(ret);
|
|
return(NULL);
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
int i;
|
|
|
|
if (dtd == NULL) {
|
|
fprintf(stderr, "xmlScanAttributeDecl: dtd == NULL\n");
|
|
return(NULL);
|
|
}
|
|
if (elem == NULL) {
|
|
fprintf(stderr, "xmlScanAttributeDecl: elem == NULL\n");
|
|
return(NULL);
|
|
}
|
|
table = dtd->attributes;
|
|
if (table == NULL)
|
|
return(NULL);
|
|
|
|
for (i = 0;i < table->nb_attributes;i++) {
|
|
if (!xmlStrcmp(table->table[i]->elem, elem)) {
|
|
table->table[i]->next = ret;
|
|
ret = table->table[i];
|
|
}
|
|
}
|
|
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->type == 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->next;
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
|
|
/**
|
|
* xmlAddAttributeDecl:
|
|
* @ctxt: the validation context
|
|
* @dtd: pointer to the DTD
|
|
* @elem: the element name
|
|
* @name: the attribute name
|
|
* @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
|
|
*
|
|
* Returns NULL if not, othervise the entity
|
|
*/
|
|
xmlAttributePtr
|
|
xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
|
|
const xmlChar *name, xmlAttributeType type,
|
|
xmlAttributeDefault def, const xmlChar *defaultValue,
|
|
xmlEnumerationPtr tree) {
|
|
xmlAttributePtr ret, cur;
|
|
xmlAttributeTablePtr table;
|
|
xmlElementPtr elemDef;
|
|
xmlChar *rname;
|
|
xmlChar *ns;
|
|
int i;
|
|
|
|
if (dtd == NULL) {
|
|
fprintf(stderr, "xmlAddAttributeDecl: dtd == NULL\n");
|
|
return(NULL);
|
|
}
|
|
if (name == NULL) {
|
|
fprintf(stderr, "xmlAddAttributeDecl: name == NULL\n");
|
|
return(NULL);
|
|
}
|
|
if (elem == NULL) {
|
|
fprintf(stderr, "xmlAddAttributeDecl: elem == NULL\n");
|
|
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:
|
|
fprintf(stderr, "xmlAddAttributeDecl: unknown type %d\n", type);
|
|
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 = dtd->attributes;
|
|
if (table == NULL)
|
|
table = dtd->attributes = xmlCreateAttributeTable();
|
|
if (table == NULL) {
|
|
fprintf(stderr, "xmlAddAttributeDecl: Table creation failed!\n");
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Split the full name into a namespace prefix and the tag name
|
|
*/
|
|
rname = xmlSplitQName(name, &ns);
|
|
|
|
/*
|
|
* Validity Check:
|
|
* Search the DTD for previous declarations of the ATTLIST
|
|
*/
|
|
for (i = 0;i < table->nb_attributes;i++) {
|
|
cur = table->table[i];
|
|
if ((ns != NULL) && (cur->prefix == NULL)) continue;
|
|
if ((ns == NULL) && (cur->prefix != NULL)) continue;
|
|
if ((!xmlStrcmp(cur->name, rname)) && (!xmlStrcmp(cur->elem, elem)) &&
|
|
((ns == NULL) || (!xmlStrcmp(cur->prefix, ns)))) {
|
|
/*
|
|
* The attribute is already defined in this Dtd.
|
|
*/
|
|
VERROR(ctxt->userData, "Attribute %s on %s: already defined\n",
|
|
elem, name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Grow the table, if needed.
|
|
*/
|
|
if (table->nb_attributes >= table->max_attributes) {
|
|
/*
|
|
* need more attributes.
|
|
*/
|
|
table->max_attributes *= 2;
|
|
table->table = (xmlAttributePtr *)
|
|
xmlRealloc(table->table, table->max_attributes *
|
|
sizeof(xmlAttributePtr));
|
|
if (table->table == NULL) {
|
|
fprintf(stderr, "xmlAddAttributeDecl: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
}
|
|
ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlAddAttributeDecl: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
table->table[table->nb_attributes] = ret;
|
|
|
|
/*
|
|
* fill the structure.
|
|
*/
|
|
ret->type = type;
|
|
ret->name = rname;
|
|
ret->prefix = ns;
|
|
ret->elem = xmlStrdup(elem);
|
|
ret->def = def;
|
|
ret->tree = tree;
|
|
if (defaultValue != NULL)
|
|
ret->defaultValue = xmlStrdup(defaultValue);
|
|
else
|
|
ret->defaultValue = NULL;
|
|
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->next = elemDef->attributes;
|
|
elemDef->attributes = ret;
|
|
}
|
|
table->nb_attributes++;
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlFreeAttribute:
|
|
* @elem: An attribute
|
|
*
|
|
* Deallocate the memory used by an attribute definition
|
|
*/
|
|
void
|
|
xmlFreeAttribute(xmlAttributePtr attr) {
|
|
if (attr == NULL) return;
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* xmlFreeAttributeTable:
|
|
* @table: An attribute table
|
|
*
|
|
* Deallocate the memory used by an entities hash table.
|
|
*/
|
|
void
|
|
xmlFreeAttributeTable(xmlAttributeTablePtr table) {
|
|
int i;
|
|
|
|
if (table == NULL) return;
|
|
|
|
for (i = 0;i < table->nb_attributes;i++) {
|
|
xmlFreeAttribute(table->table[i]);
|
|
}
|
|
xmlFree(table->table);
|
|
xmlFree(table);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
xmlAttributeTablePtr ret;
|
|
xmlAttributePtr cur, attr;
|
|
int i;
|
|
|
|
ret = (xmlAttributeTablePtr) xmlMalloc(sizeof(xmlAttributeTable));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCopyAttributeTable: out of memory !\n");
|
|
return(NULL);
|
|
}
|
|
ret->table = (xmlAttributePtr *) xmlMalloc(table->max_attributes *
|
|
sizeof(xmlAttributePtr));
|
|
if (ret->table == NULL) {
|
|
fprintf(stderr, "xmlCopyAttributeTable: out of memory !\n");
|
|
xmlFree(ret);
|
|
return(NULL);
|
|
}
|
|
ret->max_attributes = table->max_attributes;
|
|
ret->nb_attributes = table->nb_attributes;
|
|
for (i = 0;i < ret->nb_attributes;i++) {
|
|
attr = table->table[i];
|
|
cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
|
|
if (cur == NULL) {
|
|
fprintf(stderr, "xmlCopyAttributeTable: out of memory !\n");
|
|
xmlFree(ret);
|
|
xmlFree(ret->table);
|
|
return(NULL);
|
|
}
|
|
ret->table[i] = cur;
|
|
cur->type = attr->type;
|
|
cur->def = attr->def;
|
|
cur->tree = xmlCopyEnumeration(attr->tree);
|
|
if (attr->elem != NULL)
|
|
cur->elem = xmlStrdup(attr->elem);
|
|
else
|
|
cur->elem = NULL;
|
|
if (attr->name != NULL)
|
|
cur->name = xmlStrdup(attr->name);
|
|
else
|
|
cur->name = NULL;
|
|
if (attr->defaultValue != NULL)
|
|
cur->defaultValue = xmlStrdup(attr->defaultValue);
|
|
else
|
|
cur->defaultValue = NULL;
|
|
/* NEED to rebuild the next chain !!!!!! */
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
int i;
|
|
xmlAttributePtr cur;
|
|
|
|
if (table == NULL) return;
|
|
|
|
for (i = 0;i < table->nb_attributes;i++) {
|
|
cur = table->table[i];
|
|
xmlBufferWriteChar(buf, "<!ATTLIST ");
|
|
xmlBufferWriteCHAR(buf, cur->elem);
|
|
xmlBufferWriteChar(buf, " ");
|
|
xmlBufferWriteCHAR(buf, cur->name);
|
|
switch (cur->type) {
|
|
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, cur->tree);
|
|
break;
|
|
case XML_ATTRIBUTE_NOTATION:
|
|
xmlBufferWriteChar(buf, " NOTATION (");
|
|
xmlDumpEnumeration(buf, cur->tree);
|
|
break;
|
|
default:
|
|
fprintf(stderr,
|
|
"xmlDumpAttributeTable: internal: unknown type %d\n",
|
|
cur->type);
|
|
}
|
|
switch (cur->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:
|
|
fprintf(stderr,
|
|
"xmlDumpAttributeTable: internal: unknown default %d\n",
|
|
cur->def);
|
|
}
|
|
if (cur->defaultValue != NULL) {
|
|
xmlBufferWriteChar(buf, " ");
|
|
xmlBufferWriteQuotedString(buf, cur->defaultValue);
|
|
}
|
|
xmlBufferWriteChar(buf, ">\n");
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* NOTATIONs *
|
|
* *
|
|
************************************************************************/
|
|
/**
|
|
* xmlCreateNotationTable:
|
|
*
|
|
* create and initialize an empty notation hash table.
|
|
*
|
|
* Returns the xmlNotationTablePtr just created or NULL in case
|
|
* of error.
|
|
*/
|
|
xmlNotationTablePtr
|
|
xmlCreateNotationTable(void) {
|
|
xmlNotationTablePtr ret;
|
|
|
|
ret = (xmlNotationTablePtr)
|
|
xmlMalloc(sizeof(xmlNotationTable));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCreateNotationTable : xmlMalloc(%ld) failed\n",
|
|
(long)sizeof(xmlNotationTable));
|
|
return(NULL);
|
|
}
|
|
ret->max_notations = XML_MIN_NOTATION_TABLE;
|
|
ret->nb_notations = 0;
|
|
ret->table = (xmlNotationPtr *)
|
|
xmlMalloc(ret->max_notations * sizeof(xmlNotationPtr));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCreateNotationTable : xmlMalloc(%ld) failed\n",
|
|
ret->max_notations * (long)sizeof(xmlNotation));
|
|
xmlFree(ret);
|
|
return(NULL);
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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, cur;
|
|
xmlNotationTablePtr table;
|
|
int i;
|
|
|
|
if (dtd == NULL) {
|
|
fprintf(stderr, "xmlAddNotationDecl: dtd == NULL\n");
|
|
return(NULL);
|
|
}
|
|
if (name == NULL) {
|
|
fprintf(stderr, "xmlAddNotationDecl: name == NULL\n");
|
|
return(NULL);
|
|
}
|
|
if ((PublicID == NULL) && (SystemID == NULL)) {
|
|
fprintf(stderr, "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
|
|
}
|
|
|
|
/*
|
|
* Create the Notation table if needed.
|
|
*/
|
|
table = dtd->notations;
|
|
if (table == NULL)
|
|
table = dtd->notations = xmlCreateNotationTable();
|
|
if (table == NULL) {
|
|
fprintf(stderr, "xmlAddNotationDecl: Table creation failed!\n");
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Validity Check:
|
|
* Search the DTD for previous declarations of the ATTLIST
|
|
*/
|
|
for (i = 0;i < table->nb_notations;i++) {
|
|
cur = table->table[i];
|
|
if (!xmlStrcmp(cur->name, name)) {
|
|
/*
|
|
* The notation is already defined in this Dtd.
|
|
*/
|
|
fprintf(stderr,
|
|
"xmlAddNotationDecl: %s already defined\n", name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Grow the table, if needed.
|
|
*/
|
|
if (table->nb_notations >= table->max_notations) {
|
|
/*
|
|
* need more notations.
|
|
*/
|
|
table->max_notations *= 2;
|
|
table->table = (xmlNotationPtr *)
|
|
xmlRealloc(table->table, table->max_notations *
|
|
sizeof(xmlNotationPtr));
|
|
if (table->table == NULL) {
|
|
fprintf(stderr, "xmlAddNotationDecl: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
}
|
|
ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlAddNotationDecl: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
table->table[table->nb_notations] = ret;
|
|
|
|
/*
|
|
* fill the structure.
|
|
*/
|
|
ret->name = xmlStrdup(name);
|
|
if (SystemID != NULL)
|
|
ret->SystemID = xmlStrdup(SystemID);
|
|
else
|
|
ret->SystemID = NULL;
|
|
if (PublicID != NULL)
|
|
ret->PublicID = xmlStrdup(PublicID);
|
|
else
|
|
ret->PublicID = NULL;
|
|
table->nb_notations++;
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* xmlFreeNotationTable:
|
|
* @table: An notation table
|
|
*
|
|
* Deallocate the memory used by an entities hash table.
|
|
*/
|
|
void
|
|
xmlFreeNotationTable(xmlNotationTablePtr table) {
|
|
int i;
|
|
|
|
if (table == NULL) return;
|
|
|
|
for (i = 0;i < table->nb_notations;i++) {
|
|
xmlFreeNotation(table->table[i]);
|
|
}
|
|
xmlFree(table->table);
|
|
xmlFree(table);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
xmlNotationTablePtr ret;
|
|
xmlNotationPtr cur, nota;
|
|
int i;
|
|
|
|
ret = (xmlNotationTablePtr) xmlMalloc(sizeof(xmlNotationTable));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCopyNotationTable: out of memory !\n");
|
|
return(NULL);
|
|
}
|
|
ret->table = (xmlNotationPtr *) xmlMalloc(table->max_notations *
|
|
sizeof(xmlNotationPtr));
|
|
if (ret->table == NULL) {
|
|
fprintf(stderr, "xmlCopyNotationTable: out of memory !\n");
|
|
xmlFree(ret);
|
|
return(NULL);
|
|
}
|
|
ret->max_notations = table->max_notations;
|
|
ret->nb_notations = table->nb_notations;
|
|
for (i = 0;i < ret->nb_notations;i++) {
|
|
cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
|
|
if (cur == NULL) {
|
|
fprintf(stderr, "xmlCopyNotationTable: out of memory !\n");
|
|
xmlFree(ret);
|
|
xmlFree(ret->table);
|
|
return(NULL);
|
|
}
|
|
ret->table[i] = cur;
|
|
nota = table->table[i];
|
|
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(ret);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
int i;
|
|
xmlNotationPtr cur;
|
|
|
|
if (table == NULL) return;
|
|
|
|
for (i = 0;i < table->nb_notations;i++) {
|
|
cur = table->table[i];
|
|
xmlBufferWriteChar(buf, "<!NOTATION ");
|
|
xmlBufferWriteCHAR(buf, cur->name);
|
|
if (cur->PublicID != NULL) {
|
|
xmlBufferWriteChar(buf, " PUBLIC ");
|
|
xmlBufferWriteQuotedString(buf, cur->PublicID);
|
|
if (cur->SystemID != NULL) {
|
|
xmlBufferWriteChar(buf, " ");
|
|
xmlBufferWriteCHAR(buf, cur->SystemID);
|
|
}
|
|
} else {
|
|
xmlBufferWriteChar(buf, " SYSTEM ");
|
|
xmlBufferWriteCHAR(buf, cur->SystemID);
|
|
}
|
|
xmlBufferWriteChar(buf, " >\n");
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* IDs *
|
|
* *
|
|
************************************************************************/
|
|
/**
|
|
* xmlCreateIDTable:
|
|
*
|
|
* create and initialize an empty id hash table.
|
|
*
|
|
* Returns the xmlIDTablePtr just created or NULL in case
|
|
* of error.
|
|
*/
|
|
xmlIDTablePtr
|
|
xmlCreateIDTable(void) {
|
|
xmlIDTablePtr ret;
|
|
|
|
ret = (xmlIDTablePtr)
|
|
xmlMalloc(sizeof(xmlIDTable));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCreateIDTable : xmlMalloc(%ld) failed\n",
|
|
(long)sizeof(xmlIDTable));
|
|
return(NULL);
|
|
}
|
|
ret->max_ids = XML_MIN_NOTATION_TABLE;
|
|
ret->nb_ids = 0;
|
|
ret->table = (xmlIDPtr *)
|
|
xmlMalloc(ret->max_ids * sizeof(xmlIDPtr));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCreateIDTable : xmlMalloc(%ld) failed\n",
|
|
ret->max_ids * (long)sizeof(xmlID));
|
|
xmlFree(ret);
|
|
return(NULL);
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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, cur;
|
|
xmlIDTablePtr table;
|
|
int i;
|
|
|
|
if (doc == NULL) {
|
|
fprintf(stderr, "xmlAddIDDecl: doc == NULL\n");
|
|
return(NULL);
|
|
}
|
|
if (value == NULL) {
|
|
fprintf(stderr, "xmlAddIDDecl: value == NULL\n");
|
|
return(NULL);
|
|
}
|
|
if (attr == NULL) {
|
|
fprintf(stderr, "xmlAddIDDecl: attr == NULL\n");
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Create the ID table if needed.
|
|
*/
|
|
table = doc->ids;
|
|
if (table == NULL)
|
|
table = doc->ids = xmlCreateIDTable();
|
|
if (table == NULL) {
|
|
fprintf(stderr, "xmlAddID: Table creation failed!\n");
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Validity Check:
|
|
* Search the DTD for previous declarations of the ATTLIST
|
|
*/
|
|
for (i = 0;i < table->nb_ids;i++) {
|
|
cur = table->table[i];
|
|
if (!xmlStrcmp(cur->value, value)) {
|
|
/*
|
|
* The id is already defined in this Dtd.
|
|
*/
|
|
VERROR(ctxt->userData, "ID %s already defined\n", value);
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Grow the table, if needed.
|
|
*/
|
|
if (table->nb_ids >= table->max_ids) {
|
|
/*
|
|
* need more ids.
|
|
*/
|
|
table->max_ids *= 2;
|
|
table->table = (xmlIDPtr *)
|
|
xmlRealloc(table->table, table->max_ids *
|
|
sizeof(xmlIDPtr));
|
|
if (table->table == NULL) {
|
|
fprintf(stderr, "xmlAddID: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
}
|
|
ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlAddID: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
table->table[table->nb_ids] = ret;
|
|
|
|
/*
|
|
* fill the structure.
|
|
*/
|
|
ret->value = xmlStrdup(value);
|
|
ret->attr = attr;
|
|
table->nb_ids++;
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* xmlFreeIDTable:
|
|
* @table: An id table
|
|
*
|
|
* Deallocate the memory used by an ID hash table.
|
|
*/
|
|
void
|
|
xmlFreeIDTable(xmlIDTablePtr table) {
|
|
int i;
|
|
|
|
if (table == NULL) return;
|
|
|
|
for (i = 0;i < table->nb_ids;i++) {
|
|
xmlFreeID(table->table[i]);
|
|
}
|
|
xmlFree(table->table);
|
|
xmlFree(table);
|
|
}
|
|
|
|
/**
|
|
* 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)) {
|
|
if (((attr->name[0] == 'I') || (attr->name[0] == 'i')) &&
|
|
((attr->name[1] == 'D') || (attr->name[1] == 'd')) &&
|
|
(attr->name[2] == 0)) return(1);
|
|
} else if (doc->type == XML_HTML_DOCUMENT_NODE) {
|
|
if ((!xmlStrcmp(BAD_CAST "id", attr->name)) ||
|
|
(!xmlStrcmp(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->type == 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) {
|
|
xmlIDPtr cur;
|
|
xmlIDTablePtr table;
|
|
int i;
|
|
|
|
if (doc == NULL) return(-1);
|
|
if (attr == NULL) return(-1);
|
|
table = doc->ids;
|
|
if (table == NULL)
|
|
return(-1);
|
|
|
|
/*
|
|
* Search the ID list.
|
|
*/
|
|
for (i = 0;i < table->nb_ids;i++) {
|
|
cur = table->table[i];
|
|
if (cur->attr == attr) {
|
|
table->nb_ids--;
|
|
memmove(&table->table[i], &table->table[i+1],
|
|
(table->nb_ids - i) * sizeof(xmlIDPtr));
|
|
return(0);
|
|
}
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
xmlIDPtr cur;
|
|
xmlIDTablePtr table;
|
|
int i;
|
|
|
|
if (doc == NULL) {
|
|
fprintf(stderr, "xmlGetID: doc == NULL\n");
|
|
return(NULL);
|
|
}
|
|
|
|
if (ID == NULL) {
|
|
fprintf(stderr, "xmlGetID: ID == NULL\n");
|
|
return(NULL);
|
|
}
|
|
|
|
table = doc->ids;
|
|
if (table == NULL)
|
|
return(NULL);
|
|
|
|
/*
|
|
* Search the ID list.
|
|
*/
|
|
for (i = 0;i < table->nb_ids;i++) {
|
|
cur = table->table[i];
|
|
if (!xmlStrcmp(cur->value, ID)) {
|
|
return(cur->attr);
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Refs *
|
|
* *
|
|
************************************************************************/
|
|
/**
|
|
* xmlCreateRefTable:
|
|
*
|
|
* create and initialize an empty ref hash table.
|
|
*
|
|
* Returns the xmlRefTablePtr just created or NULL in case
|
|
* of error.
|
|
*/
|
|
xmlRefTablePtr
|
|
xmlCreateRefTable(void) {
|
|
xmlRefTablePtr ret;
|
|
|
|
ret = (xmlRefTablePtr)
|
|
xmlMalloc(sizeof(xmlRefTable));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCreateRefTable : xmlMalloc(%ld) failed\n",
|
|
(long)sizeof(xmlRefTable));
|
|
return(NULL);
|
|
}
|
|
ret->max_refs = XML_MIN_NOTATION_TABLE;
|
|
ret->nb_refs = 0;
|
|
ret->table = (xmlRefPtr *)
|
|
xmlMalloc(ret->max_refs * sizeof(xmlRefPtr));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlCreateRefTable : xmlMalloc(%ld) failed\n",
|
|
ret->max_refs * (long)sizeof(xmlRef));
|
|
xmlFree(ret);
|
|
return(NULL);
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
|
|
/**
|
|
* 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;
|
|
|
|
if (doc == NULL) {
|
|
fprintf(stderr, "xmlAddRefDecl: doc == NULL\n");
|
|
return(NULL);
|
|
}
|
|
if (value == NULL) {
|
|
fprintf(stderr, "xmlAddRefDecl: value == NULL\n");
|
|
return(NULL);
|
|
}
|
|
if (attr == NULL) {
|
|
fprintf(stderr, "xmlAddRefDecl: attr == NULL\n");
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Create the Ref table if needed.
|
|
*/
|
|
table = doc->refs;
|
|
if (table == NULL)
|
|
table = doc->refs = xmlCreateRefTable();
|
|
if (table == NULL) {
|
|
fprintf(stderr, "xmlAddRef: Table creation failed!\n");
|
|
return(NULL);
|
|
}
|
|
|
|
/*
|
|
* Grow the table, if needed.
|
|
*/
|
|
if (table->nb_refs >= table->max_refs) {
|
|
/*
|
|
* need more refs.
|
|
*/
|
|
table->max_refs *= 2;
|
|
table->table = (xmlRefPtr *)
|
|
xmlRealloc(table->table, table->max_refs *
|
|
sizeof(xmlRefPtr));
|
|
if (table->table == NULL) {
|
|
fprintf(stderr, "xmlAddRef: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
}
|
|
ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
|
|
if (ret == NULL) {
|
|
fprintf(stderr, "xmlAddRef: out of memory\n");
|
|
return(NULL);
|
|
}
|
|
table->table[table->nb_refs] = ret;
|
|
|
|
/*
|
|
* fill the structure.
|
|
*/
|
|
ret->value = xmlStrdup(value);
|
|
ret->attr = attr;
|
|
table->nb_refs++;
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* xmlFreeRef:
|
|
* @not: A ref
|
|
*
|
|
* Deallocate the memory used by an ref definition
|
|
*/
|
|
void
|
|
xmlFreeRef(xmlRefPtr ref) {
|
|
if (ref == NULL) return;
|
|
if (ref->value != NULL)
|
|
xmlFree((xmlChar *) ref->value);
|
|
memset(ref, -1, sizeof(xmlRef));
|
|
xmlFree(ref);
|
|
}
|
|
|
|
/**
|
|
* xmlFreeRefTable:
|
|
* @table: An ref table
|
|
*
|
|
* Deallocate the memory used by an Ref hash table.
|
|
*/
|
|
void
|
|
xmlFreeRefTable(xmlRefTablePtr table) {
|
|
int i;
|
|
|
|
if (table == NULL) return;
|
|
|
|
for (i = 0;i < table->nb_refs;i++) {
|
|
xmlFreeRef(table->table[i]);
|
|
}
|
|
xmlFree(table->table);
|
|
xmlFree(table);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
/*******************
|
|
if (((attr->name[0] == 'I') || (attr->name[0] == 'i')) &&
|
|
((attr->name[1] == 'D') || (attr->name[1] == 'd')) &&
|
|
(attr->name[2] == 0)) return(1);
|
|
*******************/
|
|
} 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->type == 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) {
|
|
xmlRefPtr cur;
|
|
xmlRefTablePtr table;
|
|
int i;
|
|
|
|
if (doc == NULL) return(-1);
|
|
if (attr == NULL) return(-1);
|
|
table = doc->refs;
|
|
if (table == NULL)
|
|
return(-1);
|
|
|
|
/*
|
|
* Search the Ref list.
|
|
*/
|
|
for (i = 0;i < table->nb_refs;i++) {
|
|
cur = table->table[i];
|
|
if (cur->attr == attr) {
|
|
table->nb_refs--;
|
|
memmove(&table->table[i], &table->table[i+1],
|
|
(table->nb_refs - i) * sizeof(xmlRefPtr));
|
|
return(0);
|
|
}
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
/**
|
|
* xmlGetRef:
|
|
* @doc: pointer to the document
|
|
* @Ref: the Ref value
|
|
*
|
|
* Search the next attribute declaring the given Ref
|
|
*
|
|
* Returns NULL if not found, otherwise the xmlAttrPtr defining the Ref
|
|
*/
|
|
xmlAttrPtr
|
|
xmlGetRef(xmlDocPtr doc, const xmlChar *Ref) {
|
|
xmlRefPtr cur;
|
|
xmlRefTablePtr table;
|
|
int i;
|
|
|
|
if (doc == NULL) {
|
|
fprintf(stderr, "xmlGetRef: doc == NULL\n");
|
|
return(NULL);
|
|
}
|
|
|
|
if (Ref == NULL) {
|
|
fprintf(stderr, "xmlGetRef: Ref == NULL\n");
|
|
return(NULL);
|
|
}
|
|
|
|
table = doc->refs;
|
|
if (table == NULL)
|
|
return(NULL);
|
|
|
|
/*
|
|
* Search the Ref list.
|
|
*/
|
|
for (i = 0;i < table->nb_refs;i++) {
|
|
cur = table->table[i];
|
|
if (!xmlStrcmp(cur->value, Ref)) {
|
|
return(cur->attr);
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* 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;
|
|
int i;
|
|
|
|
if (dtd == NULL) return(NULL);
|
|
if (dtd->elements == NULL) return(NULL);
|
|
table = dtd->elements;
|
|
|
|
for (i = 0;i < table->nb_elements;i++) {
|
|
cur = table->table[i];
|
|
if (!xmlStrcmp(cur->name, name))
|
|
return(cur);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
int i;
|
|
|
|
if (dtd == NULL) return(NULL);
|
|
if (dtd->attributes == NULL) return(NULL);
|
|
table = dtd->attributes;
|
|
|
|
for (i = 0;i < table->nb_attributes;i++) {
|
|
cur = table->table[i];
|
|
if ((!xmlStrcmp(cur->name, name)) &&
|
|
(!xmlStrcmp(cur->elem, elem)))
|
|
return(cur);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
xmlNotationPtr cur;
|
|
int i;
|
|
|
|
if (dtd == NULL) return(NULL);
|
|
if (dtd->notations == NULL) return(NULL);
|
|
table = dtd->notations;
|
|
|
|
for (i = 0;i < table->nb_notations;i++) {
|
|
cur = table->table[i];
|
|
if (!xmlStrcmp(cur->name, name))
|
|
return(cur);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* 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->type) {
|
|
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;
|
|
|
|
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 (!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);
|
|
}
|
|
|
|
/**
|
|
* 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->type, 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->type == 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->type == XML_ATTRIBUTE_ID) && (doc->extSubset != NULL)) {
|
|
int nbId = 0;
|
|
|
|
/* the trick is taht we parse DtD as their own internal subset */
|
|
xmlElementPtr elem = xmlGetDtdElementDesc(doc->extSubset,
|
|
attr->elem);
|
|
if (elem != NULL) {
|
|
nbId = xmlScanIDAttributeDecl(NULL, elem);
|
|
}
|
|
if (nbId > 1)
|
|
VERROR(ctxt->userData,
|
|
"Element %s has ID attribute defined in the external subset : %s\n",
|
|
attr->elem, attr->name);
|
|
}
|
|
|
|
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->type == 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 (!xmlStrcmp(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 (!xmlStrcmp(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;
|
|
int val;
|
|
int ret = 1;
|
|
|
|
CHECK_DTD;
|
|
if ((elem == NULL) || (elem->name == NULL)) return(0);
|
|
if ((attr == NULL) || (attr->name == 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);
|
|
|
|
|
|
/* 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);
|
|
}
|
|
val = xmlValidateAttributeValue(attrDecl->type, 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: ID uniqueness */
|
|
if (attrDecl->type == XML_ATTRIBUTE_ID) {
|
|
xmlAddID(ctxt, doc, value, attr);
|
|
}
|
|
|
|
if (attrDecl->type == XML_ATTRIBUTE_IDREF) {
|
|
xmlAddRef(ctxt, doc, value, attr);
|
|
}
|
|
|
|
/* Validity Constraint: Notation Attributes */
|
|
if (attrDecl->type == 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 (!xmlStrcmp(tree->name, value)) break;
|
|
tree = tree->next;
|
|
}
|
|
if (tree == NULL) {
|
|
VERROR(ctxt->userData,
|
|
"Value \"%s\" for attribute %s on %s is among the enumerated notations\n",
|
|
value, attr->name, elem->name);
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
/* Validity Constraint: Enumeration */
|
|
if (attrDecl->type == XML_ATTRIBUTE_ENUMERATION) {
|
|
xmlEnumerationPtr tree = attrDecl->tree;
|
|
while (tree != NULL) {
|
|
if (!xmlStrcmp(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) &&
|
|
(xmlStrcmp(attrDecl->defaultValue, value))) {
|
|
VERROR(ctxt->userData,
|
|
"Value for attribute %s on %s must be \"%s\"\n",
|
|
attr->name, elem->name, attrDecl->defaultValue);
|
|
ret = 0;
|
|
}
|
|
|
|
/********
|
|
elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
|
|
if ((elemDecl == NULL) && (doc->extSubset != NULL))
|
|
elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
|
|
if (elemDecl == NULL) {
|
|
return(0);
|
|
}
|
|
********/
|
|
return(ret);
|
|
}
|
|
|
|
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);
|
|
while (*child != NULL) {
|
|
if ((*child)->type == XML_PI_NODE) {
|
|
*child = (*child)->next;
|
|
continue;
|
|
}
|
|
if ((*child)->type == XML_COMMENT_NODE) {
|
|
*child = (*child)->next;
|
|
continue;
|
|
}
|
|
else if ((*child)->type != XML_ELEMENT_NODE) {
|
|
return(-1);
|
|
}
|
|
break;
|
|
}
|
|
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 = (!xmlStrcmp((*child)->name, cont->name));
|
|
if (ret == 1)
|
|
*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 = 1;
|
|
|
|
if (cont == NULL) return(-1);
|
|
while (*child != NULL) {
|
|
if ((*child)->type == XML_PI_NODE) {
|
|
*child = (*child)->next;
|
|
continue;
|
|
}
|
|
if ((*child)->type == XML_COMMENT_NODE) {
|
|
*child = (*child)->next;
|
|
continue;
|
|
}
|
|
else if ((*child)->type != XML_ELEMENT_NODE) {
|
|
return(-1);
|
|
}
|
|
break;
|
|
}
|
|
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 = (*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);
|
|
}
|
|
do {
|
|
cur = *child;
|
|
ret = xmlValidateElementTypeExpr(ctxt, child, cont);
|
|
} while (ret == 1);
|
|
if (ret == -1) return(-1);
|
|
*child = cur;
|
|
break;
|
|
}
|
|
while (*child != NULL) {
|
|
if ((*child)->type == XML_PI_NODE) {
|
|
*child = (*child)->next;
|
|
continue;
|
|
}
|
|
if ((*child)->type == XML_COMMENT_NODE) {
|
|
*child = (*child)->next;
|
|
continue;
|
|
}
|
|
else if ((*child)->type != XML_ELEMENT_NODE) {
|
|
return(-1);
|
|
}
|
|
break;
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
/**
|
|
* 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->childs;
|
|
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:
|
|
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:
|
|
case XML_HTML_DOCUMENT_NODE:
|
|
case XML_DOCUMENT_TYPE_NODE:
|
|
case XML_DOCUMENT_FRAG_NODE:
|
|
case XML_NOTATION_NODE:
|
|
strcat(buf, "???");
|
|
if (cur->next != NULL)
|
|
strcat(buf, " ");
|
|
break;
|
|
case XML_ENTITY_NODE:
|
|
case XML_PI_NODE:
|
|
case XML_COMMENT_NODE:
|
|
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;
|
|
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->childs != 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->ns != 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_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);
|
|
|
|
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->type) {
|
|
case XML_ELEMENT_TYPE_EMPTY:
|
|
if (elem->childs != 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->childs;
|
|
while (child != NULL) {
|
|
if (child->type == XML_ELEMENT_NODE) {
|
|
name = child->name;
|
|
cont = elemDecl->content;
|
|
while (cont != NULL) {
|
|
if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
|
|
if (!xmlStrcmp(cont->name, name)) break;
|
|
} else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
|
|
(cont->c1 != NULL) &&
|
|
(cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
|
|
if (!xmlStrcmp(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 !!! */
|
|
fprintf(stderr, "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 = child->next;
|
|
}
|
|
break;
|
|
case XML_ELEMENT_TYPE_ELEMENT:
|
|
child = elem->childs;
|
|
cont = elemDecl->content;
|
|
ret = xmlValidateElementTypeElement(ctxt, &child, cont);
|
|
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 (!xmlStrcmp(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 (xmlStrcmp(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);
|
|
} else {
|
|
VERROR(ctxt->userData,
|
|
"Element %s doesn't carry attribute %s:%s\n",
|
|
elem->name, attr->prefix,attr->name);
|
|
}
|
|
} 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->next;
|
|
}
|
|
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);
|
|
|
|
if ((doc->intSubset == NULL) ||
|
|
(doc->intSubset->name == NULL)) {
|
|
VERROR(ctxt->userData, "Not valid: no DtD found\n");
|
|
return(0);
|
|
}
|
|
root = xmlDocGetRootElement(doc);
|
|
if ((root == NULL) || (root->name == NULL)) {
|
|
VERROR(ctxt->userData, "Not valid: no root element\n");
|
|
return(0);
|
|
}
|
|
if (xmlStrcmp(doc->intSubset->name, root->name)) {
|
|
if ((xmlStrcmp(doc->intSubset->name, BAD_CAST "HTML")) ||
|
|
(xmlStrcmp(root->name, BAD_CAST "html"))) {
|
|
VERROR(ctxt->userData,
|
|
"Not valid: root and DtD name do not match '%s' and '%s'\n",
|
|
root->name, doc->intSubset->name);
|
|
return(0);
|
|
}
|
|
}
|
|
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);
|
|
CHECK_DTD;
|
|
|
|
ret &= xmlValidateOneElement(ctxt, doc, elem);
|
|
attr = elem->properties;
|
|
while(attr != NULL) {
|
|
value = xmlNodeListGetString(doc, attr->val, 0);
|
|
ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
|
|
if (value != NULL)
|
|
xmlFree(value);
|
|
attr= attr->next;
|
|
}
|
|
child = elem->childs;
|
|
while (child != NULL) {
|
|
ret &= xmlValidateElement(ctxt, doc, child);
|
|
child = child->next;
|
|
}
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
int ret = 1, i;
|
|
xmlRefTablePtr table;
|
|
xmlAttrPtr id;
|
|
|
|
if (doc == NULL) {
|
|
fprintf(stderr, "xmlValidateDocumentFinal: doc == NULL\n");
|
|
return(0);
|
|
}
|
|
|
|
/*
|
|
* Get the refs table
|
|
*/
|
|
table = doc->refs;
|
|
if (table != NULL) {
|
|
for (i = 0; i < table->nb_refs; i++) {
|
|
id = xmlGetID(doc, table->table[i]->value);
|
|
if (id == NULL) {
|
|
VERROR(ctxt->userData,
|
|
"IDREF attribute %s reference an unknown ID '%s'\n",
|
|
table->table[i]->attr->name, table->table[i]->value);
|
|
ret = 0;
|
|
}
|
|
}
|
|
}
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
root = xmlDocGetRootElement(doc);
|
|
ret = xmlValidateElement(ctxt, doc, root);
|
|
ret &= xmlValidateDocumentFinal(ctxt, doc);
|
|
doc->extSubset = oldExt;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* 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 (!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 (!xmlStrcmp(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 (!xmlStrcmp(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->childs;
|
|
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->childs = 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 (!xmlStrcmp(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->childs = parent_childs;
|
|
parent->last = parent_last;
|
|
|
|
return(nb_valid_elements);
|
|
}
|