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