xref: /aosp_15_r20/external/cronet/third_party/libxml/src/valid.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 /*
2  * valid.c : part of the code use to do the DTD handling and the validity
3  *           checking
4  *
5  * See Copyright for the status of this software.
6  *
7  * [email protected]
8  */
9 
10 #define IN_LIBXML
11 #include "libxml.h"
12 
13 #include <string.h>
14 #include <stdlib.h>
15 
16 #include <libxml/xmlmemory.h>
17 #include <libxml/hash.h>
18 #include <libxml/uri.h>
19 #include <libxml/valid.h>
20 #include <libxml/parser.h>
21 #include <libxml/parserInternals.h>
22 #include <libxml/xmlerror.h>
23 #include <libxml/list.h>
24 #include <libxml/xmlsave.h>
25 
26 #include "private/error.h"
27 #include "private/parser.h"
28 #include "private/regexp.h"
29 #include "private/save.h"
30 #include "private/tree.h"
31 
32 static xmlElementPtr
33 xmlGetDtdElementDesc2(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name);
34 
35 #ifdef LIBXML_VALID_ENABLED
36 static int
37 xmlValidateAttributeValueInternal(xmlDocPtr doc, xmlAttributeType type,
38                                   const xmlChar *value);
39 #endif
40 /************************************************************************
41  *									*
42  *			Error handling routines				*
43  *									*
44  ************************************************************************/
45 
46 /**
47  * xmlVErrMemory:
48  * @ctxt:  an XML validation parser context
49  * @extra:  extra information
50  *
51  * Handle an out of memory error
52  */
53 static void
xmlVErrMemory(xmlValidCtxtPtr ctxt)54 xmlVErrMemory(xmlValidCtxtPtr ctxt)
55 {
56     if (ctxt != NULL) {
57         if (ctxt->flags & XML_VCTXT_USE_PCTXT) {
58             xmlCtxtErrMemory(ctxt->userData);
59         } else {
60             xmlRaiseMemoryError(NULL, ctxt->error, ctxt->userData,
61                                 XML_FROM_VALID, NULL);
62         }
63     } else {
64         xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_VALID, NULL);
65     }
66 }
67 
68 static void
xmlDoErrValid(xmlValidCtxtPtr ctxt,xmlNodePtr node,xmlParserErrors code,int level,const xmlChar * str1,const xmlChar * str2,const xmlChar * str3,int int1,const char * msg,...)69 xmlDoErrValid(xmlValidCtxtPtr ctxt, xmlNodePtr node,
70               xmlParserErrors code, int level,
71               const xmlChar *str1, const xmlChar *str2, const xmlChar *str3,
72               int int1,
73               const char *msg, ...) {
74     xmlParserCtxtPtr pctxt = NULL;
75     va_list ap;
76 
77     if ((ctxt != NULL) && (ctxt->flags & XML_VCTXT_USE_PCTXT))
78         pctxt = ctxt->userData;
79 
80     va_start(ap, msg);
81     if (pctxt != NULL) {
82         xmlCtxtVErr(pctxt, node, XML_FROM_VALID, code, level,
83                     str1, str2, str3, int1, msg, ap);
84     } else {
85         xmlGenericErrorFunc channel = NULL;
86         void *data = NULL;
87         int res;
88 
89         if (ctxt != NULL) {
90             channel = ctxt->error;
91             data = ctxt->userData;
92         }
93         res = xmlVRaiseError(NULL, channel, data, NULL, node,
94                              XML_FROM_VALID, code, level, NULL, 0,
95                              (const char *) str1, (const char *) str2,
96                              (const char *) str2, int1, 0,
97                              msg, ap);
98         if (res < 0)
99             xmlVErrMemory(ctxt);
100     }
101     va_end(ap);
102 }
103 
104 /**
105  * xmlErrValid:
106  * @ctxt:  an XML validation parser context
107  * @error:  the error number
108  * @extra:  extra information
109  *
110  * Handle a validation error
111  */
112 static void LIBXML_ATTR_FORMAT(3,0)
xmlErrValid(xmlValidCtxtPtr ctxt,xmlParserErrors error,const char * msg,const char * extra)113 xmlErrValid(xmlValidCtxtPtr ctxt, xmlParserErrors error,
114             const char *msg, const char *extra)
115 {
116     xmlDoErrValid(ctxt, NULL, error, XML_ERR_ERROR, (const xmlChar *) extra,
117                   NULL, NULL, 0, msg, extra);
118 }
119 
120 #if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
121 /**
122  * xmlErrValidNode:
123  * @ctxt:  an XML validation parser context
124  * @node:  the node raising the error
125  * @error:  the error number
126  * @str1:  extra information
127  * @str2:  extra information
128  * @str3:  extra information
129  *
130  * Handle a validation error, provide contextual information
131  */
132 static void LIBXML_ATTR_FORMAT(4,0)
xmlErrValidNode(xmlValidCtxtPtr ctxt,xmlNodePtr node,xmlParserErrors error,const char * msg,const xmlChar * str1,const xmlChar * str2,const xmlChar * str3)133 xmlErrValidNode(xmlValidCtxtPtr ctxt,
134                 xmlNodePtr node, xmlParserErrors error,
135                 const char *msg, const xmlChar * str1,
136                 const xmlChar * str2, const xmlChar * str3)
137 {
138     xmlDoErrValid(ctxt, node, error, XML_ERR_ERROR, str1, str2, str3, 0,
139                   msg, str1, str2, str3);
140 }
141 #endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */
142 
143 #ifdef LIBXML_VALID_ENABLED
144 /**
145  * xmlErrValidNodeNr:
146  * @ctxt:  an XML validation parser context
147  * @node:  the node raising the error
148  * @error:  the error number
149  * @str1:  extra information
150  * @int2:  extra information
151  * @str3:  extra information
152  *
153  * Handle a validation error, provide contextual information
154  */
155 static void LIBXML_ATTR_FORMAT(4,0)
xmlErrValidNodeNr(xmlValidCtxtPtr ctxt,xmlNodePtr node,xmlParserErrors error,const char * msg,const xmlChar * str1,int int2,const xmlChar * str3)156 xmlErrValidNodeNr(xmlValidCtxtPtr ctxt,
157                 xmlNodePtr node, xmlParserErrors error,
158                 const char *msg, const xmlChar * str1,
159                 int int2, const xmlChar * str3)
160 {
161     xmlDoErrValid(ctxt, node, error, XML_ERR_ERROR, str1, str3, NULL, int2,
162                   msg, str1, int2, str3);
163 }
164 
165 /**
166  * xmlErrValidWarning:
167  * @ctxt:  an XML validation parser context
168  * @node:  the node raising the error
169  * @error:  the error number
170  * @str1:  extra information
171  * @str2:  extra information
172  * @str3:  extra information
173  *
174  * Handle a validation error, provide contextual information
175  */
176 static void LIBXML_ATTR_FORMAT(4,0)
xmlErrValidWarning(xmlValidCtxtPtr ctxt,xmlNodePtr node,xmlParserErrors error,const char * msg,const xmlChar * str1,const xmlChar * str2,const xmlChar * str3)177 xmlErrValidWarning(xmlValidCtxtPtr ctxt,
178                 xmlNodePtr node, xmlParserErrors error,
179                 const char *msg, const xmlChar * str1,
180                 const xmlChar * str2, const xmlChar * str3)
181 {
182     xmlDoErrValid(ctxt, node, error, XML_ERR_WARNING, str1, str2, str3, 0,
183                   msg, str1, str2, str3);
184 }
185 
186 
187 
188 #ifdef LIBXML_REGEXP_ENABLED
189 /*
190  * If regexp are enabled we can do continuous validation without the
191  * need of a tree to validate the content model. this is done in each
192  * callbacks.
193  * Each xmlValidState represent the validation state associated to the
194  * set of nodes currently open from the document root to the current element.
195  */
196 
197 
198 typedef struct _xmlValidState {
199     xmlElementPtr	 elemDecl;	/* pointer to the content model */
200     xmlNodePtr           node;		/* pointer to the current node */
201     xmlRegExecCtxtPtr    exec;		/* regexp runtime */
202 } _xmlValidState;
203 
204 
205 static int
vstateVPush(xmlValidCtxtPtr ctxt,xmlElementPtr elemDecl,xmlNodePtr node)206 vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
207     if ((ctxt->vstateMax == 0) || (ctxt->vstateTab == NULL)) {
208 	ctxt->vstateMax = 10;
209 	ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
210 		              sizeof(ctxt->vstateTab[0]));
211         if (ctxt->vstateTab == NULL) {
212 	    xmlVErrMemory(ctxt);
213 	    return(-1);
214 	}
215     }
216 
217     if (ctxt->vstateNr >= ctxt->vstateMax) {
218         xmlValidState *tmp;
219 
220 	tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
221 	             2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
222         if (tmp == NULL) {
223 	    xmlVErrMemory(ctxt);
224 	    return(-1);
225 	}
226 	ctxt->vstateMax *= 2;
227 	ctxt->vstateTab = tmp;
228     }
229     ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
230     ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
231     ctxt->vstateTab[ctxt->vstateNr].node = node;
232     if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
233 	if (elemDecl->contModel == NULL)
234 	    xmlValidBuildContentModel(ctxt, elemDecl);
235 	if (elemDecl->contModel != NULL) {
236 	    ctxt->vstateTab[ctxt->vstateNr].exec =
237 		xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
238             if (ctxt->vstateTab[ctxt->vstateNr].exec == NULL) {
239                 xmlVErrMemory(ctxt);
240                 return(-1);
241             }
242 	} else {
243 	    ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
244 	    xmlErrValidNode(ctxt, (xmlNodePtr) elemDecl,
245 	                    XML_ERR_INTERNAL_ERROR,
246 			    "Failed to build content model regexp for %s\n",
247 			    node->name, NULL, NULL);
248 	}
249     }
250     return(ctxt->vstateNr++);
251 }
252 
253 static int
vstateVPop(xmlValidCtxtPtr ctxt)254 vstateVPop(xmlValidCtxtPtr ctxt) {
255     xmlElementPtr elemDecl;
256 
257     if (ctxt->vstateNr < 1) return(-1);
258     ctxt->vstateNr--;
259     elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
260     ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
261     ctxt->vstateTab[ctxt->vstateNr].node = NULL;
262     if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
263 	xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
264     }
265     ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
266     if (ctxt->vstateNr >= 1)
267 	ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
268     else
269 	ctxt->vstate = NULL;
270     return(ctxt->vstateNr);
271 }
272 
273 #else /* not LIBXML_REGEXP_ENABLED */
274 /*
275  * If regexp are not enabled, it uses a home made algorithm less
276  * complex and easier to
277  * debug/maintain than a generic NFA -> DFA state based algo. The
278  * only restriction is on the deepness of the tree limited by the
279  * size of the occurs bitfield
280  *
281  * this is the content of a saved state for rollbacks
282  */
283 
284 #define ROLLBACK_OR	0
285 #define ROLLBACK_PARENT	1
286 
287 typedef struct _xmlValidState {
288     xmlElementContentPtr cont;	/* pointer to the content model subtree */
289     xmlNodePtr           node;	/* pointer to the current node in the list */
290     long                 occurs;/* bitfield for multiple occurrences */
291     unsigned char        depth; /* current depth in the overall tree */
292     unsigned char        state; /* ROLLBACK_XXX */
293 } _xmlValidState;
294 
295 #define MAX_RECURSE 25000
296 #define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
297 #define CONT ctxt->vstate->cont
298 #define NODE ctxt->vstate->node
299 #define DEPTH ctxt->vstate->depth
300 #define OCCURS ctxt->vstate->occurs
301 #define STATE ctxt->vstate->state
302 
303 #define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
304 #define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
305 
306 #define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
307 #define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
308 
309 static int
vstateVPush(xmlValidCtxtPtr ctxt,xmlElementContentPtr cont,xmlNodePtr node,unsigned char depth,long occurs,unsigned char state)310 vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
311 	    xmlNodePtr node, unsigned char depth, long occurs,
312 	    unsigned char state) {
313     int i = ctxt->vstateNr - 1;
314 
315     if (ctxt->vstateNr > MAX_RECURSE) {
316 	return(-1);
317     }
318     if (ctxt->vstateTab == NULL) {
319 	ctxt->vstateMax = 8;
320 	ctxt->vstateTab = (xmlValidState *) xmlMalloc(
321 		     ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
322 	if (ctxt->vstateTab == NULL) {
323 	    xmlVErrMemory(ctxt);
324 	    return(-1);
325 	}
326     }
327     if (ctxt->vstateNr >= ctxt->vstateMax) {
328         xmlValidState *tmp;
329 
330         tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
331 	             2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
332         if (tmp == NULL) {
333 	    xmlVErrMemory(ctxt);
334 	    return(-1);
335 	}
336 	ctxt->vstateMax *= 2;
337 	ctxt->vstateTab = tmp;
338 	ctxt->vstate = &ctxt->vstateTab[0];
339     }
340     /*
341      * Don't push on the stack a state already here
342      */
343     if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
344 	(ctxt->vstateTab[i].node == node) &&
345 	(ctxt->vstateTab[i].depth == depth) &&
346 	(ctxt->vstateTab[i].occurs == occurs) &&
347 	(ctxt->vstateTab[i].state == state))
348 	return(ctxt->vstateNr);
349     ctxt->vstateTab[ctxt->vstateNr].cont = cont;
350     ctxt->vstateTab[ctxt->vstateNr].node = node;
351     ctxt->vstateTab[ctxt->vstateNr].depth = depth;
352     ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
353     ctxt->vstateTab[ctxt->vstateNr].state = state;
354     return(ctxt->vstateNr++);
355 }
356 
357 static int
vstateVPop(xmlValidCtxtPtr ctxt)358 vstateVPop(xmlValidCtxtPtr ctxt) {
359     if (ctxt->vstateNr <= 1) return(-1);
360     ctxt->vstateNr--;
361     ctxt->vstate = &ctxt->vstateTab[0];
362     ctxt->vstate->cont =  ctxt->vstateTab[ctxt->vstateNr].cont;
363     ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
364     ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
365     ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
366     ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
367     return(ctxt->vstateNr);
368 }
369 
370 #endif /* LIBXML_REGEXP_ENABLED */
371 
372 static int
nodeVPush(xmlValidCtxtPtr ctxt,xmlNodePtr value)373 nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
374 {
375     if (ctxt->nodeMax <= 0) {
376         ctxt->nodeMax = 4;
377         ctxt->nodeTab =
378             (xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
379                                      sizeof(ctxt->nodeTab[0]));
380         if (ctxt->nodeTab == NULL) {
381 	    xmlVErrMemory(ctxt);
382             ctxt->nodeMax = 0;
383             return (0);
384         }
385     }
386     if (ctxt->nodeNr >= ctxt->nodeMax) {
387         xmlNodePtr *tmp;
388         tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
389 			      ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0]));
390         if (tmp == NULL) {
391 	    xmlVErrMemory(ctxt);
392             return (0);
393         }
394         ctxt->nodeMax *= 2;
395 	ctxt->nodeTab = tmp;
396     }
397     ctxt->nodeTab[ctxt->nodeNr] = value;
398     ctxt->node = value;
399     return (ctxt->nodeNr++);
400 }
401 static xmlNodePtr
nodeVPop(xmlValidCtxtPtr ctxt)402 nodeVPop(xmlValidCtxtPtr ctxt)
403 {
404     xmlNodePtr ret;
405 
406     if (ctxt->nodeNr <= 0)
407         return (NULL);
408     ctxt->nodeNr--;
409     if (ctxt->nodeNr > 0)
410         ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
411     else
412         ctxt->node = NULL;
413     ret = ctxt->nodeTab[ctxt->nodeNr];
414     ctxt->nodeTab[ctxt->nodeNr] = NULL;
415     return (ret);
416 }
417 
418 /* TODO: use hash table for accesses to elem and attribute definitions */
419 
420 
421 #define CHECK_DTD						\
422    if (doc == NULL) return(0);					\
423    else if ((doc->intSubset == NULL) &&				\
424 	    (doc->extSubset == NULL)) return(0)
425 
426 #ifdef LIBXML_REGEXP_ENABLED
427 
428 /************************************************************************
429  *									*
430  *		Content model validation based on the regexps		*
431  *									*
432  ************************************************************************/
433 
434 /**
435  * xmlValidBuildAContentModel:
436  * @content:  the content model
437  * @ctxt:  the schema parser context
438  * @name:  the element name whose content is being built
439  *
440  * Generate the automata sequence needed for that type
441  *
442  * Returns 1 if successful or 0 in case of error.
443  */
444 static int
xmlValidBuildAContentModel(xmlElementContentPtr content,xmlValidCtxtPtr ctxt,const xmlChar * name)445 xmlValidBuildAContentModel(xmlElementContentPtr content,
446 		           xmlValidCtxtPtr ctxt,
447 		           const xmlChar *name) {
448     if (content == NULL) {
449 	xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
450 			"Found NULL content in content model of %s\n",
451 			name, NULL, NULL);
452 	return(0);
453     }
454     switch (content->type) {
455 	case XML_ELEMENT_CONTENT_PCDATA:
456 	    xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
457 			    "Found PCDATA in content model of %s\n",
458 		            name, NULL, NULL);
459 	    return(0);
460 	    break;
461 	case XML_ELEMENT_CONTENT_ELEMENT: {
462 	    xmlAutomataStatePtr oldstate = ctxt->state;
463 	    xmlChar fn[50];
464 	    xmlChar *fullname;
465 
466 	    fullname = xmlBuildQName(content->name, content->prefix, fn, 50);
467 	    if (fullname == NULL) {
468 	        xmlVErrMemory(ctxt);
469 		return(0);
470 	    }
471 
472 	    switch (content->ocur) {
473 		case XML_ELEMENT_CONTENT_ONCE:
474 		    ctxt->state = xmlAutomataNewTransition(ctxt->am,
475 			    ctxt->state, NULL, fullname, NULL);
476 		    break;
477 		case XML_ELEMENT_CONTENT_OPT:
478 		    ctxt->state = xmlAutomataNewTransition(ctxt->am,
479 			    ctxt->state, NULL, fullname, NULL);
480 		    xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
481 		    break;
482 		case XML_ELEMENT_CONTENT_PLUS:
483 		    ctxt->state = xmlAutomataNewTransition(ctxt->am,
484 			    ctxt->state, NULL, fullname, NULL);
485 		    xmlAutomataNewTransition(ctxt->am, ctxt->state,
486 			                     ctxt->state, fullname, NULL);
487 		    break;
488 		case XML_ELEMENT_CONTENT_MULT:
489 		    ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
490 					    ctxt->state, NULL);
491 		    xmlAutomataNewTransition(ctxt->am,
492 			    ctxt->state, ctxt->state, fullname, NULL);
493 		    break;
494 	    }
495 	    if ((fullname != fn) && (fullname != content->name))
496 		xmlFree(fullname);
497 	    break;
498 	}
499 	case XML_ELEMENT_CONTENT_SEQ: {
500 	    xmlAutomataStatePtr oldstate, oldend;
501 	    xmlElementContentOccur ocur;
502 
503 	    /*
504 	     * Simply iterate over the content
505 	     */
506 	    oldstate = ctxt->state;
507 	    ocur = content->ocur;
508 	    if (ocur != XML_ELEMENT_CONTENT_ONCE) {
509 		ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
510 		oldstate = ctxt->state;
511 	    }
512 	    do {
513 		xmlValidBuildAContentModel(content->c1, ctxt, name);
514 		content = content->c2;
515 	    } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
516 		     (content->ocur == XML_ELEMENT_CONTENT_ONCE));
517 	    xmlValidBuildAContentModel(content, ctxt, name);
518 	    oldend = ctxt->state;
519 	    ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
520 	    switch (ocur) {
521 		case XML_ELEMENT_CONTENT_ONCE:
522 		    break;
523 		case XML_ELEMENT_CONTENT_OPT:
524 		    xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
525 		    break;
526 		case XML_ELEMENT_CONTENT_MULT:
527 		    xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
528 		    xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
529 		    break;
530 		case XML_ELEMENT_CONTENT_PLUS:
531 		    xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
532 		    break;
533 	    }
534 	    break;
535 	}
536 	case XML_ELEMENT_CONTENT_OR: {
537 	    xmlAutomataStatePtr oldstate, oldend;
538 	    xmlElementContentOccur ocur;
539 
540 	    ocur = content->ocur;
541 	    if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
542 		(ocur == XML_ELEMENT_CONTENT_MULT)) {
543 		ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
544 			ctxt->state, NULL);
545 	    }
546 	    oldstate = ctxt->state;
547 	    oldend = xmlAutomataNewState(ctxt->am);
548 
549 	    /*
550 	     * iterate over the subtypes and remerge the end with an
551 	     * epsilon transition
552 	     */
553 	    do {
554 		ctxt->state = oldstate;
555 		xmlValidBuildAContentModel(content->c1, ctxt, name);
556 		xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
557 		content = content->c2;
558 	    } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
559 		     (content->ocur == XML_ELEMENT_CONTENT_ONCE));
560 	    ctxt->state = oldstate;
561 	    xmlValidBuildAContentModel(content, ctxt, name);
562 	    xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
563 	    ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
564 	    switch (ocur) {
565 		case XML_ELEMENT_CONTENT_ONCE:
566 		    break;
567 		case XML_ELEMENT_CONTENT_OPT:
568 		    xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
569 		    break;
570 		case XML_ELEMENT_CONTENT_MULT:
571 		    xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
572 		    xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
573 		    break;
574 		case XML_ELEMENT_CONTENT_PLUS:
575 		    xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
576 		    break;
577 	    }
578 	    break;
579 	}
580 	default:
581 	    xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
582 	                "ContentModel broken for element %s\n",
583 			(const char *) name);
584 	    return(0);
585     }
586     return(1);
587 }
588 /**
589  * xmlValidBuildContentModel:
590  * @ctxt:  a validation context
591  * @elem:  an element declaration node
592  *
593  * (Re)Build the automata associated to the content model of this
594  * element
595  *
596  * Returns 1 in case of success, 0 in case of error
597  */
598 int
xmlValidBuildContentModel(xmlValidCtxtPtr ctxt,xmlElementPtr elem)599 xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
600     int ret = 0;
601 
602     if ((ctxt == NULL) || (elem == NULL))
603 	return(0);
604     if (elem->type != XML_ELEMENT_DECL)
605 	return(0);
606     if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
607 	return(1);
608     /* TODO: should we rebuild in this case ? */
609     if (elem->contModel != NULL) {
610 	if (!xmlRegexpIsDeterminist(elem->contModel)) {
611 	    ctxt->valid = 0;
612 	    return(0);
613 	}
614 	return(1);
615     }
616 
617     ctxt->am = xmlNewAutomata();
618     if (ctxt->am == NULL) {
619         xmlVErrMemory(ctxt);
620 	return(0);
621     }
622     ctxt->state = xmlAutomataGetInitState(ctxt->am);
623     xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
624     xmlAutomataSetFinalState(ctxt->am, ctxt->state);
625     elem->contModel = xmlAutomataCompile(ctxt->am);
626     if (elem->contModel == NULL) {
627         xmlVErrMemory(ctxt);
628         goto done;
629     }
630     if (xmlRegexpIsDeterminist(elem->contModel) != 1) {
631 	char expr[5000];
632 	expr[0] = 0;
633 	xmlSnprintfElementContent(expr, 5000, elem->content, 1);
634 	xmlErrValidNode(ctxt, (xmlNodePtr) elem,
635 	                XML_DTD_CONTENT_NOT_DETERMINIST,
636 	       "Content model of %s is not deterministic: %s\n",
637 	       elem->name, BAD_CAST expr, NULL);
638         ctxt->valid = 0;
639 	goto done;
640     }
641 
642     ret = 1;
643 
644 done:
645     ctxt->state = NULL;
646     xmlFreeAutomata(ctxt->am);
647     ctxt->am = NULL;
648     return(ret);
649 }
650 
651 #endif /* LIBXML_REGEXP_ENABLED */
652 
653 /****************************************************************
654  *								*
655  *	Util functions for data allocation/deallocation		*
656  *								*
657  ****************************************************************/
658 
659 /**
660  * xmlNewValidCtxt:
661  *
662  * Allocate a validation context structure.
663  *
664  * Returns NULL if not, otherwise the new validation context structure
665  */
xmlNewValidCtxt(void)666 xmlValidCtxtPtr xmlNewValidCtxt(void) {
667     xmlValidCtxtPtr ret;
668 
669     if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL)
670 	return (NULL);
671 
672     (void) memset(ret, 0, sizeof (xmlValidCtxt));
673 
674     return (ret);
675 }
676 
677 /**
678  * xmlFreeValidCtxt:
679  * @cur:  the validation context to free
680  *
681  * Free a validation context structure.
682  */
683 void
xmlFreeValidCtxt(xmlValidCtxtPtr cur)684 xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
685     if (cur == NULL)
686         return;
687     if (cur->vstateTab != NULL)
688         xmlFree(cur->vstateTab);
689     if (cur->nodeTab != NULL)
690         xmlFree(cur->nodeTab);
691     xmlFree(cur);
692 }
693 
694 #endif /* LIBXML_VALID_ENABLED */
695 
696 /**
697  * xmlNewDocElementContent:
698  * @doc:  the document
699  * @name:  the subelement name or NULL
700  * @type:  the type of element content decl
701  *
702  * Allocate an element content structure for the document.
703  *
704  * Returns NULL if not, otherwise the new element content structure
705  */
706 xmlElementContentPtr
xmlNewDocElementContent(xmlDocPtr doc,const xmlChar * name,xmlElementContentType type)707 xmlNewDocElementContent(xmlDocPtr doc, const xmlChar *name,
708                         xmlElementContentType type) {
709     xmlElementContentPtr ret;
710     xmlDictPtr dict = NULL;
711 
712     if (doc != NULL)
713         dict = doc->dict;
714 
715     switch(type) {
716 	case XML_ELEMENT_CONTENT_ELEMENT:
717 	    if (name == NULL) {
718 	        xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
719 			"xmlNewElementContent : name == NULL !\n",
720 			NULL);
721 	    }
722 	    break;
723         case XML_ELEMENT_CONTENT_PCDATA:
724 	case XML_ELEMENT_CONTENT_SEQ:
725 	case XML_ELEMENT_CONTENT_OR:
726 	    if (name != NULL) {
727 	        xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
728 			"xmlNewElementContent : name != NULL !\n",
729 			NULL);
730 	    }
731 	    break;
732 	default:
733 	    xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
734 		    "Internal: ELEMENT content corrupted invalid type\n",
735 		    NULL);
736 	    return(NULL);
737     }
738     ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
739     if (ret == NULL)
740 	return(NULL);
741     memset(ret, 0, sizeof(xmlElementContent));
742     ret->type = type;
743     ret->ocur = XML_ELEMENT_CONTENT_ONCE;
744     if (name != NULL) {
745         int l;
746 	const xmlChar *tmp;
747 
748 	tmp = xmlSplitQName3(name, &l);
749 	if (tmp == NULL) {
750 	    if (dict == NULL)
751 		ret->name = xmlStrdup(name);
752 	    else
753 	        ret->name = xmlDictLookup(dict, name, -1);
754 	} else {
755 	    if (dict == NULL) {
756 		ret->prefix = xmlStrndup(name, l);
757 		ret->name = xmlStrdup(tmp);
758 	    } else {
759 	        ret->prefix = xmlDictLookup(dict, name, l);
760 		ret->name = xmlDictLookup(dict, tmp, -1);
761 	    }
762             if (ret->prefix == NULL)
763                 goto error;
764 	}
765         if (ret->name == NULL)
766             goto error;
767     }
768     return(ret);
769 
770 error:
771     xmlFreeDocElementContent(doc, ret);
772     return(NULL);
773 }
774 
775 /**
776  * xmlNewElementContent:
777  * @name:  the subelement name or NULL
778  * @type:  the type of element content decl
779  *
780  * Allocate an element content structure.
781  * Deprecated in favor of xmlNewDocElementContent
782  *
783  * Returns NULL if not, otherwise the new element content structure
784  */
785 xmlElementContentPtr
xmlNewElementContent(const xmlChar * name,xmlElementContentType type)786 xmlNewElementContent(const xmlChar *name, xmlElementContentType type) {
787     return(xmlNewDocElementContent(NULL, name, type));
788 }
789 
790 /**
791  * xmlCopyDocElementContent:
792  * @doc:  the document owning the element declaration
793  * @cur:  An element content pointer.
794  *
795  * Build a copy of an element content description.
796  *
797  * Returns the new xmlElementContentPtr or NULL in case of error.
798  */
799 xmlElementContentPtr
xmlCopyDocElementContent(xmlDocPtr doc,xmlElementContentPtr cur)800 xmlCopyDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) {
801     xmlElementContentPtr ret = NULL, prev = NULL, tmp;
802     xmlDictPtr dict = NULL;
803 
804     if (cur == NULL) return(NULL);
805 
806     if (doc != NULL)
807         dict = doc->dict;
808 
809     ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
810     if (ret == NULL)
811 	return(NULL);
812     memset(ret, 0, sizeof(xmlElementContent));
813     ret->type = cur->type;
814     ret->ocur = cur->ocur;
815     if (cur->name != NULL) {
816 	if (dict)
817 	    ret->name = xmlDictLookup(dict, cur->name, -1);
818 	else
819 	    ret->name = xmlStrdup(cur->name);
820         if (ret->name == NULL)
821             goto error;
822     }
823 
824     if (cur->prefix != NULL) {
825 	if (dict)
826 	    ret->prefix = xmlDictLookup(dict, cur->prefix, -1);
827 	else
828 	    ret->prefix = xmlStrdup(cur->prefix);
829         if (ret->prefix == NULL)
830             goto error;
831     }
832     if (cur->c1 != NULL) {
833         ret->c1 = xmlCopyDocElementContent(doc, cur->c1);
834         if (ret->c1 == NULL)
835             goto error;
836 	ret->c1->parent = ret;
837     }
838     if (cur->c2 != NULL) {
839         prev = ret;
840 	cur = cur->c2;
841 	while (cur != NULL) {
842 	    tmp = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
843 	    if (tmp == NULL)
844                 goto error;
845 	    memset(tmp, 0, sizeof(xmlElementContent));
846 	    tmp->type = cur->type;
847 	    tmp->ocur = cur->ocur;
848 	    prev->c2 = tmp;
849 	    tmp->parent = prev;
850 	    if (cur->name != NULL) {
851 		if (dict)
852 		    tmp->name = xmlDictLookup(dict, cur->name, -1);
853 		else
854 		    tmp->name = xmlStrdup(cur->name);
855                 if (tmp->name == NULL)
856                     goto error;
857 	    }
858 
859 	    if (cur->prefix != NULL) {
860 		if (dict)
861 		    tmp->prefix = xmlDictLookup(dict, cur->prefix, -1);
862 		else
863 		    tmp->prefix = xmlStrdup(cur->prefix);
864                 if (tmp->prefix == NULL)
865                     goto error;
866 	    }
867 	    if (cur->c1 != NULL) {
868 	        tmp->c1 = xmlCopyDocElementContent(doc,cur->c1);
869 	        if (tmp->c1 == NULL)
870                     goto error;
871 		tmp->c1->parent = tmp;
872             }
873 	    prev = tmp;
874 	    cur = cur->c2;
875 	}
876     }
877     return(ret);
878 
879 error:
880     xmlFreeElementContent(ret);
881     return(NULL);
882 }
883 
884 /**
885  * xmlCopyElementContent:
886  * @cur:  An element content pointer.
887  *
888  * Build a copy of an element content description.
889  * Deprecated, use xmlCopyDocElementContent instead
890  *
891  * Returns the new xmlElementContentPtr or NULL in case of error.
892  */
893 xmlElementContentPtr
xmlCopyElementContent(xmlElementContentPtr cur)894 xmlCopyElementContent(xmlElementContentPtr cur) {
895     return(xmlCopyDocElementContent(NULL, cur));
896 }
897 
898 /**
899  * xmlFreeDocElementContent:
900  * @doc: the document owning the element declaration
901  * @cur:  the element content tree to free
902  *
903  * Free an element content structure. The whole subtree is removed.
904  */
905 void
xmlFreeDocElementContent(xmlDocPtr doc,xmlElementContentPtr cur)906 xmlFreeDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) {
907     xmlDictPtr dict = NULL;
908     size_t depth = 0;
909 
910     if (cur == NULL)
911         return;
912     if (doc != NULL)
913         dict = doc->dict;
914 
915     while (1) {
916         xmlElementContentPtr parent;
917 
918         while ((cur->c1 != NULL) || (cur->c2 != NULL)) {
919             cur = (cur->c1 != NULL) ? cur->c1 : cur->c2;
920             depth += 1;
921         }
922 
923 	switch (cur->type) {
924 	    case XML_ELEMENT_CONTENT_PCDATA:
925 	    case XML_ELEMENT_CONTENT_ELEMENT:
926 	    case XML_ELEMENT_CONTENT_SEQ:
927 	    case XML_ELEMENT_CONTENT_OR:
928 		break;
929 	    default:
930 		xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
931 			"Internal: ELEMENT content corrupted invalid type\n",
932 			NULL);
933 		return;
934 	}
935 	if (dict) {
936 	    if ((cur->name != NULL) && (!xmlDictOwns(dict, cur->name)))
937 	        xmlFree((xmlChar *) cur->name);
938 	    if ((cur->prefix != NULL) && (!xmlDictOwns(dict, cur->prefix)))
939 	        xmlFree((xmlChar *) cur->prefix);
940 	} else {
941 	    if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
942 	    if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
943 	}
944         parent = cur->parent;
945         if ((depth == 0) || (parent == NULL)) {
946             xmlFree(cur);
947             break;
948         }
949         if (cur == parent->c1)
950             parent->c1 = NULL;
951         else
952             parent->c2 = NULL;
953 	xmlFree(cur);
954 
955         if (parent->c2 != NULL) {
956 	    cur = parent->c2;
957         } else {
958             depth -= 1;
959             cur = parent;
960         }
961     }
962 }
963 
964 /**
965  * xmlFreeElementContent:
966  * @cur:  the element content tree to free
967  *
968  * Free an element content structure. The whole subtree is removed.
969  * Deprecated, use xmlFreeDocElementContent instead
970  */
971 void
xmlFreeElementContent(xmlElementContentPtr cur)972 xmlFreeElementContent(xmlElementContentPtr cur) {
973     xmlFreeDocElementContent(NULL, cur);
974 }
975 
976 #ifdef LIBXML_OUTPUT_ENABLED
977 /**
978  * xmlSprintfElementContent:
979  * @buf:  an output buffer
980  * @content:  An element table
981  * @englob: 1 if one must print the englobing parenthesis, 0 otherwise
982  *
983  * Deprecated, unsafe, use xmlSnprintfElementContent
984  */
985 void
xmlSprintfElementContent(char * buf ATTRIBUTE_UNUSED,xmlElementContentPtr content ATTRIBUTE_UNUSED,int englob ATTRIBUTE_UNUSED)986 xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
987 	                 xmlElementContentPtr content ATTRIBUTE_UNUSED,
988 			 int englob ATTRIBUTE_UNUSED) {
989 }
990 #endif /* LIBXML_OUTPUT_ENABLED */
991 
992 /**
993  * xmlSnprintfElementContent:
994  * @buf:  an output buffer
995  * @size:  the buffer size
996  * @content:  An element table
997  * @englob: 1 if one must print the englobing parenthesis, 0 otherwise
998  *
999  * This will dump the content of the element content definition
1000  * Intended just for the debug routine
1001  */
1002 void
xmlSnprintfElementContent(char * buf,int size,xmlElementContentPtr content,int englob)1003 xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int englob) {
1004     int len;
1005 
1006     if (content == NULL) return;
1007     len = strlen(buf);
1008     if (size - len < 50) {
1009 	if ((size - len > 4) && (buf[len - 1] != '.'))
1010 	    strcat(buf, " ...");
1011 	return;
1012     }
1013     if (englob) strcat(buf, "(");
1014     switch (content->type) {
1015         case XML_ELEMENT_CONTENT_PCDATA:
1016             strcat(buf, "#PCDATA");
1017 	    break;
1018 	case XML_ELEMENT_CONTENT_ELEMENT: {
1019             int qnameLen = xmlStrlen(content->name);
1020 
1021 	    if (content->prefix != NULL)
1022                 qnameLen += xmlStrlen(content->prefix) + 1;
1023 	    if (size - len < qnameLen + 10) {
1024 		strcat(buf, " ...");
1025 		return;
1026 	    }
1027 	    if (content->prefix != NULL) {
1028 		strcat(buf, (char *) content->prefix);
1029 		strcat(buf, ":");
1030 	    }
1031 	    if (content->name != NULL)
1032 		strcat(buf, (char *) content->name);
1033 	    break;
1034         }
1035 	case XML_ELEMENT_CONTENT_SEQ:
1036 	    if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1037 	        (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
1038 		xmlSnprintfElementContent(buf, size, content->c1, 1);
1039 	    else
1040 		xmlSnprintfElementContent(buf, size, content->c1, 0);
1041 	    len = strlen(buf);
1042 	    if (size - len < 50) {
1043 		if ((size - len > 4) && (buf[len - 1] != '.'))
1044 		    strcat(buf, " ...");
1045 		return;
1046 	    }
1047             strcat(buf, " , ");
1048 	    if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
1049 		 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1050 		(content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
1051 		xmlSnprintfElementContent(buf, size, content->c2, 1);
1052 	    else
1053 		xmlSnprintfElementContent(buf, size, content->c2, 0);
1054 	    break;
1055 	case XML_ELEMENT_CONTENT_OR:
1056 	    if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1057 	        (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
1058 		xmlSnprintfElementContent(buf, size, content->c1, 1);
1059 	    else
1060 		xmlSnprintfElementContent(buf, size, content->c1, 0);
1061 	    len = strlen(buf);
1062 	    if (size - len < 50) {
1063 		if ((size - len > 4) && (buf[len - 1] != '.'))
1064 		    strcat(buf, " ...");
1065 		return;
1066 	    }
1067             strcat(buf, " | ");
1068 	    if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
1069 		 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1070 		(content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
1071 		xmlSnprintfElementContent(buf, size, content->c2, 1);
1072 	    else
1073 		xmlSnprintfElementContent(buf, size, content->c2, 0);
1074 	    break;
1075     }
1076     if (size - strlen(buf) <= 2) return;
1077     if (englob)
1078         strcat(buf, ")");
1079     switch (content->ocur) {
1080         case XML_ELEMENT_CONTENT_ONCE:
1081 	    break;
1082         case XML_ELEMENT_CONTENT_OPT:
1083 	    strcat(buf, "?");
1084 	    break;
1085         case XML_ELEMENT_CONTENT_MULT:
1086 	    strcat(buf, "*");
1087 	    break;
1088         case XML_ELEMENT_CONTENT_PLUS:
1089 	    strcat(buf, "+");
1090 	    break;
1091     }
1092 }
1093 
1094 /****************************************************************
1095  *								*
1096  *	Registration of DTD declarations			*
1097  *								*
1098  ****************************************************************/
1099 
1100 /**
1101  * xmlFreeElement:
1102  * @elem:  An element
1103  *
1104  * Deallocate the memory used by an element definition
1105  */
1106 static void
xmlFreeElement(xmlElementPtr elem)1107 xmlFreeElement(xmlElementPtr elem) {
1108     if (elem == NULL) return;
1109     xmlUnlinkNode((xmlNodePtr) elem);
1110     xmlFreeDocElementContent(elem->doc, elem->content);
1111     if (elem->name != NULL)
1112 	xmlFree((xmlChar *) elem->name);
1113     if (elem->prefix != NULL)
1114 	xmlFree((xmlChar *) elem->prefix);
1115 #ifdef LIBXML_REGEXP_ENABLED
1116     if (elem->contModel != NULL)
1117 	xmlRegFreeRegexp(elem->contModel);
1118 #endif
1119     xmlFree(elem);
1120 }
1121 
1122 
1123 /**
1124  * xmlAddElementDecl:
1125  * @ctxt:  the validation context
1126  * @dtd:  pointer to the DTD
1127  * @name:  the entity name
1128  * @type:  the element type
1129  * @content:  the element content tree or NULL
1130  *
1131  * Register a new element declaration
1132  *
1133  * Returns NULL if not, otherwise the entity
1134  */
1135 xmlElementPtr
xmlAddElementDecl(xmlValidCtxtPtr ctxt,xmlDtdPtr dtd,const xmlChar * name,xmlElementTypeVal type,xmlElementContentPtr content)1136 xmlAddElementDecl(xmlValidCtxtPtr ctxt,
1137                   xmlDtdPtr dtd, const xmlChar *name,
1138                   xmlElementTypeVal type,
1139 		  xmlElementContentPtr content) {
1140     xmlElementPtr ret;
1141     xmlElementTablePtr table;
1142     xmlAttributePtr oldAttributes = NULL;
1143     const xmlChar *localName;
1144     xmlChar *prefix = NULL;
1145 
1146     if (dtd == NULL) {
1147 	return(NULL);
1148     }
1149     if (name == NULL) {
1150 	return(NULL);
1151     }
1152 
1153     switch (type) {
1154         case XML_ELEMENT_TYPE_EMPTY:
1155 	    if (content != NULL) {
1156 		xmlErrValid(ctxt, XML_DTD_CONTENT_ERROR,
1157 		        "xmlAddElementDecl: content != NULL for EMPTY\n",
1158 			NULL);
1159 		return(NULL);
1160 	    }
1161 	    break;
1162 	case XML_ELEMENT_TYPE_ANY:
1163 	    if (content != NULL) {
1164 		xmlErrValid(ctxt, XML_DTD_CONTENT_ERROR,
1165 		        "xmlAddElementDecl: content != NULL for ANY\n",
1166 			NULL);
1167 		return(NULL);
1168 	    }
1169 	    break;
1170 	case XML_ELEMENT_TYPE_MIXED:
1171 	    if (content == NULL) {
1172 		xmlErrValid(ctxt, XML_DTD_CONTENT_ERROR,
1173 		        "xmlAddElementDecl: content == NULL for MIXED\n",
1174 			NULL);
1175 		return(NULL);
1176 	    }
1177 	    break;
1178 	case XML_ELEMENT_TYPE_ELEMENT:
1179 	    if (content == NULL) {
1180 		xmlErrValid(ctxt, XML_DTD_CONTENT_ERROR,
1181 		        "xmlAddElementDecl: content == NULL for ELEMENT\n",
1182 			NULL);
1183 		return(NULL);
1184 	    }
1185 	    break;
1186 	default:
1187 	    xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1188 		    "Internal: ELEMENT decl corrupted invalid type\n",
1189 		    NULL);
1190 	    return(NULL);
1191     }
1192 
1193     /*
1194      * check if name is a QName
1195      */
1196     localName = xmlSplitQName4(name, &prefix);
1197     if (localName == NULL)
1198         goto mem_error;
1199 
1200     /*
1201      * Create the Element table if needed.
1202      */
1203     table = (xmlElementTablePtr) dtd->elements;
1204     if (table == NULL) {
1205 	xmlDictPtr dict = NULL;
1206 
1207 	if (dtd->doc != NULL)
1208 	    dict = dtd->doc->dict;
1209         table = xmlHashCreateDict(0, dict);
1210         if (table == NULL)
1211             goto mem_error;
1212 	dtd->elements = (void *) table;
1213     }
1214 
1215     /*
1216      * lookup old attributes inserted on an undefined element in the
1217      * internal subset.
1218      */
1219     if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1220 	ret = xmlHashLookup2(dtd->doc->intSubset->elements, localName, prefix);
1221 	if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1222 	    oldAttributes = ret->attributes;
1223 	    ret->attributes = NULL;
1224 	    xmlHashRemoveEntry2(dtd->doc->intSubset->elements, localName, prefix,
1225                                 NULL);
1226 	    xmlFreeElement(ret);
1227 	}
1228     }
1229 
1230     /*
1231      * The element may already be present if one of its attribute
1232      * was registered first
1233      */
1234     ret = xmlHashLookup2(table, localName, prefix);
1235     if (ret != NULL) {
1236 	if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1237 #ifdef LIBXML_VALID_ENABLED
1238 	    /*
1239 	     * The element is already defined in this DTD.
1240 	     */
1241 	    xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED,
1242 	                    "Redefinition of element %s\n",
1243 			    name, NULL, NULL);
1244 #endif /* LIBXML_VALID_ENABLED */
1245             if (prefix != NULL)
1246 	        xmlFree(prefix);
1247 	    return(NULL);
1248 	}
1249 	if (prefix != NULL) {
1250 	    xmlFree(prefix);
1251 	    prefix = NULL;
1252 	}
1253     } else {
1254         int res;
1255 
1256 	ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1257 	if (ret == NULL)
1258             goto mem_error;
1259 	memset(ret, 0, sizeof(xmlElement));
1260 	ret->type = XML_ELEMENT_DECL;
1261 
1262 	/*
1263 	 * fill the structure.
1264 	 */
1265 	ret->name = xmlStrdup(localName);
1266 	if (ret->name == NULL) {
1267 	    xmlFree(ret);
1268 	    goto mem_error;
1269 	}
1270 	ret->prefix = prefix;
1271         prefix = NULL;
1272 
1273 	/*
1274 	 * Validity Check:
1275 	 * Insertion must not fail
1276 	 */
1277         res = xmlHashAdd2(table, localName, ret->prefix, ret);
1278         if (res <= 0) {
1279 	    xmlFreeElement(ret);
1280             goto mem_error;
1281 	}
1282 	/*
1283 	 * For new element, may have attributes from earlier
1284 	 * definition in internal subset
1285 	 */
1286 	ret->attributes = oldAttributes;
1287     }
1288 
1289     /*
1290      * Finish to fill the structure.
1291      */
1292     ret->etype = type;
1293     /*
1294      * Avoid a stupid copy when called by the parser
1295      * and flag it by setting a special parent value
1296      * so the parser doesn't unallocate it.
1297      */
1298     if (content != NULL) {
1299         if ((ctxt != NULL) && (ctxt->flags & XML_VCTXT_USE_PCTXT)) {
1300             ret->content = content;
1301             content->parent = (xmlElementContentPtr) 1;
1302         } else if (content != NULL){
1303             ret->content = xmlCopyDocElementContent(dtd->doc, content);
1304             if (ret->content == NULL)
1305                 goto mem_error;
1306         }
1307     }
1308 
1309     /*
1310      * Link it to the DTD
1311      */
1312     ret->parent = dtd;
1313     ret->doc = dtd->doc;
1314     if (dtd->last == NULL) {
1315 	dtd->children = dtd->last = (xmlNodePtr) ret;
1316     } else {
1317         dtd->last->next = (xmlNodePtr) ret;
1318 	ret->prev = dtd->last;
1319 	dtd->last = (xmlNodePtr) ret;
1320     }
1321     if (prefix != NULL)
1322 	xmlFree(prefix);
1323     return(ret);
1324 
1325 mem_error:
1326     xmlVErrMemory(ctxt);
1327     if (prefix != NULL)
1328         xmlFree(prefix);
1329     return(NULL);
1330 }
1331 
1332 static void
xmlFreeElementTableEntry(void * elem,const xmlChar * name ATTRIBUTE_UNUSED)1333 xmlFreeElementTableEntry(void *elem, const xmlChar *name ATTRIBUTE_UNUSED) {
1334     xmlFreeElement((xmlElementPtr) elem);
1335 }
1336 
1337 /**
1338  * xmlFreeElementTable:
1339  * @table:  An element table
1340  *
1341  * Deallocate the memory used by an element hash table.
1342  */
1343 void
xmlFreeElementTable(xmlElementTablePtr table)1344 xmlFreeElementTable(xmlElementTablePtr table) {
1345     xmlHashFree(table, xmlFreeElementTableEntry);
1346 }
1347 
1348 #ifdef LIBXML_TREE_ENABLED
1349 /**
1350  * xmlCopyElement:
1351  * @elem:  An element
1352  *
1353  * Build a copy of an element.
1354  *
1355  * Returns the new xmlElementPtr or NULL in case of error.
1356  */
1357 static void *
xmlCopyElement(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)1358 xmlCopyElement(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
1359     xmlElementPtr elem = (xmlElementPtr) payload;
1360     xmlElementPtr cur;
1361 
1362     cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1363     if (cur == NULL)
1364 	return(NULL);
1365     memset(cur, 0, sizeof(xmlElement));
1366     cur->type = XML_ELEMENT_DECL;
1367     cur->etype = elem->etype;
1368     if (elem->name != NULL) {
1369 	cur->name = xmlStrdup(elem->name);
1370         if (cur->name == NULL)
1371             goto error;
1372     }
1373     if (elem->prefix != NULL) {
1374 	cur->prefix = xmlStrdup(elem->prefix);
1375         if (cur->prefix == NULL)
1376             goto error;
1377     }
1378     if (elem->content != NULL) {
1379         cur->content = xmlCopyElementContent(elem->content);
1380         if (cur->content == NULL)
1381             goto error;
1382     }
1383     /* TODO : rebuild the attribute list on the copy */
1384     cur->attributes = NULL;
1385     return(cur);
1386 
1387 error:
1388     xmlFreeElement(cur);
1389     return(NULL);
1390 }
1391 
1392 /**
1393  * xmlCopyElementTable:
1394  * @table:  An element table
1395  *
1396  * Build a copy of an element table.
1397  *
1398  * Returns the new xmlElementTablePtr or NULL in case of error.
1399  */
1400 xmlElementTablePtr
xmlCopyElementTable(xmlElementTablePtr table)1401 xmlCopyElementTable(xmlElementTablePtr table) {
1402     return(xmlHashCopySafe(table, xmlCopyElement, xmlFreeElementTableEntry));
1403 }
1404 #endif /* LIBXML_TREE_ENABLED */
1405 
1406 #ifdef LIBXML_OUTPUT_ENABLED
1407 /**
1408  * xmlDumpElementDecl:
1409  * @buf:  the XML buffer output
1410  * @elem:  An element table
1411  *
1412  * DEPRECATED: Use xmlSaveTree.
1413  *
1414  * This will dump the content of the element declaration as an XML
1415  * DTD definition
1416  */
1417 void
xmlDumpElementDecl(xmlBufferPtr buf,xmlElementPtr elem)1418 xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1419     xmlSaveCtxtPtr save;
1420 
1421     if ((buf == NULL) || (elem == NULL))
1422         return;
1423 
1424     save = xmlSaveToBuffer(buf, NULL, 0);
1425     xmlSaveTree(save, (xmlNodePtr) elem);
1426     xmlSaveClose(save);
1427 }
1428 
1429 /**
1430  * xmlDumpElementDeclScan:
1431  * @elem:  An element table
1432  * @buf:  the XML buffer output
1433  *
1434  * This routine is used by the hash scan function.  It just reverses
1435  * the arguments.
1436  */
1437 static void
xmlDumpElementDeclScan(void * elem,void * buf,const xmlChar * name ATTRIBUTE_UNUSED)1438 xmlDumpElementDeclScan(void *elem, void *buf,
1439                        const xmlChar *name ATTRIBUTE_UNUSED) {
1440     xmlDumpElementDecl((xmlBufferPtr) buf, (xmlElementPtr) elem);
1441 }
1442 
1443 /**
1444  * xmlDumpElementTable:
1445  * @buf:  the XML buffer output
1446  * @table:  An element table
1447  *
1448  * DEPRECATED: Don't use.
1449  *
1450  * This will dump the content of the element table as an XML DTD definition
1451  */
1452 void
xmlDumpElementTable(xmlBufferPtr buf,xmlElementTablePtr table)1453 xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1454     if ((buf == NULL) || (table == NULL))
1455         return;
1456     xmlHashScan(table, xmlDumpElementDeclScan, buf);
1457 }
1458 #endif /* LIBXML_OUTPUT_ENABLED */
1459 
1460 /**
1461  * xmlCreateEnumeration:
1462  * @name:  the enumeration name or NULL
1463  *
1464  * create and initialize an enumeration attribute node.
1465  *
1466  * Returns the xmlEnumerationPtr just created or NULL in case
1467  *                of error.
1468  */
1469 xmlEnumerationPtr
xmlCreateEnumeration(const xmlChar * name)1470 xmlCreateEnumeration(const xmlChar *name) {
1471     xmlEnumerationPtr ret;
1472 
1473     ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1474     if (ret == NULL)
1475         return(NULL);
1476     memset(ret, 0, sizeof(xmlEnumeration));
1477 
1478     if (name != NULL) {
1479         ret->name = xmlStrdup(name);
1480         if (ret->name == NULL) {
1481             xmlFree(ret);
1482             return(NULL);
1483         }
1484     }
1485 
1486     return(ret);
1487 }
1488 
1489 /**
1490  * xmlFreeEnumeration:
1491  * @cur:  the tree to free.
1492  *
1493  * free an enumeration attribute node (recursive).
1494  */
1495 void
xmlFreeEnumeration(xmlEnumerationPtr cur)1496 xmlFreeEnumeration(xmlEnumerationPtr cur) {
1497     while (cur != NULL) {
1498         xmlEnumerationPtr next = cur->next;
1499 
1500         xmlFree((xmlChar *) cur->name);
1501         xmlFree(cur);
1502 
1503         cur = next;
1504     }
1505 }
1506 
1507 #ifdef LIBXML_TREE_ENABLED
1508 /**
1509  * xmlCopyEnumeration:
1510  * @cur:  the tree to copy.
1511  *
1512  * Copy an enumeration attribute node (recursive).
1513  *
1514  * Returns the xmlEnumerationPtr just created or NULL in case
1515  *                of error.
1516  */
1517 xmlEnumerationPtr
xmlCopyEnumeration(xmlEnumerationPtr cur)1518 xmlCopyEnumeration(xmlEnumerationPtr cur) {
1519     xmlEnumerationPtr ret = NULL;
1520     xmlEnumerationPtr last = NULL;
1521 
1522     while (cur != NULL) {
1523         xmlEnumerationPtr copy = xmlCreateEnumeration(cur->name);
1524 
1525         if (copy == NULL) {
1526             xmlFreeEnumeration(ret);
1527             return(NULL);
1528         }
1529 
1530         if (ret == NULL) {
1531             ret = last = copy;
1532         } else {
1533             last->next = copy;
1534             last = copy;
1535         }
1536 
1537         cur = cur->next;
1538     }
1539 
1540     return(ret);
1541 }
1542 #endif /* LIBXML_TREE_ENABLED */
1543 
1544 #ifdef LIBXML_VALID_ENABLED
1545 /**
1546  * xmlScanIDAttributeDecl:
1547  * @ctxt:  the validation context
1548  * @elem:  the element name
1549  * @err: whether to raise errors here
1550  *
1551  * Verify that the element don't have too many ID attributes
1552  * declared.
1553  *
1554  * Returns the number of ID attributes found.
1555  */
1556 static int
xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt,xmlElementPtr elem,int err)1557 xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem, int err) {
1558     xmlAttributePtr cur;
1559     int ret = 0;
1560 
1561     if (elem == NULL) return(0);
1562     cur = elem->attributes;
1563     while (cur != NULL) {
1564         if (cur->atype == XML_ATTRIBUTE_ID) {
1565 	    ret ++;
1566 	    if ((ret > 1) && (err))
1567 		xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_MULTIPLE_ID,
1568 	       "Element %s has too many ID attributes defined : %s\n",
1569 		       elem->name, cur->name, NULL);
1570 	}
1571 	cur = cur->nexth;
1572     }
1573     return(ret);
1574 }
1575 #endif /* LIBXML_VALID_ENABLED */
1576 
1577 /**
1578  * xmlFreeAttribute:
1579  * @elem:  An attribute
1580  *
1581  * Deallocate the memory used by an attribute definition
1582  */
1583 static void
xmlFreeAttribute(xmlAttributePtr attr)1584 xmlFreeAttribute(xmlAttributePtr attr) {
1585     xmlDictPtr dict;
1586 
1587     if (attr == NULL) return;
1588     if (attr->doc != NULL)
1589 	dict = attr->doc->dict;
1590     else
1591 	dict = NULL;
1592     xmlUnlinkNode((xmlNodePtr) attr);
1593     if (attr->tree != NULL)
1594         xmlFreeEnumeration(attr->tree);
1595     if (dict) {
1596         if ((attr->elem != NULL) && (!xmlDictOwns(dict, attr->elem)))
1597 	    xmlFree((xmlChar *) attr->elem);
1598         if ((attr->name != NULL) && (!xmlDictOwns(dict, attr->name)))
1599 	    xmlFree((xmlChar *) attr->name);
1600         if ((attr->prefix != NULL) && (!xmlDictOwns(dict, attr->prefix)))
1601 	    xmlFree((xmlChar *) attr->prefix);
1602         if ((attr->defaultValue != NULL) &&
1603 	    (!xmlDictOwns(dict, attr->defaultValue)))
1604 	    xmlFree((xmlChar *) attr->defaultValue);
1605     } else {
1606 	if (attr->elem != NULL)
1607 	    xmlFree((xmlChar *) attr->elem);
1608 	if (attr->name != NULL)
1609 	    xmlFree((xmlChar *) attr->name);
1610 	if (attr->defaultValue != NULL)
1611 	    xmlFree((xmlChar *) attr->defaultValue);
1612 	if (attr->prefix != NULL)
1613 	    xmlFree((xmlChar *) attr->prefix);
1614     }
1615     xmlFree(attr);
1616 }
1617 
1618 
1619 /**
1620  * xmlAddAttributeDecl:
1621  * @ctxt:  the validation context
1622  * @dtd:  pointer to the DTD
1623  * @elem:  the element name
1624  * @name:  the attribute name
1625  * @ns:  the attribute namespace prefix
1626  * @type:  the attribute type
1627  * @def:  the attribute default type
1628  * @defaultValue:  the attribute default value
1629  * @tree:  if it's an enumeration, the associated list
1630  *
1631  * Register a new attribute declaration
1632  * Note that @tree becomes the ownership of the DTD
1633  *
1634  * Returns NULL if not new, otherwise the attribute decl
1635  */
1636 xmlAttributePtr
xmlAddAttributeDecl(xmlValidCtxtPtr ctxt,xmlDtdPtr dtd,const xmlChar * elem,const xmlChar * name,const xmlChar * ns,xmlAttributeType type,xmlAttributeDefault def,const xmlChar * defaultValue,xmlEnumerationPtr tree)1637 xmlAddAttributeDecl(xmlValidCtxtPtr ctxt,
1638                     xmlDtdPtr dtd, const xmlChar *elem,
1639                     const xmlChar *name, const xmlChar *ns,
1640 		    xmlAttributeType type, xmlAttributeDefault def,
1641 		    const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1642     xmlAttributePtr ret = NULL;
1643     xmlAttributeTablePtr table;
1644     xmlElementPtr elemDef;
1645     xmlDictPtr dict = NULL;
1646     int res;
1647 
1648     if (dtd == NULL) {
1649 	xmlFreeEnumeration(tree);
1650 	return(NULL);
1651     }
1652     if (name == NULL) {
1653 	xmlFreeEnumeration(tree);
1654 	return(NULL);
1655     }
1656     if (elem == NULL) {
1657 	xmlFreeEnumeration(tree);
1658 	return(NULL);
1659     }
1660     if (dtd->doc != NULL)
1661 	dict = dtd->doc->dict;
1662 
1663 #ifdef LIBXML_VALID_ENABLED
1664     /*
1665      * Check the type and possibly the default value.
1666      */
1667     switch (type) {
1668         case XML_ATTRIBUTE_CDATA:
1669 	    break;
1670         case XML_ATTRIBUTE_ID:
1671 	    break;
1672         case XML_ATTRIBUTE_IDREF:
1673 	    break;
1674         case XML_ATTRIBUTE_IDREFS:
1675 	    break;
1676         case XML_ATTRIBUTE_ENTITY:
1677 	    break;
1678         case XML_ATTRIBUTE_ENTITIES:
1679 	    break;
1680         case XML_ATTRIBUTE_NMTOKEN:
1681 	    break;
1682         case XML_ATTRIBUTE_NMTOKENS:
1683 	    break;
1684         case XML_ATTRIBUTE_ENUMERATION:
1685 	    break;
1686         case XML_ATTRIBUTE_NOTATION:
1687 	    break;
1688 	default:
1689 	    xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1690 		    "Internal: ATTRIBUTE struct corrupted invalid type\n",
1691 		    NULL);
1692 	    xmlFreeEnumeration(tree);
1693 	    return(NULL);
1694     }
1695     if ((defaultValue != NULL) &&
1696         (!xmlValidateAttributeValueInternal(dtd->doc, type, defaultValue))) {
1697 	xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_DEFAULT,
1698 	                "Attribute %s of %s: invalid default value\n",
1699 	                elem, name, defaultValue);
1700 	defaultValue = NULL;
1701 	if (ctxt != NULL)
1702 	    ctxt->valid = 0;
1703     }
1704 #endif /* LIBXML_VALID_ENABLED */
1705 
1706     /*
1707      * Check first that an attribute defined in the external subset wasn't
1708      * already defined in the internal subset
1709      */
1710     if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1711 	(dtd->doc->intSubset != NULL) &&
1712 	(dtd->doc->intSubset->attributes != NULL)) {
1713         ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1714 	if (ret != NULL) {
1715 	    xmlFreeEnumeration(tree);
1716 	    return(NULL);
1717 	}
1718     }
1719 
1720     /*
1721      * Create the Attribute table if needed.
1722      */
1723     table = (xmlAttributeTablePtr) dtd->attributes;
1724     if (table == NULL) {
1725         table = xmlHashCreateDict(0, dict);
1726 	dtd->attributes = (void *) table;
1727     }
1728     if (table == NULL)
1729         goto mem_error;
1730 
1731     ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1732     if (ret == NULL)
1733         goto mem_error;
1734     memset(ret, 0, sizeof(xmlAttribute));
1735     ret->type = XML_ATTRIBUTE_DECL;
1736 
1737     /*
1738      * fill the structure.
1739      */
1740     ret->atype = type;
1741     /*
1742      * doc must be set before possible error causes call
1743      * to xmlFreeAttribute (because it's used to check on
1744      * dict use)
1745      */
1746     ret->doc = dtd->doc;
1747     if (dict) {
1748 	ret->name = xmlDictLookup(dict, name, -1);
1749 	ret->elem = xmlDictLookup(dict, elem, -1);
1750     } else {
1751 	ret->name = xmlStrdup(name);
1752 	ret->elem = xmlStrdup(elem);
1753     }
1754     if ((ret->name == NULL) || (ret->elem == NULL))
1755         goto mem_error;
1756     if (ns != NULL) {
1757         if (dict)
1758             ret->prefix = xmlDictLookup(dict, ns, -1);
1759         else
1760             ret->prefix = xmlStrdup(ns);
1761         if (ret->prefix == NULL)
1762             goto mem_error;
1763     }
1764     ret->def = def;
1765     ret->tree = tree;
1766     tree = NULL;
1767     if (defaultValue != NULL) {
1768         if (dict)
1769 	    ret->defaultValue = xmlDictLookup(dict, defaultValue, -1);
1770 	else
1771 	    ret->defaultValue = xmlStrdup(defaultValue);
1772         if (ret->defaultValue == NULL)
1773             xmlVErrMemory(ctxt);
1774     }
1775 
1776     /*
1777      * Validity Check:
1778      * Search the DTD for previous declarations of the ATTLIST
1779      */
1780     res = xmlHashAdd3(table, ret->name, ret->prefix, ret->elem, ret);
1781     if (res <= 0) {
1782         if (res < 0)
1783             goto mem_error;
1784 #ifdef LIBXML_VALID_ENABLED
1785         /*
1786          * The attribute is already defined in this DTD.
1787          */
1788         xmlErrValidWarning(ctxt, (xmlNodePtr) dtd,
1789                 XML_DTD_ATTRIBUTE_REDEFINED,
1790                 "Attribute %s of element %s: already defined\n",
1791                 name, elem, NULL);
1792 #endif /* LIBXML_VALID_ENABLED */
1793 	xmlFreeAttribute(ret);
1794 	return(NULL);
1795     }
1796 
1797     /*
1798      * Validity Check:
1799      * Multiple ID per element
1800      */
1801     elemDef = xmlGetDtdElementDesc2(ctxt, dtd, elem);
1802     if (elemDef != NULL) {
1803 
1804 #ifdef LIBXML_VALID_ENABLED
1805         if ((type == XML_ATTRIBUTE_ID) &&
1806 	    (xmlScanIDAttributeDecl(ctxt, elemDef, 1) != 0)) {
1807 	    xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID,
1808 	   "Element %s has too may ID attributes defined : %s\n",
1809 		   elem, name, NULL);
1810 	    if (ctxt != NULL)
1811 		ctxt->valid = 0;
1812 	}
1813 #endif /* LIBXML_VALID_ENABLED */
1814 
1815 	/*
1816 	 * Insert namespace default def first they need to be
1817 	 * processed first.
1818 	 */
1819 	if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1820 	    ((ret->prefix != NULL &&
1821 	     (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1822 	    ret->nexth = elemDef->attributes;
1823 	    elemDef->attributes = ret;
1824 	} else {
1825 	    xmlAttributePtr tmp = elemDef->attributes;
1826 
1827 	    while ((tmp != NULL) &&
1828 		   ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1829 		    ((ret->prefix != NULL &&
1830 		     (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1831 		if (tmp->nexth == NULL)
1832 		    break;
1833 		tmp = tmp->nexth;
1834 	    }
1835 	    if (tmp != NULL) {
1836 		ret->nexth = tmp->nexth;
1837 	        tmp->nexth = ret;
1838 	    } else {
1839 		ret->nexth = elemDef->attributes;
1840 		elemDef->attributes = ret;
1841 	    }
1842 	}
1843     }
1844 
1845     /*
1846      * Link it to the DTD
1847      */
1848     ret->parent = dtd;
1849     if (dtd->last == NULL) {
1850 	dtd->children = dtd->last = (xmlNodePtr) ret;
1851     } else {
1852         dtd->last->next = (xmlNodePtr) ret;
1853 	ret->prev = dtd->last;
1854 	dtd->last = (xmlNodePtr) ret;
1855     }
1856     return(ret);
1857 
1858 mem_error:
1859     xmlVErrMemory(ctxt);
1860     xmlFreeEnumeration(tree);
1861     xmlFreeAttribute(ret);
1862     return(NULL);
1863 }
1864 
1865 static void
xmlFreeAttributeTableEntry(void * attr,const xmlChar * name ATTRIBUTE_UNUSED)1866 xmlFreeAttributeTableEntry(void *attr, const xmlChar *name ATTRIBUTE_UNUSED) {
1867     xmlFreeAttribute((xmlAttributePtr) attr);
1868 }
1869 
1870 /**
1871  * xmlFreeAttributeTable:
1872  * @table:  An attribute table
1873  *
1874  * Deallocate the memory used by an entities hash table.
1875  */
1876 void
xmlFreeAttributeTable(xmlAttributeTablePtr table)1877 xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1878     xmlHashFree(table, xmlFreeAttributeTableEntry);
1879 }
1880 
1881 #ifdef LIBXML_TREE_ENABLED
1882 /**
1883  * xmlCopyAttribute:
1884  * @attr:  An attribute
1885  *
1886  * Build a copy of an attribute.
1887  *
1888  * Returns the new xmlAttributePtr or NULL in case of error.
1889  */
1890 static void *
xmlCopyAttribute(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)1891 xmlCopyAttribute(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
1892     xmlAttributePtr attr = (xmlAttributePtr) payload;
1893     xmlAttributePtr cur;
1894 
1895     cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1896     if (cur == NULL)
1897 	return(NULL);
1898     memset(cur, 0, sizeof(xmlAttribute));
1899     cur->type = XML_ATTRIBUTE_DECL;
1900     cur->atype = attr->atype;
1901     cur->def = attr->def;
1902     if (attr->tree != NULL) {
1903         cur->tree = xmlCopyEnumeration(attr->tree);
1904         if (cur->tree == NULL)
1905             goto error;
1906     }
1907     if (attr->elem != NULL) {
1908 	cur->elem = xmlStrdup(attr->elem);
1909         if (cur->elem == NULL)
1910             goto error;
1911     }
1912     if (attr->name != NULL) {
1913 	cur->name = xmlStrdup(attr->name);
1914         if (cur->name == NULL)
1915             goto error;
1916     }
1917     if (attr->prefix != NULL) {
1918 	cur->prefix = xmlStrdup(attr->prefix);
1919         if (cur->prefix == NULL)
1920             goto error;
1921     }
1922     if (attr->defaultValue != NULL) {
1923 	cur->defaultValue = xmlStrdup(attr->defaultValue);
1924         if (cur->defaultValue == NULL)
1925             goto error;
1926     }
1927     return(cur);
1928 
1929 error:
1930     xmlFreeAttribute(cur);
1931     return(NULL);
1932 }
1933 
1934 /**
1935  * xmlCopyAttributeTable:
1936  * @table:  An attribute table
1937  *
1938  * Build a copy of an attribute table.
1939  *
1940  * Returns the new xmlAttributeTablePtr or NULL in case of error.
1941  */
1942 xmlAttributeTablePtr
xmlCopyAttributeTable(xmlAttributeTablePtr table)1943 xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1944     return(xmlHashCopySafe(table, xmlCopyAttribute,
1945                            xmlFreeAttributeTableEntry));
1946 }
1947 #endif /* LIBXML_TREE_ENABLED */
1948 
1949 #ifdef LIBXML_OUTPUT_ENABLED
1950 /**
1951  * xmlDumpAttributeDecl:
1952  * @buf:  the XML buffer output
1953  * @attr:  An attribute declaration
1954  *
1955  * DEPRECATED: Use xmlSaveTree.
1956  *
1957  * This will dump the content of the attribute declaration as an XML
1958  * DTD definition
1959  */
1960 void
xmlDumpAttributeDecl(xmlBufferPtr buf,xmlAttributePtr attr)1961 xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1962     xmlSaveCtxtPtr save;
1963 
1964     if ((buf == NULL) || (attr == NULL))
1965         return;
1966 
1967     save = xmlSaveToBuffer(buf, NULL, 0);
1968     xmlSaveTree(save, (xmlNodePtr) attr);
1969     xmlSaveClose(save);
1970 }
1971 
1972 /**
1973  * xmlDumpAttributeDeclScan:
1974  * @attr:  An attribute declaration
1975  * @buf:  the XML buffer output
1976  *
1977  * This is used with the hash scan function - just reverses arguments
1978  */
1979 static void
xmlDumpAttributeDeclScan(void * attr,void * buf,const xmlChar * name ATTRIBUTE_UNUSED)1980 xmlDumpAttributeDeclScan(void *attr, void *buf,
1981                          const xmlChar *name ATTRIBUTE_UNUSED) {
1982     xmlDumpAttributeDecl((xmlBufferPtr) buf, (xmlAttributePtr) attr);
1983 }
1984 
1985 /**
1986  * xmlDumpAttributeTable:
1987  * @buf:  the XML buffer output
1988  * @table:  An attribute table
1989  *
1990  * DEPRECATED: Don't use.
1991  *
1992  * This will dump the content of the attribute table as an XML DTD definition
1993  */
1994 void
xmlDumpAttributeTable(xmlBufferPtr buf,xmlAttributeTablePtr table)1995 xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1996     if ((buf == NULL) || (table == NULL))
1997         return;
1998     xmlHashScan(table, xmlDumpAttributeDeclScan, buf);
1999 }
2000 #endif /* LIBXML_OUTPUT_ENABLED */
2001 
2002 /************************************************************************
2003  *									*
2004  *				NOTATIONs				*
2005  *									*
2006  ************************************************************************/
2007 /**
2008  * xmlFreeNotation:
2009  * @not:  A notation
2010  *
2011  * Deallocate the memory used by an notation definition
2012  */
2013 static void
xmlFreeNotation(xmlNotationPtr nota)2014 xmlFreeNotation(xmlNotationPtr nota) {
2015     if (nota == NULL) return;
2016     if (nota->name != NULL)
2017 	xmlFree((xmlChar *) nota->name);
2018     if (nota->PublicID != NULL)
2019 	xmlFree((xmlChar *) nota->PublicID);
2020     if (nota->SystemID != NULL)
2021 	xmlFree((xmlChar *) nota->SystemID);
2022     xmlFree(nota);
2023 }
2024 
2025 
2026 /**
2027  * xmlAddNotationDecl:
2028  * @dtd:  pointer to the DTD
2029  * @ctxt:  the validation context
2030  * @name:  the entity name
2031  * @PublicID:  the public identifier or NULL
2032  * @SystemID:  the system identifier or NULL
2033  *
2034  * Register a new notation declaration
2035  *
2036  * Returns NULL if not, otherwise the entity
2037  */
2038 xmlNotationPtr
xmlAddNotationDecl(xmlValidCtxtPtr ctxt,xmlDtdPtr dtd,const xmlChar * name,const xmlChar * PublicID,const xmlChar * SystemID)2039 xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd,
2040 	           const xmlChar *name,
2041                    const xmlChar *PublicID, const xmlChar *SystemID) {
2042     xmlNotationPtr ret = NULL;
2043     xmlNotationTablePtr table;
2044     int res;
2045 
2046     if (dtd == NULL) {
2047 	return(NULL);
2048     }
2049     if (name == NULL) {
2050 	return(NULL);
2051     }
2052     if ((PublicID == NULL) && (SystemID == NULL)) {
2053 	return(NULL);
2054     }
2055 
2056     /*
2057      * Create the Notation table if needed.
2058      */
2059     table = (xmlNotationTablePtr) dtd->notations;
2060     if (table == NULL) {
2061 	xmlDictPtr dict = NULL;
2062 	if (dtd->doc != NULL)
2063 	    dict = dtd->doc->dict;
2064 
2065         dtd->notations = table = xmlHashCreateDict(0, dict);
2066         if (table == NULL)
2067             goto mem_error;
2068     }
2069 
2070     ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2071     if (ret == NULL)
2072         goto mem_error;
2073     memset(ret, 0, sizeof(xmlNotation));
2074 
2075     /*
2076      * fill the structure.
2077      */
2078     ret->name = xmlStrdup(name);
2079     if (ret->name == NULL)
2080         goto mem_error;
2081     if (SystemID != NULL) {
2082         ret->SystemID = xmlStrdup(SystemID);
2083         if (ret->SystemID == NULL)
2084             goto mem_error;
2085     }
2086     if (PublicID != NULL) {
2087         ret->PublicID = xmlStrdup(PublicID);
2088         if (ret->PublicID == NULL)
2089             goto mem_error;
2090     }
2091 
2092     /*
2093      * Validity Check:
2094      * Check the DTD for previous declarations of the ATTLIST
2095      */
2096     res = xmlHashAdd(table, name, ret);
2097     if (res <= 0) {
2098         if (res < 0)
2099             goto mem_error;
2100 #ifdef LIBXML_VALID_ENABLED
2101         xmlErrValid(ctxt, XML_DTD_NOTATION_REDEFINED,
2102                     "xmlAddNotationDecl: %s already defined\n",
2103                     (const char *) name);
2104 #endif /* LIBXML_VALID_ENABLED */
2105 	xmlFreeNotation(ret);
2106 	return(NULL);
2107     }
2108     return(ret);
2109 
2110 mem_error:
2111     xmlVErrMemory(ctxt);
2112     xmlFreeNotation(ret);
2113     return(NULL);
2114 }
2115 
2116 static void
xmlFreeNotationTableEntry(void * nota,const xmlChar * name ATTRIBUTE_UNUSED)2117 xmlFreeNotationTableEntry(void *nota, const xmlChar *name ATTRIBUTE_UNUSED) {
2118     xmlFreeNotation((xmlNotationPtr) nota);
2119 }
2120 
2121 /**
2122  * xmlFreeNotationTable:
2123  * @table:  An notation table
2124  *
2125  * Deallocate the memory used by an entities hash table.
2126  */
2127 void
xmlFreeNotationTable(xmlNotationTablePtr table)2128 xmlFreeNotationTable(xmlNotationTablePtr table) {
2129     xmlHashFree(table, xmlFreeNotationTableEntry);
2130 }
2131 
2132 #ifdef LIBXML_TREE_ENABLED
2133 /**
2134  * xmlCopyNotation:
2135  * @nota:  A notation
2136  *
2137  * Build a copy of a notation.
2138  *
2139  * Returns the new xmlNotationPtr or NULL in case of error.
2140  */
2141 static void *
xmlCopyNotation(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)2142 xmlCopyNotation(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
2143     xmlNotationPtr nota = (xmlNotationPtr) payload;
2144     xmlNotationPtr cur;
2145 
2146     cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2147     if (cur == NULL)
2148 	return(NULL);
2149     memset(cur, 0, sizeof(*cur));
2150     if (nota->name != NULL) {
2151 	cur->name = xmlStrdup(nota->name);
2152         if (cur->name == NULL)
2153             goto error;
2154     }
2155     if (nota->PublicID != NULL) {
2156 	cur->PublicID = xmlStrdup(nota->PublicID);
2157         if (cur->PublicID == NULL)
2158             goto error;
2159     }
2160     if (nota->SystemID != NULL) {
2161 	cur->SystemID = xmlStrdup(nota->SystemID);
2162         if (cur->SystemID == NULL)
2163             goto error;
2164     }
2165     return(cur);
2166 
2167 error:
2168     xmlFreeNotation(cur);
2169     return(NULL);
2170 }
2171 
2172 /**
2173  * xmlCopyNotationTable:
2174  * @table:  A notation table
2175  *
2176  * Build a copy of a notation table.
2177  *
2178  * Returns the new xmlNotationTablePtr or NULL in case of error.
2179  */
2180 xmlNotationTablePtr
xmlCopyNotationTable(xmlNotationTablePtr table)2181 xmlCopyNotationTable(xmlNotationTablePtr table) {
2182     return(xmlHashCopySafe(table, xmlCopyNotation, xmlFreeNotationTableEntry));
2183 }
2184 #endif /* LIBXML_TREE_ENABLED */
2185 
2186 #ifdef LIBXML_OUTPUT_ENABLED
2187 /**
2188  * xmlDumpNotationDecl:
2189  * @buf:  the XML buffer output
2190  * @nota:  A notation declaration
2191  *
2192  * DEPRECATED: Don't use.
2193  *
2194  * This will dump the content the notation declaration as an XML DTD definition
2195  */
2196 void
xmlDumpNotationDecl(xmlBufferPtr buf,xmlNotationPtr nota)2197 xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2198     xmlSaveCtxtPtr save;
2199 
2200     if ((buf == NULL) || (nota == NULL))
2201         return;
2202 
2203     save = xmlSaveToBuffer(buf, NULL, 0);
2204     xmlSaveNotationDecl(save, nota);
2205     xmlSaveClose(save);
2206 }
2207 
2208 /**
2209  * xmlDumpNotationTable:
2210  * @buf:  the XML buffer output
2211  * @table:  A notation table
2212  *
2213  * DEPRECATED: Don't use.
2214  *
2215  * This will dump the content of the notation table as an XML DTD definition
2216  */
2217 void
xmlDumpNotationTable(xmlBufferPtr buf,xmlNotationTablePtr table)2218 xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2219     xmlSaveCtxtPtr save;
2220 
2221     if ((buf == NULL) || (table == NULL))
2222         return;
2223 
2224     save = xmlSaveToBuffer(buf, NULL, 0);
2225     xmlSaveNotationTable(save, table);
2226     xmlSaveClose(save);
2227 }
2228 #endif /* LIBXML_OUTPUT_ENABLED */
2229 
2230 /************************************************************************
2231  *									*
2232  *				IDs					*
2233  *									*
2234  ************************************************************************/
2235 /**
2236  * DICT_FREE:
2237  * @str:  a string
2238  *
2239  * Free a string if it is not owned by the "dict" dictionary in the
2240  * current scope
2241  */
2242 #define DICT_FREE(str)						\
2243 	if ((str) && ((!dict) ||				\
2244 	    (xmlDictOwns(dict, (const xmlChar *)(str)) == 0)))	\
2245 	    xmlFree((char *)(str));
2246 
2247 static int
xmlIsStreaming(xmlValidCtxtPtr ctxt)2248 xmlIsStreaming(xmlValidCtxtPtr ctxt) {
2249     xmlParserCtxtPtr pctxt;
2250 
2251     if (ctxt == NULL)
2252         return(0);
2253     if ((ctxt->flags & XML_VCTXT_USE_PCTXT) == 0)
2254         return(0);
2255     pctxt = ctxt->userData;
2256     return(pctxt->parseMode == XML_PARSE_READER);
2257 }
2258 
2259 /**
2260  * xmlFreeID:
2261  * @not:  A id
2262  *
2263  * Deallocate the memory used by an id definition
2264  */
2265 static void
xmlFreeID(xmlIDPtr id)2266 xmlFreeID(xmlIDPtr id) {
2267     xmlDictPtr dict = NULL;
2268 
2269     if (id == NULL) return;
2270 
2271     if (id->doc != NULL)
2272         dict = id->doc->dict;
2273 
2274     if (id->value != NULL)
2275 	DICT_FREE(id->value)
2276     if (id->name != NULL)
2277 	DICT_FREE(id->name)
2278     xmlFree(id);
2279 }
2280 
2281 
2282 /**
2283  * xmlAddIDSafe:
2284  * @doc:  pointer to the document
2285  * @value:  the value name
2286  * @attr:  the attribute holding the ID
2287  * @id:  pointer to new xmlIdPtr (optional)
2288  *
2289  * Register a new id declaration
2290  *
2291  * Returns 1 on success, 0 if the ID already exists, -1 if a memory
2292  * allocation fails.
2293  */
2294 int
xmlAddIDSafe(xmlDocPtr doc,const xmlChar * value,xmlAttrPtr attr,int streaming,xmlIDPtr * id)2295 xmlAddIDSafe(xmlDocPtr doc, const xmlChar *value, xmlAttrPtr attr,
2296              int streaming, xmlIDPtr *id) {
2297     xmlIDPtr ret;
2298     xmlIDTablePtr table;
2299 
2300     if (id != NULL)
2301         *id = NULL;
2302 
2303     if (doc == NULL) {
2304 	return(-1);
2305     }
2306     if ((value == NULL) || (value[0] == 0)) {
2307 	return(0);
2308     }
2309     if (attr == NULL) {
2310 	return(-1);
2311     }
2312 
2313     /*
2314      * Create the ID table if needed.
2315      */
2316     table = (xmlIDTablePtr) doc->ids;
2317     if (table == NULL)  {
2318         doc->ids = table = xmlHashCreateDict(0, doc->dict);
2319         if (table == NULL)
2320             return(-1);
2321     } else {
2322         ret = xmlHashLookup(table, value);
2323         if (ret != NULL) {
2324             /*
2325              * Update the attribute unless we are parsing in streaming
2326              * mode. If the attribute is copied from an entity we want
2327              * the ID reference the copy.
2328              */
2329             if (ret->attr != NULL) {
2330                 ret->attr->id = NULL;
2331                 ret->attr = attr;
2332                 attr->id = ret;
2333             }
2334             ret->lineno = xmlGetLineNo(attr->parent);
2335 	    attr->atype = XML_ATTRIBUTE_ID;
2336             return(0);
2337         }
2338     }
2339 
2340     ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2341     if (ret == NULL)
2342 	return(-1);
2343     memset(ret, 0, sizeof(*ret));
2344 
2345     /*
2346      * fill the structure.
2347      */
2348     ret->doc = doc;
2349     ret->value = xmlStrdup(value);
2350     if (ret->value == NULL) {
2351         xmlFreeID(ret);
2352         return(-1);
2353     }
2354     if (streaming) {
2355 	/*
2356 	 * Operating in streaming mode, attr is gonna disappear
2357 	 */
2358 	if (doc->dict != NULL)
2359 	    ret->name = xmlDictLookup(doc->dict, attr->name, -1);
2360 	else
2361 	    ret->name = xmlStrdup(attr->name);
2362         if (ret->name == NULL) {
2363             xmlFreeID(ret);
2364             return(-1);
2365         }
2366 	ret->attr = NULL;
2367     } else {
2368 	ret->attr = attr;
2369 	ret->name = NULL;
2370     }
2371     ret->lineno = xmlGetLineNo(attr->parent);
2372 
2373     if (xmlHashAddEntry(table, value, ret) < 0) {
2374 	xmlFreeID(ret);
2375 	return(-1);
2376     }
2377 
2378     attr->atype = XML_ATTRIBUTE_ID;
2379     if (!streaming)
2380         attr->id = ret;
2381 
2382     if (id != NULL)
2383         *id = ret;
2384     return(1);
2385 }
2386 
2387 /**
2388  * xmlAddID:
2389  * @ctxt:  the validation context
2390  * @doc:  pointer to the document
2391  * @value:  the value name
2392  * @attr:  the attribute holding the ID
2393  *
2394  * Register a new id declaration
2395  *
2396  * Returns NULL if not, otherwise the new xmlIDPtr
2397  */
2398 xmlIDPtr
xmlAddID(xmlValidCtxtPtr ctxt,xmlDocPtr doc,const xmlChar * value,xmlAttrPtr attr)2399 xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2400          xmlAttrPtr attr) {
2401     xmlIDPtr id;
2402     int res;
2403 
2404     res = xmlAddIDSafe(doc, value, attr, xmlIsStreaming(ctxt), &id);
2405     if (res < 0) {
2406         xmlVErrMemory(ctxt);
2407     }
2408 #ifdef LIBXML_VALID_ENABLED
2409     else if (res == 0) {
2410         if (ctxt != NULL) {
2411             /*
2412              * The id is already defined in this DTD.
2413              */
2414             xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED,
2415                             "ID %s already defined\n", value, NULL, NULL);
2416         }
2417     }
2418 #endif /* LIBXML_VALID_ENABLED */
2419 
2420     return(id);
2421 }
2422 
2423 static void
xmlFreeIDTableEntry(void * id,const xmlChar * name ATTRIBUTE_UNUSED)2424 xmlFreeIDTableEntry(void *id, const xmlChar *name ATTRIBUTE_UNUSED) {
2425     xmlFreeID((xmlIDPtr) id);
2426 }
2427 
2428 /**
2429  * xmlFreeIDTable:
2430  * @table:  An id table
2431  *
2432  * Deallocate the memory used by an ID hash table.
2433  */
2434 void
xmlFreeIDTable(xmlIDTablePtr table)2435 xmlFreeIDTable(xmlIDTablePtr table) {
2436     xmlHashFree(table, xmlFreeIDTableEntry);
2437 }
2438 
2439 /**
2440  * xmlIsID:
2441  * @doc:  the document
2442  * @elem:  the element carrying the attribute
2443  * @attr:  the attribute
2444  *
2445  * Determine whether an attribute is of type ID. In case we have DTD(s)
2446  * then this is done if DTD loading has been requested. In the case
2447  * of HTML documents parsed with the HTML parser, then ID detection is
2448  * done systematically.
2449  *
2450  * Returns 0 or 1 depending on the lookup result or -1 if a memory allocation
2451  * failed.
2452  */
2453 int
xmlIsID(xmlDocPtr doc,xmlNodePtr elem,xmlAttrPtr attr)2454 xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2455     if ((attr == NULL) || (attr->name == NULL)) return(0);
2456     if ((attr->ns != NULL) && (attr->ns->prefix != NULL) &&
2457         (!strcmp((char *) attr->name, "id")) &&
2458         (!strcmp((char *) attr->ns->prefix, "xml")))
2459 	return(1);
2460     if (doc == NULL) return(0);
2461     if ((doc->intSubset == NULL) && (doc->extSubset == NULL) &&
2462         (doc->type != XML_HTML_DOCUMENT_NODE)) {
2463 	return(0);
2464     } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2465         if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2466 	    ((xmlStrEqual(BAD_CAST "name", attr->name)) &&
2467 	    ((elem == NULL) || (xmlStrEqual(elem->name, BAD_CAST "a")))))
2468 	    return(1);
2469 	return(0);
2470     } else if (elem == NULL) {
2471 	return(0);
2472     } else {
2473 	xmlAttributePtr attrDecl = NULL;
2474 
2475 	xmlChar felem[50];
2476 	xmlChar *fullelemname;
2477         const xmlChar *aprefix;
2478 
2479 	fullelemname = (elem->ns != NULL && elem->ns->prefix != NULL) ?
2480 	    xmlBuildQName(elem->name, elem->ns->prefix, felem, 50) :
2481 	    (xmlChar *)elem->name;
2482         if (fullelemname == NULL)
2483             return(-1);
2484 
2485         aprefix = (attr->ns != NULL) ? attr->ns->prefix : NULL;
2486 
2487 	if (fullelemname != NULL) {
2488 	    attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullelemname,
2489 		                          attr->name, aprefix);
2490 	    if ((attrDecl == NULL) && (doc->extSubset != NULL))
2491 		attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullelemname,
2492 					      attr->name, aprefix);
2493 	}
2494 
2495 	if ((fullelemname != felem) && (fullelemname != elem->name))
2496 	    xmlFree(fullelemname);
2497 
2498         if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2499 	    return(1);
2500     }
2501     return(0);
2502 }
2503 
2504 /**
2505  * xmlRemoveID:
2506  * @doc:  the document
2507  * @attr:  the attribute
2508  *
2509  * Remove the given attribute from the ID table maintained internally.
2510  *
2511  * Returns -1 if the lookup failed and 0 otherwise
2512  */
2513 int
xmlRemoveID(xmlDocPtr doc,xmlAttrPtr attr)2514 xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2515     xmlIDTablePtr table;
2516 
2517     if (doc == NULL) return(-1);
2518     if ((attr == NULL) || (attr->id == NULL)) return(-1);
2519 
2520     table = (xmlIDTablePtr) doc->ids;
2521     if (table == NULL)
2522         return(-1);
2523 
2524     if (xmlHashRemoveEntry(table, attr->id->value, xmlFreeIDTableEntry) < 0)
2525         return(-1);
2526 
2527     attr->atype = 0;
2528     attr->id = NULL;
2529 
2530     return(0);
2531 }
2532 
2533 /**
2534  * xmlGetID:
2535  * @doc:  pointer to the document
2536  * @ID:  the ID value
2537  *
2538  * Search the attribute declaring the given ID
2539  *
2540  * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2541  */
2542 xmlAttrPtr
xmlGetID(xmlDocPtr doc,const xmlChar * ID)2543 xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2544     xmlIDTablePtr table;
2545     xmlIDPtr id;
2546 
2547     if (doc == NULL) {
2548 	return(NULL);
2549     }
2550 
2551     if (ID == NULL) {
2552 	return(NULL);
2553     }
2554 
2555     table = (xmlIDTablePtr) doc->ids;
2556     if (table == NULL)
2557         return(NULL);
2558 
2559     id = xmlHashLookup(table, ID);
2560     if (id == NULL)
2561 	return(NULL);
2562     if (id->attr == NULL) {
2563 	/*
2564 	 * We are operating on a stream, return a well known reference
2565 	 * since the attribute node doesn't exist anymore
2566 	 */
2567 	return((xmlAttrPtr) doc);
2568     }
2569     return(id->attr);
2570 }
2571 
2572 /************************************************************************
2573  *									*
2574  *				Refs					*
2575  *									*
2576  ************************************************************************/
2577 typedef struct xmlRemoveMemo_t
2578 {
2579 	xmlListPtr l;
2580 	xmlAttrPtr ap;
2581 } xmlRemoveMemo;
2582 
2583 typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2584 
2585 typedef struct xmlValidateMemo_t
2586 {
2587     xmlValidCtxtPtr ctxt;
2588     const xmlChar *name;
2589 } xmlValidateMemo;
2590 
2591 typedef xmlValidateMemo *xmlValidateMemoPtr;
2592 
2593 /**
2594  * xmlFreeRef:
2595  * @lk:  A list link
2596  *
2597  * Deallocate the memory used by a ref definition
2598  */
2599 static void
xmlFreeRef(xmlLinkPtr lk)2600 xmlFreeRef(xmlLinkPtr lk) {
2601     xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2602     if (ref == NULL) return;
2603     if (ref->value != NULL)
2604         xmlFree((xmlChar *)ref->value);
2605     if (ref->name != NULL)
2606         xmlFree((xmlChar *)ref->name);
2607     xmlFree(ref);
2608 }
2609 
2610 /**
2611  * xmlFreeRefTableEntry:
2612  * @list_ref:  A list of references.
2613  *
2614  * Deallocate the memory used by a list of references
2615  */
2616 static void
xmlFreeRefTableEntry(void * payload,const xmlChar * name ATTRIBUTE_UNUSED)2617 xmlFreeRefTableEntry(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) {
2618     xmlListPtr list_ref = (xmlListPtr) payload;
2619     if (list_ref == NULL) return;
2620     xmlListDelete(list_ref);
2621 }
2622 
2623 /**
2624  * xmlWalkRemoveRef:
2625  * @data:  Contents of current link
2626  * @user:  Value supplied by the user
2627  *
2628  * Returns 0 to abort the walk or 1 to continue
2629  */
2630 static int
xmlWalkRemoveRef(const void * data,void * user)2631 xmlWalkRemoveRef(const void *data, void *user)
2632 {
2633     xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2634     xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2635     xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
2636 
2637     if (attr0 == attr1) { /* Matched: remove and terminate walk */
2638         xmlListRemoveFirst(ref_list, (void *)data);
2639         return 0;
2640     }
2641     return 1;
2642 }
2643 
2644 /**
2645  * xmlDummyCompare
2646  * @data0:  Value supplied by the user
2647  * @data1:  Value supplied by the user
2648  *
2649  * Do nothing, return 0. Used to create unordered lists.
2650  */
2651 static int
xmlDummyCompare(const void * data0 ATTRIBUTE_UNUSED,const void * data1 ATTRIBUTE_UNUSED)2652 xmlDummyCompare(const void *data0 ATTRIBUTE_UNUSED,
2653                 const void *data1 ATTRIBUTE_UNUSED)
2654 {
2655     return (0);
2656 }
2657 
2658 /**
2659  * xmlAddRef:
2660  * @ctxt:  the validation context
2661  * @doc:  pointer to the document
2662  * @value:  the value name
2663  * @attr:  the attribute holding the Ref
2664  *
2665  * DEPRECATED, do not use. This function will be removed from the public API.
2666  *
2667  * Register a new ref declaration
2668  *
2669  * Returns NULL if not, otherwise the new xmlRefPtr
2670  */
2671 xmlRefPtr
xmlAddRef(xmlValidCtxtPtr ctxt,xmlDocPtr doc,const xmlChar * value,xmlAttrPtr attr)2672 xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2673     xmlAttrPtr attr) {
2674     xmlRefPtr ret = NULL;
2675     xmlRefTablePtr table;
2676     xmlListPtr ref_list;
2677 
2678     if (doc == NULL) {
2679         return(NULL);
2680     }
2681     if (value == NULL) {
2682         return(NULL);
2683     }
2684     if (attr == NULL) {
2685         return(NULL);
2686     }
2687 
2688     /*
2689      * Create the Ref table if needed.
2690      */
2691     table = (xmlRefTablePtr) doc->refs;
2692     if (table == NULL) {
2693         doc->refs = table = xmlHashCreateDict(0, doc->dict);
2694         if (table == NULL)
2695             goto failed;
2696     }
2697 
2698     ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2699     if (ret == NULL)
2700         goto failed;
2701     memset(ret, 0, sizeof(*ret));
2702 
2703     /*
2704      * fill the structure.
2705      */
2706     ret->value = xmlStrdup(value);
2707     if (ret->value == NULL)
2708         goto failed;
2709     if (xmlIsStreaming(ctxt)) {
2710 	/*
2711 	 * Operating in streaming mode, attr is gonna disappear
2712 	 */
2713 	ret->name = xmlStrdup(attr->name);
2714         if (ret->name == NULL)
2715             goto failed;
2716 	ret->attr = NULL;
2717     } else {
2718 	ret->name = NULL;
2719 	ret->attr = attr;
2720     }
2721     ret->lineno = xmlGetLineNo(attr->parent);
2722 
2723     /* To add a reference :-
2724      * References are maintained as a list of references,
2725      * Lookup the entry, if no entry create new nodelist
2726      * Add the owning node to the NodeList
2727      * Return the ref
2728      */
2729 
2730     if (NULL == (ref_list = xmlHashLookup(table, value))) {
2731         int res;
2732 
2733         if (NULL == (ref_list = xmlListCreate(xmlFreeRef, xmlDummyCompare)))
2734 	    goto failed;
2735         res = xmlHashAdd(table, value, ref_list);
2736         if (res <= 0) {
2737             xmlListDelete(ref_list);
2738 	    goto failed;
2739         }
2740     }
2741     if (xmlListAppend(ref_list, ret) != 0)
2742         goto failed;
2743     return(ret);
2744 
2745 failed:
2746     xmlVErrMemory(ctxt);
2747     if (ret != NULL) {
2748         if (ret->value != NULL)
2749 	    xmlFree((char *)ret->value);
2750         if (ret->name != NULL)
2751 	    xmlFree((char *)ret->name);
2752         xmlFree(ret);
2753     }
2754     return(NULL);
2755 }
2756 
2757 /**
2758  * xmlFreeRefTable:
2759  * @table:  An ref table
2760  *
2761  * DEPRECATED, do not use. This function will be removed from the public API.
2762  *
2763  * Deallocate the memory used by an Ref hash table.
2764  */
2765 void
xmlFreeRefTable(xmlRefTablePtr table)2766 xmlFreeRefTable(xmlRefTablePtr table) {
2767     xmlHashFree(table, xmlFreeRefTableEntry);
2768 }
2769 
2770 /**
2771  * xmlIsRef:
2772  * @doc:  the document
2773  * @elem:  the element carrying the attribute
2774  * @attr:  the attribute
2775  *
2776  * DEPRECATED, do not use. This function will be removed from the public API.
2777  *
2778  * Determine whether an attribute is of type Ref. In case we have DTD(s)
2779  * then this is simple, otherwise we use an heuristic: name Ref (upper
2780  * or lowercase).
2781  *
2782  * Returns 0 or 1 depending on the lookup result
2783  */
2784 int
xmlIsRef(xmlDocPtr doc,xmlNodePtr elem,xmlAttrPtr attr)2785 xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2786     if (attr == NULL)
2787         return(0);
2788     if (doc == NULL) {
2789         doc = attr->doc;
2790 	if (doc == NULL) return(0);
2791     }
2792 
2793     if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2794         return(0);
2795     } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2796         /* TODO @@@ */
2797         return(0);
2798     } else {
2799         xmlAttributePtr attrDecl;
2800         const xmlChar *aprefix;
2801 
2802         if (elem == NULL) return(0);
2803         aprefix = (attr->ns != NULL) ? attr->ns->prefix : NULL;
2804         attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, attr->name,
2805                                       aprefix);
2806         if ((attrDecl == NULL) && (doc->extSubset != NULL))
2807             attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, attr->name,
2808                                           aprefix);
2809 
2810 	if ((attrDecl != NULL) &&
2811 	    (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2812 	     attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2813 	return(1);
2814     }
2815     return(0);
2816 }
2817 
2818 /**
2819  * xmlRemoveRef:
2820  * @doc:  the document
2821  * @attr:  the attribute
2822  *
2823  * DEPRECATED, do not use. This function will be removed from the public API.
2824  *
2825  * Remove the given attribute from the Ref table maintained internally.
2826  *
2827  * Returns -1 if the lookup failed and 0 otherwise
2828  */
2829 int
xmlRemoveRef(xmlDocPtr doc,xmlAttrPtr attr)2830 xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
2831     xmlListPtr ref_list;
2832     xmlRefTablePtr table;
2833     xmlChar *ID;
2834     xmlRemoveMemo target;
2835 
2836     if (doc == NULL) return(-1);
2837     if (attr == NULL) return(-1);
2838 
2839     table = (xmlRefTablePtr) doc->refs;
2840     if (table == NULL)
2841         return(-1);
2842 
2843     ID = xmlNodeListGetString(doc, attr->children, 1);
2844     if (ID == NULL)
2845         return(-1);
2846 
2847     ref_list = xmlHashLookup(table, ID);
2848     if(ref_list == NULL) {
2849         xmlFree(ID);
2850         return (-1);
2851     }
2852 
2853     /* At this point, ref_list refers to a list of references which
2854      * have the same key as the supplied attr. Our list of references
2855      * is ordered by reference address and we don't have that information
2856      * here to use when removing. We'll have to walk the list and
2857      * check for a matching attribute, when we find one stop the walk
2858      * and remove the entry.
2859      * The list is ordered by reference, so that means we don't have the
2860      * key. Passing the list and the reference to the walker means we
2861      * will have enough data to be able to remove the entry.
2862      */
2863     target.l = ref_list;
2864     target.ap = attr;
2865 
2866     /* Remove the supplied attr from our list */
2867     xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
2868 
2869     /*If the list is empty then remove the list entry in the hash */
2870     if (xmlListEmpty(ref_list))
2871         xmlHashUpdateEntry(table, ID, NULL, xmlFreeRefTableEntry);
2872     xmlFree(ID);
2873     return(0);
2874 }
2875 
2876 /**
2877  * xmlGetRefs:
2878  * @doc:  pointer to the document
2879  * @ID:  the ID value
2880  *
2881  * DEPRECATED, do not use. This function will be removed from the public API.
2882  *
2883  * Find the set of references for the supplied ID.
2884  *
2885  * Returns NULL if not found, otherwise node set for the ID.
2886  */
2887 xmlListPtr
xmlGetRefs(xmlDocPtr doc,const xmlChar * ID)2888 xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
2889     xmlRefTablePtr table;
2890 
2891     if (doc == NULL) {
2892         return(NULL);
2893     }
2894 
2895     if (ID == NULL) {
2896         return(NULL);
2897     }
2898 
2899     table = (xmlRefTablePtr) doc->refs;
2900     if (table == NULL)
2901         return(NULL);
2902 
2903     return (xmlHashLookup(table, ID));
2904 }
2905 
2906 /************************************************************************
2907  *									*
2908  *		Routines for validity checking				*
2909  *									*
2910  ************************************************************************/
2911 
2912 /**
2913  * xmlGetDtdElementDesc:
2914  * @dtd:  a pointer to the DtD to search
2915  * @name:  the element name
2916  *
2917  * Search the DTD for the description of this element
2918  *
2919  * NOTE: A NULL return value can also mean that a memory allocation failed.
2920  *
2921  * returns the xmlElementPtr if found or NULL
2922  */
2923 
2924 xmlElementPtr
xmlGetDtdElementDesc(xmlDtdPtr dtd,const xmlChar * name)2925 xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2926     xmlElementTablePtr table;
2927     xmlElementPtr cur;
2928     xmlChar *uqname = NULL, *prefix = NULL;
2929 
2930     if ((dtd == NULL) || (name == NULL)) return(NULL);
2931     if (dtd->elements == NULL)
2932 	return(NULL);
2933     table = (xmlElementTablePtr) dtd->elements;
2934 
2935     uqname = xmlSplitQName2(name, &prefix);
2936     if (uqname != NULL)
2937         name = uqname;
2938     cur = xmlHashLookup2(table, name, prefix);
2939     if (prefix != NULL) xmlFree(prefix);
2940     if (uqname != NULL) xmlFree(uqname);
2941     return(cur);
2942 }
2943 
2944 /**
2945  * xmlGetDtdElementDesc2:
2946  * @dtd:  a pointer to the DtD to search
2947  * @name:  the element name
2948  * @create:  create an empty description if not found
2949  *
2950  * Search the DTD for the description of this element
2951  *
2952  * returns the xmlElementPtr if found or NULL
2953  */
2954 
2955 static xmlElementPtr
xmlGetDtdElementDesc2(xmlValidCtxtPtr ctxt,xmlDtdPtr dtd,const xmlChar * name)2956 xmlGetDtdElementDesc2(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name) {
2957     xmlElementTablePtr table;
2958     xmlElementPtr cur = NULL;
2959     const xmlChar *localName;
2960     xmlChar *prefix = NULL;
2961 
2962     if (dtd == NULL) return(NULL);
2963 
2964     /*
2965      * Create the Element table if needed.
2966      */
2967     if (dtd->elements == NULL) {
2968 	xmlDictPtr dict = NULL;
2969 
2970 	if (dtd->doc != NULL)
2971 	    dict = dtd->doc->dict;
2972 
2973 	dtd->elements = xmlHashCreateDict(0, dict);
2974 	if (dtd->elements == NULL)
2975             goto mem_error;
2976     }
2977     table = (xmlElementTablePtr) dtd->elements;
2978 
2979     localName = xmlSplitQName4(name, &prefix);
2980     if (localName == NULL)
2981         goto mem_error;
2982     cur = xmlHashLookup2(table, localName, prefix);
2983     if (cur == NULL) {
2984 	cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2985 	if (cur == NULL)
2986             goto mem_error;
2987 	memset(cur, 0, sizeof(xmlElement));
2988 	cur->type = XML_ELEMENT_DECL;
2989 
2990 	/*
2991 	 * fill the structure.
2992 	 */
2993 	cur->name = xmlStrdup(localName);
2994         if (cur->name == NULL)
2995             goto mem_error;
2996 	cur->prefix = prefix;
2997         prefix = NULL;
2998 	cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2999 
3000 	if (xmlHashAdd2(table, localName, cur->prefix, cur) <= 0)
3001             goto mem_error;
3002     }
3003 
3004     if (prefix != NULL)
3005         xmlFree(prefix);
3006     return(cur);
3007 
3008 mem_error:
3009     xmlVErrMemory(ctxt);
3010     xmlFree(prefix);
3011     xmlFreeElement(cur);
3012     return(NULL);
3013 }
3014 
3015 /**
3016  * xmlGetDtdQElementDesc:
3017  * @dtd:  a pointer to the DtD to search
3018  * @name:  the element name
3019  * @prefix:  the element namespace prefix
3020  *
3021  * Search the DTD for the description of this element
3022  *
3023  * returns the xmlElementPtr if found or NULL
3024  */
3025 
3026 xmlElementPtr
xmlGetDtdQElementDesc(xmlDtdPtr dtd,const xmlChar * name,const xmlChar * prefix)3027 xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
3028 	              const xmlChar *prefix) {
3029     xmlElementTablePtr table;
3030 
3031     if (dtd == NULL) return(NULL);
3032     if (dtd->elements == NULL) return(NULL);
3033     table = (xmlElementTablePtr) dtd->elements;
3034 
3035     return(xmlHashLookup2(table, name, prefix));
3036 }
3037 
3038 /**
3039  * xmlGetDtdAttrDesc:
3040  * @dtd:  a pointer to the DtD to search
3041  * @elem:  the element name
3042  * @name:  the attribute name
3043  *
3044  * Search the DTD for the description of this attribute on
3045  * this element.
3046  *
3047  * returns the xmlAttributePtr if found or NULL
3048  */
3049 
3050 xmlAttributePtr
xmlGetDtdAttrDesc(xmlDtdPtr dtd,const xmlChar * elem,const xmlChar * name)3051 xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
3052     xmlAttributeTablePtr table;
3053     xmlAttributePtr cur;
3054     xmlChar *uqname = NULL, *prefix = NULL;
3055 
3056     if (dtd == NULL) return(NULL);
3057     if (dtd->attributes == NULL) return(NULL);
3058 
3059     table = (xmlAttributeTablePtr) dtd->attributes;
3060     if (table == NULL)
3061 	return(NULL);
3062 
3063     uqname = xmlSplitQName2(name, &prefix);
3064 
3065     if (uqname != NULL) {
3066 	cur = xmlHashLookup3(table, uqname, prefix, elem);
3067 	if (prefix != NULL) xmlFree(prefix);
3068 	if (uqname != NULL) xmlFree(uqname);
3069     } else
3070 	cur = xmlHashLookup3(table, name, NULL, elem);
3071     return(cur);
3072 }
3073 
3074 /**
3075  * xmlGetDtdQAttrDesc:
3076  * @dtd:  a pointer to the DtD to search
3077  * @elem:  the element name
3078  * @name:  the attribute name
3079  * @prefix:  the attribute namespace prefix
3080  *
3081  * Search the DTD for the description of this qualified attribute on
3082  * this element.
3083  *
3084  * returns the xmlAttributePtr if found or NULL
3085  */
3086 
3087 xmlAttributePtr
xmlGetDtdQAttrDesc(xmlDtdPtr dtd,const xmlChar * elem,const xmlChar * name,const xmlChar * prefix)3088 xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
3089 	          const xmlChar *prefix) {
3090     xmlAttributeTablePtr table;
3091 
3092     if (dtd == NULL) return(NULL);
3093     if (dtd->attributes == NULL) return(NULL);
3094     table = (xmlAttributeTablePtr) dtd->attributes;
3095 
3096     return(xmlHashLookup3(table, name, prefix, elem));
3097 }
3098 
3099 /**
3100  * xmlGetDtdNotationDesc:
3101  * @dtd:  a pointer to the DtD to search
3102  * @name:  the notation name
3103  *
3104  * Search the DTD for the description of this notation
3105  *
3106  * returns the xmlNotationPtr if found or NULL
3107  */
3108 
3109 xmlNotationPtr
xmlGetDtdNotationDesc(xmlDtdPtr dtd,const xmlChar * name)3110 xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
3111     xmlNotationTablePtr table;
3112 
3113     if (dtd == NULL) return(NULL);
3114     if (dtd->notations == NULL) return(NULL);
3115     table = (xmlNotationTablePtr) dtd->notations;
3116 
3117     return(xmlHashLookup(table, name));
3118 }
3119 
3120 #if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
3121 /**
3122  * xmlValidateNotationUse:
3123  * @ctxt:  the validation context
3124  * @doc:  the document
3125  * @notationName:  the notation name to check
3126  *
3127  * Validate that the given name match a notation declaration.
3128  * - [ VC: Notation Declared ]
3129  *
3130  * returns 1 if valid or 0 otherwise
3131  */
3132 
3133 int
xmlValidateNotationUse(xmlValidCtxtPtr ctxt,xmlDocPtr doc,const xmlChar * notationName)3134 xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3135                        const xmlChar *notationName) {
3136     xmlNotationPtr notaDecl;
3137     if ((doc == NULL) || (doc->intSubset == NULL) ||
3138         (notationName == NULL)) return(-1);
3139 
3140     notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
3141     if ((notaDecl == NULL) && (doc->extSubset != NULL))
3142 	notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
3143 
3144     if ((notaDecl == NULL) && (ctxt != NULL)) {
3145 	xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION,
3146 	                "NOTATION %s is not declared\n",
3147 		        notationName, NULL, NULL);
3148 	return(0);
3149     }
3150     return(1);
3151 }
3152 #endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */
3153 
3154 /**
3155  * xmlIsMixedElement:
3156  * @doc:  the document
3157  * @name:  the element name
3158  *
3159  * Search in the DtDs whether an element accept Mixed content (or ANY)
3160  * basically if it is supposed to accept text childs
3161  *
3162  * returns 0 if no, 1 if yes, and -1 if no element description is available
3163  */
3164 
3165 int
xmlIsMixedElement(xmlDocPtr doc,const xmlChar * name)3166 xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
3167     xmlElementPtr elemDecl;
3168 
3169     if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3170 
3171     elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3172     if ((elemDecl == NULL) && (doc->extSubset != NULL))
3173 	elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3174     if (elemDecl == NULL) return(-1);
3175     switch (elemDecl->etype) {
3176 	case XML_ELEMENT_TYPE_UNDEFINED:
3177 	    return(-1);
3178 	case XML_ELEMENT_TYPE_ELEMENT:
3179 	    return(0);
3180         case XML_ELEMENT_TYPE_EMPTY:
3181 	    /*
3182 	     * return 1 for EMPTY since we want VC error to pop up
3183 	     * on <empty>     </empty> for example
3184 	     */
3185 	case XML_ELEMENT_TYPE_ANY:
3186 	case XML_ELEMENT_TYPE_MIXED:
3187 	    return(1);
3188     }
3189     return(1);
3190 }
3191 
3192 #ifdef LIBXML_VALID_ENABLED
3193 
3194 /**
3195  * xmlValidNormalizeString:
3196  * @str: a string
3197  *
3198  * Normalize a string in-place.
3199  */
3200 static void
xmlValidNormalizeString(xmlChar * str)3201 xmlValidNormalizeString(xmlChar *str) {
3202     xmlChar *dst;
3203     const xmlChar *src;
3204 
3205     if (str == NULL)
3206         return;
3207     src = str;
3208     dst = str;
3209 
3210     while (*src == 0x20) src++;
3211     while (*src != 0) {
3212 	if (*src == 0x20) {
3213 	    while (*src == 0x20) src++;
3214 	    if (*src != 0)
3215 		*dst++ = 0x20;
3216 	} else {
3217 	    *dst++ = *src++;
3218 	}
3219     }
3220     *dst = 0;
3221 }
3222 
3223 /**
3224  * xmlCtxtGetDtdElementDesc:
3225  * @ctxt:  validation context
3226  * @dtd:  a pointer to the DtD to search
3227  * @name:  the element name
3228  *
3229  * Search the DTD for the description of this element
3230  *
3231  * returns the xmlElementPtr if found or NULL
3232  */
3233 
3234 static xmlElementPtr
xmlCtxtGetDtdElementDesc(xmlValidCtxtPtr ctxt,xmlDtdPtr dtd,const xmlChar * name)3235 xmlCtxtGetDtdElementDesc(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd,
3236                          const xmlChar *name) {
3237     xmlElementTablePtr table;
3238     xmlElementPtr cur;
3239     const xmlChar *localName;
3240     xmlChar *prefix;
3241 
3242     if ((dtd == NULL) || (name == NULL)) return(NULL);
3243     if (dtd->elements == NULL)
3244 	return(NULL);
3245     table = (xmlElementTablePtr) dtd->elements;
3246 
3247     localName = xmlSplitQName4(name, &prefix);
3248     if (localName == NULL) {
3249         xmlVErrMemory(ctxt);
3250         return(NULL);
3251     }
3252     cur = xmlHashLookup2(table, localName, prefix);
3253     if (prefix != NULL)
3254         xmlFree(prefix);
3255     return(cur);
3256 }
3257 
3258 static int
xmlIsDocNameStartChar(xmlDocPtr doc,int c)3259 xmlIsDocNameStartChar(xmlDocPtr doc, int c) {
3260     if ((doc == NULL) || (doc->properties & XML_DOC_OLD10) == 0) {
3261         /*
3262 	 * Use the new checks of production [4] [4a] amd [5] of the
3263 	 * Update 5 of XML-1.0
3264 	 */
3265 	if (((c >= 'a') && (c <= 'z')) ||
3266 	    ((c >= 'A') && (c <= 'Z')) ||
3267 	    (c == '_') || (c == ':') ||
3268 	    ((c >= 0xC0) && (c <= 0xD6)) ||
3269 	    ((c >= 0xD8) && (c <= 0xF6)) ||
3270 	    ((c >= 0xF8) && (c <= 0x2FF)) ||
3271 	    ((c >= 0x370) && (c <= 0x37D)) ||
3272 	    ((c >= 0x37F) && (c <= 0x1FFF)) ||
3273 	    ((c >= 0x200C) && (c <= 0x200D)) ||
3274 	    ((c >= 0x2070) && (c <= 0x218F)) ||
3275 	    ((c >= 0x2C00) && (c <= 0x2FEF)) ||
3276 	    ((c >= 0x3001) && (c <= 0xD7FF)) ||
3277 	    ((c >= 0xF900) && (c <= 0xFDCF)) ||
3278 	    ((c >= 0xFDF0) && (c <= 0xFFFD)) ||
3279 	    ((c >= 0x10000) && (c <= 0xEFFFF)))
3280 	    return(1);
3281     } else {
3282         if (IS_LETTER(c) || (c == '_') || (c == ':'))
3283 	    return(1);
3284     }
3285     return(0);
3286 }
3287 
3288 static int
xmlIsDocNameChar(xmlDocPtr doc,int c)3289 xmlIsDocNameChar(xmlDocPtr doc, int c) {
3290     if ((doc == NULL) || (doc->properties & XML_DOC_OLD10) == 0) {
3291         /*
3292 	 * Use the new checks of production [4] [4a] amd [5] of the
3293 	 * Update 5 of XML-1.0
3294 	 */
3295 	if (((c >= 'a') && (c <= 'z')) ||
3296 	    ((c >= 'A') && (c <= 'Z')) ||
3297 	    ((c >= '0') && (c <= '9')) || /* !start */
3298 	    (c == '_') || (c == ':') ||
3299 	    (c == '-') || (c == '.') || (c == 0xB7) || /* !start */
3300 	    ((c >= 0xC0) && (c <= 0xD6)) ||
3301 	    ((c >= 0xD8) && (c <= 0xF6)) ||
3302 	    ((c >= 0xF8) && (c <= 0x2FF)) ||
3303 	    ((c >= 0x300) && (c <= 0x36F)) || /* !start */
3304 	    ((c >= 0x370) && (c <= 0x37D)) ||
3305 	    ((c >= 0x37F) && (c <= 0x1FFF)) ||
3306 	    ((c >= 0x200C) && (c <= 0x200D)) ||
3307 	    ((c >= 0x203F) && (c <= 0x2040)) || /* !start */
3308 	    ((c >= 0x2070) && (c <= 0x218F)) ||
3309 	    ((c >= 0x2C00) && (c <= 0x2FEF)) ||
3310 	    ((c >= 0x3001) && (c <= 0xD7FF)) ||
3311 	    ((c >= 0xF900) && (c <= 0xFDCF)) ||
3312 	    ((c >= 0xFDF0) && (c <= 0xFFFD)) ||
3313 	    ((c >= 0x10000) && (c <= 0xEFFFF)))
3314 	     return(1);
3315     } else {
3316         if ((IS_LETTER(c)) || (IS_DIGIT(c)) ||
3317             (c == '.') || (c == '-') ||
3318 	    (c == '_') || (c == ':') ||
3319 	    (IS_COMBINING(c)) ||
3320 	    (IS_EXTENDER(c)))
3321 	    return(1);
3322     }
3323     return(0);
3324 }
3325 
3326 /**
3327  * xmlValidateNameValue:
3328  * @doc:  pointer to the document or NULL
3329  * @value:  an Name value
3330  *
3331  * Validate that the given value match Name production
3332  *
3333  * returns 1 if valid or 0 otherwise
3334  */
3335 
3336 static int
xmlValidateNameValueInternal(xmlDocPtr doc,const xmlChar * value)3337 xmlValidateNameValueInternal(xmlDocPtr doc, const xmlChar *value) {
3338     const xmlChar *cur;
3339     int val, len;
3340 
3341     if (value == NULL) return(0);
3342     cur = value;
3343     val = xmlStringCurrentChar(NULL, cur, &len);
3344     cur += len;
3345     if (!xmlIsDocNameStartChar(doc, val))
3346 	return(0);
3347 
3348     val = xmlStringCurrentChar(NULL, cur, &len);
3349     cur += len;
3350     while (xmlIsDocNameChar(doc, val)) {
3351 	val = xmlStringCurrentChar(NULL, cur, &len);
3352 	cur += len;
3353     }
3354 
3355     if (val != 0) return(0);
3356 
3357     return(1);
3358 }
3359 
3360 /**
3361  * xmlValidateNameValue:
3362  * @value:  an Name value
3363  *
3364  * Validate that the given value match Name production
3365  *
3366  * returns 1 if valid or 0 otherwise
3367  */
3368 
3369 int
xmlValidateNameValue(const xmlChar * value)3370 xmlValidateNameValue(const xmlChar *value) {
3371     return(xmlValidateNameValueInternal(NULL, value));
3372 }
3373 
3374 /**
3375  * xmlValidateNamesValueInternal:
3376  * @doc:  pointer to the document or NULL
3377  * @value:  an Names value
3378  *
3379  * Validate that the given value match Names production
3380  *
3381  * returns 1 if valid or 0 otherwise
3382  */
3383 
3384 static int
xmlValidateNamesValueInternal(xmlDocPtr doc,const xmlChar * value)3385 xmlValidateNamesValueInternal(xmlDocPtr doc, const xmlChar *value) {
3386     const xmlChar *cur;
3387     int val, len;
3388 
3389     if (value == NULL) return(0);
3390     cur = value;
3391     val = xmlStringCurrentChar(NULL, cur, &len);
3392     cur += len;
3393 
3394     if (!xmlIsDocNameStartChar(doc, val))
3395 	return(0);
3396 
3397     val = xmlStringCurrentChar(NULL, cur, &len);
3398     cur += len;
3399     while (xmlIsDocNameChar(doc, val)) {
3400 	val = xmlStringCurrentChar(NULL, cur, &len);
3401 	cur += len;
3402     }
3403 
3404     /* Should not test IS_BLANK(val) here -- see erratum E20*/
3405     while (val == 0x20) {
3406 	while (val == 0x20) {
3407 	    val = xmlStringCurrentChar(NULL, cur, &len);
3408 	    cur += len;
3409 	}
3410 
3411 	if (!xmlIsDocNameStartChar(doc, val))
3412 	    return(0);
3413 
3414 	val = xmlStringCurrentChar(NULL, cur, &len);
3415 	cur += len;
3416 
3417 	while (xmlIsDocNameChar(doc, val)) {
3418 	    val = xmlStringCurrentChar(NULL, cur, &len);
3419 	    cur += len;
3420 	}
3421     }
3422 
3423     if (val != 0) return(0);
3424 
3425     return(1);
3426 }
3427 
3428 /**
3429  * xmlValidateNamesValue:
3430  * @value:  an Names value
3431  *
3432  * Validate that the given value match Names production
3433  *
3434  * returns 1 if valid or 0 otherwise
3435  */
3436 
3437 int
xmlValidateNamesValue(const xmlChar * value)3438 xmlValidateNamesValue(const xmlChar *value) {
3439     return(xmlValidateNamesValueInternal(NULL, value));
3440 }
3441 
3442 /**
3443  * xmlValidateNmtokenValueInternal:
3444  * @doc:  pointer to the document or NULL
3445  * @value:  an Nmtoken value
3446  *
3447  * Validate that the given value match Nmtoken production
3448  *
3449  * [ VC: Name Token ]
3450  *
3451  * returns 1 if valid or 0 otherwise
3452  */
3453 
3454 static int
xmlValidateNmtokenValueInternal(xmlDocPtr doc,const xmlChar * value)3455 xmlValidateNmtokenValueInternal(xmlDocPtr doc, const xmlChar *value) {
3456     const xmlChar *cur;
3457     int val, len;
3458 
3459     if (value == NULL) return(0);
3460     cur = value;
3461     val = xmlStringCurrentChar(NULL, cur, &len);
3462     cur += len;
3463 
3464     if (!xmlIsDocNameChar(doc, val))
3465 	return(0);
3466 
3467     val = xmlStringCurrentChar(NULL, cur, &len);
3468     cur += len;
3469     while (xmlIsDocNameChar(doc, val)) {
3470 	val = xmlStringCurrentChar(NULL, cur, &len);
3471 	cur += len;
3472     }
3473 
3474     if (val != 0) return(0);
3475 
3476     return(1);
3477 }
3478 
3479 /**
3480  * xmlValidateNmtokenValue:
3481  * @value:  an Nmtoken value
3482  *
3483  * Validate that the given value match Nmtoken production
3484  *
3485  * [ VC: Name Token ]
3486  *
3487  * returns 1 if valid or 0 otherwise
3488  */
3489 
3490 int
xmlValidateNmtokenValue(const xmlChar * value)3491 xmlValidateNmtokenValue(const xmlChar *value) {
3492     return(xmlValidateNmtokenValueInternal(NULL, value));
3493 }
3494 
3495 /**
3496  * xmlValidateNmtokensValueInternal:
3497  * @doc:  pointer to the document or NULL
3498  * @value:  an Nmtokens value
3499  *
3500  * Validate that the given value match Nmtokens production
3501  *
3502  * [ VC: Name Token ]
3503  *
3504  * returns 1 if valid or 0 otherwise
3505  */
3506 
3507 static int
xmlValidateNmtokensValueInternal(xmlDocPtr doc,const xmlChar * value)3508 xmlValidateNmtokensValueInternal(xmlDocPtr doc, const xmlChar *value) {
3509     const xmlChar *cur;
3510     int val, len;
3511 
3512     if (value == NULL) return(0);
3513     cur = value;
3514     val = xmlStringCurrentChar(NULL, cur, &len);
3515     cur += len;
3516 
3517     while (IS_BLANK(val)) {
3518 	val = xmlStringCurrentChar(NULL, cur, &len);
3519 	cur += len;
3520     }
3521 
3522     if (!xmlIsDocNameChar(doc, val))
3523 	return(0);
3524 
3525     while (xmlIsDocNameChar(doc, val)) {
3526 	val = xmlStringCurrentChar(NULL, cur, &len);
3527 	cur += len;
3528     }
3529 
3530     /* Should not test IS_BLANK(val) here -- see erratum E20*/
3531     while (val == 0x20) {
3532 	while (val == 0x20) {
3533 	    val = xmlStringCurrentChar(NULL, cur, &len);
3534 	    cur += len;
3535 	}
3536 	if (val == 0) return(1);
3537 
3538 	if (!xmlIsDocNameChar(doc, val))
3539 	    return(0);
3540 
3541 	val = xmlStringCurrentChar(NULL, cur, &len);
3542 	cur += len;
3543 
3544 	while (xmlIsDocNameChar(doc, val)) {
3545 	    val = xmlStringCurrentChar(NULL, cur, &len);
3546 	    cur += len;
3547 	}
3548     }
3549 
3550     if (val != 0) return(0);
3551 
3552     return(1);
3553 }
3554 
3555 /**
3556  * xmlValidateNmtokensValue:
3557  * @value:  an Nmtokens value
3558  *
3559  * Validate that the given value match Nmtokens production
3560  *
3561  * [ VC: Name Token ]
3562  *
3563  * returns 1 if valid or 0 otherwise
3564  */
3565 
3566 int
xmlValidateNmtokensValue(const xmlChar * value)3567 xmlValidateNmtokensValue(const xmlChar *value) {
3568     return(xmlValidateNmtokensValueInternal(NULL, value));
3569 }
3570 
3571 /**
3572  * xmlValidateNotationDecl:
3573  * @ctxt:  the validation context
3574  * @doc:  a document instance
3575  * @nota:  a notation definition
3576  *
3577  * Try to validate a single notation definition
3578  * basically it does the following checks as described by the
3579  * XML-1.0 recommendation:
3580  *  - it seems that no validity constraint exists on notation declarations
3581  * But this function get called anyway ...
3582  *
3583  * returns 1 if valid or 0 otherwise
3584  */
3585 
3586 int
xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,xmlDocPtr doc ATTRIBUTE_UNUSED,xmlNotationPtr nota ATTRIBUTE_UNUSED)3587 xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3588                          xmlNotationPtr nota ATTRIBUTE_UNUSED) {
3589     int ret = 1;
3590 
3591     return(ret);
3592 }
3593 
3594 /**
3595  * xmlValidateAttributeValueInternal:
3596  * @doc: the document
3597  * @type:  an attribute type
3598  * @value:  an attribute value
3599  *
3600  * Validate that the given attribute value match  the proper production
3601  *
3602  * returns 1 if valid or 0 otherwise
3603  */
3604 
3605 static int
xmlValidateAttributeValueInternal(xmlDocPtr doc,xmlAttributeType type,const xmlChar * value)3606 xmlValidateAttributeValueInternal(xmlDocPtr doc, xmlAttributeType type,
3607                                   const xmlChar *value) {
3608     switch (type) {
3609 	case XML_ATTRIBUTE_ENTITIES:
3610 	case XML_ATTRIBUTE_IDREFS:
3611 	    return(xmlValidateNamesValueInternal(doc, value));
3612 	case XML_ATTRIBUTE_ENTITY:
3613 	case XML_ATTRIBUTE_IDREF:
3614 	case XML_ATTRIBUTE_ID:
3615 	case XML_ATTRIBUTE_NOTATION:
3616 	    return(xmlValidateNameValueInternal(doc, value));
3617 	case XML_ATTRIBUTE_NMTOKENS:
3618 	case XML_ATTRIBUTE_ENUMERATION:
3619 	    return(xmlValidateNmtokensValueInternal(doc, value));
3620 	case XML_ATTRIBUTE_NMTOKEN:
3621 	    return(xmlValidateNmtokenValueInternal(doc, value));
3622         case XML_ATTRIBUTE_CDATA:
3623 	    break;
3624     }
3625     return(1);
3626 }
3627 
3628 /**
3629  * xmlValidateAttributeValue:
3630  * @type:  an attribute type
3631  * @value:  an attribute value
3632  *
3633  * Validate that the given attribute value match  the proper production
3634  *
3635  * [ VC: ID ]
3636  * Values of type ID must match the Name production....
3637  *
3638  * [ VC: IDREF ]
3639  * Values of type IDREF must match the Name production, and values
3640  * of type IDREFS must match Names ...
3641  *
3642  * [ VC: Entity Name ]
3643  * Values of type ENTITY must match the Name production, values
3644  * of type ENTITIES must match Names ...
3645  *
3646  * [ VC: Name Token ]
3647  * Values of type NMTOKEN must match the Nmtoken production; values
3648  * of type NMTOKENS must match Nmtokens.
3649  *
3650  * returns 1 if valid or 0 otherwise
3651  */
3652 int
xmlValidateAttributeValue(xmlAttributeType type,const xmlChar * value)3653 xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3654     return(xmlValidateAttributeValueInternal(NULL, type, value));
3655 }
3656 
3657 /**
3658  * xmlValidateAttributeValue2:
3659  * @ctxt:  the validation context
3660  * @doc:  the document
3661  * @name:  the attribute name (used for error reporting only)
3662  * @type:  the attribute type
3663  * @value:  the attribute value
3664  *
3665  * Validate that the given attribute value match a given type.
3666  * This typically cannot be done before having finished parsing
3667  * the subsets.
3668  *
3669  * [ VC: IDREF ]
3670  * Values of type IDREF must match one of the declared IDs
3671  * Values of type IDREFS must match a sequence of the declared IDs
3672  * each Name must match the value of an ID attribute on some element
3673  * in the XML document; i.e. IDREF values must match the value of
3674  * some ID attribute
3675  *
3676  * [ VC: Entity Name ]
3677  * Values of type ENTITY must match one declared entity
3678  * Values of type ENTITIES must match a sequence of declared entities
3679  *
3680  * [ VC: Notation Attributes ]
3681  * all notation names in the declaration must be declared.
3682  *
3683  * returns 1 if valid or 0 otherwise
3684  */
3685 
3686 static int
xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt,xmlDocPtr doc,const xmlChar * name,xmlAttributeType type,const xmlChar * value)3687 xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3688       const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3689     int ret = 1;
3690     switch (type) {
3691 	case XML_ATTRIBUTE_IDREFS:
3692 	case XML_ATTRIBUTE_IDREF:
3693 	case XML_ATTRIBUTE_ID:
3694 	case XML_ATTRIBUTE_NMTOKENS:
3695 	case XML_ATTRIBUTE_ENUMERATION:
3696 	case XML_ATTRIBUTE_NMTOKEN:
3697         case XML_ATTRIBUTE_CDATA:
3698 	    break;
3699 	case XML_ATTRIBUTE_ENTITY: {
3700 	    xmlEntityPtr ent;
3701 
3702 	    ent = xmlGetDocEntity(doc, value);
3703 	    /* yeah it's a bit messy... */
3704 	    if ((ent == NULL) && (doc->standalone == 1)) {
3705 		doc->standalone = 0;
3706 		ent = xmlGetDocEntity(doc, value);
3707 	    }
3708 	    if (ent == NULL) {
3709 		xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3710 				XML_DTD_UNKNOWN_ENTITY,
3711    "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3712 		       name, value, NULL);
3713 		ret = 0;
3714 	    } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3715 		xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3716 				XML_DTD_ENTITY_TYPE,
3717    "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3718 		       name, value, NULL);
3719 		ret = 0;
3720 	    }
3721 	    break;
3722         }
3723 	case XML_ATTRIBUTE_ENTITIES: {
3724 	    xmlChar *dup, *nam = NULL, *cur, save;
3725 	    xmlEntityPtr ent;
3726 
3727 	    dup = xmlStrdup(value);
3728 	    if (dup == NULL) {
3729                 xmlVErrMemory(ctxt);
3730 		return(0);
3731             }
3732 	    cur = dup;
3733 	    while (*cur != 0) {
3734 		nam = cur;
3735 		while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
3736 		save = *cur;
3737 		*cur = 0;
3738 		ent = xmlGetDocEntity(doc, nam);
3739 		if (ent == NULL) {
3740 		    xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3741 				    XML_DTD_UNKNOWN_ENTITY,
3742        "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3743 			   name, nam, NULL);
3744 		    ret = 0;
3745 		} else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3746 		    xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3747 				    XML_DTD_ENTITY_TYPE,
3748        "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3749 			   name, nam, NULL);
3750 		    ret = 0;
3751 		}
3752 		if (save == 0)
3753 		    break;
3754 		*cur = save;
3755 		while (IS_BLANK_CH(*cur)) cur++;
3756 	    }
3757 	    xmlFree(dup);
3758 	    break;
3759 	}
3760 	case XML_ATTRIBUTE_NOTATION: {
3761 	    xmlNotationPtr nota;
3762 
3763 	    nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3764 	    if ((nota == NULL) && (doc->extSubset != NULL))
3765 		nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3766 
3767 	    if (nota == NULL) {
3768 		xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3769 		                XML_DTD_UNKNOWN_NOTATION,
3770        "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3771 		       name, value, NULL);
3772 		ret = 0;
3773 	    }
3774 	    break;
3775         }
3776     }
3777     return(ret);
3778 }
3779 
3780 /**
3781  * xmlValidCtxtNormalizeAttributeValue:
3782  * @ctxt: the validation context
3783  * @doc:  the document
3784  * @elem:  the parent
3785  * @name:  the attribute name
3786  * @value:  the attribute value
3787  * @ctxt:  the validation context or NULL
3788  *
3789  * Does the validation related extra step of the normalization of attribute
3790  * values:
3791  *
3792  * If the declared value is not CDATA, then the XML processor must further
3793  * process the normalized attribute value by discarding any leading and
3794  * trailing space (#x20) characters, and by replacing sequences of space
3795  * (#x20) characters by single space (#x20) character.
3796  *
3797  * Also  check VC: Standalone Document Declaration in P32, and update
3798  *  ctxt->valid accordingly
3799  *
3800  * returns a new normalized string if normalization is needed, NULL otherwise
3801  *      the caller must free the returned value.
3802  */
3803 
3804 xmlChar *
xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlNodePtr elem,const xmlChar * name,const xmlChar * value)3805 xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3806 	     xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3807     xmlChar *ret;
3808     xmlAttributePtr attrDecl = NULL;
3809     const xmlChar *localName;
3810     xmlChar *prefix = NULL;
3811     int extsubset = 0;
3812 
3813     if (doc == NULL) return(NULL);
3814     if (elem == NULL) return(NULL);
3815     if (name == NULL) return(NULL);
3816     if (value == NULL) return(NULL);
3817 
3818     localName = xmlSplitQName4(name, &prefix);
3819     if (localName == NULL)
3820         goto mem_error;
3821 
3822     if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3823 	xmlChar buf[50];
3824 	xmlChar *elemname;
3825 
3826 	elemname = xmlBuildQName(elem->name, elem->ns->prefix, buf, 50);
3827 	if (elemname == NULL)
3828 	    goto mem_error;
3829         if (doc->intSubset != NULL)
3830             attrDecl = xmlHashLookup3(doc->intSubset->attributes, localName,
3831                                       prefix, elemname);
3832 	if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3833 	    attrDecl = xmlHashLookup3(doc->extSubset->attributes, localName,
3834                                       prefix, elemname);
3835 	    if (attrDecl != NULL)
3836 		extsubset = 1;
3837 	}
3838 	if ((elemname != buf) && (elemname != elem->name))
3839 	    xmlFree(elemname);
3840     }
3841     if ((attrDecl == NULL) && (doc->intSubset != NULL))
3842 	attrDecl = xmlHashLookup3(doc->intSubset->attributes, localName,
3843                                   prefix, elem->name);
3844     if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3845 	attrDecl = xmlHashLookup3(doc->extSubset->attributes, localName,
3846                                   prefix, elem->name);
3847 	if (attrDecl != NULL)
3848 	    extsubset = 1;
3849     }
3850 
3851     if (attrDecl == NULL)
3852 	goto done;
3853     if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3854 	goto done;
3855 
3856     ret = xmlStrdup(value);
3857     if (ret == NULL)
3858 	goto mem_error;
3859     xmlValidNormalizeString(ret);
3860     if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3861 	xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
3862 "standalone: %s on %s value had to be normalized based on external subset declaration\n",
3863 	       name, elem->name, NULL);
3864 	ctxt->valid = 0;
3865     }
3866 
3867     xmlFree(prefix);
3868     return(ret);
3869 
3870 mem_error:
3871     xmlVErrMemory(ctxt);
3872 
3873 done:
3874     xmlFree(prefix);
3875     return(NULL);
3876 }
3877 
3878 /**
3879  * xmlValidNormalizeAttributeValue:
3880  * @doc:  the document
3881  * @elem:  the parent
3882  * @name:  the attribute name
3883  * @value:  the attribute value
3884  *
3885  * Does the validation related extra step of the normalization of attribute
3886  * values:
3887  *
3888  * If the declared value is not CDATA, then the XML processor must further
3889  * process the normalized attribute value by discarding any leading and
3890  * trailing space (#x20) characters, and by replacing sequences of space
3891  * (#x20) characters by single space (#x20) character.
3892  *
3893  * Returns a new normalized string if normalization is needed, NULL otherwise
3894  *      the caller must free the returned value.
3895  */
3896 
3897 xmlChar *
xmlValidNormalizeAttributeValue(xmlDocPtr doc,xmlNodePtr elem,const xmlChar * name,const xmlChar * value)3898 xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3899 			        const xmlChar *name, const xmlChar *value) {
3900     xmlChar *ret;
3901     xmlAttributePtr attrDecl = NULL;
3902 
3903     if (doc == NULL) return(NULL);
3904     if (elem == NULL) return(NULL);
3905     if (name == NULL) return(NULL);
3906     if (value == NULL) return(NULL);
3907 
3908     if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3909 	xmlChar fn[50];
3910 	xmlChar *fullname;
3911 
3912 	fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3913 	if (fullname == NULL)
3914 	    return(NULL);
3915 	if ((fullname != fn) && (fullname != elem->name))
3916 	    xmlFree(fullname);
3917     }
3918     attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3919     if ((attrDecl == NULL) && (doc->extSubset != NULL))
3920 	attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3921 
3922     if (attrDecl == NULL)
3923 	return(NULL);
3924     if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3925 	return(NULL);
3926 
3927     ret = xmlStrdup(value);
3928     if (ret == NULL)
3929 	return(NULL);
3930     xmlValidNormalizeString(ret);
3931     return(ret);
3932 }
3933 
3934 static void
xmlValidateAttributeIdCallback(void * payload,void * data,const xmlChar * name ATTRIBUTE_UNUSED)3935 xmlValidateAttributeIdCallback(void *payload, void *data,
3936 	                       const xmlChar *name ATTRIBUTE_UNUSED) {
3937     xmlAttributePtr attr = (xmlAttributePtr) payload;
3938     int *count = (int *) data;
3939     if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3940 }
3941 
3942 /**
3943  * xmlValidateAttributeDecl:
3944  * @ctxt:  the validation context
3945  * @doc:  a document instance
3946  * @attr:  an attribute definition
3947  *
3948  * Try to validate a single attribute definition
3949  * basically it does the following checks as described by the
3950  * XML-1.0 recommendation:
3951  *  - [ VC: Attribute Default Legal ]
3952  *  - [ VC: Enumeration ]
3953  *  - [ VC: ID Attribute Default ]
3954  *
3955  * The ID/IDREF uniqueness and matching are done separately
3956  *
3957  * returns 1 if valid or 0 otherwise
3958  */
3959 
3960 int
xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlAttributePtr attr)3961 xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3962                          xmlAttributePtr attr) {
3963     int ret = 1;
3964     int val;
3965     CHECK_DTD;
3966     if(attr == NULL) return(1);
3967 
3968     /* Attribute Default Legal */
3969     /* Enumeration */
3970     if (attr->defaultValue != NULL) {
3971 	val = xmlValidateAttributeValueInternal(doc, attr->atype,
3972 	                                        attr->defaultValue);
3973 	if (val == 0) {
3974 	    xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_DEFAULT,
3975 	       "Syntax of default value for attribute %s of %s is not valid\n",
3976 	           attr->name, attr->elem, NULL);
3977 	}
3978         ret &= val;
3979     }
3980 
3981     /* ID Attribute Default */
3982     if ((attr->atype == XML_ATTRIBUTE_ID)&&
3983         (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3984 	(attr->def != XML_ATTRIBUTE_REQUIRED)) {
3985 	xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_FIXED,
3986           "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
3987 	       attr->name, attr->elem, NULL);
3988 	ret = 0;
3989     }
3990 
3991     /* One ID per Element Type */
3992     if (attr->atype == XML_ATTRIBUTE_ID) {
3993         int nbId;
3994 
3995 	/* the trick is that we parse DtD as their own internal subset */
3996         xmlElementPtr elem = xmlCtxtGetDtdElementDesc(ctxt, doc->intSubset,
3997 	                                              attr->elem);
3998 	if (elem != NULL) {
3999 	    nbId = xmlScanIDAttributeDecl(ctxt, elem, 0);
4000 	} else {
4001 	    xmlAttributeTablePtr table;
4002 
4003 	    /*
4004 	     * The attribute may be declared in the internal subset and the
4005 	     * element in the external subset.
4006 	     */
4007 	    nbId = 0;
4008 	    if (doc->intSubset != NULL) {
4009 		table = (xmlAttributeTablePtr) doc->intSubset->attributes;
4010 		xmlHashScan3(table, NULL, NULL, attr->elem,
4011 			     xmlValidateAttributeIdCallback, &nbId);
4012 	    }
4013 	}
4014 	if (nbId > 1) {
4015 
4016 	    xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
4017        "Element %s has %d ID attribute defined in the internal subset : %s\n",
4018 		   attr->elem, nbId, attr->name);
4019 	} else if (doc->extSubset != NULL) {
4020 	    int extId = 0;
4021 	    elem = xmlCtxtGetDtdElementDesc(ctxt, doc->extSubset, attr->elem);
4022 	    if (elem != NULL) {
4023 		extId = xmlScanIDAttributeDecl(ctxt, elem, 0);
4024 	    }
4025 	    if (extId > 1) {
4026 		xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
4027        "Element %s has %d ID attribute defined in the external subset : %s\n",
4028 		       attr->elem, extId, attr->name);
4029 	    } else if (extId + nbId > 1) {
4030 		xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
4031 "Element %s has ID attributes defined in the internal and external subset : %s\n",
4032 		       attr->elem, attr->name, NULL);
4033 	    }
4034 	}
4035     }
4036 
4037     /* Validity Constraint: Enumeration */
4038     if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
4039         xmlEnumerationPtr tree = attr->tree;
4040 	while (tree != NULL) {
4041 	    if (xmlStrEqual(tree->name, attr->defaultValue)) break;
4042 	    tree = tree->next;
4043 	}
4044 	if (tree == NULL) {
4045 	    xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_VALUE,
4046 "Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
4047 		   attr->defaultValue, attr->name, attr->elem);
4048 	    ret = 0;
4049 	}
4050     }
4051 
4052     return(ret);
4053 }
4054 
4055 /**
4056  * xmlValidateElementDecl:
4057  * @ctxt:  the validation context
4058  * @doc:  a document instance
4059  * @elem:  an element definition
4060  *
4061  * Try to validate a single element definition
4062  * basically it does the following checks as described by the
4063  * XML-1.0 recommendation:
4064  *  - [ VC: One ID per Element Type ]
4065  *  - [ VC: No Duplicate Types ]
4066  *  - [ VC: Unique Element Type Declaration ]
4067  *
4068  * returns 1 if valid or 0 otherwise
4069  */
4070 
4071 int
xmlValidateElementDecl(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlElementPtr elem)4072 xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4073                        xmlElementPtr elem) {
4074     int ret = 1;
4075     xmlElementPtr tst;
4076 
4077     CHECK_DTD;
4078 
4079     if (elem == NULL) return(1);
4080 
4081 #if 0
4082 #ifdef LIBXML_REGEXP_ENABLED
4083     /* Build the regexp associated to the content model */
4084     ret = xmlValidBuildContentModel(ctxt, elem);
4085 #endif
4086 #endif
4087 
4088     /* No Duplicate Types */
4089     if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
4090 	xmlElementContentPtr cur, next;
4091         const xmlChar *name;
4092 
4093 	cur = elem->content;
4094 	while (cur != NULL) {
4095 	    if (cur->type != XML_ELEMENT_CONTENT_OR) break;
4096 	    if (cur->c1 == NULL) break;
4097 	    if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4098 		name = cur->c1->name;
4099 		next = cur->c2;
4100 		while (next != NULL) {
4101 		    if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
4102 		        if ((xmlStrEqual(next->name, name)) &&
4103 			    (xmlStrEqual(next->prefix, cur->c1->prefix))) {
4104 			    if (cur->c1->prefix == NULL) {
4105 				xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
4106 		   "Definition of %s has duplicate references of %s\n",
4107 				       elem->name, name, NULL);
4108 			    } else {
4109 				xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
4110 		   "Definition of %s has duplicate references of %s:%s\n",
4111 				       elem->name, cur->c1->prefix, name);
4112 			    }
4113 			    ret = 0;
4114 			}
4115 			break;
4116 		    }
4117 		    if (next->c1 == NULL) break;
4118 		    if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
4119 		    if ((xmlStrEqual(next->c1->name, name)) &&
4120 		        (xmlStrEqual(next->c1->prefix, cur->c1->prefix))) {
4121 			if (cur->c1->prefix == NULL) {
4122 			    xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
4123 	       "Definition of %s has duplicate references to %s\n",
4124 				   elem->name, name, NULL);
4125 			} else {
4126 			    xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
4127 	       "Definition of %s has duplicate references to %s:%s\n",
4128 				   elem->name, cur->c1->prefix, name);
4129 			}
4130 			ret = 0;
4131 		    }
4132 		    next = next->c2;
4133 		}
4134 	    }
4135 	    cur = cur->c2;
4136 	}
4137     }
4138 
4139     /* VC: Unique Element Type Declaration */
4140     tst = xmlCtxtGetDtdElementDesc(ctxt, doc->intSubset, elem->name);
4141     if ((tst != NULL ) && (tst != elem) &&
4142 	((tst->prefix == elem->prefix) ||
4143 	 (xmlStrEqual(tst->prefix, elem->prefix))) &&
4144 	(tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
4145 	xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4146 	                "Redefinition of element %s\n",
4147 		       elem->name, NULL, NULL);
4148 	ret = 0;
4149     }
4150     tst = xmlCtxtGetDtdElementDesc(ctxt, doc->extSubset, elem->name);
4151     if ((tst != NULL ) && (tst != elem) &&
4152 	((tst->prefix == elem->prefix) ||
4153 	 (xmlStrEqual(tst->prefix, elem->prefix))) &&
4154 	(tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
4155 	xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4156 	                "Redefinition of element %s\n",
4157 		       elem->name, NULL, NULL);
4158 	ret = 0;
4159     }
4160     /* One ID per Element Type
4161      * already done when registering the attribute
4162     if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
4163 	ret = 0;
4164     } */
4165     return(ret);
4166 }
4167 
4168 /**
4169  * xmlValidateOneAttribute:
4170  * @ctxt:  the validation context
4171  * @doc:  a document instance
4172  * @elem:  an element instance
4173  * @attr:  an attribute instance
4174  * @value:  the attribute value (without entities processing)
4175  *
4176  * Try to validate a single attribute for an element
4177  * basically it does the following checks as described by the
4178  * XML-1.0 recommendation:
4179  *  - [ VC: Attribute Value Type ]
4180  *  - [ VC: Fixed Attribute Default ]
4181  *  - [ VC: Entity Name ]
4182  *  - [ VC: Name Token ]
4183  *  - [ VC: ID ]
4184  *  - [ VC: IDREF ]
4185  *  - [ VC: Entity Name ]
4186  *  - [ VC: Notation Attributes ]
4187  *
4188  * The ID/IDREF uniqueness and matching are done separately
4189  *
4190  * returns 1 if valid or 0 otherwise
4191  */
4192 
4193 int
xmlValidateOneAttribute(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlNodePtr elem,xmlAttrPtr attr,const xmlChar * value)4194 xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4195                         xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value)
4196 {
4197     xmlAttributePtr attrDecl =  NULL;
4198     const xmlChar *aprefix;
4199     int val;
4200     int ret = 1;
4201 
4202     CHECK_DTD;
4203     if ((elem == NULL) || (elem->name == NULL)) return(0);
4204     if ((attr == NULL) || (attr->name == NULL)) return(0);
4205 
4206     aprefix = (attr->ns != NULL) ? attr->ns->prefix : NULL;
4207 
4208     if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
4209 	xmlChar fn[50];
4210 	xmlChar *fullname;
4211 
4212 	fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
4213 	if (fullname == NULL) {
4214             xmlVErrMemory(ctxt);
4215 	    return(0);
4216         }
4217         attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
4218                                       attr->name, aprefix);
4219         if ((attrDecl == NULL) && (doc->extSubset != NULL))
4220             attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
4221                                           attr->name, aprefix);
4222 	if ((fullname != fn) && (fullname != elem->name))
4223 	    xmlFree(fullname);
4224     }
4225     if (attrDecl == NULL) {
4226         attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4227                                       attr->name, aprefix);
4228         if ((attrDecl == NULL) && (doc->extSubset != NULL))
4229             attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4230                                           attr->name, aprefix);
4231     }
4232 
4233 
4234     /* Validity Constraint: Attribute Value Type */
4235     if (attrDecl == NULL) {
4236 	xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
4237 	       "No declaration for attribute %s of element %s\n",
4238 	       attr->name, elem->name, NULL);
4239 	return(0);
4240     }
4241     attr->atype = attrDecl->atype;
4242 
4243     val = xmlValidateAttributeValueInternal(doc, attrDecl->atype, value);
4244     if (val == 0) {
4245 	    xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
4246 	   "Syntax of value for attribute %s of %s is not valid\n",
4247 	       attr->name, elem->name, NULL);
4248         ret = 0;
4249     }
4250 
4251     /* Validity constraint: Fixed Attribute Default */
4252     if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4253 	if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4254 	    xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
4255 	   "Value for attribute %s of %s is different from default \"%s\"\n",
4256 		   attr->name, elem->name, attrDecl->defaultValue);
4257 	    ret = 0;
4258 	}
4259     }
4260 
4261     /* Validity Constraint: ID uniqueness */
4262     if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4263         if (xmlAddID(ctxt, doc, value, attr) == NULL)
4264 	    ret = 0;
4265     }
4266 
4267     if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4268 	(attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4269         if (xmlAddRef(ctxt, doc, value, attr) == NULL)
4270 	    ret = 0;
4271     }
4272 
4273     /* Validity Constraint: Notation Attributes */
4274     if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4275         xmlEnumerationPtr tree = attrDecl->tree;
4276         xmlNotationPtr nota;
4277 
4278         /* First check that the given NOTATION was declared */
4279 	nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4280 	if (nota == NULL)
4281 	    nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4282 
4283 	if (nota == NULL) {
4284 	    xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
4285        "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
4286 		   value, attr->name, elem->name);
4287 	    ret = 0;
4288         }
4289 
4290 	/* Second, verify that it's among the list */
4291 	while (tree != NULL) {
4292 	    if (xmlStrEqual(tree->name, value)) break;
4293 	    tree = tree->next;
4294 	}
4295 	if (tree == NULL) {
4296 	    xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
4297 "Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
4298 		   value, attr->name, elem->name);
4299 	    ret = 0;
4300 	}
4301     }
4302 
4303     /* Validity Constraint: Enumeration */
4304     if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4305         xmlEnumerationPtr tree = attrDecl->tree;
4306 	while (tree != NULL) {
4307 	    if (xmlStrEqual(tree->name, value)) break;
4308 	    tree = tree->next;
4309 	}
4310 	if (tree == NULL) {
4311 	    xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
4312        "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
4313 		   value, attr->name, elem->name);
4314 	    ret = 0;
4315 	}
4316     }
4317 
4318     /* Fixed Attribute Default */
4319     if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4320         (!xmlStrEqual(attrDecl->defaultValue, value))) {
4321 	xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
4322 	   "Value for attribute %s of %s must be \"%s\"\n",
4323 	       attr->name, elem->name, attrDecl->defaultValue);
4324         ret = 0;
4325     }
4326 
4327     /* Extra check for the attribute value */
4328     ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
4329 				      attrDecl->atype, value);
4330 
4331     return(ret);
4332 }
4333 
4334 /**
4335  * xmlValidateOneNamespace:
4336  * @ctxt:  the validation context
4337  * @doc:  a document instance
4338  * @elem:  an element instance
4339  * @prefix:  the namespace prefix
4340  * @ns:  an namespace declaration instance
4341  * @value:  the attribute value (without entities processing)
4342  *
4343  * Try to validate a single namespace declaration for an element
4344  * basically it does the following checks as described by the
4345  * XML-1.0 recommendation:
4346  *  - [ VC: Attribute Value Type ]
4347  *  - [ VC: Fixed Attribute Default ]
4348  *  - [ VC: Entity Name ]
4349  *  - [ VC: Name Token ]
4350  *  - [ VC: ID ]
4351  *  - [ VC: IDREF ]
4352  *  - [ VC: Entity Name ]
4353  *  - [ VC: Notation Attributes ]
4354  *
4355  * The ID/IDREF uniqueness and matching are done separately
4356  *
4357  * returns 1 if valid or 0 otherwise
4358  */
4359 
4360 int
xmlValidateOneNamespace(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlNodePtr elem,const xmlChar * prefix,xmlNsPtr ns,const xmlChar * value)4361 xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4362 xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4363     /* xmlElementPtr elemDecl; */
4364     xmlAttributePtr attrDecl =  NULL;
4365     int val;
4366     int ret = 1;
4367 
4368     CHECK_DTD;
4369     if ((elem == NULL) || (elem->name == NULL)) return(0);
4370     if ((ns == NULL) || (ns->href == NULL)) return(0);
4371 
4372     if (prefix != NULL) {
4373 	xmlChar fn[50];
4374 	xmlChar *fullname;
4375 
4376 	fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4377 	if (fullname == NULL) {
4378 	    xmlVErrMemory(ctxt);
4379 	    return(0);
4380 	}
4381 	if (ns->prefix != NULL) {
4382 	    attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
4383 		                          ns->prefix, BAD_CAST "xmlns");
4384 	    if ((attrDecl == NULL) && (doc->extSubset != NULL))
4385 		attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
4386 					  ns->prefix, BAD_CAST "xmlns");
4387 	} else {
4388 	    attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
4389                                           BAD_CAST "xmlns", NULL);
4390 	    if ((attrDecl == NULL) && (doc->extSubset != NULL))
4391 		attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
4392                                               BAD_CAST "xmlns", NULL);
4393 	}
4394 	if ((fullname != fn) && (fullname != elem->name))
4395 	    xmlFree(fullname);
4396     }
4397     if (attrDecl == NULL) {
4398 	if (ns->prefix != NULL) {
4399 	    attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4400 		                          ns->prefix, BAD_CAST "xmlns");
4401 	    if ((attrDecl == NULL) && (doc->extSubset != NULL))
4402 		attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4403 					      ns->prefix, BAD_CAST "xmlns");
4404 	} else {
4405 	    attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4406                                           BAD_CAST "xmlns", NULL);
4407 	    if ((attrDecl == NULL) && (doc->extSubset != NULL))
4408 		attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4409                                               BAD_CAST "xmlns", NULL);
4410 	}
4411     }
4412 
4413 
4414     /* Validity Constraint: Attribute Value Type */
4415     if (attrDecl == NULL) {
4416 	if (ns->prefix != NULL) {
4417 	    xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
4418 		   "No declaration for attribute xmlns:%s of element %s\n",
4419 		   ns->prefix, elem->name, NULL);
4420 	} else {
4421 	    xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
4422 		   "No declaration for attribute xmlns of element %s\n",
4423 		   elem->name, NULL, NULL);
4424 	}
4425 	return(0);
4426     }
4427 
4428     val = xmlValidateAttributeValueInternal(doc, attrDecl->atype, value);
4429     if (val == 0) {
4430 	if (ns->prefix != NULL) {
4431 	    xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
4432 	       "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4433 		   ns->prefix, elem->name, NULL);
4434 	} else {
4435 	    xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
4436 	       "Syntax of value for attribute xmlns of %s is not valid\n",
4437 		   elem->name, NULL, NULL);
4438 	}
4439         ret = 0;
4440     }
4441 
4442     /* Validity constraint: Fixed Attribute Default */
4443     if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4444 	if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4445 	    if (ns->prefix != NULL) {
4446 		xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
4447        "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4448 		       ns->prefix, elem->name, attrDecl->defaultValue);
4449 	    } else {
4450 		xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
4451        "Value for attribute xmlns of %s is different from default \"%s\"\n",
4452 		       elem->name, attrDecl->defaultValue, NULL);
4453 	    }
4454 	    ret = 0;
4455 	}
4456     }
4457 
4458     /*
4459      * Casting ns to xmlAttrPtr is wrong. We'd need separate functions
4460      * xmlAddID and xmlAddRef for namespace declarations, but it makes
4461      * no practical sense to use ID types anyway.
4462      */
4463 #if 0
4464     /* Validity Constraint: ID uniqueness */
4465     if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4466         if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4467 	    ret = 0;
4468     }
4469 
4470     if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4471 	(attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4472         if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4473 	    ret = 0;
4474     }
4475 #endif
4476 
4477     /* Validity Constraint: Notation Attributes */
4478     if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4479         xmlEnumerationPtr tree = attrDecl->tree;
4480         xmlNotationPtr nota;
4481 
4482         /* First check that the given NOTATION was declared */
4483 	nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4484 	if (nota == NULL)
4485 	    nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4486 
4487 	if (nota == NULL) {
4488 	    if (ns->prefix != NULL) {
4489 		xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
4490        "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4491 		       value, ns->prefix, elem->name);
4492 	    } else {
4493 		xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
4494        "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4495 		       value, elem->name, NULL);
4496 	    }
4497 	    ret = 0;
4498         }
4499 
4500 	/* Second, verify that it's among the list */
4501 	while (tree != NULL) {
4502 	    if (xmlStrEqual(tree->name, value)) break;
4503 	    tree = tree->next;
4504 	}
4505 	if (tree == NULL) {
4506 	    if (ns->prefix != NULL) {
4507 		xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
4508 "Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4509 		       value, ns->prefix, elem->name);
4510 	    } else {
4511 		xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
4512 "Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4513 		       value, elem->name, NULL);
4514 	    }
4515 	    ret = 0;
4516 	}
4517     }
4518 
4519     /* Validity Constraint: Enumeration */
4520     if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4521         xmlEnumerationPtr tree = attrDecl->tree;
4522 	while (tree != NULL) {
4523 	    if (xmlStrEqual(tree->name, value)) break;
4524 	    tree = tree->next;
4525 	}
4526 	if (tree == NULL) {
4527 	    if (ns->prefix != NULL) {
4528 		xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
4529 "Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4530 		       value, ns->prefix, elem->name);
4531 	    } else {
4532 		xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
4533 "Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4534 		       value, elem->name, NULL);
4535 	    }
4536 	    ret = 0;
4537 	}
4538     }
4539 
4540     /* Fixed Attribute Default */
4541     if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4542         (!xmlStrEqual(attrDecl->defaultValue, value))) {
4543 	if (ns->prefix != NULL) {
4544 	    xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
4545 		   "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4546 		   ns->prefix, elem->name, attrDecl->defaultValue);
4547 	} else {
4548 	    xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
4549 		   "Value for attribute xmlns of %s must be \"%s\"\n",
4550 		   elem->name, attrDecl->defaultValue, NULL);
4551 	}
4552         ret = 0;
4553     }
4554 
4555     /* Extra check for the attribute value */
4556     if (ns->prefix != NULL) {
4557 	ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4558 					  attrDecl->atype, value);
4559     } else {
4560 	ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4561 					  attrDecl->atype, value);
4562     }
4563 
4564     return(ret);
4565 }
4566 
4567 #ifndef  LIBXML_REGEXP_ENABLED
4568 /**
4569  * xmlValidateSkipIgnorable:
4570  * @ctxt:  the validation context
4571  * @child:  the child list
4572  *
4573  * Skip ignorable elements w.r.t. the validation process
4574  *
4575  * returns the first element to consider for validation of the content model
4576  */
4577 
4578 static xmlNodePtr
xmlValidateSkipIgnorable(xmlNodePtr child)4579 xmlValidateSkipIgnorable(xmlNodePtr child) {
4580     while (child != NULL) {
4581 	switch (child->type) {
4582 	    /* These things are ignored (skipped) during validation.  */
4583 	    case XML_PI_NODE:
4584 	    case XML_COMMENT_NODE:
4585 	    case XML_XINCLUDE_START:
4586 	    case XML_XINCLUDE_END:
4587 		child = child->next;
4588 		break;
4589 	    case XML_TEXT_NODE:
4590 		if (xmlIsBlankNode(child))
4591 		    child = child->next;
4592 		else
4593 		    return(child);
4594 		break;
4595 	    /* keep current node */
4596 	    default:
4597 		return(child);
4598 	}
4599     }
4600     return(child);
4601 }
4602 
4603 /**
4604  * xmlValidateElementType:
4605  * @ctxt:  the validation context
4606  *
4607  * Try to validate the content model of an element internal function
4608  *
4609  * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4610  *           reference is found and -3 if the validation succeeded but
4611  *           the content model is not determinist.
4612  */
4613 
4614 static int
xmlValidateElementType(xmlValidCtxtPtr ctxt)4615 xmlValidateElementType(xmlValidCtxtPtr ctxt) {
4616     int ret = -1;
4617     int determinist = 1;
4618 
4619     NODE = xmlValidateSkipIgnorable(NODE);
4620     if ((NODE == NULL) && (CONT == NULL))
4621 	return(1);
4622     if ((NODE == NULL) &&
4623 	((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4624 	 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4625 	return(1);
4626     }
4627     if (CONT == NULL) return(-1);
4628     if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
4629 	return(-2);
4630 
4631     /*
4632      * We arrive here when more states need to be examined
4633      */
4634 cont:
4635 
4636     /*
4637      * We just recovered from a rollback generated by a possible
4638      * epsilon transition, go directly to the analysis phase
4639      */
4640     if (STATE == ROLLBACK_PARENT) {
4641 	ret = 1;
4642 	goto analyze;
4643     }
4644 
4645     /*
4646      * we may have to save a backup state here. This is the equivalent
4647      * of handling epsilon transition in NFAs.
4648      */
4649     if ((CONT != NULL) &&
4650 	((CONT->parent == NULL) ||
4651 	 (CONT->parent == (xmlElementContentPtr) 1) ||
4652 	 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
4653 	((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4654 	 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
4655 	 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
4656 	if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4657 	    return(0);
4658     }
4659 
4660 
4661     /*
4662      * Check first if the content matches
4663      */
4664     switch (CONT->type) {
4665 	case XML_ELEMENT_CONTENT_PCDATA:
4666 	    if (NODE == NULL) {
4667 		ret = 0;
4668 		break;
4669 	    }
4670 	    if (NODE->type == XML_TEXT_NODE) {
4671 		/*
4672 		 * go to next element in the content model
4673 		 * skipping ignorable elems
4674 		 */
4675 		do {
4676 		    NODE = NODE->next;
4677 		    NODE = xmlValidateSkipIgnorable(NODE);
4678 		    if ((NODE != NULL) &&
4679 			(NODE->type == XML_ENTITY_REF_NODE))
4680 			return(-2);
4681 		} while ((NODE != NULL) &&
4682 			 ((NODE->type != XML_ELEMENT_NODE) &&
4683 			  (NODE->type != XML_TEXT_NODE) &&
4684 			  (NODE->type != XML_CDATA_SECTION_NODE)));
4685                 ret = 1;
4686 		break;
4687 	    } else {
4688 		ret = 0;
4689 		break;
4690 	    }
4691 	    break;
4692 	case XML_ELEMENT_CONTENT_ELEMENT:
4693 	    if (NODE == NULL) {
4694 		ret = 0;
4695 		break;
4696 	    }
4697 	    ret = ((NODE->type == XML_ELEMENT_NODE) &&
4698 		   (xmlStrEqual(NODE->name, CONT->name)));
4699 	    if (ret == 1) {
4700 		if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4701 		    ret = (CONT->prefix == NULL);
4702 		} else if (CONT->prefix == NULL) {
4703 		    ret = 0;
4704 		} else {
4705 		    ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4706 		}
4707 	    }
4708 	    if (ret == 1) {
4709 		/*
4710 		 * go to next element in the content model
4711 		 * skipping ignorable elems
4712 		 */
4713 		do {
4714 		    NODE = NODE->next;
4715 		    NODE = xmlValidateSkipIgnorable(NODE);
4716 		    if ((NODE != NULL) &&
4717 			(NODE->type == XML_ENTITY_REF_NODE))
4718 			return(-2);
4719 		} while ((NODE != NULL) &&
4720 			 ((NODE->type != XML_ELEMENT_NODE) &&
4721 			  (NODE->type != XML_TEXT_NODE) &&
4722 			  (NODE->type != XML_CDATA_SECTION_NODE)));
4723 	    } else {
4724 		ret = 0;
4725 		break;
4726 	    }
4727 	    break;
4728 	case XML_ELEMENT_CONTENT_OR:
4729 	    /*
4730 	     * Small optimization.
4731 	     */
4732 	    if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4733 		if ((NODE == NULL) ||
4734 		    (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4735 		    DEPTH++;
4736 		    CONT = CONT->c2;
4737 		    goto cont;
4738 		}
4739 		if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4740 		    ret = (CONT->c1->prefix == NULL);
4741 		} else if (CONT->c1->prefix == NULL) {
4742 		    ret = 0;
4743 		} else {
4744 		    ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4745 		}
4746 		if (ret == 0) {
4747 		    DEPTH++;
4748 		    CONT = CONT->c2;
4749 		    goto cont;
4750 		}
4751 	    }
4752 
4753 	    /*
4754 	     * save the second branch 'or' branch
4755 	     */
4756 	    if (vstateVPush(ctxt, CONT->c2, NODE, DEPTH + 1,
4757 			    OCCURS, ROLLBACK_OR) < 0)
4758 		return(-1);
4759 	    DEPTH++;
4760 	    CONT = CONT->c1;
4761 	    goto cont;
4762 	case XML_ELEMENT_CONTENT_SEQ:
4763 	    /*
4764 	     * Small optimization.
4765 	     */
4766 	    if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4767 		((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4768 		 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4769 		if ((NODE == NULL) ||
4770 		    (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4771 		    DEPTH++;
4772 		    CONT = CONT->c2;
4773 		    goto cont;
4774 		}
4775 		if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4776 		    ret = (CONT->c1->prefix == NULL);
4777 		} else if (CONT->c1->prefix == NULL) {
4778 		    ret = 0;
4779 		} else {
4780 		    ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4781 		}
4782 		if (ret == 0) {
4783 		    DEPTH++;
4784 		    CONT = CONT->c2;
4785 		    goto cont;
4786 		}
4787 	    }
4788 	    DEPTH++;
4789 	    CONT = CONT->c1;
4790 	    goto cont;
4791     }
4792 
4793     /*
4794      * At this point handle going up in the tree
4795      */
4796     if (ret == -1) {
4797 	return(ret);
4798     }
4799 analyze:
4800     while (CONT != NULL) {
4801 	/*
4802 	 * First do the analysis depending on the occurrence model at
4803 	 * this level.
4804 	 */
4805 	if (ret == 0) {
4806 	    switch (CONT->ocur) {
4807 		xmlNodePtr cur;
4808 
4809 		case XML_ELEMENT_CONTENT_ONCE:
4810 		    cur = ctxt->vstate->node;
4811 		    if (vstateVPop(ctxt) < 0 ) {
4812 			return(0);
4813 		    }
4814 		    if (cur != ctxt->vstate->node)
4815 			determinist = -3;
4816 		    goto cont;
4817 		case XML_ELEMENT_CONTENT_PLUS:
4818 		    if (OCCURRENCE == 0) {
4819 			cur = ctxt->vstate->node;
4820 			if (vstateVPop(ctxt) < 0 ) {
4821 			    return(0);
4822 			}
4823 			if (cur != ctxt->vstate->node)
4824 			    determinist = -3;
4825 			goto cont;
4826 		    }
4827 		    ret = 1;
4828 		    break;
4829 		case XML_ELEMENT_CONTENT_MULT:
4830 		    ret = 1;
4831 		    break;
4832 		case XML_ELEMENT_CONTENT_OPT:
4833 		    ret = 1;
4834 		    break;
4835 	    }
4836 	} else {
4837 	    switch (CONT->ocur) {
4838 		case XML_ELEMENT_CONTENT_OPT:
4839 		    ret = 1;
4840 		    break;
4841 		case XML_ELEMENT_CONTENT_ONCE:
4842 		    ret = 1;
4843 		    break;
4844 		case XML_ELEMENT_CONTENT_PLUS:
4845 		    if (STATE == ROLLBACK_PARENT) {
4846 			ret = 1;
4847 			break;
4848 		    }
4849 		    if (NODE == NULL) {
4850 			ret = 1;
4851 			break;
4852 		    }
4853 		    SET_OCCURRENCE;
4854 		    goto cont;
4855 		case XML_ELEMENT_CONTENT_MULT:
4856 		    if (STATE == ROLLBACK_PARENT) {
4857 			ret = 1;
4858 			break;
4859 		    }
4860 		    if (NODE == NULL) {
4861 			ret = 1;
4862 			break;
4863 		    }
4864 		    /* SET_OCCURRENCE; */
4865 		    goto cont;
4866 	    }
4867 	}
4868 	STATE = 0;
4869 
4870 	/*
4871 	 * Then act accordingly at the parent level
4872 	 */
4873 	RESET_OCCURRENCE;
4874 	if ((CONT->parent == NULL) ||
4875             (CONT->parent == (xmlElementContentPtr) 1))
4876 	    break;
4877 
4878 	switch (CONT->parent->type) {
4879 	    case XML_ELEMENT_CONTENT_PCDATA:
4880 		return(-1);
4881 	    case XML_ELEMENT_CONTENT_ELEMENT:
4882 		return(-1);
4883 	    case XML_ELEMENT_CONTENT_OR:
4884 		if (ret == 1) {
4885 		    CONT = CONT->parent;
4886 		    DEPTH--;
4887 		} else {
4888 		    CONT = CONT->parent;
4889 		    DEPTH--;
4890 		}
4891 		break;
4892 	    case XML_ELEMENT_CONTENT_SEQ:
4893 		if (ret == 0) {
4894 		    CONT = CONT->parent;
4895 		    DEPTH--;
4896 		} else if (CONT == CONT->parent->c1) {
4897 		    CONT = CONT->parent->c2;
4898 		    goto cont;
4899 		} else {
4900 		    CONT = CONT->parent;
4901 		    DEPTH--;
4902 		}
4903 	}
4904     }
4905     if (NODE != NULL) {
4906 	xmlNodePtr cur;
4907 
4908 	cur = ctxt->vstate->node;
4909 	if (vstateVPop(ctxt) < 0 ) {
4910 	    return(0);
4911 	}
4912 	if (cur != ctxt->vstate->node)
4913 	    determinist = -3;
4914 	goto cont;
4915     }
4916     if (ret == 0) {
4917 	xmlNodePtr cur;
4918 
4919 	cur = ctxt->vstate->node;
4920 	if (vstateVPop(ctxt) < 0 ) {
4921 	    return(0);
4922 	}
4923 	if (cur != ctxt->vstate->node)
4924 	    determinist = -3;
4925 	goto cont;
4926     }
4927     return(determinist);
4928 }
4929 #endif
4930 
4931 /**
4932  * xmlSnprintfElements:
4933  * @buf:  an output buffer
4934  * @size:  the size of the buffer
4935  * @content:  An element
4936  * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4937  *
4938  * This will dump the list of elements to the buffer
4939  * Intended just for the debug routine
4940  */
4941 static void
xmlSnprintfElements(char * buf,int size,xmlNodePtr node,int glob)4942 xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
4943     xmlNodePtr cur;
4944     int len;
4945 
4946     if (node == NULL) return;
4947     if (glob) strcat(buf, "(");
4948     cur = node;
4949     while (cur != NULL) {
4950 	len = strlen(buf);
4951 	if (size - len < 50) {
4952 	    if ((size - len > 4) && (buf[len - 1] != '.'))
4953 		strcat(buf, " ...");
4954 	    return;
4955 	}
4956         switch (cur->type) {
4957             case XML_ELEMENT_NODE:
4958 		if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4959 		    if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
4960 			if ((size - len > 4) && (buf[len - 1] != '.'))
4961 			    strcat(buf, " ...");
4962 			return;
4963 		    }
4964 		    strcat(buf, (char *) cur->ns->prefix);
4965 		    strcat(buf, ":");
4966 		}
4967                 if (size - len < xmlStrlen(cur->name) + 10) {
4968 		    if ((size - len > 4) && (buf[len - 1] != '.'))
4969 			strcat(buf, " ...");
4970 		    return;
4971 		}
4972 	        strcat(buf, (char *) cur->name);
4973 		if (cur->next != NULL)
4974 		    strcat(buf, " ");
4975 		break;
4976             case XML_TEXT_NODE:
4977 		if (xmlIsBlankNode(cur))
4978 		    break;
4979                 /* Falls through. */
4980             case XML_CDATA_SECTION_NODE:
4981             case XML_ENTITY_REF_NODE:
4982 	        strcat(buf, "CDATA");
4983 		if (cur->next != NULL)
4984 		    strcat(buf, " ");
4985 		break;
4986             case XML_ATTRIBUTE_NODE:
4987             case XML_DOCUMENT_NODE:
4988 	    case XML_HTML_DOCUMENT_NODE:
4989             case XML_DOCUMENT_TYPE_NODE:
4990             case XML_DOCUMENT_FRAG_NODE:
4991             case XML_NOTATION_NODE:
4992 	    case XML_NAMESPACE_DECL:
4993 	        strcat(buf, "???");
4994 		if (cur->next != NULL)
4995 		    strcat(buf, " ");
4996 		break;
4997             case XML_ENTITY_NODE:
4998             case XML_PI_NODE:
4999             case XML_DTD_NODE:
5000             case XML_COMMENT_NODE:
5001 	    case XML_ELEMENT_DECL:
5002 	    case XML_ATTRIBUTE_DECL:
5003 	    case XML_ENTITY_DECL:
5004 	    case XML_XINCLUDE_START:
5005 	    case XML_XINCLUDE_END:
5006 		break;
5007 	}
5008 	cur = cur->next;
5009     }
5010     if (glob) strcat(buf, ")");
5011 }
5012 
5013 /**
5014  * xmlValidateElementContent:
5015  * @ctxt:  the validation context
5016  * @child:  the child list
5017  * @elemDecl:  pointer to the element declaration
5018  * @warn:  emit the error message
5019  * @parent: the parent element (for error reporting)
5020  *
5021  * Try to validate the content model of an element
5022  *
5023  * returns 1 if valid or 0 if not and -1 in case of error
5024  */
5025 
5026 static int
xmlValidateElementContent(xmlValidCtxtPtr ctxt,xmlNodePtr child,xmlElementPtr elemDecl,int warn,xmlNodePtr parent)5027 xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
5028        xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
5029     int ret = 1;
5030 #ifndef  LIBXML_REGEXP_ENABLED
5031     xmlNodePtr repl = NULL, last = NULL, tmp;
5032 #endif
5033     xmlNodePtr cur;
5034     xmlElementContentPtr cont;
5035     const xmlChar *name;
5036 
5037     if ((elemDecl == NULL) || (parent == NULL) || (ctxt == NULL))
5038 	return(-1);
5039     cont = elemDecl->content;
5040     name = elemDecl->name;
5041 
5042 #ifdef LIBXML_REGEXP_ENABLED
5043     /* Build the regexp associated to the content model */
5044     if (elemDecl->contModel == NULL)
5045 	ret = xmlValidBuildContentModel(ctxt, elemDecl);
5046     if (elemDecl->contModel == NULL) {
5047 	return(-1);
5048     } else {
5049 	xmlRegExecCtxtPtr exec;
5050 
5051 	if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
5052 	    return(-1);
5053 	}
5054 	ctxt->nodeMax = 0;
5055 	ctxt->nodeNr = 0;
5056 	ctxt->nodeTab = NULL;
5057 	exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
5058 	if (exec == NULL) {
5059             xmlVErrMemory(ctxt);
5060             return(-1);
5061         }
5062         cur = child;
5063         while (cur != NULL) {
5064             switch (cur->type) {
5065                 case XML_ENTITY_REF_NODE:
5066                     /*
5067                      * Push the current node to be able to roll back
5068                      * and process within the entity
5069                      */
5070                     if ((cur->children != NULL) &&
5071                         (cur->children->children != NULL)) {
5072                         nodeVPush(ctxt, cur);
5073                         cur = cur->children->children;
5074                         continue;
5075                     }
5076                     break;
5077                 case XML_TEXT_NODE:
5078                     if (xmlIsBlankNode(cur))
5079                         break;
5080                     ret = 0;
5081                     goto fail;
5082                 case XML_CDATA_SECTION_NODE:
5083                     /* TODO */
5084                     ret = 0;
5085                     goto fail;
5086                 case XML_ELEMENT_NODE:
5087                     if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
5088                         xmlChar fn[50];
5089                         xmlChar *fullname;
5090 
5091                         fullname = xmlBuildQName(cur->name,
5092                                                  cur->ns->prefix, fn, 50);
5093                         if (fullname == NULL) {
5094                             xmlVErrMemory(ctxt);
5095                             ret = -1;
5096                             goto fail;
5097                         }
5098                         ret = xmlRegExecPushString(exec, fullname, NULL);
5099                         if ((fullname != fn) && (fullname != cur->name))
5100                             xmlFree(fullname);
5101                     } else {
5102                         ret = xmlRegExecPushString(exec, cur->name, NULL);
5103                     }
5104                     break;
5105                 default:
5106                     break;
5107             }
5108             if (ret == XML_REGEXP_OUT_OF_MEMORY)
5109                 xmlVErrMemory(ctxt);
5110             /*
5111              * Switch to next element
5112              */
5113             cur = cur->next;
5114             while (cur == NULL) {
5115                 cur = nodeVPop(ctxt);
5116                 if (cur == NULL)
5117                     break;
5118                 cur = cur->next;
5119             }
5120         }
5121         ret = xmlRegExecPushString(exec, NULL, NULL);
5122 fail:
5123         xmlRegFreeExecCtxt(exec);
5124     }
5125 #else  /* LIBXML_REGEXP_ENABLED */
5126     /*
5127      * Allocate the stack
5128      */
5129     ctxt->vstateMax = 8;
5130     ctxt->vstateTab = (xmlValidState *) xmlMalloc(
5131 		 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
5132     if (ctxt->vstateTab == NULL) {
5133 	xmlVErrMemory(ctxt);
5134 	return(-1);
5135     }
5136     /*
5137      * The first entry in the stack is reserved to the current state
5138      */
5139     ctxt->nodeMax = 0;
5140     ctxt->nodeNr = 0;
5141     ctxt->nodeTab = NULL;
5142     ctxt->vstate = &ctxt->vstateTab[0];
5143     ctxt->vstateNr = 1;
5144     CONT = cont;
5145     NODE = child;
5146     DEPTH = 0;
5147     OCCURS = 0;
5148     STATE = 0;
5149     ret = xmlValidateElementType(ctxt);
5150     if ((ret == -3) && (warn)) {
5151 	char expr[5000];
5152 	expr[0] = 0;
5153 	xmlSnprintfElementContent(expr, 5000, elemDecl->content, 1);
5154 	xmlErrValidNode(ctxt, (xmlNodePtr) elemDecl,
5155                 XML_DTD_CONTENT_NOT_DETERMINIST,
5156 	        "Content model of %s is not deterministic: %s\n",
5157 	        name, BAD_CAST expr, NULL);
5158     } else if (ret == -2) {
5159 	/*
5160 	 * An entities reference appeared at this level.
5161 	 * Build a minimal representation of this node content
5162 	 * sufficient to run the validation process on it
5163 	 */
5164 	cur = child;
5165 	while (cur != NULL) {
5166 	    switch (cur->type) {
5167 		case XML_ENTITY_REF_NODE:
5168 		    /*
5169 		     * Push the current node to be able to roll back
5170 		     * and process within the entity
5171 		     */
5172 		    if ((cur->children != NULL) &&
5173 			(cur->children->children != NULL)) {
5174 			nodeVPush(ctxt, cur);
5175 			cur = cur->children->children;
5176 			continue;
5177 		    }
5178 		    break;
5179 		case XML_TEXT_NODE:
5180 		    if (xmlIsBlankNode(cur))
5181 			break;
5182 		    /* no break on purpose */
5183 		case XML_CDATA_SECTION_NODE:
5184 		    /* no break on purpose */
5185 		case XML_ELEMENT_NODE:
5186 		    /*
5187 		     * Allocate a new node and minimally fills in
5188 		     * what's required
5189 		     */
5190 		    tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
5191 		    if (tmp == NULL) {
5192 			xmlVErrMemory(ctxt);
5193 			xmlFreeNodeList(repl);
5194 			ret = -1;
5195 			goto done;
5196 		    }
5197 		    tmp->type = cur->type;
5198 		    tmp->name = cur->name;
5199 		    tmp->ns = cur->ns;
5200 		    tmp->next = NULL;
5201 		    tmp->content = NULL;
5202 		    if (repl == NULL)
5203 			repl = last = tmp;
5204 		    else {
5205 			last->next = tmp;
5206 			last = tmp;
5207 		    }
5208 		    if (cur->type == XML_CDATA_SECTION_NODE) {
5209 			/*
5210 			 * E59 spaces in CDATA does not match the
5211 			 * nonterminal S
5212 			 */
5213 			tmp->content = xmlStrdup(BAD_CAST "CDATA");
5214 		    }
5215 		    break;
5216 		default:
5217 		    break;
5218 	    }
5219 	    /*
5220 	     * Switch to next element
5221 	     */
5222 	    cur = cur->next;
5223 	    while (cur == NULL) {
5224 		cur = nodeVPop(ctxt);
5225 		if (cur == NULL)
5226 		    break;
5227 		cur = cur->next;
5228 	    }
5229 	}
5230 
5231 	/*
5232 	 * Relaunch the validation
5233 	 */
5234 	ctxt->vstate = &ctxt->vstateTab[0];
5235 	ctxt->vstateNr = 1;
5236 	CONT = cont;
5237 	NODE = repl;
5238 	DEPTH = 0;
5239 	OCCURS = 0;
5240 	STATE = 0;
5241 	ret = xmlValidateElementType(ctxt);
5242     }
5243 #endif /* LIBXML_REGEXP_ENABLED */
5244     if ((warn) && ((ret != 1) && (ret != -3))) {
5245 	if (ctxt != NULL) {
5246 	    char expr[5000];
5247 	    char list[5000];
5248 
5249 	    expr[0] = 0;
5250 	    xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
5251 	    list[0] = 0;
5252 #ifndef LIBXML_REGEXP_ENABLED
5253 	    if (repl != NULL)
5254 		xmlSnprintfElements(&list[0], 5000, repl, 1);
5255 	    else
5256 #endif /* LIBXML_REGEXP_ENABLED */
5257 		xmlSnprintfElements(&list[0], 5000, child, 1);
5258 
5259 	    if (name != NULL) {
5260 		xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5261 	   "Element %s content does not follow the DTD, expecting %s, got %s\n",
5262 		       name, BAD_CAST expr, BAD_CAST list);
5263 	    } else {
5264 		xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5265 	   "Element content does not follow the DTD, expecting %s, got %s\n",
5266 		       BAD_CAST expr, BAD_CAST list, NULL);
5267 	    }
5268 	} else {
5269 	    if (name != NULL) {
5270 		xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5271 		       "Element %s content does not follow the DTD\n",
5272 		       name, NULL, NULL);
5273 	    } else {
5274 		xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5275 		       "Element content does not follow the DTD\n",
5276 		                NULL, NULL, NULL);
5277 	    }
5278 	}
5279 	ret = 0;
5280     }
5281     if (ret == -3)
5282 	ret = 1;
5283 
5284 #ifndef  LIBXML_REGEXP_ENABLED
5285 done:
5286     /*
5287      * Deallocate the copy if done, and free up the validation stack
5288      */
5289     while (repl != NULL) {
5290 	tmp = repl->next;
5291 	xmlFree(repl);
5292 	repl = tmp;
5293     }
5294     ctxt->vstateMax = 0;
5295     if (ctxt->vstateTab != NULL) {
5296 	xmlFree(ctxt->vstateTab);
5297 	ctxt->vstateTab = NULL;
5298     }
5299 #endif
5300     ctxt->nodeMax = 0;
5301     ctxt->nodeNr = 0;
5302     if (ctxt->nodeTab != NULL) {
5303 	xmlFree(ctxt->nodeTab);
5304 	ctxt->nodeTab = NULL;
5305     }
5306     return(ret);
5307 
5308 }
5309 
5310 /**
5311  * xmlValidateCdataElement:
5312  * @ctxt:  the validation context
5313  * @doc:  a document instance
5314  * @elem:  an element instance
5315  *
5316  * Check that an element follows #CDATA
5317  *
5318  * returns 1 if valid or 0 otherwise
5319  */
5320 static int
xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlNodePtr elem)5321 xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5322                            xmlNodePtr elem) {
5323     int ret = 1;
5324     xmlNodePtr cur, child;
5325 
5326     if ((ctxt == NULL) || (doc == NULL) || (elem == NULL) ||
5327         (elem->type != XML_ELEMENT_NODE))
5328 	return(0);
5329 
5330     child = elem->children;
5331 
5332     cur = child;
5333     while (cur != NULL) {
5334 	switch (cur->type) {
5335 	    case XML_ENTITY_REF_NODE:
5336 		/*
5337 		 * Push the current node to be able to roll back
5338 		 * and process within the entity
5339 		 */
5340 		if ((cur->children != NULL) &&
5341 		    (cur->children->children != NULL)) {
5342 		    nodeVPush(ctxt, cur);
5343 		    cur = cur->children->children;
5344 		    continue;
5345 		}
5346 		break;
5347 	    case XML_COMMENT_NODE:
5348 	    case XML_PI_NODE:
5349 	    case XML_TEXT_NODE:
5350 	    case XML_CDATA_SECTION_NODE:
5351 		break;
5352 	    default:
5353 		ret = 0;
5354 		goto done;
5355 	}
5356 	/*
5357 	 * Switch to next element
5358 	 */
5359 	cur = cur->next;
5360 	while (cur == NULL) {
5361 	    cur = nodeVPop(ctxt);
5362 	    if (cur == NULL)
5363 		break;
5364 	    cur = cur->next;
5365 	}
5366     }
5367 done:
5368     ctxt->nodeMax = 0;
5369     ctxt->nodeNr = 0;
5370     if (ctxt->nodeTab != NULL) {
5371 	xmlFree(ctxt->nodeTab);
5372 	ctxt->nodeTab = NULL;
5373     }
5374     return(ret);
5375 }
5376 
5377 #ifdef LIBXML_REGEXP_ENABLED
5378 /**
5379  * xmlValidateCheckMixed:
5380  * @ctxt:  the validation context
5381  * @cont:  the mixed content model
5382  * @qname:  the qualified name as appearing in the serialization
5383  *
5384  * Check if the given node is part of the content model.
5385  *
5386  * Returns 1 if yes, 0 if no, -1 in case of error
5387  */
5388 static int
xmlValidateCheckMixed(xmlValidCtxtPtr ctxt,xmlElementContentPtr cont,const xmlChar * qname)5389 xmlValidateCheckMixed(xmlValidCtxtPtr ctxt,
5390 	              xmlElementContentPtr cont, const xmlChar *qname) {
5391     const xmlChar *name;
5392     int plen;
5393     name = xmlSplitQName3(qname, &plen);
5394 
5395     if (name == NULL) {
5396 	while (cont != NULL) {
5397 	    if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5398 		if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname)))
5399 		    return(1);
5400 	    } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5401 	       (cont->c1 != NULL) &&
5402 	       (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5403 		if ((cont->c1->prefix == NULL) &&
5404 		    (xmlStrEqual(cont->c1->name, qname)))
5405 		    return(1);
5406 	    } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5407 		(cont->c1 == NULL) ||
5408 		(cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5409 		xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5410 			"Internal: MIXED struct corrupted\n",
5411 			NULL);
5412 		break;
5413 	    }
5414 	    cont = cont->c2;
5415 	}
5416     } else {
5417 	while (cont != NULL) {
5418 	    if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5419 		if ((cont->prefix != NULL) &&
5420 		    (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5421 		    (xmlStrEqual(cont->name, name)))
5422 		    return(1);
5423 	    } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5424 	       (cont->c1 != NULL) &&
5425 	       (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5426 		if ((cont->c1->prefix != NULL) &&
5427 		    (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5428 		    (xmlStrEqual(cont->c1->name, name)))
5429 		    return(1);
5430 	    } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5431 		(cont->c1 == NULL) ||
5432 		(cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5433 		xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5434 			"Internal: MIXED struct corrupted\n",
5435 			NULL);
5436 		break;
5437 	    }
5438 	    cont = cont->c2;
5439 	}
5440     }
5441     return(0);
5442 }
5443 #endif /* LIBXML_REGEXP_ENABLED */
5444 
5445 /**
5446  * xmlValidGetElemDecl:
5447  * @ctxt:  the validation context
5448  * @doc:  a document instance
5449  * @elem:  an element instance
5450  * @extsubset:  pointer, (out) indicate if the declaration was found
5451  *              in the external subset.
5452  *
5453  * Finds a declaration associated to an element in the document.
5454  *
5455  * returns the pointer to the declaration or NULL if not found.
5456  */
5457 static xmlElementPtr
xmlValidGetElemDecl(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlNodePtr elem,int * extsubset)5458 xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5459 	            xmlNodePtr elem, int *extsubset) {
5460     xmlElementPtr elemDecl = NULL;
5461     const xmlChar *prefix = NULL;
5462 
5463     if ((ctxt == NULL) || (doc == NULL) ||
5464         (elem == NULL) || (elem->name == NULL))
5465         return(NULL);
5466     if (extsubset != NULL)
5467 	*extsubset = 0;
5468 
5469     /*
5470      * Fetch the declaration for the qualified name
5471      */
5472     if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5473 	prefix = elem->ns->prefix;
5474 
5475     if (prefix != NULL) {
5476 	elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5477 		                         elem->name, prefix);
5478 	if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5479 	    elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5480 		                             elem->name, prefix);
5481 	    if ((elemDecl != NULL) && (extsubset != NULL))
5482 		*extsubset = 1;
5483 	}
5484     }
5485 
5486     /*
5487      * Fetch the declaration for the non qualified name
5488      * This is "non-strict" validation should be done on the
5489      * full QName but in that case being flexible makes sense.
5490      */
5491     if (elemDecl == NULL) {
5492 	elemDecl = xmlGetDtdQElementDesc(doc->intSubset, elem->name, NULL);
5493 	if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5494 	    elemDecl = xmlGetDtdQElementDesc(doc->extSubset, elem->name, NULL);
5495 	    if ((elemDecl != NULL) && (extsubset != NULL))
5496 		*extsubset = 1;
5497 	}
5498     }
5499     if (elemDecl == NULL) {
5500 	xmlErrValidNode(ctxt, elem,
5501 			XML_DTD_UNKNOWN_ELEM,
5502 	       "No declaration for element %s\n",
5503 	       elem->name, NULL, NULL);
5504     }
5505     return(elemDecl);
5506 }
5507 
5508 #ifdef LIBXML_REGEXP_ENABLED
5509 /**
5510  * xmlValidatePushElement:
5511  * @ctxt:  the validation context
5512  * @doc:  a document instance
5513  * @elem:  an element instance
5514  * @qname:  the qualified name as appearing in the serialization
5515  *
5516  * Push a new element start on the validation stack.
5517  *
5518  * returns 1 if no validation problem was found or 0 otherwise
5519  */
5520 int
xmlValidatePushElement(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlNodePtr elem,const xmlChar * qname)5521 xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5522                        xmlNodePtr elem, const xmlChar *qname) {
5523     int ret = 1;
5524     xmlElementPtr eDecl;
5525     int extsubset = 0;
5526 
5527     if (ctxt == NULL)
5528         return(0);
5529 /* printf("PushElem %s\n", qname); */
5530     if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5531 	xmlValidStatePtr state = ctxt->vstate;
5532 	xmlElementPtr elemDecl;
5533 
5534 	/*
5535 	 * Check the new element against the content model of the new elem.
5536 	 */
5537 	if (state->elemDecl != NULL) {
5538 	    elemDecl = state->elemDecl;
5539 
5540 	    switch(elemDecl->etype) {
5541 		case XML_ELEMENT_TYPE_UNDEFINED:
5542 		    ret = 0;
5543 		    break;
5544 		case XML_ELEMENT_TYPE_EMPTY:
5545 		    xmlErrValidNode(ctxt, state->node,
5546 				    XML_DTD_NOT_EMPTY,
5547 	       "Element %s was declared EMPTY this one has content\n",
5548 			   state->node->name, NULL, NULL);
5549 		    ret = 0;
5550 		    break;
5551 		case XML_ELEMENT_TYPE_ANY:
5552 		    /* I don't think anything is required then */
5553 		    break;
5554 		case XML_ELEMENT_TYPE_MIXED:
5555 		    /* simple case of declared as #PCDATA */
5556 		    if ((elemDecl->content != NULL) &&
5557 			(elemDecl->content->type ==
5558 			 XML_ELEMENT_CONTENT_PCDATA)) {
5559 			xmlErrValidNode(ctxt, state->node,
5560 					XML_DTD_NOT_PCDATA,
5561 	       "Element %s was declared #PCDATA but contains non text nodes\n",
5562 				state->node->name, NULL, NULL);
5563 			ret = 0;
5564 		    } else {
5565 			ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5566 				                    qname);
5567 			if (ret != 1) {
5568 			    xmlErrValidNode(ctxt, state->node,
5569 					    XML_DTD_INVALID_CHILD,
5570 	       "Element %s is not declared in %s list of possible children\n",
5571 				    qname, state->node->name, NULL);
5572 			}
5573 		    }
5574 		    break;
5575 		case XML_ELEMENT_TYPE_ELEMENT:
5576 		    /*
5577 		     * TODO:
5578 		     * VC: Standalone Document Declaration
5579 		     *     - element types with element content, if white space
5580 		     *       occurs directly within any instance of those types.
5581 		     */
5582 		    if (state->exec != NULL) {
5583 			ret = xmlRegExecPushString(state->exec, qname, NULL);
5584                         if (ret == XML_REGEXP_OUT_OF_MEMORY) {
5585                             xmlVErrMemory(ctxt);
5586                             return(0);
5587                         }
5588 			if (ret < 0) {
5589 			    xmlErrValidNode(ctxt, state->node,
5590 					    XML_DTD_CONTENT_MODEL,
5591 	       "Element %s content does not follow the DTD, Misplaced %s\n",
5592 				   state->node->name, qname, NULL);
5593 			    ret = 0;
5594 			} else {
5595 			    ret = 1;
5596 			}
5597 		    }
5598 		    break;
5599 	    }
5600 	}
5601     }
5602     eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5603     vstateVPush(ctxt, eDecl, elem);
5604     return(ret);
5605 }
5606 
5607 /**
5608  * xmlValidatePushCData:
5609  * @ctxt:  the validation context
5610  * @data:  some character data read
5611  * @len:  the length of the data
5612  *
5613  * check the CData parsed for validation in the current stack
5614  *
5615  * returns 1 if no validation problem was found or 0 otherwise
5616  */
5617 int
xmlValidatePushCData(xmlValidCtxtPtr ctxt,const xmlChar * data,int len)5618 xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5619     int ret = 1;
5620 
5621 /* printf("CDATA %s %d\n", data, len); */
5622     if (ctxt == NULL)
5623         return(0);
5624     if (len <= 0)
5625 	return(ret);
5626     if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5627 	xmlValidStatePtr state = ctxt->vstate;
5628 	xmlElementPtr elemDecl;
5629 
5630 	/*
5631 	 * Check the new element against the content model of the new elem.
5632 	 */
5633 	if (state->elemDecl != NULL) {
5634 	    elemDecl = state->elemDecl;
5635 
5636 	    switch(elemDecl->etype) {
5637 		case XML_ELEMENT_TYPE_UNDEFINED:
5638 		    ret = 0;
5639 		    break;
5640 		case XML_ELEMENT_TYPE_EMPTY:
5641 		    xmlErrValidNode(ctxt, state->node,
5642 				    XML_DTD_NOT_EMPTY,
5643 	       "Element %s was declared EMPTY this one has content\n",
5644 			   state->node->name, NULL, NULL);
5645 		    ret = 0;
5646 		    break;
5647 		case XML_ELEMENT_TYPE_ANY:
5648 		    break;
5649 		case XML_ELEMENT_TYPE_MIXED:
5650 		    break;
5651 		case XML_ELEMENT_TYPE_ELEMENT: {
5652                     int i;
5653 
5654                     for (i = 0;i < len;i++) {
5655                         if (!IS_BLANK_CH(data[i])) {
5656                             xmlErrValidNode(ctxt, state->node,
5657                                             XML_DTD_CONTENT_MODEL,
5658        "Element %s content does not follow the DTD, Text not allowed\n",
5659                                    state->node->name, NULL, NULL);
5660                             ret = 0;
5661                             goto done;
5662                         }
5663                     }
5664                     /*
5665                      * TODO:
5666                      * VC: Standalone Document Declaration
5667                      *  element types with element content, if white space
5668                      *  occurs directly within any instance of those types.
5669                      */
5670                     break;
5671                 }
5672 	    }
5673 	}
5674     }
5675 done:
5676     return(ret);
5677 }
5678 
5679 /**
5680  * xmlValidatePopElement:
5681  * @ctxt:  the validation context
5682  * @doc:  a document instance
5683  * @elem:  an element instance
5684  * @qname:  the qualified name as appearing in the serialization
5685  *
5686  * Pop the element end from the validation stack.
5687  *
5688  * returns 1 if no validation problem was found or 0 otherwise
5689  */
5690 int
xmlValidatePopElement(xmlValidCtxtPtr ctxt,xmlDocPtr doc ATTRIBUTE_UNUSED,xmlNodePtr elem ATTRIBUTE_UNUSED,const xmlChar * qname ATTRIBUTE_UNUSED)5691 xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
5692                       xmlNodePtr elem ATTRIBUTE_UNUSED,
5693 		      const xmlChar *qname ATTRIBUTE_UNUSED) {
5694     int ret = 1;
5695 
5696     if (ctxt == NULL)
5697         return(0);
5698 /* printf("PopElem %s\n", qname); */
5699     if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5700 	xmlValidStatePtr state = ctxt->vstate;
5701 	xmlElementPtr elemDecl;
5702 
5703 	/*
5704 	 * Check the new element against the content model of the new elem.
5705 	 */
5706 	if (state->elemDecl != NULL) {
5707 	    elemDecl = state->elemDecl;
5708 
5709 	    if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5710 		if (state->exec != NULL) {
5711 		    ret = xmlRegExecPushString(state->exec, NULL, NULL);
5712 		    if (ret <= 0) {
5713 			xmlErrValidNode(ctxt, state->node,
5714 			                XML_DTD_CONTENT_MODEL,
5715 	   "Element %s content does not follow the DTD, Expecting more children\n",
5716 			       state->node->name, NULL,NULL);
5717 			ret = 0;
5718 		    } else {
5719 			/*
5720 			 * previous validation errors should not generate
5721 			 * a new one here
5722 			 */
5723 			ret = 1;
5724 		    }
5725 		}
5726 	    }
5727 	}
5728 	vstateVPop(ctxt);
5729     }
5730     return(ret);
5731 }
5732 #endif /* LIBXML_REGEXP_ENABLED */
5733 
5734 /**
5735  * xmlValidateOneElement:
5736  * @ctxt:  the validation context
5737  * @doc:  a document instance
5738  * @elem:  an element instance
5739  *
5740  * Try to validate a single element and it's attributes,
5741  * basically it does the following checks as described by the
5742  * XML-1.0 recommendation:
5743  *  - [ VC: Element Valid ]
5744  *  - [ VC: Required Attribute ]
5745  * Then call xmlValidateOneAttribute() for each attribute present.
5746  *
5747  * The ID/IDREF checkings are done separately
5748  *
5749  * returns 1 if valid or 0 otherwise
5750  */
5751 
5752 int
xmlValidateOneElement(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlNodePtr elem)5753 xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5754                       xmlNodePtr elem) {
5755     xmlElementPtr elemDecl = NULL;
5756     xmlElementContentPtr cont;
5757     xmlAttributePtr attr;
5758     xmlNodePtr child;
5759     int ret = 1, tmp;
5760     const xmlChar *name;
5761     int extsubset = 0;
5762 
5763     CHECK_DTD;
5764 
5765     if (elem == NULL) return(0);
5766     switch (elem->type) {
5767         case XML_ATTRIBUTE_NODE:
5768 	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5769 		   "Attribute element not expected\n", NULL, NULL ,NULL);
5770 	    return(0);
5771         case XML_TEXT_NODE:
5772 	    if (elem->children != NULL) {
5773 		xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5774 		                "Text element has children !\n",
5775 				NULL,NULL,NULL);
5776 		return(0);
5777 	    }
5778 	    if (elem->ns != NULL) {
5779 		xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5780 		                "Text element has namespace !\n",
5781 				NULL,NULL,NULL);
5782 		return(0);
5783 	    }
5784 	    if (elem->content == NULL) {
5785 		xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5786 		                "Text element has no content !\n",
5787 				NULL,NULL,NULL);
5788 		return(0);
5789 	    }
5790 	    return(1);
5791         case XML_XINCLUDE_START:
5792         case XML_XINCLUDE_END:
5793             return(1);
5794         case XML_CDATA_SECTION_NODE:
5795         case XML_ENTITY_REF_NODE:
5796         case XML_PI_NODE:
5797         case XML_COMMENT_NODE:
5798 	    return(1);
5799         case XML_ENTITY_NODE:
5800 	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5801 		   "Entity element not expected\n", NULL, NULL ,NULL);
5802 	    return(0);
5803         case XML_NOTATION_NODE:
5804 	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5805 		   "Notation element not expected\n", NULL, NULL ,NULL);
5806 	    return(0);
5807         case XML_DOCUMENT_NODE:
5808         case XML_DOCUMENT_TYPE_NODE:
5809         case XML_DOCUMENT_FRAG_NODE:
5810 	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5811 		   "Document element not expected\n", NULL, NULL ,NULL);
5812 	    return(0);
5813         case XML_HTML_DOCUMENT_NODE:
5814 	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5815 		   "HTML Document not expected\n", NULL, NULL ,NULL);
5816 	    return(0);
5817         case XML_ELEMENT_NODE:
5818 	    break;
5819 	default:
5820 	    xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5821 		   "unknown element type\n", NULL, NULL ,NULL);
5822 	    return(0);
5823     }
5824 
5825     /*
5826      * Fetch the declaration
5827      */
5828     elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5829     if (elemDecl == NULL)
5830 	return(0);
5831 
5832     /*
5833      * If vstateNr is not zero that means continuous validation is
5834      * activated, do not try to check the content model at that level.
5835      */
5836     if (ctxt->vstateNr == 0) {
5837     /* Check that the element content matches the definition */
5838     switch (elemDecl->etype) {
5839         case XML_ELEMENT_TYPE_UNDEFINED:
5840 	    xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM,
5841 	                    "No declaration for element %s\n",
5842 		   elem->name, NULL, NULL);
5843 	    return(0);
5844         case XML_ELEMENT_TYPE_EMPTY:
5845 	    if (elem->children != NULL) {
5846 		xmlErrValidNode(ctxt, elem, XML_DTD_NOT_EMPTY,
5847 	       "Element %s was declared EMPTY this one has content\n",
5848 	               elem->name, NULL, NULL);
5849 		ret = 0;
5850 	    }
5851 	    break;
5852         case XML_ELEMENT_TYPE_ANY:
5853 	    /* I don't think anything is required then */
5854 	    break;
5855         case XML_ELEMENT_TYPE_MIXED:
5856 
5857 	    /* simple case of declared as #PCDATA */
5858 	    if ((elemDecl->content != NULL) &&
5859 		(elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5860 		ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5861 		if (!ret) {
5862 		    xmlErrValidNode(ctxt, elem, XML_DTD_NOT_PCDATA,
5863 	       "Element %s was declared #PCDATA but contains non text nodes\n",
5864 			   elem->name, NULL, NULL);
5865 		}
5866 		break;
5867 	    }
5868 	    child = elem->children;
5869 	    /* Hum, this start to get messy */
5870 	    while (child != NULL) {
5871 	        if (child->type == XML_ELEMENT_NODE) {
5872 		    name = child->name;
5873 		    if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
5874 			xmlChar fn[50];
5875 			xmlChar *fullname;
5876 
5877 			fullname = xmlBuildQName(child->name, child->ns->prefix,
5878 				                 fn, 50);
5879 			if (fullname == NULL)
5880 			    return(0);
5881 			cont = elemDecl->content;
5882 			while (cont != NULL) {
5883 			    if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5884 				if (xmlStrEqual(cont->name, fullname))
5885 				    break;
5886 			    } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5887 			       (cont->c1 != NULL) &&
5888 			       (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5889 				if (xmlStrEqual(cont->c1->name, fullname))
5890 				    break;
5891 			    } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5892 				(cont->c1 == NULL) ||
5893 				(cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5894 				xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5895 					"Internal: MIXED struct corrupted\n",
5896 					NULL);
5897 				break;
5898 			    }
5899 			    cont = cont->c2;
5900 			}
5901 			if ((fullname != fn) && (fullname != child->name))
5902 			    xmlFree(fullname);
5903 			if (cont != NULL)
5904 			    goto child_ok;
5905 		    }
5906 		    cont = elemDecl->content;
5907 		    while (cont != NULL) {
5908 		        if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5909 			    if (xmlStrEqual(cont->name, name)) break;
5910 			} else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5911 			   (cont->c1 != NULL) &&
5912 			   (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5913 			    if (xmlStrEqual(cont->c1->name, name)) break;
5914 			} else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5915 			    (cont->c1 == NULL) ||
5916 			    (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5917 			    xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5918 				    "Internal: MIXED struct corrupted\n",
5919 				    NULL);
5920 			    break;
5921 			}
5922 			cont = cont->c2;
5923 		    }
5924 		    if (cont == NULL) {
5925 			xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_CHILD,
5926 	       "Element %s is not declared in %s list of possible children\n",
5927 			       name, elem->name, NULL);
5928 			ret = 0;
5929 		    }
5930 		}
5931 child_ok:
5932 	        child = child->next;
5933 	    }
5934 	    break;
5935         case XML_ELEMENT_TYPE_ELEMENT:
5936 	    if ((doc->standalone == 1) && (extsubset == 1)) {
5937 		/*
5938 		 * VC: Standalone Document Declaration
5939 		 *     - element types with element content, if white space
5940 		 *       occurs directly within any instance of those types.
5941 		 */
5942 		child = elem->children;
5943 		while (child != NULL) {
5944 		    if (child->type == XML_TEXT_NODE) {
5945 			const xmlChar *content = child->content;
5946 
5947 			while (IS_BLANK_CH(*content))
5948 			    content++;
5949 			if (*content == 0) {
5950 			    xmlErrValidNode(ctxt, elem,
5951 			                    XML_DTD_STANDALONE_WHITE_SPACE,
5952 "standalone: %s declared in the external subset contains white spaces nodes\n",
5953 				   elem->name, NULL, NULL);
5954 			    ret = 0;
5955 			    break;
5956 			}
5957 		    }
5958 		    child =child->next;
5959 		}
5960 	    }
5961 	    child = elem->children;
5962 	    cont = elemDecl->content;
5963 	    tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
5964 	    if (tmp <= 0)
5965 		ret = tmp;
5966 	    break;
5967     }
5968     } /* not continuous */
5969 
5970     /* [ VC: Required Attribute ] */
5971     attr = elemDecl->attributes;
5972     while (attr != NULL) {
5973 	if (attr->def == XML_ATTRIBUTE_REQUIRED) {
5974 	    int qualified = -1;
5975 
5976 	    if ((attr->prefix == NULL) &&
5977 		(xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5978 		xmlNsPtr ns;
5979 
5980 		ns = elem->nsDef;
5981 		while (ns != NULL) {
5982 		    if (ns->prefix == NULL)
5983 			goto found;
5984 		    ns = ns->next;
5985 		}
5986 	    } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5987 		xmlNsPtr ns;
5988 
5989 		ns = elem->nsDef;
5990 		while (ns != NULL) {
5991 		    if (xmlStrEqual(attr->name, ns->prefix))
5992 			goto found;
5993 		    ns = ns->next;
5994 		}
5995 	    } else {
5996 		xmlAttrPtr attrib;
5997 
5998 		attrib = elem->properties;
5999 		while (attrib != NULL) {
6000 		    if (xmlStrEqual(attrib->name, attr->name)) {
6001 			if (attr->prefix != NULL) {
6002 			    xmlNsPtr nameSpace = attrib->ns;
6003 
6004 			    if (nameSpace == NULL)
6005 				nameSpace = elem->ns;
6006 			    /*
6007 			     * qualified names handling is problematic, having a
6008 			     * different prefix should be possible but DTDs don't
6009 			     * allow to define the URI instead of the prefix :-(
6010 			     */
6011 			    if (nameSpace == NULL) {
6012 				if (qualified < 0)
6013 				    qualified = 0;
6014 			    } else if (!xmlStrEqual(nameSpace->prefix,
6015 						    attr->prefix)) {
6016 				if (qualified < 1)
6017 				    qualified = 1;
6018 			    } else
6019 				goto found;
6020 			} else {
6021 			    /*
6022 			     * We should allow applications to define namespaces
6023 			     * for their application even if the DTD doesn't
6024 			     * carry one, otherwise, basically we would always
6025 			     * break.
6026 			     */
6027 			    goto found;
6028 			}
6029 		    }
6030 		    attrib = attrib->next;
6031 		}
6032 	    }
6033 	    if (qualified == -1) {
6034 		if (attr->prefix == NULL) {
6035 		    xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
6036 		       "Element %s does not carry attribute %s\n",
6037 			   elem->name, attr->name, NULL);
6038 		    ret = 0;
6039 	        } else {
6040 		    xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
6041 		       "Element %s does not carry attribute %s:%s\n",
6042 			   elem->name, attr->prefix,attr->name);
6043 		    ret = 0;
6044 		}
6045 	    } else if (qualified == 0) {
6046 		xmlErrValidWarning(ctxt, elem, XML_DTD_NO_PREFIX,
6047 		   "Element %s required attribute %s:%s has no prefix\n",
6048 		       elem->name, attr->prefix, attr->name);
6049 	    } else if (qualified == 1) {
6050 		xmlErrValidWarning(ctxt, elem, XML_DTD_DIFFERENT_PREFIX,
6051 		   "Element %s required attribute %s:%s has different prefix\n",
6052 		       elem->name, attr->prefix, attr->name);
6053 	    }
6054 	} else if (attr->def == XML_ATTRIBUTE_FIXED) {
6055 	    /*
6056 	     * Special tests checking #FIXED namespace declarations
6057 	     * have the right value since this is not done as an
6058 	     * attribute checking
6059 	     */
6060 	    if ((attr->prefix == NULL) &&
6061 		(xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
6062 		xmlNsPtr ns;
6063 
6064 		ns = elem->nsDef;
6065 		while (ns != NULL) {
6066 		    if (ns->prefix == NULL) {
6067 			if (!xmlStrEqual(attr->defaultValue, ns->href)) {
6068 			    xmlErrValidNode(ctxt, elem,
6069 			           XML_DTD_ELEM_DEFAULT_NAMESPACE,
6070    "Element %s namespace name for default namespace does not match the DTD\n",
6071 				   elem->name, NULL, NULL);
6072 			    ret = 0;
6073 			}
6074 			goto found;
6075 		    }
6076 		    ns = ns->next;
6077 		}
6078 	    } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
6079 		xmlNsPtr ns;
6080 
6081 		ns = elem->nsDef;
6082 		while (ns != NULL) {
6083 		    if (xmlStrEqual(attr->name, ns->prefix)) {
6084 			if (!xmlStrEqual(attr->defaultValue, ns->href)) {
6085 			    xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
6086 		   "Element %s namespace name for %s does not match the DTD\n",
6087 				   elem->name, ns->prefix, NULL);
6088 			    ret = 0;
6089 			}
6090 			goto found;
6091 		    }
6092 		    ns = ns->next;
6093 		}
6094 	    }
6095 	}
6096 found:
6097         attr = attr->nexth;
6098     }
6099     return(ret);
6100 }
6101 
6102 /**
6103  * xmlValidateRoot:
6104  * @ctxt:  the validation context
6105  * @doc:  a document instance
6106  *
6107  * Try to validate a the root element
6108  * basically it does the following check as described by the
6109  * XML-1.0 recommendation:
6110  *  - [ VC: Root Element Type ]
6111  * it doesn't try to recurse or apply other check to the element
6112  *
6113  * returns 1 if valid or 0 otherwise
6114  */
6115 
6116 int
xmlValidateRoot(xmlValidCtxtPtr ctxt,xmlDocPtr doc)6117 xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6118     xmlNodePtr root;
6119     int ret;
6120 
6121     if (doc == NULL) return(0);
6122 
6123     root = xmlDocGetRootElement(doc);
6124     if ((root == NULL) || (root->name == NULL)) {
6125 	xmlErrValid(ctxt, XML_DTD_NO_ROOT,
6126 	            "no root element\n", NULL);
6127         return(0);
6128     }
6129 
6130     /*
6131      * When doing post validation against a separate DTD, those may
6132      * no internal subset has been generated
6133      */
6134     if ((doc->intSubset != NULL) &&
6135 	(doc->intSubset->name != NULL)) {
6136 	/*
6137 	 * Check first the document root against the NQName
6138 	 */
6139 	if (!xmlStrEqual(doc->intSubset->name, root->name)) {
6140 	    if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
6141 		xmlChar fn[50];
6142 		xmlChar *fullname;
6143 
6144 		fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
6145 		if (fullname == NULL) {
6146 		    xmlVErrMemory(ctxt);
6147 		    return(0);
6148 		}
6149 		ret = xmlStrEqual(doc->intSubset->name, fullname);
6150 		if ((fullname != fn) && (fullname != root->name))
6151 		    xmlFree(fullname);
6152 		if (ret == 1)
6153 		    goto name_ok;
6154 	    }
6155 	    if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
6156 		(xmlStrEqual(root->name, BAD_CAST "html")))
6157 		goto name_ok;
6158 	    xmlErrValidNode(ctxt, root, XML_DTD_ROOT_NAME,
6159 		   "root and DTD name do not match '%s' and '%s'\n",
6160 		   root->name, doc->intSubset->name, NULL);
6161 	    return(0);
6162 	}
6163     }
6164 name_ok:
6165     return(1);
6166 }
6167 
6168 
6169 /**
6170  * xmlValidateElement:
6171  * @ctxt:  the validation context
6172  * @doc:  a document instance
6173  * @root:  an element instance
6174  *
6175  * Try to validate the subtree under an element
6176  *
6177  * returns 1 if valid or 0 otherwise
6178  */
6179 
6180 int
xmlValidateElement(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlNodePtr root)6181 xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr root) {
6182     xmlNodePtr elem;
6183     xmlAttrPtr attr;
6184     xmlNsPtr ns;
6185     const xmlChar *value;
6186     int ret = 1;
6187 
6188     if (root == NULL) return(0);
6189 
6190     CHECK_DTD;
6191 
6192     elem = root;
6193     while (1) {
6194         ret &= xmlValidateOneElement(ctxt, doc, elem);
6195 
6196         if (elem->type == XML_ELEMENT_NODE) {
6197             attr = elem->properties;
6198             while (attr != NULL) {
6199                 value = xmlNodeListGetString(doc, attr->children, 0);
6200                 if (value == NULL)
6201                     xmlVErrMemory(ctxt);
6202                 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
6203                 if (value != NULL)
6204                     xmlFree((char *)value);
6205                 attr= attr->next;
6206             }
6207 
6208             ns = elem->nsDef;
6209             while (ns != NULL) {
6210                 if (elem->ns == NULL)
6211                     ret &= xmlValidateOneNamespace(ctxt, doc, elem, NULL,
6212                                                    ns, ns->href);
6213                 else
6214                     ret &= xmlValidateOneNamespace(ctxt, doc, elem,
6215                                                    elem->ns->prefix, ns,
6216                                                    ns->href);
6217                 ns = ns->next;
6218             }
6219 
6220             if (elem->children != NULL) {
6221                 elem = elem->children;
6222                 continue;
6223             }
6224         }
6225 
6226         while (1) {
6227             if (elem == root)
6228                 goto done;
6229             if (elem->next != NULL)
6230                 break;
6231             elem = elem->parent;
6232         }
6233         elem = elem->next;
6234     }
6235 
6236 done:
6237     return(ret);
6238 }
6239 
6240 /**
6241  * xmlValidateRef:
6242  * @ref:   A reference to be validated
6243  * @ctxt:  Validation context
6244  * @name:  Name of ID we are searching for
6245  *
6246  */
6247 static void
xmlValidateRef(xmlRefPtr ref,xmlValidCtxtPtr ctxt,const xmlChar * name)6248 xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
6249 	                   const xmlChar *name) {
6250     xmlAttrPtr id;
6251     xmlAttrPtr attr;
6252 
6253     if (ref == NULL)
6254 	return;
6255     if ((ref->attr == NULL) && (ref->name == NULL))
6256 	return;
6257     attr = ref->attr;
6258     if (attr == NULL) {
6259 	xmlChar *dup, *str = NULL, *cur, save;
6260 
6261 	dup = xmlStrdup(name);
6262 	if (dup == NULL) {
6263             xmlVErrMemory(ctxt);
6264 	    return;
6265 	}
6266 	cur = dup;
6267 	while (*cur != 0) {
6268 	    str = cur;
6269 	    while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
6270 	    save = *cur;
6271 	    *cur = 0;
6272 	    id = xmlGetID(ctxt->doc, str);
6273 	    if (id == NULL) {
6274 		xmlErrValidNodeNr(ctxt, NULL, XML_DTD_UNKNOWN_ID,
6275 	   "attribute %s line %d references an unknown ID \"%s\"\n",
6276 		       ref->name, ref->lineno, str);
6277 		ctxt->valid = 0;
6278 	    }
6279 	    if (save == 0)
6280 		break;
6281 	    *cur = save;
6282 	    while (IS_BLANK_CH(*cur)) cur++;
6283 	}
6284 	xmlFree(dup);
6285     } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
6286 	id = xmlGetID(ctxt->doc, name);
6287 	if (id == NULL) {
6288 	    xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
6289 	   "IDREF attribute %s references an unknown ID \"%s\"\n",
6290 		   attr->name, name, NULL);
6291 	    ctxt->valid = 0;
6292 	}
6293     } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
6294 	xmlChar *dup, *str = NULL, *cur, save;
6295 
6296 	dup = xmlStrdup(name);
6297 	if (dup == NULL) {
6298 	    xmlVErrMemory(ctxt);
6299 	    ctxt->valid = 0;
6300 	    return;
6301 	}
6302 	cur = dup;
6303 	while (*cur != 0) {
6304 	    str = cur;
6305 	    while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
6306 	    save = *cur;
6307 	    *cur = 0;
6308 	    id = xmlGetID(ctxt->doc, str);
6309 	    if (id == NULL) {
6310 		xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
6311 	   "IDREFS attribute %s references an unknown ID \"%s\"\n",
6312 			     attr->name, str, NULL);
6313 		ctxt->valid = 0;
6314 	    }
6315 	    if (save == 0)
6316 		break;
6317 	    *cur = save;
6318 	    while (IS_BLANK_CH(*cur)) cur++;
6319 	}
6320 	xmlFree(dup);
6321     }
6322 }
6323 
6324 /**
6325  * xmlWalkValidateList:
6326  * @data:  Contents of current link
6327  * @user:  Value supplied by the user
6328  *
6329  * Returns 0 to abort the walk or 1 to continue
6330  */
6331 static int
xmlWalkValidateList(const void * data,void * user)6332 xmlWalkValidateList(const void *data, void *user)
6333 {
6334 	xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
6335 	xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6336 	return 1;
6337 }
6338 
6339 /**
6340  * xmlValidateCheckRefCallback:
6341  * @ref_list:  List of references
6342  * @ctxt:  Validation context
6343  * @name:  Name of ID we are searching for
6344  *
6345  */
6346 static void
xmlValidateCheckRefCallback(void * payload,void * data,const xmlChar * name)6347 xmlValidateCheckRefCallback(void *payload, void *data, const xmlChar *name) {
6348     xmlListPtr ref_list = (xmlListPtr) payload;
6349     xmlValidCtxtPtr ctxt = (xmlValidCtxtPtr) data;
6350     xmlValidateMemo memo;
6351 
6352     if (ref_list == NULL)
6353 	return;
6354     memo.ctxt = ctxt;
6355     memo.name = name;
6356 
6357     xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6358 
6359 }
6360 
6361 /**
6362  * xmlValidateDocumentFinal:
6363  * @ctxt:  the validation context
6364  * @doc:  a document instance
6365  *
6366  * Does the final step for the document validation once all the
6367  * incremental validation steps have been completed
6368  *
6369  * basically it does the following checks described by the XML Rec
6370  *
6371  * Check all the IDREF/IDREFS attributes definition for validity
6372  *
6373  * returns 1 if valid or 0 otherwise
6374  */
6375 
6376 int
xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt,xmlDocPtr doc)6377 xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6378     xmlRefTablePtr table;
6379     xmlParserCtxtPtr pctxt = NULL;
6380     xmlParserInputPtr oldInput = NULL;
6381 
6382     if (ctxt == NULL)
6383         return(0);
6384     if (doc == NULL) {
6385         xmlErrValid(ctxt, XML_DTD_NO_DOC,
6386 		"xmlValidateDocumentFinal: doc == NULL\n", NULL);
6387 	return(0);
6388     }
6389 
6390     /*
6391      * Check all the NOTATION/NOTATIONS attributes
6392      */
6393     /*
6394      * Check all the ENTITY/ENTITIES attributes definition for validity
6395      */
6396     /*
6397      * Check all the IDREF/IDREFS attributes definition for validity
6398      */
6399 
6400     /*
6401      * Don't print line numbers.
6402      */
6403     if (ctxt->flags & XML_VCTXT_USE_PCTXT) {
6404         pctxt = ctxt->userData;
6405         oldInput = pctxt->input;
6406         pctxt->input = NULL;
6407     }
6408 
6409     table = (xmlRefTablePtr) doc->refs;
6410     ctxt->doc = doc;
6411     ctxt->valid = 1;
6412     xmlHashScan(table, xmlValidateCheckRefCallback, ctxt);
6413 
6414     if (ctxt->flags & XML_VCTXT_USE_PCTXT)
6415         pctxt->input = oldInput;
6416 
6417     return(ctxt->valid);
6418 }
6419 
6420 /**
6421  * xmlValidateDtd:
6422  * @ctxt:  the validation context
6423  * @doc:  a document instance
6424  * @dtd:  a dtd instance
6425  *
6426  * Try to validate the document against the dtd instance
6427  *
6428  * Basically it does check all the definitions in the DtD.
6429  * Note the the internal subset (if present) is de-coupled
6430  * (i.e. not used), which could give problems if ID or IDREF
6431  * is present.
6432  *
6433  * returns 1 if valid or 0 otherwise
6434  */
6435 
6436 int
xmlValidateDtd(xmlValidCtxtPtr ctxt,xmlDocPtr doc,xmlDtdPtr dtd)6437 xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6438     int ret;
6439     xmlDtdPtr oldExt, oldInt;
6440     xmlNodePtr root;
6441 
6442     if (dtd == NULL) return(0);
6443     if (doc == NULL) return(0);
6444     oldExt = doc->extSubset;
6445     oldInt = doc->intSubset;
6446     doc->extSubset = dtd;
6447     doc->intSubset = NULL;
6448     ret = xmlValidateRoot(ctxt, doc);
6449     if (ret == 0) {
6450 	doc->extSubset = oldExt;
6451 	doc->intSubset = oldInt;
6452 	return(ret);
6453     }
6454     if (doc->ids != NULL) {
6455           xmlFreeIDTable(doc->ids);
6456           doc->ids = NULL;
6457     }
6458     if (doc->refs != NULL) {
6459           xmlFreeRefTable(doc->refs);
6460           doc->refs = NULL;
6461     }
6462     root = xmlDocGetRootElement(doc);
6463     ret = xmlValidateElement(ctxt, doc, root);
6464     ret &= xmlValidateDocumentFinal(ctxt, doc);
6465     doc->extSubset = oldExt;
6466     doc->intSubset = oldInt;
6467     return(ret);
6468 }
6469 
6470 static void
xmlValidateNotationCallback(void * payload,void * data,const xmlChar * name ATTRIBUTE_UNUSED)6471 xmlValidateNotationCallback(void *payload, void *data,
6472 	                    const xmlChar *name ATTRIBUTE_UNUSED) {
6473     xmlEntityPtr cur = (xmlEntityPtr) payload;
6474     xmlValidCtxtPtr ctxt = (xmlValidCtxtPtr) data;
6475     if (cur == NULL)
6476 	return;
6477     if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6478 	xmlChar *notation = cur->content;
6479 
6480 	if (notation != NULL) {
6481 	    int ret;
6482 
6483 	    ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6484 	    if (ret != 1) {
6485 		ctxt->valid = 0;
6486 	    }
6487 	}
6488     }
6489 }
6490 
6491 static void
xmlValidateAttributeCallback(void * payload,void * data,const xmlChar * name ATTRIBUTE_UNUSED)6492 xmlValidateAttributeCallback(void *payload, void *data,
6493 	                     const xmlChar *name ATTRIBUTE_UNUSED) {
6494     xmlAttributePtr cur = (xmlAttributePtr) payload;
6495     xmlValidCtxtPtr ctxt = (xmlValidCtxtPtr) data;
6496     int ret;
6497     xmlDocPtr doc;
6498     xmlElementPtr elem = NULL;
6499 
6500     if (cur == NULL)
6501 	return;
6502     switch (cur->atype) {
6503 	case XML_ATTRIBUTE_CDATA:
6504 	case XML_ATTRIBUTE_ID:
6505 	case XML_ATTRIBUTE_IDREF	:
6506 	case XML_ATTRIBUTE_IDREFS:
6507 	case XML_ATTRIBUTE_NMTOKEN:
6508 	case XML_ATTRIBUTE_NMTOKENS:
6509 	case XML_ATTRIBUTE_ENUMERATION:
6510 	    break;
6511 	case XML_ATTRIBUTE_ENTITY:
6512 	case XML_ATTRIBUTE_ENTITIES:
6513 	case XML_ATTRIBUTE_NOTATION:
6514 	    if (cur->defaultValue != NULL) {
6515 
6516 		ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6517 			                         cur->atype, cur->defaultValue);
6518 		if ((ret == 0) && (ctxt->valid == 1))
6519 		    ctxt->valid = 0;
6520 	    }
6521 	    if (cur->tree != NULL) {
6522 		xmlEnumerationPtr tree = cur->tree;
6523 		while (tree != NULL) {
6524 		    ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
6525 				    cur->name, cur->atype, tree->name);
6526 		    if ((ret == 0) && (ctxt->valid == 1))
6527 			ctxt->valid = 0;
6528 		    tree = tree->next;
6529 		}
6530 	    }
6531     }
6532     if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6533 	doc = cur->doc;
6534 	if (cur->elem == NULL) {
6535 	    xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
6536 		   "xmlValidateAttributeCallback(%s): internal error\n",
6537 		   (const char *) cur->name);
6538 	    return;
6539 	}
6540 
6541 	if (doc != NULL)
6542 	    elem = xmlCtxtGetDtdElementDesc(ctxt, doc->intSubset, cur->elem);
6543 	if ((elem == NULL) && (doc != NULL))
6544 	    elem = xmlCtxtGetDtdElementDesc(ctxt, doc->extSubset, cur->elem);
6545 	if ((elem == NULL) && (cur->parent != NULL) &&
6546 	    (cur->parent->type == XML_DTD_NODE))
6547 	    elem = xmlCtxtGetDtdElementDesc(ctxt, (xmlDtdPtr) cur->parent,
6548                                             cur->elem);
6549 	if (elem == NULL) {
6550 	    xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM,
6551 		   "attribute %s: could not find decl for element %s\n",
6552 		   cur->name, cur->elem, NULL);
6553 	    return;
6554 	}
6555 	if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6556 	    xmlErrValidNode(ctxt, NULL, XML_DTD_EMPTY_NOTATION,
6557 		   "NOTATION attribute %s declared for EMPTY element %s\n",
6558 		   cur->name, cur->elem, NULL);
6559 	    ctxt->valid = 0;
6560 	}
6561     }
6562 }
6563 
6564 /**
6565  * xmlValidateDtdFinal:
6566  * @ctxt:  the validation context
6567  * @doc:  a document instance
6568  *
6569  * Does the final step for the dtds validation once all the
6570  * subsets have been parsed
6571  *
6572  * basically it does the following checks described by the XML Rec
6573  * - check that ENTITY and ENTITIES type attributes default or
6574  *   possible values matches one of the defined entities.
6575  * - check that NOTATION type attributes default or
6576  *   possible values matches one of the defined notations.
6577  *
6578  * returns 1 if valid or 0 if invalid and -1 if not well-formed
6579  */
6580 
6581 int
xmlValidateDtdFinal(xmlValidCtxtPtr ctxt,xmlDocPtr doc)6582 xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6583     xmlDtdPtr dtd;
6584     xmlAttributeTablePtr table;
6585     xmlEntitiesTablePtr entities;
6586 
6587     if ((doc == NULL) || (ctxt == NULL)) return(0);
6588     if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6589 	return(0);
6590     ctxt->doc = doc;
6591     ctxt->valid = 1;
6592     dtd = doc->intSubset;
6593     if ((dtd != NULL) && (dtd->attributes != NULL)) {
6594 	table = (xmlAttributeTablePtr) dtd->attributes;
6595 	xmlHashScan(table, xmlValidateAttributeCallback, ctxt);
6596     }
6597     if ((dtd != NULL) && (dtd->entities != NULL)) {
6598 	entities = (xmlEntitiesTablePtr) dtd->entities;
6599 	xmlHashScan(entities, xmlValidateNotationCallback, ctxt);
6600     }
6601     dtd = doc->extSubset;
6602     if ((dtd != NULL) && (dtd->attributes != NULL)) {
6603 	table = (xmlAttributeTablePtr) dtd->attributes;
6604 	xmlHashScan(table, xmlValidateAttributeCallback, ctxt);
6605     }
6606     if ((dtd != NULL) && (dtd->entities != NULL)) {
6607 	entities = (xmlEntitiesTablePtr) dtd->entities;
6608 	xmlHashScan(entities, xmlValidateNotationCallback, ctxt);
6609     }
6610     return(ctxt->valid);
6611 }
6612 
6613 /**
6614  * xmlValidateDocument:
6615  * @ctxt:  the validation context
6616  * @doc:  a document instance
6617  *
6618  * Try to validate the document instance
6619  *
6620  * basically it does the all the checks described by the XML Rec
6621  * i.e. validates the internal and external subset (if present)
6622  * and validate the document tree.
6623  *
6624  * returns 1 if valid or 0 otherwise
6625  */
6626 
6627 int
xmlValidateDocument(xmlValidCtxtPtr ctxt,xmlDocPtr doc)6628 xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6629     int ret;
6630     xmlNodePtr root;
6631 
6632     if (doc == NULL)
6633         return(0);
6634     if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6635         xmlErrValid(ctxt, XML_DTD_NO_DTD,
6636 	            "no DTD found!\n", NULL);
6637 	return(0);
6638     }
6639     if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6640 	(doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6641 	xmlChar *sysID;
6642 	if (doc->intSubset->SystemID != NULL) {
6643 	    sysID = xmlBuildURI(doc->intSubset->SystemID,
6644 			doc->URL);
6645 	    if (sysID == NULL) {
6646 	        xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
6647 			"Could not build URI for external subset \"%s\"\n",
6648 			(const char *) doc->intSubset->SystemID);
6649 		return 0;
6650 	    }
6651 	} else
6652 	    sysID = NULL;
6653         doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6654 			(const xmlChar *)sysID);
6655 	if (sysID != NULL)
6656 	    xmlFree(sysID);
6657         if (doc->extSubset == NULL) {
6658 	    if (doc->intSubset->SystemID != NULL) {
6659 		xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
6660 		       "Could not load the external subset \"%s\"\n",
6661 		       (const char *) doc->intSubset->SystemID);
6662 	    } else {
6663 		xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
6664 		       "Could not load the external subset \"%s\"\n",
6665 		       (const char *) doc->intSubset->ExternalID);
6666 	    }
6667 	    return(0);
6668 	}
6669     }
6670 
6671     if (doc->ids != NULL) {
6672           xmlFreeIDTable(doc->ids);
6673           doc->ids = NULL;
6674     }
6675     if (doc->refs != NULL) {
6676           xmlFreeRefTable(doc->refs);
6677           doc->refs = NULL;
6678     }
6679     ret = xmlValidateDtdFinal(ctxt, doc);
6680     if (!xmlValidateRoot(ctxt, doc)) return(0);
6681 
6682     root = xmlDocGetRootElement(doc);
6683     ret &= xmlValidateElement(ctxt, doc, root);
6684     ret &= xmlValidateDocumentFinal(ctxt, doc);
6685     return(ret);
6686 }
6687 
6688 /************************************************************************
6689  *									*
6690  *		Routines for dynamic validation editing			*
6691  *									*
6692  ************************************************************************/
6693 
6694 /**
6695  * xmlValidGetPotentialChildren:
6696  * @ctree:  an element content tree
6697  * @names:  an array to store the list of child names
6698  * @len:  a pointer to the number of element in the list
6699  * @max:  the size of the array
6700  *
6701  * Build/extend a list of  potential children allowed by the content tree
6702  *
6703  * returns the number of element in the list, or -1 in case of error.
6704  */
6705 
6706 int
xmlValidGetPotentialChildren(xmlElementContent * ctree,const xmlChar ** names,int * len,int max)6707 xmlValidGetPotentialChildren(xmlElementContent *ctree,
6708                              const xmlChar **names,
6709                              int *len, int max) {
6710     int i;
6711 
6712     if ((ctree == NULL) || (names == NULL) || (len == NULL))
6713         return(-1);
6714     if (*len >= max) return(*len);
6715 
6716     switch (ctree->type) {
6717 	case XML_ELEMENT_CONTENT_PCDATA:
6718 	    for (i = 0; i < *len;i++)
6719 		if (xmlStrEqual(BAD_CAST "#PCDATA", names[i])) return(*len);
6720 	    names[(*len)++] = BAD_CAST "#PCDATA";
6721 	    break;
6722 	case XML_ELEMENT_CONTENT_ELEMENT:
6723 	    for (i = 0; i < *len;i++)
6724 		if (xmlStrEqual(ctree->name, names[i])) return(*len);
6725 	    names[(*len)++] = ctree->name;
6726 	    break;
6727 	case XML_ELEMENT_CONTENT_SEQ:
6728 	    xmlValidGetPotentialChildren(ctree->c1, names, len, max);
6729 	    xmlValidGetPotentialChildren(ctree->c2, names, len, max);
6730 	    break;
6731 	case XML_ELEMENT_CONTENT_OR:
6732 	    xmlValidGetPotentialChildren(ctree->c1, names, len, max);
6733 	    xmlValidGetPotentialChildren(ctree->c2, names, len, max);
6734 	    break;
6735    }
6736 
6737    return(*len);
6738 }
6739 
6740 /*
6741  * Dummy function to suppress messages while we try out valid elements
6742  */
xmlNoValidityErr(void * ctx ATTRIBUTE_UNUSED,const char * msg ATTRIBUTE_UNUSED,...)6743 static void xmlNoValidityErr(void *ctx ATTRIBUTE_UNUSED,
6744                                 const char *msg ATTRIBUTE_UNUSED, ...) {
6745     return;
6746 }
6747 
6748 /**
6749  * xmlValidGetValidElements:
6750  * @prev:  an element to insert after
6751  * @next:  an element to insert next
6752  * @names:  an array to store the list of child names
6753  * @max:  the size of the array
6754  *
6755  * This function returns the list of authorized children to insert
6756  * within an existing tree while respecting the validity constraints
6757  * forced by the Dtd. The insertion point is defined using @prev and
6758  * @next in the following ways:
6759  *  to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6760  *  to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6761  *  to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6762  *  to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6763  *  to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6764  *
6765  * pointers to the element names are inserted at the beginning of the array
6766  * and do not need to be freed.
6767  *
6768  * returns the number of element in the list, or -1 in case of error. If
6769  *    the function returns the value @max the caller is invited to grow the
6770  *    receiving array and retry.
6771  */
6772 
6773 int
xmlValidGetValidElements(xmlNode * prev,xmlNode * next,const xmlChar ** names,int max)6774 xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **names,
6775                          int max) {
6776     xmlValidCtxt vctxt;
6777     int nb_valid_elements = 0;
6778     const xmlChar *elements[256]={0};
6779     int nb_elements = 0, i;
6780     const xmlChar *name;
6781 
6782     xmlNode *ref_node;
6783     xmlNode *parent;
6784     xmlNode *test_node;
6785 
6786     xmlNode *prev_next;
6787     xmlNode *next_prev;
6788     xmlNode *parent_childs;
6789     xmlNode *parent_last;
6790 
6791     xmlElement *element_desc;
6792 
6793     if (prev == NULL && next == NULL)
6794         return(-1);
6795 
6796     if (names == NULL) return(-1);
6797     if (max <= 0) return(-1);
6798 
6799     memset(&vctxt, 0, sizeof (xmlValidCtxt));
6800     vctxt.error = xmlNoValidityErr;	/* this suppresses err/warn output */
6801 
6802     nb_valid_elements = 0;
6803     ref_node = prev ? prev : next;
6804     parent = ref_node->parent;
6805 
6806     /*
6807      * Retrieves the parent element declaration
6808      */
6809     element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6810                                          parent->name);
6811     if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6812         element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6813                                              parent->name);
6814     if (element_desc == NULL) return(-1);
6815 
6816     /*
6817      * Do a backup of the current tree structure
6818      */
6819     prev_next = prev ? prev->next : NULL;
6820     next_prev = next ? next->prev : NULL;
6821     parent_childs = parent->children;
6822     parent_last = parent->last;
6823 
6824     /*
6825      * Creates a dummy node and insert it into the tree
6826      */
6827     test_node = xmlNewDocNode (ref_node->doc, NULL, BAD_CAST "<!dummy?>", NULL);
6828     if (test_node == NULL)
6829         return(-1);
6830 
6831     test_node->parent = parent;
6832     test_node->prev = prev;
6833     test_node->next = next;
6834     name = test_node->name;
6835 
6836     if (prev) prev->next = test_node;
6837     else parent->children = test_node;
6838 
6839     if (next) next->prev = test_node;
6840     else parent->last = test_node;
6841 
6842     /*
6843      * Insert each potential child node and check if the parent is
6844      * still valid
6845      */
6846     nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6847 		       elements, &nb_elements, 256);
6848 
6849     for (i = 0;i < nb_elements;i++) {
6850 	test_node->name = elements[i];
6851 	if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
6852 	    int j;
6853 
6854 	    for (j = 0; j < nb_valid_elements;j++)
6855 		if (xmlStrEqual(elements[i], names[j])) break;
6856 	    names[nb_valid_elements++] = elements[i];
6857 	    if (nb_valid_elements >= max) break;
6858 	}
6859     }
6860 
6861     /*
6862      * Restore the tree structure
6863      */
6864     if (prev) prev->next = prev_next;
6865     if (next) next->prev = next_prev;
6866     parent->children = parent_childs;
6867     parent->last = parent_last;
6868 
6869     /*
6870      * Free up the dummy node
6871      */
6872     test_node->name = name;
6873     xmlFreeNode(test_node);
6874 
6875     return(nb_valid_elements);
6876 }
6877 #endif /* LIBXML_VALID_ENABLED */
6878 
6879