/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % X X M M L % % X X MM MM L % % X M M M L % % X X M M L % % X X M M LLLLL % % % % TTTTT RRRR EEEEE EEEEE % % T R R E E % % T RRRR EEE EEE % % T R R E E % % T R R EEEEE EEEEE % % % % % % XML Tree Methods % % % % Software Design % % Cristy % % December 2004 % % % % % % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization % % dedicated to making software imaging solutions freely available. % % % % You may not use this file except in compliance with the License. You may % % obtain a copy of the License at % % % % https://imagemagick.org/script/license.php % % % % Unless required by applicable law or agreed to in writing, software % % distributed under the License is distributed on an "AS IS" BASIS, % % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % % See the License for the specific language governing permissions and % % limitations under the License. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % This module implements the standard handy xml-tree methods for storing and % retrieving nodes and attributes from an XML string. % */ /* Include declarations. */ #include "MagickCore/studio.h" #include "MagickCore/blob.h" #include "MagickCore/blob-private.h" #include "MagickCore/exception.h" #include "MagickCore/exception-private.h" #include "MagickCore/image-private.h" #include "MagickCore/log.h" #include "MagickCore/memory_.h" #include "MagickCore/memory-private.h" #include "MagickCore/semaphore.h" #include "MagickCore/string_.h" #include "MagickCore/string-private.h" #include "MagickCore/token-private.h" #include "MagickCore/xml-tree.h" #include "MagickCore/xml-tree-private.h" #include "MagickCore/utility.h" #include "MagickCore/utility-private.h" /* Define declarations. */ #define NumberPredefinedEntities 10 #define XMLWhitespace "\t\r\n " /* Typedef declarations. */ struct _XMLTreeInfo { char *tag, **attributes, *content; size_t offset; XMLTreeInfo *parent, *next, *sibling, *ordered, *child; MagickBooleanType debug; SemaphoreInfo *semaphore; size_t signature; }; typedef struct _XMLTreeRoot XMLTreeRoot; struct _XMLTreeRoot { struct _XMLTreeInfo root; XMLTreeInfo *node; MagickBooleanType standalone; char ***processing_instructions, **entities, ***attributes; MagickBooleanType debug; SemaphoreInfo *semaphore; size_t signature; }; /* Global declarations. */ static char *sentinel[] = { (char *) NULL }; /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % A d d C h i l d T o X M L T r e e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % AddChildToXMLTree() adds a child tag at an offset relative to the start of % the parent tag's character content. Return the child tag. % % The format of the AddChildToXMLTree method is: % % XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,const char *tag, % const size_t offset) % % A description of each parameter follows: % % o xml_info: the xml info. % % o tag: the tag. % % o offset: the tag offset. % */ MagickExport XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info, const char *tag,const size_t offset) { XMLTreeInfo *child; if (xml_info == (XMLTreeInfo *) NULL) return((XMLTreeInfo *) NULL); child=(XMLTreeInfo *) AcquireMagickMemory(sizeof(*child)); if (child == (XMLTreeInfo *) NULL) return((XMLTreeInfo *) NULL); (void) memset(child,0,sizeof(*child)); child->tag=ConstantString(tag); child->attributes=sentinel; child->content=ConstantString(""); child->debug=IsEventLogging(); child->signature=MagickCoreSignature; return(InsertTagIntoXMLTree(xml_info,child,offset)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % A d d P a t h T o X M L T r e e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % AddPathToXMLTree() adds a child tag at an offset relative to the start of % the parent tag's character content. This method returns the child tag. % % The format of the AddPathToXMLTree method is: % % XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,const char *path, % const size_t offset) % % A description of each parameter follows: % % o xml_info: the xml info. % % o path: the path. % % o offset: the tag offset. % */ MagickPrivate XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info, const char *path,const size_t offset) { char **components, subnode[MagickPathExtent], tag[MagickPathExtent]; ssize_t i; size_t number_components; ssize_t j; XMLTreeInfo *child, *node; assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); node=xml_info; components=GetPathComponents(path,&number_components); if (components == (char **) NULL) return((XMLTreeInfo *) NULL); for (i=0; i < (ssize_t) number_components; i++) { GetPathComponent(components[i],SubimagePath,subnode); GetPathComponent(components[i],CanonicalPath,tag); child=GetXMLTreeChild(node,tag); if (child == (XMLTreeInfo *) NULL) child=AddChildToXMLTree(node,tag,offset); node=child; if (node == (XMLTreeInfo *) NULL) break; for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--) { node=GetXMLTreeOrdered(node); if (node == (XMLTreeInfo *) NULL) break; } if (node == (XMLTreeInfo *) NULL) break; components[i]=DestroyString(components[i]); } for ( ; i < (ssize_t) number_components; i++) components[i]=DestroyString(components[i]); components=(char **) RelinquishMagickMemory(components); return(node); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C a n o n i c a l X M L C o n t e n t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % CanonicalXMLContent() converts text to canonical XML content by converting % to UTF-8, substituting predefined entities, wrapping as CDATA, or encoding % as base-64 as required. % % The format of the CanonicalXMLContent method is: % % char *CanonicalXMLContent(const char *content, % const MagickBooleanType pedantic) % % A description of each parameter follows: % % o content: the content. % % o pedantic: if true, replace newlines and tabs with their respective % entities. % */ MagickPrivate char *CanonicalXMLContent(const char *content, const MagickBooleanType pedantic) { char *base64, *canonical_content; const unsigned char *p; ssize_t i; size_t extent, length; unsigned char *utf8; utf8=ConvertLatin1ToUTF8((const unsigned char *) content); if (utf8 == (unsigned char *) NULL) return((char *) NULL); for (p=utf8; *p != '\0'; p++) if ((*p < 0x20) && (*p != 0x09) && (*p != 0x0a) && (*p != 0x0d)) break; if (*p != '\0') { /* String is binary, base64-encode it. */ base64=Base64Encode(utf8,strlen((char *) utf8),&length); utf8=(unsigned char *) RelinquishMagickMemory(utf8); if (base64 == (char *) NULL) return((char *) NULL); canonical_content=AcquireString(""); (void) ConcatenateString(&canonical_content,base64); base64=DestroyString(base64); (void) ConcatenateString(&canonical_content,""); return(canonical_content); } /* Substitute predefined entities. */ i=0; canonical_content=AcquireString((char *) NULL); extent=MagickPathExtent; for (p=utf8; *p != '\0'; p++) { if ((i+MagickPathExtent) > (ssize_t) extent) { extent+=MagickPathExtent; canonical_content=(char *) ResizeQuantumMemory(canonical_content,extent, sizeof(*canonical_content)); if (canonical_content == (char *) NULL) return(canonical_content); } switch (*p) { case '&': { i+=FormatLocaleString(canonical_content+i,extent,"&"); break; } case '<': { i+=FormatLocaleString(canonical_content+i,extent,"<"); break; } case '>': { i+=FormatLocaleString(canonical_content+i,extent,">"); break; } case '"': { i+=FormatLocaleString(canonical_content+i,extent,"""); break; } case '\n': { if (pedantic == MagickFalse) { canonical_content[i++]=(char) (*p); break; } i+=FormatLocaleString(canonical_content+i,extent," "); break; } case '\t': { if (pedantic == MagickFalse) { canonical_content[i++]=(char) (*p); break; } i+=FormatLocaleString(canonical_content+i,extent," "); break; } case '\r': { i+=FormatLocaleString(canonical_content+i,extent," "); break; } default: { canonical_content[i++]=(char) (*p); break; } } } canonical_content[i]='\0'; utf8=(unsigned char *) RelinquishMagickMemory(utf8); return(canonical_content); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % D e s t r o y X M L T r e e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % DestroyXMLTree() destroys the xml-tree. % % The format of the DestroyXMLTree method is: % % XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info) % % A description of each parameter follows: % % o xml_info: the xml info. % */ static char **DestroyXMLTreeAttributes(char **attributes) { ssize_t i; /* Destroy a tag attribute list. */ if ((attributes == (char **) NULL) || (attributes == sentinel)) return((char **) NULL); for (i=0; attributes[i] != (char *) NULL; i+=2) { /* Destroy attribute tag and value. */ if (attributes[i] != (char *) NULL) attributes[i]=DestroyString(attributes[i]); if (attributes[i+1] != (char *) NULL) attributes[i+1]=DestroyString(attributes[i+1]); } attributes=(char **) RelinquishMagickMemory(attributes); return((char **) NULL); } static void DestroyXMLTreeChild(XMLTreeInfo *xml_info) { XMLTreeInfo *child, *node; child=xml_info->child; while(child != (XMLTreeInfo *) NULL) { node=child; child=node->child; node->child=(XMLTreeInfo *) NULL; (void) DestroyXMLTree(node); } } static void DestroyXMLTreeOrdered(XMLTreeInfo *xml_info) { XMLTreeInfo *node, *ordered; ordered=xml_info->ordered; while(ordered != (XMLTreeInfo *) NULL) { node=ordered; ordered=node->ordered; node->ordered=(XMLTreeInfo *) NULL; (void) DestroyXMLTree(node); } } static void DestroyXMLTreeRoot(XMLTreeInfo *xml_info) { char **attributes; ssize_t i; ssize_t j; XMLTreeRoot *root; assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); if (xml_info->parent != (XMLTreeInfo *) NULL) return; /* Free root tag allocations. */ root=(XMLTreeRoot *) xml_info; for (i=NumberPredefinedEntities; root->entities[i] != (char *) NULL; i+=2) root->entities[i+1]=DestroyString(root->entities[i+1]); root->entities=(char **) RelinquishMagickMemory(root->entities); for (i=0; root->attributes[i] != (char **) NULL; i++) { attributes=root->attributes[i]; if (attributes[0] != (char *) NULL) attributes[0]=DestroyString(attributes[0]); for (j=1; attributes[j] != (char *) NULL; j+=3) { if (attributes[j] != (char *) NULL) attributes[j]=DestroyString(attributes[j]); if (attributes[j+1] != (char *) NULL) attributes[j+1]=DestroyString(attributes[j+1]); if (attributes[j+2] != (char *) NULL) attributes[j+2]=DestroyString(attributes[j+2]); } attributes=(char **) RelinquishMagickMemory(attributes); } if (root->attributes[0] != (char **) NULL) root->attributes=(char ***) RelinquishMagickMemory(root->attributes); if (root->processing_instructions[0] != (char **) NULL) { for (i=0; root->processing_instructions[i] != (char **) NULL; i++) { for (j=0; root->processing_instructions[i][j] != (char *) NULL; j++) root->processing_instructions[i][j]=DestroyString( root->processing_instructions[i][j]); root->processing_instructions[i][j+1]=DestroyString( root->processing_instructions[i][j+1]); root->processing_instructions[i]=(char **) RelinquishMagickMemory( root->processing_instructions[i]); } root->processing_instructions=(char ***) RelinquishMagickMemory( root->processing_instructions); } } MagickExport XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info) { assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); DestroyXMLTreeChild(xml_info); DestroyXMLTreeOrdered(xml_info); DestroyXMLTreeRoot(xml_info); xml_info->attributes=DestroyXMLTreeAttributes(xml_info->attributes); xml_info->content=DestroyString(xml_info->content); xml_info->tag=DestroyString(xml_info->tag); xml_info=(XMLTreeInfo *) RelinquishMagickMemory(xml_info); return((XMLTreeInfo *) NULL); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % F i l e T o X M L % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % FileToXML() returns the contents of a file as a XML string. % % The format of the FileToXML method is: % % char *FileToXML(const char *filename,const size_t extent) % % A description of each parameter follows: % % o filename: the filename. % % o extent: Maximum length of the string. % */ MagickPrivate char *FileToXML(const char *filename,const size_t extent) { char *xml; int file; MagickOffsetType offset; size_t i; size_t length; ssize_t count; void *map; assert(filename != (const char *) NULL); length=0; file=fileno(stdin); if (LocaleCompare(filename,"-") != 0) file=open_utf8(filename,O_RDONLY | O_BINARY,0); if (file == -1) return((char *) NULL); offset=(MagickOffsetType) lseek(file,0,SEEK_END); count=0; if ((file == fileno(stdin)) || (offset < 0) || (offset != (MagickOffsetType) ((ssize_t) offset))) { size_t quantum; struct stat file_stats; /* Stream is not seekable. */ offset=(MagickOffsetType) lseek(file,0,SEEK_SET); quantum=(size_t) MagickMaxBufferExtent; if ((fstat(file,&file_stats) == 0) && (file_stats.st_size > 0)) quantum=(size_t) MagickMin(file_stats.st_size,MagickMaxBufferExtent); xml=(char *) AcquireQuantumMemory(quantum,sizeof(*xml)); for (i=0; xml != (char *) NULL; i+=count) { count=read(file,xml+i,quantum); if (count <= 0) { count=0; if (errno != EINTR) break; } if (~((size_t) i) < (quantum+1)) { xml=(char *) RelinquishMagickMemory(xml); break; } xml=(char *) ResizeQuantumMemory(xml,i+quantum+1,sizeof(*xml)); if ((size_t) (i+count) >= extent) break; } if (LocaleCompare(filename,"-") != 0) file=close(file); if (xml == (char *) NULL) return((char *) NULL); if (file == -1) { xml=(char *) RelinquishMagickMemory(xml); return((char *) NULL); } length=(size_t) MagickMin(i+count,extent); xml[length]='\0'; return(xml); } length=(size_t) MagickMin(offset,(MagickOffsetType) extent); xml=(char *) NULL; if (~length >= (MagickPathExtent-1)) xml=(char *) AcquireQuantumMemory(length+MagickPathExtent,sizeof(*xml)); if (xml == (char *) NULL) { file=close(file); return((char *) NULL); } map=MapBlob(file,ReadMode,0,length); if (map != (char *) NULL) { (void) memcpy(xml,map,length); (void) UnmapBlob(map,length); } else { (void) lseek(file,0,SEEK_SET); for (i=0; i < length; i+=count) { count=read(file,xml+i,(size_t) MagickMin(length-i,(size_t) MAGICK_SSIZE_MAX)); if (count <= 0) { count=0; if (errno != EINTR) break; } } if (i < length) { file=close(file)-1; xml=(char *) RelinquishMagickMemory(xml); return((char *) NULL); } } xml[length]='\0'; if (LocaleCompare(filename,"-") != 0) file=close(file); if (file == -1) xml=(char *) RelinquishMagickMemory(xml); return(xml); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t N e x t X M L T r e e T a g % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetNextXMLTreeTag() returns the next tag or NULL if not found. % % The format of the GetNextXMLTreeTag method is: % % XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info) % % A description of each parameter follows: % % o xml_info: the xml info. % */ MagickExport XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info) { assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); return(xml_info->next); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t X M L T r e e A t t r i b u t e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetXMLTreeAttribute() returns the value of the attribute tag with the % specified tag if found, otherwise NULL. % % The format of the GetXMLTreeAttribute method is: % % const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag) % % A description of each parameter follows: % % o xml_info: the xml info. % % o tag: the attribute tag. % */ MagickExport const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info, const char *tag) { ssize_t i; ssize_t j; XMLTreeRoot *root; assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); if (xml_info->attributes == (char **) NULL) return((const char *) NULL); i=0; while ((xml_info->attributes[i] != (char *) NULL) && (strcmp(xml_info->attributes[i],tag) != 0)) i+=2; if (xml_info->attributes[i] != (char *) NULL) return(xml_info->attributes[i+1]); root=(XMLTreeRoot*) xml_info; while (root->root.parent != (XMLTreeInfo *) NULL) root=(XMLTreeRoot *) root->root.parent; i=0; while ((root->attributes[i] != (char **) NULL) && (strcmp(root->attributes[i][0],xml_info->tag) != 0)) i++; if (root->attributes[i] == (char **) NULL) return((const char *) NULL); j=1; while ((root->attributes[i][j] != (char *) NULL) && (strcmp(root->attributes[i][j],tag) != 0)) j+=3; if (root->attributes[i][j] == (char *) NULL) return((const char *) NULL); return(root->attributes[i][j+1]); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t X M L T r e e A t t r i b u t e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetXMLTreeAttributes() injects all attributes associated with the current % tag in the specified splay-tree. % % The format of the GetXMLTreeAttributes method is: % % MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info, % SplayTreeInfo *attributes) % % A description of each parameter follows: % % o xml_info: the xml info. % % o attributes: the attribute splay-tree. % */ MagickPrivate MagickBooleanType GetXMLTreeAttributes( const XMLTreeInfo *xml_info,SplayTreeInfo *attributes) { ssize_t i; assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((const XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); assert(attributes != (SplayTreeInfo *) NULL); if (xml_info->attributes == (char **) NULL) return(MagickTrue); i=0; while (xml_info->attributes[i] != (char *) NULL) { (void) AddValueToSplayTree(attributes, ConstantString(xml_info->attributes[i]), ConstantString(xml_info->attributes[i+1])); i+=2; } return(MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t X M L T r e e C h i l d % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetXMLTreeChild() returns the first child tag with the specified tag if % found, otherwise NULL. % % The format of the GetXMLTreeChild method is: % % XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag) % % A description of each parameter follows: % % o xml_info: the xml info. % */ MagickExport XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag) { XMLTreeInfo *child; assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); child=xml_info->child; if (tag != (const char *) NULL) while ((child != (XMLTreeInfo *) NULL) && (strcmp(child->tag,tag) != 0)) child=child->sibling; return(child); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t X M L T r e e C o n t e n t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetXMLTreeContent() returns any content associated with specified % xml-tree node. % % The format of the GetXMLTreeContent method is: % % const char *GetXMLTreeContent(XMLTreeInfo *xml_info) % % A description of each parameter follows: % % o xml_info: the xml info. % */ MagickExport const char *GetXMLTreeContent(XMLTreeInfo *xml_info) { assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); return(xml_info->content); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t X M L T r e e O r d e r e d % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetXMLTreeOrdered() returns the next ordered node if found, otherwise NULL. % % The format of the GetXMLTreeOrdered method is: % % XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info) % % A description of each parameter follows: % % o xml_info: the xml info. % */ MagickPrivate XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info) { assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); return(xml_info->ordered); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t X M L T r e e P a t h % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetXMLTreePath() traverses the XML-tree as defined by the specified path % and returns the node if found, otherwise NULL. % % The format of the GetXMLTreePath method is: % % XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path) % % A description of each parameter follows: % % o xml_info: the xml info. % % o path: the path (e.g. property/elapsed-time). % */ MagickPrivate XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info, const char *path) { char **components, subnode[MagickPathExtent], tag[MagickPathExtent]; ssize_t i; size_t number_components; ssize_t j; XMLTreeInfo *node; assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); node=xml_info; components=GetPathComponents(path,&number_components); if (components == (char **) NULL) return((XMLTreeInfo *) NULL); for (i=0; i < (ssize_t) number_components; i++) { GetPathComponent(components[i],SubimagePath,subnode); GetPathComponent(components[i],CanonicalPath,tag); node=GetXMLTreeChild(node,tag); if (node == (XMLTreeInfo *) NULL) break; for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--) { node=GetXMLTreeOrdered(node); if (node == (XMLTreeInfo *) NULL) break; } if (node == (XMLTreeInfo *) NULL) break; components[i]=DestroyString(components[i]); } for ( ; i < (ssize_t) number_components; i++) components[i]=DestroyString(components[i]); components=(char **) RelinquishMagickMemory(components); return(node); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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 % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetXMLTreeProcessingInstructions() returns a null terminated array of % processing instructions for the given target. % % The format of the GetXMLTreeProcessingInstructions method is: % % const char **GetXMLTreeProcessingInstructions(XMLTreeInfo *xml_info, % const char *target) % % A description of each parameter follows: % % o xml_info: the xml info. % */ MagickPrivate const char **GetXMLTreeProcessingInstructions( XMLTreeInfo *xml_info,const char *target) { ssize_t i; XMLTreeRoot *root; assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); root=(XMLTreeRoot *) xml_info; while (root->root.parent != (XMLTreeInfo *) NULL) root=(XMLTreeRoot *) root->root.parent; i=0; while ((root->processing_instructions[i] != (char **) NULL) && (strcmp(root->processing_instructions[i][0],target) != 0)) i++; if (root->processing_instructions[i] == (char **) NULL) return((const char **) sentinel); return((const char **) (root->processing_instructions[i]+1)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t X M L T r e e S i b l i n g % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetXMLTreeSibling() returns the node sibling if found, otherwise NULL. % % The format of the GetXMLTreeSibling method is: % % XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info) % % A description of each parameter follows: % % o xml_info: the xml info. % */ MagickExport XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info) { assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); return(xml_info->sibling); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t X M L T r e e T a g % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetXMLTreeTag() returns the tag associated with specified xml-tree node. % % The format of the GetXMLTreeTag method is: % % const char *GetXMLTreeTag(XMLTreeInfo *xml_info) % % A description of each parameter follows: % % o xml_info: the xml info. % */ MagickExport const char *GetXMLTreeTag(XMLTreeInfo *xml_info) { assert(xml_info != (XMLTreeInfo *) NULL); assert((xml_info->signature == MagickCoreSignature) || (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature)); if (xml_info->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); return(xml_info->tag); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I n s e r t I n t o T a g X M L T r e e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % InsertTagIntoXMLTree() inserts a tag at an offset relative to the start of % the parent tag's character content. This method returns the child tag. % % The format of the InsertTagIntoXMLTree method is: % % XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info, % XMLTreeInfo *child,const size_t offset) % % A description of each parameter follows: % % o xml_info: the xml info. % % o child: the child tag. % % o offset: the tag offset. % */ MagickPrivate XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info, XMLTreeInfo *child,const size_t offset) { XMLTreeInfo *head, *node, *previous; child->ordered=(XMLTreeInfo *) NULL; child->sibling=(XMLTreeInfo *) NULL; child->next=(XMLTreeInfo *) NULL; child->offset=offset; child->parent=xml_info; if (xml_info->child == (XMLTreeInfo *) NULL) { xml_info->child=child; return(child); } head=xml_info->child; if (head->offset > offset) { child->ordered=head; xml_info->child=child; } else { node=head; while ((node->ordered != (XMLTreeInfo *) NULL) && (node->ordered->offset <= offset)) node=node->ordered; child->ordered=node->ordered; node->ordered=child; } previous=(XMLTreeInfo *) NULL; node=head; while ((node != (XMLTreeInfo *) NULL) && (strcmp(node->tag,child->tag) != 0)) { previous=node; node=node->sibling; } if ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset)) { while ((node->next != (XMLTreeInfo *) NULL) && (node->next->offset <= offset)) node=node->next; child->next=node->next; node->next=child; } else { if ((previous != (XMLTreeInfo *) NULL) && (node != (XMLTreeInfo *) NULL)) previous->sibling=node->sibling; child->next=node; previous=(XMLTreeInfo *) NULL; node=head; while ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset)) { previous=node; node=node->sibling; } child->sibling=node; if (previous != (XMLTreeInfo *) NULL) previous->sibling=child; } return(child); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % N e w X M L T r e e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % NewXMLTree() returns a XMLTreeInfo xml-tree as defined by the specified % XML string. % % The format of the NewXMLTree method is: % % XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception) % % A description of each parameter follows: % % o xml: A null-terminated XML string. % % o exception: return any errors or warnings in this structure. % */ static char *ConvertUTF16ToUTF8(const char *content,size_t *length) { char *utf8; int bits, byte, c, encoding; ssize_t i; size_t extent; ssize_t j; utf8=(char *) AcquireQuantumMemory(*length+1,sizeof(*utf8)); if (utf8 == (char *) NULL) return((char *) NULL); encoding=(*content == '\xFE') ? 1 : (*content == '\xFF') ? 0 : -1; if (encoding == -1) { /* Already UTF-8. */ (void) memcpy(utf8,content,*length*sizeof(*utf8)); utf8[*length]='\0'; return(utf8); } j=0; extent=(*length); for (i=2; i < (ssize_t) (*length-1); i+=2) { c=(encoding != 0) ? ((content[i] & 0xff) << 8) | (content[i+1] & 0xff) : ((content[i+1] & 0xff) << 8) | (content[i] & 0xff); if ((c >= 0xd800) && (c <= 0xdfff) && ((i+=2) < (ssize_t) (*length-1))) { byte=(encoding != 0) ? ((content[i] & 0xff) << 8) | (content[i+1] & 0xff) : ((content[i+1] & 0xff) << 8) | (content[i] & 0xff); c=(((c & 0x3ff) << 10) | (byte & 0x3ff))+0x10000; } if ((size_t) (j+MagickPathExtent) > extent) { extent=(size_t) j+MagickPathExtent; utf8=(char *) ResizeQuantumMemory(utf8,extent,sizeof(*utf8)); if (utf8 == (char *) NULL) return(utf8); } if (c < 0x80) { utf8[j]=c; j++; continue; } /* Multi-byte UTF-8 sequence. */ byte=c; for (bits=0; byte != 0; byte/=2) bits++; bits=(bits-2)/5; utf8[j++]=(0xFF << (7-bits)) | (c >> (6*bits)); while (bits != 0) { bits--; utf8[j]=0x80 | ((c >> (6*bits)) & 0x3f); j++; } } *length=(size_t) j; utf8=(char *) ResizeQuantumMemory(utf8,*length,sizeof(*utf8)); if (utf8 != (char *) NULL) utf8[*length]='\0'; return(utf8); } static char *ParseEntities(char *xml,char **entities,int state) { char *entity; int byte, c; char *p, *q; ssize_t i; size_t extent, length; ssize_t offset; /* Normalize line endings. */ p=xml; q=xml; for ( ; *xml != '\0'; xml++) while (*xml == '\r') { *(xml++)='\n'; if (*xml == '\n') (void) memmove(xml,xml+1,strlen(xml)); } for (xml=p; ; ) { while ((*xml != '\0') && (*xml != '&') && ((*xml != '%') || (state != '%')) && (isspace((int) ((unsigned char) *xml) == 0))) xml++; if (*xml == '\0') break; /* States include: '&' for general entity decoding '%' for parameter entity decoding 'c' for CDATA sections ' ' for attributes normalization '*' for non-CDATA attributes normalization */ if ((state != 'c') && (strncmp(xml,"&#",2) == 0)) { /* Character reference. */ if (xml[2] != 'x') c=strtol(xml+2,&entity,10); /* base 10 */ else c=strtol(xml+3,&entity,16); /* base 16 */ if ((c == 0) || (*entity != ';')) { /* Not a character reference. */ xml++; continue; } if (c < 0x80) *(xml++)=c; else { /* Multi-byte UTF-8 sequence. */ byte=c; for (i=0; byte != 0; byte/=2) i++; i=(i-2)/5; *xml=(char) ((0xFF << (7-i)) | (c >> (6*i))); xml++; while (i != 0) { i--; *xml=(char) (0x80 | ((c >> (6*i)) & 0x3F)); xml++; } } (void) memmove(xml,strchr(xml,';')+1,strlen(strchr(xml,';'))); } else if (((*xml == '&') && ((state == '&') || (state == ' ') || (state == '*'))) || ((state == '%') && (*xml == '%'))) { /* Find entity in the list. */ i=0; while ((entities[i] != (char *) NULL) && (strncmp(xml+1,entities[i],strlen(entities[i])) != 0)) i+=2; if (entities[i++] == (char *) NULL) xml++; else if (entities[i] != (char *) NULL) { /* Found a match. */ length=strlen(entities[i]); entity=strchr(xml,';'); if ((entity != (char *) NULL) && ((length-1L) >= (size_t) (entity-xml))) { offset=(ssize_t) (xml-p); extent=(size_t) (offset+length+strlen(entity)); if (p != q) { p=(char *) ResizeQuantumMemory(p,extent+1,sizeof(*p)); p[extent]='\0'; } else { char *extent_xml; extent_xml=(char *) AcquireQuantumMemory(extent+1, sizeof(*extent_xml)); if (extent_xml != (char *) NULL) { memset(extent_xml,0,extent*sizeof(*extent_xml)); (void) CopyMagickString(extent_xml,p,extent* sizeof(*extent_xml)); } p=extent_xml; } if (p == (char *) NULL) ThrowFatalException(ResourceLimitFatalError, "MemoryAllocationFailed"); xml=p+offset; entity=strchr(xml,';'); } if (entity != (char *) NULL) (void) memmove(xml+length,entity+1,strlen(entity)); (void) memcpy(xml,entities[i],length); } } else if (((state == ' ') || (state == '*')) && (isspace((int) ((unsigned char) *xml) != 0))) *(xml++)=' '; else xml++; } if (state == '*') { /* Normalize spaces for non-CDATA attributes. */ for (xml=p; *xml != '\0'; xml++) { char accept[] = " "; i=(ssize_t) strspn(xml,accept); if (i != 0) (void) memmove(xml,xml+i,strlen(xml+i)+1); while ((*xml != '\0') && (*xml != ' ')) xml++; if (*xml == '\0') break; } xml--; if ((xml >= p) && (*xml == ' ')) *xml='\0'; } return(p == q ? ConstantString(p) : p); } static void ParseCharacterContent(XMLTreeRoot *root,char *xml, const size_t length,const char state) { XMLTreeInfo *xml_info; xml_info=root->node; if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) || (length == 0)) return; xml[length]='\0'; xml=ParseEntities(xml,root->entities,state); if ((xml_info->content != (char *) NULL) && (*xml_info->content != '\0')) { (void) ConcatenateString(&xml_info->content,xml); xml=DestroyString(xml); } else { if (xml_info->content != (char *) NULL) xml_info->content=DestroyString(xml_info->content); xml_info->content=xml; } } static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag, ExceptionInfo *exception) { if ((root->node == (XMLTreeInfo *) NULL) || (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0)) { (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, "ParseError","unexpected closing tag ",tag); return(&root->root); } root->node=root->node->parent; return((XMLTreeInfo *) NULL); } static MagickBooleanType ValidateEntities(char *tag,char *xml, const size_t depth,char **entities) { ssize_t i; /* Check for circular entity references. */ if (depth > MagickMaxRecursionDepth) return(MagickFalse); for ( ; ; xml++) { while ((*xml != '\0') && (*xml != '&')) xml++; if (*xml == '\0') return(MagickTrue); if (strncmp(xml+1,tag,strlen(tag)) == 0) return(MagickFalse); i=0; while ((entities[i] != (char *) NULL) && (strncmp(entities[i],xml+1,strlen(entities[i])) == 0)) i+=2; if ((entities[i] != (char *) NULL) && (ValidateEntities(tag,entities[i+1],depth+1,entities) == 0)) return(MagickFalse); } } static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml, size_t length) { char *target; ssize_t i; ssize_t j; target=xml; xml[length]='\0'; xml+=strcspn(xml,XMLWhitespace); if (*xml != '\0') { *xml='\0'; xml+=strspn(xml+1,XMLWhitespace)+1; } if (strcmp(target,"xml") == 0) { xml=strstr(xml,"standalone"); if ((xml != (char *) NULL) && (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0)) root->standalone=MagickTrue; return; } if (root->processing_instructions[0] == (char **) NULL) { root->processing_instructions=(char ***) AcquireCriticalMemory(sizeof( *root->processing_instructions)); *root->processing_instructions=(char **) NULL; } i=0; while ((root->processing_instructions[i] != (char **) NULL) && (strcmp(target,root->processing_instructions[i][0]) != 0)) i++; if (root->processing_instructions[i] == (char **) NULL) { root->processing_instructions=(char ***) ResizeQuantumMemory( root->processing_instructions,(size_t) (i+2), sizeof(*root->processing_instructions)); if (root->processing_instructions == (char ***) NULL) ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); root->processing_instructions[i]=(char **) AcquireQuantumMemory(3, sizeof(**root->processing_instructions)); if (root->processing_instructions[i] == (char **) NULL) ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); root->processing_instructions[i+1]=(char **) NULL; root->processing_instructions[i][0]=ConstantString(target); root->processing_instructions[i][1]=(char *) root->processing_instructions[i+1]; root->processing_instructions[i+1]=(char **) NULL; root->processing_instructions[i][2]=ConstantString(""); } j=1; while (root->processing_instructions[i][j] != (char *) NULL) j++; root->processing_instructions[i]=(char **) ResizeQuantumMemory( root->processing_instructions[i],(size_t) (j+3), sizeof(**root->processing_instructions)); if (root->processing_instructions[i] == (char **) NULL) ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory( root->processing_instructions[i][j+1],(size_t) (j+1), sizeof(***root->processing_instructions)); if (root->processing_instructions[i][j+2] == (char *) NULL) ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1, root->root.tag != (char *) NULL ? ">" : "<",2); root->processing_instructions[i][j]=ConstantString(xml); root->processing_instructions[i][j+1]=(char *) NULL; } static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml, size_t length,ExceptionInfo *exception) { char *c, **entities, *n, **predefined_entitites, q, *t, *v; ssize_t i; ssize_t j; n=(char *) NULL; predefined_entitites=(char **) AcquireMagickMemory(sizeof(sentinel)); if (predefined_entitites == (char **) NULL) ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed"); (void) memcpy(predefined_entitites,sentinel,sizeof(sentinel)); for (xml[length]='\0'; xml != (char *) NULL; ) { while ((*xml != '\0') && (*xml != '<') && (*xml != '%')) xml++; if (*xml == '\0') break; if ((strlen(xml) > 9) && (strncmp(xml,"'); continue; } entities=(*c == '%') ? predefined_entitites : root->entities; for (i=0; entities[i] != (char *) NULL; i++) ; entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3), sizeof(*entities)); if (entities == (char **) NULL) ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); if (*c == '%') predefined_entitites=entities; else root->entities=entities; xml++; *xml='\0'; xml=strchr(v,q); if (xml != (char *) NULL) { *xml='\0'; xml++; } entities[i+1]=ParseEntities(v,predefined_entitites,'%'); entities[i+2]=(char *) NULL; if (ValidateEntities(n,entities[i+1],0,entities) != MagickFalse) entities[i]=n; else { if (entities[i+1] != v) entities[i+1]=DestroyString(entities[i+1]); (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning,"ParseError","circular entity declaration &%s",n); predefined_entitites=(char **) RelinquishMagickMemory( predefined_entitites); return(MagickFalse); } } else if (strncmp(xml,""); if (*xml == '>') continue; *xml='\0'; i=0; while ((root->attributes[i] != (char **) NULL) && (n != (char *) NULL) && (strcmp(n,root->attributes[i][0]) != 0)) i++; while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') && (*n != '>')) { xml=n+strcspn(n,XMLWhitespace); if (*xml != '\0') *xml='\0'; else { (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning,"ParseError","malformed ")-1; if (*c == ' ') continue; v=(char *) NULL; } else if (((*xml == '"') || (*xml == '\'')) && ((xml=strchr(v=xml+1,*xml)) != (char *) NULL)) *xml='\0'; else { (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning,"ParseError","malformed attributes[i] == (char **) NULL) { /* New attribute tag. */ if (i == 0) root->attributes=(char ***) AcquireQuantumMemory(2, sizeof(*root->attributes)); else root->attributes=(char ***) ResizeQuantumMemory( root->attributes,(size_t) (i+2), sizeof(*root->attributes)); if (root->attributes == (char ***) NULL) ThrowFatalException(ResourceLimitFatalError, "MemoryAllocationFailed"); root->attributes[i]=(char **) AcquireQuantumMemory(2, sizeof(**root->attributes)); if (root->attributes[i] == (char **) NULL) ThrowFatalException(ResourceLimitFatalError, "MemoryAllocationFailed"); root->attributes[i][0]=ConstantString(t); root->attributes[i][1]=(char *) NULL; root->attributes[i+1]=(char **) NULL; } for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ; root->attributes[i]=(char **) ResizeQuantumMemory( root->attributes[i],(size_t) (j+4),sizeof(**root->attributes)); if (root->attributes[i] == (char **) NULL) ThrowFatalException(ResourceLimitFatalError, "MemoryAllocationFailed"); root->attributes[i][j+3]=(char *) NULL; root->attributes[i][j+2]=ConstantString(c); root->attributes[i][j+1]=(char *) NULL; if (v != (char *) NULL) root->attributes[i][j+1]=ParseEntities(v,root->entities,*c); root->attributes[i][j]=ConstantString(n); } } else if (strncmp(xml, ""); else if (strncmp(xml,""); if (xml != (char *) NULL) { ParseProcessingInstructions(root,c,(size_t) (xml-c)); xml++; } } else if (*xml == '<') xml=strchr(xml,'>'); else if ((*(xml++) == '%') && (root->standalone == MagickFalse)) break; } predefined_entitites=(char **) RelinquishMagickMemory(predefined_entitites); return(MagickTrue); } static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes) { XMLTreeInfo *xml_info; xml_info=root->node; if (xml_info->tag == (char *) NULL) xml_info->tag=ConstantString(tag); else xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content)); if (xml_info != (XMLTreeInfo *) NULL) xml_info->attributes=attributes; root->node=xml_info; } static const char *ignore_tags[3] = { "rdf:Bag", "rdf:Seq", (const char *) NULL }; static inline MagickBooleanType IsSkipTag(const char *tag) { ssize_t i; i=0; while (ignore_tags[i] != (const char *) NULL) { if (LocaleCompare(tag,ignore_tags[i]) == 0) return(MagickTrue); i++; } return(MagickFalse); } MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception) { char **attribute, **attributes, *tag, *utf8; int c, terminal; MagickBooleanType status; char *p; ssize_t i; size_t ignore_depth, length; ssize_t j, l; XMLTreeRoot *root; /* Convert xml-string to UTF8. */ if ((xml == (const char *) NULL) || (strlen(xml) == 0)) { (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, "ParseError","root tag missing"); return((XMLTreeInfo *) NULL); } root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL); length=strlen(xml); utf8=ConvertUTF16ToUTF8(xml,&length); if (utf8 == (char *) NULL) { (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, "ParseError","UTF16 to UTF8 failed"); return((XMLTreeInfo *) NULL); } terminal=utf8[length-1]; utf8[length-1]='\0'; p=utf8; while ((*p != '\0') && (*p != '<')) p++; if (*p == '\0') { (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, "ParseError","root tag missing"); utf8=DestroyString(utf8); return((XMLTreeInfo *) NULL); } attribute=(char **) NULL; l=0; ignore_depth=0; for (p++; ; p++) { attributes=(char **) sentinel; tag=p; c=(*p); if ((isalpha((int) ((unsigned char) *p)) != 0) || (*p == '_') || (*p == ':') || (c < '\0')) { /* Tag. */ if (root->node == (XMLTreeInfo *) NULL) { (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning,"ParseError","root tag missing"); utf8=DestroyString(utf8); return(&root->root); } p+=strcspn(p,XMLWhitespace "/>"); while (isspace((int) ((unsigned char) *p)) != 0) *p++='\0'; if (((isalpha((int) ((unsigned char) *p)) != 0) || (*p == '_')) && (ignore_depth == 0)) { if ((*p != '\0') && (*p != '/') && (*p != '>')) { /* Find tag in default attributes list. */ i=0; while ((root->attributes[i] != (char **) NULL) && (strcmp(root->attributes[i][0],tag) != 0)) i++; attribute=root->attributes[i]; } for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2) { /* Attribute. */ if (l == 0) attributes=(char **) AcquireQuantumMemory(4, sizeof(*attributes)); else attributes=(char **) ResizeQuantumMemory(attributes,(size_t) (l+4),sizeof(*attributes)); if (attributes == (char **) NULL) { (void) ThrowMagickException(exception,GetMagickModule(), ResourceLimitError,"MemoryAllocationFailed","`%s'",""); utf8=DestroyString(utf8); return(&root->root); } attributes[l+2]=(char *) NULL; attributes[l+1]=(char *) NULL; attributes[l]=p; p+=strcspn(p,XMLWhitespace "=/>"); if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0)) attributes[l]=ConstantString(""); else { *p++='\0'; p+=strspn(p,XMLWhitespace "="); c=(*p); if ((c == '"') || (c == '\'')) { /* Attributes value. */ p++; attributes[l+1]=p; while ((*p != '\0') && (*p != c)) p++; if (*p != '\0') *p++='\0'; else { attributes[l]=ConstantString(""); attributes[l+1]=ConstantString(""); (void) DestroyXMLTreeAttributes(attributes); (void) ThrowMagickException(exception, GetMagickModule(),OptionWarning,"ParseError", "missing %c",c); utf8=DestroyString(utf8); return(&root->root); } j=1; while ((attribute != (char **) NULL) && (attribute[j] != (char *) NULL) && (strcmp(attribute[j],attributes[l]) != 0)) j+=3; attributes[l+1]=ParseEntities(attributes[l+1], root->entities,(attribute != (char **) NULL) && (attribute[j] != (char *) NULL) ? *attribute[j+2] : ' '); } attributes[l]=ConstantString(attributes[l]); } while (isspace((int) ((unsigned char) *p)) != 0) p++; } } else { while((*p != '\0') && (*p != '/') && (*p != '>')) p++; } if (*p == '/') { /* Self closing tag. */ *p++='\0'; if (((*p != '\0') && (*p != '>')) || ((*p == '\0') && (terminal != '>'))) { if (l != 0) (void) DestroyXMLTreeAttributes(attributes); (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning,"ParseError","missing >"); utf8=DestroyString(utf8); return(&root->root); } if ((ignore_depth != 0) || (IsSkipTag(tag) != MagickFalse)) (void) DestroyXMLTreeAttributes(attributes); else { ParseOpenTag(root,tag,attributes); (void) ParseCloseTag(root,tag,exception); } } else { c=(*p); if ((*p == '>') || ((*p == '\0') && (terminal == '>'))) { *p='\0'; if ((ignore_depth == 0) && (IsSkipTag(tag) == MagickFalse)) ParseOpenTag(root,tag,attributes); else { ignore_depth++; (void) DestroyXMLTreeAttributes(attributes); } *p=c; } else { if (l != 0) (void) DestroyXMLTreeAttributes(attributes); (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning,"ParseError","missing >"); utf8=DestroyString(utf8); return(&root->root); } } } else if (*p == '/') { /* Close tag. */ tag=p+1; p+=strcspn(tag,XMLWhitespace ">")+1; c=(*p); if ((c == '\0') && (terminal != '>')) { (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning,"ParseError","missing >"); utf8=DestroyString(utf8); return(&root->root); } *p='\0'; if ((ignore_depth == 0) && (ParseCloseTag(root,tag,exception) != (XMLTreeInfo *) NULL)) { utf8=DestroyString(utf8); return(&root->root); } if (ignore_depth > 0) ignore_depth--; *p=c; if (isspace((int) ((unsigned char) *p)) != 0) p+=strspn(p,XMLWhitespace); } else if (strncmp(p,"!--",3) == 0) { /* Comment. */ p=strstr(p+3,"--"); if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) || ((*p == '\0') && (terminal != '>'))) { (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning,"ParseError","unclosed