yin parser: FEATURE: support submodules (via include statement)

includes some refactoring and renaming since submodules are very
similar to modules and it was needed to differentiate between them.
diff --git a/src/parser/yin.c b/src/parser/yin.c
index cd71dca..205dae6 100644
--- a/src/parser/yin.c
+++ b/src/parser/yin.c
@@ -66,7 +66,7 @@
                                           struct ly_module *module,
                                           struct ly_mnode *parent)
 {
-	int i, found = 0;
+	int i, j, found = 0;
 	int prefix_len = 0;
 	const char *qname;
 	struct ly_tpdf *tpdf;
@@ -144,6 +144,55 @@
 		}
 	}
 
+	/* search in submodules */
+	for (i = 0; i < module->inc_size; i++) {
+		for (j = 0; j < module->inc[i].submodule->tpdf_size; j++) {
+			if (!strcmp(module->inc[i].submodule->tpdf[j].name, qname)) {
+				return &module->inc[i].submodule->tpdf[j];
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static struct ly_ident *find_base_ident_sub(struct ly_module *module, struct ly_ident *ident, const char *basename)
+{
+	int i;
+	struct ly_ident *base_iter;
+	struct ly_ident_der *der;
+
+	for (i = 0; i < module->ident_size; i++) {
+		if (!strcmp(basename, module->ident[i].name)) {
+			/* we are done */
+
+			if (!ident) {
+				/* just search for type, so do not modify anything, just return
+				 * the base identity pointer
+				 */
+				return &module->ident[i];
+			}
+
+			/* we are resolving identity definition, so now update structures */
+			ident->base = base_iter = &module->ident[i];
+
+			while (base_iter) {
+				for (der = base_iter->der; der && der->next; der = der->next);
+				if (der) {
+					der->next = malloc(sizeof *der);
+					der = der->next;
+				} else {
+					ident->base->der = der = malloc(sizeof *der);
+				}
+				der->next = NULL;
+				der->ident = ident;
+
+				base_iter = base_iter->base;
+			}
+			return ident->base;
+		}
+	}
+
 	return NULL;
 }
 
@@ -152,8 +201,7 @@
 	const char *name;
 	int prefix_len = 0;
 	int i, found = 0;
-	struct ly_ident *base_iter;
-	struct ly_ident_der *der;
+	struct ly_ident *result;
 
 	if (!basename) {
 		ly_verr(LY_VERR_MISS_ARG, "name", "base");
@@ -190,43 +238,23 @@
 			ly_verr(LY_VERR_UNEXP_PREFIX, basename);
 			return NULL;
 		}
-	}
-
-	/* search in the identified module */
-	/* TODO what about submodules? */
-	for (i = 0; i < module->ident_size; i++) {
-		if (!strcmp(name, module->ident[i].name)) {
-			/* we are done */
-
-			if (!ident) {
-				/* just search for type, so do not modify anything, just return
-				 * the base identity pointer
-				 */
-				return &module->ident[i];
+	} else {
+		/* search in submodules */
+		for (i = 0; i < module->inc_size; i++) {
+			result = find_base_ident_sub((struct ly_module *)module->inc[i].submodule, ident, name);
+			if (result) {
+				return result;
 			}
-
-			/* we are resolving identity definition, so now update structures */
-			ident->base = base_iter = &module->ident[i];
-
-			while (base_iter) {
-				for (der = base_iter->der; der && der->next; der = der->next);
-				if (der) {
-					der->next = malloc(sizeof *der);
-					der = der->next;
-				} else {
-					ident->base->der = der = malloc(sizeof *der);
-				}
-				der->next = NULL;
-				der->ident = ident;
-
-				base_iter = base_iter->base;
-			}
-			return ident->base;
 		}
 	}
 
-	ly_verr(LY_VERR_UNEXP_VAL, basename, ident ? "identity" : "type");
-	return NULL;
+	/* search in the identified module */
+	result = find_base_ident_sub(module, ident, name);
+	if (!result) {
+		ly_verr(LY_VERR_UNEXP_VAL, basename, ident ? "identity" : "type");
+	}
+
+	return result;
 }
 
 static int fill_yin_identity(struct ly_module *module, struct lyxml_elem *yin, struct ly_ident *ident)
@@ -506,6 +534,71 @@
 	return EXIT_SUCCESS;
 }
 
+static int fill_yin_import(struct ly_module *module, struct lyxml_elem *yin, struct ly_import *imp)
+{
+	struct lyxml_elem *child;
+	const char *value;
+
+	LY_TREE_FOR(yin->child, child) {
+		if (!strcmp(child->name, "prefix")) {
+			value = lyxml_get_attr(child, "value", NULL);
+			imp->prefix = lydict_insert(module->ctx, value, strlen(value));
+		} else if (!strcmp(child->name, "revision-date")) {
+			value = lyxml_get_attr(child, "date", NULL);
+			if (!value) {
+				ly_verr(LY_VERR_MISS_ARG, "date", "revision-date");
+				return EXIT_FAILURE;
+			}
+			memcpy(imp->rev, value, LY_REV_SIZE - 1);
+		} else {
+			ly_verr(LY_VERR_UNEXP_STMT, child->name);
+			return EXIT_FAILURE;
+		}
+	}
+
+	value = lyxml_get_attr(yin, "module", NULL);
+	imp->module = ly_ctx_get_module(module->ctx, value, imp->rev[0] ? imp->rev : NULL);
+	if (!imp->module) {
+		LY_ERR(LY_EVALID, "Importing \"%s\" module into \"%s\" failed.",
+		       value, module->name);
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
+
+static int fill_yin_include(struct ly_module *module, struct lyxml_elem *yin, struct ly_include *inc)
+{
+	struct lyxml_elem *child;
+	const char *value;
+
+	LY_TREE_FOR(yin->child, child) {
+		if (!strcmp(child->name, "revision-date")) {
+			value = lyxml_get_attr(child, "date", NULL);
+			if (!value) {
+				ly_verr(LY_VERR_MISS_ARG, "date", "revision-date");
+				return EXIT_FAILURE;
+			}
+			memcpy(inc->rev, value, LY_REV_SIZE - 1);
+		} else {
+			ly_verr(LY_VERR_UNEXP_STMT, child->name);
+			return EXIT_FAILURE;
+		}
+	}
+
+	value = lyxml_get_attr(yin, "module", NULL);
+	inc->submodule = ly_ctx_get_submodule(module, value, inc->rev[0] ? inc->rev : NULL);
+	if (!inc->submodule) {
+		LY_ERR(LY_EVALID, "Importing \"%s\" module into \"%s\" failed.",
+		       value, module->name);
+		return EXIT_FAILURE;
+	}
+
+
+	return EXIT_SUCCESS;
+}
+
+
 /*
  * Covers:
  * description, reference, status, optionaly config
@@ -1205,45 +1298,15 @@
 	return NULL;
 }
 
-struct ly_module *ly_read_yin(struct ly_ctx *ctx, const char *data)
+/* common code for yin_read_module() and yin_read_submodule() */
+static int read_sub_module(struct ly_module *module, struct lyxml_elem *yin, int submodule)
 {
-	struct lyxml_elem *yin, *node, *next, *child, root = {0};
-	struct ly_module *module = NULL, **newlist = NULL, *imp;
+	struct ly_ctx *ctx = module->ctx;
+	struct lyxml_elem *next, *node, *child, root = {0};
 	struct ly_mnode *mnode = NULL;
 	const char *value;
+	int c_imp = 0, c_rev = 0, c_tpdf = 0, c_ident = 0, c_inc = 0; /* counters */
 	int r;
-	int i;
-	/* counters */
-	int c_imp = 0, c_rev = 0, c_tpdf = 0, c_ident = 0;
-
-	yin = lyxml_read(ctx, data, 0);
-	if (!yin) {
-		return NULL;
-	}
-
-	/* check root element */
-	if (!yin->name || strcmp(yin->name, "module")) {
-		/* TODO: support submodules */
-		ly_verr(LY_VERR_UNEXP_STMT, yin->name);
-		goto error;
-	}
-
-	value = lyxml_get_attr(yin, "name", NULL);
-	if (!value) {
-		LY_ERR(LY_EVALID, "Missing \"name\" attribute of the \"module\".");
-		goto error;
-	}
-
-	module = calloc(1, sizeof *module);
-	if (!module) {
-		ly_errno = LY_EFATAL;
-		goto error;
-	}
-
-	module->ctx = ctx;
-	module->name = lydict_insert(ctx, value, strlen(value));
-
-	LY_VRB("reading module %s", module->name);
 
 	/*
 	 * in the first run, we process elements with cardinality of 1 or 0..1 and
@@ -1259,20 +1322,18 @@
 			continue;
 		}
 
-		if (!strcmp(node->name, "namespace")) {
+		if (!submodule && !strcmp(node->name, "namespace")) {
 			value = lyxml_get_attr(node, "uri", NULL);
 			if (!value) {
-				LY_ERR(LY_EVALID,
-				       "%s: Missing \"uri\" attribute in \"namespace\" element.", module->name);
+				LY_ERR(LY_EVALID, "%s: Missing \"uri\" attribute in \"namespace\" element.", module->name);
 				goto error;
 			}
 			module->ns = lydict_insert(ctx, value, strlen(value));
 			lyxml_free_elem(ctx, node);
-		} else if (!strcmp(node->name, "prefix")) {
+		} else if (!submodule && !strcmp(node->name, "prefix")) {
 			value = lyxml_get_attr(node, "value", NULL);
 			if (!value) {
-				LY_ERR(LY_EVALID,
-				       "%s: Missing \"value\" attribute in \"prefix\" element.", module->name);
+				LY_ERR(LY_EVALID, "%s: Missing \"value\" attribute in \"prefix\" element.", module->name);
 				goto error;
 			}
 			module->prefix = lydict_insert(ctx, value, strlen(value));
@@ -1285,6 +1346,8 @@
 			c_tpdf++;
 		} else if (!strcmp(node->name, "identity")) {
 			c_ident++;
+		} else if (!strcmp(node->name, "include")) {
+			c_inc++;
 
 		/* data statements */
 		} else if (!strcmp(node->name, "container") ||
@@ -1342,92 +1405,76 @@
 		}
 	}
 
-	/* check for mandatory statements */
-	if (!module->ns) {
-		ly_verr(LY_VERR_MISS_STMT2, "namespace", "module");
-		goto error;
+	if (!submodule) {
+		/* check for mandatory statements */
+		if (!module->ns) {
+			ly_verr(LY_VERR_MISS_STMT2, "namespace", "module");
+			goto error;
+		}
+		if (!module->prefix) {
+			ly_verr(LY_VERR_MISS_STMT2, "prefix", "module");
+			goto error;
+		}
 	}
-	if (!module->prefix) {
-		ly_verr(LY_VERR_MISS_STMT2, "prefix", "module");
-		goto error;
-	}
-
 
 	/* allocate arrays for elements with cardinality of 0..n */
 	if (c_imp) {
-		module->imp_size = c_imp;
 		module->imp = calloc(c_imp, sizeof *module->imp);
-		c_imp = 0;
 	}
 	if (c_rev) {
-		module->rev_size = c_rev;
 		module->rev = calloc(c_rev, sizeof *module->rev);
-		c_rev = 0;
 	}
 	if (c_tpdf) {
-		module->tpdf_size = c_tpdf;
 		module->tpdf = calloc(c_tpdf, sizeof *module->tpdf);
-		c_tpdf = 0;
 	}
 	if (c_ident) {
-		module->ident_size = c_ident;
 		module->ident = calloc(c_ident, sizeof *module->ident);
-		c_ident = 0;
+	}
+	if (c_inc) {
+		module->inc = calloc(c_inc, sizeof *module->inc);
 	}
 
 	/* middle part - process nodes with cardinality of 0..n except the data nodes */
 	LY_TREE_FOR_SAFE(yin->child, next, node) {
 		if (!strcmp(node->name, "import")) {
-			LY_TREE_FOR(node->child, child) {
-				if (!strcmp(child->name, "prefix")) {
-					value = lyxml_get_attr(child, "value", NULL);
-					module->imp[c_imp].prefix = lydict_insert(ctx, value, strlen(value));
-				} else if (!strcmp(child->name, "revision-date")) {
-					value = lyxml_get_attr(child, "date", NULL);
-					if (!value) {
-						ly_verr(LY_VERR_MISS_ARG, "date", "revision-date");
-						goto error;
-					}
-					memcpy(module->imp[c_imp].rev, value, LY_REV_SIZE - 1);
-				}
-			}
-			value = lyxml_get_attr(node, "module", NULL);
-			imp = ly_ctx_get_model(ctx, value, module->imp[c_imp].rev[0] ? module->imp[c_imp].rev : NULL);
-			if (!imp) {
-				LY_ERR(LY_EVALID, "Importing \"%s\" module into \"%s\" failed.",
-				       value, module->name);
+			r = fill_yin_import(module, node, &module->imp[module->imp_size]);
+			module->imp_size++;
+
+			if (r) {
 				goto error;
 			}
-			module->imp[c_imp].module = imp;
-			c_imp++;
+		} else if (!strcmp(node->name, "include")) {
+			r = fill_yin_include(module, node, &module->inc[module->inc_size]);
+			module->inc_size++;
+
+			if (r) {
+				goto error;
+			}
 		} else if (!strcmp(node->name, "revision")) {
-			memcpy(module->rev[c_rev].date,
+			memcpy(module->rev[module->rev_size].date,
 			       lyxml_get_attr(node, "date", NULL), LY_REV_SIZE - 1);
 			LY_TREE_FOR(node->child, child) {
 				if (!strcmp(child->name, "description")) {
-					module->rev[c_rev].dsc = read_yin_text(ctx, child, "description");
+					module->rev[module->rev_size].dsc = read_yin_text(ctx, child, "description");
 				} else if (!strcmp(child->name, "reference")) {
-					module->rev[c_rev].ref = read_yin_text(ctx, child, "reference");
+					module->rev[module->rev_size].ref = read_yin_text(ctx, child, "reference");
 				}
 			}
-			c_rev++;
+			module->rev_size++;
 		} else if (!strcmp(node->name, "typedef")) {
-			r = fill_yin_typedef(module, NULL, node, &module->tpdf[c_tpdf]);
-			c_tpdf++;
+			r = fill_yin_typedef(module, NULL, node, &module->tpdf[module->tpdf_size]);
+			module->tpdf_size++;
 
 			if (r) {
-				module->tpdf_size = c_tpdf;
 				goto error;
 			}
 		} else if (!strcmp(node->name, "identity")) {
-			r = fill_yin_identity(module, node, &module->ident[c_ident]);
-			c_ident++;
+			r = fill_yin_identity(module, node, &module->ident[module->ident_size]);
+			module->ident_size++;
 
 			if (r) {
-				module->ident_size = c_ident;
 				goto error;
 			}
-
 		}
 
 		lyxml_free_elem(ctx, node);
@@ -1470,6 +1517,108 @@
 		}
 	}
 
+	return EXIT_SUCCESS;
+
+error:
+	/* cleanup */
+	while (root.child) {
+		lyxml_free_elem(module->ctx, root.child);
+	}
+
+	return EXIT_FAILURE;
+}
+
+struct ly_submodule *yin_read_submodule(struct ly_module *module, const char *data)
+{
+	struct lyxml_elem *yin;
+	struct ly_submodule *submodule;
+	const char *value;
+
+
+	yin = lyxml_read(module->ctx, data, 0);
+	if (!yin) {
+		return NULL;
+	}
+
+	/* check root element */
+	if (!yin->name || strcmp(yin->name, "submodule")) {
+		ly_verr(LY_VERR_UNEXP_STMT, yin->name);
+		goto error;
+	}
+
+	value = lyxml_get_attr(yin, "name", NULL);
+	if (!value) {
+		ly_verr(LY_VERR_MISS_ARG, "name", "submodule");
+		goto error;
+	}
+
+	submodule = calloc(1, sizeof *submodule);
+	if (!submodule) {
+		ly_errno = LY_EFATAL;
+		goto error;
+	}
+
+	submodule->ctx = module->ctx;
+	submodule->name = lydict_insert(submodule->ctx, value, strlen(value));
+
+	LY_VRB("reading submodule %s", submodule->name);
+	if (read_sub_module((struct ly_module *)submodule, yin, 1)) {
+		goto error;
+	}
+
+	/* cleanup */
+	lyxml_free_elem(module->ctx, yin);
+
+	LY_VRB("submodule %s successfully parsed", submodule->name);
+
+	return submodule;
+
+error:
+	/* cleanup */
+	lyxml_free_elem(module->ctx, yin);
+	ly_submodule_free(submodule);
+
+	return NULL;
+}
+
+struct ly_module *yin_read_module(struct ly_ctx *ctx, const char *data)
+{
+	struct lyxml_elem *yin;
+	struct ly_module *module = NULL, **newlist = NULL;
+	const char *value;
+	int i;
+
+	yin = lyxml_read(ctx, data, 0);
+	if (!yin) {
+		return NULL;
+	}
+
+	/* check root element */
+	if (!yin->name || strcmp(yin->name, "module")) {
+		ly_verr(LY_VERR_UNEXP_STMT, yin->name);
+		goto error;
+	}
+
+	value = lyxml_get_attr(yin, "name", NULL);
+	if (!value) {
+		ly_verr(LY_VERR_MISS_ARG, "name", "module");
+		goto error;
+	}
+
+	module = calloc(1, sizeof *module);
+	if (!module) {
+		ly_errno = LY_EFATAL;
+		goto error;
+	}
+
+	module->ctx = ctx;
+	module->name = lydict_insert(ctx, value, strlen(value));
+
+	LY_VRB("reading module %s", module->name);
+	if (read_sub_module(module, yin, 0)) {
+		goto error;
+	}
+
 	/* add to the context's list of modules */
 	if (ctx->models.used == ctx->models.size) {
 		newlist = realloc(ctx->models.list, ctx->models.size * 2);
@@ -1496,11 +1645,8 @@
 
 error:
 	/* cleanup */
-	while (root.child) {
-		lyxml_free_elem(module->ctx, root.child);
-	}
 	lyxml_free_elem(ctx, yin);
-	ly_model_free(module);
+	ly_module_free(module);
 
 	return NULL;
 }