blob: f7904d3123377193c7ad3071621fd62ef2d9950b [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 }
326 if (!lyht_find(tpdfs_scoped, &name, hash, NULL)) {
327 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
328 "Invalid name \"%s\" of typedef - top-level type collide with a scoped type.", name);
329 return LY_EEXIST;
330 }
331 }
332
333 return LY_SUCCESS;
334}
335
336static int
337lysp_id_cmp(void *val1, void *val2, int UNUSED(mod), void *UNUSED(cb_data))
338{
339 return !strcmp(val1, val2);
340}
341
342LY_ERR
343lysp_check_typedefs(struct ly_parser_ctx *ctx)
344{
345 struct hash_table *ids_global;
346 struct hash_table *ids_scoped;
347 struct lysp_tpdf **typedefs;
348 unsigned int i, u;
349 LY_ERR ret = LY_EVALID;
350
351 /* check name collisions - typedefs and groupings */
352 ids_global = lyht_new(8, sizeof(char*), lysp_id_cmp, NULL, 1);
353 ids_scoped = lyht_new(8, sizeof(char*), lysp_id_cmp, NULL, 1);
354 LY_ARRAY_FOR(ctx->mod->typedefs, i) {
355 if (lysp_check_typedef(ctx, NULL, &ctx->mod->typedefs[i], ids_global, ids_scoped)) {
356 goto cleanup;
357 }
358 }
359 for (u = 0; u < ctx->tpdfs_nodes.count; ++u) {
360 typedefs = lysp_node_typedefs((struct lysp_node *)ctx->tpdfs_nodes.objs[u]);
361 LY_ARRAY_FOR(*typedefs, i) {
362 if (lysp_check_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[u], &(*typedefs)[i], ids_global, ids_scoped)) {
363 goto cleanup;
364 }
365 }
366 }
367 ret = LY_SUCCESS;
368cleanup:
369 lyht_free(ids_global);
370 lyht_free(ids_scoped);
371 ly_set_erase(&ctx->tpdfs_nodes, NULL);
372
373 return ret;
374}
375
Radek Krejci086c7132018-10-26 15:29:04 +0200376void
377lys_module_implement(struct lys_module *mod)
378{
379 assert(mod);
380 if (mod->parsed) {
381 mod->parsed->implemented = 1;
382 }
383 if (mod->compiled) {
384 mod->compiled->implemented = 1;
385 }
386}
387
Radek Krejci9ed7a192018-10-31 16:23:51 +0100388struct lysp_load_module_check_data {
389 const char *name;
390 const char *revision;
391 const char *path;
392 const char* submoduleof;
393};
394
395static LY_ERR
396lysp_load_module_check(struct ly_ctx *ctx, struct lysp_module *mod, void *data)
397{
398 struct lysp_load_module_check_data *info = data;
399 const char *filename, *dot, *rev;
400 size_t len;
401
402 if (info->name) {
403 /* check name of the parsed model */
404 if (strcmp(info->name, mod->name)) {
405 LOGERR(ctx, LY_EINVAL, "Unexpected module \"%s\" parsed instead of \"%s\").", mod->name, info->name);
406 return LY_EINVAL;
407 }
408 }
409 if (info->revision) {
410 /* check revision of the parsed model */
411 if (!mod->revs || strcmp(info->revision, mod->revs[0].date)) {
412 LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").", mod->name,
413 mod->revs[0].date, info->revision);
414 return LY_EINVAL;
415 }
416 }
417 if (info->submoduleof) {
418 /* check that we have really a submodule */
419 if (!mod->submodule) {
420 /* submodule is not a submodule */
421 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Included \"%s\" schema from \"%s\" is actually not a submodule.",
422 mod->name, info->submoduleof);
423 return LY_EVALID;
424 }
425 /* check that the submodule belongs-to our module */
426 if (strcmp(info->submoduleof, mod->belongsto)) {
427 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Included \"%s\" submodule from \"%s\" belongs-to a different module \"%s\".",
428 mod->name, info->submoduleof, mod->belongsto);
429 return LY_EVALID;
430 }
431 /* check circular dependency */
432 if (mod->parsing) {
433 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "A circular dependency (include) for module \"%s\".", mod->name);
434 return LY_EVALID;
435 }
436 }
437 if (info->path) {
438 /* check that name and revision match filename */
439 filename = strrchr(info->path, '/');
440 if (!filename) {
441 filename = info->path;
442 } else {
443 filename++;
444 }
445 /* name */
446 len = strlen(mod->name);
447 rev = strchr(filename, '@');
448 dot = strrchr(info->path, '.');
449 if (strncmp(filename, mod->name, len) ||
450 ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
451 LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
452 }
453 /* revision */
454 if (rev) {
455 len = dot - ++rev;
456 if (!mod->revs || len != 10 || strncmp(mod->revs[0].date, rev, len)) {
457 LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
458 mod->revs ? mod->revs[0].date : "none");
459 }
460 }
461 }
462 return LY_SUCCESS;
463}
464
465LY_ERR
466lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement,
467 struct lys_module **result)
468{
469 int fd;
470 char *filepath = NULL;
471 LYS_INFORMAT format;
472 struct lys_module *mod = NULL;
473 LY_ERR ret = LY_SUCCESS;
474 struct lysp_load_module_check_data check_data = {0};
475
476 LY_CHECK_RET(lys_search_localfile(ly_ctx_get_searchdirs(ctx), !(ctx->flags & LY_CTX_DISABLE_SEARCHDIR_CWD), name, revision,
477 &filepath, &format));
478 LY_CHECK_ERR_RET(!filepath, LOGERR(ctx, LY_ENOTFOUND, "Data model \"%s%s%s\" not found in local searchdirs.",
479 name, revision ? "@" : "", revision ? revision : ""), LY_ENOTFOUND);
480
481
482 LOGVRB("Loading schema from \"%s\" file.", filepath);
483
484 /* open the file */
485 fd = open(filepath, O_RDONLY);
486 LY_CHECK_ERR_GOTO(fd < 0, LOGERR(ctx, LY_ESYS, "Unable to open data model file \"%s\" (%s).",
487 filepath, strerror(errno)); ret = LY_ESYS, cleanup);
488
489 check_data.name = name;
490 check_data.revision = revision;
491 check_data.path = filepath;
492 mod = lys_parse_fd_(ctx, fd, format, implement,
493 lysp_load_module_check, &check_data);
494 close(fd);
495 LY_CHECK_ERR_GOTO(!mod, ly_errcode(ctx), cleanup);
496
497 if (!mod->parsed->filepath) {
498 char rpath[PATH_MAX];
499 if (realpath(filepath, rpath) != NULL) {
500 mod->parsed->filepath = lydict_insert(ctx, rpath, 0);
501 } else {
502 mod->parsed->filepath = lydict_insert(ctx, filepath, 0);
503 }
504 }
505
506 *result = mod;
507
508 /* success */
509cleanup:
510 free(filepath);
511 return ret;
512}
513
Radek Krejcid33273d2018-10-25 14:55:52 +0200514LY_ERR
Radek Krejci6d6e4e42018-10-29 13:28:19 +0100515lysp_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 +0200516{
Radek Krejci9ed7a192018-10-31 16:23:51 +0100517 const char *module_data = NULL;
Radek Krejci086c7132018-10-26 15:29:04 +0200518 LYS_INFORMAT format = LYS_IN_UNKNOWN;
Radek Krejci9ed7a192018-10-31 16:23:51 +0100519 void (*module_data_free)(void *module_data, void *user_data) = NULL;
520 struct lysp_load_module_check_data check_data = {0};
Radek Krejci086c7132018-10-26 15:29:04 +0200521
522 /* try to get the module from the context */
523 if (revision) {
524 *mod = (struct lys_module*)ly_ctx_get_module(ctx, name, revision);
525 } else {
526 *mod = (struct lys_module*)ly_ctx_get_module_latest(ctx, name);
527 }
528
Radek Krejci6d6e4e42018-10-29 13:28:19 +0100529 if (!(*mod) || (require_parsed && !(*mod)->parsed)) {
530 (*mod) = NULL;
531
Radek Krejci086c7132018-10-26 15:29:04 +0200532 /* check collision with other implemented revision */
533 if (implement && ly_ctx_get_module_implemented(ctx, name)) {
534 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
535 "Module \"%s\" is already present in other implemented revision.", name);
536 return LY_EDENIED;
537 }
538
Radek Krejci9ed7a192018-10-31 16:23:51 +0100539 /* module not present in the context, get the input data and parse it */
Radek Krejci086c7132018-10-26 15:29:04 +0200540 if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
541search_clb:
542 if (ctx->imp_clb) {
543 if (ctx->imp_clb(name, revision, NULL, NULL, ctx->imp_clb_data,
Radek Krejci9ed7a192018-10-31 16:23:51 +0100544 &format, &module_data, &module_data_free) == LY_SUCCESS) {
545 check_data.name = name;
546 check_data.revision = revision;
547 *mod = lys_parse_mem_(ctx, module_data, format, implement,
548 lysp_load_module_check, &check_data);
549 if (module_data_free) {
550 module_data_free((void*)module_data, ctx->imp_clb_data);
551 }
Radek Krejci086c7132018-10-26 15:29:04 +0200552 }
553 }
554 if (!(*mod) && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
555 goto search_file;
556 }
557 } else {
558search_file:
559 if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
560 /* module was not received from the callback or there is no callback set */
561 lys_module_localfile(ctx, name, revision, implement, mod);
562 }
563 if (!(*mod) && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
564 goto search_clb;
565 }
566 }
Radek Krejci9ed7a192018-10-31 16:23:51 +0100567
Radek Krejcid14e9692018-11-01 11:00:37 +0100568 if ((*mod) && !revision && ((*mod)->parsed->latest_revision == 1)) {
Radek Krejci9ed7a192018-10-31 16:23:51 +0100569 /* update the latest_revision flag - here we have selected the latest available schema,
570 * consider that even the callback provides correct latest revision */
571 (*mod)->parsed->latest_revision = 2;
572 }
Radek Krejci086c7132018-10-26 15:29:04 +0200573 } else {
574 /* we have module from the current context */
575 if (implement && (ly_ctx_get_module_implemented(ctx, name) != *mod)) {
576 /* check collision with other implemented revision */
577 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
578 "Module \"%s\" is already present in other implemented revision.", name);
579 *mod = NULL;
580 return LY_EDENIED;
581 }
582
583 /* circular check */
Radek Krejcif8f882a2018-10-31 14:51:15 +0100584 if ((*mod)->parsed && (*mod)->parsed->parsing) {
Radek Krejci086c7132018-10-26 15:29:04 +0200585 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "A circular dependency (import) for module \"%s\".", name);
586 *mod = NULL;
587 return LY_EVALID;
588 }
589 }
590 if (!(*mod)) {
Radek Krejci9ed7a192018-10-31 16:23:51 +0100591 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "%s \"%s\" module failed.", implement ? "Loading" : "Importing", name);
Radek Krejci086c7132018-10-26 15:29:04 +0200592 return LY_EVALID;
593 }
594
595 if (implement) {
596 /* mark the module implemented, check for collision was already done */
597 lys_module_implement(*mod);
598 }
Radek Krejci086c7132018-10-26 15:29:04 +0200599
600 return LY_SUCCESS;
601}
602
603LY_ERR
604lysp_load_submodule(struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc)
Radek Krejcid33273d2018-10-25 14:55:52 +0200605{
606 struct lys_module *submod;
607 const char *submodule_data = NULL;
608 LYS_INFORMAT format = LYS_IN_UNKNOWN;
609 void (*submodule_data_free)(void *module_data, void *user_data) = NULL;
Radek Krejci9ed7a192018-10-31 16:23:51 +0100610 struct lysp_load_module_check_data check_data = {0};
Radek Krejcid33273d2018-10-25 14:55:52 +0200611
Radek Krejcibbe09a92018-11-08 09:36:54 +0100612 /* submodule not present in the context, get the input data and parse it */
613 if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
Radek Krejcid33273d2018-10-25 14:55:52 +0200614search_clb:
Radek Krejcibbe09a92018-11-08 09:36:54 +0100615 if (ctx->imp_clb) {
616 if (ctx->imp_clb(mod->name, NULL, inc->name, inc->rev[0] ? inc->rev : NULL, ctx->imp_clb_data,
617 &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) {
618 check_data.name = inc->name;
619 check_data.revision = inc->rev[0] ? inc->rev : NULL;
620 check_data.submoduleof = mod->name;
621 submod = lys_parse_mem_(ctx, submodule_data, format, mod->implemented,
622 lysp_load_module_check, &check_data);
623 if (submodule_data_free) {
624 submodule_data_free((void*)submodule_data, ctx->imp_clb_data);
Radek Krejcid33273d2018-10-25 14:55:52 +0200625 }
626 }
Radek Krejcid33273d2018-10-25 14:55:52 +0200627 }
Radek Krejcibbe09a92018-11-08 09:36:54 +0100628 if (!submod && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
629 goto search_file;
Radek Krejcid33273d2018-10-25 14:55:52 +0200630 }
Radek Krejci2d31ea72018-10-25 15:46:42 +0200631 } else {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100632search_file:
633 if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
634 /* module was not received from the callback or there is no callback set */
635 lys_module_localfile(ctx, inc->name, inc->rev[0] ? inc->rev : NULL, mod->implemented, &submod);
636 }
637 if (!submod && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
638 goto search_clb;
639 }
640 }
641 if (submod) {
642 if (!inc->rev[0] && (submod->parsed->latest_revision == 1)) {
643 /* update the latest_revision flag - here we have selected the latest available schema,
644 * consider that even the callback provides correct latest revision */
645 submod->parsed->latest_revision = 2;
646 }
647
648 inc->submodule = submod->parsed;
649 free(submod);
Radek Krejcid33273d2018-10-25 14:55:52 +0200650 }
651 if (!inc->submodule) {
Radek Krejci9ed7a192018-10-31 16:23:51 +0100652 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Including \"%s\" submodule into \"%s\" failed.", inc->name, mod->name);
Radek Krejcid33273d2018-10-25 14:55:52 +0200653 return LY_EVALID;
654 }
655
656 return LY_SUCCESS;
657}
658
Radek Krejcibbe09a92018-11-08 09:36:54 +0100659#define FIND_MODULE(TYPE, MOD, ID) \
Radek Krejcice8c1592018-10-29 15:35:51 +0100660 TYPE *imp; \
661 if (!strncmp((MOD)->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
662 /* it is the prefix of the module itself */ \
Radek Krejcibbe09a92018-11-08 09:36:54 +0100663 m = ly_ctx_get_module((MOD)->ctx, (MOD)->name, ((struct lysc_module*)(MOD))->revision); \
Radek Krejcice8c1592018-10-29 15:35:51 +0100664 } \
665 /* search in imports */ \
Radek Krejcibbe09a92018-11-08 09:36:54 +0100666 if (!m) { \
667 LY_ARRAY_FOR((MOD)->imports, TYPE, imp) { \
668 if (!strncmp(imp->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
669 m = imp->module; \
670 break; \
671 } \
Radek Krejcice8c1592018-10-29 15:35:51 +0100672 } \
Radek Krejcibbe09a92018-11-08 09:36:54 +0100673 }
Radek Krejcice8c1592018-10-29 15:35:51 +0100674
Radek Krejcibbe09a92018-11-08 09:36:54 +0100675struct lysc_module *
Radek Krejci151a5b72018-10-19 14:21:44 +0200676lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len)
677{
Radek Krejcibbe09a92018-11-08 09:36:54 +0100678 const struct lys_module *m = NULL;
679
680 FIND_MODULE(struct lysc_import, mod, 1);
681 return m ? m->compiled : NULL;
Radek Krejcice8c1592018-10-29 15:35:51 +0100682}
Radek Krejci151a5b72018-10-19 14:21:44 +0200683
Radek Krejcibbe09a92018-11-08 09:36:54 +0100684struct lysp_module *
Radek Krejcice8c1592018-10-29 15:35:51 +0100685lysp_module_find_prefix(struct lysp_module *mod, const char *prefix, size_t len)
686{
Radek Krejcibbe09a92018-11-08 09:36:54 +0100687 const struct lys_module *m = NULL;
688
689 FIND_MODULE(struct lysp_import, mod, 1);
690 return m ? m->parsed : NULL;
Radek Krejcice8c1592018-10-29 15:35:51 +0100691}
Radek Krejci151a5b72018-10-19 14:21:44 +0200692
Radek Krejcice8c1592018-10-29 15:35:51 +0100693struct lys_module *
694lys_module_find_prefix(struct lys_module *mod, const char *prefix, size_t len)
695{
Radek Krejcibbe09a92018-11-08 09:36:54 +0100696 const struct lys_module *m = NULL;
697
Radek Krejcice8c1592018-10-29 15:35:51 +0100698 if (mod->compiled) {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100699 FIND_MODULE(struct lysc_import, mod->compiled, 1);
Radek Krejcice8c1592018-10-29 15:35:51 +0100700 } else {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100701 FIND_MODULE(struct lysp_import, mod->parsed, 2);
702 }
703 return (struct lys_module*)m;
704}
705
706struct lysp_tpdf **
707lysp_node_typedefs(struct lysp_node *node)
708{
709 switch (node->nodetype) {
710 case LYS_CONTAINER:
711 return &((struct lysp_node_container*)node)->typedefs;
712 case LYS_LIST:
713 return &((struct lysp_node_list*)node)->typedefs;
714 case LYS_GROUPING:
715 return &((struct lysp_grp*)node)->typedefs;
716 case LYS_ACTION:
717 return &((struct lysp_action*)node)->typedefs;
718 case LYS_INOUT:
719 return &((struct lysp_action_inout*)node)->typedefs;
720 case LYS_NOTIF:
721 return &((struct lysp_notif*)node)->typedefs;
722 default:
723 return NULL;
Radek Krejci151a5b72018-10-19 14:21:44 +0200724 }
Radek Krejci151a5b72018-10-19 14:21:44 +0200725}
Radek Krejcibbe09a92018-11-08 09:36:54 +0100726
727struct lysp_action **
728lysp_node_actions(struct lysp_node *node)
729{
730 assert(node);
731 switch (node->nodetype) {
732 case LYS_CONTAINER:
733 return &((struct lysp_node_container*)node)->actions;
734 case LYS_LIST:
735 return &((struct lysp_node_list*)node)->actions;
736 case LYS_GROUPING:
737 return &((struct lysp_grp*)node)->actions;
738 case LYS_AUGMENT:
739 return &((struct lysp_augment*)node)->actions;
740 default:
741 return NULL;
742 }
743}
744
745struct lysp_notif **
746lysp_node_notifs(struct lysp_node *node)
747{
748 assert(node);
749 switch (node->nodetype) {
750 case LYS_CONTAINER:
751 return &((struct lysp_node_container*)node)->notifs;
752 case LYS_LIST:
753 return &((struct lysp_node_list*)node)->notifs;
754 case LYS_GROUPING:
755 return &((struct lysp_grp*)node)->notifs;
756 case LYS_AUGMENT:
757 return &((struct lysp_augment*)node)->notifs;
758 default:
759 return NULL;
760 }
761}
762
763struct lysp_node **
764lysp_node_children(struct lysp_node *node)
765{
766 assert(node);
767 switch (node->nodetype) {
768 case LYS_CONTAINER:
769 return &((struct lysp_node_container*)node)->child;
770 case LYS_CHOICE:
771 return &((struct lysp_node_choice*)node)->child;
772 case LYS_LIST:
773 return &((struct lysp_node_list*)node)->child;
774 case LYS_CASE:
775 return &((struct lysp_node_case*)node)->child;
776 case LYS_GROUPING:
777 return &((struct lysp_grp*)node)->data;
778 case LYS_AUGMENT:
779 return &((struct lysp_augment*)node)->child;
780 case LYS_INOUT:
781 return &((struct lysp_action_inout*)node)->data;
782 case LYS_NOTIF:
783 return &((struct lysp_notif*)node)->data;
784 default:
785 return NULL;
786 }
787}
788
789struct lysc_node **
790lysc_node_children(struct lysc_node *node)
791{
792 assert(node);
793 switch (node->nodetype) {
794 case LYS_CONTAINER:
795 return &((struct lysc_node_container*)node)->child;
796 case LYS_CHOICE:
797 return &((struct lysc_node_choice*)node)->child;
798 case LYS_LIST:
799 return &((struct lysc_node_list*)node)->child;
800 case LYS_CASE:
801 return &((struct lysc_node_case*)node)->child;
802 case LYS_USES:
803 return &((struct lysc_node_uses*)node)->child;
804/* TODO
805 case LYS_INOUT:
806 return &((struct lysc_action_inout*)node)->child;
807 case LYS_NOTIF:
808 return &((struct lysc_notif*)node)->child;
809*/
810 default:
811 return NULL;
812 }
813}
814