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