yin parser: initial version

Many TODOs, just part of functionality, does not validate input file,
just tries to parse it.
diff --git a/src/xml.c b/src/xml.c
index 80441de..9bf44e9 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -29,6 +29,7 @@
 
 #include "common.h"
 #include "dict.h"
+#include "tree.h"
 #include "xml.h"
 
 /*
@@ -57,7 +58,7 @@
 
 #define ign_xmlws(p) while(is_xmlws(*p)) {p++;}
 
-API int lyxml_unlink_attr(struct lyxml_attr *attr)
+int lyxml_unlink_attr(struct lyxml_attr *attr)
 {
 	struct lyxml_attr *a;
 
@@ -95,7 +96,7 @@
 	return EXIT_SUCCESS;
 }
 
-API int lyxml_unlink_elem(struct lyxml_elem *elem)
+int lyxml_unlink_elem(struct lyxml_elem *elem)
 {
 	struct lyxml_elem *e;
 
@@ -105,7 +106,7 @@
 	}
 
 	if (!elem->parent) {
-		return EXIT_SUCCESS;
+		goto siblings;
 	}
 
 	e = elem->parent->child;
@@ -113,47 +114,58 @@
 		LY_ERR(LY_EINVAL, "Broken structure (%s).", __func__);
 		return EXIT_FAILURE;
 	} else if (e == elem) {
-		/* child element of parent is going to be the next element after the
-		 * one being unlinked
-		 */
-		if (e == e->next) {
-			elem->parent->child = NULL;
-		} else {
-			elem->parent->child = e->next;
-		}
+		/* we unlink the first child */
+		/* update the parent's link */
+		elem->parent->child = e->next;
 	}
 
 	/* remove elem from ring list of sibling elements */
-	while (e != elem) {
+	while (e && e != elem) {
 		e = e->next;
 	}
 	if (!e) {
 		LY_ERR(LY_EINVAL, "Broken structure (%s).", __func__);
 		return EXIT_FAILURE;
 	}
-	e->prev->next = e->next;
-	e->next->prev = e->prev;
 
+siblings:
+	if (elem == elem->prev) {
+		/* there are no more siblings */
+		goto end;
+	}
+
+	if (elem->next) {
+		elem->next->prev = elem->prev;
+	} else {
+		/* unlinking the last child -> update the first's prev pointer */
+		elem->parent->child->prev = elem->prev;
+	}
+	if (elem->prev && elem->prev->next) {
+		elem->prev->next = elem->next;
+	}
+
+end:
 	/* clean up the unlinked element */
-	e->next = e;
-	e->prev = e;
+	elem->next = NULL;
+	elem->prev = elem;
+	elem->parent = NULL;
 
 	return EXIT_SUCCESS;
 }
 
-API void lyxml_free_attr(struct lyxml_attr *attr)
+void lyxml_free_attr(struct ly_ctx *ctx, struct lyxml_attr *attr)
 {
 	if (!attr) {
 		return;
 	}
 
 	lyxml_unlink_attr(attr);
-	lydict_remove(attr->name);
-	lydict_remove(attr->value);
+	lydict_remove(ctx, attr->name);
+	lydict_remove(ctx, attr->value);
 	free(attr);
 }
 
-API void lyxml_free_attrs(struct lyxml_elem *elem)
+void lyxml_free_attrs(struct ly_ctx *ctx, struct lyxml_elem *elem)
 {
 	struct lyxml_attr *a, *next;
 	if (!elem || !elem->attr) {
@@ -164,15 +176,15 @@
 	do {
 		next = a->next;
 
-		lydict_remove(a->name);
-		lydict_remove(a->value);
+		lydict_remove(ctx, a->name);
+		lydict_remove(ctx, a->value);
 		free(a);
 
 		a = next;
 	} while (a);
 }
 
-static void lyxml_free_elem_(struct lyxml_elem *elem)
+static void lyxml_free_elem_(struct ly_ctx *ctx, struct lyxml_elem *elem)
 {
 	struct lyxml_elem *e, *next;
 
@@ -180,32 +192,26 @@
 		return;
 	}
 
-	lyxml_free_attrs(elem);
-	if (elem->child) {
-		e = elem->child;
-		e->prev->next = NULL;
-		do {
-			next = e->next;
-			lyxml_free_elem_(e);
-			e = next;
-		} while (e);
+	lyxml_free_attrs(ctx, elem);
+	LY_TREE_FOR_SAFE(elem->child, next, e) {
+		lyxml_free_elem_(ctx, e);
 	}
-	lydict_remove(elem->name);
-	lydict_remove(elem->content);
+	lydict_remove(ctx, elem->name);
+	lydict_remove(ctx, elem->content);
 	free(elem);
 }
 
-API void lyxml_free_elem(struct lyxml_elem *elem)
+void lyxml_free_elem(struct ly_ctx *ctx, struct lyxml_elem *elem)
 {
 	if (!elem) {
 		return;
 	}
 
 	lyxml_unlink_elem(elem);
-	lyxml_free_elem_(elem);
+	lyxml_free_elem_(ctx, elem);
 }
 
-API int lyxml_add_attr(struct lyxml_elem *parent, struct lyxml_attr *attr)
+int lyxml_add_attr(struct lyxml_elem *parent, struct lyxml_attr *attr)
 {
 	struct lyxml_attr *a;
 
@@ -231,7 +237,33 @@
 	return EXIT_SUCCESS;
 }
 
-API int lyxml_add_child(struct lyxml_elem *parent, struct lyxml_elem *elem)
+const char *lyxml_get_attr(struct lyxml_elem *elem, const char *name,
+                               const char *ns)
+{
+	struct lyxml_attr *a;
+
+	if (!elem || !name) {
+		LY_ERR(LY_EINVAL, NULL);
+		return NULL;
+	}
+
+	for (a = elem->attr; a; a = a->next) {
+		if (a->type != LYXML_ATTR_STD) {
+			continue;
+		}
+
+		if (!strcmp(name, a->name)) {
+			if ((!ns && !a->ns)
+					|| (ns && a->ns && !strcmp(ns, a->ns->value))) {
+				return a->value;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+int lyxml_add_child(struct lyxml_elem *parent, struct lyxml_elem *elem)
 {
 	struct lyxml_elem *e;
 
@@ -250,13 +282,13 @@
 	if (parent->child) {
 		e = parent->child;
 		elem->prev = e->prev;
-		elem->next = e;
+		elem->next = NULL;
 		elem->prev->next = elem;
-		elem->next->prev = elem;
+		e->prev = elem;
 	} else {
 		parent->child = elem;
-		elem->next = elem;
 		elem->prev = elem;
+		elem->next = NULL;
 	}
 
 	return EXIT_SUCCESS;
@@ -620,7 +652,8 @@
 	return get_ns(elem->parent, prefix);
 }
 
-static struct lyxml_attr *parse_attr(const char *data, unsigned int *len, struct lyxml_elem *elem)
+static struct lyxml_attr *parse_attr(struct ly_ctx *ctx, const char *data,
+		                             unsigned int *len, struct lyxml_elem *elem)
 {
 	const char *c = data, *start, *delim;
 	char prefix[32];
@@ -672,7 +705,7 @@
 
 	/* store the name */
 	size = c - start;
-	attr->name = lydict_insert(start, size);
+	attr->name = lydict_insert(ctx, start, size);
 
 
 equal:
@@ -691,7 +724,7 @@
 		goto error;
 	}
 	delim = c;
-	attr->value = lydict_insert_zc(parse_text(++c, *delim, &size));
+	attr->value = lydict_insert_zc(ctx, parse_text(++c, *delim, &size));
 	if (ly_errno) {
 		goto error;
 	}
@@ -700,11 +733,13 @@
 	return attr;
 
 error:
-	lyxml_free_attr(attr);
+	lyxml_free_attr(ctx, attr);
 	return NULL;
 }
 
-static struct lyxml_elem *parse_elem(const char *data, unsigned int *len, struct lyxml_elem *parent)
+static struct lyxml_elem *parse_elem(struct ly_ctx *ctx, const char *data,
+		                             unsigned int *len,
+		                             struct lyxml_elem *parent)
 {
 	const char *c = data, *start, *e;
 	const char *lws; /* leading white space for handling mixed content */
@@ -758,14 +793,14 @@
 
 	/* allocate element structure */
 	elem = calloc(1, sizeof *elem);
-	elem->next = elem;
+	elem->next = NULL;
 	elem->prev = elem;
 	if (parent) {
 		lyxml_add_child(parent, elem);
 	}
 
 	/* store the name into the element structure */
-	elem->name = lydict_insert(c, e - c);
+	elem->name = lydict_insert(ctx, c, e - c);
 	c = e;
 
 process:
@@ -889,7 +924,7 @@
 					lyxml_add_child(elem, child);
 					elem->flags |= LYXML_ELEM_MIXED;
 				}
-				child = parse_elem(c, &size, elem);
+				child = parse_elem(ctx, c, &size, elem);
 				if (!child) {
 					LY_ERR(LY_EWELLFORM, "Unexpected end of input data.");
 					goto error;
@@ -906,7 +941,7 @@
 					c = lws;
 					lws = NULL;
 				}
-				elem->content = lydict_insert_zc(parse_text(c, '<', &size));
+				elem->content = lydict_insert_zc(ctx, parse_text(c, '<', &size));
 				if (ly_errno) {
 					goto error;
 				}
@@ -924,7 +959,7 @@
 		}
 	} else {
 		/* process attribute */
-		attr = parse_attr(c, &size, elem);
+		attr = parse_attr(ctx, c, &size, elem);
 		if (!attr) {
 			LY_ERR(LY_EWELLFORM, "Unexpected end of input data.");
 			goto error;
@@ -967,18 +1002,19 @@
 	return elem;
 
 error:
-	lyxml_free_elem(elem);
+	lyxml_free_elem(ctx, elem);
 
 	return NULL;
 }
 
-API struct lyxml_elem *lyxml_read(const char *data, int UNUSED(options))
+struct lyxml_elem *lyxml_read(struct ly_ctx *ctx, const char *data,
+                              int UNUSED(options))
 {
 	const char *c = data;
 	unsigned int len;
 	struct lyxml_elem *root = NULL;
 
-	if (!data) {
+	if (!data || !ctx) {
 		ly_errno = LY_EINVAL;
 		return NULL;
 	}
@@ -1017,7 +1053,7 @@
 		}
 	}
 
-	root = parse_elem(c, &len, NULL);
+	root = parse_elem(ctx, c, &len, NULL);
 	if (!root) {
 		return NULL;
 	}
@@ -1034,9 +1070,10 @@
 	return root;
 }
 
-API struct lyxml_elem *lyxml_read_fd(int fd, int UNUSED(options))
+struct lyxml_elem *lyxml_read_fd(struct ly_ctx *ctx, int fd,
+                                     int UNUSED(options))
 {
-	if (fd == -1) {
+	if (fd == -1 || !ctx) {
 		ly_errno = LY_EINVAL;
 		return NULL;
 	}
@@ -1044,10 +1081,10 @@
 	return NULL;
 }
 
-API struct lyxml_elem *lyxml_read_file(const char *filename,
-		                               int UNUSED(options))
+struct lyxml_elem *lyxml_read_file(struct ly_ctx *ctx, const char *filename,
+                                       int UNUSED(options))
 {
-	if (!filename) {
+	if (!filename || !ctx) {
 		LY_ERR(LY_EINVAL, NULL);
 		return NULL;
 	}
@@ -1152,11 +1189,9 @@
 	}
 
 	/* go recursively */
-	child = e->child;
-	do {
+	LY_TREE_FOR(e->child, child) {
 		size += dump_elem(f, child, level + 1);
-		child = child->next;
-	} while (child != e->child);
+	}
 
 	/* closing tag */
 	if (e->ns && e->ns->prefix) {
@@ -1169,7 +1204,7 @@
 	return size;
 }
 
-API int lyxml_dump(FILE *stream, struct lyxml_elem *elem, int UNUSED(options))
+int lyxml_dump(FILE *stream, struct lyxml_elem *elem, int UNUSED(options))
 {
 	if (!elem) {
 		return 0;