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