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