diff options
Diffstat (limited to 'tree.c')
-rw-r--r-- | tree.c | 1698 |
1 files changed, 1153 insertions, 545 deletions
@@ -119,6 +119,9 @@ static int xmlCheckDTD = 1; (n)->last = ulccur; \ }} +#define IS_STR_XML(str) ((str != NULL) && (str[0] == 'x') && \ + (str[1] == 'm') && (str[2] == 'l') && (str[3] == 0)) + /* #define DEBUG_BUFFER */ /* #define DEBUG_TREE */ @@ -1982,7 +1985,6 @@ xmlFreeProp(xmlAttrPtr cur) { xmlFree(cur); } -#ifdef LIBXML_TREE_ENABLED /** * xmlRemoveProp: * @cur: an attribute @@ -2033,7 +2035,6 @@ xmlRemoveProp(xmlAttrPtr cur) { #endif return(-1); } -#endif /* LIBXML_TREE_ENABLED */ /** * xmlNewDocPI: @@ -2803,6 +2804,55 @@ xmlNewChild(xmlNodePtr parent, xmlNsPtr ns, #endif /* LIBXML_TREE_ENABLED */ /** + * xmlAddPropSibling: + * @prev: the attribute to which @prop is added after + * @cur: the base attribute passed to calling function + * @prop: the new attribute + * + * Add a new attribute after @prev using @cur as base attribute. + * When inserting before @cur, @prev is passed as @cur->prev. + * When inserting after @cur, @prev is passed as @cur. + * If an existing attribute is found it is detroyed prior to adding @prop. + * + * Returns the attribute being inserted or NULL in case of error. + */ +static xmlNodePtr +xmlAddPropSibling(xmlNodePtr prev, xmlNodePtr cur, xmlNodePtr prop) { + xmlAttrPtr attr; + + if (cur->type != XML_ATTRIBUTE_NODE) + return(NULL); + + /* check if an attribute with the same name exists */ + if (prop->ns == NULL) + attr = xmlHasNsProp(cur->parent, prop->name, NULL); + else + attr = xmlHasNsProp(cur->parent, prop->name, prop->ns->href); + + if (prop->doc != cur->doc) { + xmlSetTreeDoc(prop, cur->doc); + } + prop->parent = cur->parent; + prop->prev = prev; + if (prev != NULL) { + prop->next = prev->next; + prev->next = prop; + if (prop->next) + prop->next->prev = prop; + } else { + prop->next = cur; + cur->prev = prop; + } + if (prop->prev == NULL && prop->parent != NULL) + prop->parent->properties = (xmlAttrPtr) prop; + if ((attr != NULL) && (attr->type != XML_ATTRIBUTE_DECL)) { + /* different instance, destroy it (attributes must be unique) */ + xmlRemoveProp((xmlAttrPtr) attr); + } + return prop; +} + +/** * xmlAddNextSibling: * @cur: the child node * @elem: the new node @@ -2861,21 +2911,7 @@ xmlAddNextSibling(xmlNodePtr cur, xmlNodePtr elem) { return(cur->next); } } else if (elem->type == XML_ATTRIBUTE_NODE) { - /* check if an attribute with the same name exists */ - xmlAttrPtr attr; - - if (cur->type != XML_ATTRIBUTE_NODE) - return(NULL); - if (elem->ns == NULL) - attr = xmlHasNsProp(cur->parent, elem->name, NULL); - else - attr = xmlHasNsProp(cur->parent, elem->name, elem->ns->href); - /* elem has already been unlinked so can never be attr */ - if ((attr != NULL) && (attr->type != XML_ATTRIBUTE_DECL)) { - /* different instance, destroy it (attributes must be unique) */ - xmlUnlinkNode((xmlNodePtr) attr); - xmlFreeProp(attr); - } + return xmlAddPropSibling(cur, cur, elem); } if (elem->doc != cur->doc) { @@ -2887,7 +2923,7 @@ xmlAddNextSibling(xmlNodePtr cur, xmlNodePtr elem) { cur->next = elem; if (elem->next != NULL) elem->next->prev = elem; - if ((elem->parent != NULL) && (elem->parent->last == cur) && (elem->type != XML_ATTRIBUTE_NODE)) + if ((elem->parent != NULL) && (elem->parent->last == cur)) elem->parent->last = elem; return(elem); } @@ -2953,21 +2989,7 @@ xmlAddPrevSibling(xmlNodePtr cur, xmlNodePtr elem) { return(cur->prev); } } else if (elem->type == XML_ATTRIBUTE_NODE) { - /* check if an attribute with the same name exists */ - xmlAttrPtr attr; - - if (cur->type != XML_ATTRIBUTE_NODE) - return(NULL); - if (elem->ns == NULL) - attr = xmlHasNsProp(cur->parent, elem->name, NULL); - else - attr = xmlHasNsProp(cur->parent, elem->name, elem->ns->href); - /* elem has already been unlinked so can never be attr */ - if ((attr != NULL) && (attr->type != XML_ATTRIBUTE_DECL)) { - /* different instance, destroy it (attributes must be unique) */ - xmlUnlinkNode((xmlNodePtr) attr); - xmlFreeProp(attr); - } + return xmlAddPropSibling(cur->prev, cur, elem); } if (elem->doc != cur->doc) { @@ -2979,16 +3001,8 @@ xmlAddPrevSibling(xmlNodePtr cur, xmlNodePtr elem) { cur->prev = elem; if (elem->prev != NULL) elem->prev->next = elem; - if (elem->parent != NULL) { - if (elem->type == XML_ATTRIBUTE_NODE) { - if (elem->parent->properties == (xmlAttrPtr) cur) { - elem->parent->properties = (xmlAttrPtr) elem; - } - } else { - if (elem->parent->children == cur) { + if ((elem->parent != NULL) && (elem->parent->children == cur)) { elem->parent->children = elem; - } - } } return(elem); } @@ -3030,7 +3044,7 @@ xmlAddSibling(xmlNodePtr cur, xmlNodePtr elem) { * Constant time is we can rely on the ->parent->last to find * the last sibling. */ - if ((cur->parent != NULL) && + if ((cur->type != XML_ATTRIBUTE_NODE) && (cur->parent != NULL) && (cur->parent->children != NULL) && (cur->parent->last != NULL) && (cur->parent->last->next == NULL)) { @@ -3046,6 +3060,8 @@ xmlAddSibling(xmlNodePtr cur, xmlNodePtr elem) { xmlNodeAddContent(cur, elem->content); xmlFreeNode(elem); return(cur); + } else if (elem->type == XML_ATTRIBUTE_NODE) { + return xmlAddPropSibling(cur, cur, elem); } if (elem->doc != cur->doc) { @@ -3454,11 +3470,6 @@ xmlUnlinkNode(xmlNodePtr cur) { xmlNodePtr parent; parent = cur->parent; if (cur->type == XML_ATTRIBUTE_NODE) { - /* If attribute is an ID from subset then remove it */ - if ((((xmlAttrPtr) cur)->atype == XML_ATTRIBUTE_ID) && - xmlIsID(parent->doc, parent, (xmlAttrPtr) cur)) { - xmlRemoveID(cur->doc, (xmlAttrPtr) cur); - } if (parent->properties == (xmlAttrPtr) cur) parent->properties = ((xmlAttrPtr) cur)->next; } else { @@ -3532,12 +3543,6 @@ xmlReplaceNode(xmlNodePtr old, xmlNodePtr cur) { if (cur->type == XML_ATTRIBUTE_NODE) { if (cur->parent->properties == (xmlAttrPtr)old) cur->parent->properties = ((xmlAttrPtr) cur); - - /* If old attribute is ID and defined in DTD then remove ID */ - if ((((xmlAttrPtr) old)->atype == XML_ATTRIBUTE_ID) && - xmlIsID(old->doc, old->parent, (xmlAttrPtr) old)) { - xmlRemoveID(old->doc, (xmlAttrPtr) old); - } } else { if (cur->parent->children == old) cur->parent->children = cur; @@ -4341,21 +4346,26 @@ xmlGetNodePath(xmlNodePtr node) */ tmp = cur->prev; while (tmp != NULL) { - if ((cur->type == XML_TEXT_NODE) || - (cur->type == XML_CDATA_SECTION_NODE)) + if ((tmp->type == XML_TEXT_NODE) || + (tmp->type == XML_CDATA_SECTION_NODE)) occur++; tmp = tmp->prev; } + /* + * Evaluate if this is the only text- or CDATA-section-node; + * if yes, then we'll get "text()", otherwise "text()[1]". + */ if (occur == 0) { tmp = cur->next; - while (tmp != NULL && occur == 0) { - if ((tmp->type == XML_TEXT_NODE) || - (tmp->type == XML_CDATA_SECTION_NODE)) - occur++; - tmp = tmp->next; - } - if (occur != 0) - occur = 1; + while (tmp != NULL) { + if ((tmp->type == XML_TEXT_NODE) || + (tmp->type == XML_CDATA_SECTION_NODE)) + { + occur = 1; + break; + } + tmp = tmp->next; + } } else occur++; } else if (cur->type == XML_PI_NODE) { @@ -5501,6 +5511,11 @@ xmlSearchNs(xmlDocPtr doc, xmlNodePtr node, const xmlChar *nameSpace) { node->nsDef = cur; return(cur); } + if (doc == NULL) { + doc = node->doc; + if (doc == NULL) + return(NULL); + } if (doc->oldNs == NULL) { /* * Allocate a new Namespace and fill the fields. @@ -5638,6 +5653,11 @@ xmlSearchNsByHref(xmlDocPtr doc, xmlNodePtr node, const xmlChar * href) node->nsDef = cur; return (cur); } + if (doc == NULL) { + doc = node->doc; + if (doc == NULL) + return(NULL); + } if (doc->oldNs == NULL) { /* * Allocate a new Namespace and fill the fields. @@ -5951,6 +5971,154 @@ xmlReconciliateNs(xmlDocPtr doc, xmlNodePtr tree) { } #endif /* LIBXML_TREE_ENABLED */ +static xmlAttrPtr +xmlGetPropNodeInternal(xmlNodePtr node, const xmlChar *name, + const xmlChar *nsName, int useDTD) +{ + xmlAttrPtr prop; + + if ((node == NULL) || (node->type != XML_ELEMENT_NODE) || (name == NULL)) + return(NULL); + + if (node->properties != NULL) { + prop = node->properties; + if (nsName == NULL) { + /* + * We want the attr to be in no namespace. + */ + do { + if ((prop->ns == NULL) && xmlStrEqual(prop->name, name)) { + return(prop); + } + prop = prop->next; + } while (prop != NULL); + } else { + /* + * We want the attr to be in the specified namespace. + */ + do { + if ((prop->ns != NULL) && xmlStrEqual(prop->name, name) && + ((prop->ns->href == nsName) || + xmlStrEqual(prop->ns->href, nsName))) + { + return(prop); + } + prop = prop->next; + } while (prop != NULL); + } + } + +#ifdef LIBXML_TREE_ENABLED + if (! useDTD) + return(NULL); + /* + * Check if there is a default/fixed attribute declaration in + * the internal or external subset. + */ + if ((node->doc != NULL) && (node->doc->intSubset != NULL)) { + xmlDocPtr doc = node->doc; + xmlAttributePtr attrDecl = NULL; + xmlChar *elemQName, *tmpstr = NULL; + + /* + * We need the QName of the element for the DTD-lookup. + */ + if ((node->ns != NULL) && (node->ns->prefix != NULL)) { + tmpstr = xmlStrdup(node->ns->prefix); + tmpstr = xmlStrcat(tmpstr, BAD_CAST ":"); + tmpstr = xmlStrcat(tmpstr, node->name); + if (tmpstr == NULL) + return(NULL); + elemQName = tmpstr; + } else + elemQName = (xmlChar *) node->name; + if (nsName == NULL) { + /* + * The common and nice case: Attr in no namespace. + */ + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, + elemQName, name, NULL); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) { + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, + elemQName, name, NULL); + } + } else { + xmlNsPtr *nsList, *cur; + + /* + * The ugly case: Search using the prefixes of in-scope + * ns-decls corresponding to @nsName. + */ + nsList = xmlGetNsList(node->doc, node); + if (nsList == NULL) { + if (tmpstr != NULL) + xmlFree(tmpstr); + return(NULL); + } + cur = nsList; + while (*cur != NULL) { + if (xmlStrEqual((*cur)->href, nsName)) { + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elemQName, + name, (*cur)->prefix); + if (attrDecl) + break; + if (doc->extSubset != NULL) { + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elemQName, + name, (*cur)->prefix); + if (attrDecl) + break; + } + } + cur++; + } + xmlFree(nsList); + } + if (tmpstr != NULL) + xmlFree(tmpstr); + /* + * Only default/fixed attrs are relevant. + */ + if ((attrDecl != NULL) && (attrDecl->defaultValue != NULL)) + return((xmlAttrPtr) attrDecl); + } +#endif /* LIBXML_TREE_ENABLED */ + return(NULL); +} + +static xmlChar* +xmlGetPropNodeValueInternal(xmlAttrPtr prop) +{ + if (prop == NULL) + return(NULL); + if (prop->type == XML_ATTRIBUTE_NODE) { + /* + * Note that we return at least the empty string. + * TODO: Do we really always want that? + */ + if (prop->children != NULL) { + if ((prop->children == prop->last) && + ((prop->children->type == XML_TEXT_NODE) || + (prop->children->type == XML_CDATA_SECTION_NODE))) + { + /* + * Optimization for the common case: only 1 text node. + */ + return(xmlStrdup(prop->children->content)); + } else { + xmlChar *ret; + + ret = xmlNodeListGetString(prop->doc, prop->children, 1); + if (ret != NULL) + return(ret); + } + } + return(xmlStrdup((xmlChar *)"")); + } else if (prop->type == XML_ATTRIBUTE_DECL) { + return(xmlStrdup(((xmlAttributePtr)prop)->defaultValue)); + } + return(NULL); +} + /** * xmlHasProp: * @node: the node @@ -6020,86 +6188,8 @@ xmlHasProp(xmlNodePtr node, const xmlChar *name) { */ xmlAttrPtr xmlHasNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) { - xmlAttrPtr prop; -#ifdef LIBXML_TREE_ENABLED - xmlDocPtr doc; -#endif /* LIBXML_TREE_ENABLED */ - - if ((node == NULL) || (node->type != XML_ELEMENT_NODE) || (name == NULL)) - return(NULL); - - prop = node->properties; - while (prop != NULL) { - /* - * One need to have - * - same attribute names - * - and the attribute carrying that namespace - */ - if (xmlStrEqual(prop->name, name)) { - if (((prop->ns != NULL) && - (xmlStrEqual(prop->ns->href, nameSpace))) || - ((prop->ns == NULL) && (nameSpace == NULL))) { - return(prop); - } - } - prop = prop->next; - } - if (!xmlCheckDTD) return(NULL); - -#ifdef LIBXML_TREE_ENABLED - /* - * Check if there is a default declaration in the internal - * or external subsets - */ - doc = node->doc; - if (doc != NULL) { - if (doc->intSubset != NULL) { - xmlAttributePtr attrDecl = NULL; - xmlNsPtr *nsList, *cur; - xmlChar *ename; - nsList = xmlGetNsList(node->doc, node); - if (nsList == NULL) - return(NULL); - if ((node->ns != NULL) && (node->ns->prefix != NULL)) { - ename = xmlStrdup(node->ns->prefix); - ename = xmlStrcat(ename, BAD_CAST ":"); - ename = xmlStrcat(ename, node->name); - } else { - ename = xmlStrdup(node->name); - } - if (ename == NULL) { - xmlFree(nsList); - return(NULL); - } - - if (nameSpace == NULL) { - attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, ename, - name, NULL); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) { - attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, ename, - name, NULL); - } - } else { - cur = nsList; - while (*cur != NULL) { - if (xmlStrEqual((*cur)->href, nameSpace)) { - attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, ename, - name, (*cur)->prefix); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, ename, - name, (*cur)->prefix); - } - cur++; - } - } - xmlFree(nsList); - xmlFree(ename); - return((xmlAttrPtr) attrDecl); - } - } -#endif /* LIBXML_TREE_ENABLED */ - return(NULL); + return(xmlGetPropNodeInternal(node, name, nameSpace, xmlCheckDTD)); } /** @@ -6120,46 +6210,12 @@ xmlHasNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) { */ xmlChar * xmlGetProp(xmlNodePtr node, const xmlChar *name) { - xmlAttrPtr prop; - xmlDocPtr doc; - - if ((node == NULL) || (node->type != XML_ELEMENT_NODE) || (name == NULL)) - return(NULL); + xmlAttrPtr prop; - /* - * Check on the properties attached to the node - */ - prop = node->properties; - while (prop != NULL) { - if (xmlStrEqual(prop->name, name)) { - xmlChar *ret; - - ret = xmlNodeListGetString(node->doc, prop->children, 1); - if (ret == NULL) return(xmlStrdup((xmlChar *)"")); - return(ret); - } - prop = prop->next; - } - if (!xmlCheckDTD) return(NULL); - - /* - * Check if there is a default declaration in the internal - * or external subsets - */ - doc = node->doc; - if (doc != NULL) { - xmlAttributePtr attrDecl; - if (doc->intSubset != NULL) { - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name); - if ((attrDecl != NULL) && (attrDecl->defaultValue != NULL)) - /* return attribute declaration only if a default value is given - (that includes #FIXED declarations) */ - return(xmlStrdup(attrDecl->defaultValue)); - } - } - return(NULL); + prop = xmlHasProp(node, name); + if (prop == NULL) + return(NULL); + return(xmlGetPropNodeValueInternal(prop)); } /** @@ -6180,44 +6236,11 @@ xmlGetProp(xmlNodePtr node, const xmlChar *name) { xmlChar * xmlGetNoNsProp(xmlNodePtr node, const xmlChar *name) { xmlAttrPtr prop; - xmlDocPtr doc; - - if ((node == NULL) || (node->type != XML_ELEMENT_NODE) || (name == NULL)) + + prop = xmlGetPropNodeInternal(node, name, NULL, xmlCheckDTD); + if (prop == NULL) return(NULL); - /* - * Check on the properties attached to the node - */ - prop = node->properties; - while (prop != NULL) { - if ((prop->ns == NULL) && (xmlStrEqual(prop->name, name))) { - xmlChar *ret; - - ret = xmlNodeListGetString(node->doc, prop->children, 1); - if (ret == NULL) return(xmlStrdup((xmlChar *)"")); - return(ret); - } - prop = prop->next; - } - if (!xmlCheckDTD) return(NULL); - - /* - * Check if there is a default declaration in the internal - * or external subsets - */ - doc = node->doc; - if (doc != NULL) { - xmlAttributePtr attrDecl; - if (doc->intSubset != NULL) { - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name); - if ((attrDecl != NULL) && (attrDecl->defaultValue != NULL)) - /* return attribute declaration only if a default value is given - (that includes #FIXED declarations) */ - return(xmlStrdup(attrDecl->defaultValue)); - } - } - return(NULL); + return(xmlGetPropNodeValueInternal(prop)); } /** @@ -6238,58 +6261,11 @@ xmlGetNoNsProp(xmlNodePtr node, const xmlChar *name) { xmlChar * xmlGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) { xmlAttrPtr prop; - xmlDocPtr doc; - xmlNsPtr ns; - if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) + prop = xmlGetPropNodeInternal(node, name, nameSpace, xmlCheckDTD); + if (prop == NULL) return(NULL); - - prop = node->properties; - if (nameSpace == NULL) - return(xmlGetNoNsProp(node, name)); - while (prop != NULL) { - /* - * One need to have - * - same attribute names - * - and the attribute carrying that namespace - */ - if ((xmlStrEqual(prop->name, name)) && - ((prop->ns != NULL) && - (xmlStrEqual(prop->ns->href, nameSpace)))) { - xmlChar *ret; - - ret = xmlNodeListGetString(node->doc, prop->children, 1); - if (ret == NULL) return(xmlStrdup((xmlChar *)"")); - return(ret); - } - prop = prop->next; - } - if (!xmlCheckDTD) return(NULL); - - /* - * Check if there is a default declaration in the internal - * or external subsets - */ - doc = node->doc; - if (doc != NULL) { - if (doc->intSubset != NULL) { - xmlAttributePtr attrDecl; - - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, node->name, name); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, node->name, name); - - if ((attrDecl != NULL) && (attrDecl->prefix != NULL)) { - /* - * The DTD declaration only allows a prefix search - */ - ns = xmlSearchNs(doc, node, attrDecl->prefix); - if ((ns != NULL) && (xmlStrEqual(ns->href, nameSpace))) - return(xmlStrdup(attrDecl->defaultValue)); - } - } - } - return(NULL); + return(xmlGetPropNodeValueInternal(prop)); } #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) @@ -6299,25 +6275,19 @@ xmlGetNsProp(xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace) { * @name: the attribute name * * Remove an attribute carried by a node. + * This handles only attributes in no namespace. * Returns 0 if successful, -1 if not found */ int xmlUnsetProp(xmlNodePtr node, const xmlChar *name) { xmlAttrPtr prop; - if ((node == NULL) || (node->type != XML_ELEMENT_NODE) || (name == NULL)) + prop = xmlGetPropNodeInternal(node, name, NULL, 0); + if (prop == NULL) return(-1); - prop = node->properties; - while (prop != NULL) { - if ((xmlStrEqual(prop->name, name)) && - (prop->ns == NULL)) { - xmlUnlinkNode((xmlNodePtr) prop); - xmlFreeProp(prop); - return(0); - } - prop = prop->next; - } - return(-1); + xmlUnlinkNode((xmlNodePtr) prop); + xmlFreeProp(prop); + return(0); } /** @@ -6332,24 +6302,13 @@ xmlUnsetProp(xmlNodePtr node, const xmlChar *name) { int xmlUnsetNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name) { xmlAttrPtr prop; - - if ((node == NULL) || (node->type != XML_ELEMENT_NODE) || (name == NULL)) - return(-1); - prop = node->properties; - if (ns == NULL) - return(xmlUnsetProp(node, name)); - if (ns->href == NULL) + + prop = xmlGetPropNodeInternal(node, name, (ns != NULL) ? ns->href : NULL, 0); + if (prop == NULL) return(-1); - while (prop != NULL) { - if ((xmlStrEqual(prop->name, name)) && - (prop->ns != NULL) && (xmlStrEqual(prop->ns->href, ns->href))) { - xmlUnlinkNode((xmlNodePtr) prop); - xmlFreeProp(prop); - return(0); - } - prop = prop->next; - } - return(-1); + xmlUnlinkNode((xmlNodePtr) prop); + xmlFreeProp(prop); + return(0); } #endif @@ -6357,16 +6316,19 @@ xmlUnsetNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name) { /** * xmlSetProp: * @node: the node - * @name: the attribute name + * @name: the attribute name (a QName) * @value: the attribute value * * Set (or reset) an attribute carried by a node. + * If @name has a prefix, then the corresponding + * namespace-binding will be used, if in scope; it is an + * error it there's no such ns-binding for the prefix in + * scope. * Returns the attribute pointer. + * */ xmlAttrPtr xmlSetProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) { - xmlAttrPtr prop; - xmlDocPtr doc; int len; const xmlChar *nqname; @@ -6385,48 +6347,15 @@ xmlSetProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) { xmlFree(prefix); if (ns != NULL) return(xmlSetNsProp(node, ns, nqname, value)); + /* + * If we get a QName and the prefix has no namespace- + * binding in scope, then this is an error. + * TODO: Previously this falled-back to non-ns handling. + * Should we revert this? + */ + return(NULL); } - - doc = node->doc; - prop = node->properties; - while (prop != NULL) { - if ((xmlStrEqual(prop->name, name)) && - (prop->ns == NULL)){ - xmlNodePtr oldprop = prop->children; - int id = xmlIsID(node->doc, node, prop); - - if (id == 1) - xmlRemoveID(node->doc, prop); - prop->children = NULL; - prop->last = NULL; - if (value != NULL) { - xmlChar *buffer; - xmlNodePtr tmp; - - buffer = xmlEncodeEntitiesReentrant(node->doc, value); - prop->children = xmlStringGetNodeList(node->doc, buffer); - prop->last = NULL; - prop->doc = doc; - tmp = prop->children; - while (tmp != NULL) { - tmp->parent = (xmlNodePtr) prop; - tmp->doc = doc; - if (tmp->next == NULL) - prop->last = tmp; - tmp = tmp->next; - } - xmlFree(buffer); - } - if (oldprop != NULL) - xmlFreeNodeList(oldprop); - if (id) - xmlAddID(NULL, node->doc, value, prop); - return(prop); - } - prop = prop->next; - } - prop = xmlNewProp(node, name, value); - return(prop); + return(xmlSetNsProp(node, NULL, name, value)); } /** @@ -6437,65 +6366,56 @@ xmlSetProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) { * @value: the attribute value * * Set (or reset) an attribute carried by a node. - * The ns structure must be in scope, this is not checked. + * The ns structure must be in scope, this is not checked * * Returns the attribute pointer. */ xmlAttrPtr xmlSetNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name, - const xmlChar *value) { + const xmlChar *value) +{ xmlAttrPtr prop; - if ((node == NULL) || (name == NULL) || (node->type != XML_ELEMENT_NODE)) - return(NULL); - - if (ns == NULL) - return(xmlSetProp(node, name, value)); - if (ns->href == NULL) + if (ns && (ns->href == NULL)) return(NULL); - prop = node->properties; - - while (prop != NULL) { + prop = xmlGetPropNodeInternal(node, name, (ns != NULL) ? ns->href : NULL, 0); + if (prop != NULL) { /* - * One need to have - * - same attribute names - * - and the attribute carrying that namespace - */ - if ((xmlStrEqual(prop->name, name)) && - (prop->ns != NULL) && (xmlStrEqual(prop->ns->href, ns->href))) { - int id = xmlIsID(node->doc, node, prop); - - if (id == 1) - xmlRemoveID(node->doc, prop); - if (prop->children != NULL) - xmlFreeNodeList(prop->children); - prop->children = NULL; + * Modify the attribute's value. + */ + if (prop->atype == XML_ATTRIBUTE_ID) { + xmlRemoveID(node->doc, prop); + prop->atype = XML_ATTRIBUTE_ID; + } + if (prop->children != NULL) + xmlFreeNodeList(prop->children); + prop->children = NULL; + prop->last = NULL; + prop->ns = ns; + if (value != NULL) { + xmlChar *buffer; + xmlNodePtr tmp; + + buffer = xmlEncodeEntitiesReentrant(node->doc, value); + prop->children = xmlStringGetNodeList(node->doc, buffer); prop->last = NULL; - prop->ns = ns; - if (value != NULL) { - xmlChar *buffer; - xmlNodePtr tmp; - - buffer = xmlEncodeEntitiesReentrant(node->doc, value); - prop->children = xmlStringGetNodeList(node->doc, buffer); - prop->last = NULL; - tmp = prop->children; - while (tmp != NULL) { - tmp->parent = (xmlNodePtr) prop; - if (tmp->next == NULL) - prop->last = tmp; - tmp = tmp->next; - } - xmlFree(buffer); - } - if (id) - xmlAddID(NULL, node->doc, value, prop); - return(prop); - } - prop = prop->next; + tmp = prop->children; + while (tmp != NULL) { + tmp->parent = (xmlNodePtr) prop; + if (tmp->next == NULL) + prop->last = tmp; + tmp = tmp->next; + } + xmlFree(buffer); + } + if (prop->atype == XML_ATTRIBUTE_ID) + xmlAddID(NULL, node->doc, value, prop); + return(prop); } - prop = xmlNewNsProp(node, ns, name, value); - return(prop); + /* + * No equal attr found; create a new one. + */ + return(xmlNewPropInternal(node, ns, name, value, 0)); } #endif /* LIBXML_TREE_ENABLED */ @@ -7291,72 +7211,138 @@ struct xmlNsMapItem { * depth: * >= 0 == @node's ns-decls * -1 == @parent's ns-decls - * -2 == @parent's out-of-scope ns-decls - * -3 == the doc->oldNs XML ns-decl - * -4 == the doc->oldNs storage ns-decls + * -2 == the doc->oldNs XML ns-decl + * -3 == the doc->oldNs storage ns-decls + * -4 == ns-decls provided via custom ns-handling */ int depth; }; +typedef struct xmlNsMap *xmlNsMapPtr; +struct xmlNsMap { + xmlNsMapItemPtr first; + xmlNsMapItemPtr last; + xmlNsMapItemPtr pool; +}; + +#define XML_NSMAP_NOTEMPTY(m) (((m) != NULL) && ((m)->first != NULL)) +#define XML_NSMAP_FOREACH(m, i) for (i = (m)->first; i != NULL; i = (i)->next) +#define XML_NSMAP_POP(m, i) \ + i = (m)->last; \ + (m)->last = (i)->prev; \ + if ((m)->last == NULL) \ + (m)->first = NULL; \ + else \ + (m)->last->next = NULL; \ + (i)->next = (m)->pool; \ + (m)->pool = i; + /* -* xmlTreeAddNsMapItem: +* xmlDOMWrapNsMapFree: +* @map: the ns-map +* +* Frees the ns-map +*/ +static void +xmlDOMWrapNsMapFree(xmlNsMapPtr nsmap) +{ + xmlNsMapItemPtr cur, tmp; + + if (nsmap == NULL) + return; + cur = nsmap->pool; + while (cur != NULL) { + tmp = cur; + cur = cur->next; + xmlFree(tmp); + } + cur = nsmap->first; + while (cur != NULL) { + tmp = cur; + cur = cur->next; + xmlFree(tmp); + } + xmlFree(nsmap); +} + +/* +* xmlDOMWrapNsMapAddItem: * @map: the ns-map * @cur: the current map entry to append a new entry to * @oldNs: the old ns-struct * @newNs: the new ns-struct * @depth: depth and ns-kind information * -* Frees the ns-map +* Adds an ns-mapping item. */ static xmlNsMapItemPtr -xmlDOMWrapNSNormAddNsMapItem(xmlNsMapItemPtr *map, - xmlNsMapItemPtr *cur, - xmlNsPtr oldNs, - xmlNsPtr newNs, - int depth) +xmlDOMWrapNsMapAddItem(xmlNsMapPtr *nsmap, int position, /* xmlNsMapItemPtr *cur, */ + xmlNsPtr oldNs, xmlNsPtr newNs, int depth) { xmlNsMapItemPtr ret; - - if ((cur != NULL) && (*cur != NULL) && ((*cur)->next != NULL)) { + xmlNsMapPtr map; + + if (nsmap == NULL) + return(NULL); + if ((position != -1) && (position != 0)) + return(NULL); + map = *nsmap; + + if (map == NULL) { /* - * Reuse. + * Create the ns-map. */ - ret = (*cur)->next; - *cur = ret; + map = (xmlNsMapPtr) xmlMalloc(sizeof(struct xmlNsMap)); + if (map == NULL) { + xmlTreeErrMemory("allocating namespace map"); + return (NULL); + } + memset(map, 0, sizeof(struct xmlNsMap)); + *nsmap = map; + } + + if (map->pool != NULL) { + /* + * Reuse an item from the pool. + */ + ret = map->pool; + map->pool = ret->next; + memset(ret, 0, sizeof(struct xmlNsMapItem)); } else { + /* + * Create a new item. + */ ret = (xmlNsMapItemPtr) xmlMalloc(sizeof(struct xmlNsMapItem)); if (ret == NULL) { xmlTreeErrMemory("allocating namespace map item"); return (NULL); } memset(ret, 0, sizeof(struct xmlNsMapItem)); - if (*map == NULL) { - /* - * First ever. - */ - *map = ret; - ret->prev = ret; - if (cur != NULL) - *cur = ret; - } else { - if (cur) { - /* - * Append. - */ - (*cur)->next = ret; - ret->prev = *cur; - *cur = ret; - } else { - /* - * Set on first position. - */ - ret->next = (*map); - ret->prev = (*map)->prev; - (*map)->prev = ret; - *map = ret; - } - } } + + if (map->first == NULL) { + /* + * First ever. + */ + map->first = ret; + map->last = ret; + } else if (position == -1) { + /* + * Append. + */ + ret->prev = map->last; + map->last->next = ret; + map->last = ret; + } else if (position == 0) { + /* + * Set on first position. + */ + map->first->prev = ret; + ret->next = map->first; + map->first = ret; + } else + return(NULL); + ret->oldNs = oldNs; ret->newNs = newNs; ret->shadowDepth = -1; @@ -7365,24 +7351,6 @@ xmlDOMWrapNSNormAddNsMapItem(xmlNsMapItemPtr *map, } /* -* xmlTreeFreeNsMap: -* @map: the ns-map -* -* Frees the ns-map -*/ -static void -xmlDOMWrapNSNormFreeNsMap(xmlNsMapItemPtr map) -{ - xmlNsMapItemPtr mi = map, miprev; - - while (mi != NULL) { - miprev = mi; - mi = mi->next; - xmlFree(miprev); - } -} - -/* * xmlTreeEnsureXMLDecl: * @doc: the doc * @@ -7488,7 +7456,7 @@ xmlTreeNSListLookupByPrefix(xmlNsPtr nsList, const xmlChar *prefix) /* * -* xmlTreeGetInScopeNamespaces: +* xmlDOMWrapNSNormGatherInScopeNs: * @map: the namespace map * @node: the node to start with * @@ -7497,7 +7465,7 @@ xmlTreeNSListLookupByPrefix(xmlNsPtr nsList, const xmlChar *prefix) * Returns 0 on success, -1 on API or internal errors. */ static int -xmlDOMWrapNSNormGatherInScopeNs(xmlNsMapItemPtr *map, +xmlDOMWrapNSNormGatherInScopeNs(xmlNsMapPtr *map, xmlNodePtr node) { xmlNodePtr cur; @@ -7517,11 +7485,11 @@ xmlDOMWrapNSNormGatherInScopeNs(xmlNsMapItemPtr *map, ns = cur->nsDef; do { shadowed = 0; - if (*map != NULL) { + if (XML_NSMAP_NOTEMPTY(*map)) { /* * Skip shadowed prefixes. */ - for (mi = *map; mi != NULL; mi = mi->next) { + XML_NSMAP_FOREACH(*map, mi) { if ((ns->prefix == mi->newNs->prefix) || xmlStrEqual(ns->prefix, mi->newNs->prefix)) { shadowed = 1; @@ -7532,7 +7500,7 @@ xmlDOMWrapNSNormGatherInScopeNs(xmlNsMapItemPtr *map, /* * Insert mapping. */ - mi = xmlDOMWrapNSNormAddNsMapItem(map, NULL, NULL, + mi = xmlDOMWrapNsMapAddItem(map, 0, NULL, ns, XML_TREE_NSMAP_PARENT); if (mi == NULL) return (-1); @@ -7750,7 +7718,7 @@ internal_error: } /* -* xmlSearchNsByHrefStrict: +* xmlSearchNsByNamespaceStrict: * @doc: the document * @node: the start node * @nsName: the searched namespace name @@ -7764,8 +7732,9 @@ internal_error: * and internal errors. */ static int -xmlSearchNsByHrefStrict(xmlDocPtr doc, xmlNodePtr node, const xmlChar* nsName, - xmlNsPtr *retNs, int prefixed) +xmlSearchNsByNamespaceStrict(xmlDocPtr doc, xmlNodePtr node, + const xmlChar* nsName, + xmlNsPtr *retNs, int prefixed) { xmlNodePtr cur, prev = NULL, out = NULL; xmlNsPtr ns, prevns; @@ -7841,9 +7810,72 @@ xmlSearchNsByHrefStrict(xmlDocPtr doc, xmlNodePtr node, const xmlChar* nsName, out = prev; prev = cur; } - } else if ((node->type == XML_ENTITY_REF_NODE) || - (node->type == XML_ENTITY_NODE) || - (node->type == XML_ENTITY_DECL)) + } else if ((cur->type == XML_ENTITY_NODE) || + (cur->type == XML_ENTITY_DECL)) + return (0); + cur = cur->parent; + } while ((cur != NULL) && (cur->doc != (xmlDocPtr) cur)); + return (0); +} + +/* +* xmlSearchNsByPrefixStrict: +* @doc: the document +* @node: the start node +* @prefix: the searched namespace prefix +* @retNs: the resulting ns-decl +* @prefixed: if the found ns-decl must have a prefix (for attributes) +* +* Dynamically searches for a ns-declaration which matches +* the given @nsName in the ancestor-or-self axis of @node. +* +* Returns 1 if a ns-decl was found, 0 if not and -1 on API +* and internal errors. +*/ +static int +xmlSearchNsByPrefixStrict(xmlDocPtr doc, xmlNodePtr node, + const xmlChar* prefix, + xmlNsPtr *retNs) +{ + xmlNodePtr cur; + xmlNsPtr ns; + + if ((doc == NULL) || (node == NULL)) + return (-1); + + if (retNs) + *retNs = NULL; + if (IS_STR_XML(prefix)) { + if (retNs) { + *retNs = xmlTreeEnsureXMLDecl(doc); + if (*retNs == NULL) + return (-1); + } + return (1); + } + cur = node; + do { + if (cur->type == XML_ELEMENT_NODE) { + if (cur->nsDef != NULL) { + ns = cur->nsDef; + do { + if ((prefix == ns->prefix) || + xmlStrEqual(prefix, ns->prefix)) + { + /* + * Disabled namespaces, e.g. xmlns:abc="". + */ + if (ns->href == NULL) + return(0); + if (retNs) + *retNs = ns; + return (1); + } + ns = ns->next; + } while (ns != NULL); + } + } else if ((cur->type == XML_ENTITY_NODE) || + (cur->type == XML_ENTITY_DECL)) return (0); cur = cur->parent; } while ((cur != NULL) && (cur->doc != (xmlDocPtr) cur)); @@ -7893,7 +7925,7 @@ xmlDOMWrapNSNormDeclareNsForced(xmlDocPtr doc, /* * Does it shadow ancestor ns-decls? */ - if (xmlSearchNs(doc, elem->parent, pref) != NULL) + if (xmlSearchNsByPrefixStrict(doc, elem->parent, pref, NULL) == 1) goto ns_next_prefix; } ret = xmlNewNs(NULL, nsName, pref); @@ -7947,8 +7979,8 @@ xmlDOMWrapNSNormAquireNormalizedNs(xmlDocPtr doc, xmlNodePtr elem, xmlNsPtr ns, xmlNsPtr *retNs, - xmlNsMapItemPtr *nsMap, - xmlNsMapItemPtr *topmi, + xmlNsMapPtr *nsMap, + int depth, int ancestorsOnly, int prefixed) @@ -7956,18 +7988,14 @@ xmlDOMWrapNSNormAquireNormalizedNs(xmlDocPtr doc, xmlNsMapItemPtr mi; if ((doc == NULL) || (ns == NULL) || (retNs == NULL) || - (nsMap == NULL) || (topmi == NULL)) + (nsMap == NULL)) return (-1); *retNs = NULL; /* * Handle XML namespace. */ - if ((ns->prefix) && - (ns->prefix[0] == 'x') && - (ns->prefix[1] == 'm') && - (ns->prefix[2] == 'l') && - (ns->prefix[3] == 0)) { + if (IS_STR_XML(ns->prefix)) { /* * Insert XML namespace mapping. */ @@ -7980,20 +8008,18 @@ xmlDOMWrapNSNormAquireNormalizedNs(xmlDocPtr doc, * If the search should be done in ancestors only and no * @elem (the first ancestor) was specified, then skip the search. */ - if ((! (ancestorsOnly && (elem == NULL))) && - (*nsMap != NULL)) { - + if ((! (ancestorsOnly && (elem == NULL))) && (XML_NSMAP_NOTEMPTY(*nsMap))) + { /* * Try to find an equal ns-name in in-scope ns-decls. */ - for (mi = *nsMap; mi != (*topmi)->next; mi = mi->next) { - + XML_NSMAP_FOREACH(*nsMap, mi) { if ((mi->depth >= XML_TREE_NSMAP_PARENT) && /* - * This should be turned on to gain speed, if one knows - * that the branch itself was already ns-wellformed and no - * stale references existed. I.e. it searches in the ancestor - * axis only. + * ancestorsOnly: This should be turned on to gain speed, + * if one knows that the branch itself was already + * ns-wellformed and no stale references existed. + * I.e. it searches in the ancestor axis only. */ ((! ancestorsOnly) || (mi->depth == XML_TREE_NSMAP_PARENT)) && /* Skip shadowed prefixes. */ @@ -8028,7 +8054,7 @@ xmlDOMWrapNSNormAquireNormalizedNs(xmlDocPtr doc, /* * Insert mapping. */ - if (xmlDOMWrapNSNormAddNsMapItem(nsMap, NULL, ns, + if (xmlDOMWrapNsMapAddItem(nsMap, -1, ns, tmpns, XML_TREE_NSMAP_DOC) == NULL) { xmlFreeNs(tmpns); return (-1); @@ -8045,8 +8071,8 @@ xmlDOMWrapNSNormAquireNormalizedNs(xmlDocPtr doc, if (*nsMap != NULL) { /* * Does it shadow ancestor ns-decls? - */ - for (mi = *nsMap; mi != (*topmi)->next; mi = mi->next) { + */ + XML_NSMAP_FOREACH(*nsMap, mi) { if ((mi->depth < depth) && (mi->shadowDepth == -1) && ((ns->prefix == mi->newNs->prefix) || @@ -8059,8 +8085,7 @@ xmlDOMWrapNSNormAquireNormalizedNs(xmlDocPtr doc, } } } - if (xmlDOMWrapNSNormAddNsMapItem(nsMap, topmi, ns, - tmpns, depth) == NULL) { + if (xmlDOMWrapNsMapAddItem(nsMap, -1, ns, tmpns, depth) == NULL) { xmlFreeNs(tmpns); return (-1); } @@ -8069,6 +8094,10 @@ xmlDOMWrapNSNormAquireNormalizedNs(xmlDocPtr doc, return (0); } +typedef enum { + XML_DOM_RECONNS_REMOVEREDUND = 1<<0 +} xmlDOMReconcileNSOptions; + /* * xmlDOMWrapReconcileNamespaces: * @ctxt: DOM wrapper context, unused at the moment @@ -8083,19 +8112,25 @@ xmlDOMWrapNSNormAquireNormalizedNs(xmlDocPtr doc, * WARNING: This function is in a experimental state. * * Returns 0 if succeeded, -1 otherwise and on API/internal errors. -*/ +*/ + int xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlNodePtr elem, - int options ATTRIBUTE_UNUSED) + int options) { int depth = -1, adoptns = 0, parnsdone = 0; - xmlNsPtr ns; + xmlNsPtr ns, prevns; xmlDocPtr doc; xmlNodePtr cur, curElem = NULL; - xmlNsMapItemPtr nsMap = NULL, topmi = NULL, mi; + xmlNsMapPtr nsMap = NULL; + xmlNsMapItemPtr /* topmi = NULL, */ mi; /* @ancestorsOnly should be set by an option flag. */ int ancestorsOnly = 0; + int optRemoveDedundantNS = + ((xmlDOMReconcileNSOptions) options & XML_DOM_RECONNS_REMOVEREDUND) ? 1 : 0; + xmlNsPtr *listRedund = NULL; + int sizeRedund = 0, nbRedund = 0, ret, i, j; if ((elem == NULL) || (elem->doc == NULL) || (elem->type != XML_ELEMENT_NODE)) @@ -8113,7 +8148,9 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, * Namespace declarations. */ if (cur->nsDef != NULL) { - for (ns = cur->nsDef; ns != NULL; ns = ns->next) { + prevns = NULL; + ns = cur->nsDef; + while (ns != NULL) { if (! parnsdone) { if ((elem->parent) && ((xmlNodePtr) elem->parent->doc != elem->parent)) { @@ -8123,11 +8160,41 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap, elem->parent) == -1) goto internal_error; - if (nsMap != NULL) - topmi = nsMap->prev; } parnsdone = 1; } + + /* + * Lookup the ns ancestor-axis for equal ns-decls in scope. + */ + if (optRemoveDedundantNS && XML_NSMAP_NOTEMPTY(nsMap)) { + XML_NSMAP_FOREACH(nsMap, mi) { + if ((mi->depth >= XML_TREE_NSMAP_PARENT) && + (mi->shadowDepth == -1) && + ((ns->prefix == mi->newNs->prefix) || + xmlStrEqual(ns->prefix, mi->newNs->prefix)) && + ((ns->href == mi->newNs->href) || + xmlStrEqual(ns->href, mi->newNs->href))) + { + /* + * A redundant ns-decl was found. + * Add it to the list of redundant ns-decls. + */ + if (xmlDOMWrapNSNormAddNsMapItem2(&listRedund, + &sizeRedund, &nbRedund, ns, mi->newNs) == -1) + goto internal_error; + /* + * Remove the ns-decl from the element-node. + */ + if (prevns) + prevns->next = ns->next; + else + cur->nsDef = ns->next; + goto next_ns_decl; + } + } + } + /* * Skip ns-references handling if the referenced * ns-decl is declared on the same element. @@ -8137,8 +8204,8 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, /* * Does it shadow any ns-decl? */ - if (nsMap) { - for (mi = nsMap; mi != topmi->next; mi = mi->next) { + if (XML_NSMAP_NOTEMPTY(nsMap)) { + XML_NSMAP_FOREACH(nsMap, mi) { if ((mi->depth >= XML_TREE_NSMAP_PARENT) && (mi->shadowDepth == -1) && ((ns->prefix == mi->newNs->prefix) || @@ -8151,37 +8218,51 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, /* * Push mapping. */ - if (xmlDOMWrapNSNormAddNsMapItem(&nsMap, &topmi, ns, ns, + if (xmlDOMWrapNsMapAddItem(&nsMap, -1, ns, ns, depth) == NULL) - goto internal_error; + goto internal_error; + + prevns = ns; +next_ns_decl: + ns = ns->next; } } if (! adoptns) goto ns_end; - /* No break on purpose. */ case XML_ATTRIBUTE_NODE: + /* No ns, no fun. */ if (cur->ns == NULL) goto ns_end; + if (! parnsdone) { if ((elem->parent) && ((xmlNodePtr) elem->parent->doc != elem->parent)) { if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap, elem->parent) == -1) goto internal_error; - if (nsMap != NULL) - topmi = nsMap->prev; } parnsdone = 1; } /* + * Adjust the reference if this was a redundant ns-decl. + */ + if (listRedund) { + for (i = 0, j = 0; i < nbRedund; i++, j += 2) { + if (cur->ns == listRedund[j]) { + cur->ns = listRedund[++j]; + break; + } + } + } + /* * Adopt ns-references. */ - if (nsMap != NULL) { + if (XML_NSMAP_NOTEMPTY(nsMap)) { /* * Search for a mapping. */ - for (mi = nsMap; mi != topmi->next; mi = mi->next) { + XML_NSMAP_FOREACH(nsMap, mi) { if ((mi->shadowDepth == -1) && (cur->ns == mi->oldNs)) { @@ -8195,7 +8276,7 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, */ if (xmlDOMWrapNSNormAquireNormalizedNs(doc, curElem, cur->ns, &ns, - &nsMap, &topmi, depth, + &nsMap, depth, ancestorsOnly, (cur->type == XML_ATTRIBUTE_NODE) ? 1 : 0) == -1) goto internal_error; @@ -8214,6 +8295,7 @@ ns_end: default: goto next_sibling; } +into_content: if ((cur->type == XML_ELEMENT_NODE) && (cur->children != NULL)) { /* @@ -8226,36 +8308,51 @@ next_sibling: if (cur == elem) break; if (cur->type == XML_ELEMENT_NODE) { - if (nsMap != NULL) { + if (XML_NSMAP_NOTEMPTY(nsMap)) { /* * Pop mappings. */ - while ((topmi->depth >= 0) && (topmi->depth >= depth)) - topmi = topmi->prev; + while ((nsMap->last != NULL) && + (nsMap->last->depth >= depth)) + { + XML_NSMAP_POP(nsMap, mi) + } /* * Unshadow. */ - for (mi = nsMap; mi != topmi->next; mi = mi->next) + XML_NSMAP_FOREACH(nsMap, mi) { if (mi->shadowDepth >= depth) mi->shadowDepth = -1; - } + } + } depth--; } if (cur->next != NULL) cur = cur->next; else { + if (cur->type == XML_ATTRIBUTE_NODE) { + cur = cur->parent; + goto into_content; + } cur = cur->parent; goto next_sibling; } } while (cur != NULL); - - if (nsMap != NULL) - xmlDOMWrapNSNormFreeNsMap(nsMap); - return (0); + + ret = 0; + goto exit; internal_error: + ret = -1; +exit: + if (listRedund) { + for (i = 0, j = 0; i < nbRedund; i++, j += 2) { + xmlFreeNs(listRedund[j]); + } + xmlFree(listRedund); + } if (nsMap != NULL) - xmlDOMWrapNSNormFreeNsMap(nsMap); - return (-1); + xmlDOMWrapNsMapFree(nsMap); + return (ret); } /* @@ -8264,7 +8361,7 @@ internal_error: * @sourceDoc: the optional sourceDoc * @node: the element-node to start with * @destDoc: the destination doc for adoption -* @parent: the optional new parent of @node in @destDoc +* @destParent: the optional new parent of @node in @destDoc * @options: option flags * * Ensures that ns-references point to @destDoc: either to @@ -8288,8 +8385,9 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, { int ret = 0; xmlNodePtr cur, curElem = NULL; - xmlNsMapItemPtr nsMap = NULL, topmi = NULL, mi; - xmlNsPtr ns; + xmlNsMapPtr nsMap = NULL; + xmlNsMapItemPtr mi; + xmlNsPtr ns = NULL; int depth = -1, adoptStr = 1; /* gather @parent's ns-decls. */ int parnsdone = 0; @@ -8348,9 +8446,7 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, */ if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap, destParent) == -1) - goto internal_error; - if (nsMap != NULL) - topmi = nsMap->prev; + goto internal_error; } parnsdone = 1; } @@ -8363,9 +8459,8 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, /* * Does it shadow any ns-decl? */ - if (nsMap) { - for (mi = nsMap; mi != topmi->next; - mi = mi->next) { + if (XML_NSMAP_NOTEMPTY(nsMap)) { + XML_NSMAP_FOREACH(nsMap, mi) { if ((mi->depth >= XML_TREE_NSMAP_PARENT) && (mi->shadowDepth == -1) && ((ns->prefix == mi->newNs->prefix) || @@ -8379,7 +8474,7 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, /* * Push mapping. */ - if (xmlDOMWrapNSNormAddNsMapItem(&nsMap, &topmi, + if (xmlDOMWrapNsMapAddItem(&nsMap, -1, ns, ns, depth) == NULL) goto internal_error; } @@ -8393,20 +8488,18 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, if (destParent && (ctxt == NULL)) { if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap, destParent) == -1) - goto internal_error; - if (nsMap != NULL) - topmi = nsMap->prev; + goto internal_error; } parnsdone = 1; } /* * Adopt ns-references. */ - if (nsMap != NULL) { + if (XML_NSMAP_NOTEMPTY(nsMap)) { /* * Search for a mapping. */ - for (mi = nsMap; mi != topmi->next; mi = mi->next) { + XML_NSMAP_FOREACH(nsMap, mi) { if ((mi->shadowDepth == -1) && (cur->ns == mi->oldNs)) { @@ -8429,7 +8522,7 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, * Insert mapping if ns is available; it's the users fault * if not. */ - if (xmlDOMWrapNSNormAddNsMapItem(&nsMap, &topmi, + if (xmlDOMWrapNsMapAddItem(&nsMap, -1, ns, ns, XML_TREE_NSMAP_CUSTOM) == NULL) goto internal_error; cur->ns = ns; @@ -8441,7 +8534,7 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, /* ns-decls on curElem or on destDoc->oldNs */ destParent ? curElem : NULL, cur->ns, &ns, - &nsMap, &topmi, depth, + &nsMap, depth, ancestorsOnly, /* ns-decls must be prefixed for attributes. */ (cur->type == XML_ATTRIBUTE_NODE) ? 1 : 0) == -1) @@ -8533,18 +8626,22 @@ leave_node: /* * TODO: Do we expect nsDefs on XML_XINCLUDE_START? */ - if (nsMap != NULL) { + if (XML_NSMAP_NOTEMPTY(nsMap)) { /* * Pop mappings. */ - while (topmi->depth >= depth) - topmi = topmi->prev; + while ((nsMap->last != NULL) && + (nsMap->last->depth >= depth)) + { + XML_NSMAP_POP(nsMap, mi) + } /* * Unshadow. */ - for (mi = nsMap; mi != topmi->next; mi = mi->next) + XML_NSMAP_FOREACH(nsMap, mi) { if (mi->shadowDepth >= depth) mi->shadowDepth = -1; + } } depth--; } @@ -8555,16 +8652,522 @@ leave_node: goto leave_node; } } + + goto exit; + +internal_error: + ret = -1; + +exit: /* * Cleanup. */ if (nsMap != NULL) - xmlDOMWrapNSNormFreeNsMap(nsMap); - return (ret); + xmlDOMWrapNsMapFree(nsMap); + return(ret); +} + +/* +* xmlDOMWrapCloneNode: +* @ctxt: the optional context for custom processing +* @sourceDoc: the optional sourceDoc +* @node: the node to start with +* @resNode: the clone of the given @node +* @destDoc: the destination doc +* @destParent: the optional new parent of @node in @destDoc +* @deep: descend into child if set +* @options: option flags +* +* References of out-of scope ns-decls are remapped to point to @destDoc: +* 1) If @destParent is given, then nsDef entries on element-nodes are used +* 2) If *no* @destParent is given, then @destDoc->oldNs entries are used +* This is the case when you have an unliked node and just want to move it +* to the context of +* +* If @destParent is given, it ensures that the tree is namespace +* wellformed by creating additional ns-decls where needed. +* Note that, since prefixes of already existent ns-decls can be +* shadowed by this process, it could break QNames in attribute +* values or element content. +* TODO: +* 1) Support dicts +* Optimize string adoption for equal or none dicts. +* 2) XInclude +* WARNING: This function is in a experimental state and should only be currently +* only be used to test it. +* +* Returns 0 if the operation succeeded, +* 1 if a node of unsupported (or not yet supported) type was given, +* -1 on API/internal errors. +*/ + +int +xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, + xmlDocPtr sourceDoc, + xmlNodePtr node, + xmlNodePtr *resNode, + xmlDocPtr destDoc, + xmlNodePtr destParent, + int deep, + int options ATTRIBUTE_UNUSED) +{ + int ret = 0; + xmlNodePtr cur, curElem = NULL; + xmlNsMapPtr nsMap = NULL; + xmlNsMapItemPtr mi; + xmlNsPtr ns; + int depth = -1; + /* int adoptStr = 1; */ + /* gather @parent's ns-decls. */ + int parnsdone = 0; + /* @ancestorsOnly should be set per option. */ + int ancestorsOnly = 0; + xmlNodePtr resultClone = NULL, clone = NULL, parentClone = NULL, prevClone = NULL; + xmlNsPtr cloneNs = NULL, *cloneNsDefSlot = NULL; + + if ((node == NULL) || (resNode == NULL) || (destDoc == NULL)) + return(-1); + /* + * TODO: Initially we support only element-nodes. + */ + if (node->type != XML_ELEMENT_NODE) + return(1); + /* + * Check node->doc sanity. + */ + if ((node->doc != NULL) && (sourceDoc != NULL) && + (node->doc != sourceDoc)) { + /* + * Might be an XIncluded node. + */ + return (-1); + } + if (sourceDoc == NULL) + sourceDoc = node->doc; + if (sourceDoc == NULL) + return (-1); + + *resNode = NULL; + + cur = node; + while (cur != NULL) { + if (cur->doc != sourceDoc) { + /* + * We'll assume XIncluded nodes if the doc differs. + * TODO: Do we need to reconciliate XIncluded nodes? + * TODO: This here returns -1 in this case. + */ + goto internal_error; + } + /* + * Create a new node. + */ + switch (cur->type) { + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + /* TODO: What to do with XInclude? */ + goto internal_error; + break; + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + case XML_ELEMENT_NODE: + case XML_DOCUMENT_FRAG_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + /* + * Nodes of xmlNode structure. + */ + clone = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); + if (clone == NULL) { + xmlTreeErrMemory("xmlDOMWrapCloneBranch(): allocating a node"); + goto internal_error; + } + memset(clone, 0, sizeof(xmlNode)); + /* + * Set hierachical links. + */ + if (resultClone != NULL) { + clone->parent = parentClone; + if (prevClone) { + prevClone->next = clone; + clone->prev = prevClone; + } else + parentClone->children = clone; + } else + resultClone = clone; + + break; + case XML_ATTRIBUTE_NODE: + /* + * Attributes (xmlAttr). + */ + clone = (xmlNodePtr) xmlMalloc(sizeof(xmlAttr)); + if (clone == NULL) { + xmlTreeErrMemory("xmlDOMWrapCloneBranch(): allocating an attr-node"); + goto internal_error; + } + memset(clone, 0, sizeof(xmlAttr)); + /* + * Set hierachical links. + */ + if (resultClone != NULL) { + clone->parent = parentClone; + if (prevClone) { + prevClone->next = clone; + clone->prev = prevClone; + } else + parentClone->properties = (xmlAttrPtr) clone; + } else + resultClone = clone; + break; + default: + /* TODO */ + goto internal_error; + } + + clone->type = cur->type; + clone->doc = destDoc; + + if (cur->name == xmlStringText) + clone->name = xmlStringText; + else if (cur->name == xmlStringTextNoenc) + /* + * TODO: xmlStringTextNoenc is never assigned to a node + * in tree.c. + */ + clone->name = xmlStringTextNoenc; + else if (cur->name == xmlStringComment) + clone->name = xmlStringComment; + else if (cur->name != NULL) { + if ((destDoc != NULL) && (destDoc->dict != NULL)) + clone->name = xmlDictLookup(destDoc->dict, cur->name, -1); + else + clone->name = xmlStrdup(cur->name); + } + + switch (cur->type) { + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + /* + * TODO + */ + return (-1); + case XML_ELEMENT_NODE: + curElem = cur; + depth++; + /* + * Namespace declarations. + */ + if (cur->nsDef != NULL) { + if (! parnsdone) { + if (destParent && (ctxt == NULL)) { + /* + * Gather @parent's in-scope ns-decls. + */ + if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap, + destParent) == -1) + goto internal_error; + } + parnsdone = 1; + } + /* + * Clone namespace declarations. + */ + cloneNsDefSlot = &(clone->nsDef); + for (ns = cur->nsDef; ns != NULL; ns = ns->next) { + /* + * Create a new xmlNs. + */ + cloneNs = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); + if (cloneNs == NULL) { + xmlTreeErrMemory("xmlDOMWrapCloneBranch(): " + "allocating namespace"); + return(-1); + } + memset(cloneNs, 0, sizeof(xmlNs)); + cloneNs->type = XML_LOCAL_NAMESPACE; + + if (ns->href != NULL) + cloneNs->href = xmlStrdup(ns->href); + if (ns->prefix != NULL) + cloneNs->prefix = xmlStrdup(ns->prefix); + + *cloneNsDefSlot = cloneNs; + cloneNsDefSlot = &(cloneNs->next); + + if (ctxt == NULL) { + /* + * Does it shadow any ns-decl? + */ + if (XML_NSMAP_NOTEMPTY(nsMap)) { + XML_NSMAP_FOREACH(nsMap, mi) { + if ((mi->depth >= XML_TREE_NSMAP_PARENT) && + (mi->shadowDepth == -1) && + ((ns->prefix == mi->newNs->prefix) || + xmlStrEqual(ns->prefix, + mi->newNs->prefix))) { + /* + * Mark as shadowed at the current + * depth. + */ + mi->shadowDepth = depth; + } + } + } + /* + * Push mapping. + */ + if (xmlDOMWrapNsMapAddItem(&nsMap, -1, + ns, cloneNs, depth) == NULL) + goto internal_error; + } + } + } + /* cur->ns will be processed further down. */ + break; + case XML_ATTRIBUTE_NODE: + /* IDs will be processed further down. */ + /* cur->ns will be processed further down. */ + break; + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + if (cur->content) + clone->content = xmlStrdup(cur->content); + goto leave_node; + case XML_ENTITY_NODE: + /* TODO: What to do here? */ + goto leave_node; + case XML_ENTITY_REF_NODE: + if (sourceDoc != destDoc) { + if ((destDoc->intSubset) || (destDoc->extSubset)) { + xmlEntityPtr ent; + /* + * Different doc: Assign new entity-node if available. + */ + ent = xmlGetDocEntity(destDoc, cur->name); + if (ent != NULL) { + clone->content = ent->content; + clone->children = (xmlNodePtr) ent; + clone->last = (xmlNodePtr) ent; + } + } + } else { + /* + * Same doc: Use the current node's entity declaration + * and value. + */ + clone->content = cur->content; + clone->children = cur->children; + clone->last = cur->last; + } + goto leave_node; + case XML_PI_NODE: + if (cur->content) + clone->content = xmlStrdup(cur->content); + goto leave_node; + case XML_COMMENT_NODE: + if (cur->content) + clone->content = xmlStrdup(cur->content); + goto leave_node; + default: + goto internal_error; + } + + if (cur->ns == NULL) + goto end_ns_reference; + +/* handle_ns_reference: */ + /* + ** The following will take care of references to ns-decls ******** + ** and is intended only for element- and attribute-nodes. + ** + */ + if (! parnsdone) { + if (destParent && (ctxt == NULL)) { + if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap, destParent) == -1) + goto internal_error; + } + parnsdone = 1; + } + /* + * Adopt ns-references. + */ + if (XML_NSMAP_NOTEMPTY(nsMap)) { + /* + * Search for a mapping. + */ + XML_NSMAP_FOREACH(nsMap, mi) { + if ((mi->shadowDepth == -1) && + (cur->ns == mi->oldNs)) { + /* + * This is the nice case: a mapping was found. + */ + clone->ns = mi->newNs; + goto end_ns_reference; + } + } + } + /* + * Start searching for an in-scope ns-decl. + */ + if (ctxt != NULL) { + /* + * User-defined behaviour. + */ +#if 0 + ctxt->aquireNsDecl(ctxt, cur->ns, &ns); +#endif + /* + * Add user's mapping. + */ + if (xmlDOMWrapNsMapAddItem(&nsMap, -1, + cur->ns, ns, XML_TREE_NSMAP_CUSTOM) == NULL) + goto internal_error; + clone->ns = ns; + } else { + /* + * Aquire a normalized ns-decl and add it to the map. + */ + if (xmlDOMWrapNSNormAquireNormalizedNs(destDoc, + /* ns-decls on curElem or on destDoc->oldNs */ + destParent ? curElem : NULL, + cur->ns, &ns, + &nsMap, depth, + ancestorsOnly, + /* ns-decls must be prefixed for attributes. */ + (cur->type == XML_ATTRIBUTE_NODE) ? 1 : 0) == -1) + goto internal_error; + clone->ns = ns; + } + +end_ns_reference: + + /* + * Some post-processing. + * + * Handle ID attributes. + */ + if ((clone->type == XML_ATTRIBUTE_NODE) && + (clone->parent != NULL)) { + if (xmlIsID(destDoc, clone->parent, (xmlAttrPtr) clone)) { + + xmlChar *idVal; + + idVal = xmlNodeListGetString(cur->doc, cur->children, 1); + if (idVal != NULL) { + if (xmlAddID(NULL, destDoc, idVal, (xmlAttrPtr) cur) == NULL) { + /* TODO: error message. */ + xmlFree(idVal); + goto internal_error; + } + xmlFree(idVal); + } + } + } + /* + ** + ** The following will traversing the tree ************************ + ** + * + * Walk the element's attributes before descending into child-nodes. + */ + if ((cur->type == XML_ELEMENT_NODE) && (cur->properties != NULL)) { + prevClone = NULL; + parentClone = clone; + cur = (xmlNodePtr) cur->properties; + continue; + } +into_content: + /* + * Descend into child-nodes. + */ + if (cur->children != NULL) { + if (deep || (cur->type == XML_ATTRIBUTE_NODE)) { + prevClone = NULL; + parentClone = clone; + cur = cur->children; + continue; + } + } + +leave_node: + /* + * At this point we are done with the node, its content + * and an element-nodes's attribute-nodes. + */ + if (cur == node) + break; + if ((cur->type == XML_ELEMENT_NODE) || + (cur->type == XML_XINCLUDE_START) || + (cur->type == XML_XINCLUDE_END)) { + /* + * TODO: Do we expect nsDefs on XML_XINCLUDE_START? + */ + if (XML_NSMAP_NOTEMPTY(nsMap)) { + /* + * Pop mappings. + */ + while ((nsMap->last != NULL) && + (nsMap->last->depth >= depth)) + { + XML_NSMAP_POP(nsMap, mi) + } + /* + * Unshadow. + */ + XML_NSMAP_FOREACH(nsMap, mi) { + if (mi->shadowDepth >= depth) + mi->shadowDepth = -1; + } + } + depth--; + } + if (cur->next != NULL) { + prevClone = clone; + cur = cur->next; + } else if (cur->type != XML_ATTRIBUTE_NODE) { + /* + * Set clone->last. + */ + if (clone->parent != NULL) + clone->parent->last = clone; + clone = clone->parent; + parentClone = clone->parent; + /* + * Process parent --> next; + */ + cur = cur->parent; + goto leave_node; + } else { + /* This is for attributes only. */ + clone = clone->parent; + parentClone = clone->parent; + /* + * Process parent-element --> children. + */ + cur = cur->parent; + goto into_content; + } + } + goto exit; + internal_error: + ret = -1; + +exit: + /* + * Cleanup. + */ if (nsMap != NULL) - xmlDOMWrapNSNormFreeNsMap(nsMap); - return (-1); + xmlDOMWrapNsMapFree(nsMap); + /* + * TODO: Should we try a cleanup of the cloned node in case of a + * fatal error? + */ + *resNode = resultClone; + return (ret); } /* @@ -8605,8 +9208,7 @@ xmlDOMWrapAdoptAttr(xmlDOMWrapCtxtPtr ctxt, /* TODO: User defined. */ } /* XML Namespace. */ - if ((attr->ns->prefix[0] == 'x') && (attr->ns->prefix[1] == 'm') && - (attr->ns->prefix[2] == 'l') && (attr->ns->prefix[3] == 0)) { + if (IS_STR_XML(attr->ns->prefix)) { ns = xmlTreeEnsureXMLDecl(destDoc); } else if (destParent == NULL) { /* @@ -8617,7 +9219,7 @@ xmlDOMWrapAdoptAttr(xmlDOMWrapCtxtPtr ctxt, /* * Declare on @destParent. */ - if (xmlSearchNsByHrefStrict(destDoc, destParent, attr->ns->href, + if (xmlSearchNsByNamespaceStrict(destDoc, destParent, attr->ns->href, &ns, 1) == -1) goto internal_error; if (ns == NULL) { @@ -8697,9 +9299,12 @@ internal_error: * @destParent: the optional new parent of @node in @destDoc * @options: option flags * -* Ensures that ns-references point to @destDoc: either to -* elements->nsDef entries if @destParent is given, or to -* @destDoc->oldNs otherwise. +* References of out-of scope ns-decls are remapped to point to @destDoc: +* 1) If @destParent is given, then nsDef entries on element-nodes are used +* 2) If *no* @destParent is given, then @destDoc->oldNs entries are used +* This is the case when you have an unliked node and just want to move it +* to the context of +* * If @destParent is given, it ensures that the tree is namespace * wellformed by creating additional ns-decls where needed. * Note that, since prefixes of already existent ns-decls can be @@ -8707,7 +9312,10 @@ internal_error: * values or element content. * WARNING: This function is in a experimental state. * -* Returns 0 if succeeded, -1 otherwise and on API/internal errors. +* Returns 0 if the operation succeeded, +* 1 if a node of unsupported type was given, +* 2 if a node of not yet supported type was given and +* -1 on API/internal errors. */ int xmlDOMWrapAdoptNode(xmlDOMWrapCtxtPtr ctxt, @@ -8716,7 +9324,7 @@ xmlDOMWrapAdoptNode(xmlDOMWrapCtxtPtr ctxt, xmlDocPtr destDoc, xmlNodePtr destParent, int options) -{ +{ if ((node == NULL) || (destDoc == NULL) || ((destParent != NULL) && (destParent->doc != destDoc))) return(-1); @@ -8744,6 +9352,7 @@ xmlDOMWrapAdoptNode(xmlDOMWrapCtxtPtr ctxt, case XML_COMMENT_NODE: break; case XML_DOCUMENT_FRAG_NODE: + /* TODO: Support document-fragment-nodes. */ return (2); default: return (1); @@ -8809,6 +9418,5 @@ xmlDOMWrapAdoptNode(xmlDOMWrapCtxtPtr ctxt, return (0); } - #define bottom_tree #include "elfgcchack.h" |