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