2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12 % TTTTT RRRR EEEEE EEEEE %
26 % Copyright 1999-2010 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 "magick/studio.h"
51 #include "magick/blob.h"
52 #include "magick/exception.h"
53 #include "magick/exception-private.h"
54 #include "magick/log.h"
55 #include "magick/memory_.h"
56 #include "magick/semaphore.h"
57 #include "magick/string_.h"
58 #include "magick/string-private.h"
59 #include "magick/token-private.h"
60 #include "magick/xml-tree.h"
61 #include "magick/utility.h"
66 #define NumberPredefinedEntities 10
67 #define XMLWhitespace "\t\r\n "
99 typedef struct _XMLTreeRoot
114 ***processing_instructions,
132 *sentinel[] = { (char *) NULL };
135 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
139 % A d d C h i l d T o X M L T r e e %
143 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
145 % AddChildToXMLTree() adds a child tag at an offset relative to the start of
146 % the parent tag's character content. Return the child tag.
148 % The format of the AddChildToXMLTree method is:
150 % XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,const char *tag,
151 % const size_t offset)
153 % A description of each parameter follows:
155 % o xml_info: the xml info.
159 % o offset: the tag offset.
162 MagickExport XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,
163 const char *tag,const size_t offset)
168 if (xml_info == (XMLTreeInfo *) NULL)
169 return((XMLTreeInfo *) NULL);
170 child=(XMLTreeInfo *) AcquireAlignedMemory(1,sizeof(*child));
171 if (child == (XMLTreeInfo *) NULL)
172 return((XMLTreeInfo *) NULL);
173 (void) ResetMagickMemory(child,0,sizeof(*child));
174 child->tag=ConstantString(tag);
175 child->attributes=sentinel;
176 child->content=ConstantString("");
177 child->debug=IsEventLogging();
178 child->signature=MagickSignature;
179 return(InsertTagIntoXMLTree(xml_info,child,offset));
183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187 % A d d P a t h T o X M L T r e e %
191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
193 % AddPathToXMLTree() adds a child tag at an offset relative to the start of
194 % the parent tag's character content. This method returns the child tag.
196 % The format of the AddPathToXMLTree method is:
198 % XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,const char *path,
199 % const size_t offset)
201 % A description of each parameter follows:
203 % o xml_info: the xml info.
207 % o offset: the tag offset.
210 MagickExport XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,
211 const char *path,const size_t offset)
215 subnode[MaxTextExtent],
231 assert(xml_info != (XMLTreeInfo *) NULL);
232 assert((xml_info->signature == MagickSignature) ||
233 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
234 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
236 components=GetPathComponents(path,&number_components);
237 if (components == (char **) NULL)
238 return((XMLTreeInfo *) NULL);
239 for (i=0; i < (long) number_components; i++)
241 GetPathComponent(components[i],SubimagePath,subnode);
242 GetPathComponent(components[i],CanonicalPath,tag);
243 child=GetXMLTreeChild(node,tag);
244 if (child == (XMLTreeInfo *) NULL)
245 child=AddChildToXMLTree(node,tag,offset);
247 if (node == (XMLTreeInfo *) NULL)
249 for (j=StringToLong(subnode)-1; j > 0; j--)
251 node=GetXMLTreeOrdered(node);
252 if (node == (XMLTreeInfo *) NULL)
255 if (node == (XMLTreeInfo *) NULL)
257 components[i]=DestroyString(components[i]);
259 for ( ; i < (long) number_components; i++)
260 components[i]=DestroyString(components[i]);
261 components=(char **) RelinquishMagickMemory(components);
266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270 % C a n o n i c a l X M L C o n t e n t %
274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
276 % CanonicalXMLContent() converts text to canonical XML content by converting
277 % to UTF-8, substituting predefined entities, wrapping as CDATA, or encoding
278 % as base-64 as required.
280 % The format of the CanonicalXMLContent method is:
283 % char *CanonicalXMLContent(const char *content,
284 % const MagickBooleanType pedantic)
286 % A description of each parameter follows:
288 % o content: the content.
290 % o pedantic: if true, replace newlines and tabs with their respective
294 MagickExport char *CanonicalXMLContent(const char *content,
295 const MagickBooleanType pedantic)
301 register const unsigned char
314 utf8=ConvertLatin1ToUTF8((const unsigned char *) content);
315 if (utf8 == (unsigned char *) NULL)
316 return((char *) NULL);
317 for (p=utf8; *p != '\0'; p++)
318 if ((*p < 0x20) && (*p != 0x09) && (*p != 0x0a) && (*p != 0x0d))
323 String is binary, base64-encode it.
325 base64=Base64Encode(utf8,strlen((char *) utf8),&length);
326 utf8=(unsigned char *) RelinquishMagickMemory(utf8);
327 if (base64 == (char *) NULL)
328 return((char *) NULL);
329 canonical_content=AcquireString("<base64>");
330 (void) ConcatenateString(&canonical_content,base64);
331 base64=DestroyString(base64);
332 (void) ConcatenateString(&canonical_content,"</base64>");
333 return(canonical_content);
336 Substitute predefined entities.
339 canonical_content=AcquireString((char *) NULL);
340 extent=MaxTextExtent;
341 for (p=utf8; *p != '\0'; p++)
343 if ((i+MaxTextExtent) > (long) extent)
345 extent+=MaxTextExtent;
346 canonical_content=(char *) ResizeQuantumMemory(canonical_content,extent,
347 sizeof(*canonical_content));
348 if (canonical_content == (char *) NULL)
349 return(canonical_content);
355 i+=FormatMagickString(canonical_content+i,extent,"&");
360 i+=FormatMagickString(canonical_content+i,extent,"<");
365 i+=FormatMagickString(canonical_content+i,extent,">");
370 i+=FormatMagickString(canonical_content+i,extent,""");
375 if (pedantic == MagickFalse)
377 canonical_content[i++]=(char) (*p);
380 i+=FormatMagickString(canonical_content+i,extent,"
");
385 if (pedantic == MagickFalse)
387 canonical_content[i++]=(char) (*p);
390 i+=FormatMagickString(canonical_content+i,extent,"	");
395 i+=FormatMagickString(canonical_content+i,extent,"
");
400 canonical_content[i++]=(char) (*p);
405 canonical_content[i]='\0';
406 utf8=(unsigned char *) RelinquishMagickMemory(utf8);
407 return(canonical_content);
411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415 % D e s t r o y X M L T r e e %
419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421 % DestroyXMLTree() destroys the xml-tree.
423 % The format of the DestroyXMLTree method is:
425 % XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
427 % A description of each parameter follows:
429 % o xml_info: the xml info.
433 static char **DestroyXMLTreeAttributes(char **attributes)
439 Destroy a tag attribute list.
441 if ((attributes == (char **) NULL) || (attributes == sentinel))
442 return((char **) NULL);
443 for (i=0; attributes[i] != (char *) NULL; i+=2)
446 Destroy attribute tag and value.
448 if (attributes[i] != (char *) NULL)
449 attributes[i]=DestroyString(attributes[i]);
450 if (attributes[i+1] != (char *) NULL)
451 attributes[i+1]=DestroyString(attributes[i+1]);
453 attributes=(char **) RelinquishMagickMemory(attributes);
454 return((char **) NULL);
457 MagickExport XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
471 assert(xml_info != (XMLTreeInfo *) NULL);
472 assert((xml_info->signature == MagickSignature) ||
473 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
474 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
475 if (xml_info->child != (XMLTreeInfo *) NULL)
476 xml_info->child=DestroyXMLTree(xml_info->child);
477 if (xml_info->ordered != (XMLTreeInfo *) NULL)
478 xml_info->ordered=DestroyXMLTree(xml_info->ordered);
479 if (xml_info->parent == (XMLTreeInfo *) NULL)
482 Free root tag allocations.
484 root=(XMLTreeRoot *) xml_info;
485 for (i=NumberPredefinedEntities; root->entities[i]; i+=2)
486 root->entities[i+1]=DestroyString(root->entities[i+1]);
487 root->entities=(char **) RelinquishMagickMemory(root->entities);
488 for (i=0; root->attributes[i] != (char **) NULL; i++)
490 attributes=root->attributes[i];
491 if (attributes[0] != (char *) NULL)
492 attributes[0]=DestroyString(attributes[0]);
493 for (j=1; attributes[j] != (char *) NULL; j+=3)
495 if (attributes[j] != (char *) NULL)
496 attributes[j]=DestroyString(attributes[j]);
497 if (attributes[j+1] != (char *) NULL)
498 attributes[j+1]=DestroyString(attributes[j+1]);
499 if (attributes[j+2] != (char *) NULL)
500 attributes[j+2]=DestroyString(attributes[j+2]);
502 attributes=(char **) RelinquishMagickMemory(attributes);
504 if (root->attributes[0] != (char **) NULL)
505 root->attributes=(char ***) RelinquishMagickMemory(root->attributes);
506 if (root->processing_instructions[0] != (char **) NULL)
508 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
510 for (j=0; root->processing_instructions[i][j] != (char *) NULL; j++)
511 root->processing_instructions[i][j]=DestroyString(
512 root->processing_instructions[i][j]);
513 root->processing_instructions[i][j+1]=DestroyString(
514 root->processing_instructions[i][j+1]);
515 root->processing_instructions[i]=(char **) RelinquishMagickMemory(
516 root->processing_instructions[i]);
518 root->processing_instructions=(char ***) RelinquishMagickMemory(
519 root->processing_instructions);
522 xml_info->attributes=DestroyXMLTreeAttributes(xml_info->attributes);
523 xml_info->content=DestroyString(xml_info->content);
524 xml_info->tag=DestroyString(xml_info->tag);
525 xml_info=(XMLTreeInfo *) RelinquishMagickMemory(xml_info);
526 return((XMLTreeInfo *) NULL);
530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
534 % G e t N e x t X M L T r e e T a g %
538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
540 % GetNextXMLTreeTag() returns the next tag or NULL if not found.
542 % The format of the GetNextXMLTreeTag method is:
544 % XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
546 % A description of each parameter follows:
548 % o xml_info: the xml info.
551 MagickExport XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
553 assert(xml_info != (XMLTreeInfo *) NULL);
554 assert((xml_info->signature == MagickSignature) ||
555 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
556 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
557 return(xml_info->next);
561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
565 % G e t X M L T r e e A t t r i b u t e %
569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
571 % GetXMLTreeAttribute() returns the value of the attribute tag with the
572 % specified tag if found, otherwise NULL.
574 % The format of the GetXMLTreeAttribute method is:
576 % const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag)
578 % A description of each parameter follows:
580 % o xml_info: the xml info.
582 % o tag: the attribute tag.
585 MagickExport const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,
597 assert(xml_info != (XMLTreeInfo *) NULL);
598 assert((xml_info->signature == MagickSignature) ||
599 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
600 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
601 if (xml_info->attributes == (char **) NULL)
602 return((const char *) NULL);
604 while ((xml_info->attributes[i] != (char *) NULL) &&
605 (strcmp(xml_info->attributes[i],tag) != 0))
607 if (xml_info->attributes[i] != (char *) NULL)
608 return(xml_info->attributes[i+1]);
609 root=(XMLTreeRoot*) xml_info;
610 while (root->root.parent != (XMLTreeInfo *) NULL)
611 root=(XMLTreeRoot *) root->root.parent;
613 while ((root->attributes[i] != (char **) NULL) &&
614 (strcmp(root->attributes[i][0],xml_info->tag) != 0))
616 if (root->attributes[i] == (char **) NULL)
617 return((const char *) NULL);
619 while ((root->attributes[i][j] != (char *) NULL) &&
620 (strcmp(root->attributes[i][j],tag) != 0))
622 if (root->attributes[i][j] == (char *) NULL)
623 return((const char *) NULL);
624 return(root->attributes[i][j+1]);
628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
632 % G e t X M L T r e e A t t r i b u t e s %
636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638 % GetXMLTreeAttributes() injects all attributes associated with the current
639 % tag in the specified splay-tree.
641 % The format of the GetXMLTreeAttributes method is:
643 % MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
644 % SplayTreeInfo *attributes)
646 % A description of each parameter follows:
648 % o xml_info: the xml info.
650 % o attributes: the attribute splay-tree.
653 MagickExport MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
654 SplayTreeInfo *attributes)
659 assert(xml_info != (XMLTreeInfo *) NULL);
660 assert((xml_info->signature == MagickSignature) ||
661 (((const XMLTreeRoot *) xml_info)->signature == MagickSignature));
662 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
663 assert(attributes != (SplayTreeInfo *) NULL);
664 if (xml_info->attributes == (char **) NULL)
667 while (xml_info->attributes[i] != (char *) NULL)
669 (void) AddValueToSplayTree(attributes,
670 ConstantString(xml_info->attributes[i]),
671 ConstantString(xml_info->attributes[i+1]));
678 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
682 % G e t X M L T r e e C h i l d %
686 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
688 % GetXMLTreeChild() returns the first child tag with the specified tag if
689 % found, otherwise NULL.
691 % The format of the GetXMLTreeChild method is:
693 % XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
695 % A description of each parameter follows:
697 % o xml_info: the xml info.
700 MagickExport XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
705 assert(xml_info != (XMLTreeInfo *) NULL);
706 assert((xml_info->signature == MagickSignature) ||
707 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
708 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
709 child=xml_info->child;
710 if (tag != (const char *) NULL)
711 while ((child != (XMLTreeInfo *) NULL) && (strcmp(child->tag,tag) != 0))
712 child=child->sibling;
717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
721 % G e t X M L T r e e C o n t e n t %
725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727 % GetXMLTreeContent() returns any content associated with specified
730 % The format of the GetXMLTreeContent method is:
732 % const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
734 % A description of each parameter follows:
736 % o xml_info: the xml info.
739 MagickExport const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
741 assert(xml_info != (XMLTreeInfo *) NULL);
742 assert((xml_info->signature == MagickSignature) ||
743 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
744 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
745 return(xml_info->content);
749 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
753 % G e t X M L T r e e O r d e r e d %
757 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759 % GetXMLTreeOrdered() returns the next ordered node if found, otherwise NULL.
761 % The format of the GetXMLTreeOrdered method is:
763 % XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
765 % A description of each parameter follows:
767 % o xml_info: the xml info.
770 MagickExport XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
772 assert(xml_info != (XMLTreeInfo *) NULL);
773 assert((xml_info->signature == MagickSignature) ||
774 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
775 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
776 return(xml_info->ordered);
780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
784 % G e t X M L T r e e P a t h %
788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
790 % GetXMLTreePath() traverses the XML-tree as defined by the specified path
791 % and returns the node if found, otherwise NULL.
793 % The format of the GetXMLTreePath method is:
795 % XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
797 % A description of each parameter follows:
799 % o xml_info: the xml info.
801 % o path: the path (e.g. property/elapsed-time).
804 MagickExport XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
808 subnode[MaxTextExtent],
823 assert(xml_info != (XMLTreeInfo *) NULL);
824 assert((xml_info->signature == MagickSignature) ||
825 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
826 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
828 components=GetPathComponents(path,&number_components);
829 if (components == (char **) NULL)
830 return((XMLTreeInfo *) NULL);
831 for (i=0; i < (long) number_components; i++)
833 GetPathComponent(components[i],SubimagePath,subnode);
834 GetPathComponent(components[i],CanonicalPath,tag);
835 node=GetXMLTreeChild(node,tag);
836 if (node == (XMLTreeInfo *) NULL)
838 for (j=StringToLong(subnode)-1; j > 0; j--)
840 node=GetXMLTreeOrdered(node);
841 if (node == (XMLTreeInfo *) NULL)
844 if (node == (XMLTreeInfo *) NULL)
846 components[i]=DestroyString(components[i]);
848 for ( ; i < (long) number_components; i++)
849 components[i]=DestroyString(components[i]);
850 components=(char **) RelinquishMagickMemory(components);
855 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
859 % 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 %
863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
865 % GetXMLTreeProcessingInstructions() returns a null terminated array of
866 % processing instructions for the given target.
868 % The format of the GetXMLTreeProcessingInstructions method is:
870 % const char **GetXMLTreeProcessingInstructions(XMLTreeInfo *xml_info,
871 % const char *target)
873 % A description of each parameter follows:
875 % o xml_info: the xml info.
878 MagickExport const char **GetXMLTreeProcessingInstructions(
879 XMLTreeInfo *xml_info,const char *target)
887 assert(xml_info != (XMLTreeInfo *) NULL);
888 assert((xml_info->signature == MagickSignature) ||
889 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
890 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
891 root=(XMLTreeRoot *) xml_info;
892 while (root->root.parent != (XMLTreeInfo *) NULL)
893 root=(XMLTreeRoot *) root->root.parent;
895 while ((root->processing_instructions[i] != (char **) NULL) &&
896 (strcmp(root->processing_instructions[i][0],target) != 0))
898 if (root->processing_instructions[i] == (char **) NULL)
899 return((const char **) sentinel);
900 return((const char **) (root->processing_instructions[i]+1));
904 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
908 % G e t X M L T r e e S i b l i n g %
912 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
914 % GetXMLTreeSibling() returns the node sibling if found, otherwise NULL.
916 % The format of the GetXMLTreeSibling method is:
918 % XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
920 % A description of each parameter follows:
922 % o xml_info: the xml info.
925 MagickExport XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
927 assert(xml_info != (XMLTreeInfo *) NULL);
928 assert((xml_info->signature == MagickSignature) ||
929 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
930 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
931 return(xml_info->sibling);
935 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
939 % G e t X M L T r e e T a g %
943 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
945 % GetXMLTreeTag() returns the tag associated with specified xml-tree node.
947 % The format of the GetXMLTreeTag method is:
949 % const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
951 % A description of each parameter follows:
953 % o xml_info: the xml info.
956 MagickExport const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
958 assert(xml_info != (XMLTreeInfo *) NULL);
959 assert((xml_info->signature == MagickSignature) ||
960 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
961 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
962 return(xml_info->tag);
966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
970 % I n s e r t I n t o T a g X M L T r e e %
974 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
976 % InsertTagIntoXMLTree() inserts a tag at an offset relative to the start of
977 % the parent tag's character content. This method returns the child tag.
979 % The format of the InsertTagIntoXMLTree method is:
981 % XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
982 % XMLTreeInfo *child,const size_t offset)
984 % A description of each parameter follows:
986 % o xml_info: the xml info.
988 % o child: the child tag.
990 % o offset: the tag offset.
993 MagickExport XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
994 XMLTreeInfo *child,const size_t offset)
1001 child->ordered=(XMLTreeInfo *) NULL;
1002 child->sibling=(XMLTreeInfo *) NULL;
1003 child->next=(XMLTreeInfo *) NULL;
1004 child->offset=offset;
1005 child->parent=xml_info;
1006 if (xml_info->child == (XMLTreeInfo *) NULL)
1008 xml_info->child=child;
1011 head=xml_info->child;
1012 if (head->offset > offset)
1014 child->ordered=head;
1015 xml_info->child=child;
1020 while ((node->ordered != (XMLTreeInfo *) NULL) &&
1021 (node->ordered->offset <= offset))
1023 child->ordered=node->ordered;
1024 node->ordered=child;
1026 previous=(XMLTreeInfo *) NULL;
1028 while ((node != (XMLTreeInfo *) NULL) && (strcmp(node->tag,child->tag) != 0))
1033 if ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1035 while ((node->next != (XMLTreeInfo *) NULL) &&
1036 (node->next->offset <= offset))
1038 child->next=node->next;
1043 if ((previous != (XMLTreeInfo *) NULL) && (node != (XMLTreeInfo *) NULL))
1044 previous->sibling=node->sibling;
1046 previous=(XMLTreeInfo *) NULL;
1048 while ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1053 child->sibling=node;
1054 if (previous != (XMLTreeInfo *) NULL)
1055 previous->sibling=child;
1061 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1065 % N e w X M L T r e e %
1069 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071 % NewXMLTree() returns a XMLTreeInfo xml-tree as defined by the specified
1074 % The format of the NewXMLTree method is:
1076 % XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1078 % A description of each parameter follows:
1080 % o xml: The XML string.
1082 % o exception: return any errors or warnings in this structure.
1086 static char *ConvertUTF16ToUTF8(const char *content,size_t *length)
1106 utf8=(char *) AcquireQuantumMemory(*length,sizeof(*utf8));
1107 if (utf8 == (char *) NULL)
1108 return((char *) NULL);
1109 encoding=(*content == '\xFE') ? 1 : (*content == '\xFF') ? 0 : -1;
1115 (void) CopyMagickMemory(utf8,content,*length*sizeof(*utf8));
1120 for (i=2; i < (long) (*length-1); i+=2)
1122 c=(encoding != 0) ? ((content[i] & 0xff) << 8) | (content[i+1] & 0xff) :
1123 ((content[i+1] & 0xff) << 8) | (content[i] & 0xff);
1124 if ((c >= 0xd800) && (c <= 0xdfff) && ((i+=2) < (long) (*length-1)))
1126 byte=(encoding != 0) ? ((content[i] & 0xff) << 8) |
1127 (content[i+1] & 0xff) : ((content[i+1] & 0xff) << 8) |
1128 (content[i] & 0xff);
1129 c=(((c & 0x3ff) << 10) | (byte & 0x3ff))+0x10000;
1131 if ((size_t) (j+MaxTextExtent) > extent)
1133 extent=(size_t) j+MaxTextExtent;
1134 utf8=(char *) ResizeQuantumMemory(utf8,extent,sizeof(*utf8));
1135 if (utf8 == (char *) NULL)
1145 Multi-byte UTF-8 sequence.
1148 for (bits=0; byte != 0; byte/=2)
1151 utf8[j++]=(0xFF << (7-bits)) | (c >> (6*bits));
1155 utf8[j]=0x80 | ((c >> (6*bits)) & 0x3f);
1160 return((char *) ResizeQuantumMemory(utf8,*length,sizeof(*utf8)));
1163 static char *ParseEntities(char *xml,char **entities,int state)
1187 Normalize line endings.
1191 for ( ; *xml != '\0'; xml++)
1192 while (*xml == '\r')
1196 (void) CopyMagickMemory(xml,xml+1,strlen(xml));
1200 while ((*xml != '\0') && (*xml != '&') && ((*xml != '%') ||
1201 (state != '%')) && (isspace((int) ((unsigned char) *xml) == 0)))
1207 '&' for general entity decoding
1208 '%' for parameter entity decoding
1209 'c' for CDATA sections
1210 ' ' for attributes normalization
1211 '*' for non-CDATA attributes normalization
1213 if ((state != 'c') && (strncmp(xml,"&#",2) == 0))
1216 Character reference.
1219 c=strtol(xml+2,&entity,10); /* base 10 */
1221 c=strtol(xml+3,&entity,16); /* base 16 */
1222 if ((c == 0) || (*entity != ';'))
1225 Not a character reference.
1235 Multi-byte UTF-8 sequence.
1238 for (i=0; byte != 0; byte/=2)
1241 *xml=(char) ((0xFF << (7-i)) | (c >> (6*i)));
1246 *xml=(char) (0x80 | ((c >> (6*i)) & 0x3F));
1250 (void) CopyMagickMemory(xml,strchr(xml,';')+1,strlen(strchr(xml,';')));
1253 if (((*xml == '&') && ((state == '&') || (state == ' ') ||
1254 (state == '*'))) || ((state == '%') && (*xml == '%')))
1257 Find entity in the list.
1260 while ((entities[i] != (char *) NULL) &&
1261 (strncmp(xml+1,entities[i],strlen(entities[i])) != 0))
1263 if (entities[i++] == (char *) NULL)
1270 length=strlen(entities[i]);
1271 entity=strchr(xml,';');
1272 if ((length-1L) >= (size_t) (entity-xml))
1274 offset=(ssize_t) (xml-p);
1275 extent=(size_t) (offset+length+strlen(entity));
1277 p=(char *) ResizeQuantumMemory(p,extent,sizeof(*p));
1283 xml=(char *) AcquireQuantumMemory(extent,sizeof(*xml));
1284 if (xml != (char *) NULL)
1286 (void) CopyMagickString(xml,p,extent*sizeof(*xml));
1290 if (p == (char *) NULL)
1291 ThrowFatalException(ResourceLimitFatalError,
1292 "MemoryAllocationFailed");
1294 entity=strchr(xml,';');
1296 (void) CopyMagickMemory(xml+length,entity+1,strlen(entity));
1297 (void) strncpy(xml,entities[i],length);
1301 if (((state == ' ') || (state == '*')) &&
1302 (isspace((int) ((unsigned char) *xml) != 0)))
1310 Normalize spaces for non-CDATA attributes.
1312 for (xml=p; *xml != '\0'; xml++)
1314 i=(long) strspn(xml," ");
1316 (void) CopyMagickMemory(xml,xml+i,strlen(xml+i)+1);
1317 while ((*xml != '\0') && (*xml != ' '))
1321 if ((xml >= p) && (*xml == ' '))
1324 return(p == q ? ConstantString(p) : p);
1327 static void ParseCharacterContent(XMLTreeRoot *root,char *xml,
1328 const size_t length,const char state)
1333 xml_info=root->node;
1334 if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) ||
1338 xml=ParseEntities(xml,root->entities,state);
1339 if (*xml_info->content != '\0')
1341 (void) ConcatenateString(&xml_info->content,xml);
1342 xml=DestroyString(xml);
1346 if (xml_info->content != (char *) NULL)
1347 xml_info->content=DestroyString(xml_info->content);
1348 xml_info->content=xml;
1352 static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag,
1353 char *magick_unused(xml),ExceptionInfo *exception)
1355 if ((root->node == (XMLTreeInfo *) NULL) ||
1356 (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0))
1358 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1359 "ParseError","unexpected closing tag </%s>",tag);
1360 return(&root->root);
1362 root->node=root->node->parent;
1363 return((XMLTreeInfo *) NULL);
1366 static MagickBooleanType ValidateEntities(char *tag,char *xml,char **entities)
1372 Check for circular entity references.
1376 while ((*xml != '\0') && (*xml != '&'))
1380 if (strncmp(xml+1,tag,strlen(tag)) == 0)
1381 return(MagickFalse);
1383 while ((entities[i] != (char *) NULL) &&
1384 (strncmp(entities[i],xml+1,strlen(entities[i]) == 0)))
1386 if ((entities[i] != (char *) NULL) &&
1387 (ValidateEntities(tag,entities[i+1],entities) == 0))
1388 return(MagickFalse);
1393 static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml,
1407 xml+=strcspn(xml,XMLWhitespace);
1411 xml+=strspn(xml+1,XMLWhitespace)+1;
1413 if (strcmp(target,"xml") == 0)
1415 xml=strstr(xml,"standalone");
1416 if ((xml != (char *) NULL) &&
1417 (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0))
1418 root->standalone=MagickTrue;
1421 if (root->processing_instructions[0] == (char **) NULL)
1423 root->processing_instructions=(char ***) AcquireAlignedMemory(1,sizeof(
1424 *root->processing_instructions));
1425 if (root->processing_instructions ==(char ***) NULL)
1426 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1427 *root->processing_instructions=(char **) NULL;
1430 while ((root->processing_instructions[i] != (char **) NULL) &&
1431 (strcmp(target,root->processing_instructions[i][0]) != 0))
1433 if (root->processing_instructions[i] == (char **) NULL)
1435 root->processing_instructions=(char ***) ResizeQuantumMemory(
1436 root->processing_instructions,(size_t) (i+2),
1437 sizeof(*root->processing_instructions));
1438 if (root->processing_instructions == (char ***) NULL)
1439 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1440 root->processing_instructions[i]=(char **) AcquireQuantumMemory(3,
1441 sizeof(**root->processing_instructions));
1442 if (root->processing_instructions[i] == (char **) NULL)
1443 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1444 root->processing_instructions[i+1]=(char **) NULL;
1445 root->processing_instructions[i][0]=ConstantString(target);
1446 root->processing_instructions[i][1]=(char *)
1447 root->processing_instructions[i+1];
1448 root->processing_instructions[i+1]=(char **) NULL;
1449 root->processing_instructions[i][2]=ConstantString("");
1452 while (root->processing_instructions[i][j] != (char *) NULL)
1454 root->processing_instructions[i]=(char **) ResizeQuantumMemory(
1455 root->processing_instructions[i],(size_t) (j+3),
1456 sizeof(**root->processing_instructions));
1457 if (root->processing_instructions[i] == (char **) NULL)
1458 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1459 root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory(
1460 root->processing_instructions[i][j+1],(size_t) (j+1),
1461 sizeof(**root->processing_instructions));
1462 if (root->processing_instructions[i][j+2] == (char *) NULL)
1463 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1464 (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1,
1465 root->root.tag != (char *) NULL ? ">" : "<",2);
1466 root->processing_instructions[i][j]=ConstantString(xml);
1467 root->processing_instructions[i][j+1]=(char *) NULL;
1470 static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml,
1471 size_t length,ExceptionInfo *exception)
1477 **predefined_entitites,
1489 predefined_entitites=(char **) AcquireAlignedMemory(1,sizeof(sentinel));
1490 if (predefined_entitites == (char **) NULL)
1491 ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed");
1492 (void) CopyMagickMemory(predefined_entitites,sentinel,sizeof(sentinel));
1493 for (xml[length]='\0'; xml != (char *) NULL; )
1495 while ((*xml != '\0') && (*xml != '<') && (*xml != '%'))
1499 if (strncmp(xml,"<!ENTITY",8) == 0)
1502 Parse entity definitions.
1504 xml+=strspn(xml+8,XMLWhitespace)+8;
1506 n=xml+strspn(xml,XMLWhitespace "%");
1507 xml=n+strcspn(n,XMLWhitespace);
1509 v=xml+strspn(xml+1,XMLWhitespace)+1;
1512 if ((q != '"') && (q != '\''))
1517 xml=strchr(xml,'>');
1520 entities=(*c == '%') ? predefined_entitites : root->entities;
1521 for (i=0; entities[i] != (char *) NULL; i++) ;
1522 entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3),
1524 if (entities == (char **) NULL)
1525 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1527 predefined_entitites=entities;
1529 root->entities=entities;
1533 if (xml != (char *) NULL)
1538 entities[i+1]=ParseEntities(v,predefined_entitites,'%');
1539 entities[i+2]=(char *) NULL;
1540 if (ValidateEntities(n,entities[i+1],entities) != MagickFalse)
1544 if (entities[i+1] != v)
1545 entities[i+1]=DestroyString(entities[i+1]);
1546 (void) ThrowMagickException(exception,GetMagickModule(),
1547 OptionWarning,"ParseError","circular entity declaration &%s",n);
1548 predefined_entitites=(char **) RelinquishMagickMemory(
1549 predefined_entitites);
1550 return(MagickFalse);
1554 if (strncmp(xml,"<!ATTLIST",9) == 0)
1557 Parse default attributes.
1559 t=xml+strspn(xml+9,XMLWhitespace)+9;
1562 (void) ThrowMagickException(exception,GetMagickModule(),
1563 OptionWarning,"ParseError","unclosed <!ATTLIST");
1564 predefined_entitites=(char **) RelinquishMagickMemory(
1565 predefined_entitites);
1566 return(MagickFalse);
1568 xml=t+strcspn(t,XMLWhitespace ">");
1573 while ((root->attributes[i] != (char **) NULL) &&
1574 (strcmp(n,root->attributes[i][0]) != 0))
1576 while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') &&
1579 xml=n+strcspn(n,XMLWhitespace);
1584 (void) ThrowMagickException(exception,GetMagickModule(),
1585 OptionWarning,"ParseError","malformed <!ATTLIST");
1586 predefined_entitites=(char **) RelinquishMagickMemory(
1587 predefined_entitites);
1588 return(MagickFalse);
1590 xml+=strspn(xml+1,XMLWhitespace)+1;
1591 c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
1592 if (strncmp(xml,"NOTATION",8) == 0)
1593 xml+=strspn(xml+8,XMLWhitespace)+8;
1594 xml=(*xml == '(') ? strchr(xml,')') : xml+
1595 strcspn(xml,XMLWhitespace);
1596 if (xml == (char *) NULL)
1598 (void) ThrowMagickException(exception,GetMagickModule(),
1599 OptionWarning,"ParseError","malformed <!ATTLIST");
1600 predefined_entitites=(char **) RelinquishMagickMemory(
1601 predefined_entitites);
1602 return(MagickFalse);
1604 xml+=strspn(xml,XMLWhitespace ")");
1605 if (strncmp(xml,"#FIXED",6) == 0)
1606 xml+=strspn(xml+6,XMLWhitespace)+6;
1609 xml+=strcspn(xml,XMLWhitespace ">")-1;
1615 if (((*xml == '"') || (*xml == '\'')) &&
1616 ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
1620 (void) ThrowMagickException(exception,GetMagickModule(),
1621 OptionWarning,"ParseError","malformed <!ATTLIST");
1622 predefined_entitites=(char **) RelinquishMagickMemory(
1623 predefined_entitites);
1624 return(MagickFalse);
1626 if (root->attributes[i] == (char **) NULL)
1632 root->attributes=(char ***) AcquireQuantumMemory(2,
1633 sizeof(*root->attributes));
1635 root->attributes=(char ***) ResizeQuantumMemory(
1636 root->attributes,(size_t) (i+2),
1637 sizeof(*root->attributes));
1638 if (root->attributes == (char ***) NULL)
1639 ThrowFatalException(ResourceLimitFatalError,
1640 "MemoryAllocationFailed");
1641 root->attributes[i]=(char **) AcquireQuantumMemory(2,
1642 sizeof(*root->attributes));
1643 if (root->attributes[i] == (char **) NULL)
1644 ThrowFatalException(ResourceLimitFatalError,
1645 "MemoryAllocationFailed");
1646 root->attributes[i][0]=ConstantString(t);
1647 root->attributes[i][1]=(char *) NULL;
1648 root->attributes[i+1]=(char **) NULL;
1650 for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
1651 root->attributes[i]=(char **) ResizeQuantumMemory(
1652 root->attributes[i],(size_t) (j+4),sizeof(*root->attributes));
1653 if (root->attributes[i] == (char **) NULL)
1654 ThrowFatalException(ResourceLimitFatalError,
1655 "MemoryAllocationFailed");
1656 root->attributes[i][j+3]=(char *) NULL;
1657 root->attributes[i][j+2]=ConstantString(c);
1658 root->attributes[i][j+1]=(char *) NULL;
1659 if (v != (char *) NULL)
1660 root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
1661 root->attributes[i][j]=ConstantString(n);
1665 if (strncmp(xml, "<!--", 4) == 0)
1666 xml=strstr(xml+4,"-->");
1668 if (strncmp(xml,"<?", 2) == 0)
1672 if (xml != (char *) NULL)
1674 ParseProcessingInstructions(root,c,(size_t) (xml-c));
1680 xml=strchr(xml,'>');
1682 if ((*(xml++) == '%') && (root->standalone == MagickFalse))
1685 predefined_entitites=(char **) RelinquishMagickMemory(predefined_entitites);
1689 static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
1694 xml_info=root->node;
1695 if (xml_info->tag == (char *) NULL)
1696 xml_info->tag=ConstantString(tag);
1698 xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
1699 xml_info->attributes=attributes;
1700 root->node=xml_info;
1703 MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1735 Convert xml-string to UTF8.
1737 if ((xml == (const char *) NULL) || (strlen(xml) == 0))
1739 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1740 "ParseError","root tag missing");
1741 return((XMLTreeInfo *) NULL);
1743 root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
1745 utf8=ConvertUTF16ToUTF8(xml,&length);
1746 if (utf8 == (char *) NULL)
1748 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1749 "ParseError","UTF16 to UTF8 failed");
1750 return((XMLTreeInfo *) NULL);
1752 terminal=utf8[length-1];
1753 utf8[length-1]='\0';
1755 while ((*p != '\0') && (*p != '<'))
1759 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1760 "ParseError","root tag missing");
1761 utf8=DestroyString(utf8);
1762 return((XMLTreeInfo *) NULL);
1764 attribute=(char **) NULL;
1767 attributes=(char **) sentinel;
1769 if ((isalpha((int) ((unsigned char) *p)) !=0) || (*p == '_') ||
1770 (*p == ':') || (((int) *p) < '\0'))
1775 if (root->node == (XMLTreeInfo *) NULL)
1777 (void) ThrowMagickException(exception,GetMagickModule(),
1778 OptionWarning,"ParseError","root tag missing");
1779 utf8=DestroyString(utf8);
1780 return(&root->root);
1782 p+=strcspn(p,XMLWhitespace "/>");
1783 while (isspace((int) ((unsigned char) *p)) != 0)
1785 if ((*p != '\0') && (*p != '/') && (*p != '>'))
1788 Find tag in default attributes list.
1791 while ((root->attributes[i] != (char **) NULL) &&
1792 (strcmp(root->attributes[i][0],tag) != 0))
1794 attribute=root->attributes[i];
1796 for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
1802 attributes=(char **) AcquireQuantumMemory(4,sizeof(*attributes));
1804 attributes=(char **) ResizeQuantumMemory(attributes,(size_t) (l+4),
1805 sizeof(*attributes));
1806 if (attributes == (char **) NULL)
1808 (void) ThrowMagickException(exception,GetMagickModule(),
1809 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
1810 utf8=DestroyString(utf8);
1811 return(&root->root);
1813 attributes[l+2]=(char *) NULL;
1814 attributes[l+1]=(char *) NULL;
1816 p+=strcspn(p,XMLWhitespace "=/>");
1817 if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
1818 attributes[l]=ConstantString("");
1822 p+=strspn(p,XMLWhitespace "=");
1824 if ((c == '"') || (c == '\''))
1831 while ((*p != '\0') && (*p != c))
1837 attributes[l]=ConstantString("");
1838 attributes[l+1]=ConstantString("");
1839 (void) DestroyXMLTreeAttributes(attributes);
1840 (void) ThrowMagickException(exception,GetMagickModule(),
1841 OptionWarning,"ParseError","missing %c",c);
1842 utf8=DestroyString(utf8);
1843 return(&root->root);
1846 while ((attribute != (char **) NULL) &&
1847 (attribute[j] != (char *) NULL) &&
1848 (strcmp(attribute[j],attributes[l]) != 0))
1850 attributes[l+1]=ParseEntities(attributes[l+1],root->entities,
1851 (attribute != (char **) NULL) && (attribute[j] !=
1852 (char *) NULL) ? *attribute[j+2] : ' ');
1854 attributes[l]=ConstantString(attributes[l]);
1856 while (isspace((int) ((unsigned char) *p)) != 0)
1865 if (((*p != '\0') && (*p != '>')) ||
1866 ((*p == '\0') && (terminal != '>')))
1869 (void) DestroyXMLTreeAttributes(attributes);
1870 (void) ThrowMagickException(exception,GetMagickModule(),
1871 OptionWarning,"ParseError","missing >");
1872 utf8=DestroyString(utf8);
1873 return(&root->root);
1875 ParseOpenTag(root,tag,attributes);
1876 (void) ParseCloseTag(root,tag,p,exception);
1881 if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
1884 ParseOpenTag(root,tag,attributes);
1890 (void) DestroyXMLTreeAttributes(attributes);
1891 (void) ThrowMagickException(exception,GetMagickModule(),
1892 OptionWarning,"ParseError","missing >");
1893 utf8=DestroyString(utf8);
1894 return(&root->root);
1905 p+=strcspn(tag,XMLWhitespace ">")+1;
1907 if ((c == '\0') && (terminal != '>'))
1909 (void) ThrowMagickException(exception,GetMagickModule(),
1910 OptionWarning,"ParseError","missing >");
1911 utf8=DestroyString(utf8);
1912 return(&root->root);
1915 if (ParseCloseTag(root,tag,p,exception) != (XMLTreeInfo *) NULL)
1917 utf8=DestroyString(utf8);
1918 return(&root->root);
1921 if (isspace((int) ((unsigned char) *p)) != 0)
1922 p+=strspn(p,XMLWhitespace);
1925 if (strncmp(p,"!--",3) == 0)
1931 if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
1932 ((*p == '\0') && (terminal != '>')))
1934 (void) ThrowMagickException(exception,GetMagickModule(),
1935 OptionWarning,"ParseError","unclosed <!--");
1936 utf8=DestroyString(utf8);
1937 return(&root->root);
1941 if (strncmp(p,"![CDATA[",8) == 0)
1947 if (p != (char *) NULL)
1950 ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
1954 (void) ThrowMagickException(exception,GetMagickModule(),
1955 OptionWarning,"ParseError","unclosed <![CDATA[");
1956 utf8=DestroyString(utf8);
1957 return(&root->root);
1961 if (strncmp(p,"!DOCTYPE",8) == 0)
1966 for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
1967 ((l != 0) && ((*p != ']') ||
1968 (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
1969 l=(*p == '[') ? 1 : l)
1970 p+=strcspn(p+1,"[]>")+1;
1971 if ((*p == '\0') && (terminal != '>'))
1973 (void) ThrowMagickException(exception,GetMagickModule(),
1974 OptionWarning,"ParseError","unclosed <!DOCTYPE");
1975 utf8=DestroyString(utf8);
1976 return(&root->root);
1979 tag=strchr(tag,'[')+1;
1982 status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
1984 if (status == MagickFalse)
1986 utf8=DestroyString(utf8);
1987 return(&root->root);
1996 Processing instructions.
2001 if (p == (char *) NULL)
2004 } while ((*p != '\0') && (*p != '>'));
2005 if ((p == (char *) NULL) || ((*p == '\0') &&
2008 (void) ThrowMagickException(exception,GetMagickModule(),
2009 OptionWarning,"ParseError","unclosed <?");
2010 utf8=DestroyString(utf8);
2011 return(&root->root);
2013 ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
2017 (void) ThrowMagickException(exception,GetMagickModule(),
2018 OptionWarning,"ParseError","unexpected <");
2019 utf8=DestroyString(utf8);
2020 return(&root->root);
2022 if ((p == (char *) NULL) || (*p == '\0'))
2026 if ((*p != '\0') && (*p != '<'))
2029 Tag character content.
2031 while ((*p != '\0') && (*p != '<'))
2035 ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
2041 utf8=DestroyString(utf8);
2042 if (root->node == (XMLTreeInfo *) NULL)
2043 return(&root->root);
2044 if (root->node->tag == (char *) NULL)
2046 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2047 "ParseError","root tag missing");
2048 return(&root->root);
2050 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2051 "ParseError","unclosed tag: `%s'",root->node->tag);
2052 return(&root->root);
2056 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2060 % N e w X M L T r e e T a g %
2064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2066 % NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
2068 % The format of the NewXMLTreeTag method is:
2070 % XMLTreeInfo *NewXMLTreeTag(const char *tag)
2072 % A description of each parameter follows:
2077 MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
2080 *predefined_entities[NumberPredefinedEntities+1] =
2082 "lt;", "<", "gt;", ">", "quot;", """,
2083 "apos;", "'", "amp;", "&", (char *) NULL
2089 root=(XMLTreeRoot *) AcquireAlignedMemory(1,sizeof(*root));
2090 if (root == (XMLTreeRoot *) NULL)
2091 return((XMLTreeInfo *) NULL);
2092 (void) ResetMagickMemory(root,0,sizeof(*root));
2093 root->root.tag=(char *) NULL;
2094 if (tag != (char *) NULL)
2095 root->root.tag=ConstantString(tag);
2096 root->node=(&root->root);
2097 root->root.content=ConstantString("");
2098 root->entities=(char **) AcquireAlignedMemory(1,sizeof(predefined_entities));
2099 if (root->entities == (char **) NULL)
2100 return((XMLTreeInfo *) NULL);
2101 (void) CopyMagickMemory(root->entities,predefined_entities,
2102 sizeof(predefined_entities));
2103 root->root.attributes=sentinel;
2104 root->attributes=(char ***) root->root.attributes;
2105 root->processing_instructions=(char ***) root->root.attributes;
2106 root->debug=IsEventLogging();
2107 root->signature=MagickSignature;
2108 return(&root->root);
2112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2116 % P r u n e T a g F r o m X M L T r e e %
2120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2122 % PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
2125 % The format of the PruneTagFromXMLTree method is:
2127 % XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2129 % A description of each parameter follows:
2131 % o xml_info: the xml info.
2134 MagickExport XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2139 assert(xml_info != (XMLTreeInfo *) NULL);
2140 assert((xml_info->signature == MagickSignature) ||
2141 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2142 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2143 if (xml_info->next != (XMLTreeInfo *) NULL)
2144 xml_info->next->sibling=xml_info->sibling;
2145 if (xml_info->parent != (XMLTreeInfo *) NULL)
2147 node=xml_info->parent->child;
2148 if (node == xml_info)
2149 xml_info->parent->child=xml_info->ordered;
2152 while (node->ordered != xml_info)
2154 node->ordered=node->ordered->ordered;
2155 node=xml_info->parent->child;
2156 if (strcmp(node->tag,xml_info->tag) != 0)
2158 while (strcmp(node->sibling->tag,xml_info->tag) != 0)
2160 if (node->sibling != xml_info)
2163 node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
2164 xml_info->next : node->sibling->sibling;
2166 while ((node->next != (XMLTreeInfo *) NULL) &&
2167 (node->next != xml_info))
2169 if (node->next != (XMLTreeInfo *) NULL)
2170 node->next=node->next->next;
2173 xml_info->ordered=(XMLTreeInfo *) NULL;
2174 xml_info->sibling=(XMLTreeInfo *) NULL;
2175 xml_info->next=(XMLTreeInfo *) NULL;
2180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2184 % S e t X M L T r e e A t t r i b u t e %
2188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2190 % SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
2191 % found. A value of NULL removes the specified attribute.
2193 % The format of the SetXMLTreeAttribute method is:
2195 % XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
2196 % const char *value)
2198 % A description of each parameter follows:
2200 % o xml_info: the xml info.
2202 % o tag: The attribute tag.
2204 % o value: The attribute value.
2207 MagickExport XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
2208 const char *tag,const char *value)
2219 assert(xml_info != (XMLTreeInfo *) NULL);
2220 assert((xml_info->signature == MagickSignature) ||
2221 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2222 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2224 while ((xml_info->attributes[i] != (char *) NULL) &&
2225 (strcmp(xml_info->attributes[i],tag) != 0))
2227 if (xml_info->attributes[i] == (char *) NULL)
2230 Add new attribute tag.
2232 if (value == (const char *) NULL)
2234 if (xml_info->attributes != sentinel)
2235 xml_info->attributes=(char **) ResizeQuantumMemory(
2236 xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
2239 xml_info->attributes=(char **) AcquireQuantumMemory(4,
2240 sizeof(*xml_info->attributes));
2241 if (xml_info->attributes != (char **) NULL)
2242 xml_info->attributes[1]=ConstantString("");
2244 if (xml_info->attributes == (char **) NULL)
2245 ThrowFatalException(ResourceLimitFatalError,
2246 "UnableToAcquireString");
2247 xml_info->attributes[i]=ConstantString(tag);
2248 xml_info->attributes[i+2]=(char *) NULL;
2249 length=strlen(xml_info->attributes[i+1]);
2252 Add new value to an existing attribute.
2254 for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
2255 if (xml_info->attributes[i+1] != (char *) NULL)
2256 xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
2257 if (value != (const char *) NULL)
2259 xml_info->attributes[i+1]=ConstantString(value);
2262 if (xml_info->attributes[i] != (char *) NULL)
2263 xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
2264 (void) CopyMagickMemory(xml_info->attributes+i,xml_info->attributes+i+2,
2265 (size_t) (j-i)*sizeof(*xml_info->attributes));
2267 xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
2268 (size_t) (j+2),sizeof(*xml_info->attributes));
2269 if (xml_info->attributes == (char **) NULL)
2270 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2271 (void) CopyMagickMemory(xml_info->attributes[j+1]+(i/2),
2272 xml_info->attributes[j+1]+(i/2)+1,(size_t) ((j/2)-(i/2))*
2273 sizeof(*xml_info->attributes));
2278 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2282 % S e t X M L T r e e C o n t e n t %
2286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2288 % SetXMLTreeContent() sets the character content for the given tag and
2291 % The format of the SetXMLTreeContent method is:
2293 % XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2294 % const char *content)
2296 % A description of each parameter follows:
2298 % o xml_info: the xml info.
2300 % o content: The content.
2303 MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2304 const char *content)
2306 assert(xml_info != (XMLTreeInfo *) NULL);
2307 assert((xml_info->signature == MagickSignature) ||
2308 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2309 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2310 if (xml_info->content != (char *) NULL)
2311 xml_info->content=DestroyString(xml_info->content);
2312 xml_info->content=(char *) ConstantString(content);
2317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2321 % X M L T r e e I n f o T o X M L %
2325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2327 % XMLTreeInfoToXML() converts an xml-tree to an XML string.
2329 % The format of the XMLTreeInfoToXML method is:
2331 % char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2333 % A description of each parameter follows:
2335 % o xml_info: the xml info.
2339 static char *EncodePredefinedEntities(const char *source,ssize_t offset,
2340 char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
2346 canonical_content=CanonicalXMLContent(source,pedantic);
2352 content=AcquireString(source);
2353 content[offset]='\0';
2354 canonical_content=CanonicalXMLContent(content,pedantic);
2355 content=DestroyString(content);
2357 if (canonical_content == (char *) NULL)
2358 return(*destination);
2359 if ((*length+strlen(canonical_content)+MaxTextExtent) > *extent)
2361 *extent=(*length)+strlen(canonical_content)+MaxTextExtent;
2362 *destination=(char *) ResizeQuantumMemory(*destination,*extent,
2363 sizeof(**destination));
2364 if (*destination == (char *) NULL)
2365 return(*destination);
2367 *length+=FormatMagickString(*destination+(*length),*extent,"%s",
2369 canonical_content=DestroyString(canonical_content);
2370 return(*destination);
2373 static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
2374 size_t *extent,size_t start,char ***attributes)
2391 content=(char *) "";
2392 if (xml_info->parent != (XMLTreeInfo *) NULL)
2393 content=xml_info->parent->content;
2395 *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
2396 start),source,length,extent,MagickFalse);
2397 if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2399 *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2400 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(*source));
2401 if (*source == (char *) NULL)
2404 *length+=FormatMagickString(*source+(*length),*extent,"<%s",xml_info->tag);
2405 for (i=0; xml_info->attributes[i]; i+=2)
2407 attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
2408 if (attribute != xml_info->attributes[i+1])
2410 if ((*length+strlen(xml_info->attributes[i])+MaxTextExtent) > *extent)
2412 *extent=(*length)+strlen(xml_info->attributes[i])+MaxTextExtent;
2413 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2414 if (*source == (char *) NULL)
2415 return((char *) NULL);
2417 *length+=FormatMagickString(*source+(*length),*extent," %s=\"",
2418 xml_info->attributes[i]);
2419 (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
2421 *length+=FormatMagickString(*source+(*length),*extent,"\"");
2424 while ((attributes[i] != (char **) NULL) &&
2425 (strcmp(attributes[i][0],xml_info->tag) != 0))
2428 while ((attributes[i] != (char **) NULL) &&
2429 (attributes[i][j] != (char *) NULL))
2431 if ((attributes[i][j+1] == (char *) NULL) ||
2432 (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
2437 if ((*length+strlen(attributes[i][j])+MaxTextExtent) > *extent)
2439 *extent=(*length)+strlen(attributes[i][j])+MaxTextExtent;
2440 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2441 if (*source == (char *) NULL)
2442 return((char *) NULL);
2444 *length+=FormatMagickString(*source+(*length),*extent," %s=\"",
2446 (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
2448 *length+=FormatMagickString(*source+(*length),*extent,"\"");
2451 *length+=FormatMagickString(*source+(*length),*extent,*xml_info->content ?
2453 if (xml_info->child != (XMLTreeInfo *) NULL)
2454 *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
2456 *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
2458 if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2460 *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2461 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2462 if (*source == (char *) NULL)
2463 return((char *) NULL);
2465 if (*xml_info->content != '\0')
2466 *length+=FormatMagickString(*source+(*length),*extent,"</%s>",
2468 while ((content[offset] != '\0') && (offset < xml_info->offset))
2470 if (xml_info->ordered != (XMLTreeInfo *) NULL)
2471 content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
2474 content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
2479 MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2506 assert(xml_info != (XMLTreeInfo *) NULL);
2507 assert((xml_info->signature == MagickSignature) ||
2508 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2509 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2510 if (xml_info->tag == (char *) NULL)
2511 return((char *) NULL);
2512 xml=AcquireString((char *) NULL);
2514 extent=MaxTextExtent;
2515 root=(XMLTreeRoot *) xml_info;
2516 while (root->root.parent != (XMLTreeInfo *) NULL)
2517 root=(XMLTreeRoot *) root->root.parent;
2518 parent=(XMLTreeInfo *) NULL;
2519 if (xml_info != (XMLTreeInfo *) NULL)
2520 parent=xml_info->parent;
2521 if (parent == (XMLTreeInfo *) NULL)
2522 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2525 Pre-root processing instructions.
2527 for (k=2; root->processing_instructions[i][k-1]; k++) ;
2528 p=root->processing_instructions[i][1];
2529 for (j=1; p != (char *) NULL; j++)
2531 if (root->processing_instructions[i][k][j-1] == '>')
2533 p=root->processing_instructions[i][j];
2536 q=root->processing_instructions[i][0];
2537 if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2539 extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2540 xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2541 if (xml == (char *) NULL)
2544 length+=FormatMagickString(xml+length,extent,"<?%s%s%s?>\n",q,
2545 *p != '\0' ? " " : "",p);
2546 p=root->processing_instructions[i][j];
2549 ordered=(XMLTreeInfo *) NULL;
2550 if (xml_info != (XMLTreeInfo *) NULL)
2551 ordered=xml_info->ordered;
2552 xml_info->parent=(XMLTreeInfo *) NULL;
2553 xml_info->ordered=(XMLTreeInfo *) NULL;
2554 xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
2555 xml_info->parent=parent;
2556 xml_info->ordered=ordered;
2557 if (parent == (XMLTreeInfo *) NULL)
2558 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2561 Post-root processing instructions.
2563 for (k=2; root->processing_instructions[i][k-1]; k++) ;
2564 p=root->processing_instructions[i][1];
2565 for (j=1; p != (char *) NULL; j++)
2567 if (root->processing_instructions[i][k][j-1] == '<')
2569 p=root->processing_instructions[i][j];
2572 q=root->processing_instructions[i][0];
2573 if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2575 extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2576 xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2577 if (xml == (char *) NULL)
2580 length+=FormatMagickString(xml+length,extent,"\n<?%s%s%s?>",q,
2581 *p != '\0' ? " " : "",p);
2582 p=root->processing_instructions[i][j];
2585 return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));