MagickCore  6.7.5
xml-tree.c
Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %                             X   X  M   M  L                                 %
00007 %                              X X   MM MM  L                                 %
00008 %                               X    M M M  L                                 %
00009 %                              X X   M   M  L                                 %
00010 %                             X   X  M   M  LLLLL                             %
00011 %                                                                             %
00012 %                         TTTTT  RRRR   EEEEE  EEEEE                          %
00013 %                           T    R   R  E      E                              %
00014 %                           T    RRRR   EEE    EEE                            %
00015 %                           T    R R    E      E                              %
00016 %                           T    R  R   EEEEE  EEEEE                          %
00017 %                                                                             %
00018 %                                                                             %
00019 %                              XML Tree Methods                               %
00020 %                                                                             %
00021 %                              Software Design                                %
00022 %                                John Cristy                                  %
00023 %                               December 2004                                 %
00024 %                                                                             %
00025 %                                                                             %
00026 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
00027 %  dedicated to making software imaging solutions freely available.           %
00028 %                                                                             %
00029 %  You may not use this file except in compliance with the License.  You may  %
00030 %  obtain a copy of the License at                                            %
00031 %                                                                             %
00032 %    http://www.imagemagick.org/script/license.php                            %
00033 %                                                                             %
00034 %  Unless required by applicable law or agreed to in writing, software        %
00035 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00036 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00037 %  See the License for the specific language governing permissions and        %
00038 %  limitations under the License.                                             %
00039 %                                                                             %
00040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00041 %
00042 %  This module implements the standard handy xml-tree methods for storing and
00043 %  retrieving nodes and attributes from an XML string.
00044 %
00045 */
00046 
00047 /*
00048   Include declarations.
00049 */
00050 #include "MagickCore/studio.h"
00051 #include "MagickCore/blob.h"
00052 #include "MagickCore/exception.h"
00053 #include "MagickCore/exception-private.h"
00054 #include "MagickCore/log.h"
00055 #include "MagickCore/memory_.h"
00056 #include "MagickCore/semaphore.h"
00057 #include "MagickCore/string_.h"
00058 #include "MagickCore/string-private.h"
00059 #include "MagickCore/token-private.h"
00060 #include "MagickCore/xml-tree.h"
00061 #include "MagickCore/xml-tree-private.h"
00062 #include "MagickCore/utility.h"
00063 #include "MagickCore/utility-private.h"
00064 
00065 /*
00066   Define declarations.
00067 */
00068 #define NumberPredefinedEntities  10
00069 #define XMLWhitespace "\t\r\n "
00070 
00071 /*
00072   Typedef declarations.
00073 */
00074 struct _XMLTreeInfo
00075 {
00076   char
00077     *tag,
00078     **attributes,
00079     *content;
00080 
00081   size_t
00082     offset;
00083 
00084   XMLTreeInfo
00085     *parent,
00086     *next,
00087     *sibling,
00088     *ordered,
00089     *child;
00090 
00091   MagickBooleanType
00092     debug;
00093 
00094   SemaphoreInfo
00095     *semaphore;
00096 
00097   size_t
00098     signature;
00099 };
00100 
00101 typedef struct _XMLTreeRoot
00102   XMLTreeRoot;
00103 
00104 struct _XMLTreeRoot
00105 {
00106   struct _XMLTreeInfo
00107     root;
00108 
00109   XMLTreeInfo
00110     *node;
00111 
00112   MagickBooleanType
00113     standalone;
00114 
00115   char
00116     ***processing_instructions,
00117     **entities,
00118     ***attributes;
00119 
00120   MagickBooleanType
00121     debug;
00122 
00123   SemaphoreInfo
00124     *semaphore;
00125 
00126   size_t
00127     signature;
00128 };
00129 
00130 /*
00131   Global declarations.
00132 */
00133 static char
00134   *sentinel[] = { (char *) NULL };
00135 
00136 /*
00137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00138 %                                                                             %
00139 %                                                                             %
00140 %                                                                             %
00141 %   A d d C h i l d T o X M L T r e e                                         %
00142 %                                                                             %
00143 %                                                                             %
00144 %                                                                             %
00145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00146 %
00147 %  AddChildToXMLTree() adds a child tag at an offset relative to the start of
00148 %  the parent tag's character content.  Return the child tag.
00149 %
00150 %  The format of the AddChildToXMLTree method is:
00151 %
00152 %      XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,const char *tag,
00153 %        const size_t offset)
00154 %
00155 %  A description of each parameter follows:
00156 %
00157 %    o xml_info: the xml info.
00158 %
00159 %    o tag: the tag.
00160 %
00161 %    o offset: the tag offset.
00162 %
00163 */
00164 MagickExport XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,
00165   const char *tag,const size_t offset)
00166 {
00167   XMLTreeInfo
00168     *child;
00169 
00170   if (xml_info == (XMLTreeInfo *) NULL)
00171     return((XMLTreeInfo *) NULL);
00172   child=(XMLTreeInfo *) AcquireMagickMemory(sizeof(*child));
00173   if (child == (XMLTreeInfo *) NULL)
00174     return((XMLTreeInfo *) NULL);
00175   (void) ResetMagickMemory(child,0,sizeof(*child));
00176   child->tag=ConstantString(tag);
00177   child->attributes=sentinel;
00178   child->content=ConstantString("");
00179   child->debug=IsEventLogging();
00180   child->signature=MagickSignature;
00181   return(InsertTagIntoXMLTree(xml_info,child,offset));
00182 }
00183 
00184 /*
00185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00186 %                                                                             %
00187 %                                                                             %
00188 %                                                                             %
00189 %   A d d P a t h T o X M L T r e e                                           %
00190 %                                                                             %
00191 %                                                                             %
00192 %                                                                             %
00193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00194 %
00195 %  AddPathToXMLTree() adds a child tag at an offset relative to the start of
00196 %  the parent tag's character content.  This method returns the child tag.
00197 %
00198 %  The format of the AddPathToXMLTree method is:
00199 %
00200 %      XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,const char *path,
00201 %        const size_t offset)
00202 %
00203 %  A description of each parameter follows:
00204 %
00205 %    o xml_info: the xml info.
00206 %
00207 %    o path: the path.
00208 %
00209 %    o offset: the tag offset.
00210 %
00211 */
00212 MagickPrivate XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,
00213   const char *path,const size_t offset)
00214 {
00215   char
00216     **components,
00217     subnode[MaxTextExtent],
00218     tag[MaxTextExtent];
00219 
00220   register ssize_t
00221     i;
00222 
00223   size_t
00224     number_components;
00225 
00226   ssize_t
00227     j;
00228 
00229   XMLTreeInfo
00230     *child,
00231     *node;
00232 
00233   assert(xml_info != (XMLTreeInfo *) NULL);
00234   assert((xml_info->signature == MagickSignature) ||
00235          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00236   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00237   node=xml_info;
00238   components=GetPathComponents(path,&number_components);
00239   if (components == (char **) NULL)
00240     return((XMLTreeInfo *) NULL);
00241   for (i=0; i < (ssize_t) number_components; i++)
00242   {
00243     GetPathComponent(components[i],SubimagePath,subnode);
00244     GetPathComponent(components[i],CanonicalPath,tag);
00245     child=GetXMLTreeChild(node,tag);
00246     if (child == (XMLTreeInfo *) NULL)
00247       child=AddChildToXMLTree(node,tag,offset);
00248     node=child;
00249     if (node == (XMLTreeInfo *) NULL)
00250       break;
00251     for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
00252     {
00253       node=GetXMLTreeOrdered(node);
00254       if (node == (XMLTreeInfo *) NULL)
00255         break;
00256     }
00257     if (node == (XMLTreeInfo *) NULL)
00258       break;
00259     components[i]=DestroyString(components[i]);
00260   }
00261   for ( ; i < (ssize_t) number_components; i++)
00262     components[i]=DestroyString(components[i]);
00263   components=(char **) RelinquishMagickMemory(components);
00264   return(node);
00265 }
00266 
00267 /*
00268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00269 %                                                                             %
00270 %                                                                             %
00271 %                                                                             %
00272 %   C a n o n i c a l X M L C o n t e n t                                     %
00273 %                                                                             %
00274 %                                                                             %
00275 %                                                                             %
00276 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00277 %
00278 %  CanonicalXMLContent() converts text to canonical XML content by converting
00279 %  to UTF-8, substituting predefined entities, wrapping as CDATA, or encoding
00280 %  as base-64 as required.
00281 %
00282 %  The format of the CanonicalXMLContent method is:
00283 %
00284 %
00285 %      char *CanonicalXMLContent(const char *content,
00286 %        const MagickBooleanType pedantic)
00287 %
00288 %  A description of each parameter follows:
00289 %
00290 %    o content: the content.
00291 %
00292 %    o pedantic: if true, replace newlines and tabs with their respective
00293 %      entities.
00294 %
00295 */
00296 MagickPrivate char *CanonicalXMLContent(const char *content,
00297   const MagickBooleanType pedantic)
00298 {
00299   char
00300     *base64,
00301     *canonical_content;
00302 
00303   register const unsigned char
00304     *p;
00305 
00306   register ssize_t
00307     i;
00308 
00309   size_t
00310     extent,
00311     length;
00312 
00313   unsigned char
00314     *utf8;
00315 
00316   utf8=ConvertLatin1ToUTF8((const unsigned char *) content);
00317   if (utf8 == (unsigned char *) NULL)
00318     return((char *) NULL);
00319   for (p=utf8; *p != '\0'; p++)
00320     if ((*p < 0x20) && (*p != 0x09) && (*p != 0x0a) && (*p != 0x0d))
00321       break;
00322   if (*p != '\0')
00323     {
00324       /*
00325         String is binary, base64-encode it.
00326       */
00327       base64=Base64Encode(utf8,strlen((char *) utf8),&length);
00328       utf8=(unsigned char *) RelinquishMagickMemory(utf8);
00329       if (base64 == (char *) NULL)
00330         return((char *) NULL);
00331       canonical_content=AcquireString("<base64>");
00332       (void) ConcatenateString(&canonical_content,base64);
00333       base64=DestroyString(base64);
00334       (void) ConcatenateString(&canonical_content,"</base64>");
00335       return(canonical_content);
00336     }
00337   /*
00338     Substitute predefined entities.
00339   */
00340   i=0;
00341   canonical_content=AcquireString((char *) NULL);
00342   extent=MaxTextExtent;
00343   for (p=utf8; *p != '\0'; p++)
00344   {
00345     if ((i+MaxTextExtent) > (ssize_t) extent)
00346       {
00347         extent+=MaxTextExtent;
00348         canonical_content=(char *) ResizeQuantumMemory(canonical_content,extent,
00349           sizeof(*canonical_content));
00350         if (canonical_content == (char *) NULL)
00351           return(canonical_content);
00352       }
00353     switch (*p)
00354     {
00355       case '&':
00356       {
00357         i+=FormatLocaleString(canonical_content+i,extent,"&amp;");
00358         break;
00359       }
00360       case '<':
00361       {
00362         i+=FormatLocaleString(canonical_content+i,extent,"&lt;");
00363         break;
00364       }
00365       case '>':
00366       {
00367         i+=FormatLocaleString(canonical_content+i,extent,"&gt;");
00368         break;
00369       }
00370       case '"':
00371       {
00372         i+=FormatLocaleString(canonical_content+i,extent,"&quot;");
00373         break;
00374       }
00375       case '\n':
00376       {
00377         if (pedantic == MagickFalse)
00378           {
00379             canonical_content[i++]=(char) (*p);
00380             break;
00381           }
00382         i+=FormatLocaleString(canonical_content+i,extent,"&#xA;");
00383         break;
00384       }
00385       case '\t':
00386       {
00387         if (pedantic == MagickFalse)
00388           {
00389             canonical_content[i++]=(char) (*p);
00390             break;
00391           }
00392         i+=FormatLocaleString(canonical_content+i,extent,"&#x9;");
00393         break;
00394       }
00395       case '\r':
00396       {
00397         i+=FormatLocaleString(canonical_content+i,extent,"&#xD;");
00398         break;
00399       }
00400       default:
00401       {
00402         canonical_content[i++]=(char) (*p);
00403         break;
00404       }
00405     }
00406   }
00407   canonical_content[i]='\0';
00408   utf8=(unsigned char *) RelinquishMagickMemory(utf8);
00409   return(canonical_content);
00410 }
00411 
00412 /*
00413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00414 %                                                                             %
00415 %                                                                             %
00416 %                                                                             %
00417 %   D e s t r o y X M L T r e e                                               %
00418 %                                                                             %
00419 %                                                                             %
00420 %                                                                             %
00421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00422 %
00423 %  DestroyXMLTree() destroys the xml-tree.
00424 %
00425 %  The format of the DestroyXMLTree method is:
00426 %
00427 %      XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
00428 %
00429 %  A description of each parameter follows:
00430 %
00431 %    o xml_info: the xml info.
00432 %
00433 */
00434 
00435 static char **DestroyXMLTreeAttributes(char **attributes)
00436 {
00437   register ssize_t
00438     i;
00439 
00440   /*
00441     Destroy a tag attribute list.
00442   */
00443   if ((attributes == (char **) NULL) || (attributes == sentinel))
00444     return((char **) NULL);
00445   for (i=0; attributes[i] != (char *) NULL; i+=2)
00446   {
00447     /*
00448       Destroy attribute tag and value.
00449     */
00450     if (attributes[i] != (char *) NULL)
00451       attributes[i]=DestroyString(attributes[i]);
00452     if (attributes[i+1] != (char *) NULL)
00453       attributes[i+1]=DestroyString(attributes[i+1]);
00454   }
00455   attributes=(char **) RelinquishMagickMemory(attributes);
00456   return((char **) NULL);
00457 }
00458 
00459 MagickExport XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
00460 {
00461   char
00462     **attributes;
00463 
00464   register ssize_t
00465     i;
00466 
00467   ssize_t
00468     j;
00469 
00470   XMLTreeRoot
00471     *root;
00472 
00473   assert(xml_info != (XMLTreeInfo *) NULL);
00474   assert((xml_info->signature == MagickSignature) ||
00475          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00476   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00477   if (xml_info->child != (XMLTreeInfo *) NULL)
00478     xml_info->child=DestroyXMLTree(xml_info->child);
00479   if (xml_info->ordered != (XMLTreeInfo *) NULL)
00480     xml_info->ordered=DestroyXMLTree(xml_info->ordered);
00481   if (xml_info->parent == (XMLTreeInfo *) NULL)
00482     {
00483       /*
00484         Free root tag allocations.
00485       */
00486       root=(XMLTreeRoot *) xml_info;
00487       for (i=NumberPredefinedEntities; root->entities[i]; i+=2)
00488         root->entities[i+1]=DestroyString(root->entities[i+1]);
00489       root->entities=(char **) RelinquishMagickMemory(root->entities);
00490       for (i=0; root->attributes[i] != (char **) NULL; i++)
00491       {
00492         attributes=root->attributes[i];
00493         if (attributes[0] != (char *) NULL)
00494           attributes[0]=DestroyString(attributes[0]);
00495         for (j=1; attributes[j] != (char *) NULL; j+=3)
00496         {
00497           if (attributes[j] != (char *) NULL)
00498             attributes[j]=DestroyString(attributes[j]);
00499           if (attributes[j+1] != (char *) NULL)
00500             attributes[j+1]=DestroyString(attributes[j+1]);
00501           if (attributes[j+2] != (char *) NULL)
00502             attributes[j+2]=DestroyString(attributes[j+2]);
00503         }
00504         attributes=(char **) RelinquishMagickMemory(attributes);
00505       }
00506       if (root->attributes[0] != (char **) NULL)
00507         root->attributes=(char ***) RelinquishMagickMemory(root->attributes);
00508       if (root->processing_instructions[0] != (char **) NULL)
00509         {
00510           for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
00511           {
00512             for (j=0; root->processing_instructions[i][j] != (char *) NULL; j++)
00513               root->processing_instructions[i][j]=DestroyString(
00514                 root->processing_instructions[i][j]);
00515             root->processing_instructions[i][j+1]=DestroyString(
00516               root->processing_instructions[i][j+1]);
00517             root->processing_instructions[i]=(char **) RelinquishMagickMemory(
00518               root->processing_instructions[i]);
00519           }
00520           root->processing_instructions=(char ***) RelinquishMagickMemory(
00521             root->processing_instructions);
00522         }
00523     }
00524   xml_info->attributes=DestroyXMLTreeAttributes(xml_info->attributes);
00525   xml_info->content=DestroyString(xml_info->content);
00526   xml_info->tag=DestroyString(xml_info->tag);
00527   xml_info=(XMLTreeInfo *) RelinquishMagickMemory(xml_info);
00528   return((XMLTreeInfo *) NULL);
00529 }
00530 
00531 /*
00532 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00533 %                                                                             %
00534 %                                                                             %
00535 %                                                                             %
00536 %   G e t N e x t X M L T r e e T a g                                         %
00537 %                                                                             %
00538 %                                                                             %
00539 %                                                                             %
00540 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00541 %
00542 %  GetNextXMLTreeTag() returns the next tag or NULL if not found.
00543 %
00544 %  The format of the GetNextXMLTreeTag method is:
00545 %
00546 %      XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
00547 %
00548 %  A description of each parameter follows:
00549 %
00550 %    o xml_info: the xml info.
00551 %
00552 */
00553 MagickPrivate XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
00554 {
00555   assert(xml_info != (XMLTreeInfo *) NULL);
00556   assert((xml_info->signature == MagickSignature) ||
00557          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00558   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00559   return(xml_info->next);
00560 }
00561 
00562 /*
00563 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00564 %                                                                             %
00565 %                                                                             %
00566 %                                                                             %
00567 %   G e t X M L T r e e A t t r i b u t e                                     %
00568 %                                                                             %
00569 %                                                                             %
00570 %                                                                             %
00571 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00572 %
00573 %  GetXMLTreeAttribute() returns the value of the attribute tag with the
00574 %  specified tag if found, otherwise NULL.
00575 %
00576 %  The format of the GetXMLTreeAttribute method is:
00577 %
00578 %      const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag)
00579 %
00580 %  A description of each parameter follows:
00581 %
00582 %    o xml_info: the xml info.
00583 %
00584 %    o tag: the attribute tag.
00585 %
00586 */
00587 MagickPrivate const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,
00588   const char *tag)
00589 {
00590   register ssize_t
00591     i;
00592 
00593   ssize_t
00594     j;
00595 
00596   XMLTreeRoot
00597     *root;
00598 
00599   assert(xml_info != (XMLTreeInfo *) NULL);
00600   assert((xml_info->signature == MagickSignature) ||
00601          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00602   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00603   if (xml_info->attributes == (char **) NULL)
00604     return((const char *) NULL);
00605   i=0;
00606   while ((xml_info->attributes[i] != (char *) NULL) &&
00607          (strcmp(xml_info->attributes[i],tag) != 0))
00608     i+=2;
00609   if (xml_info->attributes[i] != (char *) NULL)
00610     return(xml_info->attributes[i+1]);
00611   root=(XMLTreeRoot*) xml_info;
00612   while (root->root.parent != (XMLTreeInfo *) NULL)
00613     root=(XMLTreeRoot *) root->root.parent;
00614   i=0;
00615   while ((root->attributes[i] != (char **) NULL) &&
00616          (strcmp(root->attributes[i][0],xml_info->tag) != 0))
00617     i++;
00618   if (root->attributes[i] == (char **) NULL)
00619     return((const char *) NULL);
00620   j=1;
00621   while ((root->attributes[i][j] != (char *) NULL) &&
00622          (strcmp(root->attributes[i][j],tag) != 0))
00623     j+=3;
00624   if (root->attributes[i][j] == (char *) NULL)
00625     return((const char *) NULL);
00626   return(root->attributes[i][j+1]);
00627 }
00628 
00629 /*
00630 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00631 %                                                                             %
00632 %                                                                             %
00633 %                                                                             %
00634 %   G e t X M L T r e e A t t r i b u t e s                                   %
00635 %                                                                             %
00636 %                                                                             %
00637 %                                                                             %
00638 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00639 %
00640 %  GetXMLTreeAttributes() injects all attributes associated with the current
00641 %  tag in the specified splay-tree.
00642 %
00643 %  The format of the GetXMLTreeAttributes method is:
00644 %
00645 %      MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
00646 %        SplayTreeInfo *attributes)
00647 %
00648 %  A description of each parameter follows:
00649 %
00650 %    o xml_info: the xml info.
00651 %
00652 %    o attributes: the attribute splay-tree.
00653 %
00654 */
00655 MagickPrivate MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
00656   SplayTreeInfo *attributes)
00657 {
00658   register ssize_t
00659     i;
00660 
00661   assert(xml_info != (XMLTreeInfo *) NULL);
00662   assert((xml_info->signature == MagickSignature) ||
00663          (((const XMLTreeRoot *) xml_info)->signature == MagickSignature));
00664   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00665   assert(attributes != (SplayTreeInfo *) NULL);
00666   if (xml_info->attributes == (char **) NULL)
00667     return(MagickTrue);
00668   i=0;
00669   while (xml_info->attributes[i] != (char *) NULL)
00670   {
00671      (void) AddValueToSplayTree(attributes,
00672        ConstantString(xml_info->attributes[i]),
00673        ConstantString(xml_info->attributes[i+1]));
00674     i+=2;
00675   }
00676   return(MagickTrue);
00677 }
00678 
00679 /*
00680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00681 %                                                                             %
00682 %                                                                             %
00683 %                                                                             %
00684 %   G e t X M L T r e e C h i l d                                             %
00685 %                                                                             %
00686 %                                                                             %
00687 %                                                                             %
00688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00689 %
00690 %  GetXMLTreeChild() returns the first child tag with the specified tag if
00691 %  found, otherwise NULL.
00692 %
00693 %  The format of the GetXMLTreeChild method is:
00694 %
00695 %      XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
00696 %
00697 %  A description of each parameter follows:
00698 %
00699 %    o xml_info: the xml info.
00700 %
00701 */
00702 MagickExport XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
00703 {
00704   XMLTreeInfo
00705     *child;
00706 
00707   assert(xml_info != (XMLTreeInfo *) NULL);
00708   assert((xml_info->signature == MagickSignature) ||
00709          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00710   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00711   child=xml_info->child;
00712   if (tag != (const char *) NULL)
00713     while ((child != (XMLTreeInfo *) NULL) && (strcmp(child->tag,tag) != 0))
00714       child=child->sibling;
00715   return(child);
00716 }
00717 
00718 /*
00719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00720 %                                                                             %
00721 %                                                                             %
00722 %                                                                             %
00723 %   G e t X M L T r e e C o n t e n t                                         %
00724 %                                                                             %
00725 %                                                                             %
00726 %                                                                             %
00727 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00728 %
00729 %  GetXMLTreeContent() returns any content associated with specified
00730 %  xml-tree node.
00731 %
00732 %  The format of the GetXMLTreeContent method is:
00733 %
00734 %      const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
00735 %
00736 %  A description of each parameter follows:
00737 %
00738 %    o xml_info: the xml info.
00739 %
00740 */
00741 MagickExport const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
00742 {
00743   assert(xml_info != (XMLTreeInfo *) NULL);
00744   assert((xml_info->signature == MagickSignature) ||
00745          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00746   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00747   return(xml_info->content);
00748 }
00749 
00750 /*
00751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00752 %                                                                             %
00753 %                                                                             %
00754 %                                                                             %
00755 %   G e t X M L T r e e O r d e r e d                                         %
00756 %                                                                             %
00757 %                                                                             %
00758 %                                                                             %
00759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00760 %
00761 %  GetXMLTreeOrdered() returns the next ordered node if found, otherwise NULL.
00762 %
00763 %  The format of the GetXMLTreeOrdered method is:
00764 %
00765 %      XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
00766 %
00767 %  A description of each parameter follows:
00768 %
00769 %    o xml_info: the xml info.
00770 %
00771 */
00772 MagickPrivate XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
00773 {
00774   assert(xml_info != (XMLTreeInfo *) NULL);
00775   assert((xml_info->signature == MagickSignature) ||
00776          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00777   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00778   return(xml_info->ordered);
00779 }
00780 
00781 /*
00782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00783 %                                                                             %
00784 %                                                                             %
00785 %                                                                             %
00786 %   G e t X M L T r e e P a t h                                               %
00787 %                                                                             %
00788 %                                                                             %
00789 %                                                                             %
00790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00791 %
00792 %  GetXMLTreePath() traverses the XML-tree as defined by the specified path
00793 %  and returns the node if found, otherwise NULL.
00794 %
00795 %  The format of the GetXMLTreePath method is:
00796 %
00797 %      XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
00798 %
00799 %  A description of each parameter follows:
00800 %
00801 %    o xml_info: the xml info.
00802 %
00803 %    o path: the path (e.g. property/elapsed-time).
00804 %
00805 */
00806 MagickPrivate XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
00807 {
00808   char
00809     **components,
00810     subnode[MaxTextExtent],
00811     tag[MaxTextExtent];
00812 
00813   register ssize_t
00814     i;
00815 
00816   size_t
00817     number_components;
00818 
00819   ssize_t
00820     j;
00821 
00822   XMLTreeInfo
00823     *node;
00824 
00825   assert(xml_info != (XMLTreeInfo *) NULL);
00826   assert((xml_info->signature == MagickSignature) ||
00827          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00828   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00829   node=xml_info;
00830   components=GetPathComponents(path,&number_components);
00831   if (components == (char **) NULL)
00832     return((XMLTreeInfo *) NULL);
00833   for (i=0; i < (ssize_t) number_components; i++)
00834   {
00835     GetPathComponent(components[i],SubimagePath,subnode);
00836     GetPathComponent(components[i],CanonicalPath,tag);
00837     node=GetXMLTreeChild(node,tag);
00838     if (node == (XMLTreeInfo *) NULL)
00839       break;
00840     for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
00841     {
00842       node=GetXMLTreeOrdered(node);
00843       if (node == (XMLTreeInfo *) NULL)
00844         break;
00845     }
00846     if (node == (XMLTreeInfo *) NULL)
00847       break;
00848     components[i]=DestroyString(components[i]);
00849   }
00850   for ( ; i < (ssize_t) number_components; i++)
00851     components[i]=DestroyString(components[i]);
00852   components=(char **) RelinquishMagickMemory(components);
00853   return(node);
00854 }
00855 
00856 /*
00857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00858 %                                                                             %
00859 %                                                                             %
00860 %                                                                             %
00861 %   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           %
00862 %                                                                             %
00863 %                                                                             %
00864 %                                                                             %
00865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00866 %
00867 %  GetXMLTreeProcessingInstructions() returns a null terminated array of
00868 %  processing instructions for the given target.
00869 %
00870 %  The format of the GetXMLTreeProcessingInstructions method is:
00871 %
00872 %      const char **GetXMLTreeProcessingInstructions(XMLTreeInfo *xml_info,
00873 %        const char *target)
00874 %
00875 %  A description of each parameter follows:
00876 %
00877 %    o xml_info: the xml info.
00878 %
00879 */
00880 MagickPrivate const char **GetXMLTreeProcessingInstructions(
00881   XMLTreeInfo *xml_info,const char *target)
00882 {
00883   register ssize_t
00884     i;
00885 
00886   XMLTreeRoot
00887     *root;
00888 
00889   assert(xml_info != (XMLTreeInfo *) NULL);
00890   assert((xml_info->signature == MagickSignature) ||
00891          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00892   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00893   root=(XMLTreeRoot *) xml_info;
00894   while (root->root.parent != (XMLTreeInfo *) NULL)
00895     root=(XMLTreeRoot *) root->root.parent;
00896   i=0;
00897   while ((root->processing_instructions[i] != (char **) NULL) &&
00898          (strcmp(root->processing_instructions[i][0],target) != 0))
00899     i++;
00900   if (root->processing_instructions[i] == (char **) NULL)
00901     return((const char **) sentinel);
00902   return((const char **) (root->processing_instructions[i]+1));
00903 }
00904 
00905 /*
00906 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00907 %                                                                             %
00908 %                                                                             %
00909 %                                                                             %
00910 %   G e t X M L T r e e S i b l i n g                                         %
00911 %                                                                             %
00912 %                                                                             %
00913 %                                                                             %
00914 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00915 %
00916 %  GetXMLTreeSibling() returns the node sibling if found, otherwise NULL.
00917 %
00918 %  The format of the GetXMLTreeSibling method is:
00919 %
00920 %      XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
00921 %
00922 %  A description of each parameter follows:
00923 %
00924 %    o xml_info: the xml info.
00925 %
00926 */
00927 MagickExport XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
00928 {
00929   assert(xml_info != (XMLTreeInfo *) NULL);
00930   assert((xml_info->signature == MagickSignature) ||
00931          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00932   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00933   return(xml_info->sibling);
00934 }
00935 
00936 /*
00937 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00938 %                                                                             %
00939 %                                                                             %
00940 %                                                                             %
00941 %   G e t X M L T r e e T a g                                                 %
00942 %                                                                             %
00943 %                                                                             %
00944 %                                                                             %
00945 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00946 %
00947 %  GetXMLTreeTag() returns the tag associated with specified xml-tree node.
00948 %
00949 %  The format of the GetXMLTreeTag method is:
00950 %
00951 %      const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
00952 %
00953 %  A description of each parameter follows:
00954 %
00955 %    o xml_info: the xml info.
00956 %
00957 */
00958 MagickExport const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
00959 {
00960   assert(xml_info != (XMLTreeInfo *) NULL);
00961   assert((xml_info->signature == MagickSignature) ||
00962          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
00963   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
00964   return(xml_info->tag);
00965 }
00966 
00967 /*
00968 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00969 %                                                                             %
00970 %                                                                             %
00971 %                                                                             %
00972 %   I n s e r t I n t o T a g X M L T r e e                                   %
00973 %                                                                             %
00974 %                                                                             %
00975 %                                                                             %
00976 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00977 %
00978 %  InsertTagIntoXMLTree() inserts a tag at an offset relative to the start of
00979 %  the parent tag's character content.  This method returns the child tag.
00980 %
00981 %  The format of the InsertTagIntoXMLTree method is:
00982 %
00983 %      XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
00984 %        XMLTreeInfo *child,const size_t offset)
00985 %
00986 %  A description of each parameter follows:
00987 %
00988 %    o xml_info: the xml info.
00989 %
00990 %    o child: the child tag.
00991 %
00992 %    o offset: the tag offset.
00993 %
00994 */
00995 MagickPrivate XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
00996   XMLTreeInfo *child,const size_t offset)
00997 {
00998   XMLTreeInfo
00999     *head,
01000     *node,
01001     *previous;
01002 
01003   child->ordered=(XMLTreeInfo *) NULL;
01004   child->sibling=(XMLTreeInfo *) NULL;
01005   child->next=(XMLTreeInfo *) NULL;
01006   child->offset=offset;
01007   child->parent=xml_info;
01008   if (xml_info->child == (XMLTreeInfo *) NULL)
01009     {
01010       xml_info->child=child;
01011       return(child);
01012     }
01013   head=xml_info->child;
01014   if (head->offset > offset)
01015     {
01016       child->ordered=head;
01017       xml_info->child=child;
01018     }
01019   else
01020     {
01021       node=head;
01022       while ((node->ordered != (XMLTreeInfo *) NULL) &&
01023              (node->ordered->offset <= offset))
01024         node=node->ordered;
01025       child->ordered=node->ordered;
01026       node->ordered=child;
01027     }
01028   previous=(XMLTreeInfo *) NULL;
01029   node=head;
01030   while ((node != (XMLTreeInfo *) NULL) && (strcmp(node->tag,child->tag) != 0))
01031   {
01032     previous=node;
01033     node=node->sibling;
01034   }
01035   if ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
01036     {
01037       while ((node->next != (XMLTreeInfo *) NULL) &&
01038              (node->next->offset <= offset))
01039         node=node->next;
01040       child->next=node->next;
01041       node->next=child;
01042     }
01043   else
01044     {
01045       if ((previous != (XMLTreeInfo *) NULL) && (node != (XMLTreeInfo *) NULL))
01046         previous->sibling=node->sibling;
01047       child->next=node;
01048       previous=(XMLTreeInfo *) NULL;
01049       node=head;
01050       while ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
01051       {
01052         previous=node;
01053         node=node->sibling;
01054       }
01055       child->sibling=node;
01056       if (previous != (XMLTreeInfo *) NULL)
01057         previous->sibling=child;
01058     }
01059   return(child);
01060 }
01061 
01062 /*
01063 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01064 %                                                                             %
01065 %                                                                             %
01066 %                                                                             %
01067 %   N e w X M L T r e e                                                       %
01068 %                                                                             %
01069 %                                                                             %
01070 %                                                                             %
01071 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01072 %
01073 %  NewXMLTree() returns a XMLTreeInfo xml-tree as defined by the specified
01074 %  XML string.
01075 %
01076 %  The format of the NewXMLTree method is:
01077 %
01078 %      XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
01079 %
01080 %  A description of each parameter follows:
01081 %
01082 %    o xml:  The XML string.
01083 %
01084 %    o exception: return any errors or warnings in this structure.
01085 %
01086 */
01087 
01088 static char *ConvertUTF16ToUTF8(const char *content,size_t *length)
01089 {
01090   char
01091     *utf8;
01092 
01093   int
01094     bits,
01095     byte,
01096     c,
01097     encoding;
01098 
01099   register ssize_t
01100     i;
01101 
01102   size_t
01103     extent;
01104 
01105   ssize_t
01106     j;
01107 
01108   utf8=(char *) AcquireQuantumMemory(*length,sizeof(*utf8));
01109   if (utf8 == (char *) NULL)
01110     return((char *) NULL);
01111   encoding=(*content == '\xFE') ? 1 : (*content == '\xFF') ? 0 : -1;
01112   if (encoding == -1)
01113     {
01114       /*
01115         Already UTF-8.
01116       */
01117       (void) CopyMagickMemory(utf8,content,*length*sizeof(*utf8));
01118       return(utf8);
01119     }
01120   j=0;
01121   extent=(*length);
01122   for (i=2; i < (ssize_t) (*length-1); i+=2)
01123   {
01124     c=(encoding != 0) ? ((content[i] & 0xff) << 8) | (content[i+1] & 0xff) :
01125       ((content[i+1] & 0xff) << 8) | (content[i] & 0xff);
01126     if ((c >= 0xd800) && (c <= 0xdfff) && ((i+=2) < (ssize_t) (*length-1)))
01127       {
01128         byte=(encoding != 0) ? ((content[i] & 0xff) << 8) |
01129           (content[i+1] & 0xff) : ((content[i+1] & 0xff) << 8) |
01130           (content[i] & 0xff);
01131         c=(((c & 0x3ff) << 10) | (byte & 0x3ff))+0x10000;
01132       }
01133     if ((size_t) (j+MaxTextExtent) > extent)
01134       {
01135         extent=(size_t) j+MaxTextExtent;
01136         utf8=(char *) ResizeQuantumMemory(utf8,extent,sizeof(*utf8));
01137         if (utf8 == (char *) NULL)
01138           return(utf8);
01139       }
01140     if (c < 0x80)
01141       {
01142         utf8[j]=c;
01143         j++;
01144         continue;
01145       }
01146     /*
01147       Multi-byte UTF-8 sequence.
01148     */
01149     byte=c;
01150     for (bits=0; byte != 0; byte/=2)
01151       bits++;
01152     bits=(bits-2)/5;
01153     utf8[j++]=(0xFF << (7-bits)) | (c >> (6*bits));
01154     while (bits != 0)
01155     {
01156       bits--;
01157       utf8[j]=0x80 | ((c >> (6*bits)) & 0x3f);
01158       j++;
01159     }
01160   }
01161   *length=(size_t) j;
01162   return((char *) ResizeQuantumMemory(utf8,*length,sizeof(*utf8)));
01163 }
01164 
01165 static char *ParseEntities(char *xml,char **entities,int state)
01166 {
01167   char
01168     *entity;
01169 
01170   int
01171     byte,
01172     c;
01173 
01174   register char
01175     *p,
01176     *q;
01177 
01178   register ssize_t
01179     i;
01180 
01181   size_t
01182     extent,
01183     length;
01184 
01185   ssize_t
01186     offset;
01187 
01188   /*
01189     Normalize line endings.
01190   */
01191   p=xml;
01192   q=xml;
01193   for ( ; *xml != '\0'; xml++)
01194     while (*xml == '\r')
01195     {
01196       *(xml++)='\n';
01197       if (*xml == '\n')
01198         (void) CopyMagickMemory(xml,xml+1,strlen(xml));
01199     }
01200   for (xml=p; ; )
01201   {
01202     while ((*xml != '\0') && (*xml != '&') && ((*xml != '%') ||
01203            (state != '%')) && (isspace((int) ((unsigned char) *xml) == 0)))
01204       xml++;
01205     if (*xml == '\0')
01206       break;
01207     /*
01208       States include:
01209         '&' for general entity decoding
01210         '%' for parameter entity decoding
01211         'c' for CDATA sections
01212         ' ' for attributes normalization
01213         '*' for non-CDATA attributes normalization
01214     */
01215     if ((state != 'c') && (strncmp(xml,"&#",2) == 0))
01216       {
01217         /*
01218           Character reference.
01219         */
01220         if (xml[2] != 'x')
01221           c=strtol(xml+2,&entity,10);  /* base 10 */
01222         else
01223           c=strtol(xml+3,&entity,16);  /* base 16 */
01224         if ((c == 0) || (*entity != ';'))
01225           {
01226             /*
01227               Not a character reference.
01228             */
01229             xml++;
01230             continue;
01231           }
01232         if (c < 0x80)
01233           *(xml++)=c;
01234         else
01235           {
01236             /*
01237               Multi-byte UTF-8 sequence.
01238             */
01239             byte=c;
01240             for (i=0; byte != 0; byte/=2)
01241               i++;
01242             i=(i-2)/5;
01243             *xml=(char) ((0xFF << (7-i)) | (c >> (6*i)));
01244             xml++;
01245             while (i != 0)
01246             {
01247               i--;
01248               *xml=(char) (0x80 | ((c >> (6*i)) & 0x3F));
01249               xml++;
01250             }
01251           }
01252         (void) CopyMagickMemory(xml,strchr(xml,';')+1,strlen(strchr(xml,';')));
01253       }
01254     else
01255       if (((*xml == '&') && ((state == '&') || (state == ' ') ||
01256           (state == '*'))) || ((state == '%') && (*xml == '%')))
01257         {
01258           /*
01259             Find entity in the list.
01260           */
01261           i=0;
01262           while ((entities[i] != (char *) NULL) &&
01263                  (strncmp(xml+1,entities[i],strlen(entities[i])) != 0))
01264             i+=2;
01265           if (entities[i++] == (char *) NULL)
01266             xml++;
01267           else
01268             {
01269               /*
01270                 Found a match.
01271               */
01272               length=strlen(entities[i]);
01273               entity=strchr(xml,';');
01274               if ((length-1L) >= (size_t) (entity-xml))
01275                 {
01276                   offset=(ssize_t) (xml-p);
01277                   extent=(size_t) (offset+length+strlen(entity));
01278                   if (p != q)
01279                     p=(char *) ResizeQuantumMemory(p,extent,sizeof(*p));
01280                   else
01281                     {
01282                       char
01283                         *xml;
01284 
01285                       xml=(char *) AcquireQuantumMemory(extent,sizeof(*xml));
01286                       if (xml != (char *) NULL)
01287                         {
01288                           (void) CopyMagickString(xml,p,extent*sizeof(*xml));
01289                           p=xml;
01290                         }
01291                     }
01292                   if (p == (char *) NULL)
01293                     ThrowFatalException(ResourceLimitFatalError,
01294                       "MemoryAllocationFailed");
01295                   xml=p+offset;
01296                   entity=strchr(xml,';');
01297                 }
01298               (void) CopyMagickMemory(xml+length,entity+1,strlen(entity));
01299               (void) strncpy(xml,entities[i],length);
01300             }
01301         }
01302       else
01303         if (((state == ' ') || (state == '*')) &&
01304             (isspace((int) ((unsigned char) *xml) != 0)))
01305           *(xml++)=' ';
01306         else
01307           xml++;
01308   }
01309   if (state == '*')
01310     {
01311       /*
01312         Normalize spaces for non-CDATA attributes.
01313       */
01314       for (xml=p; *xml != '\0'; xml++)
01315       {
01316         i=(ssize_t) strspn(xml," ");
01317         if (i != 0)
01318           (void) CopyMagickMemory(xml,xml+i,strlen(xml+i)+1);
01319         while ((*xml != '\0') && (*xml != ' '))
01320           xml++;
01321       }
01322       xml--;
01323       if ((xml >= p) && (*xml == ' '))
01324         *xml='\0';
01325     }
01326   return(p == q ? ConstantString(p) : p);
01327 }
01328 
01329 static void ParseCharacterContent(XMLTreeRoot *root,char *xml,
01330   const size_t length,const char state)
01331 {
01332   XMLTreeInfo
01333     *xml_info;
01334 
01335   xml_info=root->node;
01336   if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) ||
01337       (length == 0))
01338     return;
01339   xml[length]='\0';
01340   xml=ParseEntities(xml,root->entities,state);
01341   if (*xml_info->content != '\0')
01342     {
01343       (void) ConcatenateString(&xml_info->content,xml);
01344       xml=DestroyString(xml);
01345     }
01346   else
01347     {
01348       if (xml_info->content != (char *) NULL)
01349         xml_info->content=DestroyString(xml_info->content);
01350       xml_info->content=xml;
01351     }
01352 }
01353 
01354 static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag,
01355   char *magick_unused(xml),ExceptionInfo *exception)
01356 {
01357   if ((root->node == (XMLTreeInfo *) NULL) ||
01358       (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0))
01359     {
01360       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
01361         "ParseError","unexpected closing tag </%s>",tag);
01362       return(&root->root);
01363     }
01364   root->node=root->node->parent;
01365   return((XMLTreeInfo *) NULL);
01366 }
01367 
01368 static MagickBooleanType ValidateEntities(char *tag,char *xml,char **entities)
01369 {
01370   register ssize_t
01371     i;
01372 
01373   /*
01374     Check for circular entity references.
01375   */
01376   for ( ; ; xml++)
01377   {
01378     while ((*xml != '\0') && (*xml != '&'))
01379       xml++;
01380     if (*xml == '\0')
01381       return(MagickTrue);
01382     if (strncmp(xml+1,tag,strlen(tag)) == 0)
01383       return(MagickFalse);
01384     i=0;
01385     while ((entities[i] != (char *) NULL) &&
01386            (strncmp(entities[i],xml+1,strlen(entities[i]) == 0)))
01387       i+=2;
01388     if ((entities[i] != (char *) NULL) &&
01389         (ValidateEntities(tag,entities[i+1],entities) == 0))
01390       return(MagickFalse);
01391   }
01392   return(MagickTrue);
01393 }
01394 
01395 static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml,
01396   size_t length)
01397 {
01398   char
01399     *target;
01400 
01401   register ssize_t
01402     i;
01403 
01404   ssize_t
01405     j;
01406 
01407   target=xml;
01408   xml[length]='\0';
01409   xml+=strcspn(xml,XMLWhitespace);
01410   if (*xml != '\0')
01411     {
01412       *xml='\0';
01413       xml+=strspn(xml+1,XMLWhitespace)+1;
01414     }
01415   if (strcmp(target,"xml") == 0)
01416     {
01417       xml=strstr(xml,"standalone");
01418       if ((xml != (char *) NULL) &&
01419           (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0))
01420         root->standalone=MagickTrue;
01421       return;
01422     }
01423   if (root->processing_instructions[0] == (char **) NULL)
01424     {
01425       root->processing_instructions=(char ***) AcquireMagickMemory(sizeof(
01426         *root->processing_instructions));
01427       if (root->processing_instructions ==(char ***) NULL)
01428         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
01429       *root->processing_instructions=(char **) NULL;
01430     }
01431   i=0;
01432   while ((root->processing_instructions[i] != (char **) NULL) &&
01433          (strcmp(target,root->processing_instructions[i][0]) != 0))
01434     i++;
01435   if (root->processing_instructions[i] == (char **) NULL)
01436     {
01437       root->processing_instructions=(char ***) ResizeQuantumMemory(
01438         root->processing_instructions,(size_t) (i+2),
01439         sizeof(*root->processing_instructions));
01440       if (root->processing_instructions == (char ***) NULL)
01441         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
01442       root->processing_instructions[i]=(char **) AcquireQuantumMemory(3,
01443         sizeof(**root->processing_instructions));
01444       if (root->processing_instructions[i] == (char **) NULL)
01445         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
01446       root->processing_instructions[i+1]=(char **) NULL;
01447       root->processing_instructions[i][0]=ConstantString(target);
01448       root->processing_instructions[i][1]=(char *)
01449         root->processing_instructions[i+1];
01450       root->processing_instructions[i+1]=(char **) NULL;
01451       root->processing_instructions[i][2]=ConstantString("");
01452     }
01453   j=1;
01454   while (root->processing_instructions[i][j] != (char *) NULL)
01455     j++;
01456   root->processing_instructions[i]=(char **) ResizeQuantumMemory(
01457     root->processing_instructions[i],(size_t) (j+3),
01458     sizeof(**root->processing_instructions));
01459   if (root->processing_instructions[i] == (char **) NULL)
01460     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
01461   root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory(
01462     root->processing_instructions[i][j+1],(size_t) (j+1),
01463     sizeof(**root->processing_instructions));
01464   if (root->processing_instructions[i][j+2] == (char *) NULL)
01465     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
01466   (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1,
01467     root->root.tag != (char *) NULL ? ">" : "<",2);
01468   root->processing_instructions[i][j]=ConstantString(xml);
01469   root->processing_instructions[i][j+1]=(char *) NULL;
01470 }
01471 
01472 static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml,
01473   size_t length,ExceptionInfo *exception)
01474 {
01475   char
01476     *c,
01477     **entities,
01478     *n,
01479     **predefined_entitites,
01480     q,
01481     *t,
01482     *v;
01483 
01484   register ssize_t
01485     i;
01486 
01487   ssize_t
01488     j;
01489 
01490   n=(char *) NULL;
01491   predefined_entitites=(char **) AcquireMagickMemory(sizeof(sentinel));
01492   if (predefined_entitites == (char **) NULL)
01493     ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed");
01494   (void) CopyMagickMemory(predefined_entitites,sentinel,sizeof(sentinel));
01495   for (xml[length]='\0'; xml != (char *) NULL; )
01496   {
01497     while ((*xml != '\0') && (*xml != '<') && (*xml != '%'))
01498       xml++;
01499     if (*xml == '\0')
01500       break;
01501     if (strncmp(xml,"<!ENTITY",8) == 0)
01502       {
01503         /*
01504           Parse entity definitions.
01505         */
01506         xml+=strspn(xml+8,XMLWhitespace)+8;
01507         c=xml;
01508         n=xml+strspn(xml,XMLWhitespace "%");
01509         xml=n+strcspn(n,XMLWhitespace);
01510         *xml=';';
01511         v=xml+strspn(xml+1,XMLWhitespace)+1;
01512         q=(*v);
01513         v++;
01514         if ((q != '"') && (q != '\''))
01515           {
01516             /*
01517               Skip externals.
01518             */
01519             xml=strchr(xml,'>');
01520             continue;
01521           }
01522         entities=(*c == '%') ? predefined_entitites : root->entities;
01523         for (i=0; entities[i] != (char *) NULL; i++) ;
01524         entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3),
01525           sizeof(*entities));
01526         if (entities == (char **) NULL)
01527           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
01528         if (*c == '%')
01529           predefined_entitites=entities;
01530         else
01531           root->entities=entities;
01532         xml++;
01533         *xml='\0';
01534         xml=strchr(v,q);
01535         if (xml != (char *) NULL)
01536           {
01537             *xml='\0';
01538             xml++;
01539           }
01540         entities[i+1]=ParseEntities(v,predefined_entitites,'%');
01541         entities[i+2]=(char *) NULL;
01542         if (ValidateEntities(n,entities[i+1],entities) != MagickFalse)
01543           entities[i]=n;
01544         else
01545           {
01546             if (entities[i+1] != v)
01547               entities[i+1]=DestroyString(entities[i+1]);
01548             (void) ThrowMagickException(exception,GetMagickModule(),
01549               OptionWarning,"ParseError","circular entity declaration &%s",n);
01550             predefined_entitites=(char **) RelinquishMagickMemory(
01551               predefined_entitites);
01552             return(MagickFalse);
01553           }
01554         }
01555       else
01556        if (strncmp(xml,"<!ATTLIST",9) == 0)
01557          {
01558             /*
01559               Parse default attributes.
01560             */
01561             t=xml+strspn(xml+9,XMLWhitespace)+9;
01562             if (*t == '\0')
01563               {
01564                 (void) ThrowMagickException(exception,GetMagickModule(),
01565                   OptionWarning,"ParseError","unclosed <!ATTLIST");
01566                 predefined_entitites=(char **) RelinquishMagickMemory(
01567                   predefined_entitites);
01568                 return(MagickFalse);
01569               }
01570             xml=t+strcspn(t,XMLWhitespace ">");
01571             if (*xml == '>')
01572               continue;
01573             *xml='\0';
01574             i=0;
01575             while ((root->attributes[i] != (char **) NULL) &&
01576                    (strcmp(n,root->attributes[i][0]) != 0))
01577               i++;
01578             while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') &&
01579                    (*n != '>'))
01580             {
01581               xml=n+strcspn(n,XMLWhitespace);
01582               if (*xml != '\0')
01583                 *xml='\0';
01584               else
01585                 {
01586                   (void) ThrowMagickException(exception,GetMagickModule(),
01587                     OptionWarning,"ParseError","malformed <!ATTLIST");
01588                   predefined_entitites=(char **) RelinquishMagickMemory(
01589                     predefined_entitites);
01590                   return(MagickFalse);
01591                 }
01592               xml+=strspn(xml+1,XMLWhitespace)+1;
01593               c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
01594               if (strncmp(xml,"NOTATION",8) == 0)
01595                 xml+=strspn(xml+8,XMLWhitespace)+8;
01596               xml=(*xml == '(') ? strchr(xml,')') : xml+
01597                 strcspn(xml,XMLWhitespace);
01598               if (xml == (char *) NULL)
01599                 {
01600                   (void) ThrowMagickException(exception,GetMagickModule(),
01601                     OptionWarning,"ParseError","malformed <!ATTLIST");
01602                   predefined_entitites=(char **) RelinquishMagickMemory(
01603                     predefined_entitites);
01604                   return(MagickFalse);
01605                 }
01606               xml+=strspn(xml,XMLWhitespace ")");
01607               if (strncmp(xml,"#FIXED",6) == 0)
01608                 xml+=strspn(xml+6,XMLWhitespace)+6;
01609               if (*xml == '#')
01610                 {
01611                   xml+=strcspn(xml,XMLWhitespace ">")-1;
01612                   if (*c == ' ')
01613                     continue;
01614                   v=(char *) NULL;
01615                 }
01616               else
01617                 if (((*xml == '"') || (*xml == '\''))  &&
01618                     ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
01619                   *xml='\0';
01620                 else
01621                   {
01622                     (void) ThrowMagickException(exception,GetMagickModule(),
01623                       OptionWarning,"ParseError","malformed <!ATTLIST");
01624                     predefined_entitites=(char **) RelinquishMagickMemory(
01625                       predefined_entitites);
01626                     return(MagickFalse);
01627                   }
01628               if (root->attributes[i] == (char **) NULL)
01629                 {
01630                   /*
01631                     New attribute tag.
01632                   */
01633                   if (i == 0)
01634                     root->attributes=(char ***) AcquireQuantumMemory(2,
01635                       sizeof(*root->attributes));
01636                   else
01637                     root->attributes=(char ***) ResizeQuantumMemory(
01638                       root->attributes,(size_t) (i+2),
01639                       sizeof(*root->attributes));
01640                   if (root->attributes == (char ***) NULL)
01641                     ThrowFatalException(ResourceLimitFatalError,
01642                       "MemoryAllocationFailed");
01643                   root->attributes[i]=(char **) AcquireQuantumMemory(2,
01644                     sizeof(*root->attributes));
01645                   if (root->attributes[i] == (char **) NULL)
01646                     ThrowFatalException(ResourceLimitFatalError,
01647                       "MemoryAllocationFailed");
01648                   root->attributes[i][0]=ConstantString(t);
01649                   root->attributes[i][1]=(char *) NULL;
01650                   root->attributes[i+1]=(char **) NULL;
01651                 }
01652               for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
01653               root->attributes[i]=(char **) ResizeQuantumMemory(
01654                 root->attributes[i],(size_t) (j+4),sizeof(*root->attributes));
01655               if (root->attributes[i] == (char **) NULL)
01656                 ThrowFatalException(ResourceLimitFatalError,
01657                   "MemoryAllocationFailed");
01658               root->attributes[i][j+3]=(char *) NULL;
01659               root->attributes[i][j+2]=ConstantString(c);
01660               root->attributes[i][j+1]=(char *) NULL;
01661               if (v != (char *) NULL)
01662                 root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
01663               root->attributes[i][j]=ConstantString(n);
01664             }
01665         }
01666       else
01667         if (strncmp(xml, "<!--", 4) == 0)
01668           xml=strstr(xml+4,"-->");
01669         else
01670           if (strncmp(xml,"<?", 2) == 0)
01671             {
01672               c=xml+2;
01673               xml=strstr(c,"?>");
01674               if (xml != (char *) NULL)
01675                 {
01676                   ParseProcessingInstructions(root,c,(size_t) (xml-c));
01677                   xml++;
01678                 }
01679             }
01680            else
01681              if (*xml == '<')
01682                xml=strchr(xml,'>');
01683              else
01684                if ((*(xml++) == '%') && (root->standalone == MagickFalse))
01685                  break;
01686     }
01687   predefined_entitites=(char **) RelinquishMagickMemory(predefined_entitites);
01688   return(MagickTrue);
01689 }
01690 
01691 static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
01692 {
01693   XMLTreeInfo
01694     *xml_info;
01695 
01696   xml_info=root->node;
01697   if (xml_info->tag == (char *) NULL)
01698     xml_info->tag=ConstantString(tag);
01699   else
01700     xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
01701   xml_info->attributes=attributes;
01702   root->node=xml_info;
01703 }
01704 
01705 MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
01706 {
01707   char
01708     **attribute,
01709     **attributes,
01710     *tag,
01711     *utf8;
01712 
01713   int
01714     c,
01715     terminal;
01716 
01717   MagickBooleanType
01718     status;
01719 
01720   register char
01721     *p;
01722 
01723   register ssize_t
01724     i;
01725 
01726   size_t
01727     length;
01728 
01729   ssize_t
01730     j,
01731     l;
01732 
01733   XMLTreeRoot
01734     *root;
01735 
01736   /*
01737     Convert xml-string to UTF8.
01738   */
01739   if ((xml == (const char *) NULL) || (strlen(xml) == 0))
01740     {
01741       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
01742         "ParseError","root tag missing");
01743       return((XMLTreeInfo *) NULL);
01744     }
01745   root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
01746   length=strlen(xml);
01747   utf8=ConvertUTF16ToUTF8(xml,&length);
01748   if (utf8 == (char *) NULL)
01749     {
01750       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
01751         "ParseError","UTF16 to UTF8 failed");
01752       return((XMLTreeInfo *) NULL);
01753     }
01754   terminal=utf8[length-1];
01755   utf8[length-1]='\0';
01756   p=utf8;
01757   while ((*p != '\0') && (*p != '<'))
01758     p++;
01759   if (*p == '\0')
01760     {
01761       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
01762         "ParseError","root tag missing");
01763       utf8=DestroyString(utf8);
01764       return((XMLTreeInfo *) NULL);
01765     }
01766   attribute=(char **) NULL;
01767   for (p++; ; p++)
01768   {
01769     attributes=(char **) sentinel;
01770     tag=p;
01771     c=(*p);
01772     if ((isalpha((int) ((unsigned char) *p)) !=0) || (*p == '_') ||
01773         (*p == ':') || (c < '\0'))
01774       {
01775         /*
01776           Tag.
01777         */
01778         if (root->node == (XMLTreeInfo *) NULL)
01779           {
01780             (void) ThrowMagickException(exception,GetMagickModule(),
01781               OptionWarning,"ParseError","root tag missing");
01782             utf8=DestroyString(utf8);
01783             return(&root->root);
01784           }
01785         p+=strcspn(p,XMLWhitespace "/>");
01786         while (isspace((int) ((unsigned char) *p)) != 0)
01787           *p++='\0';
01788         if ((*p != '\0') && (*p != '/') && (*p != '>'))
01789           {
01790             /*
01791               Find tag in default attributes list.
01792             */
01793             i=0;
01794             while ((root->attributes[i] != (char **) NULL) &&
01795                    (strcmp(root->attributes[i][0],tag) != 0))
01796               i++;
01797             attribute=root->attributes[i];
01798           }
01799         for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
01800         {
01801           /*
01802             Attribute.
01803           */
01804           if (l == 0)
01805             attributes=(char **) AcquireQuantumMemory(4,sizeof(*attributes));
01806           else
01807             attributes=(char **) ResizeQuantumMemory(attributes,(size_t) (l+4),
01808               sizeof(*attributes));
01809           if (attributes == (char **) NULL)
01810             {
01811               (void) ThrowMagickException(exception,GetMagickModule(),
01812                 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
01813               utf8=DestroyString(utf8);
01814               return(&root->root);
01815             }
01816           attributes[l+2]=(char *) NULL;
01817           attributes[l+1]=(char *) NULL;
01818           attributes[l]=p;
01819           p+=strcspn(p,XMLWhitespace "=/>");
01820           if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
01821             attributes[l]=ConstantString("");
01822           else
01823             {
01824               *p++='\0';
01825               p+=strspn(p,XMLWhitespace "=");
01826               c=(*p);
01827               if ((c == '"') || (c == '\''))
01828                 {
01829                   /*
01830                     Attributes value.
01831                   */
01832                   p++;
01833                   attributes[l+1]=p;
01834                   while ((*p != '\0') && (*p != c))
01835                     p++;
01836                   if (*p != '\0')
01837                     *p++='\0';
01838                   else
01839                     {
01840                       attributes[l]=ConstantString("");
01841                       attributes[l+1]=ConstantString("");
01842                       (void) DestroyXMLTreeAttributes(attributes);
01843                       (void) ThrowMagickException(exception,GetMagickModule(),
01844                         OptionWarning,"ParseError","missing %c",c);
01845                       utf8=DestroyString(utf8);
01846                       return(&root->root);
01847                     }
01848                   j=1;
01849                   while ((attribute != (char **) NULL) &&
01850                          (attribute[j] != (char *) NULL) &&
01851                          (strcmp(attribute[j],attributes[l]) != 0))
01852                     j+=3;
01853                   attributes[l+1]=ParseEntities(attributes[l+1],root->entities,
01854                     (attribute != (char **) NULL) && (attribute[j] !=
01855                     (char *) NULL) ? *attribute[j+2] : ' ');
01856                 }
01857               attributes[l]=ConstantString(attributes[l]);
01858             }
01859           while (isspace((int) ((unsigned char) *p)) != 0)
01860             p++;
01861         }
01862         if (*p == '/')
01863           {
01864             /*
01865               Self closing tag.
01866             */
01867             *p++='\0';
01868             if (((*p != '\0') && (*p != '>')) ||
01869                 ((*p == '\0') && (terminal != '>')))
01870               {
01871                 if (l != 0)
01872                   (void) DestroyXMLTreeAttributes(attributes);
01873                 (void) ThrowMagickException(exception,GetMagickModule(),
01874                   OptionWarning,"ParseError","missing >");
01875                 utf8=DestroyString(utf8);
01876                 return(&root->root);
01877               }
01878             ParseOpenTag(root,tag,attributes);
01879             (void) ParseCloseTag(root,tag,p,exception);
01880           }
01881         else
01882           {
01883             c=(*p);
01884             if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
01885               {
01886                 *p='\0';
01887                 ParseOpenTag(root,tag,attributes);
01888                 *p=c;
01889               }
01890             else
01891               {
01892                 if (l != 0)
01893                   (void) DestroyXMLTreeAttributes(attributes);
01894                 (void) ThrowMagickException(exception,GetMagickModule(),
01895                   OptionWarning,"ParseError","missing >");
01896                 utf8=DestroyString(utf8);
01897                 return(&root->root);
01898               }
01899           }
01900       }
01901     else
01902       if (*p == '/')
01903         {
01904           /*
01905             Close tag.
01906           */
01907           tag=p+1;
01908           p+=strcspn(tag,XMLWhitespace ">")+1;
01909           c=(*p);
01910           if ((c == '\0') && (terminal != '>'))
01911             {
01912               (void) ThrowMagickException(exception,GetMagickModule(),
01913                 OptionWarning,"ParseError","missing >");
01914               utf8=DestroyString(utf8);
01915               return(&root->root);
01916             }
01917           *p='\0';
01918           if (ParseCloseTag(root,tag,p,exception) != (XMLTreeInfo *) NULL)
01919             {
01920               utf8=DestroyString(utf8);
01921               return(&root->root);
01922             }
01923           *p=c;
01924           if (isspace((int) ((unsigned char) *p)) != 0)
01925             p+=strspn(p,XMLWhitespace);
01926         }
01927       else
01928         if (strncmp(p,"!--",3) == 0)
01929           {
01930             /*
01931               Comment.
01932             */
01933             p=strstr(p+3,"--");
01934             if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
01935                 ((*p == '\0') && (terminal != '>')))
01936               {
01937                 (void) ThrowMagickException(exception,GetMagickModule(),
01938                   OptionWarning,"ParseError","unclosed <!--");
01939                 utf8=DestroyString(utf8);
01940                 return(&root->root);
01941               }
01942           }
01943         else
01944           if (strncmp(p,"![CDATA[",8) == 0)
01945             {
01946               /*
01947                 Cdata.
01948               */
01949               p=strstr(p,"]]>");
01950               if (p != (char *) NULL)
01951                 {
01952                   p+=2;
01953                   ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
01954                 }
01955               else
01956                 {
01957                   (void) ThrowMagickException(exception,GetMagickModule(),
01958                     OptionWarning,"ParseError","unclosed <![CDATA[");
01959                   utf8=DestroyString(utf8);
01960                   return(&root->root);
01961                 }
01962             }
01963           else
01964             if (strncmp(p,"!DOCTYPE",8) == 0)
01965               {
01966                 /*
01967                   DTD.
01968                 */
01969                 for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
01970                      ((l != 0) && ((*p != ']') ||
01971                      (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
01972                   l=(ssize_t) ((*p == '[') ? 1 : l))
01973                 p+=strcspn(p+1,"[]>")+1;
01974                 if ((*p == '\0') && (terminal != '>'))
01975                   {
01976                     (void) ThrowMagickException(exception,GetMagickModule(),
01977                       OptionWarning,"ParseError","unclosed <!DOCTYPE");
01978                     utf8=DestroyString(utf8);
01979                     return(&root->root);
01980                   }
01981                 if (l != 0)
01982                   tag=strchr(tag,'[')+1;
01983                 if (l != 0)
01984                   {
01985                     status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
01986                       exception);
01987                     if (status == MagickFalse)
01988                       {
01989                         utf8=DestroyString(utf8);
01990                         return(&root->root);
01991                       }
01992                     p++;
01993                   }
01994               }
01995             else
01996               if (*p == '?')
01997                 {
01998                   /*
01999                     Processing instructions.
02000                   */
02001                   do
02002                   {
02003                     p=strchr(p,'?');
02004                     if (p == (char *) NULL)
02005                       break;
02006                     p++;
02007                   } while ((*p != '\0') && (*p != '>'));
02008                   if ((p == (char *) NULL) || ((*p == '\0') &&
02009                       (terminal != '>')))
02010                     {
02011                       (void) ThrowMagickException(exception,GetMagickModule(),
02012                         OptionWarning,"ParseError","unclosed <?");
02013                       utf8=DestroyString(utf8);
02014                       return(&root->root);
02015                     }
02016                   ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
02017                 }
02018               else
02019                 {
02020                   (void) ThrowMagickException(exception,GetMagickModule(),
02021                     OptionWarning,"ParseError","unexpected <");
02022                   utf8=DestroyString(utf8);
02023                   return(&root->root);
02024                 }
02025      if ((p == (char *) NULL) || (*p == '\0'))
02026        break;
02027      *p++='\0';
02028      tag=p;
02029      if ((*p != '\0') && (*p != '<'))
02030        {
02031         /*
02032           Tag character content.
02033         */
02034         while ((*p != '\0') && (*p != '<'))
02035           p++;
02036         if (*p == '\0')
02037           break;
02038         ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
02039       }
02040     else
02041       if (*p == '\0')
02042         break;
02043   }
02044   utf8=DestroyString(utf8);
02045   if (root->node == (XMLTreeInfo *) NULL)
02046     return(&root->root);
02047   if (root->node->tag == (char *) NULL)
02048     {
02049       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
02050         "ParseError","root tag missing");
02051       return(&root->root);
02052     }
02053   (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
02054     "ParseError","unclosed tag: `%s'",root->node->tag);
02055   return(&root->root);
02056 }
02057 
02058 /*
02059 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02060 %                                                                             %
02061 %                                                                             %
02062 %                                                                             %
02063 %   N e w X M L T r e e T a g                                                 %
02064 %                                                                             %
02065 %                                                                             %
02066 %                                                                             %
02067 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02068 %
02069 %  NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
02070 %
02071 %  The format of the NewXMLTreeTag method is:
02072 %
02073 %      XMLTreeInfo *NewXMLTreeTag(const char *tag)
02074 %
02075 %  A description of each parameter follows:
02076 %
02077 %    o tag: the tag.
02078 %
02079 */
02080 MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
02081 {
02082   static const char
02083     *predefined_entities[NumberPredefinedEntities+1] =
02084     {
02085       "lt;", "&#60;", "gt;", "&#62;", "quot;", "&#34;",
02086       "apos;", "&#39;", "amp;", "&#38;", (char *) NULL
02087     };
02088 
02089   XMLTreeRoot
02090     *root;
02091 
02092   root=(XMLTreeRoot *) AcquireMagickMemory(sizeof(*root));
02093   if (root == (XMLTreeRoot *) NULL)
02094     return((XMLTreeInfo *) NULL);
02095   (void) ResetMagickMemory(root,0,sizeof(*root));
02096   root->root.tag=(char *) NULL;
02097   if (tag != (char *) NULL)
02098     root->root.tag=ConstantString(tag);
02099   root->node=(&root->root);
02100   root->root.content=ConstantString("");
02101   root->entities=(char **) AcquireMagickMemory(sizeof(predefined_entities));
02102   if (root->entities == (char **) NULL)
02103     return((XMLTreeInfo *) NULL);
02104   (void) CopyMagickMemory(root->entities,predefined_entities,
02105     sizeof(predefined_entities));
02106   root->root.attributes=sentinel;
02107   root->attributes=(char ***) root->root.attributes;
02108   root->processing_instructions=(char ***) root->root.attributes;
02109   root->debug=IsEventLogging();
02110   root->signature=MagickSignature;
02111   return(&root->root);
02112 }
02113 
02114 /*
02115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02116 %                                                                             %
02117 %                                                                             %
02118 %                                                                             %
02119 %   P r u n e T a g F r o m X M L T r e e                                     %
02120 %                                                                             %
02121 %                                                                             %
02122 %                                                                             %
02123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02124 %
02125 %  PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
02126 %  subtags.
02127 %
02128 %  The format of the PruneTagFromXMLTree method is:
02129 %
02130 %      XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
02131 %
02132 %  A description of each parameter follows:
02133 %
02134 %    o xml_info: the xml info.
02135 %
02136 */
02137 MagickPrivate XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
02138 {
02139   XMLTreeInfo
02140     *node;
02141 
02142   assert(xml_info != (XMLTreeInfo *) NULL);
02143   assert((xml_info->signature == MagickSignature) ||
02144          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
02145   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
02146   if (xml_info->next != (XMLTreeInfo *) NULL)
02147     xml_info->next->sibling=xml_info->sibling;
02148   if (xml_info->parent != (XMLTreeInfo *) NULL)
02149     {
02150       node=xml_info->parent->child;
02151       if (node == xml_info)
02152         xml_info->parent->child=xml_info->ordered;
02153       else
02154         {
02155           while (node->ordered != xml_info)
02156             node=node->ordered;
02157           node->ordered=node->ordered->ordered;
02158           node=xml_info->parent->child;
02159           if (strcmp(node->tag,xml_info->tag) != 0)
02160             {
02161               while (strcmp(node->sibling->tag,xml_info->tag) != 0)
02162                 node=node->sibling;
02163               if (node->sibling != xml_info)
02164                 node=node->sibling;
02165               else
02166                 node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
02167                   xml_info->next : node->sibling->sibling;
02168             }
02169           while ((node->next != (XMLTreeInfo *) NULL) &&
02170                  (node->next != xml_info))
02171             node=node->next;
02172           if (node->next != (XMLTreeInfo *) NULL)
02173             node->next=node->next->next;
02174         }
02175     }
02176   xml_info->ordered=(XMLTreeInfo *) NULL;
02177   xml_info->sibling=(XMLTreeInfo *) NULL;
02178   xml_info->next=(XMLTreeInfo *) NULL;
02179   return(xml_info);
02180 }
02181 
02182 /*
02183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02184 %                                                                             %
02185 %                                                                             %
02186 %                                                                             %
02187 %   S e t X M L T r e e A t t r i b u t e                                     %
02188 %                                                                             %
02189 %                                                                             %
02190 %                                                                             %
02191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02192 %
02193 %  SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
02194 %  found.  A value of NULL removes the specified attribute.
02195 %
02196 %  The format of the SetXMLTreeAttribute method is:
02197 %
02198 %      XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
02199 %        const char *value)
02200 %
02201 %  A description of each parameter follows:
02202 %
02203 %    o xml_info: the xml info.
02204 %
02205 %    o tag:  The attribute tag.
02206 %
02207 %    o value:  The attribute value.
02208 %
02209 */
02210 MagickPrivate XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
02211   const char *tag,const char *value)
02212 {
02213   register ssize_t
02214     i;
02215 
02216   ssize_t
02217     j;
02218 
02219   assert(xml_info != (XMLTreeInfo *) NULL);
02220   assert((xml_info->signature == MagickSignature) ||
02221          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
02222   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
02223   i=0;
02224   while ((xml_info->attributes[i] != (char *) NULL) &&
02225          (strcmp(xml_info->attributes[i],tag) != 0))
02226     i+=2;
02227   if (xml_info->attributes[i] == (char *) NULL)
02228     {
02229       /*
02230         Add new attribute tag.
02231       */
02232       if (value == (const char *) NULL)
02233         return(xml_info);
02234       if (xml_info->attributes != sentinel)
02235         xml_info->attributes=(char **) ResizeQuantumMemory(
02236           xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
02237       else
02238         {
02239           xml_info->attributes=(char **) AcquireQuantumMemory(4,
02240             sizeof(*xml_info->attributes));
02241           if (xml_info->attributes != (char **) NULL)
02242             xml_info->attributes[1]=ConstantString("");
02243         }
02244       if (xml_info->attributes == (char **) NULL)
02245         ThrowFatalException(ResourceLimitFatalError,
02246           "UnableToAcquireString");
02247       xml_info->attributes[i]=ConstantString(tag);
02248       xml_info->attributes[i+2]=(char *) NULL;
02249       (void) strlen(xml_info->attributes[i+1]);
02250     }
02251   /*
02252     Add new value to an existing attribute.
02253   */
02254   for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
02255   if (xml_info->attributes[i+1] != (char *) NULL)
02256     xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
02257   if (value != (const char *) NULL)
02258     {
02259       xml_info->attributes[i+1]=ConstantString(value);
02260       return(xml_info);
02261     }
02262   if (xml_info->attributes[i] != (char *) NULL)
02263     xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
02264   (void) CopyMagickMemory(xml_info->attributes+i,xml_info->attributes+i+2,
02265     (size_t) (j-i)*sizeof(*xml_info->attributes));
02266   j-=2;
02267   xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
02268     (size_t) (j+2),sizeof(*xml_info->attributes));
02269   if (xml_info->attributes == (char **) NULL)
02270     ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
02271   (void) CopyMagickMemory(xml_info->attributes[j+1]+(i/2),
02272     xml_info->attributes[j+1]+(i/2)+1,(size_t) ((j/2)-(i/2))*
02273     sizeof(*xml_info->attributes));
02274   return(xml_info);
02275 }
02276 
02277 /*
02278 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02279 %                                                                             %
02280 %                                                                             %
02281 %                                                                             %
02282 %   S e t X M L T r e e C o n t e n t                                         %
02283 %                                                                             %
02284 %                                                                             %
02285 %                                                                             %
02286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02287 %
02288 %  SetXMLTreeContent() sets the character content for the given tag and
02289 %  returns the tag.
02290 %
02291 %  The format of the SetXMLTreeContent method is:
02292 %
02293 %      XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
02294 %        const char *content)
02295 %
02296 %  A description of each parameter follows:
02297 %
02298 %    o xml_info: the xml info.
02299 %
02300 %    o content:  The content.
02301 %
02302 */
02303 MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
02304   const char *content)
02305 {
02306   assert(xml_info != (XMLTreeInfo *) NULL);
02307   assert((xml_info->signature == MagickSignature) ||
02308          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
02309   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
02310   if (xml_info->content != (char *) NULL)
02311     xml_info->content=DestroyString(xml_info->content);
02312   xml_info->content=(char *) ConstantString(content);
02313   return(xml_info);
02314 }
02315 
02316 /*
02317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02318 %                                                                             %
02319 %                                                                             %
02320 %                                                                             %
02321 %   X M L T r e e I n f o T o X M L                                           %
02322 %                                                                             %
02323 %                                                                             %
02324 %                                                                             %
02325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02326 %
02327 %  XMLTreeInfoToXML() converts an xml-tree to an XML string.
02328 %
02329 %  The format of the XMLTreeInfoToXML method is:
02330 %
02331 %      char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
02332 %
02333 %  A description of each parameter follows:
02334 %
02335 %    o xml_info: the xml info.
02336 %
02337 */
02338 
02339 static char *EncodePredefinedEntities(const char *source,ssize_t offset,
02340   char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
02341 {
02342   char
02343     *canonical_content;
02344 
02345   if (offset < 0)
02346     canonical_content=CanonicalXMLContent(source,pedantic);
02347   else
02348     {
02349       char
02350         *content;
02351 
02352       content=AcquireString(source);
02353       content[offset]='\0';
02354       canonical_content=CanonicalXMLContent(content,pedantic);
02355       content=DestroyString(content);
02356     }
02357   if (canonical_content == (char *) NULL)
02358     return(*destination);
02359   if ((*length+strlen(canonical_content)+MaxTextExtent) > *extent)
02360     {
02361       *extent=(*length)+strlen(canonical_content)+MaxTextExtent;
02362       *destination=(char *) ResizeQuantumMemory(*destination,*extent,
02363         sizeof(**destination));
02364       if (*destination == (char *) NULL)
02365         return(*destination);
02366     }
02367   *length+=FormatLocaleString(*destination+(*length),*extent,"%s",
02368     canonical_content);
02369   canonical_content=DestroyString(canonical_content);
02370   return(*destination);
02371 }
02372 
02373 static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
02374   size_t *extent,size_t start,char ***attributes)
02375 {
02376   char
02377     *content;
02378 
02379   const char
02380     *attribute;
02381 
02382   register ssize_t
02383     i;
02384 
02385   size_t
02386     offset;
02387 
02388   ssize_t
02389     j;
02390 
02391   content=(char *) "";
02392   if (xml_info->parent != (XMLTreeInfo *) NULL)
02393     content=xml_info->parent->content;
02394   offset=0;
02395   *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
02396     start),source,length,extent,MagickFalse);
02397   if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
02398     {
02399       *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
02400       *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(*source));
02401       if (*source == (char *) NULL)
02402         return(*source);
02403     }
02404   *length+=FormatLocaleString(*source+(*length),*extent,"<%s",xml_info->tag);
02405   for (i=0; xml_info->attributes[i]; i+=2)
02406   {
02407     attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
02408     if (attribute != xml_info->attributes[i+1])
02409       continue;
02410     if ((*length+strlen(xml_info->attributes[i])+MaxTextExtent) > *extent)
02411       {
02412         *extent=(*length)+strlen(xml_info->attributes[i])+MaxTextExtent;
02413         *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
02414         if (*source == (char *) NULL)
02415           return((char *) NULL);
02416       }
02417     *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
02418       xml_info->attributes[i]);
02419     (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
02420       extent,MagickTrue);
02421     *length+=FormatLocaleString(*source+(*length),*extent,"\"");
02422   }
02423   i=0;
02424   while ((attributes[i] != (char **) NULL) &&
02425          (strcmp(attributes[i][0],xml_info->tag) != 0))
02426     i++;
02427   j=1;
02428   while ((attributes[i] != (char **) NULL) &&
02429          (attributes[i][j] != (char *) NULL))
02430   {
02431     if ((attributes[i][j+1] == (char *) NULL) ||
02432         (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
02433       {
02434         j+=3;
02435         continue;
02436       }
02437     if ((*length+strlen(attributes[i][j])+MaxTextExtent) > *extent)
02438       {
02439         *extent=(*length)+strlen(attributes[i][j])+MaxTextExtent;
02440         *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
02441         if (*source == (char *) NULL)
02442           return((char *) NULL);
02443       }
02444     *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
02445       attributes[i][j]);
02446     (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
02447       MagickTrue);
02448     *length+=FormatLocaleString(*source+(*length),*extent,"\"");
02449     j+=3;
02450   }
02451   *length+=FormatLocaleString(*source+(*length),*extent,*xml_info->content ?
02452     ">" : "/>");
02453   if (xml_info->child != (XMLTreeInfo *) NULL)
02454     *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
02455   else
02456     *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
02457       MagickFalse);
02458   if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
02459     {
02460       *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
02461       *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
02462       if (*source == (char *) NULL)
02463         return((char *) NULL);
02464     }
02465   if (*xml_info->content != '\0')
02466     *length+=FormatLocaleString(*source+(*length),*extent,"</%s>",
02467       xml_info->tag);
02468   while ((content[offset] != '\0') && (offset < xml_info->offset))
02469     offset++;
02470   if (xml_info->ordered != (XMLTreeInfo *) NULL)
02471     content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
02472       attributes);
02473   else
02474     content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
02475       MagickFalse);
02476   return(content);
02477 }
02478 
02479 MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
02480 {
02481   char
02482     *xml;
02483 
02484   register char
02485     *p,
02486     *q;
02487 
02488   register ssize_t
02489     i;
02490 
02491   size_t
02492     extent,
02493     length;
02494 
02495   ssize_t
02496     j,
02497     k;
02498 
02499   XMLTreeInfo
02500     *ordered,
02501     *parent;
02502 
02503   XMLTreeRoot
02504     *root;
02505 
02506   assert(xml_info != (XMLTreeInfo *) NULL);
02507   assert((xml_info->signature == MagickSignature) ||
02508          (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
02509   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
02510   if (xml_info->tag == (char *) NULL)
02511     return((char *) NULL);
02512   xml=AcquireString((char *) NULL);
02513   length=0;
02514   extent=MaxTextExtent;
02515   root=(XMLTreeRoot *) xml_info;
02516   while (root->root.parent != (XMLTreeInfo *) NULL)
02517     root=(XMLTreeRoot *) root->root.parent;
02518   parent=(XMLTreeInfo *) NULL;
02519   if (xml_info != (XMLTreeInfo *) NULL)
02520     parent=xml_info->parent;
02521   if (parent == (XMLTreeInfo *) NULL)
02522     for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
02523     {
02524       /*
02525         Pre-root processing instructions.
02526       */
02527       for (k=2; root->processing_instructions[i][k-1]; k++) ;
02528       p=root->processing_instructions[i][1];
02529       for (j=1; p != (char *) NULL; j++)
02530       {
02531         if (root->processing_instructions[i][k][j-1] == '>')
02532           {
02533             p=root->processing_instructions[i][j];
02534             continue;
02535           }
02536         q=root->processing_instructions[i][0];
02537         if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
02538           {
02539             extent=length+strlen(p)+strlen(q)+MaxTextExtent;
02540             xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
02541             if (xml == (char *) NULL)
02542               return(xml);
02543           }
02544         length+=FormatLocaleString(xml+length,extent,"<?%s%s%s?>\n",q,
02545           *p != '\0' ? " " : "",p);
02546         p=root->processing_instructions[i][j];
02547       }
02548     }
02549   ordered=(XMLTreeInfo *) NULL;
02550   if (xml_info != (XMLTreeInfo *) NULL)
02551     ordered=xml_info->ordered;
02552   xml_info->parent=(XMLTreeInfo *) NULL;
02553   xml_info->ordered=(XMLTreeInfo *) NULL;
02554   xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
02555   xml_info->parent=parent;
02556   xml_info->ordered=ordered;
02557   if (parent == (XMLTreeInfo *) NULL)
02558     for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
02559     {
02560       /*
02561         Post-root processing instructions.
02562       */
02563       for (k=2; root->processing_instructions[i][k-1]; k++) ;
02564       p=root->processing_instructions[i][1];
02565       for (j=1; p != (char *) NULL; j++)
02566       {
02567         if (root->processing_instructions[i][k][j-1] == '<')
02568           {
02569             p=root->processing_instructions[i][j];
02570             continue;
02571           }
02572         q=root->processing_instructions[i][0];
02573         if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
02574           {
02575             extent=length+strlen(p)+strlen(q)+MaxTextExtent;
02576             xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
02577             if (xml == (char *) NULL)
02578               return(xml);
02579           }
02580         length+=FormatLocaleString(xml+length,extent,"\n<?%s%s%s?>",q,
02581           *p != '\0' ? " " : "",p);
02582         p=root->processing_instructions[i][j];
02583       }
02584     }
02585   return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));
02586 }