blob: 6b7b653bf0781cd3caacd781a028e15c295848bc [file] [log] [blame]
Radek Krejcida04f4a2015-05-21 12:54:09 +02001/**
2 * @file context.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief context implementation for libyang
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 */
21
22#define _GNU_SOURCE
23#include <dirent.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <unistd.h>
29#include <errno.h>
30#include <fcntl.h>
31
32#include "common.h"
33#include "context.h"
34#include "dict.h"
35
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020036API struct ly_ctx *
37ly_ctx_new(const char *search_dir)
Radek Krejcida04f4a2015-05-21 12:54:09 +020038{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020039 struct ly_ctx *ctx;
40 char *cwd;
Radek Krejcida04f4a2015-05-21 12:54:09 +020041
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020042 ctx = calloc(1, sizeof *ctx);
43 if (!ctx) {
44 LOGMEM;
45 return NULL;
46 }
Radek Krejcida04f4a2015-05-21 12:54:09 +020047
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020048 /* dictionary */
49 lydict_init(&ctx->dict);
Radek Krejcida04f4a2015-05-21 12:54:09 +020050
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020051 /* models list */
52 ctx->models.list = calloc(16, sizeof *ctx->models.list);
53 ctx->models.used = 0;
54 ctx->models.size = 16;
55 if (search_dir) {
56 cwd = get_current_dir_name();
57 if (chdir(search_dir)) {
58 LOGERR(LY_ESYS, "Unable to use search directory \"%s\" (%s)",
59 search_dir, strerror(errno));
60 free(cwd);
61 ly_ctx_destroy(ctx);
62 return NULL;
63 }
64 ctx->models.search_path = get_current_dir_name();
65 chdir(cwd);
66 free(cwd);
67 }
Radek Krejcida04f4a2015-05-21 12:54:09 +020068
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020069 return ctx;
Radek Krejcida04f4a2015-05-21 12:54:09 +020070}
71
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020072API void
Michal Vasko60ba9a62015-07-03 14:42:31 +020073ly_ctx_set_searchdir(struct ly_ctx *ctx, const char *search_dir)
74{
75 char *cwd;
76
77 if (!ctx) {
78 return;
79 }
80
81 if (search_dir) {
82 cwd = get_current_dir_name();
83 if (chdir(search_dir)) {
84 LOGERR(LY_ESYS, "Unable to use search directory \"%s\" (%s)",
85 search_dir, strerror(errno));
86 free(cwd);
87 return;
88 }
89 ctx->models.search_path = get_current_dir_name();
90 chdir(cwd);
91 free(cwd);
92 } else {
93 free(ctx->models.search_path);
94 ctx->models.search_path = NULL;
95 }
96}
97
98API void
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020099ly_ctx_destroy(struct ly_ctx *ctx)
Radek Krejcida04f4a2015-05-21 12:54:09 +0200100{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200101 if (!ctx) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200102 return;
103 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200104
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200105 /* models list */
Radek Krejcidce51452015-06-16 15:20:08 +0200106 while (ctx->models.used) {
107 ly_module_free(ctx->models.list[0]);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200108 }
109 free(ctx->models.search_path);
110 free(ctx->models.list);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200111
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200112 /* dictionary */
113 lydict_clean(&ctx->dict);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200114
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200115 free(ctx);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200116}
117
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200118static struct ly_module *
119search_file(struct ly_ctx *ctx, struct ly_module *module, const char *name, const char *revision)
Radek Krejcida04f4a2015-05-21 12:54:09 +0200120{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200121 size_t len, flen;
122 int fd;
123 char *cwd;
124 DIR *dir;
125 struct dirent *file;
126 LY_MINFORMAT format;
127 struct ly_module *result = NULL;
128 int localsearch = 1;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200129
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200130 len = strlen(name);
131 cwd = get_current_dir_name();
132 dir = opendir(cwd);
133 LOGVRB("Searching for \"%s\" in %s.", name, cwd);
134 if (!dir) {
135 LOGWRN("Unable to open local directory for searching referenced modules (%s)",
136 strerror(errno));
137 /* try search directory */
138 goto searchpath;
139 }
Radek Krejci33277502015-05-27 15:38:15 +0200140
141search:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200142 while ((file = readdir(dir))) {
143 if (strncmp(name, file->d_name, len)) {
144 continue;
145 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200146
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200147 flen = strlen(file->d_name);
148 if (revision && flen > len + 5) {
149 /* check revision from the filename */
150 /* TODO */
151 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200152
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200153 /* get type according to filename suffix */
154 if (!strcmp(&file->d_name[flen - 4], ".yin")) {
155 format = LY_IN_YIN;
156 } else if (!strcmp(&file->d_name[flen - 5], ".yang")) {
157 format = LY_IN_YANG;
158 } else {
159 continue;
160 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200161
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200162 /* open the file */
163 fd = open(file->d_name, O_RDONLY);
164 if (fd < 0) {
165 LOGERR(LY_ESYS, "Unable to open data model file \"%s\" (%s).",
166 file->d_name, strerror(errno));
167 goto cleanup;
168 }
Radek Krejciefaeba32015-05-27 14:30:57 +0200169
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200170 if (module) {
171 result = (struct ly_module *)ly_submodule_read_fd(module, fd, format);
172 } else {
173 result = ly_module_read_fd(ctx, fd, format);
174 }
175 close(fd);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200176
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200177 if (result) {
178 break;
179 }
180 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200181
Radek Krejci33277502015-05-27 15:38:15 +0200182searchpath:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200183 if (!ctx->models.search_path) {
184 LOGWRN("No search path defined for the current context.");
185 } else if (!result && localsearch) {
186 /* search in local directory done, try context's search_path */
187 closedir(dir);
188 dir = opendir(ctx->models.search_path);
189 if (!dir) {
190 LOGERR(LY_ESYS, "Unable to open data model search directory \"%s\" (%s).",
191 ctx->models.search_path, strerror(errno));
192 goto cleanup;
193 }
Radek Krejci33277502015-05-27 15:38:15 +0200194
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200195 chdir(ctx->models.search_path);
196 LOGVRB("Searching for \"%s\" in %s.", name, ctx->models.search_path);
Radek Krejci33277502015-05-27 15:38:15 +0200197
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200198 localsearch = 0;
199 goto search;
200 }
Radek Krejci33277502015-05-27 15:38:15 +0200201
Radek Krejcid2b7c572015-05-26 15:59:03 +0200202cleanup:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200203 chdir(cwd);
204 free(cwd);
205 closedir(dir);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200206
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200207 return result;
Radek Krejciefaeba32015-05-27 14:30:57 +0200208}
209
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200210struct ly_submodule *
211ly_ctx_get_submodule(struct ly_module *module, const char *name, const char *revision)
Radek Krejciefaeba32015-05-27 14:30:57 +0200212{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200213 struct ly_submodule *result;
214 int i;
Radek Krejciefaeba32015-05-27 14:30:57 +0200215
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200216 if (!module || !name) {
217 ly_errno = LY_EINVAL;
218 return NULL;
219 }
Radek Krejciefaeba32015-05-27 14:30:57 +0200220
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200221 /* search in modules included by the main module */
222 if (module->type) {
223 module = ((struct ly_submodule *)module)->belongsto;
224 }
225 for (i = 0; i < module->inc_size; i++) {
226 result = module->inc[i].submodule;
227 if (strcmp(name, result->name)) {
228 continue;
229 }
Radek Krejcice7fb782015-05-29 16:52:34 +0200230
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200231 if (!revision || (result->rev_size && !strcmp(revision, result->rev[0].date))) {
232 return result;
233 }
234 }
Radek Krejcice7fb782015-05-29 16:52:34 +0200235
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200236 /* not found in context, try to get it from the search directory */
237 result = (struct ly_submodule *)search_file(module->ctx, module, name, revision);
238 if (!result) {
239 LOGERR(LY_EVALID, "Submodule \"%s\" of the \"%s\" data model not found (search path is \"%s\")",
240 name, module->name, module->ctx->models.search_path);
241 }
Radek Krejciefaeba32015-05-27 14:30:57 +0200242
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200243 return result;
Radek Krejciefaeba32015-05-27 14:30:57 +0200244}
245
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200246API struct ly_module *
247ly_ctx_get_module(struct ly_ctx *ctx, const char *name, const char *revision, int read)
Radek Krejciefaeba32015-05-27 14:30:57 +0200248{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200249 int i;
250 struct ly_module *result = NULL;
Radek Krejciefaeba32015-05-27 14:30:57 +0200251
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200252 if (!ctx || !name) {
253 ly_errno = LY_EINVAL;
254 return NULL;
255 }
Radek Krejciefaeba32015-05-27 14:30:57 +0200256
Radek Krejcidce51452015-06-16 15:20:08 +0200257 for (i = 0; i < ctx->models.used; i++) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200258 result = ctx->models.list[i];
259 if (!result || strcmp(name, result->name)) {
260 continue;
261 }
Radek Krejciefaeba32015-05-27 14:30:57 +0200262
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200263 if (!revision || (result->rev_size && !strcmp(revision, result->rev[0].date))) {
264 return result;
265 }
266 }
Radek Krejciefaeba32015-05-27 14:30:57 +0200267
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200268 if (!read) {
Michal Vasko41c47dd2015-06-30 15:23:19 +0200269 return NULL;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200270 }
Radek Krejci0af13872015-05-30 11:50:52 +0200271
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200272 /* not found in context, try to get it from the search directory */
273 result = search_file(ctx, NULL, name, revision);
274 if (!result) {
275 LOGERR(LY_EVALID, "Data model \"%s\" not found (search path is \"%s\")", name, ctx->models.search_path);
276 }
Radek Krejci25d782a2015-05-22 15:03:23 +0200277
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200278 return result;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200279}
Michal Vasko3ec07dc2015-06-30 15:51:30 +0200280
281API char **
282ly_ctx_get_module_names(struct ly_ctx *ctx)
283{
284 int i;
285 char **result = NULL;
286 unsigned int count = 0;
287
288 if (!ctx) {
289 ly_errno = LY_EINVAL;
290 return NULL;
291 }
292
293 for (i = 0; i < ctx->models.used; i++) {
294 ++count;
295 result = realloc(result, count * sizeof *result);
296 result[count-1] = strdup(ctx->models.list[i]->name);
297 }
298 ++count;
299 result = realloc(result, count * sizeof *result);
300 result[count-1] = NULL;
301
302 return result;
303}