2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12 % TTTTT RRRR EEEEE EEEEE %
26 % Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
27 % dedicated to making software imaging solutions freely available. %
29 % You may not use this file except in compliance with the License. You may %
30 % obtain a copy of the License at %
32 % http://www.imagemagick.org/script/license.php %
34 % Unless required by applicable law or agreed to in writing, software %
35 % distributed under the License is distributed on an "AS IS" BASIS, %
36 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
37 % See the License for the specific language governing permissions and %
38 % limitations under the License. %
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 % This module implements the standard handy xml-tree methods for storing and
43 % retrieving nodes and attributes from an XML string.
50 #include "MagickCore/studio.h"
51 #include "MagickCore/blob.h"
52 #include "MagickCore/blob-private.h"
53 #include "MagickCore/exception.h"
54 #include "MagickCore/exception-private.h"
55 #include "MagickCore/log.h"
56 #include "MagickCore/memory_.h"
57 #include "MagickCore/semaphore.h"
58 #include "MagickCore/string_.h"
59 #include "MagickCore/string-private.h"
60 #include "MagickCore/token-private.h"
61 #include "MagickCore/xml-tree.h"
62 #include "MagickCore/xml-tree-private.h"
63 #include "MagickCore/utility.h"
64 #include "MagickCore/utility-private.h"
69 #define NumberPredefinedEntities 10
70 #define XMLWhitespace "\t\r\n "
102 typedef struct _XMLTreeRoot
117 ***processing_instructions,
135 *sentinel[] = { (char *) NULL };
138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142 % A d d C h i l d T o X M L T r e e %
146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 % AddChildToXMLTree() adds a child tag at an offset relative to the start of
149 % the parent tag's character content. Return the child tag.
151 % The format of the AddChildToXMLTree method is:
153 % XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,const char *tag,
154 % const size_t offset)
156 % A description of each parameter follows:
158 % o xml_info: the xml info.
162 % o offset: the tag offset.
165 MagickExport XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,
166 const char *tag,const size_t offset)
171 if (xml_info == (XMLTreeInfo *) NULL)
172 return((XMLTreeInfo *) NULL);
173 child=(XMLTreeInfo *) AcquireMagickMemory(sizeof(*child));
174 if (child == (XMLTreeInfo *) NULL)
175 return((XMLTreeInfo *) NULL);
176 (void) ResetMagickMemory(child,0,sizeof(*child));
177 child->tag=ConstantString(tag);
178 child->attributes=sentinel;
179 child->content=ConstantString("");
180 child->debug=IsEventLogging();
181 child->signature=MagickSignature;
182 return(InsertTagIntoXMLTree(xml_info,child,offset));
186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190 % A d d P a t h T o X M L T r e e %
194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196 % AddPathToXMLTree() adds a child tag at an offset relative to the start of
197 % the parent tag's character content. This method returns the child tag.
199 % The format of the AddPathToXMLTree method is:
201 % XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,const char *path,
202 % const size_t offset)
204 % A description of each parameter follows:
206 % o xml_info: the xml info.
210 % o offset: the tag offset.
213 MagickPrivate XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,
214 const char *path,const size_t offset)
218 subnode[MaxTextExtent],
234 assert(xml_info != (XMLTreeInfo *) NULL);
235 assert((xml_info->signature == MagickSignature) ||
236 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
237 if (xml_info->debug != MagickFalse)
238 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
240 components=GetPathComponents(path,&number_components);
241 if (components == (char **) NULL)
242 return((XMLTreeInfo *) NULL);
243 for (i=0; i < (ssize_t) number_components; i++)
245 GetPathComponent(components[i],SubimagePath,subnode);
246 GetPathComponent(components[i],CanonicalPath,tag);
247 child=GetXMLTreeChild(node,tag);
248 if (child == (XMLTreeInfo *) NULL)
249 child=AddChildToXMLTree(node,tag,offset);
251 if (node == (XMLTreeInfo *) NULL)
253 for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
255 node=GetXMLTreeOrdered(node);
256 if (node == (XMLTreeInfo *) NULL)
259 if (node == (XMLTreeInfo *) NULL)
261 components[i]=DestroyString(components[i]);
263 for ( ; i < (ssize_t) number_components; i++)
264 components[i]=DestroyString(components[i]);
265 components=(char **) RelinquishMagickMemory(components);
270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
274 % C a n o n i c a l X M L C o n t e n t %
278 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
280 % CanonicalXMLContent() converts text to canonical XML content by converting
281 % to UTF-8, substituting predefined entities, wrapping as CDATA, or encoding
282 % as base-64 as required.
284 % The format of the CanonicalXMLContent method is:
287 % char *CanonicalXMLContent(const char *content,
288 % const MagickBooleanType pedantic)
290 % A description of each parameter follows:
292 % o content: the content.
294 % o pedantic: if true, replace newlines and tabs with their respective
298 MagickPrivate char *CanonicalXMLContent(const char *content,
299 const MagickBooleanType pedantic)
305 register const unsigned char
318 utf8=ConvertLatin1ToUTF8((const unsigned char *) content);
319 if (utf8 == (unsigned char *) NULL)
320 return((char *) NULL);
321 for (p=utf8; *p != '\0'; p++)
322 if ((*p < 0x20) && (*p != 0x09) && (*p != 0x0a) && (*p != 0x0d))
327 String is binary, base64-encode it.
329 base64=Base64Encode(utf8,strlen((char *) utf8),&length);
330 utf8=(unsigned char *) RelinquishMagickMemory(utf8);
331 if (base64 == (char *) NULL)
332 return((char *) NULL);
333 canonical_content=AcquireString("<base64>");
334 (void) ConcatenateString(&canonical_content,base64);
335 base64=DestroyString(base64);
336 (void) ConcatenateString(&canonical_content,"</base64>");
337 return(canonical_content);
340 Substitute predefined entities.
343 canonical_content=AcquireString((char *) NULL);
344 extent=MaxTextExtent;
345 for (p=utf8; *p != '\0'; p++)
347 if ((i+MaxTextExtent) > (ssize_t) extent)
349 extent+=MaxTextExtent;
350 canonical_content=(char *) ResizeQuantumMemory(canonical_content,extent,
351 sizeof(*canonical_content));
352 if (canonical_content == (char *) NULL)
353 return(canonical_content);
359 i+=FormatLocaleString(canonical_content+i,extent,"&");
364 i+=FormatLocaleString(canonical_content+i,extent,"<");
369 i+=FormatLocaleString(canonical_content+i,extent,">");
374 i+=FormatLocaleString(canonical_content+i,extent,""");
379 if (pedantic == MagickFalse)
381 canonical_content[i++]=(char) (*p);
384 i+=FormatLocaleString(canonical_content+i,extent,"
");
389 if (pedantic == MagickFalse)
391 canonical_content[i++]=(char) (*p);
394 i+=FormatLocaleString(canonical_content+i,extent,"	");
399 i+=FormatLocaleString(canonical_content+i,extent,"
");
404 canonical_content[i++]=(char) (*p);
409 canonical_content[i]='\0';
410 utf8=(unsigned char *) RelinquishMagickMemory(utf8);
411 return(canonical_content);
415 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
419 % D e s t r o y X M L T r e e %
423 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
425 % DestroyXMLTree() destroys the xml-tree.
427 % The format of the DestroyXMLTree method is:
429 % XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
431 % A description of each parameter follows:
433 % o xml_info: the xml info.
437 static char **DestroyXMLTreeAttributes(char **attributes)
443 Destroy a tag attribute list.
445 if ((attributes == (char **) NULL) || (attributes == sentinel))
446 return((char **) NULL);
447 for (i=0; attributes[i] != (char *) NULL; i+=2)
450 Destroy attribute tag and value.
452 if (attributes[i] != (char *) NULL)
453 attributes[i]=DestroyString(attributes[i]);
454 if (attributes[i+1] != (char *) NULL)
455 attributes[i+1]=DestroyString(attributes[i+1]);
457 attributes=(char **) RelinquishMagickMemory(attributes);
458 return((char **) NULL);
461 static void DestroyXMLTreeChild(XMLTreeInfo *xml_info)
467 node=xml_info->child;
468 while(node != (XMLTreeInfo *) NULL)
470 prev=(XMLTreeInfo *) NULL;
471 while(node->child != (XMLTreeInfo *) NULL)
476 (void) DestroyXMLTree(node);
477 if (prev != (XMLTreeInfo* ) NULL)
478 prev->child=(XMLTreeInfo *) NULL;
481 xml_info->child=(XMLTreeInfo *) NULL;
484 static void DestroyXMLTreeOrdered(XMLTreeInfo *xml_info)
490 node=xml_info->ordered;
491 while(node != (XMLTreeInfo *) NULL)
493 prev=(XMLTreeInfo *) NULL;
494 while(node->ordered != (XMLTreeInfo *) NULL)
499 (void) DestroyXMLTree(node);
500 if (prev != (XMLTreeInfo* ) NULL)
501 prev->ordered=(XMLTreeInfo *) NULL;
504 xml_info->ordered=(XMLTreeInfo *) NULL;
507 static void DestroyXMLTreeRoot(XMLTreeInfo *xml_info)
521 assert(xml_info != (XMLTreeInfo *) NULL);
522 assert((xml_info->signature == MagickSignature) ||
523 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
524 if (xml_info->debug != MagickFalse)
525 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
526 if (xml_info->parent != (XMLTreeInfo *) NULL)
529 Free root tag allocations.
531 root=(XMLTreeRoot *) xml_info;
532 for (i=NumberPredefinedEntities; root->entities[i] != (char *) NULL; i+=2)
533 root->entities[i+1]=DestroyString(root->entities[i+1]);
534 root->entities=(char **) RelinquishMagickMemory(root->entities);
535 for (i=0; root->attributes[i] != (char **) NULL; i++)
537 attributes=root->attributes[i];
538 if (attributes[0] != (char *) NULL)
539 attributes[0]=DestroyString(attributes[0]);
540 for (j=1; attributes[j] != (char *) NULL; j+=3)
542 if (attributes[j] != (char *) NULL)
543 attributes[j]=DestroyString(attributes[j]);
544 if (attributes[j+1] != (char *) NULL)
545 attributes[j+1]=DestroyString(attributes[j+1]);
546 if (attributes[j+2] != (char *) NULL)
547 attributes[j+2]=DestroyString(attributes[j+2]);
549 attributes=(char **) RelinquishMagickMemory(attributes);
551 if (root->attributes[0] != (char **) NULL)
552 root->attributes=(char ***) RelinquishMagickMemory(root->attributes);
553 if (root->processing_instructions[0] != (char **) NULL)
555 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
557 for (j=0; root->processing_instructions[i][j] != (char *) NULL; j++)
558 root->processing_instructions[i][j]=DestroyString(
559 root->processing_instructions[i][j]);
560 root->processing_instructions[i][j+1]=DestroyString(
561 root->processing_instructions[i][j+1]);
562 root->processing_instructions[i]=(char **) RelinquishMagickMemory(
563 root->processing_instructions[i]);
565 root->processing_instructions=(char ***) RelinquishMagickMemory(
566 root->processing_instructions);
570 MagickExport XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
572 assert(xml_info != (XMLTreeInfo *) NULL);
573 assert((xml_info->signature == MagickSignature) ||
574 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
575 if (xml_info->debug != MagickFalse)
576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
577 DestroyXMLTreeChild(xml_info);
578 DestroyXMLTreeOrdered(xml_info);
579 DestroyXMLTreeRoot(xml_info);
580 xml_info->attributes=DestroyXMLTreeAttributes(xml_info->attributes);
581 xml_info->content=DestroyString(xml_info->content);
582 xml_info->tag=DestroyString(xml_info->tag);
583 xml_info=(XMLTreeInfo *) RelinquishMagickMemory(xml_info);
584 return((XMLTreeInfo *) NULL);
588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
592 % F i l e T o X M L %
596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
598 % FileToXML() returns the contents of a file as a XML string.
600 % The format of the FileToXML method is:
602 % char *FileToXML(const char *filename,const size_t extent)
604 % A description of each parameter follows:
606 % o filename: the filename.
608 % o extent: Maximum length of the string.
612 static inline MagickSizeType MagickMin(const MagickSizeType x,
613 const MagickSizeType y)
620 MagickPrivate char *FileToXML(const char *filename,const size_t extent)
643 assert(filename != (const char *) NULL);
646 if (LocaleCompare(filename,"-") != 0)
647 file=open_utf8(filename,O_RDONLY | O_BINARY,0);
649 return((char *) NULL);
650 offset=(MagickOffsetType) lseek(file,0,SEEK_END);
652 if ((file == fileno(stdin)) || (offset < 0) ||
653 (offset != (MagickOffsetType) ((ssize_t) offset)))
662 Stream is not seekable.
664 offset=(MagickOffsetType) lseek(file,0,SEEK_SET);
665 quantum=(size_t) MagickMaxBufferExtent;
666 if ((fstat(file,&file_stats) == 0) && (file_stats.st_size != 0))
667 quantum=(size_t) MagickMin((MagickSizeType) file_stats.st_size,
668 MagickMaxBufferExtent);
669 xml=(char *) AcquireQuantumMemory(quantum,sizeof(*xml));
670 for (i=0; xml != (char *) NULL; i+=count)
672 count=read(file,xml+i,quantum);
679 if (~((size_t) i) < (quantum+1))
681 xml=(char *) RelinquishMagickMemory(xml);
684 xml=(char *) ResizeQuantumMemory(xml,i+quantum+1,sizeof(*xml));
685 if ((size_t) (i+count) >= extent)
688 if (LocaleCompare(filename,"-") != 0)
690 if (xml == (char *) NULL)
691 return((char *) NULL);
694 xml=(char *) RelinquishMagickMemory(xml);
695 return((char *) NULL);
697 length=(size_t) MagickMin(i+count,extent);
701 length=(size_t) MagickMin((MagickSizeType) offset,extent);
703 if (~length >= (MaxTextExtent-1))
704 xml=(char *) AcquireQuantumMemory(length+MaxTextExtent,sizeof(*xml));
705 if (xml == (char *) NULL)
708 return((char *) NULL);
710 map=MapBlob(file,ReadMode,0,length);
711 if (map != (char *) NULL)
713 (void) memcpy(xml,map,length);
714 (void) UnmapBlob(map,length);
718 (void) lseek(file,0,SEEK_SET);
719 for (i=0; i < length; i+=count)
721 count=read(file,xml+i,(size_t) MagickMin(length-i,(MagickSizeType)
733 xml=(char *) RelinquishMagickMemory(xml);
734 return((char *) NULL);
738 if (LocaleCompare(filename,"-") != 0)
741 xml=(char *) RelinquishMagickMemory(xml);
746 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
750 % G e t N e x t X M L T r e e T a g %
754 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756 % GetNextXMLTreeTag() returns the next tag or NULL if not found.
758 % The format of the GetNextXMLTreeTag method is:
760 % XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
762 % A description of each parameter follows:
764 % o xml_info: the xml info.
767 MagickExport XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
769 assert(xml_info != (XMLTreeInfo *) NULL);
770 assert((xml_info->signature == MagickSignature) ||
771 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
772 if (xml_info->debug != MagickFalse)
773 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
774 return(xml_info->next);
778 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
782 % G e t X M L T r e e A t t r i b u t e %
786 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788 % GetXMLTreeAttribute() returns the value of the attribute tag with the
789 % specified tag if found, otherwise NULL.
791 % The format of the GetXMLTreeAttribute method is:
793 % const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag)
795 % A description of each parameter follows:
797 % o xml_info: the xml info.
799 % o tag: the attribute tag.
802 MagickExport const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,
814 assert(xml_info != (XMLTreeInfo *) NULL);
815 assert((xml_info->signature == MagickSignature) ||
816 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
817 if (xml_info->debug != MagickFalse)
818 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
819 if (xml_info->attributes == (char **) NULL)
820 return((const char *) NULL);
822 while ((xml_info->attributes[i] != (char *) NULL) &&
823 (strcmp(xml_info->attributes[i],tag) != 0))
825 if (xml_info->attributes[i] != (char *) NULL)
826 return(xml_info->attributes[i+1]);
827 root=(XMLTreeRoot*) xml_info;
828 while (root->root.parent != (XMLTreeInfo *) NULL)
829 root=(XMLTreeRoot *) root->root.parent;
831 while ((root->attributes[i] != (char **) NULL) &&
832 (strcmp(root->attributes[i][0],xml_info->tag) != 0))
834 if (root->attributes[i] == (char **) NULL)
835 return((const char *) NULL);
837 while ((root->attributes[i][j] != (char *) NULL) &&
838 (strcmp(root->attributes[i][j],tag) != 0))
840 if (root->attributes[i][j] == (char *) NULL)
841 return((const char *) NULL);
842 return(root->attributes[i][j+1]);
846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
850 % G e t X M L T r e e A t t r i b u t e s %
854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
856 % GetXMLTreeAttributes() injects all attributes associated with the current
857 % tag in the specified splay-tree.
859 % The format of the GetXMLTreeAttributes method is:
861 % MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
862 % SplayTreeInfo *attributes)
864 % A description of each parameter follows:
866 % o xml_info: the xml info.
868 % o attributes: the attribute splay-tree.
871 MagickPrivate MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
872 SplayTreeInfo *attributes)
877 assert(xml_info != (XMLTreeInfo *) NULL);
878 assert((xml_info->signature == MagickSignature) ||
879 (((const XMLTreeRoot *) xml_info)->signature == MagickSignature));
880 if (xml_info->debug != MagickFalse)
881 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
882 assert(attributes != (SplayTreeInfo *) NULL);
883 if (xml_info->attributes == (char **) NULL)
886 while (xml_info->attributes[i] != (char *) NULL)
888 (void) AddValueToSplayTree(attributes,
889 ConstantString(xml_info->attributes[i]),
890 ConstantString(xml_info->attributes[i+1]));
897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
901 % G e t X M L T r e e C h i l d %
905 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
907 % GetXMLTreeChild() returns the first child tag with the specified tag if
908 % found, otherwise NULL.
910 % The format of the GetXMLTreeChild method is:
912 % XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
914 % A description of each parameter follows:
916 % o xml_info: the xml info.
919 MagickExport XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
924 assert(xml_info != (XMLTreeInfo *) NULL);
925 assert((xml_info->signature == MagickSignature) ||
926 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
927 if (xml_info->debug != MagickFalse)
928 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
929 child=xml_info->child;
930 if (tag != (const char *) NULL)
931 while ((child != (XMLTreeInfo *) NULL) && (strcmp(child->tag,tag) != 0))
932 child=child->sibling;
937 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
941 % G e t X M L T r e e C o n t e n t %
945 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
947 % GetXMLTreeContent() returns any content associated with specified
950 % The format of the GetXMLTreeContent method is:
952 % const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
954 % A description of each parameter follows:
956 % o xml_info: the xml info.
959 MagickExport const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
961 assert(xml_info != (XMLTreeInfo *) NULL);
962 assert((xml_info->signature == MagickSignature) ||
963 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
964 if (xml_info->debug != MagickFalse)
965 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
966 return(xml_info->content);
970 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
974 % G e t X M L T r e e O r d e r e d %
978 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
980 % GetXMLTreeOrdered() returns the next ordered node if found, otherwise NULL.
982 % The format of the GetXMLTreeOrdered method is:
984 % XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
986 % A description of each parameter follows:
988 % o xml_info: the xml info.
991 MagickPrivate XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
993 assert(xml_info != (XMLTreeInfo *) NULL);
994 assert((xml_info->signature == MagickSignature) ||
995 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
996 if (xml_info->debug != MagickFalse)
997 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
998 return(xml_info->ordered);
1002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1006 % G e t X M L T r e e P a t h %
1010 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1012 % GetXMLTreePath() traverses the XML-tree as defined by the specified path
1013 % and returns the node if found, otherwise NULL.
1015 % The format of the GetXMLTreePath method is:
1017 % XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
1019 % A description of each parameter follows:
1021 % o xml_info: the xml info.
1023 % o path: the path (e.g. property/elapsed-time).
1026 MagickPrivate XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
1030 subnode[MaxTextExtent],
1045 assert(xml_info != (XMLTreeInfo *) NULL);
1046 assert((xml_info->signature == MagickSignature) ||
1047 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
1048 if (xml_info->debug != MagickFalse)
1049 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1051 components=GetPathComponents(path,&number_components);
1052 if (components == (char **) NULL)
1053 return((XMLTreeInfo *) NULL);
1054 for (i=0; i < (ssize_t) number_components; i++)
1056 GetPathComponent(components[i],SubimagePath,subnode);
1057 GetPathComponent(components[i],CanonicalPath,tag);
1058 node=GetXMLTreeChild(node,tag);
1059 if (node == (XMLTreeInfo *) NULL)
1061 for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
1063 node=GetXMLTreeOrdered(node);
1064 if (node == (XMLTreeInfo *) NULL)
1067 if (node == (XMLTreeInfo *) NULL)
1069 components[i]=DestroyString(components[i]);
1071 for ( ; i < (ssize_t) number_components; i++)
1072 components[i]=DestroyString(components[i]);
1073 components=(char **) RelinquishMagickMemory(components);
1078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1082 % 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 %
1086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1088 % GetXMLTreeProcessingInstructions() returns a null terminated array of
1089 % processing instructions for the given target.
1091 % The format of the GetXMLTreeProcessingInstructions method is:
1093 % const char **GetXMLTreeProcessingInstructions(XMLTreeInfo *xml_info,
1094 % const char *target)
1096 % A description of each parameter follows:
1098 % o xml_info: the xml info.
1101 MagickPrivate const char **GetXMLTreeProcessingInstructions(
1102 XMLTreeInfo *xml_info,const char *target)
1110 assert(xml_info != (XMLTreeInfo *) NULL);
1111 assert((xml_info->signature == MagickSignature) ||
1112 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
1113 if (xml_info->debug != MagickFalse)
1114 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1115 root=(XMLTreeRoot *) xml_info;
1116 while (root->root.parent != (XMLTreeInfo *) NULL)
1117 root=(XMLTreeRoot *) root->root.parent;
1119 while ((root->processing_instructions[i] != (char **) NULL) &&
1120 (strcmp(root->processing_instructions[i][0],target) != 0))
1122 if (root->processing_instructions[i] == (char **) NULL)
1123 return((const char **) sentinel);
1124 return((const char **) (root->processing_instructions[i]+1));
1128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1132 % G e t X M L T r e e S i b l i n g %
1136 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1138 % GetXMLTreeSibling() returns the node sibling if found, otherwise NULL.
1140 % The format of the GetXMLTreeSibling method is:
1142 % XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
1144 % A description of each parameter follows:
1146 % o xml_info: the xml info.
1149 MagickExport XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
1151 assert(xml_info != (XMLTreeInfo *) NULL);
1152 assert((xml_info->signature == MagickSignature) ||
1153 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
1154 if (xml_info->debug != MagickFalse)
1155 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1156 return(xml_info->sibling);
1160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1164 % G e t X M L T r e e T a g %
1168 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1170 % GetXMLTreeTag() returns the tag associated with specified xml-tree node.
1172 % The format of the GetXMLTreeTag method is:
1174 % const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
1176 % A description of each parameter follows:
1178 % o xml_info: the xml info.
1181 MagickExport const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
1183 assert(xml_info != (XMLTreeInfo *) NULL);
1184 assert((xml_info->signature == MagickSignature) ||
1185 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
1186 if (xml_info->debug != MagickFalse)
1187 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1188 return(xml_info->tag);
1192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1196 % I n s e r t I n t o T a g X M L T r e e %
1200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1202 % InsertTagIntoXMLTree() inserts a tag at an offset relative to the start of
1203 % the parent tag's character content. This method returns the child tag.
1205 % The format of the InsertTagIntoXMLTree method is:
1207 % XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
1208 % XMLTreeInfo *child,const size_t offset)
1210 % A description of each parameter follows:
1212 % o xml_info: the xml info.
1214 % o child: the child tag.
1216 % o offset: the tag offset.
1219 MagickPrivate XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
1220 XMLTreeInfo *child,const size_t offset)
1227 child->ordered=(XMLTreeInfo *) NULL;
1228 child->sibling=(XMLTreeInfo *) NULL;
1229 child->next=(XMLTreeInfo *) NULL;
1230 child->offset=offset;
1231 child->parent=xml_info;
1232 if (xml_info->child == (XMLTreeInfo *) NULL)
1234 xml_info->child=child;
1237 head=xml_info->child;
1238 if (head->offset > offset)
1240 child->ordered=head;
1241 xml_info->child=child;
1246 while ((node->ordered != (XMLTreeInfo *) NULL) &&
1247 (node->ordered->offset <= offset))
1249 child->ordered=node->ordered;
1250 node->ordered=child;
1252 previous=(XMLTreeInfo *) NULL;
1254 while ((node != (XMLTreeInfo *) NULL) && (strcmp(node->tag,child->tag) != 0))
1259 if ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1261 while ((node->next != (XMLTreeInfo *) NULL) &&
1262 (node->next->offset <= offset))
1264 child->next=node->next;
1269 if ((previous != (XMLTreeInfo *) NULL) && (node != (XMLTreeInfo *) NULL))
1270 previous->sibling=node->sibling;
1272 previous=(XMLTreeInfo *) NULL;
1274 while ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1279 child->sibling=node;
1280 if (previous != (XMLTreeInfo *) NULL)
1281 previous->sibling=child;
1287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1291 % N e w X M L T r e e %
1295 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1297 % NewXMLTree() returns a XMLTreeInfo xml-tree as defined by the specified
1300 % The format of the NewXMLTree method is:
1302 % XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1304 % A description of each parameter follows:
1306 % o xml: The XML string.
1308 % o exception: return any errors or warnings in this structure.
1312 static char *ConvertUTF16ToUTF8(const char *content,size_t *length)
1332 utf8=(char *) AcquireQuantumMemory(*length,sizeof(*utf8));
1333 if (utf8 == (char *) NULL)
1334 return((char *) NULL);
1335 encoding=(*content == '\xFE') ? 1 : (*content == '\xFF') ? 0 : -1;
1341 (void) CopyMagickMemory(utf8,content,*length*sizeof(*utf8));
1346 for (i=2; i < (ssize_t) (*length-1); i+=2)
1348 c=(encoding != 0) ? ((content[i] & 0xff) << 8) | (content[i+1] & 0xff) :
1349 ((content[i+1] & 0xff) << 8) | (content[i] & 0xff);
1350 if ((c >= 0xd800) && (c <= 0xdfff) && ((i+=2) < (ssize_t) (*length-1)))
1352 byte=(encoding != 0) ? ((content[i] & 0xff) << 8) |
1353 (content[i+1] & 0xff) : ((content[i+1] & 0xff) << 8) |
1354 (content[i] & 0xff);
1355 c=(((c & 0x3ff) << 10) | (byte & 0x3ff))+0x10000;
1357 if ((size_t) (j+MaxTextExtent) > extent)
1359 extent=(size_t) j+MaxTextExtent;
1360 utf8=(char *) ResizeQuantumMemory(utf8,extent,sizeof(*utf8));
1361 if (utf8 == (char *) NULL)
1371 Multi-byte UTF-8 sequence.
1374 for (bits=0; byte != 0; byte/=2)
1377 utf8[j++]=(0xFF << (7-bits)) | (c >> (6*bits));
1381 utf8[j]=0x80 | ((c >> (6*bits)) & 0x3f);
1386 return((char *) ResizeQuantumMemory(utf8,*length,sizeof(*utf8)));
1389 static char *ParseEntities(char *xml,char **entities,int state)
1413 Normalize line endings.
1417 for ( ; *xml != '\0'; xml++)
1418 while (*xml == '\r')
1422 (void) CopyMagickMemory(xml,xml+1,strlen(xml));
1426 while ((*xml != '\0') && (*xml != '&') && ((*xml != '%') ||
1427 (state != '%')) && (isspace((int) ((unsigned char) *xml) == 0)))
1433 '&' for general entity decoding
1434 '%' for parameter entity decoding
1435 'c' for CDATA sections
1436 ' ' for attributes normalization
1437 '*' for non-CDATA attributes normalization
1439 if ((state != 'c') && (strncmp(xml,"&#",2) == 0))
1442 Character reference.
1445 c=strtol(xml+2,&entity,10); /* base 10 */
1447 c=strtol(xml+3,&entity,16); /* base 16 */
1448 if ((c == 0) || (*entity != ';'))
1451 Not a character reference.
1461 Multi-byte UTF-8 sequence.
1464 for (i=0; byte != 0; byte/=2)
1467 *xml=(char) ((0xFF << (7-i)) | (c >> (6*i)));
1472 *xml=(char) (0x80 | ((c >> (6*i)) & 0x3F));
1476 (void) CopyMagickMemory(xml,strchr(xml,';')+1,strlen(strchr(xml,';')));
1479 if (((*xml == '&') && ((state == '&') || (state == ' ') ||
1480 (state == '*'))) || ((state == '%') && (*xml == '%')))
1483 Find entity in the list.
1486 while ((entities[i] != (char *) NULL) &&
1487 (strncmp(xml+1,entities[i],strlen(entities[i])) != 0))
1489 if (entities[i++] == (char *) NULL)
1496 length=strlen(entities[i]);
1497 entity=strchr(xml,';');
1498 if ((length-1L) >= (size_t) (entity-xml))
1500 offset=(ssize_t) (xml-p);
1501 extent=(size_t) (offset+length+strlen(entity));
1503 p=(char *) ResizeQuantumMemory(p,extent,sizeof(*p));
1509 xml=(char *) AcquireQuantumMemory(extent,sizeof(*xml));
1510 if (xml != (char *) NULL)
1512 (void) CopyMagickString(xml,p,extent*sizeof(*xml));
1516 if (p == (char *) NULL)
1517 ThrowFatalException(ResourceLimitFatalError,
1518 "MemoryAllocationFailed");
1520 entity=strchr(xml,';');
1522 (void) CopyMagickMemory(xml+length,entity+1,strlen(entity));
1523 (void) strncpy(xml,entities[i],length);
1527 if (((state == ' ') || (state == '*')) &&
1528 (isspace((int) ((unsigned char) *xml) != 0)))
1536 Normalize spaces for non-CDATA attributes.
1538 for (xml=p; *xml != '\0'; xml++)
1540 i=(ssize_t) strspn(xml," ");
1542 (void) CopyMagickMemory(xml,xml+i,strlen(xml+i)+1);
1543 while ((*xml != '\0') && (*xml != ' '))
1547 if ((xml >= p) && (*xml == ' '))
1550 return(p == q ? ConstantString(p) : p);
1553 static void ParseCharacterContent(XMLTreeRoot *root,char *xml,
1554 const size_t length,const char state)
1559 xml_info=root->node;
1560 if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) ||
1564 xml=ParseEntities(xml,root->entities,state);
1565 if (*xml_info->content != '\0')
1567 (void) ConcatenateString(&xml_info->content,xml);
1568 xml=DestroyString(xml);
1572 if (xml_info->content != (char *) NULL)
1573 xml_info->content=DestroyString(xml_info->content);
1574 xml_info->content=xml;
1578 static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag,
1579 char *magick_unused(xml),ExceptionInfo *exception)
1581 if ((root->node == (XMLTreeInfo *) NULL) ||
1582 (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0))
1584 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1585 "ParseError","unexpected closing tag </%s>",tag);
1586 return(&root->root);
1588 root->node=root->node->parent;
1589 return((XMLTreeInfo *) NULL);
1592 static MagickBooleanType ValidateEntities(char *tag,char *xml,char **entities)
1598 Check for circular entity references.
1602 while ((*xml != '\0') && (*xml != '&'))
1606 if (strncmp(xml+1,tag,strlen(tag)) == 0)
1607 return(MagickFalse);
1609 while ((entities[i] != (char *) NULL) &&
1610 (strncmp(entities[i],xml+1,strlen(entities[i])) == 0))
1612 if ((entities[i] != (char *) NULL) &&
1613 (ValidateEntities(tag,entities[i+1],entities) == 0))
1614 return(MagickFalse);
1618 static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml,
1632 xml+=strcspn(xml,XMLWhitespace);
1636 xml+=strspn(xml+1,XMLWhitespace)+1;
1638 if (strcmp(target,"xml") == 0)
1640 xml=strstr(xml,"standalone");
1641 if ((xml != (char *) NULL) &&
1642 (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0))
1643 root->standalone=MagickTrue;
1646 if (root->processing_instructions[0] == (char **) NULL)
1648 root->processing_instructions=(char ***) AcquireMagickMemory(sizeof(
1649 *root->processing_instructions));
1650 if (root->processing_instructions ==(char ***) NULL)
1651 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1652 *root->processing_instructions=(char **) NULL;
1655 while ((root->processing_instructions[i] != (char **) NULL) &&
1656 (strcmp(target,root->processing_instructions[i][0]) != 0))
1658 if (root->processing_instructions[i] == (char **) NULL)
1660 root->processing_instructions=(char ***) ResizeQuantumMemory(
1661 root->processing_instructions,(size_t) (i+2),
1662 sizeof(*root->processing_instructions));
1663 if (root->processing_instructions == (char ***) NULL)
1664 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1665 root->processing_instructions[i]=(char **) AcquireQuantumMemory(3,
1666 sizeof(**root->processing_instructions));
1667 if (root->processing_instructions[i] == (char **) NULL)
1668 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1669 root->processing_instructions[i+1]=(char **) NULL;
1670 root->processing_instructions[i][0]=ConstantString(target);
1671 root->processing_instructions[i][1]=(char *)
1672 root->processing_instructions[i+1];
1673 root->processing_instructions[i+1]=(char **) NULL;
1674 root->processing_instructions[i][2]=ConstantString("");
1677 while (root->processing_instructions[i][j] != (char *) NULL)
1679 root->processing_instructions[i]=(char **) ResizeQuantumMemory(
1680 root->processing_instructions[i],(size_t) (j+3),
1681 sizeof(**root->processing_instructions));
1682 if (root->processing_instructions[i] == (char **) NULL)
1683 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1684 root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory(
1685 root->processing_instructions[i][j+1],(size_t) (j+1),
1686 sizeof(**root->processing_instructions));
1687 if (root->processing_instructions[i][j+2] == (char *) NULL)
1688 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1689 (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1,
1690 root->root.tag != (char *) NULL ? ">" : "<",2);
1691 root->processing_instructions[i][j]=ConstantString(xml);
1692 root->processing_instructions[i][j+1]=(char *) NULL;
1695 static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml,
1696 size_t length,ExceptionInfo *exception)
1702 **predefined_entitites,
1714 predefined_entitites=(char **) AcquireMagickMemory(sizeof(sentinel));
1715 if (predefined_entitites == (char **) NULL)
1716 ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed");
1717 (void) CopyMagickMemory(predefined_entitites,sentinel,sizeof(sentinel));
1718 for (xml[length]='\0'; xml != (char *) NULL; )
1720 while ((*xml != '\0') && (*xml != '<') && (*xml != '%'))
1724 if (strncmp(xml,"<!ENTITY",8) == 0)
1727 Parse entity definitions.
1729 xml+=strspn(xml+8,XMLWhitespace)+8;
1731 n=xml+strspn(xml,XMLWhitespace "%");
1732 xml=n+strcspn(n,XMLWhitespace);
1734 v=xml+strspn(xml+1,XMLWhitespace)+1;
1737 if ((q != '"') && (q != '\''))
1742 xml=strchr(xml,'>');
1745 entities=(*c == '%') ? predefined_entitites : root->entities;
1746 for (i=0; entities[i] != (char *) NULL; i++) ;
1747 entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3),
1749 if (entities == (char **) NULL)
1750 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1752 predefined_entitites=entities;
1754 root->entities=entities;
1758 if (xml != (char *) NULL)
1763 entities[i+1]=ParseEntities(v,predefined_entitites,'%');
1764 entities[i+2]=(char *) NULL;
1765 if (ValidateEntities(n,entities[i+1],entities) != MagickFalse)
1769 if (entities[i+1] != v)
1770 entities[i+1]=DestroyString(entities[i+1]);
1771 (void) ThrowMagickException(exception,GetMagickModule(),
1772 OptionWarning,"ParseError","circular entity declaration &%s",n);
1773 predefined_entitites=(char **) RelinquishMagickMemory(
1774 predefined_entitites);
1775 return(MagickFalse);
1779 if (strncmp(xml,"<!ATTLIST",9) == 0)
1782 Parse default attributes.
1784 t=xml+strspn(xml+9,XMLWhitespace)+9;
1787 (void) ThrowMagickException(exception,GetMagickModule(),
1788 OptionWarning,"ParseError","unclosed <!ATTLIST");
1789 predefined_entitites=(char **) RelinquishMagickMemory(
1790 predefined_entitites);
1791 return(MagickFalse);
1793 xml=t+strcspn(t,XMLWhitespace ">");
1798 while ((root->attributes[i] != (char **) NULL) &&
1799 (n != (char *) NULL) &&
1800 (strcmp(n,root->attributes[i][0]) != 0))
1802 while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') &&
1805 xml=n+strcspn(n,XMLWhitespace);
1810 (void) ThrowMagickException(exception,GetMagickModule(),
1811 OptionWarning,"ParseError","malformed <!ATTLIST");
1812 predefined_entitites=(char **) RelinquishMagickMemory(
1813 predefined_entitites);
1814 return(MagickFalse);
1816 xml+=strspn(xml+1,XMLWhitespace)+1;
1817 c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
1818 if (strncmp(xml,"NOTATION",8) == 0)
1819 xml+=strspn(xml+8,XMLWhitespace)+8;
1820 xml=(*xml == '(') ? strchr(xml,')') : xml+
1821 strcspn(xml,XMLWhitespace);
1822 if (xml == (char *) NULL)
1824 (void) ThrowMagickException(exception,GetMagickModule(),
1825 OptionWarning,"ParseError","malformed <!ATTLIST");
1826 predefined_entitites=(char **) RelinquishMagickMemory(
1827 predefined_entitites);
1828 return(MagickFalse);
1830 xml+=strspn(xml,XMLWhitespace ")");
1831 if (strncmp(xml,"#FIXED",6) == 0)
1832 xml+=strspn(xml+6,XMLWhitespace)+6;
1835 xml+=strcspn(xml,XMLWhitespace ">")-1;
1841 if (((*xml == '"') || (*xml == '\'')) &&
1842 ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
1846 (void) ThrowMagickException(exception,GetMagickModule(),
1847 OptionWarning,"ParseError","malformed <!ATTLIST");
1848 predefined_entitites=(char **) RelinquishMagickMemory(
1849 predefined_entitites);
1850 return(MagickFalse);
1852 if (root->attributes[i] == (char **) NULL)
1858 root->attributes=(char ***) AcquireQuantumMemory(2,
1859 sizeof(*root->attributes));
1861 root->attributes=(char ***) ResizeQuantumMemory(
1862 root->attributes,(size_t) (i+2),
1863 sizeof(*root->attributes));
1864 if (root->attributes == (char ***) NULL)
1865 ThrowFatalException(ResourceLimitFatalError,
1866 "MemoryAllocationFailed");
1867 root->attributes[i]=(char **) AcquireQuantumMemory(2,
1868 sizeof(*root->attributes));
1869 if (root->attributes[i] == (char **) NULL)
1870 ThrowFatalException(ResourceLimitFatalError,
1871 "MemoryAllocationFailed");
1872 root->attributes[i][0]=ConstantString(t);
1873 root->attributes[i][1]=(char *) NULL;
1874 root->attributes[i+1]=(char **) NULL;
1876 for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
1877 root->attributes[i]=(char **) ResizeQuantumMemory(
1878 root->attributes[i],(size_t) (j+4),sizeof(*root->attributes));
1879 if (root->attributes[i] == (char **) NULL)
1880 ThrowFatalException(ResourceLimitFatalError,
1881 "MemoryAllocationFailed");
1882 root->attributes[i][j+3]=(char *) NULL;
1883 root->attributes[i][j+2]=ConstantString(c);
1884 root->attributes[i][j+1]=(char *) NULL;
1885 if (v != (char *) NULL)
1886 root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
1887 root->attributes[i][j]=ConstantString(n);
1891 if (strncmp(xml, "<!--", 4) == 0)
1892 xml=strstr(xml+4,"-->");
1894 if (strncmp(xml,"<?", 2) == 0)
1898 if (xml != (char *) NULL)
1900 ParseProcessingInstructions(root,c,(size_t) (xml-c));
1906 xml=strchr(xml,'>');
1908 if ((*(xml++) == '%') && (root->standalone == MagickFalse))
1911 predefined_entitites=(char **) RelinquishMagickMemory(predefined_entitites);
1915 static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
1920 xml_info=root->node;
1921 if (xml_info->tag == (char *) NULL)
1922 xml_info->tag=ConstantString(tag);
1924 xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
1925 xml_info->attributes=attributes;
1926 root->node=xml_info;
1929 MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1961 Convert xml-string to UTF8.
1963 if ((xml == (const char *) NULL) || (strlen(xml) == 0))
1965 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1966 "ParseError","root tag missing");
1967 return((XMLTreeInfo *) NULL);
1969 root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
1971 utf8=ConvertUTF16ToUTF8(xml,&length);
1972 if (utf8 == (char *) NULL)
1974 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1975 "ParseError","UTF16 to UTF8 failed");
1976 return((XMLTreeInfo *) NULL);
1978 terminal=utf8[length-1];
1979 utf8[length-1]='\0';
1981 while ((*p != '\0') && (*p != '<'))
1985 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1986 "ParseError","root tag missing");
1987 utf8=DestroyString(utf8);
1988 return((XMLTreeInfo *) NULL);
1990 attribute=(char **) NULL;
1993 attributes=(char **) sentinel;
1996 if ((isalpha((int) ((unsigned char) *p)) !=0) || (*p == '_') ||
1997 (*p == ':') || (c < '\0'))
2002 if (root->node == (XMLTreeInfo *) NULL)
2004 (void) ThrowMagickException(exception,GetMagickModule(),
2005 OptionWarning,"ParseError","root tag missing");
2006 utf8=DestroyString(utf8);
2007 return(&root->root);
2009 p+=strcspn(p,XMLWhitespace "/>");
2010 while (isspace((int) ((unsigned char) *p)) != 0)
2012 if ((*p != '\0') && (*p != '/') && (*p != '>'))
2015 Find tag in default attributes list.
2018 while ((root->attributes[i] != (char **) NULL) &&
2019 (strcmp(root->attributes[i][0],tag) != 0))
2021 attribute=root->attributes[i];
2023 for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
2029 attributes=(char **) AcquireQuantumMemory(4,sizeof(*attributes));
2031 attributes=(char **) ResizeQuantumMemory(attributes,(size_t) (l+4),
2032 sizeof(*attributes));
2033 if (attributes == (char **) NULL)
2035 (void) ThrowMagickException(exception,GetMagickModule(),
2036 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
2037 utf8=DestroyString(utf8);
2038 return(&root->root);
2040 attributes[l+2]=(char *) NULL;
2041 attributes[l+1]=(char *) NULL;
2043 p+=strcspn(p,XMLWhitespace "=/>");
2044 if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
2045 attributes[l]=ConstantString("");
2049 p+=strspn(p,XMLWhitespace "=");
2051 if ((c == '"') || (c == '\''))
2058 while ((*p != '\0') && (*p != c))
2064 attributes[l]=ConstantString("");
2065 attributes[l+1]=ConstantString("");
2066 (void) DestroyXMLTreeAttributes(attributes);
2067 (void) ThrowMagickException(exception,GetMagickModule(),
2068 OptionWarning,"ParseError","missing %c",c);
2069 utf8=DestroyString(utf8);
2070 return(&root->root);
2073 while ((attribute != (char **) NULL) &&
2074 (attribute[j] != (char *) NULL) &&
2075 (strcmp(attribute[j],attributes[l]) != 0))
2077 attributes[l+1]=ParseEntities(attributes[l+1],root->entities,
2078 (attribute != (char **) NULL) && (attribute[j] !=
2079 (char *) NULL) ? *attribute[j+2] : ' ');
2081 attributes[l]=ConstantString(attributes[l]);
2083 while (isspace((int) ((unsigned char) *p)) != 0)
2092 if (((*p != '\0') && (*p != '>')) ||
2093 ((*p == '\0') && (terminal != '>')))
2096 (void) DestroyXMLTreeAttributes(attributes);
2097 (void) ThrowMagickException(exception,GetMagickModule(),
2098 OptionWarning,"ParseError","missing >");
2099 utf8=DestroyString(utf8);
2100 return(&root->root);
2102 ParseOpenTag(root,tag,attributes);
2103 (void) ParseCloseTag(root,tag,p,exception);
2108 if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
2111 ParseOpenTag(root,tag,attributes);
2117 (void) DestroyXMLTreeAttributes(attributes);
2118 (void) ThrowMagickException(exception,GetMagickModule(),
2119 OptionWarning,"ParseError","missing >");
2120 utf8=DestroyString(utf8);
2121 return(&root->root);
2132 p+=strcspn(tag,XMLWhitespace ">")+1;
2134 if ((c == '\0') && (terminal != '>'))
2136 (void) ThrowMagickException(exception,GetMagickModule(),
2137 OptionWarning,"ParseError","missing >");
2138 utf8=DestroyString(utf8);
2139 return(&root->root);
2142 if (ParseCloseTag(root,tag,p,exception) != (XMLTreeInfo *) NULL)
2144 utf8=DestroyString(utf8);
2145 return(&root->root);
2148 if (isspace((int) ((unsigned char) *p)) != 0)
2149 p+=strspn(p,XMLWhitespace);
2152 if (strncmp(p,"!--",3) == 0)
2158 if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
2159 ((*p == '\0') && (terminal != '>')))
2161 (void) ThrowMagickException(exception,GetMagickModule(),
2162 OptionWarning,"ParseError","unclosed <!--");
2163 utf8=DestroyString(utf8);
2164 return(&root->root);
2168 if (strncmp(p,"![CDATA[",8) == 0)
2174 if (p != (char *) NULL)
2177 ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
2181 (void) ThrowMagickException(exception,GetMagickModule(),
2182 OptionWarning,"ParseError","unclosed <![CDATA[");
2183 utf8=DestroyString(utf8);
2184 return(&root->root);
2188 if (strncmp(p,"!DOCTYPE",8) == 0)
2193 for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
2194 ((l != 0) && ((*p != ']') ||
2195 (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
2196 l=(ssize_t) ((*p == '[') ? 1 : l))
2197 p+=strcspn(p+1,"[]>")+1;
2198 if ((*p == '\0') && (terminal != '>'))
2200 (void) ThrowMagickException(exception,GetMagickModule(),
2201 OptionWarning,"ParseError","unclosed <!DOCTYPE");
2202 utf8=DestroyString(utf8);
2203 return(&root->root);
2206 tag=strchr(tag,'[')+1;
2209 status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
2211 if (status == MagickFalse)
2213 utf8=DestroyString(utf8);
2214 return(&root->root);
2223 Processing instructions.
2228 if (p == (char *) NULL)
2231 } while ((*p != '\0') && (*p != '>'));
2232 if ((p == (char *) NULL) || ((*p == '\0') &&
2235 (void) ThrowMagickException(exception,GetMagickModule(),
2236 OptionWarning,"ParseError","unclosed <?");
2237 utf8=DestroyString(utf8);
2238 return(&root->root);
2240 ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
2244 (void) ThrowMagickException(exception,GetMagickModule(),
2245 OptionWarning,"ParseError","unexpected <");
2246 utf8=DestroyString(utf8);
2247 return(&root->root);
2249 if ((p == (char *) NULL) || (*p == '\0'))
2253 if ((*p != '\0') && (*p != '<'))
2256 Tag character content.
2258 while ((*p != '\0') && (*p != '<'))
2262 ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
2268 utf8=DestroyString(utf8);
2269 if (root->node == (XMLTreeInfo *) NULL)
2270 return(&root->root);
2271 if (root->node->tag == (char *) NULL)
2273 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2274 "ParseError","root tag missing");
2275 return(&root->root);
2277 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2278 "ParseError","unclosed tag: '%s'",root->node->tag);
2279 return(&root->root);
2283 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2287 % N e w X M L T r e e T a g %
2291 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2293 % NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
2295 % The format of the NewXMLTreeTag method is:
2297 % XMLTreeInfo *NewXMLTreeTag(const char *tag)
2299 % A description of each parameter follows:
2304 MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
2307 *predefined_entities[NumberPredefinedEntities+1] =
2309 "lt;", "<", "gt;", ">", "quot;", """,
2310 "apos;", "'", "amp;", "&", (char *) NULL
2316 root=(XMLTreeRoot *) AcquireMagickMemory(sizeof(*root));
2317 if (root == (XMLTreeRoot *) NULL)
2318 return((XMLTreeInfo *) NULL);
2319 (void) ResetMagickMemory(root,0,sizeof(*root));
2320 root->root.tag=(char *) NULL;
2321 if (tag != (char *) NULL)
2322 root->root.tag=ConstantString(tag);
2323 root->node=(&root->root);
2324 root->root.content=ConstantString("");
2325 root->entities=(char **) AcquireMagickMemory(sizeof(predefined_entities));
2326 if (root->entities == (char **) NULL)
2327 return((XMLTreeInfo *) NULL);
2328 (void) CopyMagickMemory(root->entities,predefined_entities,
2329 sizeof(predefined_entities));
2330 root->root.attributes=sentinel;
2331 root->attributes=(char ***) root->root.attributes;
2332 root->processing_instructions=(char ***) root->root.attributes;
2333 root->debug=IsEventLogging();
2334 root->signature=MagickSignature;
2335 return(&root->root);
2339 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2343 % P r u n e T a g F r o m X M L T r e e %
2347 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2349 % PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
2352 % The format of the PruneTagFromXMLTree method is:
2354 % XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2356 % A description of each parameter follows:
2358 % o xml_info: the xml info.
2361 MagickPrivate XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2366 assert(xml_info != (XMLTreeInfo *) NULL);
2367 assert((xml_info->signature == MagickSignature) ||
2368 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2369 if (xml_info->debug != MagickFalse)
2370 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2371 if (xml_info->next != (XMLTreeInfo *) NULL)
2372 xml_info->next->sibling=xml_info->sibling;
2373 if (xml_info->parent != (XMLTreeInfo *) NULL)
2375 node=xml_info->parent->child;
2376 if (node == xml_info)
2377 xml_info->parent->child=xml_info->ordered;
2380 while (node->ordered != xml_info)
2382 node->ordered=node->ordered->ordered;
2383 node=xml_info->parent->child;
2384 if (strcmp(node->tag,xml_info->tag) != 0)
2386 while (strcmp(node->sibling->tag,xml_info->tag) != 0)
2388 if (node->sibling != xml_info)
2391 node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
2392 xml_info->next : node->sibling->sibling;
2394 while ((node->next != (XMLTreeInfo *) NULL) &&
2395 (node->next != xml_info))
2397 if (node->next != (XMLTreeInfo *) NULL)
2398 node->next=node->next->next;
2401 xml_info->ordered=(XMLTreeInfo *) NULL;
2402 xml_info->sibling=(XMLTreeInfo *) NULL;
2403 xml_info->next=(XMLTreeInfo *) NULL;
2408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2412 % S e t X M L T r e e A t t r i b u t e %
2416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2418 % SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
2419 % found. A value of NULL removes the specified attribute.
2421 % The format of the SetXMLTreeAttribute method is:
2423 % XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
2424 % const char *value)
2426 % A description of each parameter follows:
2428 % o xml_info: the xml info.
2430 % o tag: The attribute tag.
2432 % o value: The attribute value.
2435 MagickPrivate XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
2436 const char *tag,const char *value)
2444 assert(xml_info != (XMLTreeInfo *) NULL);
2445 assert((xml_info->signature == MagickSignature) ||
2446 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2447 if (xml_info->debug != MagickFalse)
2448 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2450 while ((xml_info->attributes[i] != (char *) NULL) &&
2451 (strcmp(xml_info->attributes[i],tag) != 0))
2453 if (xml_info->attributes[i] == (char *) NULL)
2456 Add new attribute tag.
2458 if (value == (const char *) NULL)
2460 if (xml_info->attributes != sentinel)
2461 xml_info->attributes=(char **) ResizeQuantumMemory(
2462 xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
2465 xml_info->attributes=(char **) AcquireQuantumMemory(4,
2466 sizeof(*xml_info->attributes));
2467 if (xml_info->attributes != (char **) NULL)
2468 xml_info->attributes[1]=ConstantString("");
2470 if (xml_info->attributes == (char **) NULL)
2471 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2472 xml_info->attributes[i]=ConstantString(tag);
2473 xml_info->attributes[i+2]=(char *) NULL;
2474 (void) strlen(xml_info->attributes[i+1]);
2477 Add new value to an existing attribute.
2479 for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
2480 if (xml_info->attributes[i+1] != (char *) NULL)
2481 xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
2482 if (value != (const char *) NULL)
2484 xml_info->attributes[i+1]=ConstantString(value);
2487 if (xml_info->attributes[i] != (char *) NULL)
2488 xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
2489 (void) CopyMagickMemory(xml_info->attributes+i,xml_info->attributes+i+2,
2490 (size_t) (j-i)*sizeof(*xml_info->attributes));
2491 xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
2492 (size_t) (j+2),sizeof(*xml_info->attributes));
2493 if (xml_info->attributes == (char **) NULL)
2494 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2496 (void) CopyMagickMemory(xml_info->attributes[j+1]+(i/2),
2497 xml_info->attributes[j+1]+(i/2)+1,(size_t) (((j+2)/2)-(i/2))*
2498 sizeof(*xml_info->attributes));
2503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2507 % S e t X M L T r e e C o n t e n t %
2511 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2513 % SetXMLTreeContent() sets the character content for the given tag and
2516 % The format of the SetXMLTreeContent method is:
2518 % XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2519 % const char *content)
2521 % A description of each parameter follows:
2523 % o xml_info: the xml info.
2525 % o content: The content.
2528 MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2529 const char *content)
2531 assert(xml_info != (XMLTreeInfo *) NULL);
2532 assert((xml_info->signature == MagickSignature) ||
2533 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2534 if (xml_info->debug != MagickFalse)
2535 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2536 if (xml_info->content != (char *) NULL)
2537 xml_info->content=DestroyString(xml_info->content);
2538 xml_info->content=(char *) ConstantString(content);
2543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2547 % X M L T r e e I n f o T o X M L %
2551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2553 % XMLTreeInfoToXML() converts an xml-tree to an XML string.
2555 % The format of the XMLTreeInfoToXML method is:
2557 % char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2559 % A description of each parameter follows:
2561 % o xml_info: the xml info.
2565 static char *EncodePredefinedEntities(const char *source,ssize_t offset,
2566 char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
2572 canonical_content=CanonicalXMLContent(source,pedantic);
2578 content=AcquireString(source);
2579 content[offset]='\0';
2580 canonical_content=CanonicalXMLContent(content,pedantic);
2581 content=DestroyString(content);
2583 if (canonical_content == (char *) NULL)
2584 return(*destination);
2585 if ((*length+strlen(canonical_content)+MaxTextExtent) > *extent)
2587 *extent=(*length)+strlen(canonical_content)+MaxTextExtent;
2588 *destination=(char *) ResizeQuantumMemory(*destination,*extent,
2589 sizeof(**destination));
2590 if (*destination == (char *) NULL)
2591 return(*destination);
2593 *length+=FormatLocaleString(*destination+(*length),*extent,"%s",
2595 canonical_content=DestroyString(canonical_content);
2596 return(*destination);
2599 static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
2600 size_t *extent,size_t start,char ***attributes)
2617 content=(char *) "";
2618 if (xml_info->parent != (XMLTreeInfo *) NULL)
2619 content=xml_info->parent->content;
2621 *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
2622 start),source,length,extent,MagickFalse);
2623 if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2625 *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2626 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2627 if (*source == (char *) NULL)
2630 *length+=FormatLocaleString(*source+(*length),*extent,"<%s",xml_info->tag);
2631 for (i=0; xml_info->attributes[i]; i+=2)
2633 attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
2634 if (attribute != xml_info->attributes[i+1])
2636 if ((*length+strlen(xml_info->attributes[i])+MaxTextExtent) > *extent)
2638 *extent=(*length)+strlen(xml_info->attributes[i])+MaxTextExtent;
2639 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2640 if (*source == (char *) NULL)
2641 return((char *) NULL);
2643 *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
2644 xml_info->attributes[i]);
2645 (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
2647 *length+=FormatLocaleString(*source+(*length),*extent,"\"");
2650 while ((attributes[i] != (char **) NULL) &&
2651 (strcmp(attributes[i][0],xml_info->tag) != 0))
2654 while ((attributes[i] != (char **) NULL) &&
2655 (attributes[i][j] != (char *) NULL))
2657 if ((attributes[i][j+1] == (char *) NULL) ||
2658 (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
2663 if ((*length+strlen(attributes[i][j])+MaxTextExtent) > *extent)
2665 *extent=(*length)+strlen(attributes[i][j])+MaxTextExtent;
2666 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2667 if (*source == (char *) NULL)
2668 return((char *) NULL);
2670 *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
2672 (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
2674 *length+=FormatLocaleString(*source+(*length),*extent,"\"");
2677 *length+=FormatLocaleString(*source+(*length),*extent,*xml_info->content ?
2679 if (xml_info->child != (XMLTreeInfo *) NULL)
2680 *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
2682 *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
2684 if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2686 *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2687 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2688 if (*source == (char *) NULL)
2689 return((char *) NULL);
2691 if (*xml_info->content != '\0')
2692 *length+=FormatLocaleString(*source+(*length),*extent,"</%s>",
2694 while ((content[offset] != '\0') && (offset < xml_info->offset))
2696 if (xml_info->ordered != (XMLTreeInfo *) NULL)
2697 content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
2700 content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
2705 MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2732 assert(xml_info != (XMLTreeInfo *) NULL);
2733 assert((xml_info->signature == MagickSignature) ||
2734 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2735 if (xml_info->debug != MagickFalse)
2736 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2737 if (xml_info->tag == (char *) NULL)
2738 return((char *) NULL);
2739 xml=AcquireString((char *) NULL);
2741 extent=MaxTextExtent;
2742 root=(XMLTreeRoot *) xml_info;
2743 while (root->root.parent != (XMLTreeInfo *) NULL)
2744 root=(XMLTreeRoot *) root->root.parent;
2745 parent=(XMLTreeInfo *) NULL;
2746 if (xml_info != (XMLTreeInfo *) NULL)
2747 parent=xml_info->parent;
2748 if (parent == (XMLTreeInfo *) NULL)
2749 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2752 Pre-root processing instructions.
2754 for (k=2; root->processing_instructions[i][k-1]; k++) ;
2755 p=root->processing_instructions[i][1];
2756 for (j=1; p != (char *) NULL; j++)
2758 if (root->processing_instructions[i][k][j-1] == '>')
2760 p=root->processing_instructions[i][j];
2763 q=root->processing_instructions[i][0];
2764 if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2766 extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2767 xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2768 if (xml == (char *) NULL)
2771 length+=FormatLocaleString(xml+length,extent,"<?%s%s%s?>\n",q,
2772 *p != '\0' ? " " : "",p);
2773 p=root->processing_instructions[i][j];
2776 ordered=(XMLTreeInfo *) NULL;
2777 if (xml_info != (XMLTreeInfo *) NULL)
2778 ordered=xml_info->ordered;
2779 xml_info->parent=(XMLTreeInfo *) NULL;
2780 xml_info->ordered=(XMLTreeInfo *) NULL;
2781 xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
2782 xml_info->parent=parent;
2783 xml_info->ordered=ordered;
2784 if (parent == (XMLTreeInfo *) NULL)
2785 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2788 Post-root processing instructions.
2790 for (k=2; root->processing_instructions[i][k-1]; k++) ;
2791 p=root->processing_instructions[i][1];
2792 for (j=1; p != (char *) NULL; j++)
2794 if (root->processing_instructions[i][k][j-1] == '<')
2796 p=root->processing_instructions[i][j];
2799 q=root->processing_instructions[i][0];
2800 if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2802 extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2803 xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2804 if (xml == (char *) NULL)
2807 length+=FormatLocaleString(xml+length,extent,"\n<?%s%s%s?>",q,
2808 *p != '\0' ? " " : "",p);
2809 p=root->processing_instructions[i][j];
2812 return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));