blob: df7259188c7b1f36601c3ddbba8870a24442779c [file] [log] [blame]
Radek Krejci86d106e2018-10-18 09:53:19 +02001/**
2 * @file tree_schema_helpers.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Parsing and validation helper functions
5 *
6 * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
Radek Krejci9ed7a192018-10-31 16:23:51 +010014#include "common.h"
Radek Krejci86d106e2018-10-18 09:53:19 +020015
16#include <ctype.h>
Radek Krejci9ed7a192018-10-31 16:23:51 +010017#include <dirent.h>
18#include <errno.h>
19#include <fcntl.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020020#include <limits.h>
Radek Krejci9ed7a192018-10-31 16:23:51 +010021#include <stdlib.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <unistd.h>
Radek Krejci86d106e2018-10-18 09:53:19 +020025#include <time.h>
26
27#include "libyang.h"
Radek Krejci86d106e2018-10-18 09:53:19 +020028#include "tree_schema_internal.h"
29
30LY_ERR
31lysp_check_prefix(struct ly_parser_ctx *ctx, struct lysp_module *module, const char **value)
32{
33 struct lysp_import *i;
34
35 if (module->prefix && &module->prefix != value && !strcmp(module->prefix, *value)) {
36 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE,
37 "Prefix \"%s\" already used as module prefix.", *value);
38 return LY_EEXIST;
39 }
40 if (module->imports) {
41 LY_ARRAY_FOR(module->imports, struct lysp_import, i) {
42 if (i->prefix && &i->prefix != value && !strcmp(i->prefix, *value)) {
43 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_REFERENCE,
44 "Prefix \"%s\" already used to import \"%s\" module.", *value, i->name);
45 return LY_EEXIST;
46 }
47 }
48 }
49 return LY_SUCCESS;
50}
51
52LY_ERR
Radek Krejcibbe09a92018-11-08 09:36:54 +010053lysp_check_date(struct ly_parser_ctx *ctx, const char *date, int date_len, const char *stmt)
Radek Krejci86d106e2018-10-18 09:53:19 +020054{
55 int i;
56 struct tm tm, tm_;
57 char *r;
58
Radek Krejcibbe09a92018-11-08 09:36:54 +010059 LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, date, LY_EINVAL);
60 LY_CHECK_ERR_RET(date_len != LY_REV_SIZE - 1, LOGARG(ctx ? ctx->ctx : NULL, date_len), LY_EINVAL);
Radek Krejci86d106e2018-10-18 09:53:19 +020061
62 /* check format */
63 for (i = 0; i < date_len; i++) {
64 if (i == 4 || i == 7) {
65 if (date[i] != '-') {
66 goto error;
67 }
68 } else if (!isdigit(date[i])) {
69 goto error;
70 }
71 }
72
73 /* check content, e.g. 2018-02-31 */
74 memset(&tm, 0, sizeof tm);
75 r = strptime(date, "%Y-%m-%d", &tm);
76 if (!r || r != &date[LY_REV_SIZE - 1]) {
77 goto error;
78 }
79 memcpy(&tm_, &tm, sizeof tm);
80 mktime(&tm_); /* mktime modifies tm_ if it refers invalid date */
81 if (tm.tm_mday != tm_.tm_mday) { /* e.g 2018-02-29 -> 2018-03-01 */
82 /* checking days is enough, since other errors
83 * have been checked by strptime() */
84 goto error;
85 }
86
87 return LY_SUCCESS;
88
89error:
Radek Krejcid33273d2018-10-25 14:55:52 +020090 if (stmt) {
Radek Krejcibbe09a92018-11-08 09:36:54 +010091 if (ctx) {
92 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LY_VCODE_INVAL, date_len, date, stmt);
93 } else {
94 LOGVAL(NULL, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, date_len, date, stmt);
95 }
Radek Krejcid33273d2018-10-25 14:55:52 +020096 }
Radek Krejci86d106e2018-10-18 09:53:19 +020097 return LY_EINVAL;
98}
99
100void
101lysp_sort_revisions(struct lysp_revision *revs)
102{
103 uint8_t i, r;
104 struct lysp_revision rev;
105
106 for (i = 1, r = 0; revs && i < LY_ARRAY_SIZE(revs); i++) {
Radek Krejcib7db73a2018-10-24 14:18:40 +0200107 if (strcmp(revs[i].date, revs[r].date) > 0) {
Radek Krejci86d106e2018-10-18 09:53:19 +0200108 r = i;
109 }
110 }
111
112 if (r) {
113 /* the newest revision is not on position 0, switch them */
Radek Krejci2c4e7172018-10-19 15:56:26 +0200114 memcpy(&rev, &revs[0], sizeof rev);
115 memcpy(&revs[0], &revs[r], sizeof rev);
116 memcpy(&revs[r], &rev, sizeof rev);
Radek Krejci86d106e2018-10-18 09:53:19 +0200117 }
118}
Radek Krejci151a5b72018-10-19 14:21:44 +0200119
Radek Krejcibbe09a92018-11-08 09:36:54 +0100120static const struct lysp_tpdf *
121lysp_type_match(const char *name, struct lysp_node *node)
122{
123 struct lysp_tpdf **typedefs;
124 unsigned int u;
125
126 typedefs = lysp_node_typedefs(node);
127 if (typedefs && *typedefs) {
128 LY_ARRAY_FOR(*typedefs, u) {
129 if (!strcmp(name, (*typedefs)[u].name)) {
130 /* match */
131 return &(*typedefs)[u];
132 }
133 }
134 }
135
136 return NULL;
137}
138
139LY_ERR
140lysp_type_find(const char *id, struct lysp_node *start_node, struct lysp_module *start_module,
141 const struct lysp_tpdf **tpdf, struct lysp_node **node, struct lysp_module **module)
142{
143 const char *str, *name;
144 struct lysp_tpdf *typedefs;
145 unsigned int u, v;
146
147 assert(id);
148 assert(start_module);
149 assert(tpdf);
150 assert(node);
151 assert(module);
152
153 str = strchr(id, ':');
154 if (str) {
155 *module = lysp_module_find_prefix(start_module, id, str - id);
156 name = str + 1;
157 } else {
158 *module = start_module;
159 name = id;
160 }
161 LY_CHECK_RET(!(*module), LY_ENOTFOUND);
162
163 if (start_node && *module == start_module) {
164 /* search typedefs in parent's nodes */
165 *node = start_node;
166 while (*node) {
167 *tpdf = lysp_type_match(name, *node);
168 if (*tpdf) {
169 /* match */
170 return LY_SUCCESS;
171 }
172 *node = (*node)->parent;
173 }
174 }
175
176 /* search in top-level typedefs */
177 if ((*module)->typedefs) {
178 LY_ARRAY_FOR((*module)->typedefs, u) {
179 if (!strcmp(name, (*module)->typedefs[u].name)) {
180 /* match */
181 *tpdf = &(*module)->typedefs[u];
182 return LY_SUCCESS;
183 }
184 }
185 }
186
187 /* search in submodules' typedefs */
188 LY_ARRAY_FOR((*module)->includes, u) {
189 typedefs = (*module)->includes[u].submodule->typedefs;
190 if (typedefs) {
191 LY_ARRAY_FOR(typedefs, v) {
192 if (!strcmp(name, typedefs[v].name)) {
193 /* match */
194 *tpdf = &typedefs[v];
195 return LY_SUCCESS;
196 }
197 }
198 }
199 }
200
201 return LY_ENOTFOUND;
202}
203
204/*
205 * @brief Check name of a new type to avoid name collisions.
206 *
207 * @param[in] ctx Parser context, module where the type is being defined is taken from here.
208 * @param[in] node Schema node where the type is being defined, NULL in case of a top-level typedef.
209 * @param[in] tpdf Typedef definition to check.
210 * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
211 * typedefs are checked, caller is supposed to free the table.
212 * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
213 * typedefs are checked, caller is supposed to free the table.
214 * @return LY_EEXIST in case of collision, LY_SUCCESS otherwise.
215 */
216static LY_ERR
217lysp_check_typedef(struct ly_parser_ctx *ctx, struct lysp_node *node, struct lysp_tpdf *tpdf,
218 struct hash_table *tpdfs_global, struct hash_table *tpdfs_scoped)
219{
220 struct lysp_node *parent;
221 uint32_t hash;
222 size_t name_len;
223 const char *name;
224 unsigned int u;
225 struct lysp_tpdf **typedefs;
226
227 assert(ctx);
228 assert(tpdf);
229
230 name = tpdf->name;
231 name_len = strlen(name);
232
233 if (name_len >= 4) {
234 /* otherwise it does not match any built-in type,
235 * check collision with the built-in types */
236 if (name[0] == 'b') {
237 if (name[1] == 'i') {
238 if ((name_len == 6 && !strcmp(&name[2], "nary")) || (name_len == 4 && !strcmp(&name[2], "ts"))) {
239collision:
240 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
241 "Invalid name \"%s\" of typedef - name collision with a built-in type.", name);
242 return LY_EEXIST;
243 }
244 } else if (name_len == 7 && !strcmp(&name[1], "oolean")) {
245 goto collision;
246 }
247 } else if (name[0] == 'd') {
248 if (name_len == 9 && !strcmp(&name[1], "ecimal64")) {
249 goto collision;
250 }
251 } else if (name[0] == 'e') {
252 if ((name_len == 5 && !strcmp(&name[1], "mpty")) || (name_len == 11 && !strcmp(&name[1], "numeration"))) {
253 goto collision;
254 }
255 } else if (name[0] == 'i') {
256 if (name[1] == 'n') {
257 if ((name_len == 4 && !strcmp(&name[2], "t8")) ||
258 (name_len == 5 && (!strcmp(&name[2], "t16") || !strcmp(&name[2], "t32") || !strcmp(&name[2], "t64"))) ||
259 (name_len == 19 && !strcmp(&name[2], "stance-identifier"))) {
260 goto collision;
261 }
262 } else if (name_len == 11 && !strcmp(&name[1], "dentityref")) {
263 goto collision;
264 }
265 } else if (name[0] == 'l') {
266 if (name_len == 7 && !strcmp(&name[1], "eafref")) {
267 goto collision;
268 }
269 } else if (name[0] == 's') {
270 if (name_len == 6 && !strcmp(&name[1], "tring")) {
271 goto collision;
272 }
273 } else if (name[0] == 'u') {
274 if (name[1] == 'n') {
275 if (name_len == 5 && !strcmp(&name[2], "ion")) {
276 goto collision;
277 }
278 } else if (name[1] == 'i' && name[2] == 'n' && name[3] == 't' &&
279 ((name_len == 5 && !strcmp(&name[4], "8")) ||
280 (name_len == 6 && (!strcmp(&name[4], "16") || !strcmp(&name[4], "32") || !strcmp(&name[4], "64"))))) {
281 goto collision;
282 }
283 }
284 }
285
286 /* check locally scoped typedefs (avoid name shadowing) */
287 if (node) {
288 typedefs = lysp_node_typedefs(node);
289 if (typedefs && *typedefs) {
290 LY_ARRAY_FOR(*typedefs, u) {
291 if (typedefs[u] == tpdf) {
292 break;
293 }
294 if (!strcmp(name, (*typedefs)[u].name)) {
295 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
296 "Invalid name \"%s\" of typedef - name collision with sibling type.", name);
297 return LY_EEXIST;
298 }
299 }
300 }
301 /* search typedefs in parent's nodes */
302 for (parent = node->parent; parent; parent = node->parent) {
303 if (lysp_type_match(name, parent)) {
304 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
305 "Invalid name \"%s\" of typedef - name collision with another scoped type.", name);
306 return LY_EEXIST;
307 }
308 }
309 }
310
311 /* check collision with the top-level typedefs */
312 hash = dict_hash(name, name_len);
313 if (node) {
314 lyht_insert(tpdfs_scoped, &name, hash, NULL);
315 if (!lyht_find(tpdfs_global, &name, hash, NULL)) {
316 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
317 "Invalid name \"%s\" of typedef - scoped type collide with a top-level type.", name);
318 return LY_EEXIST;
319 }
320 } else {
321 if (lyht_insert(tpdfs_global, &name, hash, NULL)) {
322 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
323 "Invalid name \"%s\" of typedef - name collision with another top-level type.", name);
324 return LY_EEXIST;
325 }
Radek Krejci3b1f9292018-11-08 10:58:35 +0100326 /* it is not necessary to test collision with the scoped types - in lysp_check_typedefs, all the
327 * top-level typedefs are inserted into the tables before the scoped typedefs, so the collision
328 * is detected in the first branch few lines above */
Radek Krejcibbe09a92018-11-08 09:36:54 +0100329 }
330
331 return LY_SUCCESS;
332}
333
334static int
335lysp_id_cmp(void *val1, void *val2, int UNUSED(mod), void *UNUSED(cb_data))
336{
337 return !strcmp(val1, val2);
338}
339
340LY_ERR
341lysp_check_typedefs(struct ly_parser_ctx *ctx)
342{
343 struct hash_table *ids_global;
344 struct hash_table *ids_scoped;
345 struct lysp_tpdf **typedefs;
346 unsigned int i, u;
347 LY_ERR ret = LY_EVALID;
348
349 /* check name collisions - typedefs and groupings */
350 ids_global = lyht_new(8, sizeof(char*), lysp_id_cmp, NULL, 1);
351 ids_scoped = lyht_new(8, sizeof(char*), lysp_id_cmp, NULL, 1);
352 LY_ARRAY_FOR(ctx->mod->typedefs, i) {
353 if (lysp_check_typedef(ctx, NULL, &ctx->mod->typedefs[i], ids_global, ids_scoped)) {
354 goto cleanup;
355 }
356 }
Radek Krejci3b1f9292018-11-08 10:58:35 +0100357 LY_ARRAY_FOR(ctx->mod->includes, i) {
358 LY_ARRAY_FOR(ctx->mod->includes[i].submodule->typedefs, u) {
359 if (lysp_check_typedef(ctx, NULL, &ctx->mod->includes[i].submodule->typedefs[u], ids_global, ids_scoped)) {
360 goto cleanup;
361 }
362 }
363 }
Radek Krejcibbe09a92018-11-08 09:36:54 +0100364 for (u = 0; u < ctx->tpdfs_nodes.count; ++u) {
365 typedefs = lysp_node_typedefs((struct lysp_node *)ctx->tpdfs_nodes.objs[u]);
366 LY_ARRAY_FOR(*typedefs, i) {
367 if (lysp_check_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[u], &(*typedefs)[i], ids_global, ids_scoped)) {
368 goto cleanup;
369 }
370 }
371 }
372 ret = LY_SUCCESS;
373cleanup:
374 lyht_free(ids_global);
375 lyht_free(ids_scoped);
376 ly_set_erase(&ctx->tpdfs_nodes, NULL);
377
378 return ret;
379}
380
Radek Krejci086c7132018-10-26 15:29:04 +0200381void
382lys_module_implement(struct lys_module *mod)
383{
384 assert(mod);
385 if (mod->parsed) {
386 mod->parsed->implemented = 1;
387 }
388 if (mod->compiled) {
389 mod->compiled->implemented = 1;
390 }
391}
392
Radek Krejci9ed7a192018-10-31 16:23:51 +0100393struct lysp_load_module_check_data {
394 const char *name;
395 const char *revision;
396 const char *path;
397 const char* submoduleof;
398};
399
400static LY_ERR
401lysp_load_module_check(struct ly_ctx *ctx, struct lysp_module *mod, void *data)
402{
403 struct lysp_load_module_check_data *info = data;
404 const char *filename, *dot, *rev;
405 size_t len;
406
407 if (info->name) {
408 /* check name of the parsed model */
409 if (strcmp(info->name, mod->name)) {
410 LOGERR(ctx, LY_EINVAL, "Unexpected module \"%s\" parsed instead of \"%s\").", mod->name, info->name);
411 return LY_EINVAL;
412 }
413 }
414 if (info->revision) {
415 /* check revision of the parsed model */
416 if (!mod->revs || strcmp(info->revision, mod->revs[0].date)) {
417 LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").", mod->name,
418 mod->revs[0].date, info->revision);
419 return LY_EINVAL;
420 }
421 }
422 if (info->submoduleof) {
423 /* check that we have really a submodule */
424 if (!mod->submodule) {
425 /* submodule is not a submodule */
426 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Included \"%s\" schema from \"%s\" is actually not a submodule.",
427 mod->name, info->submoduleof);
428 return LY_EVALID;
429 }
430 /* check that the submodule belongs-to our module */
431 if (strcmp(info->submoduleof, mod->belongsto)) {
432 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Included \"%s\" submodule from \"%s\" belongs-to a different module \"%s\".",
433 mod->name, info->submoduleof, mod->belongsto);
434 return LY_EVALID;
435 }
436 /* check circular dependency */
437 if (mod->parsing) {
438 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "A circular dependency (include) for module \"%s\".", mod->name);
439 return LY_EVALID;
440 }
441 }
442 if (info->path) {
443 /* check that name and revision match filename */
444 filename = strrchr(info->path, '/');
445 if (!filename) {
446 filename = info->path;
447 } else {
448 filename++;
449 }
450 /* name */
451 len = strlen(mod->name);
452 rev = strchr(filename, '@');
453 dot = strrchr(info->path, '.');
454 if (strncmp(filename, mod->name, len) ||
455 ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
456 LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
457 }
458 /* revision */
459 if (rev) {
460 len = dot - ++rev;
461 if (!mod->revs || len != 10 || strncmp(mod->revs[0].date, rev, len)) {
462 LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
463 mod->revs ? mod->revs[0].date : "none");
464 }
465 }
466 }
467 return LY_SUCCESS;
468}
469
470LY_ERR
Radek Krejci3b1f9292018-11-08 10:58:35 +0100471lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement, struct ly_parser_ctx *main_ctx,
Radek Krejci9ed7a192018-10-31 16:23:51 +0100472 struct lys_module **result)
473{
474 int fd;
475 char *filepath = NULL;
476 LYS_INFORMAT format;
477 struct lys_module *mod = NULL;
478 LY_ERR ret = LY_SUCCESS;
479 struct lysp_load_module_check_data check_data = {0};
480
481 LY_CHECK_RET(lys_search_localfile(ly_ctx_get_searchdirs(ctx), !(ctx->flags & LY_CTX_DISABLE_SEARCHDIR_CWD), name, revision,
482 &filepath, &format));
483 LY_CHECK_ERR_RET(!filepath, LOGERR(ctx, LY_ENOTFOUND, "Data model \"%s%s%s\" not found in local searchdirs.",
484 name, revision ? "@" : "", revision ? revision : ""), LY_ENOTFOUND);
485
486
487 LOGVRB("Loading schema from \"%s\" file.", filepath);
488
489 /* open the file */
490 fd = open(filepath, O_RDONLY);
491 LY_CHECK_ERR_GOTO(fd < 0, LOGERR(ctx, LY_ESYS, "Unable to open data model file \"%s\" (%s).",
492 filepath, strerror(errno)); ret = LY_ESYS, cleanup);
493
494 check_data.name = name;
495 check_data.revision = revision;
496 check_data.path = filepath;
Radek Krejci3b1f9292018-11-08 10:58:35 +0100497 mod = lys_parse_fd_(ctx, fd, format, implement, main_ctx,
Radek Krejci9ed7a192018-10-31 16:23:51 +0100498 lysp_load_module_check, &check_data);
499 close(fd);
500 LY_CHECK_ERR_GOTO(!mod, ly_errcode(ctx), cleanup);
501
502 if (!mod->parsed->filepath) {
503 char rpath[PATH_MAX];
504 if (realpath(filepath, rpath) != NULL) {
505 mod->parsed->filepath = lydict_insert(ctx, rpath, 0);
506 } else {
507 mod->parsed->filepath = lydict_insert(ctx, filepath, 0);
508 }
509 }
510
511 *result = mod;
512
513 /* success */
514cleanup:
515 free(filepath);
516 return ret;
517}
518
Radek Krejcid33273d2018-10-25 14:55:52 +0200519LY_ERR
Radek Krejci6d6e4e42018-10-29 13:28:19 +0100520lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, int implement, int require_parsed, struct lys_module **mod)
Radek Krejci086c7132018-10-26 15:29:04 +0200521{
Radek Krejci9ed7a192018-10-31 16:23:51 +0100522 const char *module_data = NULL;
Radek Krejci086c7132018-10-26 15:29:04 +0200523 LYS_INFORMAT format = LYS_IN_UNKNOWN;
Radek Krejci9ed7a192018-10-31 16:23:51 +0100524 void (*module_data_free)(void *module_data, void *user_data) = NULL;
525 struct lysp_load_module_check_data check_data = {0};
Radek Krejci086c7132018-10-26 15:29:04 +0200526
527 /* try to get the module from the context */
528 if (revision) {
529 *mod = (struct lys_module*)ly_ctx_get_module(ctx, name, revision);
530 } else {
531 *mod = (struct lys_module*)ly_ctx_get_module_latest(ctx, name);
532 }
533
Radek Krejci6d6e4e42018-10-29 13:28:19 +0100534 if (!(*mod) || (require_parsed && !(*mod)->parsed)) {
535 (*mod) = NULL;
536
Radek Krejci086c7132018-10-26 15:29:04 +0200537 /* check collision with other implemented revision */
538 if (implement && ly_ctx_get_module_implemented(ctx, name)) {
539 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
540 "Module \"%s\" is already present in other implemented revision.", name);
541 return LY_EDENIED;
542 }
543
Radek Krejci9ed7a192018-10-31 16:23:51 +0100544 /* module not present in the context, get the input data and parse it */
Radek Krejci086c7132018-10-26 15:29:04 +0200545 if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
546search_clb:
547 if (ctx->imp_clb) {
548 if (ctx->imp_clb(name, revision, NULL, NULL, ctx->imp_clb_data,
Radek Krejci9ed7a192018-10-31 16:23:51 +0100549 &format, &module_data, &module_data_free) == LY_SUCCESS) {
550 check_data.name = name;
551 check_data.revision = revision;
Radek Krejci3b1f9292018-11-08 10:58:35 +0100552 *mod = lys_parse_mem_(ctx, module_data, format, implement, NULL,
Radek Krejci9ed7a192018-10-31 16:23:51 +0100553 lysp_load_module_check, &check_data);
554 if (module_data_free) {
555 module_data_free((void*)module_data, ctx->imp_clb_data);
556 }
Radek Krejci086c7132018-10-26 15:29:04 +0200557 }
558 }
559 if (!(*mod) && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
560 goto search_file;
561 }
562 } else {
563search_file:
564 if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
565 /* module was not received from the callback or there is no callback set */
Radek Krejci3b1f9292018-11-08 10:58:35 +0100566 lys_module_localfile(ctx, name, revision, implement, NULL, mod);
Radek Krejci086c7132018-10-26 15:29:04 +0200567 }
568 if (!(*mod) && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
569 goto search_clb;
570 }
571 }
Radek Krejci9ed7a192018-10-31 16:23:51 +0100572
Radek Krejcid14e9692018-11-01 11:00:37 +0100573 if ((*mod) && !revision && ((*mod)->parsed->latest_revision == 1)) {
Radek Krejci9ed7a192018-10-31 16:23:51 +0100574 /* update the latest_revision flag - here we have selected the latest available schema,
575 * consider that even the callback provides correct latest revision */
576 (*mod)->parsed->latest_revision = 2;
577 }
Radek Krejci086c7132018-10-26 15:29:04 +0200578 } else {
579 /* we have module from the current context */
580 if (implement && (ly_ctx_get_module_implemented(ctx, name) != *mod)) {
581 /* check collision with other implemented revision */
582 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
583 "Module \"%s\" is already present in other implemented revision.", name);
584 *mod = NULL;
585 return LY_EDENIED;
586 }
587
588 /* circular check */
Radek Krejcif8f882a2018-10-31 14:51:15 +0100589 if ((*mod)->parsed && (*mod)->parsed->parsing) {
Radek Krejci086c7132018-10-26 15:29:04 +0200590 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "A circular dependency (import) for module \"%s\".", name);
591 *mod = NULL;
592 return LY_EVALID;
593 }
594 }
595 if (!(*mod)) {
Radek Krejci9ed7a192018-10-31 16:23:51 +0100596 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "%s \"%s\" module failed.", implement ? "Loading" : "Importing", name);
Radek Krejci086c7132018-10-26 15:29:04 +0200597 return LY_EVALID;
598 }
599
600 if (implement) {
601 /* mark the module implemented, check for collision was already done */
602 lys_module_implement(*mod);
603 }
Radek Krejci086c7132018-10-26 15:29:04 +0200604
605 return LY_SUCCESS;
606}
607
608LY_ERR
Radek Krejci3b1f9292018-11-08 10:58:35 +0100609lysp_load_submodule(struct ly_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc)
Radek Krejcid33273d2018-10-25 14:55:52 +0200610{
611 struct lys_module *submod;
612 const char *submodule_data = NULL;
613 LYS_INFORMAT format = LYS_IN_UNKNOWN;
614 void (*submodule_data_free)(void *module_data, void *user_data) = NULL;
Radek Krejci9ed7a192018-10-31 16:23:51 +0100615 struct lysp_load_module_check_data check_data = {0};
Radek Krejcid33273d2018-10-25 14:55:52 +0200616
Radek Krejcibbe09a92018-11-08 09:36:54 +0100617 /* submodule not present in the context, get the input data and parse it */
Radek Krejci3b1f9292018-11-08 10:58:35 +0100618 if (!(ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
Radek Krejcid33273d2018-10-25 14:55:52 +0200619search_clb:
Radek Krejci3b1f9292018-11-08 10:58:35 +0100620 if (ctx->ctx->imp_clb) {
621 if (ctx->ctx->imp_clb(mod->name, NULL, inc->name, inc->rev[0] ? inc->rev : NULL, ctx->ctx->imp_clb_data,
Radek Krejcibbe09a92018-11-08 09:36:54 +0100622 &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) {
623 check_data.name = inc->name;
624 check_data.revision = inc->rev[0] ? inc->rev : NULL;
625 check_data.submoduleof = mod->name;
Radek Krejci3b1f9292018-11-08 10:58:35 +0100626 submod = lys_parse_mem_(ctx->ctx, submodule_data, format, mod->implemented, ctx,
Radek Krejcibbe09a92018-11-08 09:36:54 +0100627 lysp_load_module_check, &check_data);
628 if (submodule_data_free) {
Radek Krejci3b1f9292018-11-08 10:58:35 +0100629 submodule_data_free((void*)submodule_data, ctx->ctx->imp_clb_data);
Radek Krejcid33273d2018-10-25 14:55:52 +0200630 }
631 }
Radek Krejcid33273d2018-10-25 14:55:52 +0200632 }
Radek Krejci3b1f9292018-11-08 10:58:35 +0100633 if (!submod && !(ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100634 goto search_file;
Radek Krejcid33273d2018-10-25 14:55:52 +0200635 }
Radek Krejci2d31ea72018-10-25 15:46:42 +0200636 } else {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100637search_file:
Radek Krejci3b1f9292018-11-08 10:58:35 +0100638 if (!(ctx->ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100639 /* module was not received from the callback or there is no callback set */
Radek Krejci3b1f9292018-11-08 10:58:35 +0100640 lys_module_localfile(ctx->ctx, inc->name, inc->rev[0] ? inc->rev : NULL, mod->implemented, ctx, &submod);
Radek Krejcibbe09a92018-11-08 09:36:54 +0100641 }
Radek Krejci3b1f9292018-11-08 10:58:35 +0100642 if (!submod && (ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100643 goto search_clb;
644 }
645 }
646 if (submod) {
647 if (!inc->rev[0] && (submod->parsed->latest_revision == 1)) {
648 /* update the latest_revision flag - here we have selected the latest available schema,
649 * consider that even the callback provides correct latest revision */
650 submod->parsed->latest_revision = 2;
651 }
652
653 inc->submodule = submod->parsed;
654 free(submod);
Radek Krejcid33273d2018-10-25 14:55:52 +0200655 }
656 if (!inc->submodule) {
Radek Krejci3b1f9292018-11-08 10:58:35 +0100657 LOGVAL(ctx->ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Including \"%s\" submodule into \"%s\" failed.", inc->name, mod->name);
Radek Krejcid33273d2018-10-25 14:55:52 +0200658 return LY_EVALID;
659 }
660
661 return LY_SUCCESS;
662}
663
Radek Krejcibbe09a92018-11-08 09:36:54 +0100664#define FIND_MODULE(TYPE, MOD, ID) \
Radek Krejcice8c1592018-10-29 15:35:51 +0100665 TYPE *imp; \
666 if (!strncmp((MOD)->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
667 /* it is the prefix of the module itself */ \
Radek Krejcibbe09a92018-11-08 09:36:54 +0100668 m = ly_ctx_get_module((MOD)->ctx, (MOD)->name, ((struct lysc_module*)(MOD))->revision); \
Radek Krejcice8c1592018-10-29 15:35:51 +0100669 } \
670 /* search in imports */ \
Radek Krejcibbe09a92018-11-08 09:36:54 +0100671 if (!m) { \
672 LY_ARRAY_FOR((MOD)->imports, TYPE, imp) { \
673 if (!strncmp(imp->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
674 m = imp->module; \
675 break; \
676 } \
Radek Krejcice8c1592018-10-29 15:35:51 +0100677 } \
Radek Krejcibbe09a92018-11-08 09:36:54 +0100678 }
Radek Krejcice8c1592018-10-29 15:35:51 +0100679
Radek Krejcibbe09a92018-11-08 09:36:54 +0100680struct lysc_module *
Radek Krejci151a5b72018-10-19 14:21:44 +0200681lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len)
682{
Radek Krejcibbe09a92018-11-08 09:36:54 +0100683 const struct lys_module *m = NULL;
684
685 FIND_MODULE(struct lysc_import, mod, 1);
686 return m ? m->compiled : NULL;
Radek Krejcice8c1592018-10-29 15:35:51 +0100687}
Radek Krejci151a5b72018-10-19 14:21:44 +0200688
Radek Krejcibbe09a92018-11-08 09:36:54 +0100689struct lysp_module *
Radek Krejcice8c1592018-10-29 15:35:51 +0100690lysp_module_find_prefix(struct lysp_module *mod, const char *prefix, size_t len)
691{
Radek Krejcibbe09a92018-11-08 09:36:54 +0100692 const struct lys_module *m = NULL;
693
694 FIND_MODULE(struct lysp_import, mod, 1);
695 return m ? m->parsed : NULL;
Radek Krejcice8c1592018-10-29 15:35:51 +0100696}
Radek Krejci151a5b72018-10-19 14:21:44 +0200697
Radek Krejcice8c1592018-10-29 15:35:51 +0100698struct lys_module *
699lys_module_find_prefix(struct lys_module *mod, const char *prefix, size_t len)
700{
Radek Krejcibbe09a92018-11-08 09:36:54 +0100701 const struct lys_module *m = NULL;
702
Radek Krejcice8c1592018-10-29 15:35:51 +0100703 if (mod->compiled) {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100704 FIND_MODULE(struct lysc_import, mod->compiled, 1);
Radek Krejcice8c1592018-10-29 15:35:51 +0100705 } else {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100706 FIND_MODULE(struct lysp_import, mod->parsed, 2);
707 }
708 return (struct lys_module*)m;
709}
710
711struct lysp_tpdf **
712lysp_node_typedefs(struct lysp_node *node)
713{
714 switch (node->nodetype) {
715 case LYS_CONTAINER:
716 return &((struct lysp_node_container*)node)->typedefs;
717 case LYS_LIST:
718 return &((struct lysp_node_list*)node)->typedefs;
719 case LYS_GROUPING:
720 return &((struct lysp_grp*)node)->typedefs;
721 case LYS_ACTION:
722 return &((struct lysp_action*)node)->typedefs;
723 case LYS_INOUT:
724 return &((struct lysp_action_inout*)node)->typedefs;
725 case LYS_NOTIF:
726 return &((struct lysp_notif*)node)->typedefs;
727 default:
728 return NULL;
Radek Krejci151a5b72018-10-19 14:21:44 +0200729 }
Radek Krejci151a5b72018-10-19 14:21:44 +0200730}
Radek Krejcibbe09a92018-11-08 09:36:54 +0100731
732struct lysp_action **
733lysp_node_actions(struct lysp_node *node)
734{
735 assert(node);
736 switch (node->nodetype) {
737 case LYS_CONTAINER:
738 return &((struct lysp_node_container*)node)->actions;
739 case LYS_LIST:
740 return &((struct lysp_node_list*)node)->actions;
741 case LYS_GROUPING:
742 return &((struct lysp_grp*)node)->actions;
743 case LYS_AUGMENT:
744 return &((struct lysp_augment*)node)->actions;
745 default:
746 return NULL;
747 }
748}
749
750struct lysp_notif **
751lysp_node_notifs(struct lysp_node *node)
752{
753 assert(node);
754 switch (node->nodetype) {
755 case LYS_CONTAINER:
756 return &((struct lysp_node_container*)node)->notifs;
757 case LYS_LIST:
758 return &((struct lysp_node_list*)node)->notifs;
759 case LYS_GROUPING:
760 return &((struct lysp_grp*)node)->notifs;
761 case LYS_AUGMENT:
762 return &((struct lysp_augment*)node)->notifs;
763 default:
764 return NULL;
765 }
766}
767
768struct lysp_node **
769lysp_node_children(struct lysp_node *node)
770{
771 assert(node);
772 switch (node->nodetype) {
773 case LYS_CONTAINER:
774 return &((struct lysp_node_container*)node)->child;
775 case LYS_CHOICE:
776 return &((struct lysp_node_choice*)node)->child;
777 case LYS_LIST:
778 return &((struct lysp_node_list*)node)->child;
779 case LYS_CASE:
780 return &((struct lysp_node_case*)node)->child;
781 case LYS_GROUPING:
782 return &((struct lysp_grp*)node)->data;
783 case LYS_AUGMENT:
784 return &((struct lysp_augment*)node)->child;
785 case LYS_INOUT:
786 return &((struct lysp_action_inout*)node)->data;
787 case LYS_NOTIF:
788 return &((struct lysp_notif*)node)->data;
789 default:
790 return NULL;
791 }
792}
793
794struct lysc_node **
795lysc_node_children(struct lysc_node *node)
796{
797 assert(node);
798 switch (node->nodetype) {
799 case LYS_CONTAINER:
800 return &((struct lysc_node_container*)node)->child;
801 case LYS_CHOICE:
802 return &((struct lysc_node_choice*)node)->child;
803 case LYS_LIST:
804 return &((struct lysc_node_list*)node)->child;
805 case LYS_CASE:
806 return &((struct lysc_node_case*)node)->child;
807 case LYS_USES:
808 return &((struct lysc_node_uses*)node)->child;
809/* TODO
810 case LYS_INOUT:
811 return &((struct lysc_action_inout*)node)->child;
812 case LYS_NOTIF:
813 return &((struct lysc_notif*)node)->child;
814*/
815 default:
816 return NULL;
817 }
818}
819