/* domc document object model library in c * Copyright (c) 2001 Michael B. Allen * * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /* dom.c - document object model interface */ #include #include #include #include #include "dom.h" #if defined(__sparc__) #define NL "\n" #define HAVE_ENCDEC 0 #define HAVE_STRDUP 1 #define HAVE_STRNLEN 0 #define HAVE_EXPAT 195 #define HAVE_MBSTATE 0 #define HAVE_WCWIDTH 1 #define HAVE_SNPRINTF 1 #define HAVE_VARMACRO 1 #define HAVE_LANGINFO 1 #define HAVE_X11_KEYSYMS 0 #elif defined(_WIN32) #define NL "\r\n" #define HAVE_ENCDEC 0 #define HAVE_STRDUP 1 #define HAVE_STRNLEN 0 #define HAVE_EXPAT 195 #define HAVE_MBSTATE 0 #define HAVE_WCWIDTH 0 #define HAVE_SNPRINTF 0 #define HAVE_VARMACRO 0 #define HAVE_LANGINFO 0 #define HAVE_X11_KEYSYMS 0 #else #define NL "\n" #define HAVE_ENCDEC 0 #define HAVE_STRDUP 1 #define HAVE_STRNLEN 1 #define HAVE_EXPAT 195 #define HAVE_MBSTATE 1 #define HAVE_WCWIDTH 1 #define HAVE_SNPRINTF 1 #define HAVE_VARMACRO 1 #define HAVE_LANGINFO 1 #define HAVE_X11_KEYSYMS 0 #endif #if defined(__GNUC__) #if defined(MSGNO) #if defined(__GNUC__) && (__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 95)) #define MSG(fmt, args...) _msgno_printf("%s:%u:%s: " fmt, \ __FILE__, __LINE__, __FUNCTION__, ## args) #define MNO(msgno) _msgno_printf("%s:%u:%s: %s", \ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno)) #define MNF(msgno, fmt, args...) _msgno_printf("%s:%u:%s: %s" fmt, \ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno), ## args) #define PMSG(fmt, args...) (_msgno_buf_idx = sprintf(_msgno_buf, \ "%s:%u:%s: " fmt "\n", \ __FILE__, __LINE__, __FUNCTION__, ## args)) #define PMNO(msgno) (_msgno_buf_idx = sprintf(_msgno_buf, \ "%s:%u:%s: %s\n", \ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno))) #define PMNF(msgno, fmt, args...) (_msgno_buf_idx = sprintf(_msgno_buf, \ "%s:%u:%s: %s" fmt "\n", \ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno), ## args)) #define AMSG(fmt, args...) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \ " %s:%u:%s: " fmt "\n", \ __FILE__, __LINE__, __FUNCTION__, ## args)) #define AMNO(msgno) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \ " %s:%u:%s: %s\n", \ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno))) #define AMNF(msgno, fmt, args...) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \ " %s:%u:%s: %s" fmt "\n", \ __FILE__, __LINE__, __FUNCTION__, msgno_msg(msgno), ## args)) #else #define MSG(fmt, args...) _msgno_printf("%s:%u: " fmt "\n", \ __FILE__, __LINE__ , ## args) #define MNO(msgno) _msgno_printf("%s:%u: %s\n", \ __FILE__, __LINE__, msgno_msg(msgno)) #define MNF(msgno, fmt, args...) _msgno_printf("%s:%u: %s" fmt "\n", \ __FILE__, __LINE__, msgno_msg(msgno) , ## args) #define PMSG(fmt, args...) (_msgno_buf_idx = sprintf(_msgno_buf, \ "%s:%u: " fmt "\n", __FILE__, __LINE__ , ## args)) #define PMNO(msgno) (_msgno_buf_idx = sprintf(_msgno_buf, \ "%s:%u: %s\n", __FILE__, __LINE__, msgno_msg(msgno))) #define PMNF(msgno, fmt, args...) (_msgno_buf_idx = sprintf(_msgno_buf, \ "%s:%u: %s" fmt "\n", __FILE__, __LINE__, msgno_msg(msgno) , ## args)) #define AMSG(fmt, args...) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \ " %s:%u: "fmt"\n", __FILE__, __LINE__ , ## args)) #define AMNO(msgno) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \ " %s:%u: %s\n", __FILE__, __LINE__, msgno_msg(msgno))) #define AMNF(msgno, fmt, args...) (_msgno_buf_idx += sprintf(_msgno_buf + _msgno_buf_idx, \ " %s:%u: %s" fmt "\n", __FILE__, __LINE__, msgno_msg(msgno) , ## args)) #endif #else #define MSG(fmt, args...) #define MNO(msgno) #define MNF(msgno, fmt, args...) #define PMSG(fmt, args...) #define PMNO(msgno) #define PMNF(msgno, fmt, args...) #define AMSG(fmt, args...) #define AMNO(msgno) #define AMNF(msgno, fmt, args...) #endif #else #undef MSG #if defined(MSGNO) #define MSG msgno_hdlr #define MNO msgno_hdlr_mno #define MNF msgno_hdlr_mnf #define PMSG msgno_hdlr #define PMNO msgno_hdlr_mno #define PMNF msgno_hdlr_mnf #define AMSG msgno_hdlr #define AMNO msgno_hdlr_mno #define AMNF msgno_hdlr_mnf #else #define MSG msgno_noop_msg #define MNO msgno_noop_mno #define MNF msgno_noop_mnf #define PMSG msgno_noop_msg #define PMNO msgno_noop_mno #define PMNF msgno_noop_mnf #define AMSG msgno_noop_msg #define AMNO msgno_noop_mno #define AMNF msgno_noop_mnf #endif #endif #ifndef MSGNO_NUM_LISTS #define MSGNO_NUM_LISTS 16 #endif #ifndef MSGNO_BUFSIZ #define MSGNO_BUFSIZ 1024 #endif #define NULL_POINTER_ERR _builtin_codes[0].msgno struct msgno_entry _builtin_codes[2] = { { (1 << 16), "A parameter was NULL" }, { 0, NULL } }; static struct tbl_entry { struct msgno_entry *list; unsigned int num_msgs; } list_tbl[MSGNO_NUM_LISTS] = { { _builtin_codes, 1 } }; static unsigned int next_tbl_idx = 1; const char * msgno_msg(int msgno) { struct tbl_entry *te; unsigned int i; i = msgno >> 16; if (i == 0) { return strerror(msgno); } else if (i >= MSGNO_NUM_LISTS || (te = list_tbl + (i - 1)) == NULL) { return "No such msgno list"; } for (i = 0; i < te->num_msgs; i++) { if (te->list[i].msgno == msgno) { return te->list[i].msg; } } return "No such message in msgno list"; } int msgno_add_codes(struct msgno_entry *list) { struct tbl_entry *te; int next_msgno = 0, hi_bits; if (list == NULL || list->msg == NULL) { errno = EINVAL; return -1; } if (next_tbl_idx == MSGNO_NUM_LISTS) { errno = ERANGE; return -1; } for (te = list_tbl + 1; te->list; te++) { if (te->list == list) { return 0; /* already in list_tbl */ } } hi_bits = (next_tbl_idx + 1) << 16; te->list = list; while (list->msg) { if ((list->msgno & 0xFFFF0000)) { te->list = NULL; errno = ERANGE; return -1; } if (list->msgno == 0) { list->msgno = hi_bits | next_msgno++; } else if (list->msgno >= next_msgno) { next_msgno = list->msgno + 1; list->msgno = hi_bits | list->msgno; } else { te->list = NULL; errno = ERANGE; return -1; } te->num_msgs++; list++; } next_tbl_idx++; return 0; } int mbslen(const char *src) { return mbsnlen(src, -1, -1); } int mbsnlen(const char *src, size_t sn, int cn) { wchar_t ucs; int count, w; size_t n; #if HAVE_MBSTATE > 0 mbstate_t ps; #endif ucs = 1; count = 0; if (sn > INT_MAX) { sn = INT_MAX; } if (cn < 0) { cn = INT_MAX; } #if HAVE_MBSTATE memset(&ps, 0, sizeof(ps)); while (ucs && (n = mbrtowc(&ucs, src, sn, &ps)) != (size_t)-2) { #else while (ucs && sn > 0) { n = mbtowc(&ucs, src, sn); #endif if (n == (size_t)-1) { PMNO(errno); return -1; } if ((w = wcwidth(ucs)) == -1) { w = 1; } if (w > cn) { break; } cn -= w; sn -= n; src += n; count += w; } return count; } size_t mbsnsize(const char *src, size_t sn, int cn) { wchar_t ucs; int w; size_t tot, n; #if HAVE_MBSTATE > 0 mbstate_t ps; #endif tot = n = 0; ucs = 1; if (sn > INT_MAX) { sn = INT_MAX; } if (cn < 0) { cn = INT_MAX; } #if HAVE_MBSTATE memset(&ps, 0, sizeof(ps)); while (ucs && sn > 0 && (n = mbrtowc(&ucs, src, sn, &ps)) != (size_t)-2 && n) { #else while (ucs && sn > 0 && (n = mbtowc(&ucs, src, sn))) { #endif if (n == (size_t)-1) { PMNO(errno); return -1; } if ((w = wcwidth(ucs)) == -1) { w = 1; } if (w > cn) { break; } cn -= w; sn -= n; src += n; tot += n; } return tot; } size_t mbssize(const char *src) { return mbsnsize(src, -1, -1); } static char * mbsnoff(char *src, int off, size_t sn) { wchar_t ucs; size_t n; int w; #if HAVE_MBSTATE > 0 mbstate_t ps; #endif if (off == 0) { return src; } if (sn > INT_MAX) { sn = 0xFFFF; } if (off < 0) { off = 0xFFFF; } #if HAVE_MBSTATE > 0 memset(&ps, 0, sizeof(ps)); while (sn > 0 && (n = mbrtowc(&ucs, src, sn, &ps)) != (size_t)-2) { #else while (sn > 0) { n = mbtowc(&ucs, src, sn); #endif if (n == (size_t)-1) { PMNF(errno, "src=[%s]", mbstoax(src, sn, 1)); return NULL; } if (n == 0 || (w = wcwidth(ucs)) != 0) { w = 1; } if (w > off) { break; } if (w) off--; sn -= n; src += n ? n : 1; } return src; } static char * mbsoff(char *src, int off) { return mbsnoff(src, off, -1); } static char * mbsndup(const char *src, size_t sn, int cn) { size_t n; char *dst; if (src == NULL) { errno = EINVAL; PMNO(errno); return NULL; } if ((n = mbsnsize(src, sn, cn)) == (size_t)-1) { AMSG(""); return NULL; } if ((dst = malloc(n + 1)) == NULL) { PMNO(errno); return NULL; } memcpy(dst, src, n); dst[n] = '\0'; return dst; } static char * mbsdup(const char *src) { return mbsndup(src, -1, -1); } #if defined(_WIN32) #include typedef unsigned __int64 uint64_t; uint64_t timestamp(void) { FILETIME ftime; uint64_t ret; GetSystemTimeAsFileTime(&ftime); ret = ftime.dwHighDateTime; ret <<= 32Ui64; ret |= ftime.dwLowDateTime; return ret; } #else #include uint64_t timestamp(void) { struct timeval tval; if (gettimeofday(&tval, NULL) < 0) { DOM_Exception = errno; PMNO(DOM_Exception); return 1; } return tval.tv_sec * 1000LL + tval.tv_usec / 1000; } #endif /* Forward references for node.c */ DOM_Node *Document_createNode(DOM_Document *doc, unsigned short nodeType); NodeEntry *NodeList_remove(DOM_NodeList *nl, DOM_Node *oldChild); NodeEntry *NodeList_append(DOM_NodeList *nl, DOM_Node *newChild); DOM_NodeList *Document_createNodeList(DOM_Document *doc); DOM_NamedNodeMap *Document_createNamedNodeMap(DOM_Document *doc); /* Forward references for events.c */ void updateCommonParent(DOM_Node *node); /* DOM_Exception */ struct msgno_entry dom_codes[] = { { 1, "The index specified was out of range" }, { 0, "The text size is out of range" }, { 0, "The request violated tree hierarchy constraints" }, { 0, "The document context is invalid" }, { 0, "An inappropriate character was encountered" }, { 0, "The node does not support the addition of data" }, { 0, "No modification allowed" }, { 0, "The specified node was not found" }, { 0, "The requested operation is not supported" }, { 0, "The attribute is being used elsewhere" }, { 0, "An XML parser error occured" }, { 0, "Failed to create DOM object" }, { 0, "Character encoding error" }, { 0, "The event type was not specified by initialization" }, { 0, "A filtered list cannot be modified" }, { 0, NULL } }; int _exception = 0; int * _DOM_Exception(void) { return &_exception; } unsigned short child_matrix[] = { 0x00dd, /* DOM_ELEMENT_NODE 1 */ 0x0014, /* DOM_ATTRIBUTE_NODE 2 */ 0x0000, /* DOM_TEXT_NODE 3 */ 0x0000, /* DOM_CDATA_SECTION_NODE 4 */ 0x00dd, /* DOM_ENTITY_REFERENCE_NODE 5 */ 0x00dd, /* DOM_ENTITY_NODE 6 */ 0x0000, /* DOM_PROCESSING_INSTRUCTION_NODE 7 */ 0x0000, /* DOM_COMMENT_NODE 8 */ 0x02c1, /* DOM_DOCUMENT_NODE 9 */ 0x0820, /* DOM_DOCUMENT_TYPE_NODE 10 */ 0x00dd, /* DOM_DOCUMENT_FRAGMENT_NODE 11 */ 0x0000 /* DOM_NOTATION_NODE 12 */ }; const char *node_names[] = { "No such node type", "DOM_ELEMENT_NODE", "DOM_ATTRIBUTE_NODE", "DOM_TEXT_NODE", "DOM_CDATA_SECTION_NODE", "DOM_ENTITY_REFERENCE_NODE", "DOM_ENTITY_NODE", "DOM_PROCESSING_INSTRUCTION_NODE", "DOM_COMMENT_NODE", "DOM_DOCUMENT_NODE", "DOM_DOCUMENT_TYPE_NODE", "DOM_DOCUMENT_FRAGMENT_NODE", "DOM_NOTATION_NODE" }; /* DOM_Implementation and DOM_ImplementationLS */ int DOM_Implementation_hasFeature(DOM_String *feature, DOM_String *version) { feature = NULL; version = NULL; return 0; } DOM_DocumentType * DOM_Implementation_createDocumentType(DOM_String *qualifiedName, DOM_String *publicId, DOM_String *systemId) { DOM_DocumentType *doctype; DOM_NamedNodeMap *entities; DOM_NamedNodeMap *notations; if ((doctype = Document_createNode(NULL, DOM_DOCUMENT_TYPE_NODE)) == NULL) { AMSG(""); return NULL; } if ((doctype->nodeName = doctype->u.DocumentType.name = strdup(qualifiedName)) == NULL || (publicId && ((doctype->u.DocumentType.publicId = strdup(publicId))) == NULL) || (systemId && ((doctype->u.DocumentType.systemId = strdup(systemId))) == NULL)) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(NULL, doctype); return NULL; } if ((entities = Document_createNamedNodeMap(NULL)) == NULL || (notations = Document_createNamedNodeMap(NULL)) == NULL) { AMNO(DOM_CREATE_FAILED); DOM_Document_destroyNode(NULL, doctype); return NULL; } entities->filter = DOM_ENTITY_NODE; notations->filter = DOM_NOTATION_NODE; entities->list = notations->list = doctype->childNodes; doctype->u.DocumentType.entities = entities; doctype->u.DocumentType.notations = notations; return doctype; } DOM_Document * DOM_Implementation_createDocument(DOM_String *namespaceURI, DOM_String *qualifiedName, DOM_DocumentType *doctype) { DOM_Document *doc; DOM_Element *el; namespaceURI = NULL; msgno_add_codes(dom_codes); if ((doc = Document_createNode(NULL, DOM_DOCUMENT_NODE)) == NULL) { AMSG(""); return NULL; } doc->nodeName = "#document"; if (doctype) { DOM_Node_appendChild(doc, doctype); } if (qualifiedName && *qualifiedName) { if ((el = DOM_Document_createElement(doc, qualifiedName)) == NULL) { AMSG(""); DOM_Document_destroyNode(doc, doc); return NULL; } DOM_Node_appendChild(doc, el); } return doc; } DOM_String * DOM_Element_getAttribute(const DOM_Element *element, const DOM_String *name) { DOM_Node *node; DOM_String *r = NULL; if (element && name && element->attributes) { if ((node = DOM_NamedNodeMap_getNamedItem(element->attributes, name))) { if ((r = strdup(node->nodeValue)) == NULL) { AMSG(""); return NULL; } } else if ((r = strdup("")) == NULL) { PMNO(errno); return NULL; } } return r; } void DOM_Element_setAttribute(DOM_Element *element, const DOM_String *name, const DOM_String *value) { DOM_Attr *attr; DOM_String *prevValue; unsigned short attrChange; DOM_MutationEvent evt; if (element == NULL || name == NULL || value == NULL || element->attributes == NULL) { return; } attr = DOM_NamedNodeMap_getNamedItem(element->attributes, name); if (attr) { prevValue = attr->nodeValue; attrChange = DOM_MUTATION_EVENT_MODIFICATION; if ((attr->nodeValue = attr->u.Attr.value = strdup(value)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(attr->ownerDocument, attr); return; } } else { prevValue = NULL; attrChange = DOM_MUTATION_EVENT_ADDITION; if ((attr = DOM_Document_createAttribute(element->ownerDocument, name)) == NULL) { AMNO(DOM_CREATE_FAILED); return; } free(attr->nodeValue); if ((attr->nodeValue = attr->u.Attr.value = strdup(value)) == NULL) { DOM_Exception = errno; DOM_Document_destroyNode(attr->ownerDocument, attr); return; } DOM_NamedNodeMap_setNamedItem(element->attributes, attr); } DOM_MutationEvent_initMutationEvent(&evt, "DOMAttrModified", 1, 0, attr, prevValue, attr->nodeValue, attr->nodeName, attrChange); /*MSG("attr=%p,prevValue=[%s],newValue=[%s],attrName=%s", attr, prevValue, attr->nodeValue, attr->nodeName); */ DOM_EventTarget_dispatchEvent(element, &evt); updateCommonParent(element->parentNode); free(prevValue); } void DOM_Element_removeAttribute(DOM_Element *element, const DOM_String *name) { DOM_Attr *attr; DOM_MutationEvent evt; if (element == NULL || name == NULL) { return; } attr = DOM_NamedNodeMap_removeNamedItem(element->attributes, name); /* removeAttribute doesn't throw exceptions on NOT_FOUND_ERR */ if (DOM_Exception == DOM_NOT_FOUND_ERR) DOM_Exception = 0; if (attr) { DOM_MutationEvent_initMutationEvent(&evt, "DOMAttrModified", 1, 0, attr, attr->nodeValue, NULL, attr->nodeName, DOM_MUTATION_EVENT_REMOVAL); DOM_EventTarget_dispatchEvent(element, &evt); updateCommonParent(element->parentNode); DOM_Document_destroyNode(attr->ownerDocument, attr); } } DOM_Attr * DOM_Element_getAttributeNode(const DOM_Element *element, const DOM_String *name) { if (element && name) { return DOM_NamedNodeMap_getNamedItem(element->attributes, name); } return NULL; } DOM_Attr * DOM_Element_setAttributeNode(DOM_Element *element, DOM_Attr *newAttr) { DOM_Node *attr; DOM_MutationEvent evt; if (element == NULL || newAttr == NULL) { return NULL; } if (element->ownerDocument != newAttr->ownerDocument) { DOM_Exception = DOM_WRONG_DOCUMENT_ERR; PMNO(DOM_Exception); return NULL; } attr = DOM_NamedNodeMap_setNamedItem(element->attributes, newAttr); if (attr) { DOM_MutationEvent_initMutationEvent(&evt, "DOMAttrModified", 1, 0, attr, attr->nodeValue, NULL, attr->nodeName, DOM_MUTATION_EVENT_REMOVAL); DOM_EventTarget_dispatchEvent(element, &evt); } DOM_MutationEvent_initMutationEvent(&evt, "DOMAttrModified", 1, 0, newAttr, NULL, newAttr->nodeValue, newAttr->nodeName, DOM_MUTATION_EVENT_ADDITION); DOM_EventTarget_dispatchEvent(element, &evt); updateCommonParent(element->parentNode); return attr; } DOM_Attr * DOM_Element_removeAttributeNode(DOM_Element *element, DOM_Attr *oldAttr) { DOM_MutationEvent evt; if (element == NULL || oldAttr == NULL || NodeList_remove(element->attributes, oldAttr) == NULL) { DOM_Exception = DOM_NOT_FOUND_ERR; PMNO(DOM_Exception); return NULL; } DOM_MutationEvent_initMutationEvent(&evt, "DOMAttrModified", 1, 0, oldAttr, oldAttr->nodeValue, NULL, oldAttr->nodeName, DOM_MUTATION_EVENT_REMOVAL); DOM_EventTarget_dispatchEvent(element, &evt); updateCommonParent(element->parentNode); return oldAttr; } static void getElementsPreorder(DOM_NodeList *list, DOM_Node *node, const DOM_String *tagname) { DOM_Node *n; if (list && node && node->nodeType == DOM_ELEMENT_NODE && tagname) { if ((tagname[0] == '*' && tagname[1] == '\0') || strcoll(tagname, node->nodeName) == 0) { NodeList_append(list, node); } for (n = node->firstChild; n != NULL; n = n->nextSibling) { getElementsPreorder(list, n, tagname); } } } DOM_NodeList * DOM_Element_getElementsByTagName(DOM_Element *element, const DOM_String *name) { DOM_NodeList *list; DOM_Node *n; if (element && element->nodeType == DOM_ELEMENT_NODE && name && (list = Document_createNodeList(element->ownerDocument))) { for (n = element->firstChild; n != NULL; n = n->nextSibling) { getElementsPreorder(list, n, name); } return list; } return NULL; } void DOM_Element_normalize(DOM_Element *element) { DOM_Node *node; DOM_Text *last = NULL; if (element) { for (node = element->firstChild; node != NULL; node = node->nextSibling) { if (node->nodeType == DOM_TEXT_NODE) { if (last) { DOM_CharacterData_insertData(node, 0, last->nodeValue); DOM_Node_removeChild(element, last); DOM_Document_destroyNode(last->ownerDocument, last); } last = node; } else { last = NULL; DOM_Element_normalize(node); } if (DOM_Exception) { AMSG(""); return; } } } } DOM_String * DOM_CharacterData_substringData(DOM_CharacterData *data, int offset, int count) { DOM_String *sub; int dlen; if (data == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (offset < 0 || offset > (dlen = data->u.CharacterData.length) || count < 0) { DOM_Exception = DOM_INDEX_SIZE_ERR; PMNO(DOM_Exception); return NULL; } if (count > (dlen - offset)) { count = dlen - offset; } if ((sub = mbsoff(data->nodeValue, offset)) == NULL || (sub = mbsndup(sub, -1, count)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return NULL; } return sub; } void DOM_CharacterData_appendData(DOM_CharacterData *data, const DOM_String *arg) { DOM_String *str, *prevValue; size_t dsize, asize; DOM_MutationEvent evt; if (data == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return; } if (arg) { dsize = mbssize(data->nodeValue); asize = mbssize(arg); if ((str = malloc(dsize + asize + 1)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return; } memcpy(str, data->nodeValue, dsize); memcpy(str + dsize, arg, asize); str[dsize + asize] = '\0'; prevValue = data->nodeValue; data->nodeValue = data->u.CharacterData.data = str; data->u.CharacterData.length += mbslen(arg); DOM_MutationEvent_initMutationEvent(&evt, "DOMCharacterDataModified", 1, 0, NULL, prevValue, data->nodeValue, NULL, 0); DOM_EventTarget_dispatchEvent(data, &evt); updateCommonParent(data->parentNode); free(prevValue); } } void DOM_CharacterData_insertData(DOM_CharacterData *data, int offset, const DOM_String *arg) { DOM_String *str, *prevValue; size_t dsize, asize, o; DOM_MutationEvent evt; if (data == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return; } if (offset < 0 || offset > data->u.CharacterData.length) { DOM_Exception = DOM_INDEX_SIZE_ERR; PMNO(DOM_Exception); return; } if (arg) { dsize = mbssize(data->nodeValue); asize = mbssize(arg); if ((str = malloc(dsize + asize + 1)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return; } o = mbsoff(data->nodeValue, offset) - data->nodeValue; memcpy(str, data->nodeValue, o); memcpy(str + o, arg, asize); memcpy(str + o + asize, data->nodeValue + o, dsize - o); str[dsize + asize] = '\0'; prevValue = data->nodeValue; data->nodeValue = data->u.CharacterData.data = str; data->u.CharacterData.length += mbslen(arg); DOM_MutationEvent_initMutationEvent(&evt, "DOMCharacterDataModified", 1, 0, NULL, prevValue, data->nodeValue, NULL, 0); DOM_EventTarget_dispatchEvent(data, &evt); updateCommonParent(data->parentNode); free(prevValue); } } void DOM_CharacterData_deleteData(DOM_CharacterData *data, int offset, int count) { DOM_String *p1, *p2, *str, *prevValue; int p1size, p2size, dlen; DOM_MutationEvent evt; if (data == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return; } if (offset < 0 || offset > (dlen = data->u.CharacterData.length)) { DOM_Exception = DOM_INDEX_SIZE_ERR; PMNO(DOM_Exception); return; } if (count < 0 || (offset + count) > dlen) { count = dlen - offset; } p1 = mbsoff(data->nodeValue, offset); p1size = p1 - data->nodeValue; p2 = mbsoff(p1, count); p2size = strlen(p2); if ((str = malloc(p1size + p2size + 1)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return; } memcpy(str, data->nodeValue, p1size); memcpy(str + p1size, p2, p2size); str[p1size + p2size] = '\0'; prevValue = data->nodeValue; data->nodeValue = data->u.CharacterData.data = str; data->u.CharacterData.length = dlen - count; DOM_MutationEvent_initMutationEvent(&evt, "DOMCharacterDataModified", 1, 0, NULL, prevValue, data->nodeValue, NULL, 0); DOM_EventTarget_dispatchEvent(data, &evt); updateCommonParent(data->parentNode); free(prevValue); } void DOM_CharacterData_replaceData(DOM_CharacterData *data, int offset, int count, const DOM_String *arg) { DOM_CharacterData_deleteData(data, offset, count); DOM_CharacterData_insertData(data, offset, arg); } int DOM_CharacterData_getLength(DOM_CharacterData *data) { return data ? data->u.CharacterData.length : 0; } DOM_Text * DOM_Text_splitText(DOM_Text *text, int offset) { DOM_Text *node; if (text == NULL || text->parentNode == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (offset < 0 || offset > text->u.CharacterData.length) { DOM_Exception = DOM_INDEX_SIZE_ERR; PMNO(DOM_Exception); return NULL; } node = DOM_Document_createTextNode(text->ownerDocument, mbsoff(text->nodeValue, offset)); if (node == NULL) { AMNO(DOM_CREATE_FAILED); return NULL; } DOM_CharacterData_deleteData(text, offset, -1); DOM_Node_insertBefore(text->parentNode, node, text->nextSibling); return node; } DOM_Element * DOM_Document_createElement(DOM_Document *doc, const DOM_String *tagName) { DOM_Element *element; element = Document_createNode(doc, DOM_ELEMENT_NODE); if (element) { if ((element->nodeName = element->u.Element.tagName = strdup(tagName)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(doc, element); return NULL; } if ((element->attributes = Document_createNamedNodeMap(doc)) == NULL) { AMNO(DOM_CREATE_FAILED); DOM_Document_destroyNode(doc, element); return NULL; } element->attributes->_ownerElement = element; /* for Attr.ownerElement */ } return element; } DOM_DocumentFragment * DOM_Document_createDocumentFragment(DOM_Document *doc) { DOM_DocumentFragment *frag; frag = Document_createNode(doc, DOM_DOCUMENT_FRAGMENT_NODE); if (frag) { frag->nodeName = "#document-fragment"; } return frag; } DOM_Text * DOM_Document_createTextNode(DOM_Document *doc, const DOM_String *data) { DOM_Text *text; text = Document_createNode(doc, DOM_TEXT_NODE); if (text) { text->nodeName = "#text"; text->nodeValue = text->u.CharacterData.data = strdup(data); if (text->nodeValue == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(doc, text); return NULL; } text->u.CharacterData.length = mbslen(data); } return text; } DOM_Comment * DOM_Document_createComment(DOM_Document *doc, const DOM_String *data) { DOM_Comment *comment; comment = Document_createNode(doc, DOM_COMMENT_NODE); if (comment) { comment->nodeName = "#comment"; comment->nodeValue = comment->u.CharacterData.data = strdup(data); if (comment->nodeValue == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(doc, comment); return NULL; } comment->u.CharacterData.length = mbslen(data); } return comment; } DOM_CDATASection * DOM_Document_createCDATASection(DOM_Document *doc, const DOM_String *data) { DOM_CDATASection *cdata; cdata = Document_createNode(doc, DOM_CDATA_SECTION_NODE); if (cdata) { cdata->nodeName = "#cdata-section"; cdata->nodeValue = cdata->u.CharacterData.data = strdup(data); if (cdata->u.CharacterData.data == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(doc, cdata); return NULL; } cdata->u.CharacterData.length = mbslen(data); } return cdata; } DOM_ProcessingInstruction * DOM_Document_createProcessingInstruction(DOM_Document *doc, const DOM_String *target, const DOM_String *data) { DOM_ProcessingInstruction *pi; pi = Document_createNode(doc, DOM_PROCESSING_INSTRUCTION_NODE); if (pi) { pi->nodeName = pi->u.ProcessingInstruction.target = strdup(target); pi->nodeValue = pi->u.ProcessingInstruction.data = strdup(data); if (pi->nodeName == NULL || pi->nodeValue == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(doc, pi); return NULL; } } return pi; } DOM_Attr * DOM_Document_createAttribute(DOM_Document *doc, const DOM_String *name) { DOM_Attr *attr; attr = Document_createNode(doc, DOM_ATTRIBUTE_NODE); if (attr) { attr->nodeName = attr->u.Attr.name = strdup(name); attr->nodeValue = attr->u.Attr.value = strdup(""); attr->u.Attr.specified = 1; if (attr->nodeName == NULL || attr->nodeValue == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(doc, attr); return NULL; } } return attr; } DOM_EntityReference * DOM_Document_createEntityReference(DOM_Document *doc, const DOM_String *name) { DOM_EntityReference *eref; eref = Document_createNode(doc, DOM_ENTITY_REFERENCE_NODE); if (eref && (eref->nodeName = strdup(name)) == NULL) { DOM_Document_destroyNode(doc, eref); return NULL; } return eref; } DOM_NodeList * DOM_Document_getElementsByTagName(DOM_Document *doc, const DOM_String *tagname) { DOM_NodeList *list; if (doc && doc->nodeType == DOM_DOCUMENT_NODE && tagname && (list = Document_createNodeList(doc))) { getElementsPreorder(list, doc->u.Document.documentElement, tagname); return list; } return NULL; } DOM_DocumentType * DOM_Document_getDoctype(DOM_Document *doc) { return doc ? doc->u.Document.doctype : NULL; } DOM_Element * DOM_Document_getDocumentElement(DOM_Document *doc) { return doc ? doc->u.Document.documentElement : NULL; } /* Temporary functions */ void printNode(DOM_Node *node, int indent) { DOM_Node *n; int i; if (node == NULL) { printf("node was null\n"); return; } for (i = 0; i < indent; i++) { printf(" "); } printf("%s: %s=%s\n", node_names[node->nodeType], node->nodeName, node->nodeValue); if (node->nodeType == DOM_ELEMENT_NODE && node->attributes->length) { printf(" "); n = DOM_NamedNodeMap_item(node->attributes, 0); printf("%s=%s", n->nodeName, n->nodeValue); for (i = 1; i < node->attributes->length; i++) { n = DOM_NamedNodeMap_item(node->attributes, i); printf(",%s=%s", n->nodeName, n->nodeValue); } printf("\n"); for (i = 0; i < indent; i++) { printf(" "); } } for (n = node->firstChild; n != NULL; n = n->nextSibling) { printNode(n, indent + 1); } } void DOM_Node_printNode2(DOM_Node *node) { printf("\n"); printNode(node, 0); } void DOM_Node_printNode(DOM_Node *node) { if (node == NULL) { printf("node was null\n"); return; } printf("\nnodeName=%s,nodeValue=%s,", node->nodeName, node->nodeValue); printf("\n\ttype=%u", node->nodeType); printf(",parentNode->nodeName=%s,firstChild->nodeName=%s", (node->parentNode == NULL ? "(null)" : node->parentNode->nodeName), (node->firstChild == NULL ? "(null)" : node->firstChild->nodeName)); printf(",lastChild->nodeName=%s,\n\tchildNodes->length=%u", (node->lastChild == NULL ? "(null)" : node->lastChild->nodeName), (node->childNodes == NULL ? 0 : node->childNodes->length)); printf(",previousSibling->nodeName=%s,nextSibling->nodeName=%s,attributes->length=%u\n", (node->previousSibling == NULL ? "(null)" : node->previousSibling->nodeName), (node->nextSibling == NULL ? "(null)": node->nextSibling->nodeName), (node->attributes == NULL ? 0 : node->attributes->length)); fflush(stdout); } /* NamedNodeMap */ void DOM_Document_destroyNamedNodeMap(DOM_Document *doc, DOM_NamedNodeMap *map, int free_nodes) { DOM_Document_destroyNodeList(doc, map, free_nodes); } DOM_NamedNodeMap * Document_createNamedNodeMap(DOM_Document *doc) { return Document_createNodeList(doc); } DOM_Node * DOM_NamedNodeMap_getNamedItem(const DOM_NamedNodeMap *map, const DOM_String *name) { NodeEntry *e; unsigned short nodeType; if (map && name) { if (map->filter) { nodeType = map->filter; map = map->list; for (e = map->first; e != NULL; e = e->next) { if (e->node->nodeType == nodeType && strcoll(name, e->node->nodeName) == 0) { return e->node; } } } else { for (e = map->first; e != NULL; e = e->next) { if (strcoll(name, e->node->nodeName) == 0) { return e->node; } } } } return NULL; } DOM_Node * DOM_NamedNodeMap_setNamedItem(DOM_NamedNodeMap *map, DOM_Node *arg) { NodeEntry *e; if (map && arg) { if (map->filter) { DOM_Exception = DOM_NO_MODIFICATION_ALLOWED_ERR; PMNO(DOM_Exception); return NULL; } if (map->_ownerDocument != arg->ownerDocument) { DOM_Exception = DOM_WRONG_DOCUMENT_ERR; PMNO(DOM_Exception); return NULL; } if (arg->nodeType == DOM_ATTRIBUTE_NODE && arg->u.Attr.ownerElement && arg->u.Attr.ownerElement != map->_ownerElement) { DOM_Exception = DOM_INUSE_ATTRIBUTE_ERR; PMNO(DOM_Exception); return NULL; } for (e = map->first; e != NULL && strcoll(arg->nodeName, e->node->nodeName); e = e->next) { ; } if (e) { DOM_Node *tmp = e->node; e->node = arg; if (arg->nodeType == DOM_ATTRIBUTE_NODE) { arg->u.Attr.ownerElement = map->_ownerElement; tmp->u.Attr.ownerElement = NULL; } return tmp; } NodeList_append(map, arg); } return NULL; } DOM_Node * DOM_NamedNodeMap_removeNamedItem(DOM_NamedNodeMap *map, const DOM_String *name) { NodeEntry *e; DOM_Node *r = NULL; if (map && name) { if (map->filter) { DOM_Exception = DOM_NO_MODIFICATION_ALLOWED_ERR; PMNO(DOM_Exception); return NULL; } for (e = map->first; e != NULL; e = e->next) { if (strcoll(name, e->node->nodeName) == 0 && NodeList_remove(map, e->node)) { r = e->node; free(e); if (r->nodeType == DOM_ATTRIBUTE_NODE) { r->u.Attr.ownerElement = NULL; } return r; } } } DOM_Exception = DOM_NOT_FOUND_ERR; PMNO(DOM_Exception); return NULL; } DOM_Node * DOM_NamedNodeMap_item(const DOM_NamedNodeMap *map, int index) { return DOM_NodeList_item(map, index); } /* Forward references */ void DOM_Document_destroyNamedNodeMap(DOM_Document *doc, DOM_NamedNodeMap *nnm, int free_nodes); void updateCommonParent(DOM_Node *node); /* Node */ void DOM_Document_destroyNode(DOM_Document *doc, DOM_Node *node) { if (node == NULL) { return; } if (node->childNodes) { DOM_Document_destroyNodeList(doc, node->childNodes, 1); } if (node->listeners) { unsigned int i; for (i = 0; i < node->listeners_len; i++) { if (node->listeners[i]) { free(node->listeners[i]->type); free(node->listeners[i]); } } free(node->listeners); } switch(node->nodeType) { case DOM_ELEMENT_NODE: DOM_Document_destroyNamedNodeMap(doc, node->attributes, 1); free(node->nodeName); break; case DOM_TEXT_NODE: case DOM_COMMENT_NODE: case DOM_CDATA_SECTION_NODE: free(node->nodeValue); break; case DOM_ATTRIBUTE_NODE: free(node->nodeName); free(node->nodeValue); break; case DOM_ENTITY_REFERENCE_NODE: case DOM_ENTITY_NODE: free(node->nodeName); free(node->nodeValue); free(node->u.Entity.publicId); free(node->u.Entity.systemId); free(node->u.Entity.notationName); break; case DOM_PROCESSING_INSTRUCTION_NODE: free(node->nodeName); free(node->nodeValue); break; case DOM_DOCUMENT_NODE: free(node->u.Document.version); free(node->u.Document.encoding); break; case DOM_DOCUMENT_TYPE_NODE: DOM_Document_destroyNamedNodeMap(doc, node->u.DocumentType.entities, 0); DOM_Document_destroyNamedNodeMap(doc, node->u.DocumentType.notations, 0); free(node->u.DocumentType.publicId); free(node->u.DocumentType.systemId); free(node->nodeName); break; case DOM_NOTATION_NODE: free(node->nodeName); free(node->u.Notation.publicId); free(node->u.Notation.systemId); break; } free(node); } DOM_Node * Document_createNode(DOM_Document *doc, unsigned short nodeType) { DOM_Node *node; msgno_add_codes(dom_codes); if (doc == NULL && nodeType != DOM_DOCUMENT_NODE && nodeType != DOM_DOCUMENT_TYPE_NODE) { DOM_Exception = NULL_POINTER_ERR; PMNF(DOM_Exception, ": doc=NULL,nodeType=%u", nodeType); return NULL; } node = calloc(sizeof *node, 1); if (node == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return NULL; } node->nodeType = nodeType; node->ownerDocument = doc; switch (nodeType) { case DOM_DOCUMENT_NODE: case DOM_DOCUMENT_TYPE_NODE: /* DocumentType doesn't really have children but we need to store DTD * entries other than entities and notations */ case DOM_ELEMENT_NODE: case DOM_ATTRIBUTE_NODE: case DOM_ENTITY_REFERENCE_NODE: case DOM_ENTITY_NODE: case DOM_DOCUMENT_FRAGMENT_NODE: node->childNodes = Document_createNodeList(doc); if (node->childNodes == NULL) { AMNO(DOM_CREATE_FAILED); DOM_Document_destroyNode(doc, node); return NULL; } } return node; } static int _isAncestor(DOM_Node *node, DOM_Node *parent) { DOM_Node *p; for (p = parent; p; p = p->parentNode) { if (p == node) { return 1; } } return 0; } static void dispatchEventPreorder(DOM_Node *node, DOM_MutationEvent *evt) { DOM_Node *n; DOM_EventTarget_dispatchEvent(node, evt); for (n = node->firstChild; n != NULL; n = n->nextSibling) { dispatchEventPreorder(n, evt); } } static void dispatchEventPostorder(DOM_Node *node, DOM_MutationEvent *evt) { DOM_Node *n; for (n = node->firstChild; n != NULL; n = n->nextSibling) { dispatchEventPostorder(n, evt); } DOM_EventTarget_dispatchEvent(node, evt); } static DOM_Node * _removeChild(DOM_Node *node, DOM_Node *oldChild) { NodeEntry *e; DOM_MutationEvent evt; if (NodeList_exists(node->childNodes, oldChild) == 0) { return NULL; } DOM_MutationEvent_initMutationEvent(&evt, "DOMNodeRemoved", 1, 0, node, NULL, NULL, NULL, 0); DOM_EventTarget_dispatchEvent(oldChild, &evt); DOM_MutationEvent_initMutationEvent(&evt, "DOMNodeRemovedFromDocument", 0, 0, NULL, NULL, NULL, NULL, 0); dispatchEventPostorder(oldChild, &evt); e = NodeList_remove(node->childNodes, oldChild); free(e); if (node->firstChild == node->lastChild) { node->firstChild = node->lastChild = NULL; } else if (oldChild == node->firstChild) { node->firstChild = oldChild->nextSibling; node->firstChild->previousSibling = NULL; } else if (oldChild == node->lastChild) { node->lastChild = oldChild->previousSibling; node->lastChild->nextSibling = NULL; } else { oldChild->previousSibling->nextSibling = oldChild->nextSibling; oldChild->nextSibling->previousSibling = oldChild->previousSibling; } oldChild->previousSibling = NULL; oldChild->nextSibling = NULL; oldChild->parentNode = NULL; if (MODIFYING_DOC_ELEM(node, oldChild)) { node->u.Document.documentElement = NULL; } else if (MODIFYING_DOCTYPE_ELEM(node, oldChild)) { node->u.Document.doctype = NULL; oldChild->ownerDocument = NULL; } else { updateCommonParent(node); } return oldChild; } DOM_Node * DOM_Node_insertBefore(DOM_Node *node, DOM_Node *newChild, DOM_Node *refChild) { NodeEntry *e; DOM_MutationEvent evt; if (node == NULL || newChild == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (newChild->ownerDocument != node->ownerDocument && newChild->ownerDocument != node) { DOM_Exception = DOM_WRONG_DOCUMENT_ERR; PMNO(DOM_Exception); return NULL; } if (refChild != NULL && refChild->parentNode != node) { DOM_Exception = DOM_HIERARCHY_REQUEST_ERR; PMNO(DOM_Exception); return NULL; } if (newChild->nodeType == DOM_DOCUMENT_FRAGMENT_NODE) { DOM_Node *n, *nxt; for (n = newChild->firstChild; n != NULL; n = n->nextSibling) { if (INVALID_HIER_REQ(node, n) || _isAncestor(n, node)) { DOM_Exception = DOM_HIERARCHY_REQUEST_ERR; PMNO(DOM_Exception); return NULL; } } for (n = newChild->firstChild; n != NULL; n = nxt) { nxt = n->nextSibling; if (_removeChild(newChild, n) == NULL) { return NULL; } if (DOM_Node_insertBefore(node, n, refChild) == NULL) { DOM_Document_destroyNode(n->ownerDocument, n); return NULL; } } return newChild; } if (INVALID_HIER_REQ(node, newChild) || _isAncestor(newChild, node)) { DOM_Exception = DOM_HIERARCHY_REQUEST_ERR; PMNO(DOM_Exception); return NULL; } _removeChild(node, newChild); if ((e = NodeList_insert(node->childNodes, newChild, refChild)) == NULL) { return NULL; } if (node->firstChild == NULL) { node->firstChild = node->lastChild = newChild; newChild->previousSibling = NULL; newChild->nextSibling = NULL; } else if (refChild == NULL) { newChild->previousSibling = node->lastChild; node->lastChild->nextSibling = newChild; node->lastChild = newChild; newChild->nextSibling = NULL; } else { newChild->previousSibling = refChild->previousSibling; newChild->nextSibling = refChild; if (refChild == node->firstChild) { node->firstChild = newChild; newChild->previousSibling = NULL; } else { refChild->previousSibling->nextSibling = newChild; } refChild->previousSibling = newChild; } newChild->parentNode = node; if (MODIFYING_DOC_ELEM(node, newChild)) { node->u.Document.documentElement = newChild; } else if (MODIFYING_DOCTYPE_ELEM(node, newChild)) { node->u.Document.doctype = newChild; newChild->ownerDocument = node; } DOM_MutationEvent_initMutationEvent(&evt, "DOMNodeInserted", 1, 0, node, NULL, NULL, NULL, 0); DOM_EventTarget_dispatchEvent(newChild, &evt); DOM_MutationEvent_initMutationEvent(&evt, "DOMNodeInsertedIntoDocument", 0, 0, NULL, NULL, NULL, NULL, 0); dispatchEventPreorder(newChild, &evt); updateCommonParent(node); return newChild; } DOM_Node * DOM_Node_replaceChild(DOM_Node *node, DOM_Node *newChild, DOM_Node *oldChild) { DOM_MutationEvent evt; if (node == NULL || newChild == NULL || oldChild == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (newChild->ownerDocument != node->ownerDocument && newChild->ownerDocument != node) { DOM_Exception = DOM_WRONG_DOCUMENT_ERR; PMNO(DOM_Exception); return NULL; } if (!NodeList_exists(node->childNodes, oldChild)) { DOM_Exception = DOM_NOT_FOUND_ERR; PMNO(DOM_Exception); return NULL; } if (newChild->nodeType == DOM_DOCUMENT_FRAGMENT_NODE) { DOM_Node *n, *nxt; for (n = newChild->firstChild; n != NULL; n = n->nextSibling) { if (INVALID_HIER_REQ(node, n) || _isAncestor(n, node)) { DOM_Exception = DOM_HIERARCHY_REQUEST_ERR; PMNO(DOM_Exception); return NULL; } } for (n = newChild->firstChild; n != NULL; n = nxt) { nxt = n->nextSibling; if (_removeChild(newChild, n) == NULL) { return NULL; } if (DOM_Node_insertBefore(node, n, oldChild) == NULL) { DOM_Document_destroyNode(n->ownerDocument, n); return NULL; } } if (_removeChild(node, oldChild) == NULL) { return NULL; } return oldChild; } if (INVALID_HIER_REQ(node, newChild) || _isAncestor(newChild, node)) { DOM_Exception = DOM_HIERARCHY_REQUEST_ERR; PMNO(DOM_Exception); return NULL; } _removeChild(node, newChild); if (NodeList_exists(node->childNodes, oldChild) == 0) { return NULL; } DOM_MutationEvent_initMutationEvent(&evt, "DOMNodeRemoved", 1, 0, node, NULL, NULL, NULL, 0); DOM_EventTarget_dispatchEvent(oldChild, &evt); DOM_MutationEvent_initMutationEvent(&evt, "DOMNodeRemovedFromDocument", 0, 0, NULL, NULL, NULL, NULL, 0); dispatchEventPostorder(oldChild, &evt); NodeList_replace(node->childNodes, newChild, oldChild); node->firstChild = node->childNodes->first->node; node->lastChild = node->childNodes->last->node; if ((newChild->previousSibling = oldChild->previousSibling)) { newChild->previousSibling->nextSibling = newChild; } if ((newChild->nextSibling = oldChild->nextSibling)) { newChild->nextSibling->previousSibling = newChild; } newChild->parentNode = node; oldChild->parentNode = NULL; oldChild->previousSibling = NULL; oldChild->nextSibling = NULL; if (MODIFYING_DOC_ELEM(node, newChild)) { node->u.Document.documentElement = newChild; } else if (MODIFYING_DOCTYPE_ELEM(node, newChild)) { node->u.Document.doctype = newChild; newChild->ownerDocument = node; } DOM_MutationEvent_initMutationEvent(&evt, "DOMNodeInserted", 1, 0, node, NULL, NULL, NULL, 0); DOM_EventTarget_dispatchEvent(newChild, &evt); DOM_MutationEvent_initMutationEvent(&evt, "DOMNodeInsertedIntoDocument", 0, 0, NULL, NULL, NULL, NULL, 0); dispatchEventPreorder(newChild, &evt); updateCommonParent(node); return oldChild; } DOM_Node * DOM_Node_removeChild(DOM_Node *node, DOM_Node *oldChild) { if (node == NULL || oldChild == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (oldChild->ownerDocument != node->ownerDocument && oldChild->ownerDocument != node) { DOM_Exception = DOM_WRONG_DOCUMENT_ERR; PMNO(DOM_Exception); return NULL; } if ((oldChild = _removeChild(node, oldChild)) == NULL) { DOM_Exception = DOM_NOT_FOUND_ERR; PMNO(DOM_Exception); } return oldChild; } DOM_Node * DOM_Node_appendChild(DOM_Node *node, DOM_Node *newChild) { DOM_MutationEvent evt; if (node == NULL || newChild == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (newChild->ownerDocument != node->ownerDocument && node->nodeType != DOM_DOCUMENT_NODE && newChild->nodeType != DOM_DOCUMENT_TYPE_NODE) { DOM_Exception = DOM_WRONG_DOCUMENT_ERR; PMNO(DOM_Exception); return NULL; } if (newChild->nodeType == DOM_DOCUMENT_FRAGMENT_NODE) { DOM_Node *n, *nxt; for (n = newChild->firstChild; n != NULL; n = n->nextSibling) { if (INVALID_HIER_REQ(node, n) || _isAncestor(n, node)) { DOM_Exception = DOM_HIERARCHY_REQUEST_ERR; PMNO(DOM_Exception); return NULL; } } for (n = newChild->firstChild; n != NULL; n = nxt) { nxt = n->nextSibling; if (_removeChild(newChild, n) == NULL) { return NULL; } if (DOM_Node_appendChild(node, n) == NULL) { DOM_Document_destroyNode(n->ownerDocument, n); return NULL; } } return newChild; } if (INVALID_HIER_REQ(node, newChild) || _isAncestor(newChild, node)) { DOM_Exception = DOM_HIERARCHY_REQUEST_ERR; PMNO(DOM_Exception); return NULL; } _removeChild(node, newChild); if (NodeList_append(node->childNodes, newChild) == NULL) { return NULL; } if (node->firstChild == NULL) { node->firstChild = node->lastChild = newChild; newChild->previousSibling = NULL; newChild->nextSibling = NULL; } else { node->lastChild->nextSibling = newChild; newChild->previousSibling = node->lastChild; node->lastChild = newChild; } newChild->nextSibling = NULL; newChild->parentNode = node; if (MODIFYING_DOC_ELEM(node, newChild)) { node->u.Document.documentElement = newChild; } else if (MODIFYING_DOCTYPE_ELEM(node, newChild)) { node->u.Document.doctype = newChild; newChild->ownerDocument = node; } DOM_MutationEvent_initMutationEvent(&evt, "DOMNodeInserted", 1, 0, node, NULL, NULL, NULL, 0); DOM_EventTarget_dispatchEvent(newChild, &evt); DOM_MutationEvent_initMutationEvent(&evt, "DOMNodeInsertedIntoDocument", 0, 0, NULL, NULL, NULL, NULL, 0); dispatchEventPreorder(newChild, &evt); updateCommonParent(node); return newChild; } int DOM_Node_hasChildNodes(const DOM_Node *node) { return node != NULL && node->firstChild; } extern const char *node_names[]; static DOM_Node * Node_cloneNode(DOM_Document *ownerDocument, DOM_Node *node, int deep) { DOM_Node *clone = NULL; DOM_Node *ntmp, *ctmp; NodeEntry *e; DOM_String *tmp; switch(node->nodeType) { case DOM_ELEMENT_NODE: clone = DOM_Document_createElement(ownerDocument, node->nodeName); if (clone) { for (e = node->attributes->first; e != NULL; e = e->next) { if ((ctmp = Node_cloneNode(ownerDocument, e->node, deep)) == NULL || NodeList_append(clone->attributes, ctmp) == NULL) { DOM_Document_destroyNode(clone->ownerDocument, ctmp); DOM_Document_destroyNode(clone->ownerDocument, clone); return NULL; } } } break; case DOM_ATTRIBUTE_NODE: if ((clone = DOM_Document_createAttribute(ownerDocument, node->nodeName))) { clone->u.Attr.specified = node->u.Attr.specified; free(clone->nodeValue); clone->u.Attr.value = clone->nodeValue = mbsdup(node->nodeValue); if (clone->u.Attr.value == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(clone->ownerDocument, clone); return NULL; } } break; case DOM_COMMENT_NODE: clone = DOM_Document_createComment(ownerDocument, node->nodeValue); break; case DOM_TEXT_NODE: clone = DOM_Document_createTextNode(ownerDocument, node->nodeValue); break; case DOM_CDATA_SECTION_NODE: clone = DOM_Document_createCDATASection(ownerDocument, node->nodeValue); break; case DOM_DOCUMENT_FRAGMENT_NODE: clone = DOM_Document_createDocumentFragment(ownerDocument); break; case DOM_DOCUMENT_NODE: clone = ownerDocument; break; case DOM_PROCESSING_INSTRUCTION_NODE: clone = DOM_Document_createProcessingInstruction(ownerDocument, node->u.ProcessingInstruction.target, node->u.ProcessingInstruction.data); break; case DOM_ENTITY_NODE: if ((clone = Document_createNode(ownerDocument, DOM_ENTITY_NODE))) { tmp = node->nodeValue; if ((clone->nodeName = mbsdup(node->nodeName)) == NULL || /* This will eventually go away as Entities should not have nodeValues */ (clone->nodeValue = mbsdup(node->nodeValue)) == NULL || (node->u.Entity.publicId && (clone->u.Entity.publicId = mbsdup(node->u.Entity.publicId)) == NULL) || (node->u.Entity.systemId && (clone->u.Entity.systemId = mbsdup(node->u.Entity.systemId)) == NULL) || (node->u.Entity.notationName && (clone->u.Entity.notationName = mbsdup(node->u.Entity.notationName)) == NULL)) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(clone->ownerDocument, clone); return NULL; } free(tmp); } break; case DOM_NOTATION_NODE: if ((clone = Document_createNode(ownerDocument, DOM_NOTATION_NODE))) { if ((clone->nodeName = mbsdup(node->nodeName)) == NULL || (node->u.Notation.publicId && (clone->u.Notation.publicId = mbsdup(node->u.Notation.publicId)) == NULL) || (node->u.Notation.systemId && (clone->u.Notation.systemId = mbsdup(node->u.Notation.systemId)) == NULL)) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(clone->ownerDocument, clone); return NULL; } } break; case DOM_DOCUMENT_TYPE_NODE: if ((clone = DOM_Implementation_createDocumentType(node->nodeName, NULL, NULL))) { if ((node->u.DocumentType.publicId && (clone->u.DocumentType.publicId = mbsdup(node->u.DocumentType.publicId)) == NULL) || (node->u.DocumentType.systemId && (clone->u.DocumentType.systemId = mbsdup(node->u.DocumentType.systemId)) == NULL)) { DOM_Exception = errno; PMNO(DOM_Exception); DOM_Document_destroyNode(clone->ownerDocument, clone); return NULL; } } ownerDocument->u.Document.doctype = clone; clone->ownerDocument = ownerDocument; break; case DOM_ENTITY_REFERENCE_NODE: DOM_Exception = DOM_NOT_SUPPORTED_ERR; PMNO(DOM_Exception); return NULL; } if (deep && clone && node->childNodes) { for (ntmp = node->firstChild; ntmp != NULL; ntmp = ntmp->nextSibling) { ctmp = Node_cloneNode(ownerDocument, ntmp, 1); if (ctmp == NULL || DOM_Node_appendChild(clone, ctmp) == NULL) { DOM_Document_destroyNode(clone->ownerDocument, ctmp); DOM_Document_destroyNode(clone->ownerDocument, clone); return NULL; } } } return clone; } DOM_Node * DOM_Node_cloneNode(DOM_Node *node, int deep) { if (node == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (node->nodeType == DOM_DOCUMENT_NODE) { DOM_Document *doc; if ((doc = DOM_Implementation_createDocument(NULL, NULL, NULL)) == NULL) { AMSG(""); return NULL; } return Node_cloneNode(doc, node, deep); } return Node_cloneNode(node->ownerDocument, node, deep); } DOM_String * DOM_Node_getNodeValue(DOM_Node *node) { if (node == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } return node->nodeValue; } void DOM_Node_setNodeValue(DOM_Node *node, DOM_String *value) { DOM_String *str = NULL; if (node == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return; } switch(node->nodeType) { case DOM_ATTRIBUTE_NODE: case DOM_COMMENT_NODE: case DOM_TEXT_NODE: case DOM_CDATA_SECTION_NODE: case DOM_PROCESSING_INSTRUCTION_NODE: if ((str = mbsdup(value)) == NULL) { DOM_Exception = errno; AMSG(""); return; } break; } switch(node->nodeType) { case DOM_ATTRIBUTE_NODE: free(node->nodeValue); node->nodeValue = node->u.Attr.value = str; break; case DOM_COMMENT_NODE: case DOM_TEXT_NODE: case DOM_CDATA_SECTION_NODE: free(node->nodeValue); node->nodeValue = node->u.CharacterData.data = str; break; case DOM_PROCESSING_INSTRUCTION_NODE: free(node->nodeValue); node->nodeValue = node->u.ProcessingInstruction.data = str; break; default: return; /* No effect */ } } #if FAST_NODELIST /* The number of nodes required in a list before hashing starts */ #define FAST_FILLFACTOR 16 static void _removeFromMap(DOM_NodeList* nl, DOM_Node* key) { if (nl->_map) { if (hashmap_get(nl->_map, key) != NULL) { void* d = NULL; void* k = key; hashmap_remove(nl->_map, &k, &d); } } } static int _addToMap(DOM_NodeList* nl, DOM_Node* key, NodeEntry* val) { if (!nl->_map && nl->length > FAST_FILLFACTOR) { nl->_map = hashmap_new(0, NULL, NULL, NULL); /* Hash what we currently have */ if (nl->_map) { NodeEntry *e = nl->first; while (e) { _addToMap(nl, e->node, e); e = e->next; } } } if (nl->_map) { _removeFromMap(nl, key); if (hashmap_put(nl->_map, key, val) == -1) { DOM_Exception = errno; return -1; } } return 0; } #endif static NodeEntry* _lookupNode(DOM_NodeList* nl, DOM_Node* node) { NodeEntry* s; #if FAST_NODELIST if (nl->_map) s = (NodeEntry*)hashmap_get(nl->_map, node); else #endif for (s = nl->first; s != NULL && s->node != node; s = s->next) { ; } return s; } /* NodeList */ DOM_NodeList * Document_createNodeList(DOM_Document *doc) { DOM_NodeList *r; if ((r = calloc(sizeof *r, 1)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); } r->_ownerDocument = doc; return r; } void DOM_Document_destroyNodeList(DOM_Document *doc, DOM_NodeList *nl, int free_nodes) { if (nl) { if (nl->filter == 0) { NodeEntry *e, *tmp; e = nl->first; while (e != NULL) { if (free_nodes) { DOM_Document_destroyNode(doc, e->node); } tmp = e; e = e->next; free(tmp); } } #if FAST_NODELIST if(nl->_map) hashmap_del(nl->_map, NULL, NULL, NULL); #endif free(nl); } } DOM_Node * NodeList_itemFiltered(const DOM_NodeList *list, int index, unsigned short nodeType) { if (list && index >= 0 && index < list->length) { NodeEntry *e; for (e = list->first; e != NULL; e = e->next) { if (e->node->nodeType == nodeType) { if (index == 0) { return e->node; } index--; } } } return NULL; } DOM_Node * DOM_NodeList_item(const DOM_NodeList *list, int index) { if (list) { if (list->filter) { return NodeList_itemFiltered(list->list, index, list->filter); } if (index >= 0 && index < list->length) { NodeEntry *e; for (e = list->first; e != NULL; e = e->next, index--) { if (index == 0) { return e->node; } } } } return NULL; } NodeEntry * NodeList_insert(DOM_NodeList *nl, DOM_Node *newChild, DOM_Node *refChild) { NodeEntry *e; NodeEntry *s = NULL; if (nl == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (nl->filter) { DOM_Exception = DOM_FILTERED_LIST_ERR; PMNO(DOM_Exception); return NULL; } if(refChild != NULL) { s = _lookupNode(nl, refChild); if(s == NULL || s->node != refChild) { DOM_Exception = DOM_NOT_FOUND_ERR; PMNO(DOM_Exception); return NULL; } } if ((e = calloc(sizeof *e, 1)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return NULL; } #if FAST_NODELIST if (_addToMap(nl, newChild, e) == -1) { PMNO(DOM_Exception); free(e); return NULL; } #endif e->node = newChild; if (nl->length == 0) { nl->first = nl->last = e; } else if (refChild == NULL) { e->prev = nl->last; nl->last->next = e; nl->last = e; } else { e->prev = s->prev; e->next = s; if (s == nl->first) { nl->first = e; } else { s->prev->next = e; } s->prev = e; } nl->length++; /* If an attribute is being added this is probably a NamedNodeMap * in which case we must set the ownerElement. */ if (newChild->nodeType == DOM_ATTRIBUTE_NODE) { newChild->u.Attr.ownerElement = nl->_ownerElement; } return e; } NodeEntry * NodeList_replace(DOM_NodeList *nl, DOM_Node *newChild, DOM_Node *oldChild) { NodeEntry *e; if (nl == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (nl->filter) { DOM_Exception = DOM_FILTERED_LIST_ERR; PMNO(DOM_Exception); return NULL; } e = _lookupNode(nl, oldChild); if (e == NULL) { DOM_Exception = DOM_NOT_FOUND_ERR; PMNO(DOM_Exception); return NULL; } #if FAST_NODELIST _removeFromMap(nl, oldChild); if(_addToMap(nl, newChild, e) == -1) { PMNO(DOM_Exception); return NULL; } #endif e->node = newChild; if (oldChild->nodeType == DOM_ATTRIBUTE_NODE) { oldChild->u.Attr.ownerElement = NULL; } return e; } NodeEntry * NodeList_remove(DOM_NodeList *nl, DOM_Node *oldChild) { NodeEntry *e; if (nl == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (nl->filter) { DOM_Exception = DOM_FILTERED_LIST_ERR; PMNO(DOM_Exception); return NULL; } e = _lookupNode(nl, oldChild); if (e == NULL) { return NULL; } #if FAST_NODELIST _removeFromMap(nl, oldChild); #endif if (nl->first == nl->last) { nl->first = nl->last = NULL; } else if (e == nl->first) { nl->first = e->next; nl->first->prev = NULL; } else if (e == nl->last) { nl->last = e->prev; nl->last->next = NULL; } else { e->prev->next = e->next; e->next->prev = e->prev; } nl->length--; /* Decrement a filtered node list too? */ if (oldChild->nodeType == DOM_ATTRIBUTE_NODE) { oldChild->u.Attr.ownerElement = NULL; } return e; } extern const char *node_names[]; NodeEntry * NodeList_append(DOM_NodeList *nl, DOM_Node *newChild) { NodeEntry *e; DOM_DocumentType *doctype; if (nl == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNF(DOM_Exception, ": %p", newChild); return NULL; } if (nl->filter) { DOM_Exception = DOM_FILTERED_LIST_ERR; PMNO(DOM_Exception); return NULL; } if ((e = calloc(sizeof *e, 1)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return NULL; } #if FAST_NODELIST if(_addToMap(nl, newChild, e) == -1) { PMNO(DOM_Exception); free(e); return NULL; } #endif e->node = newChild; if (nl->first == NULL) { nl->first = nl->last = e; } else { nl->last->next = e; e->prev = nl->last; nl->last = e; } nl->length++; /* If the node list is the DocumentType children and a Notation * or Entity is being added we must artificially update the length * member of those filtered lists */ if (newChild->ownerDocument && (doctype = newChild->ownerDocument->u.Document.doctype) && nl == doctype->childNodes) { if (newChild->nodeType == DOM_NOTATION_NODE) { doctype->u.DocumentType.notations->length++; } else if (newChild->nodeType == DOM_ENTITY_NODE) { doctype->u.DocumentType.entities->length++; } } /* If an attribute is being added this is probably a NamedNodeMap * in which case we must set the ownerElement. */ if (newChild->nodeType == DOM_ATTRIBUTE_NODE) { newChild->u.Attr.ownerElement = nl->_ownerElement; } return e; } int NodeList_exists(DOM_NodeList *nl, DOM_Node *child) { NodeEntry *e; if (nl == NULL || nl->filter) { return 0; } e = _lookupNode(nl, child); return e != NULL; } #if HAVE_ENCDEC > 0 #include #elif HAVE_STRNLEN < 1 static size_t strnlen(const char *s, size_t maxlen) { size_t len; for (len = 0; *s && len < maxlen; s++, len++); return len; } #else size_t strnlen(const char *s, size_t maxlen); #endif /* Forward references for node.c */ DOM_Node *Document_createNode(DOM_Document *doc, unsigned short nodeType); static int fputds(const DOM_String *s, FILE *stream) { return fputs(s, stream); } static void fputds_encoded(const DOM_String *s, FILE *stream) { size_t l; while (*s) { l = strcspn(s, "<>&\""); if (l > 0) { fwrite((void*)s, 1, sizeof(DOM_String) * l, stream); s += l; } switch (*s) { case '\0': break; case '<': fputs("<", stream); break; case '>': fputs(">", stream); break; case '&': fputs("'", stream); break; case '"': fputs(""", stream); break; default: AMSG(""); break; }; if(*s) ++s; } } int DOM_DocumentLS_fwrite(const DOM_DocumentLS *node, FILE *stream) { NodeEntry *e; DOM_Node *c; if (node == NULL || stream == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNF(DOM_Exception, ": node=%p,stream=%p", node, stream); return -1; } if (DOM_Exception) { return -1; } switch (node->nodeType) { case DOM_ELEMENT_NODE: fputc('<', stream); fputds(node->nodeName, stream); for (e = node->attributes->first; e != NULL; e = e->next) { fputc(' ', stream); fputds(e->node->nodeName, stream); fputs("=\"", stream); fputds_encoded(e->node->nodeValue, stream); fputc('"', stream); } if (DOM_Node_hasChildNodes(node)) { fputc('>', stream); for (c = node->firstChild; c != NULL; c = c->nextSibling) { if (DOM_DocumentLS_fwrite(c, stream) == -1) { /* Don't put msgno macro here or might overrun buf */ return -1; } } fputs("nodeName, stream); fputc('>', stream); } else { fputs("/>", stream); } break; case DOM_ATTRIBUTE_NODE: break; case DOM_TEXT_NODE: fputds_encoded(node->nodeValue, stream); break; case DOM_CDATA_SECTION_NODE: break; case DOM_ENTITY_REFERENCE_NODE: break; case DOM_NOTATION_NODE: fputs(" nodeName, stream); if (node->u.Entity.publicId) { fputs(" PUBLIC \"", stream); fputds(node->u.Entity.publicId, stream); fputs("\" \"", stream); fputds(node->u.Entity.systemId, stream); fputc('"', stream); } else if (node->u.Entity.systemId) { fputs(" SYSTEM \"", stream); fputds(node->u.Entity.systemId, stream); fputc('"', stream); } fputs(">", stream); break; case DOM_ENTITY_NODE: fputs(" nodeName, stream); if (node->nodeValue) { fputc('"', stream); fputds(node->nodeValue, stream); fputc('"', stream); } else { if (node->u.Entity.publicId) { fputs(" PUBLIC \"", stream); fputds(node->u.Entity.publicId, stream); fputs("\" \"", stream); fputds(node->u.Entity.systemId, stream); fputc('"', stream); } else if (node->u.Entity.systemId) { fputs(" SYSTEM \"", stream); fputds(node->u.Entity.systemId, stream); fputc('"', stream); } if (node->u.Entity.notationName) { fputs(" NDATA ", stream); fputds(node->u.Entity.notationName, stream); } } fputs(">", stream); break; case DOM_PROCESSING_INSTRUCTION_NODE: fputs("u.ProcessingInstruction.target, stream); fputc(' ', stream); fputds_encoded(node->u.ProcessingInstruction.data, stream); fputs("?>", stream); break; case DOM_COMMENT_NODE: fputs("", stream); break; case DOM_DOCUMENT_NODE: fputs("u.Document.version ? node->u.Document.version : "1.0", stream); fputc('\"', stream); #ifdef CODESET fputs(" encoding=\"", stream); fputs(nl_langinfo(CODESET), stream); fputc('\"', stream); #endif if (node->u.Document.standalone != 0) { fputs(" standalone=\"yes\"", stream); } fputs("?>"NL, stream); for (c = node->firstChild; c != NULL; c = c->nextSibling) { if (DOM_DocumentLS_fwrite(c, stream) == -1) { AMSG(""); return -1; } } fputs(NL, stream); break; case DOM_DOCUMENT_TYPE_NODE: fputs(NL"u.DocumentType.name, stream); if (node->u.DocumentType.systemId) { fputs(" SYSTEM \"", stream); fputds(node->u.DocumentType.systemId, stream); fputc('"', stream); } else if (node->u.DocumentType.publicId) { fputs(" PUBLIC \"", stream); fputds(node->u.DocumentType.publicId, stream); fputc('"', stream); } if (node->u.DocumentType.internalSubset) { fputs(" ["NL, stream); fputds(node->u.DocumentType.internalSubset, stream); fputs("]>"NL, stream); } else { fputs(">"NL, stream); } break; case DOM_DOCUMENT_FRAGMENT_NODE: break; } return DOM_Exception ? -1 : 0; } int DOM_DocumentLS_save(DOM_DocumentLS *doc, const char *uri, const DOM_Node *node) { FILE *fd; if ((doc == NULL && node == NULL) || uri == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNF(DOM_Exception, ": doc=%p,uri=%s,node=%p", doc, uri, node); return -1; } fd = fopen(uri, "w"); if (fd && DOM_DocumentLS_fwrite(doc ? doc : node, fd) == 0) { fclose(fd); return 0; } DOM_Exception = errno; PMNF(DOM_Exception, ": uri=%s", uri); return -1; } /* DOM_DocumentEvent - Introduced in DOM Level 2 */ void DOM_DocumentEvent_destroyEvent(DOM_DocumentEvent *doc, DOM_Event *evt) { if (doc && evt) { if (evt->type) { /*free(evt->type); */ } free(evt); } } DOM_Event * DOM_DocumentEvent_createEvent(DOM_DocumentEvent *doc, const DOM_String *eventType) { DOM_Event *evt; if (doc == NULL || eventType == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return NULL; } if (strcmp(eventType, "Events") == 0 || strcmp(eventType, "UIEvents") == 0 || strcmp(eventType, "TextEvents") == 0) { evt = calloc(sizeof *evt, 1); if (evt == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return NULL; } } else { DOM_Exception = DOM_NOT_SUPPORTED_ERR; PMNO(DOM_Exception); return NULL; } return evt; } /* DOM_Event - Introduced in DOM Level 2 */ void DOM_Event_stopPropagation(DOM_Event *evt) { if (evt) { evt->_sp = 1; } } void DOM_Event_preventDefault(DOM_Event *evt) { if (evt && evt->cancelable) { evt->_pd = 1; } } void DOM_Event_initEvent(DOM_Event *evt, const DOM_String *eventTypeArg, int canBubbleArg, int cancelableArg) { if (evt == NULL || eventTypeArg == NULL || *eventTypeArg == '\0') { return; } evt->type = eventTypeArg; /* no dup? */ evt->bubbles = canBubbleArg; evt->cancelable = cancelableArg; } /* DOM_UIEvent */ void DOM_UIEvent_initUIEvent(DOM_UIEvent *evt, const DOM_String *typeArg, int canBubbleArg, int cancelableArg, DOM_AbstractView *viewArg, long detailArg) { if (evt == NULL || typeArg == NULL || *typeArg == '\0') { return; } DOM_Event_initEvent(evt, typeArg, canBubbleArg, cancelableArg); evt->view = viewArg; evt->detail = detailArg; } /* DOM_EventTarget - Introduced in DOM Level 2 */ void DOM_EventTarget_addEventListener(DOM_EventTarget *target, const DOM_String *type, DOM_EventListener *listener, DOM_EventListener_handleEvent listener_fn, int useCapture) { ListenerEntry *e; unsigned int i; int opos = -1; if (target == NULL || type == NULL || listener_fn == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return; } for (i = 0; i < target->listeners_len; i++) { e = target->listeners[i]; /* skip duplicates */ if (e == NULL) { if (opos == -1) { opos = i; /* find open position for new entry */ } /* really need a hash code for this */ } else if (e->listener == listener && e->listener_fn == listener_fn && e->useCapture == useCapture && strcmp(e->type, type) == 0) { return; } } if ((e = malloc(sizeof *e)) == NULL || (e->type = mbsdup(type)) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); free(e); return; } e->listener = listener; e->listener_fn = listener_fn; e->useCapture = useCapture; if (opos == -1) { target->listeners = realloc(target->listeners, sizeof *e * (target->listeners_len + 1)); if (target->listeners == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); free(e); return; } target->listeners[target->listeners_len++] = e; } else { target->listeners[opos] = e; } /*MSG("added listener: type=%s,target=%s,useCapture=%d\n", type, target->nodeName, useCapture); */ } void DOM_EventTarget_removeEventListener(DOM_EventTarget *target, const DOM_String *type, DOM_EventListener *listener, DOM_EventListener_handleEvent listener_fn, int useCapture) { ListenerEntry *e; unsigned int i; if (target == NULL || type == NULL || listener_fn == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return; } for (i = 0; i < target->listeners_len; i++) { e = target->listeners[i]; if (e && e->listener == listener && e->listener_fn == listener_fn && e->useCapture == useCapture && strcmp(e->type, type) == 0) { target->listeners[i] = NULL; free(e->type); free(e); return; } } } static void trigger(DOM_EventTarget *target, DOM_Event *evt, int useCapture) { ListenerEntry *e; unsigned int j, lcount; if (target && target->listeners_len && evt->_sp == 0) { DOM_EventListener_handleEvent *cpy_of_listener_fns; if ((cpy_of_listener_fns = malloc(target->listeners_len * sizeof(*cpy_of_listener_fns))) == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return; } lcount = target->listeners_len; /* copy listeners */ for (j = 0; j < lcount; j++) { e = target->listeners[j]; cpy_of_listener_fns[j] = e ? e->listener_fn : NULL; } evt->currentTarget = target; for (j = 0; j < lcount; j++) { e = target->listeners[j]; /* If the entry is NULL, the listener has * since been removed and is therefore skipped. If it * is not NULL but the listeners do not match, then * it was removed but another listener was added in * its place and therefore should be skipped. However, * if a listener is removed but then added again * while the current set is still being processed * there is a chance that it will be tiggered if it * meets the criteria tested below. Regardless of * the slim chances of this occuring (have to be the * same listener added to the same array) it may * need to be addressed. */ /*MSG("e=%p,e->listener_fn=%p,cpy_of_listener_fns[%d]=%p,e->useCapture=%d,e->type=%s,evt->type=%s", e, e->listener_fn, j, cpy_of_listener_fns[j], e->useCapture, e->type, evt->type); */ if (e && e->listener_fn == cpy_of_listener_fns[j] && e->useCapture == useCapture && strcmp(e->type, evt->type) == 0) { e->listener_fn(e->listener, evt); /* invoke the listener function */ } } free(cpy_of_listener_fns); } } int DOM_EventTarget_dispatchEvent(DOM_EventTarget *target, DOM_Event *evt) { DOM_EventTarget **targets, *t; unsigned int tcount, i; if (target == NULL || evt == NULL) { DOM_Exception = NULL_POINTER_ERR; PMNO(DOM_Exception); return 1; } /*MSG("type=%s,target=%s,listeners_len=%d", evt->type, target->nodeName, target->listeners_len); */ if (evt->type == NULL || *evt->type == '\0') { DOM_Exception = DOM_UNSPECIFIED_EVENT_TYPE_ERR; PMNO(DOM_Exception); return 1; } targets = NULL; evt->target = target; /* post-initialization */ evt->timeStamp = timestamp(); evt->_sp = 0; evt->_pd = 0; tcount = 0; /* count targets/ancestors */ for (t = target->parentNode; t; t = t->parentNode) { tcount++; } if (tcount) { targets = malloc(sizeof *targets * tcount); if (targets == NULL) { DOM_Exception = errno; PMNO(DOM_Exception); return 1; } } i = tcount; /* save state of tree in targets array */ for (t = target->parentNode; t; t = t->parentNode) { targets[--i] = t; } /* Trigger capturers */ evt->eventPhase = DOM_EVENT_CAPTURING_PHASE; for (i = 0; i < tcount && evt->_sp == 0; i++) { trigger(targets[i], evt, 1); } /* Trigger regular listeners */ evt->eventPhase = DOM_EVENT_AT_TARGET; trigger(target, evt, 0); /* Trigger bubblers */ evt->eventPhase = DOM_EVENT_BUBBLING_PHASE; i = tcount; while (i-- && evt->bubbles && evt->_sp == 0) { trigger(targets[i], evt, 0); } if (targets) { free(targets); } return !evt->_pd; } /* DOM_TextEvent - Introduced in DOM Level 3 * http://www.w3.org/TR/2002/WD-DOM-Level-3-Events-20020208/events.html */ int DOM_TextEvent_checkModifier(DOM_TextEvent *evt, unsigned int modifier) { return evt->_modifiers & (1 << (modifier - 1)); } void DOM_TextEvent_initTextEvent(DOM_TextEvent *evt, const DOM_String *typeArg, int canBubbleArg, int cancelableArg, DOM_AbstractView *viewArg, long detailArg, DOM_String *outputStringArg, unsigned int keyValArg, unsigned int virtKeyValArg, int visibleOutputGeneratedArg, int numPadArg) { if (evt == NULL || typeArg == NULL || *typeArg == '\0') { return; } DOM_UIEvent_initUIEvent(evt, typeArg, canBubbleArg, cancelableArg, viewArg, detailArg); evt->outputString = outputStringArg; evt->keyVal = keyValArg; evt->virtKeyVal = virtKeyValArg; evt->visibleOutputGenerated = visibleOutputGeneratedArg; evt->numPad = numPadArg; } void DOM_TextEvent_initModifier(DOM_TextEvent *evt, unsigned int modifier, int value) { if (evt && modifier > 0 && modifier <= DOM_VK_RIGHT_META) { if (value) { evt->_modifiers |= 1 << (modifier - 1); } else { evt->_modifiers &= ~(1 << (modifier - 1)); } } } /* MutationEvent */ void DOM_MutationEvent_initMutationEvent(DOM_MutationEvent *evt, DOM_String *typeArg, int canBubbleArg, int cancelableArg, DOM_Node *relatedNodeArg, DOM_String *prevValueArg, DOM_String *newValueArg, DOM_String *attrNameArg, unsigned short attrChangeArg) { if (evt == NULL || typeArg == NULL || *typeArg == '\0') { return; } DOM_Event_initEvent(evt, typeArg, canBubbleArg, cancelableArg); evt->relatedNode = relatedNodeArg; evt->prevValue = prevValueArg; evt->newValue = newValueArg; evt->attrName = attrNameArg; evt->attrChange = attrChangeArg; } void updateCommonParent(DOM_Node *node) { DOM_Node *n, *cp; if (node == NULL || node->ownerDocument == NULL) { return; } if (node->ownerDocument->u.Document.commonParent == NULL) { node->ownerDocument->u.Document.commonParent = node; return; } cp = NULL; for (n = node; n; n = n->parentNode) { if (n == node->ownerDocument->u.Document.commonParent) { return; } else if (cp == NULL && n->subtreeModified == 1) { cp = n; } else { n->subtreeModified = 1; } } node->ownerDocument->u.Document.commonParent = cp; } static void _clearSubtreeModified(DOM_Document *doc) { DOM_Node *n; for (n = doc->firstChild; n != NULL; n = n->nextSibling) { if (n->subtreeModified) { n->subtreeModified = 0; _clearSubtreeModified(n); } } doc->u.Document.commonParent = NULL; } void DOM_MutationEvent_processSubtreeModified(DOM_Document *doc) { DOM_Node *target; DOM_MutationEvent evt; if (doc->u.Document.commonParent == NULL) { return; } target = doc->u.Document.commonParent; _clearSubtreeModified(doc); DOM_MutationEvent_initMutationEvent(&evt, "DOMSubtreeModified", 1, 0, NULL, NULL, NULL, NULL, 0); DOM_EventTarget_dispatchEvent(target, &evt); }