MagickCore  6.9.13-51
Convert, Edit, Or Compose Bitmap Images
xml-tree.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % X X M M L %
7 % X X MM MM L %
8 % X M M M L %
9 % X X M M L %
10 % X X M M LLLLL %
11 % %
12 % TTTTT RRRR EEEEE EEEEE %
13 % T R R E E %
14 % T RRRR EEE EEE %
15 % T R R E E %
16 % T R R EEEEE EEEEE %
17 % %
18 % %
19 % XML Tree Methods %
20 % %
21 % Software Design %
22 % Cristy %
23 % December 2004 %
24 % %
25 % %
26 % Copyright 1999 ImageMagick Studio LLC, a non-profit organization %
27 % dedicated to making software imaging solutions freely available. %
28 % %
29 % You may not use this file except in compliance with the License. You may %
30 % obtain a copy of the License at %
31 % %
32 % https://imagemagick.org/license/ %
33 % %
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. %
39 % %
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 %
42 % This module implements the standard handy xml-tree methods for storing and
43 % retrieving nodes and attributes from an XML string.
44 %
45 */
46 
47 /*
48  Include declarations.
49 */
50 #include "magick/studio.h"
51 #include "magick/blob.h"
52 #include "magick/blob-private.h"
53 #include "magick/exception.h"
54 #include "magick/exception-private.h"
55 #include "magick/image-private.h"
56 #include "magick/log.h"
57 #include "magick/memory_.h"
58 #include "magick/semaphore.h"
59 #include "magick/string_.h"
60 #include "magick/string-private.h"
61 #include "magick/token-private.h"
62 #include "magick/utility.h"
63 #include "magick/utility-private.h"
64 #include "magick/xml-tree.h"
65 #include "magick/xml-tree-private.h"
66 
67 /*
68  Define declarations.
69 */
70 #define NumberPredefinedEntities 10
71 #define XMLWhitespace "\t\r\n "
72 
73 /*
74  Typedef declarations.
75 */
77 {
78  char
79  *tag,
80  **attributes,
81  *content;
82 
83  size_t
84  offset;
85 
87  *parent,
88  *next,
89  *sibling,
90  *ordered,
91  *child;
92 
93  MagickBooleanType
94  debug;
95 
97  *semaphore;
98 
99  size_t
100  signature;
101 };
102 
103 typedef struct _XMLTreeRoot
104  XMLTreeRoot;
105 
107 {
108  struct _XMLTreeInfo
109  root;
110 
112  *node;
113 
114  MagickBooleanType
115  standalone;
116 
117  char
118  ***processing_instructions,
119  **entities,
120  ***attributes;
121 
122  MagickBooleanType
123  debug;
124 
126  *semaphore;
127 
128  size_t
129  signature;
130 };
131 
132 /*
133  Global declarations.
134 */
135 static char
136  *sentinel[] = { (char *) NULL };
137 
138 /*
139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140 % %
141 % %
142 % %
143 % A d d C h i l d T o X M L T r e e %
144 % %
145 % %
146 % %
147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148 %
149 % AddChildToXMLTree() adds a child tag at an offset relative to the start of
150 % the parent tag's character content. Return the child tag.
151 %
152 % The format of the AddChildToXMLTree method is:
153 %
154 % XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,const char *tag,
155 % const size_t offset)
156 %
157 % A description of each parameter follows:
158 %
159 % o xml_info: the xml info.
160 %
161 % o tag: the tag.
162 %
163 % o offset: the tag offset.
164 %
165 */
166 MagickExport XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,
167  const char *tag,const size_t offset)
168 {
170  *child;
171 
172  if (xml_info == (XMLTreeInfo *) NULL)
173  return((XMLTreeInfo *) NULL);
174  child=(XMLTreeInfo *) AcquireMagickMemory(sizeof(*child));
175  if (child == (XMLTreeInfo *) NULL)
176  return((XMLTreeInfo *) NULL);
177  (void) memset(child,0,sizeof(*child));
178  child->tag=ConstantString(tag);
179  child->attributes=sentinel;
180  child->content=ConstantString("");
181  child->debug=IsEventLogging();
182  child->signature=MagickCoreSignature;
183  return(InsertTagIntoXMLTree(xml_info,child,offset));
184 }
185 
186 /*
187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188 % %
189 % %
190 % %
191 % A d d P a t h T o X M L T r e e %
192 % %
193 % %
194 % %
195 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196 %
197 % AddPathToXMLTree() adds a child tag at an offset relative to the start of
198 % the parent tag's character content. This method returns the child tag.
199 %
200 % The format of the AddPathToXMLTree method is:
201 %
202 % XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,const char *path,
203 % const size_t offset)
204 %
205 % A description of each parameter follows:
206 %
207 % o xml_info: the xml info.
208 %
209 % o path: the path.
210 %
211 % o offset: the tag offset.
212 %
213 */
214 MagickExport XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,
215  const char *path,const size_t offset)
216 {
217  char
218  **components,
219  subnode[MaxTextExtent],
220  tag[MaxTextExtent];
221 
222  size_t
223  number_components;
224 
225  ssize_t
226  i,
227  j;
228 
230  *child,
231  *node;
232 
233  assert(xml_info != (XMLTreeInfo *) NULL);
234  assert((xml_info->signature == MagickCoreSignature) ||
235  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
236  if (IsEventLogging() != MagickFalse)
237  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
238  node=xml_info;
239  components=GetPathComponents(path,&number_components);
240  if (components == (char **) NULL)
241  return((XMLTreeInfo *) NULL);
242  for (i=0; i < (ssize_t) number_components; i++)
243  {
244  GetPathComponent(components[i],SubimagePath,subnode);
245  GetPathComponent(components[i],CanonicalPath,tag);
246  child=GetXMLTreeChild(node,tag);
247  if (child == (XMLTreeInfo *) NULL)
248  child=AddChildToXMLTree(node,tag,offset);
249  node=child;
250  if (node == (XMLTreeInfo *) NULL)
251  break;
252  for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
253  {
254  node=GetXMLTreeOrdered(node);
255  if (node == (XMLTreeInfo *) NULL)
256  break;
257  }
258  if (node == (XMLTreeInfo *) NULL)
259  break;
260  components[i]=DestroyString(components[i]);
261  }
262  for ( ; i < (ssize_t) number_components; i++)
263  components[i]=DestroyString(components[i]);
264  components=(char **) RelinquishMagickMemory(components);
265  return(node);
266 }
267 
268 /*
269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270 % %
271 % %
272 % %
273 % C a n o n i c a l X M L C o n t e n t %
274 % %
275 % %
276 % %
277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
278 %
279 % CanonicalXMLContent() converts text to canonical XML content by converting
280 % to UTF-8, substituting predefined entities, wrapping as CDATA, or encoding
281 % as base-64 as required.
282 %
283 % The format of the CanonicalXMLContent method is:
284 %
285 % char *CanonicalXMLContent(const char *content,
286 % const MagickBooleanType pedantic)
287 %
288 % A description of each parameter follows:
289 %
290 % o content: the content.
291 %
292 % o pedantic: if true, replace newlines and tabs with their respective
293 % entities.
294 %
295 */
296 MagickExport char *CanonicalXMLContent(const char *content,
297  const MagickBooleanType pedantic)
298 {
299  char
300  *base64,
301  *canonical_content;
302 
303  const unsigned char
304  *p;
305 
306  size_t
307  extent,
308  length;
309 
310  ssize_t
311  i;
312 
313  unsigned char
314  *utf8;
315 
316  utf8=ConvertLatin1ToUTF8((const unsigned char *) content);
317  if (utf8 == (unsigned char *) NULL)
318  return((char *) NULL);
319  for (p=utf8; *p != '\0'; p++)
320  if ((*p < 0x20) && (*p != 0x09) && (*p != 0x0a) && (*p != 0x0d))
321  break;
322  if (*p != '\0')
323  {
324  /*
325  String is binary, base64-encode it.
326  */
327  base64=Base64Encode(utf8,strlen((char *) utf8),&length);
328  utf8=(unsigned char *) RelinquishMagickMemory(utf8);
329  if (base64 == (char *) NULL)
330  return((char *) NULL);
331  canonical_content=AcquireString("<base64>");
332  (void) ConcatenateString(&canonical_content,base64);
333  base64=DestroyString(base64);
334  (void) ConcatenateString(&canonical_content,"</base64>");
335  return(canonical_content);
336  }
337  /*
338  Substitute predefined entities.
339  */
340  i=0;
341  canonical_content=AcquireString((char *) NULL);
342  extent=MaxTextExtent;
343  for (p=utf8; *p != '\0'; p++)
344  {
345  if ((i+MaxTextExtent) > (ssize_t) extent)
346  {
347  extent+=MaxTextExtent;
348  canonical_content=(char *) ResizeQuantumMemory(canonical_content,extent,
349  sizeof(*canonical_content));
350  if (canonical_content == (char *) NULL)
351  return(canonical_content);
352  }
353  switch (*p)
354  {
355  case '&':
356  {
357  i+=FormatLocaleString(canonical_content+i,extent,"&amp;");
358  break;
359  }
360  case '<':
361  {
362  i+=FormatLocaleString(canonical_content+i,extent,"&lt;");
363  break;
364  }
365  case '>':
366  {
367  i+=FormatLocaleString(canonical_content+i,extent,"&gt;");
368  break;
369  }
370  case '"':
371  {
372  i+=FormatLocaleString(canonical_content+i,extent,"&quot;");
373  break;
374  }
375  case '\n':
376  {
377  if (pedantic == MagickFalse)
378  {
379  canonical_content[i++]=(char) (*p);
380  break;
381  }
382  i+=FormatLocaleString(canonical_content+i,extent,"&#xA;");
383  break;
384  }
385  case '\t':
386  {
387  if (pedantic == MagickFalse)
388  {
389  canonical_content[i++]=(char) (*p);
390  break;
391  }
392  i+=FormatLocaleString(canonical_content+i,extent,"&#x9;");
393  break;
394  }
395  case '\r':
396  {
397  i+=FormatLocaleString(canonical_content+i,extent,"&#xD;");
398  break;
399  }
400  default:
401  {
402  canonical_content[i++]=(char) (*p);
403  break;
404  }
405  }
406  }
407  canonical_content[i]='\0';
408  utf8=(unsigned char *) RelinquishMagickMemory(utf8);
409  return(canonical_content);
410 }
411 
412 /*
413 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
414 % %
415 % %
416 % %
417 % D e s t r o y X M L T r e e %
418 % %
419 % %
420 % %
421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422 %
423 % DestroyXMLTree() destroys the xml-tree.
424 %
425 % The format of the DestroyXMLTree method is:
426 %
427 % XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
428 %
429 % A description of each parameter follows:
430 %
431 % o xml_info: the xml info.
432 %
433 */
434 
435 static XMLTreeInfo
436  *DestroyXMLTree_(XMLTreeInfo *,const size_t);
437 
438 static char **DestroyXMLTreeAttributes(char **attributes)
439 {
440  ssize_t
441  i;
442 
443  /*
444  Destroy a tag attribute list.
445  */
446  if ((attributes == (char **) NULL) || (attributes == sentinel))
447  return((char **) NULL);
448  for (i=0; attributes[i] != (char *) NULL; i+=2)
449  {
450  /*
451  Destroy attribute tag and value.
452  */
453  if (attributes[i] != (char *) NULL)
454  attributes[i]=DestroyString(attributes[i]);
455  if (attributes[i+1] != (char *) NULL)
456  attributes[i+1]=DestroyString(attributes[i+1]);
457  }
458  attributes=(char **) RelinquishMagickMemory(attributes);
459  return((char **) NULL);
460 }
461 
462 static void DestroyXMLTreeChild(XMLTreeInfo *xml_info,
463  const size_t depth)
464 {
466  *child,
467  *node;
468 
469  child=xml_info->child;
470  while (child != (XMLTreeInfo *) NULL)
471  {
472  node=child;
473  child=node->child;
474  node->child=(XMLTreeInfo *) NULL;
475  (void) DestroyXMLTree_(node,depth+1);
476  }
477 }
478 
479 static void DestroyXMLTreeOrdered(XMLTreeInfo *xml_info,
480  const size_t depth)
481 {
483  *node,
484  *ordered;
485 
486  ordered=xml_info->ordered;
487  while (ordered != (XMLTreeInfo *) NULL)
488  {
489  node=ordered;
490  ordered=node->ordered;
491  node->ordered=(XMLTreeInfo *) NULL;
492  (void) DestroyXMLTree_(node,depth+1);
493  }
494 }
495 
496 static void DestroyXMLTreeRoot(XMLTreeInfo *xml_info)
497 {
498  char
499  **attributes;
500 
501  ssize_t
502  i,
503  j;
504 
506  *root;
507 
508  assert(xml_info != (XMLTreeInfo *) NULL);
509  assert((xml_info->signature == MagickCoreSignature) ||
510  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
511  if (IsEventLogging() != MagickFalse)
512  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
513  if (xml_info->parent != (XMLTreeInfo *) NULL)
514  return;
515  /*
516  Free root tag allocations.
517  */
518  root=(XMLTreeRoot *) xml_info;
519  for (i=NumberPredefinedEntities; root->entities[i] != (char *) NULL; i+=2)
520  root->entities[i+1]=DestroyString(root->entities[i+1]);
521  root->entities=(char **) RelinquishMagickMemory(root->entities);
522  for (i=0; root->attributes[i] != (char **) NULL; i++)
523  {
524  attributes=root->attributes[i];
525  if (attributes[0] != (char *) NULL)
526  attributes[0]=DestroyString(attributes[0]);
527  for (j=1; attributes[j] != (char *) NULL; j+=3)
528  {
529  if (attributes[j] != (char *) NULL)
530  attributes[j]=DestroyString(attributes[j]);
531  if (attributes[j+1] != (char *) NULL)
532  attributes[j+1]=DestroyString(attributes[j+1]);
533  if (attributes[j+2] != (char *) NULL)
534  attributes[j+2]=DestroyString(attributes[j+2]);
535  }
536  attributes=(char **) RelinquishMagickMemory(attributes);
537  }
538  if (root->attributes[0] != (char **) NULL)
539  root->attributes=(char ***) RelinquishMagickMemory(root->attributes);
540  if (root->processing_instructions[0] != (char **) NULL)
541  {
542  for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
543  {
544  for (j=0; root->processing_instructions[i][j] != (char *) NULL; j++)
545  root->processing_instructions[i][j]=DestroyString(
546  root->processing_instructions[i][j]);
547  root->processing_instructions[i][j+1]=DestroyString(
548  root->processing_instructions[i][j+1]);
549  root->processing_instructions[i]=(char **) RelinquishMagickMemory(
550  root->processing_instructions[i]);
551  }
552  root->processing_instructions=(char ***) RelinquishMagickMemory(
553  root->processing_instructions);
554  }
555 }
556 
557 static XMLTreeInfo *DestroyXMLTree_(XMLTreeInfo *xml_info,
558  const size_t depth)
559 {
560  assert(xml_info != (XMLTreeInfo *) NULL);
561  assert((xml_info->signature == MagickCoreSignature) ||
562  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
563  if (IsEventLogging() != MagickFalse)
564  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
565  if (depth > MagickMaxRecursionDepth)
566  ThrowFatalException(ResourceLimitFatalError,
567  "MemoryAllocationFailed");
568  DestroyXMLTreeChild(xml_info,depth+1);
569  DestroyXMLTreeOrdered(xml_info,depth+1);
570  DestroyXMLTreeRoot(xml_info);
571  xml_info->attributes=DestroyXMLTreeAttributes(xml_info->attributes);
572  xml_info->content=DestroyString(xml_info->content);
573  xml_info->tag=DestroyString(xml_info->tag);
574  xml_info=(XMLTreeInfo *) RelinquishMagickMemory(xml_info);
575  return((XMLTreeInfo *) NULL);
576 }
577 
578 MagickExport XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
579 {
580  return(DestroyXMLTree_(xml_info,0));
581 }
582 
583 /*
584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
585 % %
586 % %
587 % %
588 % F i l e T o X M L %
589 % %
590 % %
591 % %
592 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
593 %
594 % FileToXML() returns the contents of a file as a XML string.
595 %
596 % The format of the FileToXML method is:
597 %
598 % char *FileToXML(const char *filename,const size_t extent)
599 %
600 % A description of each parameter follows:
601 %
602 % o filename: the filename.
603 %
604 % o extent: Maximum length of the string.
605 %
606 */
607 MagickPrivate char *FileToXML(const char *filename,const size_t extent)
608 {
609  char
610  *xml;
611 
612  int
613  file;
614 
615  MagickOffsetType
616  offset;
617 
618  size_t
619  i,
620  length;
621 
622  ssize_t
623  count;
624 
625  void
626  *map;
627 
628  assert(filename != (const char *) NULL);
629  length=0;
630  file=fileno(stdin);
631  if (LocaleCompare(filename,"-") != 0)
632  file=open_utf8(filename,O_RDONLY | O_BINARY,0);
633  if (file == -1)
634  return((char *) NULL);
635  offset=(MagickOffsetType) lseek(file,0,SEEK_END);
636  count=0;
637  if ((file == fileno(stdin)) || (offset < 0) ||
638  (offset != (MagickOffsetType) ((ssize_t) offset)))
639  {
640  size_t
641  quantum;
642 
643  struct stat
644  file_stats;
645 
646  /*
647  Stream is not seekable.
648  */
649  offset=(MagickOffsetType) lseek(file,0,SEEK_SET);
650  quantum=(size_t) MagickMaxBufferExtent;
651  if ((fstat(file,&file_stats) == 0) && (file_stats.st_size > 0))
652  quantum=(size_t) MagickMin(file_stats.st_size,MagickMaxBufferExtent);
653  xml=(char *) AcquireQuantumMemory(quantum,sizeof(*xml));
654  for (i=0; xml != (char *) NULL; i+=count)
655  {
656  count=read(file,xml+i,quantum);
657  if (count <= 0)
658  {
659  count=0;
660  if (errno != EINTR)
661  break;
662  }
663  if (~((size_t) i) < (quantum+1))
664  {
665  xml=(char *) RelinquishMagickMemory(xml);
666  break;
667  }
668  xml=(char *) ResizeQuantumMemory(xml,i+quantum+1,sizeof(*xml));
669  if ((size_t) (i+count) >= extent)
670  break;
671  }
672  if (LocaleCompare(filename,"-") != 0)
673  file=close_utf8(file);
674  if (xml == (char *) NULL)
675  return((char *) NULL);
676  if (file == -1)
677  {
678  xml=(char *) RelinquishMagickMemory(xml);
679  return((char *) NULL);
680  }
681  length=(size_t) MagickMin(i+count,extent);
682  xml[length]='\0';
683  return(xml);
684  }
685  length=(size_t) MagickMin(offset,(MagickOffsetType) extent);
686  xml=(char *) NULL;
687  if (~length >= (MaxTextExtent-1))
688  xml=(char *) AcquireQuantumMemory(length+MaxTextExtent,sizeof(*xml));
689  if (xml == (char *) NULL)
690  {
691  file=close_utf8(file);
692  return((char *) NULL);
693  }
694  map=MapBlob(file,ReadMode,0,length);
695  if (map != (char *) NULL)
696  {
697  (void) memcpy(xml,map,length);
698  (void) UnmapBlob(map,length);
699  }
700  else
701  {
702  (void) lseek(file,0,SEEK_SET);
703  for (i=0; i < length; i+=count)
704  {
705  count=read(file,xml+i,(size_t) MagickMin(length-i,(size_t) MagickMaxBufferExtent));
706  if (count <= 0)
707  {
708  count=0;
709  if (errno != EINTR)
710  break;
711  }
712  }
713  if (i < length)
714  {
715  file=close_utf8(file)-1;
716  xml=(char *) RelinquishMagickMemory(xml);
717  return((char *) NULL);
718  }
719  }
720  xml[length]='\0';
721  if (LocaleCompare(filename,"-") != 0)
722  file=close_utf8(file);
723  if (file == -1)
724  xml=(char *) RelinquishMagickMemory(xml);
725  return(xml);
726 }
727 
728 /*
729 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
730 % %
731 % %
732 % %
733 % G e t N e x t X M L T r e e T a g %
734 % %
735 % %
736 % %
737 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
738 %
739 % GetNextXMLTreeTag() returns the next tag or NULL if not found.
740 %
741 % The format of the GetNextXMLTreeTag method is:
742 %
743 % XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
744 %
745 % A description of each parameter follows:
746 %
747 % o xml_info: the xml info.
748 %
749 */
750 MagickExport XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
751 {
752  assert(xml_info != (XMLTreeInfo *) NULL);
753  assert((xml_info->signature == MagickCoreSignature) ||
754  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
755  if (IsEventLogging() != MagickFalse)
756  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
757  return(xml_info->next);
758 }
759 
760 /*
761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
762 % %
763 % %
764 % %
765 % G e t X M L T r e e A t t r i b u t e %
766 % %
767 % %
768 % %
769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
770 %
771 % GetXMLTreeAttribute() returns the value of the attribute tag with the
772 % specified tag if found, otherwise NULL.
773 %
774 % The format of the GetXMLTreeAttribute method is:
775 %
776 % const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag)
777 %
778 % A description of each parameter follows:
779 %
780 % o xml_info: the xml info.
781 %
782 % o tag: the attribute tag.
783 %
784 */
785 MagickExport const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,
786  const char *tag)
787 {
788  ssize_t
789  i,
790  j;
791 
793  *root;
794 
795  assert(xml_info != (XMLTreeInfo *) NULL);
796  assert((xml_info->signature == MagickCoreSignature) ||
797  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
798  if (IsEventLogging() != MagickFalse)
799  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
800  if (xml_info->attributes == (char **) NULL)
801  return((const char *) NULL);
802  i=0;
803  while ((xml_info->attributes[i] != (char *) NULL) &&
804  (strcmp(xml_info->attributes[i],tag) != 0))
805  i+=2;
806  if (xml_info->attributes[i] != (char *) NULL)
807  return(xml_info->attributes[i+1]);
808  root=(XMLTreeRoot*) xml_info;
809  while (root->root.parent != (XMLTreeInfo *) NULL)
810  root=(XMLTreeRoot *) root->root.parent;
811  i=0;
812  while ((root->attributes[i] != (char **) NULL) &&
813  (strcmp(root->attributes[i][0],xml_info->tag) != 0))
814  i++;
815  if (root->attributes[i] == (char **) NULL)
816  return((const char *) NULL);
817  j=1;
818  while ((root->attributes[i][j] != (char *) NULL) &&
819  (strcmp(root->attributes[i][j],tag) != 0))
820  j+=3;
821  if (root->attributes[i][j] == (char *) NULL)
822  return((const char *) NULL);
823  return(root->attributes[i][j+1]);
824 }
825 
826 /*
827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
828 % %
829 % %
830 % %
831 % G e t X M L T r e e A t t r i b u t e s %
832 % %
833 % %
834 % %
835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
836 %
837 % GetXMLTreeAttributes() injects all attributes associated with the current
838 % tag in the specified splay-tree.
839 %
840 % The format of the GetXMLTreeAttributes method is:
841 %
842 % MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
843 % SplayTreeInfo *attributes)
844 %
845 % A description of each parameter follows:
846 %
847 % o xml_info: the xml info.
848 %
849 % o attributes: the attribute splay-tree.
850 %
851 */
852 MagickExport MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
853  SplayTreeInfo *attributes)
854 {
855  ssize_t
856  i;
857 
858  assert(xml_info != (XMLTreeInfo *) NULL);
859  assert((xml_info->signature == MagickCoreSignature) ||
860  (((const XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
861  assert(attributes != (SplayTreeInfo *) NULL);
862  if (IsEventLogging() != MagickFalse)
863  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
864  if (xml_info->attributes == (char **) NULL)
865  return(MagickTrue);
866  i=0;
867  while (xml_info->attributes[i] != (char *) NULL)
868  {
869  (void) AddValueToSplayTree(attributes,
870  ConstantString(xml_info->attributes[i]),
871  ConstantString(xml_info->attributes[i+1]));
872  i+=2;
873  }
874  return(MagickTrue);
875 }
876 
877 /*
878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
879 % %
880 % %
881 % %
882 % G e t X M L T r e e C h i l d %
883 % %
884 % %
885 % %
886 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
887 %
888 % GetXMLTreeChild() returns the first child tag with the specified tag if
889 % found, otherwise NULL.
890 %
891 % The format of the GetXMLTreeChild method is:
892 %
893 % XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
894 %
895 % A description of each parameter follows:
896 %
897 % o xml_info: the xml info.
898 %
899 */
900 MagickExport XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
901 {
903  *child;
904 
905  assert(xml_info != (XMLTreeInfo *) NULL);
906  assert((xml_info->signature == MagickCoreSignature) ||
907  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
908  if (IsEventLogging() != MagickFalse)
909  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
910  child=xml_info->child;
911  if (tag != (const char *) NULL)
912  while ((child != (XMLTreeInfo *) NULL) && (strcmp(child->tag,tag) != 0))
913  child=child->sibling;
914  return(child);
915 }
916 
917 /*
918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
919 % %
920 % %
921 % %
922 % G e t X M L T r e e C o n t e n t %
923 % %
924 % %
925 % %
926 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
927 %
928 % GetXMLTreeContent() returns any content associated with specified
929 % xml-tree node.
930 %
931 % The format of the GetXMLTreeContent method is:
932 %
933 % const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
934 %
935 % A description of each parameter follows:
936 %
937 % o xml_info: the xml info.
938 %
939 */
940 MagickExport const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
941 {
942  assert(xml_info != (XMLTreeInfo *) NULL);
943  assert((xml_info->signature == MagickCoreSignature) ||
944  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
945  if (IsEventLogging() != MagickFalse)
946  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
947  return(xml_info->content);
948 }
949 
950 /*
951 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
952 % %
953 % %
954 % %
955 % G e t X M L T r e e O r d e r e d %
956 % %
957 % %
958 % %
959 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
960 %
961 % GetXMLTreeOrdered() returns the next ordered node if found, otherwise NULL.
962 %
963 % The format of the GetXMLTreeOrdered method is:
964 %
965 % XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
966 %
967 % A description of each parameter follows:
968 %
969 % o xml_info: the xml info.
970 %
971 */
972 MagickExport XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
973 {
974  assert(xml_info != (XMLTreeInfo *) NULL);
975  assert((xml_info->signature == MagickCoreSignature) ||
976  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
977  if (IsEventLogging() != MagickFalse)
978  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
979  return(xml_info->ordered);
980 }
981 
982 /*
983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
984 % %
985 % %
986 % %
987 % G e t X M L T r e e P a t h %
988 % %
989 % %
990 % %
991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
992 %
993 % GetXMLTreePath() traverses the XML-tree as defined by the specified path
994 % and returns the node if found, otherwise NULL.
995 %
996 % The format of the GetXMLTreePath method is:
997 %
998 % XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
999 %
1000 % A description of each parameter follows:
1001 %
1002 % o xml_info: the xml info.
1003 %
1004 % o path: the path (e.g. property/elapsed-time).
1005 %
1006 */
1007 MagickExport XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
1008 {
1009  char
1010  **components,
1011  subnode[MaxTextExtent],
1012  tag[MaxTextExtent];
1013 
1014  size_t
1015  number_components;
1016 
1017  ssize_t
1018  i,
1019  j;
1020 
1021  XMLTreeInfo
1022  *node;
1023 
1024  assert(xml_info != (XMLTreeInfo *) NULL);
1025  assert((xml_info->signature == MagickCoreSignature) ||
1026  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
1027  if (IsEventLogging() != MagickFalse)
1028  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1029  node=xml_info;
1030  components=GetPathComponents(path,&number_components);
1031  if (components == (char **) NULL)
1032  return((XMLTreeInfo *) NULL);
1033  for (i=0; i < (ssize_t) number_components; i++)
1034  {
1035  GetPathComponent(components[i],SubimagePath,subnode);
1036  GetPathComponent(components[i],CanonicalPath,tag);
1037  node=GetXMLTreeChild(node,tag);
1038  if (node == (XMLTreeInfo *) NULL)
1039  break;
1040  for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
1041  {
1042  node=GetXMLTreeOrdered(node);
1043  if (node == (XMLTreeInfo *) NULL)
1044  break;
1045  }
1046  if (node == (XMLTreeInfo *) NULL)
1047  break;
1048  components[i]=DestroyString(components[i]);
1049  }
1050  for ( ; i < (ssize_t) number_components; i++)
1051  components[i]=DestroyString(components[i]);
1052  components=(char **) RelinquishMagickMemory(components);
1053  return(node);
1054 }
1055 
1056 /*
1057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1058 % %
1059 % %
1060 % %
1061 % 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 %
1062 % %
1063 % %
1064 % %
1065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1066 %
1067 % GetXMLTreeProcessingInstructions() returns a null terminated array of
1068 % processing instructions for the given target.
1069 %
1070 % The format of the GetXMLTreeProcessingInstructions method is:
1071 %
1072 % const char **GetXMLTreeProcessingInstructions(XMLTreeInfo *xml_info,
1073 % const char *target)
1074 %
1075 % A description of each parameter follows:
1076 %
1077 % o xml_info: the xml info.
1078 %
1079 */
1080 MagickExport const char **GetXMLTreeProcessingInstructions(
1081  XMLTreeInfo *xml_info,const char *target)
1082 {
1083  ssize_t
1084  i;
1085 
1086  XMLTreeRoot
1087  *root;
1088 
1089  assert(xml_info != (XMLTreeInfo *) NULL);
1090  assert((xml_info->signature == MagickCoreSignature) ||
1091  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
1092  if (IsEventLogging() != MagickFalse)
1093  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1094  root=(XMLTreeRoot *) xml_info;
1095  while (root->root.parent != (XMLTreeInfo *) NULL)
1096  root=(XMLTreeRoot *) root->root.parent;
1097  i=0;
1098  while ((root->processing_instructions[i] != (char **) NULL) &&
1099  (strcmp(root->processing_instructions[i][0],target) != 0))
1100  i++;
1101  if (root->processing_instructions[i] == (char **) NULL)
1102  return((const char **) sentinel);
1103  return((const char **) (root->processing_instructions[i]+1));
1104 }
1105 
1106 /*
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108 % %
1109 % %
1110 % %
1111 % G e t X M L T r e e S i b l i n g %
1112 % %
1113 % %
1114 % %
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116 %
1117 % GetXMLTreeSibling() returns the node sibling if found, otherwise NULL.
1118 %
1119 % The format of the GetXMLTreeSibling method is:
1120 %
1121 % XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
1122 %
1123 % A description of each parameter follows:
1124 %
1125 % o xml_info: the xml info.
1126 %
1127 */
1128 MagickExport XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
1129 {
1130  assert(xml_info != (XMLTreeInfo *) NULL);
1131  assert((xml_info->signature == MagickCoreSignature) ||
1132  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
1133  if (IsEventLogging() != MagickFalse)
1134  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1135  return(xml_info->sibling);
1136 }
1137 
1138 /*
1139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1140 % %
1141 % %
1142 % %
1143 % G e t X M L T r e e T a g %
1144 % %
1145 % %
1146 % %
1147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1148 %
1149 % GetXMLTreeTag() returns the tag associated with specified xml-tree node.
1150 %
1151 % The format of the GetXMLTreeTag method is:
1152 %
1153 % const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
1154 %
1155 % A description of each parameter follows:
1156 %
1157 % o xml_info: the xml info.
1158 %
1159 */
1160 MagickExport const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
1161 {
1162  assert(xml_info != (XMLTreeInfo *) NULL);
1163  assert((xml_info->signature == MagickCoreSignature) ||
1164  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
1165  if (IsEventLogging() != MagickFalse)
1166  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1167  return(xml_info->tag);
1168 }
1169 
1170 /*
1171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1172 % %
1173 % %
1174 % %
1175 % I n s e r t I n t o T a g X M L T r e e %
1176 % %
1177 % %
1178 % %
1179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1180 %
1181 % InsertTagIntoXMLTree() inserts a tag at an offset relative to the start of
1182 % the parent tag's character content. This method returns the child tag.
1183 %
1184 % The format of the InsertTagIntoXMLTree method is:
1185 %
1186 % XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
1187 % XMLTreeInfo *child,const size_t offset)
1188 %
1189 % A description of each parameter follows:
1190 %
1191 % o xml_info: the xml info.
1192 %
1193 % o child: the child tag.
1194 %
1195 % o offset: the tag offset.
1196 %
1197 */
1198 MagickExport XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
1199  XMLTreeInfo *child,const size_t offset)
1200 {
1201  XMLTreeInfo
1202  *head,
1203  *node,
1204  *previous;
1205 
1206  child->ordered=(XMLTreeInfo *) NULL;
1207  child->sibling=(XMLTreeInfo *) NULL;
1208  child->next=(XMLTreeInfo *) NULL;
1209  child->offset=offset;
1210  child->parent=xml_info;
1211  if (xml_info->child == (XMLTreeInfo *) NULL)
1212  {
1213  xml_info->child=child;
1214  return(child);
1215  }
1216  head=xml_info->child;
1217  if (head->offset > offset)
1218  {
1219  child->ordered=head;
1220  xml_info->child=child;
1221  }
1222  else
1223  {
1224  node=head;
1225  while ((node->ordered != (XMLTreeInfo *) NULL) &&
1226  (node->ordered->offset <= offset))
1227  node=node->ordered;
1228  child->ordered=node->ordered;
1229  node->ordered=child;
1230  }
1231  previous=(XMLTreeInfo *) NULL;
1232  node=head;
1233  while ((node != (XMLTreeInfo *) NULL) && (strcmp(node->tag,child->tag) != 0))
1234  {
1235  previous=node;
1236  node=node->sibling;
1237  }
1238  if ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1239  {
1240  while ((node->next != (XMLTreeInfo *) NULL) &&
1241  (node->next->offset <= offset))
1242  node=node->next;
1243  child->next=node->next;
1244  node->next=child;
1245  }
1246  else
1247  {
1248  if ((previous != (XMLTreeInfo *) NULL) && (node != (XMLTreeInfo *) NULL))
1249  previous->sibling=node->sibling;
1250  child->next=node;
1251  previous=(XMLTreeInfo *) NULL;
1252  node=head;
1253  while ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1254  {
1255  previous=node;
1256  node=node->sibling;
1257  }
1258  child->sibling=node;
1259  if (previous != (XMLTreeInfo *) NULL)
1260  previous->sibling=child;
1261  }
1262  return(child);
1263 }
1264 
1265 /*
1266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1267 % %
1268 % %
1269 % %
1270 % N e w X M L T r e e %
1271 % %
1272 % %
1273 % %
1274 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1275 %
1276 % NewXMLTree() returns a XMLTreeInfo xml-tree as defined by the specified
1277 % XML string.
1278 %
1279 % The format of the NewXMLTree method is:
1280 %
1281 % XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1282 %
1283 % A description of each parameter follows:
1284 %
1285 % o xml: A null-terminated XML string.
1286 %
1287 % o exception: return any errors or warnings in this structure.
1288 %
1289 */
1290 
1291 static char *ConvertUTF16ToUTF8(const char *content,size_t *length)
1292 {
1293  char
1294  *utf8;
1295 
1296  int
1297  bits,
1298  byte,
1299  c,
1300  encoding;
1301 
1302  size_t
1303  extent;
1304 
1305  ssize_t
1306  i,
1307  j;
1308 
1309  utf8=(char *) AcquireQuantumMemory(*length+1,sizeof(*utf8));
1310  if (utf8 == (char *) NULL)
1311  return((char *) NULL);
1312  encoding=(*content == '\xFE') ? 1 : (*content == '\xFF') ? 0 : -1;
1313  if (encoding == -1)
1314  {
1315  /*
1316  Already UTF-8.
1317  */
1318  (void) memcpy(utf8,content,*length*sizeof(*utf8));
1319  utf8[*length]='\0';
1320  return(utf8);
1321  }
1322  j=0;
1323  extent=(*length);
1324  for (i=2; i < (ssize_t) (*length-1); i+=2)
1325  {
1326  c=(encoding != 0) ? ((content[i] & 0xff) << 8) | (content[i+1] & 0xff) :
1327  ((content[i+1] & 0xff) << 8) | (content[i] & 0xff);
1328  if ((c >= 0xd800) && (c <= 0xdfff) && ((i+=2) < (ssize_t) (*length-1)))
1329  {
1330  byte=(encoding != 0) ? ((content[i] & 0xff) << 8) |
1331  (content[i+1] & 0xff) : ((content[i+1] & 0xff) << 8) |
1332  (content[i] & 0xff);
1333  c=(((c & 0x3ff) << 10) | (byte & 0x3ff))+0x10000;
1334  }
1335  if ((size_t) (j+MaxTextExtent) > extent)
1336  {
1337  extent=(size_t) j+MaxTextExtent;
1338  utf8=(char *) ResizeQuantumMemory(utf8,extent,sizeof(*utf8));
1339  if (utf8 == (char *) NULL)
1340  return(utf8);
1341  }
1342  if (c < 0x80)
1343  {
1344  utf8[j]=c;
1345  j++;
1346  continue;
1347  }
1348  /*
1349  Multi-byte UTF-8 sequence.
1350  */
1351  byte=c;
1352  for (bits=0; byte != 0; byte/=2)
1353  bits++;
1354  bits=(bits-2)/5;
1355  utf8[j++]=(0xFF << (7-bits)) | (c >> (6*bits));
1356  while (bits != 0)
1357  {
1358  bits--;
1359  utf8[j]=0x80 | ((c >> (6*bits)) & 0x3f);
1360  j++;
1361  }
1362  }
1363  *length=(size_t) j;
1364  utf8=(char *) ResizeQuantumMemory(utf8,(*length+1),sizeof(*utf8));
1365  if (utf8 != (char *) NULL)
1366  utf8[*length]='\0';
1367  return(utf8);
1368 }
1369 
1370 static char *ParseEntities(char *xml,char **entities,int state)
1371 {
1372  char
1373  *entity,
1374  *p,
1375  *q;
1376 
1377  int
1378  byte,
1379  c;
1380 
1381  size_t
1382  extent,
1383  length;
1384 
1385  ssize_t
1386  i,
1387  offset;
1388 
1389  /*
1390  Normalize line endings.
1391  */
1392  p=xml;
1393  q=xml;
1394  for ( ; *xml != '\0'; xml++)
1395  while (*xml == '\r')
1396  {
1397  *(xml++)='\n';
1398  if (*xml == '\n')
1399  (void) memmove(xml,xml+1,strlen(xml));
1400  }
1401  for (xml=p; ; )
1402  {
1403  while ((*xml != '\0') && (*xml != '&') && ((*xml != '%') ||
1404  (state != '%')) && (isspace((int) ((unsigned char) *xml)) == 0))
1405  xml++;
1406  if (*xml == '\0')
1407  break;
1408  /*
1409  States include:
1410  '&' for general entity decoding
1411  '%' for parameter entity decoding
1412  'c' for CDATA sections
1413  ' ' for attributes normalization
1414  '*' for non-CDATA attributes normalization
1415  */
1416  if ((state != 'c') && (strncmp(xml,"&#",2) == 0))
1417  {
1418  /*
1419  Character reference.
1420  */
1421  if (xml[2] != 'x')
1422  c=strtol(xml+2,&entity,10); /* base 10 */
1423  else
1424  c=strtol(xml+3,&entity,16); /* base 16 */
1425  if ((c == 0) || (*entity != ';'))
1426  {
1427  /*
1428  Not a character reference.
1429  */
1430  xml++;
1431  continue;
1432  }
1433  if (c < 0x80)
1434  *(xml++)=c;
1435  else
1436  {
1437  /*
1438  Multi-byte UTF-8 sequence.
1439  */
1440  byte=c;
1441  for (i=0; byte != 0; byte/=2)
1442  i++;
1443  i=(i-2)/5;
1444  *xml=(char) ((0xFF << (7-i)) | (c >> (6*i)));
1445  xml++;
1446  while (i != 0)
1447  {
1448  i--;
1449  *xml=(char) (0x80 | ((c >> (6*i)) & 0x3F));
1450  xml++;
1451  }
1452  }
1453  (void) memmove(xml,strchr(xml,';')+1,strlen(strchr(xml,';')));
1454  }
1455  else
1456  if (((*xml == '&') && ((state == '&') || (state == ' ') ||
1457  (state == '*'))) || ((state == '%') && (*xml == '%')))
1458  {
1459  /*
1460  Find entity in the list.
1461  */
1462  i=0;
1463  while ((entities[i] != (char *) NULL) &&
1464  (strncmp(xml+1,entities[i],strlen(entities[i])) != 0))
1465  i+=2;
1466  if (entities[i++] == (char *) NULL)
1467  xml++;
1468  else
1469  if (entities[i] != (char *) NULL)
1470  {
1471  /*
1472  Found a match.
1473  */
1474  length=strlen(entities[i]);
1475  entity=strchr(xml,';');
1476  if ((entity != (char *) NULL) &&
1477  ((length-1L) >= (size_t) (entity-xml)))
1478  {
1479  offset=(ssize_t) (xml-p);
1480  extent=(size_t) (offset+length+strlen(entity));
1481  if (p != q)
1482  {
1483  p=(char *) ResizeQuantumMemory(p,extent+1,sizeof(*p));
1484  if (p != (char *) NULL)
1485  p[extent]='\0';
1486  }
1487  else
1488  {
1489  char
1490  *extent_xml;
1491 
1492  extent_xml=(char *) AcquireQuantumMemory(extent+1,
1493  sizeof(*extent_xml));
1494  if (extent_xml != (char *) NULL)
1495  {
1496  memset(extent_xml,0,extent*sizeof(*extent_xml));
1497  (void) CopyMagickString(extent_xml,p,extent*
1498  sizeof(*extent_xml));
1499  }
1500  p=extent_xml;
1501  }
1502  if (p == (char *) NULL)
1503  ThrowFatalException(ResourceLimitFatalError,
1504  "MemoryAllocationFailed");
1505  xml=p+offset;
1506  entity=strchr(xml,';');
1507  }
1508  if (entity != (char *) NULL)
1509  (void) memmove(xml+length,entity+1,strlen(entity));
1510  (void) memcpy(xml,entities[i],length);
1511  }
1512  }
1513  else
1514  if (((state == ' ') || (state == '*')) &&
1515  (isspace((int) ((unsigned char) *xml) != 0)))
1516  *(xml++)=' ';
1517  else
1518  xml++;
1519  }
1520  if (state == '*')
1521  {
1522 
1523  /*
1524  Normalize spaces for non-CDATA attributes.
1525  */
1526  for (xml=p; *xml != '\0'; xml++)
1527  {
1528  char
1529  accept[] = " ";
1530 
1531  i=(ssize_t) strspn(xml,accept);
1532  if (i != 0)
1533  (void) memmove(xml,xml+i,strlen(xml+i)+1);
1534  while ((*xml != '\0') && (*xml != ' '))
1535  xml++;
1536  if (*xml == '\0')
1537  break;
1538  }
1539  xml--;
1540  if ((xml >= p) && (*xml == ' '))
1541  *xml='\0';
1542  }
1543  return(p == q ? ConstantString(p) : p);
1544 }
1545 
1546 static void ParseCharacterContent(XMLTreeRoot *root,char *xml,
1547  const size_t length,const char state)
1548 {
1549  XMLTreeInfo
1550  *xml_info;
1551 
1552  xml_info=root->node;
1553  if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) ||
1554  (length == 0))
1555  return;
1556  xml[length]='\0';
1557  xml=ParseEntities(xml,root->entities,state);
1558  if ((xml_info->content != (char *) NULL) && (*xml_info->content != '\0'))
1559  {
1560  (void) ConcatenateString(&xml_info->content,xml);
1561  xml=DestroyString(xml);
1562  }
1563  else
1564  {
1565  if (xml_info->content != (char *) NULL)
1566  xml_info->content=DestroyString(xml_info->content);
1567  xml_info->content=xml;
1568  }
1569 }
1570 
1571 static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag,
1572  ExceptionInfo *exception)
1573 {
1574  if ((root->node == (XMLTreeInfo *) NULL) ||
1575  (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0))
1576  {
1577  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1578  "ParseError","unexpected closing tag </%s>",tag);
1579  return(&root->root);
1580  }
1581  root->node=root->node->parent;
1582  return((XMLTreeInfo *) NULL);
1583 }
1584 
1585 static MagickBooleanType ValidateEntities(char *tag,char *xml,
1586  const size_t depth,char **entities)
1587 {
1588  ssize_t
1589  i;
1590 
1591  /*
1592  Check for circular entity references.
1593  */
1594  if (depth > MagickMaxRecursionDepth)
1595  return(MagickFalse);
1596  for ( ; ; xml++)
1597  {
1598  while ((*xml != '\0') && (*xml != '&'))
1599  xml++;
1600  if (*xml == '\0')
1601  return(MagickTrue);
1602  if (strncmp(xml+1,tag,strlen(tag)) == 0)
1603  return(MagickFalse);
1604  i=0;
1605  while ((entities[i] != (char *) NULL) &&
1606  (strncmp(entities[i],xml+1,strlen(entities[i])) == 0))
1607  i+=2;
1608  if ((entities[i] != (char *) NULL) &&
1609  (ValidateEntities(tag,entities[i+1],depth+1,entities) == 0))
1610  return(MagickFalse);
1611  }
1612 }
1613 
1614 static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml,
1615  size_t length)
1616 {
1617  char
1618  *target;
1619 
1620  ssize_t
1621  i,
1622  j;
1623 
1624  target=xml;
1625  xml[length]='\0';
1626  xml+=strcspn(xml,XMLWhitespace);
1627  if (*xml != '\0')
1628  {
1629  *xml='\0';
1630  xml+=strspn(xml+1,XMLWhitespace)+1;
1631  }
1632  if (strcmp(target,"xml") == 0)
1633  {
1634  xml=strstr(xml,"standalone");
1635  if ((xml != (char *) NULL) &&
1636  (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0))
1637  root->standalone=MagickTrue;
1638  return;
1639  }
1640  if (root->processing_instructions[0] == (char **) NULL)
1641  {
1642  root->processing_instructions=(char ***) AcquireMagickMemory(sizeof(
1643  *root->processing_instructions));
1644  if (root->processing_instructions ==(char ***) NULL)
1645  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1646  *root->processing_instructions=(char **) NULL;
1647  }
1648  i=0;
1649  while ((root->processing_instructions[i] != (char **) NULL) &&
1650  (strcmp(target,root->processing_instructions[i][0]) != 0))
1651  i++;
1652  if (root->processing_instructions[i] == (char **) NULL)
1653  {
1654  root->processing_instructions=(char ***) ResizeQuantumMemory(
1655  root->processing_instructions,(size_t) (i+2),
1656  sizeof(*root->processing_instructions));
1657  if (root->processing_instructions == (char ***) NULL)
1658  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1659  root->processing_instructions[i]=(char **) AcquireQuantumMemory(3,
1660  sizeof(**root->processing_instructions));
1661  if (root->processing_instructions[i] == (char **) NULL)
1662  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1663  root->processing_instructions[i+1]=(char **) NULL;
1664  root->processing_instructions[i][0]=ConstantString(target);
1665  root->processing_instructions[i][1]=(char *)
1666  root->processing_instructions[i+1];
1667  root->processing_instructions[i+1]=(char **) NULL;
1668  root->processing_instructions[i][2]=ConstantString("");
1669  }
1670  j=1;
1671  while (root->processing_instructions[i][j] != (char *) NULL)
1672  j++;
1673  root->processing_instructions[i]=(char **) ResizeQuantumMemory(
1674  root->processing_instructions[i],(size_t) (j+3),
1675  sizeof(**root->processing_instructions));
1676  if (root->processing_instructions[i] == (char **) NULL)
1677  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1678  root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory(
1679  root->processing_instructions[i][j+1],(size_t) (j+1),
1680  sizeof(***root->processing_instructions));
1681  if (root->processing_instructions[i][j+2] == (char *) NULL)
1682  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1683  (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1,
1684  root->root.tag != (char *) NULL ? ">" : "<",2);
1685  root->processing_instructions[i][j]=ConstantString(xml);
1686  root->processing_instructions[i][j+1]=(char *) NULL;
1687 }
1688 
1689 static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml,
1690  size_t length,ExceptionInfo *exception)
1691 {
1692  char
1693  *c,
1694  **entities,
1695  *n,
1696  **predefined_entities,
1697  q,
1698  *t,
1699  *v;
1700 
1701  ssize_t
1702  i,
1703  j;
1704 
1705  n=(char *) NULL;
1706  predefined_entities=(char **) AcquireMagickMemory(sizeof(sentinel));
1707  if (predefined_entities == (char **) NULL)
1708  ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed");
1709  (void) memcpy(predefined_entities,sentinel,sizeof(sentinel));
1710  for (xml[length]='\0'; xml != (char *) NULL; )
1711  {
1712  while ((*xml != '\0') && (*xml != '<') && (*xml != '%'))
1713  xml++;
1714  if (*xml == '\0')
1715  break;
1716  if ((strlen(xml) > 9) && (strncmp(xml,"<!ENTITY",8) == 0))
1717  {
1718  /*
1719  Parse entity definitions.
1720  */
1721  if (strspn(xml+8,XMLWhitespace) == 0)
1722  break;
1723  xml+=strspn(xml+8,XMLWhitespace)+8;
1724  c=xml;
1725  n=xml+strspn(xml,XMLWhitespace "%");
1726  if ((isalpha((int) ((unsigned char) *n)) == 0) && (*n != '_'))
1727  break;
1728  xml=n+strcspn(n,XMLWhitespace);
1729  if (*xml == '\0')
1730  break;
1731  *xml=';';
1732  v=xml+strspn(xml+1,XMLWhitespace)+1;
1733  q=(*v);
1734  v++;
1735  if ((q != '"') && (q != '\''))
1736  {
1737  /*
1738  Skip externals.
1739  */
1740  xml=strchr(xml,'>');
1741  continue;
1742  }
1743  entities=(*c == '%') ? predefined_entities : root->entities;
1744  for (i=0; entities[i] != (char *) NULL; i++) ;
1745  entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3),
1746  sizeof(*entities));
1747  if (entities == (char **) NULL)
1748  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1749  if (*c == '%')
1750  predefined_entities=entities;
1751  else
1752  root->entities=entities;
1753  xml++;
1754  *xml='\0';
1755  xml=strchr(v,q);
1756  if (xml != (char *) NULL)
1757  {
1758  *xml='\0';
1759  xml++;
1760  }
1761  entities[i+1]=ParseEntities(v,predefined_entities,'%');
1762  entities[i+2]=(char *) NULL;
1763  if (ValidateEntities(n,entities[i+1],0,entities) != MagickFalse)
1764  entities[i]=n;
1765  else
1766  {
1767  if (entities[i+1] != v)
1768  entities[i+1]=DestroyString(entities[i+1]);
1769  (void) ThrowMagickException(exception,GetMagickModule(),
1770  OptionWarning,"ParseError","circular entity declaration &%s",n);
1771  predefined_entities=(char **) RelinquishMagickMemory(
1772  predefined_entities);
1773  return(MagickFalse);
1774  }
1775  }
1776  else
1777  if (strncmp(xml,"<!ATTLIST",9) == 0)
1778  {
1779  /*
1780  Parse default attributes.
1781  */
1782  t=xml+strspn(xml+9,XMLWhitespace)+9;
1783  if (*t == '\0')
1784  {
1785  (void) ThrowMagickException(exception,GetMagickModule(),
1786  OptionWarning,"ParseError","unclosed <!ATTLIST");
1787  predefined_entities=(char **) RelinquishMagickMemory(
1788  predefined_entities);
1789  return(MagickFalse);
1790  }
1791  xml=t+strcspn(t,XMLWhitespace ">");
1792  if (*xml == '>')
1793  continue;
1794  *xml='\0';
1795  i=0;
1796  while ((root->attributes[i] != (char **) NULL) &&
1797  (n != (char *) NULL) &&
1798  (strcmp(n,root->attributes[i][0]) != 0))
1799  i++;
1800  while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') &&
1801  (*n != '>'))
1802  {
1803  xml=n+strcspn(n,XMLWhitespace);
1804  if (*xml != '\0')
1805  *xml='\0';
1806  else
1807  {
1808  (void) ThrowMagickException(exception,GetMagickModule(),
1809  OptionWarning,"ParseError","malformed <!ATTLIST");
1810  predefined_entities=(char **) RelinquishMagickMemory(
1811  predefined_entities);
1812  return(MagickFalse);
1813  }
1814  xml+=strspn(xml+1,XMLWhitespace)+1;
1815  c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
1816  if (strncmp(xml,"NOTATION",8) == 0)
1817  xml+=strspn(xml+8,XMLWhitespace)+8;
1818  xml=(*xml == '(') ? strchr(xml,')') : xml+
1819  strcspn(xml,XMLWhitespace);
1820  if (xml == (char *) NULL)
1821  {
1822  (void) ThrowMagickException(exception,GetMagickModule(),
1823  OptionWarning,"ParseError","malformed <!ATTLIST");
1824  predefined_entities=(char **) RelinquishMagickMemory(
1825  predefined_entities);
1826  return(MagickFalse);
1827  }
1828  xml+=strspn(xml,XMLWhitespace ")");
1829  if (strncmp(xml,"#FIXED",6) == 0)
1830  xml+=strspn(xml+6,XMLWhitespace)+6;
1831  if (*xml == '#')
1832  {
1833  xml+=strcspn(xml,XMLWhitespace ">")-1;
1834  if (*c == ' ')
1835  continue;
1836  v=(char *) NULL;
1837  }
1838  else
1839  if (((*xml == '"') || (*xml == '\'')) &&
1840  ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
1841  *xml='\0';
1842  else
1843  {
1844  (void) ThrowMagickException(exception,GetMagickModule(),
1845  OptionWarning,"ParseError","malformed <!ATTLIST");
1846  predefined_entities=(char **) RelinquishMagickMemory(
1847  predefined_entities);
1848  return(MagickFalse);
1849  }
1850  if (root->attributes[i] == (char **) NULL)
1851  {
1852  /*
1853  New attribute tag.
1854  */
1855  if (i == 0)
1856  root->attributes=(char ***) AcquireQuantumMemory(2,
1857  sizeof(*root->attributes));
1858  else
1859  root->attributes=(char ***) ResizeQuantumMemory(
1860  root->attributes,(size_t) (i+2),
1861  sizeof(*root->attributes));
1862  if (root->attributes == (char ***) NULL)
1863  ThrowFatalException(ResourceLimitFatalError,
1864  "MemoryAllocationFailed");
1865  root->attributes[i]=(char **) AcquireQuantumMemory(2,
1866  sizeof(**root->attributes));
1867  if (root->attributes[i] == (char **) NULL)
1868  ThrowFatalException(ResourceLimitFatalError,
1869  "MemoryAllocationFailed");
1870  root->attributes[i][0]=ConstantString(t);
1871  root->attributes[i][1]=(char *) NULL;
1872  root->attributes[i+1]=(char **) NULL;
1873  }
1874  for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
1875  root->attributes[i]=(char **) ResizeQuantumMemory(
1876  root->attributes[i],(size_t) (j+4),sizeof(**root->attributes));
1877  if (root->attributes[i] == (char **) NULL)
1878  ThrowFatalException(ResourceLimitFatalError,
1879  "MemoryAllocationFailed");
1880  root->attributes[i][j+3]=(char *) NULL;
1881  root->attributes[i][j+2]=ConstantString(c);
1882  root->attributes[i][j+1]=(char *) NULL;
1883  if (v != (char *) NULL)
1884  root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
1885  root->attributes[i][j]=ConstantString(n);
1886  }
1887  }
1888  else
1889  if (strncmp(xml, "<!--", 4) == 0)
1890  xml=strstr(xml+4,"-->");
1891  else
1892  if (strncmp(xml,"<?", 2) == 0)
1893  {
1894  c=xml+2;
1895  xml=strstr(c,"?>");
1896  if (xml != (char *) NULL)
1897  {
1898  ParseProcessingInstructions(root,c,(size_t) (xml-c));
1899  xml++;
1900  }
1901  }
1902  else
1903  if (*xml == '<')
1904  xml=strchr(xml,'>');
1905  else
1906  if ((*(xml++) == '%') && (root->standalone == MagickFalse))
1907  break;
1908  }
1909  predefined_entities=(char **) RelinquishMagickMemory(predefined_entities);
1910  return(MagickTrue);
1911 }
1912 
1913 static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
1914 {
1915  XMLTreeInfo
1916  *xml_info;
1917 
1918  xml_info=root->node;
1919  if (xml_info->tag == (char *) NULL)
1920  xml_info->tag=ConstantString(tag);
1921  else
1922  xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
1923  if (xml_info != (XMLTreeInfo *) NULL)
1924  xml_info->attributes=attributes;
1925  root->node=xml_info;
1926 }
1927 
1928 static const char
1929  *skip_tags[3] =
1930  {
1931  "rdf:Bag",
1932  "rdf:Seq",
1933  (const char *) NULL
1934  };
1935 
1936 static inline MagickBooleanType IsSkipTag(const char *tag)
1937 {
1938  ssize_t
1939  i;
1940 
1941  i=0;
1942  while (skip_tags[i] != (const char *) NULL)
1943  {
1944  if (LocaleCompare(tag,skip_tags[i]) == 0)
1945  return(MagickTrue);
1946  i++;
1947  }
1948  return(MagickFalse);
1949 }
1950 
1951 MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1952 {
1953  char
1954  **attribute,
1955  **attributes,
1956  *p,
1957  *tag,
1958  *utf8;
1959 
1960  int
1961  c,
1962  terminal;
1963 
1964  MagickBooleanType
1965  status;
1966 
1967  size_t
1968  ignore_depth,
1969  length;
1970 
1971  ssize_t
1972  i,
1973  j,
1974  l;
1975 
1976  XMLTreeRoot
1977  *root;
1978 
1979  /*
1980  Convert xml-string to UTF8.
1981  */
1982  if ((xml == (const char *) NULL) || (strlen(xml) == 0))
1983  {
1984  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1985  "ParseError","root tag missing");
1986  return((XMLTreeInfo *) NULL);
1987  }
1988  root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
1989  length=strlen(xml);
1990  utf8=ConvertUTF16ToUTF8(xml,&length);
1991  if (utf8 == (char *) NULL)
1992  {
1993  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1994  "ParseError","UTF16 to UTF8 failed");
1995  return((XMLTreeInfo *) NULL);
1996  }
1997  if (length == 0)
1998  {
1999  utf8=DestroyString(utf8);
2000  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2001  "ParseError","root tag missing");
2002  return((XMLTreeInfo *) NULL);
2003  }
2004  terminal=utf8[length-1];
2005  utf8[length-1]='\0';
2006  p=utf8;
2007  while ((*p != '\0') && (*p != '<'))
2008  p++;
2009  if (*p == '\0')
2010  {
2011  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2012  "ParseError","root tag missing");
2013  utf8=DestroyString(utf8);
2014  return((XMLTreeInfo *) NULL);
2015  }
2016  attribute=(char **) NULL;
2017  l=0;
2018  ignore_depth=0;
2019  for (p++; ; p++)
2020  {
2021  attributes=(char **) sentinel;
2022  tag=p;
2023  c=(*p);
2024  if ((isalpha((int) ((unsigned char) *p)) !=0) || (*p == '_') ||
2025  (*p == ':') || (c < '\0'))
2026  {
2027  /*
2028  Tag.
2029  */
2030  if (root->node == (XMLTreeInfo *) NULL)
2031  {
2032  (void) ThrowMagickException(exception,GetMagickModule(),
2033  OptionWarning,"ParseError","root tag missing");
2034  utf8=DestroyString(utf8);
2035  return(&root->root);
2036  }
2037  p+=(ptrdiff_t) strcspn(p,XMLWhitespace "/>");
2038  while (isspace((int) ((unsigned char) *p)) != 0)
2039  *p++='\0';
2040  if (((isalpha((int) ((unsigned char) *p)) != 0) || (*p == '_')) &&
2041  (ignore_depth == 0))
2042  {
2043  if ((*p != '\0') && (*p != '/') && (*p != '>'))
2044  {
2045  /*
2046  Find tag in default attributes list.
2047  */
2048  i=0;
2049  while ((root->attributes[i] != (char **) NULL) &&
2050  (strcmp(root->attributes[i][0],tag) != 0))
2051  i++;
2052  attribute=root->attributes[i];
2053  }
2054  for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
2055  {
2056  /*
2057  Attribute.
2058  */
2059  if (l == 0)
2060  attributes=(char **) AcquireQuantumMemory(4,
2061  sizeof(*attributes));
2062  else
2063  attributes=(char **) ResizeQuantumMemory(attributes,
2064  (size_t) (l+4),sizeof(*attributes));
2065  if (attributes == (char **) NULL)
2066  {
2067  (void) ThrowMagickException(exception,GetMagickModule(),
2068  ResourceLimitError,"MemoryAllocationFailed","`%s'","");
2069  utf8=DestroyString(utf8);
2070  return(&root->root);
2071  }
2072  attributes[l+2]=(char *) NULL;
2073  attributes[l+1]=(char *) NULL;
2074  attributes[l]=p;
2075  p+=(ptrdiff_t) strcspn(p,XMLWhitespace "=/>");
2076  if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
2077  attributes[l]=ConstantString("");
2078  else
2079  {
2080  *p++='\0';
2081  p+=(ptrdiff_t) strspn(p,XMLWhitespace "=");
2082  c=(*p);
2083  if ((c == '"') || (c == '\''))
2084  {
2085  /*
2086  Attributes value.
2087  */
2088  p++;
2089  attributes[l+1]=p;
2090  while ((*p != '\0') && (*p != c))
2091  p++;
2092  if (*p != '\0')
2093  *p++='\0';
2094  else
2095  {
2096  attributes[l]=ConstantString("");
2097  attributes[l+1]=ConstantString("");
2098  (void) DestroyXMLTreeAttributes(attributes);
2099  (void) ThrowMagickException(exception,
2100  GetMagickModule(),OptionWarning,"ParseError",
2101  "missing %c",c);
2102  utf8=DestroyString(utf8);
2103  return(&root->root);
2104  }
2105  j=1;
2106  while ((attribute != (char **) NULL) &&
2107  (attribute[j] != (char *) NULL) &&
2108  (strcmp(attribute[j],attributes[l]) != 0))
2109  j+=3;
2110  attributes[l+1]=ParseEntities(attributes[l+1],
2111  root->entities,(attribute != (char **) NULL) &&
2112  (attribute[j] != (char *) NULL) ? *attribute[j+2] :
2113  ' ');
2114  }
2115  attributes[l]=ConstantString(attributes[l]);
2116  }
2117  while (isspace((int) ((unsigned char) *p)) != 0)
2118  p++;
2119  }
2120  }
2121  else
2122  {
2123  while((*p != '\0') && (*p != '/') && (*p != '>'))
2124  p++;
2125  }
2126  if (*p == '/')
2127  {
2128  /*
2129  Self closing tag.
2130  */
2131  *p++='\0';
2132  if (((*p != '\0') && (*p != '>')) ||
2133  ((*p == '\0') && (terminal != '>')))
2134  {
2135  if (l != 0)
2136  (void) DestroyXMLTreeAttributes(attributes);
2137  (void) ThrowMagickException(exception,GetMagickModule(),
2138  OptionWarning,"ParseError","missing >");
2139  utf8=DestroyString(utf8);
2140  return(&root->root);
2141  }
2142  if ((ignore_depth != 0) || (IsSkipTag(tag) != MagickFalse))
2143  (void) DestroyXMLTreeAttributes(attributes);
2144  else
2145  {
2146  ParseOpenTag(root,tag,attributes);
2147  (void) ParseCloseTag(root,tag,exception);
2148  }
2149  }
2150  else
2151  {
2152  c=(*p);
2153  if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
2154  {
2155  *p='\0';
2156  if ((ignore_depth == 0) && (IsSkipTag(tag) == MagickFalse))
2157  ParseOpenTag(root,tag,attributes);
2158  else
2159  {
2160  ignore_depth++;
2161  (void) DestroyXMLTreeAttributes(attributes);
2162  }
2163  *p=c;
2164  }
2165  else
2166  {
2167  if (l != 0)
2168  (void) DestroyXMLTreeAttributes(attributes);
2169  (void) ThrowMagickException(exception,GetMagickModule(),
2170  OptionWarning,"ParseError","missing >");
2171  utf8=DestroyString(utf8);
2172  return(&root->root);
2173  }
2174  }
2175  }
2176  else
2177  if (*p == '/')
2178  {
2179  /*
2180  Close tag.
2181  */
2182  tag=p+1;
2183  p+=(ptrdiff_t) strcspn(tag,XMLWhitespace ">")+1;
2184  c=(*p);
2185  if ((c == '\0') && (terminal != '>'))
2186  {
2187  (void) ThrowMagickException(exception,GetMagickModule(),
2188  OptionWarning,"ParseError","missing >");
2189  utf8=DestroyString(utf8);
2190  return(&root->root);
2191  }
2192  *p='\0';
2193  if ((ignore_depth == 0) &&
2194  (ParseCloseTag(root,tag,exception) != (XMLTreeInfo *) NULL))
2195  {
2196  utf8=DestroyString(utf8);
2197  return(&root->root);
2198  }
2199  if (ignore_depth > 0)
2200  ignore_depth--;
2201  *p=c;
2202  if (isspace((int) ((unsigned char) *p)) != 0)
2203  p+=(ptrdiff_t) strspn(p,XMLWhitespace);
2204  }
2205  else
2206  if (strncmp(p,"!--",3) == 0)
2207  {
2208  /*
2209  Comment.
2210  */
2211  p=strstr(p+3,"--");
2212  if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
2213  ((*p == '\0') && (terminal != '>')))
2214  {
2215  (void) ThrowMagickException(exception,GetMagickModule(),
2216  OptionWarning,"ParseError","unclosed <!--");
2217  utf8=DestroyString(utf8);
2218  return(&root->root);
2219  }
2220  }
2221  else
2222  if (strncmp(p,"![CDATA[",8) == 0)
2223  {
2224  /*
2225  Cdata.
2226  */
2227  p=strstr(p,"]]>");
2228  if (p != (char *) NULL)
2229  {
2230  p+=(ptrdiff_t) 2;
2231  if (ignore_depth == 0)
2232  ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
2233  }
2234  else
2235  {
2236  (void) ThrowMagickException(exception,GetMagickModule(),
2237  OptionWarning,"ParseError","unclosed <![CDATA[");
2238  utf8=DestroyString(utf8);
2239  return(&root->root);
2240  }
2241  }
2242  else
2243  if (strncmp(p,"!DOCTYPE",8) == 0)
2244  {
2245  /*
2246  DTD.
2247  */
2248  for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
2249  ((l != 0) && ((*p != ']') ||
2250  (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
2251  l=(ssize_t) ((*p == '[') ? 1 : l))
2252  p+=(ptrdiff_t) strcspn(p+1,"[]>")+1;
2253  if ((*p == '\0') && (terminal != '>'))
2254  {
2255  (void) ThrowMagickException(exception,GetMagickModule(),
2256  OptionWarning,"ParseError","unclosed <!DOCTYPE");
2257  utf8=DestroyString(utf8);
2258  return(&root->root);
2259  }
2260  if (l != 0)
2261  tag=strchr(tag,'[')+1;
2262  if (l != 0)
2263  {
2264  status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
2265  exception);
2266  if (status == MagickFalse)
2267  {
2268  utf8=DestroyString(utf8);
2269  return(&root->root);
2270  }
2271  p++;
2272  }
2273  }
2274  else
2275  if (*p == '?')
2276  {
2277  /*
2278  Processing instructions.
2279  */
2280  do
2281  {
2282  p=strchr(p,'?');
2283  if (p == (char *) NULL)
2284  break;
2285  p++;
2286  } while ((*p != '\0') && (*p != '>'));
2287  if ((p == (char *) NULL) || ((*p == '\0') &&
2288  (terminal != '>')))
2289  {
2290  (void) ThrowMagickException(exception,GetMagickModule(),
2291  OptionWarning,"ParseError","unclosed <?");
2292  utf8=DestroyString(utf8);
2293  return(&root->root);
2294  }
2295  ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
2296  }
2297  else
2298  {
2299  (void) ThrowMagickException(exception,GetMagickModule(),
2300  OptionWarning,"ParseError","unexpected <");
2301  utf8=DestroyString(utf8);
2302  return(&root->root);
2303  }
2304  if ((p == (char *) NULL) || (*p == '\0'))
2305  break;
2306  *p++='\0';
2307  tag=p;
2308  if ((*p != '\0') && (*p != '<'))
2309  {
2310  /*
2311  Tag character content.
2312  */
2313  while ((*p != '\0') && (*p != '<'))
2314  p++;
2315  if (*p == '\0')
2316  break;
2317  if (ignore_depth == 0)
2318  ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
2319  }
2320  else
2321  if (*p == '\0')
2322  break;
2323  }
2324  utf8=DestroyString(utf8);
2325  if (root->node == (XMLTreeInfo *) NULL)
2326  return(&root->root);
2327  if (root->node->tag == (char *) NULL)
2328  {
2329  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2330  "ParseError","root tag missing");
2331  return(&root->root);
2332  }
2333  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2334  "ParseError","unclosed tag: `%s'",root->node->tag);
2335  return(&root->root);
2336 }
2337 
2338 /*
2339 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2340 % %
2341 % %
2342 % %
2343 % N e w X M L T r e e T a g %
2344 % %
2345 % %
2346 % %
2347 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2348 %
2349 % NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
2350 %
2351 % The format of the NewXMLTreeTag method is:
2352 %
2353 % XMLTreeInfo *NewXMLTreeTag(const char *tag)
2354 %
2355 % A description of each parameter follows:
2356 %
2357 % o tag: the tag.
2358 %
2359 */
2360 MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
2361 {
2362  static const char
2363  *predefined_entities[NumberPredefinedEntities+1] =
2364  {
2365  "lt;", "&#60;", "gt;", "&#62;", "quot;", "&#34;",
2366  "apos;", "&#39;", "amp;", "&#38;", (char *) NULL
2367  };
2368 
2369  XMLTreeRoot
2370  *root;
2371 
2372  root=(XMLTreeRoot *) AcquireCriticalMemory(sizeof(*root));
2373  (void) memset(root,0,sizeof(*root));
2374  root->root.tag=(char *) NULL;
2375  if (tag != (char *) NULL)
2376  root->root.tag=ConstantString(tag);
2377  root->node=(&root->root);
2378  root->root.content=ConstantString("");
2379  root->entities=(char **) AcquireCriticalMemory(sizeof(predefined_entities));
2380  (void) memcpy(root->entities,predefined_entities,sizeof(predefined_entities));
2381  root->root.attributes=sentinel;
2382  root->attributes=(char ***) root->root.attributes;
2383  root->processing_instructions=(char ***) root->root.attributes;
2384  root->debug=IsEventLogging();
2385  root->signature=MagickCoreSignature;
2386  return(&root->root);
2387 }
2388 
2389 /*
2390 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2391 % %
2392 % %
2393 % %
2394 % P r u n e T a g F r o m X M L T r e e %
2395 % %
2396 % %
2397 % %
2398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2399 %
2400 % PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
2401 % subtags.
2402 %
2403 % The format of the PruneTagFromXMLTree method is:
2404 %
2405 % XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2406 %
2407 % A description of each parameter follows:
2408 %
2409 % o xml_info: the xml info.
2410 %
2411 */
2412 MagickExport XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2413 {
2414  XMLTreeInfo
2415  *node;
2416 
2417  assert(xml_info != (XMLTreeInfo *) NULL);
2418  assert((xml_info->signature == MagickCoreSignature) ||
2419  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2420  if (IsEventLogging() != MagickFalse)
2421  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2422  if (xml_info->next != (XMLTreeInfo *) NULL)
2423  xml_info->next->sibling=xml_info->sibling;
2424  if (xml_info->parent != (XMLTreeInfo *) NULL)
2425  {
2426  node=xml_info->parent->child;
2427  if (node == xml_info)
2428  xml_info->parent->child=xml_info->ordered;
2429  else
2430  {
2431  while (node->ordered != xml_info)
2432  node=node->ordered;
2433  node->ordered=node->ordered->ordered;
2434  node=xml_info->parent->child;
2435  if (strcmp(node->tag,xml_info->tag) != 0)
2436  {
2437  while (strcmp(node->sibling->tag,xml_info->tag) != 0)
2438  node=node->sibling;
2439  if (node->sibling != xml_info)
2440  node=node->sibling;
2441  else
2442  node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
2443  xml_info->next : node->sibling->sibling;
2444  }
2445  while ((node->next != (XMLTreeInfo *) NULL) &&
2446  (node->next != xml_info))
2447  node=node->next;
2448  if (node->next != (XMLTreeInfo *) NULL)
2449  node->next=node->next->next;
2450  }
2451  }
2452  xml_info->ordered=(XMLTreeInfo *) NULL;
2453  xml_info->sibling=(XMLTreeInfo *) NULL;
2454  xml_info->next=(XMLTreeInfo *) NULL;
2455  return(xml_info);
2456 }
2457 
2458 /*
2459 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2460 % %
2461 % %
2462 % %
2463 % S e t X M L T r e e A t t r i b u t e %
2464 % %
2465 % %
2466 % %
2467 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2468 %
2469 % SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
2470 % found. A value of NULL removes the specified attribute.
2471 %
2472 % The format of the SetXMLTreeAttribute method is:
2473 %
2474 % XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
2475 % const char *value)
2476 %
2477 % A description of each parameter follows:
2478 %
2479 % o xml_info: the xml info.
2480 %
2481 % o tag: The attribute tag.
2482 %
2483 % o value: The attribute value.
2484 %
2485 */
2486 MagickExport XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
2487  const char *tag,const char *value)
2488 {
2489  ssize_t
2490  i,
2491  j;
2492 
2493  assert(xml_info != (XMLTreeInfo *) NULL);
2494  assert((xml_info->signature == MagickCoreSignature) ||
2495  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2496  if (IsEventLogging() != MagickFalse)
2497  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2498  i=0;
2499  while ((xml_info->attributes[i] != (char *) NULL) &&
2500  (strcmp(xml_info->attributes[i],tag) != 0))
2501  i+=2;
2502  if (xml_info->attributes[i] == (char *) NULL)
2503  {
2504  /*
2505  Add new attribute tag.
2506  */
2507  if (value == (const char *) NULL)
2508  return(xml_info);
2509  if (xml_info->attributes != sentinel)
2510  xml_info->attributes=(char **) ResizeQuantumMemory(
2511  xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
2512  else
2513  {
2514  xml_info->attributes=(char **) AcquireQuantumMemory(4,
2515  sizeof(*xml_info->attributes));
2516  if (xml_info->attributes != (char **) NULL)
2517  xml_info->attributes[1]=ConstantString("");
2518  }
2519  if (xml_info->attributes == (char **) NULL)
2520  ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2521  xml_info->attributes[i]=ConstantString(tag);
2522  xml_info->attributes[i+2]=(char *) NULL;
2523  (void) strlen(xml_info->attributes[i+1]);
2524  }
2525  /*
2526  Add new value to an existing attribute.
2527  */
2528  for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
2529  if (xml_info->attributes[i+1] != (char *) NULL)
2530  xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
2531  if (value != (const char *) NULL)
2532  {
2533  xml_info->attributes[i+1]=ConstantString(value);
2534  return(xml_info);
2535  }
2536  if (xml_info->attributes[i] != (char *) NULL)
2537  xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
2538  (void) memmove(xml_info->attributes+i,xml_info->attributes+i+2,
2539  (size_t) (j-i)*sizeof(*xml_info->attributes));
2540  xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
2541  (size_t) (j+2),sizeof(*xml_info->attributes));
2542  if (xml_info->attributes == (char **) NULL)
2543  ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2544  j-=2;
2545  (void) memmove(xml_info->attributes[j+1]+(i/2),xml_info->attributes[j+1]+
2546  (i/2)+1,(size_t) (((j+2)/2)-(i/2))*sizeof(**xml_info->attributes));
2547  return(xml_info);
2548 }
2549 
2550 /*
2551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2552 % %
2553 % %
2554 % %
2555 % S e t X M L T r e e C o n t e n t %
2556 % %
2557 % %
2558 % %
2559 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2560 %
2561 % SetXMLTreeContent() sets the character content for the given tag and
2562 % returns the tag.
2563 %
2564 % The format of the SetXMLTreeContent method is:
2565 %
2566 % XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2567 % const char *content)
2568 %
2569 % A description of each parameter follows:
2570 %
2571 % o xml_info: the xml info.
2572 %
2573 % o content: The content.
2574 %
2575 */
2576 MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2577  const char *content)
2578 {
2579  assert(xml_info != (XMLTreeInfo *) NULL);
2580  assert((xml_info->signature == MagickCoreSignature) ||
2581  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2582  if (IsEventLogging() != MagickFalse)
2583  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2584  if (xml_info->content != (char *) NULL)
2585  xml_info->content=DestroyString(xml_info->content);
2586  xml_info->content=(char *) ConstantString(content);
2587  return(xml_info);
2588 }
2589 
2590 /*
2591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2592 % %
2593 % %
2594 % %
2595 % X M L T r e e I n f o T o X M L %
2596 % %
2597 % %
2598 % %
2599 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2600 %
2601 % XMLTreeInfoToXML() converts an xml-tree to an XML string.
2602 %
2603 % The format of the XMLTreeInfoToXML method is:
2604 %
2605 % char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2606 %
2607 % A description of each parameter follows:
2608 %
2609 % o xml_info: the xml info.
2610 %
2611 */
2612 
2613 static char *EncodePredefinedEntities(const char *source,ssize_t offset,
2614  char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
2615 {
2616  char
2617  *canonical_content;
2618 
2619  if (offset < 0)
2620  canonical_content=CanonicalXMLContent(source,pedantic);
2621  else
2622  {
2623  char
2624  *content;
2625 
2626  content=AcquireString(source);
2627  content[offset]='\0';
2628  canonical_content=CanonicalXMLContent(content,pedantic);
2629  content=DestroyString(content);
2630  }
2631  if (canonical_content == (char *) NULL)
2632  return(*destination);
2633  if ((*length+strlen(canonical_content)+MaxTextExtent) > *extent)
2634  {
2635  *extent=(*length)+strlen(canonical_content)+MaxTextExtent;
2636  *destination=(char *) ResizeQuantumMemory(*destination,*extent,
2637  sizeof(**destination));
2638  if (*destination == (char *) NULL)
2639  return(*destination);
2640  }
2641  *length+=FormatLocaleString(*destination+(*length),*extent,"%s",
2642  canonical_content);
2643  canonical_content=DestroyString(canonical_content);
2644  return(*destination);
2645 }
2646 
2647 static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
2648  size_t *extent,size_t start,char ***attributes)
2649 {
2650  char
2651  *content;
2652 
2653  const char
2654  *attribute;
2655 
2656  size_t
2657  offset;
2658 
2659  ssize_t
2660  i,
2661  j;
2662 
2663  content=(char *) "";
2664  if (xml_info->parent != (XMLTreeInfo *) NULL)
2665  content=xml_info->parent->content;
2666  offset=0;
2667  *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
2668  start),source,length,extent,MagickFalse);
2669  if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2670  {
2671  *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2672  *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2673  if (*source == (char *) NULL)
2674  return(*source);
2675  }
2676  *length+=FormatLocaleString(*source+(*length),*extent,"<%s",xml_info->tag);
2677  for (i=0; xml_info->attributes[i]; i+=2)
2678  {
2679  attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
2680  if (attribute != xml_info->attributes[i+1])
2681  continue;
2682  if ((*length+strlen(xml_info->attributes[i])+MaxTextExtent) > *extent)
2683  {
2684  *extent=(*length)+strlen(xml_info->attributes[i])+MaxTextExtent;
2685  *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2686  if (*source == (char *) NULL)
2687  return((char *) NULL);
2688  }
2689  *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
2690  xml_info->attributes[i]);
2691  (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
2692  extent,MagickTrue);
2693  *length+=FormatLocaleString(*source+(*length),*extent,"\"");
2694  }
2695  i=0;
2696  while ((attributes[i] != (char **) NULL) &&
2697  (strcmp(attributes[i][0],xml_info->tag) != 0))
2698  i++;
2699  j=1;
2700  while ((attributes[i] != (char **) NULL) &&
2701  (attributes[i][j] != (char *) NULL))
2702  {
2703  if ((attributes[i][j+1] == (char *) NULL) ||
2704  (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
2705  {
2706  j+=3;
2707  continue;
2708  }
2709  if ((*length+strlen(attributes[i][j])+MaxTextExtent) > *extent)
2710  {
2711  *extent=(*length)+strlen(attributes[i][j])+MaxTextExtent;
2712  *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2713  if (*source == (char *) NULL)
2714  return((char *) NULL);
2715  }
2716  *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
2717  attributes[i][j]);
2718  (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
2719  MagickTrue);
2720  *length+=FormatLocaleString(*source+(*length),*extent,"\"");
2721  j+=3;
2722  }
2723  *length+=FormatLocaleString(*source+(*length),*extent,*xml_info->content ?
2724  ">" : "/>");
2725  if (xml_info->child != (XMLTreeInfo *) NULL)
2726  *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
2727  else
2728  *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
2729  MagickFalse);
2730  if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2731  {
2732  *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2733  *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2734  if (*source == (char *) NULL)
2735  return((char *) NULL);
2736  }
2737  if (*xml_info->content != '\0')
2738  *length+=FormatLocaleString(*source+(*length),*extent,"</%s>",
2739  xml_info->tag);
2740  while ((offset < xml_info->offset) && (content[offset] != '\0'))
2741  offset++;
2742  if (xml_info->ordered != (XMLTreeInfo *) NULL)
2743  content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
2744  attributes);
2745  else
2746  content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
2747  MagickFalse);
2748  return(content);
2749 }
2750 
2751 MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2752 {
2753  char
2754  *p,
2755  *q,
2756  *xml;
2757 
2758  size_t
2759  extent,
2760  length;
2761 
2762  ssize_t
2763  i,
2764  j,
2765  k;
2766 
2767  XMLTreeInfo
2768  *ordered,
2769  *parent;
2770 
2771  XMLTreeRoot
2772  *root;
2773 
2774  assert(xml_info != (XMLTreeInfo *) NULL);
2775  assert((xml_info->signature == MagickCoreSignature) ||
2776  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2777  if (IsEventLogging() != MagickFalse)
2778  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2779  if (xml_info->tag == (char *) NULL)
2780  return((char *) NULL);
2781  xml=AcquireString((char *) NULL);
2782  length=0;
2783  extent=MaxTextExtent;
2784  root=(XMLTreeRoot *) xml_info;
2785  while (root->root.parent != (XMLTreeInfo *) NULL)
2786  root=(XMLTreeRoot *) root->root.parent;
2787  parent=xml_info->parent;
2788  if (parent == (XMLTreeInfo *) NULL)
2789  for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2790  {
2791  /*
2792  Pre-root processing instructions.
2793  */
2794  for (k=2; root->processing_instructions[i][k-1]; k++) ;
2795  p=root->processing_instructions[i][1];
2796  for (j=1; p != (char *) NULL; j++)
2797  {
2798  if (root->processing_instructions[i][k][j-1] == '>')
2799  {
2800  p=root->processing_instructions[i][j];
2801  continue;
2802  }
2803  q=root->processing_instructions[i][0];
2804  if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2805  {
2806  extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2807  xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2808  if (xml == (char *) NULL)
2809  return(xml);
2810  }
2811  length+=FormatLocaleString(xml+length,extent,"<?%s%s%s?>\n",q,
2812  *p != '\0' ? " " : "",p);
2813  p=root->processing_instructions[i][j];
2814  }
2815  }
2816  ordered=xml_info->ordered;
2817  xml_info->parent=(XMLTreeInfo *) NULL;
2818  xml_info->ordered=(XMLTreeInfo *) NULL;
2819  xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
2820  xml_info->parent=parent;
2821  xml_info->ordered=ordered;
2822  if (parent == (XMLTreeInfo *) NULL)
2823  for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2824  {
2825  /*
2826  Post-root processing instructions.
2827  */
2828  for (k=2; root->processing_instructions[i][k-1]; k++) ;
2829  p=root->processing_instructions[i][1];
2830  for (j=1; p != (char *) NULL; j++)
2831  {
2832  if (root->processing_instructions[i][k][j-1] == '<')
2833  {
2834  p=root->processing_instructions[i][j];
2835  continue;
2836  }
2837  q=root->processing_instructions[i][0];
2838  if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2839  {
2840  extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2841  xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2842  if (xml == (char *) NULL)
2843  return(xml);
2844  }
2845  length+=FormatLocaleString(xml+length,extent,"\n<?%s%s%s?>",q,
2846  *p != '\0' ? " " : "",p);
2847  p=root->processing_instructions[i][j];
2848  }
2849  }
2850  return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));
2851 }