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