1/*--------------------------------------------------------------------------- 2| Copyright (C) 1999 Jochen C. Loewer (loewerj@hotmail.com) 3+---------------------------------------------------------------------------- 4| 5| $Id: dom.c,v 1.107 2007/12/25 23:18:41 rolf Exp $ 6| 7| 8| A DOM interface upon the expat XML parser for the C language 9| according to the W3C recommendation REC-DOM-Level-1-19981001 10| 11| 12| The contents of this file are subject to the Mozilla Public License 13| Version 1.1 (the "License"); you may not use this file except in 14| compliance with the License. You may obtain a copy of the License at 15| http://www.mozilla.org/MPL/ 16| 17| Software distributed under the License is distributed on an "AS IS" 18| basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the 19| License for the specific language governing rights and limitations 20| under the License. 21| 22| The Original Code is tDOM. 23| 24| The Initial Developer of the Original Code is Jochen Loewer 25| Portions created by Jochen Loewer are Copyright (C) 1998, 1999 26| Jochen Loewer. All Rights Reserved. 27| 28| Contributor(s): 29| Sept99 Carsten Zerbst Added comment and processing instructions 30| nodes. 31| 32| June00 Zoran Vasiljevic Made thread-safe. 33| 34| 01 Rolf Ade baseURI stuff, ID support, external 35| entities, tdom command 36| 37| 38| written by Jochen Loewer 39| April 5, 1999 40| 41\--------------------------------------------------------------------------*/ 42 43 44 45/*--------------------------------------------------------------------------- 46| Includes 47| 48\--------------------------------------------------------------------------*/ 49#include <tcl.h> 50#include <stdlib.h> 51#include <string.h> 52#include <dom.h> 53#include <domxpath.h> 54#include <utf8conv.h> 55#include <tclexpat.h> 56 57 58 59/*--------------------------------------------------------------------------- 60| Defines 61| 62\--------------------------------------------------------------------------*/ 63#define DBG(x) 64#define TDOM_NS 65#define XSLT_NAMESPACE "http://www.w3.org/1999/XSL/Transform" 66 67#define MutationEvent() 68#define MutationEvent2(type,node) 69#define MutationEvent3(type,node,relatioNode) 70 71#define MCHK(a) if ((a)==NULL) { \ 72 fprintf(stderr, \ 73 "Memory alloc error line: %d",__LINE__); \ 74 exit(1); \ 75 } 76 77#define INITIAL_BASEURISTACK_SIZE 4; 78 79/*--------------------------------------------------------------------------- 80| Globals 81| In threading environment, some are located in domDocument structure 82| and some are handled differently (domUniqueNodeNr, domUniqueDocNr) 83| 84\--------------------------------------------------------------------------*/ 85 86#ifndef TCL_THREADS 87 unsigned long domUniqueNodeNr = 0; 88 unsigned long domUniqueDocNr = 0; 89 Tcl_HashTable tdom_tagNames; 90 Tcl_HashTable tdom_attrNames; 91#endif 92 93static int domModuleIsInitialized = 0; 94TDomThreaded(static Tcl_Mutex initMutex;) 95 96static char *domException2StringTable [] = { 97 98 "OK - no expection", 99 "INDEX_SIZE_ERR", 100 "DOMSTRING_SIZE_ERR", 101 "HIERARCHY_REQUEST_ERR", 102 "WRONG_DOCUMENT_ERR", 103 "INVALID_CHARACTER_ERR", 104 "NO_DATA_ALLOWED_ERR", 105 "NO_MODIFICATION_ALLOWED_ERR", 106 "NOT_FOUND_ERR", 107 "NOT_SUPPORTED_ERR", 108 "INUSE_ATTRIBUTE_ERR" 109}; 110 111static char tdom_usage[] = 112 "Usage tdom <expat parser obj> <subCommand>, where subCommand can be:\n" 113 " enable \n" 114 " getdoc \n" 115 " setResultEncoding \n" 116 " setStoreLineColumn \n" 117 ; 118 119 120/*--------------------------------------------------------------------------- 121| type domActiveNS 122| 123\--------------------------------------------------------------------------*/ 124typedef struct _domActiveNS { 125 126 int depth; 127 domNS *namespace; 128 129} domActiveNS; 130 131/*--------------------------------------------------------------------------- 132| type domBaseURIstackElem 133| 134\--------------------------------------------------------------------------*/ 135typedef struct _domActiveBaseURI { 136 137 int depth; 138 const char *baseURI; 139 140} domActiveBaseURI; 141 142/*--------------------------------------------------------------------------- 143| type domReadInfo 144| 145\--------------------------------------------------------------------------*/ 146typedef struct _domReadInfo { 147 148 XML_Parser parser; 149 domDocument *document; 150 domNode *currentNode; 151 int depth; 152 int ignoreWhiteSpaces; 153 Tcl_DString *cdata; 154 TEncoding *encoding_8bit; 155 int storeLineColumn; 156 int feedbackAfter; 157 int lastFeedbackPosition; 158 Tcl_Interp *interp; 159 int activeNSsize; 160 int activeNSpos; 161 domActiveNS *activeNS; 162 int baseURIstackSize; 163 int baseURIstackPos; 164 domActiveBaseURI *baseURIstack; 165 int insideDTD; 166 167} domReadInfo; 168 169/*---------------------------------------------------------------------------- 170| Prototypes 171| 172\---------------------------------------------------------------------------*/ 173static void DispatchPCDATA (domReadInfo *info); 174 175 176#ifndef TCL_THREADS 177 178/*--------------------------------------------------------------------------- 179| domModuleFinalize 180| 181\--------------------------------------------------------------------------*/ 182static void 183domModuleFinalize(ClientData unused) 184{ 185 Tcl_HashEntry *entryPtr; 186 Tcl_HashSearch search; 187 188 entryPtr = Tcl_FirstHashEntry(&tdom_tagNames, &search); 189 while (entryPtr) { 190 Tcl_DeleteHashEntry(entryPtr); 191 entryPtr = Tcl_NextHashEntry(&search); 192 } 193 Tcl_DeleteHashTable(&tdom_tagNames); 194 195 entryPtr = Tcl_FirstHashEntry(&tdom_attrNames, &search); 196 while (entryPtr) { 197 Tcl_DeleteHashEntry(entryPtr); 198 entryPtr = Tcl_NextHashEntry(&search); 199 } 200 Tcl_DeleteHashTable(&tdom_attrNames); 201 202 return; 203} 204#endif /* TCL_THREADS */ 205 206/*--------------------------------------------------------------------------- 207| domModuleInitialize 208| 209\--------------------------------------------------------------------------*/ 210void 211domModuleInitialize ( 212) 213{ 214 if (domModuleIsInitialized == 0) { 215 TDomThreaded(Tcl_MutexLock(&initMutex);) 216 if (domModuleIsInitialized == 0) { 217 domAllocInit(); 218 TDomNotThreaded ( 219 Tcl_InitHashTable(&tdom_tagNames, TCL_STRING_KEYS); 220 Tcl_InitHashTable(&tdom_attrNames, TCL_STRING_KEYS); 221 Tcl_CreateExitHandler(domModuleFinalize, NULL); 222 ) 223 TDomThreaded( 224 Tcl_CreateExitHandler(domLocksFinalize, NULL); 225 ) 226 domModuleIsInitialized = 1; 227 } 228 TDomThreaded(Tcl_MutexUnlock(&initMutex);) 229 } 230} 231 232/*--------------------------------------------------------------------------- 233| coercion routines for calling from C++ 234| 235\--------------------------------------------------------------------------*/ 236domAttrNode * coerceToAttrNode( domNode *n ) { 237 return (domAttrNode *)n; 238} 239 240domTextNode * coerceToTextNode( domNode *n ) { 241 return (domTextNode *)n; 242} 243 244domProcessingInstructionNode * coerceToProcessingInstructionNode( domNode *n ) { 245 return (domProcessingInstructionNode *)n; 246} 247 248/*--------------------------------------------------------------------------- 249| domIsNAME 250| 251\--------------------------------------------------------------------------*/ 252int 253domIsNAME ( 254 const char *name 255 ) 256{ 257 const char *p; 258 259 p = name; 260 if (!isNameStart(p)) return 0; 261 p += UTF8_CHAR_LEN(*p); 262 while (*p) { 263 if (isNameChar(p)) 264 p += UTF8_CHAR_LEN(*p); 265 else return 0; 266 } 267 return 1; 268} 269 270/*--------------------------------------------------------------------------- 271| domIsPINAME 272| 273\--------------------------------------------------------------------------*/ 274int 275domIsPINAME ( 276 const char *name 277 ) 278{ 279 if (strlen (name) == 3 280 && ((name[0] == 'x') || (name[0] == 'X')) 281 && ((name[1] == 'm') || (name[1] == 'M')) 282 && ((name[2] == 'l') || (name[2] == 'L')) ) { 283 return 0; 284 } 285 return domIsNAME (name); 286} 287 288/*--------------------------------------------------------------------------- 289| domIsQNAME 290| 291\--------------------------------------------------------------------------*/ 292int 293domIsQNAME ( 294 const char *name 295 ) 296{ 297 const char *p; 298 299 p = name; 300 if (!isNCNameStart(p)) return 0; 301 p += UTF8_CHAR_LEN(*p); 302 while (*p) { 303 if (isNCNameChar(p)) 304 p += UTF8_CHAR_LEN(*p); 305 else { 306 if (*p == ':') { 307 p += 1; 308 if (!isNCNameStart(p)) return 0; 309 p += UTF8_CHAR_LEN(*p); 310 break; 311 } 312 else return 0; 313 } 314 } 315 while (*p) { 316 if (isNCNameChar(p)) 317 p += UTF8_CHAR_LEN(*p); 318 else return 0; 319 } 320 return 1; 321} 322 323/*--------------------------------------------------------------------------- 324| domIsNCNAME 325| 326\--------------------------------------------------------------------------*/ 327int 328domIsNCNAME ( 329 const char *name 330 ) 331{ 332 const char *p; 333 334 p = name; 335 if (!isNCNameStart(p)) return 0; 336 p += UTF8_CHAR_LEN(*p); 337 while (*p) { 338 if (isNCNameChar(p)) 339 p += UTF8_CHAR_LEN(*p); 340 else return 0; 341 } 342 return 1; 343} 344 345/*--------------------------------------------------------------------------- 346| domIsChar 347| 348\--------------------------------------------------------------------------*/ 349int 350domIsChar ( 351 const char *str 352 ) 353{ 354 const char *p; 355 int clen; 356 357 p = str; 358 while (*p) { 359 clen = UTF8_CHAR_LEN(*p); 360 if (UTF8_XMLCHAR((unsigned const char *)p,clen)) 361 p += clen; 362 else return 0; 363 } 364 return 1; 365} 366 367/*--------------------------------------------------------------------------- 368| domIsComment 369| 370\--------------------------------------------------------------------------*/ 371int 372domIsComment ( 373 const char *str 374 ) 375{ 376 const char *p; 377 int len, i = 0; 378 379 p = str; 380 len = strlen (str); 381 while (i < len) { 382 if (*p == '-') { 383 if (i == len - 1) return 0; 384 p++; i++; 385 if (*p == '-') return 0; 386 } 387 p++; i++; 388 } 389 return domIsChar (str); 390} 391 392/*--------------------------------------------------------------------------- 393| domIsCDATA 394| 395\--------------------------------------------------------------------------*/ 396int 397domIsCDATA ( 398 const char *str 399 ) 400{ 401 const char *p; 402 int len, i = 0; 403 404 p = str; 405 len = strlen (str); 406 while (i < len - 2) { 407 if ( *p == ']' 408 && p[1] == ']' 409 && p[2] == '>') return 0; 410 p++; i++; 411 } 412 return domIsChar (str); 413} 414 415/*--------------------------------------------------------------------------- 416| domIsPIValue 417| 418\--------------------------------------------------------------------------*/ 419int 420domIsPIValue ( 421 const char *str 422 ) 423{ 424 const char *p; 425 int len, i = 0; 426 427 p = str; 428 len = strlen (str); 429 while (i < len - 1) { 430 if (*p == '?' && p[1] == '>') return 0; 431 p++; i++; 432 } 433 return domIsChar (str); 434} 435 436/*--------------------------------------------------------------------------- 437| domLookupNamespace 438| 439\--------------------------------------------------------------------------*/ 440domNS * 441domLookupNamespace ( 442 domDocument *doc, 443 const char *prefix, 444 const char *namespaceURI 445) 446{ 447 domNS *ns; 448 int i; 449 450 if (prefix==NULL) return NULL; 451 for (i = 0; i <= doc->nsptr; i++) { 452 ns = doc->namespaces[i]; 453 if ( (ns->prefix != NULL) 454 && (strcmp(prefix,ns->prefix)==0) 455 && (strcmp(namespaceURI, ns->uri)==0) 456 ) { 457 return ns; 458 } 459 } 460 return NULL; 461} 462 463 464/* 465 *---------------------------------------------------------------------- 466 * 467 * domPrecedes -- 468 * 469 * This helper procedure returns if node precedes other with regard 470 * to their position in the document and according to the document 471 * order. The two nodes could be out of the two documents. Both 472 * nodes must not be out of the fragments list. 473 * 474 * Results: 475 * 1 if node precedes other in document order, 0 otherwise. 476 * 477 * Side effects: 478 * None. 479 * 480 *---------------------------------------------------------------------- 481 */ 482 483int 484domPrecedes ( 485 domNode *node, 486 domNode *other 487 ) 488{ 489 domNode *nodeAncestor, *otherAncestor, *otherToplevel; 490 domAttrNode *attrN, *attrO; 491 492 if (node == other) { 493 return 0; 494 } 495 496 if (node->nodeType == ATTRIBUTE_NODE) { 497 attrN = (domAttrNode*)node; 498 if (other->nodeType == ATTRIBUTE_NODE) { 499 attrO = (domAttrNode*)other; 500 if (attrN->parentNode == attrO->parentNode) { 501 attrN = attrN->nextSibling; 502 while (attrN) { 503 if (attrN == attrO) { 504 return 1; 505 } 506 attrN = attrN->nextSibling; 507 } 508 return 0; 509 } else { 510 node = attrN->parentNode; 511 other = attrO->parentNode; 512 } 513 } else { 514 if (attrN->parentNode == other) { 515 return 0; 516 } else { 517 node = attrN->parentNode; 518 } 519 } 520 } 521 if (other->nodeType == ATTRIBUTE_NODE) { 522 attrO = (domAttrNode*)other; 523 if (node == attrO->parentNode) { 524 return 1; 525 } else { 526 other = attrO->parentNode; 527 } 528 } 529 530 if (node->ownerDocument != other->ownerDocument) { 531 /* For mt tdom, this does not, what it should: 532 whatever relative order two nodes out of different 533 documents ever have (that is not determined by the rec) it 534 must return always the same order (that is required by the 535 rec). */ 536 return (node->ownerDocument->documentNumber < 537 other->ownerDocument->documentNumber); 538 } 539 540#ifndef TCL_THREADS 541 if (node->ownerDocument->nodeFlags & NEEDS_RENUMBERING) { 542 domRenumberTree (node->ownerDocument->rootNode); 543 node->ownerDocument->nodeFlags &= ~NEEDS_RENUMBERING; 544 } 545 return (node->nodeNumber < other->nodeNumber); 546# else 547 if (!(node->ownerDocument->nodeFlags & NEEDS_RENUMBERING)) { 548 return (node->nodeNumber < other->nodeNumber); 549 } 550#endif 551 552 otherAncestor = other; 553 while (otherAncestor->parentNode) { 554 otherAncestor = otherAncestor->parentNode; 555 if (otherAncestor == node) { 556 return 1; 557 } 558 } 559 otherToplevel = otherAncestor; 560 561 nodeAncestor = node; 562 while (nodeAncestor->parentNode) { 563 otherAncestor = other; 564 while (otherAncestor->parentNode) { 565 if (nodeAncestor->parentNode == otherAncestor->parentNode) { 566 nodeAncestor = nodeAncestor->nextSibling; 567 while (nodeAncestor) { 568 if (nodeAncestor == otherAncestor) { 569 return 1; 570 } 571 nodeAncestor = nodeAncestor->nextSibling; 572 } 573 return 0; 574 } 575 otherAncestor = otherAncestor->parentNode; 576 } 577 nodeAncestor = nodeAncestor->parentNode; 578 if (nodeAncestor == other) { 579 return 0; 580 } 581 } 582 nodeAncestor = nodeAncestor->nextSibling; 583 while (nodeAncestor) { 584 if (nodeAncestor == otherAncestor) { 585 return 1; 586 } 587 nodeAncestor = nodeAncestor->nextSibling; 588 } 589 if (node == node->ownerDocument->rootNode) { 590 return 1; 591 } 592 return 0; 593} 594 595/*--------------------------------------------------------------------------- 596| domRenumberTree 597| 598\--------------------------------------------------------------------------*/ 599void 600domRenumberTree ( 601 domNode *node 602) 603{ 604 while (node) { 605 node->nodeNumber = NODE_NO(node->ownerDocument); 606 if (node->nodeType == ELEMENT_NODE) { 607 domRenumberTree (node->firstChild); 608 } 609 node = node->nextSibling; 610 } 611} 612 613/*--------------------------------------------------------------------------- 614| domLookupPrefixWithMappings 615| 616\--------------------------------------------------------------------------*/ 617const char * 618domLookupPrefixWithMappings ( 619 domNode *node, 620 const char *prefix, 621 char **prefixMappings 622 ) 623{ 624 int i; 625 domNS *ns; 626 627 if (prefixMappings) { 628 i = 0; 629 while (prefixMappings[i]) { 630 if (strcmp (prefix, prefixMappings[i]) == 0) { 631 return prefixMappings[i+1]; 632 } 633 i += 2; 634 } 635 } 636 ns = domLookupPrefix (node, prefix); 637 if (ns) return ns->uri; 638 else return NULL; 639} 640 641/*--------------------------------------------------------------------------- 642| domLookupPrefix 643| 644\--------------------------------------------------------------------------*/ 645domNS * 646domLookupPrefix ( 647 domNode *node, 648 const char *prefix 649 ) 650{ 651 domAttrNode *NSattr; 652 domNode *orgNode = node; 653 int found; 654 655 found = 0; 656 while (node) { 657 if (node->firstAttr && !(node->firstAttr->nodeFlags & IS_NS_NODE)) { 658 node = node->parentNode; 659 continue; 660 } 661 NSattr = node->firstAttr; 662 while (NSattr && (NSattr->nodeFlags & IS_NS_NODE)) { 663 if (prefix[0] == '\0') { 664 if (NSattr->nodeName[5] == '\0') { 665 found = 1; 666 break; 667 } 668 } else { 669 if (NSattr->nodeName[5] != '\0' 670 && strcmp (&NSattr->nodeName[6], prefix)==0) { 671 found = 1; 672 break; 673 } 674 } 675 NSattr = NSattr->nextSibling; 676 } 677 if (found) { 678 return domGetNamespaceByIndex (node->ownerDocument, 679 NSattr->namespace); 680 } 681 node = node->parentNode; 682 } 683 if (prefix && (strcmp (prefix, "xml")==0)) { 684 NSattr = orgNode->ownerDocument->rootNode->firstAttr; 685 return domGetNamespaceByIndex (orgNode->ownerDocument, 686 NSattr->namespace); 687 } 688 return NULL; 689} 690 691/*--------------------------------------------------------------------------- 692| domIsNamespaceInScope 693| 694\--------------------------------------------------------------------------*/ 695static int 696domIsNamespaceInScope ( 697 domActiveNS *NSstack, 698 int NSstackPos, 699 const char *prefix, 700 const char *namespaceURI 701) 702{ 703 int i; 704 705 for (i = NSstackPos; i >= 0; i--) { 706 if (NSstack[i].namespace->prefix[0] && 707 (strcmp(NSstack[i].namespace->prefix, prefix)==0)) { 708 if (strcmp(NSstack[i].namespace->uri, namespaceURI)==0) { 709 /* OK, exactly the same namespace declaration is in scope */ 710 return 1; 711 } else { 712 /* This prefix is currently assigned to another uri, 713 we need a new NS declaration, to override this one */ 714 return 0; 715 } 716 } 717 } 718 return 0; 719} 720 721/*--------------------------------------------------------------------------- 722| domLookupURI 723| 724\--------------------------------------------------------------------------*/ 725domNS * 726domLookupURI ( 727 domNode *node, 728 char *uri 729 ) 730{ 731 domAttrNode *NSattr; 732 int found, alreadyHaveDefault; 733 734 found = 0; 735 alreadyHaveDefault = 0; 736 while (node) { 737 if (node->firstAttr && !(node->firstAttr->nodeFlags & IS_NS_NODE)) { 738 node = node->parentNode; 739 continue; 740 } 741 NSattr = node->firstAttr; 742 while (NSattr && (NSattr->nodeFlags & IS_NS_NODE)) { 743 if (NSattr->nodeName[5] == '\0') { 744 if (!alreadyHaveDefault) { 745 if (strcmp (NSattr->nodeValue, uri)==0) { 746 found = 1; 747 break; 748 } else { 749 alreadyHaveDefault = 1; 750 } 751 } 752 } else { 753 if (strcmp (NSattr->nodeValue, uri)==0) { 754 found = 1; 755 break; 756 } 757 } 758 NSattr = NSattr->nextSibling; 759 } 760 if (found) { 761 return domGetNamespaceByIndex (node->ownerDocument, 762 NSattr->namespace); 763 } 764 node = node->parentNode; 765 } 766 return NULL; 767} 768 769 770/*--------------------------------------------------------------------------- 771| domGetNamespaceByIndex 772| 773\--------------------------------------------------------------------------*/ 774domNS * 775domGetNamespaceByIndex ( 776 domDocument *doc, 777 int nsIndex 778) 779{ 780 if (!nsIndex) return NULL; 781 return doc->namespaces[nsIndex-1]; 782} 783 784 785/*--------------------------------------------------------------------------- 786| domNewNamespace 787| 788\--------------------------------------------------------------------------*/ 789domNS* domNewNamespace ( 790 domDocument *doc, 791 const char *prefix, 792 const char *namespaceURI 793) 794{ 795 domNS *ns = NULL; 796 797 DBG(fprintf(stderr, "domNewNamespace '%s' --> '%s' \n", prefix, namespaceURI);) 798 799 ns = domLookupNamespace (doc, prefix, namespaceURI); 800 if (ns != NULL) return ns; 801 doc->nsptr++; 802 if (doc->nsptr > 254) { 803 DBG(fprintf (stderr, "maximum number of namespaces exceeded!!!\n");) 804 domPanic("domNewNamespace: maximum number of namespaces exceeded!"); 805 } 806 if (doc->nsptr >= doc->nslen) { 807 doc->namespaces = (domNS**) REALLOC ((char*) doc->namespaces, 808 sizeof (domNS*) * 2 * doc->nslen); 809 doc->nslen *= 2; 810 } 811 doc->namespaces[doc->nsptr] = (domNS*)MALLOC (sizeof (domNS)); 812 ns = doc->namespaces[doc->nsptr]; 813 814 815 if (prefix == NULL) { 816 ns->prefix = tdomstrdup(""); 817 } else { 818 ns->prefix = tdomstrdup(prefix); 819 } 820 if (namespaceURI == NULL) { 821 ns->uri = tdomstrdup(""); 822 } else { 823 ns->uri = tdomstrdup(namespaceURI); 824 } 825 ns->index = doc->nsptr + 1; 826 827 return ns; 828} 829 830 831/*--------------------------------------------------------------------------- 832| domSplitQName - extract namespace prefix (if any) 833| 834\--------------------------------------------------------------------------*/ 835int 836domSplitQName ( 837 const char *name, 838 char *prefix, 839 const char **localName 840) 841{ 842 const char *s; 843 char *p, *prefixEnd; 844 845 s = name; 846 p = prefix; 847 prefixEnd = &prefix[MAX_PREFIX_LEN-1]; 848 while (*s && (*s != ':')) { 849 if (p < prefixEnd) *p++ = *s; 850 s++; 851 } 852 if (*s != ':') { 853 *prefix = '\0'; 854 *localName = name; 855 return 0; 856 } 857 *p++ = '\0'; 858 *localName = ++s; 859 DBG(fprintf(stderr, "domSplitName %s -> '%s' '%s'\n", 860 name, prefix, *localName); 861 ) 862 return 1; 863} 864 865 866/*--------------------------------------------------------------------------- 867| domNamespaceURI 868| 869\--------------------------------------------------------------------------*/ 870const char * 871domNamespaceURI ( 872 domNode *node 873) 874{ 875 domAttrNode *attr; 876 domNS *ns; 877 878 if (!node->namespace) return NULL; 879 if (node->nodeType == ATTRIBUTE_NODE) { 880 attr = (domAttrNode*)node; 881 if (attr->nodeFlags & IS_NS_NODE) return NULL; 882 ns = attr->parentNode->ownerDocument->namespaces[attr->namespace-1]; 883 } else 884 if (node->nodeType == ELEMENT_NODE) { 885 ns = node->ownerDocument->namespaces[node->namespace-1]; 886 } else { 887 return NULL; 888 } 889 return ns->uri; 890} 891 892 893/*--------------------------------------------------------------------------- 894| domNamespacePrefix 895| 896\--------------------------------------------------------------------------*/ 897const char * 898domNamespacePrefix ( 899 domNode *node 900) 901{ 902 domAttrNode *attr; 903 domNS *ns; 904 905 if (!node->namespace) return NULL; 906 if (node->nodeType == ATTRIBUTE_NODE) { 907 attr = (domAttrNode*)node; 908 ns = attr->parentNode->ownerDocument->namespaces[attr->namespace-1]; 909 } else 910 if (node->nodeType == ELEMENT_NODE) { 911 ns = node->ownerDocument->namespaces[node->namespace-1]; 912 } else { 913 return NULL; 914 } 915 if (ns) return ns->prefix; 916 return NULL; 917} 918 919 920/*--------------------------------------------------------------------------- 921| domGetLocalName 922| 923\--------------------------------------------------------------------------*/ 924const char * 925domGetLocalName ( 926 const char *nodeName 927) 928{ 929 char prefix[MAX_PREFIX_LEN]; 930 const char *localName; 931 932 domSplitQName (nodeName, prefix, &localName); 933 return localName; 934} 935 936/* 937 *---------------------------------------------------------------------- 938 * 939 * domGetAttributeNodeNS -- 940 * 941 * Search a given node for an attribute with namespace "uri" and 942 * localname "localname". 943 * 944 * Results: 945 * Returns a pointer to the attribute, if there is one with the 946 * given namespace and localname. Otherwise returns NULL. 947 * 948 * Side effects: 949 * None. 950 * 951 *---------------------------------------------------------------------- 952 */ 953 954domAttrNode * 955domGetAttributeNodeNS ( 956 domNode *node, /* The attributes of this node are searched for a 957 matching attribute; the node must exist */ 958 const char *uri, /* The namespace of the demanded attribute */ 959 const char *localname /* The localname of the demanded attribute */ 960 ) 961{ 962 domAttrNode *attr; 963 domNS *ns; 964 int noNS; 965 char prefix[MAX_PREFIX_LEN]; 966 const char *attrLocalName; 967 968 if (uri[0] == '\0') noNS = 1; 969 else noNS = 0; 970 971 attr = node->firstAttr; 972 while (attr) { 973 if (noNS) { 974 if (!attr->namespace 975 && strcmp (attr->nodeName, localname) == 0) { 976 return attr; 977 978 } 979 } else { 980 if (attr->namespace) { 981 domSplitQName (attr->nodeName, prefix, &attrLocalName); 982 if (strcmp (localname, attrLocalName) == 0) { 983 ns = domGetNamespaceByIndex (node->ownerDocument, 984 attr->namespace); 985 if (strcmp (ns->uri, uri) == 0) { 986 return attr; 987 } 988 } 989 } 990 } 991 attr = attr->nextSibling; 992 } 993 return NULL; 994} 995 996/* 997 *---------------------------------------------------------------------- 998 * 999 * domPreviousSibling -- 1000 * 1001 * Returns the previous node to the given node or NULL, if there 1002 * is no previous node. This function is needed in situations, 1003 * where the given node may also be an domAttrNode. Namespace 1004 * declaring attributes are treated as any other 1005 * attributes. Since the domAttrNode struct doesn't has an 1006 * element for the previous attribute, we need a function for the 1007 * relatively rare cases, the 'previous attribute' is 1008 * needed. Remeber, that the XML rec say, that there is no 1009 * specific order of the attributes of a node. 1010 * 1011 * Results: 1012 * A pointer to the previous node of the given one 1013 * or NULL, if there isn't a previous node. 1014 * 1015 * Side effects: 1016 * None. 1017 * 1018 *---------------------------------------------------------------------- 1019 */ 1020 1021domNode * 1022domPreviousSibling ( 1023 domNode *node /* The reference attribute */ 1024 ) 1025{ 1026 domAttrNode *attr, *attr1; 1027 1028 if (node->nodeType != ATTRIBUTE_NODE) { 1029 return node->previousSibling; 1030 } 1031 1032 attr = (domAttrNode*) node; 1033 if (attr->parentNode->firstAttr == attr) { 1034 return NULL; 1035 } 1036 attr1 = attr->parentNode->firstAttr; 1037 while (attr1) { 1038 if (attr1->nextSibling == attr) { 1039 return (domNode*)attr1; 1040 } 1041 attr1 = attr1->nextSibling; 1042 } 1043 /* Not reached */ 1044 return NULL; 1045} 1046 1047#ifndef TDOM_NO_EXPAT 1048 1049 1050/*--------------------------------------------------------------------------- 1051| startElement 1052| 1053\--------------------------------------------------------------------------*/ 1054static void 1055startElement( 1056 void *userData, 1057 const char *name, 1058 const char **atts 1059) 1060{ 1061 domReadInfo *info = userData; 1062 domNode *node, *parentNode; 1063 domLineColumn *lc; 1064 domAttrNode *attrnode, *lastAttr; 1065 const char **atPtr, **idAttPtr; 1066 Tcl_HashEntry *h; 1067 int hnew, len, pos, idatt, newNS; 1068 const char *xmlns, *localname; 1069 char tagPrefix[MAX_PREFIX_LEN]; 1070 char prefix[MAX_PREFIX_LEN]; 1071 domNS *ns; 1072 char feedbackCmd[24]; 1073 1074 if (info->feedbackAfter) { 1075 1076 if (info->lastFeedbackPosition 1077 < XML_GetCurrentByteIndex (info->parser) 1078 ) { 1079 sprintf(feedbackCmd, "%s", "::dom::domParseFeedback"); 1080 if (Tcl_Eval(info->interp, feedbackCmd) != TCL_OK) { 1081 DBG(fprintf(stderr, "%s\n", 1082 Tcl_GetStringResult (info->interp));) 1083 /* FIXME: We simply ignore script errors in the 1084 feedbackCmd, for now. One fine day, expat may provide 1085 a way to cancel an already started parse run from 1086 inside a handler. Then we should revisit this. */ 1087 /* exit(1) */ 1088 } 1089 info->lastFeedbackPosition += info->feedbackAfter; 1090 } 1091 } 1092 1093 DispatchPCDATA (info); 1094 1095 h = Tcl_CreateHashEntry(&HASHTAB(info->document,tdom_tagNames), name, 1096 &hnew); 1097 if (info->storeLineColumn) { 1098 node = (domNode*) domAlloc(sizeof(domNode) 1099 + sizeof(domLineColumn)); 1100 } else { 1101 node = (domNode*) domAlloc(sizeof(domNode)); 1102 } 1103 memset(node, 0, sizeof(domNode)); 1104 node->nodeType = ELEMENT_NODE; 1105 node->nodeFlags = 0; 1106 node->namespace = 0; 1107 node->nodeName = (char *)&(h->key); 1108 node->nodeNumber = NODE_NO(info->document); 1109 node->ownerDocument = info->document; 1110 1111 if (info->baseURIstack[info->baseURIstackPos].baseURI 1112 != XML_GetBase (info->parser)) { 1113 h = Tcl_CreateHashEntry (info->document->baseURIs, 1114 (char*) node, 1115 &hnew); 1116 Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser))); 1117 node->nodeFlags |= HAS_BASEURI; 1118 info->baseURIstackPos++; 1119 if (info->baseURIstackPos >= info->baseURIstackSize) { 1120 info->baseURIstack = (domActiveBaseURI*) REALLOC( 1121 (char*)info->baseURIstack, 1122 sizeof(domActiveBaseURI) * 2 * info->baseURIstackSize); 1123 info->baseURIstackSize = 2 * info->baseURIstackSize; 1124 } 1125 info->baseURIstack[info->baseURIstackPos].baseURI 1126 = XML_GetBase (info->parser); 1127 info->baseURIstack[info->baseURIstackPos].depth 1128 = info->depth; 1129 } 1130 1131 if (info->depth == 0) { 1132 if (info->document->rootNode->lastChild) { 1133 info->document->rootNode->lastChild->nextSibling = node; 1134 node->previousSibling = info->document->rootNode->lastChild; 1135 } else { 1136 info->document->rootNode->firstChild = node; 1137 } 1138 info->document->rootNode->lastChild = node; 1139 } else { 1140 parentNode = info->currentNode; 1141 node->parentNode = parentNode; 1142 if (parentNode->firstChild) { 1143 parentNode->lastChild->nextSibling = node; 1144 node->previousSibling = parentNode->lastChild; 1145 parentNode->lastChild = node; 1146 } else { 1147 parentNode->firstChild = parentNode->lastChild = node; 1148 } 1149 } 1150 info->currentNode = node; 1151 if (info->storeLineColumn) { 1152 lc = (domLineColumn*) ( ((char*)node) + sizeof(domNode)); 1153 node->nodeFlags |= HAS_LINE_COLUMN; 1154 lc->line = XML_GetCurrentLineNumber(info->parser); 1155 lc->column = XML_GetCurrentColumnNumber(info->parser); 1156 } 1157 1158 1159 lastAttr = NULL; 1160 /*-------------------------------------------------------------- 1161 | process namespace declarations 1162 | 1163 \-------------------------------------------------------------*/ 1164#ifdef TDOM_NS 1165 for (atPtr = atts; atPtr[0] && atPtr[1]; atPtr += 2) { 1166 1167 if (strncmp(atPtr[0], "xmlns", 5) == 0) { 1168 xmlns = atPtr[0]; 1169 newNS = 1; 1170 if (xmlns[5] == ':') { 1171 if (domIsNamespaceInScope (info->activeNS, info->activeNSpos, 1172 &(xmlns[6]), atPtr[1])) { 1173 ns = domLookupPrefix (info->currentNode, &(xmlns[6])); 1174 newNS = 0; 1175 } 1176 else { 1177 ns = domNewNamespace(info->document, &xmlns[6], atPtr[1]); 1178 } 1179 } else { 1180 ns = domNewNamespace(info->document, "", atPtr[1]); 1181 } 1182 if (newNS) { 1183 /* push active namespace */ 1184 info->activeNSpos++; 1185 if (info->activeNSpos >= info->activeNSsize) { 1186 info->activeNS = (domActiveNS*) REALLOC( 1187 (char*)info->activeNS, 1188 sizeof(domActiveNS) * 2 * info->activeNSsize); 1189 info->activeNSsize = 2 * info->activeNSsize; 1190 } 1191 info->activeNS[info->activeNSpos].depth = info->depth; 1192 info->activeNS[info->activeNSpos].namespace = ns; 1193 } 1194 1195 h = Tcl_CreateHashEntry(&HASHTAB(info->document, tdom_attrNames), 1196 atPtr[0], &hnew); 1197 attrnode = (domAttrNode*) domAlloc(sizeof(domAttrNode)); 1198 memset(attrnode, 0, sizeof(domAttrNode)); 1199 attrnode->nodeType = ATTRIBUTE_NODE; 1200 attrnode->nodeFlags = IS_NS_NODE; 1201 attrnode->namespace = ns->index; 1202 attrnode->nodeName = (char *)&(h->key); 1203 attrnode->parentNode = node; 1204 len = strlen(atPtr[1]); 1205 if (TclOnly8Bits && info->encoding_8bit) { 1206 tdom_Utf8to8Bit(info->encoding_8bit, atPtr[1], &len); 1207 } 1208 attrnode->valueLength = len; 1209 attrnode->nodeValue = (char*)MALLOC(len+1); 1210 strcpy(attrnode->nodeValue, atPtr[1]); 1211 if (node->firstAttr) { 1212 lastAttr->nextSibling = attrnode; 1213 } else { 1214 node->firstAttr = attrnode; 1215 } 1216 lastAttr = attrnode; 1217 } 1218 1219 } 1220 1221 /*---------------------------------------------------------- 1222 | look for namespace of element 1223 \---------------------------------------------------------*/ 1224 domSplitQName (name, tagPrefix, &localname); 1225 for (pos = info->activeNSpos; pos >= 0; pos--) { 1226 if ( ((tagPrefix[0] == '\0') && (info->activeNS[pos].namespace->prefix[0] == '\0')) 1227 || ((tagPrefix[0] != '\0') && (info->activeNS[pos].namespace->prefix[0] != '\0') 1228 && (strcmp(tagPrefix, info->activeNS[pos].namespace->prefix) == 0)) 1229 ) { 1230 if (info->activeNS[pos].namespace->prefix[0] == '\0' 1231 && info->activeNS[pos].namespace->uri[0] == '\0' 1232 && tagPrefix[0] == '\0') { 1233 /* xml-names rec. 5.2: "The default namespace can be 1234 set to the empty string. This has the same effect, 1235 within the scope of the declaration, of there being 1236 no default namespace." */ 1237 goto elemNSfound; 1238 } 1239 node->namespace = info->activeNS[pos].namespace->index; 1240 DBG(fprintf(stderr, "tag='%s' uri='%s' \n", 1241 node->nodeName, 1242 info->activeNS[pos].namespace->uri); 1243 ) 1244 goto elemNSfound; 1245 } 1246 } 1247 if (tagPrefix[0] != '\0') { 1248 if (strcmp (tagPrefix, "xml")==0) { 1249 node->namespace = info->document->rootNode->firstAttr->namespace; 1250 } else { 1251 /* Since where here, this means, the element has a 1252 up to now not declared namespace prefix. We probably 1253 should return this as an error, shouldn't we?*/ 1254 } 1255 } 1256 elemNSfound: 1257#endif 1258 1259 /*-------------------------------------------------------------- 1260 | add the attribute nodes 1261 | 1262 \-------------------------------------------------------------*/ 1263 if ((idatt = XML_GetIdAttributeIndex (info->parser)) != -1) { 1264 if (!info->document->ids) { 1265 info->document->ids = MALLOC (sizeof (Tcl_HashTable)); 1266 Tcl_InitHashTable (info->document->ids, TCL_STRING_KEYS); 1267 } 1268 h = Tcl_CreateHashEntry (info->document->ids, 1269 atts[idatt+1], 1270 &hnew); 1271 /* if hnew isn't 1 this is a validation error. Hm, no clear way 1272 to report this. And more, xslt and xpath can process not 1273 valid XML, the spec mentioned this even within the context 1274 of id(). If some elements share the same ID, the first in 1275 document order should be used. Doing it this way, this is 1276 guaranteed for unchanged DOM trees. There are problems, if 1277 the DOM tree is changed, befor using id() */ 1278 if (hnew) { 1279 Tcl_SetHashValue (h, node); 1280 } 1281 idAttPtr = atts + idatt; 1282 } else { 1283 idAttPtr = NULL; 1284 } 1285 /* lastAttr already set right, either to NULL above, or to the last 1286 NS attribute */ 1287 for (atPtr = atts; atPtr[0] && atPtr[1]; atPtr += 2) { 1288 1289#ifdef TDOM_NS 1290 if (strncmp(atPtr[0], "xmlns", 5) == 0) { 1291 continue; 1292 } 1293#endif 1294 h = Tcl_CreateHashEntry(&HASHTAB(info->document, tdom_attrNames), 1295 atPtr[0], &hnew); 1296 attrnode = (domAttrNode*) domAlloc(sizeof(domAttrNode)); 1297 memset(attrnode, 0, sizeof(domAttrNode)); 1298 attrnode->nodeType = ATTRIBUTE_NODE; 1299 if (atPtr == idAttPtr) { 1300 attrnode->nodeFlags |= IS_ID_ATTRIBUTE; 1301 } else { 1302 attrnode->nodeFlags = 0; 1303 } 1304 attrnode->namespace = 0; 1305 attrnode->nodeName = (char *)&(h->key); 1306 attrnode->parentNode = node; 1307 len = strlen(atPtr[1]); 1308 if (TclOnly8Bits && info->encoding_8bit) { 1309 tdom_Utf8to8Bit(info->encoding_8bit, atPtr[1], &len); 1310 } 1311 attrnode->valueLength = len; 1312 attrnode->nodeValue = (char*)MALLOC(len+1); 1313 strcpy(attrnode->nodeValue, (char *)atPtr[1]); 1314 1315 if (node->firstAttr) { 1316 lastAttr->nextSibling = attrnode; 1317 } else { 1318 node->firstAttr = attrnode; 1319 } 1320 lastAttr = attrnode; 1321 1322#ifdef TDOM_NS 1323 /*---------------------------------------------------------- 1324 | look for attribute namespace 1325 \---------------------------------------------------------*/ 1326 domSplitQName (attrnode->nodeName, prefix, &localname); 1327 if (prefix[0] != '\0') { 1328 for (pos = info->activeNSpos; pos >= 0; pos--) { 1329 if ( ((prefix[0] == '\0') && (info->activeNS[pos].namespace->prefix[0] == '\0')) 1330 || ((prefix[0] != '\0') && (info->activeNS[pos].namespace->prefix[0] != '\0') 1331 && (strcmp(prefix, info->activeNS[pos].namespace->prefix) == 0)) 1332 ) { 1333 attrnode->namespace = info->activeNS[pos].namespace->index; 1334 DBG(fprintf(stderr, "attr='%s' uri='%s' \n", 1335 attrnode->nodeName, 1336 info->activeNS[pos].namespace->uri); 1337 ) 1338 goto attrNSfound; 1339 } 1340 } 1341 if (strcmp (prefix, "xml")==0) { 1342 attrnode->namespace = 1343 info->document->rootNode->firstAttr->namespace; 1344 } else { 1345 /* Since where here, this means, the attribute has a 1346 up to now not declared namespace prefix. We probably 1347 should return this as an error, shouldn't we?*/ 1348 } 1349 attrNSfound: 1350 ; 1351 } 1352#endif 1353 } 1354 1355 info->depth++; 1356} 1357 1358/*--------------------------------------------------------------------------- 1359| endElement 1360| 1361\--------------------------------------------------------------------------*/ 1362static void 1363endElement ( 1364 void *userData, 1365 const char *name 1366) 1367{ 1368 domReadInfo *info = userData; 1369 1370 DispatchPCDATA (info); 1371 1372 info->depth--; 1373#ifdef TDOM_NS 1374 /* pop active namespaces */ 1375 while ( (info->activeNSpos >= 0) && 1376 (info->activeNS[info->activeNSpos].depth == info->depth) ) 1377 { 1378 info->activeNSpos--; 1379 } 1380#endif 1381 1382 if (info->depth != -1) { 1383 info->currentNode = info->currentNode->parentNode; 1384 } else { 1385 info->currentNode = NULL; 1386 } 1387 1388 if (info->depth) { 1389 if (info->baseURIstack[info->baseURIstackPos].depth == info->depth) { 1390 info->baseURIstackPos--; 1391 } 1392 } 1393} 1394 1395/*--------------------------------------------------------------------------- 1396| characterDataHandler 1397| 1398\--------------------------------------------------------------------------*/ 1399static void 1400characterDataHandler ( 1401 void *userData, 1402 const char *s, 1403 int len 1404) 1405{ 1406 domReadInfo *info = userData; 1407 1408 Tcl_DStringAppend (info->cdata, s, len); 1409 return; 1410 1411} 1412 1413/*--------------------------------------------------------------------------- 1414| DispatchPCDATA 1415| 1416\--------------------------------------------------------------------------*/ 1417static void 1418DispatchPCDATA ( 1419 domReadInfo *info 1420 ) 1421{ 1422 domTextNode *node; 1423 domNode *parentNode; 1424 domLineColumn *lc; 1425 Tcl_HashEntry *h; 1426 char *s; 1427 int len, hnew; 1428 1429 s = Tcl_DStringValue (info->cdata); 1430 len = Tcl_DStringLength (info->cdata); 1431 if (!len) return; 1432 1433 if (TclOnly8Bits && info->encoding_8bit) { 1434 tdom_Utf8to8Bit( info->encoding_8bit, s, &len); 1435 } 1436 parentNode = info->currentNode; 1437 if (!parentNode) return; 1438 1439 if ( parentNode->lastChild 1440 && parentNode->lastChild->nodeType == TEXT_NODE) { 1441 1442 /* normalize text node, i.e. there are no adjacent text nodes */ 1443 node = (domTextNode*)parentNode->lastChild; 1444 node->nodeValue = REALLOC(node->nodeValue, node->valueLength + len); 1445 memmove(node->nodeValue + node->valueLength, s, len); 1446 node->valueLength += len; 1447 1448 } else { 1449 1450 if (info->ignoreWhiteSpaces) { 1451 char *pc; 1452 int i, only_whites; 1453 1454 only_whites = 1; 1455 for (i=0, pc = s; i < len; i++, pc++) { 1456 if ( (*pc != ' ') && 1457 (*pc != '\t') && 1458 (*pc != '\n') && 1459 (*pc != '\r') ) { 1460 only_whites = 0; 1461 break; 1462 } 1463 } 1464 if (only_whites) { 1465 Tcl_DStringSetLength (info->cdata, 0); 1466 return; 1467 } 1468 } 1469 1470 if (info->storeLineColumn) { 1471 node = (domTextNode*) domAlloc(sizeof(domTextNode) 1472 + sizeof(domLineColumn)); 1473 } else { 1474 node = (domTextNode*) domAlloc(sizeof(domTextNode)); 1475 } 1476 memset(node, 0, sizeof(domTextNode)); 1477 node->nodeType = TEXT_NODE; 1478 node->nodeFlags = 0; 1479 node->namespace = 0; 1480 node->nodeNumber = NODE_NO(info->document); 1481 node->valueLength = len; 1482 node->nodeValue = (char*)MALLOC(len); 1483 memmove(node->nodeValue, s, len); 1484 1485 node->ownerDocument = info->document; 1486 node->parentNode = parentNode; 1487 if (parentNode->nodeType == ELEMENT_NODE) { 1488 if (parentNode->firstChild) { 1489 parentNode->lastChild->nextSibling = (domNode*)node; 1490 node->previousSibling = parentNode->lastChild; 1491 } else { 1492 parentNode->firstChild = (domNode*)node; 1493 } 1494 parentNode->lastChild = (domNode*)node; 1495 } 1496 1497 if (info->baseURIstack[info->baseURIstackPos].baseURI 1498 != XML_GetBase (info->parser)) { 1499 h = Tcl_CreateHashEntry (info->document->baseURIs, 1500 (char*) node, 1501 &hnew); 1502 Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser))); 1503 node->nodeFlags |= HAS_BASEURI; 1504 } 1505 1506 if (info->storeLineColumn) { 1507 lc = (domLineColumn*) ( ((char*)node) + sizeof(domTextNode) ); 1508 node->nodeFlags |= HAS_LINE_COLUMN; 1509 lc->line = XML_GetCurrentLineNumber(info->parser); 1510 lc->column = XML_GetCurrentColumnNumber(info->parser); 1511 } 1512 } 1513 Tcl_DStringSetLength (info->cdata, 0); 1514} 1515 1516 1517/*--------------------------------------------------------------------------- 1518| commentHandler 1519| 1520\--------------------------------------------------------------------------*/ 1521static void 1522commentHandler ( 1523 void *userData, 1524 const char *s 1525) 1526{ 1527 domReadInfo *info = userData; 1528 domTextNode *node; 1529 domNode *parentNode; 1530 domLineColumn *lc; 1531 int len, hnew; 1532 Tcl_HashEntry *h; 1533 1534 if (info->insideDTD) { 1535 DBG(fprintf (stderr, "commentHandler: insideDTD, skipping\n");) 1536 return; 1537 } 1538 1539 DispatchPCDATA (info); 1540 1541 len = strlen(s); 1542 if (TclOnly8Bits && info->encoding_8bit) { 1543 tdom_Utf8to8Bit(info->encoding_8bit, s, &len); 1544 } 1545 parentNode = info->currentNode; 1546 1547 if (info->storeLineColumn) { 1548 node = (domTextNode*) domAlloc(sizeof(domTextNode) 1549 + sizeof(domLineColumn)); 1550 } else { 1551 node = (domTextNode*) domAlloc(sizeof(domTextNode)); 1552 } 1553 memset(node, 0, sizeof(domTextNode)); 1554 node->nodeType = COMMENT_NODE; 1555 node->nodeFlags = 0; 1556 node->namespace = 0; 1557 node->nodeNumber = NODE_NO(info->document); 1558 node->valueLength = len; 1559 node->nodeValue = (char*)MALLOC(len); 1560 memmove(node->nodeValue, s, len); 1561 1562 node->ownerDocument = info->document; 1563 node->parentNode = parentNode; 1564 if (parentNode == NULL) { 1565 if (info->document->rootNode->lastChild) { 1566 info->document->rootNode->lastChild->nextSibling = (domNode*)node; 1567 node->previousSibling = info->document->rootNode->lastChild; 1568 } else { 1569 info->document->rootNode->firstChild = (domNode*)node; 1570 } 1571 info->document->rootNode->lastChild = (domNode*)node; 1572 } else if(parentNode->nodeType == ELEMENT_NODE) { 1573 if (parentNode->firstChild) { 1574 parentNode->lastChild->nextSibling = (domNode*)node; 1575 node->previousSibling = parentNode->lastChild; 1576 parentNode->lastChild = (domNode*)node; 1577 } else { 1578 parentNode->firstChild = parentNode->lastChild = (domNode*)node; 1579 } 1580 } 1581 1582 if (info->baseURIstack[info->baseURIstackPos].baseURI 1583 != XML_GetBase (info->parser)) { 1584 h = Tcl_CreateHashEntry (info->document->baseURIs, 1585 (char*) node, 1586 &hnew); 1587 Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser))); 1588 node->nodeFlags |= HAS_BASEURI; 1589 } 1590 1591 if (info->storeLineColumn) { 1592 lc = (domLineColumn*) ( ((char*)node) + sizeof(domTextNode) ); 1593 node->nodeFlags |= HAS_LINE_COLUMN; 1594 lc->line = XML_GetCurrentLineNumber(info->parser); 1595 lc->column = XML_GetCurrentColumnNumber(info->parser); 1596 } 1597} 1598 1599 1600/*--------------------------------------------------------------------------- 1601| processingInstructionHandler 1602| 1603\--------------------------------------------------------------------------*/ 1604static void 1605processingInstructionHandler( 1606 void *userData, 1607 const char *target, 1608 const char *data 1609) 1610{ 1611 domProcessingInstructionNode *node; 1612 domReadInfo *info = userData; 1613 domNode *parentNode; 1614 domLineColumn *lc; 1615 int len,hnew; 1616 Tcl_HashEntry *h; 1617 1618 if (info->insideDTD) { 1619 DBG(fprintf (stderr, 1620 "processingInstructionHandler: insideDTD, skipping\n");) 1621 return; 1622 } 1623 1624 DispatchPCDATA (info); 1625 1626 parentNode = info->currentNode; 1627 1628 if (info->storeLineColumn) { 1629 node = (domProcessingInstructionNode*) 1630 domAlloc(sizeof(domProcessingInstructionNode) 1631 + sizeof(domLineColumn)); 1632 } else { 1633 node = (domProcessingInstructionNode*) 1634 domAlloc(sizeof(domProcessingInstructionNode)); 1635 } 1636 memset(node, 0, sizeof(domProcessingInstructionNode)); 1637 node->nodeType = PROCESSING_INSTRUCTION_NODE; 1638 node->nodeFlags = 0; 1639 node->namespace = 0; 1640 node->nodeNumber = NODE_NO(info->document); 1641 1642 if (info->baseURIstack[info->baseURIstackPos].baseURI 1643 != XML_GetBase (info->parser)) { 1644 h = Tcl_CreateHashEntry (info->document->baseURIs, 1645 (char*) node, 1646 &hnew); 1647 Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser))); 1648 node->nodeFlags |= HAS_BASEURI; 1649 } 1650 1651 len = strlen(target); 1652 if (TclOnly8Bits && info->encoding_8bit) { 1653 tdom_Utf8to8Bit(info->encoding_8bit, target, &len); 1654 } 1655 node->targetLength = len; 1656 node->targetValue = (char*)MALLOC(len); 1657 memmove(node->targetValue, target, len); 1658 1659 len = strlen(data); 1660 if (TclOnly8Bits && info->encoding_8bit) { 1661 tdom_Utf8to8Bit(info->encoding_8bit, data, &len); 1662 } 1663 node->dataLength = len; 1664 node->dataValue = (char*)MALLOC(len); 1665 memmove(node->dataValue, data, len); 1666 1667 node->ownerDocument = info->document; 1668 node->parentNode = parentNode; 1669 if (parentNode == NULL) { 1670 if (info->document->rootNode->lastChild) { 1671 info->document->rootNode->lastChild->nextSibling = (domNode*)node; 1672 node->previousSibling = info->document->rootNode->lastChild; 1673 } else { 1674 info->document->rootNode->firstChild = (domNode*)node; 1675 } 1676 info->document->rootNode->lastChild = (domNode*)node; 1677 } else if(parentNode->nodeType == ELEMENT_NODE) { 1678 if (parentNode->firstChild) { 1679 parentNode->lastChild->nextSibling = (domNode*)node; 1680 node->previousSibling = parentNode->lastChild; 1681 parentNode->lastChild = (domNode*)node; 1682 } else { 1683 parentNode->firstChild = parentNode->lastChild = (domNode*)node; 1684 } 1685 } 1686 if (info->storeLineColumn) { 1687 lc = (domLineColumn*)(((char*)node)+sizeof(domProcessingInstructionNode)); 1688 node->nodeFlags |= HAS_LINE_COLUMN; 1689 lc->line = XML_GetCurrentLineNumber(info->parser); 1690 lc->column = XML_GetCurrentColumnNumber(info->parser); 1691 } 1692} 1693 1694/*--------------------------------------------------------------------------- 1695| entityDeclHandler 1696| 1697\--------------------------------------------------------------------------*/ 1698static void 1699entityDeclHandler ( 1700 void *userData, 1701 const char *entityName, 1702 int is_parameter_entity, 1703 const char *value, 1704 int value_length, 1705 const char *base, 1706 const char *systemId, 1707 const char *publicId, 1708 const char *notationName 1709) 1710{ 1711 domReadInfo *info = (domReadInfo *) userData; 1712 Tcl_HashEntry *entryPtr; 1713 int hnew; 1714 1715 if (notationName) { 1716 if (!info->document->unparsedEntities) { 1717 info->document->unparsedEntities = MALLOC (sizeof (Tcl_HashTable)); 1718 Tcl_InitHashTable (info->document->unparsedEntities, 1719 TCL_STRING_KEYS); 1720 } 1721 entryPtr = Tcl_CreateHashEntry (info->document->unparsedEntities, 1722 entityName, &hnew); 1723 if (hnew) { 1724 Tcl_SetHashValue (entryPtr, tdomstrdup (systemId)); 1725 } 1726 } 1727} 1728 1729/*--------------------------------------------------------------------------- 1730| externalEntityRefHandler 1731| 1732\--------------------------------------------------------------------------*/ 1733static int 1734externalEntityRefHandler ( 1735 XML_Parser parser, 1736 CONST char *openEntityNames, 1737 CONST char *base, 1738 CONST char *systemId, 1739 CONST char *publicId 1740) 1741{ 1742 domReadInfo *info = (domReadInfo *) XML_GetUserData (parser); 1743 1744 Tcl_Obj *cmdPtr, *resultObj, *resultTypeObj, *extbaseObj, *xmlstringObj; 1745 Tcl_Obj *channelIdObj; 1746 int result, mode, done, byteIndex, i; 1747 size_t len; 1748 int tclLen; 1749 XML_Parser extparser, oldparser = NULL; 1750 char buf[4096], *resultType, *extbase, *xmlstring, *channelId, s[50]; 1751 Tcl_Channel chan = (Tcl_Channel) NULL; 1752 1753 if (info->document->extResolver == NULL) { 1754 Tcl_AppendResult (info->interp, "Can't read external entity \"", 1755 systemId, "\": No -externalentitycommand given", 1756 NULL); 1757 return 0; 1758 } 1759 1760 DispatchPCDATA (info); 1761 1762 /* 1763 * Take a copy of the callback script so that arguments may be appended. 1764 */ 1765 cmdPtr = Tcl_NewStringObj(info->document->extResolver, -1); 1766 Tcl_IncrRefCount(cmdPtr); 1767 1768 if (base) { 1769 Tcl_ListObjAppendElement(info->interp, cmdPtr, 1770 Tcl_NewStringObj(base, strlen(base))); 1771 } else { 1772 Tcl_ListObjAppendElement(info->interp, cmdPtr, 1773 Tcl_NewObj()); 1774 } 1775 1776 /* For a document with doctype declaration, the systemId is always 1777 != NULL. But if the document doesn't have a doctype declaration 1778 and the user uses -useForeignDTD 1, the externalEntityRefHandler 1779 will be called with a systemId (and publicId and openEntityNames) 1780 == NULL. */ 1781 if (systemId) { 1782 Tcl_ListObjAppendElement(info->interp, cmdPtr, 1783 Tcl_NewStringObj(systemId, strlen(systemId))); 1784 } else { 1785 Tcl_ListObjAppendElement(info->interp, cmdPtr, 1786 Tcl_NewObj()); 1787 } 1788 1789 if (publicId) { 1790 Tcl_ListObjAppendElement(info->interp, cmdPtr, 1791 Tcl_NewStringObj(publicId, strlen(publicId))); 1792 } else { 1793 Tcl_ListObjAppendElement(info->interp, cmdPtr, 1794 Tcl_NewObj()); 1795 } 1796 1797 1798#if TclOnly8Bits 1799 result = Tcl_GlobalEvalObj(info->interp, cmdPtr); 1800#else 1801 result = Tcl_EvalObjEx (info->interp, cmdPtr, 1802 TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL); 1803#endif 1804 1805 Tcl_DecrRefCount(cmdPtr); 1806 1807 if (result != TCL_OK) { 1808 return 0; 1809 } 1810 1811 extparser = XML_ExternalEntityParserCreate (parser, openEntityNames, 0); 1812 1813 resultObj = Tcl_GetObjResult (info->interp); 1814 Tcl_IncrRefCount (resultObj); 1815 1816 result = Tcl_ListObjLength (info->interp, resultObj, &tclLen); 1817 if ((result != TCL_OK) || (tclLen != 3)) { 1818 goto wrongScriptResult; 1819 } 1820 result = Tcl_ListObjIndex (info->interp, resultObj, 0, &resultTypeObj); 1821 if (result != TCL_OK) { 1822 goto wrongScriptResult; 1823 } 1824 resultType = Tcl_GetString(resultTypeObj); 1825 1826 if (strcmp (resultType, "string") == 0) { 1827 result = Tcl_ListObjIndex (info->interp, resultObj, 2, &xmlstringObj); 1828 xmlstring = Tcl_GetString(xmlstringObj); 1829 len = strlen (xmlstring); 1830 chan = NULL; 1831 } else if (strcmp (resultType, "channel") == 0) { 1832 xmlstring = NULL; 1833 len = 0; 1834 result = Tcl_ListObjIndex (info->interp, resultObj, 2, &channelIdObj); 1835 channelId = Tcl_GetString(channelIdObj); 1836 chan = Tcl_GetChannel (info->interp, channelId, &mode); 1837 if (chan == (Tcl_Channel) NULL) { 1838 goto wrongScriptResult; 1839 } 1840 if ((mode & TCL_READABLE) == 0) { 1841 return 0; 1842 } 1843 } else if (strcmp (resultType, "filename") == 0) { 1844 /* result type "filename" not yet implemented */ 1845 return 0; 1846 } else { 1847 goto wrongScriptResult; 1848 } 1849 1850 result = Tcl_ListObjIndex (info->interp, resultObj, 1, &extbaseObj); 1851 if (result != TCL_OK) { 1852 goto wrongScriptResult; 1853 } 1854 extbase = Tcl_GetString(extbaseObj); 1855 1856 /* TODO: what to do, if this document was already parsed before ? */ 1857 1858 if (!extparser) { 1859 Tcl_DecrRefCount (resultObj); 1860 Tcl_SetResult (info->interp, 1861 "unable to create expat external entity parser", 1862 NULL); 1863 return 0; 1864 } 1865 1866 oldparser = info->parser; 1867 info->parser = extparser; 1868 XML_SetBase (extparser, extbase); 1869 1870 if (chan == NULL) { 1871 if (!XML_Parse(extparser, xmlstring, strlen (xmlstring), 1)) { 1872 Tcl_ResetResult (info->interp); 1873 sprintf(s, "%ld", XML_GetCurrentLineNumber(extparser)); 1874 Tcl_AppendResult(info->interp, "error \"", 1875 XML_ErrorString(XML_GetErrorCode(extparser)), 1876 "\" in entity \"", systemId, 1877 "\" at line ", s, " character ", NULL); 1878 sprintf(s, "%ld", XML_GetCurrentColumnNumber(extparser)); 1879 Tcl_AppendResult(info->interp, s, NULL); 1880 byteIndex = XML_GetCurrentByteIndex(extparser); 1881 if (byteIndex != -1) { 1882 Tcl_AppendResult(info->interp, "\n\"", NULL); 1883 s[1] = '\0'; 1884 for (i=-20; i < 40; i++) { 1885 if ((byteIndex+i)>=0) { 1886 if (xmlstring[byteIndex+i]) { 1887 s[0] = xmlstring[byteIndex+i]; 1888 Tcl_AppendResult(info->interp, s, NULL); 1889 if (i==0) { 1890 Tcl_AppendResult(info->interp, 1891 " <--Error-- ", NULL); 1892 } 1893 } else { 1894 break; 1895 } 1896 } 1897 } 1898 Tcl_AppendResult(info->interp, "\"",NULL); 1899 } 1900 Tcl_DecrRefCount (resultObj); 1901 XML_ParserFree (extparser); 1902 info->parser = oldparser; 1903 return 0; 1904 } 1905 } else { 1906 do { 1907 len = Tcl_Read (chan, buf, sizeof(buf)); 1908 done = len < sizeof(buf); 1909 if (!XML_Parse (extparser, buf, len, done)) { 1910 Tcl_ResetResult (info->interp); 1911 sprintf(s, "%ld", XML_GetCurrentLineNumber(extparser)); 1912 Tcl_AppendResult(info->interp, "error \"", 1913 XML_ErrorString(XML_GetErrorCode(extparser)), 1914 "\" in entity \"", systemId, 1915 "\" at line ", s, " character ", NULL); 1916 sprintf(s, "%ld", XML_GetCurrentColumnNumber(extparser)); 1917 Tcl_AppendResult(info->interp, s, NULL); 1918 Tcl_DecrRefCount (resultObj); 1919 XML_ParserFree (extparser); 1920 info->parser = oldparser; 1921 return 0; 1922 } 1923 } while (!done); 1924 } 1925 1926 DispatchPCDATA (info); 1927 1928 XML_ParserFree (extparser); 1929 info->parser = oldparser; 1930 1931 Tcl_DecrRefCount (resultObj); 1932 Tcl_ResetResult (info->interp); 1933 return 1; 1934 1935 wrongScriptResult: 1936 Tcl_DecrRefCount (resultObj); 1937 Tcl_ResetResult (info->interp); 1938 XML_ParserFree (extparser); 1939 if (oldparser) { 1940 info->parser = oldparser; 1941 } 1942 Tcl_AppendResult (info->interp, "The -externalentitycommand script " 1943 "has to return a Tcl list with 3 elements.\n" 1944 "Syntax: {string|channel|filename <baseurl> <data>}\n", 1945 NULL); 1946 return 0; 1947} 1948 1949/*--------------------------------------------------------------------------- 1950| startDoctypeDeclHandler 1951| 1952\--------------------------------------------------------------------------*/ 1953static void 1954startDoctypeDeclHandler ( 1955 void *userData, 1956 const char *doctypeName, 1957 const char *sysid, 1958 const char *pubid, 1959 int has_internal_subset 1960) 1961{ 1962 domReadInfo *info = (domReadInfo *) userData; 1963 1964 if (pubid) { 1965 info->document->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo)); 1966 memset (info->document->doctype, 0, sizeof (domDocInfo)); 1967 info->document->doctype->systemId = tdomstrdup (sysid); 1968 info->document->doctype->publicId = tdomstrdup (pubid); 1969 } else if (sysid) { 1970 info->document->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo)); 1971 memset (info->document->doctype, 0, sizeof (domDocInfo)); 1972 info->document->doctype->systemId = tdomstrdup (sysid); 1973 } 1974 info->insideDTD = 1; 1975} 1976 1977/*--------------------------------------------------------------------------- 1978| endDoctypeDeclHandler 1979| 1980\--------------------------------------------------------------------------*/ 1981static void 1982endDoctypeDeclHandler ( 1983 void *userData 1984) 1985{ 1986 domReadInfo *info = (domReadInfo *) userData; 1987 1988 info->insideDTD = 0; 1989} 1990 1991/*--------------------------------------------------------------------------- 1992| domReadDocument 1993| 1994\--------------------------------------------------------------------------*/ 1995domDocument * 1996domReadDocument ( 1997 XML_Parser parser, 1998 char *xml, 1999 int length, 2000 int ignoreWhiteSpaces, 2001 TEncoding *encoding_8bit, 2002 int storeLineColumn, 2003 int feedbackAfter, 2004 Tcl_Channel channel, 2005 const char *baseurl, 2006 char *extResolver, 2007 int useForeignDTD, 2008 int paramEntityParsing, 2009 Tcl_Interp *interp 2010) 2011{ 2012 int done, tclLen; 2013 size_t len; 2014 domReadInfo info; 2015 char buf[8192]; 2016#if !TclOnly8Bits 2017 Tcl_Obj *bufObj; 2018 Tcl_DString dStr; 2019 int useBinary; 2020 char *str; 2021#endif 2022 domDocument *doc = domCreateDoc(baseurl, storeLineColumn); 2023 2024 doc->extResolver = extResolver; 2025 2026 info.parser = parser; 2027 info.document = doc; 2028 info.currentNode = NULL; 2029 info.depth = 0; 2030 info.ignoreWhiteSpaces = ignoreWhiteSpaces; 2031 info.cdata = (Tcl_DString*) MALLOC (sizeof (Tcl_DString)); 2032 Tcl_DStringInit (info.cdata); 2033 info.encoding_8bit = encoding_8bit; 2034 info.storeLineColumn = storeLineColumn; 2035 info.feedbackAfter = feedbackAfter; 2036 info.lastFeedbackPosition = 0; 2037 info.interp = interp; 2038 info.activeNSpos = -1; 2039 info.activeNSsize = 8; 2040 info.activeNS = (domActiveNS*) MALLOC (sizeof(domActiveNS) 2041 * info.activeNSsize); 2042 info.baseURIstackPos = 0; 2043 info.baseURIstackSize = INITIAL_BASEURISTACK_SIZE; 2044 info.baseURIstack = (domActiveBaseURI*) 2045 MALLOC (sizeof(domActiveBaseURI) * info.baseURIstackSize); 2046 info.insideDTD = 0; 2047 2048 XML_SetUserData(parser, &info); 2049 XML_SetBase (parser, baseurl); 2050 /* We must use XML_GetBase(), because XML_SetBase copies the baseURI, 2051 and we want to compare the pointers */ 2052 info.baseURIstack[0].baseURI = XML_GetBase (parser); 2053 info.baseURIstack[0].depth = 0; 2054 XML_UseForeignDTD (parser, (unsigned char) useForeignDTD); 2055 XML_SetElementHandler(parser, startElement, endElement); 2056 XML_SetCharacterDataHandler(parser, characterDataHandler); 2057 XML_SetCommentHandler(parser, commentHandler); 2058 XML_SetProcessingInstructionHandler(parser, processingInstructionHandler); 2059 XML_SetEntityDeclHandler (parser, entityDeclHandler); 2060 if (extResolver) { 2061 XML_SetExternalEntityRefHandler (parser, externalEntityRefHandler); 2062 } 2063 XML_SetParamEntityParsing (parser, 2064 (enum XML_ParamEntityParsing) paramEntityParsing); 2065 XML_SetDoctypeDeclHandler (parser, startDoctypeDeclHandler, 2066 endDoctypeDeclHandler); 2067 2068 if (channel == NULL) { 2069 if (!XML_Parse(parser, xml, length, 1)) { 2070 FREE ( info.activeNS ); 2071 FREE ( info.baseURIstack ); 2072 Tcl_DStringFree (info.cdata); 2073 FREE ( info.cdata); 2074 domFreeDocument (doc, NULL, NULL); 2075 return NULL; 2076 } 2077 } else { 2078#if !TclOnly8Bits 2079 Tcl_DStringInit (&dStr); 2080 if (Tcl_GetChannelOption (interp, channel, "-encoding", &dStr) != TCL_OK) { 2081 FREE ( (char*) info.activeNS ); 2082 FREE ( info.baseURIstack ); 2083 Tcl_DStringFree (info.cdata); 2084 FREE ( info.cdata); 2085 domFreeDocument (doc, NULL, NULL); 2086 return NULL; 2087 } 2088 if (strcmp (Tcl_DStringValue (&dStr), "identity")==0 ) useBinary = 1; 2089 else useBinary = 0; 2090 Tcl_DStringFree (&dStr); 2091 if (useBinary) { 2092 do { 2093 len = Tcl_Read (channel, buf, sizeof(buf)); 2094 done = len < sizeof(buf); 2095 if (!XML_Parse (parser, buf, len, done)) { 2096 FREE ( info.activeNS ); 2097 FREE ( info.baseURIstack ); 2098 Tcl_DStringFree (info.cdata); 2099 FREE ( info.cdata); 2100 domFreeDocument (doc, NULL, NULL); 2101 return NULL; 2102 } 2103 } while (!done); 2104 } else { 2105 bufObj = Tcl_NewObj(); 2106 Tcl_SetObjLength (bufObj, 6144); 2107 do { 2108 len = Tcl_ReadChars (channel, bufObj, 1024, 0); 2109 done = (len < 1024); 2110 str = Tcl_GetStringFromObj(bufObj, &tclLen); 2111 if (!XML_Parse (parser, str, tclLen, done)) { 2112 FREE ( info.activeNS ); 2113 FREE ( info.baseURIstack ); 2114 Tcl_DStringFree (info.cdata); 2115 FREE ( info.cdata); 2116 domFreeDocument (doc, NULL, NULL); 2117 Tcl_DecrRefCount (bufObj); 2118 return NULL; 2119 } 2120 } while (!done); 2121 Tcl_DecrRefCount (bufObj); 2122 } 2123#else 2124 do { 2125 len = Tcl_Read (channel, buf, sizeof(buf)); 2126 done = len < sizeof(buf); 2127 if (!XML_Parse (parser, buf, len, done)) { 2128 FREE ( info.activeNS ); 2129 FREE ( info.baseURIstack ); 2130 domFreeDocument (doc, NULL, NULL); 2131 Tcl_DStringFree (info.cdata); 2132 FREE ( info.cdata); 2133 return NULL; 2134 } 2135 } while (!done); 2136#endif 2137 } 2138 FREE ( info.activeNS ); 2139 FREE ( info.baseURIstack ); 2140 Tcl_DStringFree (info.cdata); 2141 FREE ( info.cdata); 2142 2143 domSetDocumentElement (doc); 2144 2145 return doc; 2146} 2147 2148 2149#endif /* ifndef TDOM_NO_EXPAT */ 2150 2151 2152 2153/*--------------------------------------------------------------------------- 2154| domException2String 2155| 2156\--------------------------------------------------------------------------*/ 2157const char * 2158domException2String ( 2159 domException exception 2160) 2161{ 2162 return domException2StringTable[exception]; 2163} 2164 2165 2166/*--------------------------------------------------------------------------- 2167| domGetLineColumn 2168| 2169\--------------------------------------------------------------------------*/ 2170int 2171domGetLineColumn ( 2172 domNode *node, 2173 int *line, 2174 int *column 2175) 2176{ 2177 char *v; 2178 domLineColumn *lc; 2179 2180 *line = -1; 2181 *column = -1; 2182 2183 if (node->nodeFlags & HAS_LINE_COLUMN) { 2184 v = (char*)node; 2185 switch (node->nodeType) { 2186 case ELEMENT_NODE: 2187 v = v + sizeof(domNode); 2188 break; 2189 2190 case TEXT_NODE: 2191 case CDATA_SECTION_NODE: 2192 case COMMENT_NODE: 2193 v = v + sizeof(domTextNode); 2194 break; 2195 2196 case PROCESSING_INSTRUCTION_NODE: 2197 v = v + sizeof(domProcessingInstructionNode); 2198 break; 2199 2200 default: 2201 return -1; 2202 } 2203 lc = (domLineColumn *)v; 2204 *line = lc->line; 2205 *column = lc->column; 2206 return 0; 2207 } else { 2208 return -1; 2209 } 2210} 2211 2212#ifdef TDOM_NS 2213domAttrNode * 2214domCreateXMLNamespaceNode ( 2215 domNode *parent 2216) 2217{ 2218 Tcl_HashEntry *h; 2219 int hnew; 2220 domAttrNode *attr; 2221 domNS *ns; 2222 2223 attr = (domAttrNode *) domAlloc (sizeof (domAttrNode)); 2224 memset (attr, 0, sizeof (domAttrNode)); 2225 h = Tcl_CreateHashEntry(&HASHTAB(parent->ownerDocument,tdom_attrNames), 2226 "xmlns:xml", &hnew); 2227 ns = domNewNamespace (parent->ownerDocument, "xml", XML_NAMESPACE); 2228 attr->nodeType = ATTRIBUTE_NODE; 2229 attr->nodeFlags = IS_NS_NODE; 2230 attr->namespace = ns->index; 2231 attr->nodeName = (char *)&(h->key); 2232 attr->parentNode = parent; 2233 attr->valueLength = strlen (XML_NAMESPACE); 2234 attr->nodeValue = tdomstrdup (XML_NAMESPACE); 2235 return attr; 2236} 2237#endif /* TDOM_NS */ 2238 2239 2240/* 2241 *---------------------------------------------------------------------- 2242 * 2243 * domCreateDoc -- 2244 * 2245 * This procedure allocates a new domDocument, initialize it and 2246 * creates its rootNode (with initialization). 2247 * 2248 * Results: 2249 * The domDocument node: 2250 * 2251 * Side effects: 2252 * Allocates memory for the returned domDocument and its 2253 * rootNode. 2254 * 2255 *---------------------------------------------------------------------- 2256 */ 2257 2258domDocument * 2259domCreateDoc ( 2260 const char * baseURI, 2261 int storeLineColumn 2262 ) 2263{ 2264 Tcl_HashEntry *h; 2265 int hnew; 2266 domNode *rootNode; 2267 domDocument *doc; 2268 domLineColumn *lc; 2269 2270 doc = (domDocument *) MALLOC (sizeof (domDocument)); 2271 memset(doc, 0, sizeof(domDocument)); 2272 doc->nodeType = DOCUMENT_NODE; 2273 doc->documentNumber = DOC_NO(doc); 2274 doc->nsptr = -1; 2275 doc->nslen = 4; 2276 doc->namespaces = (domNS**) MALLOC (sizeof (domNS*) * doc->nslen); 2277 2278 /* We malloc and initialze the baseURIs hash table here to avoid 2279 cluttering of the code all over the place with checks. */ 2280 doc->baseURIs = MALLOC (sizeof (Tcl_HashTable)); 2281 Tcl_InitHashTable (doc->baseURIs, TCL_ONE_WORD_KEYS); 2282 2283 TDomThreaded( 2284 domLocksAttach(doc); 2285 Tcl_InitHashTable(&doc->tdom_tagNames, TCL_STRING_KEYS); 2286 Tcl_InitHashTable(&doc->tdom_attrNames, TCL_STRING_KEYS); 2287 ) 2288 2289 if (storeLineColumn) { 2290 rootNode = (domNode*) domAlloc(sizeof(domNode)+sizeof(domLineColumn)); 2291 } else { 2292 rootNode = (domNode*) domAlloc(sizeof(domNode)); 2293 } 2294 memset(rootNode, 0, sizeof(domNode)); 2295 rootNode->nodeType = ELEMENT_NODE; 2296 if (baseURI) { 2297 h = Tcl_CreateHashEntry (doc->baseURIs, (char*)rootNode, &hnew); 2298 Tcl_SetHashValue (h, tdomstrdup (baseURI)); 2299 rootNode->nodeFlags |= HAS_BASEURI; 2300 } else { 2301 rootNode->nodeFlags = 0; 2302 } 2303 rootNode->namespace = 0; 2304 h = Tcl_CreateHashEntry(&HASHTAB(doc,tdom_tagNames), "", &hnew); 2305 rootNode->nodeName = (char *)&(h->key); 2306 rootNode->nodeNumber = NODE_NO(doc); 2307 rootNode->ownerDocument = doc; 2308 rootNode->parentNode = NULL; 2309 rootNode->firstChild = rootNode->lastChild = NULL; 2310#ifdef TDOM_NS 2311 rootNode->firstAttr = domCreateXMLNamespaceNode (rootNode); 2312#endif 2313 if (storeLineColumn) { 2314 lc = (domLineColumn*) ( ((char*)rootNode) + sizeof(domNode)); 2315 rootNode->nodeFlags |= HAS_LINE_COLUMN; 2316 lc->line = 0; 2317 lc->column = 0; 2318 } 2319 doc->rootNode = rootNode; 2320 2321 return doc; 2322} 2323 2324/*--------------------------------------------------------------------------- 2325| domCreateDocument 2326| 2327\--------------------------------------------------------------------------*/ 2328domDocument * 2329domCreateDocument ( 2330 Tcl_Interp *interp, 2331 const char *uri, 2332 char *documentElementTagName 2333) 2334{ 2335 Tcl_HashEntry *h; 2336 int hnew; 2337 domNode *node; 2338 domDocument *doc; 2339 char prefix[MAX_PREFIX_LEN]; 2340 const char *localName; 2341 domNS *ns = NULL; 2342 2343 if (uri) { 2344 domSplitQName (documentElementTagName, prefix, &localName); 2345 DBG(fprintf(stderr, 2346 "rootName: -->%s<--, prefix: -->%s<--, localName: -->%s<--\n", 2347 documentElementTagName, prefix, localName);) 2348 if (prefix[0] != '\0') { 2349 if (!domIsNCNAME (prefix)) { 2350 if (interp) { 2351 Tcl_SetObjResult(interp, 2352 Tcl_NewStringObj("invalid prefix name", -1)); 2353 } 2354 return NULL; 2355 } 2356 } 2357 if (!domIsNCNAME (localName)) { 2358 if (interp) { 2359 Tcl_SetObjResult(interp, 2360 Tcl_NewStringObj("invalid local name", -1)); 2361 } 2362 return NULL; 2363 } 2364 } else { 2365 if (!domIsNAME (documentElementTagName)) { 2366 if (interp) { 2367 Tcl_SetObjResult(interp, 2368 Tcl_NewStringObj("invalid root element name", -1)); 2369 } 2370 return NULL; 2371 } 2372 } 2373 doc = domCreateDoc (NULL, 0); 2374 2375 h = Tcl_CreateHashEntry(&HASHTAB(doc, tdom_tagNames), 2376 documentElementTagName, &hnew); 2377 node = (domNode*) domAlloc(sizeof(domNode)); 2378 memset(node, 0, sizeof(domNode)); 2379 node->nodeType = ELEMENT_NODE; 2380 node->nodeFlags = 0; 2381 node->nodeNumber = NODE_NO(doc); 2382 node->ownerDocument = doc; 2383 node->nodeName = (char *)&(h->key); 2384 doc->documentElement = node; 2385 if (uri) { 2386 ns = domNewNamespace (doc, prefix, uri); 2387 node->namespace = ns->index; 2388 domAddNSToNode (node, ns); 2389 } 2390 doc->rootNode->firstChild = doc->rootNode->lastChild = doc->documentElement; 2391 2392 return doc; 2393} 2394 2395 2396/*--------------------------------------------------------------------------- 2397| domSetDocumentElement 2398| 2399\--------------------------------------------------------------------------*/ 2400void 2401domSetDocumentElement ( 2402 domDocument *doc 2403 ) 2404{ 2405 domNode *node; 2406 2407 doc->documentElement = NULL; 2408 node = doc->rootNode->firstChild; 2409 while (node) { 2410 if (node->nodeType == ELEMENT_NODE) { 2411 doc->documentElement = node; 2412 break; 2413 } 2414 node = node->nextSibling; 2415 } 2416 if (!doc->documentElement) { 2417 doc->documentElement = doc->rootNode->firstChild; 2418 } 2419} 2420 2421/*--------------------------------------------------------------------------- 2422| domFreeNode 2423| 2424\--------------------------------------------------------------------------*/ 2425void 2426domFreeNode ( 2427 domNode * node, 2428 domFreeCallback freeCB, 2429 void * clientData, 2430 int dontfree 2431) 2432{ 2433 int shared = 0; 2434 domNode *child, *ctemp; 2435 domAttrNode *atemp, *attr, *aprev; 2436 Tcl_HashEntry *entryPtr; 2437 2438 if (node == NULL) { 2439 DBG(fprintf (stderr, "null ptr in domFreeNode (dom.c) !\n");) 2440 return; 2441 } 2442 TDomThreaded ( 2443 shared = node->ownerDocument && node->ownerDocument->refCount > 1; 2444 ) 2445 2446 /*---------------------------------------------------------------- 2447 | dontfree instruct us to walk the node tree and apply the 2448 | user-supplied callback, *w/o* actually deleting nodes. 2449 | This is normally done when a thread detaches from the 2450 | shared DOM tree and wants to garbage-collect all nodecmds 2451 | in it's interpreter which attached to the tree nodes. 2452 \---------------------------------------------------------------*/ 2453 2454 if (dontfree) { 2455 shared = 1; 2456 } else { 2457 node->nodeFlags |= IS_DELETED; 2458 } 2459 2460 if (node->nodeType == ATTRIBUTE_NODE && !shared) { 2461 attr = ((domAttrNode*)node)->parentNode->firstAttr; 2462 aprev = NULL; 2463 while (attr && (attr != (domAttrNode*)node)) { 2464 aprev = attr; 2465 attr = attr->nextSibling; 2466 } 2467 if (attr) { 2468 if (aprev) { 2469 aprev->nextSibling = attr->nextSibling; 2470 } else { 2471 ((domAttrNode*)node)->parentNode->firstAttr = attr->nextSibling; 2472 } 2473 FREE (attr->nodeValue); 2474 domFree ((void*)attr); 2475 } 2476 } else if (node->nodeType == ELEMENT_NODE) { 2477 child = node->lastChild; 2478 while (child) { 2479 ctemp = child->previousSibling; 2480 if (freeCB) { 2481 freeCB(child, clientData); 2482 } 2483 domFreeNode (child, freeCB, clientData, dontfree); 2484 child = ctemp; 2485 } 2486 if (shared) { 2487 return; 2488 } 2489 attr = node->firstAttr; 2490 while (attr) { 2491 atemp = attr; 2492 attr = attr->nextSibling; 2493 FREE (atemp->nodeValue); 2494 domFree ((void*)atemp); 2495 } 2496 if (node->nodeFlags & HAS_BASEURI) { 2497 entryPtr = Tcl_FindHashEntry (node->ownerDocument->baseURIs, 2498 (char*)node); 2499 if (entryPtr) { 2500 FREE ((char *) Tcl_GetHashValue (entryPtr)); 2501 Tcl_DeleteHashEntry (entryPtr); 2502 } 2503 } 2504 domFree ((void*)node); 2505 2506 } else if (node->nodeType == PROCESSING_INSTRUCTION_NODE && !shared) { 2507 FREE (((domProcessingInstructionNode*)node)->dataValue); 2508 FREE (((domProcessingInstructionNode*)node)->targetValue); 2509 domFree ((void*)node); 2510 2511 } else if (!shared) { 2512 FREE (((domTextNode*)node)->nodeValue); 2513 domFree ((void*)node); 2514 } 2515} 2516 2517 2518/*--------------------------------------------------------------------------- 2519| domDeleteNode - unlinks node from tree and free all child nodes 2520| and itself 2521| 2522\--------------------------------------------------------------------------*/ 2523domException 2524domDeleteNode ( 2525 domNode * node, 2526 domFreeCallback freeCB, 2527 void * clientData 2528) 2529{ 2530 TDomThreaded(int shared = 0;) 2531 domDocument *doc; 2532 2533 if (node->nodeType == ATTRIBUTE_NODE) { 2534 domPanic("domDeleteNode on ATTRIBUTE_NODE not supported!"); 2535 } 2536 TDomThreaded ( 2537 shared = node->ownerDocument->refCount > 1; 2538 ) 2539 doc = node->ownerDocument; 2540 2541 /*---------------------------------------------------------------- 2542 | unlink node from child or fragment list 2543 \---------------------------------------------------------------*/ 2544 if (node->previousSibling) { 2545 node->previousSibling->nextSibling = node->nextSibling; 2546 } else { 2547 if (node->parentNode) { 2548 node->parentNode->firstChild = node->nextSibling; 2549 } else { 2550 /* Node may be a top level node */ 2551 if (doc->rootNode->firstChild == node) { 2552 doc->rootNode->firstChild = node->nextSibling; 2553 } 2554 } 2555 } 2556 if (node->nextSibling) { 2557 node->nextSibling->previousSibling = node->previousSibling; 2558 } else { 2559 if (node->parentNode) { 2560 node->parentNode->lastChild = node->previousSibling; 2561 } else { 2562 /* Node may be a top level node */ 2563 if (doc->rootNode->lastChild == node) { 2564 doc->rootNode->lastChild = node->previousSibling; 2565 } 2566 } 2567 } 2568 if (doc->fragments == node) { 2569 doc->fragments = node->nextSibling; 2570 } 2571 if (!node->parentNode) { 2572 domSetDocumentElement (doc); 2573 } 2574 2575 /*---------------------------------------------------------------- 2576 | for shared docs, append node to the delete nodes list 2577 | otherwise delete the node physically 2578 \---------------------------------------------------------------*/ 2579 if (freeCB) { 2580 freeCB(node, clientData); 2581 } 2582 TDomThreaded ( 2583 if (shared) { 2584 if (doc->deletedNodes) { 2585 doc->deletedNodes->nextDeleted = node; 2586 } else { 2587 doc->deletedNodes = node; 2588 } 2589 node->nodeFlags |= IS_DELETED; 2590 node->nextDeleted = NULL; 2591 } 2592 ) 2593 MutationEvent3(DOMNodeRemoved, childToRemove, node); 2594 MutationEvent2(DOMSubtreeModified, node); 2595 domFreeNode(node, freeCB, clientData, 0); 2596 2597 return OK; 2598} 2599 2600 2601/*--------------------------------------------------------------------------- 2602| domFreeDocument 2603| 2604\--------------------------------------------------------------------------*/ 2605void 2606domFreeDocument ( 2607 domDocument * doc, 2608 domFreeCallback freeCB, 2609 void * clientData 2610) 2611{ 2612 domNode *node, *next; 2613 domNS *ns; 2614 int i, dontfree = 0; 2615 Tcl_HashEntry *entryPtr; 2616 Tcl_HashSearch search; 2617 2618 if (doc->nodeFlags & DONT_FREE) { 2619 doc->nodeFlags &= ~DONT_FREE; 2620 dontfree = 1; 2621 } 2622 /*----------------------------------------------------------- 2623 | delete main trees, including top level PIs, etc. 2624 \-----------------------------------------------------------*/ 2625 node = doc->rootNode; 2626 if (node) { 2627 if (freeCB) { 2628 freeCB(node, clientData); 2629 } 2630 domFreeNode (node, freeCB, clientData, dontfree); 2631 } 2632 2633 /*----------------------------------------------------------- 2634 | delete fragment trees 2635 \-----------------------------------------------------------*/ 2636 node = doc->fragments; 2637 while (node) { 2638 next = node->nextSibling; 2639 if (freeCB) { 2640 freeCB(node, clientData); 2641 } 2642 domFreeNode (node, freeCB, clientData, dontfree); 2643 node = next; 2644 } 2645 2646 if (dontfree) return; 2647 2648 /*----------------------------------------------------------- 2649 | delete namespaces 2650 \-----------------------------------------------------------*/ 2651 for (i = 0; i <= doc->nsptr; i++) { 2652 ns = doc->namespaces[i]; 2653 FREE(ns->uri); 2654 FREE(ns->prefix); 2655 FREE ((char*) ns); 2656 } 2657 FREE ((char *)doc->namespaces); 2658 2659 /*----------------------------------------------------------- 2660 | delete global selectNodes prefix namespace mappings 2661 \-----------------------------------------------------------*/ 2662 if (doc->prefixNSMappings) { 2663 i = 0; 2664 while (doc->prefixNSMappings[i]) { 2665 FREE (doc->prefixNSMappings[i]); 2666 i++; 2667 } 2668 FREE (doc->prefixNSMappings); 2669 } 2670 2671 /*----------------------------------------------------------- 2672 | delete doctype info 2673 \-----------------------------------------------------------*/ 2674 if (doc->doctype) { 2675#define DOCINFO_FREE(item) if (doc->doctype->item) FREE(doc->doctype->item) 2676 DOCINFO_FREE(systemId); 2677 DOCINFO_FREE(publicId); 2678 DOCINFO_FREE(internalSubset); 2679 DOCINFO_FREE(encoding); 2680 DOCINFO_FREE(mediaType); 2681 DOCINFO_FREE(method); 2682 if (doc->doctype->cdataSectionElements) { 2683 Tcl_DeleteHashTable (doc->doctype->cdataSectionElements); 2684 FREE (doc->doctype->cdataSectionElements); 2685 } 2686 2687 FREE((char*) doc->doctype); 2688 } 2689 2690 /*----------------------------------------------------------- 2691 | delete ID hash table 2692 \-----------------------------------------------------------*/ 2693 if (doc->ids) { 2694 Tcl_DeleteHashTable (doc->ids); 2695 FREE (doc->ids); 2696 } 2697 2698 /*----------------------------------------------------------- 2699 | delete unparsed entities hash table 2700 \-----------------------------------------------------------*/ 2701 if (doc->unparsedEntities) { 2702 entryPtr = Tcl_FirstHashEntry (doc->unparsedEntities, &search); 2703 while (entryPtr) { 2704 FREE (Tcl_GetHashValue (entryPtr)); 2705 entryPtr = Tcl_NextHashEntry (&search); 2706 } 2707 Tcl_DeleteHashTable (doc->unparsedEntities); 2708 FREE (doc->unparsedEntities); 2709 } 2710 2711 /*----------------------------------------------------------- 2712 | delete base URIs hash table 2713 \-----------------------------------------------------------*/ 2714 entryPtr = Tcl_FirstHashEntry (doc->baseURIs, &search); 2715 while (entryPtr) { 2716 FREE (Tcl_GetHashValue (entryPtr)); 2717 entryPtr = Tcl_NextHashEntry (&search); 2718 } 2719 Tcl_DeleteHashTable (doc->baseURIs); 2720 FREE (doc->baseURIs); 2721 2722 /*----------------------------------------------------------- 2723 | delete xpath cache hash table 2724 \-----------------------------------------------------------*/ 2725 if (doc->xpathCache) { 2726 entryPtr = Tcl_FirstHashEntry (doc->xpathCache, &search); 2727 while (entryPtr) { 2728 xpathFreeAst((ast)Tcl_GetHashValue (entryPtr)); 2729 entryPtr = Tcl_NextHashEntry (&search); 2730 } 2731 Tcl_DeleteHashTable (doc->xpathCache); 2732 FREE (doc->xpathCache); 2733 } 2734 2735 if (doc->extResolver) { 2736 FREE (doc->extResolver); 2737 } 2738 2739 /*----------------------------------------------------------- 2740 | delete tag/attribute hash tables (for threaded builds only) 2741 \-----------------------------------------------------------*/ 2742 TDomThreaded ( 2743 { 2744 Tcl_HashEntry *entryPtr; 2745 Tcl_HashSearch search; 2746 entryPtr = Tcl_FirstHashEntry(&doc->tdom_tagNames, &search); 2747 while (entryPtr) { 2748 Tcl_DeleteHashEntry(entryPtr); 2749 entryPtr = Tcl_NextHashEntry(&search); 2750 } 2751 Tcl_DeleteHashTable(&doc->tdom_tagNames); 2752 entryPtr = Tcl_FirstHashEntry(&doc->tdom_attrNames, &search); 2753 while (entryPtr) { 2754 Tcl_DeleteHashEntry(entryPtr); 2755 entryPtr = Tcl_NextHashEntry(&search); 2756 } 2757 Tcl_DeleteHashTable(&doc->tdom_attrNames); 2758 domLocksDetach(doc); 2759 node = doc->deletedNodes; 2760 while (node) { 2761 next = node->nextSibling; 2762 domFreeNode (node, freeCB, clientData, 0); 2763 node = next; 2764 } 2765 } 2766 ) 2767 2768 FREE ((char*)doc); 2769} 2770 2771/*--------------------------------------------------------------------------- 2772| domSetAttribute 2773| 2774\--------------------------------------------------------------------------*/ 2775domAttrNode * 2776domSetAttribute ( 2777 domNode *node, 2778 const char *attributeName, 2779 const char *attributeValue 2780) 2781{ 2782 domAttrNode *attr, *lastAttr; 2783 Tcl_HashEntry *h; 2784 int hnew; 2785 2786 if (!node || node->nodeType != ELEMENT_NODE) { 2787 return NULL; 2788 } 2789 2790 /*---------------------------------------------------- 2791 | try to find an existing attribute 2792 \---------------------------------------------------*/ 2793 attr = node->firstAttr; 2794 while (attr && strcmp(attr->nodeName, attributeName)) { 2795 attr = attr->nextSibling; 2796 } 2797 if (attr) { 2798 if (attr->nodeFlags & IS_ID_ATTRIBUTE) { 2799 h = Tcl_FindHashEntry (node->ownerDocument->ids, attr->nodeValue); 2800 if (h) { 2801 Tcl_DeleteHashEntry (h); 2802 h = Tcl_CreateHashEntry (node->ownerDocument->ids, 2803 attributeValue, &hnew); 2804 /* XXX what to do, if hnew = 0 ??? */ 2805 Tcl_SetHashValue (h, node); 2806 } 2807 } 2808 FREE (attr->nodeValue); 2809 attr->valueLength = strlen(attributeValue); 2810 attr->nodeValue = (char*)MALLOC(attr->valueLength+1); 2811 strcpy(attr->nodeValue, attributeValue); 2812 } else { 2813 /*----------------------------------------------- 2814 | add a complete new attribute node 2815 \----------------------------------------------*/ 2816 attr = (domAttrNode*) domAlloc(sizeof(domAttrNode)); 2817 memset(attr, 0, sizeof(domAttrNode)); 2818 h = Tcl_CreateHashEntry(&HASHTAB(node->ownerDocument,tdom_attrNames), 2819 attributeName, &hnew); 2820 attr->nodeType = ATTRIBUTE_NODE; 2821 attr->nodeFlags = 0; 2822 attr->namespace = 0; 2823 attr->nodeName = (char *)&(h->key); 2824 attr->parentNode = node; 2825 attr->valueLength = strlen(attributeValue); 2826 attr->nodeValue = (char*)MALLOC(attr->valueLength+1); 2827 strcpy(attr->nodeValue, attributeValue); 2828 2829 if (node->firstAttr) { 2830 lastAttr = node->firstAttr; 2831 /* move to the end of the attribute list */ 2832 while (lastAttr->nextSibling) lastAttr = lastAttr->nextSibling; 2833 lastAttr->nextSibling = attr; 2834 } else { 2835 node->firstAttr = attr; 2836 } 2837 } 2838 MutationEvent(); 2839 return attr; 2840} 2841 2842/*--------------------------------------------------------------------------- 2843| domSetAttributeNS 2844| 2845\--------------------------------------------------------------------------*/ 2846domAttrNode * 2847domSetAttributeNS ( 2848 domNode *node, 2849 const char *attributeName, 2850 const char *attributeValue, 2851 const char *uri, 2852 int createNSIfNeeded 2853) 2854{ 2855 domAttrNode *attr, *lastAttr; 2856 Tcl_HashEntry *h; 2857 int hnew, hasUri = 1, isNSAttr = 0, isDftNS = 0; 2858 domNS *ns; 2859 char prefix[MAX_PREFIX_LEN]; 2860 const char *localName, *newLocalName; 2861 Tcl_DString dStr; 2862 2863 DBG(fprintf (stderr, "domSetAttributeNS: attributeName %s, attributeValue %s, uri %s\n", attributeName, attributeValue, uri);) 2864 if (!node || node->nodeType != ELEMENT_NODE) { 2865 return NULL; 2866 } 2867 2868 domSplitQName (attributeName, prefix, &localName); 2869 if (!uri || uri[0]=='\0') hasUri = 0; 2870 if (hasUri && (prefix[0] == '\0')) return NULL; 2871 if ((prefix[0] == '\0' && strcmp (localName, "xmlns")==0) 2872 || (strcmp (prefix, "xmlns")==0)) { 2873 isNSAttr = 1; 2874 createNSIfNeeded = 0; 2875 if (prefix[0] == '\0') { 2876 isDftNS = 1; 2877 ns = domLookupPrefix (node, ""); 2878 } else { 2879 ns = domLookupPrefix (node, prefix); 2880 } 2881 if (ns && (strcmp (ns->uri, attributeValue)==0)) return NULL; 2882 if (!hasUri) { 2883 uri = attributeValue; 2884 isNSAttr = 1; 2885 hasUri = 1; 2886 if (strcmp (localName, "xmlns")==0) isDftNS = 1; 2887 } else { 2888 return NULL; 2889 } 2890 } 2891 if (!hasUri) { 2892 if (prefix[0] != '\0' && strcmp (prefix, "xml")==0) { 2893 uri = "http://www.w3.org/XML/1998/namespace"; 2894 hasUri = 1; 2895 } 2896 } 2897 if (!hasUri && prefix[0] != '\0') return NULL; 2898 2899 /*---------------------------------------------------- 2900 | try to find an existing attribute 2901 \---------------------------------------------------*/ 2902 attr = node->firstAttr; 2903 while (attr) { 2904 if (hasUri) { 2905 if (attr->nodeFlags & IS_NS_NODE) { 2906 if (isNSAttr) { 2907 if (strcmp (attributeName, attr->nodeName)==0) { 2908 break; 2909 } 2910 } 2911 } else { 2912 if (attr->namespace && !isNSAttr) { 2913 ns = domGetNamespaceByIndex (node->ownerDocument, 2914 attr->namespace); 2915 if (strcmp (uri, ns->uri)==0) { 2916 newLocalName = localName; 2917 domSplitQName (attr->nodeName, prefix, &localName); 2918 if (strcmp (newLocalName, localName)==0) break; 2919 } 2920 } 2921 } 2922 } else { 2923 if (!attr->namespace) { 2924 if (strcmp (attr->nodeName, localName)==0) break; 2925 } 2926 } 2927 attr = attr->nextSibling; 2928 } 2929 if (attr) { 2930 DBG(fprintf (stderr, "domSetAttributeNS: reseting existing attribute %s ; old value: %s\n", attr->nodeName, attr->nodeValue);) 2931 if (attr->nodeFlags & IS_ID_ATTRIBUTE) { 2932 h = Tcl_FindHashEntry (node->ownerDocument->ids, attr->nodeValue); 2933 if (h) { 2934 Tcl_DeleteHashEntry (h); 2935 h = Tcl_CreateHashEntry (node->ownerDocument->ids, 2936 attributeValue, &hnew); 2937 Tcl_SetHashValue (h, node); 2938 } 2939 } 2940 FREE (attr->nodeValue); 2941 attr->valueLength = strlen(attributeValue); 2942 attr->nodeValue = (char*)MALLOC(attr->valueLength+1); 2943 strcpy(attr->nodeValue, attributeValue); 2944 } else { 2945 /*-------------------------------------------------------- 2946 | add a complete new attribute node 2947 \-------------------------------------------------------*/ 2948 attr = (domAttrNode*) domAlloc(sizeof(domAttrNode)); 2949 memset(attr, 0, sizeof(domAttrNode)); 2950 h = Tcl_CreateHashEntry(&HASHTAB(node->ownerDocument,tdom_attrNames), 2951 attributeName, &hnew); 2952 attr->nodeType = ATTRIBUTE_NODE; 2953 if (hasUri) { 2954 if (isNSAttr) { 2955 if (isDftNS) { 2956 ns = domLookupNamespace (node->ownerDocument, "", uri); 2957 } else { 2958 ns = domLookupNamespace (node->ownerDocument, localName, uri); 2959 } 2960 } else { 2961 ns = domLookupPrefix (node, prefix); 2962 if (ns && (strcmp (ns->uri, uri)!=0)) ns = NULL; 2963 } 2964 if (!ns) { 2965 if (isNSAttr) { 2966 if (isDftNS) { 2967 ns = domNewNamespace (node->ownerDocument, "", uri); 2968 } else { 2969 ns = domNewNamespace (node->ownerDocument, localName, uri); 2970 } 2971 } else { 2972 ns = domNewNamespace (node->ownerDocument, prefix, uri); 2973 if (createNSIfNeeded) { 2974 if (prefix[0] == '\0') { 2975 domSetAttributeNS (node, "xmlns", uri, NULL, 0); 2976 } else { 2977 Tcl_DStringInit (&dStr); 2978 Tcl_DStringAppend (&dStr, "xmlns:", 6); 2979 Tcl_DStringAppend (&dStr, prefix, -1); 2980 domSetAttributeNS (node, Tcl_DStringValue (&dStr), 2981 uri, NULL, 0); 2982 } 2983 } 2984 } 2985 } 2986 attr->namespace = ns->index; 2987 if (isNSAttr) { 2988 attr->nodeFlags = IS_NS_NODE; 2989 } 2990 } 2991 attr->nodeName = (char *)&(h->key); 2992 attr->parentNode = node; 2993 attr->valueLength = strlen(attributeValue); 2994 attr->nodeValue = (char*)MALLOC(attr->valueLength+1); 2995 strcpy(attr->nodeValue, attributeValue); 2996 2997 if (isNSAttr) { 2998 if (node->firstAttr && (node->firstAttr->nodeFlags & IS_NS_NODE)) { 2999 lastAttr = node->firstAttr; 3000 while (lastAttr->nextSibling 3001 && (lastAttr->nextSibling->nodeFlags & IS_NS_NODE)) { 3002 lastAttr = lastAttr->nextSibling; 3003 } 3004 attr->nextSibling = lastAttr->nextSibling; 3005 lastAttr->nextSibling = attr; 3006 } else { 3007 attr->nextSibling = node->firstAttr; 3008 node->firstAttr = attr; 3009 } 3010 } else { 3011 if (node->firstAttr) { 3012 lastAttr = node->firstAttr; 3013 /* move to the end of the attribute list */ 3014 while (lastAttr->nextSibling) lastAttr = lastAttr->nextSibling; 3015 lastAttr->nextSibling = attr; 3016 } else { 3017 node->firstAttr = attr; 3018 } 3019 } 3020 } 3021 MutationEvent(); 3022 return attr; 3023} 3024 3025 3026/*--------------------------------------------------------------------------- 3027| domRemoveAttribute 3028| 3029\--------------------------------------------------------------------------*/ 3030int 3031domRemoveAttribute ( 3032 domNode *node, 3033 const char *attributeName 3034) 3035{ 3036 domAttrNode *attr, *previous = NULL; 3037 Tcl_HashEntry *h; 3038 3039 if (!node || node->nodeType != ELEMENT_NODE) { 3040 return -1; 3041 } 3042 3043 /*---------------------------------------------------- 3044 | try to find the attribute 3045 \---------------------------------------------------*/ 3046 attr = node->firstAttr; 3047 while (attr && strcmp(attr->nodeName, attributeName)) { 3048 previous = attr; 3049 attr = attr->nextSibling; 3050 } 3051 if (attr) { 3052 if (previous) { 3053 previous->nextSibling = attr->nextSibling; 3054 } else { 3055 attr->parentNode->firstAttr = attr->nextSibling; 3056 } 3057 3058 if (attr->nodeFlags & IS_ID_ATTRIBUTE) { 3059 h = Tcl_FindHashEntry (node->ownerDocument->ids, attr->nodeValue); 3060 if (h) Tcl_DeleteHashEntry (h); 3061 } 3062 FREE (attr->nodeValue); 3063 MutationEvent(); 3064 3065 domFree ((void*)attr); 3066 return 0; 3067 } 3068 return -1; 3069} 3070 3071 3072/*--------------------------------------------------------------------------- 3073| domRemoveAttributeNS 3074| 3075\--------------------------------------------------------------------------*/ 3076int 3077domRemoveAttributeNS ( 3078 domNode *node, 3079 const char *uri, 3080 const char *localName 3081) 3082{ 3083 domAttrNode *attr, *previous = NULL; 3084 domNS *ns = NULL; 3085 char prefix[MAX_PREFIX_LEN]; 3086 const char *str; 3087 Tcl_HashEntry *h; 3088 3089 if (!node || node->nodeType != ELEMENT_NODE) { 3090 return -1; 3091 } 3092 3093 attr = node->firstAttr; 3094 while (attr) { 3095 domSplitQName (attr->nodeName, prefix, &str); 3096 if (strcmp(localName,str)==0) { 3097 ns = domGetNamespaceByIndex(node->ownerDocument, attr->namespace); 3098 if (strcmp(ns->uri, uri)==0) { 3099 if (previous) { 3100 previous->nextSibling = attr->nextSibling; 3101 } else { 3102 attr->parentNode->firstAttr = attr->nextSibling; 3103 } 3104 3105 if (attr->nodeFlags & IS_ID_ATTRIBUTE) { 3106 h = Tcl_FindHashEntry (node->ownerDocument->ids, 3107 attr->nodeValue); 3108 if (h) Tcl_DeleteHashEntry (h); 3109 } 3110 FREE (attr->nodeValue); 3111 MutationEvent(); 3112 domFree ((void*)attr); 3113 return 0; 3114 } 3115 } 3116 previous = attr; 3117 attr = attr->nextSibling; 3118 } 3119 return -1; 3120} 3121 3122 3123/*--------------------------------------------------------------------------- 3124| __dbgAttr 3125| 3126\--------------------------------------------------------------------------*/ 3127DBG( 3128static void __dbgAttr (domAttrNode *node) { 3129 3130 DBG(fprintf(stderr, " %s=%s", node->nodeName, node->nodeValue);) 3131 if (node->nextSibling) __dbgAttr(node->nextSibling); 3132} 3133) 3134 3135 3136/*--------------------------------------------------------------------------- 3137| domSetDocument 3138| 3139\--------------------------------------------------------------------------*/ 3140void 3141domSetDocument ( 3142 domNode *node, 3143 domDocument *doc 3144) 3145{ 3146 domNode *child; 3147 domNS *ns, *origNS; 3148 domDocument *origDoc; 3149 domAttrNode *attr; 3150 Tcl_HashEntry *h; 3151 TDomThreaded ( 3152 int hnew; 3153 ) 3154 3155 if (node->nodeFlags & HAS_BASEURI) { 3156 h = Tcl_FindHashEntry (node->ownerDocument->baseURIs, (char*)node); 3157 if (h) { 3158 FREE ((char *) Tcl_GetHashValue (h)); 3159 Tcl_DeleteHashEntry (h); 3160 } 3161 node->nodeFlags &= ~HAS_BASEURI; 3162 } 3163 if (node->nodeType == ELEMENT_NODE) { 3164 origDoc = node->ownerDocument; 3165 node->ownerDocument = doc; 3166 for (attr = node->firstAttr; attr != NULL; attr = attr->nextSibling) { 3167 if (attr->nodeFlags & IS_NS_NODE) { 3168 origNS = origDoc->namespaces[attr->namespace-1]; 3169 ns = domNewNamespace (doc, origNS->prefix, origNS->uri); 3170 attr->namespace = ns->index; 3171 } else if (attr->namespace) { 3172 ns = domAddNSToNode (node, 3173 origDoc->namespaces[attr->namespace-1]); 3174 if (ns) attr->namespace = ns->index; 3175 } 3176 } 3177 if (node->namespace) { 3178 ns = domAddNSToNode (node, origDoc->namespaces[node->namespace-1]); 3179 if (ns) node->namespace = ns->index; 3180 } else { 3181 ns = domAddNSToNode (node, NULL); 3182 if (ns) { 3183 node->namespace = ns->index; 3184 } 3185 } 3186 DBG(fprintf(stderr, "domSetDocument node%s ", node->nodeName); 3187 __dbgAttr(node->firstAttr); 3188 fprintf(stderr, "\n"); 3189 ) 3190 3191 TDomThreaded ( 3192 if (origDoc != doc) { 3193 /* Make hash table entries as necessary for 3194 * tdom_tagNames and tdom_attrNames. */ 3195 h = Tcl_CreateHashEntry(&doc->tdom_tagNames, node->nodeName, 3196 &hnew); 3197 node->nodeName = (domString) &(h->key); 3198 for (attr = node->firstAttr; 3199 attr != NULL; 3200 attr = attr->nextSibling) { 3201 h = Tcl_CreateHashEntry(&doc->tdom_attrNames, 3202 attr->nodeName, &hnew); 3203 attr->nodeName = (domString) &(h->key); 3204 } 3205 } 3206 ) 3207 child = node->firstChild; 3208 while (child != NULL) { 3209 domSetDocument (child, doc); 3210 child = child->nextSibling; 3211 } 3212 } else { 3213 node->ownerDocument = doc; 3214 } 3215 3216 DBG(fprintf(stderr, "end domSetDocument node %s\n", node->nodeName);) 3217} 3218 3219 3220/*--------------------------------------------------------------------------- 3221| domSetNodeValue 3222| 3223\--------------------------------------------------------------------------*/ 3224domException 3225domSetNodeValue ( 3226 domNode *node, 3227 const char *nodeValue, 3228 int valueLen 3229) 3230{ 3231 domTextNode *textnode; 3232 3233 if ((node->nodeType != TEXT_NODE) && 3234 (node->nodeType != CDATA_SECTION_NODE) && 3235 (node->nodeType != COMMENT_NODE) 3236 ) { 3237 return NO_MODIFICATION_ALLOWED_ERR; 3238 } 3239 3240 textnode = (domTextNode*) node; 3241 FREE(textnode->nodeValue); 3242 textnode->nodeValue = MALLOC (valueLen); 3243 textnode->valueLength = valueLen; 3244 memmove(textnode->nodeValue, nodeValue, valueLen); 3245 MutationEvent(); 3246 return OK; 3247} 3248 3249 3250/* 3251 *---------------------------------------------------------------------- 3252 * 3253 * domRemoveChild -- 3254 * 3255 * This procedure implements the dom method removeChild. Removes 3256 * child from the list of children of node. 3257 * 3258 * Results: 3259 * Returns a domException: 3260 * 3261 * NOT_FOUND_ERR: Raised if the node child is not a child of node. 3262 * 3263 * OK: otherwise 3264 * 3265 * Side effects: 3266 * Alters the involved document. 3267 * 3268 *---------------------------------------------------------------------- 3269 */ 3270 3271domException 3272domRemoveChild ( 3273 domNode *node, 3274 domNode *child 3275) 3276{ 3277 domNode *n; 3278 3279 /* check, if node is in deed the parent of child */ 3280 if (child->parentNode != node) { 3281 /* If node is the root node of a document and child 3282 is in deed a child of this node, then 3283 child->parentNode will be NULL. In this case, we 3284 loop throu the childs of node, to see, if the child 3285 is valid. */ 3286 if (node->ownerDocument->rootNode == node) { 3287 n = node->firstChild; 3288 while (n) { 3289 if (n == child) { 3290 /* child is in deed a child of node */ 3291 break; 3292 } 3293 n = n->nextSibling; 3294 } 3295 if (!n) { 3296 return NOT_FOUND_ERR; 3297 } 3298 } else { 3299 return NOT_FOUND_ERR; 3300 } 3301 } 3302 3303 if (child->previousSibling) { 3304 child->previousSibling->nextSibling = child->nextSibling; 3305 } else { 3306 node->firstChild = child->nextSibling; 3307 } 3308 if (child->nextSibling) { 3309 child->nextSibling->previousSibling = child->previousSibling; 3310 } else { 3311 node->lastChild = child->previousSibling; 3312 } 3313 3314 /* link child into the fragments list */ 3315 if (child->ownerDocument->fragments) { 3316 child->nextSibling = child->ownerDocument->fragments; 3317 child->ownerDocument->fragments->previousSibling = child; 3318 child->ownerDocument->fragments = child; 3319 } else { 3320 child->ownerDocument->fragments = child; 3321 child->nextSibling = NULL; 3322 } 3323 child->parentNode = NULL; 3324 child->previousSibling = NULL; 3325 MutationEvent3(DOMNodeRemoved, child, node); 3326 MutationEvent2(DOMSubtreeModified, node); 3327 return OK; 3328} 3329 3330 3331/* 3332 *---------------------------------------------------------------------- 3333 * 3334 * domAppendChild -- 3335 * 3336 * This procedure implements the dom method appendChild. Adds the 3337 * node newChild to the end of the list of children of this 3338 * node. If the newChild is already in the tree, it is first 3339 * removed. 3340 * 3341 * Results: 3342 * Returns a domException: 3343 * 3344 * HIERARCHY_REQUEST_ERR: Raised if node is of a type that does 3345 * not allow children of the type of the childToAppend node, or 3346 * if the node to append is one of node's ancestors or the 3347 * rootNode of node's document. 3348 * 3349 * NOT_SUPPORTED_ERR: Raised if the childToInsert is the rootNode 3350 * of another document or if node is a rootNode. 3351 * 3352 * OK: otherwise 3353 * 3354 * Side effects: 3355 * Alters the involved document(s). 3356 * 3357 *---------------------------------------------------------------------- 3358 */ 3359 3360domException 3361domAppendChild ( 3362 domNode *node, 3363 domNode *childToAppend 3364) 3365{ 3366 domNode *n; 3367 3368 if (node->nodeType != ELEMENT_NODE) { 3369 return HIERARCHY_REQUEST_ERR; 3370 } 3371 3372 /* check, whether childToAppend is node or one of node's ancestors */ 3373 n = node; 3374 while (n) { 3375 if (n == childToAppend) { 3376 return HIERARCHY_REQUEST_ERR; 3377 } 3378 n = n->parentNode; 3379 } 3380 3381 if (childToAppend == childToAppend->ownerDocument->rootNode) { 3382 if (childToAppend == node->ownerDocument->rootNode) { 3383 return HIERARCHY_REQUEST_ERR; 3384 } else { 3385 return NOT_SUPPORTED_ERR; 3386 } 3387 } 3388 3389 /* unlink childToAppend */ 3390 if (childToAppend->previousSibling) { 3391 childToAppend->previousSibling->nextSibling = 3392 childToAppend->nextSibling; 3393 } else { 3394 if (childToAppend->parentNode) { 3395 childToAppend->parentNode->firstChild = childToAppend->nextSibling; 3396 } else { 3397 /* childToAppend is either out of the fragment list or 3398 a child of the rootNode of its document */ 3399 if (childToAppend->ownerDocument->fragments == childToAppend) { 3400 childToAppend->ownerDocument->fragments = 3401 childToAppend->nextSibling; 3402 } else { 3403 childToAppend->ownerDocument->rootNode->firstChild = 3404 childToAppend->nextSibling; 3405 } 3406 } 3407 } 3408 if (childToAppend->nextSibling) { 3409 childToAppend->nextSibling->previousSibling = 3410 childToAppend->previousSibling; 3411 } else { 3412 if (childToAppend->parentNode) { 3413 childToAppend->parentNode->lastChild = 3414 childToAppend->previousSibling; 3415 } else { 3416 if (childToAppend->ownerDocument->rootNode->lastChild 3417 == childToAppend) { 3418 childToAppend->ownerDocument->rootNode->lastChild = 3419 childToAppend->previousSibling; 3420 } 3421 } 3422 } 3423 3424 if (node->lastChild) { 3425 node->lastChild->nextSibling = childToAppend; 3426 childToAppend->previousSibling = node->lastChild; 3427 } else { 3428 node->firstChild = childToAppend; 3429 childToAppend->previousSibling = NULL; 3430 } 3431 node->lastChild = childToAppend; 3432 childToAppend->nextSibling = NULL; 3433 if (!childToAppend->parentNode && 3434 (childToAppend->ownerDocument->documentElement == childToAppend)) { 3435 childToAppend->ownerDocument->documentElement = 3436 childToAppend->ownerDocument->rootNode->firstChild; 3437 } 3438 if (node == node->ownerDocument->rootNode) { 3439 childToAppend->parentNode = NULL; 3440 } else { 3441 childToAppend->parentNode = node; 3442 } 3443 3444 if ((node->ownerDocument != childToAppend->ownerDocument) 3445 || node->ownerDocument->nsptr 3446 || childToAppend->ownerDocument->baseURIs->numEntries) { 3447 domSetDocument (childToAppend, node->ownerDocument); 3448 } 3449 node->ownerDocument->nodeFlags |= NEEDS_RENUMBERING; 3450 MutationEvent(); 3451 return OK; 3452} 3453 3454 3455/* 3456 *---------------------------------------------------------------------- 3457 * 3458 * domInsertBefore -- 3459 * 3460 * This procedure implements the dom method insertBefore. 3461 * It inserts the node childToInsert before the existing child 3462 * node referenceChild. If referenceChild is null, insert 3463 * childToInsert at the end of the list of children of node. The 3464 * arguments node and childToInsert must be non NULL. The 3465 * childToInsert is unlinked from its previous place (fragment 3466 * list or tree). 3467 * 3468 * Results: 3469 * Returns a domException: 3470 * 3471 * HIERARCHY_REQUEST_ERR: Raised if node is of a type that does 3472 * not allow children of the type of the childToInsert node, or 3473 * if the node to insert is node or one of node's ancestors or the 3474 * rootNode of node's document. 3475 * 3476 * NOT_FOUND_ERR: Raised if refChild is not a child of this node. 3477 * 3478 * NOT_SUPPORTED_ERR: Raised if the childToInsert is the rootNode 3479 * of another document or if node is a rootNode. 3480 * 3481 * OK: otherwise 3482 * 3483 * Side effects: 3484 * Alters the involved document(s). 3485 * 3486 *---------------------------------------------------------------------- 3487 */ 3488 3489domException 3490domInsertBefore ( 3491 domNode *node, 3492 domNode *childToInsert, 3493 domNode *referenceChild 3494) 3495{ 3496 domNode *n; 3497 3498 3499 if (node->nodeType != ELEMENT_NODE) { 3500 return HIERARCHY_REQUEST_ERR; 3501 } 3502 3503 /* check, if node is in deed the parent of referenceChild */ 3504 if (referenceChild) { 3505 if (referenceChild->parentNode != node) { 3506 /* If node is the root node of a document and referenceChild 3507 is in deed a child of this node, then 3508 referenceChild->parentNode will be NULL. In this case, we 3509 loop throu the childs of node, to see, if the referenceChild 3510 is valid. */ 3511 if (node->ownerDocument->rootNode == node) { 3512 n = node->firstChild; 3513 while (n) { 3514 if (n == referenceChild) { 3515 /* referenceChild is in deed a child of node */ 3516 break; 3517 } 3518 n = n->nextSibling; 3519 } 3520 if (!n) { 3521 return NOT_FOUND_ERR; 3522 } 3523 } else { 3524 return NOT_FOUND_ERR; 3525 } 3526 } 3527 } 3528 3529 if (childToInsert == referenceChild) { 3530 return OK; 3531 } 3532 3533 /* check, whether childToInsert is one of node's ancestors */ 3534 n = node; 3535 while (n) { 3536 if (n == childToInsert) { 3537 return HIERARCHY_REQUEST_ERR; 3538 } 3539 n = n->parentNode; 3540 } 3541 3542 if (childToInsert == childToInsert->ownerDocument->rootNode) { 3543 if (childToInsert == node->ownerDocument->rootNode) { 3544 return HIERARCHY_REQUEST_ERR; 3545 } else { 3546 /* For now, we simply don't allow the rootNode of 3547 another element as childToInsert. The way to go may 3548 be simply to treat the rootNode as DocumentFragment 3549 and to insert all childs of that rootNode before the 3550 referenceChild. This would result in a document 3551 without documentElement, which then should be 3552 handled right by other methods. This is planed, but 3553 not carefully considered, yet. */ 3554 return NOT_SUPPORTED_ERR; 3555 } 3556 } 3557 3558 3559 /* unlink childToInsert */ 3560 if (childToInsert->previousSibling) { 3561 childToInsert->previousSibling->nextSibling = 3562 childToInsert->nextSibling; 3563 } else { 3564 if (childToInsert->parentNode) { 3565 childToInsert->parentNode->firstChild = childToInsert->nextSibling; 3566 } else { 3567 /* childToInsert is either out of the fragment list or 3568 a child of the rootNode of its document */ 3569 if (childToInsert->ownerDocument->fragments == childToInsert) { 3570 childToInsert->ownerDocument->fragments = 3571 childToInsert->nextSibling; 3572 } else { 3573 childToInsert->ownerDocument->rootNode->firstChild = 3574 childToInsert->nextSibling; 3575 } 3576 } 3577 } 3578 if (childToInsert->nextSibling) { 3579 childToInsert->nextSibling->previousSibling = 3580 childToInsert->previousSibling; 3581 } else { 3582 if (childToInsert->parentNode) { 3583 childToInsert->parentNode->lastChild = 3584 childToInsert->previousSibling; 3585 } else { 3586 if (childToInsert->ownerDocument->rootNode->lastChild 3587 == childToInsert) { 3588 childToInsert->ownerDocument->rootNode->lastChild = 3589 childToInsert->previousSibling; 3590 } 3591 } 3592 } 3593 3594 childToInsert->nextSibling = referenceChild; 3595 if (referenceChild) { 3596 if (referenceChild->previousSibling) { 3597 childToInsert->previousSibling = referenceChild->previousSibling; 3598 referenceChild->previousSibling->nextSibling = childToInsert; 3599 } else { 3600 node->firstChild = childToInsert; 3601 childToInsert->previousSibling = NULL; 3602 } 3603 referenceChild->previousSibling = childToInsert; 3604 } else { 3605 if (node->lastChild) { 3606 node->lastChild->nextSibling = childToInsert; 3607 childToInsert->previousSibling = node->lastChild; 3608 } else { 3609 node->firstChild = childToInsert; 3610 childToInsert->previousSibling = NULL; 3611 } 3612 node->lastChild = childToInsert; 3613 } 3614 if (!childToInsert->parentNode && 3615 (childToInsert->ownerDocument->documentElement == childToInsert)) { 3616 childToInsert->ownerDocument->documentElement = 3617 childToInsert->ownerDocument->rootNode->firstChild; 3618 } 3619 if (node == node->ownerDocument->rootNode) { 3620 childToInsert->parentNode = NULL; 3621 } else { 3622 childToInsert->parentNode = node; 3623 } 3624 if (node->ownerDocument != childToInsert->ownerDocument 3625 || node->ownerDocument->nsptr 3626 || childToInsert->ownerDocument->baseURIs->numEntries) { 3627 domSetDocument (childToInsert, node->ownerDocument); 3628 } 3629 node->ownerDocument->nodeFlags |= NEEDS_RENUMBERING; 3630 MutationEvent3(DOMNodeInsert, childToInsert, node); 3631 MutationEvent2(DOMSubtreeModified, node); 3632 return OK; 3633} 3634 3635 3636 3637/* 3638 *---------------------------------------------------------------------- 3639 * 3640 * domReplaceChild -- 3641 * 3642 * This procedure implements the dom method replaceChild. 3643 * Replaces the child node oldChild with newChild in the list of 3644 * children of node 'node'. 3645 * 3646 * Results: 3647 * Returns a domException: 3648 * 3649 * HIERARCHY_REQUEST_ERR: Raised if node is of a type that does 3650 * not allow children of the type of the newChild node, or 3651 * if newChild is node or one of node's ancestors or the 3652 * rootNode of node's document. 3653 * 3654 * NOT_FOUND_ERR: Raised if oldChild is not a child of node. 3655 * 3656 * NOT_SUPPORTED_ERR: Raised if the newChild is the rootNode 3657 * of another document. 3658 * 3659 * OK: otherwise 3660 * 3661 * Side effects: 3662 * Alters the involved document(s). 3663 * 3664 *---------------------------------------------------------------------- 3665 */ 3666 3667domException 3668domReplaceChild ( 3669 domNode *node, 3670 domNode *newChild, 3671 domNode *oldChild 3672) 3673{ 3674 domNode *n; 3675 3676 3677 if (node->nodeType != ELEMENT_NODE) { 3678 return HIERARCHY_REQUEST_ERR; 3679 } 3680 3681 /* check, if node is in deed the parent of oldChild */ 3682 if (oldChild->parentNode != node) { 3683 /* If node is the root node of a document and oldChild 3684 is in deed a child of this node, then 3685 oldChild->parentNode will be NULL. In this case, we 3686 loop throu the childs of node, to see, if the oldChild 3687 is valid. */ 3688 if (node->ownerDocument->rootNode == node) { 3689 n = node->firstChild; 3690 while (n) { 3691 if (n == oldChild) { 3692 /* oldChild is in deed a child of node */ 3693 break; 3694 } 3695 n = n->nextSibling; 3696 } 3697 if (!n) { 3698 return NOT_FOUND_ERR; 3699 } 3700 } else { 3701 return NOT_FOUND_ERR; 3702 } 3703 } 3704 3705 if (oldChild == newChild) { 3706 return OK; 3707 } 3708 3709 /* check, whether newChild is node or one of node's ancestors */ 3710 n = node; 3711 while (n) { 3712 if (n == newChild) { 3713 return HIERARCHY_REQUEST_ERR; 3714 } 3715 n = n->parentNode; 3716 } 3717 3718 if (newChild == newChild->ownerDocument->rootNode) { 3719 if (newChild == node->ownerDocument->rootNode) { 3720 return HIERARCHY_REQUEST_ERR; 3721 } else { 3722 return NOT_SUPPORTED_ERR; 3723 } 3724 } 3725 3726 /* unlink newChild */ 3727 if (newChild->previousSibling) { 3728 newChild->previousSibling->nextSibling = newChild->nextSibling; 3729 } else { 3730 if (newChild->parentNode) { 3731 newChild->parentNode->firstChild = newChild->nextSibling; 3732 } else { 3733 /* newChild is either out of the fragment list or 3734 a child of the rootNode of its document */ 3735 if (newChild->ownerDocument->fragments == newChild) { 3736 newChild->ownerDocument->fragments = newChild->nextSibling; 3737 } else { 3738 newChild->ownerDocument->rootNode->firstChild = 3739 newChild->nextSibling; 3740 } 3741 } 3742 } 3743 if (newChild->nextSibling) { 3744 newChild->nextSibling->previousSibling = newChild->previousSibling; 3745 } else { 3746 if (newChild->parentNode) { 3747 newChild->parentNode->lastChild = newChild->previousSibling; 3748 } else { 3749 if (newChild->ownerDocument->rootNode->lastChild == newChild) { 3750 newChild->ownerDocument->rootNode->lastChild = 3751 newChild->previousSibling; 3752 } 3753 } 3754 } 3755 3756 newChild->nextSibling = oldChild->nextSibling; 3757 newChild->previousSibling = oldChild->previousSibling; 3758 if (!newChild->parentNode && 3759 (newChild->ownerDocument->documentElement == newChild)) { 3760 newChild->ownerDocument->documentElement = 3761 newChild->ownerDocument->rootNode->firstChild; 3762 } 3763 if (node == node->ownerDocument->rootNode) { 3764 newChild->parentNode = NULL; 3765 } else { 3766 newChild->parentNode = node; 3767 } 3768 if (oldChild->previousSibling) { 3769 oldChild->previousSibling->nextSibling = newChild; 3770 } else { 3771 node->firstChild = newChild; 3772 } 3773 if (oldChild->nextSibling) { 3774 oldChild->nextSibling->previousSibling = newChild; 3775 } else { 3776 node->lastChild = newChild; 3777 } 3778 3779 if (node->ownerDocument != newChild->ownerDocument 3780 || node->ownerDocument->nsptr 3781 || newChild->ownerDocument->baseURIs->numEntries) { 3782 domSetDocument (newChild, node->ownerDocument); 3783 } 3784 3785 /* add old child into his fragment list */ 3786 if (oldChild->ownerDocument->fragments) { 3787 oldChild->nextSibling = oldChild->ownerDocument->fragments; 3788 oldChild->ownerDocument->fragments->previousSibling = oldChild; 3789 oldChild->ownerDocument->fragments = oldChild; 3790 } else { 3791 oldChild->ownerDocument->fragments = oldChild; 3792 oldChild->nextSibling = oldChild->previousSibling = NULL; 3793 } 3794 oldChild->parentNode = NULL; 3795 node->ownerDocument->nodeFlags |= NEEDS_RENUMBERING; 3796 MutationEvent(); 3797 return OK; 3798} 3799 3800 3801/*--------------------------------------------------------------------------- 3802| domNewTextNode 3803| 3804\--------------------------------------------------------------------------*/ 3805domTextNode * 3806domNewTextNode( 3807 domDocument *doc, 3808 const char *value, 3809 int length, 3810 domNodeType nodeType 3811) 3812{ 3813 domTextNode *node; 3814 3815 node = (domTextNode*) domAlloc(sizeof(domTextNode)); 3816 memset(node, 0, sizeof(domTextNode)); 3817 node->nodeType = nodeType; 3818 node->nodeFlags = 0; 3819 node->namespace = 0; 3820 node->nodeNumber = NODE_NO(doc); 3821 node->ownerDocument = doc; 3822 node->valueLength = length; 3823 node->nodeValue = (char*)MALLOC(length); 3824 memmove(node->nodeValue, value, length); 3825 3826 if (doc->fragments) { 3827 node->nextSibling = doc->fragments; 3828 doc->fragments->previousSibling = (domNode*)node; 3829 doc->fragments = (domNode*)node; 3830 } else { 3831 doc->fragments = (domNode*)node; 3832 3833 } 3834 return node; 3835} 3836 3837 3838 3839void 3840domEscapeCData ( 3841 char *value, 3842 int length, 3843 Tcl_DString *escapedData 3844) 3845{ 3846 int i, start = 0; 3847 char *pc; 3848 3849 Tcl_DStringInit (escapedData); 3850 pc = value; 3851 for (i = 0; i < length; i++) { 3852 if (*pc == '&') { 3853 Tcl_DStringAppend (escapedData, &value[start], i - start); 3854 Tcl_DStringAppend (escapedData, "&", 5); 3855 start = i+1; 3856 } else 3857 if (*pc == '<') { 3858 Tcl_DStringAppend (escapedData, &value[start], i - start); 3859 Tcl_DStringAppend (escapedData, "<", 4); 3860 start = i+1; 3861 } else 3862 if (*pc == '>') { 3863 Tcl_DStringAppend (escapedData, &value[start], i - start); 3864 Tcl_DStringAppend (escapedData, ">", 4); 3865 start = i+1; 3866 } 3867 pc++; 3868 } 3869 if (start) { 3870 Tcl_DStringAppend (escapedData, &value[start], length - start); 3871 } 3872} 3873 3874 3875/*--------------------------------------------------------------------------- 3876| domAppendNewTextNode 3877| 3878\--------------------------------------------------------------------------*/ 3879domTextNode * 3880domAppendNewTextNode( 3881 domNode *parent, 3882 char *value, 3883 int length, 3884 domNodeType nodeType, 3885 int disableOutputEscaping 3886) 3887{ 3888 domTextNode *node; 3889 3890 if (!length) { 3891 return NULL; 3892 } 3893 3894 if (parent->lastChild 3895 && parent->lastChild->nodeType == TEXT_NODE 3896 && nodeType == TEXT_NODE 3897 ) { 3898 /*------------------------------------------------------------------ 3899 | append to already existing text node 3900 \-----------------------------------------------------------------*/ 3901 domAppendData ((domTextNode *) (parent->lastChild), value, length, 3902 disableOutputEscaping); 3903 MutationEvent(); 3904 return (domTextNode*)parent->lastChild; 3905 } 3906 node = (domTextNode*) domAlloc(sizeof(domTextNode)); 3907 memset(node, 0, sizeof(domTextNode)); 3908 node->nodeType = nodeType; 3909 node->nodeFlags = 0; 3910 if (disableOutputEscaping) { 3911 node->nodeFlags |= DISABLE_OUTPUT_ESCAPING; 3912 } 3913 node->namespace = 0; 3914 node->nodeNumber = NODE_NO(parent->ownerDocument); 3915 node->ownerDocument = parent->ownerDocument; 3916 node->valueLength = length; 3917 node->nodeValue = (char*)MALLOC(length); 3918 memmove(node->nodeValue, value, length); 3919 3920 if (parent->lastChild) { 3921 parent->lastChild->nextSibling = (domNode*)node; 3922 node->previousSibling = parent->lastChild; 3923 } else { 3924 parent->firstChild = (domNode*)node; 3925 node->previousSibling = NULL; 3926 } 3927 parent->lastChild = (domNode*)node; 3928 node->nextSibling = NULL; 3929 if (parent != parent->ownerDocument->rootNode) { 3930 node->parentNode = parent; 3931 } 3932 3933 MutationEvent(); 3934 return node; 3935} 3936 3937 3938/*--------------------------------------------------------------------------- 3939| domAppendNewElementNode 3940| 3941\--------------------------------------------------------------------------*/ 3942domNode * 3943domAppendNewElementNode( 3944 domNode *parent, 3945 const char *tagName, 3946 const char *uri 3947) 3948{ 3949 Tcl_HashEntry *h; 3950 domNode *node; 3951 domNS *ns; 3952 domAttrNode *NSattr; 3953 int hnew; 3954 char prefix[MAX_PREFIX_LEN]; 3955 const char *localname; 3956 Tcl_DString dStr; 3957 3958 if (parent == NULL) { 3959 DBG(fprintf(stderr, "dom.c: Error parent == NULL!\n");) 3960 return NULL; 3961 } 3962 3963 h = Tcl_CreateHashEntry(&HASHTAB(parent->ownerDocument,tdom_tagNames), 3964 tagName, &hnew); 3965 node = (domNode*) domAlloc(sizeof(domNode)); 3966 memset(node, 0, sizeof(domNode)); 3967 node->nodeType = ELEMENT_NODE; 3968 node->nodeNumber = NODE_NO(parent->ownerDocument); 3969 node->ownerDocument = parent->ownerDocument; 3970 node->nodeName = (char *)&(h->key); 3971 3972 if (parent->lastChild) { 3973 parent->lastChild->nextSibling = node; 3974 node->previousSibling = parent->lastChild; 3975 } else { 3976 parent->firstChild = node; 3977 node->previousSibling = NULL; 3978 } 3979 parent->lastChild = node; 3980 node->nextSibling = NULL; 3981 if (parent != parent->ownerDocument->rootNode) { 3982 node->parentNode = parent; 3983 } 3984 3985 /*-------------------------------------------------------- 3986 | re-use existing namespace or create a new one 3987 \-------------------------------------------------------*/ 3988 if (uri) { 3989 domSplitQName (tagName, prefix, &localname); 3990 DBG(fprintf(stderr, "tag '%s' has prefix='%s' \n", tagName, prefix);) 3991 ns = domLookupPrefix (node, prefix); 3992 if (!ns || (strcmp (uri, ns->uri)!=0)) { 3993 ns = domNewNamespace(node->ownerDocument, prefix, uri); 3994 if (prefix[0] == '\0') { 3995 domSetAttributeNS (node, "xmlns", uri, NULL, 1); 3996 } else { 3997 Tcl_DStringInit (&dStr); 3998 Tcl_DStringAppend (&dStr, "xmlns:", 6); 3999 Tcl_DStringAppend (&dStr, prefix, -1); 4000 domSetAttributeNS (node, Tcl_DStringValue (&dStr), uri, NULL, 4001 1); 4002 } 4003 } 4004 node->namespace = ns->index; 4005 } else { 4006 ns = domLookupPrefix (node, ""); 4007 if (ns) { 4008 if (strcmp (ns->uri, "")!=0) { 4009 NSattr = domSetAttributeNS (node, "xmlns", "", NULL, 1); 4010 if (NSattr) { 4011 node->namespace = NSattr->namespace; 4012 } 4013 } else { 4014 node->namespace = ns->index; 4015 } 4016 } 4017 } 4018 MutationEvent(); 4019 return node; 4020} 4021 4022 4023/* 4024 *---------------------------------------------------------------------- 4025 * 4026 * domAppendData -- 4027 * 4028 * This procedure implements the dom method appendData. It is 4029 * also used by domNormalize and domAppendNewTextNode. 4030 * 4031 * Results: 4032 * A domException; currently always OK. 4033 * 4034 * Side effects: 4035 * Appends the data to node. 4036 * 4037 *---------------------------------------------------------------------- 4038 */ 4039 4040domException 4041domAppendData ( 4042 domTextNode *node, /* The node, to append value to. Must be 4043 a TEXT_NODE, COMMENT_NODE or 4044 CDATA_SECTION_NODE*/ 4045 char *value, /* The data to append */ 4046 int length, /* The length of value in byte */ 4047 int disableOutputEscaping /* If true, disable output 4048 escaping on the node */ 4049 ) 4050{ 4051 Tcl_DString escData; 4052 4053 if (node->nodeFlags & DISABLE_OUTPUT_ESCAPING) { 4054 if (disableOutputEscaping) { 4055 node->nodeValue = REALLOC (node->nodeValue, 4056 node->valueLength + length); 4057 memmove (node->nodeValue + node->valueLength, value, length); 4058 node->valueLength += length; 4059 } else { 4060 domEscapeCData (value, length, &escData); 4061 if (Tcl_DStringLength (&escData)) { 4062 node->nodeValue = REALLOC (node->nodeValue, 4063 node->valueLength + 4064 Tcl_DStringLength (&escData)); 4065 memmove (node->nodeValue + node->valueLength, 4066 Tcl_DStringValue (&escData), 4067 Tcl_DStringLength (&escData)); 4068 node->valueLength += Tcl_DStringLength (&escData); 4069 } else { 4070 node->nodeValue = REALLOC (node->nodeValue, 4071 node->valueLength+length); 4072 memmove (node->nodeValue + node->valueLength, 4073 value, length); 4074 node->valueLength += length; 4075 } 4076 Tcl_DStringFree (&escData); 4077 } 4078 } else { 4079 if (disableOutputEscaping) { 4080 node->nodeFlags |= DISABLE_OUTPUT_ESCAPING; 4081 domEscapeCData (node->nodeValue, node->valueLength, 4082 &escData); 4083 if (Tcl_DStringLength (&escData)) { 4084 FREE (node->nodeValue); 4085 node->nodeValue = 4086 MALLOC (Tcl_DStringLength (&escData) + length); 4087 memmove (node->nodeValue, Tcl_DStringValue (&escData), 4088 Tcl_DStringLength (&escData)); 4089 node->valueLength = Tcl_DStringLength (&escData); 4090 } else { 4091 node->nodeValue = REALLOC (node->nodeValue, 4092 node->valueLength+length); 4093 } 4094 Tcl_DStringFree (&escData); 4095 } else { 4096 node->nodeValue = REALLOC (node->nodeValue, 4097 node->valueLength + length); 4098 } 4099 memmove (node->nodeValue + node->valueLength, value, length); 4100 node->valueLength += length; 4101 } 4102 4103 return OK; 4104} 4105 4106 4107/* 4108 *---------------------------------------------------------------------- 4109 * 4110 * domNormalize -- 4111 * 4112 * This procedure implements the dom method normalize. Puts all 4113 * Text nodes in the full depth of the sub-tree underneath node, 4114 * including attribute nodes, into a "normal" form where only 4115 * structure (e.g., elements, comments, processing instructions, 4116 * CDATA sections, and entity references) separates Text nodes, 4117 * i.e., there are neither adjacent Text nodes nor empty Text 4118 * nodes. If the flag forXPath is true, then CDATASection nodes 4119 * are treated as if they are text nodes (and merged with 4120 * circumjacent text nodes). Node must be an ELEMENT_NODE. 4121 * 4122 * Results: 4123 * None. 4124 * 4125 * Side effects: 4126 * May alter the tree. 4127 * 4128 *---------------------------------------------------------------------- 4129 */ 4130 4131void 4132domNormalize ( 4133 domNode *node, /* root of the sub-tree to normalize */ 4134 int forXPath, /* if true, treat CDATA_SECTION_NODEs as if 4135 they where TEXT_NODEs */ 4136 domFreeCallback freeCB, /* Function to call, if a node must be freed */ 4137 void *clientData /* ClientData, to provide to the freeCB */ 4138 4139 ) 4140{ 4141 domNode *child, *nextChild; 4142 int merge = 0; 4143 4144 if (node->nodeType != ELEMENT_NODE) return; 4145 4146 child = node->firstChild; 4147 while (child) { 4148 merge = 0; 4149 switch (child->nodeType) { 4150 case ELEMENT_NODE: 4151 domNormalize (child, forXPath, freeCB, clientData); 4152 break; 4153 case TEXT_NODE: 4154 if (child->previousSibling 4155 && child->previousSibling->nodeType == TEXT_NODE) { 4156 merge = 1; 4157 } else { 4158 if (((domTextNode *)child)->valueLength == 0) { 4159 nextChild = child->nextSibling; 4160 domDeleteNode (child, freeCB, clientData); 4161 child = nextChild; 4162 continue; 4163 } 4164 } 4165 break; 4166 case CDATA_SECTION_NODE: 4167 if (forXPath) { 4168 if (child->previousSibling 4169 && child->previousSibling->nodeType == TEXT_NODE) { 4170 merge = 1; 4171 } else { 4172 if (((domTextNode *)child)->valueLength == 0) { 4173 nextChild = child->nextSibling; 4174 domDeleteNode (child, freeCB, clientData); 4175 child = nextChild; 4176 continue; 4177 } 4178 child->nodeType = TEXT_NODE; 4179 } 4180 } 4181 break; 4182 default: 4183 break; 4184 } 4185 if (merge) { 4186 domAppendData ( (domTextNode *)(child->previousSibling), 4187 ((domTextNode *)child)->nodeValue, 4188 ((domTextNode *)child)->valueLength, 4189 (child->nodeFlags & DISABLE_OUTPUT_ESCAPING) ); 4190 nextChild = child->nextSibling; 4191 domDeleteNode (child, freeCB, clientData); 4192 child = nextChild; 4193 } else { 4194 child = child->nextSibling; 4195 } 4196 } 4197} 4198 4199/*--------------------------------------------------------------------------- 4200| domAddNSToNode 4201| 4202\--------------------------------------------------------------------------*/ 4203domNS * 4204domAddNSToNode ( 4205 domNode *node, 4206 domNS *nsToAdd 4207 ) 4208{ 4209 domAttrNode *attr, *lastNSAttr; 4210 domNS *ns, noNS; 4211 Tcl_HashEntry *h; 4212 int hnew; 4213 Tcl_DString dStr; 4214 4215 if (!nsToAdd) { 4216 noNS.uri = ""; 4217 noNS.prefix = ""; 4218 noNS.index = 0; 4219 nsToAdd = &noNS; 4220 } 4221 DBG(fprintf (stderr, "domAddNSToNode to node '%s': prefix: %s, uri: %s\n", node->nodeName, nsToAdd->prefix, nsToAdd->uri);) 4222 4223 ns = domLookupPrefix (node, nsToAdd->prefix); 4224 if (ns) { 4225 if (strcmp (ns->uri, nsToAdd->uri)==0) { 4226 /* namespace already in scope, we're done. */ 4227 return ns; 4228 } 4229 } else { 4230 /* If the NS to set was no NS and there isn't a default NS 4231 we're done */ 4232 if (nsToAdd->prefix[0] == '\0' && nsToAdd->uri[0] == '\0') return NULL; 4233 } 4234 ns = domNewNamespace (node->ownerDocument, nsToAdd->prefix, nsToAdd->uri); 4235 Tcl_DStringInit (&dStr); 4236 if (nsToAdd->prefix[0] == '\0') { 4237 Tcl_DStringAppend (&dStr, "xmlns", 5); 4238 } else { 4239 Tcl_DStringAppend (&dStr, "xmlns:", 6); 4240 Tcl_DStringAppend (&dStr, nsToAdd->prefix, -1); 4241 } 4242 /* Add new namespace attribute */ 4243 attr = (domAttrNode*) domAlloc(sizeof(domAttrNode)); 4244 memset(attr, 0, sizeof(domAttrNode)); 4245 h = Tcl_CreateHashEntry(&HASHTAB(node->ownerDocument,tdom_attrNames), 4246 Tcl_DStringValue(&dStr), &hnew); 4247 attr->nodeType = ATTRIBUTE_NODE; 4248 attr->nodeFlags = IS_NS_NODE; 4249 attr->namespace = ns->index; 4250 attr->nodeName = (char *)&(h->key); 4251 attr->parentNode = node; 4252 attr->valueLength = strlen(nsToAdd->uri); 4253 attr->nodeValue = (char*)MALLOC(attr->valueLength+1); 4254 strcpy(attr->nodeValue, nsToAdd->uri); 4255 4256 lastNSAttr = NULL; 4257 if (node->firstAttr && (node->firstAttr->nodeFlags & IS_NS_NODE)) { 4258 lastNSAttr = node->firstAttr; 4259 while (lastNSAttr->nextSibling 4260 && (lastNSAttr->nextSibling->nodeFlags & IS_NS_NODE)) { 4261 lastNSAttr = lastNSAttr->nextSibling; 4262 } 4263 } 4264 if (lastNSAttr) { 4265 attr->nextSibling = lastNSAttr->nextSibling; 4266 lastNSAttr->nextSibling = attr; 4267 } else { 4268 attr->nextSibling = node->firstAttr; 4269 node->firstAttr = attr; 4270 } 4271 Tcl_DStringFree (&dStr); 4272 return ns; 4273} 4274 4275/*--------------------------------------------------------------------------- 4276| domAppendLiteralNode 4277| 4278\--------------------------------------------------------------------------*/ 4279domNode * 4280domAppendLiteralNode( 4281 domNode *parent, 4282 domNode *literalNode 4283) 4284{ 4285 Tcl_HashEntry *h; 4286 domNode *node; 4287 int hnew; 4288 4289 if (parent == NULL) { 4290 DBG(fprintf(stderr, "dom.c: Error parent == NULL!\n");) 4291 return NULL; 4292 } 4293 4294 h = Tcl_CreateHashEntry(&HASHTAB(parent->ownerDocument, tdom_tagNames), 4295 literalNode->nodeName, &hnew); 4296 node = (domNode*) domAlloc(sizeof(domNode)); 4297 memset(node, 0, sizeof(domNode)); 4298 node->nodeType = ELEMENT_NODE; 4299 node->nodeNumber = NODE_NO(parent->ownerDocument); 4300 node->ownerDocument = parent->ownerDocument; 4301 node->nodeName = (char *)&(h->key); 4302 4303 if (parent->lastChild) { 4304 parent->lastChild->nextSibling = node; 4305 node->previousSibling = parent->lastChild; 4306 } else { 4307 parent->firstChild = node; 4308 node->previousSibling = NULL; 4309 } 4310 parent->lastChild = node; 4311 node->nextSibling = NULL; 4312 if (parent != parent->ownerDocument->rootNode) { 4313 node->parentNode = parent; 4314 } 4315 4316 MutationEvent(); 4317 return node; 4318} 4319 4320 4321/*--------------------------------------------------------------------------- 4322| domNewProcessingInstructionNode 4323| 4324\--------------------------------------------------------------------------*/ 4325domProcessingInstructionNode * 4326domNewProcessingInstructionNode( 4327 domDocument *doc, 4328 const char *targetValue, 4329 int targetLength, 4330 const char *dataValue, 4331 int dataLength 4332) 4333{ 4334 domProcessingInstructionNode *node; 4335 4336 node = (domProcessingInstructionNode*) domAlloc(sizeof(domProcessingInstructionNode)); 4337 memset(node, 0, sizeof(domProcessingInstructionNode)); 4338 node->nodeType = PROCESSING_INSTRUCTION_NODE; 4339 node->nodeFlags = 0; 4340 node->namespace = 0; 4341 node->nodeNumber = NODE_NO(doc); 4342 node->ownerDocument = doc; 4343 node->targetLength = targetLength; 4344 node->targetValue = (char*)MALLOC(targetLength); 4345 memmove(node->targetValue, targetValue, targetLength); 4346 4347 node->dataLength = dataLength; 4348 node->dataValue = (char*)MALLOC(dataLength); 4349 memmove(node->dataValue, dataValue, dataLength); 4350 4351 if (doc->fragments) { 4352 node->nextSibling = doc->fragments; 4353 doc->fragments->previousSibling = (domNode*)node; 4354 doc->fragments = (domNode*)node; 4355 } else { 4356 doc->fragments = (domNode*)node; 4357 4358 } 4359 MutationEvent(); 4360 return node; 4361} 4362 4363 4364/*--------------------------------------------------------------------------- 4365| domNewElementNode 4366| 4367\--------------------------------------------------------------------------*/ 4368domNode * 4369domNewElementNode( 4370 domDocument *doc, 4371 const char *tagName, 4372 domNodeType nodeType 4373) 4374{ 4375 domNode *node; 4376 Tcl_HashEntry *h; 4377 int hnew; 4378 4379 h = Tcl_CreateHashEntry(&HASHTAB(doc, tdom_tagNames), tagName, &hnew); 4380 node = (domNode*) domAlloc(sizeof(domNode)); 4381 memset(node, 0, sizeof(domNode)); 4382 node->nodeType = nodeType; 4383 node->nodeFlags = 0; 4384 node->namespace = 0; 4385 node->nodeNumber = NODE_NO(doc); 4386 node->ownerDocument = doc; 4387 node->nodeName = (char *)&(h->key); 4388 4389 if (doc->fragments) { 4390 node->nextSibling = doc->fragments; 4391 doc->fragments->previousSibling = node; 4392 doc->fragments = node; 4393 } else { 4394 doc->fragments = node; 4395 4396 } 4397 return node; 4398} 4399 4400 4401/*--------------------------------------------------------------------------- 4402| domNewElementNodeNS 4403| 4404\--------------------------------------------------------------------------*/ 4405domNode * 4406domNewElementNodeNS ( 4407 domDocument *doc, 4408 const char *tagName, 4409 const char *uri, 4410 domNodeType nodeType 4411) 4412{ 4413 domNode *node; 4414 Tcl_HashEntry *h; 4415 int hnew; 4416 char prefix[MAX_PREFIX_LEN]; 4417 const char *localname; 4418 domNS *ns; 4419 4420 h = Tcl_CreateHashEntry(&HASHTAB(doc, tdom_tagNames), tagName, &hnew); 4421 node = (domNode*) domAlloc(sizeof(domNode)); 4422 memset(node, 0, sizeof(domNode)); 4423 node->nodeType = nodeType; 4424 node->nodeFlags = 0; 4425 node->namespace = 0; 4426 node->nodeNumber = NODE_NO(doc); 4427 node->ownerDocument = doc; 4428 node->nodeName = (char *)&(h->key); 4429 4430 domSplitQName (tagName, prefix, &localname); 4431 ns = domNewNamespace(doc, prefix, uri); 4432 node->namespace = ns->index; 4433 4434 if (doc->fragments) { 4435 node->nextSibling = doc->fragments; 4436 doc->fragments->previousSibling = node; 4437 doc->fragments = node; 4438 } else { 4439 doc->fragments = node; 4440 4441 } 4442 return node; 4443} 4444 4445/*--------------------------------------------------------------------------- 4446| domCloneNode 4447| 4448\--------------------------------------------------------------------------*/ 4449domNode * 4450domCloneNode ( 4451 domNode *node, 4452 int deep 4453) 4454{ 4455 domAttrNode *attr, *nattr; 4456 domNode *n, *child, *newChild; 4457 4458 /*------------------------------------------------------------------ 4459 | create new node 4460 \-----------------------------------------------------------------*/ 4461 if (node->nodeType == PROCESSING_INSTRUCTION_NODE) { 4462 domProcessingInstructionNode *pinode = (domProcessingInstructionNode*)node; 4463 return (domNode*) domNewProcessingInstructionNode( 4464 pinode->ownerDocument, 4465 pinode->targetValue, 4466 pinode->targetLength, 4467 pinode->dataValue, 4468 pinode->dataLength); 4469 } 4470 if (node->nodeType != ELEMENT_NODE) { 4471 domTextNode *tnode = (domTextNode*)node; 4472 return (domNode*) domNewTextNode(tnode->ownerDocument, 4473 tnode->nodeValue, tnode->valueLength, 4474 tnode->nodeType); 4475 } 4476 4477 n = domNewElementNode(node->ownerDocument, node->nodeName, node->nodeType); 4478 n->namespace = node->namespace; 4479 4480 4481 /*------------------------------------------------------------------ 4482 | copy attributes (if any) 4483 \-----------------------------------------------------------------*/ 4484 attr = node->firstAttr; 4485 while (attr != NULL) { 4486 nattr = domSetAttribute (n, attr->nodeName, attr->nodeValue ); 4487 nattr->namespace = attr->namespace; 4488 if (attr->nodeFlags & IS_NS_NODE) { 4489 nattr->nodeFlags |= IS_NS_NODE; 4490 } 4491 attr = attr->nextSibling; 4492 } 4493 4494 if (deep) { 4495 child = node->firstChild; 4496 while (child) { 4497 newChild = domCloneNode(child, deep); 4498 4499 /* append new (cloned)child to cloned node, its new parent. 4500 Don't use domAppendChild for this, because that would 4501 mess around with the namespaces */ 4502 if (n->ownerDocument->fragments->nextSibling) { 4503 n->ownerDocument->fragments = 4504 n->ownerDocument->fragments->nextSibling; 4505 n->ownerDocument->fragments->previousSibling = NULL; 4506 newChild->nextSibling = NULL; 4507 } else { 4508 n->ownerDocument->fragments = NULL; 4509 } 4510 if (n->firstChild) { 4511 newChild->previousSibling = n->lastChild; 4512 n->lastChild->nextSibling = newChild; 4513 } else { 4514 n->firstChild = newChild; 4515 } 4516 n->lastChild = newChild; 4517 newChild->parentNode = n; 4518 4519 /* clone next child */ 4520 child = child->nextSibling; 4521 } 4522 } 4523 return n; 4524} 4525 4526/*---------------------------------------------------------------------------- 4527| domCopyNS 4528| 4529\---------------------------------------------------------------------------*/ 4530void 4531domCopyNS ( 4532 domNode *from, 4533 domNode *to 4534 ) 4535{ 4536 domNode *n, *n1; 4537 domNS *ns, *ns1; 4538 domAttrNode *attr, *attr1; 4539 int skip; 4540 4541 n = from; 4542 while (n) { 4543 attr = n->firstAttr; 4544 while (attr && (attr->nodeFlags & IS_NS_NODE)) { 4545 ns = n->ownerDocument->namespaces[attr->namespace-1]; 4546 skip = 0; 4547 n1 = from; 4548 while (n1 != n) { 4549 attr1 = n1->firstAttr; 4550 while (attr1 && (attr1->nodeFlags & IS_NS_NODE)) { 4551 ns1 = n1->ownerDocument->namespaces[attr1->namespace-1]; 4552 if ((ns1->prefix == NULL && ns->prefix == NULL) 4553 || (strcmp (ns1->prefix, ns->prefix)==0)) { 4554 skip = 1; 4555 break; 4556 } 4557 attr1 = attr1->nextSibling; 4558 } 4559 if (skip) break; 4560 n1 = n1->parentNode; 4561 } 4562 if (!skip) { 4563 /* Add this prefix/uri combination only to the 4564 destination, if it isn't already in scope */ 4565 ns1 = domLookupPrefix (to, ns->prefix); 4566 if (!ns1 || (strcmp (ns->uri, ns1->uri)!=0)) { 4567 domAddNSToNode (to, ns); 4568 } 4569 } 4570 attr = attr->nextSibling; 4571 } 4572 n = n->parentNode; 4573 } 4574} 4575 4576 4577/*--------------------------------------------------------------------------- 4578| domCopyTo 4579| 4580\--------------------------------------------------------------------------*/ 4581void 4582domCopyTo ( 4583 domNode *node, 4584 domNode *parent, 4585 int copyNS 4586) 4587{ 4588 domAttrNode *attr, *nattr; 4589 domNode *n, *child; 4590 domNS *ns, *ns1; 4591 4592 /*------------------------------------------------------------------ 4593 | create new node 4594 \-----------------------------------------------------------------*/ 4595 if (node->nodeType == PROCESSING_INSTRUCTION_NODE) { 4596 domProcessingInstructionNode *pinode = (domProcessingInstructionNode*)node; 4597 n = (domNode*) domNewProcessingInstructionNode( 4598 parent->ownerDocument, 4599 pinode->targetValue, 4600 pinode->targetLength, 4601 pinode->dataValue, 4602 pinode->dataLength); 4603 domAppendChild (parent, n); 4604 return; 4605 } 4606 if (node->nodeType != ELEMENT_NODE) { 4607 domTextNode *tnode = (domTextNode*)node; 4608 n = (domNode*) domNewTextNode(parent->ownerDocument, 4609 tnode->nodeValue, tnode->valueLength, 4610 tnode->nodeType); 4611 domAppendChild (parent, n); 4612 return; 4613 } 4614 4615 n = domAppendLiteralNode (parent, node); 4616 if (copyNS) { 4617 domCopyNS (node, n); 4618 } 4619 4620 /*------------------------------------------------------------------ 4621 | copy attributes (if any) 4622 \-----------------------------------------------------------------*/ 4623 attr = node->firstAttr; 4624 while (attr != NULL) { 4625 if (attr->nodeFlags & IS_NS_NODE) { 4626 if (copyNS) { 4627 /* If copyNS is true, then all namespaces in scope 4628 * (including the one declared with the node to copy) 4629 * are allready copied over. */ 4630 attr = attr->nextSibling; 4631 continue; 4632 4633 } 4634 ns = node->ownerDocument->namespaces[attr->namespace-1]; 4635 ns1 = domLookupPrefix (n, ns->prefix); 4636 if (ns1 && strcmp (ns->uri, ns1->uri)==0) { 4637 /* This namespace is already in scope, so we 4638 don't copy the namespace attribute over */ 4639 attr = attr->nextSibling; 4640 continue; 4641 } 4642 nattr = domSetAttribute (n, attr->nodeName, attr->nodeValue ); 4643 nattr->nodeFlags = attr->nodeFlags; 4644 ns1 = domNewNamespace (n->ownerDocument, ns->prefix, ns->uri); 4645 nattr->namespace = ns1->index; 4646 } else { 4647 nattr = domSetAttribute (n, attr->nodeName, attr->nodeValue ); 4648 nattr->nodeFlags = attr->nodeFlags; 4649 if (attr->namespace) { 4650 ns = node->ownerDocument->namespaces[attr->namespace-1]; 4651 ns1 = domLookupPrefix (n, ns->prefix); 4652 if (ns1) { 4653 nattr->namespace = ns1->index; 4654 } 4655 } 4656 } 4657 attr = attr->nextSibling; 4658 } 4659 4660 /* We have to set the node namespace index after copying the 4661 attribute nodes over, because the node may be in a namespace, 4662 which is declared just at the node. */ 4663 if (node->namespace) { 4664 ns = node->ownerDocument->namespaces[node->namespace-1]; 4665 ns1 = domLookupPrefix (n, ns->prefix); 4666 n->namespace = ns1->index; 4667 } 4668 4669 child = node->firstChild; 4670 while (child) { 4671 domCopyTo(child, n, 0); 4672 child = child->nextSibling; 4673 } 4674} 4675 4676 4677/*--------------------------------------------------------------------------- 4678| domXPointerChild 4679| 4680\--------------------------------------------------------------------------*/ 4681int 4682domXPointerChild ( 4683 domNode * node, 4684 int all, 4685 int instance, 4686 domNodeType type, 4687 char * element, 4688 char * attrName, 4689 char * attrValue, 4690 int attrLen, 4691 domAddCallback addCallback, 4692 void * clientData 4693) 4694{ 4695 domNode *child; 4696 domAttrNode *attr; 4697 int i=0, result; 4698 4699 4700 if (node->nodeType != ELEMENT_NODE) { 4701 return 0; 4702 } 4703 4704 if (instance<0) { 4705 child = node->lastChild; 4706 } else { 4707 child = node->firstChild; 4708 } 4709 while (child) { 4710 if ((type == ALL_NODES) || (child->nodeType == type)) { 4711 if ((element == NULL) || 4712 ((child->nodeType == ELEMENT_NODE) && (strcmp(child->nodeName,element)==0)) 4713 ) { 4714 if (attrName == NULL) { 4715 i = (instance<0) ? i-1 : i+1; 4716 if (all || (i == instance)) { 4717 result = addCallback (child, clientData); 4718 if (result) { 4719 return result; 4720 } 4721 } 4722 } else { 4723 attr = child->firstAttr; 4724 while (attr) { 4725 if ((strcmp(attr->nodeName,attrName)==0) && 4726 ( (strcmp(attrValue,"*")==0) || 4727 ( (attr->valueLength == attrLen) && 4728 (strcmp(attr->nodeValue,attrValue)==0) 4729 ) 4730 ) 4731 ) { 4732 i = (instance<0) ? i-1 : i+1; 4733 if (all || (i == instance)) { 4734 result = addCallback (child, clientData); 4735 if (result) { 4736 return result; 4737 } 4738 } 4739 } 4740 attr = attr->nextSibling; 4741 } 4742 } 4743 } 4744 } 4745 if (instance<0) { 4746 child = child->previousSibling; 4747 } else { 4748 child = child->nextSibling; 4749 } 4750 } 4751 return 0; 4752} 4753 4754 4755/*--------------------------------------------------------------------------- 4756| domXPointerXSibling 4757| 4758\--------------------------------------------------------------------------*/ 4759int 4760domXPointerXSibling ( 4761 domNode * node, 4762 int forward_mode, 4763 int all, 4764 int instance, 4765 domNodeType type, 4766 char * element, 4767 char * attrName, 4768 char * attrValue, 4769 int attrLen, 4770 domAddCallback addCallback, 4771 void * clientData 4772) 4773{ 4774 domNode *sibling, *endSibling; 4775 domAttrNode *attr; 4776 int i=0, result; 4777 4778 4779 if (forward_mode) { 4780 if (instance<0) { 4781 endSibling = node; 4782 sibling = node; 4783 if (node->parentNode) { 4784 sibling = node->parentNode->lastChild; 4785 } 4786 } else { 4787 sibling = node->nextSibling; 4788 endSibling = NULL; 4789 } 4790 } else { 4791 if (instance<0) { 4792 endSibling = node; 4793 sibling = node; 4794 if (node->parentNode) { 4795 sibling = node->parentNode->firstChild; 4796 } 4797 } else { 4798 sibling = node->previousSibling; 4799 endSibling = NULL; 4800 } 4801 instance = -1 * instance; 4802 } 4803 4804 while (sibling != endSibling) { 4805 if ((type == ALL_NODES) || (sibling->nodeType == type)) { 4806 if ((element == NULL) || 4807 ((sibling->nodeType == ELEMENT_NODE) && (strcmp(sibling->nodeName,element)==0)) 4808 ) { 4809 if (attrName == NULL) { 4810 i = (instance<0) ? i-1 : i+1; 4811 if (all || (i == instance)) { 4812 result = addCallback (sibling, clientData); 4813 if (result) { 4814 return result; 4815 } 4816 } 4817 } else { 4818 attr = sibling->firstAttr; 4819 while (attr) { 4820 if ((strcmp(attr->nodeName,attrName)==0) && 4821 ( (strcmp(attrValue,"*")==0) || 4822 ( (attr->valueLength == attrLen) && 4823 (strcmp(attr->nodeValue,attrValue)==0) 4824 ) 4825 ) 4826 ) { 4827 i = (instance<0) ? i-1 : i+1; 4828 if (all || (i == instance)) { 4829 result = addCallback (sibling, clientData); 4830 if (result) { 4831 return result; 4832 } 4833 } 4834 } 4835 attr = attr->nextSibling; 4836 } 4837 } 4838 } 4839 } 4840 if (instance<0) { 4841 sibling = sibling->previousSibling; 4842 } else { 4843 sibling = sibling->nextSibling; 4844 } 4845 } 4846 return 0; 4847} 4848 4849 4850/*--------------------------------------------------------------------------- 4851| domXPointerDescendant 4852| 4853\--------------------------------------------------------------------------*/ 4854int 4855domXPointerDescendant ( 4856 domNode * node, 4857 int all, 4858 int instance, 4859 int * i, 4860 domNodeType type, 4861 char * element, 4862 char * attrName, 4863 char * attrValue, 4864 int attrLen, 4865 domAddCallback addCallback, 4866 void * clientData 4867) 4868{ 4869 domNode *child; 4870 domAttrNode *attr; 4871 int found=0, result; 4872 4873 4874 if (node->nodeType != ELEMENT_NODE) { 4875 return 0; 4876 } 4877 4878 if (instance<0) { 4879 child = node->lastChild; 4880 } else { 4881 child = node->firstChild; 4882 } 4883 while (child) { 4884 found = 0; 4885 if ((type == ALL_NODES) || (child->nodeType == type)) { 4886 if ((element == NULL) || 4887 ((child->nodeType == ELEMENT_NODE) && (strcmp(child->nodeName,element)==0)) 4888 ) { 4889 if (attrName == NULL) { 4890 *i = (instance<0) ? (*i)-1 : (*i)+1; 4891 if (all || (*i == instance)) { 4892 result = addCallback (child, clientData); 4893 if (result) { 4894 return result; 4895 } 4896 found = 1; 4897 } 4898 } else { 4899 attr = child->firstAttr; 4900 while (attr) { 4901 if ((strcmp(attr->nodeName,attrName)==0) && 4902 ( (strcmp(attrValue,"*")==0) || 4903 ( (attr->valueLength == attrLen) && 4904 (strcmp(attr->nodeValue,attrValue)==0) 4905 ) 4906 ) 4907 ) { 4908 *i = (instance<0) ? (*i)-1 : (*i)+1; 4909 if (all || (*i == instance)) { 4910 result = addCallback (child, clientData); 4911 if (result) { 4912 return result; 4913 } 4914 found = 1; 4915 } 4916 } 4917 attr = attr->nextSibling; 4918 } 4919 } 4920 } 4921 } 4922 if (!found) { 4923 /* recurs into childs */ 4924 result = domXPointerDescendant (child, all, instance, i, 4925 type, element, attrName, 4926 attrValue, attrLen, 4927 addCallback, clientData); 4928 if (result) { 4929 return result; 4930 } 4931 } 4932 if (instance<0) { 4933 child = child->previousSibling; 4934 } else { 4935 child = child->nextSibling; 4936 } 4937 } 4938 return 0; 4939} 4940 4941 4942/*--------------------------------------------------------------------------- 4943| domXPointerAncestor 4944| 4945\--------------------------------------------------------------------------*/ 4946int 4947domXPointerAncestor ( 4948 domNode * node, 4949 int all, 4950 int instance, 4951 int * i, 4952 domNodeType type, 4953 char * element, 4954 char * attrName, 4955 char * attrValue, 4956 int attrLen, 4957 domAddCallback addCallback, 4958 void * clientData 4959) 4960{ 4961 domNode *ancestor; 4962 domAttrNode *attr; 4963 int found=0, result; 4964 4965 4966 ancestor = node->parentNode; 4967 if (ancestor) { 4968 found = 0; 4969 if ((type == ALL_NODES) || (ancestor->nodeType == type)) { 4970 if ((element == NULL) || 4971 ((ancestor->nodeType == ELEMENT_NODE) && (strcmp(ancestor->nodeName,element)==0)) 4972 ) { 4973 if (attrName == NULL) { 4974 *i = (instance<0) ? (*i)-1 : (*i)+1; 4975 if (all || (*i == instance)) { 4976 result = addCallback (ancestor, clientData); 4977 if (result) { 4978 return result; 4979 } 4980 found = 1; 4981 } 4982 } else { 4983 attr = ancestor->firstAttr; 4984 while (attr) { 4985 if ((strcmp(attr->nodeName,attrName)==0) && 4986 ( (strcmp(attrValue,"*")==0) || 4987 ( (attr->valueLength == attrLen) && 4988 (strcmp(attr->nodeValue,attrValue)==0) 4989 ) 4990 ) 4991 ) { 4992 *i = (instance<0) ? (*i)-1 : (*i)+1; 4993 if (all || (*i == instance)) { 4994 result = addCallback (ancestor, clientData); 4995 if (result) { 4996 return result; 4997 } 4998 found = 1; 4999 } 5000 } 5001 attr = attr->nextSibling; 5002 } 5003 } 5004 } 5005 } 5006 5007 /* go up */ 5008 result = domXPointerAncestor (ancestor, all, instance, i, 5009 type, element, attrName, 5010 attrValue, attrLen, 5011 addCallback, clientData); 5012 if (result) { 5013 return result; 5014 } 5015 } 5016 return 0; 5017} 5018 5019 5020 5021/*--------------------------------------------------------------------------- 5022| type tdomCmdReadInfo 5023| 5024\--------------------------------------------------------------------------*/ 5025typedef struct _tdomCmdReadInfo { 5026 5027 XML_Parser parser; 5028 domDocument *document; 5029 domNode *currentNode; 5030 int depth; 5031 int ignoreWhiteSpaces; 5032 Tcl_DString *cdata; 5033 TEncoding *encoding_8bit; 5034 int storeLineColumn; 5035 int feedbackAfter; 5036 int lastFeedbackPosition; 5037 Tcl_Interp *interp; 5038 int activeNSsize; 5039 int activeNSpos; 5040 domActiveNS *activeNS; 5041 int baseURIstackSize; 5042 int baseURIstackPos; 5043 domActiveBaseURI *baseURIstack; 5044 int insideDTD; 5045 /* Now the tdom cmd specific elements */ 5046 int tdomStatus; 5047 Tcl_Obj *extResolver; 5048 5049} tdomCmdReadInfo; 5050 5051EXTERN int tcldom_returnDocumentObj (Tcl_Interp *interp, 5052 domDocument *document, 5053 int setVariable, Tcl_Obj *var_name, 5054 int trace, int forOwnerDocument); 5055 5056void 5057tdom_freeProc ( 5058 Tcl_Interp *interp, 5059 void *userData 5060) 5061{ 5062 tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData; 5063 5064 if (info->document) { 5065 domFreeDocument (info->document, NULL, NULL); 5066 } 5067 if (info->activeNS) { 5068 FREE (info->activeNS); 5069 } 5070 if (info->baseURIstack) { 5071 FREE (info->baseURIstack); 5072 } 5073 5074 Tcl_DStringFree (info->cdata); 5075 FREE (info->cdata); 5076 if (info->extResolver) { 5077 Tcl_DecrRefCount (info->extResolver); 5078 } 5079 FREE (info); 5080} 5081 5082void 5083tdom_parserResetProc ( 5084 XML_Parser parser, 5085 void *userData 5086) 5087{ 5088 tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData; 5089 5090 info->parser = parser; 5091} 5092 5093void 5094tdom_resetProc ( 5095 Tcl_Interp *interp, 5096 void *userData 5097) 5098{ 5099 tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData; 5100 5101 if (!info->tdomStatus) return; 5102 5103 if (info->document) { 5104 domFreeDocument (info->document, NULL, NULL); 5105 } 5106 5107 info->document = NULL; 5108 info->currentNode = NULL; 5109 info->depth = 0; 5110 info->feedbackAfter = 0; 5111 Tcl_DStringSetLength (info->cdata, 0); 5112 info->lastFeedbackPosition = 0; 5113 info->interp = interp; 5114 info->activeNSpos = -1; 5115 info->insideDTD = 0; 5116 info->baseURIstackPos = 0; 5117 info->tdomStatus = 0; 5118 5119} 5120 5121void 5122tdom_initParseProc ( 5123 Tcl_Interp *interp, 5124 void *userData 5125 ) 5126{ 5127 tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData; 5128 5129 info->document = domCreateDoc(XML_GetBase (info->parser), 5130 info->storeLineColumn); 5131 if (info->extResolver) { 5132 info->document->extResolver = 5133 tdomstrdup (Tcl_GetString (info->extResolver)); 5134 } 5135 info->baseURIstack[0].baseURI = XML_GetBase (info->parser); 5136 info->baseURIstack[0].depth = 0; 5137 info->tdomStatus = 2; 5138 5139} 5140 5141static void 5142tdom_charDataHandler ( 5143 void *userData, 5144 const char *s, 5145 int len 5146) 5147{ 5148 domReadInfo *info = userData; 5149 5150 Tcl_DStringAppend (info->cdata, s, len); 5151 DispatchPCDATA (info); 5152 return; 5153} 5154 5155int 5156TclTdomObjCmd (dummy, interp, objc, objv) 5157 ClientData dummy; 5158 Tcl_Interp *interp; 5159 int objc; 5160 Tcl_Obj *CONST objv[]; 5161{ 5162 char *method, *encodingName; 5163 CHandlerSet *handlerSet; 5164 int methodIndex, result, bool; 5165 tdomCmdReadInfo *info; 5166 TclGenExpatInfo *expat; 5167 Tcl_Obj *newObjName = NULL; 5168 TEncoding *encoding; 5169 5170 static CONST84 char *tdomMethods[] = { 5171 "enable", "getdoc", 5172 "setResultEncoding", "setStoreLineColumn", 5173 "setExternalEntityResolver", "keepEmpties", 5174 "remove", 5175 NULL 5176 }; 5177 enum tdomMethod { 5178 m_enable, m_getdoc, 5179 m_setResultEncoding, m_setStoreLineColumn, 5180 m_setExternalEntityResolver, m_keepEmpties, 5181 m_remove 5182 }; 5183 5184 if (objc < 3 || objc > 4) { 5185 Tcl_WrongNumArgs (interp, 1, objv, tdom_usage); 5186 return TCL_ERROR; 5187 } 5188 5189 if (!CheckExpatParserObj (interp, objv[1])) { 5190 Tcl_SetResult (interp, "First argument has to be a expat parser object", NULL); 5191 return TCL_ERROR; 5192 } 5193 5194 method = Tcl_GetString(objv[2]); 5195 if (Tcl_GetIndexFromObj (interp, objv[2], tdomMethods, "method", 0, 5196 &methodIndex) != TCL_OK) 5197 { 5198 Tcl_SetResult (interp, tdom_usage, NULL); 5199 return TCL_ERROR; 5200 } 5201 5202 switch ((enum tdomMethod) methodIndex) { 5203 5204 default: 5205 Tcl_SetResult (interp, "unknown method", NULL); 5206 return TCL_ERROR; 5207 5208 case m_enable: 5209 handlerSet = CHandlerSetCreate ("tdom"); 5210 handlerSet->ignoreWhiteCDATAs = 1; 5211 handlerSet->resetProc = tdom_resetProc; 5212 handlerSet->freeProc = tdom_freeProc; 5213 handlerSet->parserResetProc = tdom_parserResetProc; 5214 handlerSet->initParseProc = tdom_initParseProc; 5215 handlerSet->elementstartcommand = startElement; 5216 handlerSet->elementendcommand = endElement; 5217 handlerSet->datacommand = tdom_charDataHandler; 5218/* handlerSet->datacommand = characterDataHandler; */ 5219 handlerSet->commentCommand = commentHandler; 5220 handlerSet->picommand = processingInstructionHandler; 5221 handlerSet->entityDeclCommand = entityDeclHandler; 5222 handlerSet->startDoctypeDeclCommand = startDoctypeDeclHandler; 5223 handlerSet->endDoctypeDeclCommand = endDoctypeDeclHandler; 5224 5225 expat = GetExpatInfo (interp, objv[1]); 5226 5227 info = (tdomCmdReadInfo *) MALLOC (sizeof (tdomCmdReadInfo)); 5228 info->parser = expat->parser; 5229 info->document = NULL; 5230 info->currentNode = NULL; 5231 info->depth = 0; 5232 info->ignoreWhiteSpaces = 1; 5233 info->cdata = (Tcl_DString*) MALLOC (sizeof (Tcl_DString)); 5234 Tcl_DStringInit (info->cdata); 5235 info->encoding_8bit = 0; 5236 info->storeLineColumn = 0; 5237 info->feedbackAfter = 0; 5238 info->lastFeedbackPosition = 0; 5239 info->interp = interp; 5240 info->activeNSpos = -1; 5241 info->activeNSsize = 8; 5242 info->activeNS = 5243 (domActiveNS*) MALLOC(sizeof(domActiveNS) * info->activeNSsize); 5244 info->baseURIstackPos = 0; 5245 info->baseURIstackSize = INITIAL_BASEURISTACK_SIZE; 5246 info->baseURIstack = (domActiveBaseURI*) 5247 MALLOC (sizeof(domActiveBaseURI) * info->baseURIstackSize); 5248 info->insideDTD = 0; 5249 info->tdomStatus = 0; 5250 info->extResolver = NULL; 5251 5252 handlerSet->userData = info; 5253 5254 CHandlerSetInstall (interp, objv[1], handlerSet); 5255 break; 5256 5257 case m_getdoc: 5258 info = CHandlerSetGetUserData (interp, objv[1], "tdom"); 5259 if (!info) { 5260 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); 5261 return TCL_ERROR; 5262 } 5263 expat = GetExpatInfo (interp, objv[1]); 5264 if (info->tdomStatus != 2 || !expat->finished) { 5265 Tcl_SetResult (interp, "No DOM tree avaliable.", NULL); 5266 return TCL_ERROR; 5267 } 5268 domSetDocumentElement (info->document); 5269 result = tcldom_returnDocumentObj (interp, info->document, 0, 5270 newObjName, 0, 0); 5271 info->document = NULL; 5272 return result; 5273 5274 case m_setResultEncoding: 5275 info = CHandlerSetGetUserData (interp, objv[1], "tdom"); 5276 if (!info) { 5277 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); 5278 return TCL_ERROR; 5279 } 5280 if (info->encoding_8bit == NULL) { 5281 Tcl_AppendResult (interp, "UTF-8", NULL); 5282 } 5283 else { 5284 Tcl_AppendResult (interp, 5285 tdom_GetEncodingName (info->encoding_8bit), 5286 NULL); 5287 } 5288 if (objc == 4) { 5289 encodingName = Tcl_GetString(objv[3]); 5290 5291 if ( (strcmp(encodingName, "UTF-8") == 0) 5292 ||(strcmp(encodingName, "UTF8" ) == 0) 5293 ||(strcmp(encodingName, "utf-8") == 0) 5294 ||(strcmp(encodingName, "utf8" ) == 0)) { 5295 5296 info->encoding_8bit = NULL; 5297 } else { 5298 encoding = tdom_GetEncoding ( encodingName ); 5299 if (encoding == NULL) { 5300 Tcl_AppendResult(interp, "encoding not found", NULL); 5301 return TCL_ERROR; 5302 } 5303 info->encoding_8bit = encoding; 5304 } 5305 } 5306 info->tdomStatus = 1; 5307 break; 5308 5309 case m_setStoreLineColumn: 5310 info = CHandlerSetGetUserData (interp, objv[1], "tdom"); 5311 if (!info) { 5312 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); 5313 return TCL_ERROR; 5314 } 5315 Tcl_SetIntObj (Tcl_GetObjResult (interp), info->storeLineColumn); 5316 if (objc == 4) { 5317 Tcl_GetBooleanFromObj (interp, objv[3], &bool); 5318 info->storeLineColumn = bool; 5319 } 5320 info->tdomStatus = 1; 5321 break; 5322 5323 case m_remove: 5324 result = CHandlerSetRemove (interp, objv[1], "tdom"); 5325 if (result == 2) { 5326 Tcl_SetResult (interp, "expat parser obj hasn't a C handler set named \"tdom\"", NULL); 5327 return TCL_ERROR; 5328 } 5329 break; 5330 5331 case m_setExternalEntityResolver: 5332 if (objc != 4) { 5333 Tcl_SetResult (interp, "You must name a tcl command as external entity resolver for setExternalEntityResolver.", NULL); 5334 return TCL_ERROR; 5335 } 5336 info = CHandlerSetGetUserData (interp, objv[1], "tdom"); 5337 if (!info) { 5338 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); 5339 return TCL_ERROR; 5340 } 5341 if (info->extResolver) { 5342 Tcl_DecrRefCount (info->extResolver); 5343 } 5344 if (strcmp (Tcl_GetString (objv[3]), "") == 0) { 5345 info->extResolver = NULL; 5346 } else { 5347 info->extResolver = objv[3]; 5348 Tcl_IncrRefCount (info->extResolver); 5349 } 5350 info->tdomStatus = 1; 5351 break; 5352 5353 case m_keepEmpties: 5354 if (objc != 4) { 5355 Tcl_SetResult (interp, "wrong # of args for method keepEmpties.", 5356 NULL); 5357 return TCL_ERROR; 5358 } 5359 handlerSet = CHandlerSetGet (interp, objv[1], "tdom"); 5360 info = handlerSet->userData; 5361 if (!info) { 5362 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); 5363 return TCL_ERROR; 5364 } 5365 Tcl_SetIntObj (Tcl_GetObjResult (interp), info->ignoreWhiteSpaces); 5366 Tcl_GetBooleanFromObj (interp, objv[3], &bool); 5367 info->ignoreWhiteSpaces = !bool; 5368 handlerSet->ignoreWhiteCDATAs = !bool; 5369 info->tdomStatus = 1; 5370 break; 5371 } 5372 5373 return TCL_OK; 5374} 5375