diff --git a/src/build/common/xml.c b/src/build/common/xml.c
index b5ad1198e..56b239b6e 100644
--- a/src/build/common/xml.c
+++ b/src/build/common/xml.c
@@ -4,6 +4,31 @@ XML Handler Extensions
// Include core module
#include "common/type/xml.c"
+#include "build/common/xml.h"
+
+/**********************************************************************************************************************************/
+XmlDocument *
+xmlDocumentNewParam(const String *const rootNode, const XmlDocumentNewParam param)
+{
+ FUNCTION_TEST_BEGIN();
+ FUNCTION_TEST_PARAM(STRING, rootNode);
+ FUNCTION_TEST_PARAM(STRING, param.dtdName);
+ FUNCTION_TEST_PARAM(STRING, param.dtdFile);
+ FUNCTION_TEST_END();
+
+ ASSERT(rootNode != NULL);
+
+ XmlDocument *const result = xmlDocumentNew(rootNode);
+
+ if (param.dtdName != NULL)
+ {
+ ASSERT(param.dtdFile != NULL);
+ xmlCreateIntSubset(result->xml, (unsigned char *)strZ(param.dtdName), NULL, (unsigned char *)strZ(param.dtdFile));
+ }
+
+ FUNCTION_TEST_RETURN(XML_DOCUMENT, result);
+}
+
/**********************************************************************************************************************************/
String *
xmlNodeAttribute(const XmlNode *this, const String *name)
@@ -27,3 +52,74 @@ xmlNodeAttribute(const XmlNode *this, const String *name)
FUNCTION_TEST_RETURN(STRING, result);
}
+
+/**********************************************************************************************************************************/
+void
+xmlNodeAttributeSet(XmlNode *const this, const String *const name, const String *const value)
+{
+ FUNCTION_TEST_BEGIN();
+ FUNCTION_TEST_PARAM(XML_NODE, this);
+ FUNCTION_TEST_PARAM(STRING, name);
+ FUNCTION_TEST_PARAM(STRING, value);
+ FUNCTION_TEST_END();
+
+ ASSERT(this != NULL);
+ ASSERT(name != NULL);
+ ASSERT(value != NULL);
+
+ xmlSetProp(this->node, (unsigned char *)strZ(name), (unsigned char *)strZ(value));
+
+ FUNCTION_TEST_RETURN_VOID();
+}
+
+/**********************************************************************************************************************************/
+void
+xmlNodeChildAdd(XmlNode *const this, const XmlNode *const child)
+{
+ FUNCTION_TEST_BEGIN();
+ FUNCTION_TEST_PARAM(XML_NODE, this);
+ FUNCTION_TEST_PARAM(XML_NODE, child);
+ FUNCTION_TEST_END();
+
+ ASSERT(this != NULL);
+ ASSERT(child != NULL);
+
+ MEM_CONTEXT_TEMP_BEGIN()
+ {
+ for (xmlNodePtr currentNodeRaw = child->node->children; currentNodeRaw != NULL; currentNodeRaw = currentNodeRaw->next)
+ {
+ // Skip comments
+ if (currentNodeRaw->type == XML_COMMENT_NODE)
+ continue;
+
+ // Copy all child nodes (only node and text types are copied)
+ XmlNode *const currentNode = xmlNodeNew(currentNodeRaw);
+
+ if (currentNode->node->type == XML_ELEMENT_NODE)
+ {
+ XmlNode *const node = xmlNodeAdd(this, STR((char *)currentNode->node->name));
+
+ // Copy node attributes
+ for (xmlAttrPtr currentAttr = currentNode->node->properties; currentAttr != NULL; currentAttr = currentAttr->next)
+ {
+ xmlNodeAttributeSet(
+ node, STR((char *)currentAttr->name), xmlNodeAttribute(currentNode, STR((char *)currentAttr->name)));
+ }
+
+ // Recurse to copy child nodes
+ xmlNodeChildAdd(node, currentNode);
+ }
+ else
+ {
+ CHECK_FMT(
+ AssertError, currentNode->node->type == XML_TEXT_NODE, "unknown type %u in node '%s'", currentNode->node->type,
+ (char *)currentNode->node->name);
+
+ xmlNodeContentSet(this, xmlNodeContent(currentNode));
+ }
+ }
+ }
+ MEM_CONTEXT_TEMP_END();
+
+ FUNCTION_TEST_RETURN_VOID();
+}
diff --git a/src/build/common/xml.h b/src/build/common/xml.h
index 6da781333..4ed66da23 100644
--- a/src/build/common/xml.h
+++ b/src/build/common/xml.h
@@ -6,10 +6,32 @@ XML Handler Extensions
#include "common/type/xml.h"
+/***********************************************************************************************************************************
+Constructors
+***********************************************************************************************************************************/
+typedef struct XmlDocumentNewParam
+{
+ VAR_PARAM_HEADER;
+ const String *dtdName; // DTD name, if any
+ const String *dtdFile; // DTD file (must be set if dtdName is set)
+} XmlDocumentNewParam;
+
+#define xmlDocumentNewP(rootNode, ...) \
+ xmlDocumentNewParam(rootNode, (XmlDocumentNewParam){VAR_PARAM_INIT, __VA_ARGS__})
+
+XmlDocument *xmlDocumentNewParam(const String *rootNode, XmlDocumentNewParam param);
+
/***********************************************************************************************************************************
Node Getters/Setters
***********************************************************************************************************************************/
-// Node attribute
+// Get/set node attribute
String *xmlNodeAttribute(const XmlNode *this, const String *name);
+void xmlNodeAttributeSet(XmlNode *this, const String *name, const String *value);
+
+/***********************************************************************************************************************************
+Functions
+***********************************************************************************************************************************/
+// Add all child nodes to another node
+void xmlNodeChildAdd(XmlNode *this, const XmlNode *child);
#endif
diff --git a/test/src/module/common/typeXmlTest.c b/test/src/module/common/typeXmlTest.c
index 679aed1bc..adc29e37d 100644
--- a/test/src/module/common/typeXmlTest.c
+++ b/test/src/module/common/typeXmlTest.c
@@ -84,7 +84,7 @@ testRun(void)
// Create an empty document, add data to it, and output xml
// -------------------------------------------------------------------------------------------------------------------------
- TEST_ASSIGN(xmlDocument, xmlDocumentNew(STRDEF("CompleteMultipartUpload")), "new xml with root node");
+ TEST_ASSIGN(xmlDocument, xmlDocumentNewP(STRDEF("CompleteMultipartUpload")), "new xml with root node");
XmlNode *partNode = NULL;
TEST_ASSIGN(partNode, xmlNodeAdd(xmlDocumentRoot(xmlDocument), STRDEF("Part")), "create part node 1");
@@ -103,6 +103,38 @@ testRun(void)
"2E2"
"\n",
"get xml");
+
+ // -------------------------------------------------------------------------------------------------------------------------
+ TEST_TITLE("copy xml between documents");
+
+ XmlDocument *xmlDocument2 = NULL;
+
+ TEST_ASSIGN(
+ xmlDocument, xmlDocumentNewP(STRDEF("doc1"), .dtdName = STRDEF("doc"), .dtdFile = STRDEF("doc.dtd")),
+ "new xml with dtd");
+ TEST_ASSIGN(
+ xmlDocument2,
+ xmlDocumentNewBuf(
+ BUFSTRDEF(
+ "\n"
+ "\n"
+ " \n"
+ " text55\n"
+ " name55\n"
+ "")),
+ "valid xml");
+
+ TEST_RESULT_VOID(xmlNodeChildAdd(xmlDocumentRoot(xmlDocument), xmlDocumentRoot(xmlDocument2)), "copy xml");
+ TEST_RESULT_STR_Z(
+ strNewBuf(xmlDocumentBuf(xmlDocument)),
+ "\n"
+ "\n"
+ "\n"
+ " \n"
+ " text55\n"
+ " name55\n"
+ "\n",
+ "get xml");
}
FUNCTION_HARNESS_RETURN_VOID();