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