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