/*--------------------------------------------------------------------------- | Copyright (C) 1999 Jochen C. Loewer (loewerj@hotmail.com) +---------------------------------------------------------------------------- | | $Id$ | | | A DOM interface upon the expat XML parser for the C language | according to the W3C recommendation REC-DOM-Level-1-19981001 | | | The contents of this file are subject to the Mozilla Public License | Version 2.0 (the "License"); you may not use this file except in | compliance with the License. You may obtain a copy of the License at | http://www.mozilla.org/MPL/ | | Software distributed under the License is distributed on an "AS IS" | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the | License for the specific language governing rights and limitations | under the License. | | The Original Code is tDOM. | | The Initial Developer of the Original Code is Jochen Loewer | Portions created by Jochen Loewer are Copyright (C) 1998, 1999 | Jochen Loewer. All Rights Reserved. | | Contributor(s): | Sept99 Carsten Zerbst Added comment and processing instructions | nodes. | | June00 Zoran Vasiljevic Made thread-safe. | | 01 Rolf Ade baseURI stuff, ID support, external | entities, tdom command | | | written by Jochen Loewer | April 5, 1999 | \--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------- | Includes | \--------------------------------------------------------------------------*/ #include #include #include #include /* #define DEBUG */ /*---------------------------------------------------------------------------- | Debug Macros | \---------------------------------------------------------------------------*/ #ifdef DEBUG # define DBG(x) x #else # define DBG(x) #endif #define MutationEvent() #define MutationEvent2(type,node) #define MutationEvent3(type,node,relatioNode) #define MCHK(a) if ((a)==NULL) { \ fprintf(stderr, \ "Memory alloc error line: %d",__LINE__); \ exit(1); \ } #define INITIAL_BASEURISTACK_SIZE 4; /*--------------------------------------------------------------------------- | Globals | In threading environment, some are located in domDocument structure | and some are handled differently (domUniqueNodeNr, domUniqueDocNr) | \--------------------------------------------------------------------------*/ #ifndef TCL_THREADS unsigned long domUniqueNodeNr = 0; unsigned long domUniqueDocNr = 0; Tcl_HashTable tdom_tagNames; Tcl_HashTable tdom_attrNames; #endif static int domModuleIsInitialized = 0; TDomThreaded(static Tcl_Mutex initMutex;) static char *domException2StringTable [] = { "OK - no exception", "INDEX_SIZE_ERR", "DOMSTRING_SIZE_ERR", "HIERARCHY_REQUEST_ERR", "WRONG_DOCUMENT_ERR", "INVALID_CHARACTER_ERR", "NO_DATA_ALLOWED_ERR", "NO_MODIFICATION_ALLOWED_ERR", "NOT_FOUND_ERR", "NOT_SUPPORTED_ERR", "INUSE_ATTRIBUTE_ERR" }; static char tdom_usage[] = "Usage tdom , where subCommand can be:\n" " enable \n" " getdoc \n" " setStoreLineColumn \n" ; /*--------------------------------------------------------------------------- | type domBaseURIstackElem | \--------------------------------------------------------------------------*/ typedef struct _domActiveBaseURI { int depth; const char *baseURI; } domActiveBaseURI; /*--------------------------------------------------------------------------- | type domReadInfo | \--------------------------------------------------------------------------*/ typedef struct _domReadInfo { XML_Parser parser; domDocument *document; domNode *currentNode; int depth; int ignoreWhiteSpaces; int cdataSection; Tcl_DString *cdata; int storeLineColumn; int ignorexmlns; int feedbackAfter; Tcl_Obj *feedbackCmd; XML_Index nextFeedbackPosition; Tcl_Interp *interp; int activeNSsize; int activeNSpos; domActiveNS *activeNS; int baseURIstackSize; int baseURIstackPos; domActiveBaseURI *baseURIstack; int insideDTD; int status; } domReadInfo; /*---------------------------------------------------------------------------- | Prototypes | \---------------------------------------------------------------------------*/ static void DispatchPCDATA (domReadInfo *info); #ifndef TCL_THREADS /*--------------------------------------------------------------------------- | domModuleFinalize | \--------------------------------------------------------------------------*/ static void domModuleFinalize(ClientData unused) { Tcl_HashEntry *entryPtr; Tcl_HashSearch search; entryPtr = Tcl_FirstHashEntry(&tdom_tagNames, &search); while (entryPtr) { Tcl_DeleteHashEntry(entryPtr); entryPtr = Tcl_NextHashEntry(&search); } Tcl_DeleteHashTable(&tdom_tagNames); entryPtr = Tcl_FirstHashEntry(&tdom_attrNames, &search); while (entryPtr) { Tcl_DeleteHashEntry(entryPtr); entryPtr = Tcl_NextHashEntry(&search); } Tcl_DeleteHashTable(&tdom_attrNames); return; } #endif /* TCL_THREADS */ /*--------------------------------------------------------------------------- | domModuleInitialize | \--------------------------------------------------------------------------*/ void domModuleInitialize ( ) { if (domModuleIsInitialized == 0) { TDomThreaded(Tcl_MutexLock(&initMutex);) if (domModuleIsInitialized == 0) { domAllocInit(); TDomNotThreaded ( Tcl_InitHashTable(&tdom_tagNames, TCL_STRING_KEYS); Tcl_InitHashTable(&tdom_attrNames, TCL_STRING_KEYS); Tcl_CreateExitHandler(domModuleFinalize, NULL); ) TDomThreaded( Tcl_CreateExitHandler(domLocksFinalize, NULL); ) domModuleIsInitialized = 1; } TDomThreaded(Tcl_MutexUnlock(&initMutex);) } } /*--------------------------------------------------------------------------- | coercion routines for calling from C++ | \--------------------------------------------------------------------------*/ domAttrNode * coerceToAttrNode( domNode *n ) { return (domAttrNode *)n; } domTextNode * coerceToTextNode( domNode *n ) { return (domTextNode *)n; } domProcessingInstructionNode * coerceToProcessingInstructionNode( domNode *n ) { return (domProcessingInstructionNode *)n; } /*--------------------------------------------------------------------------- | domIsNAME | \--------------------------------------------------------------------------*/ int domIsNAME ( const char *name ) { const char *p; p = name; if (!isNameStart(p)) return 0; p += UTF8_CHAR_LEN(*p); while (*p) { if (isNameChar(p)) p += UTF8_CHAR_LEN(*p); else return 0; } return 1; } /*--------------------------------------------------------------------------- | domIsPINAME | \--------------------------------------------------------------------------*/ int domIsPINAME ( const char *name ) { if (strlen (name) == 3 && ((name[0] == 'x') || (name[0] == 'X')) && ((name[1] == 'm') || (name[1] == 'M')) && ((name[2] == 'l') || (name[2] == 'L')) ) { return 0; } return domIsNAME (name); } /*--------------------------------------------------------------------------- | domIsQNAME | \--------------------------------------------------------------------------*/ int domIsQNAME ( const char *name ) { const char *p; p = name; if (!isNCNameStart(p)) return 0; p += UTF8_CHAR_LEN(*p); while (*p) { if (isNCNameChar(p)) p += UTF8_CHAR_LEN(*p); else { if (*p == ':') { p += 1; if (!isNCNameStart(p)) return 0; p += UTF8_CHAR_LEN(*p); break; } else return 0; } } while (*p) { if (isNCNameChar(p)) p += UTF8_CHAR_LEN(*p); else return 0; } return 1; } /*--------------------------------------------------------------------------- | domIsNCNAME | \--------------------------------------------------------------------------*/ int domIsNCNAME ( const char *name ) { const char *p; p = name; if (!isNCNameStart(p)) return 0; p += UTF8_CHAR_LEN(*p); while (*p) { if (isNCNameChar(p)) p += UTF8_CHAR_LEN(*p); else return 0; } return 1; } /*--------------------------------------------------------------------------- | domIsChar | \--------------------------------------------------------------------------*/ int domIsChar ( const char *str ) { const char *p; int clen; p = str; while (*p) { clen = UTF8_CHAR_LEN(*p); if (clen > 4) return 0; if (UTF8_XMLCHAR((unsigned const char *)p,clen)) p += clen; else return 0; } return 1; } /*--------------------------------------------------------------------------- | domIsBMPChar | \--------------------------------------------------------------------------*/ int domIsBMPChar ( const char *str ) { const char *p; int clen; p = str; while (*p) { clen = UTF8_CHAR_LEN(*p); if (clen > 3 || clen == 0) return 0; p += clen; } return 1; } /*--------------------------------------------------------------------------- | domIsComment | \--------------------------------------------------------------------------*/ int domIsComment ( const char *str ) { const char *p; int len, i = 0; p = str; len = strlen (str); while (i < len) { if (*p == '-') { if (i == len - 1) return 0; p++; i++; if (*p == '-') return 0; } p++; i++; } return domIsChar (str); } /*--------------------------------------------------------------------------- | domIsCDATA | \--------------------------------------------------------------------------*/ int domIsCDATA ( const char *str ) { const char *p; int len, i = 0; p = str; len = strlen (str); while (i < len - 2) { if ( *p == ']' && p[1] == ']' && p[2] == '>') return 0; p++; i++; } return domIsChar (str); } /*--------------------------------------------------------------------------- | domIsPIValue | \--------------------------------------------------------------------------*/ int domIsPIValue ( const char *str ) { const char *p; int len, i = 0; p = str; len = strlen (str); while (i < len - 1) { if (*p == '?' && p[1] == '>') return 0; p++; i++; } return domIsChar (str); } /*--------------------------------------------------------------------------- | domLookupNamespace | \--------------------------------------------------------------------------*/ domNS * domLookupNamespace ( domDocument *doc, const char *prefix, const char *namespaceURI ) { domNS *ns; int i; if (prefix==NULL) return NULL; for (i = 0; i <= doc->nsptr; i++) { ns = doc->namespaces[i]; if ( (ns->prefix != NULL) && (strcmp(prefix,ns->prefix)==0) && (strcmp(namespaceURI, ns->uri)==0) ) { return ns; } } return NULL; } /* *---------------------------------------------------------------------- * * domPrecedes -- * * This helper procedure returns if node precedes other with regard * to their position in the document and according to the document * order. The two nodes could be out of the two documents. Both * nodes must not be out of the fragments list. * * Results: * 1 if node precedes other in document order, 0 otherwise. * * Side effects: * None. * *---------------------------------------------------------------------- */ int domPrecedes ( domNode *node, domNode *other ) { domNode *nodeAncestor, *otherAncestor; domAttrNode *attrN, *attrO; if (node == other) { return 0; } if (node->nodeType == ATTRIBUTE_NODE) { attrN = (domAttrNode*)node; if (other->nodeType == ATTRIBUTE_NODE) { attrO = (domAttrNode*)other; if (attrN->parentNode == attrO->parentNode) { attrN = attrN->nextSibling; while (attrN) { if (attrN == attrO) { return 1; } attrN = attrN->nextSibling; } return 0; } else { node = attrN->parentNode; other = attrO->parentNode; } } else { if (attrN->parentNode == other) { return 0; } else { node = attrN->parentNode; } } } if (other->nodeType == ATTRIBUTE_NODE) { attrO = (domAttrNode*)other; if (node == attrO->parentNode) { return 1; } else { other = attrO->parentNode; } } if (node->ownerDocument != other->ownerDocument) { /* For mt tdom, this does not, what it should: whatever relative order two nodes out of different documents ever have (that is not determined by the rec) it must return always the same order (that is required by the rec). */ return (node->ownerDocument->documentNumber < other->ownerDocument->documentNumber); } #ifndef TCL_THREADS if (node->ownerDocument->nodeFlags & NEEDS_RENUMBERING) { domRenumberTree (node->ownerDocument->rootNode); node->ownerDocument->nodeFlags &= ~NEEDS_RENUMBERING; } return (node->nodeNumber < other->nodeNumber); # else if (node->ownerDocument->nodeFlags & NEEDS_RENUMBERING && node->ownerDocument->refCount <= 1) { domRenumberTree (node->ownerDocument->rootNode); node->ownerDocument->nodeFlags &= ~NEEDS_RENUMBERING; } if (!(node->ownerDocument->nodeFlags & NEEDS_RENUMBERING)) { return (node->nodeNumber < other->nodeNumber); } #endif otherAncestor = other; while (otherAncestor->parentNode) { otherAncestor = otherAncestor->parentNode; if (otherAncestor == node) { return 1; } } nodeAncestor = node; while (nodeAncestor->parentNode) { otherAncestor = other; while (otherAncestor->parentNode) { if (nodeAncestor->parentNode == otherAncestor->parentNode) { nodeAncestor = nodeAncestor->nextSibling; while (nodeAncestor) { if (nodeAncestor == otherAncestor) { return 1; } nodeAncestor = nodeAncestor->nextSibling; } return 0; } otherAncestor = otherAncestor->parentNode; } nodeAncestor = nodeAncestor->parentNode; if (nodeAncestor == other) { return 0; } } nodeAncestor = nodeAncestor->nextSibling; while (nodeAncestor) { if (nodeAncestor == otherAncestor) { return 1; } nodeAncestor = nodeAncestor->nextSibling; } if (node == node->ownerDocument->rootNode) { return 1; } return 0; } /*--------------------------------------------------------------------------- | domRenumberTree | \--------------------------------------------------------------------------*/ void domRenumberTree ( domNode *node ) { while (node) { node->nodeNumber = NODE_NO(node->ownerDocument); if (node->nodeType == ELEMENT_NODE) { domRenumberTree (node->firstChild); } node = node->nextSibling; } } /*--------------------------------------------------------------------------- | domLookupPrefixWithMappings | \--------------------------------------------------------------------------*/ const char * domLookupPrefixWithMappings ( domNode *node, const char *prefix, char **prefixMappings ) { int i; domNS *ns; if (prefixMappings) { i = 0; while (prefixMappings[i]) { if (strcmp (prefix, prefixMappings[i]) == 0) { return prefixMappings[i+1]; } i += 2; } } ns = domLookupPrefix (node, prefix); if (ns) return ns->uri; else return NULL; } /*--------------------------------------------------------------------------- | domLookupPrefix | \--------------------------------------------------------------------------*/ domNS * domLookupPrefix ( domNode *node, const char *prefix ) { domAttrNode *NSattr; domNode *orgNode = node; int found; found = 0; while (node) { if (node->firstAttr && !(node->firstAttr->nodeFlags & IS_NS_NODE)) { node = node->parentNode; continue; } NSattr = node->firstAttr; while (NSattr && (NSattr->nodeFlags & IS_NS_NODE)) { if (prefix[0] == '\0') { if (NSattr->nodeName[5] == '\0') { found = 1; break; } } else { if (NSattr->nodeName[5] != '\0' && strcmp (&NSattr->nodeName[6], prefix)==0) { found = 1; break; } } NSattr = NSattr->nextSibling; } if (found) { return domGetNamespaceByIndex (node->ownerDocument, NSattr->namespace); } node = node->parentNode; } if (prefix && (strcmp (prefix, "xml")==0)) { NSattr = orgNode->ownerDocument->rootNode->firstAttr; return domGetNamespaceByIndex (orgNode->ownerDocument, NSattr->namespace); } return NULL; } /*--------------------------------------------------------------------------- | domIsNamespaceInScope | \--------------------------------------------------------------------------*/ int domIsNamespaceInScope ( domActiveNS *NSstack, int NSstackPos, const char *prefix, const char *namespaceURI ) { int i; for (i = NSstackPos; i >= 0; i--) { if (NSstack[i].namespace->prefix[0] && (strcmp(NSstack[i].namespace->prefix, prefix)==0)) { if (strcmp(NSstack[i].namespace->uri, namespaceURI)==0) { /* OK, exactly the same namespace declaration is in scope */ return 1; } else { /* This prefix is currently assigned to another uri, we need a new NS declaration, to override this one */ return 0; } } } return 0; } /*--------------------------------------------------------------------------- | domLookupURI | \--------------------------------------------------------------------------*/ domNS * domLookupURI ( domNode *node, char *uri ) { domAttrNode *NSattr; int found, alreadyHaveDefault; found = 0; alreadyHaveDefault = 0; while (node) { if (node->firstAttr && !(node->firstAttr->nodeFlags & IS_NS_NODE)) { node = node->parentNode; continue; } NSattr = node->firstAttr; while (NSattr && (NSattr->nodeFlags & IS_NS_NODE)) { if (NSattr->nodeName[5] == '\0') { if (!alreadyHaveDefault) { if (strcmp (NSattr->nodeValue, uri)==0) { found = 1; break; } else { alreadyHaveDefault = 1; } } } else { if (strcmp (NSattr->nodeValue, uri)==0) { found = 1; break; } } NSattr = NSattr->nextSibling; } if (found) { return domGetNamespaceByIndex (node->ownerDocument, NSattr->namespace); } node = node->parentNode; } return NULL; } /*--------------------------------------------------------------------------- | domGetNamespaceByIndex | \--------------------------------------------------------------------------*/ domNS * domGetNamespaceByIndex ( domDocument *doc, int nsIndex ) { if (!nsIndex) return NULL; return doc->namespaces[nsIndex-1]; } /*--------------------------------------------------------------------------- | domNewNamespace | \--------------------------------------------------------------------------*/ domNS* domNewNamespace ( domDocument *doc, const char *prefix, const char *namespaceURI ) { domNS *ns = NULL; DBG(fprintf(stderr, "domNewNamespace '%s' --> '%s' \n", prefix, namespaceURI);) ns = domLookupNamespace (doc, prefix, namespaceURI); if (ns != NULL) return ns; doc->nsptr++; #ifdef TDOM_LESS_NS if (doc->nsptr > 254) { DBG(fprintf (stderr, "maximum number of namespaces exceeded!!!\n");) domPanic("domNewNamespace: maximum number of namespaces exceeded!"); } #endif if (doc->nsptr >= doc->nslen) { doc->namespaces = (domNS**) REALLOC ((char*) doc->namespaces, sizeof (domNS*) * 2 * doc->nslen); doc->nslen *= 2; } doc->namespaces[doc->nsptr] = (domNS*)MALLOC (sizeof (domNS)); ns = doc->namespaces[doc->nsptr]; if (prefix == NULL) { ns->prefix = tdomstrdup(""); } else { ns->prefix = tdomstrdup(prefix); } if (namespaceURI == NULL) { ns->uri = tdomstrdup(""); } else { ns->uri = tdomstrdup(namespaceURI); } ns->index = doc->nsptr + 1; return ns; } /*--------------------------------------------------------------------------- | domSplitQName - extract namespace prefix (if any) | \--------------------------------------------------------------------------*/ int domSplitQName ( const char *name, char *prefix, const char **localName ) { const char *s; char *p, *prefixEnd; s = name; p = prefix; prefixEnd = &prefix[MAX_PREFIX_LEN-1]; while (*s && (*s != ':')) { if (p < prefixEnd) *p++ = *s; s++; } if (*s != ':') { *prefix = '\0'; *localName = name; return 0; } *p++ = '\0'; *localName = ++s; DBG(fprintf(stderr, "domSplitName %s -> '%s' '%s'\n", name, prefix, *localName); ) return 1; } /*--------------------------------------------------------------------------- | domNamespaceURI | \--------------------------------------------------------------------------*/ const char * domNamespaceURI ( domNode *node ) { domAttrNode *attr; domNS *ns; if (node->nodeType == ATTRIBUTE_NODE) { attr = (domAttrNode*)node; if (!attr->namespace) return NULL; if (attr->nodeFlags & IS_NS_NODE) return NULL; ns = attr->parentNode->ownerDocument->namespaces[attr->namespace-1]; } else if (node->nodeType == ELEMENT_NODE) { if (!node->namespace) return NULL; ns = node->ownerDocument->namespaces[node->namespace-1]; } else { return NULL; } return ns->uri; } /*--------------------------------------------------------------------------- | domNamespacePrefix | \--------------------------------------------------------------------------*/ const char * domNamespacePrefix ( domNode *node ) { domAttrNode *attr; domNS *ns; if (node->nodeType == ATTRIBUTE_NODE) { attr = (domAttrNode*)node; if (!attr->namespace) return NULL; ns = attr->parentNode->ownerDocument->namespaces[attr->namespace-1]; } else if (node->nodeType == ELEMENT_NODE) { if (!node->namespace) return NULL; ns = node->ownerDocument->namespaces[node->namespace-1]; } else { return NULL; } if (ns) return ns->prefix; return NULL; } /*--------------------------------------------------------------------------- | domGetLocalName | \--------------------------------------------------------------------------*/ const char * domGetLocalName ( const char *nodeName ) { char prefix[MAX_PREFIX_LEN]; const char *localName; domSplitQName (nodeName, prefix, &localName); return localName; } /* *---------------------------------------------------------------------- * * domGetAttributeNodeNS -- * * Search a given node for an attribute with namespace "uri" and * localname "localname". * * Results: * Returns a pointer to the attribute, if there is one with the * given namespace and localname. Otherwise returns NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ domAttrNode * domGetAttributeNodeNS ( domNode *node, /* The attributes of this node are searched for a matching attribute; the node must exist */ const char *uri, /* The namespace of the demanded attribute */ const char *localname /* The localname of the demanded attribute */ ) { domAttrNode *attr; domNS *ns; int noNS; char prefix[MAX_PREFIX_LEN]; const char *attrLocalName; if (uri[0] == '\0') noNS = 1; else noNS = 0; attr = node->firstAttr; while (attr) { if (noNS) { if (!attr->namespace && strcmp (attr->nodeName, localname) == 0) { return attr; } } else { if (attr->namespace) { domSplitQName (attr->nodeName, prefix, &attrLocalName); if (strcmp (localname, attrLocalName) == 0) { ns = domGetNamespaceByIndex (node->ownerDocument, attr->namespace); if (strcmp (ns->uri, uri) == 0) { return attr; } } } } attr = attr->nextSibling; } return NULL; } /* *---------------------------------------------------------------------- * * domPreviousSibling -- * * Returns the previous node to the given node or NULL, if there * is no previous node. This function is needed in situations, * where the given node may also be an domAttrNode. Namespace * declaring attributes are treated as any other * attributes. Since the domAttrNode struct doesn't has an * element for the previous attribute, we need a function for the * relatively rare cases, the 'previous attribute' is * needed. Remember, that the XML rec say, that there is no * specific order of the attributes of a node. * * Results: * A pointer to the previous node of the given one * or NULL, if there isn't a previous node. * * Side effects: * None. * *---------------------------------------------------------------------- */ domNode * domPreviousSibling ( domNode *node /* The reference attribute */ ) { domAttrNode *attr, *attr1; if (node->nodeType != ATTRIBUTE_NODE) { return node->previousSibling; } attr = (domAttrNode*) node; if (attr->parentNode->firstAttr == attr) { return NULL; } attr1 = attr->parentNode->firstAttr; while (attr1) { if (attr1->nextSibling == attr) { return (domNode*)attr1; } attr1 = attr1->nextSibling; } /* Not reached */ return NULL; } #ifndef TDOM_NO_EXPAT /*--------------------------------------------------------------------------- | startElement | \--------------------------------------------------------------------------*/ static void startElement( void *userData, const char *name, const char **atts ) { domReadInfo *info = userData; domNode *node, *parentNode; domLineColumn *lc; domAttrNode *attrnode, *lastAttr; const char **atPtr, **idAttPtr; Tcl_HashEntry *h; int hnew, len, pos, idatt, newNS, result; const char *xmlns, *localname; char tagPrefix[MAX_PREFIX_LEN]; char prefix[MAX_PREFIX_LEN]; domNS *ns; char feedbackCmd[24]; if (info->feedbackAfter) { if (info->nextFeedbackPosition <= XML_GetCurrentByteIndex (info->parser) ) { if (info->feedbackCmd) { result = Tcl_GlobalEvalObj(info->interp, info->feedbackCmd); } else { sprintf(feedbackCmd, "%s", "::dom::domParseFeedback"); result = Tcl_Eval(info->interp, feedbackCmd); } if (result != TCL_OK) { DBG(fprintf(stderr, "%s\n", Tcl_GetStringResult (info->interp));); info->status = result; XML_StopParser(info->parser, 1); return; } info->nextFeedbackPosition = XML_GetCurrentByteIndex (info->parser) + info->feedbackAfter; Tcl_ResetResult (info->interp); } } DispatchPCDATA (info); h = Tcl_CreateHashEntry(&HASHTAB(info->document,tdom_tagNames), name, &hnew); if (info->storeLineColumn) { node = (domNode*) domAlloc(sizeof(domNode) + sizeof(domLineColumn)); } else { node = (domNode*) domAlloc(sizeof(domNode)); } memset(node, 0, sizeof(domNode)); node->nodeType = ELEMENT_NODE; node->nodeName = (char *)&(h->key); node->nodeNumber = NODE_NO(info->document); node->ownerDocument = info->document; if (info->baseURIstack[info->baseURIstackPos].baseURI != XML_GetBase (info->parser)) { h = Tcl_CreateHashEntry (info->document->baseURIs, (char*) node, &hnew); Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser))); node->nodeFlags |= HAS_BASEURI; info->baseURIstackPos++; if (info->baseURIstackPos >= info->baseURIstackSize) { info->baseURIstack = (domActiveBaseURI*) REALLOC( (char*)info->baseURIstack, sizeof(domActiveBaseURI) * 2 * info->baseURIstackSize); info->baseURIstackSize = 2 * info->baseURIstackSize; } info->baseURIstack[info->baseURIstackPos].baseURI = XML_GetBase (info->parser); info->baseURIstack[info->baseURIstackPos].depth = info->depth; } if (info->depth == 0) { if (info->document->rootNode->lastChild) { info->document->rootNode->lastChild->nextSibling = node; node->previousSibling = info->document->rootNode->lastChild; } else { info->document->rootNode->firstChild = node; } info->document->rootNode->lastChild = node; } else { parentNode = info->currentNode; node->parentNode = parentNode; if (parentNode->firstChild) { parentNode->lastChild->nextSibling = node; node->previousSibling = parentNode->lastChild; parentNode->lastChild = node; } else { parentNode->firstChild = parentNode->lastChild = node; } } info->currentNode = node; if (info->storeLineColumn) { lc = (domLineColumn*) ( ((char*)node) + sizeof(domNode)); node->nodeFlags |= HAS_LINE_COLUMN; lc->line = XML_GetCurrentLineNumber(info->parser); lc->column = XML_GetCurrentColumnNumber(info->parser); } lastAttr = NULL; /*-------------------------------------------------------------- | process namespace declarations | \-------------------------------------------------------------*/ if (!info->ignorexmlns) { for (atPtr = atts; atPtr[0] && atPtr[1]; atPtr += 2) { if (strncmp(atPtr[0], "xmlns", 5) == 0) { xmlns = atPtr[0]; newNS = 1; if (xmlns[5] == ':') { if (atPtr[1][0] == '\0') { Tcl_SetResult (info->interp, "Missing URI in Namespace " "declaration", NULL); XML_StopParser(info->parser, 0); return; } if (domIsNamespaceInScope (info->activeNS, info->activeNSpos, &(xmlns[6]), atPtr[1])) { ns = domLookupPrefix (info->currentNode, &(xmlns[6])); newNS = 0; } else { ns = domNewNamespace(info->document, &xmlns[6], atPtr[1]); } } else { ns = domNewNamespace(info->document, "", atPtr[1]); } if (newNS) { /* push active namespace */ info->activeNSpos++; if (info->activeNSpos >= info->activeNSsize) { info->activeNS = (domActiveNS*) REALLOC( (char*)info->activeNS, sizeof(domActiveNS) * 2 * info->activeNSsize); info->activeNSsize = 2 * info->activeNSsize; } info->activeNS[info->activeNSpos].depth = info->depth; info->activeNS[info->activeNSpos].namespace = ns; } h = Tcl_CreateHashEntry(&HASHTAB(info->document, tdom_attrNames), atPtr[0], &hnew); attrnode = (domAttrNode*) domAlloc(sizeof(domAttrNode)); memset(attrnode, 0, sizeof(domAttrNode)); attrnode->nodeType = ATTRIBUTE_NODE; attrnode->nodeFlags = IS_NS_NODE; attrnode->namespace = ns->index; attrnode->nodeName = (char *)&(h->key); attrnode->parentNode = node; len = strlen(atPtr[1]); attrnode->valueLength = len; attrnode->nodeValue = (char*)MALLOC(len+1); strcpy(attrnode->nodeValue, atPtr[1]); if (node->firstAttr) { lastAttr->nextSibling = attrnode; } else { node->firstAttr = attrnode; } lastAttr = attrnode; } } /*---------------------------------------------------------- | look for namespace of element \---------------------------------------------------------*/ domSplitQName (name, tagPrefix, &localname); for (pos = info->activeNSpos; pos >= 0; pos--) { if ( ((tagPrefix[0] == '\0') && (info->activeNS[pos].namespace->prefix[0] == '\0')) || ((tagPrefix[0] != '\0') && (info->activeNS[pos].namespace->prefix[0] != '\0') && (strcmp(tagPrefix, info->activeNS[pos].namespace->prefix) == 0)) ) { if (info->activeNS[pos].namespace->prefix[0] == '\0' && info->activeNS[pos].namespace->uri[0] == '\0' && tagPrefix[0] == '\0') { /* xml-names rec. 5.2: "The default namespace can be set to the empty string. This has the same effect, within the scope of the declaration, of there being no default namespace." */ goto elemNSfound; } node->namespace = info->activeNS[pos].namespace->index; DBG(fprintf(stderr, "tag='%s' uri='%s' \n", node->nodeName, info->activeNS[pos].namespace->uri); ) goto elemNSfound; } } if (tagPrefix[0] != '\0') { if (strcmp (tagPrefix, "xml")==0) { node->namespace = info->document->rootNode->firstAttr->namespace; } else { /* Since where here, this means, the element has a up to now not declared namespace prefix. */ Tcl_SetResult (info->interp, "Namespace prefix is not " "defined", NULL); XML_StopParser(info->parser, 0); return; } } } elemNSfound: /*-------------------------------------------------------------- | add the attribute nodes | \-------------------------------------------------------------*/ if ((idatt = XML_GetIdAttributeIndex (info->parser)) != -1) { if (!info->document->ids) { info->document->ids = MALLOC (sizeof (Tcl_HashTable)); Tcl_InitHashTable (info->document->ids, TCL_STRING_KEYS); } h = Tcl_CreateHashEntry (info->document->ids, atts[idatt+1], &hnew); /* if hnew isn't 1 this is a validation error. Hm, no clear way to report this. And more, XSLT and XPath can process not valid XML, the spec mentioned this even within the context of id(). If some elements share the same ID, the first in document order should be used. Doing it this way, this is guaranteed for unchanged DOM trees. There are problems, if the DOM tree is changed, befor using id() */ if (hnew) { Tcl_SetHashValue (h, node); } idAttPtr = atts + idatt; } else { idAttPtr = NULL; } /* lastAttr already set right, either to NULL above, or to the last NS attribute */ for (atPtr = atts; atPtr[0] && atPtr[1]; atPtr += 2) { if (!info->ignorexmlns) { if (strncmp(atPtr[0], "xmlns", 5) == 0) { continue; } } h = Tcl_CreateHashEntry(&HASHTAB(info->document, tdom_attrNames), atPtr[0], &hnew); attrnode = (domAttrNode*) domAlloc(sizeof(domAttrNode)); memset(attrnode, 0, sizeof(domAttrNode)); attrnode->nodeType = ATTRIBUTE_NODE; if (atPtr == idAttPtr) { attrnode->nodeFlags |= IS_ID_ATTRIBUTE; } attrnode->nodeName = (char *)&(h->key); attrnode->parentNode = node; len = strlen(atPtr[1]); attrnode->valueLength = len; attrnode->nodeValue = (char*)MALLOC(len+1); strcpy(attrnode->nodeValue, (char *)atPtr[1]); if (node->firstAttr) { lastAttr->nextSibling = attrnode; } else { node->firstAttr = attrnode; } lastAttr = attrnode; if (!info->ignorexmlns) { /*---------------------------------------------------------- | look for attribute namespace \---------------------------------------------------------*/ domSplitQName (attrnode->nodeName, prefix, &localname); if (prefix[0] != '\0') { for (pos = info->activeNSpos; pos >= 0; pos--) { if ( ((prefix[0] == '\0') && (info->activeNS[pos].namespace->prefix[0] == '\0')) || ((prefix[0] != '\0') && (info->activeNS[pos].namespace->prefix[0] != '\0') && (strcmp(prefix, info->activeNS[pos].namespace->prefix) == 0)) ) { attrnode->namespace = info->activeNS[pos].namespace->index; DBG(fprintf(stderr, "attr='%s' uri='%s' \n", attrnode->nodeName, info->activeNS[pos].namespace->uri); ) goto attrNSfound; } } if (strcmp (prefix, "xml")==0) { attrnode->namespace = info->document->rootNode->firstAttr->namespace; } else { /* Since where here, this means, the attribute has a up to now not declared namespace prefix. We probably should return this as an error, shouldn't we?*/ } attrNSfound: ; } } } info->depth++; } /*--------------------------------------------------------------------------- | endElement | \--------------------------------------------------------------------------*/ static void endElement ( void *userData, const char *name ) { domReadInfo *info = userData; DispatchPCDATA (info); info->depth--; if (!info->ignorexmlns) { /* pop active namespaces */ while ( (info->activeNSpos >= 0) && (info->activeNS[info->activeNSpos].depth == info->depth) ) { info->activeNSpos--; } } if (info->depth != -1) { info->currentNode = info->currentNode->parentNode; } else { info->currentNode = NULL; } if (info->depth) { if (info->baseURIstack[info->baseURIstackPos].depth == info->depth) { info->baseURIstackPos--; } } } /*--------------------------------------------------------------------------- | characterDataHandler | \--------------------------------------------------------------------------*/ static void characterDataHandler ( void *userData, const char *s, int len ) { domReadInfo *info = userData; Tcl_DStringAppend (info->cdata, s, len); return; } /*--------------------------------------------------------------------------- | startCDATA | \--------------------------------------------------------------------------*/ static void startCDATA ( void *userData ) { domReadInfo *info = userData; DispatchPCDATA (info); info->cdataSection = 1; } /*--------------------------------------------------------------------------- | endCDATA | \--------------------------------------------------------------------------*/ static void endCDATA ( void *userData ) { domReadInfo *info = userData; DispatchPCDATA (info); info->cdataSection = 0; } /*--------------------------------------------------------------------------- | DispatchPCDATA | \--------------------------------------------------------------------------*/ static void DispatchPCDATA ( domReadInfo *info ) { domTextNode *node; domNode *parentNode; domLineColumn *lc; Tcl_HashEntry *h; char *s; int len, hnew; len = Tcl_DStringLength (info->cdata); if (!len && !info->cdataSection) return; s = Tcl_DStringValue (info->cdata); parentNode = info->currentNode; if (!parentNode) return; if ( parentNode->lastChild && parentNode->lastChild->nodeType == TEXT_NODE && !info->cdataSection) { /* normalize text node, i.e. there are no adjacent text nodes */ node = (domTextNode*)parentNode->lastChild; node->nodeValue = REALLOC(node->nodeValue, node->valueLength + len); memmove(node->nodeValue + node->valueLength, s, len); node->valueLength += len; } else { if (info->ignoreWhiteSpaces) { char *pc; int i, only_whites; only_whites = 1; for (i=0, pc = s; i < len; i++, pc++) { if ( (*pc != ' ') && (*pc != '\t') && (*pc != '\n') && (*pc != '\r') ) { only_whites = 0; break; } } if (only_whites) { Tcl_DStringSetLength (info->cdata, 0); return; } } if (info->storeLineColumn) { node = (domTextNode*) domAlloc(sizeof(domTextNode) + sizeof(domLineColumn)); } else { node = (domTextNode*) domAlloc(sizeof(domTextNode)); } memset(node, 0, sizeof(domTextNode)); if (info->cdataSection) node->nodeType = CDATA_SECTION_NODE; else node->nodeType = TEXT_NODE; node->nodeNumber = NODE_NO(info->document); node->valueLength = len; node->nodeValue = (char*)MALLOC(len); memmove(node->nodeValue, s, len); node->ownerDocument = info->document; node->parentNode = parentNode; if (parentNode->nodeType == ELEMENT_NODE) { if (parentNode->firstChild) { parentNode->lastChild->nextSibling = (domNode*)node; node->previousSibling = parentNode->lastChild; } else { parentNode->firstChild = (domNode*)node; } parentNode->lastChild = (domNode*)node; } if (info->baseURIstack[info->baseURIstackPos].baseURI != XML_GetBase (info->parser)) { h = Tcl_CreateHashEntry (info->document->baseURIs, (char*) node, &hnew); Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser))); node->nodeFlags |= HAS_BASEURI; } if (info->storeLineColumn) { lc = (domLineColumn*) ( ((char*)node) + sizeof(domTextNode) ); node->nodeFlags |= HAS_LINE_COLUMN; lc->line = XML_GetCurrentLineNumber(info->parser); lc->column = XML_GetCurrentColumnNumber(info->parser); } } Tcl_DStringSetLength (info->cdata, 0); } /*--------------------------------------------------------------------------- | commentHandler | \--------------------------------------------------------------------------*/ static void commentHandler ( void *userData, const char *s ) { domReadInfo *info = userData; domTextNode *node; domNode *parentNode; domLineColumn *lc; int len, hnew; Tcl_HashEntry *h; if (info->insideDTD) { DBG(fprintf (stderr, "commentHandler: insideDTD, skipping\n");) return; } DispatchPCDATA (info); len = strlen(s); parentNode = info->currentNode; if (info->storeLineColumn) { node = (domTextNode*) domAlloc(sizeof(domTextNode) + sizeof(domLineColumn)); } else { node = (domTextNode*) domAlloc(sizeof(domTextNode)); } memset(node, 0, sizeof(domTextNode)); node->nodeType = COMMENT_NODE; node->nodeNumber = NODE_NO(info->document); node->valueLength = len; node->nodeValue = (char*)MALLOC(len); memmove(node->nodeValue, s, len); node->ownerDocument = info->document; node->parentNode = parentNode; if (parentNode == NULL) { if (info->document->rootNode->lastChild) { info->document->rootNode->lastChild->nextSibling = (domNode*)node; node->previousSibling = info->document->rootNode->lastChild; } else { info->document->rootNode->firstChild = (domNode*)node; } info->document->rootNode->lastChild = (domNode*)node; } else if(parentNode->nodeType == ELEMENT_NODE) { if (parentNode->firstChild) { parentNode->lastChild->nextSibling = (domNode*)node; node->previousSibling = parentNode->lastChild; parentNode->lastChild = (domNode*)node; } else { parentNode->firstChild = parentNode->lastChild = (domNode*)node; } } if (info->baseURIstack[info->baseURIstackPos].baseURI != XML_GetBase (info->parser)) { h = Tcl_CreateHashEntry (info->document->baseURIs, (char*) node, &hnew); Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser))); node->nodeFlags |= HAS_BASEURI; } if (info->storeLineColumn) { lc = (domLineColumn*) ( ((char*)node) + sizeof(domTextNode) ); node->nodeFlags |= HAS_LINE_COLUMN; lc->line = XML_GetCurrentLineNumber(info->parser); lc->column = XML_GetCurrentColumnNumber(info->parser); } } /*--------------------------------------------------------------------------- | processingInstructionHandler | \--------------------------------------------------------------------------*/ static void processingInstructionHandler( void *userData, const char *target, const char *data ) { domProcessingInstructionNode *node; domReadInfo *info = userData; domNode *parentNode; domLineColumn *lc; int len,hnew; Tcl_HashEntry *h; if (info->insideDTD) { DBG(fprintf (stderr, "processingInstructionHandler: insideDTD, skipping\n");) return; } DispatchPCDATA (info); parentNode = info->currentNode; if (info->storeLineColumn) { node = (domProcessingInstructionNode*) domAlloc(sizeof(domProcessingInstructionNode) + sizeof(domLineColumn)); } else { node = (domProcessingInstructionNode*) domAlloc(sizeof(domProcessingInstructionNode)); } memset(node, 0, sizeof(domProcessingInstructionNode)); node->nodeType = PROCESSING_INSTRUCTION_NODE; node->nodeNumber = NODE_NO(info->document); if (info->baseURIstack[info->baseURIstackPos].baseURI != XML_GetBase (info->parser)) { h = Tcl_CreateHashEntry (info->document->baseURIs, (char*) node, &hnew); Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser))); node->nodeFlags |= HAS_BASEURI; } len = strlen(target); node->targetLength = len; node->targetValue = (char*)MALLOC(len); memmove(node->targetValue, target, len); len = strlen(data); node->dataLength = len; node->dataValue = (char*)MALLOC(len); memmove(node->dataValue, data, len); node->ownerDocument = info->document; node->parentNode = parentNode; if (parentNode == NULL) { if (info->document->rootNode->lastChild) { info->document->rootNode->lastChild->nextSibling = (domNode*)node; node->previousSibling = info->document->rootNode->lastChild; } else { info->document->rootNode->firstChild = (domNode*)node; } info->document->rootNode->lastChild = (domNode*)node; } else if(parentNode->nodeType == ELEMENT_NODE) { if (parentNode->firstChild) { parentNode->lastChild->nextSibling = (domNode*)node; node->previousSibling = parentNode->lastChild; parentNode->lastChild = (domNode*)node; } else { parentNode->firstChild = parentNode->lastChild = (domNode*)node; } } if (info->storeLineColumn) { lc = (domLineColumn*)(((char*)node)+sizeof(domProcessingInstructionNode)); node->nodeFlags |= HAS_LINE_COLUMN; lc->line = XML_GetCurrentLineNumber(info->parser); lc->column = XML_GetCurrentColumnNumber(info->parser); } } /*--------------------------------------------------------------------------- | entityDeclHandler | \--------------------------------------------------------------------------*/ static void entityDeclHandler ( void *userData, const char *entityName, int is_parameter_entity, const char *value, int value_length, const char *base, const char *systemId, const char *publicId, const char *notationName ) { domReadInfo *info = (domReadInfo *) userData; Tcl_HashEntry *entryPtr; int hnew; if (notationName) { if (!info->document->unparsedEntities) { info->document->unparsedEntities = MALLOC (sizeof (Tcl_HashTable)); Tcl_InitHashTable (info->document->unparsedEntities, TCL_STRING_KEYS); } entryPtr = Tcl_CreateHashEntry (info->document->unparsedEntities, entityName, &hnew); if (hnew) { Tcl_SetHashValue (entryPtr, tdomstrdup (systemId)); } } } /*--------------------------------------------------------------------------- | externalEntityRefHandler | \--------------------------------------------------------------------------*/ static int externalEntityRefHandler ( XML_Parser parser, const char *openEntityNames, const char *base, const char *systemId, const char *publicId ) { domReadInfo *info = (domReadInfo *) XML_GetUserData (parser); Tcl_Obj *cmdPtr, *resultObj, *resultTypeObj, *extbaseObj, *xmlstringObj; Tcl_Obj *channelIdObj; int result, mode, done, byteIndex, i; int keepresult = 0; size_t len; int tclLen; XML_Parser extparser, oldparser = NULL; char buf[4096], *resultType, *extbase, *xmlstring, *channelId, s[50]; Tcl_Channel chan = (Tcl_Channel) NULL; enum XML_Status status; XML_Index storedNextFeedbackPosition; const char *interpResult; if (info->document->extResolver == NULL) { Tcl_AppendResult (info->interp, "Can't read external entity \"", systemId, "\": No -externalentitycommand given", NULL); return 0; } DispatchPCDATA (info); /* * Take a copy of the callback script so that arguments may be appended. */ cmdPtr = Tcl_NewStringObj(info->document->extResolver, -1); Tcl_IncrRefCount(cmdPtr); if (base) { Tcl_ListObjAppendElement(info->interp, cmdPtr, Tcl_NewStringObj(base, strlen(base))); } else { Tcl_ListObjAppendElement(info->interp, cmdPtr, Tcl_NewObj()); } /* For a document with doctype declaration, the systemId is always != NULL. But if the document doesn't have a doctype declaration and the user uses -useForeignDTD 1, the externalEntityRefHandler will be called with a systemId (and publicId and openEntityNames) == NULL. */ if (systemId) { Tcl_ListObjAppendElement(info->interp, cmdPtr, Tcl_NewStringObj(systemId, strlen(systemId))); } else { Tcl_ListObjAppendElement(info->interp, cmdPtr, Tcl_NewObj()); } if (publicId) { Tcl_ListObjAppendElement(info->interp, cmdPtr, Tcl_NewStringObj(publicId, strlen(publicId))); } else { Tcl_ListObjAppendElement(info->interp, cmdPtr, Tcl_NewObj()); } result = Tcl_EvalObjEx (info->interp, cmdPtr, TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL); Tcl_DecrRefCount(cmdPtr); if (result != TCL_OK) { info->status = result; return 0; } extparser = XML_ExternalEntityParserCreate (parser, openEntityNames, 0); resultObj = Tcl_GetObjResult (info->interp); Tcl_IncrRefCount (resultObj); result = Tcl_ListObjLength (info->interp, resultObj, &tclLen); if ((result != TCL_OK) || (tclLen != 3)) { goto wrongScriptResult; } result = Tcl_ListObjIndex (info->interp, resultObj, 0, &resultTypeObj); if (result != TCL_OK) { goto wrongScriptResult; } resultType = Tcl_GetString(resultTypeObj); if (strcmp (resultType, "string") == 0) { result = Tcl_ListObjIndex (info->interp, resultObj, 2, &xmlstringObj); xmlstring = Tcl_GetString(xmlstringObj); len = strlen (xmlstring); chan = NULL; } else if (strcmp (resultType, "channel") == 0) { xmlstring = NULL; len = 0; result = Tcl_ListObjIndex (info->interp, resultObj, 2, &channelIdObj); channelId = Tcl_GetString(channelIdObj); chan = Tcl_GetChannel (info->interp, channelId, &mode); if (chan == (Tcl_Channel) NULL) { goto wrongScriptResult; } if ((mode & TCL_READABLE) == 0) { return 0; } } else if (strcmp (resultType, "filename") == 0) { /* result type "filename" not yet implemented */ return 0; } else { goto wrongScriptResult; } result = Tcl_ListObjIndex (info->interp, resultObj, 1, &extbaseObj); if (result != TCL_OK) { goto wrongScriptResult; } extbase = Tcl_GetString(extbaseObj); /* TODO: what to do, if this document was already parsed before ? */ if (!extparser) { Tcl_DecrRefCount (resultObj); Tcl_SetResult (info->interp, "unable to create expat external entity parser", NULL); return 0; } oldparser = info->parser; info->parser = extparser; XML_SetBase (extparser, extbase); storedNextFeedbackPosition = info->nextFeedbackPosition; info->nextFeedbackPosition = info->feedbackAfter; Tcl_ResetResult (info->interp); result = 1; if (chan == NULL) { status = XML_Parse(extparser, xmlstring, strlen (xmlstring), 1); switch (status) { case XML_STATUS_ERROR: interpResult = Tcl_GetStringResult(info->interp); sprintf(s, "%ld", XML_GetCurrentLineNumber(extparser)); if (interpResult[0] == '\0') { Tcl_ResetResult (info->interp); Tcl_AppendResult(info->interp, "error \"", XML_ErrorString(XML_GetErrorCode(extparser)), "\" in entity \"", systemId, "\" at line ", s, " character ", NULL); sprintf(s, "%ld", XML_GetCurrentColumnNumber(extparser)); Tcl_AppendResult(info->interp, s, NULL); byteIndex = XML_GetCurrentByteIndex(extparser); if (byteIndex != -1) { Tcl_AppendResult(info->interp, "\n\"", NULL); s[1] = '\0'; for (i=-20; i < 40; i++) { if ((byteIndex+i)>=0) { if (xmlstring[byteIndex+i]) { s[0] = xmlstring[byteIndex+i]; Tcl_AppendResult(info->interp, s, NULL); if (i==0) { Tcl_AppendResult(info->interp, " <--Error-- ", NULL); } } else { break; } } } Tcl_AppendResult(info->interp, "\"",NULL); } } else { Tcl_AppendResult(info->interp, ", referenced in entity \"", systemId, "\" at line ", s, " character ", NULL); sprintf(s, "%ld", XML_GetCurrentColumnNumber(extparser)); Tcl_AppendResult(info->interp, s, NULL); } keepresult = 1; result = 0; break; case XML_STATUS_SUSPENDED: XML_StopParser (oldparser, 1); keepresult = 1; break; default: break; } } else { do { len = Tcl_Read (chan, buf, sizeof(buf)); done = len < sizeof(buf); status = XML_Parse (extparser, buf, len, done); switch (status) { case XML_STATUS_ERROR: interpResult = Tcl_GetStringResult(info->interp); sprintf(s, "%ld", XML_GetCurrentLineNumber(extparser)); if (interpResult[0] == '\0') { Tcl_ResetResult (info->interp); Tcl_AppendResult(info->interp, "error \"", XML_ErrorString(XML_GetErrorCode(extparser)), "\" in entity \"", systemId, "\" at line ", s, " character ", NULL); sprintf(s, "%ld", XML_GetCurrentColumnNumber(extparser)); Tcl_AppendResult(info->interp, s, NULL); } else { Tcl_AppendResult(info->interp, ", referenced in entity \"", systemId, "\" at line ", s, " character ", NULL); sprintf(s, "%ld", XML_GetCurrentColumnNumber(extparser)); Tcl_AppendResult(info->interp, s, NULL); } result = 0; keepresult = 1; done = 1; break; case XML_STATUS_SUSPENDED: XML_StopParser (oldparser, 1); keepresult = 1; done = 1; break; default: break; } } while (!done); } if (result) { DispatchPCDATA (info); } if (!keepresult) { Tcl_ResetResult (info->interp); } XML_ParserFree (extparser); info->parser = oldparser; info->nextFeedbackPosition = storedNextFeedbackPosition; Tcl_DecrRefCount (resultObj); return result; wrongScriptResult: Tcl_DecrRefCount (resultObj); Tcl_ResetResult (info->interp); XML_ParserFree (extparser); if (oldparser) { info->parser = oldparser; } info->status = TCL_ERROR; Tcl_AppendResult (info->interp, "The -externalentitycommand script " "has to return a Tcl list with 3 elements.\n" "Syntax: {string|channel|filename }\n", NULL); return 0; } /*--------------------------------------------------------------------------- | startDoctypeDeclHandler | \--------------------------------------------------------------------------*/ static void startDoctypeDeclHandler ( void *userData, const char *doctypeName, const char *sysid, const char *pubid, int has_internal_subset ) { domReadInfo *info = (domReadInfo *) userData; if (pubid) { info->document->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo)); memset (info->document->doctype, 0, sizeof (domDocInfo)); info->document->doctype->systemId = tdomstrdup (sysid); info->document->doctype->publicId = tdomstrdup (pubid); } else if (sysid) { info->document->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo)); memset (info->document->doctype, 0, sizeof (domDocInfo)); info->document->doctype->systemId = tdomstrdup (sysid); } info->insideDTD = 1; } /*--------------------------------------------------------------------------- | endDoctypeDeclHandler | \--------------------------------------------------------------------------*/ static void endDoctypeDeclHandler ( void *userData ) { domReadInfo *info = (domReadInfo *) userData; info->insideDTD = 0; } /*--------------------------------------------------------------------------- | domReadDocument | \--------------------------------------------------------------------------*/ domDocument * domReadDocument ( XML_Parser parser, char *xml, int length, int ignoreWhiteSpaces, int keepCDATA, int storeLineColumn, int ignorexmlns, int feedbackAfter, Tcl_Obj *feedbackCmd, Tcl_Channel channel, const char *baseurl, Tcl_Obj *extResolver, int useForeignDTD, int paramEntityParsing, Tcl_Interp *interp, int *resultcode ) { int done, tclLen; enum XML_Status status; size_t len; domReadInfo info; char buf[8192]; Tcl_Obj *bufObj; Tcl_DString dStr; int useBinary; char *str; domDocument *doc = domCreateDoc(baseurl, storeLineColumn); if (extResolver) { doc->extResolver = tdomstrdup (Tcl_GetString (extResolver)); } if (ignorexmlns) { doc->nodeFlags |= IGNORE_XMLNS; } info.parser = parser; info.document = doc; info.currentNode = NULL; info.depth = 0; info.ignoreWhiteSpaces = ignoreWhiteSpaces; info.cdata = (Tcl_DString*) MALLOC (sizeof (Tcl_DString)); Tcl_DStringInit (info.cdata); info.cdataSection = 0; info.storeLineColumn = storeLineColumn; info.ignorexmlns = ignorexmlns; info.feedbackAfter = feedbackAfter; info.feedbackCmd = feedbackCmd; info.nextFeedbackPosition = feedbackAfter; info.interp = interp; info.activeNSpos = -1; info.activeNSsize = 8; info.activeNS = (domActiveNS*) MALLOC (sizeof(domActiveNS) * info.activeNSsize); info.baseURIstackPos = 0; info.baseURIstackSize = INITIAL_BASEURISTACK_SIZE; info.baseURIstack = (domActiveBaseURI*) MALLOC (sizeof(domActiveBaseURI) * info.baseURIstackSize); info.insideDTD = 0; info.status = 0; XML_SetUserData(parser, &info); XML_SetBase (parser, baseurl); /* We must use XML_GetBase(), because XML_SetBase copies the baseURI, and we want to compare the pointers */ info.baseURIstack[0].baseURI = XML_GetBase (parser); info.baseURIstack[0].depth = 0; XML_UseForeignDTD (parser, (unsigned char) useForeignDTD); XML_SetElementHandler(parser, startElement, endElement); XML_SetCharacterDataHandler(parser, characterDataHandler); XML_SetCommentHandler(parser, commentHandler); XML_SetProcessingInstructionHandler(parser, processingInstructionHandler); XML_SetEntityDeclHandler (parser, entityDeclHandler); if (extResolver) { XML_SetExternalEntityRefHandler (parser, externalEntityRefHandler); } XML_SetParamEntityParsing (parser, (enum XML_ParamEntityParsing) paramEntityParsing); XML_SetDoctypeDeclHandler (parser, startDoctypeDeclHandler, endDoctypeDeclHandler); if (keepCDATA) { XML_SetCdataSectionHandler(parser, startCDATA, endCDATA); } if (channel == NULL) { status = XML_Parse(parser, xml, length, 1); switch (status) { case XML_STATUS_SUSPENDED: DBG(fprintf(stderr, "XML_STATUS_SUSPENDED\n");) if (info.status == TCL_BREAK) { Tcl_ResetResult(interp); } /* fall throu */ case XML_STATUS_ERROR: DBG(fprintf(stderr, "XML_STATUS_ERROR\n");) FREE ( info.activeNS ); FREE ( info.baseURIstack ); Tcl_DStringFree (info.cdata); FREE ( info.cdata); domFreeDocument (doc, NULL, NULL); *resultcode = info.status; return NULL; case XML_STATUS_OK: break; } } else { Tcl_DStringInit (&dStr); if (Tcl_GetChannelOption (interp, channel, "-encoding", &dStr) != TCL_OK) { FREE ( (char*) info.activeNS ); FREE ( info.baseURIstack ); Tcl_DStringFree (info.cdata); FREE ( info.cdata); domFreeDocument (doc, NULL, NULL); *resultcode = info.status; return NULL; } if (strcmp (Tcl_DStringValue (&dStr), "utf-8")==0 ) useBinary = 1; else useBinary = 0; Tcl_DStringFree (&dStr); if (useBinary) { do { len = Tcl_Read (channel, buf, sizeof(buf)); done = len < sizeof(buf); status = XML_Parse (parser, buf, len, done); switch (status) { case XML_STATUS_SUSPENDED: DBG(fprintf(stderr, "XML_STATUS_SUSPENDED\n");); if (info.status == TCL_BREAK) { Tcl_ResetResult(interp); } /* fall throu */ case XML_STATUS_ERROR: DBG(fprintf(stderr, "XML_STATUS_ERROR\n");) FREE ( info.activeNS ); FREE ( info.baseURIstack ); Tcl_DStringFree (info.cdata); FREE ( info.cdata); domFreeDocument (doc, NULL, NULL); *resultcode = info.status; return NULL; case XML_STATUS_OK: break; } } while (!done); } else { bufObj = Tcl_NewObj(); Tcl_SetObjLength (bufObj, 6144); do { len = Tcl_ReadChars (channel, bufObj, 1024, 0); done = (len < 1024); str = Tcl_GetStringFromObj(bufObj, &tclLen); status = XML_Parse (parser, str, tclLen, done); switch (status) { case XML_STATUS_SUSPENDED: DBG(fprintf(stderr, "XML_STATUS_SUSPENDED\n");); if (info.status == TCL_BREAK) { Tcl_ResetResult(interp); } /* fall throu */ case XML_STATUS_ERROR: DBG(fprintf(stderr, "XML_STATUS_ERROR\n");) FREE ( info.activeNS ); FREE ( info.baseURIstack ); Tcl_DStringFree (info.cdata); FREE ( info.cdata); domFreeDocument (doc, NULL, NULL); Tcl_DecrRefCount (bufObj); *resultcode = info.status; return NULL; case XML_STATUS_OK: break; } } while (!done); Tcl_DecrRefCount (bufObj); } } FREE ( info.activeNS ); FREE ( info.baseURIstack ); Tcl_DStringFree (info.cdata); FREE ( info.cdata); domSetDocumentElement (doc); return doc; } #endif /* ifndef TDOM_NO_EXPAT */ /*--------------------------------------------------------------------------- | domException2String | \--------------------------------------------------------------------------*/ const char * domException2String ( domException exception ) { return domException2StringTable[exception]; } /*--------------------------------------------------------------------------- | domGetLineColumn | \--------------------------------------------------------------------------*/ int domGetLineColumn ( domNode *node, int *line, int *column ) { char *v; domLineColumn *lc; *line = -1; *column = -1; if (node->nodeFlags & HAS_LINE_COLUMN) { v = (char*)node; switch (node->nodeType) { case ELEMENT_NODE: v = v + sizeof(domNode); break; case TEXT_NODE: case CDATA_SECTION_NODE: case COMMENT_NODE: v = v + sizeof(domTextNode); break; case PROCESSING_INSTRUCTION_NODE: v = v + sizeof(domProcessingInstructionNode); break; default: return -1; } lc = (domLineColumn *)v; *line = lc->line; *column = lc->column; return 0; } else { return -1; } } domAttrNode * domCreateXMLNamespaceNode ( domNode *parent ) { Tcl_HashEntry *h; int hnew; domAttrNode *attr; domNS *ns; attr = (domAttrNode *) domAlloc (sizeof (domAttrNode)); memset (attr, 0, sizeof (domAttrNode)); h = Tcl_CreateHashEntry(&HASHTAB(parent->ownerDocument,tdom_attrNames), "xmlns:xml", &hnew); ns = domNewNamespace (parent->ownerDocument, "xml", XML_NAMESPACE); attr->nodeType = ATTRIBUTE_NODE; attr->nodeFlags = IS_NS_NODE; attr->namespace = ns->index; attr->nodeName = (char *)&(h->key); attr->parentNode = parent; attr->valueLength = strlen (XML_NAMESPACE); attr->nodeValue = tdomstrdup (XML_NAMESPACE); return attr; } /* *---------------------------------------------------------------------- * * domCreateDoc -- * * This procedure allocates a new domDocument, initialize it and * creates its rootNode (with initialization). * * Results: * The domDocument node: * * Side effects: * Allocates memory for the returned domDocument and its * rootNode. * *---------------------------------------------------------------------- */ domDocument * domCreateDoc ( const char * baseURI, int storeLineColumn ) { Tcl_HashEntry *h; int hnew; domNode *rootNode; domDocument *doc; domLineColumn *lc; doc = (domDocument *) MALLOC (sizeof (domDocument)); memset(doc, 0, sizeof(domDocument)); doc->nodeType = DOCUMENT_NODE; doc->documentNumber = DOC_NO(doc); doc->nsptr = -1; doc->nslen = 4; doc->namespaces = (domNS**) MALLOC (sizeof (domNS*) * doc->nslen); /* We malloc and initialize the baseURIs hash table here to avoid cluttering of the code all over the place with checks. */ doc->baseURIs = MALLOC (sizeof (Tcl_HashTable)); Tcl_InitHashTable (doc->baseURIs, TCL_ONE_WORD_KEYS); TDomThreaded( domLocksAttach(doc); Tcl_InitHashTable(&doc->tdom_tagNames, TCL_STRING_KEYS); Tcl_InitHashTable(&doc->tdom_attrNames, TCL_STRING_KEYS); ) if (storeLineColumn) { rootNode = (domNode*) domAlloc(sizeof(domNode)+sizeof(domLineColumn)); } else { rootNode = (domNode*) domAlloc(sizeof(domNode)); } memset(rootNode, 0, sizeof(domNode)); rootNode->nodeType = ELEMENT_NODE; if (baseURI) { h = Tcl_CreateHashEntry (doc->baseURIs, (char*)rootNode, &hnew); Tcl_SetHashValue (h, tdomstrdup (baseURI)); rootNode->nodeFlags |= HAS_BASEURI; } rootNode->namespace = 0; h = Tcl_CreateHashEntry(&HASHTAB(doc,tdom_tagNames), "", &hnew); rootNode->nodeName = (char *)&(h->key); rootNode->nodeNumber = NODE_NO(doc); rootNode->ownerDocument = doc; rootNode->parentNode = NULL; rootNode->firstChild = rootNode->lastChild = NULL; rootNode->firstAttr = domCreateXMLNamespaceNode (rootNode); if (storeLineColumn) { lc = (domLineColumn*) ( ((char*)rootNode) + sizeof(domNode)); rootNode->nodeFlags |= HAS_LINE_COLUMN; lc->line = 0; lc->column = 0; } doc->rootNode = rootNode; return doc; } /*--------------------------------------------------------------------------- | domCreateDocument | \--------------------------------------------------------------------------*/ domDocument * domCreateDocument ( const char *uri, char *documentElementTagName ) { Tcl_HashEntry *h; int hnew; domNode *node; domDocument *doc; char prefix[MAX_PREFIX_LEN]; const char *localName; domNS *ns = NULL; if (uri) { domSplitQName (documentElementTagName, prefix, &localName); DBG(fprintf(stderr, "rootName: -->%s<--, prefix: -->%s<--, localName: -->%s<--\n", documentElementTagName, prefix, localName);); } doc = domCreateDoc (NULL, 0); h = Tcl_CreateHashEntry(&HASHTAB(doc, tdom_tagNames), documentElementTagName, &hnew); node = (domNode*) domAlloc(sizeof(domNode)); memset(node, 0, sizeof(domNode)); node->nodeType = ELEMENT_NODE; node->nodeNumber = NODE_NO(doc); node->ownerDocument = doc; node->nodeName = (char *)&(h->key); doc->documentElement = node; if (uri) { ns = domNewNamespace (doc, prefix, uri); node->namespace = ns->index; domAddNSToNode (node, ns); } doc->rootNode->firstChild = doc->rootNode->lastChild = doc->documentElement; return doc; } /*--------------------------------------------------------------------------- | domSetDocumentElement | \--------------------------------------------------------------------------*/ void domSetDocumentElement ( domDocument *doc ) { domNode *node; doc->documentElement = NULL; node = doc->rootNode->firstChild; while (node) { if (node->nodeType == ELEMENT_NODE) { doc->documentElement = node; break; } node = node->nextSibling; } if (!doc->documentElement) { doc->documentElement = doc->rootNode->firstChild; } } /*--------------------------------------------------------------------------- | domFreeNode | \--------------------------------------------------------------------------*/ void domFreeNode ( domNode * node, domFreeCallback freeCB, void * clientData, int dontfree ) { int shared = 0; domNode *child, *ctemp; domAttrNode *atemp, *attr, *aprev; Tcl_HashEntry *entryPtr; if (node == NULL) { DBG(fprintf (stderr, "null ptr in domFreeNode (dom.c) !\n");) return; } TDomThreaded ( shared = node->ownerDocument && node->ownerDocument->refCount > 1; ) /*---------------------------------------------------------------- | dontfree instruct us to walk the node tree and apply the | user-supplied callback, *w/o* actually deleting nodes. | This is normally done when a thread detaches from the | shared DOM tree and wants to garbage-collect all nodecmds | in it's interpreter which attached to the tree nodes. \---------------------------------------------------------------*/ if (dontfree) { shared = 1; } else { node->nodeFlags |= IS_DELETED; } if (node->nodeType == ATTRIBUTE_NODE && !shared) { attr = ((domAttrNode*)node)->parentNode->firstAttr; aprev = NULL; while (attr && (attr != (domAttrNode*)node)) { aprev = attr; attr = attr->nextSibling; } if (attr) { if (aprev) { aprev->nextSibling = attr->nextSibling; } else { ((domAttrNode*)node)->parentNode->firstAttr = attr->nextSibling; } FREE (attr->nodeValue); domFree ((void*)attr); } } else if (node->nodeType == ELEMENT_NODE) { child = node->lastChild; while (child) { ctemp = child->previousSibling; if (freeCB) { freeCB(child, clientData); } domFreeNode (child, freeCB, clientData, dontfree); child = ctemp; } if (shared) { return; } attr = node->firstAttr; while (attr) { atemp = attr; attr = attr->nextSibling; FREE (atemp->nodeValue); domFree ((void*)atemp); } if (node->nodeFlags & HAS_BASEURI) { entryPtr = Tcl_FindHashEntry (node->ownerDocument->baseURIs, (char*)node); if (entryPtr) { FREE ((char *) Tcl_GetHashValue (entryPtr)); Tcl_DeleteHashEntry (entryPtr); } } domFree ((void*)node); } else if (node->nodeType == PROCESSING_INSTRUCTION_NODE && !shared) { FREE (((domProcessingInstructionNode*)node)->dataValue); FREE (((domProcessingInstructionNode*)node)->targetValue); domFree ((void*)node); } else if (!shared) { FREE (((domTextNode*)node)->nodeValue); domFree ((void*)node); } } /*--------------------------------------------------------------------------- | domDeleteNode - unlinks node from tree and free all child nodes | and itself | \--------------------------------------------------------------------------*/ domException domDeleteNode ( domNode * node, domFreeCallback freeCB, void * clientData ) { TDomThreaded(int shared = 0;) domDocument *doc; if (node->nodeType == ATTRIBUTE_NODE) { domPanic("domDeleteNode on ATTRIBUTE_NODE not supported!"); } TDomThreaded ( shared = node->ownerDocument->refCount > 1; ) doc = node->ownerDocument; /*---------------------------------------------------------------- | unlink node from child or fragment list \---------------------------------------------------------------*/ if (node->previousSibling) { node->previousSibling->nextSibling = node->nextSibling; } else { if (node->parentNode) { node->parentNode->firstChild = node->nextSibling; } else { /* Node may be a top level node */ if (doc->rootNode->firstChild == node) { doc->rootNode->firstChild = node->nextSibling; } } } if (node->nextSibling) { node->nextSibling->previousSibling = node->previousSibling; } else { if (node->parentNode) { node->parentNode->lastChild = node->previousSibling; } else { /* Node may be a top level node */ if (doc->rootNode->lastChild == node) { doc->rootNode->lastChild = node->previousSibling; } } } if (doc->fragments == node) { doc->fragments = node->nextSibling; } if (!node->parentNode) { domSetDocumentElement (doc); } /*---------------------------------------------------------------- | for shared docs, append node to the delete nodes list | otherwise delete the node physically \---------------------------------------------------------------*/ if (freeCB) { freeCB(node, clientData); } TDomThreaded ( if (shared) { if (doc->deletedNodes) { node->nextSibling = doc->deletedNodes; } else { node->nextSibling = NULL; } doc->deletedNodes = node; node->nodeFlags |= IS_DELETED; } ) MutationEvent3(DOMNodeRemoved, childToRemove, node); MutationEvent2(DOMSubtreeModified, node); domFreeNode(node, freeCB, clientData, 0); return OK; } /*--------------------------------------------------------------------------- | domFreeDocument | \--------------------------------------------------------------------------*/ void domFreeDocument ( domDocument * doc, domFreeCallback freeCB, void * clientData ) { domNode *node, *next; domNS *ns; int i, dontfree = 0; Tcl_HashEntry *entryPtr; Tcl_HashSearch search; if (doc->nodeFlags & DONT_FREE) { doc->nodeFlags &= ~DONT_FREE; dontfree = 1; } /*----------------------------------------------------------- | delete main trees, including top level PIs, etc. \-----------------------------------------------------------*/ node = doc->rootNode; if (node) { if (freeCB) { freeCB(node, clientData); } domFreeNode (node, freeCB, clientData, dontfree); } /*----------------------------------------------------------- | delete fragment trees \-----------------------------------------------------------*/ node = doc->fragments; while (node) { next = node->nextSibling; if (freeCB) { freeCB(node, clientData); } domFreeNode (node, freeCB, clientData, dontfree); node = next; } if (dontfree) return; /*----------------------------------------------------------- | delete namespaces \-----------------------------------------------------------*/ for (i = 0; i <= doc->nsptr; i++) { ns = doc->namespaces[i]; FREE(ns->uri); FREE(ns->prefix); FREE ((char*) ns); } FREE ((char *)doc->namespaces); /*----------------------------------------------------------- | delete global selectNodes prefix namespace mappings \-----------------------------------------------------------*/ if (doc->prefixNSMappings) { i = 0; while (doc->prefixNSMappings[i]) { FREE (doc->prefixNSMappings[i]); i++; } FREE (doc->prefixNSMappings); } /*----------------------------------------------------------- | delete doctype info \-----------------------------------------------------------*/ if (doc->doctype) { #define DOCINFO_FREE(item) if (doc->doctype->item) FREE(doc->doctype->item) DOCINFO_FREE(systemId); DOCINFO_FREE(publicId); DOCINFO_FREE(internalSubset); DOCINFO_FREE(encoding); DOCINFO_FREE(mediaType); DOCINFO_FREE(method); if (doc->doctype->cdataSectionElements) { Tcl_DeleteHashTable (doc->doctype->cdataSectionElements); FREE (doc->doctype->cdataSectionElements); } FREE((char*) doc->doctype); } /*----------------------------------------------------------- | delete ID hash table \-----------------------------------------------------------*/ if (doc->ids) { Tcl_DeleteHashTable (doc->ids); FREE (doc->ids); } /*----------------------------------------------------------- | delete unparsed entities hash table \-----------------------------------------------------------*/ if (doc->unparsedEntities) { entryPtr = Tcl_FirstHashEntry (doc->unparsedEntities, &search); while (entryPtr) { FREE (Tcl_GetHashValue (entryPtr)); entryPtr = Tcl_NextHashEntry (&search); } Tcl_DeleteHashTable (doc->unparsedEntities); FREE (doc->unparsedEntities); } /*----------------------------------------------------------- | delete base URIs hash table \-----------------------------------------------------------*/ entryPtr = Tcl_FirstHashEntry (doc->baseURIs, &search); while (entryPtr) { FREE (Tcl_GetHashValue (entryPtr)); entryPtr = Tcl_NextHashEntry (&search); } Tcl_DeleteHashTable (doc->baseURIs); FREE (doc->baseURIs); /*----------------------------------------------------------- | delete XPath cache hash table \-----------------------------------------------------------*/ if (doc->xpathCache) { entryPtr = Tcl_FirstHashEntry (doc->xpathCache, &search); while (entryPtr) { xpathFreeAst((ast)Tcl_GetHashValue (entryPtr)); entryPtr = Tcl_NextHashEntry (&search); } Tcl_DeleteHashTable (doc->xpathCache); FREE (doc->xpathCache); } if (doc->extResolver) { FREE (doc->extResolver); } /*----------------------------------------------------------- | delete tag/attribute hash tables (for threaded builds only) \-----------------------------------------------------------*/ TDomThreaded ( { Tcl_HashEntry *entryPtr; Tcl_HashSearch search; entryPtr = Tcl_FirstHashEntry(&doc->tdom_tagNames, &search); while (entryPtr) { Tcl_DeleteHashEntry(entryPtr); entryPtr = Tcl_NextHashEntry(&search); } Tcl_DeleteHashTable(&doc->tdom_tagNames); entryPtr = Tcl_FirstHashEntry(&doc->tdom_attrNames, &search); while (entryPtr) { Tcl_DeleteHashEntry(entryPtr); entryPtr = Tcl_NextHashEntry(&search); } Tcl_DeleteHashTable(&doc->tdom_attrNames); domLocksDetach(doc); node = doc->deletedNodes; while (node) { next = node->nextSibling; domFreeNode (node, freeCB, clientData, 0); node = next; } } ) FREE ((char*)doc); } /*--------------------------------------------------------------------------- | domSetAttribute | \--------------------------------------------------------------------------*/ domAttrNode * domSetAttribute ( domNode *node, const char *attributeName, const char *attributeValue ) { domAttrNode *attr, *lastAttr; Tcl_HashEntry *h; int hnew; if (!node || node->nodeType != ELEMENT_NODE) { return NULL; } /*---------------------------------------------------- | try to find an existing attribute \---------------------------------------------------*/ attr = node->firstAttr; while (attr && strcmp(attr->nodeName, attributeName)) { attr = attr->nextSibling; } if (attr) { if (attr->nodeFlags & IS_ID_ATTRIBUTE) { h = Tcl_FindHashEntry (node->ownerDocument->ids, attr->nodeValue); if (h) { Tcl_DeleteHashEntry (h); h = Tcl_CreateHashEntry (node->ownerDocument->ids, attributeValue, &hnew); /* XXX what to do, if hnew = 0 ??? */ Tcl_SetHashValue (h, node); } } FREE (attr->nodeValue); attr->valueLength = strlen(attributeValue); attr->nodeValue = (char*)MALLOC(attr->valueLength+1); strcpy(attr->nodeValue, attributeValue); } else { /*----------------------------------------------- | add a complete new attribute node \----------------------------------------------*/ attr = (domAttrNode*) domAlloc(sizeof(domAttrNode)); memset(attr, 0, sizeof(domAttrNode)); h = Tcl_CreateHashEntry(&HASHTAB(node->ownerDocument,tdom_attrNames), attributeName, &hnew); attr->nodeType = ATTRIBUTE_NODE; attr->nodeFlags = 0; attr->namespace = 0; attr->nodeName = (char *)&(h->key); attr->parentNode = node; attr->valueLength = strlen(attributeValue); attr->nodeValue = (char*)MALLOC(attr->valueLength+1); strcpy(attr->nodeValue, attributeValue); if (node->firstAttr) { lastAttr = node->firstAttr; /* move to the end of the attribute list */ while (lastAttr->nextSibling) lastAttr = lastAttr->nextSibling; lastAttr->nextSibling = attr; } else { node->firstAttr = attr; } } MutationEvent(); return attr; } /*--------------------------------------------------------------------------- | domSetAttributeNS | \--------------------------------------------------------------------------*/ domAttrNode * domSetAttributeNS ( domNode *node, const char *attributeName, const char *attributeValue, const char *uri, int createNSIfNeeded ) { domAttrNode *attr, *lastAttr; Tcl_HashEntry *h; int hnew, hasUri = 1, isNSAttr = 0, isDftNS = 0; domNS *ns; char prefix[MAX_PREFIX_LEN]; const char *localName, *newLocalName; Tcl_DString dStr; DBG(fprintf (stderr, "domSetAttributeNS: attributeName %s, attributeValue %s, uri %s\n", attributeName, attributeValue, uri);) if (!node || node->nodeType != ELEMENT_NODE) { return NULL; } domSplitQName (attributeName, prefix, &localName); if (!uri || uri[0]=='\0') hasUri = 0; if (hasUri && (prefix[0] == '\0')) return NULL; if ((prefix[0] == '\0' && strcmp (localName, "xmlns")==0) || (strcmp (prefix, "xmlns")==0)) { isNSAttr = 1; createNSIfNeeded = 0; if (prefix[0] == '\0') { isDftNS = 1; ns = domLookupPrefix (node, ""); } else { ns = domLookupPrefix (node, prefix); } if (ns && (strcmp (ns->uri, attributeValue)==0)) return NULL; if (!hasUri) { uri = attributeValue; isNSAttr = 1; hasUri = 1; if (strcmp (localName, "xmlns")==0) isDftNS = 1; } else { return NULL; } } if (!hasUri) { if (prefix[0] != '\0' && strcmp (prefix, "xml")==0) { uri = "http://www.w3.org/XML/1998/namespace"; hasUri = 1; } } if (!hasUri && prefix[0] != '\0') return NULL; /*---------------------------------------------------- | try to find an existing attribute \---------------------------------------------------*/ attr = node->firstAttr; while (attr) { if (hasUri) { if (attr->nodeFlags & IS_NS_NODE) { if (isNSAttr) { if (strcmp (attributeName, attr->nodeName)==0) { break; } } } else { if (attr->namespace && !isNSAttr) { ns = domGetNamespaceByIndex (node->ownerDocument, attr->namespace); if (strcmp (uri, ns->uri)==0) { newLocalName = localName; domSplitQName (attr->nodeName, prefix, &localName); if (strcmp (newLocalName, localName)==0) break; } } } } else { if (!attr->namespace) { if (strcmp (attr->nodeName, localName)==0) break; } } attr = attr->nextSibling; } if (attr) { DBG(fprintf (stderr, "domSetAttributeNS: resetting existing attribute %s ; old value: %s\n", attr->nodeName, attr->nodeValue);) if (attr->nodeFlags & IS_ID_ATTRIBUTE) { h = Tcl_FindHashEntry (node->ownerDocument->ids, attr->nodeValue); if (h) { Tcl_DeleteHashEntry (h); h = Tcl_CreateHashEntry (node->ownerDocument->ids, attributeValue, &hnew); Tcl_SetHashValue (h, node); } } FREE (attr->nodeValue); attr->valueLength = strlen(attributeValue); attr->nodeValue = (char*)MALLOC(attr->valueLength+1); strcpy(attr->nodeValue, attributeValue); } else { /*-------------------------------------------------------- | add a complete new attribute node \-------------------------------------------------------*/ attr = (domAttrNode*) domAlloc(sizeof(domAttrNode)); memset(attr, 0, sizeof(domAttrNode)); h = Tcl_CreateHashEntry(&HASHTAB(node->ownerDocument,tdom_attrNames), attributeName, &hnew); attr->nodeType = ATTRIBUTE_NODE; if (hasUri) { if (isNSAttr) { if (isDftNS) { ns = domLookupNamespace (node->ownerDocument, "", uri); } else { ns = domLookupNamespace (node->ownerDocument, localName, uri); } } else { ns = domLookupPrefix (node, prefix); if (ns && (strcmp (ns->uri, uri)!=0)) ns = NULL; } if (!ns) { if (isNSAttr) { if (isDftNS) { ns = domNewNamespace (node->ownerDocument, "", uri); } else { ns = domNewNamespace (node->ownerDocument, localName, uri); } } else { ns = domNewNamespace (node->ownerDocument, prefix, uri); if (createNSIfNeeded) { if (prefix[0] == '\0') { domSetAttributeNS (node, "xmlns", uri, NULL, 0); } else { Tcl_DStringInit (&dStr); Tcl_DStringAppend (&dStr, "xmlns:", 6); Tcl_DStringAppend (&dStr, prefix, -1); domSetAttributeNS (node, Tcl_DStringValue (&dStr), uri, NULL, 0); } } } } attr->namespace = ns->index; if (isNSAttr) { attr->nodeFlags = IS_NS_NODE; } } attr->nodeName = (char *)&(h->key); attr->parentNode = node; attr->valueLength = strlen(attributeValue); attr->nodeValue = (char*)MALLOC(attr->valueLength+1); strcpy(attr->nodeValue, attributeValue); if (isNSAttr) { if (node->firstAttr && (node->firstAttr->nodeFlags & IS_NS_NODE)) { lastAttr = node->firstAttr; while (lastAttr->nextSibling && (lastAttr->nextSibling->nodeFlags & IS_NS_NODE)) { lastAttr = lastAttr->nextSibling; } attr->nextSibling = lastAttr->nextSibling; lastAttr->nextSibling = attr; } else { attr->nextSibling = node->firstAttr; node->firstAttr = attr; } } else { if (node->firstAttr) { lastAttr = node->firstAttr; /* move to the end of the attribute list */ while (lastAttr->nextSibling) lastAttr = lastAttr->nextSibling; lastAttr->nextSibling = attr; } else { node->firstAttr = attr; } } } MutationEvent(); return attr; } /*--------------------------------------------------------------------------- | domRemoveAttribute | \--------------------------------------------------------------------------*/ int domRemoveAttribute ( domNode *node, const char *attributeName ) { domAttrNode *attr, *previous = NULL; Tcl_HashEntry *h; if (!node || node->nodeType != ELEMENT_NODE) { return -1; } /*---------------------------------------------------- | try to find the attribute \---------------------------------------------------*/ attr = node->firstAttr; while (attr && strcmp(attr->nodeName, attributeName)) { previous = attr; attr = attr->nextSibling; } if (attr) { if (previous) { previous->nextSibling = attr->nextSibling; } else { attr->parentNode->firstAttr = attr->nextSibling; } if (attr->nodeFlags & IS_ID_ATTRIBUTE) { h = Tcl_FindHashEntry (node->ownerDocument->ids, attr->nodeValue); if (h) Tcl_DeleteHashEntry (h); } FREE (attr->nodeValue); MutationEvent(); domFree ((void*)attr); return 0; } return -1; } /*--------------------------------------------------------------------------- | domRemoveAttributeNS | \--------------------------------------------------------------------------*/ int domRemoveAttributeNS ( domNode *node, const char *uri, const char *localName ) { domAttrNode *attr, *previous = NULL; domNS *ns = NULL; char prefix[MAX_PREFIX_LEN]; const char *str; Tcl_HashEntry *h; if (!node || node->nodeType != ELEMENT_NODE) { return -1; } attr = node->firstAttr; while (attr) { domSplitQName (attr->nodeName, prefix, &str); if (strcmp(localName,str)==0) { ns = domGetNamespaceByIndex(node->ownerDocument, attr->namespace); if (ns && strcmp(ns->uri, uri)==0) { if (previous) { previous->nextSibling = attr->nextSibling; } else { attr->parentNode->firstAttr = attr->nextSibling; } if (attr->nodeFlags & IS_ID_ATTRIBUTE) { h = Tcl_FindHashEntry (node->ownerDocument->ids, attr->nodeValue); if (h) Tcl_DeleteHashEntry (h); } FREE (attr->nodeValue); MutationEvent(); domFree ((void*)attr); return 0; } } previous = attr; attr = attr->nextSibling; } return -1; } /*--------------------------------------------------------------------------- | __dbgAttr | \--------------------------------------------------------------------------*/ DBG( static void __dbgAttr (domAttrNode *node) { DBG(fprintf(stderr, " %s=%s", node->nodeName, node->nodeValue);) if (node->nextSibling) __dbgAttr(node->nextSibling); } ) /*--------------------------------------------------------------------------- | domSetDocument | \--------------------------------------------------------------------------*/ void domSetDocument ( domNode *node, domDocument *doc ) { domNode *child; domNS *ns, *origNS; domDocument *origDoc; domAttrNode *attr; Tcl_HashEntry *h; TDomThreaded ( int hnew; ) if (node->nodeFlags & HAS_BASEURI) { h = Tcl_FindHashEntry (node->ownerDocument->baseURIs, (char*)node); if (h) { FREE ((char *) Tcl_GetHashValue (h)); Tcl_DeleteHashEntry (h); } node->nodeFlags &= ~HAS_BASEURI; } if (node->nodeType == ELEMENT_NODE) { origDoc = node->ownerDocument; node->ownerDocument = doc; for (attr = node->firstAttr; attr != NULL; attr = attr->nextSibling) { if (attr->nodeFlags & IS_NS_NODE) { origNS = origDoc->namespaces[attr->namespace-1]; ns = domNewNamespace (doc, origNS->prefix, origNS->uri); attr->namespace = ns->index; } else if (attr->namespace) { ns = domAddNSToNode (node, origDoc->namespaces[attr->namespace-1]); if (ns) attr->namespace = ns->index; } } if (node->namespace) { ns = domAddNSToNode (node, origDoc->namespaces[node->namespace-1]); if (ns) node->namespace = ns->index; } else { ns = domAddNSToNode (node, NULL); if (ns) { node->namespace = ns->index; } } DBG(fprintf(stderr, "domSetDocument node%s ", node->nodeName); __dbgAttr(node->firstAttr); fprintf(stderr, "\n"); ) TDomThreaded ( if (origDoc != doc) { /* Make hash table entries as necessary for * tdom_tagNames and tdom_attrNames. */ h = Tcl_CreateHashEntry(&doc->tdom_tagNames, node->nodeName, &hnew); node->nodeName = (domString) &(h->key); for (attr = node->firstAttr; attr != NULL; attr = attr->nextSibling) { h = Tcl_CreateHashEntry(&doc->tdom_attrNames, attr->nodeName, &hnew); attr->nodeName = (domString) &(h->key); } } ) child = node->firstChild; while (child != NULL) { domSetDocument (child, doc); child = child->nextSibling; } } else { node->ownerDocument = doc; } DBG(fprintf(stderr, "end domSetDocument node %s\n", node->nodeName);) } /*--------------------------------------------------------------------------- | domSetNodeValue | \--------------------------------------------------------------------------*/ domException domSetNodeValue ( domNode *node, const char *nodeValue, int valueLen ) { domTextNode *textnode; if ((node->nodeType != TEXT_NODE) && (node->nodeType != CDATA_SECTION_NODE) && (node->nodeType != COMMENT_NODE) ) { return NO_MODIFICATION_ALLOWED_ERR; } textnode = (domTextNode*) node; FREE(textnode->nodeValue); textnode->nodeValue = MALLOC (valueLen); textnode->valueLength = valueLen; memmove(textnode->nodeValue, nodeValue, valueLen); MutationEvent(); return OK; } /* *---------------------------------------------------------------------- * * domRemoveChild -- * * This procedure implements the dom method removeChild. Removes * child from the list of children of node. * * Results: * Returns a domException: * * NOT_FOUND_ERR: Raised if the node child is not a child of node. * * OK: otherwise * * Side effects: * Alters the involved document. * *---------------------------------------------------------------------- */ domException domRemoveChild ( domNode *node, domNode *child ) { domNode *n; /* check, if node is in deed the parent of child */ if (child->parentNode != node) { /* If node is the root node of a document and child is in deed a child of this node, then child->parentNode will be NULL. In this case, we loop throu the childs of node, to see, if the child is valid. */ if (node->ownerDocument->rootNode == node) { n = node->firstChild; while (n) { if (n == child) { /* child is in deed a child of node */ break; } n = n->nextSibling; } if (!n) { return NOT_FOUND_ERR; } } else { return NOT_FOUND_ERR; } } if (child->previousSibling) { child->previousSibling->nextSibling = child->nextSibling; } else { node->firstChild = child->nextSibling; } if (child->nextSibling) { child->nextSibling->previousSibling = child->previousSibling; } else { node->lastChild = child->previousSibling; } /* link child into the fragments list */ if (child->ownerDocument->fragments) { child->nextSibling = child->ownerDocument->fragments; child->ownerDocument->fragments->previousSibling = child; child->ownerDocument->fragments = child; } else { child->ownerDocument->fragments = child; child->nextSibling = NULL; } child->parentNode = NULL; child->previousSibling = NULL; MutationEvent3(DOMNodeRemoved, child, node); MutationEvent2(DOMSubtreeModified, node); return OK; } /* *---------------------------------------------------------------------- * * domAppendChild -- * * This procedure implements the dom method appendChild. Adds the * node newChild to the end of the list of children of this * node. If the newChild is already in the tree, it is first * removed. * * Results: * Returns a domException: * * HIERARCHY_REQUEST_ERR: Raised if node is of a type that does * not allow children of the type of the childToAppend node, or * if the node to append is one of node's ancestors or the * rootNode of node's document. * * NOT_SUPPORTED_ERR: Raised if the childToInsert is the rootNode * of another document or if node is a rootNode. * * OK: otherwise * * Side effects: * Alters the involved document(s). * *---------------------------------------------------------------------- */ domException domAppendChild ( domNode *node, domNode *childToAppend ) { domNode *n; if (node->nodeType != ELEMENT_NODE) { return HIERARCHY_REQUEST_ERR; } /* check, whether childToAppend is node or one of node's ancestors */ n = node; while (n) { if (n == childToAppend) { return HIERARCHY_REQUEST_ERR; } n = n->parentNode; } if (childToAppend == childToAppend->ownerDocument->rootNode) { if (childToAppend == node->ownerDocument->rootNode) { return HIERARCHY_REQUEST_ERR; } else { return NOT_SUPPORTED_ERR; } } /* unlink childToAppend */ if (childToAppend->previousSibling) { childToAppend->previousSibling->nextSibling = childToAppend->nextSibling; } else { if (childToAppend->parentNode) { childToAppend->parentNode->firstChild = childToAppend->nextSibling; } else { /* childToAppend is either out of the fragment list or a child of the rootNode of its document */ if (childToAppend->ownerDocument->fragments == childToAppend) { childToAppend->ownerDocument->fragments = childToAppend->nextSibling; } else { childToAppend->ownerDocument->rootNode->firstChild = childToAppend->nextSibling; } } } if (childToAppend->nextSibling) { childToAppend->nextSibling->previousSibling = childToAppend->previousSibling; } else { if (childToAppend->parentNode) { childToAppend->parentNode->lastChild = childToAppend->previousSibling; } else { if (childToAppend->ownerDocument->rootNode->lastChild == childToAppend) { childToAppend->ownerDocument->rootNode->lastChild = childToAppend->previousSibling; } } } if (node->lastChild) { node->lastChild->nextSibling = childToAppend; childToAppend->previousSibling = node->lastChild; } else { node->firstChild = childToAppend; childToAppend->previousSibling = NULL; } node->lastChild = childToAppend; childToAppend->nextSibling = NULL; if (!childToAppend->parentNode && (childToAppend->ownerDocument->documentElement == childToAppend)) { childToAppend->ownerDocument->documentElement = childToAppend->ownerDocument->rootNode->firstChild; } if (node == node->ownerDocument->rootNode) { childToAppend->parentNode = NULL; } else { childToAppend->parentNode = node; } if ((node->ownerDocument != childToAppend->ownerDocument) || node->ownerDocument->nsptr || childToAppend->ownerDocument->baseURIs->numEntries) { domSetDocument (childToAppend, node->ownerDocument); } node->ownerDocument->nodeFlags |= NEEDS_RENUMBERING; MutationEvent(); return OK; } /* *---------------------------------------------------------------------- * * domInsertBefore -- * * This procedure implements the dom method insertBefore. * It inserts the node childToInsert before the existing child * node referenceChild. If referenceChild is null, insert * childToInsert at the end of the list of children of node. The * arguments node and childToInsert must be non NULL. The * childToInsert is unlinked from its previous place (fragment * list or tree). * * Results: * Returns a domException: * * HIERARCHY_REQUEST_ERR: Raised if node is of a type that does * not allow children of the type of the childToInsert node, or * if the node to insert is node or one of node's ancestors or the * rootNode of node's document. * * NOT_FOUND_ERR: Raised if refChild is not a child of this node. * * NOT_SUPPORTED_ERR: Raised if the childToInsert is the rootNode * of another document or if node is a rootNode. * * OK: otherwise * * Side effects: * Alters the involved document(s). * *---------------------------------------------------------------------- */ domException domInsertBefore ( domNode *node, domNode *childToInsert, domNode *referenceChild ) { domNode *n; if (node->nodeType != ELEMENT_NODE) { return HIERARCHY_REQUEST_ERR; } /* check, if node is in deed the parent of referenceChild */ if (referenceChild) { if (referenceChild->parentNode != node) { /* If node is the root node of a document and referenceChild is in deed a child of this node, then referenceChild->parentNode will be NULL. In this case, we loop throu the childs of node, to see, if the referenceChild is valid. */ if (node->ownerDocument->rootNode == node) { n = node->firstChild; while (n) { if (n == referenceChild) { /* referenceChild is in deed a child of node */ break; } n = n->nextSibling; } if (!n) { return NOT_FOUND_ERR; } } else { return NOT_FOUND_ERR; } } } if (childToInsert == referenceChild) { return OK; } /* check, whether childToInsert is one of node's ancestors */ n = node; while (n) { if (n == childToInsert) { return HIERARCHY_REQUEST_ERR; } n = n->parentNode; } if (childToInsert == childToInsert->ownerDocument->rootNode) { if (childToInsert == node->ownerDocument->rootNode) { return HIERARCHY_REQUEST_ERR; } else { /* For now, we simply don't allow the rootNode of another element as childToInsert. The way to go may be simply to treat the rootNode as DocumentFragment and to insert all childs of that rootNode before the referenceChild. This would result in a document without documentElement, which then should be handled right by other methods. This is planed, but not carefully considered, yet. */ return NOT_SUPPORTED_ERR; } } /* unlink childToInsert */ if (childToInsert->previousSibling) { childToInsert->previousSibling->nextSibling = childToInsert->nextSibling; } else { if (childToInsert->parentNode) { childToInsert->parentNode->firstChild = childToInsert->nextSibling; } else { /* childToInsert is either out of the fragment list or a child of the rootNode of its document */ if (childToInsert->ownerDocument->fragments == childToInsert) { childToInsert->ownerDocument->fragments = childToInsert->nextSibling; } else { childToInsert->ownerDocument->rootNode->firstChild = childToInsert->nextSibling; } } } if (childToInsert->nextSibling) { childToInsert->nextSibling->previousSibling = childToInsert->previousSibling; } else { if (childToInsert->parentNode) { childToInsert->parentNode->lastChild = childToInsert->previousSibling; } else { if (childToInsert->ownerDocument->rootNode->lastChild == childToInsert) { childToInsert->ownerDocument->rootNode->lastChild = childToInsert->previousSibling; } } } childToInsert->nextSibling = referenceChild; if (referenceChild) { if (referenceChild->previousSibling) { childToInsert->previousSibling = referenceChild->previousSibling; referenceChild->previousSibling->nextSibling = childToInsert; } else { node->firstChild = childToInsert; childToInsert->previousSibling = NULL; } referenceChild->previousSibling = childToInsert; } else { if (node->lastChild) { node->lastChild->nextSibling = childToInsert; childToInsert->previousSibling = node->lastChild; } else { node->firstChild = childToInsert; childToInsert->previousSibling = NULL; } node->lastChild = childToInsert; } if (!childToInsert->parentNode && (childToInsert->ownerDocument->documentElement == childToInsert)) { childToInsert->ownerDocument->documentElement = childToInsert->ownerDocument->rootNode->firstChild; } if (node == node->ownerDocument->rootNode) { childToInsert->parentNode = NULL; } else { childToInsert->parentNode = node; } if (node->ownerDocument != childToInsert->ownerDocument || node->ownerDocument->nsptr || childToInsert->ownerDocument->baseURIs->numEntries) { domSetDocument (childToInsert, node->ownerDocument); } node->ownerDocument->nodeFlags |= NEEDS_RENUMBERING; MutationEvent3(DOMNodeInsert, childToInsert, node); MutationEvent2(DOMSubtreeModified, node); return OK; } /* *---------------------------------------------------------------------- * * domReplaceChild -- * * This procedure implements the dom method replaceChild. * Replaces the child node oldChild with newChild in the list of * children of node 'node'. * * Results: * Returns a domException: * * HIERARCHY_REQUEST_ERR: Raised if node is of a type that does * not allow children of the type of the newChild node, or * if newChild is node or one of node's ancestors or the * rootNode of node's document. * * NOT_FOUND_ERR: Raised if oldChild is not a child of node. * * NOT_SUPPORTED_ERR: Raised if the newChild is the rootNode * of another document. * * OK: otherwise * * Side effects: * Alters the involved document(s). * *---------------------------------------------------------------------- */ domException domReplaceChild ( domNode *node, domNode *newChild, domNode *oldChild ) { domNode *n; if (node->nodeType != ELEMENT_NODE) { return HIERARCHY_REQUEST_ERR; } /* check, if node is in deed the parent of oldChild */ if (oldChild->parentNode != node) { /* If node is the root node of a document and oldChild is in deed a child of this node, then oldChild->parentNode will be NULL. In this case, we loop throu the childs of node, to see, if the oldChild is valid. */ if (node->ownerDocument->rootNode == node) { n = node->firstChild; while (n) { if (n == oldChild) { /* oldChild is in deed a child of node */ break; } n = n->nextSibling; } if (!n) { return NOT_FOUND_ERR; } } else { return NOT_FOUND_ERR; } } if (oldChild == newChild) { return OK; } /* check, whether newChild is node or one of node's ancestors */ n = node; while (n) { if (n == newChild) { return HIERARCHY_REQUEST_ERR; } n = n->parentNode; } if (newChild == newChild->ownerDocument->rootNode) { if (newChild == node->ownerDocument->rootNode) { return HIERARCHY_REQUEST_ERR; } else { return NOT_SUPPORTED_ERR; } } /* unlink newChild */ if (newChild->previousSibling) { newChild->previousSibling->nextSibling = newChild->nextSibling; } else { if (newChild->parentNode) { newChild->parentNode->firstChild = newChild->nextSibling; } else { /* newChild is either out of the fragment list or a child of the rootNode of its document */ if (newChild->ownerDocument->fragments == newChild) { newChild->ownerDocument->fragments = newChild->nextSibling; } else { newChild->ownerDocument->rootNode->firstChild = newChild->nextSibling; } } } if (newChild->nextSibling) { newChild->nextSibling->previousSibling = newChild->previousSibling; } else { if (newChild->parentNode) { newChild->parentNode->lastChild = newChild->previousSibling; } else { if (newChild->ownerDocument->rootNode->lastChild == newChild) { newChild->ownerDocument->rootNode->lastChild = newChild->previousSibling; } } } newChild->nextSibling = oldChild->nextSibling; newChild->previousSibling = oldChild->previousSibling; if (!newChild->parentNode && (newChild->ownerDocument->documentElement == newChild)) { newChild->ownerDocument->documentElement = newChild->ownerDocument->rootNode->firstChild; } if (node == node->ownerDocument->rootNode) { newChild->parentNode = NULL; } else { newChild->parentNode = node; } if (oldChild->previousSibling) { oldChild->previousSibling->nextSibling = newChild; } else { node->firstChild = newChild; } if (oldChild->nextSibling) { oldChild->nextSibling->previousSibling = newChild; } else { node->lastChild = newChild; } if (node->ownerDocument != newChild->ownerDocument || node->ownerDocument->nsptr || newChild->ownerDocument->baseURIs->numEntries) { domSetDocument (newChild, node->ownerDocument); } /* add old child into his fragment list */ if (oldChild->ownerDocument->fragments) { oldChild->nextSibling = oldChild->ownerDocument->fragments; oldChild->ownerDocument->fragments->previousSibling = oldChild; oldChild->ownerDocument->fragments = oldChild; } else { oldChild->ownerDocument->fragments = oldChild; oldChild->nextSibling = oldChild->previousSibling = NULL; } oldChild->parentNode = NULL; node->ownerDocument->nodeFlags |= NEEDS_RENUMBERING; MutationEvent(); return OK; } /*--------------------------------------------------------------------------- | domNewTextNode | \--------------------------------------------------------------------------*/ domTextNode * domNewTextNode( domDocument *doc, const char *value, int length, domNodeType nodeType ) { domTextNode *node; node = (domTextNode*) domAlloc(sizeof(domTextNode)); memset(node, 0, sizeof(domTextNode)); node->nodeType = nodeType; node->nodeNumber = NODE_NO(doc); node->ownerDocument = doc; node->valueLength = length; node->nodeValue = (char*)MALLOC(length); memmove(node->nodeValue, value, length); if (doc->fragments) { node->nextSibling = doc->fragments; doc->fragments->previousSibling = (domNode*)node; doc->fragments = (domNode*)node; } else { doc->fragments = (domNode*)node; } return node; } void domEscapeCData ( char *value, int length, Tcl_DString *escapedData ) { int i, start = 0; char *pc; Tcl_DStringInit (escapedData); pc = value; for (i = 0; i < length; i++) { if (*pc == '&') { Tcl_DStringAppend (escapedData, &value[start], i - start); Tcl_DStringAppend (escapedData, "&", 5); start = i+1; } else if (*pc == '<') { Tcl_DStringAppend (escapedData, &value[start], i - start); Tcl_DStringAppend (escapedData, "<", 4); start = i+1; } else if (*pc == '>') { Tcl_DStringAppend (escapedData, &value[start], i - start); Tcl_DStringAppend (escapedData, ">", 4); start = i+1; } pc++; } if (start) { Tcl_DStringAppend (escapedData, &value[start], length - start); } } /*--------------------------------------------------------------------------- | domAppendNewTextNode | \--------------------------------------------------------------------------*/ domTextNode * domAppendNewTextNode( domNode *parent, char *value, int length, domNodeType nodeType, int disableOutputEscaping ) { domTextNode *node; if (!length && (nodeType == TEXT_NODE)) { return NULL; } if (parent->lastChild && parent->lastChild->nodeType == TEXT_NODE && nodeType == TEXT_NODE ) { /*------------------------------------------------------------------ | append to already existing text node \-----------------------------------------------------------------*/ domAppendData ((domTextNode *) (parent->lastChild), value, length, disableOutputEscaping); MutationEvent(); return (domTextNode*)parent->lastChild; } node = (domTextNode*) domAlloc(sizeof(domTextNode)); memset(node, 0, sizeof(domTextNode)); node->nodeType = nodeType; if (disableOutputEscaping) { node->nodeFlags |= DISABLE_OUTPUT_ESCAPING; } node->nodeNumber = NODE_NO(parent->ownerDocument); node->ownerDocument = parent->ownerDocument; node->valueLength = length; node->nodeValue = (char*)MALLOC(length); memmove(node->nodeValue, value, length); if (parent->lastChild) { parent->lastChild->nextSibling = (domNode*)node; node->previousSibling = parent->lastChild; } else { parent->firstChild = (domNode*)node; node->previousSibling = NULL; } parent->lastChild = (domNode*)node; node->nextSibling = NULL; if (parent != parent->ownerDocument->rootNode) { node->parentNode = parent; } MutationEvent(); return node; } /*--------------------------------------------------------------------------- | domAppendNewElementNode | \--------------------------------------------------------------------------*/ domNode * domAppendNewElementNode( domNode *parent, const char *tagName, const char *uri ) { Tcl_HashEntry *h; domNode *node; domNS *ns; domAttrNode *NSattr; int hnew; char prefix[MAX_PREFIX_LEN]; const char *localname; Tcl_DString dStr; if (parent == NULL) { DBG(fprintf(stderr, "dom.c: Error parent == NULL!\n");) return NULL; } h = Tcl_CreateHashEntry(&HASHTAB(parent->ownerDocument,tdom_tagNames), tagName, &hnew); node = (domNode*) domAlloc(sizeof(domNode)); memset(node, 0, sizeof(domNode)); node->nodeType = ELEMENT_NODE; node->nodeNumber = NODE_NO(parent->ownerDocument); node->ownerDocument = parent->ownerDocument; node->nodeName = (char *)&(h->key); if (parent->lastChild) { parent->lastChild->nextSibling = node; node->previousSibling = parent->lastChild; } else { parent->firstChild = node; node->previousSibling = NULL; } parent->lastChild = node; node->nextSibling = NULL; if (parent != parent->ownerDocument->rootNode) { node->parentNode = parent; } /*-------------------------------------------------------- | re-use existing namespace or create a new one \-------------------------------------------------------*/ if (uri) { domSplitQName (tagName, prefix, &localname); DBG(fprintf(stderr, "tag '%s' has prefix='%s' \n", tagName, prefix);) ns = domLookupPrefix (node, prefix); if (!ns || (strcmp (uri, ns->uri)!=0)) { ns = domNewNamespace(node->ownerDocument, prefix, uri); if (prefix[0] == '\0') { domSetAttributeNS (node, "xmlns", uri, NULL, 1); } else { Tcl_DStringInit (&dStr); Tcl_DStringAppend (&dStr, "xmlns:", 6); Tcl_DStringAppend (&dStr, prefix, -1); domSetAttributeNS (node, Tcl_DStringValue (&dStr), uri, NULL, 1); } } node->namespace = ns->index; } else { ns = domLookupPrefix (node, ""); if (ns) { if (strcmp (ns->uri, "")!=0) { NSattr = domSetAttributeNS (node, "xmlns", "", NULL, 1); if (NSattr) { node->namespace = NSattr->namespace; } } else { node->namespace = ns->index; } } } MutationEvent(); return node; } /* *---------------------------------------------------------------------- * * domAppendData -- * * This procedure implements the dom method appendData. It is * also used by domNormalize and domAppendNewTextNode. * * Results: * A domException; currently always OK. * * Side effects: * Appends the data to node. * *---------------------------------------------------------------------- */ domException domAppendData ( domTextNode *node, /* The node, to append value to. Must be a TEXT_NODE, COMMENT_NODE or CDATA_SECTION_NODE*/ char *value, /* The data to append */ int length, /* The length of value in byte */ int disableOutputEscaping /* If true, disable output escaping on the node */ ) { Tcl_DString escData; if (node->nodeFlags & DISABLE_OUTPUT_ESCAPING) { if (disableOutputEscaping) { node->nodeValue = REALLOC (node->nodeValue, node->valueLength + length); memmove (node->nodeValue + node->valueLength, value, length); node->valueLength += length; } else { domEscapeCData (value, length, &escData); if (Tcl_DStringLength (&escData)) { node->nodeValue = REALLOC (node->nodeValue, node->valueLength + Tcl_DStringLength (&escData)); memmove (node->nodeValue + node->valueLength, Tcl_DStringValue (&escData), Tcl_DStringLength (&escData)); node->valueLength += Tcl_DStringLength (&escData); } else { node->nodeValue = REALLOC (node->nodeValue, node->valueLength+length); memmove (node->nodeValue + node->valueLength, value, length); node->valueLength += length; } Tcl_DStringFree (&escData); } } else { if (disableOutputEscaping) { node->nodeFlags |= DISABLE_OUTPUT_ESCAPING; domEscapeCData (node->nodeValue, node->valueLength, &escData); if (Tcl_DStringLength (&escData)) { FREE (node->nodeValue); node->nodeValue = MALLOC (Tcl_DStringLength (&escData) + length); memmove (node->nodeValue, Tcl_DStringValue (&escData), Tcl_DStringLength (&escData)); node->valueLength = Tcl_DStringLength (&escData); } else { node->nodeValue = REALLOC (node->nodeValue, node->valueLength+length); } Tcl_DStringFree (&escData); } else { node->nodeValue = REALLOC (node->nodeValue, node->valueLength + length); } memmove (node->nodeValue + node->valueLength, value, length); node->valueLength += length; } return OK; } /* *---------------------------------------------------------------------- * * domNormalize -- * * This procedure implements the dom method normalize. Puts all * Text nodes in the full depth of the sub-tree underneath node, * including attribute nodes, into a "normal" form where only * structure (e.g., elements, comments, processing instructions, * CDATA sections, and entity references) separates Text nodes, * i.e., there are neither adjacent Text nodes nor empty Text * nodes. If the flag forXPath is true, then CDATASection nodes * are treated as if they are text nodes (and merged with * circumjacent text nodes). Node must be an ELEMENT_NODE. * * Results: * None. * * Side effects: * May alter the tree. * *---------------------------------------------------------------------- */ void domNormalize ( domNode *node, /* root of the sub-tree to normalize */ int forXPath, /* if true, treat CDATA_SECTION_NODEs as if they where TEXT_NODEs */ domFreeCallback freeCB, /* Function to call, if a node must be freed */ void *clientData /* ClientData, to provide to the freeCB */ ) { domNode *child, *nextChild; int merge = 0; if (node->nodeType != ELEMENT_NODE) return; child = node->firstChild; while (child) { merge = 0; switch (child->nodeType) { case ELEMENT_NODE: domNormalize (child, forXPath, freeCB, clientData); break; case TEXT_NODE: if (child->previousSibling && child->previousSibling->nodeType == TEXT_NODE) { merge = 1; } else { if (((domTextNode *)child)->valueLength == 0) { nextChild = child->nextSibling; domDeleteNode (child, freeCB, clientData); child = nextChild; continue; } } break; case CDATA_SECTION_NODE: if (forXPath) { if (child->previousSibling && child->previousSibling->nodeType == TEXT_NODE) { merge = 1; } else { if (((domTextNode *)child)->valueLength == 0) { nextChild = child->nextSibling; domDeleteNode (child, freeCB, clientData); child = nextChild; continue; } child->nodeType = TEXT_NODE; } } break; default: break; } if (merge) { domAppendData ( (domTextNode *)(child->previousSibling), ((domTextNode *)child)->nodeValue, ((domTextNode *)child)->valueLength, (child->nodeFlags & DISABLE_OUTPUT_ESCAPING) ); nextChild = child->nextSibling; domDeleteNode (child, freeCB, clientData); child = nextChild; } else { child = child->nextSibling; } } } /*--------------------------------------------------------------------------- | domAddNSToNode | \--------------------------------------------------------------------------*/ domNS * domAddNSToNode ( domNode *node, domNS *nsToAdd ) { domAttrNode *attr, *lastNSAttr; domNS *ns, noNS; Tcl_HashEntry *h; int hnew; Tcl_DString dStr; if (!nsToAdd) { noNS.uri = ""; noNS.prefix = ""; noNS.index = 0; nsToAdd = &noNS; } DBG(fprintf (stderr, "domAddNSToNode to node '%s': prefix: %s, uri: %s\n", node->nodeName, nsToAdd->prefix, nsToAdd->uri);) ns = domLookupPrefix (node, nsToAdd->prefix); if (ns) { if (strcmp (ns->uri, nsToAdd->uri)==0) { /* namespace already in scope, we're done. */ return ns; } } else { /* If the NS to set was no NS and there isn't a default NS we're done */ if (nsToAdd->prefix[0] == '\0' && nsToAdd->uri[0] == '\0') return NULL; } ns = domNewNamespace (node->ownerDocument, nsToAdd->prefix, nsToAdd->uri); Tcl_DStringInit (&dStr); if (nsToAdd->prefix[0] == '\0') { Tcl_DStringAppend (&dStr, "xmlns", 5); } else { Tcl_DStringAppend (&dStr, "xmlns:", 6); Tcl_DStringAppend (&dStr, nsToAdd->prefix, -1); } /* Add new namespace attribute */ attr = (domAttrNode*) domAlloc(sizeof(domAttrNode)); memset(attr, 0, sizeof(domAttrNode)); h = Tcl_CreateHashEntry(&HASHTAB(node->ownerDocument,tdom_attrNames), Tcl_DStringValue(&dStr), &hnew); attr->nodeType = ATTRIBUTE_NODE; attr->nodeFlags = IS_NS_NODE; attr->namespace = ns->index; attr->nodeName = (char *)&(h->key); attr->parentNode = node; attr->valueLength = strlen(nsToAdd->uri); attr->nodeValue = (char*)MALLOC(attr->valueLength+1); strcpy(attr->nodeValue, nsToAdd->uri); lastNSAttr = NULL; if (node->firstAttr && (node->firstAttr->nodeFlags & IS_NS_NODE)) { lastNSAttr = node->firstAttr; while (lastNSAttr->nextSibling && (lastNSAttr->nextSibling->nodeFlags & IS_NS_NODE)) { lastNSAttr = lastNSAttr->nextSibling; } } if (lastNSAttr) { attr->nextSibling = lastNSAttr->nextSibling; lastNSAttr->nextSibling = attr; } else { attr->nextSibling = node->firstAttr; node->firstAttr = attr; } Tcl_DStringFree (&dStr); return ns; } /*--------------------------------------------------------------------------- | domAppendLiteralNode | \--------------------------------------------------------------------------*/ domNode * domAppendLiteralNode( domNode *parent, domNode *literalNode ) { Tcl_HashEntry *h; domNode *node; int hnew; if (parent == NULL) { DBG(fprintf(stderr, "dom.c: Error parent == NULL!\n");) return NULL; } h = Tcl_CreateHashEntry(&HASHTAB(parent->ownerDocument, tdom_tagNames), literalNode->nodeName, &hnew); node = (domNode*) domAlloc(sizeof(domNode)); memset(node, 0, sizeof(domNode)); node->nodeType = ELEMENT_NODE; node->nodeNumber = NODE_NO(parent->ownerDocument); node->ownerDocument = parent->ownerDocument; node->nodeName = (char *)&(h->key); if (parent->lastChild) { parent->lastChild->nextSibling = node; node->previousSibling = parent->lastChild; } else { parent->firstChild = node; node->previousSibling = NULL; } parent->lastChild = node; node->nextSibling = NULL; if (parent != parent->ownerDocument->rootNode) { node->parentNode = parent; } MutationEvent(); return node; } /*--------------------------------------------------------------------------- | domNewProcessingInstructionNode | \--------------------------------------------------------------------------*/ domProcessingInstructionNode * domNewProcessingInstructionNode( domDocument *doc, const char *targetValue, int targetLength, const char *dataValue, int dataLength ) { domProcessingInstructionNode *node; node = (domProcessingInstructionNode*) domAlloc(sizeof(domProcessingInstructionNode)); memset(node, 0, sizeof(domProcessingInstructionNode)); node->nodeType = PROCESSING_INSTRUCTION_NODE; node->nodeNumber = NODE_NO(doc); node->ownerDocument = doc; node->targetLength = targetLength; node->targetValue = (char*)MALLOC(targetLength); memmove(node->targetValue, targetValue, targetLength); node->dataLength = dataLength; node->dataValue = (char*)MALLOC(dataLength); memmove(node->dataValue, dataValue, dataLength); if (doc->fragments) { node->nextSibling = doc->fragments; doc->fragments->previousSibling = (domNode*)node; doc->fragments = (domNode*)node; } else { doc->fragments = (domNode*)node; } MutationEvent(); return node; } /*--------------------------------------------------------------------------- | domNewElementNode | \--------------------------------------------------------------------------*/ domNode * domNewElementNode( domDocument *doc, const char *tagName ) { domNode *node; Tcl_HashEntry *h; int hnew; h = Tcl_CreateHashEntry(&HASHTAB(doc, tdom_tagNames), tagName, &hnew); node = (domNode*) domAlloc(sizeof(domNode)); memset(node, 0, sizeof(domNode)); node->nodeType = ELEMENT_NODE; node->nodeNumber = NODE_NO(doc); node->ownerDocument = doc; node->nodeName = (char *)&(h->key); if (doc->fragments) { node->nextSibling = doc->fragments; doc->fragments->previousSibling = node; doc->fragments = node; } else { doc->fragments = node; } return node; } /*--------------------------------------------------------------------------- | domNewElementNodeNS | \--------------------------------------------------------------------------*/ domNode * domNewElementNodeNS ( domDocument *doc, const char *tagName, const char *uri ) { domNode *node; Tcl_HashEntry *h; int hnew; char prefix[MAX_PREFIX_LEN]; const char *localname; domNS *ns; domSplitQName (tagName, prefix, &localname); if (prefix[0] == '\0' && uri[0] == '\0') { return NULL; } h = Tcl_CreateHashEntry(&HASHTAB(doc, tdom_tagNames), tagName, &hnew); node = (domNode*) domAlloc(sizeof(domNode)); memset(node, 0, sizeof(domNode)); node->nodeType = ELEMENT_NODE; node->nodeNumber = NODE_NO(doc); node->ownerDocument = doc; node->nodeName = (char *)&(h->key); ns = domNewNamespace(doc, prefix, uri); node->namespace = ns->index; if (doc->fragments) { node->nextSibling = doc->fragments; doc->fragments->previousSibling = node; doc->fragments = node; } else { doc->fragments = node; } return node; } /*--------------------------------------------------------------------------- | domCloneNode | \--------------------------------------------------------------------------*/ domNode * domCloneNode ( domNode *node, int deep ) { domAttrNode *attr, *nattr; domNode *n, *child, *newChild; /*------------------------------------------------------------------ | create new node \-----------------------------------------------------------------*/ if (node->nodeType == PROCESSING_INSTRUCTION_NODE) { domProcessingInstructionNode *pinode = (domProcessingInstructionNode*)node; return (domNode*) domNewProcessingInstructionNode( pinode->ownerDocument, pinode->targetValue, pinode->targetLength, pinode->dataValue, pinode->dataLength); } if (node->nodeType != ELEMENT_NODE) { domTextNode *t1node, *tnode = (domTextNode*)node; if (tnode->info) { t1node = domNewTextNode(tnode->ownerDocument, tnode->nodeValue, tnode->valueLength, tnode->nodeType); t1node->info = tnode->info; return (domNode*) t1node; } else { return (domNode*) domNewTextNode(tnode->ownerDocument, tnode->nodeValue, tnode->valueLength, tnode->nodeType); } } n = domNewElementNode(node->ownerDocument, node->nodeName); n->namespace = node->namespace; n->info = node->info; /*------------------------------------------------------------------ | copy attributes (if any) \-----------------------------------------------------------------*/ attr = node->firstAttr; while (attr != NULL) { nattr = domSetAttribute (n, attr->nodeName, attr->nodeValue ); nattr->namespace = attr->namespace; if (attr->nodeFlags & IS_NS_NODE) { nattr->nodeFlags |= IS_NS_NODE; } attr = attr->nextSibling; } if (deep) { child = node->firstChild; while (child) { newChild = domCloneNode(child, deep); /* append new (cloned)child to cloned node, its new parent. Don't use domAppendChild for this, because that would mess around with the namespaces */ if (n->ownerDocument->fragments->nextSibling) { n->ownerDocument->fragments = n->ownerDocument->fragments->nextSibling; n->ownerDocument->fragments->previousSibling = NULL; newChild->nextSibling = NULL; } else { n->ownerDocument->fragments = NULL; } if (n->firstChild) { newChild->previousSibling = n->lastChild; n->lastChild->nextSibling = newChild; } else { n->firstChild = newChild; } n->lastChild = newChild; newChild->parentNode = n; /* clone next child */ child = child->nextSibling; } } return n; } /*---------------------------------------------------------------------------- | domCopyNS | \---------------------------------------------------------------------------*/ void domCopyNS ( domNode *from, domNode *to ) { domNode *n, *n1; domNS *ns, *ns1; domAttrNode *attr, *attr1; int skip; n = from; while (n) { attr = n->firstAttr; while (attr && (attr->nodeFlags & IS_NS_NODE)) { ns = n->ownerDocument->namespaces[attr->namespace-1]; skip = 0; n1 = from; while (n1 != n) { attr1 = n1->firstAttr; while (attr1 && (attr1->nodeFlags & IS_NS_NODE)) { ns1 = n1->ownerDocument->namespaces[attr1->namespace-1]; if ((ns1->prefix == NULL && ns->prefix == NULL) || (strcmp (ns1->prefix, ns->prefix)==0)) { skip = 1; break; } attr1 = attr1->nextSibling; } if (skip) break; n1 = n1->parentNode; } if (!skip) { /* Add this prefix/uri combination only to the destination, if it isn't already in scope */ ns1 = domLookupPrefix (to, ns->prefix); if (!ns1 || (strcmp (ns->uri, ns1->uri)!=0)) { domAddNSToNode (to, ns); } } attr = attr->nextSibling; } n = n->parentNode; } } /*--------------------------------------------------------------------------- | domCopyTo | \--------------------------------------------------------------------------*/ void domCopyTo ( domNode *node, domNode *parent, int copyNS ) { domAttrNode *attr, *nattr; domNode *n, *child; domNS *ns, *ns1; /*------------------------------------------------------------------ | create new node \-----------------------------------------------------------------*/ if (node->nodeType == PROCESSING_INSTRUCTION_NODE) { domProcessingInstructionNode *pinode = (domProcessingInstructionNode*)node; n = (domNode*) domNewProcessingInstructionNode( parent->ownerDocument, pinode->targetValue, pinode->targetLength, pinode->dataValue, pinode->dataLength); domAppendChild (parent, n); return; } if (node->nodeType != ELEMENT_NODE) { domTextNode *tnode = (domTextNode*)node; n = (domNode*) domNewTextNode(parent->ownerDocument, tnode->nodeValue, tnode->valueLength, tnode->nodeType); domAppendChild (parent, n); return; } n = domAppendLiteralNode (parent, node); if (copyNS) { domCopyNS (node, n); } /*------------------------------------------------------------------ | copy attributes (if any) \-----------------------------------------------------------------*/ attr = node->firstAttr; while (attr != NULL) { if (attr->nodeFlags & IS_NS_NODE) { if (copyNS) { /* If copyNS is true, then all namespaces in scope * (including the one declared with the node to copy) * are already copied over. */ attr = attr->nextSibling; continue; } ns = node->ownerDocument->namespaces[attr->namespace-1]; ns1 = domLookupPrefix (n, ns->prefix); if (ns1 && strcmp (ns->uri, ns1->uri)==0) { /* This namespace is already in scope, so we don't copy the namespace attribute over */ attr = attr->nextSibling; continue; } nattr = domSetAttribute (n, attr->nodeName, attr->nodeValue ); nattr->nodeFlags = attr->nodeFlags; ns1 = domNewNamespace (n->ownerDocument, ns->prefix, ns->uri); nattr->namespace = ns1->index; } else { nattr = domSetAttribute (n, attr->nodeName, attr->nodeValue ); nattr->nodeFlags = attr->nodeFlags; if (attr->namespace) { ns = node->ownerDocument->namespaces[attr->namespace-1]; ns1 = domLookupPrefix (n, ns->prefix); if (ns1) { nattr->namespace = ns1->index; } } } attr = attr->nextSibling; } /* We have to set the node namespace index after copying the attribute nodes over, because the node may be in a namespace, which is declared just at the node. */ if (node->namespace) { ns = node->ownerDocument->namespaces[node->namespace-1]; ns1 = domLookupPrefix (n, ns->prefix); n->namespace = ns1->index; } child = node->firstChild; while (child) { domCopyTo(child, n, 0); child = child->nextSibling; } } /*--------------------------------------------------------------------------- | domXPointerChild | \--------------------------------------------------------------------------*/ int domXPointerChild ( domNode * node, int all, int instance, domNodeType type, char * element, char * attrName, char * attrValue, int attrLen, domAddCallback addCallback, void * clientData ) { domNode *child; domAttrNode *attr; int i=0, result; if (node->nodeType != ELEMENT_NODE) { return 0; } if (instance<0) { child = node->lastChild; } else { child = node->firstChild; } while (child) { if ((type == ALL_NODES) || (child->nodeType == type)) { if ((element == NULL) || ((child->nodeType == ELEMENT_NODE) && (strcmp(child->nodeName,element)==0)) ) { if (attrName == NULL) { i = (instance<0) ? i-1 : i+1; if (all || (i == instance)) { result = addCallback (child, clientData); if (result) { return result; } } } else { attr = child->firstAttr; while (attr) { if ((strcmp(attr->nodeName,attrName)==0) && ( (strcmp(attrValue,"*")==0) || ( (attr->valueLength == attrLen) && (strcmp(attr->nodeValue,attrValue)==0) ) ) ) { i = (instance<0) ? i-1 : i+1; if (all || (i == instance)) { result = addCallback (child, clientData); if (result) { return result; } } } attr = attr->nextSibling; } } } } if (instance<0) { child = child->previousSibling; } else { child = child->nextSibling; } } return 0; } /*--------------------------------------------------------------------------- | domXPointerXSibling | \--------------------------------------------------------------------------*/ int domXPointerXSibling ( domNode * node, int forward_mode, int all, int instance, domNodeType type, char * element, char * attrName, char * attrValue, int attrLen, domAddCallback addCallback, void * clientData ) { domNode *sibling, *endSibling; domAttrNode *attr; int i=0, result; if (forward_mode) { if (instance<0) { endSibling = node; sibling = node; if (node->parentNode) { sibling = node->parentNode->lastChild; } } else { sibling = node->nextSibling; endSibling = NULL; } } else { if (instance<0) { endSibling = node; sibling = node; if (node->parentNode) { sibling = node->parentNode->firstChild; } } else { sibling = node->previousSibling; endSibling = NULL; } instance = -1 * instance; } while (sibling != endSibling) { if ((type == ALL_NODES) || (sibling->nodeType == type)) { if ((element == NULL) || ((sibling->nodeType == ELEMENT_NODE) && (strcmp(sibling->nodeName,element)==0)) ) { if (attrName == NULL) { i = (instance<0) ? i-1 : i+1; if (all || (i == instance)) { result = addCallback (sibling, clientData); if (result) { return result; } } } else { attr = sibling->firstAttr; while (attr) { if ((strcmp(attr->nodeName,attrName)==0) && ( (strcmp(attrValue,"*")==0) || ( (attr->valueLength == attrLen) && (strcmp(attr->nodeValue,attrValue)==0) ) ) ) { i = (instance<0) ? i-1 : i+1; if (all || (i == instance)) { result = addCallback (sibling, clientData); if (result) { return result; } } } attr = attr->nextSibling; } } } } if (instance<0) { sibling = sibling->previousSibling; } else { sibling = sibling->nextSibling; } } return 0; } /*--------------------------------------------------------------------------- | domXPointerDescendant | \--------------------------------------------------------------------------*/ int domXPointerDescendant ( domNode * node, int all, int instance, int * i, domNodeType type, char * element, char * attrName, char * attrValue, int attrLen, domAddCallback addCallback, void * clientData ) { domNode *child; domAttrNode *attr; int found=0, result; if (node->nodeType != ELEMENT_NODE) { return 0; } if (instance<0) { child = node->lastChild; } else { child = node->firstChild; } while (child) { found = 0; if ((type == ALL_NODES) || (child->nodeType == type)) { if ((element == NULL) || ((child->nodeType == ELEMENT_NODE) && (strcmp(child->nodeName,element)==0)) ) { if (attrName == NULL) { *i = (instance<0) ? (*i)-1 : (*i)+1; if (all || (*i == instance)) { result = addCallback (child, clientData); if (result) { return result; } found = 1; } } else { attr = child->firstAttr; while (attr) { if ((strcmp(attr->nodeName,attrName)==0) && ( (strcmp(attrValue,"*")==0) || ( (attr->valueLength == attrLen) && (strcmp(attr->nodeValue,attrValue)==0) ) ) ) { *i = (instance<0) ? (*i)-1 : (*i)+1; if (all || (*i == instance)) { result = addCallback (child, clientData); if (result) { return result; } found = 1; } } attr = attr->nextSibling; } } } } if (!found) { /* recurs into childs */ result = domXPointerDescendant (child, all, instance, i, type, element, attrName, attrValue, attrLen, addCallback, clientData); if (result) { return result; } } if (instance<0) { child = child->previousSibling; } else { child = child->nextSibling; } } return 0; } /*--------------------------------------------------------------------------- | domXPointerAncestor | \--------------------------------------------------------------------------*/ int domXPointerAncestor ( domNode * node, int all, int instance, int * i, domNodeType type, char * element, char * attrName, char * attrValue, int attrLen, domAddCallback addCallback, void * clientData ) { domNode *ancestor; domAttrNode *attr; int result; ancestor = node->parentNode; if (ancestor) { if ((type == ALL_NODES) || (ancestor->nodeType == type)) { if ((element == NULL) || ((ancestor->nodeType == ELEMENT_NODE) && (strcmp(ancestor->nodeName,element)==0)) ) { if (attrName == NULL) { *i = (instance<0) ? (*i)-1 : (*i)+1; if (all || (*i == instance)) { result = addCallback (ancestor, clientData); if (result) { return result; } } } else { attr = ancestor->firstAttr; while (attr) { if ((strcmp(attr->nodeName,attrName)==0) && ( (strcmp(attrValue,"*")==0) || ( (attr->valueLength == attrLen) && (strcmp(attr->nodeValue,attrValue)==0) ) ) ) { *i = (instance<0) ? (*i)-1 : (*i)+1; if (all || (*i == instance)) { result = addCallback (ancestor, clientData); if (result) { return result; } } } attr = attr->nextSibling; } } } } /* go up */ result = domXPointerAncestor (ancestor, all, instance, i, type, element, attrName, attrValue, attrLen, addCallback, clientData); if (result) { return result; } } return 0; } /*--------------------------------------------------------------------------- | type tdomCmdReadInfo | \--------------------------------------------------------------------------*/ typedef struct _tdomCmdReadInfo { XML_Parser parser; domDocument *document; domNode *currentNode; int depth; int ignoreWhiteSpaces; int cdataSection; Tcl_DString *cdata; int storeLineColumn; int ignorexmlns; int feedbackAfter; Tcl_Obj *feedbackCmd; int nextFeedbackPosition; Tcl_Interp *interp; int activeNSsize; int activeNSpos; domActiveNS *activeNS; int baseURIstackSize; int baseURIstackPos; domActiveBaseURI *baseURIstack; int insideDTD; /* Now the tdom cmd specific elements */ int tdomStatus; Tcl_Obj *extResolver; } tdomCmdReadInfo; int tcldom_returnDocumentObj (Tcl_Interp *interp, domDocument *document, int setVariable, Tcl_Obj *var_name, int trace, int forOwnerDocument); void tdom_freeProc ( Tcl_Interp *interp, void *userData ) { tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData; if (info->document) { domFreeDocument (info->document, NULL, NULL); } if (info->activeNS) { FREE (info->activeNS); } if (info->baseURIstack) { FREE (info->baseURIstack); } Tcl_DStringFree (info->cdata); FREE (info->cdata); if (info->extResolver) { Tcl_DecrRefCount (info->extResolver); } FREE (info); } void tdom_parserResetProc ( XML_Parser parser, void *userData ) { tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData; info->parser = parser; } void tdom_resetProc ( Tcl_Interp *interp, void *userData ) { tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData; if (!info->tdomStatus) return; if (info->document) { domFreeDocument (info->document, NULL, NULL); } info->document = NULL; info->currentNode = NULL; info->depth = 0; info->feedbackAfter = 0; info->ignorexmlns = 0; Tcl_DStringSetLength (info->cdata, 0); info->nextFeedbackPosition = info->feedbackAfter; info->interp = interp; info->activeNSpos = -1; info->insideDTD = 0; info->baseURIstackPos = 0; info->tdomStatus = 0; } void tdom_initParseProc ( Tcl_Interp *interp, void *userData ) { tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData; info->document = domCreateDoc(XML_GetBase (info->parser), info->storeLineColumn); if (info->extResolver) { info->document->extResolver = tdomstrdup (Tcl_GetString (info->extResolver)); } info->baseURIstack[0].baseURI = XML_GetBase (info->parser); info->baseURIstack[0].depth = 0; info->tdomStatus = 2; } static void tdom_charDataHandler ( void *userData, const char *s, int len ) { domReadInfo *info = userData; Tcl_DStringAppend (info->cdata, s, len); DispatchPCDATA (info); return; } int TclTdomObjCmd (dummy, interp, objc, objv) ClientData dummy; Tcl_Interp *interp; int objc; Tcl_Obj *const objv[]; { CHandlerSet *handlerSet; int methodIndex, result, bool; tdomCmdReadInfo *info; TclGenExpatInfo *expat; Tcl_Obj *newObjName = NULL; static const char *tdomMethods[] = { "enable", "getdoc", "setStoreLineColumn", "setExternalEntityResolver", "keepEmpties", "remove", "ignorexmlns", "keepCDATA", NULL }; enum tdomMethod { m_enable, m_getdoc, m_setStoreLineColumn, m_setExternalEntityResolver, m_keepEmpties, m_remove, m_ignorexmlns, m_keepCDATA }; if (objc < 3 || objc > 4) { Tcl_WrongNumArgs (interp, 1, objv, tdom_usage); return TCL_ERROR; } if (!CheckExpatParserObj (interp, objv[1])) { Tcl_SetResult (interp, "First argument has to be a expat parser object", NULL); return TCL_ERROR; } if (Tcl_GetIndexFromObj (interp, objv[2], tdomMethods, "method", 0, &methodIndex) != TCL_OK) { Tcl_SetResult (interp, tdom_usage, NULL); return TCL_ERROR; } switch ((enum tdomMethod) methodIndex) { default: Tcl_SetResult (interp, "unknown method", NULL); return TCL_ERROR; case m_enable: expat = GetExpatInfo (interp, objv[1]); if (expat->parsingState != 0) { Tcl_SetResult (interp, "Parser is not in init or reset state.", NULL); return TCL_ERROR; } handlerSet = CHandlerSetCreate ("tdom"); handlerSet->ignoreWhiteCDATAs = 1; handlerSet->resetProc = tdom_resetProc; handlerSet->freeProc = tdom_freeProc; handlerSet->parserResetProc = tdom_parserResetProc; handlerSet->initParseProc = tdom_initParseProc; handlerSet->elementstartcommand = startElement; handlerSet->elementendcommand = endElement; handlerSet->datacommand = tdom_charDataHandler; /* handlerSet->datacommand = characterDataHandler; */ handlerSet->commentCommand = commentHandler; handlerSet->picommand = processingInstructionHandler; handlerSet->entityDeclCommand = entityDeclHandler; handlerSet->startDoctypeDeclCommand = startDoctypeDeclHandler; handlerSet->endDoctypeDeclCommand = endDoctypeDeclHandler; info = (tdomCmdReadInfo *) MALLOC (sizeof (tdomCmdReadInfo)); info->parser = expat->parser; info->document = NULL; info->currentNode = NULL; info->depth = 0; info->ignoreWhiteSpaces = 1; info->cdataSection = 0; info->cdata = (Tcl_DString*) MALLOC (sizeof (Tcl_DString)); Tcl_DStringInit (info->cdata); info->storeLineColumn = 0; info->ignorexmlns = 0; info->feedbackAfter = 0; info->feedbackCmd = NULL; info->nextFeedbackPosition = 0; info->interp = interp; info->activeNSpos = -1; info->activeNSsize = 8; info->activeNS = (domActiveNS*) MALLOC(sizeof(domActiveNS) * info->activeNSsize); info->baseURIstackPos = 0; info->baseURIstackSize = INITIAL_BASEURISTACK_SIZE; info->baseURIstack = (domActiveBaseURI*) MALLOC (sizeof(domActiveBaseURI) * info->baseURIstackSize); info->insideDTD = 0; info->tdomStatus = 0; info->extResolver = NULL; handlerSet->userData = info; CHandlerSetInstall (interp, objv[1], handlerSet); break; case m_getdoc: info = CHandlerSetGetUserData (interp, objv[1], "tdom"); if (!info) { Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); return TCL_ERROR; } expat = GetExpatInfo (interp, objv[1]); if (info->tdomStatus != 2 || !expat->finished) { Tcl_SetResult (interp, "No DOM tree available.", NULL); return TCL_ERROR; } domSetDocumentElement (info->document); result = tcldom_returnDocumentObj (interp, info->document, 0, newObjName, 0, 0); info->document = NULL; return result; case m_setStoreLineColumn: info = CHandlerSetGetUserData (interp, objv[1], "tdom"); if (!info) { Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); return TCL_ERROR; } Tcl_SetIntObj (Tcl_GetObjResult (interp), info->storeLineColumn); if (objc == 4) { if (Tcl_GetBooleanFromObj (interp, objv[3], &bool) != TCL_OK) { return TCL_ERROR; } info->storeLineColumn = bool; } info->tdomStatus = 1; break; case m_remove: result = CHandlerSetRemove (interp, objv[1], "tdom"); if (result == 2) { Tcl_SetResult (interp, "expat parser obj hasn't a C handler set named \"tdom\"", NULL); return TCL_ERROR; } break; case m_setExternalEntityResolver: if (objc != 4) { Tcl_SetResult (interp, "You must name a Tcl command as external entity resolver for setExternalEntityResolver.", NULL); return TCL_ERROR; } info = CHandlerSetGetUserData (interp, objv[1], "tdom"); if (!info) { Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); return TCL_ERROR; } if (info->extResolver) { Tcl_DecrRefCount (info->extResolver); } if (strcmp (Tcl_GetString (objv[3]), "") == 0) { info->extResolver = NULL; } else { info->extResolver = objv[3]; Tcl_IncrRefCount (info->extResolver); } info->tdomStatus = 1; break; case m_keepEmpties: if (objc != 4) { Tcl_SetResult (interp, "wrong # of args for method keepEmpties.", NULL); return TCL_ERROR; } handlerSet = CHandlerSetGet (interp, objv[1], "tdom"); if (!handlerSet) { Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); return TCL_ERROR; } info = handlerSet->userData; if (!info) { Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); return TCL_ERROR; } Tcl_SetIntObj (Tcl_GetObjResult (interp), info->ignoreWhiteSpaces); if (Tcl_GetBooleanFromObj (interp, objv[3], &bool) != TCL_OK) { return TCL_ERROR; } info->ignoreWhiteSpaces = !bool; handlerSet->ignoreWhiteCDATAs = !bool; info->tdomStatus = 1; break; case m_keepCDATA: if (objc != 4) { Tcl_SetResult (interp, "wrong # of args for method keepCDATA.", NULL); return TCL_ERROR; } handlerSet = CHandlerSetGet (interp, objv[1], "tdom"); if (!handlerSet) { Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); return TCL_ERROR; } info = handlerSet->userData; if (!info) { Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); return TCL_ERROR; } if (Tcl_GetBooleanFromObj (interp, objv[3], &bool) != TCL_OK) { return TCL_ERROR; } if (bool) { handlerSet->startCdataSectionCommand = startCDATA; handlerSet->endCdataSectionCommand = endCDATA; } else { handlerSet->startCdataSectionCommand = startCDATA; handlerSet->endCdataSectionCommand = endCDATA; } info->tdomStatus = 1; break; case m_ignorexmlns: info = CHandlerSetGetUserData (interp, objv[1], "tdom"); if (!info) { Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL); return TCL_ERROR; } Tcl_SetIntObj (Tcl_GetObjResult (interp), info->ignorexmlns); if (objc == 4) { if (Tcl_GetBooleanFromObj (interp, objv[3], &bool) != TCL_OK) { return TCL_ERROR; } info->ignorexmlns = bool; } info->tdomStatus = 1; break; } return TCL_OK; }