blob: d189070128f5a46540ea16f291a0b701cd91d757 [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% %
cristyb56bb242014-11-25 17:12:48 +000026% Copyright 1999-2015 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"
dirk007e9252015-01-15 21:02:57 +000055#include "MagickCore/image-private.h"
cristy4c08aed2011-07-01 19:47:50 +000056#include "MagickCore/log.h"
57#include "MagickCore/memory_.h"
58#include "MagickCore/semaphore.h"
59#include "MagickCore/string_.h"
60#include "MagickCore/string-private.h"
61#include "MagickCore/token-private.h"
62#include "MagickCore/xml-tree.h"
cristy433d1182011-09-04 13:38:52 +000063#include "MagickCore/xml-tree-private.h"
cristy4c08aed2011-07-01 19:47:50 +000064#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000065#include "MagickCore/utility-private.h"
cristy3ed852e2009-09-05 21:47:34 +000066
67/*
68 Define declarations.
69*/
70#define NumberPredefinedEntities 10
71#define XMLWhitespace "\t\r\n "
72
73/*
74 Typedef declarations.
75*/
76struct _XMLTreeInfo
77{
78 char
79 *tag,
80 **attributes,
81 *content;
82
83 size_t
84 offset;
85
86 XMLTreeInfo
87 *parent,
88 *next,
89 *sibling,
90 *ordered,
91 *child;
92
93 MagickBooleanType
94 debug;
95
96 SemaphoreInfo
97 *semaphore;
98
cristybb503372010-05-27 20:51:26 +000099 size_t
cristy3ed852e2009-09-05 21:47:34 +0000100 signature;
101};
102
103typedef struct _XMLTreeRoot
104 XMLTreeRoot;
105
106struct _XMLTreeRoot
107{
108 struct _XMLTreeInfo
109 root;
110
111 XMLTreeInfo
112 *node;
113
114 MagickBooleanType
115 standalone;
116
117 char
118 ***processing_instructions,
119 **entities,
120 ***attributes;
121
122 MagickBooleanType
123 debug;
124
125 SemaphoreInfo
126 *semaphore;
127
cristybb503372010-05-27 20:51:26 +0000128 size_t
cristy3ed852e2009-09-05 21:47:34 +0000129 signature;
130};
131
132/*
133 Global declarations.
134*/
135static char
136 *sentinel[] = { (char *) NULL };
137
138/*
139%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
140% %
141% %
142% %
143% A d d C h i l d T o X M L T r e e %
144% %
145% %
146% %
147%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
148%
149% AddChildToXMLTree() adds a child tag at an offset relative to the start of
150% the parent tag's character content. Return the child tag.
151%
152% The format of the AddChildToXMLTree method is:
153%
154% XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,const char *tag,
155% const size_t offset)
156%
157% A description of each parameter follows:
158%
159% o xml_info: the xml info.
160%
161% o tag: the tag.
162%
163% o offset: the tag offset.
164%
165*/
166MagickExport XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,
167 const char *tag,const size_t offset)
168{
169 XMLTreeInfo
170 *child;
171
172 if (xml_info == (XMLTreeInfo *) NULL)
173 return((XMLTreeInfo *) NULL);
cristy73bd4a52010-10-05 11:24:23 +0000174 child=(XMLTreeInfo *) AcquireMagickMemory(sizeof(*child));
cristy3ed852e2009-09-05 21:47:34 +0000175 if (child == (XMLTreeInfo *) NULL)
176 return((XMLTreeInfo *) NULL);
177 (void) ResetMagickMemory(child,0,sizeof(*child));
178 child->tag=ConstantString(tag);
179 child->attributes=sentinel;
180 child->content=ConstantString("");
181 child->debug=IsEventLogging();
cristye1c94d92015-06-28 12:16:33 +0000182 child->signature=MagickCoreSignature;
cristy3ed852e2009-09-05 21:47:34 +0000183 return(InsertTagIntoXMLTree(xml_info,child,offset));
184}
185
186/*
187%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
188% %
189% %
190% %
191% A d d P a t h T o X M L T r e e %
192% %
193% %
194% %
195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196%
197% AddPathToXMLTree() adds a child tag at an offset relative to the start of
198% the parent tag's character content. This method returns the child tag.
199%
200% The format of the AddPathToXMLTree method is:
201%
202% XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,const char *path,
203% const size_t offset)
204%
205% A description of each parameter follows:
206%
207% o xml_info: the xml info.
208%
209% o path: the path.
210%
211% o offset: the tag offset.
212%
213*/
cristy433d1182011-09-04 13:38:52 +0000214MagickPrivate XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,
cristy3ed852e2009-09-05 21:47:34 +0000215 const char *path,const size_t offset)
216{
217 char
218 **components,
cristy151b66d2015-04-15 10:50:31 +0000219 subnode[MagickPathExtent],
220 tag[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000221
cristybb503372010-05-27 20:51:26 +0000222 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000223 i;
224
cristy9d314ff2011-03-09 01:30:28 +0000225 size_t
226 number_components;
227
228 ssize_t
229 j;
230
cristy3ed852e2009-09-05 21:47:34 +0000231 XMLTreeInfo
232 *child,
233 *node;
234
cristy3ed852e2009-09-05 21:47:34 +0000235 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000236 assert((xml_info->signature == MagickCoreSignature) ||
237 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +0000238 if (xml_info->debug != MagickFalse)
239 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +0000240 node=xml_info;
241 components=GetPathComponents(path,&number_components);
242 if (components == (char **) NULL)
243 return((XMLTreeInfo *) NULL);
cristybb503372010-05-27 20:51:26 +0000244 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000245 {
246 GetPathComponent(components[i],SubimagePath,subnode);
247 GetPathComponent(components[i],CanonicalPath,tag);
248 child=GetXMLTreeChild(node,tag);
249 if (child == (XMLTreeInfo *) NULL)
250 child=AddChildToXMLTree(node,tag,offset);
251 node=child;
252 if (node == (XMLTreeInfo *) NULL)
253 break;
cristy55a91cd2010-12-01 00:57:40 +0000254 for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
cristy3ed852e2009-09-05 21:47:34 +0000255 {
256 node=GetXMLTreeOrdered(node);
257 if (node == (XMLTreeInfo *) NULL)
258 break;
259 }
260 if (node == (XMLTreeInfo *) NULL)
261 break;
262 components[i]=DestroyString(components[i]);
263 }
cristybb503372010-05-27 20:51:26 +0000264 for ( ; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +0000265 components[i]=DestroyString(components[i]);
266 components=(char **) RelinquishMagickMemory(components);
267 return(node);
268}
269
270/*
271%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
272% %
273% %
274% %
275% C a n o n i c a l X M L C o n t e n t %
276% %
277% %
278% %
279%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
280%
281% CanonicalXMLContent() converts text to canonical XML content by converting
282% to UTF-8, substituting predefined entities, wrapping as CDATA, or encoding
283% as base-64 as required.
284%
285% The format of the CanonicalXMLContent method is:
286%
287%
288% char *CanonicalXMLContent(const char *content,
289% const MagickBooleanType pedantic)
290%
291% A description of each parameter follows:
292%
293% o content: the content.
294%
295% o pedantic: if true, replace newlines and tabs with their respective
296% entities.
297%
298*/
cristy433d1182011-09-04 13:38:52 +0000299MagickPrivate char *CanonicalXMLContent(const char *content,
cristy3ed852e2009-09-05 21:47:34 +0000300 const MagickBooleanType pedantic)
301{
302 char
303 *base64,
304 *canonical_content;
305
306 register const unsigned char
307 *p;
308
cristybb503372010-05-27 20:51:26 +0000309 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000310 i;
311
312 size_t
313 extent,
314 length;
315
316 unsigned char
317 *utf8;
318
319 utf8=ConvertLatin1ToUTF8((const unsigned char *) content);
320 if (utf8 == (unsigned char *) NULL)
321 return((char *) NULL);
322 for (p=utf8; *p != '\0'; p++)
323 if ((*p < 0x20) && (*p != 0x09) && (*p != 0x0a) && (*p != 0x0d))
324 break;
325 if (*p != '\0')
326 {
327 /*
328 String is binary, base64-encode it.
329 */
330 base64=Base64Encode(utf8,strlen((char *) utf8),&length);
331 utf8=(unsigned char *) RelinquishMagickMemory(utf8);
332 if (base64 == (char *) NULL)
333 return((char *) NULL);
334 canonical_content=AcquireString("<base64>");
335 (void) ConcatenateString(&canonical_content,base64);
336 base64=DestroyString(base64);
337 (void) ConcatenateString(&canonical_content,"</base64>");
338 return(canonical_content);
339 }
340 /*
341 Substitute predefined entities.
342 */
343 i=0;
344 canonical_content=AcquireString((char *) NULL);
cristy151b66d2015-04-15 10:50:31 +0000345 extent=MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +0000346 for (p=utf8; *p != '\0'; p++)
347 {
cristy151b66d2015-04-15 10:50:31 +0000348 if ((i+MagickPathExtent) > (ssize_t) extent)
cristy3ed852e2009-09-05 21:47:34 +0000349 {
cristy151b66d2015-04-15 10:50:31 +0000350 extent+=MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +0000351 canonical_content=(char *) ResizeQuantumMemory(canonical_content,extent,
352 sizeof(*canonical_content));
353 if (canonical_content == (char *) NULL)
354 return(canonical_content);
355 }
356 switch (*p)
357 {
358 case '&':
359 {
cristyb51dff52011-05-19 16:55:47 +0000360 i+=FormatLocaleString(canonical_content+i,extent,"&amp;");
cristy3ed852e2009-09-05 21:47:34 +0000361 break;
362 }
363 case '<':
364 {
cristyb51dff52011-05-19 16:55:47 +0000365 i+=FormatLocaleString(canonical_content+i,extent,"&lt;");
cristy3ed852e2009-09-05 21:47:34 +0000366 break;
367 }
368 case '>':
369 {
cristyb51dff52011-05-19 16:55:47 +0000370 i+=FormatLocaleString(canonical_content+i,extent,"&gt;");
cristy3ed852e2009-09-05 21:47:34 +0000371 break;
372 }
373 case '"':
374 {
cristyb51dff52011-05-19 16:55:47 +0000375 i+=FormatLocaleString(canonical_content+i,extent,"&quot;");
cristy3ed852e2009-09-05 21:47:34 +0000376 break;
377 }
378 case '\n':
379 {
380 if (pedantic == MagickFalse)
381 {
382 canonical_content[i++]=(char) (*p);
383 break;
384 }
cristyb51dff52011-05-19 16:55:47 +0000385 i+=FormatLocaleString(canonical_content+i,extent,"&#xA;");
cristy3ed852e2009-09-05 21:47:34 +0000386 break;
387 }
388 case '\t':
389 {
390 if (pedantic == MagickFalse)
391 {
392 canonical_content[i++]=(char) (*p);
393 break;
394 }
cristyb51dff52011-05-19 16:55:47 +0000395 i+=FormatLocaleString(canonical_content+i,extent,"&#x9;");
cristy3ed852e2009-09-05 21:47:34 +0000396 break;
397 }
398 case '\r':
399 {
cristyb51dff52011-05-19 16:55:47 +0000400 i+=FormatLocaleString(canonical_content+i,extent,"&#xD;");
cristy3ed852e2009-09-05 21:47:34 +0000401 break;
402 }
403 default:
404 {
405 canonical_content[i++]=(char) (*p);
406 break;
407 }
408 }
409 }
410 canonical_content[i]='\0';
411 utf8=(unsigned char *) RelinquishMagickMemory(utf8);
412 return(canonical_content);
413}
414
415/*
416%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
417% %
418% %
419% %
420% D e s t r o y X M L T r e e %
421% %
422% %
423% %
424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
425%
426% DestroyXMLTree() destroys the xml-tree.
427%
428% The format of the DestroyXMLTree method is:
429%
430% XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
431%
432% A description of each parameter follows:
433%
434% o xml_info: the xml info.
435%
436*/
437
438static char **DestroyXMLTreeAttributes(char **attributes)
439{
cristybb503372010-05-27 20:51:26 +0000440 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000441 i;
442
443 /*
444 Destroy a tag attribute list.
445 */
446 if ((attributes == (char **) NULL) || (attributes == sentinel))
447 return((char **) NULL);
448 for (i=0; attributes[i] != (char *) NULL; i+=2)
449 {
450 /*
451 Destroy attribute tag and value.
452 */
453 if (attributes[i] != (char *) NULL)
454 attributes[i]=DestroyString(attributes[i]);
455 if (attributes[i+1] != (char *) NULL)
456 attributes[i+1]=DestroyString(attributes[i+1]);
457 }
458 attributes=(char **) RelinquishMagickMemory(attributes);
459 return((char **) NULL);
460}
461
dirkaa5746e2014-04-24 19:08:38 +0000462static void DestroyXMLTreeChild(XMLTreeInfo *xml_info)
463{
464 XMLTreeInfo
dirkb37c8cc2014-07-01 21:08:06 +0000465 *child,
466 *node;
dirkaa5746e2014-04-24 19:08:38 +0000467
dirkb37c8cc2014-07-01 21:08:06 +0000468 child=xml_info->child;
469 while(child != (XMLTreeInfo *) NULL)
dirkaa5746e2014-04-24 19:08:38 +0000470 {
dirkb37c8cc2014-07-01 21:08:06 +0000471 node=child;
472 child=node->child;
473 node->child=(XMLTreeInfo *) NULL;
cristy421cc1b2014-04-24 20:33:46 +0000474 (void) DestroyXMLTree(node);
dirkaa5746e2014-04-24 19:08:38 +0000475 }
dirkaa5746e2014-04-24 19:08:38 +0000476}
477
478static void DestroyXMLTreeOrdered(XMLTreeInfo *xml_info)
479{
480 XMLTreeInfo
481 *node,
dirkb37c8cc2014-07-01 21:08:06 +0000482 *ordered;
dirkaa5746e2014-04-24 19:08:38 +0000483
dirkb37c8cc2014-07-01 21:08:06 +0000484 ordered=xml_info->ordered;
485 while(ordered != (XMLTreeInfo *) NULL)
dirkaa5746e2014-04-24 19:08:38 +0000486 {
dirkb37c8cc2014-07-01 21:08:06 +0000487 node=ordered;
488 ordered=node->ordered;
489 node->ordered=(XMLTreeInfo *) NULL;
cristy421cc1b2014-04-24 20:33:46 +0000490 (void) DestroyXMLTree(node);
dirkaa5746e2014-04-24 19:08:38 +0000491 }
dirkaa5746e2014-04-24 19:08:38 +0000492}
493
cristya5fbc3b2013-12-28 01:38:07 +0000494static void DestroyXMLTreeRoot(XMLTreeInfo *xml_info)
495{
496 char
497 **attributes;
498
499 register ssize_t
500 i;
501
502 ssize_t
503 j;
504
505 XMLTreeRoot
506 *root;
507
508 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000509 assert((xml_info->signature == MagickCoreSignature) ||
510 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +0000511 if (xml_info->debug != MagickFalse)
512 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy28d3edf2013-12-29 11:21:11 +0000513 if (xml_info->parent != (XMLTreeInfo *) NULL)
cristya5fbc3b2013-12-28 01:38:07 +0000514 return;
515 /*
516 Free root tag allocations.
517 */
cristya5fbc3b2013-12-28 01:38:07 +0000518 root=(XMLTreeRoot *) xml_info;
cristy5364f9c2013-12-28 23:29:28 +0000519 for (i=NumberPredefinedEntities; root->entities[i] != (char *) NULL; i+=2)
cristya5fbc3b2013-12-28 01:38:07 +0000520 root->entities[i+1]=DestroyString(root->entities[i+1]);
521 root->entities=(char **) RelinquishMagickMemory(root->entities);
522 for (i=0; root->attributes[i] != (char **) NULL; i++)
523 {
524 attributes=root->attributes[i];
525 if (attributes[0] != (char *) NULL)
526 attributes[0]=DestroyString(attributes[0]);
527 for (j=1; attributes[j] != (char *) NULL; j+=3)
528 {
529 if (attributes[j] != (char *) NULL)
530 attributes[j]=DestroyString(attributes[j]);
531 if (attributes[j+1] != (char *) NULL)
532 attributes[j+1]=DestroyString(attributes[j+1]);
533 if (attributes[j+2] != (char *) NULL)
534 attributes[j+2]=DestroyString(attributes[j+2]);
535 }
536 attributes=(char **) RelinquishMagickMemory(attributes);
537 }
538 if (root->attributes[0] != (char **) NULL)
539 root->attributes=(char ***) RelinquishMagickMemory(root->attributes);
540 if (root->processing_instructions[0] != (char **) NULL)
541 {
542 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
543 {
544 for (j=0; root->processing_instructions[i][j] != (char *) NULL; j++)
545 root->processing_instructions[i][j]=DestroyString(
546 root->processing_instructions[i][j]);
547 root->processing_instructions[i][j+1]=DestroyString(
548 root->processing_instructions[i][j+1]);
549 root->processing_instructions[i]=(char **) RelinquishMagickMemory(
550 root->processing_instructions[i]);
551 }
552 root->processing_instructions=(char ***) RelinquishMagickMemory(
553 root->processing_instructions);
554 }
555}
556
cristy3ed852e2009-09-05 21:47:34 +0000557MagickExport XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
558{
cristy3ed852e2009-09-05 21:47:34 +0000559 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000560 assert((xml_info->signature == MagickCoreSignature) ||
561 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +0000562 if (xml_info->debug != MagickFalse)
563 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
dirkaa5746e2014-04-24 19:08:38 +0000564 DestroyXMLTreeChild(xml_info);
565 DestroyXMLTreeOrdered(xml_info);
cristya5fbc3b2013-12-28 01:38:07 +0000566 DestroyXMLTreeRoot(xml_info);
cristy3ed852e2009-09-05 21:47:34 +0000567 xml_info->attributes=DestroyXMLTreeAttributes(xml_info->attributes);
568 xml_info->content=DestroyString(xml_info->content);
569 xml_info->tag=DestroyString(xml_info->tag);
570 xml_info=(XMLTreeInfo *) RelinquishMagickMemory(xml_info);
571 return((XMLTreeInfo *) NULL);
572}
573
574/*
575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
576% %
577% %
578% %
cristy3291f512014-03-16 22:16:22 +0000579% F i l e T o X M L %
580% %
581% %
582% %
583%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584%
585% FileToXML() returns the contents of a file as a XML string.
586%
587% The format of the FileToXML method is:
588%
589% char *FileToXML(const char *filename,const size_t extent)
590%
591% A description of each parameter follows:
592%
593% o filename: the filename.
594%
595% o extent: Maximum length of the string.
596%
597*/
598
cristy3291f512014-03-16 22:16:22 +0000599MagickPrivate char *FileToXML(const char *filename,const size_t extent)
600{
601 char
602 *xml;
603
604 int
605 file;
606
607 MagickOffsetType
608 offset;
609
610 register size_t
611 i;
612
613 size_t
614 length;
615
616 ssize_t
617 count;
618
619 void
620 *map;
621
622 assert(filename != (const char *) NULL);
623 length=0;
624 file=fileno(stdin);
625 if (LocaleCompare(filename,"-") != 0)
626 file=open_utf8(filename,O_RDONLY | O_BINARY,0);
627 if (file == -1)
628 return((char *) NULL);
629 offset=(MagickOffsetType) lseek(file,0,SEEK_END);
630 count=0;
631 if ((file == fileno(stdin)) || (offset < 0) ||
632 (offset != (MagickOffsetType) ((ssize_t) offset)))
633 {
634 size_t
635 quantum;
636
637 struct stat
638 file_stats;
639
640 /*
641 Stream is not seekable.
642 */
643 offset=(MagickOffsetType) lseek(file,0,SEEK_SET);
644 quantum=(size_t) MagickMaxBufferExtent;
cristyf201ba62015-07-05 13:54:28 +0000645 if ((fstat(file,&file_stats) == 0) && (file_stats.st_size > 0))
646 quantum=(size_t) MagickMin(file_stats.st_size,MagickMaxBufferExtent);
cristy3291f512014-03-16 22:16:22 +0000647 xml=(char *) AcquireQuantumMemory(quantum,sizeof(*xml));
648 for (i=0; xml != (char *) NULL; i+=count)
649 {
650 count=read(file,xml+i,quantum);
651 if (count <= 0)
652 {
653 count=0;
654 if (errno != EINTR)
655 break;
656 }
657 if (~((size_t) i) < (quantum+1))
658 {
659 xml=(char *) RelinquishMagickMemory(xml);
660 break;
661 }
662 xml=(char *) ResizeQuantumMemory(xml,i+quantum+1,sizeof(*xml));
663 if ((size_t) (i+count) >= extent)
664 break;
665 }
666 if (LocaleCompare(filename,"-") != 0)
667 file=close(file);
668 if (xml == (char *) NULL)
669 return((char *) NULL);
670 if (file == -1)
671 {
672 xml=(char *) RelinquishMagickMemory(xml);
673 return((char *) NULL);
674 }
675 length=(size_t) MagickMin(i+count,extent);
676 xml[length]='\0';
677 return(xml);
678 }
Cristybcc9f052015-09-26 19:21:38 -0400679 length=(size_t) MagickMin(offset,extent);
cristy3291f512014-03-16 22:16:22 +0000680 xml=(char *) NULL;
cristy151b66d2015-04-15 10:50:31 +0000681 if (~length >= (MagickPathExtent-1))
682 xml=(char *) AcquireQuantumMemory(length+MagickPathExtent,sizeof(*xml));
cristy3291f512014-03-16 22:16:22 +0000683 if (xml == (char *) NULL)
684 {
685 file=close(file);
686 return((char *) NULL);
687 }
688 map=MapBlob(file,ReadMode,0,length);
689 if (map != (char *) NULL)
690 {
691 (void) memcpy(xml,map,length);
692 (void) UnmapBlob(map,length);
693 }
694 else
695 {
696 (void) lseek(file,0,SEEK_SET);
697 for (i=0; i < length; i+=count)
698 {
cristyf201ba62015-07-05 13:54:28 +0000699 count=read(file,xml+i,(size_t) MagickMin(length-i,SSIZE_MAX));
cristy3291f512014-03-16 22:16:22 +0000700 if (count <= 0)
701 {
702 count=0;
703 if (errno != EINTR)
704 break;
705 }
706 }
707 if (i < length)
708 {
709 file=close(file)-1;
710 xml=(char *) RelinquishMagickMemory(xml);
711 return((char *) NULL);
712 }
713 }
714 xml[length]='\0';
715 if (LocaleCompare(filename,"-") != 0)
716 file=close(file);
717 if (file == -1)
718 xml=(char *) RelinquishMagickMemory(xml);
719 return(xml);
720}
721
722/*
723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
724% %
725% %
726% %
cristy3ed852e2009-09-05 21:47:34 +0000727% G e t N e x t X M L T r e e T a g %
728% %
729% %
730% %
731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
732%
733% GetNextXMLTreeTag() returns the next tag or NULL if not found.
734%
735% The format of the GetNextXMLTreeTag method is:
736%
737% XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
738%
739% A description of each parameter follows:
740%
741% o xml_info: the xml info.
742%
743*/
cristy1b58f252012-03-01 01:41:41 +0000744MagickExport XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
cristy3ed852e2009-09-05 21:47:34 +0000745{
746 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000747 assert((xml_info->signature == MagickCoreSignature) ||
748 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +0000749 if (xml_info->debug != MagickFalse)
750 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +0000751 return(xml_info->next);
752}
753
754/*
755%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756% %
757% %
758% %
759% G e t X M L T r e e A t t r i b u t e %
760% %
761% %
762% %
763%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
764%
765% GetXMLTreeAttribute() returns the value of the attribute tag with the
766% specified tag if found, otherwise NULL.
767%
768% The format of the GetXMLTreeAttribute method is:
769%
770% const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag)
771%
772% A description of each parameter follows:
773%
774% o xml_info: the xml info.
775%
776% o tag: the attribute tag.
777%
778*/
cristy1b58f252012-03-01 01:41:41 +0000779MagickExport const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,
cristy3ed852e2009-09-05 21:47:34 +0000780 const char *tag)
781{
cristybb503372010-05-27 20:51:26 +0000782 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000783 i;
784
cristy9d314ff2011-03-09 01:30:28 +0000785 ssize_t
786 j;
787
cristy3ed852e2009-09-05 21:47:34 +0000788 XMLTreeRoot
789 *root;
790
791 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000792 assert((xml_info->signature == MagickCoreSignature) ||
793 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +0000794 if (xml_info->debug != MagickFalse)
795 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +0000796 if (xml_info->attributes == (char **) NULL)
797 return((const char *) NULL);
798 i=0;
799 while ((xml_info->attributes[i] != (char *) NULL) &&
800 (strcmp(xml_info->attributes[i],tag) != 0))
801 i+=2;
802 if (xml_info->attributes[i] != (char *) NULL)
803 return(xml_info->attributes[i+1]);
804 root=(XMLTreeRoot*) xml_info;
805 while (root->root.parent != (XMLTreeInfo *) NULL)
806 root=(XMLTreeRoot *) root->root.parent;
807 i=0;
808 while ((root->attributes[i] != (char **) NULL) &&
809 (strcmp(root->attributes[i][0],xml_info->tag) != 0))
810 i++;
811 if (root->attributes[i] == (char **) NULL)
812 return((const char *) NULL);
813 j=1;
814 while ((root->attributes[i][j] != (char *) NULL) &&
815 (strcmp(root->attributes[i][j],tag) != 0))
816 j+=3;
817 if (root->attributes[i][j] == (char *) NULL)
818 return((const char *) NULL);
819 return(root->attributes[i][j+1]);
820}
821
822/*
823%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
824% %
825% %
826% %
827% G e t X M L T r e e A t t r i b u t e s %
828% %
829% %
830% %
831%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
832%
833% GetXMLTreeAttributes() injects all attributes associated with the current
834% tag in the specified splay-tree.
835%
836% The format of the GetXMLTreeAttributes method is:
837%
838% MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
839% SplayTreeInfo *attributes)
840%
841% A description of each parameter follows:
842%
843% o xml_info: the xml info.
844%
845% o attributes: the attribute splay-tree.
846%
847*/
cristy433d1182011-09-04 13:38:52 +0000848MagickPrivate MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
cristy3ed852e2009-09-05 21:47:34 +0000849 SplayTreeInfo *attributes)
850{
cristybb503372010-05-27 20:51:26 +0000851 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000852 i;
853
854 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000855 assert((xml_info->signature == MagickCoreSignature) ||
856 (((const XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +0000857 if (xml_info->debug != MagickFalse)
858 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +0000859 assert(attributes != (SplayTreeInfo *) NULL);
860 if (xml_info->attributes == (char **) NULL)
861 return(MagickTrue);
862 i=0;
863 while (xml_info->attributes[i] != (char *) NULL)
864 {
865 (void) AddValueToSplayTree(attributes,
866 ConstantString(xml_info->attributes[i]),
867 ConstantString(xml_info->attributes[i+1]));
868 i+=2;
869 }
870 return(MagickTrue);
871}
872
873/*
874%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
875% %
876% %
877% %
878% G e t X M L T r e e C h i l d %
879% %
880% %
881% %
882%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
883%
884% GetXMLTreeChild() returns the first child tag with the specified tag if
885% found, otherwise NULL.
886%
887% The format of the GetXMLTreeChild method is:
888%
889% XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
890%
891% A description of each parameter follows:
892%
893% o xml_info: the xml info.
894%
895*/
896MagickExport XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
897{
898 XMLTreeInfo
899 *child;
900
901 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000902 assert((xml_info->signature == MagickCoreSignature) ||
903 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +0000904 if (xml_info->debug != MagickFalse)
905 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +0000906 child=xml_info->child;
907 if (tag != (const char *) NULL)
908 while ((child != (XMLTreeInfo *) NULL) && (strcmp(child->tag,tag) != 0))
909 child=child->sibling;
910 return(child);
911}
912
913/*
914%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
915% %
916% %
917% %
918% G e t X M L T r e e C o n t e n t %
919% %
920% %
921% %
922%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
923%
924% GetXMLTreeContent() returns any content associated with specified
925% xml-tree node.
926%
927% The format of the GetXMLTreeContent method is:
928%
929% const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
930%
931% A description of each parameter follows:
932%
933% o xml_info: the xml info.
934%
935*/
936MagickExport const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
937{
938 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000939 assert((xml_info->signature == MagickCoreSignature) ||
940 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +0000941 if (xml_info->debug != MagickFalse)
942 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +0000943 return(xml_info->content);
944}
945
946/*
947%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
948% %
949% %
950% %
951% G e t X M L T r e e O r d e r e d %
952% %
953% %
954% %
955%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
956%
957% GetXMLTreeOrdered() returns the next ordered node if found, otherwise NULL.
958%
959% The format of the GetXMLTreeOrdered method is:
960%
961% XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
962%
963% A description of each parameter follows:
964%
965% o xml_info: the xml info.
966%
967*/
cristy433d1182011-09-04 13:38:52 +0000968MagickPrivate XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
cristy3ed852e2009-09-05 21:47:34 +0000969{
970 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000971 assert((xml_info->signature == MagickCoreSignature) ||
972 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +0000973 if (xml_info->debug != MagickFalse)
974 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +0000975 return(xml_info->ordered);
976}
977
978/*
979%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
980% %
981% %
982% %
983% G e t X M L T r e e P a t h %
984% %
985% %
986% %
987%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
988%
989% GetXMLTreePath() traverses the XML-tree as defined by the specified path
990% and returns the node if found, otherwise NULL.
991%
992% The format of the GetXMLTreePath method is:
993%
994% XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
995%
996% A description of each parameter follows:
997%
998% o xml_info: the xml info.
999%
1000% o path: the path (e.g. property/elapsed-time).
1001%
1002*/
cristy433d1182011-09-04 13:38:52 +00001003MagickPrivate XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
cristy3ed852e2009-09-05 21:47:34 +00001004{
1005 char
1006 **components,
cristy151b66d2015-04-15 10:50:31 +00001007 subnode[MagickPathExtent],
1008 tag[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +00001009
cristybb503372010-05-27 20:51:26 +00001010 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001011 i;
1012
cristybb503372010-05-27 20:51:26 +00001013 size_t
cristy3ed852e2009-09-05 21:47:34 +00001014 number_components;
1015
cristy9d314ff2011-03-09 01:30:28 +00001016 ssize_t
1017 j;
1018
1019 XMLTreeInfo
1020 *node;
1021
cristy3ed852e2009-09-05 21:47:34 +00001022 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001023 assert((xml_info->signature == MagickCoreSignature) ||
1024 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +00001025 if (xml_info->debug != MagickFalse)
1026 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +00001027 node=xml_info;
1028 components=GetPathComponents(path,&number_components);
1029 if (components == (char **) NULL)
1030 return((XMLTreeInfo *) NULL);
cristybb503372010-05-27 20:51:26 +00001031 for (i=0; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +00001032 {
1033 GetPathComponent(components[i],SubimagePath,subnode);
1034 GetPathComponent(components[i],CanonicalPath,tag);
1035 node=GetXMLTreeChild(node,tag);
1036 if (node == (XMLTreeInfo *) NULL)
1037 break;
cristy55a91cd2010-12-01 00:57:40 +00001038 for (j=(ssize_t) StringToLong(subnode)-1; j > 0; j--)
cristy3ed852e2009-09-05 21:47:34 +00001039 {
1040 node=GetXMLTreeOrdered(node);
1041 if (node == (XMLTreeInfo *) NULL)
1042 break;
1043 }
1044 if (node == (XMLTreeInfo *) NULL)
1045 break;
1046 components[i]=DestroyString(components[i]);
1047 }
cristybb503372010-05-27 20:51:26 +00001048 for ( ; i < (ssize_t) number_components; i++)
cristy3ed852e2009-09-05 21:47:34 +00001049 components[i]=DestroyString(components[i]);
1050 components=(char **) RelinquishMagickMemory(components);
1051 return(node);
1052}
1053
1054/*
1055%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1056% %
1057% %
1058% %
1059% 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 %
1060% %
1061% %
1062% %
1063%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1064%
1065% GetXMLTreeProcessingInstructions() returns a null terminated array of
1066% processing instructions for the given target.
1067%
1068% The format of the GetXMLTreeProcessingInstructions method is:
1069%
1070% const char **GetXMLTreeProcessingInstructions(XMLTreeInfo *xml_info,
1071% const char *target)
1072%
1073% A description of each parameter follows:
1074%
1075% o xml_info: the xml info.
1076%
1077*/
cristy433d1182011-09-04 13:38:52 +00001078MagickPrivate const char **GetXMLTreeProcessingInstructions(
cristy3ed852e2009-09-05 21:47:34 +00001079 XMLTreeInfo *xml_info,const char *target)
1080{
cristybb503372010-05-27 20:51:26 +00001081 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001082 i;
1083
1084 XMLTreeRoot
1085 *root;
1086
1087 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001088 assert((xml_info->signature == MagickCoreSignature) ||
1089 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +00001090 if (xml_info->debug != MagickFalse)
1091 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +00001092 root=(XMLTreeRoot *) xml_info;
1093 while (root->root.parent != (XMLTreeInfo *) NULL)
1094 root=(XMLTreeRoot *) root->root.parent;
1095 i=0;
1096 while ((root->processing_instructions[i] != (char **) NULL) &&
1097 (strcmp(root->processing_instructions[i][0],target) != 0))
1098 i++;
1099 if (root->processing_instructions[i] == (char **) NULL)
1100 return((const char **) sentinel);
1101 return((const char **) (root->processing_instructions[i]+1));
1102}
1103
1104/*
1105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1106% %
1107% %
1108% %
1109% G e t X M L T r e e S i b l i n g %
1110% %
1111% %
1112% %
1113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1114%
1115% GetXMLTreeSibling() returns the node sibling if found, otherwise NULL.
1116%
1117% The format of the GetXMLTreeSibling method is:
1118%
1119% XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
1120%
1121% A description of each parameter follows:
1122%
1123% o xml_info: the xml info.
1124%
1125*/
1126MagickExport XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
1127{
1128 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001129 assert((xml_info->signature == MagickCoreSignature) ||
1130 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +00001131 if (xml_info->debug != MagickFalse)
1132 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +00001133 return(xml_info->sibling);
1134}
1135
1136/*
1137%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1138% %
1139% %
1140% %
1141% G e t X M L T r e e T a g %
1142% %
1143% %
1144% %
1145%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1146%
1147% GetXMLTreeTag() returns the tag associated with specified xml-tree node.
1148%
1149% The format of the GetXMLTreeTag method is:
1150%
1151% const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
1152%
1153% A description of each parameter follows:
1154%
1155% o xml_info: the xml info.
1156%
1157*/
1158MagickExport const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
1159{
1160 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001161 assert((xml_info->signature == MagickCoreSignature) ||
1162 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +00001163 if (xml_info->debug != MagickFalse)
1164 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +00001165 return(xml_info->tag);
1166}
1167
1168/*
1169%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1170% %
1171% %
1172% %
1173% I n s e r t I n t o T a g X M L T r e e %
1174% %
1175% %
1176% %
1177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1178%
1179% InsertTagIntoXMLTree() inserts a tag at an offset relative to the start of
1180% the parent tag's character content. This method returns the child tag.
1181%
1182% The format of the InsertTagIntoXMLTree method is:
1183%
1184% XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
1185% XMLTreeInfo *child,const size_t offset)
1186%
1187% A description of each parameter follows:
1188%
1189% o xml_info: the xml info.
1190%
1191% o child: the child tag.
1192%
1193% o offset: the tag offset.
1194%
1195*/
cristy433d1182011-09-04 13:38:52 +00001196MagickPrivate XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
cristy3ed852e2009-09-05 21:47:34 +00001197 XMLTreeInfo *child,const size_t offset)
1198{
1199 XMLTreeInfo
1200 *head,
1201 *node,
1202 *previous;
1203
1204 child->ordered=(XMLTreeInfo *) NULL;
1205 child->sibling=(XMLTreeInfo *) NULL;
1206 child->next=(XMLTreeInfo *) NULL;
1207 child->offset=offset;
1208 child->parent=xml_info;
1209 if (xml_info->child == (XMLTreeInfo *) NULL)
1210 {
1211 xml_info->child=child;
1212 return(child);
1213 }
1214 head=xml_info->child;
1215 if (head->offset > offset)
1216 {
1217 child->ordered=head;
1218 xml_info->child=child;
1219 }
1220 else
1221 {
1222 node=head;
1223 while ((node->ordered != (XMLTreeInfo *) NULL) &&
1224 (node->ordered->offset <= offset))
1225 node=node->ordered;
1226 child->ordered=node->ordered;
1227 node->ordered=child;
1228 }
1229 previous=(XMLTreeInfo *) NULL;
1230 node=head;
1231 while ((node != (XMLTreeInfo *) NULL) && (strcmp(node->tag,child->tag) != 0))
1232 {
1233 previous=node;
1234 node=node->sibling;
1235 }
1236 if ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1237 {
1238 while ((node->next != (XMLTreeInfo *) NULL) &&
1239 (node->next->offset <= offset))
1240 node=node->next;
1241 child->next=node->next;
1242 node->next=child;
1243 }
1244 else
1245 {
1246 if ((previous != (XMLTreeInfo *) NULL) && (node != (XMLTreeInfo *) NULL))
1247 previous->sibling=node->sibling;
1248 child->next=node;
1249 previous=(XMLTreeInfo *) NULL;
1250 node=head;
1251 while ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
1252 {
1253 previous=node;
1254 node=node->sibling;
1255 }
1256 child->sibling=node;
1257 if (previous != (XMLTreeInfo *) NULL)
1258 previous->sibling=child;
1259 }
1260 return(child);
1261}
1262
1263/*
1264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1265% %
1266% %
1267% %
1268% N e w X M L T r e e %
1269% %
1270% %
1271% %
1272%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1273%
1274% NewXMLTree() returns a XMLTreeInfo xml-tree as defined by the specified
1275% XML string.
1276%
1277% The format of the NewXMLTree method is:
1278%
1279% XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1280%
1281% A description of each parameter follows:
1282%
1283% o xml: The XML string.
1284%
1285% o exception: return any errors or warnings in this structure.
1286%
1287*/
1288
1289static char *ConvertUTF16ToUTF8(const char *content,size_t *length)
1290{
1291 char
1292 *utf8;
1293
1294 int
1295 bits,
1296 byte,
1297 c,
1298 encoding;
1299
cristybb503372010-05-27 20:51:26 +00001300 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001301 i;
1302
1303 size_t
1304 extent;
1305
cristy9d314ff2011-03-09 01:30:28 +00001306 ssize_t
1307 j;
1308
cristy3ed852e2009-09-05 21:47:34 +00001309 utf8=(char *) AcquireQuantumMemory(*length,sizeof(*utf8));
1310 if (utf8 == (char *) NULL)
1311 return((char *) NULL);
1312 encoding=(*content == '\xFE') ? 1 : (*content == '\xFF') ? 0 : -1;
1313 if (encoding == -1)
1314 {
1315 /*
1316 Already UTF-8.
1317 */
1318 (void) CopyMagickMemory(utf8,content,*length*sizeof(*utf8));
1319 return(utf8);
1320 }
1321 j=0;
1322 extent=(*length);
cristybb503372010-05-27 20:51:26 +00001323 for (i=2; i < (ssize_t) (*length-1); i+=2)
cristy3ed852e2009-09-05 21:47:34 +00001324 {
1325 c=(encoding != 0) ? ((content[i] & 0xff) << 8) | (content[i+1] & 0xff) :
1326 ((content[i+1] & 0xff) << 8) | (content[i] & 0xff);
cristybb503372010-05-27 20:51:26 +00001327 if ((c >= 0xd800) && (c <= 0xdfff) && ((i+=2) < (ssize_t) (*length-1)))
cristy3ed852e2009-09-05 21:47:34 +00001328 {
1329 byte=(encoding != 0) ? ((content[i] & 0xff) << 8) |
1330 (content[i+1] & 0xff) : ((content[i+1] & 0xff) << 8) |
1331 (content[i] & 0xff);
1332 c=(((c & 0x3ff) << 10) | (byte & 0x3ff))+0x10000;
1333 }
cristy151b66d2015-04-15 10:50:31 +00001334 if ((size_t) (j+MagickPathExtent) > extent)
cristy3ed852e2009-09-05 21:47:34 +00001335 {
cristy151b66d2015-04-15 10:50:31 +00001336 extent=(size_t) j+MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +00001337 utf8=(char *) ResizeQuantumMemory(utf8,extent,sizeof(*utf8));
1338 if (utf8 == (char *) NULL)
1339 return(utf8);
1340 }
1341 if (c < 0x80)
1342 {
1343 utf8[j]=c;
1344 j++;
1345 continue;
1346 }
1347 /*
1348 Multi-byte UTF-8 sequence.
1349 */
1350 byte=c;
1351 for (bits=0; byte != 0; byte/=2)
1352 bits++;
1353 bits=(bits-2)/5;
1354 utf8[j++]=(0xFF << (7-bits)) | (c >> (6*bits));
1355 while (bits != 0)
1356 {
1357 bits--;
1358 utf8[j]=0x80 | ((c >> (6*bits)) & 0x3f);
1359 j++;
1360 }
1361 }
1362 *length=(size_t) j;
1363 return((char *) ResizeQuantumMemory(utf8,*length,sizeof(*utf8)));
1364}
1365
1366static char *ParseEntities(char *xml,char **entities,int state)
1367{
1368 char
1369 *entity;
1370
1371 int
1372 byte,
1373 c;
1374
1375 register char
1376 *p,
1377 *q;
1378
cristybb503372010-05-27 20:51:26 +00001379 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001380 i;
1381
1382 size_t
1383 extent,
1384 length;
1385
1386 ssize_t
1387 offset;
1388
1389 /*
1390 Normalize line endings.
1391 */
1392 p=xml;
1393 q=xml;
1394 for ( ; *xml != '\0'; xml++)
1395 while (*xml == '\r')
1396 {
1397 *(xml++)='\n';
1398 if (*xml == '\n')
1399 (void) CopyMagickMemory(xml,xml+1,strlen(xml));
1400 }
1401 for (xml=p; ; )
1402 {
1403 while ((*xml != '\0') && (*xml != '&') && ((*xml != '%') ||
1404 (state != '%')) && (isspace((int) ((unsigned char) *xml) == 0)))
1405 xml++;
1406 if (*xml == '\0')
1407 break;
1408 /*
1409 States include:
1410 '&' for general entity decoding
1411 '%' for parameter entity decoding
1412 'c' for CDATA sections
1413 ' ' for attributes normalization
1414 '*' for non-CDATA attributes normalization
1415 */
1416 if ((state != 'c') && (strncmp(xml,"&#",2) == 0))
1417 {
1418 /*
1419 Character reference.
1420 */
1421 if (xml[2] != 'x')
1422 c=strtol(xml+2,&entity,10); /* base 10 */
1423 else
1424 c=strtol(xml+3,&entity,16); /* base 16 */
1425 if ((c == 0) || (*entity != ';'))
1426 {
1427 /*
1428 Not a character reference.
1429 */
1430 xml++;
1431 continue;
1432 }
1433 if (c < 0x80)
1434 *(xml++)=c;
1435 else
1436 {
1437 /*
1438 Multi-byte UTF-8 sequence.
1439 */
1440 byte=c;
1441 for (i=0; byte != 0; byte/=2)
1442 i++;
1443 i=(i-2)/5;
1444 *xml=(char) ((0xFF << (7-i)) | (c >> (6*i)));
1445 xml++;
1446 while (i != 0)
1447 {
1448 i--;
1449 *xml=(char) (0x80 | ((c >> (6*i)) & 0x3F));
1450 xml++;
1451 }
1452 }
1453 (void) CopyMagickMemory(xml,strchr(xml,';')+1,strlen(strchr(xml,';')));
1454 }
1455 else
1456 if (((*xml == '&') && ((state == '&') || (state == ' ') ||
1457 (state == '*'))) || ((state == '%') && (*xml == '%')))
1458 {
1459 /*
1460 Find entity in the list.
1461 */
1462 i=0;
1463 while ((entities[i] != (char *) NULL) &&
1464 (strncmp(xml+1,entities[i],strlen(entities[i])) != 0))
1465 i+=2;
1466 if (entities[i++] == (char *) NULL)
1467 xml++;
1468 else
cristy6f008fd2014-05-25 23:27:26 +00001469 if (entities[i] != (char *) NULL)
1470 {
1471 /*
1472 Found a match.
1473 */
1474 length=strlen(entities[i]);
1475 entity=strchr(xml,';');
cristyf2a82ee2014-05-26 17:49:54 +00001476 if ((entity != (char *) NULL) &&
1477 ((length-1L) >= (size_t) (entity-xml)))
cristy6f008fd2014-05-25 23:27:26 +00001478 {
1479 offset=(ssize_t) (xml-p);
1480 extent=(size_t) (offset+length+strlen(entity));
1481 if (p != q)
1482 p=(char *) ResizeQuantumMemory(p,extent,sizeof(*p));
1483 else
1484 {
1485 char
1486 *xml;
cristy3ed852e2009-09-05 21:47:34 +00001487
cristy6f008fd2014-05-25 23:27:26 +00001488 xml=(char *) AcquireQuantumMemory(extent,sizeof(*xml));
1489 if (xml != (char *) NULL)
1490 {
1491 (void) CopyMagickString(xml,p,extent*sizeof(*xml));
1492 p=xml;
1493 }
1494 }
1495 if (p == (char *) NULL)
1496 ThrowFatalException(ResourceLimitFatalError,
1497 "MemoryAllocationFailed");
1498 xml=p+offset;
1499 entity=strchr(xml,';');
1500 }
1501 if (entity != (char *) NULL)
1502 (void) CopyMagickMemory(xml+length,entity+1,strlen(entity));
1503 (void) strncpy(xml,entities[i],length);
1504 }
cristy3ed852e2009-09-05 21:47:34 +00001505 }
1506 else
1507 if (((state == ' ') || (state == '*')) &&
1508 (isspace((int) ((unsigned char) *xml) != 0)))
1509 *(xml++)=' ';
1510 else
1511 xml++;
1512 }
1513 if (state == '*')
1514 {
1515 /*
1516 Normalize spaces for non-CDATA attributes.
1517 */
1518 for (xml=p; *xml != '\0'; xml++)
1519 {
cristyfee36712014-12-20 23:28:16 +00001520 char
1521 accept[] = " ";
1522
1523 i=(ssize_t) strspn(xml,accept);
cristy3ed852e2009-09-05 21:47:34 +00001524 if (i != 0)
1525 (void) CopyMagickMemory(xml,xml+i,strlen(xml+i)+1);
1526 while ((*xml != '\0') && (*xml != ' '))
1527 xml++;
1528 }
1529 xml--;
1530 if ((xml >= p) && (*xml == ' '))
1531 *xml='\0';
1532 }
1533 return(p == q ? ConstantString(p) : p);
1534}
1535
1536static void ParseCharacterContent(XMLTreeRoot *root,char *xml,
1537 const size_t length,const char state)
1538{
1539 XMLTreeInfo
1540 *xml_info;
1541
1542 xml_info=root->node;
1543 if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) ||
1544 (length == 0))
1545 return;
1546 xml[length]='\0';
1547 xml=ParseEntities(xml,root->entities,state);
cristy5467fcf2014-05-18 17:20:55 +00001548 if ((xml_info->content != (char *) NULL) && (*xml_info->content != '\0'))
cristy3ed852e2009-09-05 21:47:34 +00001549 {
1550 (void) ConcatenateString(&xml_info->content,xml);
1551 xml=DestroyString(xml);
1552 }
1553 else
1554 {
1555 if (xml_info->content != (char *) NULL)
1556 xml_info->content=DestroyString(xml_info->content);
1557 xml_info->content=xml;
1558 }
1559}
1560
1561static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag,
dirk90d68962014-05-23 11:11:17 +00001562 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +00001563{
1564 if ((root->node == (XMLTreeInfo *) NULL) ||
1565 (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0))
1566 {
1567 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1568 "ParseError","unexpected closing tag </%s>",tag);
1569 return(&root->root);
1570 }
1571 root->node=root->node->parent;
1572 return((XMLTreeInfo *) NULL);
1573}
1574
1575static MagickBooleanType ValidateEntities(char *tag,char *xml,char **entities)
1576{
cristybb503372010-05-27 20:51:26 +00001577 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001578 i;
1579
1580 /*
1581 Check for circular entity references.
1582 */
1583 for ( ; ; xml++)
1584 {
1585 while ((*xml != '\0') && (*xml != '&'))
1586 xml++;
1587 if (*xml == '\0')
1588 return(MagickTrue);
1589 if (strncmp(xml+1,tag,strlen(tag)) == 0)
1590 return(MagickFalse);
1591 i=0;
1592 while ((entities[i] != (char *) NULL) &&
cristy99c9fa02013-12-28 00:36:53 +00001593 (strncmp(entities[i],xml+1,strlen(entities[i])) == 0))
cristy3ed852e2009-09-05 21:47:34 +00001594 i+=2;
1595 if ((entities[i] != (char *) NULL) &&
1596 (ValidateEntities(tag,entities[i+1],entities) == 0))
1597 return(MagickFalse);
1598 }
cristy3ed852e2009-09-05 21:47:34 +00001599}
1600
1601static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml,
1602 size_t length)
1603{
1604 char
1605 *target;
1606
cristybb503372010-05-27 20:51:26 +00001607 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001608 i;
1609
cristy9d314ff2011-03-09 01:30:28 +00001610 ssize_t
1611 j;
1612
cristy3ed852e2009-09-05 21:47:34 +00001613 target=xml;
1614 xml[length]='\0';
1615 xml+=strcspn(xml,XMLWhitespace);
1616 if (*xml != '\0')
1617 {
1618 *xml='\0';
1619 xml+=strspn(xml+1,XMLWhitespace)+1;
1620 }
1621 if (strcmp(target,"xml") == 0)
1622 {
1623 xml=strstr(xml,"standalone");
1624 if ((xml != (char *) NULL) &&
1625 (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0))
1626 root->standalone=MagickTrue;
1627 return;
1628 }
1629 if (root->processing_instructions[0] == (char **) NULL)
1630 {
cristy73bd4a52010-10-05 11:24:23 +00001631 root->processing_instructions=(char ***) AcquireMagickMemory(sizeof(
cristy3ed852e2009-09-05 21:47:34 +00001632 *root->processing_instructions));
1633 if (root->processing_instructions ==(char ***) NULL)
1634 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1635 *root->processing_instructions=(char **) NULL;
1636 }
1637 i=0;
1638 while ((root->processing_instructions[i] != (char **) NULL) &&
1639 (strcmp(target,root->processing_instructions[i][0]) != 0))
1640 i++;
1641 if (root->processing_instructions[i] == (char **) NULL)
1642 {
1643 root->processing_instructions=(char ***) ResizeQuantumMemory(
1644 root->processing_instructions,(size_t) (i+2),
1645 sizeof(*root->processing_instructions));
1646 if (root->processing_instructions == (char ***) NULL)
1647 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1648 root->processing_instructions[i]=(char **) AcquireQuantumMemory(3,
cristyd67357f2015-03-22 17:14:25 +00001649 sizeof(**root->processing_instructions));
cristy3ed852e2009-09-05 21:47:34 +00001650 if (root->processing_instructions[i] == (char **) NULL)
1651 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1652 root->processing_instructions[i+1]=(char **) NULL;
1653 root->processing_instructions[i][0]=ConstantString(target);
1654 root->processing_instructions[i][1]=(char *)
1655 root->processing_instructions[i+1];
1656 root->processing_instructions[i+1]=(char **) NULL;
1657 root->processing_instructions[i][2]=ConstantString("");
1658 }
1659 j=1;
1660 while (root->processing_instructions[i][j] != (char *) NULL)
1661 j++;
1662 root->processing_instructions[i]=(char **) ResizeQuantumMemory(
1663 root->processing_instructions[i],(size_t) (j+3),
cristy85893d32015-05-06 13:44:09 +00001664 sizeof(**root->processing_instructions));
cristy3ed852e2009-09-05 21:47:34 +00001665 if (root->processing_instructions[i] == (char **) NULL)
1666 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1667 root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory(
1668 root->processing_instructions[i][j+1],(size_t) (j+1),
cristy30b2c242015-06-27 15:24:03 +00001669 sizeof(***root->processing_instructions));
cristy3ed852e2009-09-05 21:47:34 +00001670 if (root->processing_instructions[i][j+2] == (char *) NULL)
1671 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1672 (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1,
1673 root->root.tag != (char *) NULL ? ">" : "<",2);
1674 root->processing_instructions[i][j]=ConstantString(xml);
1675 root->processing_instructions[i][j+1]=(char *) NULL;
1676}
1677
1678static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml,
1679 size_t length,ExceptionInfo *exception)
1680{
1681 char
1682 *c,
1683 **entities,
1684 *n,
1685 **predefined_entitites,
1686 q,
1687 *t,
1688 *v;
1689
cristybb503372010-05-27 20:51:26 +00001690 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001691 i;
1692
cristy9d314ff2011-03-09 01:30:28 +00001693 ssize_t
1694 j;
1695
cristy3ed852e2009-09-05 21:47:34 +00001696 n=(char *) NULL;
cristy73bd4a52010-10-05 11:24:23 +00001697 predefined_entitites=(char **) AcquireMagickMemory(sizeof(sentinel));
cristy3ed852e2009-09-05 21:47:34 +00001698 if (predefined_entitites == (char **) NULL)
1699 ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed");
1700 (void) CopyMagickMemory(predefined_entitites,sentinel,sizeof(sentinel));
1701 for (xml[length]='\0'; xml != (char *) NULL; )
1702 {
1703 while ((*xml != '\0') && (*xml != '<') && (*xml != '%'))
1704 xml++;
1705 if (*xml == '\0')
1706 break;
1707 if (strncmp(xml,"<!ENTITY",8) == 0)
1708 {
1709 /*
1710 Parse entity definitions.
1711 */
1712 xml+=strspn(xml+8,XMLWhitespace)+8;
1713 c=xml;
1714 n=xml+strspn(xml,XMLWhitespace "%");
1715 xml=n+strcspn(n,XMLWhitespace);
1716 *xml=';';
1717 v=xml+strspn(xml+1,XMLWhitespace)+1;
1718 q=(*v);
1719 v++;
1720 if ((q != '"') && (q != '\''))
1721 {
1722 /*
1723 Skip externals.
1724 */
1725 xml=strchr(xml,'>');
1726 continue;
1727 }
1728 entities=(*c == '%') ? predefined_entitites : root->entities;
1729 for (i=0; entities[i] != (char *) NULL; i++) ;
1730 entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3),
1731 sizeof(*entities));
1732 if (entities == (char **) NULL)
1733 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1734 if (*c == '%')
1735 predefined_entitites=entities;
1736 else
1737 root->entities=entities;
1738 xml++;
1739 *xml='\0';
1740 xml=strchr(v,q);
1741 if (xml != (char *) NULL)
1742 {
1743 *xml='\0';
1744 xml++;
1745 }
1746 entities[i+1]=ParseEntities(v,predefined_entitites,'%');
1747 entities[i+2]=(char *) NULL;
1748 if (ValidateEntities(n,entities[i+1],entities) != MagickFalse)
1749 entities[i]=n;
1750 else
1751 {
1752 if (entities[i+1] != v)
1753 entities[i+1]=DestroyString(entities[i+1]);
1754 (void) ThrowMagickException(exception,GetMagickModule(),
1755 OptionWarning,"ParseError","circular entity declaration &%s",n);
1756 predefined_entitites=(char **) RelinquishMagickMemory(
1757 predefined_entitites);
1758 return(MagickFalse);
1759 }
1760 }
1761 else
1762 if (strncmp(xml,"<!ATTLIST",9) == 0)
1763 {
1764 /*
1765 Parse default attributes.
1766 */
1767 t=xml+strspn(xml+9,XMLWhitespace)+9;
1768 if (*t == '\0')
1769 {
1770 (void) ThrowMagickException(exception,GetMagickModule(),
1771 OptionWarning,"ParseError","unclosed <!ATTLIST");
1772 predefined_entitites=(char **) RelinquishMagickMemory(
1773 predefined_entitites);
1774 return(MagickFalse);
1775 }
1776 xml=t+strcspn(t,XMLWhitespace ">");
1777 if (*xml == '>')
1778 continue;
1779 *xml='\0';
1780 i=0;
1781 while ((root->attributes[i] != (char **) NULL) &&
cristy20b9e9a2014-05-01 00:52:50 +00001782 (n != (char *) NULL) &&
cristy8eaf1852010-01-14 03:08:10 +00001783 (strcmp(n,root->attributes[i][0]) != 0))
cristy3ed852e2009-09-05 21:47:34 +00001784 i++;
cristyeaf70f72010-01-14 14:22:44 +00001785 while ((*(n=xml+strspn(xml+1,XMLWhitespace)+1) != '\0') &&
1786 (*n != '>'))
cristy3ed852e2009-09-05 21:47:34 +00001787 {
1788 xml=n+strcspn(n,XMLWhitespace);
1789 if (*xml != '\0')
1790 *xml='\0';
1791 else
1792 {
1793 (void) ThrowMagickException(exception,GetMagickModule(),
1794 OptionWarning,"ParseError","malformed <!ATTLIST");
1795 predefined_entitites=(char **) RelinquishMagickMemory(
1796 predefined_entitites);
1797 return(MagickFalse);
1798 }
1799 xml+=strspn(xml+1,XMLWhitespace)+1;
1800 c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
1801 if (strncmp(xml,"NOTATION",8) == 0)
1802 xml+=strspn(xml+8,XMLWhitespace)+8;
1803 xml=(*xml == '(') ? strchr(xml,')') : xml+
1804 strcspn(xml,XMLWhitespace);
1805 if (xml == (char *) NULL)
1806 {
1807 (void) ThrowMagickException(exception,GetMagickModule(),
1808 OptionWarning,"ParseError","malformed <!ATTLIST");
1809 predefined_entitites=(char **) RelinquishMagickMemory(
1810 predefined_entitites);
1811 return(MagickFalse);
1812 }
1813 xml+=strspn(xml,XMLWhitespace ")");
1814 if (strncmp(xml,"#FIXED",6) == 0)
1815 xml+=strspn(xml+6,XMLWhitespace)+6;
1816 if (*xml == '#')
1817 {
1818 xml+=strcspn(xml,XMLWhitespace ">")-1;
1819 if (*c == ' ')
1820 continue;
1821 v=(char *) NULL;
1822 }
1823 else
1824 if (((*xml == '"') || (*xml == '\'')) &&
1825 ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
1826 *xml='\0';
1827 else
1828 {
1829 (void) ThrowMagickException(exception,GetMagickModule(),
1830 OptionWarning,"ParseError","malformed <!ATTLIST");
1831 predefined_entitites=(char **) RelinquishMagickMemory(
1832 predefined_entitites);
1833 return(MagickFalse);
1834 }
1835 if (root->attributes[i] == (char **) NULL)
1836 {
1837 /*
1838 New attribute tag.
1839 */
1840 if (i == 0)
1841 root->attributes=(char ***) AcquireQuantumMemory(2,
1842 sizeof(*root->attributes));
1843 else
1844 root->attributes=(char ***) ResizeQuantumMemory(
1845 root->attributes,(size_t) (i+2),
1846 sizeof(*root->attributes));
1847 if (root->attributes == (char ***) NULL)
1848 ThrowFatalException(ResourceLimitFatalError,
1849 "MemoryAllocationFailed");
1850 root->attributes[i]=(char **) AcquireQuantumMemory(2,
cristy85893d32015-05-06 13:44:09 +00001851 sizeof(**root->attributes));
cristy3ed852e2009-09-05 21:47:34 +00001852 if (root->attributes[i] == (char **) NULL)
1853 ThrowFatalException(ResourceLimitFatalError,
1854 "MemoryAllocationFailed");
1855 root->attributes[i][0]=ConstantString(t);
1856 root->attributes[i][1]=(char *) NULL;
1857 root->attributes[i+1]=(char **) NULL;
1858 }
1859 for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
1860 root->attributes[i]=(char **) ResizeQuantumMemory(
cristy85893d32015-05-06 13:44:09 +00001861 root->attributes[i],(size_t) (j+4),sizeof(**root->attributes));
cristy3ed852e2009-09-05 21:47:34 +00001862 if (root->attributes[i] == (char **) NULL)
1863 ThrowFatalException(ResourceLimitFatalError,
1864 "MemoryAllocationFailed");
1865 root->attributes[i][j+3]=(char *) NULL;
1866 root->attributes[i][j+2]=ConstantString(c);
1867 root->attributes[i][j+1]=(char *) NULL;
1868 if (v != (char *) NULL)
1869 root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
1870 root->attributes[i][j]=ConstantString(n);
1871 }
1872 }
1873 else
1874 if (strncmp(xml, "<!--", 4) == 0)
1875 xml=strstr(xml+4,"-->");
1876 else
1877 if (strncmp(xml,"<?", 2) == 0)
1878 {
1879 c=xml+2;
1880 xml=strstr(c,"?>");
1881 if (xml != (char *) NULL)
1882 {
1883 ParseProcessingInstructions(root,c,(size_t) (xml-c));
1884 xml++;
1885 }
1886 }
1887 else
1888 if (*xml == '<')
1889 xml=strchr(xml,'>');
1890 else
1891 if ((*(xml++) == '%') && (root->standalone == MagickFalse))
1892 break;
1893 }
1894 predefined_entitites=(char **) RelinquishMagickMemory(predefined_entitites);
1895 return(MagickTrue);
1896}
1897
1898static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
1899{
1900 XMLTreeInfo
1901 *xml_info;
1902
1903 xml_info=root->node;
1904 if (xml_info->tag == (char *) NULL)
1905 xml_info->tag=ConstantString(tag);
1906 else
1907 xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
cristy8418c7e2014-05-18 18:38:26 +00001908 if (xml_info != (XMLTreeInfo *) NULL)
1909 xml_info->attributes=attributes;
cristy3ed852e2009-09-05 21:47:34 +00001910 root->node=xml_info;
1911}
1912
cristy9745ca82014-05-23 20:10:13 +00001913static const char
1914 *ignore_tags[3] =
1915 {
1916 "rdf:Bag",
1917 "rdf:Seq",
1918 (const char *) NULL
1919 };
dirk90d68962014-05-23 11:11:17 +00001920
cristy9745ca82014-05-23 20:10:13 +00001921static inline MagickBooleanType IsSkipTag(const char *tag)
dirk90d68962014-05-23 11:11:17 +00001922{
1923 register ssize_t
1924 i;
1925
1926 i=0;
1927 while (ignore_tags[i] != (const char *) NULL)
1928 {
1929 if (LocaleCompare(tag,ignore_tags[i]) == 0)
1930 return(MagickTrue);
1931 i++;
1932 }
1933 return(MagickFalse);
1934}
1935
cristy3ed852e2009-09-05 21:47:34 +00001936MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
1937{
1938 char
1939 **attribute,
1940 **attributes,
1941 *tag,
1942 *utf8;
1943
1944 int
1945 c,
1946 terminal;
1947
cristy9d314ff2011-03-09 01:30:28 +00001948 MagickBooleanType
1949 status;
cristy3ed852e2009-09-05 21:47:34 +00001950
1951 register char
1952 *p;
1953
cristybb503372010-05-27 20:51:26 +00001954 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001955 i;
1956
1957 size_t
dirk90d68962014-05-23 11:11:17 +00001958 ignore_depth,
cristy3ed852e2009-09-05 21:47:34 +00001959 length;
1960
cristy9d314ff2011-03-09 01:30:28 +00001961 ssize_t
1962 j,
1963 l;
cristy3ed852e2009-09-05 21:47:34 +00001964
1965 XMLTreeRoot
1966 *root;
1967
1968 /*
1969 Convert xml-string to UTF8.
1970 */
1971 if ((xml == (const char *) NULL) || (strlen(xml) == 0))
1972 {
1973 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1974 "ParseError","root tag missing");
1975 return((XMLTreeInfo *) NULL);
1976 }
1977 root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
1978 length=strlen(xml);
1979 utf8=ConvertUTF16ToUTF8(xml,&length);
1980 if (utf8 == (char *) NULL)
1981 {
1982 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1983 "ParseError","UTF16 to UTF8 failed");
1984 return((XMLTreeInfo *) NULL);
1985 }
1986 terminal=utf8[length-1];
1987 utf8[length-1]='\0';
1988 p=utf8;
1989 while ((*p != '\0') && (*p != '<'))
1990 p++;
1991 if (*p == '\0')
1992 {
1993 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1994 "ParseError","root tag missing");
1995 utf8=DestroyString(utf8);
1996 return((XMLTreeInfo *) NULL);
1997 }
1998 attribute=(char **) NULL;
dirk90d68962014-05-23 11:11:17 +00001999 l=0;
2000 ignore_depth=0;
cristy3ed852e2009-09-05 21:47:34 +00002001 for (p++; ; p++)
2002 {
2003 attributes=(char **) sentinel;
2004 tag=p;
cristyad7e6032011-09-06 14:47:06 +00002005 c=(*p);
cristy3ed852e2009-09-05 21:47:34 +00002006 if ((isalpha((int) ((unsigned char) *p)) !=0) || (*p == '_') ||
cristyad7e6032011-09-06 14:47:06 +00002007 (*p == ':') || (c < '\0'))
cristy3ed852e2009-09-05 21:47:34 +00002008 {
2009 /*
2010 Tag.
2011 */
2012 if (root->node == (XMLTreeInfo *) NULL)
2013 {
2014 (void) ThrowMagickException(exception,GetMagickModule(),
2015 OptionWarning,"ParseError","root tag missing");
2016 utf8=DestroyString(utf8);
2017 return(&root->root);
2018 }
2019 p+=strcspn(p,XMLWhitespace "/>");
2020 while (isspace((int) ((unsigned char) *p)) != 0)
2021 *p++='\0';
dirk90d68962014-05-23 11:11:17 +00002022 if (ignore_depth == 0)
cristy3ed852e2009-09-05 21:47:34 +00002023 {
dirk90d68962014-05-23 11:11:17 +00002024 if ((*p != '\0') && (*p != '/') && (*p != '>'))
2025 {
2026 /*
2027 Find tag in default attributes list.
2028 */
2029 i=0;
2030 while ((root->attributes[i] != (char **) NULL) &&
2031 (strcmp(root->attributes[i][0],tag) != 0))
2032 i++;
2033 attribute=root->attributes[i];
2034 }
2035 for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
cristy3ed852e2009-09-05 21:47:34 +00002036 {
dirk90d68962014-05-23 11:11:17 +00002037 /*
2038 Attribute.
2039 */
2040 if (l == 0)
2041 attributes=(char **) AcquireQuantumMemory(4,
2042 sizeof(*attributes));
2043 else
2044 attributes=(char **) ResizeQuantumMemory(attributes,
2045 (size_t) (l+4),sizeof(*attributes));
2046 if (attributes == (char **) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002047 {
dirk90d68962014-05-23 11:11:17 +00002048 (void) ThrowMagickException(exception,GetMagickModule(),
2049 ResourceLimitError,"MemoryAllocationFailed","`%s'","");
2050 utf8=DestroyString(utf8);
2051 return(&root->root);
cristy3ed852e2009-09-05 21:47:34 +00002052 }
dirk90d68962014-05-23 11:11:17 +00002053 attributes[l+2]=(char *) NULL;
2054 attributes[l+1]=(char *) NULL;
2055 attributes[l]=p;
2056 p+=strcspn(p,XMLWhitespace "=/>");
2057 if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
2058 attributes[l]=ConstantString("");
2059 else
2060 {
2061 *p++='\0';
2062 p+=strspn(p,XMLWhitespace "=");
2063 c=(*p);
2064 if ((c == '"') || (c == '\''))
2065 {
2066 /*
2067 Attributes value.
2068 */
2069 p++;
2070 attributes[l+1]=p;
2071 while ((*p != '\0') && (*p != c))
2072 p++;
2073 if (*p != '\0')
2074 *p++='\0';
2075 else
2076 {
2077 attributes[l]=ConstantString("");
2078 attributes[l+1]=ConstantString("");
2079 (void) DestroyXMLTreeAttributes(attributes);
2080 (void) ThrowMagickException(exception,
2081 GetMagickModule(),OptionWarning,"ParseError",
2082 "missing %c",c);
2083 utf8=DestroyString(utf8);
2084 return(&root->root);
2085 }
2086 j=1;
2087 while ((attribute != (char **) NULL) &&
2088 (attribute[j] != (char *) NULL) &&
2089 (strcmp(attribute[j],attributes[l]) != 0))
2090 j+=3;
2091 attributes[l+1]=ParseEntities(attributes[l+1],
2092 root->entities,(attribute != (char **) NULL) &&
2093 (attribute[j] != (char *) NULL) ? *attribute[j+2] :
2094 ' ');
2095 }
2096 attributes[l]=ConstantString(attributes[l]);
2097 }
2098 while (isspace((int) ((unsigned char) *p)) != 0)
2099 p++;
cristy3ed852e2009-09-05 21:47:34 +00002100 }
dirk90d68962014-05-23 11:11:17 +00002101 }
2102 else
2103 {
2104 while((*p != '\0') && (*p != '/') && (*p != '>'))
2105 p++;
2106 }
cristy3ed852e2009-09-05 21:47:34 +00002107 if (*p == '/')
2108 {
2109 /*
2110 Self closing tag.
2111 */
2112 *p++='\0';
2113 if (((*p != '\0') && (*p != '>')) ||
2114 ((*p == '\0') && (terminal != '>')))
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 }
cristy9745ca82014-05-23 20:10:13 +00002123 if ((ignore_depth == 0) && (IsSkipTag(tag) == MagickFalse))
dirk90d68962014-05-23 11:11:17 +00002124 {
2125 ParseOpenTag(root,tag,attributes);
2126 (void) ParseCloseTag(root,tag,exception);
2127 }
cristy3ed852e2009-09-05 21:47:34 +00002128 }
2129 else
2130 {
2131 c=(*p);
2132 if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
2133 {
2134 *p='\0';
cristy9745ca82014-05-23 20:10:13 +00002135 if ((ignore_depth == 0) && (IsSkipTag(tag) == MagickFalse))
dirk90d68962014-05-23 11:11:17 +00002136 ParseOpenTag(root,tag,attributes);
2137 else
2138 ignore_depth++;
cristy3ed852e2009-09-05 21:47:34 +00002139 *p=c;
2140 }
2141 else
2142 {
2143 if (l != 0)
2144 (void) DestroyXMLTreeAttributes(attributes);
2145 (void) ThrowMagickException(exception,GetMagickModule(),
2146 OptionWarning,"ParseError","missing >");
2147 utf8=DestroyString(utf8);
2148 return(&root->root);
2149 }
2150 }
2151 }
2152 else
2153 if (*p == '/')
2154 {
2155 /*
2156 Close tag.
2157 */
2158 tag=p+1;
2159 p+=strcspn(tag,XMLWhitespace ">")+1;
2160 c=(*p);
2161 if ((c == '\0') && (terminal != '>'))
2162 {
2163 (void) ThrowMagickException(exception,GetMagickModule(),
2164 OptionWarning,"ParseError","missing >");
2165 utf8=DestroyString(utf8);
2166 return(&root->root);
2167 }
2168 *p='\0';
dirk90d68962014-05-23 11:11:17 +00002169 if (ignore_depth == 0 && ParseCloseTag(root,tag,exception) !=
2170 (XMLTreeInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00002171 {
2172 utf8=DestroyString(utf8);
2173 return(&root->root);
2174 }
dirk90d68962014-05-23 11:11:17 +00002175 if (ignore_depth > 0)
2176 ignore_depth--;
cristy3ed852e2009-09-05 21:47:34 +00002177 *p=c;
2178 if (isspace((int) ((unsigned char) *p)) != 0)
2179 p+=strspn(p,XMLWhitespace);
2180 }
2181 else
2182 if (strncmp(p,"!--",3) == 0)
2183 {
2184 /*
2185 Comment.
2186 */
2187 p=strstr(p+3,"--");
2188 if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
2189 ((*p == '\0') && (terminal != '>')))
2190 {
2191 (void) ThrowMagickException(exception,GetMagickModule(),
2192 OptionWarning,"ParseError","unclosed <!--");
2193 utf8=DestroyString(utf8);
2194 return(&root->root);
2195 }
2196 }
2197 else
2198 if (strncmp(p,"![CDATA[",8) == 0)
2199 {
2200 /*
2201 Cdata.
2202 */
2203 p=strstr(p,"]]>");
2204 if (p != (char *) NULL)
2205 {
2206 p+=2;
dirk90d68962014-05-23 11:11:17 +00002207 if (ignore_depth == 0)
2208 ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
cristy3ed852e2009-09-05 21:47:34 +00002209 }
2210 else
2211 {
2212 (void) ThrowMagickException(exception,GetMagickModule(),
2213 OptionWarning,"ParseError","unclosed <![CDATA[");
2214 utf8=DestroyString(utf8);
2215 return(&root->root);
2216 }
2217 }
2218 else
2219 if (strncmp(p,"!DOCTYPE",8) == 0)
2220 {
2221 /*
2222 DTD.
2223 */
2224 for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
2225 ((l != 0) && ((*p != ']') ||
2226 (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
cristyecd0ab52010-05-30 14:59:20 +00002227 l=(ssize_t) ((*p == '[') ? 1 : l))
cristy3ed852e2009-09-05 21:47:34 +00002228 p+=strcspn(p+1,"[]>")+1;
2229 if ((*p == '\0') && (terminal != '>'))
2230 {
2231 (void) ThrowMagickException(exception,GetMagickModule(),
2232 OptionWarning,"ParseError","unclosed <!DOCTYPE");
2233 utf8=DestroyString(utf8);
2234 return(&root->root);
2235 }
2236 if (l != 0)
2237 tag=strchr(tag,'[')+1;
2238 if (l != 0)
2239 {
2240 status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
2241 exception);
2242 if (status == MagickFalse)
2243 {
2244 utf8=DestroyString(utf8);
2245 return(&root->root);
2246 }
2247 p++;
2248 }
2249 }
2250 else
2251 if (*p == '?')
2252 {
2253 /*
2254 Processing instructions.
2255 */
2256 do
2257 {
2258 p=strchr(p,'?');
2259 if (p == (char *) NULL)
2260 break;
2261 p++;
2262 } while ((*p != '\0') && (*p != '>'));
2263 if ((p == (char *) NULL) || ((*p == '\0') &&
2264 (terminal != '>')))
2265 {
2266 (void) ThrowMagickException(exception,GetMagickModule(),
2267 OptionWarning,"ParseError","unclosed <?");
2268 utf8=DestroyString(utf8);
2269 return(&root->root);
2270 }
2271 ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
2272 }
2273 else
2274 {
2275 (void) ThrowMagickException(exception,GetMagickModule(),
2276 OptionWarning,"ParseError","unexpected <");
2277 utf8=DestroyString(utf8);
2278 return(&root->root);
2279 }
2280 if ((p == (char *) NULL) || (*p == '\0'))
2281 break;
2282 *p++='\0';
2283 tag=p;
2284 if ((*p != '\0') && (*p != '<'))
2285 {
2286 /*
2287 Tag character content.
2288 */
2289 while ((*p != '\0') && (*p != '<'))
2290 p++;
2291 if (*p == '\0')
2292 break;
dirk90d68962014-05-23 11:11:17 +00002293 if (ignore_depth == 0)
2294 ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
cristy3ed852e2009-09-05 21:47:34 +00002295 }
2296 else
2297 if (*p == '\0')
2298 break;
2299 }
2300 utf8=DestroyString(utf8);
2301 if (root->node == (XMLTreeInfo *) NULL)
2302 return(&root->root);
2303 if (root->node->tag == (char *) NULL)
2304 {
2305 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
2306 "ParseError","root tag missing");
2307 return(&root->root);
2308 }
2309 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
anthonye5b39652012-04-21 05:37:29 +00002310 "ParseError","unclosed tag: '%s'",root->node->tag);
cristy3ed852e2009-09-05 21:47:34 +00002311 return(&root->root);
2312}
2313
2314/*
2315%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2316% %
2317% %
2318% %
2319% N e w X M L T r e e T a g %
2320% %
2321% %
2322% %
2323%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2324%
2325% NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
2326%
2327% The format of the NewXMLTreeTag method is:
2328%
2329% XMLTreeInfo *NewXMLTreeTag(const char *tag)
2330%
2331% A description of each parameter follows:
2332%
2333% o tag: the tag.
2334%
2335*/
2336MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
2337{
2338 static const char
2339 *predefined_entities[NumberPredefinedEntities+1] =
2340 {
2341 "lt;", "&#60;", "gt;", "&#62;", "quot;", "&#34;",
2342 "apos;", "&#39;", "amp;", "&#38;", (char *) NULL
2343 };
2344
2345 XMLTreeRoot
2346 *root;
2347
cristy73bd4a52010-10-05 11:24:23 +00002348 root=(XMLTreeRoot *) AcquireMagickMemory(sizeof(*root));
cristy3ed852e2009-09-05 21:47:34 +00002349 if (root == (XMLTreeRoot *) NULL)
2350 return((XMLTreeInfo *) NULL);
2351 (void) ResetMagickMemory(root,0,sizeof(*root));
2352 root->root.tag=(char *) NULL;
2353 if (tag != (char *) NULL)
2354 root->root.tag=ConstantString(tag);
2355 root->node=(&root->root);
2356 root->root.content=ConstantString("");
cristy73bd4a52010-10-05 11:24:23 +00002357 root->entities=(char **) AcquireMagickMemory(sizeof(predefined_entities));
cristy3ed852e2009-09-05 21:47:34 +00002358 if (root->entities == (char **) NULL)
2359 return((XMLTreeInfo *) NULL);
2360 (void) CopyMagickMemory(root->entities,predefined_entities,
2361 sizeof(predefined_entities));
2362 root->root.attributes=sentinel;
cristyb068b7a2010-01-06 02:35:13 +00002363 root->attributes=(char ***) root->root.attributes;
2364 root->processing_instructions=(char ***) root->root.attributes;
cristy3ed852e2009-09-05 21:47:34 +00002365 root->debug=IsEventLogging();
cristye1c94d92015-06-28 12:16:33 +00002366 root->signature=MagickCoreSignature;
cristy3ed852e2009-09-05 21:47:34 +00002367 return(&root->root);
2368}
2369
2370/*
2371%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2372% %
2373% %
2374% %
2375% P r u n e T a g F r o m X M L T r e e %
2376% %
2377% %
2378% %
2379%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2380%
cristycee97112010-05-28 00:44:52 +00002381% PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
cristy3ed852e2009-09-05 21:47:34 +00002382% subtags.
2383%
2384% The format of the PruneTagFromXMLTree method is:
2385%
2386% XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
2387%
2388% A description of each parameter follows:
2389%
2390% o xml_info: the xml info.
2391%
2392*/
cristy433d1182011-09-04 13:38:52 +00002393MagickPrivate XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
cristy3ed852e2009-09-05 21:47:34 +00002394{
2395 XMLTreeInfo
2396 *node;
2397
2398 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002399 assert((xml_info->signature == MagickCoreSignature) ||
2400 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +00002401 if (xml_info->debug != MagickFalse)
2402 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +00002403 if (xml_info->next != (XMLTreeInfo *) NULL)
2404 xml_info->next->sibling=xml_info->sibling;
2405 if (xml_info->parent != (XMLTreeInfo *) NULL)
2406 {
2407 node=xml_info->parent->child;
2408 if (node == xml_info)
2409 xml_info->parent->child=xml_info->ordered;
2410 else
2411 {
2412 while (node->ordered != xml_info)
2413 node=node->ordered;
2414 node->ordered=node->ordered->ordered;
2415 node=xml_info->parent->child;
2416 if (strcmp(node->tag,xml_info->tag) != 0)
2417 {
2418 while (strcmp(node->sibling->tag,xml_info->tag) != 0)
2419 node=node->sibling;
2420 if (node->sibling != xml_info)
2421 node=node->sibling;
2422 else
2423 node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
2424 xml_info->next : node->sibling->sibling;
2425 }
2426 while ((node->next != (XMLTreeInfo *) NULL) &&
2427 (node->next != xml_info))
2428 node=node->next;
2429 if (node->next != (XMLTreeInfo *) NULL)
2430 node->next=node->next->next;
2431 }
2432 }
2433 xml_info->ordered=(XMLTreeInfo *) NULL;
2434 xml_info->sibling=(XMLTreeInfo *) NULL;
2435 xml_info->next=(XMLTreeInfo *) NULL;
2436 return(xml_info);
2437}
2438
2439/*
2440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2441% %
2442% %
2443% %
2444% S e t X M L T r e e A t t r i b u t e %
2445% %
2446% %
2447% %
2448%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2449%
2450% SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
2451% found. A value of NULL removes the specified attribute.
2452%
2453% The format of the SetXMLTreeAttribute method is:
2454%
2455% XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
2456% const char *value)
2457%
2458% A description of each parameter follows:
2459%
2460% o xml_info: the xml info.
2461%
2462% o tag: The attribute tag.
2463%
2464% o value: The attribute value.
2465%
2466*/
cristy433d1182011-09-04 13:38:52 +00002467MagickPrivate XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
cristy3ed852e2009-09-05 21:47:34 +00002468 const char *tag,const char *value)
2469{
cristybb503372010-05-27 20:51:26 +00002470 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002471 i;
2472
cristy9d314ff2011-03-09 01:30:28 +00002473 ssize_t
2474 j;
2475
cristy3ed852e2009-09-05 21:47:34 +00002476 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002477 assert((xml_info->signature == MagickCoreSignature) ||
2478 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +00002479 if (xml_info->debug != MagickFalse)
2480 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +00002481 i=0;
2482 while ((xml_info->attributes[i] != (char *) NULL) &&
2483 (strcmp(xml_info->attributes[i],tag) != 0))
2484 i+=2;
2485 if (xml_info->attributes[i] == (char *) NULL)
2486 {
2487 /*
2488 Add new attribute tag.
2489 */
2490 if (value == (const char *) NULL)
2491 return(xml_info);
2492 if (xml_info->attributes != sentinel)
2493 xml_info->attributes=(char **) ResizeQuantumMemory(
2494 xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
2495 else
2496 {
2497 xml_info->attributes=(char **) AcquireQuantumMemory(4,
2498 sizeof(*xml_info->attributes));
2499 if (xml_info->attributes != (char **) NULL)
2500 xml_info->attributes[1]=ConstantString("");
2501 }
2502 if (xml_info->attributes == (char **) NULL)
cristya5fbc3b2013-12-28 01:38:07 +00002503 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
cristy3ed852e2009-09-05 21:47:34 +00002504 xml_info->attributes[i]=ConstantString(tag);
2505 xml_info->attributes[i+2]=(char *) NULL;
cristyda16f162011-02-19 23:52:17 +00002506 (void) strlen(xml_info->attributes[i+1]);
cristy3ed852e2009-09-05 21:47:34 +00002507 }
2508 /*
2509 Add new value to an existing attribute.
2510 */
2511 for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
2512 if (xml_info->attributes[i+1] != (char *) NULL)
2513 xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
2514 if (value != (const char *) NULL)
2515 {
2516 xml_info->attributes[i+1]=ConstantString(value);
2517 return(xml_info);
2518 }
2519 if (xml_info->attributes[i] != (char *) NULL)
2520 xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
2521 (void) CopyMagickMemory(xml_info->attributes+i,xml_info->attributes+i+2,
2522 (size_t) (j-i)*sizeof(*xml_info->attributes));
cristy3ed852e2009-09-05 21:47:34 +00002523 xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
2524 (size_t) (j+2),sizeof(*xml_info->attributes));
2525 if (xml_info->attributes == (char **) NULL)
2526 ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
cristya5fbc3b2013-12-28 01:38:07 +00002527 j-=2;
cristy3ed852e2009-09-05 21:47:34 +00002528 (void) CopyMagickMemory(xml_info->attributes[j+1]+(i/2),
cristya5fbc3b2013-12-28 01:38:07 +00002529 xml_info->attributes[j+1]+(i/2)+1,(size_t) (((j+2)/2)-(i/2))*
cristy28dc7c02014-05-18 14:59:50 +00002530 sizeof(**xml_info->attributes));
cristy3ed852e2009-09-05 21:47:34 +00002531 return(xml_info);
2532}
2533
2534/*
2535%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2536% %
2537% %
2538% %
2539% S e t X M L T r e e C o n t e n t %
2540% %
2541% %
2542% %
2543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2544%
2545% SetXMLTreeContent() sets the character content for the given tag and
2546% returns the tag.
2547%
2548% The format of the SetXMLTreeContent method is:
2549%
2550% XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2551% const char *content)
2552%
2553% A description of each parameter follows:
2554%
2555% o xml_info: the xml info.
2556%
2557% o content: The content.
2558%
2559*/
2560MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
2561 const char *content)
2562{
2563 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002564 assert((xml_info->signature == MagickCoreSignature) ||
2565 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +00002566 if (xml_info->debug != MagickFalse)
2567 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +00002568 if (xml_info->content != (char *) NULL)
2569 xml_info->content=DestroyString(xml_info->content);
2570 xml_info->content=(char *) ConstantString(content);
2571 return(xml_info);
2572}
2573
2574/*
2575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2576% %
2577% %
2578% %
2579% X M L T r e e I n f o T o X M L %
2580% %
2581% %
2582% %
2583%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2584%
2585% XMLTreeInfoToXML() converts an xml-tree to an XML string.
2586%
2587% The format of the XMLTreeInfoToXML method is:
2588%
2589% char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2590%
2591% A description of each parameter follows:
2592%
2593% o xml_info: the xml info.
2594%
2595*/
2596
2597static char *EncodePredefinedEntities(const char *source,ssize_t offset,
2598 char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
2599{
2600 char
2601 *canonical_content;
2602
2603 if (offset < 0)
2604 canonical_content=CanonicalXMLContent(source,pedantic);
2605 else
2606 {
2607 char
2608 *content;
2609
2610 content=AcquireString(source);
2611 content[offset]='\0';
2612 canonical_content=CanonicalXMLContent(content,pedantic);
2613 content=DestroyString(content);
2614 }
2615 if (canonical_content == (char *) NULL)
2616 return(*destination);
cristy151b66d2015-04-15 10:50:31 +00002617 if ((*length+strlen(canonical_content)+MagickPathExtent) > *extent)
cristy3ed852e2009-09-05 21:47:34 +00002618 {
cristy151b66d2015-04-15 10:50:31 +00002619 *extent=(*length)+strlen(canonical_content)+MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +00002620 *destination=(char *) ResizeQuantumMemory(*destination,*extent,
2621 sizeof(**destination));
2622 if (*destination == (char *) NULL)
2623 return(*destination);
2624 }
cristyb51dff52011-05-19 16:55:47 +00002625 *length+=FormatLocaleString(*destination+(*length),*extent,"%s",
cristy3ed852e2009-09-05 21:47:34 +00002626 canonical_content);
2627 canonical_content=DestroyString(canonical_content);
2628 return(*destination);
2629}
2630
2631static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
2632 size_t *extent,size_t start,char ***attributes)
2633{
2634 char
2635 *content;
2636
2637 const char
2638 *attribute;
2639
cristybb503372010-05-27 20:51:26 +00002640 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002641 i;
2642
2643 size_t
2644 offset;
2645
cristy9d314ff2011-03-09 01:30:28 +00002646 ssize_t
2647 j;
2648
cristy3ed852e2009-09-05 21:47:34 +00002649 content=(char *) "";
2650 if (xml_info->parent != (XMLTreeInfo *) NULL)
2651 content=xml_info->parent->content;
2652 offset=0;
2653 *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
2654 start),source,length,extent,MagickFalse);
cristy151b66d2015-04-15 10:50:31 +00002655 if ((*length+strlen(xml_info->tag)+MagickPathExtent) > *extent)
cristy3ed852e2009-09-05 21:47:34 +00002656 {
cristy151b66d2015-04-15 10:50:31 +00002657 *extent=(*length)+strlen(xml_info->tag)+MagickPathExtent;
dirkc8adaac2014-04-04 20:52:51 +00002658 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
cristy3ed852e2009-09-05 21:47:34 +00002659 if (*source == (char *) NULL)
2660 return(*source);
2661 }
cristyb51dff52011-05-19 16:55:47 +00002662 *length+=FormatLocaleString(*source+(*length),*extent,"<%s",xml_info->tag);
cristy3ed852e2009-09-05 21:47:34 +00002663 for (i=0; xml_info->attributes[i]; i+=2)
2664 {
2665 attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
2666 if (attribute != xml_info->attributes[i+1])
2667 continue;
cristy151b66d2015-04-15 10:50:31 +00002668 if ((*length+strlen(xml_info->attributes[i])+MagickPathExtent) > *extent)
cristy3ed852e2009-09-05 21:47:34 +00002669 {
cristy151b66d2015-04-15 10:50:31 +00002670 *extent=(*length)+strlen(xml_info->attributes[i])+MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +00002671 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2672 if (*source == (char *) NULL)
2673 return((char *) NULL);
2674 }
cristyb51dff52011-05-19 16:55:47 +00002675 *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
cristy3ed852e2009-09-05 21:47:34 +00002676 xml_info->attributes[i]);
2677 (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
2678 extent,MagickTrue);
cristyb51dff52011-05-19 16:55:47 +00002679 *length+=FormatLocaleString(*source+(*length),*extent,"\"");
cristy3ed852e2009-09-05 21:47:34 +00002680 }
2681 i=0;
2682 while ((attributes[i] != (char **) NULL) &&
2683 (strcmp(attributes[i][0],xml_info->tag) != 0))
2684 i++;
2685 j=1;
2686 while ((attributes[i] != (char **) NULL) &&
2687 (attributes[i][j] != (char *) NULL))
2688 {
2689 if ((attributes[i][j+1] == (char *) NULL) ||
2690 (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
2691 {
2692 j+=3;
2693 continue;
2694 }
cristy151b66d2015-04-15 10:50:31 +00002695 if ((*length+strlen(attributes[i][j])+MagickPathExtent) > *extent)
cristy3ed852e2009-09-05 21:47:34 +00002696 {
cristy151b66d2015-04-15 10:50:31 +00002697 *extent=(*length)+strlen(attributes[i][j])+MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +00002698 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2699 if (*source == (char *) NULL)
2700 return((char *) NULL);
2701 }
cristyb51dff52011-05-19 16:55:47 +00002702 *length+=FormatLocaleString(*source+(*length),*extent," %s=\"",
cristy3ed852e2009-09-05 21:47:34 +00002703 attributes[i][j]);
2704 (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
2705 MagickTrue);
cristyb51dff52011-05-19 16:55:47 +00002706 *length+=FormatLocaleString(*source+(*length),*extent,"\"");
cristy3ed852e2009-09-05 21:47:34 +00002707 j+=3;
2708 }
cristyb51dff52011-05-19 16:55:47 +00002709 *length+=FormatLocaleString(*source+(*length),*extent,*xml_info->content ?
cristy3ed852e2009-09-05 21:47:34 +00002710 ">" : "/>");
2711 if (xml_info->child != (XMLTreeInfo *) NULL)
2712 *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
2713 else
2714 *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
2715 MagickFalse);
cristy151b66d2015-04-15 10:50:31 +00002716 if ((*length+strlen(xml_info->tag)+MagickPathExtent) > *extent)
cristy3ed852e2009-09-05 21:47:34 +00002717 {
cristy151b66d2015-04-15 10:50:31 +00002718 *extent=(*length)+strlen(xml_info->tag)+MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +00002719 *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
2720 if (*source == (char *) NULL)
2721 return((char *) NULL);
2722 }
2723 if (*xml_info->content != '\0')
cristyb51dff52011-05-19 16:55:47 +00002724 *length+=FormatLocaleString(*source+(*length),*extent,"</%s>",
cristy3ed852e2009-09-05 21:47:34 +00002725 xml_info->tag);
2726 while ((content[offset] != '\0') && (offset < xml_info->offset))
2727 offset++;
2728 if (xml_info->ordered != (XMLTreeInfo *) NULL)
2729 content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
2730 attributes);
2731 else
2732 content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
2733 MagickFalse);
2734 return(content);
2735}
2736
2737MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
2738{
2739 char
2740 *xml;
2741
cristy3ed852e2009-09-05 21:47:34 +00002742 register char
2743 *p,
2744 *q;
2745
cristybb503372010-05-27 20:51:26 +00002746 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00002747 i;
2748
2749 size_t
2750 extent,
2751 length;
2752
cristy9d314ff2011-03-09 01:30:28 +00002753 ssize_t
2754 j,
2755 k;
2756
cristy3ed852e2009-09-05 21:47:34 +00002757 XMLTreeInfo
2758 *ordered,
2759 *parent;
2760
2761 XMLTreeRoot
2762 *root;
2763
2764 assert(xml_info != (XMLTreeInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00002765 assert((xml_info->signature == MagickCoreSignature) ||
2766 (((XMLTreeRoot *) xml_info)->signature == MagickCoreSignature));
dirkfa3d8ae2014-04-23 16:26:55 +00002767 if (xml_info->debug != MagickFalse)
2768 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
cristy3ed852e2009-09-05 21:47:34 +00002769 if (xml_info->tag == (char *) NULL)
2770 return((char *) NULL);
2771 xml=AcquireString((char *) NULL);
2772 length=0;
cristy151b66d2015-04-15 10:50:31 +00002773 extent=MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +00002774 root=(XMLTreeRoot *) xml_info;
2775 while (root->root.parent != (XMLTreeInfo *) NULL)
2776 root=(XMLTreeRoot *) root->root.parent;
cristy4662a132015-01-19 19:27:00 +00002777 parent=xml_info->parent;
cristy3ed852e2009-09-05 21:47:34 +00002778 if (parent == (XMLTreeInfo *) NULL)
2779 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2780 {
2781 /*
2782 Pre-root processing instructions.
2783 */
2784 for (k=2; root->processing_instructions[i][k-1]; k++) ;
2785 p=root->processing_instructions[i][1];
2786 for (j=1; p != (char *) NULL; j++)
2787 {
2788 if (root->processing_instructions[i][k][j-1] == '>')
2789 {
2790 p=root->processing_instructions[i][j];
2791 continue;
2792 }
2793 q=root->processing_instructions[i][0];
cristy151b66d2015-04-15 10:50:31 +00002794 if ((length+strlen(p)+strlen(q)+MagickPathExtent) > extent)
cristy3ed852e2009-09-05 21:47:34 +00002795 {
cristy151b66d2015-04-15 10:50:31 +00002796 extent=length+strlen(p)+strlen(q)+MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +00002797 xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2798 if (xml == (char *) NULL)
2799 return(xml);
2800 }
cristyb51dff52011-05-19 16:55:47 +00002801 length+=FormatLocaleString(xml+length,extent,"<?%s%s%s?>\n",q,
cristy3ed852e2009-09-05 21:47:34 +00002802 *p != '\0' ? " " : "",p);
2803 p=root->processing_instructions[i][j];
2804 }
2805 }
cristy27d19a02015-02-28 22:18:22 +00002806 ordered=xml_info->ordered;
cristy3ed852e2009-09-05 21:47:34 +00002807 xml_info->parent=(XMLTreeInfo *) NULL;
2808 xml_info->ordered=(XMLTreeInfo *) NULL;
2809 xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
2810 xml_info->parent=parent;
2811 xml_info->ordered=ordered;
2812 if (parent == (XMLTreeInfo *) NULL)
2813 for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
2814 {
2815 /*
2816 Post-root processing instructions.
2817 */
2818 for (k=2; root->processing_instructions[i][k-1]; k++) ;
2819 p=root->processing_instructions[i][1];
2820 for (j=1; p != (char *) NULL; j++)
2821 {
2822 if (root->processing_instructions[i][k][j-1] == '<')
2823 {
2824 p=root->processing_instructions[i][j];
2825 continue;
2826 }
2827 q=root->processing_instructions[i][0];
cristy151b66d2015-04-15 10:50:31 +00002828 if ((length+strlen(p)+strlen(q)+MagickPathExtent) > extent)
cristy3ed852e2009-09-05 21:47:34 +00002829 {
cristy151b66d2015-04-15 10:50:31 +00002830 extent=length+strlen(p)+strlen(q)+MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +00002831 xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
2832 if (xml == (char *) NULL)
2833 return(xml);
2834 }
cristyb51dff52011-05-19 16:55:47 +00002835 length+=FormatLocaleString(xml+length,extent,"\n<?%s%s%s?>",q,
cristy3ed852e2009-09-05 21:47:34 +00002836 *p != '\0' ? " " : "",p);
2837 p=root->processing_instructions[i][j];
2838 }
2839 }
2840 return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));
2841}