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