Ticket #868: Added functions to search XML child nodes recursively

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@2727 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjlib-util/include/pjlib-util/xml.h b/pjlib-util/include/pjlib-util/xml.h
index 5595c20..9188ae6 100644
--- a/pjlib-util/include/pjlib-util/xml.h
+++ b/pjlib-util/include/pjlib-util/xml.h
@@ -149,17 +149,18 @@
 PJ_DECL(void) pj_xml_add_attr( pj_xml_node *node, pj_xml_attr *attr );
 
 /**
- * Find first node with the specified name.
+ * Find first direct child node with the specified name.
  *
  * @param parent    Parent node.
  * @param name	    Node name to find.
  *
  * @return	    XML node found or NULL.
  */
-PJ_DECL(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name);
+PJ_DECL(pj_xml_node*) pj_xml_find_node(const pj_xml_node *parent, 
+				       const pj_str_t *name);
 
 /**
- * Find first node with the specified name.
+ * Find next direct child node with the specified name.
  *
  * @param parent    Parent node.
  * @param node	    node->next is the starting point.
@@ -167,11 +168,26 @@
  *
  * @return	    XML node found or NULL.
  */
-PJ_DECL(pj_xml_node*) pj_xml_find_next_node(pj_xml_node *parent, pj_xml_node *node,
+PJ_DECL(pj_xml_node*) pj_xml_find_next_node(const pj_xml_node *parent, 
+					    const pj_xml_node *node,
 					    const pj_str_t *name);
 
 /**
- * Find first attribute within a node with the specified name and optional value.
+ * Recursively find the first node with the specified name in the child nodes
+ * and their children.
+ *
+ * @param parent    Parent node.
+ * @param name	    Node name to find.
+ *
+ * @return	    XML node found or NULL.
+ */
+PJ_DECL(pj_xml_node*) pj_xml_find_node_rec(const pj_xml_node *parent, 
+					   const pj_str_t *name);
+
+
+/**
+ * Find first attribute within a node with the specified name and optional 
+ * value.
  *
  * @param node	    XML Node.
  * @param name	    Attribute name to find.
@@ -179,7 +195,8 @@
  *
  * @return	    XML attribute found, or NULL.
  */
-PJ_DECL(pj_xml_attr*) pj_xml_find_attr(pj_xml_node *node, const pj_str_t *name,
+PJ_DECL(pj_xml_attr*) pj_xml_find_attr(const pj_xml_node *node, 
+				       const pj_str_t *name,
 				       const pj_str_t *value);
 
 
@@ -187,15 +204,37 @@
  * Find a direct child node with the specified name and match the function.
  *
  * @param parent    Parent node.
- * @param name	    Optional name.
+ * @param name	    Optional name. If this is NULL, the name will not be
+ *		    matched.
  * @param data	    Data to be passed to matching function.
  * @param match	    Optional matching function.
  *
  * @return	    The first matched node, or NULL.
  */
-PJ_DECL(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name,
+PJ_DECL(pj_xml_node*) pj_xml_find( const pj_xml_node *parent, 
+				   const pj_str_t *name,
 				   const void *data, 
-				   pj_bool_t (*match)(pj_xml_node *, const void*));
+				   pj_bool_t (*match)(const pj_xml_node *, 
+						      const void*));
+
+
+/**
+ * Recursively find a child node with the specified name and match the 
+ * function.
+ *
+ * @param parent    Parent node.
+ * @param name	    Optional name. If this is NULL, the name will not be
+ *		    matched.
+ * @param data	    Data to be passed to matching function.
+ * @param match	    Optional matching function.
+ *
+ * @return	    The first matched node, or NULL.
+ */
+PJ_DECL(pj_xml_node*) pj_xml_find_rec(const pj_xml_node *parent, 
+				      const pj_str_t *name,
+				      const void *data, 
+				      pj_bool_t (*match)(const pj_xml_node*, 
+							 const void*));
 
 
 /**
diff --git a/pjlib-util/src/pjlib-util/xml.c b/pjlib-util/src/pjlib-util/xml.c
index e1bb43e..b7b8faa 100644
--- a/pjlib-util/src/pjlib-util/xml.c
+++ b/pjlib-util/src/pjlib-util/xml.c
@@ -340,22 +340,42 @@
     pj_list_push_back(&node->attr_head, attr);
 }
 
-PJ_DEF(pj_xml_node*) pj_xml_find_node(pj_xml_node *parent, const pj_str_t *name)
+PJ_DEF(pj_xml_node*) pj_xml_find_node(const pj_xml_node *parent, 
+				      const pj_str_t *name)
 {
-    pj_xml_node *node = parent->node_head.next;
+    const pj_xml_node *node = parent->node_head.next;
 
     PJ_CHECK_STACK();
 
     while (node != (void*)&parent->node_head) {
 	if (pj_stricmp(&node->name, name) == 0)
-	    return node;
+	    return (pj_xml_node*)node;
 	node = node->next;
     }
     return NULL;
 }
 
+PJ_DEF(pj_xml_node*) pj_xml_find_node_rec(const pj_xml_node *parent, 
+					  const pj_str_t *name)
+{
+    const pj_xml_node *node = parent->node_head.next;
 
-PJ_DEF(pj_xml_node*) pj_xml_find_next_node( pj_xml_node *parent, pj_xml_node *node,
+    PJ_CHECK_STACK();
+
+    while (node != (void*)&parent->node_head) {
+	pj_xml_node *found;
+	if (pj_stricmp(&node->name, name) == 0)
+	    return (pj_xml_node*)node;
+	found = pj_xml_find_node_rec(node, name);
+	if (found)
+	    return (pj_xml_node*)found;
+	node = node->next;
+    }
+    return NULL;
+}
+
+PJ_DEF(pj_xml_node*) pj_xml_find_next_node( const pj_xml_node *parent, 
+					    const pj_xml_node *node,
 					    const pj_str_t *name)
 {
     PJ_CHECK_STACK();
@@ -363,24 +383,25 @@
     node = node->next;
     while (node != (void*)&parent->node_head) {
 	if (pj_stricmp(&node->name, name) == 0)
-	    return node;
+	    return (pj_xml_node*)node;
 	node = node->next;
     }
     return NULL;
 }
 
 
-PJ_DEF(pj_xml_attr*) pj_xml_find_attr( pj_xml_node *node, const pj_str_t *name,
+PJ_DEF(pj_xml_attr*) pj_xml_find_attr( const pj_xml_node *node, 
+				       const pj_str_t *name,
 				       const pj_str_t *value)
 {
-    pj_xml_attr *attr = node->attr_head.next;
+    const pj_xml_attr *attr = node->attr_head.next;
     while (attr != (void*)&node->attr_head) {
 	if (pj_stricmp(&attr->name, name)==0) {
 	    if (value) {
 		if (pj_stricmp(&attr->value, value)==0)
-		    return attr;
+		    return (pj_xml_attr*)attr;
 	    } else {
-		return attr;
+		return (pj_xml_attr*)attr;
 	    }
 	}
 	attr = attr->next;
@@ -390,26 +411,73 @@
 
 
 
-PJ_DEF(pj_xml_node*) pj_xml_find( pj_xml_node *parent, const pj_str_t *name,
+PJ_DEF(pj_xml_node*) pj_xml_find( const pj_xml_node *parent, 
+				  const pj_str_t *name,
 				  const void *data, 
-				  pj_bool_t (*match)(pj_xml_node *, const void*))
+				  pj_bool_t (*match)(const pj_xml_node *, 
+						     const void*))
 {
-    pj_xml_node *head = (pj_xml_node*) &parent->node_head, *node = head->next;
+    const pj_xml_node *node = (const pj_xml_node *)parent->node_head.next;
 
-    while (node != (void*)head) {
-	if (name && pj_stricmp(&node->name, name)==0) {
-	    if (match) {
-		if (match(node, data))
-		    return node;
-	    } else {
-		return node;
+    if (!name && !match)
+	return NULL;
+
+    while (node != (const pj_xml_node*) &parent->node_head) {
+	if (name) {
+	    if (pj_stricmp(&node->name, name)!=0) {
+		node = node->next;
+		continue;
 	    }
 	}
+	if (match) {
+	    if (match(node, data))
+		return (pj_xml_node*)node;
+	} else {
+	    return (pj_xml_node*)node;
+	}
+
 	node = node->next;
     }
     return NULL;
 }
 
+PJ_DEF(pj_xml_node*) pj_xml_find_rec( const pj_xml_node *parent, 
+				      const pj_str_t *name,
+				      const void *data, 
+				      pj_bool_t (*match)(const pj_xml_node*, 
+							 const void*))
+{
+    const pj_xml_node *node = (const pj_xml_node *)parent->node_head.next;
+
+    if (!name && !match)
+	return NULL;
+
+    while (node != (const pj_xml_node*) &parent->node_head) {
+	pj_xml_node *found;
+
+	if (name) {
+	    if (pj_stricmp(&node->name, name)==0) {
+		if (match) {
+		    if (match(node, data))
+			return (pj_xml_node*)node;
+		} else {
+		    return (pj_xml_node*)node;
+		}
+	    }
+
+	} else if (match) {
+	    if (match(node, data))
+		return (pj_xml_node*)node;
+	}
+
+	found = pj_xml_find_rec(node, name, data, match);
+	if (found)
+	    return found;
+
+	node = node->next;
+    }
+    return NULL;
+}
 
 PJ_DEF(pj_xml_node*) pj_xml_clone( pj_pool_t *pool, const pj_xml_node *rhs)
 {