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

Generated on 19 Nov 2009 for MagickCore by  doxygen 1.6.1