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