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