DriveHQ Start Menu
Cloud Drive Mapping
Folder Sync
Cloud Backup
True Drop Box
FTP/SFTP Hosting
Group Account
DriveHQ Start Menu
Online File Server
My Storage
|
Manage Shares
|
Publishes
|
Drop Boxes
|
Group Account
WebDAV Drive Mapping
Cloud Drive Home
|
WebDAV Guide
|
Drive Mapping Tool
|
Drive Mapping URL
Complete Data Backup
Backup Guide
|
Online Backup Tool
|
Cloud-to-Cloud Backup
FTP, Email & Web Service
FTP Home
|
FTP Hosting FAQ
|
Email Hosting
|
EmailManager
|
Web Hosting
Help & Resources
About
|
Enterprise Service
|
Partnership
|
Comparisons
|
Support
Quick Links
Security and Privacy
Download Software
Service Manual
Use Cases
Group Account
Online Help
Blog
Contact
Cloud Surveillance
Sign Up
Login
Features
Business Features
Online File Server
FTP Hosting
Cloud Drive Mapping
Cloud File Backup
Email Backup & Hosting
Cloud File Sharing
Folder Synchronization
Group Management
True Drop Box
Full-text Search
AD Integration/SSO
Mobile Access
IP Camera & DVR Solution
More...
Personal Features
Personal Cloud Drive
Backup All Devices
Mobile APPs
Personal Web Hosting
Sub-Account (for Kids)
Home/PC/Kids Monitoring
More...
Software
DriveHQ Drive Mapping Tool
DriveHQ FileManager
DriveHQ Online Backup
DriveHQ Mobile Apps
Pricing
Business Plans & Pricing
Personal Plans & Pricing
Price Comparison with Others
Feature Comparison with Others
Install Mobile App
Sign up
Creating account...
Invalid character in username! Only 0-9, a-z, A-Z, _, -, . allowed.
Username is required!
Invalid email address!
E-mail is required!
Password is required!
Password is invalid!
Password and confirmation do not match.
Confirm password is required!
I accept
Membership Agreement
Please read the Membership Agreement and check "I accept"!
Free Quick Sign-up
Sign-up Page
Log in
Signing in...
Username or e-mail address is required!
Password is required!
Keep me logged in
Quick Login
Forgot Password
Up
Upload
Download
Share
Publish
New Folder
New File
Copy
Cut
Delete
Paste
Rate
Upgrade
Rotate
Effect
Edit
Slide
History
/* * 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
#ifdef HAVE_STDLIB_H #include
#endif #include
#include
#include
#include
#include
#include
#include
#include
#include
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__); /************************************************************************ * * * Error handling routines * * * ************************************************************************/ /** * xmlVErrMemory: * @ctxt: an XML validation parser context * @extra: extra informations * * 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)) { 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 informations * * Handle a validation error */ static void 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)) { 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, 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 informations * @str2: extra informations * @str3: extra informations * * Handle a validation error, provide contextual informations */ static void 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)) { pctxt = ctxt->userData; } } __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, XML_ERR_ERROR, NULL, 0, (const char *) str1, (const char *) str1, (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 informations * @int2: extra informations * @str3: extra informations * * Handle a validation error, provide contextual informations */ static void 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)) { 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 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)) { pctxt = ctxt->userData; } } __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, XML_ERR_WARNING, NULL, 0, (const char *) str1, (const char *) str1, (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) xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem); #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) { xmlElementContentPtr next; xmlDictPtr dict = NULL; if (doc != NULL) dict = doc->dict; while (cur != NULL) { next = cur->c2; 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 (cur->c1 != NULL) xmlFreeDocElementContent(doc, cur->c1); 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); } xmlFree(cur); cur = next; } } /** * 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 /** * xmlDumpElementContent: * @buf: An XML buffer * @content: An element table * @glob: 1 if one must print the englobing parenthesis, 0 otherwise * * This will dump the content of the element table as an XML DTD definition */ static void xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) { if (content == NULL) return; if (glob) xmlBufferWriteChar(buf, "("); switch (content->type) { case XML_ELEMENT_CONTENT_PCDATA: xmlBufferWriteChar(buf, "#PCDATA"); break; case XML_ELEMENT_CONTENT_ELEMENT: if (content->prefix != NULL) { xmlBufferWriteCHAR(buf, content->prefix); xmlBufferWriteChar(buf, ":"); } xmlBufferWriteCHAR(buf, content->name); break; case XML_ELEMENT_CONTENT_SEQ: if ((content->c1->type == XML_ELEMENT_CONTENT_OR) || (content->c1->type == XML_ELEMENT_CONTENT_SEQ)) xmlDumpElementContent(buf, content->c1, 1); else xmlDumpElementContent(buf, content->c1, 0); xmlBufferWriteChar(buf, " , "); if ((content->c2->type == XML_ELEMENT_CONTENT_OR) || ((content->c2->type == XML_ELEMENT_CONTENT_SEQ) && (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE))) xmlDumpElementContent(buf, content->c2, 1); else xmlDumpElementContent(buf, content->c2, 0); break; case XML_ELEMENT_CONTENT_OR: if ((content->c1->type == XML_ELEMENT_CONTENT_OR) || (content->c1->type == XML_ELEMENT_CONTENT_SEQ)) xmlDumpElementContent(buf, content->c1, 1); else xmlDumpElementContent(buf, content->c1, 0); xmlBufferWriteChar(buf, " | "); if ((content->c2->type == XML_ELEMENT_CONTENT_SEQ) || ((content->c2->type == XML_ELEMENT_CONTENT_OR) && (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE))) xmlDumpElementContent(buf, content->c2, 1); else xmlDumpElementContent(buf, content->c2, 0); break; default: xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, "Internal: ELEMENT content corrupted invalid type\n", NULL); } if (glob) xmlBufferWriteChar(buf, ")"); switch (content->ocur) { case XML_ELEMENT_CONTENT_ONCE: break; case XML_ELEMENT_CONTENT_OPT: xmlBufferWriteChar(buf, "?"); break; case XML_ELEMENT_CONTENT_MULT: xmlBufferWriteChar(buf, "*"); break; case XML_ELEMENT_CONTENT_PLUS: xmlBufferWriteChar(buf, "+"); break; } } /** * xmlSprintfElementContent: * @buf: an output buffer * @content: An element table * @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: if (content->prefix != NULL) { if (size - len < xmlStrlen(content->prefix) + 10) { strcat(buf, " ..."); return; } strcat(buf, (char *) content->prefix); strcat(buf, ":"); } if (size - len < xmlStrlen(content->name) + 10) { strcat(buf, " ..."); return; } 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 (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); } /** * xmlFreeElementTable: * @table: An element table * * Deallocate the memory used by an element hash table. */ void xmlFreeElementTable(xmlElementTablePtr table) { xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement); } #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 xmlElementPtr xmlCopyElement(xmlElementPtr elem) { 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, (xmlHashCopier) 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, "prefix != NULL) { xmlBufferWriteCHAR(buf, elem->prefix); xmlBufferWriteChar(buf, ":"); } xmlBufferWriteCHAR(buf, elem->name); xmlBufferWriteChar(buf, " EMPTY>\n"); break; case XML_ELEMENT_TYPE_ANY: xmlBufferWriteChar(buf, "prefix != NULL) { xmlBufferWriteCHAR(buf, elem->prefix); xmlBufferWriteChar(buf, ":"); } xmlBufferWriteCHAR(buf, elem->name); xmlBufferWriteChar(buf, " ANY>\n"); break; case XML_ELEMENT_TYPE_MIXED: xmlBufferWriteChar(buf, "prefix != NULL) { xmlBufferWriteCHAR(buf, elem->prefix); xmlBufferWriteChar(buf, ":"); } xmlBufferWriteCHAR(buf, elem->name); xmlBufferWriteChar(buf, " "); xmlDumpElementContent(buf, elem->content, 1); xmlBufferWriteChar(buf, ">\n"); break; case XML_ELEMENT_TYPE_ELEMENT: xmlBufferWriteChar(buf, "prefix != NULL) { xmlBufferWriteCHAR(buf, elem->prefix); xmlBufferWriteChar(buf, ":"); } xmlBufferWriteCHAR(buf, elem->name); xmlBufferWriteChar(buf, " "); xmlDumpElementContent(buf, elem->content, 1); 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(xmlElementPtr elem, xmlBufferPtr buf) { xmlDumpElementDecl(buf, 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, (xmlHashScanner) 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 (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 /** * xmlScanAttributeDeclCallback: * @attr: the attribute decl * @list: the list to update * * Callback called by xmlScanAttributeDecl when a new attribute * has to be entered in the list. */ static void xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list, const xmlChar* name ATTRIBUTE_UNUSED) { attr->nexth = *list; *list = attr; } /** * xmlScanAttributeDecl: * @dtd: pointer to the DTD * @elem: the element name * * When inserting a new element scan the DtD for existing attributes * for that element and initialize the Attribute chain * * Returns the pointer to the first attribute decl in the chain, * possibly NULL. */ xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) { xmlAttributePtr ret = NULL; xmlAttributeTablePtr table; if (dtd == NULL) { return(NULL); } if (elem == NULL) { return(NULL); } table = (xmlAttributeTablePtr) dtd->attributes; if (table == NULL) return(NULL); /* WRONG !!! */ xmlHashScan3(table, NULL, NULL, elem, (xmlHashScanner) xmlScanAttributeDeclCallback, &ret); return(ret); } /** * xmlScanIDAttributeDecl: * @ctxt: the validation context * @elem: the element name * @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) && (!xmlValidateAttributeValue(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) 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"); return(NULL); } ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute)); if (ret == NULL) { xmlVErrMemory(ctxt, "malloc failed"); 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); } /** * xmlFreeAttributeTable: * @table: An attribute table * * Deallocate the memory used by an entities hash table. */ void xmlFreeAttributeTable(xmlAttributeTablePtr table) { xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute); } #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 xmlAttributePtr xmlCopyAttribute(xmlAttributePtr attr) { 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, (xmlHashCopier) 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, "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(xmlAttributePtr attr, xmlBufferPtr buf) { xmlDumpAttributeDecl(buf, 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, (xmlHashScanner) 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); } /** * xmlFreeNotationTable: * @table: An notation table * * Deallocate the memory used by an entities hash table. */ void xmlFreeNotationTable(xmlNotationTablePtr table) { xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation); } #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 xmlNotationPtr xmlCopyNotation(xmlNotationPtr nota) { 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, (xmlHashCopier) 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, "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(xmlNotationPtr nota, xmlBufferPtr buf) { xmlDumpNotationDecl(buf, 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, (xmlHashScanner) xmlDumpNotationDeclScan, buf); } #endif /* LIBXML_OUTPUT_ENABLED */ /************************************************************************ * * * IDs * * * ************************************************************************/ /** * DICT_FREE: * @str: a string * * Free a string if it is not owned by the "dict" dictionnary 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 disapear */ 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) && (ctxt->error != 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); } /** * xmlFreeIDTable: * @table: An id table * * Deallocate the memory used by an ID hash table. */ void xmlFreeIDTable(xmlIDTablePtr table) { xmlHashFree(table, (xmlHashDeallocator) xmlFreeID); } /** * xmlIsID: * @doc: the document * @elem: the element carrying the attribute * @attr: the attribute * * Determine whether an attribute is of type ID. In case we have DTD(s) * then this is 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)) { 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 "input"))))) 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); if (attr == 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, (xmlHashDeallocator) xmlFreeID); 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); } /** * xmlFreeRefList: * @list_ref: A list of references. * * Deallocate the memory used by a list of references */ static void xmlFreeRefList(xmlListPtr list_ref) { if (list_ref == NULL) return; xmlListDelete(list_ref); } /** * xmlWalkRemoveRef: * @data: Contents of current link * @user: Value supplied by the user * * Returns 0 to abort the walk or 1 to continue */ static int xmlWalkRemoveRef(const void *data, const void *user) { xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr; xmlAttrPtr attr1 = ((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 disapear */ 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); return(NULL); } if (xmlHashAddEntry(table, value, ref_list) < 0) { xmlListDelete(ref_list); xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, "xmlAddRef: Reference list insertion failed!\n", NULL); return(NULL); } } /* xmlListInsert(ref_list, ret); */ xmlListAppend(ref_list, ret); return(ret); } /** * xmlFreeRefTable: * @table: An ref table * * Deallocate the memory used by an Ref hash table. */ void xmlFreeRefTable(xmlRefTablePtr table) { xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList); } /** * xmlIsRef: * @doc: the document * @elem: the element carrying the attribute * @attr: the attribute * * Determine whether an attribute is of type Ref. In case we have DTD(s) * then this is simple, otherwise we use an heuristic: name Ref (upper * or lowercase). * * Returns 0 or 1 depending on the lookup result */ int xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) { if (attr == NULL) return(0); if (doc == NULL) { doc = attr->doc; if (doc == NULL) return(0); } if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) { return(0); } else if (doc->type == XML_HTML_DOCUMENT_NODE) { /* TODO @@@ */ return(0); } else { xmlAttributePtr attrDecl; if (elem == NULL) return(0); attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, attr->name); if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_IDREF || attrDecl->atype == XML_ATTRIBUTE_IDREFS)) return(1); } return(0); } /** * xmlRemoveRef: * @doc: the document * @attr: the attribute * * Remove the given attribute from the Ref table maintained internally. * * Returns -1 if the lookup failed and 0 otherwise */ int xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) { xmlListPtr ref_list; xmlRefTablePtr table; xmlChar *ID; xmlRemoveMemo target; if (doc == NULL) return(-1); if (attr == NULL) return(-1); table = (xmlRefTablePtr) doc->refs; if (table == NULL) return(-1); if (attr == NULL) return(-1); ID = xmlNodeListGetString(doc, attr->children, 1); if (ID == NULL) return(-1); ref_list = xmlHashLookup(table, ID); if(ref_list == NULL) { xmlFree(ID); return (-1); } /* At this point, ref_list refers to a list of references which * have the same key as the supplied attr. Our list of references * is ordered by reference address and we don't have that information * here to use when removing. We'll have to walk the list and * check for a matching attribute, when we find one stop the walk * and remove the entry. * The list is ordered by reference, so that means we don't have the * key. Passing the list and the reference to the walker means we * will have enough data to be able to remove the entry. */ target.l = ref_list; target.ap = attr; /* Remove the supplied attr from our list */ xmlListWalk(ref_list, xmlWalkRemoveRef, &target); /*If the list is empty then remove the list entry in the hash */ if (xmlListEmpty(ref_list)) xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeRefList); xmlFree(ID); return(0); } /** * xmlGetRefs: * @doc: pointer to the document * @ID: the ID value * * Find the set of references for the supplied ID. * * Returns NULL if not found, otherwise node set for the ID. */ xmlListPtr xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) { xmlRefTablePtr table; if (doc == NULL) { return(NULL); } if (ID == NULL) { return(NULL); } table = (xmlRefTablePtr) doc->refs; if (table == NULL) return(NULL); return (xmlHashLookup(table, ID)); } /************************************************************************ * * * Routines for validity checking * * * ************************************************************************/ /** * xmlGetDtdElementDesc: * @dtd: a pointer to the DtD to search * @name: the element name * * Search the DTD for the description of this element * * returns the xmlElementPtr if found or NULL */ xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) { xmlElementTablePtr table; xmlElementPtr cur; xmlChar *uqname = NULL, *prefix = NULL; if ((dtd == NULL) || (name == NULL)) return(NULL); if (dtd->elements == NULL) return(NULL); table = (xmlElementTablePtr) dtd->elements; uqname = xmlSplitQName2(name, &prefix); if (uqname != NULL) name = uqname; cur = xmlHashLookup2(table, name, prefix); if (prefix != NULL) xmlFree(prefix); if (uqname != NULL) xmlFree(uqname); return(cur); } /** * xmlGetDtdElementDesc2: * @dtd: a pointer to the DtD to search * @name: the element name * @create: create an empty description if not found * * Search the DTD for the description of this element * * returns the xmlElementPtr if found or NULL */ static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) { xmlElementTablePtr table; xmlElementPtr cur; xmlChar *uqname = NULL, *prefix = NULL; if (dtd == NULL) return(NULL); if (dtd->elements == NULL) { xmlDictPtr dict = NULL; if (dtd->doc != NULL) dict = dtd->doc->dict; if (!create) return(NULL); /* * Create the Element table if needed. */ table = (xmlElementTablePtr) dtd->elements; if (table == NULL) { table = xmlHashCreateDict(0, dict); dtd->elements = (void *) table; } if (table == NULL) { xmlVErrMemory(NULL, "element table allocation failed"); return(NULL); } } table = (xmlElementTablePtr) dtd->elements; uqname = xmlSplitQName2(name, &prefix); if (uqname != NULL) name = uqname; cur = xmlHashLookup2(table, name, prefix); if ((cur == NULL) && (create)) { cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement)); if (cur == NULL) { xmlVErrMemory(NULL, "malloc failed"); return(NULL); } memset(cur, 0, sizeof(xmlElement)); cur->type = XML_ELEMENT_DECL; /* * fill the structure. */ cur->name = xmlStrdup(name); cur->prefix = xmlStrdup(prefix); cur->etype = XML_ELEMENT_TYPE_UNDEFINED; xmlHashAddEntry2(table, name, prefix, cur); } if (prefix != NULL) xmlFree(prefix); if (uqname != NULL) xmlFree(uqname); return(cur); } /** * xmlGetDtdQElementDesc: * @dtd: a pointer to the DtD to search * @name: the element name * @prefix: the element namespace prefix * * Search the DTD for the description of this element * * returns the xmlElementPtr if found or NULL */ xmlElementPtr xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name, const xmlChar *prefix) { xmlElementTablePtr table; if (dtd == NULL) return(NULL); if (dtd->elements == NULL) return(NULL); table = (xmlElementTablePtr) dtd->elements; return(xmlHashLookup2(table, name, prefix)); } /** * xmlGetDtdAttrDesc: * @dtd: a pointer to the DtD to search * @elem: the element name * @name: the attribute name * * Search the DTD for the description of this attribute on * this element. * * returns the xmlAttributePtr if found or NULL */ xmlAttributePtr xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) { xmlAttributeTablePtr table; xmlAttributePtr cur; xmlChar *uqname = NULL, *prefix = NULL; if (dtd == NULL) return(NULL); if (dtd->attributes == NULL) return(NULL); table = (xmlAttributeTablePtr) dtd->attributes; if (table == NULL) return(NULL); uqname = xmlSplitQName2(name, &prefix); if (uqname != NULL) { cur = xmlHashLookup3(table, uqname, prefix, elem); if (prefix != NULL) xmlFree(prefix); if (uqname != NULL) xmlFree(uqname); } else cur = xmlHashLookup3(table, name, NULL, elem); return(cur); } /** * xmlGetDtdQAttrDesc: * @dtd: a pointer to the DtD to search * @elem: the element name * @name: the attribute name * @prefix: the attribute namespace prefix * * Search the DTD for the description of this qualified attribute on * this element. * * returns the xmlAttributePtr if found or NULL */ xmlAttributePtr xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name, const xmlChar *prefix) { xmlAttributeTablePtr table; if (dtd == NULL) return(NULL); if (dtd->attributes == NULL) return(NULL); table = (xmlAttributeTablePtr) dtd->attributes; return(xmlHashLookup3(table, name, prefix, elem)); } /** * xmlGetDtdNotationDesc: * @dtd: a pointer to the DtD to search * @name: the notation name * * Search the DTD for the description of this notation * * returns the xmlNotationPtr if found or NULL */ xmlNotationPtr xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) { xmlNotationTablePtr table; if (dtd == NULL) return(NULL); if (dtd->notations == NULL) return(NULL); table = (xmlNotationTablePtr) dtd->notations; return(xmlHashLookup(table, name)); } #if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) /** * xmlValidateNotationUse: * @ctxt: the validation context * @doc: the document * @notationName: the notation name to check * * Validate that the given name match a notation declaration. * - [ VC: Notation Declared ] * * returns 1 if valid or 0 otherwise */ int xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *notationName) { xmlNotationPtr notaDecl; if ((doc == NULL) || (doc->intSubset == NULL)) return(-1); notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName); if ((notaDecl == NULL) && (doc->extSubset != NULL)) notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName); if ((notaDecl == NULL) && (ctxt != NULL)) { xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION, "NOTATION %s is not declared\n", notationName, NULL, NULL); return(0); } return(1); } #endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */ /** * xmlIsMixedElement: * @doc: the document * @name: the element name * * Search in the DtDs whether an element accept Mixed content (or ANY) * basically if it is supposed to accept text childs * * returns 0 if no, 1 if yes, and -1 if no element description is available */ int xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) { xmlElementPtr elemDecl; if ((doc == NULL) || (doc->intSubset == NULL)) return(-1); elemDecl = xmlGetDtdElementDesc(doc->intSubset, name); if ((elemDecl == NULL) && (doc->extSubset != NULL)) elemDecl = xmlGetDtdElementDesc(doc->extSubset, name); if (elemDecl == NULL) return(-1); switch (elemDecl->etype) { case XML_ELEMENT_TYPE_UNDEFINED: return(-1); case XML_ELEMENT_TYPE_ELEMENT: return(0); case XML_ELEMENT_TYPE_EMPTY: /* * return 1 for EMPTY since we want VC error to pop up * on
for example */ case XML_ELEMENT_TYPE_ANY: case XML_ELEMENT_TYPE_MIXED: return(1); } return(1); } #ifdef LIBXML_VALID_ENABLED /** * xmlValidateNameValue: * @value: an Name value * * Validate that the given value match Name production * * returns 1 if valid or 0 otherwise */ int xmlValidateNameValue(const xmlChar *value) { const xmlChar *cur; int val, len; if (value == NULL) return(0); cur = value; val = xmlStringCurrentChar(NULL, cur, &len); cur += len; if (!IS_LETTER(val) && (val != '_') && (val != ':')) { return(0); } val = xmlStringCurrentChar(NULL, cur, &len); cur += len; while ((IS_LETTER(val)) || (IS_DIGIT(val)) || (val == '.') || (val == '-') || (val == '_') || (val == ':') || (IS_COMBINING(val)) || (IS_EXTENDER(val))) { val = xmlStringCurrentChar(NULL, cur, &len); cur += len; } if (val != 0) return(0); return(1); } /** * xmlValidateNamesValue: * @value: an Names value * * Validate that the given value match Names production * * returns 1 if valid or 0 otherwise */ int xmlValidateNamesValue(const xmlChar *value) { const xmlChar *cur; int val, len; if (value == NULL) return(0); cur = value; val = xmlStringCurrentChar(NULL, cur, &len); cur += len; if (!IS_LETTER(val) && (val != '_') && (val != ':')) { return(0); } val = xmlStringCurrentChar(NULL, cur, &len); cur += len; while ((IS_LETTER(val)) || (IS_DIGIT(val)) || (val == '.') || (val == '-') || (val == '_') || (val == ':') || (IS_COMBINING(val)) || (IS_EXTENDER(val))) { val = xmlStringCurrentChar(NULL, cur, &len); cur += len; } /* Should not test IS_BLANK(val) here -- see erratum E20*/ while (val == 0x20) { while (val == 0x20) { val = xmlStringCurrentChar(NULL, cur, &len); cur += len; } if (!IS_LETTER(val) && (val != '_') && (val != ':')) { return(0); } val = xmlStringCurrentChar(NULL, cur, &len); cur += len; while ((IS_LETTER(val)) || (IS_DIGIT(val)) || (val == '.') || (val == '-') || (val == '_') || (val == ':') || (IS_COMBINING(val)) || (IS_EXTENDER(val))) { val = xmlStringCurrentChar(NULL, cur, &len); cur += len; } } if (val != 0) return(0); return(1); } /** * xmlValidateNmtokenValue: * @value: an Nmtoken value * * Validate that the given value match Nmtoken production * * [ VC: Name Token ] * * returns 1 if valid or 0 otherwise */ int xmlValidateNmtokenValue(const xmlChar *value) { const xmlChar *cur; int val, len; if (value == NULL) return(0); cur = value; val = xmlStringCurrentChar(NULL, cur, &len); cur += len; if (!IS_LETTER(val) && !IS_DIGIT(val) && (val != '.') && (val != '-') && (val != '_') && (val != ':') && (!IS_COMBINING(val)) && (!IS_EXTENDER(val))) return(0); while ((IS_LETTER(val)) || (IS_DIGIT(val)) || (val == '.') || (val == '-') || (val == '_') || (val == ':') || (IS_COMBINING(val)) || (IS_EXTENDER(val))) { val = xmlStringCurrentChar(NULL, cur, &len); cur += len; } if (val != 0) return(0); return(1); } /** * xmlValidateNmtokensValue: * @value: an Nmtokens value * * Validate that the given value match Nmtokens production * * [ VC: Name Token ] * * returns 1 if valid or 0 otherwise */ int xmlValidateNmtokensValue(const xmlChar *value) { const xmlChar *cur; int val, len; if (value == NULL) return(0); cur = value; val = xmlStringCurrentChar(NULL, cur, &len); cur += len; while (IS_BLANK(val)) { val = xmlStringCurrentChar(NULL, cur, &len); cur += len; } if (!IS_LETTER(val) && !IS_DIGIT(val) && (val != '.') && (val != '-') && (val != '_') && (val != ':') && (!IS_COMBINING(val)) && (!IS_EXTENDER(val))) return(0); while ((IS_LETTER(val)) || (IS_DIGIT(val)) || (val == '.') || (val == '-') || (val == '_') || (val == ':') || (IS_COMBINING(val)) || (IS_EXTENDER(val))) { val = xmlStringCurrentChar(NULL, cur, &len); cur += len; } /* Should not test IS_BLANK(val) here -- see erratum E20*/ while (val == 0x20) { while (val == 0x20) { val = xmlStringCurrentChar(NULL, cur, &len); cur += len; } if (val == 0) return(1); if (!IS_LETTER(val) && !IS_DIGIT(val) && (val != '.') && (val != '-') && (val != '_') && (val != ':') && (!IS_COMBINING(val)) && (!IS_EXTENDER(val))) return(0); while ((IS_LETTER(val)) || (IS_DIGIT(val)) || (val == '.') || (val == '-') || (val == '_') || (val == ':') || (IS_COMBINING(val)) || (IS_EXTENDER(val))) { val = xmlStringCurrentChar(NULL, cur, &len); cur += len; } } if (val != 0) return(0); return(1); } /** * xmlValidateNotationDecl: * @ctxt: the validation context * @doc: a document instance * @nota: a notation definition * * Try to validate a single notation definition * basically it does the following checks as described by the * XML-1.0 recommendation: * - it seems that no validity constraint exists on notation declarations * But this function get called anyway ... * * returns 1 if valid or 0 otherwise */ int xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNotationPtr nota ATTRIBUTE_UNUSED) { int ret = 1; return(ret); } /** * xmlValidateAttributeValue: * @type: an attribute type * @value: an attribute value * * Validate that the given attribute value match the proper production * * [ VC: ID ] * Values of type ID must match the Name production.... * * [ VC: IDREF ] * Values of type IDREF must match the Name production, and values * of type IDREFS must match Names ... * * [ VC: Entity Name ] * Values of type ENTITY must match the Name production, values * of type ENTITIES must match Names ... * * [ VC: Name Token ] * Values of type NMTOKEN must match the Nmtoken production; values * of type NMTOKENS must match Nmtokens. * * returns 1 if valid or 0 otherwise */ int xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) { switch (type) { case XML_ATTRIBUTE_ENTITIES: case XML_ATTRIBUTE_IDREFS: return(xmlValidateNamesValue(value)); case XML_ATTRIBUTE_ENTITY: case XML_ATTRIBUTE_IDREF: case XML_ATTRIBUTE_ID: case XML_ATTRIBUTE_NOTATION: return(xmlValidateNameValue(value)); case XML_ATTRIBUTE_NMTOKENS: case XML_ATTRIBUTE_ENUMERATION: return(xmlValidateNmtokensValue(value)); case XML_ATTRIBUTE_NMTOKEN: return(xmlValidateNmtokenValue(value)); case XML_ATTRIBUTE_CDATA: break; } return(1); } /** * xmlValidateAttributeValue2: * @ctxt: the validation context * @doc: the document * @name: the attribute name (used for error reporting only) * @type: the attribute type * @value: the attribute value * * Validate that the given attribute value match a given type. * This typically cannot be done before having finished parsing * the subsets. * * [ VC: IDREF ] * Values of type IDREF must match one of the declared IDs * Values of type IDREFS must match a sequence of the declared IDs * each Name must match the value of an ID attribute on some element * in the XML document; i.e. IDREF values must match the value of * some ID attribute * * [ VC: Entity Name ] * Values of type ENTITY must match one declared entity * Values of type ENTITIES must match a sequence of declared entities * * [ VC: Notation Attributes ] * all notation names in the declaration must be declared. * * returns 1 if valid or 0 otherwise */ static int xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *name, xmlAttributeType type, const xmlChar *value) { int ret = 1; switch (type) { case XML_ATTRIBUTE_IDREFS: case XML_ATTRIBUTE_IDREF: case XML_ATTRIBUTE_ID: case XML_ATTRIBUTE_NMTOKENS: case XML_ATTRIBUTE_ENUMERATION: case XML_ATTRIBUTE_NMTOKEN: case XML_ATTRIBUTE_CDATA: break; case XML_ATTRIBUTE_ENTITY: { xmlEntityPtr ent; ent = xmlGetDocEntity(doc, value); /* yeah it's a bit messy... */ if ((ent == NULL) && (doc->standalone == 1)) { doc->standalone = 0; ent = xmlGetDocEntity(doc, value); } if (ent == NULL) { xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_ENTITY, "ENTITY attribute %s reference an unknown entity \"%s\"\n", name, value, NULL); ret = 0; } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) { xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_ENTITY_TYPE, "ENTITY attribute %s reference an entity \"%s\" of wrong type\n", name, value, NULL); ret = 0; } break; } case XML_ATTRIBUTE_ENTITIES: { xmlChar *dup, *nam = NULL, *cur, save; xmlEntityPtr ent; dup = xmlStrdup(value); if (dup == NULL) return(0); cur = dup; while (*cur != 0) { nam = cur; while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++; save = *cur; *cur = 0; ent = xmlGetDocEntity(doc, nam); if (ent == NULL) { xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_ENTITY, "ENTITIES attribute %s reference an unknown entity \"%s\"\n", name, nam, NULL); ret = 0; } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) { xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_ENTITY_TYPE, "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n", name, nam, NULL); ret = 0; } if (save == 0) break; *cur = save; while (IS_BLANK_CH(*cur)) cur++; } xmlFree(dup); break; } case XML_ATTRIBUTE_NOTATION: { xmlNotationPtr nota; nota = xmlGetDtdNotationDesc(doc->intSubset, value); if ((nota == NULL) && (doc->extSubset != NULL)) nota = xmlGetDtdNotationDesc(doc->extSubset, value); if (nota == NULL) { xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION, "NOTATION attribute %s reference an unknown notation \"%s\"\n", name, value, NULL); ret = 0; } break; } } return(ret); } /** * xmlValidCtxtNormalizeAttributeValue: * @ctxt: the validation context * @doc: the document * @elem: the parent * @name: the attribute name * @value: the attribute value * @ctxt: the validation context or NULL * * Does the validation related extra step of the normalization of attribute * values: * * If the declared value is not CDATA, then the XML processor must further * process the normalized attribute value by discarding any leading and * trailing space (#x20) characters, and by replacing sequences of space * (#x20) characters by single space (#x20) character. * * Also check VC: Standalone Document Declaration in P32, and update * ctxt->valid accordingly * * returns a new normalized string if normalization is needed, NULL otherwise * the caller must free the returned value. */ xmlChar * xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem, const xmlChar *name, const xmlChar *value) { xmlChar *ret, *dst; const xmlChar *src; xmlAttributePtr attrDecl = NULL; int extsubset = 0; if (doc == NULL) return(NULL); if (elem == NULL) return(NULL); if (name == NULL) return(NULL); if (value == NULL) return(NULL); if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { xmlChar fn[50]; xmlChar *fullname; fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); if (fullname == NULL) return(NULL); attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name); if ((attrDecl == NULL) && (doc->extSubset != NULL)) { attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name); if (attrDecl != NULL) extsubset = 1; } if ((fullname != fn) && (fullname != elem->name)) xmlFree(fullname); } if ((attrDecl == NULL) && (doc->intSubset != NULL)) attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name); if ((attrDecl == NULL) && (doc->extSubset != NULL)) { attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name); if (attrDecl != NULL) extsubset = 1; } if (attrDecl == NULL) return(NULL); if (attrDecl->atype == XML_ATTRIBUTE_CDATA) return(NULL); ret = xmlStrdup(value); if (ret == NULL) return(NULL); src = value; dst = ret; while (*src == 0x20) src++; while (*src != 0) { if (*src == 0x20) { while (*src == 0x20) src++; if (*src != 0) *dst++ = 0x20; } else { *dst++ = *src++; } } *dst = 0; if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) { xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE, "standalone: %s on %s value had to be normalized based on external subset declaration\n", name, elem->name, NULL); ctxt->valid = 0; } return(ret); } /** * xmlValidNormalizeAttributeValue: * @doc: the document * @elem: the parent * @name: the attribute name * @value: the attribute value * * Does the validation related extra step of the normalization of attribute * values: * * If the declared value is not CDATA, then the XML processor must further * process the normalized attribute value by discarding any leading and * trailing space (#x20) characters, and by replacing sequences of space * (#x20) characters by single space (#x20) character. * * Returns a new normalized string if normalization is needed, NULL otherwise * the caller must free the returned value. */ xmlChar * xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem, const xmlChar *name, const xmlChar *value) { xmlChar *ret, *dst; const xmlChar *src; xmlAttributePtr attrDecl = NULL; if (doc == NULL) return(NULL); if (elem == NULL) return(NULL); if (name == NULL) return(NULL); if (value == NULL) return(NULL); if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { xmlChar fn[50]; xmlChar *fullname; fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); if (fullname == NULL) return(NULL); attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name); if ((fullname != fn) && (fullname != elem->name)) xmlFree(fullname); } attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name); if (attrDecl == NULL) return(NULL); if (attrDecl->atype == XML_ATTRIBUTE_CDATA) return(NULL); ret = xmlStrdup(value); if (ret == NULL) return(NULL); src = value; dst = ret; while (*src == 0x20) src++; while (*src != 0) { if (*src == 0x20) { while (*src == 0x20) src++; if (*src != 0) *dst++ = 0x20; } else { *dst++ = *src++; } } *dst = 0; return(ret); } static void xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count, const xmlChar* name ATTRIBUTE_UNUSED) { if (attr->atype == XML_ATTRIBUTE_ID) (*count)++; } /** * xmlValidateAttributeDecl: * @ctxt: the validation context * @doc: a document instance * @attr: an attribute definition * * Try to validate a single attribute definition * basically it does the following checks as described by the * XML-1.0 recommendation: * - [ VC: Attribute Default Legal ] * - [ VC: Enumeration ] * - [ VC: ID Attribute Default ] * * The ID/IDREF uniqueness and matching are done separately * * returns 1 if valid or 0 otherwise */ int xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlAttributePtr attr) { int ret = 1; int val; CHECK_DTD; if(attr == NULL) return(1); /* Attribute Default Legal */ /* Enumeration */ if (attr->defaultValue != NULL) { val = xmlValidateAttributeValue(attr->atype, attr->defaultValue); if (val == 0) { xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_DEFAULT, "Syntax of default value for attribute %s of %s is not valid\n", attr->name, attr->elem, NULL); } ret &= val; } /* ID Attribute Default */ if ((attr->atype == XML_ATTRIBUTE_ID)&& (attr->def != XML_ATTRIBUTE_IMPLIED) && (attr->def != XML_ATTRIBUTE_REQUIRED)) { xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_FIXED, "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n", attr->name, attr->elem, NULL); ret = 0; } /* One ID per Element Type */ if (attr->atype == XML_ATTRIBUTE_ID) { int nbId; /* the trick is that we parse DtD as their own internal subset */ xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset, attr->elem); if (elem != NULL) { nbId = xmlScanIDAttributeDecl(NULL, elem, 0); } else { xmlAttributeTablePtr table; /* * The attribute may be declared in the internal subset and the * element in the external subset. */ nbId = 0; if (doc->intSubset != NULL) { table = (xmlAttributeTablePtr) doc->intSubset->attributes; xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner) xmlValidateAttributeIdCallback, &nbId); } } if (nbId > 1) { xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, "Element %s has %d ID attribute defined in the internal subset : %s\n", attr->elem, nbId, attr->name); } else if (doc->extSubset != NULL) { int extId = 0; elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem); if (elem != NULL) { extId = xmlScanIDAttributeDecl(NULL, elem, 0); } if (extId > 1) { xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, "Element %s has %d ID attribute defined in the external subset : %s\n", attr->elem, extId, attr->name); } else if (extId + nbId > 1) { xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, "Element %s has ID attributes defined in the internal and external subset : %s\n", attr->elem, attr->name, NULL); } } } /* Validity Constraint: Enumeration */ if ((attr->defaultValue != NULL) && (attr->tree != NULL)) { xmlEnumerationPtr tree = attr->tree; while (tree != NULL) { if (xmlStrEqual(tree->name, attr->defaultValue)) break; tree = tree->next; } if (tree == NULL) { xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_VALUE, "Default value \"%s\" for attribute %s of %s is not among the enumerated set\n", attr->defaultValue, attr->name, attr->elem); ret = 0; } } return(ret); } /** * xmlValidateElementDecl: * @ctxt: the validation context * @doc: a document instance * @elem: an element definition * * Try to validate a single element definition * basically it does the following checks as described by the * XML-1.0 recommendation: * - [ VC: One ID per Element Type ] * - [ VC: No Duplicate Types ] * - [ VC: Unique Element Type Declaration ] * * returns 1 if valid or 0 otherwise */ int xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlElementPtr elem) { int ret = 1; xmlElementPtr tst; CHECK_DTD; if (elem == NULL) return(1); #if 0 #ifdef LIBXML_REGEXP_ENABLED /* Build the regexp associated to the content model */ ret = xmlValidBuildContentModel(ctxt, elem); #endif #endif /* No Duplicate Types */ if (elem->etype == XML_ELEMENT_TYPE_MIXED) { xmlElementContentPtr cur, next; const xmlChar *name; cur = elem->content; while (cur != NULL) { if (cur->type != XML_ELEMENT_CONTENT_OR) break; if (cur->c1 == NULL) break; if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) { name = cur->c1->name; next = cur->c2; while (next != NULL) { if (next->type == XML_ELEMENT_CONTENT_ELEMENT) { if ((xmlStrEqual(next->name, name)) && (xmlStrEqual(next->prefix, cur->prefix))) { if (cur->prefix == NULL) { xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, "Definition of %s has duplicate references of %s\n", elem->name, name, NULL); } else { xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, "Definition of %s has duplicate references of %s:%s\n", elem->name, cur->prefix, name); } ret = 0; } break; } if (next->c1 == NULL) break; if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break; if ((xmlStrEqual(next->c1->name, name)) && (xmlStrEqual(next->c1->prefix, cur->prefix))) { if (cur->prefix == NULL) { xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, "Definition of %s has duplicate references to %s\n", elem->name, name, NULL); } else { xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR, "Definition of %s has duplicate references to %s:%s\n", elem->name, cur->prefix, name); } ret = 0; } next = next->c2; } } cur = cur->c2; } } /* VC: Unique Element Type Declaration */ tst = xmlGetDtdElementDesc(doc->intSubset, elem->name); if ((tst != NULL ) && (tst != elem) && ((tst->prefix == elem->prefix) || (xmlStrEqual(tst->prefix, elem->prefix))) && (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) { xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED, "Redefinition of element %s\n", elem->name, NULL, NULL); ret = 0; } tst = xmlGetDtdElementDesc(doc->extSubset, elem->name); if ((tst != NULL ) && (tst != elem) && ((tst->prefix == elem->prefix) || (xmlStrEqual(tst->prefix, elem->prefix))) && (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) { xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED, "Redefinition of element %s\n", elem->name, NULL, NULL); ret = 0; } /* One ID per Element Type * already done when registering the attribute if (xmlScanIDAttributeDecl(ctxt, elem) > 1) { ret = 0; } */ return(ret); } /** * xmlValidateOneAttribute: * @ctxt: the validation context * @doc: a document instance * @elem: an element instance * @attr: an attribute instance * @value: the attribute value (without entities processing) * * Try to validate a single attribute for an element * basically it does the following checks as described by the * XML-1.0 recommendation: * - [ VC: Attribute Value Type ] * - [ VC: Fixed Attribute Default ] * - [ VC: Entity Name ] * - [ VC: Name Token ] * - [ VC: ID ] * - [ VC: IDREF ] * - [ VC: Entity Name ] * - [ VC: Notation Attributes ] * * The ID/IDREF uniqueness and matching are done separately * * returns 1 if valid or 0 otherwise */ int xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) { xmlAttributePtr attrDecl = NULL; int val; int ret = 1; CHECK_DTD; if ((elem == NULL) || (elem->name == NULL)) return(0); if ((attr == NULL) || (attr->name == NULL)) return(0); if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { xmlChar fn[50]; xmlChar *fullname; fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); if (fullname == NULL) return(0); if (attr->ns != NULL) { attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname, attr->name, attr->ns->prefix); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname, attr->name, attr->ns->prefix); } else { attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, attr->name); } if ((fullname != fn) && (fullname != elem->name)) xmlFree(fullname); } if (attrDecl == NULL) { if (attr->ns != NULL) { attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, attr->name, attr->ns->prefix); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, attr->name, attr->ns->prefix); } else { attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, attr->name); } } /* Validity Constraint: Attribute Value Type */ if (attrDecl == NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE, "No declaration for attribute %s of element %s\n", attr->name, elem->name, NULL); return(0); } attr->atype = attrDecl->atype; val = xmlValidateAttributeValue(attrDecl->atype, value); if (val == 0) { xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, "Syntax of value for attribute %s of %s is not valid\n", attr->name, elem->name, NULL); ret = 0; } /* Validity constraint: Fixed Attribute Default */ if (attrDecl->def == XML_ATTRIBUTE_FIXED) { if (!xmlStrEqual(value, attrDecl->defaultValue)) { xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT, "Value for attribute %s of %s is different from default \"%s\"\n", attr->name, elem->name, attrDecl->defaultValue); ret = 0; } } /* Validity Constraint: ID uniqueness */ if (attrDecl->atype == XML_ATTRIBUTE_ID) { if (xmlAddID(ctxt, doc, value, attr) == NULL) ret = 0; } if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) || (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) { if (xmlAddRef(ctxt, doc, value, attr) == NULL) ret = 0; } /* Validity Constraint: Notation Attributes */ if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) { xmlEnumerationPtr tree = attrDecl->tree; xmlNotationPtr nota; /* First check that the given NOTATION was declared */ nota = xmlGetDtdNotationDesc(doc->intSubset, value); if (nota == NULL) nota = xmlGetDtdNotationDesc(doc->extSubset, value); if (nota == NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION, "Value \"%s\" for attribute %s of %s is not a declared Notation\n", value, attr->name, elem->name); ret = 0; } /* Second, verify that it's among the list */ while (tree != NULL) { if (xmlStrEqual(tree->name, value)) break; tree = tree->next; } if (tree == NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE, "Value \"%s\" for attribute %s of %s is not among the enumerated notations\n", value, attr->name, elem->name); ret = 0; } } /* Validity Constraint: Enumeration */ if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) { xmlEnumerationPtr tree = attrDecl->tree; while (tree != NULL) { if (xmlStrEqual(tree->name, value)) break; tree = tree->next; } if (tree == NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, "Value \"%s\" for attribute %s of %s is not among the enumerated set\n", value, attr->name, elem->name); ret = 0; } } /* Fixed Attribute Default */ if ((attrDecl->def == XML_ATTRIBUTE_FIXED) && (!xmlStrEqual(attrDecl->defaultValue, value))) { xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, "Value for attribute %s of %s must be \"%s\"\n", attr->name, elem->name, attrDecl->defaultValue); ret = 0; } /* Extra check for the attribute value */ ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name, attrDecl->atype, value); return(ret); } /** * xmlValidateOneNamespace: * @ctxt: the validation context * @doc: a document instance * @elem: an element instance * @prefix: the namespace prefix * @ns: an namespace declaration instance * @value: the attribute value (without entities processing) * * Try to validate a single namespace declaration for an element * basically it does the following checks as described by the * XML-1.0 recommendation: * - [ VC: Attribute Value Type ] * - [ VC: Fixed Attribute Default ] * - [ VC: Entity Name ] * - [ VC: Name Token ] * - [ VC: ID ] * - [ VC: IDREF ] * - [ VC: Entity Name ] * - [ VC: Notation Attributes ] * * The ID/IDREF uniqueness and matching are done separately * * returns 1 if valid or 0 otherwise */ int xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) { /* xmlElementPtr elemDecl; */ xmlAttributePtr attrDecl = NULL; int val; int ret = 1; CHECK_DTD; if ((elem == NULL) || (elem->name == NULL)) return(0); if ((ns == NULL) || (ns->href == NULL)) return(0); if (prefix != NULL) { xmlChar fn[50]; xmlChar *fullname; fullname = xmlBuildQName(elem->name, prefix, fn, 50); if (fullname == NULL) { xmlVErrMemory(ctxt, "Validating namespace"); return(0); } if (ns->prefix != NULL) { attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname, ns->prefix, BAD_CAST "xmlns"); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname, ns->prefix, BAD_CAST "xmlns"); } else { attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, BAD_CAST "xmlns"); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, BAD_CAST "xmlns"); } if ((fullname != fn) && (fullname != elem->name)) xmlFree(fullname); } if (attrDecl == NULL) { if (ns->prefix != NULL) { attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, ns->prefix, BAD_CAST "xmlns"); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, ns->prefix, BAD_CAST "xmlns"); } else { attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, BAD_CAST "xmlns"); if ((attrDecl == NULL) && (doc->extSubset != NULL)) attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, BAD_CAST "xmlns"); } } /* Validity Constraint: Attribute Value Type */ if (attrDecl == NULL) { if (ns->prefix != NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE, "No declaration for attribute xmlns:%s of element %s\n", ns->prefix, elem->name, NULL); } else { xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE, "No declaration for attribute xmlns of element %s\n", elem->name, NULL, NULL); } return(0); } val = xmlValidateAttributeValue(attrDecl->atype, value); if (val == 0) { if (ns->prefix != NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT, "Syntax of value for attribute xmlns:%s of %s is not valid\n", ns->prefix, elem->name, NULL); } else { xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT, "Syntax of value for attribute xmlns of %s is not valid\n", elem->name, NULL, NULL); } ret = 0; } /* Validity constraint: Fixed Attribute Default */ if (attrDecl->def == XML_ATTRIBUTE_FIXED) { if (!xmlStrEqual(value, attrDecl->defaultValue)) { if (ns->prefix != NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT, "Value for attribute xmlns:%s of %s is different from default \"%s\"\n", ns->prefix, elem->name, attrDecl->defaultValue); } else { xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT, "Value for attribute xmlns of %s is different from default \"%s\"\n", elem->name, attrDecl->defaultValue, NULL); } ret = 0; } } /* Validity Constraint: ID uniqueness */ if (attrDecl->atype == XML_ATTRIBUTE_ID) { if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL) ret = 0; } if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) || (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) { if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL) ret = 0; } /* Validity Constraint: Notation Attributes */ if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) { xmlEnumerationPtr tree = attrDecl->tree; xmlNotationPtr nota; /* First check that the given NOTATION was declared */ nota = xmlGetDtdNotationDesc(doc->intSubset, value); if (nota == NULL) nota = xmlGetDtdNotationDesc(doc->extSubset, value); if (nota == NULL) { if (ns->prefix != NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION, "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n", value, ns->prefix, elem->name); } else { xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION, "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n", value, elem->name, NULL); } ret = 0; } /* Second, verify that it's among the list */ while (tree != NULL) { if (xmlStrEqual(tree->name, value)) break; tree = tree->next; } if (tree == NULL) { if (ns->prefix != NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE, "Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n", value, ns->prefix, elem->name); } else { xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE, "Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n", value, elem->name, NULL); } ret = 0; } } /* Validity Constraint: Enumeration */ if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) { xmlEnumerationPtr tree = attrDecl->tree; while (tree != NULL) { if (xmlStrEqual(tree->name, value)) break; tree = tree->next; } if (tree == NULL) { if (ns->prefix != NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, "Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n", value, ns->prefix, elem->name); } else { xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE, "Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n", value, elem->name, NULL); } ret = 0; } } /* Fixed Attribute Default */ if ((attrDecl->def == XML_ATTRIBUTE_FIXED) && (!xmlStrEqual(attrDecl->defaultValue, value))) { if (ns->prefix != NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE, "Value for attribute xmlns:%s of %s must be \"%s\"\n", ns->prefix, elem->name, attrDecl->defaultValue); } else { xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE, "Value for attribute xmlns of %s must be \"%s\"\n", elem->name, attrDecl->defaultValue, NULL); } ret = 0; } /* Extra check for the attribute value */ if (ns->prefix != NULL) { ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix, attrDecl->atype, value); } else { ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns", attrDecl->atype, value); } return(ret); } #ifndef LIBXML_REGEXP_ENABLED /** * xmlValidateSkipIgnorable: * @ctxt: the validation context * @child: the child list * * Skip ignorable elements w.r.t. the validation process * * returns the first element to consider for validation of the content model */ static xmlNodePtr xmlValidateSkipIgnorable(xmlNodePtr child) { while (child != NULL) { switch (child->type) { /* These things are ignored (skipped) during validation. */ case XML_PI_NODE: case XML_COMMENT_NODE: case XML_XINCLUDE_START: case XML_XINCLUDE_END: child = child->next; break; case XML_TEXT_NODE: if (xmlIsBlankNode(child)) child = child->next; else return(child); break; /* keep current node */ default: return(child); } } return(child); } /** * xmlValidateElementType: * @ctxt: the validation context * * Try to validate the content model of an element internal function * * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity * reference is found and -3 if the validation succeeded but * the content model is not determinist. */ static int xmlValidateElementType(xmlValidCtxtPtr ctxt) { int ret = -1; int determinist = 1; NODE = xmlValidateSkipIgnorable(NODE); if ((NODE == NULL) && (CONT == NULL)) return(1); if ((NODE == NULL) && ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) || (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) { return(1); } if (CONT == NULL) return(-1); if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE)) return(-2); /* * We arrive here when more states need to be examined */ cont: /* * We just recovered from a rollback generated by a possible * epsilon transition, go directly to the analysis phase */ if (STATE == ROLLBACK_PARENT) { DEBUG_VALID_MSG("restored parent branch"); DEBUG_VALID_STATE(NODE, CONT) ret = 1; goto analyze; } DEBUG_VALID_STATE(NODE, CONT) /* * we may have to save a backup state here. This is the equivalent * of handling epsilon transition in NFAs. */ if ((CONT != NULL) && ((CONT->parent == NULL) || (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) && ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) || (CONT->ocur == XML_ELEMENT_CONTENT_OPT) || ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) { DEBUG_VALID_MSG("saving parent branch"); if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0) return(0); } /* * Check first if the content matches */ switch (CONT->type) { case XML_ELEMENT_CONTENT_PCDATA: if (NODE == NULL) { DEBUG_VALID_MSG("pcdata failed no node"); ret = 0; break; } if (NODE->type == XML_TEXT_NODE) { DEBUG_VALID_MSG("pcdata found, skip to next"); /* * go to next element in the content model * skipping ignorable elems */ do { NODE = NODE->next; NODE = xmlValidateSkipIgnorable(NODE); if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE)) return(-2); } while ((NODE != NULL) && ((NODE->type != XML_ELEMENT_NODE) && (NODE->type != XML_TEXT_NODE) && (NODE->type != XML_CDATA_SECTION_NODE))); ret = 1; break; } else { DEBUG_VALID_MSG("pcdata failed"); ret = 0; break; } break; case XML_ELEMENT_CONTENT_ELEMENT: if (NODE == NULL) { DEBUG_VALID_MSG("element failed no node"); ret = 0; break; } ret = ((NODE->type == XML_ELEMENT_NODE) && (xmlStrEqual(NODE->name, CONT->name))); if (ret == 1) { if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) { ret = (CONT->prefix == NULL); } else if (CONT->prefix == NULL) { ret = 0; } else { ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix); } } if (ret == 1) { DEBUG_VALID_MSG("element found, skip to next"); /* * go to next element in the content model * skipping ignorable elems */ do { NODE = NODE->next; NODE = xmlValidateSkipIgnorable(NODE); if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE)) return(-2); } while ((NODE != NULL) && ((NODE->type != XML_ELEMENT_NODE) && (NODE->type != XML_TEXT_NODE) && (NODE->type != XML_CDATA_SECTION_NODE))); } else { DEBUG_VALID_MSG("element failed"); ret = 0; break; } break; case XML_ELEMENT_CONTENT_OR: /* * Small optimization. */ if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) { if ((NODE == NULL) || (!xmlStrEqual(NODE->name, CONT->c1->name))) { DEPTH++; CONT = CONT->c2; goto cont; } if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) { ret = (CONT->c1->prefix == NULL); } else if (CONT->c1->prefix == NULL) { ret = 0; } else { ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix); } if (ret == 0) { DEPTH++; CONT = CONT->c2; goto cont; } } /* * save the second branch 'or' branch */ DEBUG_VALID_MSG("saving 'or' branch"); if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1), OCCURS, ROLLBACK_OR) < 0) return(-1); DEPTH++; CONT = CONT->c1; goto cont; case XML_ELEMENT_CONTENT_SEQ: /* * Small optimization. */ if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) && ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) || (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) { if ((NODE == NULL) || (!xmlStrEqual(NODE->name, CONT->c1->name))) { DEPTH++; CONT = CONT->c2; goto cont; } if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) { ret = (CONT->c1->prefix == NULL); } else if (CONT->c1->prefix == NULL) { ret = 0; } else { ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix); } if (ret == 0) { DEPTH++; CONT = CONT->c2; goto cont; } } DEPTH++; CONT = CONT->c1; goto cont; } /* * At this point handle going up in the tree */ if (ret == -1) { DEBUG_VALID_MSG("error found returning"); return(ret); } analyze: while (CONT != NULL) { /* * First do the analysis depending on the occurrence model at * this level. */ if (ret == 0) { switch (CONT->ocur) { xmlNodePtr cur; case XML_ELEMENT_CONTENT_ONCE: cur = ctxt->vstate->node; DEBUG_VALID_MSG("Once branch failed, rollback"); if (vstateVPop(ctxt) < 0 ) { DEBUG_VALID_MSG("exhaustion, failed"); return(0); } if (cur != ctxt->vstate->node) determinist = -3; goto cont; case XML_ELEMENT_CONTENT_PLUS: if (OCCURRENCE == 0) { cur = ctxt->vstate->node; DEBUG_VALID_MSG("Plus branch failed, rollback"); if (vstateVPop(ctxt) < 0 ) { DEBUG_VALID_MSG("exhaustion, failed"); return(0); } if (cur != ctxt->vstate->node) determinist = -3; goto cont; } DEBUG_VALID_MSG("Plus branch found"); ret = 1; break; case XML_ELEMENT_CONTENT_MULT: #ifdef DEBUG_VALID_ALGO if (OCCURRENCE == 0) { DEBUG_VALID_MSG("Mult branch failed"); } else { DEBUG_VALID_MSG("Mult branch found"); } #endif ret = 1; break; case XML_ELEMENT_CONTENT_OPT: DEBUG_VALID_MSG("Option branch failed"); ret = 1; break; } } else { switch (CONT->ocur) { case XML_ELEMENT_CONTENT_OPT: DEBUG_VALID_MSG("Option branch succeeded"); ret = 1; break; case XML_ELEMENT_CONTENT_ONCE: DEBUG_VALID_MSG("Once branch succeeded"); ret = 1; break; case XML_ELEMENT_CONTENT_PLUS: if (STATE == ROLLBACK_PARENT) { DEBUG_VALID_MSG("Plus branch rollback"); ret = 1; break; } if (NODE == NULL) { DEBUG_VALID_MSG("Plus branch exhausted"); ret = 1; break; } DEBUG_VALID_MSG("Plus branch succeeded, continuing"); SET_OCCURRENCE; goto cont; case XML_ELEMENT_CONTENT_MULT: if (STATE == ROLLBACK_PARENT) { DEBUG_VALID_MSG("Mult branch rollback"); ret = 1; break; } if (NODE == NULL) { DEBUG_VALID_MSG("Mult branch exhausted"); ret = 1; break; } DEBUG_VALID_MSG("Mult branch succeeded, continuing"); /* SET_OCCURRENCE; */ goto cont; } } STATE = 0; /* * Then act accordingly at the parent level */ RESET_OCCURRENCE; if (CONT->parent == NULL) break; switch (CONT->parent->type) { case XML_ELEMENT_CONTENT_PCDATA: DEBUG_VALID_MSG("Error: parent pcdata"); return(-1); case XML_ELEMENT_CONTENT_ELEMENT: DEBUG_VALID_MSG("Error: parent element"); return(-1); case XML_ELEMENT_CONTENT_OR: if (ret == 1) { DEBUG_VALID_MSG("Or succeeded"); CONT = CONT->parent; DEPTH--; } else { DEBUG_VALID_MSG("Or failed"); CONT = CONT->parent; DEPTH--; } break; case XML_ELEMENT_CONTENT_SEQ: if (ret == 0) { DEBUG_VALID_MSG("Sequence failed"); CONT = CONT->parent; DEPTH--; } else if (CONT == CONT->parent->c1) { DEBUG_VALID_MSG("Sequence testing 2nd branch"); CONT = CONT->parent->c2; goto cont; } else { DEBUG_VALID_MSG("Sequence succeeded"); CONT = CONT->parent; DEPTH--; } } } if (NODE != NULL) { xmlNodePtr cur; cur = ctxt->vstate->node; DEBUG_VALID_MSG("Failed, remaining input, rollback"); if (vstateVPop(ctxt) < 0 ) { DEBUG_VALID_MSG("exhaustion, failed"); return(0); } if (cur != ctxt->vstate->node) determinist = -3; goto cont; } if (ret == 0) { xmlNodePtr cur; cur = ctxt->vstate->node; DEBUG_VALID_MSG("Failure, rollback"); if (vstateVPop(ctxt) < 0 ) { DEBUG_VALID_MSG("exhaustion, failed"); return(0); } if (cur != ctxt->vstate->node) determinist = -3; goto cont; } return(determinist); } #endif /** * xmlSnprintfElements: * @buf: an output buffer * @size: the size of the buffer * @content: An element * @glob: 1 if one must print the englobing parenthesis, 0 otherwise * * This will dump the list of elements to the buffer * Intended just for the debug routine */ static void xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) { xmlNodePtr cur; int len; if (node == NULL) return; if (glob) strcat(buf, "("); cur = node; while (cur != NULL) { len = strlen(buf); if (size - len < 50) { if ((size - len > 4) && (buf[len - 1] != '.')) strcat(buf, " ..."); return; } switch (cur->type) { case XML_ELEMENT_NODE: if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { if (size - len < xmlStrlen(cur->ns->prefix) + 10) { if ((size - len > 4) && (buf[len - 1] != '.')) strcat(buf, " ..."); return; } strcat(buf, (char *) cur->ns->prefix); strcat(buf, ":"); } if (size - len < xmlStrlen(cur->name) + 10) { if ((size - len > 4) && (buf[len - 1] != '.')) strcat(buf, " ..."); return; } strcat(buf, (char *) cur->name); if (cur->next != NULL) strcat(buf, " "); break; case XML_TEXT_NODE: if (xmlIsBlankNode(cur)) break; case XML_CDATA_SECTION_NODE: case XML_ENTITY_REF_NODE: strcat(buf, "CDATA"); if (cur->next != NULL) strcat(buf, " "); break; case XML_ATTRIBUTE_NODE: case XML_DOCUMENT_NODE: #ifdef LIBXML_DOCB_ENABLED case XML_DOCB_DOCUMENT_NODE: #endif case XML_HTML_DOCUMENT_NODE: case XML_DOCUMENT_TYPE_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_NOTATION_NODE: case XML_NAMESPACE_DECL: strcat(buf, "???"); if (cur->next != NULL) strcat(buf, " "); break; case XML_ENTITY_NODE: case XML_PI_NODE: case XML_DTD_NODE: case XML_COMMENT_NODE: case XML_ELEMENT_DECL: case XML_ATTRIBUTE_DECL: case XML_ENTITY_DECL: case XML_XINCLUDE_START: case XML_XINCLUDE_END: break; } cur = cur->next; } if (glob) strcat(buf, ")"); } /** * xmlValidateElementContent: * @ctxt: the validation context * @child: the child list * @elemDecl: pointer to the element declaration * @warn: emit the error message * @parent: the parent element (for error reporting) * * Try to validate the content model of an element * * returns 1 if valid or 0 if not and -1 in case of error */ static int xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child, xmlElementPtr elemDecl, int warn, xmlNodePtr parent) { int ret = 1; #ifndef LIBXML_REGEXP_ENABLED xmlNodePtr repl = NULL, last = NULL, tmp; #endif xmlNodePtr cur; xmlElementContentPtr cont; const xmlChar *name; if (elemDecl == NULL) return(-1); cont = elemDecl->content; name = elemDecl->name; #ifdef LIBXML_REGEXP_ENABLED /* Build the regexp associated to the content model */ if (elemDecl->contModel == NULL) ret = xmlValidBuildContentModel(ctxt, elemDecl); if (elemDecl->contModel == NULL) { return(-1); } else { xmlRegExecCtxtPtr exec; if (!xmlRegexpIsDeterminist(elemDecl->contModel)) { return(-1); } ctxt->nodeMax = 0; ctxt->nodeNr = 0; ctxt->nodeTab = NULL; exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL); if (exec != NULL) { cur = child; while (cur != NULL) { switch (cur->type) { case XML_ENTITY_REF_NODE: /* * Push the current node to be able to roll back * and process within the entity */ if ((cur->children != NULL) && (cur->children->children != NULL)) { nodeVPush(ctxt, cur); cur = cur->children->children; continue; } break; case XML_TEXT_NODE: if (xmlIsBlankNode(cur)) break; ret = 0; goto fail; case XML_CDATA_SECTION_NODE: /* TODO */ ret = 0; goto fail; case XML_ELEMENT_NODE: if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { xmlChar fn[50]; xmlChar *fullname; fullname = xmlBuildQName(cur->name, cur->ns->prefix, fn, 50); if (fullname == NULL) { ret = -1; goto fail; } ret = xmlRegExecPushString(exec, fullname, NULL); if ((fullname != fn) && (fullname != cur->name)) xmlFree(fullname); } else { ret = xmlRegExecPushString(exec, cur->name, NULL); } break; default: break; } /* * Switch to next element */ cur = cur->next; while (cur == NULL) { cur = nodeVPop(ctxt); if (cur == NULL) break; cur = cur->next; } } ret = xmlRegExecPushString(exec, NULL, NULL); fail: xmlRegFreeExecCtxt(exec); } } #else /* LIBXML_REGEXP_ENABLED */ /* * Allocate the stack */ ctxt->vstateMax = 8; ctxt->vstateTab = (xmlValidState *) xmlMalloc( ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); if (ctxt->vstateTab == NULL) { xmlVErrMemory(ctxt, "malloc failed"); return(-1); } /* * The first entry in the stack is reserved to the current state */ ctxt->nodeMax = 0; ctxt->nodeNr = 0; ctxt->nodeTab = NULL; ctxt->vstate = &ctxt->vstateTab[0]; ctxt->vstateNr = 1; CONT = cont; NODE = child; DEPTH = 0; OCCURS = 0; STATE = 0; ret = xmlValidateElementType(ctxt); if ((ret == -3) && (warn)) { xmlErrValidWarning(ctxt, child, XML_DTD_CONTENT_NOT_DETERMINIST, "Content model for Element %s is ambiguous\n", name, NULL, NULL); } else if (ret == -2) { /* * An entities reference appeared at this level. * Buid a minimal representation of this node content * sufficient to run the validation process on it */ DEBUG_VALID_MSG("Found an entity reference, linearizing"); cur = child; while (cur != NULL) { switch (cur->type) { case XML_ENTITY_REF_NODE: /* * Push the current node to be able to roll back * and process within the entity */ if ((cur->children != NULL) && (cur->children->children != NULL)) { nodeVPush(ctxt, cur); cur = cur->children->children; continue; } break; case XML_TEXT_NODE: if (xmlIsBlankNode(cur)) break; /* no break on purpose */ case XML_CDATA_SECTION_NODE: /* no break on purpose */ case XML_ELEMENT_NODE: /* * Allocate a new node and minimally fills in * what's required */ tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); if (tmp == NULL) { xmlVErrMemory(ctxt, "malloc failed"); xmlFreeNodeList(repl); ret = -1; goto done; } tmp->type = cur->type; tmp->name = cur->name; tmp->ns = cur->ns; tmp->next = NULL; tmp->content = NULL; if (repl == NULL) repl = last = tmp; else { last->next = tmp; last = tmp; } if (cur->type == XML_CDATA_SECTION_NODE) { /* * E59 spaces in CDATA does not match the * nonterminal S */ tmp->content = xmlStrdup(BAD_CAST "CDATA"); } break; default: break; } /* * Switch to next element */ cur = cur->next; while (cur == NULL) { cur = nodeVPop(ctxt); if (cur == NULL) break; cur = cur->next; } } /* * Relaunch the validation */ ctxt->vstate = &ctxt->vstateTab[0]; ctxt->vstateNr = 1; CONT = cont; NODE = repl; DEPTH = 0; OCCURS = 0; STATE = 0; ret = xmlValidateElementType(ctxt); } #endif /* LIBXML_REGEXP_ENABLED */ if ((warn) && ((ret != 1) && (ret != -3))) { if ((ctxt != NULL) && (ctxt->warning != NULL)) { char expr[5000]; char list[5000]; expr[0] = 0; xmlSnprintfElementContent(&expr[0], 5000, cont, 1); list[0] = 0; #ifndef LIBXML_REGEXP_ENABLED if (repl != NULL) xmlSnprintfElements(&list[0], 5000, repl, 1); else #endif /* LIBXML_REGEXP_ENABLED */ xmlSnprintfElements(&list[0], 5000, child, 1); if (name != NULL) { xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, "Element %s content does not follow the DTD, expecting %s, got %s\n", name, BAD_CAST expr, BAD_CAST list); } else { xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, "Element content does not follow the DTD, expecting %s, got %s\n", BAD_CAST expr, BAD_CAST list, NULL); } } else { if (name != NULL) { xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, "Element %s content does not follow the DTD\n", name, NULL, NULL); } else { xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL, "Element content does not follow the DTD\n", NULL, NULL, NULL); } } ret = 0; } if (ret == -3) ret = 1; #ifndef LIBXML_REGEXP_ENABLED done: /* * Deallocate the copy if done, and free up the validation stack */ while (repl != NULL) { tmp = repl->next; xmlFree(repl); repl = tmp; } ctxt->vstateMax = 0; if (ctxt->vstateTab != NULL) { xmlFree(ctxt->vstateTab); ctxt->vstateTab = NULL; } #endif ctxt->nodeMax = 0; ctxt->nodeNr = 0; if (ctxt->nodeTab != NULL) { xmlFree(ctxt->nodeTab); ctxt->nodeTab = NULL; } return(ret); } /** * xmlValidateCdataElement: * @ctxt: the validation context * @doc: a document instance * @elem: an element instance * * Check that an element follows #CDATA * * returns 1 if valid or 0 otherwise */ static int xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) { int ret = 1; xmlNodePtr cur, child; if ((ctxt == NULL) || (doc == NULL) || (elem == NULL)) return(0); child = elem->children; cur = child; while (cur != NULL) { switch (cur->type) { case XML_ENTITY_REF_NODE: /* * Push the current node to be able to roll back * and process within the entity */ if ((cur->children != NULL) && (cur->children->children != NULL)) { nodeVPush(ctxt, cur); cur = cur->children->children; continue; } break; case XML_COMMENT_NODE: case XML_PI_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: break; default: ret = 0; goto done; } /* * Switch to next element */ cur = cur->next; while (cur == NULL) { cur = nodeVPop(ctxt); if (cur == NULL) break; cur = cur->next; } } done: ctxt->nodeMax = 0; ctxt->nodeNr = 0; if (ctxt->nodeTab != NULL) { xmlFree(ctxt->nodeTab); ctxt->nodeTab = NULL; } return(ret); } /** * xmlValidateCheckMixed: * @ctxt: the validation context * @cont: the mixed content model * @qname: the qualified name as appearing in the serialization * * Check if the given node is part of the content model. * * Returns 1 if yes, 0 if no, -1 in case of error */ static int xmlValidateCheckMixed(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont, const xmlChar *qname) { const xmlChar *name; int plen; name = xmlSplitQName3(qname, &plen); if (name == NULL) { while (cont != NULL) { if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname))) return(1); } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && (cont->c1 != NULL) && (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){ if ((cont->c1->prefix == NULL) && (xmlStrEqual(cont->c1->name, qname))) return(1); } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || (cont->c1 == NULL) || (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){ xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT, "Internal: MIXED struct corrupted\n", NULL); break; } cont = cont->c2; } } else { while (cont != NULL) { if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { if ((cont->prefix != NULL) && (xmlStrncmp(cont->prefix, qname, plen) == 0) && (xmlStrEqual(cont->name, name))) return(1); } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && (cont->c1 != NULL) && (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){ if ((cont->c1->prefix != NULL) && (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) && (xmlStrEqual(cont->c1->name, name))) return(1); } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || (cont->c1 == NULL) || (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){ xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT, "Internal: MIXED struct corrupted\n", NULL); break; } cont = cont->c2; } } return(0); } /** * xmlValidGetElemDecl: * @ctxt: the validation context * @doc: a document instance * @elem: an element instance * @extsubset: pointer, (out) indicate if the declaration was found * in the external subset. * * Finds a declaration associated to an element in the document. * * returns the pointer to the declaration or NULL if not found. */ static xmlElementPtr xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem, int *extsubset) { xmlElementPtr elemDecl = NULL; const xmlChar *prefix = NULL; if ((ctxt == NULL) || (doc == NULL) || (elem == NULL) || (elem->name == NULL)) return(NULL); if (extsubset != NULL) *extsubset = 0; /* * Fetch the declaration for the qualified name */ if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) prefix = elem->ns->prefix; if (prefix != NULL) { elemDecl = xmlGetDtdQElementDesc(doc->intSubset, elem->name, prefix); if ((elemDecl == NULL) && (doc->extSubset != NULL)) { elemDecl = xmlGetDtdQElementDesc(doc->extSubset, elem->name, prefix); if ((elemDecl != NULL) && (extsubset != NULL)) *extsubset = 1; } } /* * Fetch the declaration for the non qualified name * This is "non-strict" validation should be done on the * full QName but in that case being flexible makes sense. */ if (elemDecl == NULL) { elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name); if ((elemDecl == NULL) && (doc->extSubset != NULL)) { elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name); if ((elemDecl != NULL) && (extsubset != NULL)) *extsubset = 1; } } if (elemDecl == NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM, "No declaration for element %s\n", elem->name, NULL, NULL); } return(elemDecl); } #ifdef LIBXML_REGEXP_ENABLED /** * xmlValidatePushElement: * @ctxt: the validation context * @doc: a document instance * @elem: an element instance * @qname: the qualified name as appearing in the serialization * * Push a new element start on the validation stack. * * returns 1 if no validation problem was found or 0 otherwise */ int xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem, const xmlChar *qname) { int ret = 1; xmlElementPtr eDecl; int extsubset = 0; if (ctxt == NULL) return(0); /* printf("PushElem %s\n", qname); */ if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) { xmlValidStatePtr state = ctxt->vstate; xmlElementPtr elemDecl; /* * Check the new element agaisnt the content model of the new elem. */ if (state->elemDecl != NULL) { elemDecl = state->elemDecl; switch(elemDecl->etype) { case XML_ELEMENT_TYPE_UNDEFINED: ret = 0; break; case XML_ELEMENT_TYPE_EMPTY: xmlErrValidNode(ctxt, state->node, XML_DTD_NOT_EMPTY, "Element %s was declared EMPTY this one has content\n", state->node->name, NULL, NULL); ret = 0; break; case XML_ELEMENT_TYPE_ANY: /* I don't think anything is required then */ break; case XML_ELEMENT_TYPE_MIXED: /* simple case of declared as #PCDATA */ if ((elemDecl->content != NULL) && (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) { xmlErrValidNode(ctxt, state->node, XML_DTD_NOT_PCDATA, "Element %s was declared #PCDATA but contains non text nodes\n", state->node->name, NULL, NULL); ret = 0; } else { ret = xmlValidateCheckMixed(ctxt, elemDecl->content, qname); if (ret != 1) { xmlErrValidNode(ctxt, state->node, XML_DTD_INVALID_CHILD, "Element %s is not declared in %s list of possible children\n", qname, state->node->name, NULL); } } break; case XML_ELEMENT_TYPE_ELEMENT: /* * TODO: * VC: Standalone Document Declaration * - element types with element content, if white space * occurs directly within any instance of those types. */ if (state->exec != NULL) { ret = xmlRegExecPushString(state->exec, qname, NULL); if (ret < 0) { xmlErrValidNode(ctxt, state->node, XML_DTD_CONTENT_MODEL, "Element %s content does not follow the DTD, Misplaced %s\n", state->node->name, qname, NULL); ret = 0; } else { ret = 1; } } break; } } } eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset); vstateVPush(ctxt, eDecl, elem); return(ret); } /** * xmlValidatePushCData: * @ctxt: the validation context * @data: some character data read * @len: the lenght of the data * * check the CData parsed for validation in the current stack * * returns 1 if no validation problem was found or 0 otherwise */ int xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) { int ret = 1; /* printf("CDATA %s %d\n", data, len); */ if (ctxt == NULL) return(0); if (len <= 0) return(ret); if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) { xmlValidStatePtr state = ctxt->vstate; xmlElementPtr elemDecl; /* * Check the new element agaisnt the content model of the new elem. */ if (state->elemDecl != NULL) { elemDecl = state->elemDecl; switch(elemDecl->etype) { case XML_ELEMENT_TYPE_UNDEFINED: ret = 0; break; case XML_ELEMENT_TYPE_EMPTY: xmlErrValidNode(ctxt, state->node, XML_DTD_NOT_EMPTY, "Element %s was declared EMPTY this one has content\n", state->node->name, NULL, NULL); ret = 0; break; case XML_ELEMENT_TYPE_ANY: break; case XML_ELEMENT_TYPE_MIXED: break; case XML_ELEMENT_TYPE_ELEMENT: if (len > 0) { int i; for (i = 0;i < len;i++) { if (!IS_BLANK_CH(data[i])) { xmlErrValidNode(ctxt, state->node, XML_DTD_CONTENT_MODEL, "Element %s content does not follow the DTD, Text not allowed\n", state->node->name, NULL, NULL); ret = 0; goto done; } } /* * TODO: * VC: Standalone Document Declaration * element types with element content, if white space * occurs directly within any instance of those types. */ } break; } } } done: return(ret); } /** * xmlValidatePopElement: * @ctxt: the validation context * @doc: a document instance * @elem: an element instance * @qname: the qualified name as appearing in the serialization * * Pop the element end from the validation stack. * * returns 1 if no validation problem was found or 0 otherwise */ int xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr elem ATTRIBUTE_UNUSED, const xmlChar *qname ATTRIBUTE_UNUSED) { int ret = 1; if (ctxt == NULL) return(0); /* printf("PopElem %s\n", qname); */ if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) { xmlValidStatePtr state = ctxt->vstate; xmlElementPtr elemDecl; /* * Check the new element agaisnt the content model of the new elem. */ if (state->elemDecl != NULL) { elemDecl = state->elemDecl; if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) { if (state->exec != NULL) { ret = xmlRegExecPushString(state->exec, NULL, NULL); if (ret == 0) { xmlErrValidNode(ctxt, state->node, XML_DTD_CONTENT_MODEL, "Element %s content does not follow the DTD, Expecting more child\n", state->node->name, NULL,NULL); } else { /* * previous validation errors should not generate * a new one here */ ret = 1; } } } } vstateVPop(ctxt); } return(ret); } #endif /* LIBXML_REGEXP_ENABLED */ /** * xmlValidateOneElement: * @ctxt: the validation context * @doc: a document instance * @elem: an element instance * * Try to validate a single element and it's attributes, * basically it does the following checks as described by the * XML-1.0 recommendation: * - [ VC: Element Valid ] * - [ VC: Required Attribute ] * Then call xmlValidateOneAttribute() for each attribute present. * * The ID/IDREF checkings are done separately * * returns 1 if valid or 0 otherwise */ int xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) { xmlElementPtr elemDecl = NULL; xmlElementContentPtr cont; xmlAttributePtr attr; xmlNodePtr child; int ret = 1, tmp; const xmlChar *name; int extsubset = 0; CHECK_DTD; if (elem == NULL) return(0); switch (elem->type) { case XML_ATTRIBUTE_NODE: xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, "Attribute element not expected\n", NULL, NULL ,NULL); return(0); case XML_TEXT_NODE: if (elem->children != NULL) { xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, "Text element has children !\n", NULL,NULL,NULL); return(0); } if (elem->ns != NULL) { xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, "Text element has namespace !\n", NULL,NULL,NULL); return(0); } if (elem->content == NULL) { xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, "Text element has no content !\n", NULL,NULL,NULL); return(0); } return(1); case XML_XINCLUDE_START: case XML_XINCLUDE_END: return(1); case XML_CDATA_SECTION_NODE: case XML_ENTITY_REF_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: return(1); case XML_ENTITY_NODE: xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, "Entity element not expected\n", NULL, NULL ,NULL); return(0); case XML_NOTATION_NODE: xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, "Notation element not expected\n", NULL, NULL ,NULL); return(0); case XML_DOCUMENT_NODE: case XML_DOCUMENT_TYPE_NODE: case XML_DOCUMENT_FRAG_NODE: xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, "Document element not expected\n", NULL, NULL ,NULL); return(0); case XML_HTML_DOCUMENT_NODE: xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, "HTML Document not expected\n", NULL, NULL ,NULL); return(0); case XML_ELEMENT_NODE: break; default: xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, "unknown element type\n", NULL, NULL ,NULL); return(0); } /* * Fetch the declaration */ elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset); if (elemDecl == NULL) return(0); /* * If vstateNr is not zero that means continuous validation is * activated, do not try to check the content model at that level. */ if (ctxt->vstateNr == 0) { /* Check that the element content matches the definition */ switch (elemDecl->etype) { case XML_ELEMENT_TYPE_UNDEFINED: xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM, "No declaration for element %s\n", elem->name, NULL, NULL); return(0); case XML_ELEMENT_TYPE_EMPTY: if (elem->children != NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_NOT_EMPTY, "Element %s was declared EMPTY this one has content\n", elem->name, NULL, NULL); ret = 0; } break; case XML_ELEMENT_TYPE_ANY: /* I don't think anything is required then */ break; case XML_ELEMENT_TYPE_MIXED: /* simple case of declared as #PCDATA */ if ((elemDecl->content != NULL) && (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) { ret = xmlValidateOneCdataElement(ctxt, doc, elem); if (!ret) { xmlErrValidNode(ctxt, elem, XML_DTD_NOT_PCDATA, "Element %s was declared #PCDATA but contains non text nodes\n", elem->name, NULL, NULL); } break; } child = elem->children; /* Hum, this start to get messy */ while (child != NULL) { if (child->type == XML_ELEMENT_NODE) { name = child->name; if ((child->ns != NULL) && (child->ns->prefix != NULL)) { xmlChar fn[50]; xmlChar *fullname; fullname = xmlBuildQName(child->name, child->ns->prefix, fn, 50); if (fullname == NULL) return(0); cont = elemDecl->content; while (cont != NULL) { if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { if (xmlStrEqual(cont->name, fullname)) break; } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && (cont->c1 != NULL) && (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){ if (xmlStrEqual(cont->c1->name, fullname)) break; } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || (cont->c1 == NULL) || (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){ xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT, "Internal: MIXED struct corrupted\n", NULL); break; } cont = cont->c2; } if ((fullname != fn) && (fullname != child->name)) xmlFree(fullname); if (cont != NULL) goto child_ok; } cont = elemDecl->content; while (cont != NULL) { if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { if (xmlStrEqual(cont->name, name)) break; } else if ((cont->type == XML_ELEMENT_CONTENT_OR) && (cont->c1 != NULL) && (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) { if (xmlStrEqual(cont->c1->name, name)) break; } else if ((cont->type != XML_ELEMENT_CONTENT_OR) || (cont->c1 == NULL) || (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) { xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT, "Internal: MIXED struct corrupted\n", NULL); break; } cont = cont->c2; } if (cont == NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_CHILD, "Element %s is not declared in %s list of possible children\n", name, elem->name, NULL); ret = 0; } } child_ok: child = child->next; } break; case XML_ELEMENT_TYPE_ELEMENT: if ((doc->standalone == 1) && (extsubset == 1)) { /* * VC: Standalone Document Declaration * - element types with element content, if white space * occurs directly within any instance of those types. */ child = elem->children; while (child != NULL) { if (child->type == XML_TEXT_NODE) { const xmlChar *content = child->content; while (IS_BLANK_CH(*content)) content++; if (*content == 0) { xmlErrValidNode(ctxt, elem, XML_DTD_STANDALONE_WHITE_SPACE, "standalone: %s declared in the external subset contains white spaces nodes\n", elem->name, NULL, NULL); ret = 0; break; } } child =child->next; } } child = elem->children; cont = elemDecl->content; tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem); if (tmp <= 0) ret = tmp; break; } } /* not continuous */ /* [ VC: Required Attribute ] */ attr = elemDecl->attributes; while (attr != NULL) { if (attr->def == XML_ATTRIBUTE_REQUIRED) { int qualified = -1; if ((attr->prefix == NULL) && (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) { xmlNsPtr ns; ns = elem->nsDef; while (ns != NULL) { if (ns->prefix == NULL) goto found; ns = ns->next; } } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) { xmlNsPtr ns; ns = elem->nsDef; while (ns != NULL) { if (xmlStrEqual(attr->name, ns->prefix)) goto found; ns = ns->next; } } else { xmlAttrPtr attrib; attrib = elem->properties; while (attrib != NULL) { if (xmlStrEqual(attrib->name, attr->name)) { if (attr->prefix != NULL) { xmlNsPtr nameSpace = attrib->ns; if (nameSpace == NULL) nameSpace = elem->ns; /* * qualified names handling is problematic, having a * different prefix should be possible but DTDs don't * allow to define the URI instead of the prefix :-( */ if (nameSpace == NULL) { if (qualified < 0) qualified = 0; } else if (!xmlStrEqual(nameSpace->prefix, attr->prefix)) { if (qualified < 1) qualified = 1; } else goto found; } else { /* * We should allow applications to define namespaces * for their application even if the DTD doesn't * carry one, otherwise, basically we would always * break. */ goto found; } } attrib = attrib->next; } } if (qualified == -1) { if (attr->prefix == NULL) { xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE, "Element %s does not carry attribute %s\n", elem->name, attr->name, NULL); ret = 0; } else { xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE, "Element %s does not carry attribute %s:%s\n", elem->name, attr->prefix,attr->name); ret = 0; } } else if (qualified == 0) { xmlErrValidWarning(ctxt, elem, XML_DTD_NO_PREFIX, "Element %s required attribute %s:%s has no prefix\n", elem->name, attr->prefix, attr->name); } else if (qualified == 1) { xmlErrValidWarning(ctxt, elem, XML_DTD_DIFFERENT_PREFIX, "Element %s required attribute %s:%s has different prefix\n", elem->name, attr->prefix, attr->name); } } else if (attr->def == XML_ATTRIBUTE_FIXED) { /* * Special tests checking #FIXED namespace declarations * have the right value since this is not done as an * attribute checking */ if ((attr->prefix == NULL) && (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) { xmlNsPtr ns; ns = elem->nsDef; while (ns != NULL) { if (ns->prefix == NULL) { if (!xmlStrEqual(attr->defaultValue, ns->href)) { xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_DEFAULT_NAMESPACE, "Element %s namespace name for default namespace does not match the DTD\n", elem->name, NULL, NULL); ret = 0; } goto found; } ns = ns->next; } } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) { xmlNsPtr ns; ns = elem->nsDef; while (ns != NULL) { if (xmlStrEqual(attr->name, ns->prefix)) { if (!xmlStrEqual(attr->defaultValue, ns->href)) { xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE, "Element %s namespace name for %s does not match the DTD\n", elem->name, ns->prefix, NULL); ret = 0; } goto found; } ns = ns->next; } } } found: attr = attr->nexth; } return(ret); } /** * xmlValidateRoot: * @ctxt: the validation context * @doc: a document instance * * Try to validate a the root element * basically it does the following check as described by the * XML-1.0 recommendation: * - [ VC: Root Element Type ] * it doesn't try to recurse or apply other check to the element * * returns 1 if valid or 0 otherwise */ int xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { xmlNodePtr root; int ret; if (doc == NULL) return(0); root = xmlDocGetRootElement(doc); if ((root == NULL) || (root->name == NULL)) { xmlErrValid(ctxt, XML_DTD_NO_ROOT, "no root element\n", NULL); return(0); } /* * When doing post validation against a separate DTD, those may * no internal subset has been generated */ if ((doc->intSubset != NULL) && (doc->intSubset->name != NULL)) { /* * Check first the document root against the NQName */ if (!xmlStrEqual(doc->intSubset->name, root->name)) { if ((root->ns != NULL) && (root->ns->prefix != NULL)) { xmlChar fn[50]; xmlChar *fullname; fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50); if (fullname == NULL) { xmlVErrMemory(ctxt, NULL); return(0); } ret = xmlStrEqual(doc->intSubset->name, fullname); if ((fullname != fn) && (fullname != root->name)) xmlFree(fullname); if (ret == 1) goto name_ok; } if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) && (xmlStrEqual(root->name, BAD_CAST "html"))) goto name_ok; xmlErrValidNode(ctxt, root, XML_DTD_ROOT_NAME, "root and DTD name do not match '%s' and '%s'\n", root->name, doc->intSubset->name, NULL); return(0); } } name_ok: return(1); } /** * xmlValidateElement: * @ctxt: the validation context * @doc: a document instance * @elem: an element instance * * Try to validate the subtree under an element * * returns 1 if valid or 0 otherwise */ int xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) { xmlNodePtr child; xmlAttrPtr attr; xmlNsPtr ns; const xmlChar *value; int ret = 1; if (elem == NULL) return(0); /* * XInclude elements were added after parsing in the infoset, * they don't really mean anything validation wise. */ if ((elem->type == XML_XINCLUDE_START) || (elem->type == XML_XINCLUDE_END)) return(1); CHECK_DTD; /* * Entities references have to be handled separately */ if (elem->type == XML_ENTITY_REF_NODE) { return(1); } ret &= xmlValidateOneElement(ctxt, doc, elem); if (elem->type == XML_ELEMENT_NODE) { attr = elem->properties; while (attr != NULL) { value = xmlNodeListGetString(doc, attr->children, 0); ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value); if (value != NULL) xmlFree((char *)value); attr= attr->next; } ns = elem->nsDef; while (ns != NULL) { if (elem->ns == NULL) ret &= xmlValidateOneNamespace(ctxt, doc, elem, NULL, ns, ns->href); else ret &= xmlValidateOneNamespace(ctxt, doc, elem, elem->ns->prefix, ns, ns->href); ns = ns->next; } } child = elem->children; while (child != NULL) { ret &= xmlValidateElement(ctxt, doc, child); child = child->next; } return(ret); } /** * xmlValidateRef: * @ref: A reference to be validated * @ctxt: Validation context * @name: Name of ID we are searching for * */ static void xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt, const xmlChar *name) { xmlAttrPtr id; xmlAttrPtr attr; if (ref == NULL) return; if ((ref->attr == NULL) && (ref->name == NULL)) return; attr = ref->attr; if (attr == NULL) { xmlChar *dup, *str = NULL, *cur, save; dup = xmlStrdup(name); if (dup == NULL) { ctxt->valid = 0; return; } cur = dup; while (*cur != 0) { str = cur; while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++; save = *cur; *cur = 0; id = xmlGetID(ctxt->doc, str); if (id == NULL) { xmlErrValidNodeNr(ctxt, NULL, XML_DTD_UNKNOWN_ID, "attribute %s line %d references an unknown ID \"%s\"\n", ref->name, ref->lineno, str); ctxt->valid = 0; } if (save == 0) break; *cur = save; while (IS_BLANK_CH(*cur)) cur++; } xmlFree(dup); } else if (attr->atype == XML_ATTRIBUTE_IDREF) { id = xmlGetID(ctxt->doc, name); if (id == NULL) { xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID, "IDREF attribute %s references an unknown ID \"%s\"\n", attr->name, name, NULL); ctxt->valid = 0; } } else if (attr->atype == XML_ATTRIBUTE_IDREFS) { xmlChar *dup, *str = NULL, *cur, save; dup = xmlStrdup(name); if (dup == NULL) { xmlVErrMemory(ctxt, "IDREFS split"); ctxt->valid = 0; return; } cur = dup; while (*cur != 0) { str = cur; while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++; save = *cur; *cur = 0; id = xmlGetID(ctxt->doc, str); if (id == NULL) { xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID, "IDREFS attribute %s references an unknown ID \"%s\"\n", attr->name, str, NULL); ctxt->valid = 0; } if (save == 0) break; *cur = save; while (IS_BLANK_CH(*cur)) cur++; } xmlFree(dup); } } /** * xmlWalkValidateList: * @data: Contents of current link * @user: Value supplied by the user * * Returns 0 to abort the walk or 1 to continue */ static int xmlWalkValidateList(const void *data, const void *user) { xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user; xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name); return 1; } /** * xmlValidateCheckRefCallback: * @ref_list: List of references * @ctxt: Validation context * @name: Name of ID we are searching for * */ static void xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt, const xmlChar *name) { xmlValidateMemo memo; if (ref_list == NULL) return; memo.ctxt = ctxt; memo.name = name; xmlListWalk(ref_list, xmlWalkValidateList, &memo); } /** * xmlValidateDocumentFinal: * @ctxt: the validation context * @doc: a document instance * * Does the final step for the document validation once all the * incremental validation steps have been completed * * basically it does the following checks described by the XML Rec * * Check all the IDREF/IDREFS attributes definition for validity * * returns 1 if valid or 0 otherwise */ int xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { xmlRefTablePtr table; if (ctxt == NULL) return(0); if (doc == NULL) { xmlErrValid(ctxt, XML_DTD_NO_DOC, "xmlValidateDocumentFinal: doc == NULL\n", NULL); return(0); } /* * Check all the NOTATION/NOTATIONS attributes */ /* * Check all the ENTITY/ENTITIES attributes definition for validity */ /* * Check all the IDREF/IDREFS attributes definition for validity */ table = (xmlRefTablePtr) doc->refs; ctxt->doc = doc; ctxt->valid = 1; xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt); return(ctxt->valid); } /** * xmlValidateDtd: * @ctxt: the validation context * @doc: a document instance * @dtd: a dtd instance * * Try to validate the document against the dtd instance * * Basically it does check all the definitions in the DtD. * Note the the internal subset (if present) is de-coupled * (i.e. not used), which could give problems if ID or IDREF * is present. * * returns 1 if valid or 0 otherwise */ int xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) { int ret; xmlDtdPtr oldExt, oldInt; xmlNodePtr root; if (dtd == NULL) return(0); if (doc == NULL) return(0); oldExt = doc->extSubset; oldInt = doc->intSubset; doc->extSubset = dtd; doc->intSubset = NULL; ret = xmlValidateRoot(ctxt, doc); if (ret == 0) { doc->extSubset = oldExt; doc->intSubset = oldInt; return(ret); } if (doc->ids != NULL) { xmlFreeIDTable(doc->ids); doc->ids = NULL; } if (doc->refs != NULL) { xmlFreeRefTable(doc->refs); doc->refs = NULL; } root = xmlDocGetRootElement(doc); ret = xmlValidateElement(ctxt, doc, root); ret &= xmlValidateDocumentFinal(ctxt, doc); doc->extSubset = oldExt; doc->intSubset = oldInt; return(ret); } static void xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt, const xmlChar *name ATTRIBUTE_UNUSED) { if (cur == NULL) return; if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) { xmlChar *notation = cur->content; if (notation != NULL) { int ret; ret = xmlValidateNotationUse(ctxt, cur->doc, notation); if (ret != 1) { ctxt->valid = 0; } } } } static void xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt, const xmlChar *name ATTRIBUTE_UNUSED) { int ret; xmlDocPtr doc; xmlElementPtr elem = NULL; if (cur == NULL) return; switch (cur->atype) { case XML_ATTRIBUTE_CDATA: case XML_ATTRIBUTE_ID: case XML_ATTRIBUTE_IDREF : case XML_ATTRIBUTE_IDREFS: case XML_ATTRIBUTE_NMTOKEN: case XML_ATTRIBUTE_NMTOKENS: case XML_ATTRIBUTE_ENUMERATION: break; case XML_ATTRIBUTE_ENTITY: case XML_ATTRIBUTE_ENTITIES: case XML_ATTRIBUTE_NOTATION: if (cur->defaultValue != NULL) { ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name, cur->atype, cur->defaultValue); if ((ret == 0) && (ctxt->valid == 1)) ctxt->valid = 0; } if (cur->tree != NULL) { xmlEnumerationPtr tree = cur->tree; while (tree != NULL) { ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name, cur->atype, tree->name); if ((ret == 0) && (ctxt->valid == 1)) ctxt->valid = 0; tree = tree->next; } } } if (cur->atype == XML_ATTRIBUTE_NOTATION) { doc = cur->doc; if (cur->elem == NULL) { xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, "xmlValidateAttributeCallback(%s): internal error\n", (const char *) cur->name); return; } if (doc != NULL) elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem); if ((elem == NULL) && (doc != NULL)) elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem); if ((elem == NULL) && (cur->parent != NULL) && (cur->parent->type == XML_DTD_NODE)) elem = xmlGetDtdElementDesc((xmlDtdPtr) cur->parent, cur->elem); if (elem == NULL) { xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM, "attribute %s: could not find decl for element %s\n", cur->name, cur->elem, NULL); return; } if (elem->etype == XML_ELEMENT_TYPE_EMPTY) { xmlErrValidNode(ctxt, NULL, XML_DTD_EMPTY_NOTATION, "NOTATION attribute %s declared for EMPTY element %s\n", cur->name, cur->elem, NULL); ctxt->valid = 0; } } } /** * xmlValidateDtdFinal: * @ctxt: the validation context * @doc: a document instance * * Does the final step for the dtds validation once all the * subsets have been parsed * * basically it does the following checks described by the XML Rec * - check that ENTITY and ENTITIES type attributes default or * possible values matches one of the defined entities. * - check that NOTATION type attributes default or * possible values matches one of the defined notations. * * returns 1 if valid or 0 if invalid and -1 if not well-formed */ int xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { xmlDtdPtr dtd; xmlAttributeTablePtr table; xmlEntitiesTablePtr entities; if (doc == NULL) return(0); if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) return(0); ctxt->doc = doc; ctxt->valid = 1; dtd = doc->intSubset; if ((dtd != NULL) && (dtd->attributes != NULL)) { table = (xmlAttributeTablePtr) dtd->attributes; xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt); } if ((dtd != NULL) && (dtd->entities != NULL)) { entities = (xmlEntitiesTablePtr) dtd->entities; xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback, ctxt); } dtd = doc->extSubset; if ((dtd != NULL) && (dtd->attributes != NULL)) { table = (xmlAttributeTablePtr) dtd->attributes; xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt); } if ((dtd != NULL) && (dtd->entities != NULL)) { entities = (xmlEntitiesTablePtr) dtd->entities; xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback, ctxt); } return(ctxt->valid); } /** * xmlValidateDocument: * @ctxt: the validation context * @doc: a document instance * * Try to validate the document instance * * basically it does the all the checks described by the XML Rec * i.e. validates the internal and external subset (if present) * and validate the document tree. * * returns 1 if valid or 0 otherwise */ int xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { int ret; xmlNodePtr root; if (doc == NULL) return(0); if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) { xmlErrValid(ctxt, XML_DTD_NO_DTD, "no DTD found!\n", NULL); return(0); } if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) || (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) { xmlChar *sysID; if (doc->intSubset->SystemID != NULL) { sysID = xmlBuildURI(doc->intSubset->SystemID, doc->URL); if (sysID == NULL) { xmlErrValid(ctxt, XML_DTD_LOAD_ERROR, "Could not build URI for external subset \"%s\"\n", (const char *) doc->intSubset->SystemID); return 0; } } else sysID = NULL; doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID, (const xmlChar *)sysID); if (sysID != NULL) xmlFree(sysID); if (doc->extSubset == NULL) { if (doc->intSubset->SystemID != NULL) { xmlErrValid(ctxt, XML_DTD_LOAD_ERROR, "Could not load the external subset \"%s\"\n", (const char *) doc->intSubset->SystemID); } else { xmlErrValid(ctxt, XML_DTD_LOAD_ERROR, "Could not load the external subset \"%s\"\n", (const char *) doc->intSubset->ExternalID); } return(0); } } if (doc->ids != NULL) { xmlFreeIDTable(doc->ids); doc->ids = NULL; } if (doc->refs != NULL) { xmlFreeRefTable(doc->refs); doc->refs = NULL; } ret = xmlValidateDtdFinal(ctxt, doc); if (!xmlValidateRoot(ctxt, doc)) return(0); root = xmlDocGetRootElement(doc); ret &= xmlValidateElement(ctxt, doc, root); ret &= xmlValidateDocumentFinal(ctxt, doc); return(ret); } /************************************************************************ * * * Routines for dynamic validation editing * * * ************************************************************************/ /** * xmlValidGetPotentialChildren: * @ctree: an element content tree * @names: an array to store the list of child names * @len: a pointer to the number of element in the list * @max: the size of the array * * Build/extend a list of potential children allowed by the content tree * * returns the number of element in the list, or -1 in case of error. */ int xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **names, int *len, int max) { int i; if ((ctree == NULL) || (names == NULL) || (len == NULL)) return(-1); if (*len >= max) return(*len); switch (ctree->type) { case XML_ELEMENT_CONTENT_PCDATA: for (i = 0; i < *len;i++) if (xmlStrEqual(BAD_CAST "#PCDATA", names[i])) return(*len); names[(*len)++] = BAD_CAST "#PCDATA"; break; case XML_ELEMENT_CONTENT_ELEMENT: for (i = 0; i < *len;i++) if (xmlStrEqual(ctree->name, names[i])) return(*len); names[(*len)++] = ctree->name; break; case XML_ELEMENT_CONTENT_SEQ: xmlValidGetPotentialChildren(ctree->c1, names, len, max); xmlValidGetPotentialChildren(ctree->c2, names, len, max); break; case XML_ELEMENT_CONTENT_OR: xmlValidGetPotentialChildren(ctree->c1, names, len, max); xmlValidGetPotentialChildren(ctree->c2, names, len, max); break; } return(*len); } /* * Dummy function to suppress messages while we try out valid elements */ static void XMLCDECL xmlNoValidityErr(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED, ...) { return; } /** * xmlValidGetValidElements: * @prev: an element to insert after * @next: an element to insert next * @names: an array to store the list of child names * @max: the size of the array * * This function returns the list of authorized children to insert * within an existing tree while respecting the validity constraints * forced by the Dtd. The insertion point is defined using @prev and * @next in the following ways: * to insert before 'node': xmlValidGetValidElements(node->prev, node, ... * to insert next 'node': xmlValidGetValidElements(node, node->next, ... * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ... * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs, * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ... * * pointers to the element names are inserted at the beginning of the array * and do not need to be freed. * * returns the number of element in the list, or -1 in case of error. If * the function returns the value @max the caller is invited to grow the * receiving array and retry. */ int xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **names, int max) { xmlValidCtxt vctxt; int nb_valid_elements = 0; const xmlChar *elements[256]; int nb_elements = 0, i; const xmlChar *name; xmlNode *ref_node; xmlNode *parent; xmlNode *test_node; xmlNode *prev_next; xmlNode *next_prev; xmlNode *parent_childs; xmlNode *parent_last; xmlElement *element_desc; if (prev == NULL && next == NULL) return(-1); if (names == NULL) return(-1); if (max <= 0) return(-1); memset(&vctxt, 0, sizeof (xmlValidCtxt)); vctxt.error = xmlNoValidityErr; /* this suppresses err/warn output */ nb_valid_elements = 0; ref_node = prev ? prev : next; parent = ref_node->parent; /* * Retrieves the parent element declaration */ element_desc = xmlGetDtdElementDesc(parent->doc->intSubset, parent->name); if ((element_desc == NULL) && (parent->doc->extSubset != NULL)) element_desc = xmlGetDtdElementDesc(parent->doc->extSubset, parent->name); if (element_desc == NULL) return(-1); /* * Do a backup of the current tree structure */ prev_next = prev ? prev->next : NULL; next_prev = next ? next->prev : NULL; parent_childs = parent->children; parent_last = parent->last; /* * Creates a dummy node and insert it into the tree */ test_node = xmlNewDocNode (ref_node->doc, NULL, BAD_CAST "", NULL); test_node->parent = parent; test_node->prev = prev; test_node->next = next; name = test_node->name; if (prev) prev->next = test_node; else parent->children = test_node; if (next) next->prev = test_node; else parent->last = test_node; /* * Insert each potential child node and check if the parent is * still valid */ nb_elements = xmlValidGetPotentialChildren(element_desc->content, elements, &nb_elements, 256); for (i = 0;i < nb_elements;i++) { test_node->name = elements[i]; if (xmlValidateOneElement(&vctxt, parent->doc, parent)) { int j; for (j = 0; j < nb_valid_elements;j++) if (xmlStrEqual(elements[i], names[j])) break; names[nb_valid_elements++] = elements[i]; if (nb_valid_elements >= max) break; } } /* * Restore the tree structure */ if (prev) prev->next = prev_next; if (next) next->prev = next_prev; parent->children = parent_childs; parent->last = parent_last; /* * Free up the dummy node */ test_node->name = name; xmlFreeNode(test_node); return(nb_valid_elements); } #endif /* LIBXML_VALID_ENABLED */ #define bottom_valid #include "elfgcchack.h"
valid.c
Page URL
File URL
Prev
95/114
Next
Download
( 192 KB )
Note: The DriveHQ service banners will NOT be displayed if the file owner is a paid member.
Comments
Total ratings:
0
Average rating:
Not Rated
Would you like to comment?
Join DriveHQ
for a free account, or
Logon
if you are already a member.