/* * 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 #define HAVE_FCNTL_H #include #else #include "config.h" #endif #include #include #ifdef HAVE_STDLIB_H #include #endif #include "xmlmemory.h" #include "valid.h" #include "parser.h" #include "parserInternals.h" #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); exit(1); } 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); 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, "name); xmlBufferWriteChar(buf, " EMPTY>\n"); break; case XML_ELEMENT_TYPE_ANY: xmlBufferWriteChar(buf, "name); xmlBufferWriteChar(buf, " ANY>\n"); break; case XML_ELEMENT_TYPE_MIXED: xmlBufferWriteChar(buf, "name); xmlBufferWriteChar(buf, " "); xmlDumpElementContent(buf, cur->content, 1); xmlBufferWriteChar(buf, ">\n"); break; case XML_ELEMENT_TYPE_ELEMENT: xmlBufferWriteChar(buf, "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 * * Veryfy 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, "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, "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->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 { 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_ID)) return(1); } 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) { 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); } /** * xmlGetRef: * @doc: pointer to the document * @Ref: the Ref value * * Search the 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 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 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: /* Internal error !!! */ fprintf(stderr, "Internal: MIXED struct bad\n"); return(-1); 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) { 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; xmlNodePtr child; int ret = 1; const xmlChar *name; CHECK_DTD; if (elem == NULL) return(0); if (elem->type == 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); } 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; } /* TODO - [ VC: Required Attribute ] */ 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)) { 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 ""); 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); }