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