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