]> 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               if (entity != (char *) NULL)
1523                 (void) CopyMagickMemory(xml+length,entity+1,strlen(entity));
1524               (void) strncpy(xml,entities[i],length);
1525             }
1526         }
1527       else
1528         if (((state == ' ') || (state == '*')) &&
1529             (isspace((int) ((unsigned char) *xml) != 0)))
1530           *(xml++)=' ';
1531         else
1532           xml++;
1533   }
1534   if (state == '*')
1535     {
1536       /*
1537         Normalize spaces for non-CDATA attributes.
1538       */
1539       for (xml=p; *xml != '\0'; xml++)
1540       {
1541         i=(ssize_t) strspn(xml," ");
1542         if (i != 0)
1543           (void) CopyMagickMemory(xml,xml+i,strlen(xml+i)+1);
1544         while ((*xml != '\0') && (*xml != ' '))
1545           xml++;
1546       }
1547       xml--;
1548       if ((xml >= p) && (*xml == ' '))
1549         *xml='\0';
1550     }
1551   return(p == q ? ConstantString(p) : p);
1552 }
1553
1554 static void ParseCharacterContent(XMLTreeRoot *root,char *xml,
1555   const size_t length,const char state)
1556 {
1557   XMLTreeInfo
1558     *xml_info;
1559
1560   xml_info=root->node;
1561   if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) ||
1562       (length == 0))
1563     return;
1564   xml[length]='\0';
1565   xml=ParseEntities(xml,root->entities,state);
1566   if ((xml_info->content != (char *) NULL) && (*xml_info->content != '\0'))
1567     {
1568       (void) ConcatenateString(&xml_info->content,xml);
1569       xml=DestroyString(xml);
1570     }
1571   else
1572     {
1573       if (xml_info->content != (char *) NULL)
1574         xml_info->content=DestroyString(xml_info->content);
1575       xml_info->content=xml;
1576     }
1577 }
1578
1579 static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag,
1580   ExceptionInfo *exception)
1581 {
1582   if ((root->node == (XMLTreeInfo *) NULL) ||
1583       (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0))
1584     {
1585       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1586         "ParseError","unexpected closing tag </%s>",tag);
1587       return(&root->root);
1588     }
1589   root->node=root->node->parent;
1590   return((XMLTreeInfo *) NULL);
1591 }
1592
1593 static MagickBooleanType ValidateEntities(char *tag,char *xml,char **entities)
1594 {
1595   register ssize_t
1596     i;
1597
1598   /*
1599     Check for circular entity references.
1600   */
1601   for ( ; ; xml++)
1602   {
1603     while ((*xml != '\0') && (*xml != '&'))
1604       xml++;
1605     if (*xml == '\0')
1606       return(MagickTrue);
1607     if (strncmp(xml+1,tag,strlen(tag)) == 0)
1608       return(MagickFalse);
1609     i=0;
1610     while ((entities[i] != (char *) NULL) &&
1611            (strncmp(entities[i],xml+1,strlen(entities[i])) == 0))
1612       i+=2;
1613     if ((entities[i] != (char *) NULL) &&
1614         (ValidateEntities(tag,entities[i+1],entities) == 0))
1615       return(MagickFalse);
1616   }
1617 }
1618
1619 static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml,
1620   size_t length)
1621 {
1622   char
1623     *target;
1624
1625   register ssize_t
1626     i;
1627
1628   ssize_t
1629     j;
1630
1631   target=xml;
1632   xml[length]='\0';
1633   xml+=strcspn(xml,XMLWhitespace);
1634   if (*xml != '\0')
1635     {
1636       *xml='\0';
1637       xml+=strspn(xml+1,XMLWhitespace)+1;
1638     }
1639   if (strcmp(target,"xml") == 0)
1640     {
1641       xml=strstr(xml,"standalone");
1642       if ((xml != (char *) NULL) &&
1643           (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0))
1644         root->standalone=MagickTrue;
1645       return;
1646     }
1647   if (root->processing_instructions[0] == (char **) NULL)
1648     {
1649       root->processing_instructions=(char ***) AcquireMagickMemory(sizeof(
1650         *root->processing_instructions));
1651       if (root->processing_instructions ==(char ***) NULL)
1652         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1653       *root->processing_instructions=(char **) NULL;
1654     }
1655   i=0;
1656   while ((root->processing_instructions[i] != (char **) NULL) &&
1657          (strcmp(target,root->processing_instructions[i][0]) != 0))
1658     i++;
1659   if (root->processing_instructions[i] == (char **) NULL)
1660     {
1661       root->processing_instructions=(char ***) ResizeQuantumMemory(
1662         root->processing_instructions,(size_t) (i+2),
1663         sizeof(*root->processing_instructions));
1664       if (root->processing_instructions == (char ***) NULL)
1665         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1666       root->processing_instructions[i]=(char **) AcquireQuantumMemory(3,
1667         sizeof(**root->processing_instructions));
1668       if (root->processing_instructions[i] == (char **) NULL)
1669         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1670       root->processing_instructions[i+1]=(char **) NULL;
1671       root->processing_instructions[i][0]=ConstantString(target);
1672       root->processing_instructions[i][1]=(char *)
1673         root->processing_instructions[i+1];
1674       root->processing_instructions[i+1]=(char **) NULL;
1675       root->processing_instructions[i][2]=ConstantString("");
1676     }
1677   j=1;
1678   while (root->processing_instructions[i][j] != (char *) NULL)
1679     j++;
1680   root->processing_instructions[i]=(char **) ResizeQuantumMemory(
1681     root->processing_instructions[i],(size_t) (j+3),
1682     sizeof(**root->processing_instructions));
1683   if (root->processing_instructions[i] == (char **) NULL)
1684     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1685   root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory(
1686     root->processing_instructions[i][j+1],(size_t) (j+1),
1687     sizeof(*root->processing_instructions));
1688   if (root->processing_instructions[i][j+2] == (char *) NULL)
1689     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1690   (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1,
1691     root->root.tag != (char *) NULL ? ">" : "<",2);
1692   root->processing_instructions[i][j]=ConstantString(xml);
1693   root->processing_instructions[i][j+1]=(char *) NULL;
1694 }
1695
1696 static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml,
1697   size_t length,ExceptionInfo *exception)
1698 {
1699   char
1700     *c,
1701     **entities,
1702     *n,
1703     **predefined_entitites,
1704     q,
1705     *t,
1706     *v;
1707
1708   register ssize_t
1709     i;
1710
1711   ssize_t
1712     j;
1713
1714   n=(char *) NULL;
1715   predefined_entitites=(char **) AcquireMagickMemory(sizeof(sentinel));
1716   if (predefined_entitites == (char **) NULL)
1717     ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed");
1718   (void) CopyMagickMemory(predefined_entitites,sentinel,sizeof(sentinel));
1719   for (xml[length]='\0'; xml != (char *) NULL; )
1720   {
1721     while ((*xml != '\0') && (*xml != '<') && (*xml != '%'))
1722       xml++;
1723     if (*xml == '\0')
1724       break;
1725     if (strncmp(xml,"<!ENTITY",8) == 0)
1726       {
1727         /*
1728           Parse entity definitions.
1729         */
1730         xml+=strspn(xml+8,XMLWhitespace)+8;
1731         c=xml;
1732         n=xml+strspn(xml,XMLWhitespace "%");
1733         xml=n+strcspn(n,XMLWhitespace);
1734         *xml=';';
1735         v=xml+strspn(xml+1,XMLWhitespace)+1;
1736         q=(*v);
1737         v++;
1738         if ((q != '"') && (q != '\''))
1739           {
1740             /*
1741               Skip externals.
1742             */
1743             xml=strchr(xml,'>');
1744             continue;
1745           }
1746         entities=(*c == '%') ? predefined_entitites : root->entities;
1747         for (i=0; entities[i] != (char *) NULL; i++) ;
1748         entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3),
1749           sizeof(*entities));
1750         if (entities == (char **) NULL)
1751           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1752         if (*c == '%')
1753           predefined_entitites=entities;
1754         else
1755           root->entities=entities;
1756         xml++;
1757         *xml='\0';
1758         xml=strchr(v,q);
1759         if (xml != (char *) NULL)
1760           {
1761             *xml='\0';
1762             xml++;
1763           }
1764         entities[i+1]=ParseEntities(v,predefined_entitites,'%');
1765         entities[i+2]=(char *) NULL;
1766         if (ValidateEntities(n,entities[i+1],entities) != MagickFalse)
1767           entities[i]=n;
1768         else
1769           {
1770             if (entities[i+1] != v)
1771               entities[i+1]=DestroyString(entities[i+1]);
1772             (void) ThrowMagickException(exception,GetMagickModule(),
1773               OptionWarning,"ParseError","circular entity declaration &%s",n);
1774             predefined_entitites=(char **) RelinquishMagickMemory(
1775               predefined_entitites);
1776             return(MagickFalse);
1777           }
1778         }
1779       else
1780        if (strncmp(xml,"<!ATTLIST",9) == 0)
1781          {
1782             /*
1783               Parse default attributes.
1784             */
1785             t=xml+strspn(xml+9,XMLWhitespace)+9;
1786             if (*t == '\0')
1787               {
1788                 (void) ThrowMagickException(exception,GetMagickModule(),
1789                   OptionWarning,"ParseError","unclosed <!ATTLIST");
1790                 predefined_entitites=(char **) RelinquishMagickMemory(
1791                   predefined_entitites);
1792                 return(MagickFalse);
1793               }
1794             xml=t+strcspn(t,XMLWhitespace ">");
1795             if (*xml == '>')
1796               continue;
1797             *xml='\0';
1798             i=0;
1799             while ((root->attributes[i] != (char **) NULL) &&
1800                    (n != (char *) NULL) &&
1801                    (strcmp(n,root->attributes[i][0]) != 0))
1802               i++;
1803             while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') &&
1804                    (*n != '>'))
1805             {
1806               xml=n+strcspn(n,XMLWhitespace);
1807               if (*xml != '\0')
1808                 *xml='\0';
1809               else
1810                 {
1811                   (void) ThrowMagickException(exception,GetMagickModule(),
1812                     OptionWarning,"ParseError","malformed <!ATTLIST");
1813                   predefined_entitites=(char **) RelinquishMagickMemory(
1814                     predefined_entitites);
1815                   return(MagickFalse);
1816                 }
1817               xml+=strspn(xml+1,XMLWhitespace)+1;
1818               c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
1819               if (strncmp(xml,"NOTATION",8) == 0)
1820                 xml+=strspn(xml+8,XMLWhitespace)+8;
1821               xml=(*xml == '(') ? strchr(xml,')') : xml+
1822                 strcspn(xml,XMLWhitespace);
1823               if (xml == (char *) NULL)
1824                 {
1825                   (void) ThrowMagickException(exception,GetMagickModule(),
1826                     OptionWarning,"ParseError","malformed <!ATTLIST");
1827                   predefined_entitites=(char **) RelinquishMagickMemory(
1828                     predefined_entitites);
1829                   return(MagickFalse);
1830                 }
1831               xml+=strspn(xml,XMLWhitespace ")");
1832               if (strncmp(xml,"#FIXED",6) == 0)
1833                 xml+=strspn(xml+6,XMLWhitespace)+6;
1834               if (*xml == '#')
1835                 {
1836                   xml+=strcspn(xml,XMLWhitespace ">")-1;
1837                   if (*c == ' ')
1838                     continue;
1839                   v=(char *) NULL;
1840                 }
1841               else
1842                 if (((*xml == '"') || (*xml == '\''))  &&
1843                     ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
1844                   *xml='\0';
1845                 else
1846                   {
1847                     (void) ThrowMagickException(exception,GetMagickModule(),
1848                       OptionWarning,"ParseError","malformed <!ATTLIST");
1849                     predefined_entitites=(char **) RelinquishMagickMemory(
1850                       predefined_entitites);
1851                     return(MagickFalse);
1852                   }
1853               if (root->attributes[i] == (char **) NULL)
1854                 {
1855                   /*
1856                     New attribute tag.
1857                   */
1858                   if (i == 0)
1859                     root->attributes=(char ***) AcquireQuantumMemory(2,
1860                       sizeof(*root->attributes));
1861                   else
1862                     root->attributes=(char ***) ResizeQuantumMemory(
1863                       root->attributes,(size_t) (i+2),
1864                       sizeof(*root->attributes));
1865                   if (root->attributes == (char ***) NULL)
1866                     ThrowFatalException(ResourceLimitFatalError,
1867                       "MemoryAllocationFailed");
1868                   root->attributes[i]=(char **) AcquireQuantumMemory(2,
1869                     sizeof(*root->attributes));
1870                   if (root->attributes[i] == (char **) NULL)
1871                     ThrowFatalException(ResourceLimitFatalError,
1872                       "MemoryAllocationFailed");
1873                   root->attributes[i][0]=ConstantString(t);
1874                   root->attributes[i][1]=(char *) NULL;
1875                   root->attributes[i+1]=(char **) NULL;
1876                 }
1877               for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
1878               root->attributes[i]=(char **) ResizeQuantumMemory(
1879                 root->attributes[i],(size_t) (j+4),sizeof(*root->attributes));
1880               if (root->attributes[i] == (char **) NULL)
1881                 ThrowFatalException(ResourceLimitFatalError,
1882                   "MemoryAllocationFailed");
1883               root->attributes[i][j+3]=(char *) NULL;
1884               root->attributes[i][j+2]=ConstantString(c);
1885               root->attributes[i][j+1]=(char *) NULL;
1886               if (v != (char *) NULL)
1887                 root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
1888               root->attributes[i][j]=ConstantString(n);
1889             }
1890         }
1891       else
1892         if (strncmp(xml, "<!--", 4) == 0)
1893           xml=strstr(xml+4,"-->");
1894         else
1895           if (strncmp(xml,"<?", 2) == 0)
1896             {
1897               c=xml+2;
1898               xml=strstr(c,"?>");
1899               if (xml != (char *) NULL)
1900                 {
1901                   ParseProcessingInstructions(root,c,(size_t) (xml-c));
1902                   xml++;
1903                 }
1904             }
1905            else
1906              if (*xml == '<')
1907                xml=strchr(xml,'>');
1908              else
1909                if ((*(xml++) == '%') && (root->standalone == MagickFalse))
1910                  break;
1911     }
1912   predefined_entitites=(char **) RelinquishMagickMemory(predefined_entitites);
1913   return(MagickTrue);
1914 }
1915
1916 static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
1917 {
1918   XMLTreeInfo
1919     *xml_info;
1920
1921   xml_info=root->node;
1922   if (xml_info->tag == (char *) NULL)
1923     xml_info->tag=ConstantString(tag);
1924   else
1925     xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
1926   if (xml_info != (XMLTreeInfo *) NULL)
1927     xml_info->attributes=attributes;
1928   root->node=xml_info;
1929 }
1930
1931 static const char
1932   *ignore_tags[3] =
1933   {
1934     "rdf:Bag",
1935     "rdf:Seq",
1936     (const char *) NULL
1937   };
1938
1939 static inline MagickBooleanType IsSkipTag(const char *tag)
1940 {
1941   register ssize_t
1942     i;
1943
1944   i=0;
1945   while (ignore_tags[i] != (const char *) NULL)
1946   {
1947     if (LocaleCompare(tag,ignore_tags[i]) == 0)
1948       return(MagickTrue);
1949     i++;
1950   }
1951   return(MagickFalse);
1952 }
1953
1954 MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1955 {
1956   char
1957     **attribute,
1958     **attributes,
1959     *tag,
1960     *utf8;
1961
1962   int
1963     c,
1964     terminal;
1965
1966   MagickBooleanType
1967     status;
1968
1969   register char
1970     *p;
1971
1972   register ssize_t
1973     i;
1974
1975   size_t
1976     ignore_depth,
1977     length;
1978
1979   ssize_t
1980     j,
1981     l;
1982
1983   XMLTreeRoot
1984     *root;
1985
1986   /*
1987     Convert xml-string to UTF8.
1988   */
1989   if ((xml == (const char *) NULL) || (strlen(xml) == 0))
1990     {
1991       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1992         "ParseError","root tag missing");
1993       return((XMLTreeInfo *) NULL);
1994     }
1995   root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
1996   length=strlen(xml);
1997   utf8=ConvertUTF16ToUTF8(xml,&length);
1998   if (utf8 == (char *) NULL)
1999     {
2000       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2001         "ParseError","UTF16 to UTF8 failed");
2002       return((XMLTreeInfo *) NULL);
2003     }
2004   terminal=utf8[length-1];
2005   utf8[length-1]='\0';
2006   p=utf8;
2007   while ((*p != '\0') && (*p != '<'))
2008     p++;
2009   if (*p == '\0')
2010     {
2011       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2012         "ParseError","root tag missing");
2013       utf8=DestroyString(utf8);
2014       return((XMLTreeInfo *) NULL);
2015     }
2016   attribute=(char **) NULL;
2017   l=0;
2018   ignore_depth=0;
2019   for (p++; ; p++)
2020   {
2021     attributes=(char **) sentinel;
2022     tag=p;
2023     c=(*p);
2024     if ((isalpha((int) ((unsigned char) *p)) !=0) || (*p == '_') ||
2025         (*p == ':') || (c < '\0'))
2026       {
2027         /*
2028           Tag.
2029         */
2030         if (root->node == (XMLTreeInfo *) NULL)
2031           {
2032             (void) ThrowMagickException(exception,GetMagickModule(),
2033               OptionWarning,"ParseError","root tag missing");
2034             utf8=DestroyString(utf8);
2035             return(&root->root);
2036           }
2037         p+=strcspn(p,XMLWhitespace "/>");
2038         while (isspace((int) ((unsigned char) *p)) != 0)
2039           *p++='\0';
2040         if (ignore_depth == 0)
2041           {
2042             if ((*p != '\0') && (*p != '/') && (*p != '>'))
2043               {
2044                 /*
2045                   Find tag in default attributes list.
2046                 */
2047                 i=0;
2048                 while ((root->attributes[i] != (char **) NULL) &&
2049                        (strcmp(root->attributes[i][0],tag) != 0))
2050                   i++;
2051                 attribute=root->attributes[i];
2052               }
2053             for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
2054             {
2055               /*
2056                 Attribute.
2057               */
2058               if (l == 0)
2059                 attributes=(char **) AcquireQuantumMemory(4,
2060                   sizeof(*attributes));
2061               else
2062                 attributes=(char **) ResizeQuantumMemory(attributes,
2063                   (size_t) (l+4),sizeof(*attributes));
2064               if (attributes == (char **) NULL)
2065                 {
2066                   (void) ThrowMagickException(exception,GetMagickModule(),
2067                     ResourceLimitError,"MemoryAllocationFailed","`%s'","");
2068                   utf8=DestroyString(utf8);
2069                   return(&root->root);
2070                 }
2071               attributes[l+2]=(char *) NULL;
2072               attributes[l+1]=(char *) NULL;
2073               attributes[l]=p;
2074               p+=strcspn(p,XMLWhitespace "=/>");
2075               if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
2076                 attributes[l]=ConstantString("");
2077               else
2078                 {
2079                   *p++='\0';
2080                   p+=strspn(p,XMLWhitespace "=");
2081                   c=(*p);
2082                   if ((c == '"') || (c == '\''))
2083                     {
2084                       /*
2085                         Attributes value.
2086                       */
2087                       p++;
2088                       attributes[l+1]=p;
2089                       while ((*p != '\0') && (*p != c))
2090                         p++;
2091                       if (*p != '\0')
2092                         *p++='\0';
2093                       else
2094                         {
2095                           attributes[l]=ConstantString("");
2096                           attributes[l+1]=ConstantString("");
2097                           (void) DestroyXMLTreeAttributes(attributes);
2098                           (void) ThrowMagickException(exception,
2099                             GetMagickModule(),OptionWarning,"ParseError",
2100                             "missing %c",c);
2101                           utf8=DestroyString(utf8);
2102                           return(&root->root);
2103                         }
2104                       j=1;
2105                       while ((attribute != (char **) NULL) &&
2106                              (attribute[j] != (char *) NULL) &&
2107                              (strcmp(attribute[j],attributes[l]) != 0))
2108                         j+=3;
2109                       attributes[l+1]=ParseEntities(attributes[l+1],
2110                         root->entities,(attribute != (char **) NULL) &&
2111                         (attribute[j] != (char *) NULL) ? *attribute[j+2] :
2112                         ' ');
2113                     }
2114                   attributes[l]=ConstantString(attributes[l]);
2115                 }
2116               while (isspace((int) ((unsigned char) *p)) != 0)
2117                 p++;
2118             }
2119           }
2120         else
2121           {
2122             while((*p != '\0') && (*p != '/') && (*p != '>'))
2123               p++;
2124           }
2125         if (*p == '/')
2126           {
2127             /*
2128               Self closing tag.
2129             */
2130             *p++='\0';
2131             if (((*p != '\0') && (*p != '>')) ||
2132                 ((*p == '\0') && (terminal != '>')))
2133               {
2134                 if (l != 0)
2135                   (void) DestroyXMLTreeAttributes(attributes);
2136                 (void) ThrowMagickException(exception,GetMagickModule(),
2137                   OptionWarning,"ParseError","missing >");
2138                 utf8=DestroyString(utf8);
2139                 return(&root->root);
2140               }
2141             if ((ignore_depth == 0) && (IsSkipTag(tag) == MagickFalse))
2142               {
2143                 ParseOpenTag(root,tag,attributes);
2144                 (void) ParseCloseTag(root,tag,exception);
2145               }
2146           }
2147         else
2148           {
2149             c=(*p);
2150             if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
2151               {
2152                 *p='\0';
2153                 if ((ignore_depth == 0) && (IsSkipTag(tag) == MagickFalse))
2154                   ParseOpenTag(root,tag,attributes);
2155                 else
2156                   ignore_depth++;
2157                 *p=c;
2158               }
2159             else
2160               {
2161                 if (l != 0)
2162                   (void) DestroyXMLTreeAttributes(attributes);
2163                 (void) ThrowMagickException(exception,GetMagickModule(),
2164                   OptionWarning,"ParseError","missing >");
2165                 utf8=DestroyString(utf8);
2166                 return(&root->root);
2167               }
2168           }
2169       }
2170     else
2171       if (*p == '/')
2172         {
2173           /*
2174             Close tag.
2175           */
2176           tag=p+1;
2177           p+=strcspn(tag,XMLWhitespace ">")+1;
2178           c=(*p);
2179           if ((c == '\0') && (terminal != '>'))
2180             {
2181               (void) ThrowMagickException(exception,GetMagickModule(),
2182                 OptionWarning,"ParseError","missing >");
2183               utf8=DestroyString(utf8);
2184               return(&root->root);
2185             }
2186           *p='\0';
2187           if (ignore_depth == 0 && ParseCloseTag(root,tag,exception) !=
2188               (XMLTreeInfo *) NULL)
2189             {
2190               utf8=DestroyString(utf8);
2191               return(&root->root);
2192             }
2193           if (ignore_depth > 0)
2194             ignore_depth--;
2195           *p=c;
2196           if (isspace((int) ((unsigned char) *p)) != 0)
2197             p+=strspn(p,XMLWhitespace);
2198         }
2199       else
2200         if (strncmp(p,"!--",3) == 0)
2201           {
2202             /*
2203               Comment.
2204             */
2205             p=strstr(p+3,"--");
2206             if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
2207                 ((*p == '\0') && (terminal != '>')))
2208               {
2209                 (void) ThrowMagickException(exception,GetMagickModule(),
2210                   OptionWarning,"ParseError","unclosed <!--");
2211                 utf8=DestroyString(utf8);
2212                 return(&root->root);
2213               }
2214           }
2215         else
2216           if (strncmp(p,"![CDATA[",8) == 0)
2217             {
2218               /*
2219                 Cdata.
2220               */
2221               p=strstr(p,"]]>");
2222               if (p != (char *) NULL)
2223                 {
2224                   p+=2;
2225                   if (ignore_depth == 0)
2226                     ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
2227                 }
2228               else
2229                 {
2230                   (void) ThrowMagickException(exception,GetMagickModule(),
2231                     OptionWarning,"ParseError","unclosed <![CDATA[");
2232                   utf8=DestroyString(utf8);
2233                   return(&root->root);
2234                 }
2235             }
2236           else
2237             if (strncmp(p,"!DOCTYPE",8) == 0)
2238               {
2239                 /*
2240                   DTD.
2241                 */
2242                 for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
2243                      ((l != 0) && ((*p != ']') ||
2244                      (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
2245                   l=(ssize_t) ((*p == '[') ? 1 : l))
2246                 p+=strcspn(p+1,"[]>")+1;
2247                 if ((*p == '\0') && (terminal != '>'))
2248                   {
2249                     (void) ThrowMagickException(exception,GetMagickModule(),
2250                       OptionWarning,"ParseError","unclosed <!DOCTYPE");
2251                     utf8=DestroyString(utf8);
2252                     return(&root->root);
2253                   }
2254                 if (l != 0)
2255                   tag=strchr(tag,'[')+1;
2256                 if (l != 0)
2257                   {
2258                     status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
2259                       exception);
2260                     if (status == MagickFalse)
2261                       {
2262                         utf8=DestroyString(utf8);
2263                         return(&root->root);
2264                       }
2265                     p++;
2266                   }
2267               }
2268             else
2269               if (*p == '?')
2270                 {
2271                   /*
2272                     Processing instructions.
2273                   */
2274                   do
2275                   {
2276                     p=strchr(p,'?');
2277                     if (p == (char *) NULL)
2278                       break;
2279                     p++;
2280                   } while ((*p != '\0') && (*p != '>'));
2281                   if ((p == (char *) NULL) || ((*p == '\0') &&
2282                       (terminal != '>')))
2283                     {
2284                       (void) ThrowMagickException(exception,GetMagickModule(),
2285                         OptionWarning,"ParseError","unclosed <?");
2286                       utf8=DestroyString(utf8);
2287                       return(&root->root);
2288                     }
2289                   ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
2290                 }
2291               else
2292                 {
2293                   (void) ThrowMagickException(exception,GetMagickModule(),
2294                     OptionWarning,"ParseError","unexpected <");
2295                   utf8=DestroyString(utf8);
2296                   return(&root->root);
2297                 }
2298      if ((p == (char *) NULL) || (*p == '\0'))
2299        break;
2300      *p++='\0';
2301      tag=p;
2302      if ((*p != '\0') && (*p != '<'))
2303        {
2304         /*
2305           Tag character content.
2306         */
2307         while ((*p != '\0') && (*p != '<'))
2308           p++;
2309         if (*p == '\0')
2310           break;
2311         if (ignore_depth == 0)
2312           ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
2313       }
2314     else
2315       if (*p == '\0')
2316         break;
2317   }
2318   utf8=DestroyString(utf8);
2319   if (root->node == (XMLTreeInfo *) NULL)
2320     return(&root->root);
2321   if (root->node->tag == (char *) NULL)
2322     {
2323       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2324         "ParseError","root tag missing");
2325       return(&root->root);
2326     }
2327   (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2328     "ParseError","unclosed tag: '%s'",root->node->tag);
2329   return(&root->root);
2330 }
2331 \f
2332 /*
2333 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2334 %                                                                             %
2335 %                                                                             %
2336 %                                                                             %
2337 %   N e w X M L T r e e T a g                                                 %
2338 %                                                                             %
2339 %                                                                             %
2340 %                                                                             %
2341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2342 %
2343 %  NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
2344 %
2345 %  The format of the NewXMLTreeTag method is:
2346 %
2347 %      XMLTreeInfo *NewXMLTreeTag(const char *tag)
2348 %
2349 %  A description of each parameter follows:
2350 %
2351 %    o tag: the tag.
2352 %
2353 */
2354 MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
2355 {
2356   static const char
2357     *predefined_entities[NumberPredefinedEntities+1] =
2358     {
2359       "lt;", "&#60;", "gt;", "&#62;", "quot;", "&#34;",
2360       "apos;", "&#39;", "amp;", "&#38;", (char *) NULL
2361     };
2362
2363   XMLTreeRoot
2364     *root;
2365
2366   root=(XMLTreeRoot *) AcquireMagickMemory(sizeof(*root));
2367   if (root == (XMLTreeRoot *) NULL)
2368     return((XMLTreeInfo *) NULL);
2369   (void) ResetMagickMemory(root,0,sizeof(*root));
2370   root->root.tag=(char *) NULL;
2371   if (tag != (char *) NULL)
2372     root->root.tag=ConstantString(tag);
2373   root->node=(&root->root);
2374   root->root.content=ConstantString("");
2375   root->entities=(char **) AcquireMagickMemory(sizeof(predefined_entities));
2376   if (root->entities == (char **) NULL)
2377     return((XMLTreeInfo *) NULL);
2378   (void) CopyMagickMemory(root->entities,predefined_entities,
2379     sizeof(predefined_entities));
2380   root->root.attributes=sentinel;
2381   root->attributes=(char ***) root->root.attributes;
2382   root->processing_instructions=(char ***) root->root.attributes;
2383   root->debug=IsEventLogging();
2384   root->signature=MagickSignature;
2385   return(&root->root);
2386 }
2387 \f
2388 /*
2389 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2390 %                                                                             %
2391 %                                                                             %
2392 %                                                                             %
2393 %   P r u n e T a g F r o m X M L T r e e                                     %
2394 %                                                                             %
2395 %                                                                             %
2396 %                                                                             %
2397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2398 %
2399 %  PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
2400 %  subtags.
2401 %
2402 %  The format of the PruneTagFromXMLTree method is:
2403 %
2404 %      XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2405 %
2406 %  A description of each parameter follows:
2407 %
2408 %    o xml_info: the xml info.
2409 %
2410 */
2411 MagickPrivate XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2412 {
2413   XMLTreeInfo
2414     *node;
2415
2416   assert(xml_info != (XMLTreeInfo *) NULL);
2417   assert((xml_info->signature == MagickSignature) ||
2418          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2419   if (xml_info->debug != MagickFalse)
2420     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2421   if (xml_info->next != (XMLTreeInfo *) NULL)
2422     xml_info->next->sibling=xml_info->sibling;
2423   if (xml_info->parent != (XMLTreeInfo *) NULL)
2424     {
2425       node=xml_info->parent->child;
2426       if (node == xml_info)
2427         xml_info->parent->child=xml_info->ordered;
2428       else
2429         {
2430           while (node->ordered != xml_info)
2431             node=node->ordered;
2432           node->ordered=node->ordered->ordered;
2433           node=xml_info->parent->child;
2434           if (strcmp(node->tag,xml_info->tag) != 0)
2435             {
2436               while (strcmp(node->sibling->tag,xml_info->tag) != 0)
2437                 node=node->sibling;
2438               if (node->sibling != xml_info)
2439                 node=node->sibling;
2440               else
2441                 node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
2442                   xml_info->next : node->sibling->sibling;
2443             }
2444           while ((node->next != (XMLTreeInfo *) NULL) &&
2445                  (node->next != xml_info))
2446             node=node->next;
2447           if (node->next != (XMLTreeInfo *) NULL)
2448             node->next=node->next->next;
2449         }
2450     }
2451   xml_info->ordered=(XMLTreeInfo *) NULL;
2452   xml_info->sibling=(XMLTreeInfo *) NULL;
2453   xml_info->next=(XMLTreeInfo *) NULL;
2454   return(xml_info);
2455 }
2456 \f
2457 /*
2458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2459 %                                                                             %
2460 %                                                                             %
2461 %                                                                             %
2462 %   S e t X M L T r e e A t t r i b u t e                                     %
2463 %                                                                             %
2464 %                                                                             %
2465 %                                                                             %
2466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2467 %
2468 %  SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
2469 %  found.  A value of NULL removes the specified attribute.
2470 %
2471 %  The format of the SetXMLTreeAttribute method is:
2472 %
2473 %      XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
2474 %        const char *value)
2475 %
2476 %  A description of each parameter follows:
2477 %
2478 %    o xml_info: the xml info.
2479 %
2480 %    o tag:  The attribute tag.
2481 %
2482 %    o value:  The attribute value.
2483 %
2484 */
2485 MagickPrivate XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
2486   const char *tag,const char *value)
2487 {
2488   register ssize_t
2489     i;
2490
2491   ssize_t
2492     j;
2493
2494   assert(xml_info != (XMLTreeInfo *) NULL);
2495   assert((xml_info->signature == MagickSignature) ||
2496          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2497   if (xml_info->debug != MagickFalse)
2498     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2499   i=0;
2500   while ((xml_info->attributes[i] != (char *) NULL) &&
2501          (strcmp(xml_info->attributes[i],tag) != 0))
2502     i+=2;
2503   if (xml_info->attributes[i] == (char *) NULL)
2504     {
2505       /*
2506         Add new attribute tag.
2507       */
2508       if (value == (const char *) NULL)
2509         return(xml_info);
2510       if (xml_info->attributes != sentinel)
2511         xml_info->attributes=(char **) ResizeQuantumMemory(
2512           xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
2513       else
2514         {
2515           xml_info->attributes=(char **) AcquireQuantumMemory(4,
2516             sizeof(*xml_info->attributes));
2517           if (xml_info->attributes != (char **) NULL)
2518             xml_info->attributes[1]=ConstantString("");
2519         }
2520       if (xml_info->attributes == (char **) NULL)
2521         ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2522       xml_info->attributes[i]=ConstantString(tag);
2523       xml_info->attributes[i+2]=(char *) NULL;
2524       (void) strlen(xml_info->attributes[i+1]);
2525     }
2526   /*
2527     Add new value to an existing attribute.
2528   */
2529   for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
2530   if (xml_info->attributes[i+1] != (char *) NULL)
2531     xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
2532   if (value != (const char *) NULL)
2533     {
2534       xml_info->attributes[i+1]=ConstantString(value);
2535       return(xml_info);
2536     }
2537   if (xml_info->attributes[i] != (char *) NULL)
2538     xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
2539   (void) CopyMagickMemory(xml_info->attributes+i,xml_info->attributes+i+2,
2540     (size_t) (j-i)*sizeof(*xml_info->attributes));
2541   xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
2542     (size_t) (j+2),sizeof(*xml_info->attributes));
2543   if (xml_info->attributes == (char **) NULL)
2544     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2545   j-=2;
2546   (void) CopyMagickMemory(xml_info->attributes[j+1]+(i/2),
2547     xml_info->attributes[j+1]+(i/2)+1,(size_t) (((j+2)/2)-(i/2))*
2548     sizeof(**xml_info->attributes));
2549   return(xml_info);
2550 }
2551 \f
2552 /*
2553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2554 %                                                                             %
2555 %                                                                             %
2556 %                                                                             %
2557 %   S e t X M L T r e e C o n t e n t                                         %
2558 %                                                                             %
2559 %                                                                             %
2560 %                                                                             %
2561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2562 %
2563 %  SetXMLTreeContent() sets the character content for the given tag and
2564 %  returns the tag.
2565 %
2566 %  The format of the SetXMLTreeContent method is:
2567 %
2568 %      XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2569 %        const char *content)
2570 %
2571 %  A description of each parameter follows:
2572 %
2573 %    o xml_info: the xml info.
2574 %
2575 %    o content:  The content.
2576 %
2577 */
2578 MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2579   const char *content)
2580 {
2581   assert(xml_info != (XMLTreeInfo *) NULL);
2582   assert((xml_info->signature == MagickSignature) ||
2583          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2584   if (xml_info->debug != MagickFalse)
2585     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2586   if (xml_info->content != (char *) NULL)
2587     xml_info->content=DestroyString(xml_info->content);
2588   xml_info->content=(char *) ConstantString(content);
2589   return(xml_info);
2590 }
2591 \f
2592 /*
2593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2594 %                                                                             %
2595 %                                                                             %
2596 %                                                                             %
2597 %   X M L T r e e I n f o T o X M L                                           %
2598 %                                                                             %
2599 %                                                                             %
2600 %                                                                             %
2601 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2602 %
2603 %  XMLTreeInfoToXML() converts an xml-tree to an XML string.
2604 %
2605 %  The format of the XMLTreeInfoToXML method is:
2606 %
2607 %      char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2608 %
2609 %  A description of each parameter follows:
2610 %
2611 %    o xml_info: the xml info.
2612 %
2613 */
2614
2615 static char *EncodePredefinedEntities(const char *source,ssize_t offset,
2616   char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
2617 {
2618   char
2619     *canonical_content;
2620
2621   if (offset < 0)
2622     canonical_content=CanonicalXMLContent(source,pedantic);
2623   else
2624     {
2625       char
2626         *content;
2627
2628       content=AcquireString(source);
2629       content[offset]='\0';
2630       canonical_content=CanonicalXMLContent(content,pedantic);
2631       content=DestroyString(content);
2632     }
2633   if (canonical_content == (char *) NULL)
2634     return(*destination);
2635   if ((*length+strlen(canonical_content)+MaxTextExtent) > *extent)
2636     {
2637       *extent=(*length)+strlen(canonical_content)+MaxTextExtent;
2638       *destination=(char *) ResizeQuantumMemory(*destination,*extent,
2639         sizeof(**destination));
2640       if (*destination == (char *) NULL)
2641         return(*destination);
2642     }
2643   *length+=FormatLocaleString(*destination+(*length),*extent,"%s",
2644     canonical_content);
2645   canonical_content=DestroyString(canonical_content);
2646   return(*destination);
2647 }
2648
2649 static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
2650   size_t *extent,size_t start,char ***attributes)
2651 {
2652   char
2653     *content;
2654
2655   const char
2656     *attribute;
2657
2658   register ssize_t
2659     i;
2660
2661   size_t
2662     offset;
2663
2664   ssize_t
2665     j;
2666
2667   content=(char *) "";
2668   if (xml_info->parent != (XMLTreeInfo *) NULL)
2669     content=xml_info->parent->content;
2670   offset=0;
2671   *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
2672     start),source,length,extent,MagickFalse);
2673   if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2674     {
2675       *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2676       *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2677       if (*source == (char *) NULL)
2678         return(*source);
2679     }
2680   *length+=FormatLocaleString(*source+(*length),*extent,"<%s",xml_info->tag);
2681   for (i=0; xml_info->attributes[i]; i+=2)
2682   {
2683     attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
2684     if (attribute != xml_info->attributes[i+1])
2685       continue;
2686     if ((*length+strlen(xml_info->attributes[i])+MaxTextExtent) > *extent)
2687       {
2688         *extent=(*length)+strlen(xml_info->attributes[i])+MaxTextExtent;
2689         *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2690         if (*source == (char *) NULL)
2691           return((char *) NULL);
2692       }
2693     *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
2694       xml_info->attributes[i]);
2695     (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
2696       extent,MagickTrue);
2697     *length+=FormatLocaleString(*source+(*length),*extent,"\"");
2698   }
2699   i=0;
2700   while ((attributes[i] != (char **) NULL) &&
2701          (strcmp(attributes[i][0],xml_info->tag) != 0))
2702     i++;
2703   j=1;
2704   while ((attributes[i] != (char **) NULL) &&
2705          (attributes[i][j] != (char *) NULL))
2706   {
2707     if ((attributes[i][j+1] == (char *) NULL) ||
2708         (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
2709       {
2710         j+=3;
2711         continue;
2712       }
2713     if ((*length+strlen(attributes[i][j])+MaxTextExtent) > *extent)
2714       {
2715         *extent=(*length)+strlen(attributes[i][j])+MaxTextExtent;
2716         *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2717         if (*source == (char *) NULL)
2718           return((char *) NULL);
2719       }
2720     *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
2721       attributes[i][j]);
2722     (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
2723       MagickTrue);
2724     *length+=FormatLocaleString(*source+(*length),*extent,"\"");
2725     j+=3;
2726   }
2727   *length+=FormatLocaleString(*source+(*length),*extent,*xml_info->content ?
2728     ">" : "/>");
2729   if (xml_info->child != (XMLTreeInfo *) NULL)
2730     *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
2731   else
2732     *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
2733       MagickFalse);
2734   if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2735     {
2736       *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2737       *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2738       if (*source == (char *) NULL)
2739         return((char *) NULL);
2740     }
2741   if (*xml_info->content != '\0')
2742     *length+=FormatLocaleString(*source+(*length),*extent,"</%s>",
2743       xml_info->tag);
2744   while ((content[offset] != '\0') && (offset < xml_info->offset))
2745     offset++;
2746   if (xml_info->ordered != (XMLTreeInfo *) NULL)
2747     content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
2748       attributes);
2749   else
2750     content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
2751       MagickFalse);
2752   return(content);
2753 }
2754
2755 MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2756 {
2757   char
2758     *xml;
2759
2760   register char
2761     *p,
2762     *q;
2763
2764   register ssize_t
2765     i;
2766
2767   size_t
2768     extent,
2769     length;
2770
2771   ssize_t
2772     j,
2773     k;
2774
2775   XMLTreeInfo
2776     *ordered,
2777     *parent;
2778
2779   XMLTreeRoot
2780     *root;
2781
2782   assert(xml_info != (XMLTreeInfo *) NULL);
2783   assert((xml_info->signature == MagickSignature) ||
2784          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2785   if (xml_info->debug != MagickFalse)
2786     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2787   if (xml_info->tag == (char *) NULL)
2788     return((char *) NULL);
2789   xml=AcquireString((char *) NULL);
2790   length=0;
2791   extent=MaxTextExtent;
2792   root=(XMLTreeRoot *) xml_info;
2793   while (root->root.parent != (XMLTreeInfo *) NULL)
2794     root=(XMLTreeRoot *) root->root.parent;
2795   parent=(XMLTreeInfo *) NULL;
2796   if (xml_info != (XMLTreeInfo *) NULL)
2797     parent=xml_info->parent;
2798   if (parent == (XMLTreeInfo *) NULL)
2799     for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2800     {
2801       /*
2802         Pre-root processing instructions.
2803       */
2804       for (k=2; root->processing_instructions[i][k-1]; k++) ;
2805       p=root->processing_instructions[i][1];
2806       for (j=1; p != (char *) NULL; j++)
2807       {
2808         if (root->processing_instructions[i][k][j-1] == '>')
2809           {
2810             p=root->processing_instructions[i][j];
2811             continue;
2812           }
2813         q=root->processing_instructions[i][0];
2814         if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2815           {
2816             extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2817             xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2818             if (xml == (char *) NULL)
2819               return(xml);
2820           }
2821         length+=FormatLocaleString(xml+length,extent,"<?%s%s%s?>\n",q,
2822           *p != '\0' ? " " : "",p);
2823         p=root->processing_instructions[i][j];
2824       }
2825     }
2826   ordered=(XMLTreeInfo *) NULL;
2827   if (xml_info != (XMLTreeInfo *) NULL)
2828     ordered=xml_info->ordered;
2829   xml_info->parent=(XMLTreeInfo *) NULL;
2830   xml_info->ordered=(XMLTreeInfo *) NULL;
2831   xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
2832   xml_info->parent=parent;
2833   xml_info->ordered=ordered;
2834   if (parent == (XMLTreeInfo *) NULL)
2835     for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2836     {
2837       /*
2838         Post-root processing instructions.
2839       */
2840       for (k=2; root->processing_instructions[i][k-1]; k++) ;
2841       p=root->processing_instructions[i][1];
2842       for (j=1; p != (char *) NULL; j++)
2843       {
2844         if (root->processing_instructions[i][k][j-1] == '<')
2845           {
2846             p=root->processing_instructions[i][j];
2847             continue;
2848           }
2849         q=root->processing_instructions[i][0];
2850         if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2851           {
2852             extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2853             xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2854             if (xml == (char *) NULL)
2855               return(xml);
2856           }
2857         length+=FormatLocaleString(xml+length,extent,"\n<?%s%s%s?>",q,
2858           *p != '\0' ? " " : "",p);
2859         p=root->processing_instructions[i][j];
2860       }
2861     }
2862   return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));
2863 }