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