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