| /* |
| * 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); |
|