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