diff options
Diffstat (limited to 'src/tree_data_common.c')
-rw-r--r-- | src/tree_data_common.c | 458 |
1 files changed, 317 insertions, 141 deletions
diff --git a/src/tree_data_common.c b/src/tree_data_common.c index f35f8f5..672f720 100644 --- a/src/tree_data_common.c +++ b/src/tree_data_common.c @@ -44,32 +44,65 @@ #include "xpath.h" /** + * @brief Callback for checking first instance hash table values equivalence. + * + * @param[in] val1_p If not @p mod, pointer to the first instance. + * @param[in] val2_p If not @p mod, pointer to the found dup inst item. + */ +static ly_bool +lyht_dup_inst_ht_equal_cb(void *val1_p, void *val2_p, ly_bool mod, void *UNUSED(cb_data)) +{ + if (mod) { + struct lyd_dup_inst **item1 = val1_p, **item2 = val2_p; + + /* equal on 2 dup inst items */ + return *item1 == *item2 ? 1 : 0; + } else { + struct lyd_node **first_inst = val1_p; + struct lyd_dup_inst **item = val2_p; + + /* equal on dup inst item and a first instance */ + return (*item)->set->dnodes[0] == *first_inst ? 1 : 0; + } +} + +/** * @brief Find an entry in duplicate instance cache for an instance. Create it if it does not exist. * - * @param[in] first_inst Instance of the cache entry. - * @param[in,out] dup_inst_cache Duplicate instance cache. + * @param[in] first_inst First instance of the cache entry. + * @param[in] dup_inst_ht Duplicate instance cache hash table. * @return Instance cache entry. */ static struct lyd_dup_inst * -lyd_dup_inst_get(const struct lyd_node *first_inst, struct lyd_dup_inst **dup_inst_cache) +lyd_dup_inst_get(const struct lyd_node *first_inst, struct ly_ht **dup_inst_ht) { - struct lyd_dup_inst *item; - LY_ARRAY_COUNT_TYPE u; + struct lyd_dup_inst **item_p, *item; - LY_ARRAY_FOR(*dup_inst_cache, u) { - if ((*dup_inst_cache)[u].inst_set->dnodes[0] == first_inst) { - return &(*dup_inst_cache)[u]; + if (*dup_inst_ht) { + /* find the item of the first instance */ + if (!lyht_find(*dup_inst_ht, &first_inst, first_inst->hash, (void **)&item_p)) { + return *item_p; } + } else { + /* create the hash table */ + *dup_inst_ht = lyht_new(2, sizeof item, lyht_dup_inst_ht_equal_cb, NULL, 1); + LY_CHECK_RET(!*dup_inst_ht, NULL); } - /* it was not added yet, add it now */ - LY_ARRAY_NEW_RET(LYD_CTX(first_inst), *dup_inst_cache, item, NULL); + /* first instance has no dup inst item, create it */ + item = calloc(1, sizeof *item); + LY_CHECK_RET(!item, NULL); + + /* add into the hash table */ + if (lyht_insert(*dup_inst_ht, &item, first_inst->hash, NULL)) { + return NULL; + } return item; } LY_ERR -lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struct lyd_dup_inst **dup_inst_cache) +lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struct ly_ht **dup_inst_ht) { struct lyd_dup_inst *dup_inst; @@ -80,40 +113,47 @@ lyd_dup_inst_next(struct lyd_node **inst, const struct lyd_node *siblings, struc /* there can be more exact same instances (even if not allowed in invalid data) and we must make sure we do not * match a single node more times */ - dup_inst = lyd_dup_inst_get(*inst, dup_inst_cache); + dup_inst = lyd_dup_inst_get(*inst, dup_inst_ht); LY_CHECK_ERR_RET(!dup_inst, LOGMEM(LYD_CTX(siblings)), LY_EMEM); if (!dup_inst->used) { /* we did not cache these instances yet, do so */ - lyd_find_sibling_dup_inst_set(siblings, *inst, &dup_inst->inst_set); - assert(dup_inst->inst_set->count && (dup_inst->inst_set->dnodes[0] == *inst)); + lyd_find_sibling_dup_inst_set(siblings, *inst, &dup_inst->set); + assert(dup_inst->set->count && (dup_inst->set->dnodes[0] == *inst)); } - if (dup_inst->used == dup_inst->inst_set->count) { + if (dup_inst->used == dup_inst->set->count) { if (lysc_is_dup_inst_list((*inst)->schema)) { /* we have used all the instances */ *inst = NULL; } /* else just keep using the last (ideally only) instance */ } else { - assert(dup_inst->used < dup_inst->inst_set->count); + assert(dup_inst->used < dup_inst->set->count); /* use another instance */ - *inst = dup_inst->inst_set->dnodes[dup_inst->used]; + *inst = dup_inst->set->dnodes[dup_inst->used]; ++dup_inst->used; } return LY_SUCCESS; } -void -lyd_dup_inst_free(struct lyd_dup_inst *dup_inst) +/** + * @brief Callback for freeing first instance hash table values. + */ +static void +lyht_dup_inst_ht_free_cb(void *val_p) { - LY_ARRAY_COUNT_TYPE u; + struct lyd_dup_inst **item = val_p; - LY_ARRAY_FOR(dup_inst, u) { - ly_set_free(dup_inst[u].inst_set, NULL); - } - LY_ARRAY_FREE(dup_inst); + ly_set_free((*item)->set, NULL); + free(*item); +} + +void +lyd_dup_inst_free(struct ly_ht *dup_inst_ht) +{ + lyht_free(dup_inst_ht, lyht_dup_inst_ht_free_cb); } struct lyd_node * @@ -180,12 +220,12 @@ lyxp_vars_set(struct lyxp_var **vars, const char *name, const char *value) return LY_EINVAL; } - /* If variable is already defined then change its value. */ - if (*vars && !lyxp_vars_find(*vars, name, 0, &item)) { + /* if variable is already defined then change its value */ + if (*vars && !lyxp_vars_find(NULL, *vars, name, 0, &item)) { var_value = strdup(value); LY_CHECK_RET(!var_value, LY_EMEM); - /* Set new value. */ + /* update value */ free(item->value); item->value = var_value; } else { @@ -193,7 +233,7 @@ lyxp_vars_set(struct lyxp_var **vars, const char *name, const char *value) var_value = strdup(value); LY_CHECK_ERR_GOTO(!var_name || !var_value, ret = LY_EMEM, error); - /* Add new variable. */ + /* add new variable */ LY_ARRAY_NEW_GOTO(NULL, *vars, item, ret, error); item->name = var_name; item->value = var_value; @@ -260,21 +300,68 @@ lyd_owner_module(const struct lyd_node *node) return NULL; } + while (!node->schema && node->parent) { + node = lyd_parent(node); + } + if (!node->schema) { + /* top-level opaque node */ opaq = (struct lyd_node_opaq *)node; switch (opaq->format) { case LY_VALUE_XML: - return opaq->name.module_ns ? ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns) : NULL; + if (opaq->name.module_ns) { + return ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns); + } + break; case LY_VALUE_JSON: - return opaq->name.module_name ? ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name) : NULL; + if (opaq->name.module_name) { + return ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name); + } + break; default: return NULL; } + + return NULL; } return lysc_owner_module(node->schema); } +LIBYANG_API_DEF const struct lys_module * +lyd_node_module(const struct lyd_node *node) +{ + const struct lyd_node_opaq *opaq; + + while (node) { + /* data node */ + if (node->schema) { + return node->schema->module; + } + + /* opaque node */ + opaq = (struct lyd_node_opaq *)node; + switch (opaq->format) { + case LY_VALUE_XML: + if (opaq->name.module_ns) { + return ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns); + } + break; + case LY_VALUE_JSON: + if (opaq->name.module_name) { + return ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name); + } + break; + default: + break; + } + + node = lyd_parent(node); + } + + return NULL; +} + void lyd_first_module_sibling(struct lyd_node **node, const struct lys_module *mod) { @@ -389,12 +476,12 @@ lyd_data_next_module(struct lyd_node **next, struct lyd_node **first) LY_ERR lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct lysc_type *type, const void *value, - size_t value_len, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, + size_t value_len, ly_bool is_utf8, ly_bool *dynamic, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node, ly_bool *incomplete) { LY_ERR ret; struct ly_err_item *err = NULL; - uint32_t options = (dynamic && *dynamic ? LYPLG_TYPE_STORE_DYNAMIC : 0); + uint32_t options = 0; if (!value) { value = ""; @@ -403,6 +490,13 @@ lyd_value_store(const struct ly_ctx *ctx, struct lyd_value *val, const struct ly *incomplete = 0; } + if (dynamic && *dynamic) { + options |= LYPLG_TYPE_STORE_DYNAMIC; + } + if (is_utf8) { + options |= LYPLG_TYPE_STORE_IS_UTF8; + } + ret = type->plugin->store(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node, val, NULL, &err); if (dynamic) { *dynamic = 0; @@ -440,8 +534,8 @@ lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc_type * LOGVAL_ERRITEM(ctx, err); ly_err_free(err); } else { - LOGVAL(ctx, LYVE_OTHER, "Resolving value \"%s\" failed.", type->plugin->print(ctx, val, LY_VALUE_CANON, - NULL, NULL, NULL)); + LOGVAL(ctx, LYVE_OTHER, "Resolving value \"%s\" failed.", + (char *)type->plugin->print(ctx, val, LY_VALUE_CANON, NULL, NULL, NULL)); } return ret; } @@ -450,15 +544,15 @@ lyd_value_validate_incomplete(const struct ly_ctx *ctx, const struct lysc_type * } LY_ERR -lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len, - LY_VALUE_FORMAT format, void *prefix_data) +ly_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len, + LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints) { LY_ERR rc = LY_SUCCESS; struct ly_err_item *err = NULL; struct lyd_value storage; struct lysc_type *type; - LY_CHECK_ARG_RET(ctx, node, value, LY_EINVAL); + LY_CHECK_ARG_RET(ctx, node, LY_EINVAL); if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) { LOGARG(ctx, node); @@ -466,8 +560,8 @@ lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const } type = ((struct lysc_node_leaf *)node)->type; - rc = type->plugin->store(ctx ? ctx : node->module->ctx, type, value, value_len, 0, format, prefix_data, - LYD_HINT_SCHEMA, node, &storage, NULL, &err); + rc = type->plugin->store(ctx ? ctx : node->module->ctx, type, value, value_len, 0, format, prefix_data, hints, node, + &storage, NULL, &err); if (rc == LY_EINCOMPLETE) { /* actually success since we do not provide the context tree and call validation with * LY_TYPE_OPTS_INCOMPLETE_DATA */ @@ -478,14 +572,13 @@ lys_value_validate(const struct ly_ctx *ctx, const struct lysc_node *node, const if (err->path) { LOG_LOCSET(NULL, NULL, err->path, NULL); } else { - /* use at least the schema path */ LOG_LOCSET(node, NULL, NULL, NULL); } LOGVAL_ERRITEM(ctx, err); if (err->path) { LOG_LOCBACK(0, 0, 1, 0); } else { - LOG_LOCBACK(1, 0, 0, 0); + LOG_LOCBACK(1, 0, 1, 0); } } ly_err_free(err); @@ -590,7 +683,7 @@ lyd_value_compare(const struct lyd_node_term *node, const char *value, size_t va /* store the value */ LOG_LOCSET(node->schema, &node->node, NULL, NULL); - ret = lyd_value_store(ctx, &val, type, value, value_len, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, node->schema, NULL); + ret = lyd_value_store(ctx, &val, type, value, value_len, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, node->schema, NULL); LOG_LOCBACK(1, 1, 0, 0); LY_CHECK_RET(ret); @@ -704,24 +797,15 @@ lyd_parse_opaq_list_error(const struct lyd_node *node, const struct lysc_node *s assert(!node->schema); /* get all keys into a set */ - while ((key = lys_getnext(key, snode, NULL, 0)) && (snode->flags & LYS_KEY)) { - LY_CHECK_GOTO(ret = ly_set_add(&key_set, (void *)snode, 1, NULL), cleanup); + while ((key = lys_getnext(key, snode, NULL, 0)) && (key->flags & LYS_KEY)) { + LY_CHECK_GOTO(ret = ly_set_add(&key_set, (void *)key, 1, NULL), cleanup); } LY_LIST_FOR(lyd_child(node), child) { - if (child->schema) { - LOGERR(LYD_CTX(node), LY_EINVAL, "Unexpected node %s \"%s\".", lys_nodetype2str(child->schema->nodetype), - LYD_NAME(child)); - ret = LY_EINVAL; - goto cleanup; - } - - opaq_k = (struct lyd_node_opaq *)child; - /* find the key schema node */ for (i = 0; i < key_set.count; ++i) { key = key_set.snodes[i]; - if (!strcmp(key->name, opaq_k->name.name)) { + if (!strcmp(key->name, LYD_NAME(child))) { break; } } @@ -733,9 +817,15 @@ lyd_parse_opaq_list_error(const struct lyd_node *node, const struct lysc_node *s /* key found */ ly_set_rm_index(&key_set, i, NULL); + if (child->schema) { + /* valid key */ + continue; + } + /* check value */ - ret = lys_value_validate(LYD_CTX(node), key, opaq_k->value, strlen(opaq_k->value), opaq_k->format, - opaq_k->val_prefix_data); + opaq_k = (struct lyd_node_opaq *)child; + ret = ly_value_validate(LYD_CTX(node), key, opaq_k->value, strlen(opaq_k->value), opaq_k->format, + opaq_k->val_prefix_data, opaq_k->hints); LY_CHECK_GOTO(ret, cleanup); } @@ -754,93 +844,124 @@ cleanup: LIBYANG_API_DEF LY_ERR lyd_parse_opaq_error(const struct lyd_node *node) { + LY_ERR rc = LY_SUCCESS; const struct ly_ctx *ctx; const struct lyd_node_opaq *opaq; const struct lyd_node *parent; const struct lys_module *mod; - const struct lysc_node *snode; + const struct lysc_node *sparent, *snode; + uint32_t loc_node = 0, loc_path = 0; - LY_CHECK_ARG_RET(LYD_CTX(node), node, !node->schema, !lyd_parent(node) || lyd_parent(node)->schema, LY_EINVAL); + LY_CHECK_ARG_RET(LYD_CTX(node), node, !node->schema, LY_EINVAL); ctx = LYD_CTX(node); opaq = (struct lyd_node_opaq *)node; parent = lyd_parent(node); + sparent = lyd_node_schema(parent); + + if (parent) { + LOG_LOCSET(NULL, parent, NULL, NULL); + ++loc_node; + } else { + LOG_LOCSET(NULL, NULL, "/", NULL); + ++loc_path; + } if (!opaq->name.module_ns) { LOGVAL(ctx, LYVE_REFERENCE, "Unknown module of node \"%s\".", opaq->name.name); - return LY_EVALID; + rc = LY_EVALID; + goto cleanup; } /* module */ switch (opaq->format) { case LY_VALUE_XML: - if (!parent || strcmp(opaq->name.module_ns, parent->schema->module->ns)) { + if (!sparent || strcmp(opaq->name.module_ns, sparent->module->ns)) { mod = ly_ctx_get_module_implemented_ns(ctx, opaq->name.module_ns); if (!mod) { LOGVAL(ctx, LYVE_REFERENCE, "No (implemented) module with namespace \"%s\" of node \"%s\" in the context.", opaq->name.module_ns, opaq->name.name); - return LY_EVALID; + rc = LY_EVALID; + goto cleanup; } } else { /* inherit */ - mod = parent->schema->module; + mod = sparent->module; } break; case LY_VALUE_JSON: case LY_VALUE_LYB: - if (!parent || strcmp(opaq->name.module_name, parent->schema->module->name)) { + if (!sparent || strcmp(opaq->name.module_name, sparent->module->name)) { mod = ly_ctx_get_module_implemented(ctx, opaq->name.module_name); if (!mod) { LOGVAL(ctx, LYVE_REFERENCE, "No (implemented) module named \"%s\" of node \"%s\" in the context.", opaq->name.module_name, opaq->name.name); - return LY_EVALID; + rc = LY_EVALID; + goto cleanup; } } else { /* inherit */ - mod = parent->schema->module; + mod = sparent->module; } break; default: LOGERR(ctx, LY_EINVAL, "Unsupported value format."); - return LY_EINVAL; + rc = LY_EINVAL; + goto cleanup; } /* schema */ - snode = lys_find_child(parent ? parent->schema : NULL, mod, opaq->name.name, 0, 0, 0); - if (!snode && parent && parent->schema && (parent->schema->nodetype & (LYS_RPC | LYS_ACTION))) { + snode = lys_find_child(sparent, mod, opaq->name.name, 0, 0, 0); + if (!snode && sparent && (sparent->nodetype & (LYS_RPC | LYS_ACTION))) { /* maybe output node */ - snode = lys_find_child(parent->schema, mod, opaq->name.name, 0, 0, LYS_GETNEXT_OUTPUT); + snode = lys_find_child(sparent, mod, opaq->name.name, 0, 0, LYS_GETNEXT_OUTPUT); } if (!snode) { - if (parent) { + if (sparent) { LOGVAL(ctx, LYVE_REFERENCE, "Node \"%s\" not found as a child of \"%s\" node.", opaq->name.name, - LYD_NAME(parent)); + sparent->name); } else { LOGVAL(ctx, LYVE_REFERENCE, "Node \"%s\" not found in the \"%s\" module.", opaq->name.name, mod->name); } - return LY_EVALID; + rc = LY_EVALID; + goto cleanup; } + /* schema node exists */ + LOG_LOCBACK(0, loc_node, loc_path, 0); + loc_node = 0; + loc_path = 0; + LOG_LOCSET(NULL, node, NULL, NULL); + ++loc_node; + if (snode->nodetype & LYD_NODE_TERM) { /* leaf / leaf-list */ - LY_CHECK_RET(lys_value_validate(ctx, snode, opaq->value, strlen(opaq->value), opaq->format, opaq->val_prefix_data)); + rc = ly_value_validate(ctx, snode, opaq->value, strlen(opaq->value), opaq->format, opaq->val_prefix_data, opaq->hints); + LY_CHECK_GOTO(rc, cleanup); } else if (snode->nodetype == LYS_LIST) { /* list */ - LY_CHECK_RET(lyd_parse_opaq_list_error(node, snode)); + rc = lyd_parse_opaq_list_error(node, snode); + LY_CHECK_GOTO(rc, cleanup); } else if (snode->nodetype & LYD_NODE_INNER) { /* inner node */ if (opaq->value) { LOGVAL(ctx, LYVE_DATA, "Invalid value \"%s\" for %s \"%s\".", opaq->value, lys_nodetype2str(snode->nodetype), snode->name); - return LY_EVALID; + rc = LY_EVALID; + goto cleanup; } } else { LOGERR(ctx, LY_EINVAL, "Unexpected opaque schema node %s \"%s\".", lys_nodetype2str(snode->nodetype), snode->name); - return LY_EINVAL; + rc = LY_EINVAL; + goto cleanup; } LOGERR(ctx, LY_EINVAL, "Unexpected valid opaque node %s \"%s\".", lys_nodetype2str(snode->nodetype), snode->name); - return LY_EINVAL; + rc = LY_EINVAL; + +cleanup: + LOG_LOCBACK(0, loc_node, loc_path, 0); + return rc; } LIBYANG_API_DEF const char * @@ -970,7 +1091,7 @@ lyd_any_copy_value(struct lyd_node *trg, const union lyd_any_value *value, LYD_A return LY_SUCCESS; } -const struct lysc_node * +LIBYANG_API_DEF const struct lysc_node * lyd_node_schema(const struct lyd_node *node) { const struct lysc_node *schema = NULL; @@ -983,27 +1104,30 @@ lyd_node_schema(const struct lyd_node *node) return node->schema; } + /* find the first schema node in the parents */ + for (iter = lyd_parent(node); iter && !iter->schema; iter = lyd_parent(iter)) {} + if (iter) { + prev_iter = iter; + schema = prev_iter->schema; + } + /* get schema node of an opaque node */ do { /* get next data node */ for (iter = node; lyd_parent(iter) != prev_iter; iter = lyd_parent(iter)) {} - /* get equivalent schema node */ - if (iter->schema) { - schema = iter->schema; - } else { - /* get module */ - mod = lyd_owner_module(iter); - if (!mod && !schema) { - /* top-level opaque node has unknown module */ - break; - } - - /* get schema node */ - schema = lys_find_child(schema, mod ? mod : schema->module, LYD_NAME(iter), 0, 0, 0); + /* get module */ + mod = lyd_node_module(iter); + if (!mod) { + /* unknown module, no schema node */ + schema = NULL; + break; } - /* remember to move to the descendant */ + /* get schema node */ + schema = lys_find_child(schema, mod, LYD_NAME(iter), 0, 0, 0); + + /* move to the descendant */ prev_iter = iter; } while (schema && (iter != node)); @@ -1068,9 +1192,7 @@ lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node { struct lyd_node **match_p; struct lyd_node_inner *parent; - const struct lysc_node *cur_schema; uint32_t hash; - lyht_value_equal_cb ht_cb; assert(schema); if (!siblings) { @@ -1084,23 +1206,17 @@ lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node parent = siblings->parent; if (parent && parent->schema && parent->children_ht) { /* calculate our hash */ - hash = dict_hash_multi(0, schema->module->name, strlen(schema->module->name)); - hash = dict_hash_multi(hash, schema->name, strlen(schema->name)); - hash = dict_hash_multi(hash, NULL, 0); - - /* use special hash table function */ - ht_cb = lyht_set_cb(parent->children_ht, lyd_hash_table_schema_val_equal); + hash = lyht_hash_multi(0, schema->module->name, strlen(schema->module->name)); + hash = lyht_hash_multi(hash, schema->name, strlen(schema->name)); + hash = lyht_hash_multi(hash, NULL, 0); - /* find by hash */ - if (!lyht_find(parent->children_ht, &schema, hash, (void **)&match_p)) { + /* find by hash but use special hash table function (and stay thread-safe) */ + if (!lyht_find_with_val_cb(parent->children_ht, &schema, hash, lyd_hash_table_schema_val_equal, (void **)&match_p)) { siblings = *match_p; } else { /* not found */ siblings = NULL; } - - /* set the original hash table compare function back */ - lyht_set_cb(parent->children_ht, ht_cb); } else { /* find first sibling */ if (siblings->parent) { @@ -1111,25 +1227,22 @@ lyd_find_sibling_schema(const struct lyd_node *siblings, const struct lysc_node } } - /* search manually without hashes */ - for ( ; siblings; siblings = siblings->next) { - cur_schema = lyd_node_schema(siblings); - if (!cur_schema) { - /* some unknown opaque node */ - continue; - } - + /* search manually without hashes and ignore opaque nodes (cannot be found by hashes) */ + for ( ; siblings && siblings->schema; siblings = siblings->next) { /* schema match is enough */ - if (cur_schema->module->ctx == schema->module->ctx) { - if (cur_schema == schema) { + if (LYD_CTX(siblings) == schema->module->ctx) { + if (siblings->schema == schema) { break; } } else { - if (!strcmp(cur_schema->name, schema->name) && !strcmp(cur_schema->module->name, schema->module->name)) { + if (!strcmp(LYD_NAME(siblings), schema->name) && !strcmp(siblings->schema->module->name, schema->module->name)) { break; } } } + if (siblings && !siblings->schema) { + siblings = NULL; + } } if (!siblings) { @@ -1468,6 +1581,52 @@ ly_format2str(LY_VALUE_FORMAT format) return NULL; } +LIBYANG_API_DEF int +ly_time_tz_offset(void) +{ + return ly_time_tz_offset_at(time(NULL)); +} + +LIBYANG_API_DEF int +ly_time_tz_offset_at(time_t time) +{ + struct tm tm_local, tm_utc; + int result = 0; + + /* init timezone */ + tzset(); + + /* get local and UTC time */ + localtime_r(&time, &tm_local); + gmtime_r(&time, &tm_utc); + + /* account for year/month/day change by adding/subtracting from the hours, the change cannot be more than 1 day */ + if (tm_local.tm_year < tm_utc.tm_year) { + tm_utc.tm_hour += 24; + } else if (tm_local.tm_year > tm_utc.tm_year) { + tm_local.tm_hour += 24; + } else if (tm_local.tm_mon < tm_utc.tm_mon) { + tm_utc.tm_hour += 24; + } else if (tm_local.tm_mon > tm_utc.tm_mon) { + tm_local.tm_hour += 24; + } else if (tm_local.tm_mday < tm_utc.tm_mday) { + tm_utc.tm_hour += 24; + } else if (tm_local.tm_mday > tm_utc.tm_mday) { + tm_local.tm_hour += 24; + } + + /* hours shift in seconds */ + result += (tm_local.tm_hour - tm_utc.tm_hour) * 3600; + + /* minutes shift in seconds */ + result += (tm_local.tm_min - tm_utc.tm_min) * 60; + + /* seconds shift */ + result += tm_local.tm_sec - tm_utc.tm_sec; + + return result; +} + LIBYANG_API_DEF LY_ERR ly_time_str2time(const char *value, time_t *time, char **fractions_s) { @@ -1486,6 +1645,28 @@ ly_time_str2time(const char *value, time_t *time, char **fractions_s) tm.tm_min = atoi(&value[14]); tm.tm_sec = atoi(&value[17]); + /* explicit checks for some gross errors */ + if (tm.tm_mon > 11) { + LOGERR(NULL, LY_EINVAL, "Invalid date-and-time month \"%d\".", tm.tm_mon); + return LY_EINVAL; + } + if ((tm.tm_mday < 1) || (tm.tm_mday > 31)) { + LOGERR(NULL, LY_EINVAL, "Invalid date-and-time day of month \"%d\".", tm.tm_mday); + return LY_EINVAL; + } + if (tm.tm_hour > 23) { + LOGERR(NULL, LY_EINVAL, "Invalid date-and-time hours \"%d\".", tm.tm_hour); + return LY_EINVAL; + } + if (tm.tm_min > 59) { + LOGERR(NULL, LY_EINVAL, "Invalid date-and-time minutes \"%d\".", tm.tm_min); + return LY_EINVAL; + } + if (tm.tm_sec > 60) { + LOGERR(NULL, LY_EINVAL, "Invalid date-and-time seconds \"%d\".", tm.tm_sec); + return LY_EINVAL; + } + t = timegm(&tm); i = 19; @@ -1506,12 +1687,24 @@ ly_time_str2time(const char *value, time_t *time, char **fractions_s) shift = 0; } else { shift = strtol(&value[i], NULL, 10); + if (shift > 23) { + LOGERR(NULL, LY_EINVAL, "Invalid date-and-time timezone hour \"%" PRIi64 "\".", shift); + return LY_EINVAL; + } shift = shift * 60 * 60; /* convert from hours to seconds */ - shift_m = strtol(&value[i + 4], NULL, 10) * 60; /* includes conversion from minutes to seconds */ + + shift_m = strtol(&value[i + 4], NULL, 10); + if (shift_m > 59) { + LOGERR(NULL, LY_EINVAL, "Invalid date-and-time timezone minutes \"%" PRIi64 "\".", shift_m); + return LY_EINVAL; + } + shift_m *= 60; /* convert from minutes to seconds */ + /* correct sign */ if (shift < 0) { shift_m *= -1; } + /* connect hours and minutes of the shift */ shift = shift + shift_m; } @@ -1535,41 +1728,24 @@ LIBYANG_API_DEF LY_ERR ly_time_time2str(time_t time, const char *fractions_s, char **str) { struct tm tm; - char zoneshift[8]; - int32_t zonediff_h, zonediff_m; + char zoneshift[12]; + int zonediff_s, zonediff_h, zonediff_m; LY_CHECK_ARG_RET(NULL, str, LY_EINVAL); - /* initialize the local timezone */ + /* init timezone */ tzset(); -#ifdef HAVE_TM_GMTOFF /* convert */ if (!localtime_r(&time, &tm)) { return LY_ESYS; } - /* get timezone offset */ - if (tm.tm_gmtoff == 0) { - /* time is Zulu (UTC) */ - zonediff_h = 0; - zonediff_m = 0; - } else { - /* timezone offset */ - zonediff_h = tm.tm_gmtoff / 60 / 60; - zonediff_m = tm.tm_gmtoff / 60 % 60; - } + /* get timezone offset (do not use tm_gmtoff to avoid portability problems) */ + zonediff_s = ly_time_tz_offset_at(time); + zonediff_h = zonediff_s / 60 / 60; + zonediff_m = zonediff_s / 60 % 60; sprintf(zoneshift, "%+03d:%02d", zonediff_h, zonediff_m); -#else - /* convert */ - if (!gmtime_r(&time, &tm)) { - return LY_ESYS; - } - - (void)zonediff_h; - (void)zonediff_m; - sprintf(zoneshift, "-00:00"); -#endif /* print */ if (asprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d%s%s%s", |