blob: 5ee391c0418327f5e46293aedafdb353550f6f5b [file] [log] [blame]
/*
* valid.c : part of the code use to do the DTD handling and the validity
* checking
*
* See Copyright for the status of this software.
*
* daniel@veillard.com
*/
#define IN_LIBXML
#include "libxml.h"
#include <string.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <libxml/xmlmemory.h>
#include <libxml/hash.h>
#include <libxml/uri.h>
#include <libxml/valid.h>
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/xmlerror.h>
#include <libxml/list.h>
#include <libxml/globals.h>
static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
int create);
/* #define DEBUG_VALID_ALGO */
/* #define DEBUG_REGEXP_ALGO */
#define TODO \
xmlGenericError(xmlGenericErrorContext, \
"Unimplemented block at %s:%d\n", \
__FILE__, __LINE__);
#ifdef LIBXML_VALID_ENABLED
static int
xmlValidateAttributeValueInternal(xmlDocPtr doc, xmlAttributeType type,
const xmlChar *value);
#endif
/************************************************************************
* *
* Error handling routines *
* *
************************************************************************/
/**
* xmlVErrMemory:
* @ctxt: an XML validation parser context
* @extra: extra information
*
* Handle an out of memory error
*/
static void
xmlVErrMemory(xmlValidCtxtPtr ctxt, const char *extra)
{
xmlGenericErrorFunc channel = NULL;
xmlParserCtxtPtr pctxt = NULL;
void *data = NULL;
if (ctxt != NULL) {
channel = ctxt->error;
data = ctxt->userData;
/* Use the special values to detect if it is part of a parsing
context */
if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
(ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
long delta = (char *) ctxt - (char *) ctxt->userData;
if ((delta > 0) && (delta < 250))
pctxt = ctxt->userData;
}
}
if (extra)
__xmlRaiseError(NULL, channel, data,
pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY,
XML_ERR_FATAL, NULL, 0, extra, NULL, NULL, 0, 0,
"Memory allocation failed : %s\n", extra);
else
__xmlRaiseError(NULL, channel, data,
pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY,
XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0,
"Memory allocation failed\n");
}
/**
* xmlErrValid:
* @ctxt: an XML validation parser context
* @error: the error number
* @extra: extra information
*
* Handle a validation error
*/
static void LIBXML_ATTR_FORMAT(3,0)
xmlErrValid(xmlValidCtxtPtr ctxt, xmlParserErrors error,
const char *msg, const char *extra)
{
xmlGenericErrorFunc channel = NULL;
xmlParserCtxtPtr pctxt = NULL;
void *data = NULL;
if (ctxt != NULL) {
channel = ctxt->error;
data = ctxt->userData;
/* Use the special values to detect if it is part of a parsing
context */
if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
(ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
long delta = (char *) ctxt - (char *) ctxt->userData;
if ((delta > 0) && (delta < 250))
pctxt = ctxt->userData;
}
}
if (extra)
__xmlRaiseError(NULL, channel, data,
pctxt, NULL, XML_FROM_VALID, error,
XML_ERR_ERROR, NULL, 0, extra, NULL, NULL, 0, 0,
msg, extra);
else
__xmlRaiseError(NULL, channel, data,
pctxt, NULL, XML_FROM_VALID, error,
XML_ERR_ERROR, NULL, 0, NULL, NULL, NULL, 0, 0,
"%s", msg);
}
#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
/**
* xmlErrValidNode:
* @ctxt: an XML validation parser context
* @node: the node raising the error
* @error: the error number
* @str1: extra information
* @str2: extra information
* @str3: extra information
*
* Handle a validation error, provide contextual information
*/
static void LIBXML_ATTR_FORMAT(4,0)
xmlErrValidNode(xmlValidCtxtPtr ctxt,
xmlNodePtr node, xmlParserErrors error,
const char *msg, const xmlChar * str1,
const xmlChar * str2, const xmlChar * str3)
{
xmlStructuredErrorFunc schannel = NULL;
xmlGenericErrorFunc channel = NULL;
xmlParserCtxtPtr pctxt = NULL;
void *data = NULL;
if (ctxt != NULL) {
channel = ctxt->error;
data = ctxt->userData;
/* Use the special values to detect if it is part of a parsing
context */
if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
(ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
long delta = (char *) ctxt - (char *) ctxt->userData;
if ((delta > 0) && (delta < 250))
pctxt = ctxt->userData;
}
}
__xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error,
XML_ERR_ERROR, NULL, 0,
(const char *) str1,
(const char *) str2,
(const char *) str3, 0, 0, msg, str1, str2, str3);
}
#endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */
#ifdef LIBXML_VALID_ENABLED
/**
* xmlErrValidNodeNr:
* @ctxt: an XML validation parser context
* @node: the node raising the error
* @error: the error number
* @str1: extra information
* @int2: extra information
* @str3: extra information
*
* Handle a validation error, provide contextual information
*/
static void LIBXML_ATTR_FORMAT(4,0)
xmlErrValidNodeNr(xmlValidCtxtPtr ctxt,
xmlNodePtr node, xmlParserErrors error,
const char *msg, const xmlChar * str1,
int int2, const xmlChar * str3)
{
xmlStructuredErrorFunc schannel = NULL;
xmlGenericErrorFunc channel = NULL;
xmlParserCtxtPtr pctxt = NULL;
void *data = NULL;
if (ctxt != NULL) {
channel = ctxt->error;
data = ctxt->userData;
/* Use the special values to detect if it is part of a parsing
context */
if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
(ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
long delta = (char *) ctxt - (char *) ctxt->userData;
if ((delta > 0) && (delta < 250))
pctxt = ctxt->userData;
}
}
__xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error,
XML_ERR_ERROR, NULL, 0,
(const char *) str1,
(const char *) str3,
NULL, int2, 0, msg, str1, int2, str3);
}
/**
* xmlErrValidWarning:
* @ctxt: an XML validation parser context
* @node: the node raising the error
* @error: the error number
* @str1: extra information
* @str2: extra information
* @str3: extra information
*
* Handle a validation error, provide contextual information
*/
static void LIBXML_ATTR_FORMAT(4,0)
xmlErrValidWarning(xmlValidCtxtPtr ctxt,
xmlNodePtr node, xmlParserErrors error,
const char *msg, const xmlChar * str1,
const xmlChar * str2, const xmlChar * str3)
{
xmlStructuredErrorFunc schannel = NULL;
xmlGenericErrorFunc channel = NULL;
xmlParserCtxtPtr pctxt = NULL;
void *data = NULL;
if (ctxt != NULL) {
channel = ctxt->warning;
data = ctxt->userData;
/* Use the special values to detect if it is part of a parsing
context */
if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
(ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
long delta = (char *) ctxt - (char *) ctxt->userData;
if ((delta > 0) && (delta < 250))
pctxt = ctxt->userData;
}
}
__xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error,
XML_ERR_WARNING, NULL, 0,
(const char *) str1,
(const char *) str2,
(const char *) str3, 0, 0, msg, str1, str2, str3);
}
#ifdef LIBXML_REGEXP_ENABLED
/*
* If regexp are enabled we can do continuous validation without the
* need of a tree to validate the content model. this is done in each
* callbacks.
* Each xmlValidState represent the validation state associated to the
* set of nodes currently open from the document root to the current element.
*/
typedef struct _xmlValidState {
xmlElementPtr elemDecl; /* pointer to the content model */
xmlNodePtr node; /* pointer to the current node */
xmlRegExecCtxtPtr exec; /* regexp runtime */
} _xmlValidState;
static int
vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
if ((ctxt->vstateMax == 0) || (ctxt->vstateTab == NULL)) {
ctxt->vstateMax = 10;
ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
sizeof(ctxt->vstateTab[0]));
if (ctxt->vstateTab == NULL) {
xmlVErrMemory(ctxt, "malloc failed");
return(-1);
}
}
if (ctxt->vstateNr >= ctxt->vstateMax) {
xmlValidState *tmp;
tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
if (tmp == NULL) {
xmlVErrMemory(ctxt, "realloc failed");
return(-1);
}
ctxt->vstateMax *= 2;
ctxt->vstateTab = tmp;
}
ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
ctxt->vstateTab[ctxt->vstateNr].node = node;
if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
if (elemDecl->contModel == NULL)
xmlValidBuildContentModel(ctxt, elemDecl);
if (elemDecl->contModel != NULL) {
ctxt->vstateTab[ctxt->vstateNr].exec =
xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
} else {
ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
xmlErrValidNode(ctxt, (xmlNodePtr) elemDecl,
XML_ERR_INTERNAL_ERROR,
"Failed to build content model regexp for %s\n",
node->name, NULL, NULL);
}
}
return(ctxt->vstateNr++);
}
static int
vstateVPop(xmlValidCtxtPtr ctxt) {
xmlElementPtr elemDecl;
if (ctxt->vstateNr < 1) return(-1);
ctxt->vstateNr--;
elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
ctxt->vstateTab[ctxt->vstateNr].node = NULL;
if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
}
ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
if (ctxt->vstateNr >= 1)
ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
else
ctxt->vstate = NULL;
return(ctxt->vstateNr);
}
#else /* not LIBXML_REGEXP_ENABLED */
/*
* If regexp are not enabled, it uses a home made algorithm less
* complex and easier to
* debug/maintain than a generic NFA -> DFA state based algo. The
* only restriction is on the deepness of the tree limited by the
* size of the occurs bitfield
*
* this is the content of a saved state for rollbacks
*/
#define ROLLBACK_OR 0
#define ROLLBACK_PARENT 1
typedef struct _xmlValidState {
xmlElementContentPtr cont; /* pointer to the content model subtree */
xmlNodePtr node; /* pointer to the current node in the list */
long occurs;/* bitfield for multiple occurrences */
unsigned char depth; /* current depth in the overall tree */
unsigned char state; /* ROLLBACK_XXX */
} _xmlValidState;
#define MAX_RECURSE 25000
#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
#define CONT ctxt->vstate->cont
#define NODE ctxt->vstate->node
#define DEPTH ctxt->vstate->depth
#define OCCURS ctxt->vstate->occurs
#define STATE ctxt->vstate->state
#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
static int
vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
xmlNodePtr node, unsigned char depth, long occurs,
unsigned char state) {
int i = ctxt->vstateNr - 1;
if (ctxt->vstateNr > MAX_RECURSE) {
return(-1);
}
if (ctxt->vstateTab == NULL) {
ctxt->vstateMax = 8;
ctxt->vstateTab = (xmlValidState *) xmlMalloc(
ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
if (ctxt->vstateTab == NULL) {
xmlVErrMemory(ctxt, "malloc failed");
return(-1);
}
}
if (ctxt->vstateNr >= ctxt->vstateMax) {
xmlValidState *tmp;
tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
if (tmp == NULL) {
xmlVErrMemory(ctxt, "malloc failed");
return(-1);
}
ctxt->vstateMax *= 2;
ctxt->vstateTab = tmp;
ctxt->vstate = &ctxt->vstateTab[0];
}
/*
* Don't push on the stack a state already here
*/
if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
(ctxt->vstateTab[i].node == node) &&
(ctxt->vstateTab[i].depth == depth) &&
(ctxt->vstateTab[i].occurs == occurs) &&
(ctxt->vstateTab[i].state == state))
return(ctxt->vstateNr);
ctxt->vstateTab[ctxt->vstateNr].cont = cont;
ctxt->vstateTab[ctxt->vstateNr].node = node;
ctxt->vstateTab[ctxt->vstateNr].depth = depth;
ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
ctxt->vstateTab[ctxt->vstateNr].state = state;
return(ctxt->vstateNr++);
}
static int
vstateVPop(xmlValidCtxtPtr ctxt) {
if (ctxt->vstateNr <= 1) return(-1);
ctxt->vstateNr--;
ctxt->vstate = &ctxt->vstateTab[0];
ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
return(ctxt->vstateNr);
}
#endif /* LIBXML_REGEXP_ENABLED */
static int
nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
{
if (ctxt->nodeMax <= 0) {
ctxt->nodeMax = 4;
ctxt->nodeTab =
(xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
sizeof(ctxt->nodeTab[0]));
if (ctxt->nodeTab == NULL) {
xmlVErrMemory(ctxt, "malloc failed");
ctxt->nodeMax = 0;
return (0);
}
}
if (ctxt->nodeNr >= ctxt->nodeMax) {
xmlNodePtr *tmp;
tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0]));
if (tmp == NULL) {
xmlVErrMemory(ctxt, "realloc failed");
return (0);
}
ctxt->nodeMax *= 2;
ctxt->nodeTab = tmp;
}
ctxt->nodeTab[ctxt->nodeNr] = value;
ctxt->node = value;
return (ctxt->nodeNr++);
}
static xmlNodePtr
nodeVPop(xmlValidCtxtPtr ctxt)
{
xmlNodePtr ret;
if (ctxt->nodeNr <= 0)
return (NULL);
ctxt->nodeNr--;
if (ctxt->nodeNr > 0)
ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
else
ctxt->node = NULL;
ret = ctxt->nodeTab[ctxt->nodeNr];
ctxt->nodeTab[ctxt->nodeNr] = NULL;
return (ret);
}
#ifdef DEBUG_VALID_ALGO
static void
xmlValidPrintNode(xmlNodePtr cur) {
if (cur == NULL) {
xmlGenericError(xmlGenericErrorContext, "null");
return;
}
switch (cur->type) {
case XML_ELEMENT_NODE:
xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
break;
case XML_TEXT_NODE:
xmlGenericError(xmlGenericErrorContext, "text ");
break;
case XML_CDATA_SECTION_NODE:
xmlGenericError(xmlGenericErrorContext, "cdata ");
break;
case XML_ENTITY_REF_NODE:
xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
break;
case XML_PI_NODE:
xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
break;
case XML_COMMENT_NODE:
xmlGenericError(xmlGenericErrorContext, "comment ");
break;
case XML_ATTRIBUTE_NODE:
xmlGenericError(xmlGenericErrorContext, "?attr? ");
break;
case XML_ENTITY_NODE:
xmlGenericError(xmlGenericErrorContext, "?ent? ");
break;
case XML_DOCUMENT_NODE:
xmlGenericError(xmlGenericErrorContext, "?doc? ");
break;
case XML_DOCUMENT_TYPE_NODE:
xmlGenericError(xmlGenericErrorContext, "?doctype? ");
break;
case XML_DOCUMENT_FRAG_NODE:
xmlGenericError(xmlGenericErrorContext, "?frag? ");
break;
case XML_NOTATION_NODE:
xmlGenericError(xmlGenericErrorContext, "?nota? ");
break;
case XML_HTML_DOCUMENT_NODE:
xmlGenericError(xmlGenericErrorContext, "?html? ");
break;
#ifdef LIBXML_DOCB_ENABLED
case XML_DOCB_DOCUMENT_NODE:
xmlGenericError(xmlGenericErrorContext, "?docb? ");
break;
#endif
case XML_DTD_NODE:
xmlGenericError(xmlGenericErrorContext, "?dtd? ");
break;
case XML_ELEMENT_DECL:
xmlGenericError(xmlGenericErrorContext, "?edecl? ");
break;
case XML_ATTRIBUTE_DECL:
xmlGenericError(xmlGenericErrorContext, "?adecl? ");
break;
case XML_ENTITY_DECL:
xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
break;
case XML_NAMESPACE_DECL:
xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
break;
case XML_XINCLUDE_START:
xmlGenericError(xmlGenericErrorContext, "incstart ");
break;
case XML_XINCLUDE_END:
xmlGenericError(xmlGenericErrorContext, "incend ");
break;
}
}
static void
xmlValidPrintNodeList(xmlNodePtr cur) {
if (cur == NULL)
xmlGenericError(xmlGenericErrorContext, "null ");
while (cur != NULL) {
xmlValidPrintNode(cur);
cur = cur->next;
}
}
static void
xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
char expr[5000];
expr[0] = 0;
xmlGenericError(xmlGenericErrorContext, "valid: ");
xmlValidPrintNodeList(cur);
xmlGenericError(xmlGenericErrorContext, "against ");
xmlSnprintfElementContent(expr, 5000, cont, 1);
xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
}
static void
xmlValidDebugState(xmlValidStatePtr state) {
xmlGenericError(xmlGenericErrorContext, "(");
if (state->cont == NULL)
xmlGenericError(xmlGenericErrorContext, "null,");
else
switch (state->cont->type) {
case XML_ELEMENT_CONTENT_PCDATA:
xmlGenericError(xmlGenericErrorContext, "pcdata,");
break;
case XML_ELEMENT_CONTENT_ELEMENT:
xmlGenericError(xmlGenericErrorContext, "%s,",
state->cont->name);
break;
case XML_ELEMENT_CONTENT_SEQ:
xmlGenericError(xmlGenericErrorContext, "seq,");
break;
case XML_ELEMENT_CONTENT_OR:
xmlGenericError(xmlGenericErrorContext, "or,");
break;
}
xmlValidPrintNode(state->node);
xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
state->depth, state->occurs, state->state);
}
static void
xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
int i, j;
xmlGenericError(xmlGenericErrorContext, "state: ");
xmlValidDebugState(ctxt->vstate);
xmlGenericError(xmlGenericErrorContext, " stack: %d ",
ctxt->vstateNr - 1);
for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
xmlValidDebugState(&ctxt->vstateTab[j]);
xmlGenericError(xmlGenericErrorContext, "\n");
}
/*****
#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
*****/
#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
#define DEBUG_VALID_MSG(m) \
xmlGenericError(xmlGenericErrorContext, "%s\n", m);
#else
#define DEBUG_VALID_STATE(n,c)
#define DEBUG_VALID_MSG(m)
#endif
/* TODO: use hash table for accesses to elem and attribute definitions */
#define CHECK_DTD \
if (doc == NULL) return(0); \
else if ((doc->intSubset == NULL) && \
(doc->extSubset == NULL)) return(0)
#ifdef LIBXML_REGEXP_ENABLED
/************************************************************************
* *
* Content model validation based on the regexps *
* *
************************************************************************/
/**
* xmlValidBuildAContentModel:
* @content: the content model
* @ctxt: the schema parser context
* @name: the element name whose content is being built
*
* Generate the automata sequence needed for that type
*
* Returns 1 if successful or 0 in case of error.
*/
static int
xmlValidBuildAContentModel(xmlElementContentPtr content,
xmlValidCtxtPtr ctxt,
const xmlChar *name) {
if (content == NULL) {
xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
"Found NULL content in content model of %s\n",
name, NULL, NULL);
return(0);
}
switch (content->type) {
case XML_ELEMENT_CONTENT_PCDATA:
xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
"Found PCDATA in content model of %s\n",
name, NULL, NULL);
return(0);
break;
case XML_ELEMENT_CONTENT_ELEMENT: {
xmlAutomataStatePtr oldstate = ctxt->state;
xmlChar fn[50];
xmlChar *fullname;
fullname = xmlBuildQName(content->name, content->prefix, fn, 50);
if (fullname == NULL) {
xmlVErrMemory(ctxt, "Building content model");
return(0);
}
switch (content->ocur) {
case XML_ELEMENT_CONTENT_ONCE:
ctxt->state = xmlAutomataNewTransition(ctxt->am,
ctxt->state, NULL, fullname, NULL);
break;
case XML_ELEMENT_CONTENT_OPT:
ctxt->state = xmlAutomataNewTransition(ctxt->am,
ctxt->state, NULL, fullname, NULL);
xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
break;
case XML_ELEMENT_CONTENT_PLUS:
ctxt->state = xmlAutomataNewTransition(ctxt->am,
ctxt->state, NULL, fullname, NULL);
xmlAutomataNewTransition(ctxt->am, ctxt->state,
ctxt->state, fullname, NULL);
break;
case XML_ELEMENT_CONTENT_MULT:
ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
ctxt->state, NULL);
xmlAutomataNewTransition(ctxt->am,
ctxt->state, ctxt->state, fullname, NULL);
break;
}
if ((fullname != fn) && (fullname != content->name))
xmlFree(fullname);
break;
}
case XML_ELEMENT_CONTENT_SEQ: {
xmlAutomataStatePtr oldstate, oldend;
xmlElementContentOccur ocur;
/*
* Simply iterate over the content
*/
oldstate = ctxt->state;
ocur = content->ocur;
if (ocur != XML_ELEMENT_CONTENT_ONCE) {
ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
oldstate = ctxt->state;
}
do {
xmlValidBuildAContentModel(content->c1, ctxt, name);
content = content->c2;
} while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
(content->ocur == XML_ELEMENT_CONTENT_ONCE));
xmlValidBuildAContentModel(content, ctxt, name);
oldend = ctxt->state;
ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
switch (ocur) {
case XML_ELEMENT_CONTENT_ONCE:
break;
case XML_ELEMENT_CONTENT_OPT:
xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
break;
case XML_ELEMENT_CONTENT_MULT:
xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
break;
case XML_ELEMENT_CONTENT_PLUS:
xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
break;
}
break;
}
case XML_ELEMENT_CONTENT_OR: {
xmlAutomataStatePtr oldstate, oldend;
xmlElementContentOccur ocur;
ocur = content->ocur;
if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
(ocur == XML_ELEMENT_CONTENT_MULT)) {
ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
ctxt->state, NULL);
}
oldstate = ctxt->state;
oldend = xmlAutomataNewState(ctxt->am);
/*
* iterate over the subtypes and remerge the end with an
* epsilon transition
*/
do {
ctxt->state = oldstate;
xmlValidBuildAContentModel(content->c1, ctxt, name);
xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
content = content->c2;
} while ((content->type == XML_ELEMENT_CONTENT_OR) &&
(content->ocur == XML_ELEMENT_CONTENT_ONCE));
ctxt->state = oldstate;
xmlValidBuildAContentModel(content, ctxt, name);
xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
switch (ocur) {
case XML_ELEMENT_CONTENT_ONCE:
break;
case XML_ELEMENT_CONTENT_OPT:
xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
break;
case XML_ELEMENT_CONTENT_MULT:
xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
break;
case XML_ELEMENT_CONTENT_PLUS:
xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
break;
}
break;
}
default:
xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
"ContentModel broken for element %s\n",
(const char *) name);
return(0);
}
return(1);
}
/**
* xmlValidBuildContentModel:
* @ctxt: a validation context
* @elem: an element declaration node
*
* (Re)Build the automata associated to the content model of this
* element
*
* Returns 1 in case of success, 0 in case of error
*/
int
xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
if ((ctxt == NULL) || (elem == NULL))
return(0);
if (elem->type != XML_ELEMENT_DECL)
return(0);
if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
return(1);
/* TODO: should we rebuild in this case ? */
if (elem->contModel != NULL) {
if (!xmlRegexpIsDeterminist(elem->contModel)) {
ctxt->valid = 0;
return(0);
}
return(1);
}
ctxt->am = xmlNewAutomata();
if (ctxt->am == NULL) {
xmlErrValidNode(ctxt, (xmlNodePtr) elem,
XML_ERR_INTERNAL_ERROR,
"Cannot create automata for element %s\n",
elem->name, NULL, NULL);
return(0);
}
ctxt->state = xmlAutomataGetInitState(ctxt->am);
xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
xmlAutomataSetFinalState(ctxt->am, ctxt->state);
elem->contModel = xmlAutomataCompile(ctxt->am);
if (xmlRegexpIsDeterminist(elem->contModel) != 1) {
char expr[5000];
expr[0] = 0;
xmlSnprintfElementContent(expr, 5000, elem->content, 1);
xmlErrValidNode(ctxt, (xmlNodePtr) elem,
XML_DTD_CONTENT_NOT_DETERMINIST,
"Content model of %s is not determinist: %s\n",
elem->name, BAD_CAST expr, NULL);
#ifdef DEBUG_REGEXP_ALGO
xmlRegexpPrint(stderr, elem->contModel);
#endif
ctxt->valid = 0;
ctxt->state = NULL;
xmlFreeAutomata(ctxt->am);
ctxt->am = NULL;
return(0);
}
ctxt->state = NULL;
xmlFreeAutomata(ctxt->am);
ctxt->am = NULL;
return(1);
}
#endif /* LIBXML_REGEXP_ENABLED */
/****************************************************************
* *
* Util functions for data allocation/deallocation *
* *
****************************************************************/
/**
* xmlNewValidCtxt:
*
* Allocate a validation context structure.
*
* Returns NULL if not, otherwise the new validation context structure
*/
xmlValidCtxtPtr xmlNewValidCtxt(void) {
xmlValidCtxtPtr ret;
if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL) {
xmlVErrMemory(NULL, "malloc failed");
return (NULL);
}
(void) memset(ret, 0, sizeof (xmlValidCtxt));
return (ret);
}
/**
* xmlFreeValidCtxt:
* @cur: the validation context to free
*
* Free a validation context structure.
*/
void
xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
if (cur->vstateTab != NULL)
xmlFree(cur->vstateTab);
if (cur->nodeTab != NULL)
xmlFree(cur->nodeTab);
xmlFree(cur);
}
#endif /* LIBXML_VALID_ENABLED */
/**
* xmlNewDocElementContent:
* @doc: the document
* @name: the subelement name or NULL
* @type: the type of element content decl
*
* Allocate an element content structure for the document.
*
* Returns NULL if not, otherwise the new element content structure
*/
xmlElementContentPtr
xmlNewDocElementContent(xmlDocPtr doc, const xmlChar *name,
xmlElementContentType type) {
xmlElementContentPtr ret;
xmlDictPtr dict = NULL;
if (doc != NULL)
dict = doc->dict;
switch(type) {
case XML_ELEMENT_CONTENT_ELEMENT:
if (name == NULL) {
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"xmlNewElementContent : name == NULL !\n",
NULL);
}
break;
case XML_ELEMENT_CONTENT_PCDATA:
case XML_ELEMENT_CONTENT_SEQ:
case XML_ELEMENT_CONTENT_OR:
if (name != NULL) {
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"xmlNewElementContent : name != NULL !\n",
NULL);
}
break;
default:
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"Internal: ELEMENT content corrupted invalid type\n",
NULL);
return(NULL);
}
ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
if (ret == NULL) {
xmlVErrMemory(NULL, "malloc failed");
return(NULL);
}
memset(ret, 0, sizeof(xmlElementContent));
ret->type = type;
ret->ocur = XML_ELEMENT_CONTENT_ONCE;
if (name != NULL) {
int l;
const xmlChar *tmp;
tmp = xmlSplitQName3(name, &l);
if (tmp == NULL) {
if (dict == NULL)
ret->name = xmlStrdup(name);
else
ret->name = xmlDictLookup(dict, name, -1);
} else {
if (dict == NULL) {
ret->prefix = xmlStrndup(name, l);
ret->name = xmlStrdup(tmp);
} else {
ret->prefix = xmlDictLookup(dict, name, l);
ret->name = xmlDictLookup(dict, tmp, -1);
}
}
}
return(ret);
}
/**
* xmlNewElementContent:
* @name: the subelement name or NULL
* @type: the type of element content decl
*
* Allocate an element content structure.
* Deprecated in favor of xmlNewDocElementContent
*
* Returns NULL if not, otherwise the new element content structure
*/
xmlElementContentPtr
xmlNewElementContent(const xmlChar *name, xmlElementContentType type) {
return(xmlNewDocElementContent(NULL, name, type));
}
/**
* xmlCopyDocElementContent:
* @doc: the document owning the element declaration
* @cur: An element content pointer.
*
* Build a copy of an element content description.
*
* Returns the new xmlElementContentPtr or NULL in case of error.
*/
xmlElementContentPtr
xmlCopyDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) {
xmlElementContentPtr ret = NULL, prev = NULL, tmp;
xmlDictPtr dict = NULL;
if (cur == NULL) return(NULL);
if (doc != NULL)
dict = doc->dict;
ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
if (ret == NULL) {
xmlVErrMemory(NULL, "malloc failed");
return(NULL);
}
memset(ret, 0, sizeof(xmlElementContent));
ret->type = cur->type;
ret->ocur = cur->ocur;
if (cur->name != NULL) {
if (dict)
ret->name = xmlDictLookup(dict, cur->name, -1);
else
ret->name = xmlStrdup(cur->name);
}
if (cur->prefix != NULL) {
if (dict)
ret->prefix = xmlDictLookup(dict, cur->prefix, -1);
else
ret->prefix = xmlStrdup(cur->prefix);
}
if (cur->c1 != NULL)
ret->c1 = xmlCopyDocElementContent(doc, cur->c1);
if (ret->c1 != NULL)
ret->c1->parent = ret;
if (cur->c2 != NULL) {
prev = ret;
cur = cur->c2;
while (cur != NULL) {
tmp = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
if (tmp == NULL) {
xmlVErrMemory(NULL, "malloc failed");
return(ret);
}
memset(tmp, 0, sizeof(xmlElementContent));
tmp->type = cur->type;
tmp->ocur = cur->ocur;
prev->c2 = tmp;
if (cur->name != NULL) {
if (dict)
tmp->name = xmlDictLookup(dict, cur->name, -1);
else
tmp->name = xmlStrdup(cur->name);
}
if (cur->prefix != NULL) {
if (dict)
tmp->prefix = xmlDictLookup(dict, cur->prefix, -1);
else
tmp->prefix = xmlStrdup(cur->prefix);
}
if (cur->c1 != NULL)
tmp->c1 = xmlCopyDocElementContent(doc,cur->c1);
if (tmp->c1 != NULL)
tmp->c1->parent = ret;
prev = tmp;
cur = cur->c2;
}
}
return(ret);
}
/**
* xmlCopyElementContent:
* @cur: An element content pointer.
*
* Build a copy of an element content description.
* Deprecated, use xmlCopyDocElementContent instead
*
* Returns the new xmlElementContentPtr or NULL in case of error.
*/
xmlElementContentPtr
xmlCopyElementContent(xmlElementContentPtr cur) {
return(xmlCopyDocElementContent(NULL, cur));
}
/**
* xmlFreeDocElementContent:
* @doc: the document owning the element declaration
* @cur: the element content tree to free
*
* Free an element content structure. The whole subtree is removed.
*/
void
xmlFreeDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) {
xmlDictPtr dict = NULL;
size_t depth = 0;
if (cur == NULL)
return;
if (doc != NULL)
dict = doc->dict;
while (1) {
xmlElementContentPtr parent;
while ((cur->c1 != NULL) || (cur->c2 != NULL)) {
cur = (cur->c1 != NULL) ? cur->c1 : cur->c2;
depth += 1;
}
switch (cur->type) {
case XML_ELEMENT_CONTENT_PCDATA:
case XML_ELEMENT_CONTENT_ELEMENT:
case XML_ELEMENT_CONTENT_SEQ:
case XML_ELEMENT_CONTENT_OR:
break;
default:
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"Internal: ELEMENT content corrupted invalid type\n",
NULL);
return;
}
if (dict) {
if ((cur->name != NULL) && (!xmlDictOwns(dict, cur->name)))
xmlFree((xmlChar *) cur->name);
if ((cur->prefix != NULL) && (!xmlDictOwns(dict, cur->prefix)))
xmlFree((xmlChar *) cur->prefix);
} else {
if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
}
parent = cur->parent;
if ((depth == 0) || (parent == NULL)) {
xmlFree(cur);
break;
}
if (cur == parent->c1)
parent->c1 = NULL;
else
parent->c2 = NULL;
xmlFree(cur);
if (parent->c2 != NULL) {
cur = parent->c2;
} else {
depth -= 1;
cur = parent;
}
}
}
/**
* xmlFreeElementContent:
* @cur: the element content tree to free
*
* Free an element content structure. The whole subtree is removed.
* Deprecated, use xmlFreeDocElementContent instead
*/
void
xmlFreeElementContent(xmlElementContentPtr cur) {
xmlFreeDocElementContent(NULL, cur);
}
#ifdef LIBXML_OUTPUT_ENABLED
/**
* xmlDumpElementOccur:
* @buf: An XML buffer
* @cur: An element table
*
* Dump the occurrence operator of an element.
*/
static void
xmlDumpElementOccur(xmlBufferPtr buf, xmlElementContentPtr cur) {
switch (cur->ocur) {
case XML_ELEMENT_CONTENT_ONCE:
break;
case XML_ELEMENT_CONTENT_OPT:
xmlBufferWriteChar(buf, "?");
break;
case XML_ELEMENT_CONTENT_MULT:
xmlBufferWriteChar(buf, "*");
break;
case XML_ELEMENT_CONTENT_PLUS:
xmlBufferWriteChar(buf, "+");
break;
}
}
/**
* xmlDumpElementContent:
* @buf: An XML buffer
* @content: An element table
*
* This will dump the content of the element table as an XML DTD definition
*/
static void
xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content) {
xmlElementContentPtr cur;
if (content == NULL) return;
xmlBufferWriteChar(buf, "(");
cur = content;
do {
if (cur == NULL) return;
switch (cur->type) {
case XML_ELEMENT_CONTENT_PCDATA:
xmlBufferWriteChar(buf, "#PCDATA");
break;
case XML_ELEMENT_CONTENT_ELEMENT:
if (cur->prefix != NULL) {
xmlBufferWriteCHAR(buf, cur->prefix);
xmlBufferWriteChar(buf, ":");
}
xmlBufferWriteCHAR(buf, cur->name);
break;
case XML_ELEMENT_CONTENT_SEQ:
case XML_ELEMENT_CONTENT_OR:
if ((cur != content) &&
(cur->parent != NULL) &&
((cur->type != cur->parent->type) ||
(cur->ocur != XML_ELEMENT_CONTENT_ONCE)))
xmlBufferWriteChar(buf, "(");
cur = cur->c1;
continue;
default:
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"Internal: ELEMENT cur corrupted invalid type\n",
NULL);
}
while (cur != content) {
xmlElementContentPtr parent = cur->parent;
if (parent == NULL) return;
if (((cur->type == XML_ELEMENT_CONTENT_OR) ||
(cur->type == XML_ELEMENT_CONTENT_SEQ)) &&
((cur->type != parent->type) ||
(cur->ocur != XML_ELEMENT_CONTENT_ONCE)))
xmlBufferWriteChar(buf, ")");
xmlDumpElementOccur(buf, cur);
if (cur == parent->c1) {
if (parent->type == XML_ELEMENT_CONTENT_SEQ)
xmlBufferWriteChar(buf, " , ");
else if (parent->type == XML_ELEMENT_CONTENT_OR)
xmlBufferWriteChar(buf, " | ");
cur = parent->c2;
break;
}
cur = parent;
}
} while (cur != content);
xmlBufferWriteChar(buf, ")");
xmlDumpElementOccur(buf, content);
}
/**
* xmlSprintfElementContent:
* @buf: an output buffer
* @content: An element table
* @englob: 1 if one must print the englobing parenthesis, 0 otherwise
*
* Deprecated, unsafe, use xmlSnprintfElementContent
*/
void
xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
xmlElementContentPtr content ATTRIBUTE_UNUSED,
int englob ATTRIBUTE_UNUSED) {
}
#endif /* LIBXML_OUTPUT_ENABLED */
/**
* xmlSnprintfElementContent:
* @buf: an output buffer
* @size: the buffer size
* @content: An element table
* @englob: 1 if one must print the englobing parenthesis, 0 otherwise
*
* This will dump the content of the element content definition
* Intended just for the debug routine
*/
void
xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int englob) {
int len;
if (content == NULL) return;
len = strlen(buf);
if (size - len < 50) {
if ((size - len > 4) && (buf[len - 1] != '.'))
strcat(buf, " ...");
return;
}
if (englob) strcat(buf, "(");
switch (content->type) {
case XML_ELEMENT_CONTENT_PCDATA:
strcat(buf, "#PCDATA");
break;
case XML_ELEMENT_CONTENT_ELEMENT: {
int qnameLen = xmlStrlen(content->name);
if (content->prefix != NULL)
qnameLen += xmlStrlen(content->prefix) + 1;
if (size - len < qnameLen + 10) {
strcat(buf, " ...");
return;
}
if (content->prefix != NULL) {
strcat(buf, (char *) content->prefix);
strcat(buf, ":");
}
if (content->name != NULL)
strcat(buf, (char *) content->name);
break;
}
case XML_ELEMENT_CONTENT_SEQ:
if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
(content->c1->type == XML_ELEMENT_CONTENT_SEQ))
xmlSnprintfElementContent(buf, size, content->c1, 1);
else
xmlSnprintfElementContent(buf, size, content->c1, 0);
len = strlen(buf);
if (size - len < 50) {
if ((size - len > 4) && (buf[len - 1] != '.'))
strcat(buf, " ...");
return;
}
strcat(buf, " , ");
if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
(content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
(content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
xmlSnprintfElementContent(buf, size, content->c2, 1);
else
xmlSnprintfElementContent(buf, size, content->c2, 0);
break;
case XML_ELEMENT_CONTENT_OR:
if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
(content->c1->type == XML_ELEMENT_CONTENT_SEQ))
xmlSnprintfElementContent(buf, size, content->c1, 1);
else
xmlSnprintfElementContent(buf, size, content->c1, 0);
len = strlen(buf);
if (size - len < 50) {
if ((size - len > 4) && (buf[len - 1] != '.'))
strcat(buf, " ...");
return;
}
strcat(buf, " | ");
if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
(content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
(content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
xmlSnprintfElementContent(buf, size, content->c2, 1);
else
xmlSnprintfElementContent(buf, size, content->c2, 0);
break;
}
if (size - strlen(buf) <= 2) return;
if (englob)
strcat(buf, ")");
switch (content->ocur) {
case XML_ELEMENT_CONTENT_ONCE:
break;
case XML_ELEMENT_CONTENT_OPT:
strcat(buf, "?");
break;
case XML_ELEMENT_CONTENT_MULT:
strcat(buf, "*");
break;
case XML_ELEMENT_CONTENT_PLUS:
strcat(buf, "+");
break;
}
}
/****************************************************************
* *
* Registration of DTD declarations *
* *
****************************************************************/
/**
* xmlFreeElement:
* @elem: An element
*
* Deallocate the memory used by an element definition
*/
static void
xmlFreeElement(xmlElementPtr elem) {
if (elem == NULL) return;
xmlUnlinkNode((xmlNodePtr) elem);
xmlFreeDocElementContent(elem->doc, elem->content);
if (elem->name != NULL)
xmlFree((xmlChar *) elem->name);
if (elem->prefix != NULL)
xmlFree((xmlChar *) elem->prefix);
#ifdef LIBXML_REGEXP_ENABLED
if (elem->contModel != NULL)
xmlRegFreeRegexp(elem->contModel);
#endif
xmlFree(elem);
}
/**
* xmlAddElementDecl:
* @ctxt: the validation context
* @dtd: pointer to the DTD
* @name: the entity name
* @type: the element type
* @content: the element content tree or NULL
*
* Register a new element declaration
*
* Returns NULL if not, otherwise the entity
*/
xmlElementPtr
xmlAddElementDecl(xmlValidCtxtPtr ctxt,
xmlDtdPtr dtd, const xmlChar *name,
xmlElementTypeVal type,
xmlElementContentPtr content) {
xmlElementPtr ret;
xmlElementTablePtr table;
xmlAttributePtr oldAttributes = NULL;
xmlChar *ns, *uqname;
if (dtd == NULL) {
return(NULL);
}
if (name == NULL) {
return(NULL);
}
switch (type) {
case XML_ELEMENT_TYPE_EMPTY:
if (content != NULL) {
xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
"xmlAddElementDecl: content != NULL for EMPTY\n",
NULL);
return(NULL);
}
break;
case XML_ELEMENT_TYPE_ANY:
if (content != NULL) {
xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
"xmlAddElementDecl: content != NULL for ANY\n",
NULL);
return(NULL);
}
break;
case XML_ELEMENT_TYPE_MIXED:
if (content == NULL) {
xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
"xmlAddElementDecl: content == NULL for MIXED\n",
NULL);
return(NULL);
}
break;
case XML_ELEMENT_TYPE_ELEMENT:
if (content == NULL) {
xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
"xmlAddElementDecl: content == NULL for ELEMENT\n",
NULL);
return(NULL);
}
break;
default:
xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
"Internal: ELEMENT decl corrupted invalid type\n",
NULL);
return(NULL);
}
/*
* check if name is a QName
*/
uqname = xmlSplitQName2(name, &ns);
if (uqname != NULL)
name = uqname;
/*
* Create the Element table if needed.
*/
table = (xmlElementTablePtr) dtd->elements;
if (table == NULL) {
xmlDictPtr dict = NULL;
if (dtd->doc != NULL)
dict = dtd->doc->dict;
table = xmlHashCreateDict(0, dict);
dtd->elements = (void *) table;
}
if (table == NULL) {
xmlVErrMemory(ctxt,
"xmlAddElementDecl: Table creation failed!\n");
if (uqname != NULL)
xmlFree(uqname);
if (ns != NULL)
xmlFree(ns);
return(NULL);
}
/*
* lookup old attributes inserted on an undefined element in the
* internal subset.
*/
if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
oldAttributes = ret->attributes;
ret->attributes = NULL;
xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
xmlFreeElement(ret);
}
}
/*
* The element may already be present if one of its attribute
* was registered first
*/
ret = xmlHashLookup2(table, name, ns);
if (ret != NULL) {
if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
#ifdef LIBXML_VALID_ENABLED
/*
* The element is already defined in this DTD.
*/
xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED,
"Redefinition of element %s\n",
name, NULL, NULL);
#endif /* LIBXML_VALID_ENABLED */
if (uqname != NULL)
xmlFree(uqname);
if (ns != NULL)
xmlFree(ns);
return(NULL);
}
if (ns != NULL) {
xmlFree(ns);
ns = NULL;
}
} else {
ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
if (ret == NULL) {
xmlVErrMemory(ctxt, "malloc failed");
if (uqname != NULL)
xmlFree(uqname);
if (ns != NULL)
xmlFree(ns);
return(NULL);
}
memset(ret, 0, sizeof(xmlElement));
ret->type = XML_ELEMENT_DECL;
/*
* fill the structure.
*/
ret->name = xmlStrdup(name);
if (ret->name == NULL) {
xmlVErrMemory(ctxt, "malloc failed");
if (uqname != NULL)
xmlFree(uqname);
if (ns != NULL)
xmlFree(ns);
xmlFree(ret);
return(NULL);
}
ret->prefix = ns;
/*
* Validity Check:
* Insertion must not fail
*/
if (xmlHashAddEntry2(table, name, ns, ret)) {
#ifdef LIBXML_VALID_ENABLED
/*
* The element is already defined in this DTD.
*/
xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED,
"Redefinition of element %s\n",
name, NULL, NULL);
#endif /* LIBXML_VALID_ENABLED */
xmlFreeElement(ret);
if (uqname != NULL)
xmlFree(uqname);
return(NULL);
}
/*
* For new element, may have attributes from earlier
* definition in internal subset
*/
ret->attributes = oldAttributes;
}
/*
* Finish to fill the structure.
*/
ret->etype = type;
/*
* Avoid a stupid copy when called by the parser
* and flag it by setting a special parent value
* so the parser doesn't unallocate it.
*/
if ((ctxt != NULL) &&
((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
(ctxt->finishDtd == XML_CTXT_FINISH_DTD_1))) {
ret->content = content;
if (content != NULL)
content->parent = (xmlElementContentPtr) 1;
} else {
ret->content = xmlCopyDocElementContent(dtd->doc, content);
}
/*
* Link it to the DTD
*/
ret->parent = dtd;
ret->doc = dtd->doc;
if (dtd->last == NULL) {
dtd->children = dtd->last = (xmlNodePtr) ret;
} else {
dtd->last->next = (xmlNodePtr) ret;
ret->prev = dtd->last;
dtd->last = (xmlNodePtr) ret;
}
if (uqname != NULL)
xmlFree(uqname);
return(ret);
}
static void
xmlFreeElementTableEntry(void *elem, const xmlChar *name ATTRIBUTE_UNUSED) {
xmlFreeElement((xmlElementPtr) elem);
}
/**
* xmlFreeElementTable:
* @table: An element table
*
* Deallocate the memory used by an element hash table.
*/
void
xmlFreeElementTable(xmlElementTablePtr table) {
xmlHashFree(table, xmlFreeElementTableEntry);
}
#ifdef LIBXML_TREE_ENABLED
/**
* xmlCopyElement:
* @elem: An element
*
* Build a copy of an element.
*
* Returns the new xmlElementPtr or NULL in case of error.
*/
static void *
xmlCopyElement(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
xmlElementPtr elem = (xmlElementPtr) payload;
xmlElementPtr cur;
cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
if (cur == NULL) {
xmlVErrMemory(NULL, "malloc failed");
return(NULL);
}
memset(cur, 0, sizeof(xmlElement));
cur->type = XML_ELEMENT_DECL;
cur->etype = elem->etype;
if (elem->name != NULL)
cur->name = xmlStrdup(elem->name);
else
cur->name = NULL;
if (elem->prefix != NULL)
cur->prefix = xmlStrdup(elem->prefix);
else
cur->prefix = NULL;
cur->content = xmlCopyElementContent(elem->content);
/* TODO : rebuild the attribute list on the copy */
cur->attributes = NULL;
return(cur);
}
/**
* xmlCopyElementTable:
* @table: An element table
*
* Build a copy of an element table.
*
* Returns the new xmlElementTablePtr or NULL in case of error.
*/
xmlElementTablePtr
xmlCopyElementTable(xmlElementTablePtr table) {
return((xmlElementTablePtr) xmlHashCopy(table, xmlCopyElement));
}
#endif /* LIBXML_TREE_ENABLED */
#ifdef LIBXML_OUTPUT_ENABLED
/**
* xmlDumpElementDecl:
* @buf: the XML buffer output
* @elem: An element table
*
* This will dump the content of the element declaration as an XML
* DTD definition
*/
void
xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
if ((buf == NULL) || (elem == NULL))
return;
switch (elem->etype) {
case XML_ELEMENT_TYPE_EMPTY:
xmlBufferWriteChar(buf, "<!ELEMENT ");
if (elem->prefix != NULL) {
xmlBufferWriteCHAR(buf, elem->prefix);
xmlBufferWriteChar(buf, ":");
}
xmlBufferWriteCHAR(buf, elem->name);
xmlBufferWriteChar(buf, " EMPTY>\n");
break;
case XML_ELEMENT_TYPE_ANY:
xmlBufferWriteChar(buf, "<!ELEMENT ");
if (elem->prefix != NULL) {
xmlBufferWriteCHAR(buf, elem->prefix);
xmlBufferWriteChar(buf, ":");
}
xmlBufferWriteCHAR(buf, elem->name);
xmlBufferWriteChar(buf, " ANY>\n");
break;
case XML_ELEMENT_TYPE_MIXED:
xmlBufferWriteChar(buf, "<!ELEMENT ");
if (elem->prefix != NULL) {
xmlBufferWriteCHAR(buf, elem->prefix);
xmlBufferWriteChar(buf, ":");
}
xmlBufferWriteCHAR(buf, elem->name);
xmlBufferWriteChar(buf, " ");
xmlDumpElementContent(buf, elem->content);
xmlBufferWriteChar(buf, ">\n");
break;
case XML_ELEMENT_TYPE_ELEMENT:
xmlBufferWriteChar(buf, "<!ELEMENT ");
if (elem->prefix != NULL) {
xmlBufferWriteCHAR(buf, elem->prefix);
xmlBufferWriteChar(buf, ":");
}
xmlBufferWriteCHAR(buf, elem->name);
xmlBufferWriteChar(buf, " ");
xmlDumpElementContent(buf, elem->content);
xmlBufferWriteChar(buf, ">\n");
break;
default:
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"Internal: ELEMENT struct corrupted invalid type\n",
NULL);
}
}
/**
* xmlDumpElementDeclScan:
* @elem: An element table
* @buf: the XML buffer output
*
* This routine is used by the hash scan function. It just reverses
* the arguments.
*/
static void
xmlDumpElementDeclScan(void *elem, void *buf,
const xmlChar *name ATTRIBUTE_UNUSED) {
xmlDumpElementDecl((xmlBufferPtr) buf, (xmlElementPtr) elem);
}
/**
* xmlDumpElementTable:
* @buf: the XML buffer output
* @table: An element table
*
* This will dump the content of the element table as an XML DTD definition
*/
void
xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
if ((buf == NULL) || (table == NULL))
return;
xmlHashScan(table, xmlDumpElementDeclScan, buf);
}
#endif /* LIBXML_OUTPUT_ENABLED */
/**
* xmlCreateEnumeration:
* @name: the enumeration name or NULL
*
* create and initialize an enumeration attribute node.
*
* Returns the xmlEnumerationPtr just created or NULL in case
* of error.
*/
xmlEnumerationPtr
xmlCreateEnumeration(const xmlChar *name) {
xmlEnumerationPtr ret;
ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
if (ret == NULL) {
xmlVErrMemory(NULL, "malloc failed");
return(NULL);
}
memset(ret, 0, sizeof(xmlEnumeration));
if (name != NULL)
ret->name = xmlStrdup(name);
return(ret);
}
/**
* xmlFreeEnumeration:
* @cur: the tree to free.
*
* free an enumeration attribute node (recursive).
*/
void
xmlFreeEnumeration(xmlEnumerationPtr cur) {
if (cur == NULL) return;
if (cur->next != NULL) xmlFreeEnumeration(cur->next);
if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
xmlFree(cur);
}
#ifdef LIBXML_TREE_ENABLED
/**
* xmlCopyEnumeration:
* @cur: the tree to copy.
*
* Copy an enumeration attribute node (recursive).
*
* Returns the xmlEnumerationPtr just created or NULL in case
* of error.
*/
xmlEnumerationPtr
xmlCopyEnumeration(xmlEnumerationPtr cur) {
xmlEnumerationPtr ret;
if (cur == NULL) return(NULL);
ret = xmlCreateEnumeration((xmlChar *) cur->name);
if (ret == NULL) return(NULL);
if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
else ret->next = NULL;
return(ret);
}
#endif /* LIBXML_TREE_ENABLED */
#ifdef LIBXML_OUTPUT_ENABLED
/**
* xmlDumpEnumeration:
* @buf: the XML buffer output
* @enum: An enumeration
*
* This will dump the content of the enumeration
*/
static void
xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
if ((buf == NULL) || (cur == NULL))
return;
xmlBufferWriteCHAR(buf, cur->name);
if (cur->next == NULL)
xmlBufferWriteChar(buf, ")");
else {
xmlBufferWriteChar(buf, " | ");
xmlDumpEnumeration(buf, cur->next);
}
}
#endif /* LIBXML_OUTPUT_ENABLED */
#ifdef LIBXML_VALID_ENABLED
/**
* xmlScanIDAttributeDecl:
* @ctxt: the validation context
* @elem: the element name
* @err: whether to raise errors here
*
* Verify that the element don't have too many ID attributes
* declared.
*
* Returns the number of ID attributes found.
*/
static int
xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem, int err) {
xmlAttributePtr cur;
int ret = 0;
if (elem == NULL) return(0);
cur = elem->attributes;
while (cur != NULL) {
if (cur->atype == XML_ATTRIBUTE_ID) {
ret ++;
if ((ret > 1) && (err))
xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_MULTIPLE_ID,
"Element %s has too many ID attributes defined : %s\n",
elem->name, cur->name, NULL);
}
cur = cur->nexth;
}
return(ret);
}
#endif /* LIBXML_VALID_ENABLED */
/**
* xmlFreeAttribute:
* @elem: An attribute
*
* Deallocate the memory used by an attribute definition
*/
static void
xmlFreeAttribute(xmlAttributePtr attr) {
xmlDictPtr dict;
if (attr == NULL) return;
if (attr->doc != NULL)
dict = attr->doc->dict;
else
dict = NULL;
xmlUnlinkNode((xmlNodePtr) attr);
if (attr->tree != NULL)
xmlFreeEnumeration(attr->tree);
if (dict) {
if ((attr->elem != NULL) && (!xmlDictOwns(dict, attr->elem)))
xmlFree((xmlChar *) attr->elem);
if ((attr->name != NULL) && (!xmlDictOwns(dict, attr->name)))
xmlFree((xmlChar *) attr->name);
if ((attr->prefix != NULL) && (!xmlDictOwns(dict, attr->prefix)))
xmlFree((xmlChar *) attr->prefix);
if ((attr->defaultValue != NULL) &&
(!xmlDictOwns(dict, attr->defaultValue)))
xmlFree((xmlChar *) attr->defaultValue);
} else {
if (attr->elem != NULL)
xmlFree((xmlChar *) attr->elem);
if (attr->name != NULL)
xmlFree((xmlChar *) attr->name);
if (attr->defaultValue != NULL)
xmlFree((xmlChar *) attr->defaultValue);
if (attr->prefix != NULL)
xmlFree((xmlChar *) attr->prefix);
}
xmlFree(attr);
}
/**
* xmlAddAttributeDecl:
* @ctxt: the validation context
* @dtd: pointer to the DTD
* @elem: the element name
* @name: the attribute name
* @ns: the attribute namespace prefix
* @type: the attribute type
* @def: the attribute default type
* @defaultValue: the attribute default value
* @tree: if it's an enumeration, the associated list
*
* Register a new attribute declaration
* Note that @tree becomes the ownership of the DTD
*
* Returns NULL if not new, otherwise the attribute decl
*/
xmlAttributePtr
xmlAddAttributeDecl(xmlValidCtxtPtr ctxt,
xmlDtdPtr dtd, const xmlChar *elem,
const xmlChar *name, const xmlChar *ns,
xmlAttributeType type, xmlAttributeDefault def,
const xmlChar *defaultValue, xmlEnumerationPtr tree) {
xmlAttributePtr ret;
xmlAttributeTablePtr table;
xmlElementPtr elemDef;
xmlDictPtr dict = NULL;
if (dtd == NULL) {
xmlFreeEnumeration(tree);
return(NULL);
}
if (name == NULL) {
xmlFreeEnumeration(tree);
return(NULL);
}
if (elem == NULL) {
xmlFreeEnumeration(tree);
return(NULL);
}
if (dtd->doc != NULL)
dict = dtd->doc->dict;
#ifdef LIBXML_VALID_ENABLED
/*
* Check the type and possibly the default value.
*/
switch (type) {
case XML_ATTRIBUTE_CDATA:
break;
case XML_ATTRIBUTE_ID:
break;
case XML_ATTRIBUTE_IDREF:
break;
case XML_ATTRIBUTE_IDREFS:
break;
case XML_ATTRIBUTE_ENTITY:
break;
case XML_ATTRIBUTE_ENTITIES:
break;
case XML_ATTRIBUTE_NMTOKEN:
break;
case XML_ATTRIBUTE_NMTOKENS:
break;
case XML_ATTRIBUTE_ENUMERATION:
break;
case XML_ATTRIBUTE_NOTATION:
break;
default:
xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
"Internal: ATTRIBUTE struct corrupted invalid type\n",
NULL);
xmlFreeEnumeration(tree);
return(NULL);
}
if ((defaultValue != NULL) &&
(!xmlValidateAttributeValueInternal(dtd->doc, type, defaultValue))) {
xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_DEFAULT,
"Attribute %s of %s: invalid default value\n",
elem, name, defaultValue);
defaultValue = NULL;
if (ctxt != NULL)
ctxt->valid = 0;
}
#endif /* LIBXML_VALID_ENABLED */
/*
* Check first that an attribute defined in the external subset wasn't
* already defined in the internal subset
*/
if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
(dtd->doc->intSubset != NULL) &&
(dtd->doc->intSubset->attributes != NULL)) {
ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
if (ret != NULL) {
xmlFreeEnumeration(tree);
return(NULL);
}
}
/*
* Create the Attribute table if needed.
*/
table = (xmlAttributeTablePtr) dtd->attributes;
if (table == NULL) {
table = xmlHashCreateDict(0, dict);
dtd->attributes = (void *) table;
}
if (table == NULL) {
xmlVErrMemory(ctxt,
"xmlAddAttributeDecl: Table creation failed!\n");
xmlFreeEnumeration(tree);
return(NULL);
}
ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
if (ret == NULL) {
xmlVErrMemory(ctxt, "malloc failed");
xmlFreeEnumeration(tree);
return(NULL);
}
memset(ret, 0, sizeof(xmlAttribute));
ret->type = XML_ATTRIBUTE_DECL;
/*
* fill the structure.
*/
ret->atype = type;
/*
* doc must be set before possible error causes call
* to xmlFreeAttribute (because it's used to check on
* dict use)
*/
ret->doc = dtd->doc;
if (dict) {
ret->name = xmlDictLookup(dict, name, -1);
ret->prefix = xmlDictLookup(dict, ns, -1);
ret->elem = xmlDictLookup(dict, elem, -1);
} else {
ret->name = xmlStrdup(name);
ret->prefix = xmlStrdup(ns);
ret->elem = xmlStrdup(elem);
}
ret->def = def;
ret->tree = tree;
if (defaultValue != NULL) {
if (dict)
ret->defaultValue = xmlDictLookup(dict, defaultValue, -1);
else
ret->defaultValue = xmlStrdup(defaultValue);
}
/*
* Validity Check:
* Search the DTD for previous declarations of the ATTLIST
*/
if (xmlHashAddEntry3(table, ret->name, ret->prefix, ret->elem, ret) < 0) {
#ifdef LIBXML_VALID_ENABLED
/*
* The attribute is already defined in this DTD.
*/
xmlErrValidWarning(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_REDEFINED,
"Attribute %s of element %s: already defined\n",
name, elem, NULL);
#endif /* LIBXML_VALID_ENABLED */
xmlFreeAttribute(ret);
return(NULL);
}
/*
* Validity Check:
* Multiple ID per element
*/
elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
if (elemDef != NULL) {
#ifdef LIBXML_VALID_ENABLED
if ((type == XML_ATTRIBUTE_ID) &&
(xmlScanIDAttributeDecl(NULL, elemDef, 1) != 0)) {
xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID,
"Element %s has too may ID attributes defined : %s\n",
elem, name, NULL);
if (ctxt != NULL)
ctxt->valid = 0;
}
#endif /* LIBXML_VALID_ENABLED */
/*
* Insert namespace default def first they need to be
* processed first.
*/
if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
((ret->prefix != NULL &&
(xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
ret->nexth = elemDef->attributes;
elemDef->attributes = ret;
} else {
xmlAttributePtr tmp = elemDef->attributes;
while ((tmp != NULL) &&
((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
((ret->prefix != NULL &&
(xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
if (tmp->nexth == NULL)
break;
tmp = tmp->nexth;
}
if (tmp != NULL) {
ret->nexth = tmp->nexth;
tmp->nexth = ret;
} else {
ret->nexth = elemDef->attributes;
elemDef->attributes = ret;
}
}
}
/*
* Link it to the DTD
*/
ret->parent = dtd;
if (dtd->last == NULL) {
dtd->children = dtd->last = (xmlNodePtr) ret;
} else {
dtd->last->next = (xmlNodePtr) ret;
ret->prev = dtd->last;
dtd->last = (xmlNodePtr) ret;
}
return(ret);
}
static void
xmlFreeAttributeTableEntry(void *attr, const xmlChar *name ATTRIBUTE_UNUSED) {
xmlFreeAttribute((xmlAttributePtr) attr);
}
/**
* xmlFreeAttributeTable:
* @table: An attribute table
*
* Deallocate the memory used by an entities hash table.
*/
void
xmlFreeAttributeTable(xmlAttributeTablePtr table) {
xmlHashFree(table, xmlFreeAttributeTableEntry);
}
#ifdef LIBXML_TREE_ENABLED
/**
* xmlCopyAttribute:
* @attr: An attribute
*
* Build a copy of an attribute.
*
* Returns the new xmlAttributePtr or NULL in case of error.
*/
static void *
xmlCopyAttribute(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
xmlAttributePtr attr = (xmlAttributePtr) payload;
xmlAttributePtr cur;
cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
if (cur == NULL) {
xmlVErrMemory(NULL, "malloc failed");
return(NULL);
}
memset(cur, 0, sizeof(xmlAttribute));
cur->type = XML_ATTRIBUTE_DECL;
cur->atype = attr->atype;
cur->def = attr->def;
cur->tree = xmlCopyEnumeration(attr->tree);
if (attr->elem != NULL)
cur->elem = xmlStrdup(attr->elem);
if (attr->name != NULL)
cur->name = xmlStrdup(attr->name);
if (attr->prefix != NULL)
cur->prefix = xmlStrdup(attr->prefix);
if (attr->defaultValue != NULL)
cur->defaultValue = xmlStrdup(attr->defaultValue);
return(cur);
}
/**
* xmlCopyAttributeTable:
* @table: An attribute table
*
* Build a copy of an attribute table.
*
* Returns the new xmlAttributeTablePtr or NULL in case of error.
*/
xmlAttributeTablePtr
xmlCopyAttributeTable(xmlAttributeTablePtr table) {
return((xmlAttributeTablePtr) xmlHashCopy(table, xmlCopyAttribute));
}
#endif /* LIBXML_TREE_ENABLED */
#ifdef LIBXML_OUTPUT_ENABLED
/**
* xmlDumpAttributeDecl:
* @buf: the XML buffer output
* @attr: An attribute declaration
*
* This will dump the content of the attribute declaration as an XML
* DTD definition
*/
void
xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
if ((buf == NULL) || (attr == NULL))
return;
xmlBufferWriteChar(buf, "<!ATTLIST ");
xmlBufferWriteCHAR(buf, attr->elem);
xmlBufferWriteChar(buf, " ");
if (attr->prefix != NULL) {
xmlBufferWriteCHAR(buf, attr->prefix);
xmlBufferWriteChar(buf, ":");
}
xmlBufferWriteCHAR(buf, attr->name);
switch (attr->atype) {
case XML_ATTRIBUTE_CDATA:
xmlBufferWriteChar(buf, " CDATA");
break;
case XML_ATTRIBUTE_ID:
xmlBufferWriteChar(buf, " ID");
break;
case XML_ATTRIBUTE_IDREF:
xmlBufferWriteChar(buf, " IDREF");
break;
case XML_ATTRIBUTE_IDREFS:
xmlBufferWriteChar(buf, " IDREFS");
break;
case XML_ATTRIBUTE_ENTITY:
xmlBufferWriteChar(buf, " ENTITY");
break;
case XML_ATTRIBUTE_ENTITIES:
xmlBufferWriteChar(buf, " ENTITIES");
break;
case XML_ATTRIBUTE_NMTOKEN:
xmlBufferWriteChar(buf, " NMTOKEN");
break;
case XML_ATTRIBUTE_NMTOKENS:
xmlBufferWriteChar(buf, " NMTOKENS");
break;
case XML_ATTRIBUTE_ENUMERATION:
xmlBufferWriteChar(buf, " (");
xmlDumpEnumeration(buf, attr->tree);
break;
case XML_ATTRIBUTE_NOTATION:
xmlBufferWriteChar(buf, " NOTATION (");
xmlDumpEnumeration(buf, attr->tree);
break;
default:
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"Internal: ATTRIBUTE struct corrupted invalid type\n",
NULL);
}
switch (attr->def) {
case XML_ATTRIBUTE_NONE:
break;
case XML_ATTRIBUTE_REQUIRED:
xmlBufferWriteChar(buf, " #REQUIRED");
break;
case XML_ATTRIBUTE_IMPLIED:
xmlBufferWriteChar(buf, " #IMPLIED");
break;
case XML_ATTRIBUTE_FIXED:
xmlBufferWriteChar(buf, " #FIXED");
break;
default:
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"Internal: ATTRIBUTE struct corrupted invalid def\n",
NULL);
}
if (attr->defaultValue != NULL) {
xmlBufferWriteChar(buf, " ");
xmlBufferWriteQuotedString(buf, attr->defaultValue);
}
xmlBufferWriteChar(buf, ">\n");
}
/**
* xmlDumpAttributeDeclScan:
* @attr: An attribute declaration
* @buf: the XML buffer output
*
* This is used with the hash scan function - just reverses arguments
*/
static void
xmlDumpAttributeDeclScan(void *attr, void *buf,
const xmlChar *name ATTRIBUTE_UNUSED) {
xmlDumpAttributeDecl((xmlBufferPtr) buf, (xmlAttributePtr) attr);
}
/**
* xmlDumpAttributeTable:
* @buf: the XML buffer output
* @table: An attribute table
*
* This will dump the content of the attribute table as an XML DTD definition
*/
void
xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
if ((buf == NULL) || (table == NULL))
return;
xmlHashScan(table, xmlDumpAttributeDeclScan, buf);
}
#endif /* LIBXML_OUTPUT_ENABLED */
/************************************************************************
* *
* NOTATIONs *
* *
************************************************************************/
/**
* xmlFreeNotation:
* @not: A notation
*
* Deallocate the memory used by an notation definition
*/
static void
xmlFreeNotation(xmlNotationPtr nota) {
if (nota == NULL) return;
if (nota->name != NULL)
xmlFree((xmlChar *) nota->name);
if (nota->PublicID != NULL)
xmlFree((xmlChar *) nota->PublicID);
if (nota->SystemID != NULL)
xmlFree((xmlChar *) nota->SystemID);
xmlFree(nota);
}
/**
* xmlAddNotationDecl:
* @dtd: pointer to the DTD
* @ctxt: the validation context
* @name: the entity name
* @PublicID: the public identifier or NULL
* @SystemID: the system identifier or NULL
*
* Register a new notation declaration
*
* Returns NULL if not, otherwise the entity
*/
xmlNotationPtr
xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd,
const xmlChar *name,
const xmlChar *PublicID, const xmlChar *SystemID) {
xmlNotationPtr ret;
xmlNotationTablePtr table;
if (dtd == NULL) {
return(NULL);
}
if (name == NULL) {
return(NULL);
}
if ((PublicID == NULL) && (SystemID == NULL)) {
return(NULL);
}
/*
* Create the Notation table if needed.
*/
table = (xmlNotationTablePtr) dtd->notations;
if (table == NULL) {
xmlDictPtr dict = NULL;
if (dtd->doc != NULL)
dict = dtd->doc->dict;
dtd->notations = table = xmlHashCreateDict(0, dict);
}
if (table == NULL) {
xmlVErrMemory(ctxt,
"xmlAddNotationDecl: Table creation failed!\n");
return(NULL);
}
ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
if (ret == NULL) {
xmlVErrMemory(ctxt, "malloc failed");
return(NULL);
}
memset(ret, 0, sizeof(xmlNotation));
/*
* fill the structure.
*/
ret->name = xmlStrdup(name);
if (SystemID != NULL)
ret->SystemID = xmlStrdup(SystemID);
if (PublicID != NULL)
ret->PublicID = xmlStrdup(PublicID);
/*
* Validity Check:
* Check the DTD for previous declarations of the ATTLIST
*/
if (xmlHashAddEntry(table, name, ret)) {
#ifdef LIBXML_VALID_ENABLED
xmlErrValid(NULL, XML_DTD_NOTATION_REDEFINED,
"xmlAddNotationDecl: %s already defined\n",
(const char *) name);
#endif /* LIBXML_VALID_ENABLED */
xmlFreeNotation(ret);
return(NULL);
}
return(ret);
}
static void
xmlFreeNotationTableEntry(void *nota, const xmlChar *name ATTRIBUTE_UNUSED) {
xmlFreeNotation((xmlNotationPtr) nota);
}
/**
* xmlFreeNotationTable:
* @table: An notation table
*
* Deallocate the memory used by an entities hash table.
*/
void
xmlFreeNotationTable(xmlNotationTablePtr table) {
xmlHashFree(table, xmlFreeNotationTableEntry);
}
#ifdef LIBXML_TREE_ENABLED
/**
* xmlCopyNotation:
* @nota: A notation
*
* Build a copy of a notation.
*
* Returns the new xmlNotationPtr or NULL in case of error.
*/
static void *
xmlCopyNotation(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
xmlNotationPtr nota = (xmlNotationPtr) payload;
xmlNotationPtr cur;
cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
if (cur == NULL) {
xmlVErrMemory(NULL, "malloc failed");
return(NULL);
}
if (nota->name != NULL)
cur->name = xmlStrdup(nota->name);
else
cur->name = NULL;
if (nota->PublicID != NULL)
cur->PublicID = xmlStrdup(nota->PublicID);
else
cur->PublicID = NULL;
if (nota->SystemID != NULL)
cur->SystemID = xmlStrdup(nota->SystemID);
else
cur->SystemID = NULL;
return(cur);
}
/**
* xmlCopyNotationTable:
* @table: A notation table
*
* Build a copy of a notation table.
*
* Returns the new xmlNotationTablePtr or NULL in case of error.
*/
xmlNotationTablePtr
xmlCopyNotationTable(xmlNotationTablePtr table) {
return((xmlNotationTablePtr) xmlHashCopy(table, xmlCopyNotation));
}
#endif /* LIBXML_TREE_ENABLED */
#ifdef LIBXML_OUTPUT_ENABLED
/**
* xmlDumpNotationDecl:
* @buf: the XML buffer output
* @nota: A notation declaration
*
* This will dump the content the notation declaration as an XML DTD definition
*/
void
xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
if ((buf == NULL) || (nota == NULL))
return;
xmlBufferWriteChar(buf, "<!NOTATION ");
xmlBufferWriteCHAR(buf, nota->name);
if (nota->PublicID != NULL) {
xmlBufferWriteChar(buf, " PUBLIC ");
xmlBufferWriteQuotedString(buf, nota->PublicID);
if (nota->SystemID != NULL) {
xmlBufferWriteChar(buf, " ");
xmlBufferWriteQuotedString(buf, nota->SystemID);
}
} else {
xmlBufferWriteChar(buf, " SYSTEM ");
xmlBufferWriteQuotedString(buf, nota->SystemID);
}
xmlBufferWriteChar(buf, " >\n");
}
/**
* xmlDumpNotationDeclScan:
* @nota: A notation declaration
* @buf: the XML buffer output
*
* This is called with the hash scan function, and just reverses args
*/
static void
xmlDumpNotationDeclScan(void *nota, void *buf,
const xmlChar *name ATTRIBUTE_UNUSED) {
xmlDumpNotationDecl((xmlBufferPtr) buf, (xmlNotationPtr) nota);
}
/**
* xmlDumpNotationTable:
* @buf: the XML buffer output
* @table: A notation table
*
* This will dump the content of the notation table as an XML DTD definition
*/
void
xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
if ((buf == NULL) || (table == NULL))
return;
xmlHashScan(table, xmlDumpNotationDeclScan, buf);
}
#endif /* LIBXML_OUTPUT_ENABLED */
/************************************************************************
* *
* IDs *
* *
************************************************************************/
/**
* DICT_FREE:
* @str: a string
*
* Free a string if it is not owned by the "dict" dictionary in the
* current scope
*/
#define DICT_FREE(str) \
if ((str) && ((!dict) || \
(xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \
xmlFree((char *)(str));
/**
* xmlFreeID:
* @not: A id
*
* Deallocate the memory used by an id definition
*/
static void
xmlFreeID(xmlIDPtr id) {
xmlDictPtr dict = NULL;
if (id == NULL) return;
if (id->doc != NULL)
dict = id->doc->dict;
if (id->value != NULL)
DICT_FREE(id->value)
if (id->name != NULL)
DICT_FREE(id->name)
xmlFree(id);
}
/**
* xmlAddID:
* @ctxt: the validation context
* @doc: pointer to the document
* @value: the value name
* @attr: the attribute holding the ID
*
* Register a new id declaration
*
* Returns NULL if not, otherwise the new xmlIDPtr
*/
xmlIDPtr
xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
xmlAttrPtr attr) {
xmlIDPtr ret;
xmlIDTablePtr table;
if (doc == NULL) {
return(NULL);
}
if (value == NULL) {
return(NULL);
}
if (attr == NULL) {
return(NULL);
}
/*
* Create the ID table if needed.
*/
table = (xmlIDTablePtr) doc->ids;
if (table == NULL) {
doc->ids = table = xmlHashCreateDict(0, doc->dict);
}
if (table == NULL) {
xmlVErrMemory(ctxt,
"xmlAddID: Table creation failed!\n");
return(NULL);
}
ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
if (ret == NULL) {
xmlVErrMemory(ctxt, "malloc failed");
return(NULL);
}
/*
* fill the structure.
*/
ret->value = xmlStrdup(value);
ret->doc = doc;
if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
/*
* Operating in streaming mode, attr is gonna disappear
*/
if (doc->dict != NULL)
ret->name = xmlDictLookup(doc->dict, attr->name, -1);
else
ret->name = xmlStrdup(attr->name);
ret->attr = NULL;
} else {
ret->attr = attr;
ret->name = NULL;
}
ret->lineno = xmlGetLineNo(attr->parent);
if (xmlHashAddEntry(table, value, ret) < 0) {
#ifdef LIBXML_VALID_ENABLED
/*
* The id is already defined in this DTD.
*/
if (ctxt != NULL) {
xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED,
"ID %s already defined\n", value, NULL, NULL);
}
#endif /* LIBXML_VALID_ENABLED */
xmlFreeID(ret);
return(NULL);
}
if (attr != NULL)
attr->atype = XML_ATTRIBUTE_ID;
return(ret);
}
static void
xmlFreeIDTableEntry(void *id, const xmlChar *name ATTRIBUTE_UNUSED) {
xmlFreeID((xmlIDPtr) id);
}
/**
* xmlFreeIDTable:
* @table: An id table
*
* Deallocate the memory used by an ID hash table.
*/
void
xmlFreeIDTable(xmlIDTablePtr table) {
xmlHashFree(table, xmlFreeIDTableEntry);
}
/**
* xmlIsID:
* @doc: the document
* @elem: the element carrying the attribute
* @attr: the attribute
*
* Determine whether an attribute is of type ID. In case we have DTD(s)
* then this is done if DTD loading has been requested. In the case
* of HTML documents parsed with the HTML parser, then ID detection is
* done systematically.
*
* Returns 0 or 1 depending on the lookup result
*/
int
xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
if ((attr == NULL) || (attr->name == NULL)) return(0);
if ((attr->ns != NULL) && (attr->ns->prefix != NULL) &&
(!strcmp((char *) attr->name, "id")) &&
(!strcmp((char *) attr->ns->prefix, "xml")))
return(1);
if (doc == NULL) return(0);
if ((doc->intSubset == NULL) && (doc->extSubset == NULL) &&
(doc->type != XML_HTML_DOCUMENT_NODE)) {
return(0);
} else if (doc->type == XML_HTML_DOCUMENT_NODE) {
if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
((xmlStrEqual(BAD_CAST "name", attr->name)) &&
((elem == NULL) || (xmlStrEqual(elem->name, BAD_CAST "a")))))
return(1);
return(0);
} else if (elem == NULL) {
return(0);
} else {
xmlAttributePtr attrDecl = NULL;
xmlChar felem[50], fattr[50];
xmlChar *fullelemname, *fullattrname;
fullelemname = (elem->ns != NULL && elem->ns->prefix != NULL) ?
xmlBuildQName(elem->name, elem->ns->prefix, felem, 50) :
(xmlChar *)elem->name;
fullattrname = (attr->ns != NULL && attr->ns->prefix != NULL) ?
xmlBuildQName(attr->name, attr->ns->prefix, fattr, 50) :
(xmlChar *)attr->name;
if (fullelemname != NULL && fullattrname != NULL) {
attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullelemname,
fullattrname);
if ((attrDecl == NULL) && (doc->extSubset != NULL))
attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullelemname,
fullattrname);
}
if ((fullattrname != fattr) && (fullattrname != attr->name))
xmlFree(fullattrname);
if ((fullelemname != felem) && (fullelemname != elem->name))
xmlFree(fullelemname);
if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
return(1);
}
return(0);
}
/**
* xmlRemoveID:
* @doc: the document
* @attr: the attribute
*
* Remove the given attribute from the ID table maintained internally.
*
* Returns -1 if the lookup failed and 0 otherwise
*/
int
xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
xmlIDTablePtr table;
xmlIDPtr id;
xmlChar *ID;
if (doc == NULL) return(-1);
if (attr == NULL) return(-1);
table = (xmlIDTablePtr) doc->ids;
if (table == NULL)
return(-1);
ID = xmlNodeListGetString(doc, attr->children, 1);
if (ID == NULL)
return(-1);
id = xmlHashLookup(table, ID);
if (id == NULL || id->attr != attr) {
xmlFree(ID);
return(-1);
}
xmlHashRemoveEntry(table, ID, xmlFreeIDTableEntry);
xmlFree(ID);
attr->atype = 0;
return(0);
}
/**
* xmlGetID:
* @doc: pointer to the document
* @ID: the ID value
*
* Search the attribute declaring the given ID
*
* Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
*/
xmlAttrPtr
xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
xmlIDTablePtr table;
xmlIDPtr id;
if (doc == NULL) {
return(NULL);
}
if (ID == NULL) {
return(NULL);
}
table = (xmlIDTablePtr) doc->ids;
if (table == NULL)
return(NULL);
id = xmlHashLookup(table, ID);
if (id == NULL)
return(NULL);
if (id->attr == NULL) {
/*
* We are operating on a stream, return a well known reference
* since the attribute node doesn't exist anymore
*/
return((xmlAttrPtr) doc);
}
return(id->attr);
}
/************************************************************************
* *
* Refs *
* *
************************************************************************/
typedef struct xmlRemoveMemo_t
{
xmlListPtr l;
xmlAttrPtr ap;
} xmlRemoveMemo;
typedef xmlRemoveMemo *xmlRemoveMemoPtr;
typedef struct xmlValidateMemo_t
{
xmlValidCtxtPtr ctxt;
const xmlChar *name;
} xmlValidateMemo;
typedef xmlValidateMemo *xmlValidateMemoPtr;
/**
* xmlFreeRef:
* @lk: A list link
*
* Deallocate the memory used by a ref definition
*/
static void
xmlFreeRef(xmlLinkPtr lk) {
xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
if (ref == NULL) return;
if (ref->value != NULL)
xmlFree((xmlChar *)ref->value);
if (ref->name != NULL)
xmlFree((xmlChar *)ref->name);
xmlFree(ref);
}
/**
* xmlFreeRefTableEntry:
* @list_ref: A list of references.
*
* Deallocate the memory used by a list of references
*/
static void
xmlFreeRefTableEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
xmlListPtr list_ref = (xmlListPtr) payload;
if (list_ref == NULL) return;
xmlListDelete(list_ref);
}
/**
* xmlWalkRemoveRef:
* @data: Contents of current link
* @user: Value supplied by the user
*
* Returns 0 to abort the walk or 1 to continue
*/
static int
xmlWalkRemoveRef(const void *data, void *user)
{
xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
if (attr0 == attr1) { /* Matched: remove and terminate walk */
xmlListRemoveFirst(ref_list, (void *)data);
return 0;
}
return 1;
}
/**
* xmlDummyCompare
* @data0: Value supplied by the user
* @data1: Value supplied by the user
*
* Do nothing, return 0. Used to create unordered lists.
*/
static int
xmlDummyCompare(const void *data0 ATTRIBUTE_UNUSED,
const void *data1 ATTRIBUTE_UNUSED)
{
return (0);
}
/**
* xmlAddRef:
* @ctxt: the validation context
* @doc: pointer to the document
* @value: the value name
* @attr: the attribute holding the Ref
*
* Register a new ref declaration
*
* Returns NULL if not, otherwise the new xmlRefPtr
*/
xmlRefPtr
xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
xmlAttrPtr attr) {
xmlRefPtr ret;
xmlRefTablePtr table;
xmlListPtr ref_list;
if (doc == NULL) {
return(NULL);
}
if (value == NULL) {
return(NULL);
}
if (attr == NULL) {
return(NULL);
}
/*
* Create the Ref table if needed.
*/
table = (xmlRefTablePtr) doc->refs;
if (table == NULL) {
doc->refs = table = xmlHashCreateDict(0, doc->dict);
}
if (table == NULL) {
xmlVErrMemory(ctxt,
"xmlAddRef: Table creation failed!\n");
return(NULL);
}
ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
if (ret == NULL) {
xmlVErrMemory(ctxt, "malloc failed");
return(NULL);
}
/*
* fill the structure.
*/
ret->value = xmlStrdup(value);
if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
/*
* Operating in streaming mode, attr is gonna disappear
*/
ret->name = xmlStrdup(attr->name);
ret->attr = NULL;
} else {
ret->name = NULL;
ret->attr = attr;
}
ret->lineno = xmlGetLineNo(attr->parent);
/* To add a reference :-
* References are maintained as a list of references,
* Lookup the entry, if no entry create new nodelist
* Add the owning node to the NodeList
* Return the ref
*/
if (NULL == (ref_list = xmlHashLookup(table, value))) {
if (NULL == (ref_list = xmlListCreate(xmlFreeRef, xmlDummyCompare))) {
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"xmlAddRef: Reference list creation failed!\n",
NULL);
goto failed;
}
if (xmlHashAddEntry(table, value, ref_list) < 0) {
xmlListDelete(ref_list);
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"xmlAddRef: Reference list insertion failed!\n",
NULL);
goto failed;
}
}
if (xmlListAppend(ref_list, ret) != 0) {
xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
"xmlAddRef: Reference list insertion failed!\n",
NULL);
goto failed;
}
return(ret);
failed:
if (ret != NULL) {
if (ret->value != NULL)
xmlFree((char *)ret->value);
if (ret->name != NULL)
xmlFree((char *)ret->name);
xmlFree(ret);
}
return(NULL);
}
/**
* xmlFreeRefTable:
* @table: An ref table
*
* Deallocate the memory used by an Ref hash table.
*/
void
xmlFreeRefTable(xmlRefTablePtr table) {
xmlHashFree(table, xmlFreeRefTableEntry);
}
/**
* xmlIsRef:
* @doc: the document
* @elem: the element carrying the attribute
* @attr: the attribute
*
* Determine whether an attribute is of type Ref. In case we have DTD(s)
* then this is simple, otherwise we use an heuristic: name Ref (upper
* or lowercase).
*
* Returns 0 or 1 depending on the lookup result
*/
int
xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
if (attr == NULL)
return(0);
if (doc == NULL) {
doc = attr->doc;
if (doc == NULL) return(0);