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