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