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