blob: 5851322cb8209b75dbe1a1154a09a09db4d200e4 [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) &&
cristy8eaf1852010-01-14 03:08:10 +00001615 (strcmp(n,root->attributes[i][0]) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001616 i++;
cristyeaf70f72010-01-14 14:22:44 +00001617 while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') &&
1618 (*n != '>'))
cristy3ed852e2009-09-05 21:47:34 +00001619 {
1620 xml=n+strcspn(n,XMLWhitespace);
1621 if (*xml != '\0')
1622 *xml='\0';
1623 else
1624 {
1625 (void) ThrowMagickException(exception,GetMagickModule(),
1626 OptionWarning,"ParseError","malformed <!ATTLIST");
1627 predefined_entitites=(char **) RelinquishMagickMemory(
1628 predefined_entitites);
1629 return(MagickFalse);
1630 }
1631 xml+=strspn(xml+1,XMLWhitespace)+1;
1632 c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
1633 if (strncmp(xml,"NOTATION",8) == 0)
1634 xml+=strspn(xml+8,XMLWhitespace)+8;
1635 xml=(*xml == '(') ? strchr(xml,')') : xml+
1636 strcspn(xml,XMLWhitespace);
1637 if (xml == (char *) NULL)
1638 {
1639 (void) ThrowMagickException(exception,GetMagickModule(),
1640 OptionWarning,"ParseError","malformed <!ATTLIST");
1641 predefined_entitites=(char **) RelinquishMagickMemory(
1642 predefined_entitites);
1643 return(MagickFalse);
1644 }
1645 xml+=strspn(xml,XMLWhitespace ")");
1646 if (strncmp(xml,"#FIXED",6) == 0)
1647 xml+=strspn(xml+6,XMLWhitespace)+6;
1648 if (*xml == '#')
1649 {
1650 xml+=strcspn(xml,XMLWhitespace ">")-1;
1651 if (*c == ' ')
1652 continue;
1653 v=(char *) NULL;
1654 }
1655 else
1656 if (((*xml == '"') || (*xml == '\'')) &&
1657 ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
1658 *xml='\0';
1659 else
1660 {
1661 (void) ThrowMagickException(exception,GetMagickModule(),
1662 OptionWarning,"ParseError","malformed <!ATTLIST");
1663 predefined_entitites=(char **) RelinquishMagickMemory(
1664 predefined_entitites);
1665 return(MagickFalse);
1666 }
1667 if (root->attributes[i] == (char **) NULL)
1668 {
1669 /*
1670 New attribute tag.
1671 */
1672 if (i == 0)
1673 root->attributes=(char ***) AcquireQuantumMemory(2,
1674 sizeof(*root->attributes));
1675 else
1676 root->attributes=(char ***) ResizeQuantumMemory(
1677 root->attributes,(size_t) (i+2),
1678 sizeof(*root->attributes));
1679 if (root->attributes == (char ***) NULL)
1680 ThrowFatalException(ResourceLimitFatalError,
1681 "MemoryAllocationFailed");
1682 root->attributes[i]=(char **) AcquireQuantumMemory(2,
1683 sizeof(*root->attributes));
1684 if (root->attributes[i] == (char **) NULL)
1685 ThrowFatalException(ResourceLimitFatalError,
1686 "MemoryAllocationFailed");
1687 root->attributes[i][0]=ConstantString(t);
1688 root->attributes[i][1]=(char *) NULL;
1689 root->attributes[i+1]=(char **) NULL;
1690 }
1691 for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
1692 root->attributes[i]=(char **) ResizeQuantumMemory(
1693 root->attributes[i],(size_t) (j+4),sizeof(*root->attributes));
1694 if (root->attributes[i] == (char **) NULL)
1695 ThrowFatalException(ResourceLimitFatalError,
1696 "MemoryAllocationFailed");
1697 root->attributes[i][j+3]=(char *) NULL;
1698 root->attributes[i][j+2]=ConstantString(c);
1699 root->attributes[i][j+1]=(char *) NULL;
1700 if (v != (char *) NULL)
1701 root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
1702 root->attributes[i][j]=ConstantString(n);
1703 }
1704 }
1705 else
1706 if (strncmp(xml, "<!--", 4) == 0)
1707 xml=strstr(xml+4,"-->");
1708 else
1709 if (strncmp(xml,"<?", 2) == 0)
1710 {
1711 c=xml+2;
1712 xml=strstr(c,"?>");
1713 if (xml != (char *) NULL)
1714 {
1715 ParseProcessingInstructions(root,c,(size_t) (xml-c));
1716 xml++;
1717 }
1718 }
1719 else
1720 if (*xml == '<')
1721 xml=strchr(xml,'>');
1722 else
1723 if ((*(xml++) == '%') && (root->standalone == MagickFalse))
1724 break;
1725 }
1726 predefined_entitites=(char **) RelinquishMagickMemory(predefined_entitites);
1727 return(MagickTrue);
1728}
1729
1730static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
1731{
1732 XMLTreeInfo
1733 *xml_info;
1734
1735 xml_info=root->node;
1736 if (xml_info->tag == (char *) NULL)
1737 xml_info->tag=ConstantString(tag);
1738 else
1739 xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
1740 xml_info->attributes=attributes;
1741 root->node=xml_info;
1742}
1743
1744MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1745{
1746 char
1747 **attribute,
1748 **attributes,
1749 *tag,
1750 *utf8;
1751
1752 int
1753 c,
1754 terminal;
1755
1756 long
1757 j,
1758 l;
1759
1760 register char
1761 *p;
1762
1763 register long
1764 i;
1765
1766 size_t
1767 length;
1768
1769 MagickBooleanType
1770 status;
1771
1772 XMLTreeRoot
1773 *root;
1774
1775 /*
1776 Convert xml-string to UTF8.
1777 */
1778 if ((xml == (const char *) NULL) || (strlen(xml) == 0))
1779 {
1780 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1781 "ParseError","root tag missing");
1782 return((XMLTreeInfo *) NULL);
1783 }
1784 root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
1785 length=strlen(xml);
1786 utf8=ConvertUTF16ToUTF8(xml,&length);
1787 if (utf8 == (char *) NULL)
1788 {
1789 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1790 "ParseError","UTF16 to UTF8 failed");
1791 return((XMLTreeInfo *) NULL);
1792 }
1793 terminal=utf8[length-1];
1794 utf8[length-1]='\0';
1795 p=utf8;
1796 while ((*p != '\0') && (*p != '<'))
1797 p++;
1798 if (*p == '\0')
1799 {
1800 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1801 "ParseError","root tag missing");
1802 utf8=DestroyString(utf8);
1803 return((XMLTreeInfo *) NULL);
1804 }
1805 attribute=(char **) NULL;
1806 for (p++; ; p++)
1807 {
1808 attributes=(char **) sentinel;
1809 tag=p;
1810 if ((isalpha((int) ((unsigned char) *p)) !=0) || (*p == '_') ||
1811 (*p == ':') || (*p < '\0'))
1812 {
1813 /*
1814 Tag.
1815 */
1816 if (root->node == (XMLTreeInfo *) NULL)
1817 {
1818 (void) ThrowMagickException(exception,GetMagickModule(),
1819 OptionWarning,"ParseError","root tag missing");
1820 utf8=DestroyString(utf8);
1821 return(&root->root);
1822 }
1823 p+=strcspn(p,XMLWhitespace "/>");
1824 while (isspace((int) ((unsigned char) *p)) != 0)
1825 *p++='\0';
1826 if ((*p != '\0') && (*p != '/') && (*p != '>'))
1827 {
1828 /*
1829 Find tag in default attributes list.
1830 */
1831 i=0;
1832 while ((root->attributes[i] != (char **) NULL) &&
1833 (strcmp(root->attributes[i][0],tag) != 0))
1834 i++;
1835 attribute=root->attributes[i];
1836 }
1837 for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
1838 {
1839 /*
1840 Attribute.
1841 */
1842 if (l == 0)
1843 attributes=(char **) AcquireQuantumMemory(4,sizeof(*attributes));
1844 else
1845 attributes=(char **) ResizeQuantumMemory(attributes,(size_t) (l+4),
1846 sizeof(*attributes));
1847 if (attributes == (char **) NULL)
1848 {
1849 (void) ThrowMagickException(exception,GetMagickModule(),
1850 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
1851 utf8=DestroyString(utf8);
1852 return(&root->root);
1853 }
1854 attributes[l+2]=(char *) NULL;
1855 attributes[l+1]=(char *) NULL;
1856 attributes[l]=p;
1857 p+=strcspn(p,XMLWhitespace "=/>");
1858 if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
1859 attributes[l]=ConstantString("");
1860 else
1861 {
1862 *p++='\0';
1863 p+=strspn(p,XMLWhitespace "=");
1864 c=(*p);
1865 if ((c == '"') || (c == '\''))
1866 {
1867 /*
1868 Attributes value.
1869 */
1870 p++;
1871 attributes[l+1]=p;
1872 while ((*p != '\0') && (*p != c))
1873 p++;
1874 if (*p != '\0')
1875 *p++='\0';
1876 else
1877 {
1878 attributes[l]=ConstantString("");
1879 attributes[l+1]=ConstantString("");
1880 (void) DestroyXMLTreeAttributes(attributes);
1881 (void) ThrowMagickException(exception,GetMagickModule(),
1882 OptionWarning,"ParseError","missing %c",c);
1883 utf8=DestroyString(utf8);
1884 return(&root->root);
1885 }
1886 j=1;
1887 while ((attribute != (char **) NULL) &&
1888 (attribute[j] != (char *) NULL) &&
1889 (strcmp(attribute[j],attributes[l]) != 0))
1890 j+=3;
1891 attributes[l+1]=ParseEntities(attributes[l+1],root->entities,
1892 (attribute != (char **) NULL) && (attribute[j] !=
1893 (char *) NULL) ? *attribute[j+2] : ' ');
1894 }
1895 attributes[l]=ConstantString(attributes[l]);
1896 }
1897 while (isspace((int) ((unsigned char) *p)) != 0)
1898 p++;
1899 }
1900 if (*p == '/')
1901 {
1902 /*
1903 Self closing tag.
1904 */
1905 *p++='\0';
1906 if (((*p != '\0') && (*p != '>')) ||
1907 ((*p == '\0') && (terminal != '>')))
1908 {
1909 if (l != 0)
1910 (void) DestroyXMLTreeAttributes(attributes);
1911 (void) ThrowMagickException(exception,GetMagickModule(),
1912 OptionWarning,"ParseError","missing >");
1913 utf8=DestroyString(utf8);
1914 return(&root->root);
1915 }
1916 ParseOpenTag(root,tag,attributes);
1917 (void) ParseCloseTag(root,tag,p,exception);
1918 }
1919 else
1920 {
1921 c=(*p);
1922 if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
1923 {
1924 *p='\0';
1925 ParseOpenTag(root,tag,attributes);
1926 *p=c;
1927 }
1928 else
1929 {
1930 if (l != 0)
1931 (void) DestroyXMLTreeAttributes(attributes);
1932 (void) ThrowMagickException(exception,GetMagickModule(),
1933 OptionWarning,"ParseError","missing >");
1934 utf8=DestroyString(utf8);
1935 return(&root->root);
1936 }
1937 }
1938 }
1939 else
1940 if (*p == '/')
1941 {
1942 /*
1943 Close tag.
1944 */
1945 tag=p+1;
1946 p+=strcspn(tag,XMLWhitespace ">")+1;
1947 c=(*p);
1948 if ((c == '\0') && (terminal != '>'))
1949 {
1950 (void) ThrowMagickException(exception,GetMagickModule(),
1951 OptionWarning,"ParseError","missing >");
1952 utf8=DestroyString(utf8);
1953 return(&root->root);
1954 }
1955 *p='\0';
1956 if (ParseCloseTag(root,tag,p,exception) != (XMLTreeInfo *) NULL)
1957 {
1958 utf8=DestroyString(utf8);
1959 return(&root->root);
1960 }
1961 *p=c;
1962 if (isspace((int) ((unsigned char) *p)) != 0)
1963 p+=strspn(p,XMLWhitespace);
1964 }
1965 else
1966 if (strncmp(p,"!--",3) == 0)
1967 {
1968 /*
1969 Comment.
1970 */
1971 p=strstr(p+3,"--");
1972 if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
1973 ((*p == '\0') && (terminal != '>')))
1974 {
1975 (void) ThrowMagickException(exception,GetMagickModule(),
1976 OptionWarning,"ParseError","unclosed <!--");
1977 utf8=DestroyString(utf8);
1978 return(&root->root);
1979 }
1980 }
1981 else
1982 if (strncmp(p,"![CDATA[",8) == 0)
1983 {
1984 /*
1985 Cdata.
1986 */
1987 p=strstr(p,"]]>");
1988 if (p != (char *) NULL)
1989 {
1990 p+=2;
1991 ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
1992 }
1993 else
1994 {
1995 (void) ThrowMagickException(exception,GetMagickModule(),
1996 OptionWarning,"ParseError","unclosed <![CDATA[");
1997 utf8=DestroyString(utf8);
1998 return(&root->root);
1999 }
2000 }
2001 else
2002 if (strncmp(p,"!DOCTYPE",8) == 0)
2003 {
2004 /*
2005 DTD.
2006 */
2007 for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
2008 ((l != 0) && ((*p != ']') ||
2009 (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
2010 l=(*p == '[') ? 1 : l)
2011 p+=strcspn(p+1,"[]>")+1;
2012 if ((*p == '\0') && (terminal != '>'))
2013 {
2014 (void) ThrowMagickException(exception,GetMagickModule(),
2015 OptionWarning,"ParseError","unclosed <!DOCTYPE");
2016 utf8=DestroyString(utf8);
2017 return(&root->root);
2018 }
2019 if (l != 0)
2020 tag=strchr(tag,'[')+1;
2021 if (l != 0)
2022 {
2023 status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
2024 exception);
2025 if (status == MagickFalse)
2026 {
2027 utf8=DestroyString(utf8);
2028 return(&root->root);
2029 }
2030 p++;
2031 }
2032 }
2033 else
2034 if (*p == '?')
2035 {
2036 /*
2037 Processing instructions.
2038 */
2039 do
2040 {
2041 p=strchr(p,'?');
2042 if (p == (char *) NULL)
2043 break;
2044 p++;
2045 } while ((*p != '\0') && (*p != '>'));
2046 if ((p == (char *) NULL) || ((*p == '\0') &&
2047 (terminal != '>')))
2048 {
2049 (void) ThrowMagickException(exception,GetMagickModule(),
2050 OptionWarning,"ParseError","unclosed <?");
2051 utf8=DestroyString(utf8);
2052 return(&root->root);
2053 }
2054 ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
2055 }
2056 else
2057 {
2058 (void) ThrowMagickException(exception,GetMagickModule(),
2059 OptionWarning,"ParseError","unexpected <");
2060 utf8=DestroyString(utf8);
2061 return(&root->root);
2062 }
2063 if ((p == (char *) NULL) || (*p == '\0'))
2064 break;
2065 *p++='\0';
2066 tag=p;
2067 if ((*p != '\0') && (*p != '<'))
2068 {
2069 /*
2070 Tag character content.
2071 */
2072 while ((*p != '\0') && (*p != '<'))
2073 p++;
2074 if (*p == '\0')
2075 break;
2076 ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
2077 }
2078 else
2079 if (*p == '\0')
2080 break;
2081 }
2082 utf8=DestroyString(utf8);
2083 if (root->node == (XMLTreeInfo *) NULL)
2084 return(&root->root);
2085 if (root->node->tag == (char *) NULL)
2086 {
2087 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2088 "ParseError","root tag missing");
2089 return(&root->root);
2090 }
2091 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2092 "ParseError","unclosed tag: `%s'",root->node->tag);
2093 return(&root->root);
2094}
2095
2096/*
2097%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2098% %
2099% %
2100% %
2101% N e w X M L T r e e T a g %
2102% %
2103% %
2104% %
2105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2106%
2107% NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
2108%
2109% The format of the NewXMLTreeTag method is:
2110%
2111% XMLTreeInfo *NewXMLTreeTag(const char *tag)
2112%
2113% A description of each parameter follows:
2114%
2115% o tag: the tag.
2116%
2117*/
2118MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
2119{
2120 static const char
2121 *predefined_entities[NumberPredefinedEntities+1] =
2122 {
2123 "lt;", "&#60;", "gt;", "&#62;", "quot;", "&#34;",
2124 "apos;", "&#39;", "amp;", "&#38;", (char *) NULL
2125 };
2126
2127 XMLTreeRoot
2128 *root;
2129
cristy90823212009-12-12 20:48:33 +00002130 root=(XMLTreeRoot *) AcquireAlignedMemory(1,sizeof(*root));
cristy3ed852e2009-09-05 21:47:34 +00002131 if (root == (XMLTreeRoot *) NULL)
2132 return((XMLTreeInfo *) NULL);
2133 (void) ResetMagickMemory(root,0,sizeof(*root));
2134 root->root.tag=(char *) NULL;
2135 if (tag != (char *) NULL)
2136 root->root.tag=ConstantString(tag);
2137 root->node=(&root->root);
2138 root->root.content=ConstantString("");
cristy90823212009-12-12 20:48:33 +00002139 root->entities=(char **) AcquireAlignedMemory(1,sizeof(predefined_entities));
cristy3ed852e2009-09-05 21:47:34 +00002140 if (root->entities == (char **) NULL)
2141 return((XMLTreeInfo *) NULL);
2142 (void) CopyMagickMemory(root->entities,predefined_entities,
2143 sizeof(predefined_entities));
2144 root->root.attributes=sentinel;
cristyb068b7a2010-01-06 02:35:13 +00002145 root->attributes=(char ***) root->root.attributes;
2146 root->processing_instructions=(char ***) root->root.attributes;
cristy3ed852e2009-09-05 21:47:34 +00002147 root->debug=IsEventLogging();
2148 root->signature=MagickSignature;
2149 return(&root->root);
2150}
2151
2152/*
2153%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2154% %
2155% %
2156% %
2157% P r u n e T a g F r o m X M L T r e e %
2158% %
2159% %
2160% %
2161%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2162%
2163% PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
2164% subtags.
2165%
2166% The format of the PruneTagFromXMLTree method is:
2167%
2168% XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2169%
2170% A description of each parameter follows:
2171%
2172% o xml_info: the xml info.
2173%
2174*/
2175MagickExport XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2176{
2177 XMLTreeInfo
2178 *node;
2179
2180 assert(xml_info != (XMLTreeInfo *) NULL);
2181 assert((xml_info->signature == MagickSignature) ||
2182 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2183 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2184 if (xml_info->next != (XMLTreeInfo *) NULL)
2185 xml_info->next->sibling=xml_info->sibling;
2186 if (xml_info->parent != (XMLTreeInfo *) NULL)
2187 {
2188 node=xml_info->parent->child;
2189 if (node == xml_info)
2190 xml_info->parent->child=xml_info->ordered;
2191 else
2192 {
2193 while (node->ordered != xml_info)
2194 node=node->ordered;
2195 node->ordered=node->ordered->ordered;
2196 node=xml_info->parent->child;
2197 if (strcmp(node->tag,xml_info->tag) != 0)
2198 {
2199 while (strcmp(node->sibling->tag,xml_info->tag) != 0)
2200 node=node->sibling;
2201 if (node->sibling != xml_info)
2202 node=node->sibling;
2203 else
2204 node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
2205 xml_info->next : node->sibling->sibling;
2206 }
2207 while ((node->next != (XMLTreeInfo *) NULL) &&
2208 (node->next != xml_info))
2209 node=node->next;
2210 if (node->next != (XMLTreeInfo *) NULL)
2211 node->next=node->next->next;
2212 }
2213 }
2214 xml_info->ordered=(XMLTreeInfo *) NULL;
2215 xml_info->sibling=(XMLTreeInfo *) NULL;
2216 xml_info->next=(XMLTreeInfo *) NULL;
2217 return(xml_info);
2218}
2219
2220/*
2221%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2222% %
2223% %
2224% %
2225% S e t X M L T r e e A t t r i b u t e %
2226% %
2227% %
2228% %
2229%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2230%
2231% SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
2232% found. A value of NULL removes the specified attribute.
2233%
2234% The format of the SetXMLTreeAttribute method is:
2235%
2236% XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
2237% const char *value)
2238%
2239% A description of each parameter follows:
2240%
2241% o xml_info: the xml info.
2242%
2243% o tag: The attribute tag.
2244%
2245% o value: The attribute value.
2246%
2247*/
2248MagickExport XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
2249 const char *tag,const char *value)
2250{
2251 long
2252 j;
2253
2254 register long
2255 i;
2256
2257 size_t
2258 length;
2259
2260 assert(xml_info != (XMLTreeInfo *) NULL);
2261 assert((xml_info->signature == MagickSignature) ||
2262 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2263 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2264 i=0;
2265 while ((xml_info->attributes[i] != (char *) NULL) &&
2266 (strcmp(xml_info->attributes[i],tag) != 0))
2267 i+=2;
2268 if (xml_info->attributes[i] == (char *) NULL)
2269 {
2270 /*
2271 Add new attribute tag.
2272 */
2273 if (value == (const char *) NULL)
2274 return(xml_info);
2275 if (xml_info->attributes != sentinel)
2276 xml_info->attributes=(char **) ResizeQuantumMemory(
2277 xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
2278 else
2279 {
2280 xml_info->attributes=(char **) AcquireQuantumMemory(4,
2281 sizeof(*xml_info->attributes));
2282 if (xml_info->attributes != (char **) NULL)
2283 xml_info->attributes[1]=ConstantString("");
2284 }
2285 if (xml_info->attributes == (char **) NULL)
2286 ThrowFatalException(ResourceLimitFatalError,
2287 "UnableToAcquireString");
2288 xml_info->attributes[i]=ConstantString(tag);
2289 xml_info->attributes[i+2]=(char *) NULL;
2290 length=strlen(xml_info->attributes[i+1]);
2291 }
2292 /*
2293 Add new value to an existing attribute.
2294 */
2295 for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
2296 if (xml_info->attributes[i+1] != (char *) NULL)
2297 xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
2298 if (value != (const char *) NULL)
2299 {
2300 xml_info->attributes[i+1]=ConstantString(value);
2301 return(xml_info);
2302 }
2303 if (xml_info->attributes[i] != (char *) NULL)
2304 xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
2305 (void) CopyMagickMemory(xml_info->attributes+i,xml_info->attributes+i+2,
2306 (size_t) (j-i)*sizeof(*xml_info->attributes));
2307 j-=2;
2308 xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
2309 (size_t) (j+2),sizeof(*xml_info->attributes));
2310 if (xml_info->attributes == (char **) NULL)
2311 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
2312 (void) CopyMagickMemory(xml_info->attributes[j+1]+(i/2),
2313 xml_info->attributes[j+1]+(i/2)+1,(size_t) ((j/2)-(i/2))*
2314 sizeof(*xml_info->attributes));
2315 return(xml_info);
2316}
2317
2318/*
2319%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2320% %
2321% %
2322% %
2323% S e t X M L T r e e C o n t e n t %
2324% %
2325% %
2326% %
2327%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2328%
2329% SetXMLTreeContent() sets the character content for the given tag and
2330% returns the tag.
2331%
2332% The format of the SetXMLTreeContent method is:
2333%
2334% XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2335% const char *content)
2336%
2337% A description of each parameter follows:
2338%
2339% o xml_info: the xml info.
2340%
2341% o content: The content.
2342%
2343*/
2344MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2345 const char *content)
2346{
2347 assert(xml_info != (XMLTreeInfo *) NULL);
2348 assert((xml_info->signature == MagickSignature) ||
2349 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2350 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2351 if (xml_info->content != (char *) NULL)
2352 xml_info->content=DestroyString(xml_info->content);
2353 xml_info->content=(char *) ConstantString(content);
2354 return(xml_info);
2355}
2356
2357/*
2358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2359% %
2360% %
2361% %
2362% X M L T r e e I n f o T o X M L %
2363% %
2364% %
2365% %
2366%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2367%
2368% XMLTreeInfoToXML() converts an xml-tree to an XML string.
2369%
2370% The format of the XMLTreeInfoToXML method is:
2371%
2372% char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2373%
2374% A description of each parameter follows:
2375%
2376% o xml_info: the xml info.
2377%
2378*/
2379
2380static char *EncodePredefinedEntities(const char *source,ssize_t offset,
2381 char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
2382{
2383 char
2384 *canonical_content;
2385
2386 if (offset < 0)
2387 canonical_content=CanonicalXMLContent(source,pedantic);
2388 else
2389 {
2390 char
2391 *content;
2392
2393 content=AcquireString(source);
2394 content[offset]='\0';
2395 canonical_content=CanonicalXMLContent(content,pedantic);
2396 content=DestroyString(content);
2397 }
2398 if (canonical_content == (char *) NULL)
2399 return(*destination);
2400 if ((*length+strlen(canonical_content)+MaxTextExtent) > *extent)
2401 {
2402 *extent=(*length)+strlen(canonical_content)+MaxTextExtent;
2403 *destination=(char *) ResizeQuantumMemory(*destination,*extent,
2404 sizeof(**destination));
2405 if (*destination == (char *) NULL)
2406 return(*destination);
2407 }
2408 *length+=FormatMagickString(*destination+(*length),*extent,"%s",
2409 canonical_content);
2410 canonical_content=DestroyString(canonical_content);
2411 return(*destination);
2412}
2413
2414static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
2415 size_t *extent,size_t start,char ***attributes)
2416{
2417 char
2418 *content;
2419
2420 const char
2421 *attribute;
2422
2423 long
2424 j;
2425
2426 register long
2427 i;
2428
2429 size_t
2430 offset;
2431
2432 content=(char *) "";
2433 if (xml_info->parent != (XMLTreeInfo *) NULL)
2434 content=xml_info->parent->content;
2435 offset=0;
2436 *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
2437 start),source,length,extent,MagickFalse);
2438 if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2439 {
2440 *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2441 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(*source));
2442 if (*source == (char *) NULL)
2443 return(*source);
2444 }
2445 *length+=FormatMagickString(*source+(*length),*extent,"<%s",xml_info->tag);
2446 for (i=0; xml_info->attributes[i]; i+=2)
2447 {
2448 attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
2449 if (attribute != xml_info->attributes[i+1])
2450 continue;
2451 if ((*length+strlen(xml_info->attributes[i])+MaxTextExtent) > *extent)
2452 {
2453 *extent=(*length)+strlen(xml_info->attributes[i])+MaxTextExtent;
2454 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2455 if (*source == (char *) NULL)
2456 return((char *) NULL);
2457 }
2458 *length+=FormatMagickString(*source+(*length),*extent," %s=\"",
2459 xml_info->attributes[i]);
2460 (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
2461 extent,MagickTrue);
2462 *length+=FormatMagickString(*source+(*length),*extent,"\"");
2463 }
2464 i=0;
2465 while ((attributes[i] != (char **) NULL) &&
2466 (strcmp(attributes[i][0],xml_info->tag) != 0))
2467 i++;
2468 j=1;
2469 while ((attributes[i] != (char **) NULL) &&
2470 (attributes[i][j] != (char *) NULL))
2471 {
2472 if ((attributes[i][j+1] == (char *) NULL) ||
2473 (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
2474 {
2475 j+=3;
2476 continue;
2477 }
2478 if ((*length+strlen(attributes[i][j])+MaxTextExtent) > *extent)
2479 {
2480 *extent=(*length)+strlen(attributes[i][j])+MaxTextExtent;
2481 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2482 if (*source == (char *) NULL)
2483 return((char *) NULL);
2484 }
2485 *length+=FormatMagickString(*source+(*length),*extent," %s=\"",
2486 attributes[i][j]);
2487 (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
2488 MagickTrue);
2489 *length+=FormatMagickString(*source+(*length),*extent,"\"");
2490 j+=3;
2491 }
2492 *length+=FormatMagickString(*source+(*length),*extent,*xml_info->content ?
2493 ">" : "/>");
2494 if (xml_info->child != (XMLTreeInfo *) NULL)
2495 *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
2496 else
2497 *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
2498 MagickFalse);
2499 if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
2500 {
2501 *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
2502 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2503 if (*source == (char *) NULL)
2504 return((char *) NULL);
2505 }
2506 if (*xml_info->content != '\0')
2507 *length+=FormatMagickString(*source+(*length),*extent,"</%s>",
2508 xml_info->tag);
2509 while ((content[offset] != '\0') && (offset < xml_info->offset))
2510 offset++;
2511 if (xml_info->ordered != (XMLTreeInfo *) NULL)
2512 content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
2513 attributes);
2514 else
2515 content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
2516 MagickFalse);
2517 return(content);
2518}
2519
2520MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2521{
2522 char
2523 *xml;
2524
2525 long
2526 j,
2527 k;
2528
2529 register char
2530 *p,
2531 *q;
2532
2533 register long
2534 i;
2535
2536 size_t
2537 extent,
2538 length;
2539
2540 XMLTreeInfo
2541 *ordered,
2542 *parent;
2543
2544 XMLTreeRoot
2545 *root;
2546
2547 assert(xml_info != (XMLTreeInfo *) NULL);
2548 assert((xml_info->signature == MagickSignature) ||
2549 (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
2550 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2551 if (xml_info->tag == (char *) NULL)
2552 return((char *) NULL);
2553 xml=AcquireString((char *) NULL);
2554 length=0;
2555 extent=MaxTextExtent;
2556 root=(XMLTreeRoot *) xml_info;
2557 while (root->root.parent != (XMLTreeInfo *) NULL)
2558 root=(XMLTreeRoot *) root->root.parent;
2559 parent=(XMLTreeInfo *) NULL;
2560 if (xml_info != (XMLTreeInfo *) NULL)
2561 parent=xml_info->parent;
2562 if (parent == (XMLTreeInfo *) NULL)
2563 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2564 {
2565 /*
2566 Pre-root processing instructions.
2567 */
2568 for (k=2; root->processing_instructions[i][k-1]; k++) ;
2569 p=root->processing_instructions[i][1];
2570 for (j=1; p != (char *) NULL; j++)
2571 {
2572 if (root->processing_instructions[i][k][j-1] == '>')
2573 {
2574 p=root->processing_instructions[i][j];
2575 continue;
2576 }
2577 q=root->processing_instructions[i][0];
2578 if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2579 {
2580 extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2581 xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2582 if (xml == (char *) NULL)
2583 return(xml);
2584 }
2585 length+=FormatMagickString(xml+length,extent,"<?%s%s%s?>\n",q,
2586 *p != '\0' ? " " : "",p);
2587 p=root->processing_instructions[i][j];
2588 }
2589 }
2590 ordered=(XMLTreeInfo *) NULL;
2591 if (xml_info != (XMLTreeInfo *) NULL)
2592 ordered=xml_info->ordered;
2593 xml_info->parent=(XMLTreeInfo *) NULL;
2594 xml_info->ordered=(XMLTreeInfo *) NULL;
2595 xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
2596 xml_info->parent=parent;
2597 xml_info->ordered=ordered;
2598 if (parent == (XMLTreeInfo *) NULL)
2599 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2600 {
2601 /*
2602 Post-root processing instructions.
2603 */
2604 for (k=2; root->processing_instructions[i][k-1]; k++) ;
2605 p=root->processing_instructions[i][1];
2606 for (j=1; p != (char *) NULL; j++)
2607 {
2608 if (root->processing_instructions[i][k][j-1] == '<')
2609 {
2610 p=root->processing_instructions[i][j];
2611 continue;
2612 }
2613 q=root->processing_instructions[i][0];
2614 if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
2615 {
2616 extent=length+strlen(p)+strlen(q)+MaxTextExtent;
2617 xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2618 if (xml == (char *) NULL)
2619 return(xml);
2620 }
2621 length+=FormatMagickString(xml+length,extent,"\n<?%s%s%s?>",q,
2622 *p != '\0' ? " " : "",p);
2623 p=root->processing_instructions[i][j];
2624 }
2625 }
2626 return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));
2627}