blob: 52b6f613013f4525468a519dd5c6e538d403fab8 [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 Krejci4f28eda2018-11-12 11:46:16 +010053lysc_check_status(struct lysc_ctx *ctx,
54 uint16_t flags1, void *mod1, const char *name1,
55 uint16_t flags2, void *mod2, const char *name2)
56{
57 uint16_t flg1, flg2;
58
59 flg1 = (flags1 & LYS_STATUS_MASK) ? (flags1 & LYS_STATUS_MASK) : LYS_STATUS_CURR;
60 flg2 = (flags2 & LYS_STATUS_MASK) ? (flags2 & LYS_STATUS_MASK) : LYS_STATUS_CURR;
61
62 if ((flg1 < flg2) && (mod1 == mod2)) {
63 if (ctx) {
64 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
65 "A %s definition \"%s\" is not allowed to reference %s definition \"%s\".",
66 flg1 == LYS_STATUS_CURR ? "current" : "deprecated", name1,
67 flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", name2);
68 }
69 return LY_EVALID;
70 }
71
72 return LY_SUCCESS;
73}
74
75LY_ERR
Radek Krejcibbe09a92018-11-08 09:36:54 +010076lysp_check_date(struct ly_parser_ctx *ctx, const char *date, int date_len, const char *stmt)
Radek Krejci86d106e2018-10-18 09:53:19 +020077{
78 int i;
79 struct tm tm, tm_;
80 char *r;
81
Radek Krejcibbe09a92018-11-08 09:36:54 +010082 LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, date, LY_EINVAL);
83 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 +020084
85 /* check format */
86 for (i = 0; i < date_len; i++) {
87 if (i == 4 || i == 7) {
88 if (date[i] != '-') {
89 goto error;
90 }
91 } else if (!isdigit(date[i])) {
92 goto error;
93 }
94 }
95
96 /* check content, e.g. 2018-02-31 */
97 memset(&tm, 0, sizeof tm);
98 r = strptime(date, "%Y-%m-%d", &tm);
99 if (!r || r != &date[LY_REV_SIZE - 1]) {
100 goto error;
101 }
102 memcpy(&tm_, &tm, sizeof tm);
103 mktime(&tm_); /* mktime modifies tm_ if it refers invalid date */
104 if (tm.tm_mday != tm_.tm_mday) { /* e.g 2018-02-29 -> 2018-03-01 */
105 /* checking days is enough, since other errors
106 * have been checked by strptime() */
107 goto error;
108 }
109
110 return LY_SUCCESS;
111
112error:
Radek Krejcid33273d2018-10-25 14:55:52 +0200113 if (stmt) {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100114 if (ctx) {
115 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LY_VCODE_INVAL, date_len, date, stmt);
116 } else {
117 LOGVAL(NULL, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, date_len, date, stmt);
118 }
Radek Krejcid33273d2018-10-25 14:55:52 +0200119 }
Radek Krejci86d106e2018-10-18 09:53:19 +0200120 return LY_EINVAL;
121}
122
123void
124lysp_sort_revisions(struct lysp_revision *revs)
125{
126 uint8_t i, r;
127 struct lysp_revision rev;
128
129 for (i = 1, r = 0; revs && i < LY_ARRAY_SIZE(revs); i++) {
Radek Krejcib7db73a2018-10-24 14:18:40 +0200130 if (strcmp(revs[i].date, revs[r].date) > 0) {
Radek Krejci86d106e2018-10-18 09:53:19 +0200131 r = i;
132 }
133 }
134
135 if (r) {
136 /* the newest revision is not on position 0, switch them */
Radek Krejci2c4e7172018-10-19 15:56:26 +0200137 memcpy(&rev, &revs[0], sizeof rev);
138 memcpy(&revs[0], &revs[r], sizeof rev);
139 memcpy(&revs[r], &rev, sizeof rev);
Radek Krejci86d106e2018-10-18 09:53:19 +0200140 }
141}
Radek Krejci151a5b72018-10-19 14:21:44 +0200142
Radek Krejcibbe09a92018-11-08 09:36:54 +0100143static const struct lysp_tpdf *
144lysp_type_match(const char *name, struct lysp_node *node)
145{
146 struct lysp_tpdf **typedefs;
147 unsigned int u;
148
149 typedefs = lysp_node_typedefs(node);
150 if (typedefs && *typedefs) {
151 LY_ARRAY_FOR(*typedefs, u) {
152 if (!strcmp(name, (*typedefs)[u].name)) {
153 /* match */
154 return &(*typedefs)[u];
155 }
156 }
157 }
158
159 return NULL;
160}
161
Radek Krejci4f28eda2018-11-12 11:46:16 +0100162static LY_DATA_TYPE
163lysp_type_str2builtin(const char *name, size_t len)
164{
165 if (len >= 4) { /* otherwise it does not match any built-in type */
166 if (name[0] == 'b') {
167 if (name[1] == 'i') {
168 if (len == 6 && !strncmp(&name[2], "nary", 4)) {
169 return LY_TYPE_BINARY;
170 } else if (len == 4 && !strncmp(&name[2], "ts", 2)) {
171 return LY_TYPE_BITS;
172 }
173 } else if (len == 7 && !strncmp(&name[1], "oolean", 6)) {
174 return LY_TYPE_BOOL;
175 }
176 } else if (name[0] == 'd') {
177 if (len == 9 && !strncmp(&name[1], "ecimal64", 8)) {
178 return LY_TYPE_DEC64;
179 }
180 } else if (name[0] == 'e') {
181 if (len == 5 && !strncmp(&name[1], "mpty", 4)) {
182 return LY_TYPE_EMPTY;
183 } else if (len == 11 && !strncmp(&name[1], "numeration", 10)) {
184 return LY_TYPE_ENUM;
185 }
186 } else if (name[0] == 'i') {
187 if (name[1] == 'n') {
188 if (len == 4 && !strncmp(&name[2], "t8", 2)) {
189 return LY_TYPE_INT8;
190 } else if (len == 5) {
191 if (!strncmp(&name[2], "t16", 3)) {
192 return LY_TYPE_INT16;
193 } else if (!strncmp(&name[2], "t32", 3)) {
194 return LY_TYPE_INT32;
195 } else if (!strncmp(&name[2], "t64", 3)) {
196 return LY_TYPE_INT64;
197 }
198 } else if (len == 19 && !strncmp(&name[2], "stance-identifier", 17)) {
199 return LY_TYPE_INST;
200 }
201 } else if (len == 11 && !strncmp(&name[1], "dentityref", 10)) {
202 return LY_TYPE_IDENT;
203 }
204 } else if (name[0] == 'l') {
205 if (len == 7 && !strncmp(&name[1], "eafref", 6)) {
206 return LY_TYPE_LEAFREF;
207 }
208 } else if (name[0] == 's') {
209 if (len == 6 && !strncmp(&name[1], "tring", 5)) {
210 return LY_TYPE_STRING;
211 }
212 } else if (name[0] == 'u') {
213 if (name[1] == 'n') {
214 if (len == 5 && !strncmp(&name[2], "ion", 3)) {
215 return LY_TYPE_UNION;
216 }
217 } else if (name[1] == 'i' && name[2] == 'n' && name[3] == 't') {
218 if (len == 5 && name[4] == '8') {
219 return LY_TYPE_UINT8;
220 } else if (len == 6) {
221 if (!strncmp(&name[4], "16", 2)) {
222 return LY_TYPE_UINT16;
223 } else if (!strncmp(&name[4], "32", 2)) {
224 return LY_TYPE_UINT32;
225 } else if (!strncmp(&name[4], "64", 2)) {
226 return LY_TYPE_UINT64;
227 }
228 }
229 }
230 }
231 }
232
233 return LY_TYPE_UNKNOWN;
234}
235
Radek Krejcibbe09a92018-11-08 09:36:54 +0100236LY_ERR
237lysp_type_find(const char *id, struct lysp_node *start_node, struct lysp_module *start_module,
Radek Krejci4f28eda2018-11-12 11:46:16 +0100238 LY_DATA_TYPE *type, const struct lysp_tpdf **tpdf, struct lysp_node **node, struct lysp_module **module)
Radek Krejcibbe09a92018-11-08 09:36:54 +0100239{
240 const char *str, *name;
241 struct lysp_tpdf *typedefs;
242 unsigned int u, v;
243
244 assert(id);
245 assert(start_module);
246 assert(tpdf);
247 assert(node);
248 assert(module);
249
Radek Krejci4f28eda2018-11-12 11:46:16 +0100250 *node = NULL;
Radek Krejcibbe09a92018-11-08 09:36:54 +0100251 str = strchr(id, ':');
252 if (str) {
253 *module = lysp_module_find_prefix(start_module, id, str - id);
254 name = str + 1;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100255 *type = LY_TYPE_UNKNOWN;
Radek Krejcibbe09a92018-11-08 09:36:54 +0100256 } else {
257 *module = start_module;
258 name = id;
Radek Krejci4f28eda2018-11-12 11:46:16 +0100259
260 /* check for built-in types */
261 *type = lysp_type_str2builtin(name, strlen(name));
262 if (*type) {
263 *tpdf = NULL;
264 return LY_SUCCESS;
265 }
Radek Krejcibbe09a92018-11-08 09:36:54 +0100266 }
267 LY_CHECK_RET(!(*module), LY_ENOTFOUND);
268
269 if (start_node && *module == start_module) {
270 /* search typedefs in parent's nodes */
271 *node = start_node;
272 while (*node) {
273 *tpdf = lysp_type_match(name, *node);
274 if (*tpdf) {
275 /* match */
276 return LY_SUCCESS;
277 }
278 *node = (*node)->parent;
279 }
280 }
281
282 /* search in top-level typedefs */
283 if ((*module)->typedefs) {
284 LY_ARRAY_FOR((*module)->typedefs, u) {
285 if (!strcmp(name, (*module)->typedefs[u].name)) {
286 /* match */
287 *tpdf = &(*module)->typedefs[u];
288 return LY_SUCCESS;
289 }
290 }
291 }
292
293 /* search in submodules' typedefs */
294 LY_ARRAY_FOR((*module)->includes, u) {
295 typedefs = (*module)->includes[u].submodule->typedefs;
296 if (typedefs) {
297 LY_ARRAY_FOR(typedefs, v) {
298 if (!strcmp(name, typedefs[v].name)) {
299 /* match */
300 *tpdf = &typedefs[v];
301 return LY_SUCCESS;
302 }
303 }
304 }
305 }
306
307 return LY_ENOTFOUND;
308}
309
310/*
311 * @brief Check name of a new type to avoid name collisions.
312 *
313 * @param[in] ctx Parser context, module where the type is being defined is taken from here.
314 * @param[in] node Schema node where the type is being defined, NULL in case of a top-level typedef.
315 * @param[in] tpdf Typedef definition to check.
316 * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
317 * typedefs are checked, caller is supposed to free the table.
318 * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
319 * typedefs are checked, caller is supposed to free the table.
320 * @return LY_EEXIST in case of collision, LY_SUCCESS otherwise.
321 */
322static LY_ERR
323lysp_check_typedef(struct ly_parser_ctx *ctx, struct lysp_node *node, struct lysp_tpdf *tpdf,
324 struct hash_table *tpdfs_global, struct hash_table *tpdfs_scoped)
325{
326 struct lysp_node *parent;
327 uint32_t hash;
328 size_t name_len;
329 const char *name;
330 unsigned int u;
331 struct lysp_tpdf **typedefs;
332
333 assert(ctx);
334 assert(tpdf);
335
336 name = tpdf->name;
337 name_len = strlen(name);
338
Radek Krejci4f28eda2018-11-12 11:46:16 +0100339 if (lysp_type_str2builtin(name, name_len)) {
340 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
341 "Invalid name \"%s\" of typedef - name collision with a built-in type.", name);
342 return LY_EEXIST;
Radek Krejcibbe09a92018-11-08 09:36:54 +0100343 }
344
345 /* check locally scoped typedefs (avoid name shadowing) */
346 if (node) {
347 typedefs = lysp_node_typedefs(node);
348 if (typedefs && *typedefs) {
349 LY_ARRAY_FOR(*typedefs, u) {
350 if (typedefs[u] == tpdf) {
351 break;
352 }
353 if (!strcmp(name, (*typedefs)[u].name)) {
354 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
355 "Invalid name \"%s\" of typedef - name collision with sibling type.", name);
356 return LY_EEXIST;
357 }
358 }
359 }
360 /* search typedefs in parent's nodes */
361 for (parent = node->parent; parent; parent = node->parent) {
362 if (lysp_type_match(name, parent)) {
363 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
364 "Invalid name \"%s\" of typedef - name collision with another scoped type.", name);
365 return LY_EEXIST;
366 }
367 }
368 }
369
370 /* check collision with the top-level typedefs */
371 hash = dict_hash(name, name_len);
372 if (node) {
373 lyht_insert(tpdfs_scoped, &name, hash, NULL);
374 if (!lyht_find(tpdfs_global, &name, hash, NULL)) {
375 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
376 "Invalid name \"%s\" of typedef - scoped type collide with a top-level type.", name);
377 return LY_EEXIST;
378 }
379 } else {
380 if (lyht_insert(tpdfs_global, &name, hash, NULL)) {
381 LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
382 "Invalid name \"%s\" of typedef - name collision with another top-level type.", name);
383 return LY_EEXIST;
384 }
Radek Krejci3b1f9292018-11-08 10:58:35 +0100385 /* it is not necessary to test collision with the scoped types - in lysp_check_typedefs, all the
386 * top-level typedefs are inserted into the tables before the scoped typedefs, so the collision
387 * is detected in the first branch few lines above */
Radek Krejcibbe09a92018-11-08 09:36:54 +0100388 }
389
390 return LY_SUCCESS;
391}
392
393static int
394lysp_id_cmp(void *val1, void *val2, int UNUSED(mod), void *UNUSED(cb_data))
395{
396 return !strcmp(val1, val2);
397}
398
399LY_ERR
400lysp_check_typedefs(struct ly_parser_ctx *ctx)
401{
402 struct hash_table *ids_global;
403 struct hash_table *ids_scoped;
404 struct lysp_tpdf **typedefs;
405 unsigned int i, u;
406 LY_ERR ret = LY_EVALID;
407
408 /* check name collisions - typedefs and groupings */
409 ids_global = lyht_new(8, sizeof(char*), lysp_id_cmp, NULL, 1);
410 ids_scoped = lyht_new(8, sizeof(char*), lysp_id_cmp, NULL, 1);
411 LY_ARRAY_FOR(ctx->mod->typedefs, i) {
412 if (lysp_check_typedef(ctx, NULL, &ctx->mod->typedefs[i], ids_global, ids_scoped)) {
413 goto cleanup;
414 }
415 }
Radek Krejci3b1f9292018-11-08 10:58:35 +0100416 LY_ARRAY_FOR(ctx->mod->includes, i) {
417 LY_ARRAY_FOR(ctx->mod->includes[i].submodule->typedefs, u) {
418 if (lysp_check_typedef(ctx, NULL, &ctx->mod->includes[i].submodule->typedefs[u], ids_global, ids_scoped)) {
419 goto cleanup;
420 }
421 }
422 }
Radek Krejcibbe09a92018-11-08 09:36:54 +0100423 for (u = 0; u < ctx->tpdfs_nodes.count; ++u) {
424 typedefs = lysp_node_typedefs((struct lysp_node *)ctx->tpdfs_nodes.objs[u]);
425 LY_ARRAY_FOR(*typedefs, i) {
426 if (lysp_check_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[u], &(*typedefs)[i], ids_global, ids_scoped)) {
427 goto cleanup;
428 }
429 }
430 }
431 ret = LY_SUCCESS;
432cleanup:
433 lyht_free(ids_global);
434 lyht_free(ids_scoped);
435 ly_set_erase(&ctx->tpdfs_nodes, NULL);
436
437 return ret;
438}
439
Radek Krejci086c7132018-10-26 15:29:04 +0200440void
441lys_module_implement(struct lys_module *mod)
442{
443 assert(mod);
444 if (mod->parsed) {
445 mod->parsed->implemented = 1;
446 }
447 if (mod->compiled) {
448 mod->compiled->implemented = 1;
449 }
450}
451
Radek Krejci9ed7a192018-10-31 16:23:51 +0100452struct lysp_load_module_check_data {
453 const char *name;
454 const char *revision;
455 const char *path;
456 const char* submoduleof;
457};
458
459static LY_ERR
460lysp_load_module_check(struct ly_ctx *ctx, struct lysp_module *mod, void *data)
461{
462 struct lysp_load_module_check_data *info = data;
463 const char *filename, *dot, *rev;
464 size_t len;
465
466 if (info->name) {
467 /* check name of the parsed model */
468 if (strcmp(info->name, mod->name)) {
469 LOGERR(ctx, LY_EINVAL, "Unexpected module \"%s\" parsed instead of \"%s\").", mod->name, info->name);
470 return LY_EINVAL;
471 }
472 }
473 if (info->revision) {
474 /* check revision of the parsed model */
475 if (!mod->revs || strcmp(info->revision, mod->revs[0].date)) {
476 LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").", mod->name,
477 mod->revs[0].date, info->revision);
478 return LY_EINVAL;
479 }
480 }
481 if (info->submoduleof) {
482 /* check that we have really a submodule */
483 if (!mod->submodule) {
484 /* submodule is not a submodule */
485 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Included \"%s\" schema from \"%s\" is actually not a submodule.",
486 mod->name, info->submoduleof);
487 return LY_EVALID;
488 }
489 /* check that the submodule belongs-to our module */
490 if (strcmp(info->submoduleof, mod->belongsto)) {
491 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Included \"%s\" submodule from \"%s\" belongs-to a different module \"%s\".",
492 mod->name, info->submoduleof, mod->belongsto);
493 return LY_EVALID;
494 }
495 /* check circular dependency */
496 if (mod->parsing) {
497 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "A circular dependency (include) for module \"%s\".", mod->name);
498 return LY_EVALID;
499 }
500 }
501 if (info->path) {
502 /* check that name and revision match filename */
503 filename = strrchr(info->path, '/');
504 if (!filename) {
505 filename = info->path;
506 } else {
507 filename++;
508 }
509 /* name */
510 len = strlen(mod->name);
511 rev = strchr(filename, '@');
512 dot = strrchr(info->path, '.');
513 if (strncmp(filename, mod->name, len) ||
514 ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
515 LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
516 }
517 /* revision */
518 if (rev) {
519 len = dot - ++rev;
520 if (!mod->revs || len != 10 || strncmp(mod->revs[0].date, rev, len)) {
521 LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
522 mod->revs ? mod->revs[0].date : "none");
523 }
524 }
525 }
526 return LY_SUCCESS;
527}
528
529LY_ERR
Radek Krejci3b1f9292018-11-08 10:58:35 +0100530lys_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 +0100531 struct lys_module **result)
532{
533 int fd;
534 char *filepath = NULL;
535 LYS_INFORMAT format;
536 struct lys_module *mod = NULL;
537 LY_ERR ret = LY_SUCCESS;
538 struct lysp_load_module_check_data check_data = {0};
539
540 LY_CHECK_RET(lys_search_localfile(ly_ctx_get_searchdirs(ctx), !(ctx->flags & LY_CTX_DISABLE_SEARCHDIR_CWD), name, revision,
541 &filepath, &format));
542 LY_CHECK_ERR_RET(!filepath, LOGERR(ctx, LY_ENOTFOUND, "Data model \"%s%s%s\" not found in local searchdirs.",
543 name, revision ? "@" : "", revision ? revision : ""), LY_ENOTFOUND);
544
545
546 LOGVRB("Loading schema from \"%s\" file.", filepath);
547
548 /* open the file */
549 fd = open(filepath, O_RDONLY);
550 LY_CHECK_ERR_GOTO(fd < 0, LOGERR(ctx, LY_ESYS, "Unable to open data model file \"%s\" (%s).",
551 filepath, strerror(errno)); ret = LY_ESYS, cleanup);
552
553 check_data.name = name;
554 check_data.revision = revision;
555 check_data.path = filepath;
Radek Krejci3b1f9292018-11-08 10:58:35 +0100556 mod = lys_parse_fd_(ctx, fd, format, implement, main_ctx,
Radek Krejci9ed7a192018-10-31 16:23:51 +0100557 lysp_load_module_check, &check_data);
558 close(fd);
559 LY_CHECK_ERR_GOTO(!mod, ly_errcode(ctx), cleanup);
560
561 if (!mod->parsed->filepath) {
562 char rpath[PATH_MAX];
563 if (realpath(filepath, rpath) != NULL) {
564 mod->parsed->filepath = lydict_insert(ctx, rpath, 0);
565 } else {
566 mod->parsed->filepath = lydict_insert(ctx, filepath, 0);
567 }
568 }
569
570 *result = mod;
571
572 /* success */
573cleanup:
574 free(filepath);
575 return ret;
576}
577
Radek Krejcid33273d2018-10-25 14:55:52 +0200578LY_ERR
Radek Krejci6d6e4e42018-10-29 13:28:19 +0100579lysp_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 +0200580{
Radek Krejci9ed7a192018-10-31 16:23:51 +0100581 const char *module_data = NULL;
Radek Krejci086c7132018-10-26 15:29:04 +0200582 LYS_INFORMAT format = LYS_IN_UNKNOWN;
Radek Krejci9ed7a192018-10-31 16:23:51 +0100583 void (*module_data_free)(void *module_data, void *user_data) = NULL;
584 struct lysp_load_module_check_data check_data = {0};
Radek Krejci086c7132018-10-26 15:29:04 +0200585
586 /* try to get the module from the context */
587 if (revision) {
588 *mod = (struct lys_module*)ly_ctx_get_module(ctx, name, revision);
589 } else {
590 *mod = (struct lys_module*)ly_ctx_get_module_latest(ctx, name);
591 }
592
Radek Krejci6d6e4e42018-10-29 13:28:19 +0100593 if (!(*mod) || (require_parsed && !(*mod)->parsed)) {
594 (*mod) = NULL;
595
Radek Krejci086c7132018-10-26 15:29:04 +0200596 /* check collision with other implemented revision */
597 if (implement && ly_ctx_get_module_implemented(ctx, name)) {
598 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
599 "Module \"%s\" is already present in other implemented revision.", name);
600 return LY_EDENIED;
601 }
602
Radek Krejci9ed7a192018-10-31 16:23:51 +0100603 /* module not present in the context, get the input data and parse it */
Radek Krejci086c7132018-10-26 15:29:04 +0200604 if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
605search_clb:
606 if (ctx->imp_clb) {
607 if (ctx->imp_clb(name, revision, NULL, NULL, ctx->imp_clb_data,
Radek Krejci9ed7a192018-10-31 16:23:51 +0100608 &format, &module_data, &module_data_free) == LY_SUCCESS) {
609 check_data.name = name;
610 check_data.revision = revision;
Radek Krejci3b1f9292018-11-08 10:58:35 +0100611 *mod = lys_parse_mem_(ctx, module_data, format, implement, NULL,
Radek Krejci9ed7a192018-10-31 16:23:51 +0100612 lysp_load_module_check, &check_data);
613 if (module_data_free) {
614 module_data_free((void*)module_data, ctx->imp_clb_data);
615 }
Radek Krejci086c7132018-10-26 15:29:04 +0200616 }
617 }
618 if (!(*mod) && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
619 goto search_file;
620 }
621 } else {
622search_file:
623 if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
624 /* module was not received from the callback or there is no callback set */
Radek Krejci3b1f9292018-11-08 10:58:35 +0100625 lys_module_localfile(ctx, name, revision, implement, NULL, mod);
Radek Krejci086c7132018-10-26 15:29:04 +0200626 }
627 if (!(*mod) && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
628 goto search_clb;
629 }
630 }
Radek Krejci9ed7a192018-10-31 16:23:51 +0100631
Radek Krejcid14e9692018-11-01 11:00:37 +0100632 if ((*mod) && !revision && ((*mod)->parsed->latest_revision == 1)) {
Radek Krejci9ed7a192018-10-31 16:23:51 +0100633 /* update the latest_revision flag - here we have selected the latest available schema,
634 * consider that even the callback provides correct latest revision */
635 (*mod)->parsed->latest_revision = 2;
636 }
Radek Krejci086c7132018-10-26 15:29:04 +0200637 } else {
638 /* we have module from the current context */
639 if (implement && (ly_ctx_get_module_implemented(ctx, name) != *mod)) {
640 /* check collision with other implemented revision */
641 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
642 "Module \"%s\" is already present in other implemented revision.", name);
643 *mod = NULL;
644 return LY_EDENIED;
645 }
646
647 /* circular check */
Radek Krejcif8f882a2018-10-31 14:51:15 +0100648 if ((*mod)->parsed && (*mod)->parsed->parsing) {
Radek Krejci086c7132018-10-26 15:29:04 +0200649 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "A circular dependency (import) for module \"%s\".", name);
650 *mod = NULL;
651 return LY_EVALID;
652 }
653 }
654 if (!(*mod)) {
Radek Krejci9ed7a192018-10-31 16:23:51 +0100655 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "%s \"%s\" module failed.", implement ? "Loading" : "Importing", name);
Radek Krejci086c7132018-10-26 15:29:04 +0200656 return LY_EVALID;
657 }
658
659 if (implement) {
660 /* mark the module implemented, check for collision was already done */
661 lys_module_implement(*mod);
662 }
Radek Krejci086c7132018-10-26 15:29:04 +0200663
664 return LY_SUCCESS;
665}
666
667LY_ERR
Radek Krejci3b1f9292018-11-08 10:58:35 +0100668lysp_load_submodule(struct ly_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc)
Radek Krejcid33273d2018-10-25 14:55:52 +0200669{
670 struct lys_module *submod;
671 const char *submodule_data = NULL;
672 LYS_INFORMAT format = LYS_IN_UNKNOWN;
673 void (*submodule_data_free)(void *module_data, void *user_data) = NULL;
Radek Krejci9ed7a192018-10-31 16:23:51 +0100674 struct lysp_load_module_check_data check_data = {0};
Radek Krejcid33273d2018-10-25 14:55:52 +0200675
Radek Krejcibbe09a92018-11-08 09:36:54 +0100676 /* submodule not present in the context, get the input data and parse it */
Radek Krejci3b1f9292018-11-08 10:58:35 +0100677 if (!(ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
Radek Krejcid33273d2018-10-25 14:55:52 +0200678search_clb:
Radek Krejci3b1f9292018-11-08 10:58:35 +0100679 if (ctx->ctx->imp_clb) {
680 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 +0100681 &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) {
682 check_data.name = inc->name;
683 check_data.revision = inc->rev[0] ? inc->rev : NULL;
684 check_data.submoduleof = mod->name;
Radek Krejci3b1f9292018-11-08 10:58:35 +0100685 submod = lys_parse_mem_(ctx->ctx, submodule_data, format, mod->implemented, ctx,
Radek Krejcibbe09a92018-11-08 09:36:54 +0100686 lysp_load_module_check, &check_data);
687 if (submodule_data_free) {
Radek Krejci3b1f9292018-11-08 10:58:35 +0100688 submodule_data_free((void*)submodule_data, ctx->ctx->imp_clb_data);
Radek Krejcid33273d2018-10-25 14:55:52 +0200689 }
690 }
Radek Krejcid33273d2018-10-25 14:55:52 +0200691 }
Radek Krejci3b1f9292018-11-08 10:58:35 +0100692 if (!submod && !(ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100693 goto search_file;
Radek Krejcid33273d2018-10-25 14:55:52 +0200694 }
Radek Krejci2d31ea72018-10-25 15:46:42 +0200695 } else {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100696search_file:
Radek Krejci3b1f9292018-11-08 10:58:35 +0100697 if (!(ctx->ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100698 /* module was not received from the callback or there is no callback set */
Radek Krejci3b1f9292018-11-08 10:58:35 +0100699 lys_module_localfile(ctx->ctx, inc->name, inc->rev[0] ? inc->rev : NULL, mod->implemented, ctx, &submod);
Radek Krejcibbe09a92018-11-08 09:36:54 +0100700 }
Radek Krejci3b1f9292018-11-08 10:58:35 +0100701 if (!submod && (ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100702 goto search_clb;
703 }
704 }
705 if (submod) {
706 if (!inc->rev[0] && (submod->parsed->latest_revision == 1)) {
707 /* update the latest_revision flag - here we have selected the latest available schema,
708 * consider that even the callback provides correct latest revision */
709 submod->parsed->latest_revision = 2;
710 }
711
712 inc->submodule = submod->parsed;
713 free(submod);
Radek Krejcid33273d2018-10-25 14:55:52 +0200714 }
715 if (!inc->submodule) {
Radek Krejci3b1f9292018-11-08 10:58:35 +0100716 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 +0200717 return LY_EVALID;
718 }
719
720 return LY_SUCCESS;
721}
722
Radek Krejcibbe09a92018-11-08 09:36:54 +0100723#define FIND_MODULE(TYPE, MOD, ID) \
Radek Krejcice8c1592018-10-29 15:35:51 +0100724 TYPE *imp; \
725 if (!strncmp((MOD)->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
726 /* it is the prefix of the module itself */ \
Radek Krejcibbe09a92018-11-08 09:36:54 +0100727 m = ly_ctx_get_module((MOD)->ctx, (MOD)->name, ((struct lysc_module*)(MOD))->revision); \
Radek Krejcice8c1592018-10-29 15:35:51 +0100728 } \
729 /* search in imports */ \
Radek Krejcibbe09a92018-11-08 09:36:54 +0100730 if (!m) { \
731 LY_ARRAY_FOR((MOD)->imports, TYPE, imp) { \
732 if (!strncmp(imp->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
733 m = imp->module; \
734 break; \
735 } \
Radek Krejcice8c1592018-10-29 15:35:51 +0100736 } \
Radek Krejcibbe09a92018-11-08 09:36:54 +0100737 }
Radek Krejcice8c1592018-10-29 15:35:51 +0100738
Radek Krejcibbe09a92018-11-08 09:36:54 +0100739struct lysc_module *
Radek Krejci151a5b72018-10-19 14:21:44 +0200740lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len)
741{
Radek Krejcibbe09a92018-11-08 09:36:54 +0100742 const struct lys_module *m = NULL;
743
744 FIND_MODULE(struct lysc_import, mod, 1);
745 return m ? m->compiled : NULL;
Radek Krejcice8c1592018-10-29 15:35:51 +0100746}
Radek Krejci151a5b72018-10-19 14:21:44 +0200747
Radek Krejcibbe09a92018-11-08 09:36:54 +0100748struct lysp_module *
Radek Krejcice8c1592018-10-29 15:35:51 +0100749lysp_module_find_prefix(struct lysp_module *mod, const char *prefix, size_t len)
750{
Radek Krejcibbe09a92018-11-08 09:36:54 +0100751 const struct lys_module *m = NULL;
752
753 FIND_MODULE(struct lysp_import, mod, 1);
754 return m ? m->parsed : NULL;
Radek Krejcice8c1592018-10-29 15:35:51 +0100755}
Radek Krejci151a5b72018-10-19 14:21:44 +0200756
Radek Krejcice8c1592018-10-29 15:35:51 +0100757struct lys_module *
758lys_module_find_prefix(struct lys_module *mod, const char *prefix, size_t len)
759{
Radek Krejcibbe09a92018-11-08 09:36:54 +0100760 const struct lys_module *m = NULL;
761
Radek Krejcice8c1592018-10-29 15:35:51 +0100762 if (mod->compiled) {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100763 FIND_MODULE(struct lysc_import, mod->compiled, 1);
Radek Krejcice8c1592018-10-29 15:35:51 +0100764 } else {
Radek Krejcibbe09a92018-11-08 09:36:54 +0100765 FIND_MODULE(struct lysp_import, mod->parsed, 2);
766 }
767 return (struct lys_module*)m;
768}
769
770struct lysp_tpdf **
771lysp_node_typedefs(struct lysp_node *node)
772{
773 switch (node->nodetype) {
774 case LYS_CONTAINER:
775 return &((struct lysp_node_container*)node)->typedefs;
776 case LYS_LIST:
777 return &((struct lysp_node_list*)node)->typedefs;
778 case LYS_GROUPING:
779 return &((struct lysp_grp*)node)->typedefs;
780 case LYS_ACTION:
781 return &((struct lysp_action*)node)->typedefs;
782 case LYS_INOUT:
783 return &((struct lysp_action_inout*)node)->typedefs;
784 case LYS_NOTIF:
785 return &((struct lysp_notif*)node)->typedefs;
786 default:
787 return NULL;
Radek Krejci151a5b72018-10-19 14:21:44 +0200788 }
Radek Krejci151a5b72018-10-19 14:21:44 +0200789}
Radek Krejcibbe09a92018-11-08 09:36:54 +0100790
791struct lysp_action **
792lysp_node_actions(struct lysp_node *node)
793{
794 assert(node);
795 switch (node->nodetype) {
796 case LYS_CONTAINER:
797 return &((struct lysp_node_container*)node)->actions;
798 case LYS_LIST:
799 return &((struct lysp_node_list*)node)->actions;
800 case LYS_GROUPING:
801 return &((struct lysp_grp*)node)->actions;
802 case LYS_AUGMENT:
803 return &((struct lysp_augment*)node)->actions;
804 default:
805 return NULL;
806 }
807}
808
809struct lysp_notif **
810lysp_node_notifs(struct lysp_node *node)
811{
812 assert(node);
813 switch (node->nodetype) {
814 case LYS_CONTAINER:
815 return &((struct lysp_node_container*)node)->notifs;
816 case LYS_LIST:
817 return &((struct lysp_node_list*)node)->notifs;
818 case LYS_GROUPING:
819 return &((struct lysp_grp*)node)->notifs;
820 case LYS_AUGMENT:
821 return &((struct lysp_augment*)node)->notifs;
822 default:
823 return NULL;
824 }
825}
826
827struct lysp_node **
828lysp_node_children(struct lysp_node *node)
829{
830 assert(node);
831 switch (node->nodetype) {
832 case LYS_CONTAINER:
833 return &((struct lysp_node_container*)node)->child;
834 case LYS_CHOICE:
835 return &((struct lysp_node_choice*)node)->child;
836 case LYS_LIST:
837 return &((struct lysp_node_list*)node)->child;
838 case LYS_CASE:
839 return &((struct lysp_node_case*)node)->child;
840 case LYS_GROUPING:
841 return &((struct lysp_grp*)node)->data;
842 case LYS_AUGMENT:
843 return &((struct lysp_augment*)node)->child;
844 case LYS_INOUT:
845 return &((struct lysp_action_inout*)node)->data;
846 case LYS_NOTIF:
847 return &((struct lysp_notif*)node)->data;
848 default:
849 return NULL;
850 }
851}
852
853struct lysc_node **
854lysc_node_children(struct lysc_node *node)
855{
856 assert(node);
857 switch (node->nodetype) {
858 case LYS_CONTAINER:
859 return &((struct lysc_node_container*)node)->child;
860 case LYS_CHOICE:
861 return &((struct lysc_node_choice*)node)->child;
862 case LYS_LIST:
863 return &((struct lysc_node_list*)node)->child;
864 case LYS_CASE:
865 return &((struct lysc_node_case*)node)->child;
866 case LYS_USES:
867 return &((struct lysc_node_uses*)node)->child;
868/* TODO
869 case LYS_INOUT:
870 return &((struct lysc_action_inout*)node)->child;
871 case LYS_NOTIF:
872 return &((struct lysc_notif*)node)->child;
873*/
874 default:
875 return NULL;
876 }
877}
878