MagickCore  6.9.13-46
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  p[extent]='\0';
1485  }
1486  else
1487  {
1488  char
1489  *extent_xml;
1490 
1491  extent_xml=(char *) AcquireQuantumMemory(extent+1,
1492  sizeof(*extent_xml));
1493  if (extent_xml != (char *) NULL)
1494  {
1495  memset(extent_xml,0,extent*sizeof(*extent_xml));
1496  (void) CopyMagickString(extent_xml,p,extent*
1497  sizeof(*extent_xml));
1498  }
1499  p=extent_xml;
1500  }
1501  if (p == (char *) NULL)
1502  ThrowFatalException(ResourceLimitFatalError,
1503  "MemoryAllocationFailed");
1504  xml=p+offset;
1505  entity=strchr(xml,';');
1506  }
1507  if (entity != (char *) NULL)
1508  (void) memmove(xml+length,entity+1,strlen(entity));
1509  (void) memcpy(xml,entities[i],length);
1510  }
1511  }
1512  else
1513  if (((state == ' ') || (state == '*')) &&
1514  (isspace((int) ((unsigned char) *xml) != 0)))
1515  *(xml++)=' ';
1516  else
1517  xml++;
1518  }
1519  if (state == '*')
1520  {
1521 
1522  /*
1523  Normalize spaces for non-CDATA attributes.
1524  */
1525  for (xml=p; *xml != '\0'; xml++)
1526  {
1527  char
1528  accept[] = " ";
1529 
1530  i=(ssize_t) strspn(xml,accept);
1531  if (i != 0)
1532  (void) memmove(xml,xml+i,strlen(xml+i)+1);
1533  while ((*xml != '\0') && (*xml != ' '))
1534  xml++;
1535  if (*xml == '\0')
1536  break;
1537  }
1538  xml--;
1539  if ((xml >= p) && (*xml == ' '))
1540  *xml='\0';
1541  }
1542  return(p == q ? ConstantString(p) : p);
1543 }
1544 
1545 static void ParseCharacterContent(XMLTreeRoot *root,char *xml,
1546  const size_t length,const char state)
1547 {
1548  XMLTreeInfo
1549  *xml_info;
1550 
1551  xml_info=root->node;
1552  if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) ||
1553  (length == 0))
1554  return;
1555  xml[length]='\0';
1556  xml=ParseEntities(xml,root->entities,state);
1557  if ((xml_info->content != (char *) NULL) && (*xml_info->content != '\0'))
1558  {
1559  (void) ConcatenateString(&xml_info->content,xml);
1560  xml=DestroyString(xml);
1561  }
1562  else
1563  {
1564  if (xml_info->content != (char *) NULL)
1565  xml_info->content=DestroyString(xml_info->content);
1566  xml_info->content=xml;
1567  }
1568 }
1569 
1570 static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag,
1571  ExceptionInfo *exception)
1572 {
1573  if ((root->node == (XMLTreeInfo *) NULL) ||
1574  (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0))
1575  {
1576  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1577  "ParseError","unexpected closing tag </%s>",tag);
1578  return(&root->root);
1579  }
1580  root->node=root->node->parent;
1581  return((XMLTreeInfo *) NULL);
1582 }
1583 
1584 static MagickBooleanType ValidateEntities(char *tag,char *xml,
1585  const size_t depth,char **entities)
1586 {
1587  ssize_t
1588  i;
1589 
1590  /*
1591  Check for circular entity references.
1592  */
1593  if (depth > MagickMaxRecursionDepth)
1594  return(MagickFalse);
1595  for ( ; ; xml++)
1596  {
1597  while ((*xml != '\0') && (*xml != '&'))
1598  xml++;
1599  if (*xml == '\0')
1600  return(MagickTrue);
1601  if (strncmp(xml+1,tag,strlen(tag)) == 0)
1602  return(MagickFalse);
1603  i=0;
1604  while ((entities[i] != (char *) NULL) &&
1605  (strncmp(entities[i],xml+1,strlen(entities[i])) == 0))
1606  i+=2;
1607  if ((entities[i] != (char *) NULL) &&
1608  (ValidateEntities(tag,entities[i+1],depth+1,entities) == 0))
1609  return(MagickFalse);
1610  }
1611 }
1612 
1613 static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml,
1614  size_t length)
1615 {
1616  char
1617  *target;
1618 
1619  ssize_t
1620  i,
1621  j;
1622 
1623  target=xml;
1624  xml[length]='\0';
1625  xml+=strcspn(xml,XMLWhitespace);
1626  if (*xml != '\0')
1627  {
1628  *xml='\0';
1629  xml+=strspn(xml+1,XMLWhitespace)+1;
1630  }
1631  if (strcmp(target,"xml") == 0)
1632  {
1633  xml=strstr(xml,"standalone");
1634  if ((xml != (char *) NULL) &&
1635  (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0))
1636  root->standalone=MagickTrue;
1637  return;
1638  }
1639  if (root->processing_instructions[0] == (char **) NULL)
1640  {
1641  root->processing_instructions=(char ***) AcquireMagickMemory(sizeof(
1642  *root->processing_instructions));
1643  if (root->processing_instructions ==(char ***) NULL)
1644  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1645  *root->processing_instructions=(char **) NULL;
1646  }
1647  i=0;
1648  while ((root->processing_instructions[i] != (char **) NULL) &&
1649  (strcmp(target,root->processing_instructions[i][0]) != 0))
1650  i++;
1651  if (root->processing_instructions[i] == (char **) NULL)
1652  {
1653  root->processing_instructions=(char ***) ResizeQuantumMemory(
1654  root->processing_instructions,(size_t) (i+2),
1655  sizeof(*root->processing_instructions));
1656  if (root->processing_instructions == (char ***) NULL)
1657  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1658  root->processing_instructions[i]=(char **) AcquireQuantumMemory(3,
1659  sizeof(**root->processing_instructions));
1660  if (root->processing_instructions[i] == (char **) NULL)
1661  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1662  root->processing_instructions[i+1]=(char **) NULL;
1663  root->processing_instructions[i][0]=ConstantString(target);
1664  root->processing_instructions[i][1]=(char *)
1665  root->processing_instructions[i+1];
1666  root->processing_instructions[i+1]=(char **) NULL;
1667  root->processing_instructions[i][2]=ConstantString("");
1668  }
1669  j=1;
1670  while (root->processing_instructions[i][j] != (char *) NULL)
1671  j++;
1672  root->processing_instructions[i]=(char **) ResizeQuantumMemory(
1673  root->processing_instructions[i],(size_t) (j+3),
1674  sizeof(**root->processing_instructions));
1675  if (root->processing_instructions[i] == (char **) NULL)
1676  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1677  root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory(
1678  root->processing_instructions[i][j+1],(size_t) (j+1),
1679  sizeof(***root->processing_instructions));
1680  if (root->processing_instructions[i][j+2] == (char *) NULL)
1681  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1682  (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1,
1683  root->root.tag != (char *) NULL ? ">" : "<",2);
1684  root->processing_instructions[i][j]=ConstantString(xml);
1685  root->processing_instructions[i][j+1]=(char *) NULL;
1686 }
1687 
1688 static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml,
1689  size_t length,ExceptionInfo *exception)
1690 {
1691  char
1692  *c,
1693  **entities,
1694  *n,
1695  **predefined_entities,
1696  q,
1697  *t,
1698  *v;
1699 
1700  ssize_t
1701  i,
1702  j;
1703 
1704  n=(char *) NULL;
1705  predefined_entities=(char **) AcquireMagickMemory(sizeof(sentinel));
1706  if (predefined_entities == (char **) NULL)
1707  ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed");
1708  (void) memcpy(predefined_entities,sentinel,sizeof(sentinel));
1709  for (xml[length]='\0'; xml != (char *) NULL; )
1710  {
1711  while ((*xml != '\0') && (*xml != '<') && (*xml != '%'))
1712  xml++;
1713  if (*xml == '\0')
1714  break;
1715  if ((strlen(xml) > 9) && (strncmp(xml,"<!ENTITY",8) == 0))
1716  {
1717  /*
1718  Parse entity definitions.
1719  */
1720  if (strspn(xml+8,XMLWhitespace) == 0)
1721  break;
1722  xml+=strspn(xml+8,XMLWhitespace)+8;
1723  c=xml;
1724  n=xml+strspn(xml,XMLWhitespace "%");
1725  if ((isalpha((int) ((unsigned char) *n)) == 0) && (*n != '_'))
1726  break;
1727  xml=n+strcspn(n,XMLWhitespace);
1728  if (*xml == '\0')
1729  break;
1730  *xml=';';
1731  v=xml+strspn(xml+1,XMLWhitespace)+1;
1732  q=(*v);
1733  v++;
1734  if ((q != '"') && (q != '\''))
1735  {
1736  /*
1737  Skip externals.
1738  */
1739  xml=strchr(xml,'>');
1740  continue;
1741  }
1742  entities=(*c == '%') ? predefined_entities : root->entities;
1743  for (i=0; entities[i] != (char *) NULL; i++) ;
1744  entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3),
1745  sizeof(*entities));
1746  if (entities == (char **) NULL)
1747  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1748  if (*c == '%')
1749  predefined_entities=entities;
1750  else
1751  root->entities=entities;
1752  xml++;
1753  *xml='\0';
1754  xml=strchr(v,q);
1755  if (xml != (char *) NULL)
1756  {
1757  *xml='\0';
1758  xml++;
1759  }
1760  entities[i+1]=ParseEntities(v,predefined_entities,'%');
1761  entities[i+2]=(char *) NULL;
1762  if (ValidateEntities(n,entities[i+1],0,entities) != MagickFalse)
1763  entities[i]=n;
1764  else
1765  {
1766  if (entities[i+1] != v)
1767  entities[i+1]=DestroyString(entities[i+1]);
1768  (void) ThrowMagickException(exception,GetMagickModule(),
1769  OptionWarning,"ParseError","circular entity declaration &%s",n);
1770  predefined_entities=(char **) RelinquishMagickMemory(
1771  predefined_entities);
1772  return(MagickFalse);
1773  }
1774  }
1775  else
1776  if (strncmp(xml,"<!ATTLIST",9) == 0)
1777  {
1778  /*
1779  Parse default attributes.
1780  */
1781  t=xml+strspn(xml+9,XMLWhitespace)+9;
1782  if (*t == '\0')
1783  {
1784  (void) ThrowMagickException(exception,GetMagickModule(),
1785  OptionWarning,"ParseError","unclosed <!ATTLIST");
1786  predefined_entities=(char **) RelinquishMagickMemory(
1787  predefined_entities);
1788  return(MagickFalse);
1789  }
1790  xml=t+strcspn(t,XMLWhitespace ">");
1791  if (*xml == '>')
1792  continue;
1793  *xml='\0';
1794  i=0;
1795  while ((root->attributes[i] != (char **) NULL) &&
1796  (n != (char *) NULL) &&
1797  (strcmp(n,root->attributes[i][0]) != 0))
1798  i++;
1799  while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') &&
1800  (*n != '>'))
1801  {
1802  xml=n+strcspn(n,XMLWhitespace);
1803  if (*xml != '\0')
1804  *xml='\0';
1805  else
1806  {
1807  (void) ThrowMagickException(exception,GetMagickModule(),
1808  OptionWarning,"ParseError","malformed <!ATTLIST");
1809  predefined_entities=(char **) RelinquishMagickMemory(
1810  predefined_entities);
1811  return(MagickFalse);
1812  }
1813  xml+=strspn(xml+1,XMLWhitespace)+1;
1814  c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
1815  if (strncmp(xml,"NOTATION",8) == 0)
1816  xml+=strspn(xml+8,XMLWhitespace)+8;
1817  xml=(*xml == '(') ? strchr(xml,')') : xml+
1818  strcspn(xml,XMLWhitespace);
1819  if (xml == (char *) NULL)
1820  {
1821  (void) ThrowMagickException(exception,GetMagickModule(),
1822  OptionWarning,"ParseError","malformed <!ATTLIST");
1823  predefined_entities=(char **) RelinquishMagickMemory(
1824  predefined_entities);
1825  return(MagickFalse);
1826  }
1827  xml+=strspn(xml,XMLWhitespace ")");
1828  if (strncmp(xml,"#FIXED",6) == 0)
1829  xml+=strspn(xml+6,XMLWhitespace)+6;
1830  if (*xml == '#')
1831  {
1832  xml+=strcspn(xml,XMLWhitespace ">")-1;
1833  if (*c == ' ')
1834  continue;
1835  v=(char *) NULL;
1836  }
1837  else
1838  if (((*xml == '"') || (*xml == '\'')) &&
1839  ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
1840  *xml='\0';
1841  else
1842  {
1843  (void) ThrowMagickException(exception,GetMagickModule(),
1844  OptionWarning,"ParseError","malformed <!ATTLIST");
1845  predefined_entities=(char **) RelinquishMagickMemory(
1846  predefined_entities);
1847  return(MagickFalse);
1848  }
1849  if (root->attributes[i] == (char **) NULL)
1850  {
1851  /*
1852  New attribute tag.
1853  */
1854  if (i == 0)
1855  root->attributes=(char ***) AcquireQuantumMemory(2,
1856  sizeof(*root->attributes));
1857  else
1858  root->attributes=(char ***) ResizeQuantumMemory(
1859  root->attributes,(size_t) (i+2),
1860  sizeof(*root->attributes));
1861  if (root->attributes == (char ***) NULL)
1862  ThrowFatalException(ResourceLimitFatalError,
1863  "MemoryAllocationFailed");
1864  root->attributes[i]=(char **) AcquireQuantumMemory(2,
1865  sizeof(**root->attributes));
1866  if (root->attributes[i] == (char **) NULL)
1867  ThrowFatalException(ResourceLimitFatalError,
1868  "MemoryAllocationFailed");
1869  root->attributes[i][0]=ConstantString(t);
1870  root->attributes[i][1]=(char *) NULL;
1871  root->attributes[i+1]=(char **) NULL;
1872  }
1873  for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
1874  root->attributes[i]=(char **) ResizeQuantumMemory(
1875  root->attributes[i],(size_t) (j+4),sizeof(**root->attributes));
1876  if (root->attributes[i] == (char **) NULL)
1877  ThrowFatalException(ResourceLimitFatalError,
1878  "MemoryAllocationFailed");
1879  root->attributes[i][j+3]=(char *) NULL;
1880  root->attributes[i][j+2]=ConstantString(c);
1881  root->attributes[i][j+1]=(char *) NULL;
1882  if (v != (char *) NULL)
1883  root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
1884  root->attributes[i][j]=ConstantString(n);
1885  }
1886  }
1887  else
1888  if (strncmp(xml, "<!--", 4) == 0)
1889  xml=strstr(xml+4,"-->");
1890  else
1891  if (strncmp(xml,"<?", 2) == 0)
1892  {
1893  c=xml+2;
1894  xml=strstr(c,"?>");
1895  if (xml != (char *) NULL)
1896  {
1897  ParseProcessingInstructions(root,c,(size_t) (xml-c));
1898  xml++;
1899  }
1900  }
1901  else
1902  if (*xml == '<')
1903  xml=strchr(xml,'>');
1904  else
1905  if ((*(xml++) == '%') && (root->standalone == MagickFalse))
1906  break;
1907  }
1908  predefined_entities=(char **) RelinquishMagickMemory(predefined_entities);
1909  return(MagickTrue);
1910 }
1911 
1912 static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
1913 {
1914  XMLTreeInfo
1915  *xml_info;
1916 
1917  xml_info=root->node;
1918  if (xml_info->tag == (char *) NULL)
1919  xml_info->tag=ConstantString(tag);
1920  else
1921  xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
1922  if (xml_info != (XMLTreeInfo *) NULL)
1923  xml_info->attributes=attributes;
1924  root->node=xml_info;
1925 }
1926 
1927 static const char
1928  *skip_tags[3] =
1929  {
1930  "rdf:Bag",
1931  "rdf:Seq",
1932  (const char *) NULL
1933  };
1934 
1935 static inline MagickBooleanType IsSkipTag(const char *tag)
1936 {
1937  ssize_t
1938  i;
1939 
1940  i=0;
1941  while (skip_tags[i] != (const char *) NULL)
1942  {
1943  if (LocaleCompare(tag,skip_tags[i]) == 0)
1944  return(MagickTrue);
1945  i++;
1946  }
1947  return(MagickFalse);
1948 }
1949 
1950 MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1951 {
1952  char
1953  **attribute,
1954  **attributes,
1955  *p,
1956  *tag,
1957  *utf8;
1958 
1959  int
1960  c,
1961  terminal;
1962 
1963  MagickBooleanType
1964  status;
1965 
1966  size_t
1967  ignore_depth,
1968  length;
1969 
1970  ssize_t
1971  i,
1972  j,
1973  l;
1974 
1975  XMLTreeRoot
1976  *root;
1977 
1978  /*
1979  Convert xml-string to UTF8.
1980  */
1981  if ((xml == (const char *) NULL) || (strlen(xml) == 0))
1982  {
1983  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1984  "ParseError","root tag missing");
1985  return((XMLTreeInfo *) NULL);
1986  }
1987  root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
1988  length=strlen(xml);
1989  utf8=ConvertUTF16ToUTF8(xml,&length);
1990  if (utf8 == (char *) NULL)
1991  {
1992  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1993  "ParseError","UTF16 to UTF8 failed");
1994  return((XMLTreeInfo *) NULL);
1995  }
1996  if (length == 0)
1997  {
1998  utf8=DestroyString(utf8);
1999  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2000  "ParseError","root tag missing");
2001  return((XMLTreeInfo *) NULL);
2002  }
2003  terminal=utf8[length-1];
2004  utf8[length-1]='\0';
2005  p=utf8;
2006  while ((*p != '\0') && (*p != '<'))
2007  p++;
2008  if (*p == '\0')
2009  {
2010  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2011  "ParseError","root tag missing");
2012  utf8=DestroyString(utf8);
2013  return((XMLTreeInfo *) NULL);
2014  }
2015  attribute=(char **) NULL;
2016  l=0;
2017  ignore_depth=0;
2018  for (p++; ; p++)
2019  {
2020  attributes=(char **) sentinel;
2021  tag=p;
2022  c=(*p);
2023  if ((isalpha((int) ((unsigned char) *p)) !=0) || (*p == '_') ||
2024  (*p == ':') || (c < '\0'))
2025  {
2026  /*
2027  Tag.
2028  */
2029  if (root->node == (XMLTreeInfo *) NULL)
2030  {
2031  (void) ThrowMagickException(exception,GetMagickModule(),
2032  OptionWarning,"ParseError","root tag missing");
2033  utf8=DestroyString(utf8);
2034  return(&root->root);
2035  }
2036  p+=(ptrdiff_t) strcspn(p,XMLWhitespace "/>");
2037  while (isspace((int) ((unsigned char) *p)) != 0)
2038  *p++='\0';
2039  if (((isalpha((int) ((unsigned char) *p)) != 0) || (*p == '_')) &&
2040  (ignore_depth == 0))
2041  {
2042  if ((*p != '\0') && (*p != '/') && (*p != '>'))
2043  {
2044  /*
2045  Find tag in default attributes list.
2046  */
2047  i=0;
2048  while ((root->attributes[i] != (char **) NULL) &&
2049  (strcmp(root->attributes[i][0],tag) != 0))
2050  i++;
2051  attribute=root->attributes[i];
2052  }
2053  for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
2054  {
2055  /*
2056  Attribute.
2057  */
2058  if (l == 0)
2059  attributes=(char **) AcquireQuantumMemory(4,
2060  sizeof(*attributes));
2061  else
2062  attributes=(char **) ResizeQuantumMemory(attributes,
2063  (size_t) (l+4),sizeof(*attributes));
2064  if (attributes == (char **) NULL)
2065  {
2066  (void) ThrowMagickException(exception,GetMagickModule(),
2067  ResourceLimitError,"MemoryAllocationFailed","`%s'","");
2068  utf8=DestroyString(utf8);
2069  return(&root->root);
2070  }
2071  attributes[l+2]=(char *) NULL;
2072  attributes[l+1]=(char *) NULL;
2073  attributes[l]=p;
2074  p+=(ptrdiff_t) strcspn(p,XMLWhitespace "=/>");
2075  if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
2076  attributes[l]=ConstantString("");
2077  else
2078  {
2079  *p++='\0';
2080  p+=(ptrdiff_t) strspn(p,XMLWhitespace "=");
2081  c=(*p);
2082  if ((c == '"') || (c == '\''))
2083  {
2084  /*
2085  Attributes value.
2086  */
2087  p++;
2088  attributes[l+1]=p;
2089  while ((*p != '\0') && (*p != c))
2090  p++;
2091  if (*p != '\0')
2092  *p++='\0';
2093  else
2094  {
2095  attributes[l]=ConstantString("");
2096  attributes[l+1]=ConstantString("");
2097  (void) DestroyXMLTreeAttributes(attributes);
2098  (void) ThrowMagickException(exception,
2099  GetMagickModule(),OptionWarning,"ParseError",
2100  "missing %c",c);
2101  utf8=DestroyString(utf8);
2102  return(&root->root);
2103  }
2104  j=1;
2105  while ((attribute != (char **) NULL) &&
2106  (attribute[j] != (char *) NULL) &&
2107  (strcmp(attribute[j],attributes[l]) != 0))
2108  j+=3;
2109  attributes[l+1]=ParseEntities(attributes[l+1],
2110  root->entities,(attribute != (char **) NULL) &&
2111  (attribute[j] != (char *) NULL) ? *attribute[j+2] :
2112  ' ');
2113  }
2114  attributes[l]=ConstantString(attributes[l]);
2115  }
2116  while (isspace((int) ((unsigned char) *p)) != 0)
2117  p++;
2118  }
2119  }
2120  else
2121  {
2122  while((*p != '\0') && (*p != '/') && (*p != '>'))
2123  p++;
2124  }
2125  if (*p == '/')
2126  {
2127  /*
2128  Self closing tag.
2129  */
2130  *p++='\0';
2131  if (((*p != '\0') && (*p != '>')) ||
2132  ((*p == '\0') && (terminal != '>')))
2133  {
2134  if (l != 0)
2135  (void) DestroyXMLTreeAttributes(attributes);
2136  (void) ThrowMagickException(exception,GetMagickModule(),
2137  OptionWarning,"ParseError","missing >");
2138  utf8=DestroyString(utf8);
2139  return(&root->root);
2140  }
2141  if ((ignore_depth != 0) || (IsSkipTag(tag) != MagickFalse))
2142  (void) DestroyXMLTreeAttributes(attributes);
2143  else
2144  {
2145  ParseOpenTag(root,tag,attributes);
2146  (void) ParseCloseTag(root,tag,exception);
2147  }
2148  }
2149  else
2150  {
2151  c=(*p);
2152  if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
2153  {
2154  *p='\0';
2155  if ((ignore_depth == 0) && (IsSkipTag(tag) == MagickFalse))
2156  ParseOpenTag(root,tag,attributes);
2157  else
2158  {
2159  ignore_depth++;
2160  (void) DestroyXMLTreeAttributes(attributes);
2161  }
2162  *p=c;
2163  }
2164  else
2165  {
2166  if (l != 0)
2167  (void) DestroyXMLTreeAttributes(attributes);
2168  (void) ThrowMagickException(exception,GetMagickModule(),
2169  OptionWarning,"ParseError","missing >");
2170  utf8=DestroyString(utf8);
2171  return(&root->root);
2172  }
2173  }
2174  }
2175  else
2176  if (*p == '/')
2177  {
2178  /*
2179  Close tag.
2180  */
2181  tag=p+1;
2182  p+=(ptrdiff_t) strcspn(tag,XMLWhitespace ">")+1;
2183  c=(*p);
2184  if ((c == '\0') && (terminal != '>'))
2185  {
2186  (void) ThrowMagickException(exception,GetMagickModule(),
2187  OptionWarning,"ParseError","missing >");
2188  utf8=DestroyString(utf8);
2189  return(&root->root);
2190  }
2191  *p='\0';
2192  if ((ignore_depth == 0) &&
2193  (ParseCloseTag(root,tag,exception) != (XMLTreeInfo *) NULL))
2194  {
2195  utf8=DestroyString(utf8);
2196  return(&root->root);
2197  }
2198  if (ignore_depth > 0)
2199  ignore_depth--;
2200  *p=c;
2201  if (isspace((int) ((unsigned char) *p)) != 0)
2202  p+=(ptrdiff_t) strspn(p,XMLWhitespace);
2203  }
2204  else
2205  if (strncmp(p,"!--",3) == 0)
2206  {
2207  /*
2208  Comment.
2209  */
2210  p=strstr(p+3,"--");
2211  if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
2212  ((*p == '\0') && (terminal != '>')))
2213  {
2214  (void) ThrowMagickException(exception,GetMagickModule(),
2215  OptionWarning,"ParseError","unclosed <!--");
2216  utf8=DestroyString(utf8);
2217  return(&root->root);
2218  }
2219  }
2220  else
2221  if (strncmp(p,"![CDATA[",8) == 0)
2222  {
2223  /*
2224  Cdata.
2225  */
2226  p=strstr(p,"]]>");
2227  if (p != (char *) NULL)
2228  {
2229  p+=(ptrdiff_t) 2;
2230  if (ignore_depth == 0)
2231  ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
2232  }
2233  else
2234  {
2235  (void) ThrowMagickException(exception,GetMagickModule(),
2236  OptionWarning,"ParseError","unclosed <![CDATA[");
2237  utf8=DestroyString(utf8);
2238  return(&root->root);
2239  }
2240  }
2241  else
2242  if (strncmp(p,"!DOCTYPE",8) == 0)
2243  {
2244  /*
2245  DTD.
2246  */
2247  for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
2248  ((l != 0) && ((*p != ']') ||
2249  (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
2250  l=(ssize_t) ((*p == '[') ? 1 : l))
2251  p+=(ptrdiff_t) strcspn(p+1,"[]>")+1;
2252  if ((*p == '\0') && (terminal != '>'))
2253  {
2254  (void) ThrowMagickException(exception,GetMagickModule(),
2255  OptionWarning,"ParseError","unclosed <!DOCTYPE");
2256  utf8=DestroyString(utf8);
2257  return(&root->root);
2258  }
2259  if (l != 0)
2260  tag=strchr(tag,'[')+1;
2261  if (l != 0)
2262  {
2263  status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
2264  exception);
2265  if (status == MagickFalse)
2266  {
2267  utf8=DestroyString(utf8);
2268  return(&root->root);
2269  }
2270  p++;
2271  }
2272  }
2273  else
2274  if (*p == '?')
2275  {
2276  /*
2277  Processing instructions.
2278  */
2279  do
2280  {
2281  p=strchr(p,'?');
2282  if (p == (char *) NULL)
2283  break;
2284  p++;
2285  } while ((*p != '\0') && (*p != '>'));
2286  if ((p == (char *) NULL) || ((*p == '\0') &&
2287  (terminal != '>')))
2288  {
2289  (void) ThrowMagickException(exception,GetMagickModule(),
2290  OptionWarning,"ParseError","unclosed <?");
2291  utf8=DestroyString(utf8);
2292  return(&root->root);
2293  }
2294  ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
2295  }
2296  else
2297  {
2298  (void) ThrowMagickException(exception,GetMagickModule(),
2299  OptionWarning,"ParseError","unexpected <");
2300  utf8=DestroyString(utf8);
2301  return(&root->root);
2302  }
2303  if ((p == (char *) NULL) || (*p == '\0'))
2304  break;
2305  *p++='\0';
2306  tag=p;
2307  if ((*p != '\0') && (*p != '<'))
2308  {
2309  /*
2310  Tag character content.
2311  */
2312  while ((*p != '\0') && (*p != '<'))
2313  p++;
2314  if (*p == '\0')
2315  break;
2316  if (ignore_depth == 0)
2317  ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
2318  }
2319  else
2320  if (*p == '\0')
2321  break;
2322  }
2323  utf8=DestroyString(utf8);
2324  if (root->node == (XMLTreeInfo *) NULL)
2325  return(&root->root);
2326  if (root->node->tag == (char *) NULL)
2327  {
2328  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2329  "ParseError","root tag missing");
2330  return(&root->root);
2331  }
2332  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2333  "ParseError","unclosed tag: `%s'",root->node->tag);
2334  return(&root->root);
2335 }
2336 
2337 /*
2338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2339 % %
2340 % %
2341 % %
2342 % N e w X M L T r e e T a g %
2343 % %
2344 % %
2345 % %
2346 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2347 %
2348 % NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
2349 %
2350 % The format of the NewXMLTreeTag method is:
2351 %
2352 % XMLTreeInfo *NewXMLTreeTag(const char *tag)
2353 %
2354 % A description of each parameter follows:
2355 %
2356 % o tag: the tag.
2357 %
2358 */
2359 MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
2360 {
2361  static const char
2362  *predefined_entities[NumberPredefinedEntities+1] =
2363  {
2364  "lt;", "&#60;", "gt;", "&#62;", "quot;", "&#34;",
2365  "apos;", "&#39;", "amp;", "&#38;", (char *) NULL
2366  };
2367 
2368  XMLTreeRoot
2369  *root;
2370 
2371  root=(XMLTreeRoot *) AcquireCriticalMemory(sizeof(*root));
2372  (void) memset(root,0,sizeof(*root));
2373  root->root.tag=(char *) NULL;
2374  if (tag != (char *) NULL)
2375  root->root.tag=ConstantString(tag);
2376  root->node=(&root->root);
2377  root->root.content=ConstantString("");
2378  root->entities=(char **) AcquireCriticalMemory(sizeof(predefined_entities));
2379  (void) memcpy(root->entities,predefined_entities,sizeof(predefined_entities));
2380  root->root.attributes=sentinel;
2381  root->attributes=(char ***) root->root.attributes;
2382  root->processing_instructions=(char ***) root->root.attributes;
2383  root->debug=IsEventLogging();
2384  root->signature=MagickCoreSignature;
2385  return(&root->root);
2386 }
2387 
2388 /*
2389 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2390 % %
2391 % %
2392 % %
2393 % P r u n e T a g F r o m X M L T r e e %
2394 % %
2395 % %
2396 % %
2397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2398 %
2399 % PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
2400 % subtags.
2401 %
2402 % The format of the PruneTagFromXMLTree method is:
2403 %
2404 % XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2405 %
2406 % A description of each parameter follows:
2407 %
2408 % o xml_info: the xml info.
2409 %
2410 */
2411 MagickExport XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2412 {
2413  XMLTreeInfo
2414  *node;
2415 
2416  assert(xml_info != (XMLTreeInfo *) NULL);
2417  assert((xml_info->signature == MagickCoreSignature) ||
2418  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2419  if (IsEventLogging() != MagickFalse)
2420  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2421  if (xml_info->next != (XMLTreeInfo *) NULL)
2422  xml_info->next->sibling=xml_info->sibling;
2423  if (xml_info->parent != (XMLTreeInfo *) NULL)
2424  {
2425  node=xml_info->parent->child;
2426  if (node == xml_info)
2427  xml_info->parent->child=xml_info->ordered;
2428  else
2429  {
2430  while (node->ordered != xml_info)
2431  node=node->ordered;
2432  node->ordered=node->ordered->ordered;
2433  node=xml_info->parent->child;
2434  if (strcmp(node->tag,xml_info->tag) != 0)
2435  {
2436  while (strcmp(node->sibling->tag,xml_info->tag) != 0)
2437  node=node->sibling;
2438  if (node->sibling != xml_info)
2439  node=node->sibling;
2440  else
2441  node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
2442  xml_info->next : node->sibling->sibling;
2443  }
2444  while ((node->next != (XMLTreeInfo *) NULL) &&
2445  (node->next != xml_info))
2446  node=node->next;
2447  if (node->next != (XMLTreeInfo *) NULL)
2448  node->next=node->next->next;
2449  }
2450  }
2451  xml_info->ordered=(XMLTreeInfo *) NULL;
2452  xml_info->sibling=(XMLTreeInfo *) NULL;
2453  xml_info->next=(XMLTreeInfo *) NULL;
2454  return(xml_info);
2455 }
2456 
2457 /*
2458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2459 % %
2460 % %
2461 % %
2462 % S e t X M L T r e e A t t r i b u t e %
2463 % %
2464 % %
2465 % %
2466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2467 %
2468 % SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
2469 % found. A value of NULL removes the specified attribute.
2470 %
2471 % The format of the SetXMLTreeAttribute method is:
2472 %
2473 % XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
2474 % const char *value)
2475 %
2476 % A description of each parameter follows:
2477 %
2478 % o xml_info: the xml info.
2479 %
2480 % o tag: The attribute tag.
2481 %
2482 % o value: The attribute value.
2483 %
2484 */
2485 MagickExport XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
2486  const char *tag,const char *value)
2487 {
2488  ssize_t
2489  i,
2490  j;
2491 
2492  assert(xml_info != (XMLTreeInfo *) NULL);
2493  assert((xml_info->signature == MagickCoreSignature) ||
2494  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2495  if (IsEventLogging() != MagickFalse)
2496  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2497  i=0;
2498  while ((xml_info->attributes[i] != (char *) NULL) &&
2499  (strcmp(xml_info->attributes[i],tag) != 0))
2500  i+=2;
2501  if (xml_info->attributes[i] == (char *) NULL)
2502  {
2503  /*
2504  Add new attribute tag.
2505  */
2506  if (value == (const char *) NULL)
2507  return(xml_info);
2508  if (xml_info->attributes != sentinel)
2509  xml_info->attributes=(char **) ResizeQuantumMemory(
2510  xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
2511  else
2512  {
2513  xml_info->attributes=(char **) AcquireQuantumMemory(4,
2514  sizeof(*xml_info->attributes));
2515  if (xml_info->attributes != (char **) NULL)
2516  xml_info->attributes[1]=ConstantString("");
2517  }
2518  if (xml_info->attributes == (char **) NULL)
2519  ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2520  xml_info->attributes[i]=ConstantString(tag);
2521  xml_info->attributes[i+2]=(char *) NULL;
2522  (void) strlen(xml_info->attributes[i+1]);
2523  }
2524  /*
2525  Add new value to an existing attribute.
2526  */
2527  for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
2528  if (xml_info->attributes[i+1] != (char *) NULL)
2529  xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
2530  if (value != (const char *) NULL)
2531  {
2532  xml_info->attributes[i+1]=ConstantString(value);
2533  return(xml_info);
2534  }
2535  if (xml_info->attributes[i] != (char *) NULL)
2536  xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
2537  (void) memmove(xml_info->attributes+i,xml_info->attributes+i+2,
2538  (size_t) (j-i)*sizeof(*xml_info->attributes));
2539  xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
2540  (size_t) (j+2),sizeof(*xml_info->attributes));
2541  if (xml_info->attributes == (char **) NULL)
2542  ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2543  j-=2;
2544  (void) memmove(xml_info->attributes[j+1]+(i/2),xml_info->attributes[j+1]+
2545  (i/2)+1,(size_t) (((j+2)/2)-(i/2))*sizeof(**xml_info->attributes));
2546  return(xml_info);
2547 }
2548 
2549 /*
2550 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2551 % %
2552 % %
2553 % %
2554 % S e t X M L T r e e C o n t e n t %
2555 % %
2556 % %
2557 % %
2558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2559 %
2560 % SetXMLTreeContent() sets the character content for the given tag and
2561 % returns the tag.
2562 %
2563 % The format of the SetXMLTreeContent method is:
2564 %
2565 % XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2566 % const char *content)
2567 %
2568 % A description of each parameter follows:
2569 %
2570 % o xml_info: the xml info.
2571 %
2572 % o content: The content.
2573 %
2574 */
2575 MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2576  const char *content)
2577 {
2578  assert(xml_info != (XMLTreeInfo *) NULL);
2579  assert((xml_info->signature == MagickCoreSignature) ||
2580  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2581  if (IsEventLogging() != MagickFalse)
2582  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2583  if (xml_info->content != (char *) NULL)
2584  xml_info->content=DestroyString(xml_info->content);
2585  xml_info->content=(char *) ConstantString(content);
2586  return(xml_info);
2587 }
2588 
2589 /*
2590 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2591 % %
2592 % %
2593 % %
2594 % X M L T r e e I n f o T o X M L %
2595 % %
2596 % %
2597 % %
2598 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2599 %
2600 % XMLTreeInfoToXML() converts an xml-tree to an XML string.
2601 %
2602 % The format of the XMLTreeInfoToXML method is:
2603 %
2604 % char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2605 %
2606 % A description of each parameter follows:
2607 %
2608 % o xml_info: the xml info.
2609 %
2610 */
2611 
2612 static char *EncodePredefinedEntities(const char *source,ssize_t offset,
2613  char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
2614 {
2615  char
2616  *canonical_content;
2617 
2618  if (offset < 0)
2619  canonical_content=CanonicalXMLContent(source,pedantic);
2620  else
2621  {
2622  char
2623  *content;
2624 
2625  content=AcquireString(source);
2626  content[offset]='\0';
2627  canonical_content=CanonicalXMLContent(content,pedantic);
2628  content=DestroyString(content);
2629  }
2630  if (canonical_content == (char *) NULL)
2631  return(*destination);
2632  if ((*length+strlen(canonical_content)+MaxTextExtent) > *extent)
2633  {
2634  *extent=(*length)+strlen(canonical_content)+MaxTextExtent;
2635  *destination=(char *) ResizeQuantumMemory(*destination,*extent,
2636  sizeof(**destination));
2637  if (*destination == (char *) NULL)
2638  return(*destination);
2639  }
2640  *length+=FormatLocaleString(*destination+(*length),*extent,"%s",
2641  canonical_content);
2642  canonical_content=DestroyString(canonical_content);
2643  return(*destination);
2644 }
2645 
2646 static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
2647  size_t *extent,size_t start,char ***attributes)
2648 {
2649  char
2650  *content;
2651 
2652  const char
2653  *attribute;
2654 
2655  size_t
2656  offset;
2657 
2658  ssize_t
2659  i,
2660  j;
2661 
2662  content=(char *) "";
2663  if (xml_info->parent != (XMLTreeInfo *) NULL)
2664  content=xml_info->parent->content;
2665  offset=0;
2666  *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
2667  start),source,length,extent,MagickFalse);
2668  if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2669  {
2670  *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2671  *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2672  if (*source == (char *) NULL)
2673  return(*source);
2674  }
2675  *length+=FormatLocaleString(*source+(*length),*extent,"<%s",xml_info->tag);
2676  for (i=0; xml_info->attributes[i]; i+=2)
2677  {
2678  attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
2679  if (attribute != xml_info->attributes[i+1])
2680  continue;
2681  if ((*length+strlen(xml_info->attributes[i])+MaxTextExtent) > *extent)
2682  {
2683  *extent=(*length)+strlen(xml_info->attributes[i])+MaxTextExtent;
2684  *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2685  if (*source == (char *) NULL)
2686  return((char *) NULL);
2687  }
2688  *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
2689  xml_info->attributes[i]);
2690  (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
2691  extent,MagickTrue);
2692  *length+=FormatLocaleString(*source+(*length),*extent,"\"");
2693  }
2694  i=0;
2695  while ((attributes[i] != (char **) NULL) &&
2696  (strcmp(attributes[i][0],xml_info->tag) != 0))
2697  i++;
2698  j=1;
2699  while ((attributes[i] != (char **) NULL) &&
2700  (attributes[i][j] != (char *) NULL))
2701  {
2702  if ((attributes[i][j+1] == (char *) NULL) ||
2703  (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
2704  {
2705  j+=3;
2706  continue;
2707  }
2708  if ((*length+strlen(attributes[i][j])+MaxTextExtent) > *extent)
2709  {
2710  *extent=(*length)+strlen(attributes[i][j])+MaxTextExtent;
2711  *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2712  if (*source == (char *) NULL)
2713  return((char *) NULL);
2714  }
2715  *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
2716  attributes[i][j]);
2717  (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
2718  MagickTrue);
2719  *length+=FormatLocaleString(*source+(*length),*extent,"\"");
2720  j+=3;
2721  }
2722  *length+=FormatLocaleString(*source+(*length),*extent,*xml_info->content ?
2723  ">" : "/>");
2724  if (xml_info->child != (XMLTreeInfo *) NULL)
2725  *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
2726  else
2727  *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
2728  MagickFalse);
2729  if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2730  {
2731  *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2732  *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2733  if (*source == (char *) NULL)
2734  return((char *) NULL);
2735  }
2736  if (*xml_info->content != '\0')
2737  *length+=FormatLocaleString(*source+(*length),*extent,"</%s>",
2738  xml_info->tag);
2739  while ((offset < xml_info->offset) && (content[offset] != '\0'))
2740  offset++;
2741  if (xml_info->ordered != (XMLTreeInfo *) NULL)
2742  content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
2743  attributes);
2744  else
2745  content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
2746  MagickFalse);
2747  return(content);
2748 }
2749 
2750 MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2751 {
2752  char
2753  *p,
2754  *q,
2755  *xml;
2756 
2757  size_t
2758  extent,
2759  length;
2760 
2761  ssize_t
2762  i,
2763  j,
2764  k;
2765 
2766  XMLTreeInfo
2767  *ordered,
2768  *parent;
2769 
2770  XMLTreeRoot
2771  *root;
2772 
2773  assert(xml_info != (XMLTreeInfo *) NULL);
2774  assert((xml_info->signature == MagickCoreSignature) ||
2775  (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
2776  if (IsEventLogging() != MagickFalse)
2777  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2778  if (xml_info->tag == (char *) NULL)
2779  return((char *) NULL);
2780  xml=AcquireString((char *) NULL);
2781  length=0;
2782  extent=MaxTextExtent;
2783  root=(XMLTreeRoot *) xml_info;
2784  while (root->root.parent != (XMLTreeInfo *) NULL)
2785  root=(XMLTreeRoot *) root->root.parent;
2786  parent=xml_info->parent;
2787  if (parent == (XMLTreeInfo *) NULL)
2788  for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2789  {
2790  /*
2791  Pre-root processing instructions.
2792  */
2793  for (k=2; root->processing_instructions[i][k-1]; k++) ;
2794  p=root->processing_instructions[i][1];
2795  for (j=1; p != (char *) NULL; j++)
2796  {
2797  if (root->processing_instructions[i][k][j-1] == '>')
2798  {
2799  p=root->processing_instructions[i][j];
2800  continue;
2801  }
2802  q=root->processing_instructions[i][0];
2803  if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2804  {
2805  extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2806  xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2807  if (xml == (char *) NULL)
2808  return(xml);
2809  }
2810  length+=FormatLocaleString(xml+length,extent,"<?%s%s%s?>\n",q,
2811  *p != '\0' ? " " : "",p);
2812  p=root->processing_instructions[i][j];
2813  }
2814  }
2815  ordered=xml_info->ordered;
2816  xml_info->parent=(XMLTreeInfo *) NULL;
2817  xml_info->ordered=(XMLTreeInfo *) NULL;
2818  xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
2819  xml_info->parent=parent;
2820  xml_info->ordered=ordered;
2821  if (parent == (XMLTreeInfo *) NULL)
2822  for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2823  {
2824  /*
2825  Post-root processing instructions.
2826  */
2827  for (k=2; root->processing_instructions[i][k-1]; k++) ;
2828  p=root->processing_instructions[i][1];
2829  for (j=1; p != (char *) NULL; j++)
2830  {
2831  if (root->processing_instructions[i][k][j-1] == '<')
2832  {
2833  p=root->processing_instructions[i][j];
2834  continue;
2835  }
2836  q=root->processing_instructions[i][0];
2837  if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2838  {
2839  extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2840  xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2841  if (xml == (char *) NULL)
2842  return(xml);
2843  }
2844  length+=FormatLocaleString(xml+length,extent,"\n<?%s%s%s?>",q,
2845  *p != '\0' ? " " : "",p);
2846  p=root->processing_instructions[i][j];
2847  }
2848  }
2849  return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));
2850 }