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