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