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