identityref UPDATE support optional module prefix
Fixes #420
diff --git a/src/plugins_types.c b/src/plugins_types.c
index cb4b896..7f62be8 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -224,24 +224,52 @@
}
/**
- * @brief Simply return module local prefix. Also, store the module in a set.
+ * @brief Get prefix for XML print.
+ *
+ * @param[in] mod Module whose prefix to get.
+ * @param[in,out] prefix_data Set of used modules in the print. If @p mod is found in this set, no string (prefix) is
+ * returned.
+ * @return Prefix to print, may be NULL if the default namespace should be used.
*/
static const char *
ly_xml_get_prefix(const struct lys_module *mod, void *prefix_data)
{
- struct ly_set *ns_list = prefix_data;
+ struct ly_set *mods = prefix_data;
+ uint32_t i;
- LY_CHECK_RET(ly_set_add(ns_list, (void *)mod, 0, NULL), NULL);
+ /* first is the local module */
+ assert(mods->count);
+ if (mods->objs[0] == mod) {
+ return NULL;
+ }
+
+ /* check for duplicates in the rest of the modules and add there */
+ for (i = 1; i < mods->count; ++i) {
+ if (mods->objs[i] == mod) {
+ break;
+ }
+ }
+ if (i == mods->count) {
+ LY_CHECK_RET(ly_set_add(mods, (void *)mod, 1, NULL), NULL);
+ }
+
+ /* return the prefix */
return mod->prefix;
}
/**
- * @brief Simply return module name.
+ * @brief Get prefix for JSON print.
+ *
+ * @param[in] mod Module whose prefix to get.
+ * @param[in] prefix_data Current local module, may be NULL. If it matches @p mod, no string (preifx) is returned.
+ * @return Prefix (module name) to print, may be NULL if the default module should be used.
*/
static const char *
-ly_json_get_prefix(const struct lys_module *mod, void *UNUSED(prefix_data))
+ly_json_get_prefix(const struct lys_module *mod, void *prefix_data)
{
- return mod->name;
+ const struct lys_module *local_mod = prefix_data;
+
+ return (local_mod == mod) ? NULL : mod->name;
}
const char *
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 3ec1d3b..8490e3a 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -367,7 +367,7 @@
* @param[in] format Format of the prefix (::lyplg_type_print_clb's format parameter).
* @param[in] prefix_data Format-specific data (::lyplg_type_print_clb's prefix_data parameter).
* @return Module prefix to print.
- * @return NULL on error.
+ * @return NULL on using the current module/namespace.
*/
LIBYANG_API_DECL const char *lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data);
diff --git a/src/plugins_types/identityref.c b/src/plugins_types/identityref.c
index 8b7985d..e33b012 100644
--- a/src/plugins_types/identityref.c
+++ b/src/plugins_types/identityref.c
@@ -50,8 +50,16 @@
identityref_ident2str(const struct lysc_ident *ident, LY_VALUE_FORMAT format, void *prefix_data, char **str, size_t *str_len)
{
int len;
+ const char *prefix;
- len = asprintf(str, "%s:%s", lyplg_type_get_prefix(ident->module, format, prefix_data), ident->name);
+ /* get the prefix, may be NULL for no prefix and the default namespace */
+ prefix = lyplg_type_get_prefix(ident->module, format, prefix_data);
+
+ if (prefix) {
+ len = asprintf(str, "%s:%s", prefix, ident->name);
+ } else {
+ len = asprintf(str, "%s", ident->name);
+ }
if (len == -1) {
return LY_EMEM;
}
@@ -269,8 +277,8 @@
LY_CHECK_GOTO(ret, cleanup);
}
} else {
- /* JSON format with prefix is the canonical one */
- ret = identityref_ident2str(ident, LY_VALUE_JSON, NULL, &canon, NULL);
+ /* JSON format is the canonical one */
+ ret = identityref_ident2str(ident, LY_VALUE_JSON, ctx_node ? ctx_node->module : NULL, &canon, NULL);
LY_CHECK_GOTO(ret, cleanup);
ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c
index 0eef9cc..53a1de1 100644
--- a/src/plugins_types/instanceid.c
+++ b/src/plugins_types/instanceid.c
@@ -50,12 +50,19 @@
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u, v;
char *result = NULL, quot;
- const struct lys_module *mod = NULL;
+ const struct lys_module *mod = NULL, *local_mod = NULL;
+ struct ly_set *mods;
ly_bool inherit_prefix = 0, d;
const char *strval;
switch (format) {
case LY_VALUE_XML:
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+
+ /* fallthrough */
case LY_VALUE_SCHEMA:
case LY_VALUE_SCHEMA_RESOLVED:
/* everything is prefixed */
@@ -136,6 +143,9 @@
}
cleanup:
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
if (ret) {
free(result);
} else {
diff --git a/src/plugins_types/instanceid_keys.c b/src/plugins_types/instanceid_keys.c
index 163bf19..ab7751c 100644
--- a/src/plugins_types/instanceid_keys.c
+++ b/src/plugins_types/instanceid_keys.c
@@ -67,10 +67,18 @@
void *mem;
const char *cur_exp_ptr;
ly_bool is_nt;
- const struct lys_module *context_mod = NULL;
+ const struct lys_module *context_mod = NULL, *local_mod = NULL;
+ struct ly_set *mods;
*str_value = NULL;
+ if (format == LY_VALUE_XML) {
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+ }
+
while (cur_idx < val->keys->used) {
cur_tok = val->keys->tokens[cur_idx];
cur_exp_ptr = val->keys->expr + val->keys->tok_pos[cur_idx];
@@ -79,11 +87,15 @@
/* tokens that may include prefixes, get them in the target format */
is_nt = (cur_tok == LYXP_TOKEN_NAMETEST) ? 1 : 0;
LY_CHECK_GOTO(ret = lyplg_type_xpath10_print_token(cur_exp_ptr, val->keys->tok_len[cur_idx], is_nt, &context_mod,
- val->ctx, val->format, val->prefix_data, format, prefix_data, &str_tok, err), error);
+ val->ctx, val->format, val->prefix_data, format, prefix_data, &str_tok, err), cleanup);
/* append the converted token */
mem = realloc(*str_value, str_len + strlen(str_tok) + 1);
- LY_CHECK_ERR_GOTO(!mem, free(str_tok), error_mem);
+ if (!mem) {
+ free(str_tok);
+ ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
+ goto cleanup;
+ }
*str_value = mem;
str_len += sprintf(*str_value + str_len, "%s", str_tok);
free(str_tok);
@@ -93,7 +105,10 @@
} else {
/* just copy the token */
mem = realloc(*str_value, str_len + val->keys->tok_len[cur_idx] + 1);
- LY_CHECK_GOTO(!mem, error_mem);
+ if (!mem) {
+ ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
+ goto cleanup;
+ }
*str_value = mem;
str_len += sprintf(*str_value + str_len, "%.*s", (int)val->keys->tok_len[cur_idx], cur_exp_ptr);
@@ -102,13 +117,14 @@
}
}
- return LY_SUCCESS;
-
-error_mem:
- ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
-
-error:
- free(*str_value);
+cleanup:
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
+ if (ret) {
+ free(*str_value);
+ *str_value = NULL;
+ }
return ret;
}
diff --git a/src/plugins_types/node_instanceid.c b/src/plugins_types/node_instanceid.c
index 00cf207..7833263 100644
--- a/src/plugins_types/node_instanceid.c
+++ b/src/plugins_types/node_instanceid.c
@@ -50,7 +50,8 @@
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u, v;
char *result = NULL, quot;
- const struct lys_module *mod = NULL;
+ const struct lys_module *mod = NULL, *local_mod = NULL;
+ struct ly_set *mods;
ly_bool inherit_prefix = 0, d;
const char *strval;
@@ -62,6 +63,12 @@
switch (format) {
case LY_VALUE_XML:
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+
+ /* fallthrough */
case LY_VALUE_SCHEMA:
case LY_VALUE_SCHEMA_RESOLVED:
/* everything is prefixed */
@@ -148,6 +155,9 @@
}
cleanup:
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
if (ret) {
free(result);
} else {
diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c
index 6f05bd2..fdfc2a9 100644
--- a/src/plugins_types/xpath1.0.c
+++ b/src/plugins_types/xpath1.0.c
@@ -3,7 +3,7 @@
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief ietf-yang-types xpath1.0 type plugin.
*
- * Copyright (c) 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2021 - 2023 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
@@ -57,12 +57,9 @@
while (!(ret = ly_value_prefix_next(str_begin, token + tok_len, &len, &is_prefix, &str_next)) && len) {
if (!is_prefix) {
if (!has_prefix && is_nametest && (get_format == LY_VALUE_XML) && *context_mod) {
- /* prefix is always needed, get it in the target format */
+ /* get the prefix */
prefix = lyplg_type_get_prefix(*context_mod, get_format, get_prefix_data);
- if (!prefix) {
- ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Internal error.");
- goto cleanup;
- }
+ assert(prefix);
/* append the nametest and prefix */
mem = realloc(str, str_len + strlen(prefix) + 1 + len + 1);
@@ -94,10 +91,7 @@
if (mod) {
/* get the prefix in the target format */
prefix = lyplg_type_get_prefix(mod, get_format, get_prefix_data);
- if (!prefix) {
- ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Internal error.");
- goto cleanup;
- }
+ assert(prefix);
pref_len = strlen(prefix);
} else {
/* invalid prefix, just copy it */
@@ -223,13 +217,25 @@
LY_ERR ret = LY_SUCCESS;
uint16_t expr_idx = 0;
uint32_t str_len = 0;
+ const struct lys_module *local_mod = NULL;
+ struct ly_set *mods;
*str_value = NULL;
*err = NULL;
+ if (format == LY_VALUE_XML) {
+ /* null the local module so that all the prefixes are printed */
+ mods = prefix_data;
+ local_mod = mods->objs[0];
+ mods->objs[0] = NULL;
+ }
+
/* recursively print the expression */
ret = xpath10_print_subexpr_r(&expr_idx, 0, NULL, xp_val, format, prefix_data, str_value, &str_len, err);
+ if (local_mod) {
+ mods->objs[0] = (void *)local_mod;
+ }
if (ret) {
free(*str_value);
*str_value = NULL;
diff --git a/src/printer_json.c b/src/printer_json.c
index 98671d9..97347e1 100644
--- a/src/printer_json.c
+++ b/src/printer_json.c
@@ -343,15 +343,19 @@
* @param[in] pctx JSON printer context.
* @param[in] ctx Context used to print the value.
* @param[in] val Data value to be printed.
+ * @param[in] local_mod Module of the current node.
* @return LY_ERR value.
*/
static LY_ERR
-json_print_value(struct jsonpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val)
+json_print_value(struct jsonpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val,
+ const struct lys_module *local_mod)
{
ly_bool dynamic;
LY_DATA_TYPE basetype;
- const char *value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, NULL, &dynamic, NULL);
+ const char *value;
+ value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, (void *)local_mod, &dynamic, NULL);
+ LY_CHECK_RET(!value, LY_EINVAL);
basetype = val->realtype->basetype;
print_val:
@@ -458,7 +462,7 @@
for (meta = node->meta; meta; meta = meta->next) {
PRINT_COMMA;
ly_print_(pctx->out, "%*s\"%s:%s\":%s", INDENT, meta->annotation->module->name, meta->name, DO_FORMAT ? " " : "");
- LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &meta->value));
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &meta->value, NULL));
LEVEL_PRINTED;
}
@@ -526,7 +530,7 @@
json_print_leaf(struct jsonpr_ctx *pctx, const struct lyd_node *node)
{
LY_CHECK_RET(json_print_member(pctx, node, 0));
- LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, node->schema->module));
LEVEL_PRINTED;
/* print attributes as sibling */
@@ -791,7 +795,7 @@
} else {
assert(node->schema->nodetype == LYS_LEAFLIST);
- LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value));
+ LY_CHECK_RET(json_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, node->schema->module));
if (!pctx->print_sibling_metadata) {
if ((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) {
diff --git a/src/printer_xml.c b/src/printer_xml.c
index a7f4c73..47b0acc 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -178,6 +178,8 @@
struct ly_set ns_list = {0};
LY_ARRAY_COUNT_TYPE u;
ly_bool dynamic, filter_attrs = 0;
+ const char *value;
+ uint32_t i;
/* with-defaults */
if (node->schema->nodetype & LYD_NODE_TERM) {
@@ -205,11 +207,14 @@
}
for (meta = node->meta; meta; meta = meta->next) {
- const char *value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list,
- &dynamic, NULL);
+ /* store the module of the default namespace, NULL because there is none */
+ ly_set_add(&ns_list, NULL, 0, NULL);
+
+ /* print the value */
+ value = meta->value.realtype->plugin->print(LYD_CTX(node), &meta->value, LY_VALUE_XML, &ns_list, &dynamic, NULL);
/* print namespaces connected with the value's prefixes */
- for (uint32_t i = 0; i < ns_list.count; ++i) {
+ for (i = 1; i < ns_list.count; ++i) {
mod = ns_list.objs[i];
xml_print_ns(pctx, mod->ns, mod->prefix, 1);
}
@@ -314,22 +319,32 @@
static LY_ERR
xml_print_term(struct xmlpr_ctx *pctx, const struct lyd_node_term *node)
{
+ LY_ERR rc = LY_SUCCESS;
struct ly_set ns_list = {0};
- ly_bool dynamic;
- const char *value;
+ ly_bool dynamic = 0;
+ const char *value = NULL;
+ const struct lys_module *mod;
+ uint32_t i;
- xml_print_node_open(pctx, &node->node);
+ /* store the module of the default namespace */
+ if ((rc = ly_set_add(&ns_list, node->schema->module, 0, NULL))) {
+ LOGMEM(pctx->ctx);
+ goto cleanup;
+ }
+
+ /* print the value */
value = ((struct lysc_node_leaf *)node->schema)->type->plugin->print(LYD_CTX(node), &node->value, LY_VALUE_XML,
&ns_list, &dynamic, NULL);
- LY_CHECK_RET(!value, LY_EINVAL);
+ LY_CHECK_ERR_GOTO(!value, rc = LY_EINVAL, cleanup);
+
+ /* print node opening */
+ xml_print_node_open(pctx, &node->node);
/* print namespaces connected with the values's prefixes */
- for (uint32_t u = 0; u < ns_list.count; ++u) {
- const struct lys_module *mod = (const struct lys_module *)ns_list.objs[u];
-
+ for (i = 1; i < ns_list.count; ++i) {
+ mod = ns_list.objs[i];
ly_print_(pctx->out, " xmlns:%s=\"%s\"", mod->prefix, mod->ns);
}
- ly_set_erase(&ns_list, NULL);
if (!value[0]) {
ly_print_(pctx->out, "/>%s", DO_FORMAT ? "\n" : "");
@@ -338,11 +353,13 @@
lyxml_dump_text(pctx->out, value, 0);
ly_print_(pctx->out, "</%s>%s", node->schema->name, DO_FORMAT ? "\n" : "");
}
+
+cleanup:
+ ly_set_erase(&ns_list, NULL);
if (dynamic) {
free((void *)value);
}
-
- return LY_SUCCESS;
+ return rc;
}
/**
diff --git a/tests/utests/types/identityref.c b/tests/utests/types/identityref.c
index cdfe057..b69299e 100644
--- a/tests/utests/types/identityref.c
+++ b/tests/utests/types/identityref.c
@@ -27,15 +27,6 @@
NODES \
"}\n"
-#define TEST_SUCCESS_XML(MOD_NAME, NAMESPACES, NODE_NAME, DATA, TYPE, ...) \
- { \
- struct lyd_node *tree; \
- const char *data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\" " NAMESPACES ">" DATA "</" NODE_NAME ">"; \
- CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree); \
- CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, TYPE, __VA_ARGS__); \
- lyd_free_all(tree); \
- }
-
#define TEST_ERROR_XML(MOD_NAME, NAMESPACES, NODE_NAME, DATA) \
{\
struct lyd_node *tree; \
@@ -64,21 +55,44 @@
test_data_xml(void **state)
{
const char *schema, *schema2;
+ struct lyd_node *tree;
+ const char *data;
/* xml test */
- schema = MODULE_CREATE_YANG("ident-base", "identity ident-base;"
- "identity ident-imp {base ident-base;}");
+ schema = "module ident-base {"
+ " yang-version 1.1;"
+ " namespace \"urn:tests:ident-base\";"
+ " prefix ib;"
+ " identity ident-base;"
+ " identity ident-imp {base ident-base;}"
+ "}";
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
- schema2 = MODULE_CREATE_YANG("defs", "import ident-base {prefix ib;}"
- "identity ident1 {base ib:ident-base;}"
- "leaf l1 {type identityref {base ib:ident-base;}}");
+ schema2 = "module defs {"
+ " yang-version 1.1;"
+ " namespace \"urn:tests:defs\";"
+ " prefix d;"
+ " import ident-base {prefix ib;}"
+ " identity ident1 {base ib:ident-base;}"
+ " leaf l1 {type identityref {base ib:ident-base;}}"
+ "}";
UTEST_ADD_MODULE(schema2, LYS_IN_YANG, NULL, NULL);
- TEST_SUCCESS_XML("defs", "", "l1", "ident1", IDENT, "defs:ident1", "ident1");
+ /* local ident, XML/JSON print */
+ data = "<l1 xmlns=\"urn:tests:defs\">ident1</l1>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, IDENT, "ident1", "ident1");
+ CHECK_LYD_STRING_PARAM(tree, data, LYD_XML, LYD_PRINT_SHRINK);
+ CHECK_LYD_STRING_PARAM(tree, "{\"defs:l1\":\"ident1\"}", LYD_JSON, LYD_PRINT_SHRINK);
+ lyd_free_all(tree);
- TEST_SUCCESS_XML("defs", "xmlns:i=\"urn:tests:ident-base\"", "l1", "i:ident-imp", IDENT, "ident-base:ident-imp",
- "ident-imp");
+ /* foreign ident, XML/JSON print */
+ data = "<l1 xmlns=\"urn:tests:defs\" xmlns:ib=\"urn:tests:ident-base\">ib:ident-imp</l1>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ CHECK_LYD_NODE_TERM((struct lyd_node_term *)tree, 0, 0, 0, 0, 1, IDENT, "ident-base:ident-imp", "ident-imp");
+ CHECK_LYD_STRING_PARAM(tree, data, LYD_XML, LYD_PRINT_SHRINK);
+ CHECK_LYD_STRING_PARAM(tree, "{\"defs:l1\":\"ident-base:ident-imp\"}", LYD_JSON, LYD_PRINT_SHRINK);
+ lyd_free_all(tree);
/* invalid value */
TEST_ERROR_XML("defs", "", "l1", "fast-ethernet");
diff --git a/tests/utests/types/instanceid.c b/tests/utests/types/instanceid.c
index 4174e4d..efc2b44 100644
--- a/tests/utests/types/instanceid.c
+++ b/tests/utests/types/instanceid.c
@@ -125,7 +125,7 @@
"<list-ident xmlns=\"urn:tests:defs\"><id xmlns:b=\"urn:tests:defs\">b:ident-der2</id>"
"<value>y</value></list-ident>",
"defs", "xmlns:a=\"urn:tests:defs\"", "a:l1", "/a:list-ident[a:id='a:ident-der1']/a:value",
- INST, "/defs:list-ident[id='defs:ident-der1']/value", val5);
+ INST, "/defs:list-ident[id='ident-der1']/value", val5);
TEST_SUCCESS_XML2("<list2 xmlns=\"urn:tests:defs\"><id>defs:xxx</id><id2>x</id2></list2>"
"<list2 xmlns=\"urn:tests:defs\"><id>a:xxx</id><id2>y</id2></list2>",
diff --git a/tests/utests/types/union.c b/tests/utests/types/union.c
index 3522795..c4c282c 100644
--- a/tests/utests/types/union.c
+++ b/tests/utests/types/union.c
@@ -86,7 +86,7 @@
"defs", "", "un1", "2", UNION, "2", STRING, "2");
TEST_SUCCESS_XML2("<int8 xmlns=\"urn:tests:defs\">10</int8>",
- "defs", "xmlns:x=\"urn:tests:defs\"", "un1", "x:ident2", UNION, "defs:ident2", IDENT, "defs:ident2", "ident2");
+ "defs", "xmlns:x=\"urn:tests:defs\"", "un1", "x:ident2", UNION, "ident2", IDENT, "ident2", "ident2");
TEST_SUCCESS_XML2("<int8 xmlns=\"urn:tests:defs\">10</int8>",
"defs", "xmlns:x=\"urn:tests:defs\"", "un1", "x:ident55", UNION, "x:ident55", STRING, "x:ident55");