#define IS_STR_XML(str) ((str != NULL) && (str[0] == 'x') && \ (str[1] == 'm') && (str[2] == 'l') && (str[3] == 0)) #define IS_STR_ID(str) ((str != NULL) && (str[0] == 'i') && \ (str[1] == 'd') && (str[2] == 0)) #define DOC_DICT_STRDUP(doc, dest, str) \ if ((doc != NULL) && (doc->dict != NULL)) \ dest = (xmlChar *) xmlDictLookup(doc->dict, str, -1); \ else \ dest = xmlStrdup(str); #define HAS_DOC_DTD(doc) ((doc != NULL) && \ ((doc->intSubset != NULL) || (doc->extSubset != NULL))) /* * xmlGetNoNsPropNode: * * NOTE that we don't have a mechanism to distinguish between * DOM level 1 and level 2 attrs. */ static xmlAttrPtr xmlGetNoNsPropNode(xmlNodePtr node, const xmlChar *name) { if ((node == NULL) || (name == NULL) || (node->properties == NULL)) { return(NULL); } else { xmlAttrPtr cur = node->properties; do { if ((cur->ns = NULL) && ((cur->name == name) || xmlStrEqual(cur->name, name))) { return(cur); } cur = cur->next; } while (cur != NULL); } return(NULL); } static xmlAttrPtr xmlGetNsPropNode(xmlNodePtr node, const xmlChar *name, const xmlChar *namespaceName) { if ((node == NULL) || (name == NULL) || (node->properties == NULL)) { return(NULL); } else { xmlAttrPtr cur = node->properties; do { if ((cur->ns) && ((cur->name == name) || xmlStrEqual(cur->name, name)) && ((cur->ns->href == namespaceName) || xmlStrEqual(cur->ns->href, namespaceName))) { return(cur); } cur = cur->next; } while (cur != NULL); } return(NULL); } static xmlAttrPtr xmlTreeNewPropRaw(xmlDocPtr doc, const xmlChar *name, xmlNsPtr ns) { xmlAttrPtr res; res = (xmlAttrPtr) xmlMalloc(sizeof(xmlAttr)); if (res == NULL) { xmlTreeErrMemory("building attribute"); return (NULL); } memset(res, 0, sizeof(xmlAttr)); res->type = XML_ATTRIBUTE_NODE; DOC_DICT_STRDUP(doc, res->name, name); res->ns = ns; return(res); } static int xmlDOMWrapRegisterIfId(xmlAttrPtr attr, int isId, int isIdSchemaAware) { xmlNodePtr cur; if (attr == NULL) return(-1); if ((attr->doc == NULL) || (attr->parent == NULL) || (attr->children == NULL)) return(0); /* * We want the attr to be attached in the doc's tree. */ cur = attr->parent; while (cur->parent != NULL) cur = cur->parent; if ((cur->type != XML_DOCUMENT_NODE) && #ifdef LIBXML_DOCB_ENABLED (cur->type != XML_DOCB_DOCUMENT_NODE) && #endif (cur->type != XML_HTML_DOCUMENT_NODE)) return(0); /* Paranoid */ if (attr->doc != (xmlDocPtr) cur) return(-1); /* * We want to register IDs if... */ if (attr->atype == XML_ATTRIBUTE_ID) { /* * Preserved IDness. */ goto registerId; } else if (isId) { /* * "user-determined ID attribute": Register the ID value. * xmlAddID() sets: attr->atype = XML_ATTRIBUTE_ID; */ goto registerId; } else if ((attr->ns != NULL) && IS_STR_ID(attr->name) && IS_STR_XML(attr->ns->prefix)) { /* * xml:id. * TODO: Not sure about this one: does it really automatically * create an ID entry? Or do we still have to use * Element.setIdAttributeNode() even for xml:id with DOM? */ goto registerId; } else if (isIdSchemaAware) { /* * "DTD-determined ID attribute". */ if (HAS_DOC_DTD(attr->doc) && (xmlIsID(attr->doc, attr->parent, attr) == 1)) goto registerId; /* * REVISIT: "schema-determined ID attribute". */ } return(0); registerId: { const xmlChar *value; if ((attr->children == attr->last) && ((attr->children->type == XML_TEXT_NODE) || (attr->children->type == XML_CDATA_SECTION_NODE))) { /* * The most common case: only 1 text-node; no need * for duplicating the content. */ value = attr->children->content; if (value == NULL) return(0); if (xmlAddID(NULL, attr->doc, value, attr) == NULL) return(-1); } else { xmlIDPtr id; value = xmlNodeListGetString(attr->doc, attr->children, 1); if (value != NULL) { id = xmlAddID(NULL, attr->doc, value, attr); xmlFree(id); if (id == NULL) return(-1); } } } return(0); } /* * xmlDOMWrapSetAttrNode: * * @ctxt: The DOM wrapper context (optional) * @doc: The document-node * @ns: The namespace of the attribute * @name: The name of the attribute * @value: The value of the attribute (optional) * @resAttr: The created attribute, if any * @options: Options * * Sets an attribute and its value DOM-wise. * * Returns 0 if succeeded, -1 on internal or API errors. */ static int xmlDOMWrapSetAttrNode(xmlDOMWrapCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr owner, xmlAttrPtr attr, xmlAttrPtr *removedAttr, int options, /* Those are just temporary explicit options to ease the design */ int registerId, int isId, int isIdSchemaAware) { if ((doc == NULL) || (owner == NULL) || (removedAttr == NULL)) return(-1); /* * TODO: Shall we expect the attr to be unliked already? * What about the doc, need the correct doc to be set * on @attr already? */ if (attr->parent != NULL) return(-1); *removedAttr = NULL; if (owner->properties != NULL) { xmlAttrPtr oldattr; /* * Eval if there already exists such an attr. */ if (attr->ns == NULL) oldattr = xmlGetNoNsPropNode(owner, attr->name); else { oldattr = xmlGetNsPropNode(owner, attr->name, attr->ns->href); if (oldattr != NULL) { /* * We need to remove the old attribute. Play save and return * this attr even if the removal failed. */ *removedAttr = oldattr; if (xmlDOMWrapRemoveNode(ctxt, doc, (xmlNodePtr) oldattr, 0) == -1) goto internal_error; } } } if (registerId && (attr->children != NULL)) { /* * Register IDs if wanted. */ if (registerId) { if (xmlDOMWrapRegisterIfId(attr, isId, isIdSchemaAware) == -1) goto internal_error; } } /* * Attach the attribute to the parent elem. */ attr->parent = owner; if (owner->properties == NULL) { owner->properties = attr; } else { xmlAttrPtr prev = owner->properties; while (prev->next != NULL) prev = prev->next; prev->next = attr; attr->prev = prev; } return(0); internal_error: return(-1); } /* * xmlDOMWrapSetAttr: * * @ctxt: The DOM wrapper context (optional) * @doc: The document-node * @ns: The namespace of the attribute * @name: The name of the attribute * @value: The value of the attribute (optional) * @resAttr: The created attribute, if any * @options: Options * * Sets an attribute and its value DOM-wise. * * Returns 0 if succeeded, -1 on internal or API errors. */ static int xmlDOMWrapSetAttr(xmlDOMWrapCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr owner, xmlNsPtr ns, const xmlChar *name, const xmlChar *value, xmlAttrPtr *resAttr, int options, /* Those are just temporary explicit options to ease the design */ int registerId, int isId, int preserveId, int isIdSchemaAware) { xmlAttrPtr attr = NULL; int ret = 0, created = 0; if ((doc == NULL) || (owner == NULL) || (resAttr == NULL)) return(-1); *resAttr = NULL; if (owner->properties != NULL) { /* * Eval if there already exists such an attr. */ if (ns == NULL) attr = xmlGetNoNsPropNode(owner, name); else { attr = xmlGetNsPropNode(owner, name, ns->href); /* * Changes the prefix if different. */ attr->ns = ns; } } if (attr == NULL) { /* * Create a new attribute-node. */ attr = xmlTreeNewPropRaw(doc, name, ns); if (attr == NULL) goto internal_error; attr->parent = owner; created = 1; } else if (attr->children != NULL) { /* * Free the old content. */ if ((doc != NULL) && (attr->atype == XML_ATTRIBUTE_ID)) { if (xmlRemoveID(doc, attr) == -1) goto internal_error; /* * Preserve IDness if we have a value. */ if (value != NULL) attr->atype = XML_ATTRIBUTE_ID; } xmlFreeNodeList(attr->children); attr->children = NULL; attr->last = NULL; } /* * Assign new value. */ if (value != NULL) { /* * DOM: * "This value is a simple string; it is not parsed as it is * being set. So any markup (such as syntax to be recognized * as an entity reference) is treated as literal text, and * needs to be appropriately escaped by the implementation * when it is written out" * * TODO: Use dict for the value? */ attr->children = xmlNewDocText(doc, value); if (attr->children == NULL) goto internal_error; /* * Register IDs if wanted. */ if (registerId) { if (xmlDOMWrapRegisterIfId(attr, isId, isIdSchemaAware) == -1) goto internal_error; } } /* * Attach the attribute to the parent elem. */ if (owner->properties == NULL) { owner->properties = attr; } else { xmlAttrPtr prev = owner->properties; while (prev->next != NULL) prev = prev->next; prev->next = attr; attr->prev = prev; } if ((created) && (__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue((xmlNodePtr) attr); *resAttr = attr; goto exit; internal_error: ret = -1; exit: /* * TODO: Think about cleanup on errors. */ return(ret); }