]> granicus.if.org Git - imagemagick/blob - MagickCore/xml-tree.c
(no commit message)
[imagemagick] / MagickCore / xml-tree.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                             X   X  M   M  L                                 %
7 %                              X X   MM MM  L                                 %
8 %                               X    M M M  L                                 %
9 %                              X X   M   M  L                                 %
10 %                             X   X  M   M  LLLLL                             %
11 %                                                                             %
12 %                         TTTTT  RRRR   EEEEE  EEEEE                          %
13 %                           T    R   R  E      E                              %
14 %                           T    RRRR   EEE    EEE                            %
15 %                           T    R R    E      E                              %
16 %                           T    R  R   EEEEE  EEEEE                          %
17 %                                                                             %
18 %                                                                             %
19 %                              XML Tree Methods                               %
20 %                                                                             %
21 %                              Software Design                                %
22 %                                   Cristy                                    %
23 %                               December 2004                                 %
24 %                                                                             %
25 %                                                                             %
26 %  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
27 %  dedicated to making software imaging solutions freely available.           %
28 %                                                                             %
29 %  You may not use this file except in compliance with the License.  You may  %
30 %  obtain a copy of the License at                                            %
31 %                                                                             %
32 %    http://www.imagemagick.org/script/license.php                            %
33 %                                                                             %
34 %  Unless required by applicable law or agreed to in writing, software        %
35 %  distributed under the License is distributed on an "AS IS" BASIS,          %
36 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
37 %  See the License for the specific language governing permissions and        %
38 %  limitations under the License.                                             %
39 %                                                                             %
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 %
42 %  This module implements the standard handy xml-tree methods for storing and
43 %  retrieving nodes and attributes from an XML string.
44 %
45 */
46 \f
47 /*
48   Include declarations.
49 */
50 #include "MagickCore/studio.h"
51 #include "MagickCore/blob.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/log.h"
55 #include "MagickCore/memory_.h"
56 #include "MagickCore/semaphore.h"
57 #include "MagickCore/string_.h"
58 #include "MagickCore/string-private.h"
59 #include "MagickCore/token-private.h"
60 #include "MagickCore/xml-tree.h"
61 #include "MagickCore/xml-tree-private.h"
62 #include "MagickCore/utility.h"
63 #include "MagickCore/utility-private.h"
64 \f
65 /*
66   Define declarations.
67 */
68 #define NumberPredefinedEntities  10
69 #define XMLWhitespace "\t\r\n "
70 \f
71 /*
72   Typedef declarations.
73 */
74 struct _XMLTreeInfo
75 {
76   char
77     *tag,
78     **attributes,
79     *content;
80
81   size_t
82     offset;
83
84   XMLTreeInfo
85     *parent,
86     *next,
87     *sibling,
88     *ordered,
89     *child;
90
91   MagickBooleanType
92     debug;
93
94   SemaphoreInfo
95     *semaphore;
96
97   size_t
98     signature;
99 };
100
101 typedef struct _XMLTreeRoot
102   XMLTreeRoot;
103
104 struct _XMLTreeRoot
105 {
106   struct _XMLTreeInfo
107     root;
108
109   XMLTreeInfo
110     *node;
111
112   MagickBooleanType
113     standalone;
114
115   char
116     ***processing_instructions,
117     **entities,
118     ***attributes;
119
120   MagickBooleanType
121     debug;
122
123   SemaphoreInfo
124     *semaphore;
125
126   size_t
127     signature;
128 };
129 \f
130 /*
131   Global declarations.
132 */
133 static char
134   *sentinel[] = { (char *) NULL };
135 \f
136 /*
137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
138 %                                                                             %
139 %                                                                             %
140 %                                                                             %
141 %   A d d C h i l d T o X M L T r e e                                         %
142 %                                                                             %
143 %                                                                             %
144 %                                                                             %
145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146 %
147 %  AddChildToXMLTree() adds a child tag at an offset relative to the start of
148 %  the parent tag's character content.  Return the child tag.
149 %
150 %  The format of the AddChildToXMLTree method is:
151 %
152 %      XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,const char *tag,
153 %        const size_t offset)
154 %
155 %  A description of each parameter follows:
156 %
157 %    o xml_info: the xml info.
158 %
159 %    o tag: the tag.
160 %
161 %    o offset: the tag offset.
162 %
163 */
164 MagickExport XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,
165   const char *tag,const size_t offset)
166 {
167   XMLTreeInfo
168     *child;
169
170   if (xml_info == (XMLTreeInfo *) NULL)
171     return((XMLTreeInfo *) NULL);
172   child=(XMLTreeInfo *) AcquireMagickMemory(sizeof(*child));
173   if (child == (XMLTreeInfo *) NULL)
174     return((XMLTreeInfo *) NULL);
175   (void) ResetMagickMemory(child,0,sizeof(*child));
176   child->tag=ConstantString(tag);
177   child->attributes=sentinel;
178   child->content=ConstantString("");
179   child->debug=IsEventLogging();
180   child->signature=MagickSignature;
181   return(InsertTagIntoXMLTree(xml_info,child,offset));
182 }
183 \f
184 /*
185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
186 %                                                                             %
187 %                                                                             %
188 %                                                                             %
189 %   A d d P a t h T o X M L T r e e                                           %
190 %                                                                             %
191 %                                                                             %
192 %                                                                             %
193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
194 %
195 %  AddPathToXMLTree() adds a child tag at an offset relative to the start of
196 %  the parent tag's character content.  This method returns the child tag.
197 %
198 %  The format of the AddPathToXMLTree method is:
199 %
200 %      XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,const char *path,
201 %        const size_t offset)
202 %
203 %  A description of each parameter follows:
204 %
205 %    o xml_info: the xml info.
206 %
207 %    o path: the path.
208 %
209 %    o offset: the tag offset.
210 %
211 */
212 MagickPrivate XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,
213   const char *path,const size_t offset)
214 {
215   char
216     **components,
217     subnode[MaxTextExtent],
218     tag[MaxTextExtent];
219
220   register ssize_t
221     i;
222
223   size_t
224     number_components;
225
226   ssize_t
227     j;
228
229   XMLTreeInfo
230     *child,
231     *node;
232
233   assert(xml_info != (XMLTreeInfo *) NULL);
234   assert((xml_info->signature == MagickSignature) ||
235          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
236   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
237   node=xml_info;
238   components=GetPathComponents(path,&number_components);
239   if (components == (char **) NULL)
240     return((XMLTreeInfo *) NULL);
241   for (i=0; i < (ssize_t) number_components; i++)
242   {
243     GetPathComponent(components[i],SubimagePath,subnode);
244     GetPathComponent(components[i],CanonicalPath,tag);
245     child=GetXMLTreeChild(node,tag);
246     if (child == (XMLTreeInfo *) NULL)
247       child=AddChildToXMLTree(node,tag,offset);
248     node=child;
249     if (node == (XMLTreeInfo *) NULL)
250       break;
251     for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
252     {
253       node=GetXMLTreeOrdered(node);
254       if (node == (XMLTreeInfo *) NULL)
255         break;
256     }
257     if (node == (XMLTreeInfo *) NULL)
258       break;
259     components[i]=DestroyString(components[i]);
260   }
261   for ( ; i < (ssize_t) number_components; i++)
262     components[i]=DestroyString(components[i]);
263   components=(char **) RelinquishMagickMemory(components);
264   return(node);
265 }
266 \f
267 /*
268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269 %                                                                             %
270 %                                                                             %
271 %                                                                             %
272 %   C a n o n i c a l X M L C o n t e n t                                     %
273 %                                                                             %
274 %                                                                             %
275 %                                                                             %
276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277 %
278 %  CanonicalXMLContent() converts text to canonical XML content by converting
279 %  to UTF-8, substituting predefined entities, wrapping as CDATA, or encoding
280 %  as base-64 as required.
281 %
282 %  The format of the CanonicalXMLContent method is:
283 %
284 %
285 %      char *CanonicalXMLContent(const char *content,
286 %        const MagickBooleanType pedantic)
287 %
288 %  A description of each parameter follows:
289 %
290 %    o content: the content.
291 %
292 %    o pedantic: if true, replace newlines and tabs with their respective
293 %      entities.
294 %
295 */
296 MagickPrivate char *CanonicalXMLContent(const char *content,
297   const MagickBooleanType pedantic)
298 {
299   char
300     *base64,
301     *canonical_content;
302
303   register const unsigned char
304     *p;
305
306   register ssize_t
307     i;
308
309   size_t
310     extent,
311     length;
312
313   unsigned char
314     *utf8;
315
316   utf8=ConvertLatin1ToUTF8((const unsigned char *) content);
317   if (utf8 == (unsigned char *) NULL)
318     return((char *) NULL);
319   for (p=utf8; *p != '\0'; p++)
320     if ((*p < 0x20) && (*p != 0x09) && (*p != 0x0a) && (*p != 0x0d))
321       break;
322   if (*p != '\0')
323     {
324       /*
325         String is binary, base64-encode it.
326       */
327       base64=Base64Encode(utf8,strlen((char *) utf8),&length);
328       utf8=(unsigned char *) RelinquishMagickMemory(utf8);
329       if (base64 == (char *) NULL)
330         return((char *) NULL);
331       canonical_content=AcquireString("<base64>");
332       (void) ConcatenateString(&canonical_content,base64);
333       base64=DestroyString(base64);
334       (void) ConcatenateString(&canonical_content,"</base64>");
335       return(canonical_content);
336     }
337   /*
338     Substitute predefined entities.
339   */
340   i=0;
341   canonical_content=AcquireString((char *) NULL);
342   extent=MaxTextExtent;
343   for (p=utf8; *p != '\0'; p++)
344   {
345     if ((i+MaxTextExtent) > (ssize_t) extent)
346       {
347         extent+=MaxTextExtent;
348         canonical_content=(char *) ResizeQuantumMemory(canonical_content,extent,
349           sizeof(*canonical_content));
350         if (canonical_content == (char *) NULL)
351           return(canonical_content);
352       }
353     switch (*p)
354     {
355       case '&':
356       {
357         i+=FormatLocaleString(canonical_content+i,extent,"&amp;");
358         break;
359       }
360       case '<':
361       {
362         i+=FormatLocaleString(canonical_content+i,extent,"&lt;");
363         break;
364       }
365       case '>':
366       {
367         i+=FormatLocaleString(canonical_content+i,extent,"&gt;");
368         break;
369       }
370       case '"':
371       {
372         i+=FormatLocaleString(canonical_content+i,extent,"&quot;");
373         break;
374       }
375       case '\n':
376       {
377         if (pedantic == MagickFalse)
378           {
379             canonical_content[i++]=(char) (*p);
380             break;
381           }
382         i+=FormatLocaleString(canonical_content+i,extent,"&#xA;");
383         break;
384       }
385       case '\t':
386       {
387         if (pedantic == MagickFalse)
388           {
389             canonical_content[i++]=(char) (*p);
390             break;
391           }
392         i+=FormatLocaleString(canonical_content+i,extent,"&#x9;");
393         break;
394       }
395       case '\r':
396       {
397         i+=FormatLocaleString(canonical_content+i,extent,"&#xD;");
398         break;
399       }
400       default:
401       {
402         canonical_content[i++]=(char) (*p);
403         break;
404       }
405     }
406   }
407   canonical_content[i]='\0';
408   utf8=(unsigned char *) RelinquishMagickMemory(utf8);
409   return(canonical_content);
410 }
411 \f
412 /*
413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
414 %                                                                             %
415 %                                                                             %
416 %                                                                             %
417 %   D e s t r o y X M L T r e e                                               %
418 %                                                                             %
419 %                                                                             %
420 %                                                                             %
421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422 %
423 %  DestroyXMLTree() destroys the xml-tree.
424 %
425 %  The format of the DestroyXMLTree method is:
426 %
427 %      XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
428 %
429 %  A description of each parameter follows:
430 %
431 %    o xml_info: the xml info.
432 %
433 */
434
435 static char **DestroyXMLTreeAttributes(char **attributes)
436 {
437   register ssize_t
438     i;
439
440   /*
441     Destroy a tag attribute list.
442   */
443   if ((attributes == (char **) NULL) || (attributes == sentinel))
444     return((char **) NULL);
445   for (i=0; attributes[i] != (char *) NULL; i+=2)
446   {
447     /*
448       Destroy attribute tag and value.
449     */
450     if (attributes[i] != (char *) NULL)
451       attributes[i]=DestroyString(attributes[i]);
452     if (attributes[i+1] != (char *) NULL)
453       attributes[i+1]=DestroyString(attributes[i+1]);
454   }
455   attributes=(char **) RelinquishMagickMemory(attributes);
456   return((char **) NULL);
457 }
458
459 static void DestroyXMLTreeRoot(XMLTreeInfo *xml_info)
460 {
461   char
462     **attributes;
463
464   register ssize_t
465     i;
466
467   ssize_t
468     j;
469
470   XMLTreeRoot
471     *root;
472
473   assert(xml_info != (XMLTreeInfo *) NULL);
474   assert((xml_info->signature == MagickSignature) ||
475          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
476   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
477   if (xml_info->parent != (XMLTreeInfo *) NULL)
478     return;
479   /*
480     Free root tag allocations.
481   */
482   root=(XMLTreeRoot *) xml_info;
483   for (i=NumberPredefinedEntities; root->entities[i] != (char *) NULL; i+=2)
484     root->entities[i+1]=DestroyString(root->entities[i+1]);
485   root->entities=(char **) RelinquishMagickMemory(root->entities);
486   for (i=0; root->attributes[i] != (char **) NULL; i++)
487   {
488     attributes=root->attributes[i];
489     if (attributes[0] != (char *) NULL)
490       attributes[0]=DestroyString(attributes[0]);
491     for (j=1; attributes[j] != (char *) NULL; j+=3)
492     {
493       if (attributes[j] != (char *) NULL)
494         attributes[j]=DestroyString(attributes[j]);
495       if (attributes[j+1] != (char *) NULL)
496         attributes[j+1]=DestroyString(attributes[j+1]);
497       if (attributes[j+2] != (char *) NULL)
498         attributes[j+2]=DestroyString(attributes[j+2]);
499     }
500     attributes=(char **) RelinquishMagickMemory(attributes);
501   }
502   if (root->attributes[0] != (char **) NULL)
503     root->attributes=(char ***) RelinquishMagickMemory(root->attributes);
504   if (root->processing_instructions[0] != (char **) NULL)
505     {
506       for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
507       {
508         for (j=0; root->processing_instructions[i][j] != (char *) NULL; j++)
509           root->processing_instructions[i][j]=DestroyString(
510             root->processing_instructions[i][j]);
511         root->processing_instructions[i][j+1]=DestroyString(
512           root->processing_instructions[i][j+1]);
513         root->processing_instructions[i]=(char **) RelinquishMagickMemory(
514           root->processing_instructions[i]);
515       }
516       root->processing_instructions=(char ***) RelinquishMagickMemory(
517         root->processing_instructions);
518     }
519 }
520
521 MagickExport XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
522 {
523   char
524     **attributes;
525
526   register ssize_t
527     i;
528
529   ssize_t
530     j;
531
532   XMLTreeRoot
533     *root;
534
535   assert(xml_info != (XMLTreeInfo *) NULL);
536   assert((xml_info->signature == MagickSignature) ||
537          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
538   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
539   if (xml_info->child != (XMLTreeInfo *) NULL)
540     xml_info->child=DestroyXMLTree(xml_info->child);
541   if (xml_info->ordered != (XMLTreeInfo *) NULL)
542     xml_info->ordered=DestroyXMLTree(xml_info->ordered);
543   DestroyXMLTreeRoot(xml_info);
544   xml_info->attributes=DestroyXMLTreeAttributes(xml_info->attributes);
545   xml_info->content=DestroyString(xml_info->content);
546   xml_info->tag=DestroyString(xml_info->tag);
547   xml_info=(XMLTreeInfo *) RelinquishMagickMemory(xml_info);
548   return((XMLTreeInfo *) NULL);
549 }
550 \f
551 /*
552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553 %                                                                             %
554 %                                                                             %
555 %                                                                             %
556 %   G e t N e x t X M L T r e e T a g                                         %
557 %                                                                             %
558 %                                                                             %
559 %                                                                             %
560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
561 %
562 %  GetNextXMLTreeTag() returns the next tag or NULL if not found.
563 %
564 %  The format of the GetNextXMLTreeTag method is:
565 %
566 %      XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
567 %
568 %  A description of each parameter follows:
569 %
570 %    o xml_info: the xml info.
571 %
572 */
573 MagickExport XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
574 {
575   assert(xml_info != (XMLTreeInfo *) NULL);
576   assert((xml_info->signature == MagickSignature) ||
577          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
578   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
579   return(xml_info->next);
580 }
581 \f
582 /*
583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584 %                                                                             %
585 %                                                                             %
586 %                                                                             %
587 %   G e t X M L T r e e A t t r i b u t e                                     %
588 %                                                                             %
589 %                                                                             %
590 %                                                                             %
591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
592 %
593 %  GetXMLTreeAttribute() returns the value of the attribute tag with the
594 %  specified tag if found, otherwise NULL.
595 %
596 %  The format of the GetXMLTreeAttribute method is:
597 %
598 %      const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag)
599 %
600 %  A description of each parameter follows:
601 %
602 %    o xml_info: the xml info.
603 %
604 %    o tag: the attribute tag.
605 %
606 */
607 MagickExport const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,
608   const char *tag)
609 {
610   register ssize_t
611     i;
612
613   ssize_t
614     j;
615
616   XMLTreeRoot
617     *root;
618
619   assert(xml_info != (XMLTreeInfo *) NULL);
620   assert((xml_info->signature == MagickSignature) ||
621          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
622   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
623   if (xml_info->attributes == (char **) NULL)
624     return((const char *) NULL);
625   i=0;
626   while ((xml_info->attributes[i] != (char *) NULL) &&
627          (strcmp(xml_info->attributes[i],tag) != 0))
628     i+=2;
629   if (xml_info->attributes[i] != (char *) NULL)
630     return(xml_info->attributes[i+1]);
631   root=(XMLTreeRoot*) xml_info;
632   while (root->root.parent != (XMLTreeInfo *) NULL)
633     root=(XMLTreeRoot *) root->root.parent;
634   i=0;
635   while ((root->attributes[i] != (char **) NULL) &&
636          (strcmp(root->attributes[i][0],xml_info->tag) != 0))
637     i++;
638   if (root->attributes[i] == (char **) NULL)
639     return((const char *) NULL);
640   j=1;
641   while ((root->attributes[i][j] != (char *) NULL) &&
642          (strcmp(root->attributes[i][j],tag) != 0))
643     j+=3;
644   if (root->attributes[i][j] == (char *) NULL)
645     return((const char *) NULL);
646   return(root->attributes[i][j+1]);
647 }
648 \f
649 /*
650 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
651 %                                                                             %
652 %                                                                             %
653 %                                                                             %
654 %   G e t X M L T r e e A t t r i b u t e s                                   %
655 %                                                                             %
656 %                                                                             %
657 %                                                                             %
658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659 %
660 %  GetXMLTreeAttributes() injects all attributes associated with the current
661 %  tag in the specified splay-tree.
662 %
663 %  The format of the GetXMLTreeAttributes method is:
664 %
665 %      MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
666 %        SplayTreeInfo *attributes)
667 %
668 %  A description of each parameter follows:
669 %
670 %    o xml_info: the xml info.
671 %
672 %    o attributes: the attribute splay-tree.
673 %
674 */
675 MagickPrivate MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
676   SplayTreeInfo *attributes)
677 {
678   register ssize_t
679     i;
680
681   assert(xml_info != (XMLTreeInfo *) NULL);
682   assert((xml_info->signature == MagickSignature) ||
683          (((const XMLTreeRoot *) xml_info)->signature == MagickSignature));
684   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
685   assert(attributes != (SplayTreeInfo *) NULL);
686   if (xml_info->attributes == (char **) NULL)
687     return(MagickTrue);
688   i=0;
689   while (xml_info->attributes[i] != (char *) NULL)
690   {
691      (void) AddValueToSplayTree(attributes,
692        ConstantString(xml_info->attributes[i]),
693        ConstantString(xml_info->attributes[i+1]));
694     i+=2;
695   }
696   return(MagickTrue);
697 }
698 \f
699 /*
700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
701 %                                                                             %
702 %                                                                             %
703 %                                                                             %
704 %   G e t X M L T r e e C h i l d                                             %
705 %                                                                             %
706 %                                                                             %
707 %                                                                             %
708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
709 %
710 %  GetXMLTreeChild() returns the first child tag with the specified tag if
711 %  found, otherwise NULL.
712 %
713 %  The format of the GetXMLTreeChild method is:
714 %
715 %      XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
716 %
717 %  A description of each parameter follows:
718 %
719 %    o xml_info: the xml info.
720 %
721 */
722 MagickExport XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
723 {
724   XMLTreeInfo
725     *child;
726
727   assert(xml_info != (XMLTreeInfo *) NULL);
728   assert((xml_info->signature == MagickSignature) ||
729          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
730   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
731   child=xml_info->child;
732   if (tag != (const char *) NULL)
733     while ((child != (XMLTreeInfo *) NULL) && (strcmp(child->tag,tag) != 0))
734       child=child->sibling;
735   return(child);
736 }
737 \f
738 /*
739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
740 %                                                                             %
741 %                                                                             %
742 %                                                                             %
743 %   G e t X M L T r e e C o n t e n t                                         %
744 %                                                                             %
745 %                                                                             %
746 %                                                                             %
747 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748 %
749 %  GetXMLTreeContent() returns any content associated with specified
750 %  xml-tree node.
751 %
752 %  The format of the GetXMLTreeContent method is:
753 %
754 %      const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
755 %
756 %  A description of each parameter follows:
757 %
758 %    o xml_info: the xml info.
759 %
760 */
761 MagickExport const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
762 {
763   assert(xml_info != (XMLTreeInfo *) NULL);
764   assert((xml_info->signature == MagickSignature) ||
765          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
766   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
767   return(xml_info->content);
768 }
769 \f
770 /*
771 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
772 %                                                                             %
773 %                                                                             %
774 %                                                                             %
775 %   G e t X M L T r e e O r d e r e d                                         %
776 %                                                                             %
777 %                                                                             %
778 %                                                                             %
779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780 %
781 %  GetXMLTreeOrdered() returns the next ordered node if found, otherwise NULL.
782 %
783 %  The format of the GetXMLTreeOrdered method is:
784 %
785 %      XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
786 %
787 %  A description of each parameter follows:
788 %
789 %    o xml_info: the xml info.
790 %
791 */
792 MagickPrivate XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
793 {
794   assert(xml_info != (XMLTreeInfo *) NULL);
795   assert((xml_info->signature == MagickSignature) ||
796          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
797   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
798   return(xml_info->ordered);
799 }
800 \f
801 /*
802 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
803 %                                                                             %
804 %                                                                             %
805 %                                                                             %
806 %   G e t X M L T r e e P a t h                                               %
807 %                                                                             %
808 %                                                                             %
809 %                                                                             %
810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
811 %
812 %  GetXMLTreePath() traverses the XML-tree as defined by the specified path
813 %  and returns the node if found, otherwise NULL.
814 %
815 %  The format of the GetXMLTreePath method is:
816 %
817 %      XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
818 %
819 %  A description of each parameter follows:
820 %
821 %    o xml_info: the xml info.
822 %
823 %    o path: the path (e.g. property/elapsed-time).
824 %
825 */
826 MagickPrivate XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
827 {
828   char
829     **components,
830     subnode[MaxTextExtent],
831     tag[MaxTextExtent];
832
833   register ssize_t
834     i;
835
836   size_t
837     number_components;
838
839   ssize_t
840     j;
841
842   XMLTreeInfo
843     *node;
844
845   assert(xml_info != (XMLTreeInfo *) NULL);
846   assert((xml_info->signature == MagickSignature) ||
847          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
848   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
849   node=xml_info;
850   components=GetPathComponents(path,&number_components);
851   if (components == (char **) NULL)
852     return((XMLTreeInfo *) NULL);
853   for (i=0; i < (ssize_t) number_components; i++)
854   {
855     GetPathComponent(components[i],SubimagePath,subnode);
856     GetPathComponent(components[i],CanonicalPath,tag);
857     node=GetXMLTreeChild(node,tag);
858     if (node == (XMLTreeInfo *) NULL)
859       break;
860     for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
861     {
862       node=GetXMLTreeOrdered(node);
863       if (node == (XMLTreeInfo *) NULL)
864         break;
865     }
866     if (node == (XMLTreeInfo *) NULL)
867       break;
868     components[i]=DestroyString(components[i]);
869   }
870   for ( ; i < (ssize_t) number_components; i++)
871     components[i]=DestroyString(components[i]);
872   components=(char **) RelinquishMagickMemory(components);
873   return(node);
874 }
875 \f
876 /*
877 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
878 %                                                                             %
879 %                                                                             %
880 %                                                                             %
881 %   G e t X M L T r e e P r o c e s s i n g I n s t r u c t i o n s           %
882 %                                                                             %
883 %                                                                             %
884 %                                                                             %
885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
886 %
887 %  GetXMLTreeProcessingInstructions() returns a null terminated array of
888 %  processing instructions for the given target.
889 %
890 %  The format of the GetXMLTreeProcessingInstructions method is:
891 %
892 %      const char **GetXMLTreeProcessingInstructions(XMLTreeInfo *xml_info,
893 %        const char *target)
894 %
895 %  A description of each parameter follows:
896 %
897 %    o xml_info: the xml info.
898 %
899 */
900 MagickPrivate const char **GetXMLTreeProcessingInstructions(
901   XMLTreeInfo *xml_info,const char *target)
902 {
903   register ssize_t
904     i;
905
906   XMLTreeRoot
907     *root;
908
909   assert(xml_info != (XMLTreeInfo *) NULL);
910   assert((xml_info->signature == MagickSignature) ||
911          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
912   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
913   root=(XMLTreeRoot *) xml_info;
914   while (root->root.parent != (XMLTreeInfo *) NULL)
915     root=(XMLTreeRoot *) root->root.parent;
916   i=0;
917   while ((root->processing_instructions[i] != (char **) NULL) &&
918          (strcmp(root->processing_instructions[i][0],target) != 0))
919     i++;
920   if (root->processing_instructions[i] == (char **) NULL)
921     return((const char **) sentinel);
922   return((const char **) (root->processing_instructions[i]+1));
923 }
924 \f
925 /*
926 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
927 %                                                                             %
928 %                                                                             %
929 %                                                                             %
930 %   G e t X M L T r e e S i b l i n g                                         %
931 %                                                                             %
932 %                                                                             %
933 %                                                                             %
934 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
935 %
936 %  GetXMLTreeSibling() returns the node sibling if found, otherwise NULL.
937 %
938 %  The format of the GetXMLTreeSibling method is:
939 %
940 %      XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
941 %
942 %  A description of each parameter follows:
943 %
944 %    o xml_info: the xml info.
945 %
946 */
947 MagickExport XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
948 {
949   assert(xml_info != (XMLTreeInfo *) NULL);
950   assert((xml_info->signature == MagickSignature) ||
951          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
952   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
953   return(xml_info->sibling);
954 }
955 \f
956 /*
957 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
958 %                                                                             %
959 %                                                                             %
960 %                                                                             %
961 %   G e t X M L T r e e T a g                                                 %
962 %                                                                             %
963 %                                                                             %
964 %                                                                             %
965 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
966 %
967 %  GetXMLTreeTag() returns the tag associated with specified xml-tree node.
968 %
969 %  The format of the GetXMLTreeTag method is:
970 %
971 %      const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
972 %
973 %  A description of each parameter follows:
974 %
975 %    o xml_info: the xml info.
976 %
977 */
978 MagickExport const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
979 {
980   assert(xml_info != (XMLTreeInfo *) NULL);
981   assert((xml_info->signature == MagickSignature) ||
982          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
983   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
984   return(xml_info->tag);
985 }
986 \f
987 /*
988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
989 %                                                                             %
990 %                                                                             %
991 %                                                                             %
992 %   I n s e r t I n t o T a g X M L T r e e                                   %
993 %                                                                             %
994 %                                                                             %
995 %                                                                             %
996 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
997 %
998 %  InsertTagIntoXMLTree() inserts a tag at an offset relative to the start of
999 %  the parent tag's character content.  This method returns the child tag.
1000 %
1001 %  The format of the InsertTagIntoXMLTree method is:
1002 %
1003 %      XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
1004 %        XMLTreeInfo *child,const size_t offset)
1005 %
1006 %  A description of each parameter follows:
1007 %
1008 %    o xml_info: the xml info.
1009 %
1010 %    o child: the child tag.
1011 %
1012 %    o offset: the tag offset.
1013 %
1014 */
1015 MagickPrivate XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
1016   XMLTreeInfo *child,const size_t offset)
1017 {
1018   XMLTreeInfo
1019     *head,
1020     *node,
1021     *previous;
1022
1023   child->ordered=(XMLTreeInfo *) NULL;
1024   child->sibling=(XMLTreeInfo *) NULL;
1025   child->next=(XMLTreeInfo *) NULL;
1026   child->offset=offset;
1027   child->parent=xml_info;
1028   if (xml_info->child == (XMLTreeInfo *) NULL)
1029     {
1030       xml_info->child=child;
1031       return(child);
1032     }
1033   head=xml_info->child;
1034   if (head->offset > offset)
1035     {
1036       child->ordered=head;
1037       xml_info->child=child;
1038     }
1039   else
1040     {
1041       node=head;
1042       while ((node->ordered != (XMLTreeInfo *) NULL) &&
1043              (node->ordered->offset <= offset))
1044         node=node->ordered;
1045       child->ordered=node->ordered;
1046       node->ordered=child;
1047     }
1048   previous=(XMLTreeInfo *) NULL;
1049   node=head;
1050   while ((node != (XMLTreeInfo *) NULL) && (strcmp(node->tag,child->tag) != 0))
1051   {
1052     previous=node;
1053     node=node->sibling;
1054   }
1055   if ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1056     {
1057       while ((node->next != (XMLTreeInfo *) NULL) &&
1058              (node->next->offset <= offset))
1059         node=node->next;
1060       child->next=node->next;
1061       node->next=child;
1062     }
1063   else
1064     {
1065       if ((previous != (XMLTreeInfo *) NULL) && (node != (XMLTreeInfo *) NULL))
1066         previous->sibling=node->sibling;
1067       child->next=node;
1068       previous=(XMLTreeInfo *) NULL;
1069       node=head;
1070       while ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1071       {
1072         previous=node;
1073         node=node->sibling;
1074       }
1075       child->sibling=node;
1076       if (previous != (XMLTreeInfo *) NULL)
1077         previous->sibling=child;
1078     }
1079   return(child);
1080 }
1081 \f
1082 /*
1083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1084 %                                                                             %
1085 %                                                                             %
1086 %                                                                             %
1087 %   N e w X M L T r e e                                                       %
1088 %                                                                             %
1089 %                                                                             %
1090 %                                                                             %
1091 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1092 %
1093 %  NewXMLTree() returns a XMLTreeInfo xml-tree as defined by the specified
1094 %  XML string.
1095 %
1096 %  The format of the NewXMLTree method is:
1097 %
1098 %      XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1099 %
1100 %  A description of each parameter follows:
1101 %
1102 %    o xml:  The XML string.
1103 %
1104 %    o exception: return any errors or warnings in this structure.
1105 %
1106 */
1107
1108 static char *ConvertUTF16ToUTF8(const char *content,size_t *length)
1109 {
1110   char
1111     *utf8;
1112
1113   int
1114     bits,
1115     byte,
1116     c,
1117     encoding;
1118
1119   register ssize_t
1120     i;
1121
1122   size_t
1123     extent;
1124
1125   ssize_t
1126     j;
1127
1128   utf8=(char *) AcquireQuantumMemory(*length,sizeof(*utf8));
1129   if (utf8 == (char *) NULL)
1130     return((char *) NULL);
1131   encoding=(*content == '\xFE') ? 1 : (*content == '\xFF') ? 0 : -1;
1132   if (encoding == -1)
1133     {
1134       /*
1135         Already UTF-8.
1136       */
1137       (void) CopyMagickMemory(utf8,content,*length*sizeof(*utf8));
1138       return(utf8);
1139     }
1140   j=0;
1141   extent=(*length);
1142   for (i=2; i < (ssize_t) (*length-1); i+=2)
1143   {
1144     c=(encoding != 0) ? ((content[i] & 0xff) << 8) | (content[i+1] & 0xff) :
1145       ((content[i+1] & 0xff) << 8) | (content[i] & 0xff);
1146     if ((c >= 0xd800) && (c <= 0xdfff) && ((i+=2) < (ssize_t) (*length-1)))
1147       {
1148         byte=(encoding != 0) ? ((content[i] & 0xff) << 8) |
1149           (content[i+1] & 0xff) : ((content[i+1] & 0xff) << 8) |
1150           (content[i] & 0xff);
1151         c=(((c & 0x3ff) << 10) | (byte & 0x3ff))+0x10000;
1152       }
1153     if ((size_t) (j+MaxTextExtent) > extent)
1154       {
1155         extent=(size_t) j+MaxTextExtent;
1156         utf8=(char *) ResizeQuantumMemory(utf8,extent,sizeof(*utf8));
1157         if (utf8 == (char *) NULL)
1158           return(utf8);
1159       }
1160     if (c < 0x80)
1161       {
1162         utf8[j]=c;
1163         j++;
1164         continue;
1165       }
1166     /*
1167       Multi-byte UTF-8 sequence.
1168     */
1169     byte=c;
1170     for (bits=0; byte != 0; byte/=2)
1171       bits++;
1172     bits=(bits-2)/5;
1173     utf8[j++]=(0xFF << (7-bits)) | (c >> (6*bits));
1174     while (bits != 0)
1175     {
1176       bits--;
1177       utf8[j]=0x80 | ((c >> (6*bits)) & 0x3f);
1178       j++;
1179     }
1180   }
1181   *length=(size_t) j;
1182   return((char *) ResizeQuantumMemory(utf8,*length,sizeof(*utf8)));
1183 }
1184
1185 static char *ParseEntities(char *xml,char **entities,int state)
1186 {
1187   char
1188     *entity;
1189
1190   int
1191     byte,
1192     c;
1193
1194   register char
1195     *p,
1196     *q;
1197
1198   register ssize_t
1199     i;
1200
1201   size_t
1202     extent,
1203     length;
1204
1205   ssize_t
1206     offset;
1207
1208   /*
1209     Normalize line endings.
1210   */
1211   p=xml;
1212   q=xml;
1213   for ( ; *xml != '\0'; xml++)
1214     while (*xml == '\r')
1215     {
1216       *(xml++)='\n';
1217       if (*xml == '\n')
1218         (void) CopyMagickMemory(xml,xml+1,strlen(xml));
1219     }
1220   for (xml=p; ; )
1221   {
1222     while ((*xml != '\0') && (*xml != '&') && ((*xml != '%') ||
1223            (state != '%')) && (isspace((int) ((unsigned char) *xml) == 0)))
1224       xml++;
1225     if (*xml == '\0')
1226       break;
1227     /*
1228       States include:
1229         '&' for general entity decoding
1230         '%' for parameter entity decoding
1231         'c' for CDATA sections
1232         ' ' for attributes normalization
1233         '*' for non-CDATA attributes normalization
1234     */
1235     if ((state != 'c') && (strncmp(xml,"&#",2) == 0))
1236       {
1237         /*
1238           Character reference.
1239         */
1240         if (xml[2] != 'x')
1241           c=strtol(xml+2,&entity,10);  /* base 10 */
1242         else
1243           c=strtol(xml+3,&entity,16);  /* base 16 */
1244         if ((c == 0) || (*entity != ';'))
1245           {
1246             /*
1247               Not a character reference.
1248             */
1249             xml++;
1250             continue;
1251           }
1252         if (c < 0x80)
1253           *(xml++)=c;
1254         else
1255           {
1256             /*
1257               Multi-byte UTF-8 sequence.
1258             */
1259             byte=c;
1260             for (i=0; byte != 0; byte/=2)
1261               i++;
1262             i=(i-2)/5;
1263             *xml=(char) ((0xFF << (7-i)) | (c >> (6*i)));
1264             xml++;
1265             while (i != 0)
1266             {
1267               i--;
1268               *xml=(char) (0x80 | ((c >> (6*i)) & 0x3F));
1269               xml++;
1270             }
1271           }
1272         (void) CopyMagickMemory(xml,strchr(xml,';')+1,strlen(strchr(xml,';')));
1273       }
1274     else
1275       if (((*xml == '&') && ((state == '&') || (state == ' ') ||
1276           (state == '*'))) || ((state == '%') && (*xml == '%')))
1277         {
1278           /*
1279             Find entity in the list.
1280           */
1281           i=0;
1282           while ((entities[i] != (char *) NULL) &&
1283                  (strncmp(xml+1,entities[i],strlen(entities[i])) != 0))
1284             i+=2;
1285           if (entities[i++] == (char *) NULL)
1286             xml++;
1287           else
1288             {
1289               /*
1290                 Found a match.
1291               */
1292               length=strlen(entities[i]);
1293               entity=strchr(xml,';');
1294               if ((length-1L) >= (size_t) (entity-xml))
1295                 {
1296                   offset=(ssize_t) (xml-p);
1297                   extent=(size_t) (offset+length+strlen(entity));
1298                   if (p != q)
1299                     p=(char *) ResizeQuantumMemory(p,extent,sizeof(*p));
1300                   else
1301                     {
1302                       char
1303                         *xml;
1304
1305                       xml=(char *) AcquireQuantumMemory(extent,sizeof(*xml));
1306                       if (xml != (char *) NULL)
1307                         {
1308                           (void) CopyMagickString(xml,p,extent*sizeof(*xml));
1309                           p=xml;
1310                         }
1311                     }
1312                   if (p == (char *) NULL)
1313                     ThrowFatalException(ResourceLimitFatalError,
1314                       "MemoryAllocationFailed");
1315                   xml=p+offset;
1316                   entity=strchr(xml,';');
1317                 }
1318               (void) CopyMagickMemory(xml+length,entity+1,strlen(entity));
1319               (void) strncpy(xml,entities[i],length);
1320             }
1321         }
1322       else
1323         if (((state == ' ') || (state == '*')) &&
1324             (isspace((int) ((unsigned char) *xml) != 0)))
1325           *(xml++)=' ';
1326         else
1327           xml++;
1328   }
1329   if (state == '*')
1330     {
1331       /*
1332         Normalize spaces for non-CDATA attributes.
1333       */
1334       for (xml=p; *xml != '\0'; xml++)
1335       {
1336         i=(ssize_t) strspn(xml," ");
1337         if (i != 0)
1338           (void) CopyMagickMemory(xml,xml+i,strlen(xml+i)+1);
1339         while ((*xml != '\0') && (*xml != ' '))
1340           xml++;
1341       }
1342       xml--;
1343       if ((xml >= p) && (*xml == ' '))
1344         *xml='\0';
1345     }
1346   return(p == q ? ConstantString(p) : p);
1347 }
1348
1349 static void ParseCharacterContent(XMLTreeRoot *root,char *xml,
1350   const size_t length,const char state)
1351 {
1352   XMLTreeInfo
1353     *xml_info;
1354
1355   xml_info=root->node;
1356   if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) ||
1357       (length == 0))
1358     return;
1359   xml[length]='\0';
1360   xml=ParseEntities(xml,root->entities,state);
1361   if (*xml_info->content != '\0')
1362     {
1363       (void) ConcatenateString(&xml_info->content,xml);
1364       xml=DestroyString(xml);
1365     }
1366   else
1367     {
1368       if (xml_info->content != (char *) NULL)
1369         xml_info->content=DestroyString(xml_info->content);
1370       xml_info->content=xml;
1371     }
1372 }
1373
1374 static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag,
1375   char *magick_unused(xml),ExceptionInfo *exception)
1376 {
1377   if ((root->node == (XMLTreeInfo *) NULL) ||
1378       (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0))
1379     {
1380       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1381         "ParseError","unexpected closing tag </%s>",tag);
1382       return(&root->root);
1383     }
1384   root->node=root->node->parent;
1385   return((XMLTreeInfo *) NULL);
1386 }
1387
1388 static MagickBooleanType ValidateEntities(char *tag,char *xml,char **entities)
1389 {
1390   register ssize_t
1391     i;
1392
1393   /*
1394     Check for circular entity references.
1395   */
1396   for ( ; ; xml++)
1397   {
1398     while ((*xml != '\0') && (*xml != '&'))
1399       xml++;
1400     if (*xml == '\0')
1401       return(MagickTrue);
1402     if (strncmp(xml+1,tag,strlen(tag)) == 0)
1403       return(MagickFalse);
1404     i=0;
1405     while ((entities[i] != (char *) NULL) &&
1406            (strncmp(entities[i],xml+1,strlen(entities[i])) == 0))
1407       i+=2;
1408     if ((entities[i] != (char *) NULL) &&
1409         (ValidateEntities(tag,entities[i+1],entities) == 0))
1410       return(MagickFalse);
1411   }
1412 }
1413
1414 static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml,
1415   size_t length)
1416 {
1417   char
1418     *target;
1419
1420   register ssize_t
1421     i;
1422
1423   ssize_t
1424     j;
1425
1426   target=xml;
1427   xml[length]='\0';
1428   xml+=strcspn(xml,XMLWhitespace);
1429   if (*xml != '\0')
1430     {
1431       *xml='\0';
1432       xml+=strspn(xml+1,XMLWhitespace)+1;
1433     }
1434   if (strcmp(target,"xml") == 0)
1435     {
1436       xml=strstr(xml,"standalone");
1437       if ((xml != (char *) NULL) &&
1438           (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0))
1439         root->standalone=MagickTrue;
1440       return;
1441     }
1442   if (root->processing_instructions[0] == (char **) NULL)
1443     {
1444       root->processing_instructions=(char ***) AcquireMagickMemory(sizeof(
1445         *root->processing_instructions));
1446       if (root->processing_instructions ==(char ***) NULL)
1447         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1448       *root->processing_instructions=(char **) NULL;
1449     }
1450   i=0;
1451   while ((root->processing_instructions[i] != (char **) NULL) &&
1452          (strcmp(target,root->processing_instructions[i][0]) != 0))
1453     i++;
1454   if (root->processing_instructions[i] == (char **) NULL)
1455     {
1456       root->processing_instructions=(char ***) ResizeQuantumMemory(
1457         root->processing_instructions,(size_t) (i+2),
1458         sizeof(*root->processing_instructions));
1459       if (root->processing_instructions == (char ***) NULL)
1460         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1461       root->processing_instructions[i]=(char **) AcquireQuantumMemory(3,
1462         sizeof(**root->processing_instructions));
1463       if (root->processing_instructions[i] == (char **) NULL)
1464         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1465       root->processing_instructions[i+1]=(char **) NULL;
1466       root->processing_instructions[i][0]=ConstantString(target);
1467       root->processing_instructions[i][1]=(char *)
1468         root->processing_instructions[i+1];
1469       root->processing_instructions[i+1]=(char **) NULL;
1470       root->processing_instructions[i][2]=ConstantString("");
1471     }
1472   j=1;
1473   while (root->processing_instructions[i][j] != (char *) NULL)
1474     j++;
1475   root->processing_instructions[i]=(char **) ResizeQuantumMemory(
1476     root->processing_instructions[i],(size_t) (j+3),
1477     sizeof(**root->processing_instructions));
1478   if (root->processing_instructions[i] == (char **) NULL)
1479     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1480   root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory(
1481     root->processing_instructions[i][j+1],(size_t) (j+1),
1482     sizeof(**root->processing_instructions));
1483   if (root->processing_instructions[i][j+2] == (char *) NULL)
1484     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1485   (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1,
1486     root->root.tag != (char *) NULL ? ">" : "<",2);
1487   root->processing_instructions[i][j]=ConstantString(xml);
1488   root->processing_instructions[i][j+1]=(char *) NULL;
1489 }
1490
1491 static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml,
1492   size_t length,ExceptionInfo *exception)
1493 {
1494   char
1495     *c,
1496     **entities,
1497     *n,
1498     **predefined_entitites,
1499     q,
1500     *t,
1501     *v;
1502
1503   register ssize_t
1504     i;
1505
1506   ssize_t
1507     j;
1508
1509   n=(char *) NULL;
1510   predefined_entitites=(char **) AcquireMagickMemory(sizeof(sentinel));
1511   if (predefined_entitites == (char **) NULL)
1512     ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed");
1513   (void) CopyMagickMemory(predefined_entitites,sentinel,sizeof(sentinel));
1514   for (xml[length]='\0'; xml != (char *) NULL; )
1515   {
1516     while ((*xml != '\0') && (*xml != '<') && (*xml != '%'))
1517       xml++;
1518     if (*xml == '\0')
1519       break;
1520     if (strncmp(xml,"<!ENTITY",8) == 0)
1521       {
1522         /*
1523           Parse entity definitions.
1524         */
1525         xml+=strspn(xml+8,XMLWhitespace)+8;
1526         c=xml;
1527         n=xml+strspn(xml,XMLWhitespace "%");
1528         xml=n+strcspn(n,XMLWhitespace);
1529         *xml=';';
1530         v=xml+strspn(xml+1,XMLWhitespace)+1;
1531         q=(*v);
1532         v++;
1533         if ((q != '"') && (q != '\''))
1534           {
1535             /*
1536               Skip externals.
1537             */
1538             xml=strchr(xml,'>');
1539             continue;
1540           }
1541         entities=(*c == '%') ? predefined_entitites : root->entities;
1542         for (i=0; entities[i] != (char *) NULL; i++) ;
1543         entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3),
1544           sizeof(*entities));
1545         if (entities == (char **) NULL)
1546           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1547         if (*c == '%')
1548           predefined_entitites=entities;
1549         else
1550           root->entities=entities;
1551         xml++;
1552         *xml='\0';
1553         xml=strchr(v,q);
1554         if (xml != (char *) NULL)
1555           {
1556             *xml='\0';
1557             xml++;
1558           }
1559         entities[i+1]=ParseEntities(v,predefined_entitites,'%');
1560         entities[i+2]=(char *) NULL;
1561         if (ValidateEntities(n,entities[i+1],entities) != MagickFalse)
1562           entities[i]=n;
1563         else
1564           {
1565             if (entities[i+1] != v)
1566               entities[i+1]=DestroyString(entities[i+1]);
1567             (void) ThrowMagickException(exception,GetMagickModule(),
1568               OptionWarning,"ParseError","circular entity declaration &%s",n);
1569             predefined_entitites=(char **) RelinquishMagickMemory(
1570               predefined_entitites);
1571             return(MagickFalse);
1572           }
1573         }
1574       else
1575        if (strncmp(xml,"<!ATTLIST",9) == 0)
1576          {
1577             /*
1578               Parse default attributes.
1579             */
1580             t=xml+strspn(xml+9,XMLWhitespace)+9;
1581             if (*t == '\0')
1582               {
1583                 (void) ThrowMagickException(exception,GetMagickModule(),
1584                   OptionWarning,"ParseError","unclosed <!ATTLIST");
1585                 predefined_entitites=(char **) RelinquishMagickMemory(
1586                   predefined_entitites);
1587                 return(MagickFalse);
1588               }
1589             xml=t+strcspn(t,XMLWhitespace ">");
1590             if (*xml == '>')
1591               continue;
1592             *xml='\0';
1593             i=0;
1594             while ((root->attributes[i] != (char **) NULL) &&
1595                    (strcmp(n,root->attributes[i][0]) != 0))
1596               i++;
1597             while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') &&
1598                    (*n != '>'))
1599             {
1600               xml=n+strcspn(n,XMLWhitespace);
1601               if (*xml != '\0')
1602                 *xml='\0';
1603               else
1604                 {
1605                   (void) ThrowMagickException(exception,GetMagickModule(),
1606                     OptionWarning,"ParseError","malformed <!ATTLIST");
1607                   predefined_entitites=(char **) RelinquishMagickMemory(
1608                     predefined_entitites);
1609                   return(MagickFalse);
1610                 }
1611               xml+=strspn(xml+1,XMLWhitespace)+1;
1612               c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
1613               if (strncmp(xml,"NOTATION",8) == 0)
1614                 xml+=strspn(xml+8,XMLWhitespace)+8;
1615               xml=(*xml == '(') ? strchr(xml,')') : xml+
1616                 strcspn(xml,XMLWhitespace);
1617               if (xml == (char *) NULL)
1618                 {
1619                   (void) ThrowMagickException(exception,GetMagickModule(),
1620                     OptionWarning,"ParseError","malformed <!ATTLIST");
1621                   predefined_entitites=(char **) RelinquishMagickMemory(
1622                     predefined_entitites);
1623                   return(MagickFalse);
1624                 }
1625               xml+=strspn(xml,XMLWhitespace ")");
1626               if (strncmp(xml,"#FIXED",6) == 0)
1627                 xml+=strspn(xml+6,XMLWhitespace)+6;
1628               if (*xml == '#')
1629                 {
1630                   xml+=strcspn(xml,XMLWhitespace ">")-1;
1631                   if (*c == ' ')
1632                     continue;
1633                   v=(char *) NULL;
1634                 }
1635               else
1636                 if (((*xml == '"') || (*xml == '\''))  &&
1637                     ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
1638                   *xml='\0';
1639                 else
1640                   {
1641                     (void) ThrowMagickException(exception,GetMagickModule(),
1642                       OptionWarning,"ParseError","malformed <!ATTLIST");
1643                     predefined_entitites=(char **) RelinquishMagickMemory(
1644                       predefined_entitites);
1645                     return(MagickFalse);
1646                   }
1647               if (root->attributes[i] == (char **) NULL)
1648                 {
1649                   /*
1650                     New attribute tag.
1651                   */
1652                   if (i == 0)
1653                     root->attributes=(char ***) AcquireQuantumMemory(2,
1654                       sizeof(*root->attributes));
1655                   else
1656                     root->attributes=(char ***) ResizeQuantumMemory(
1657                       root->attributes,(size_t) (i+2),
1658                       sizeof(*root->attributes));
1659                   if (root->attributes == (char ***) NULL)
1660                     ThrowFatalException(ResourceLimitFatalError,
1661                       "MemoryAllocationFailed");
1662                   root->attributes[i]=(char **) AcquireQuantumMemory(2,
1663                     sizeof(*root->attributes));
1664                   if (root->attributes[i] == (char **) NULL)
1665                     ThrowFatalException(ResourceLimitFatalError,
1666                       "MemoryAllocationFailed");
1667                   root->attributes[i][0]=ConstantString(t);
1668                   root->attributes[i][1]=(char *) NULL;
1669                   root->attributes[i+1]=(char **) NULL;
1670                 }
1671               for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
1672               root->attributes[i]=(char **) ResizeQuantumMemory(
1673                 root->attributes[i],(size_t) (j+4),sizeof(*root->attributes));
1674               if (root->attributes[i] == (char **) NULL)
1675                 ThrowFatalException(ResourceLimitFatalError,
1676                   "MemoryAllocationFailed");
1677               root->attributes[i][j+3]=(char *) NULL;
1678               root->attributes[i][j+2]=ConstantString(c);
1679               root->attributes[i][j+1]=(char *) NULL;
1680               if (v != (char *) NULL)
1681                 root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
1682               root->attributes[i][j]=ConstantString(n);
1683             }
1684         }
1685       else
1686         if (strncmp(xml, "<!--", 4) == 0)
1687           xml=strstr(xml+4,"-->");
1688         else
1689           if (strncmp(xml,"<?", 2) == 0)
1690             {
1691               c=xml+2;
1692               xml=strstr(c,"?>");
1693               if (xml != (char *) NULL)
1694                 {
1695                   ParseProcessingInstructions(root,c,(size_t) (xml-c));
1696                   xml++;
1697                 }
1698             }
1699            else
1700              if (*xml == '<')
1701                xml=strchr(xml,'>');
1702              else
1703                if ((*(xml++) == '%') && (root->standalone == MagickFalse))
1704                  break;
1705     }
1706   predefined_entitites=(char **) RelinquishMagickMemory(predefined_entitites);
1707   return(MagickTrue);
1708 }
1709
1710 static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
1711 {
1712   XMLTreeInfo
1713     *xml_info;
1714
1715   xml_info=root->node;
1716   if (xml_info->tag == (char *) NULL)
1717     xml_info->tag=ConstantString(tag);
1718   else
1719     xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
1720   xml_info->attributes=attributes;
1721   root->node=xml_info;
1722 }
1723
1724 MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1725 {
1726   char
1727     **attribute,
1728     **attributes,
1729     *tag,
1730     *utf8;
1731
1732   int
1733     c,
1734     terminal;
1735
1736   MagickBooleanType
1737     status;
1738
1739   register char
1740     *p;
1741
1742   register ssize_t
1743     i;
1744
1745   size_t
1746     length;
1747
1748   ssize_t
1749     j,
1750     l;
1751
1752   XMLTreeRoot
1753     *root;
1754
1755   /*
1756     Convert xml-string to UTF8.
1757   */
1758   if ((xml == (const char *) NULL) || (strlen(xml) == 0))
1759     {
1760       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1761         "ParseError","root tag missing");
1762       return((XMLTreeInfo *) NULL);
1763     }
1764   root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
1765   length=strlen(xml);
1766   utf8=ConvertUTF16ToUTF8(xml,&length);
1767   if (utf8 == (char *) NULL)
1768     {
1769       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1770         "ParseError","UTF16 to UTF8 failed");
1771       return((XMLTreeInfo *) NULL);
1772     }
1773   terminal=utf8[length-1];
1774   utf8[length-1]='\0';
1775   p=utf8;
1776   while ((*p != '\0') && (*p != '<'))
1777     p++;
1778   if (*p == '\0')
1779     {
1780       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1781         "ParseError","root tag missing");
1782       utf8=DestroyString(utf8);
1783       return((XMLTreeInfo *) NULL);
1784     }
1785   attribute=(char **) NULL;
1786   for (p++; ; p++)
1787   {
1788     attributes=(char **) sentinel;
1789     tag=p;
1790     c=(*p);
1791     if ((isalpha((int) ((unsigned char) *p)) !=0) || (*p == '_') ||
1792         (*p == ':') || (c < '\0'))
1793       {
1794         /*
1795           Tag.
1796         */
1797         if (root->node == (XMLTreeInfo *) NULL)
1798           {
1799             (void) ThrowMagickException(exception,GetMagickModule(),
1800               OptionWarning,"ParseError","root tag missing");
1801             utf8=DestroyString(utf8);
1802             return(&root->root);
1803           }
1804         p+=strcspn(p,XMLWhitespace "/>");
1805         while (isspace((int) ((unsigned char) *p)) != 0)
1806           *p++='\0';
1807         if ((*p != '\0') && (*p != '/') && (*p != '>'))
1808           {
1809             /*
1810               Find tag in default attributes list.
1811             */
1812             i=0;
1813             while ((root->attributes[i] != (char **) NULL) &&
1814                    (strcmp(root->attributes[i][0],tag) != 0))
1815               i++;
1816             attribute=root->attributes[i];
1817           }
1818         for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
1819         {
1820           /*
1821             Attribute.
1822           */
1823           if (l == 0)
1824             attributes=(char **) AcquireQuantumMemory(4,sizeof(*attributes));
1825           else
1826             attributes=(char **) ResizeQuantumMemory(attributes,(size_t) (l+4),
1827               sizeof(*attributes));
1828           if (attributes == (char **) NULL)
1829             {
1830               (void) ThrowMagickException(exception,GetMagickModule(),
1831                 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
1832               utf8=DestroyString(utf8);
1833               return(&root->root);
1834             }
1835           attributes[l+2]=(char *) NULL;
1836           attributes[l+1]=(char *) NULL;
1837           attributes[l]=p;
1838           p+=strcspn(p,XMLWhitespace "=/>");
1839           if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
1840             attributes[l]=ConstantString("");
1841           else
1842             {
1843               *p++='\0';
1844               p+=strspn(p,XMLWhitespace "=");
1845               c=(*p);
1846               if ((c == '"') || (c == '\''))
1847                 {
1848                   /*
1849                     Attributes value.
1850                   */
1851                   p++;
1852                   attributes[l+1]=p;
1853                   while ((*p != '\0') && (*p != c))
1854                     p++;
1855                   if (*p != '\0')
1856                     *p++='\0';
1857                   else
1858                     {
1859                       attributes[l]=ConstantString("");
1860                       attributes[l+1]=ConstantString("");
1861                       (void) DestroyXMLTreeAttributes(attributes);
1862                       (void) ThrowMagickException(exception,GetMagickModule(),
1863                         OptionWarning,"ParseError","missing %c",c);
1864                       utf8=DestroyString(utf8);
1865                       return(&root->root);
1866                     }
1867                   j=1;
1868                   while ((attribute != (char **) NULL) &&
1869                          (attribute[j] != (char *) NULL) &&
1870                          (strcmp(attribute[j],attributes[l]) != 0))
1871                     j+=3;
1872                   attributes[l+1]=ParseEntities(attributes[l+1],root->entities,
1873                     (attribute != (char **) NULL) && (attribute[j] !=
1874                     (char *) NULL) ? *attribute[j+2] : ' ');
1875                 }
1876               attributes[l]=ConstantString(attributes[l]);
1877             }
1878           while (isspace((int) ((unsigned char) *p)) != 0)
1879             p++;
1880         }
1881         if (*p == '/')
1882           {
1883             /*
1884               Self closing tag.
1885             */
1886             *p++='\0';
1887             if (((*p != '\0') && (*p != '>')) ||
1888                 ((*p == '\0') && (terminal != '>')))
1889               {
1890                 if (l != 0)
1891                   (void) DestroyXMLTreeAttributes(attributes);
1892                 (void) ThrowMagickException(exception,GetMagickModule(),
1893                   OptionWarning,"ParseError","missing >");
1894                 utf8=DestroyString(utf8);
1895                 return(&root->root);
1896               }
1897             ParseOpenTag(root,tag,attributes);
1898             (void) ParseCloseTag(root,tag,p,exception);
1899           }
1900         else
1901           {
1902             c=(*p);
1903             if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
1904               {
1905                 *p='\0';
1906                 ParseOpenTag(root,tag,attributes);
1907                 *p=c;
1908               }
1909             else
1910               {
1911                 if (l != 0)
1912                   (void) DestroyXMLTreeAttributes(attributes);
1913                 (void) ThrowMagickException(exception,GetMagickModule(),
1914                   OptionWarning,"ParseError","missing >");
1915                 utf8=DestroyString(utf8);
1916                 return(&root->root);
1917               }
1918           }
1919       }
1920     else
1921       if (*p == '/')
1922         {
1923           /*
1924             Close tag.
1925           */
1926           tag=p+1;
1927           p+=strcspn(tag,XMLWhitespace ">")+1;
1928           c=(*p);
1929           if ((c == '\0') && (terminal != '>'))
1930             {
1931               (void) ThrowMagickException(exception,GetMagickModule(),
1932                 OptionWarning,"ParseError","missing >");
1933               utf8=DestroyString(utf8);
1934               return(&root->root);
1935             }
1936           *p='\0';
1937           if (ParseCloseTag(root,tag,p,exception) != (XMLTreeInfo *) NULL)
1938             {
1939               utf8=DestroyString(utf8);
1940               return(&root->root);
1941             }
1942           *p=c;
1943           if (isspace((int) ((unsigned char) *p)) != 0)
1944             p+=strspn(p,XMLWhitespace);
1945         }
1946       else
1947         if (strncmp(p,"!--",3) == 0)
1948           {
1949             /*
1950               Comment.
1951             */
1952             p=strstr(p+3,"--");
1953             if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
1954                 ((*p == '\0') && (terminal != '>')))
1955               {
1956                 (void) ThrowMagickException(exception,GetMagickModule(),
1957                   OptionWarning,"ParseError","unclosed <!--");
1958                 utf8=DestroyString(utf8);
1959                 return(&root->root);
1960               }
1961           }
1962         else
1963           if (strncmp(p,"![CDATA[",8) == 0)
1964             {
1965               /*
1966                 Cdata.
1967               */
1968               p=strstr(p,"]]>");
1969               if (p != (char *) NULL)
1970                 {
1971                   p+=2;
1972                   ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
1973                 }
1974               else
1975                 {
1976                   (void) ThrowMagickException(exception,GetMagickModule(),
1977                     OptionWarning,"ParseError","unclosed <![CDATA[");
1978                   utf8=DestroyString(utf8);
1979                   return(&root->root);
1980                 }
1981             }
1982           else
1983             if (strncmp(p,"!DOCTYPE",8) == 0)
1984               {
1985                 /*
1986                   DTD.
1987                 */
1988                 for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
1989                      ((l != 0) && ((*p != ']') ||
1990                      (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
1991                   l=(ssize_t) ((*p == '[') ? 1 : l))
1992                 p+=strcspn(p+1,"[]>")+1;
1993                 if ((*p == '\0') && (terminal != '>'))
1994                   {
1995                     (void) ThrowMagickException(exception,GetMagickModule(),
1996                       OptionWarning,"ParseError","unclosed <!DOCTYPE");
1997                     utf8=DestroyString(utf8);
1998                     return(&root->root);
1999                   }
2000                 if (l != 0)
2001                   tag=strchr(tag,'[')+1;
2002                 if (l != 0)
2003                   {
2004                     status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
2005                       exception);
2006                     if (status == MagickFalse)
2007                       {
2008                         utf8=DestroyString(utf8);
2009                         return(&root->root);
2010                       }
2011                     p++;
2012                   }
2013               }
2014             else
2015               if (*p == '?')
2016                 {
2017                   /*
2018                     Processing instructions.
2019                   */
2020                   do
2021                   {
2022                     p=strchr(p,'?');
2023                     if (p == (char *) NULL)
2024                       break;
2025                     p++;
2026                   } while ((*p != '\0') && (*p != '>'));
2027                   if ((p == (char *) NULL) || ((*p == '\0') &&
2028                       (terminal != '>')))
2029                     {
2030                       (void) ThrowMagickException(exception,GetMagickModule(),
2031                         OptionWarning,"ParseError","unclosed <?");
2032                       utf8=DestroyString(utf8);
2033                       return(&root->root);
2034                     }
2035                   ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
2036                 }
2037               else
2038                 {
2039                   (void) ThrowMagickException(exception,GetMagickModule(),
2040                     OptionWarning,"ParseError","unexpected <");
2041                   utf8=DestroyString(utf8);
2042                   return(&root->root);
2043                 }
2044      if ((p == (char *) NULL) || (*p == '\0'))
2045        break;
2046      *p++='\0';
2047      tag=p;
2048      if ((*p != '\0') && (*p != '<'))
2049        {
2050         /*
2051           Tag character content.
2052         */
2053         while ((*p != '\0') && (*p != '<'))
2054           p++;
2055         if (*p == '\0')
2056           break;
2057         ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
2058       }
2059     else
2060       if (*p == '\0')
2061         break;
2062   }
2063   utf8=DestroyString(utf8);
2064   if (root->node == (XMLTreeInfo *) NULL)
2065     return(&root->root);
2066   if (root->node->tag == (char *) NULL)
2067     {
2068       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2069         "ParseError","root tag missing");
2070       return(&root->root);
2071     }
2072   (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2073     "ParseError","unclosed tag: '%s'",root->node->tag);
2074   return(&root->root);
2075 }
2076 \f
2077 /*
2078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2079 %                                                                             %
2080 %                                                                             %
2081 %                                                                             %
2082 %   N e w X M L T r e e T a g                                                 %
2083 %                                                                             %
2084 %                                                                             %
2085 %                                                                             %
2086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2087 %
2088 %  NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
2089 %
2090 %  The format of the NewXMLTreeTag method is:
2091 %
2092 %      XMLTreeInfo *NewXMLTreeTag(const char *tag)
2093 %
2094 %  A description of each parameter follows:
2095 %
2096 %    o tag: the tag.
2097 %
2098 */
2099 MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
2100 {
2101   static const char
2102     *predefined_entities[NumberPredefinedEntities+1] =
2103     {
2104       "lt;", "&#60;", "gt;", "&#62;", "quot;", "&#34;",
2105       "apos;", "&#39;", "amp;", "&#38;", (char *) NULL
2106     };
2107
2108   XMLTreeRoot
2109     *root;
2110
2111   root=(XMLTreeRoot *) AcquireMagickMemory(sizeof(*root));
2112   if (root == (XMLTreeRoot *) NULL)
2113     return((XMLTreeInfo *) NULL);
2114   (void) ResetMagickMemory(root,0,sizeof(*root));
2115   root->root.tag=(char *) NULL;
2116   if (tag != (char *) NULL)
2117     root->root.tag=ConstantString(tag);
2118   root->node=(&root->root);
2119   root->root.content=ConstantString("");
2120   root->entities=(char **) AcquireMagickMemory(sizeof(predefined_entities));
2121   if (root->entities == (char **) NULL)
2122     return((XMLTreeInfo *) NULL);
2123   (void) CopyMagickMemory(root->entities,predefined_entities,
2124     sizeof(predefined_entities));
2125   root->root.attributes=sentinel;
2126   root->attributes=(char ***) root->root.attributes;
2127   root->processing_instructions=(char ***) root->root.attributes;
2128   root->debug=IsEventLogging();
2129   root->signature=MagickSignature;
2130   return(&root->root);
2131 }
2132 \f
2133 /*
2134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2135 %                                                                             %
2136 %                                                                             %
2137 %                                                                             %
2138 %   P r u n e T a g F r o m X M L T r e e                                     %
2139 %                                                                             %
2140 %                                                                             %
2141 %                                                                             %
2142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2143 %
2144 %  PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
2145 %  subtags.
2146 %
2147 %  The format of the PruneTagFromXMLTree method is:
2148 %
2149 %      XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2150 %
2151 %  A description of each parameter follows:
2152 %
2153 %    o xml_info: the xml info.
2154 %
2155 */
2156 MagickPrivate XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2157 {
2158   XMLTreeInfo
2159     *node;
2160
2161   assert(xml_info != (XMLTreeInfo *) NULL);
2162   assert((xml_info->signature == MagickSignature) ||
2163          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2164   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2165   if (xml_info->next != (XMLTreeInfo *) NULL)
2166     xml_info->next->sibling=xml_info->sibling;
2167   if (xml_info->parent != (XMLTreeInfo *) NULL)
2168     {
2169       node=xml_info->parent->child;
2170       if (node == xml_info)
2171         xml_info->parent->child=xml_info->ordered;
2172       else
2173         {
2174           while (node->ordered != xml_info)
2175             node=node->ordered;
2176           node->ordered=node->ordered->ordered;
2177           node=xml_info->parent->child;
2178           if (strcmp(node->tag,xml_info->tag) != 0)
2179             {
2180               while (strcmp(node->sibling->tag,xml_info->tag) != 0)
2181                 node=node->sibling;
2182               if (node->sibling != xml_info)
2183                 node=node->sibling;
2184               else
2185                 node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
2186                   xml_info->next : node->sibling->sibling;
2187             }
2188           while ((node->next != (XMLTreeInfo *) NULL) &&
2189                  (node->next != xml_info))
2190             node=node->next;
2191           if (node->next != (XMLTreeInfo *) NULL)
2192             node->next=node->next->next;
2193         }
2194     }
2195   xml_info->ordered=(XMLTreeInfo *) NULL;
2196   xml_info->sibling=(XMLTreeInfo *) NULL;
2197   xml_info->next=(XMLTreeInfo *) NULL;
2198   return(xml_info);
2199 }
2200 \f
2201 /*
2202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2203 %                                                                             %
2204 %                                                                             %
2205 %                                                                             %
2206 %   S e t X M L T r e e A t t r i b u t e                                     %
2207 %                                                                             %
2208 %                                                                             %
2209 %                                                                             %
2210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2211 %
2212 %  SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
2213 %  found.  A value of NULL removes the specified attribute.
2214 %
2215 %  The format of the SetXMLTreeAttribute method is:
2216 %
2217 %      XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
2218 %        const char *value)
2219 %
2220 %  A description of each parameter follows:
2221 %
2222 %    o xml_info: the xml info.
2223 %
2224 %    o tag:  The attribute tag.
2225 %
2226 %    o value:  The attribute value.
2227 %
2228 */
2229 MagickPrivate XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
2230   const char *tag,const char *value)
2231 {
2232   register ssize_t
2233     i;
2234
2235   ssize_t
2236     j;
2237
2238   assert(xml_info != (XMLTreeInfo *) NULL);
2239   assert((xml_info->signature == MagickSignature) ||
2240          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2241   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2242   i=0;
2243   while ((xml_info->attributes[i] != (char *) NULL) &&
2244          (strcmp(xml_info->attributes[i],tag) != 0))
2245     i+=2;
2246   if (xml_info->attributes[i] == (char *) NULL)
2247     {
2248       /*
2249         Add new attribute tag.
2250       */
2251       if (value == (const char *) NULL)
2252         return(xml_info);
2253       if (xml_info->attributes != sentinel)
2254         xml_info->attributes=(char **) ResizeQuantumMemory(
2255           xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
2256       else
2257         {
2258           xml_info->attributes=(char **) AcquireQuantumMemory(4,
2259             sizeof(*xml_info->attributes));
2260           if (xml_info->attributes != (char **) NULL)
2261             xml_info->attributes[1]=ConstantString("");
2262         }
2263       if (xml_info->attributes == (char **) NULL)
2264         ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2265       xml_info->attributes[i]=ConstantString(tag);
2266       xml_info->attributes[i+2]=(char *) NULL;
2267       (void) strlen(xml_info->attributes[i+1]);
2268     }
2269   /*
2270     Add new value to an existing attribute.
2271   */
2272   for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
2273   if (xml_info->attributes[i+1] != (char *) NULL)
2274     xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
2275   if (value != (const char *) NULL)
2276     {
2277       xml_info->attributes[i+1]=ConstantString(value);
2278       return(xml_info);
2279     }
2280   if (xml_info->attributes[i] != (char *) NULL)
2281     xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
2282   (void) CopyMagickMemory(xml_info->attributes+i,xml_info->attributes+i+2,
2283     (size_t) (j-i)*sizeof(*xml_info->attributes));
2284   xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
2285     (size_t) (j+2),sizeof(*xml_info->attributes));
2286   if (xml_info->attributes == (char **) NULL)
2287     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2288   j-=2;
2289   (void) CopyMagickMemory(xml_info->attributes[j+1]+(i/2),
2290     xml_info->attributes[j+1]+(i/2)+1,(size_t) (((j+2)/2)-(i/2))*
2291     sizeof(*xml_info->attributes));
2292   return(xml_info);
2293 }
2294 \f
2295 /*
2296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2297 %                                                                             %
2298 %                                                                             %
2299 %                                                                             %
2300 %   S e t X M L T r e e C o n t e n t                                         %
2301 %                                                                             %
2302 %                                                                             %
2303 %                                                                             %
2304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2305 %
2306 %  SetXMLTreeContent() sets the character content for the given tag and
2307 %  returns the tag.
2308 %
2309 %  The format of the SetXMLTreeContent method is:
2310 %
2311 %      XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2312 %        const char *content)
2313 %
2314 %  A description of each parameter follows:
2315 %
2316 %    o xml_info: the xml info.
2317 %
2318 %    o content:  The content.
2319 %
2320 */
2321 MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2322   const char *content)
2323 {
2324   assert(xml_info != (XMLTreeInfo *) NULL);
2325   assert((xml_info->signature == MagickSignature) ||
2326          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2327   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2328   if (xml_info->content != (char *) NULL)
2329     xml_info->content=DestroyString(xml_info->content);
2330   xml_info->content=(char *) ConstantString(content);
2331   return(xml_info);
2332 }
2333 \f
2334 /*
2335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2336 %                                                                             %
2337 %                                                                             %
2338 %                                                                             %
2339 %   X M L T r e e I n f o T o X M L                                           %
2340 %                                                                             %
2341 %                                                                             %
2342 %                                                                             %
2343 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2344 %
2345 %  XMLTreeInfoToXML() converts an xml-tree to an XML string.
2346 %
2347 %  The format of the XMLTreeInfoToXML method is:
2348 %
2349 %      char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2350 %
2351 %  A description of each parameter follows:
2352 %
2353 %    o xml_info: the xml info.
2354 %
2355 */
2356
2357 static char *EncodePredefinedEntities(const char *source,ssize_t offset,
2358   char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
2359 {
2360   char
2361     *canonical_content;
2362
2363   if (offset < 0)
2364     canonical_content=CanonicalXMLContent(source,pedantic);
2365   else
2366     {
2367       char
2368         *content;
2369
2370       content=AcquireString(source);
2371       content[offset]='\0';
2372       canonical_content=CanonicalXMLContent(content,pedantic);
2373       content=DestroyString(content);
2374     }
2375   if (canonical_content == (char *) NULL)
2376     return(*destination);
2377   if ((*length+strlen(canonical_content)+MaxTextExtent) > *extent)
2378     {
2379       *extent=(*length)+strlen(canonical_content)+MaxTextExtent;
2380       *destination=(char *) ResizeQuantumMemory(*destination,*extent,
2381         sizeof(**destination));
2382       if (*destination == (char *) NULL)
2383         return(*destination);
2384     }
2385   *length+=FormatLocaleString(*destination+(*length),*extent,"%s",
2386     canonical_content);
2387   canonical_content=DestroyString(canonical_content);
2388   return(*destination);
2389 }
2390
2391 static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
2392   size_t *extent,size_t start,char ***attributes)
2393 {
2394   char
2395     *content;
2396
2397   const char
2398     *attribute;
2399
2400   register ssize_t
2401     i;
2402
2403   size_t
2404     offset;
2405
2406   ssize_t
2407     j;
2408
2409   content=(char *) "";
2410   if (xml_info->parent != (XMLTreeInfo *) NULL)
2411     content=xml_info->parent->content;
2412   offset=0;
2413   *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
2414     start),source,length,extent,MagickFalse);
2415   if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2416     {
2417       *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2418       *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(*source));
2419       if (*source == (char *) NULL)
2420         return(*source);
2421     }
2422   *length+=FormatLocaleString(*source+(*length),*extent,"<%s",xml_info->tag);
2423   for (i=0; xml_info->attributes[i]; i+=2)
2424   {
2425     attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
2426     if (attribute != xml_info->attributes[i+1])
2427       continue;
2428     if ((*length+strlen(xml_info->attributes[i])+MaxTextExtent) > *extent)
2429       {
2430         *extent=(*length)+strlen(xml_info->attributes[i])+MaxTextExtent;
2431         *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2432         if (*source == (char *) NULL)
2433           return((char *) NULL);
2434       }
2435     *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
2436       xml_info->attributes[i]);
2437     (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
2438       extent,MagickTrue);
2439     *length+=FormatLocaleString(*source+(*length),*extent,"\"");
2440   }
2441   i=0;
2442   while ((attributes[i] != (char **) NULL) &&
2443          (strcmp(attributes[i][0],xml_info->tag) != 0))
2444     i++;
2445   j=1;
2446   while ((attributes[i] != (char **) NULL) &&
2447          (attributes[i][j] != (char *) NULL))
2448   {
2449     if ((attributes[i][j+1] == (char *) NULL) ||
2450         (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
2451       {
2452         j+=3;
2453         continue;
2454       }
2455     if ((*length+strlen(attributes[i][j])+MaxTextExtent) > *extent)
2456       {
2457         *extent=(*length)+strlen(attributes[i][j])+MaxTextExtent;
2458         *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2459         if (*source == (char *) NULL)
2460           return((char *) NULL);
2461       }
2462     *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
2463       attributes[i][j]);
2464     (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
2465       MagickTrue);
2466     *length+=FormatLocaleString(*source+(*length),*extent,"\"");
2467     j+=3;
2468   }
2469   *length+=FormatLocaleString(*source+(*length),*extent,*xml_info->content ?
2470     ">" : "/>");
2471   if (xml_info->child != (XMLTreeInfo *) NULL)
2472     *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
2473   else
2474     *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
2475       MagickFalse);
2476   if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2477     {
2478       *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2479       *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2480       if (*source == (char *) NULL)
2481         return((char *) NULL);
2482     }
2483   if (*xml_info->content != '\0')
2484     *length+=FormatLocaleString(*source+(*length),*extent,"</%s>",
2485       xml_info->tag);
2486   while ((content[offset] != '\0') && (offset < xml_info->offset))
2487     offset++;
2488   if (xml_info->ordered != (XMLTreeInfo *) NULL)
2489     content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
2490       attributes);
2491   else
2492     content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
2493       MagickFalse);
2494   return(content);
2495 }
2496
2497 MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2498 {
2499   char
2500     *xml;
2501
2502   register char
2503     *p,
2504     *q;
2505
2506   register ssize_t
2507     i;
2508
2509   size_t
2510     extent,
2511     length;
2512
2513   ssize_t
2514     j,
2515     k;
2516
2517   XMLTreeInfo
2518     *ordered,
2519     *parent;
2520
2521   XMLTreeRoot
2522     *root;
2523
2524   assert(xml_info != (XMLTreeInfo *) NULL);
2525   assert((xml_info->signature == MagickSignature) ||
2526          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2527   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2528   if (xml_info->tag == (char *) NULL)
2529     return((char *) NULL);
2530   xml=AcquireString((char *) NULL);
2531   length=0;
2532   extent=MaxTextExtent;
2533   root=(XMLTreeRoot *) xml_info;
2534   while (root->root.parent != (XMLTreeInfo *) NULL)
2535     root=(XMLTreeRoot *) root->root.parent;
2536   parent=(XMLTreeInfo *) NULL;
2537   if (xml_info != (XMLTreeInfo *) NULL)
2538     parent=xml_info->parent;
2539   if (parent == (XMLTreeInfo *) NULL)
2540     for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2541     {
2542       /*
2543         Pre-root processing instructions.
2544       */
2545       for (k=2; root->processing_instructions[i][k-1]; k++) ;
2546       p=root->processing_instructions[i][1];
2547       for (j=1; p != (char *) NULL; j++)
2548       {
2549         if (root->processing_instructions[i][k][j-1] == '>')
2550           {
2551             p=root->processing_instructions[i][j];
2552             continue;
2553           }
2554         q=root->processing_instructions[i][0];
2555         if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2556           {
2557             extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2558             xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2559             if (xml == (char *) NULL)
2560               return(xml);
2561           }
2562         length+=FormatLocaleString(xml+length,extent,"<?%s%s%s?>\n",q,
2563           *p != '\0' ? " " : "",p);
2564         p=root->processing_instructions[i][j];
2565       }
2566     }
2567   ordered=(XMLTreeInfo *) NULL;
2568   if (xml_info != (XMLTreeInfo *) NULL)
2569     ordered=xml_info->ordered;
2570   xml_info->parent=(XMLTreeInfo *) NULL;
2571   xml_info->ordered=(XMLTreeInfo *) NULL;
2572   xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
2573   xml_info->parent=parent;
2574   xml_info->ordered=ordered;
2575   if (parent == (XMLTreeInfo *) NULL)
2576     for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2577     {
2578       /*
2579         Post-root processing instructions.
2580       */
2581       for (k=2; root->processing_instructions[i][k-1]; k++) ;
2582       p=root->processing_instructions[i][1];
2583       for (j=1; p != (char *) NULL; j++)
2584       {
2585         if (root->processing_instructions[i][k][j-1] == '<')
2586           {
2587             p=root->processing_instructions[i][j];
2588             continue;
2589           }
2590         q=root->processing_instructions[i][0];
2591         if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2592           {
2593             extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2594             xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2595             if (xml == (char *) NULL)
2596               return(xml);
2597           }
2598         length+=FormatLocaleString(xml+length,extent,"\n<?%s%s%s?>",q,
2599           *p != '\0' ? " " : "",p);
2600         p=root->processing_instructions[i][j];
2601       }
2602     }
2603   return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));
2604 }