parsers CHANGE redesign API of all the parsers
Simplify work with different inputs by introducing ly_in handler similar
to the printers' ly_out. The input handler can be used repeatedly and
also some input data manipulation functions are added.
Add new lys_parse() as a generic function using ly_in input handler. The
current API (lys_parse_* functions) was rewritten to be a wrapper of
the generic lys_parse().
Next to the unit tests for the parsers functions, also the similar tests
for printers functions are added.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c3d1f1b..9db7241 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -222,6 +222,7 @@
src/tree_schema_free.c
src/tree_schema_compile.c
src/tree_schema_helpers.c
+ src/parser.c
src/parser_yang.c
src/parser_yin.c
src/parser_stmt.c
@@ -251,6 +252,10 @@
src/context.h
src/tree.h
src/tree_data.h
+ src/parser.h
+ src/parser_schema.h
+ src/plugins_exts.h
+ src/plugins_types.h
src/printer.h
src/printer_data.h
src/tree_schema.h
diff --git a/src/context.c b/src/context.c
index 338c824..9da44c5 100644
--- a/src/context.c
+++ b/src/context.c
@@ -251,7 +251,7 @@
module = (struct lys_module *)lys_parse_mem_module(ctx, internal_modules[i].data, internal_modules[i].format,
internal_modules[i].implemented, NULL, NULL);
LY_CHECK_ERR_GOTO(!module, rc = ly_errcode(ctx), error);
- LY_CHECK_GOTO((rc = lys_compile(module, 0)), error);
+ LY_CHECK_GOTO((rc = lys_compile(&module, 0)), error);
}
*new_ctx = ctx;
diff --git a/src/context.h b/src/context.h
index a1b771b..7dabc3e 100644
--- a/src/context.h
+++ b/src/context.h
@@ -18,6 +18,7 @@
#include <stdint.h>
#include "log.h"
+#include "parser_schema.h"
#include "tree_schema.h"
#ifdef __cplusplus
diff --git a/src/libyang.h b/src/libyang.h
index c86bd13..72486e3 100644
--- a/src/libyang.h
+++ b/src/libyang.h
@@ -22,6 +22,12 @@
#endif
#include "log.h"
+#include "parser.h"
+#include "parser_schema.h"
+#include "plugins_types.h"
+#include "printer.h"
+#include "printer_data.h"
+#include "printer_schema.h"
#include "set.h"
#include "dict.h"
#include "context.h"
diff --git a/src/parser.c b/src/parser.c
new file mode 100644
index 0000000..0b2761d
--- /dev/null
+++ b/src/parser.c
@@ -0,0 +1,314 @@
+/**
+ * @file printer.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic libyang printers functions.
+ *
+ * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "parser_internal.h"
+
+API LY_IN_TYPE
+ly_in_type(const struct ly_in *in)
+{
+ LY_CHECK_ARG_RET(NULL, in, LY_IN_ERROR);
+ return in->type;
+}
+
+API LY_ERR
+ly_in_new_fd(int fd, struct ly_in **in)
+{
+ size_t length;
+ char *addr;
+
+ LY_CHECK_ARG_RET(NULL, fd >= 0, in, LY_EINVAL);
+
+ LY_CHECK_RET(ly_mmap(NULL, fd, &length, (void **)&addr));
+ if (!addr) {
+ LOGERR(NULL, LY_EINVAL, "Empty input file.");
+ return LY_EINVAL;
+ }
+
+ *in = calloc(1, sizeof **in);
+ LY_CHECK_ERR_RET(!*in, LOGMEM(NULL); ly_munmap(addr, length), LY_EMEM);
+
+ (*in)->type = LY_IN_FD;
+ (*in)->method.fd = fd;
+ (*in)->current = (*in)->start = addr;
+ (*in)->length = length;
+
+ return LY_SUCCESS;
+}
+
+API int
+ly_in_fd(struct ly_in *in, int fd)
+{
+ int prev_fd;
+ size_t length;
+ const char *addr;
+
+ LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FD, -1);
+
+ prev_fd = in->method.fd;
+
+ if (fd != -1) {
+ LY_CHECK_RET(ly_mmap(NULL, fd, &length, (void **)&addr), -1);
+ if (!addr) {
+ LOGERR(NULL, LY_EINVAL, "Empty input file.");
+ return -1;
+ }
+
+ ly_munmap((char*)in->start, in->length);
+
+ in->method.fd = fd;
+ in->current = in->start = addr;
+ in->length = length;
+ }
+
+ return prev_fd;
+}
+
+API LY_ERR
+ly_in_new_file(FILE *f, struct ly_in **in)
+{
+ LY_CHECK_ARG_RET(NULL, f, in, LY_EINVAL);
+
+ LY_CHECK_RET(ly_in_new_fd(fileno(f), in));
+
+ /* convert the LY_IN_FD input handler into the LY_IN_FILE */
+ (*in)->type = LY_IN_FILE;
+ (*in)->method.f = f;
+
+ return LY_SUCCESS;
+}
+
+API FILE *
+ly_in_file(struct ly_in *in, FILE *f)
+{
+ FILE *prev_f;
+
+ LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FILE, NULL);
+
+ prev_f = in->method.f;
+
+ if (f) {
+ /* convert LY_IN_FILE handler into LY_IN_FD to be able to update it via ly_in_fd() */
+ in->type = LY_IN_FD;
+ in->method.fd = fileno(prev_f);
+ if (ly_in_fd(in, fileno(f)) == -1) {
+ in->type = LY_IN_FILE;
+ in->method.f = prev_f;
+ return NULL;
+ }
+
+ /* if success, convert the result back */
+ in->type = LY_IN_FILE;
+ in->method.f = f;
+ }
+
+ return prev_f;
+}
+
+API LY_ERR
+ly_in_new_memory(const char *str, struct ly_in **in)
+{
+ LY_CHECK_ARG_RET(NULL, str, in, LY_EINVAL);
+
+ *in = calloc(1, sizeof **in);
+ LY_CHECK_ERR_RET(!*in, LOGMEM(NULL), LY_EMEM);
+
+ (*in)->type = LY_IN_MEMORY;
+ (*in)->start = (*in)->current = str;
+
+ return LY_SUCCESS;
+}
+
+const char *
+ly_in_memory(struct ly_in *in, const char *str)
+{
+ const char *data;
+
+ LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_MEMORY, NULL);
+
+ data = in->current;
+
+ if (str) {
+ in->start = in->current = str;
+ }
+
+ return data;
+}
+
+API LY_ERR
+ly_in_reset(struct ly_in *in)
+{
+ LY_CHECK_ARG_RET(NULL, in, LY_EINVAL);
+
+ in->current = in->start;
+ return LY_SUCCESS;
+}
+
+API LY_ERR
+ly_in_new_filepath(const char *filepath, size_t len, struct ly_in **in)
+{
+ LY_ERR ret;
+ char *fp;
+ int fd;
+
+ LY_CHECK_ARG_RET(NULL, filepath, in, LY_EINVAL);
+
+ if (len) {
+ fp = strndup(filepath, len);
+ } else {
+ fp = strdup(filepath);
+ }
+
+ fd = open(fp, O_RDONLY);
+ LY_CHECK_ERR_RET(!fd, LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", fp, strerror(errno)); free(fp), LY_ESYS);
+
+ LY_CHECK_ERR_RET(ret = ly_in_new_fd(fd, in), free(fp), ret);
+
+ /* convert the LY_IN_FD input handler into the LY_IN_FILE */
+ (*in)->type = LY_IN_FILEPATH;
+ (*in)->method.fpath.fd = fd;
+ (*in)->method.fpath.filepath = fp;
+
+ return LY_SUCCESS;
+}
+
+API const char *
+ly_in_filepath(struct ly_in *in, const char *filepath, size_t len)
+{
+ int fd, prev_fd;
+ char *fp = NULL;
+
+ LY_CHECK_ARG_RET(NULL, in, in->type == LY_IN_FILEPATH, filepath ? NULL : ((void *)-1));
+
+ if (!filepath) {
+ return in->method.fpath.filepath;
+ }
+
+ if (len) {
+ fp = strndup(filepath, len);
+ } else {
+ fp = strdup(filepath);
+ }
+
+ /* replace filepath */
+ fd = open(fp, O_RDONLY);
+ LY_CHECK_ERR_RET(!fd, LOGERR(NULL, LY_ESYS, "Failed to open file \"%s\" (%s).", fp, strerror(errno)); free(fp), NULL);
+
+ /* convert LY_IN_FILEPATH handler into LY_IN_FD to be able to update it via ly_in_fd() */
+ in->type = LY_IN_FD;
+ prev_fd = ly_in_fd(in, fd);
+ LY_CHECK_ERR_RET(prev_fd == -1, in->type = LY_IN_FILEPATH; free(fp), NULL);
+
+ /* and convert the result back */
+ in->type = LY_IN_FILEPATH;
+ close(prev_fd);
+ free(in->method.fpath.filepath);
+ in->method.fpath.fd = fd;
+ in->method.fpath.filepath = fp;
+
+ return NULL;
+}
+
+void
+lys_parser_fill_filepath(struct ly_ctx *ctx, struct ly_in *in, const char **filepath)
+{
+ char path[PATH_MAX];
+ char proc_path[32];
+ int len;
+
+ LY_CHECK_ARG_RET(NULL, ctx, in, filepath, );
+ if (*filepath) {
+ /* filepath already set */
+ return;
+ }
+
+ switch (in->type) {
+ case LY_IN_FILEPATH:
+ if (realpath(in->method.fpath.filepath, path) != NULL) {
+ *filepath = lydict_insert(ctx, path, 0);
+ } else {
+ *filepath = lydict_insert(ctx, in->method.fpath.filepath, 0);
+ }
+
+ break;
+ case LY_IN_FD:
+#ifdef __APPLE__
+ if (fcntl(in->method.fd, F_GETPATH, path) != -1) {
+ *filepath = lydict_insert(ctx, path, 0);
+ }
+#else
+ /* get URI if there is /proc */
+ sprintf(proc_path, "/proc/self/fd/%d", in->method.fd);
+ if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
+ *filepath = lydict_insert(ctx, path, len);
+ }
+#endif
+ break;
+ case LY_IN_MEMORY:
+ case LY_IN_FILE:
+ /* nothing to do */
+ break;
+ default:
+ LOGINT(ctx);
+ break;
+ }
+
+}
+
+API void
+ly_in_free(struct ly_in *in, int destroy)
+{
+ if (!in) {
+ return;
+ } else if (in->type == LY_IN_ERROR) {
+ LOGINT(NULL);
+ return;
+ }
+
+ if (destroy) {
+ if (in->type == LY_IN_MEMORY) {
+ free((char*)in->start);
+ } else {
+ ly_munmap((char*)in->start, in->length);
+
+ if (in->type == LY_IN_FILE) {
+ fclose(in->method.f);
+ } else {
+ close(in->method.fd);
+
+ if (in->type == LY_IN_FILEPATH) {
+ free(in->method.fpath.filepath);
+ }
+ }
+ }
+ } else if (in->type != LY_IN_MEMORY) {
+ ly_munmap((char*)in->start, in->length);
+
+ if (in->type == LY_IN_FILEPATH) {
+ close(in->method.fpath.fd);
+ free(in->method.fpath.filepath);
+ }
+ }
+
+ free(in);
+}
diff --git a/src/parser.h b/src/parser.h
new file mode 100644
index 0000000..fcdc5dc
--- /dev/null
+++ b/src/parser.h
@@ -0,0 +1,158 @@
+/**
+ * @file parser.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Generic libyang parsers structures and functions
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PARSER_H_
+#define LY_PARSER_H_
+
+#include <unistd.h>
+
+/**
+ * @brief Parser input structure specifying where the data are read.
+ */
+struct ly_in;
+
+/**
+ * @brief Types of the parser's inputs
+ */
+typedef enum LY_IN_TYPE {
+ LY_IN_ERROR = -1, /**< error value to indicate failure of the functions returning LY_IN_TYPE */
+ LY_IN_FD, /**< file descriptor printer */
+ LY_IN_FILE, /**< FILE stream parser */
+ LY_IN_FILEPATH, /**< filepath parser */
+ LY_IN_MEMORY /**< memory parser */
+} LY_IN_TYPE;
+
+/**
+ * @brief Get input type of the input handler.
+ *
+ * @param[in] in Input handler.
+ * @return Type of the parser's input.
+ */
+LY_IN_TYPE ly_in_type(const struct ly_in *in);
+
+/**
+ * @brief Reset the input medium to read from its beginning, so the following parser function will read from the object's beginning.
+ *
+ * Note that in case the underlying output is not seekable (stream referring a pipe/FIFO/socket or the callback output type),
+ * nothing actually happens despite the function succeeds. Also note that the medium is not returned to the state it was when
+ * the handler was created. For example, file is seeked into the offset zero, not to the offset where it was opened when
+ * ly_in_new_file() was called.
+ *
+ * @param[in] in Input handler.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ESYS in case of failure
+ */
+LY_ERR ly_in_reset(struct ly_in *in);
+
+/**
+ * @brief Create input handler using file descriptor.
+ *
+ * @param[in] fd File descriptor to use.
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LY_ERR ly_in_new_fd(int fd, struct ly_in **in);
+
+/**
+ * @brief Get or reset file descriptor input handler.
+ *
+ * @param[in] in Input handler.
+ * @param[in] fd Optional value of a new file descriptor for the handler. If -1, only the current file descriptor value is returned.
+ * @return Previous value of the file descriptor. Note that caller is responsible for closing the returned file descriptor in case of setting new descriptor @p fd.
+ * @return -1 in case of error when setting up the new file descriptor.
+ */
+int ly_in_fd(struct ly_in *in, int fd);
+
+/**
+ * @brief Create input handler using file stream.
+ *
+ * @param[in] f File stream to use.
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LY_ERR ly_in_new_file(FILE *f, struct ly_in **in);
+
+/**
+ * @brief Get or reset file stream input handler.
+ *
+ * @param[in] in Input handler.
+ * @param[in] f Optional new file stream for the handler. If NULL, only the current file stream is returned.
+ * @return NULL in case of invalid argument or an error when setting up the new input file, original input handler @p in is untouched in this case.
+ * @return Previous file stream of the handler. Note that caller is responsible for closing the returned stream in case of setting new stream @p f.
+ */
+FILE *ly_in_file(struct ly_in *in, FILE *f);
+
+/**
+ * @brief Create input handler using memory to read data.
+ *
+ * @param[in] str Pointer where to start reading data. The input data are expected to be NULL-terminated.
+ * Note that in case the destroy argument of ly_in_free() is used, the input string is passed to free(),
+ * so if it is really a static string, do not use the destroy argument!
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LY_ERR ly_in_new_memory(const char *str, struct ly_in **in);
+
+/**
+ * @brief Get or change memory where the data are read from.
+ *
+ * @param[in] in Input handler.
+ * @param[in] str String containing the data to read. The input data are expected to be NULL-terminated.
+ * Note that in case the destroy argument of ly_in_free() is used, the input string is passed to free(),
+ * so if it is really a static string, do not use the destroy argument!
+ * @return Previous starting address to read data from. Note that the caller is responsible to free
+ * the data in case of changing string pointer @p str.
+ */
+const char *ly_in_memory(struct ly_in *in, const char *str);
+
+/**
+ * @brief Create input handler file of the given filename.
+ *
+ * @param[in] filepath Path of the file where to read data.
+ * @param[in] len Optional number of bytes to use from @p filepath. If 0, the @p filepath is considered to be NULL-terminated and
+ * the whole string is taken into account.
+ * @param[out] in Created input handler supposed to be passed to different ly*_parse() functions.
+ * @return LY_SUCCESS in case of success
+ * @return LY_ERR value in case of failure.
+ */
+LY_ERR ly_in_new_filepath(const char *filepath, size_t len, struct ly_in **in);
+
+/**
+ * @brief Get or change the filepath of the file where the parser reads the data.
+ *
+ * Note that in case of changing the filepath, the current file is closed and a new one is
+ * created/opened instead of renaming the previous file. Also note that the previous filepath
+ * string is returned only in case of not changing it's value.
+ *
+ * @param[in] in Input handler.
+ * @param[in] filepath Optional new filepath for the handler. If and only if NULL, the current filepath string is returned.
+ * @param[in] len Optional number of bytes to use from @p filepath. If 0, the @p filepath is considered to be NULL-terminated and
+ * the whole string is taken into account.
+ * @return Previous filepath string in case the @p filepath argument is NULL.
+ * @return NULL if changing filepath succeedes and ((void *)-1) otherwise.
+ */
+const char *ly_in_filepath(struct ly_in *in, const char *filepath, size_t len);
+
+/**
+ * @brief Free the input handler.
+ * @param[in] in Input handler to free.
+ * @param[in] destroy Flag to free the input data buffer (for LY_IN_MEMORY) or to
+ * close stream/file descriptor (for LY_IN_FD and LY_IN_FILE)
+ */
+void ly_in_free(struct ly_in *in, int destroy);
+
+#endif /* LY_PARSER_H_ */
diff --git a/src/parser_internal.h b/src/parser_internal.h
new file mode 100644
index 0000000..9dc3c50
--- /dev/null
+++ b/src/parser_internal.h
@@ -0,0 +1,86 @@
+/**
+ * @file parser_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Internal structures and functions for libyang parsers
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PARSER_INTERNAL_H_
+#define LY_PARSER_INTERNAL_H_
+
+#include "parser.h"
+#include "tree_schema_internal.h"
+
+/**
+ * @brief Parser input structure specifying where the data are read.
+ */
+struct ly_in {
+ LY_IN_TYPE type; /**< type of the output to select the output method */
+ const char *current;
+ const char *start;
+ size_t length;
+ union {
+ int fd; /**< file descriptor for LY_IN_FD type */
+ FILE *f; /**< file structure for LY_IN_FILE and LY_IN_FILEPATH types */
+ struct {
+ int fd; /**< file descriptor for LY_IN_FILEPATH */
+ char *filepath; /**< stored original filepath */
+ } fpath; /**< filepath structure for LY_IN_FILEPATH */
+ } method; /**< type-specific information about the output */
+};
+
+/**
+ * @brief Parse submodule from YANG data.
+ * @param[in,out] ctx Parser context.
+ * @param[in] ly_ctx Context of YANG schemas.
+ * @param[in] main_ctx Parser context of main module.
+ * @param[in] data Input data to be parsed.
+ * @param[out] submod Pointer to the parsed submodule structure.
+ * @return LY_ERR value - LY_SUCCESS, LY_EINVAL or LY_EVALID.
+ */
+LY_ERR yang_parse_submodule(struct lys_yang_parser_ctx **context, struct ly_ctx *ly_ctx, struct lys_parser_ctx *main_ctx,
+ const char *data, struct lysp_submodule **submod);
+
+/**
+ * @brief Parse module from YANG data.
+ * @param[in] ctx Parser context.
+ * @param[in] data Input data to be parsed.
+ * @param[in, out] mod Prepared module structure where the parsed information, including the parsed
+ * module structure, will be filled in.
+ * @return LY_ERR value - LY_SUCCESS, LY_EINVAL or LY_EVALID.
+ */
+LY_ERR yang_parse_module(struct lys_yang_parser_ctx **context, const char *data, struct lys_module *mod);
+
+/**
+ * @brief Parse module from YIN data.
+ *
+ * @param[in,out] yin_ctx Context created during parsing, is used to finalize lysp_model after it's completly parsed.
+ * @param[in] data Input data to be parsed.
+ * @param[in,out] mod Prepared module structure where the parsed information, including the parsed
+ * module structure, will be filled in.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_module(struct lys_yin_parser_ctx **yin_ctx, const char *data, struct lys_module *mod);
+
+/**
+ * @brief Parse submodule from YIN data.
+ *
+ * @param[in,out] yin_ctx Context created during parsing, is used to finalize lysp_model after it's completly parsed.
+ * @param[in] ctx Libyang context.
+ * @param[in] main_ctx Parser context of main module.
+ * @param[in,out] data Input data to be parsed.
+ * @param[in,out] submod Submodule structure where the parsed information, will be filled in.
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_submodule(struct lys_yin_parser_ctx **yin_ctx, struct ly_ctx *ctx, struct lys_parser_ctx *main_ctx,
+ const char *data, struct lysp_submodule **submod);
+
+#endif /* LY_PARSER_INTERNAL_H_ */
diff --git a/src/parser_schema.h b/src/parser_schema.h
new file mode 100644
index 0000000..82bdd9b
--- /dev/null
+++ b/src/parser_schema.h
@@ -0,0 +1,114 @@
+/**
+ * @file parser_schema.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema parsers for libyang
+ *
+ * Copyright (c) 2015-2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_PARSER_SCHEMA_H_
+#define LY_PARSER_SCHEMA_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ly_in;
+
+/**
+ * @addtogroup schematree
+ * @{
+ */
+
+/**
+ * @brief Schema input formats accepted by libyang [parser functions](@ref howtoschemasparsers).
+ */
+typedef enum {
+ LYS_IN_UNKNOWN = 0, /**< unknown format, used as return value in case of error */
+ LYS_IN_YANG = 1, /**< YANG schema input format */
+ LYS_IN_YIN = 3 /**< YIN schema input format */
+} LYS_INFORMAT;
+
+/**
+ * @brief Load a schema into the specified context.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] in The input handle to provide the dumped data model in the specified format.
+ * @param[in] format Format of the schema to parse.
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format);
+
+/**
+ * @brief Load a schema into the specified context.
+ *
+ * This function is comsidered for a simple use, if you have a complex usecase,
+ * consider use of lys_parse() with a standalone input handler.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] data The string containing the dumped data model in the specified
+ * format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format);
+
+/**
+ * @brief Read a schema from file descriptor into the specified context.
+ *
+ * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
+ *
+ * This function is comsidered for a simple use, if you have a complex usecase,
+ * consider use of lys_parse() with a standalone input handler.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
+ * in the specified format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format);
+
+/**
+ * @brief Load a schema into the specified context from a file.
+ *
+ * This function is comsidered for a simple use, if you have a complex usecase,
+ * consider use of lys_parse() with a standalone input handler.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] path Path to the file with the model in the specified format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format);
+
+/**
+ * @brief Search for the schema file in the specified searchpaths.
+ *
+ * @param[in] searchpaths NULL-terminated array of paths to be searched (recursively). Current working
+ * directory is searched automatically (but non-recursively if not in the provided list). Caller can use
+ * result of the ly_ctx_get_searchdirs().
+ * @param[in] cwd Flag to implicitly search also in the current working directory (non-recursively).
+ * @param[in] name Name of the schema to find.
+ * @param[in] revision Revision of the schema to find. If NULL, the newest found schema filepath is returned.
+ * @param[out] localfile Mandatory output variable containing absolute path of the found schema. If no schema
+ * complying the provided restriction is found, NULL is set.
+ * @param[out] format Optional output variable containing expected format of the schema document according to the
+ * file suffix.
+ * @return LY_ERR value (LY_SUCCESS is returned even if the file is not found, then the *localfile is NULL).
+ */
+LY_ERR lys_search_localfile(const char * const *searchpaths, int cwd, const char *name, const char *revision, char **localfile, LYS_INFORMAT *format);
+
+/** @} schematree */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PARSER_SCHEMA_H_ */
diff --git a/src/printer_schema.h b/src/printer_schema.h
index b06d216..892f326 100644
--- a/src/printer_schema.h
+++ b/src/printer_schema.h
@@ -40,6 +40,18 @@
/** @} schemaprinterflags */
/**
+ * @brief Schema output formats accepted by libyang [printer functions](@ref howtoschemasprinters).
+ */
+typedef enum {
+ LYS_OUT_UNKNOWN = 0, /**< unknown format, used as return value in case of error */
+ LYS_OUT_YANG = 1, /**< YANG schema output format */
+ LYS_OUT_YANG_COMPILED = 2, /**< YANG schema output format of the compiled schema tree */
+ LYS_OUT_YIN = 3, /**< YIN schema output format */
+
+ LYS_OUT_TREE, /**< Tree schema output format, for more information see the [printers](@ref howtoschemasprinters) page */
+} LYS_OUTFORMAT;
+
+/**
* @brief Schema module printer.
*
* @param[in] out Printer handler for a specific output. Use ly_out_*() functions to create and free the handler.
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 826e025..7bafc82 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -20,7 +20,6 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -37,6 +36,7 @@
#include "tree.h"
#include "tree_schema.h"
#include "tree_schema_internal.h"
+#include "parser_internal.h"
API const struct lysc_node *
lys_getnext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module, int options)
@@ -737,7 +737,7 @@
mod->implemented = value;
/* compile the schema */
- LY_CHECK_RET(lys_compile(mod, LYSC_OPT_INTERNAL));
+ LY_CHECK_RET(lys_compile(&mod, LYSC_OPT_INTERNAL));
return LY_SUCCESS;
}
@@ -992,165 +992,109 @@
}
API struct lys_module *
-lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format)
+lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format)
{
struct lys_module *mod;
+ char *filename, *rev, *dot;
+ size_t len;
- mod = lys_parse_mem_module(ctx, data, format, 1, NULL, NULL);
+ LY_CHECK_ARG_RET(NULL, ctx, in, format > LYS_IN_UNKNOWN, NULL);
+
+ mod = lys_parse_mem_module(ctx, in->current, format, 1, NULL, NULL);
LY_CHECK_RET(!mod, NULL);
- if (lys_compile(mod, 0)) {
- ly_set_rm(&ctx->list, mod, NULL);
- lys_module_free(mod, NULL);
- return NULL;
+ switch (in->type) {
+ case LY_IN_FILEPATH:
+ /* check that name and revision match filename */
+ filename = strrchr(in->method.fpath.filepath, '/');
+ if (!filename) {
+ filename = in->method.fpath.filepath;
+ } else {
+ filename++;
+ }
+ rev = strchr(filename, '@');
+ dot = strrchr(filename, '.');
+
+ /* name */
+ len = strlen(mod->name);
+ if (strncmp(filename, mod->name, len) ||
+ ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
+ LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
+ }
+ if (rev) {
+ len = dot - ++rev;
+ if (!mod->parsed->revs || len != 10 || strncmp(mod->parsed->revs[0].date, rev, len)) {
+ LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
+ mod->parsed->revs ? mod->parsed->revs[0].date : "none");
+ }
+ }
+
+ break;
+ case LY_IN_FD:
+ case LY_IN_FILE:
+ case LY_IN_MEMORY:
+ /* nothing special to do */
+ break;
+ default:
+ LOGINT(ctx);
+ break;
}
+
+ lys_parser_fill_filepath(ctx, in, &mod->filepath);
+ lys_compile(&mod, 0);
+
return mod;
}
-static void
-lys_parse_set_filename(struct ly_ctx *ctx, const char **filename, int fd)
+API struct lys_module *
+lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format)
{
- char path[PATH_MAX];
+ LY_ERR ret;
+ struct ly_in *in = NULL;
+ struct lys_module *result = NULL;
-#ifdef __APPLE__
- if (fcntl(fd, F_GETPATH, path) != -1) {
- *filename = lydict_insert(ctx, path, 0);
- }
-#else
- int len;
- char proc_path[32];
+ LY_CHECK_ARG_RET(ctx, data, format != LYS_IN_UNKNOWN, NULL);
- /* get URI if there is /proc */
- sprintf(proc_path, "/proc/self/fd/%d", fd);
- if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
- *filename = lydict_insert(ctx, path, len);
- }
-#endif
-}
+ LY_CHECK_ERR_RET(ret = ly_in_new_memory(data, &in), LOGERR(ctx, ret, "Unable to create input handler."), NULL);
-void *
-lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct lys_parser_ctx *main_ctx,
- lys_custom_check custom_check, void *check_data)
-{
- void *result;
- struct lys_module *mod = NULL;
- struct lysp_submodule *submod = NULL;
- size_t length;
- char *addr;
-
- LY_CHECK_ARG_RET(ctx, ctx, NULL);
- if (fd < 0) {
- LOGARG(ctx, fd);
- return NULL;
- }
-
- LY_CHECK_RET(ly_mmap(ctx, fd, &length, (void **)&addr), NULL);
- if (!addr) {
- LOGERR(ctx, LY_EINVAL, "Empty schema file.");
- return NULL;
- }
-
- if (main_ctx) {
- result = submod = lys_parse_mem_submodule(ctx, addr, format, main_ctx, custom_check, check_data);
- } else {
- result = mod = lys_parse_mem_module(ctx, addr, format, implement, custom_check, check_data);
- if (mod && implement && lys_compile(mod, 0)) {
- ly_set_rm(&ctx->list, mod, NULL);
- lys_module_free(mod, NULL);
- result = mod = NULL;
- }
- }
- ly_munmap(addr, length);
-
- if (mod && !mod->filepath) {
- lys_parse_set_filename(ctx, &mod->filepath, fd);
- } else if (submod && !submod->filepath) {
- lys_parse_set_filename(ctx, &submod->filepath, fd);
- }
+ result = lys_parse(ctx, in, format);
+ ly_in_free(in, 0);
return result;
}
-struct lys_module *
-lys_parse_fd_module(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, lys_custom_check custom_check,
- void *check_data)
-{
- return (struct lys_module*)lys_parse_fd_(ctx, fd, format, implement, NULL, custom_check, check_data);
-}
-
-struct lysp_submodule *
-lys_parse_fd_submodule(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct lys_parser_ctx *main_ctx,
- lys_custom_check custom_check, void *check_data)
-{
- assert(main_ctx);
- return (struct lysp_submodule*)lys_parse_fd_(ctx, fd, format, 0, main_ctx, custom_check, check_data);
-}
-
API struct lys_module *
lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format)
{
- return lys_parse_fd_module(ctx, fd, format, 1, NULL, NULL);
-}
+ LY_ERR ret;
+ struct ly_in *in = NULL;
+ struct lys_module *result = NULL;
-struct lys_module *
-lys_parse_path_(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, int implement,
- lys_custom_check custom_check, void *check_data)
-{
- int fd;
- struct lys_module *mod;
- const char *rev, *dot, *filename;
- size_t len;
+ LY_CHECK_ARG_RET(ctx, fd != -1, format != LYS_IN_UNKNOWN, NULL);
- LY_CHECK_ARG_RET(ctx, ctx, path, NULL);
+ LY_CHECK_ERR_RET(ret = ly_in_new_fd(fd, &in), LOGERR(ctx, ret, "Unable to create input handler."), NULL);
- fd = open(path, O_RDONLY);
- LY_CHECK_ERR_RET(fd == -1, LOGERR(ctx, LY_ESYS, "Opening file \"%s\" failed (%s).", path, strerror(errno)), NULL);
+ result = lys_parse(ctx, in, format);
+ ly_in_free(in, 0);
- mod = lys_parse_fd_module(ctx, fd, format, implement, custom_check, check_data);
- close(fd);
- LY_CHECK_RET(!mod, NULL);
-
- /* check that name and revision match filename */
- filename = strrchr(path, '/');
- if (!filename) {
- filename = path;
- } else {
- filename++;
- }
- rev = strchr(filename, '@');
- dot = strrchr(filename, '.');
-
- /* name */
- len = strlen(mod->name);
- if (strncmp(filename, mod->name, len) ||
- ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
- LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
- }
- if (rev) {
- len = dot - ++rev;
- if (!mod->parsed->revs || len != 10 || strncmp(mod->parsed->revs[0].date, rev, len)) {
- LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
- mod->parsed->revs ? mod->parsed->revs[0].date : "none");
- }
- }
-
- if (!mod->filepath) {
- /* store URI */
- char rpath[PATH_MAX];
- if (realpath(path, rpath) != NULL) {
- mod->filepath = lydict_insert(ctx, rpath, 0);
- } else {
- mod->filepath = lydict_insert(ctx, path, 0);
- }
- }
-
- return mod;
+ return result;
}
API struct lys_module *
lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format)
{
- return lys_parse_path_(ctx, path, format, 1, NULL, NULL);
+ LY_ERR ret;
+ struct ly_in *in = NULL;
+ struct lys_module *result = NULL;
+
+ LY_CHECK_ARG_RET(ctx, path, format != LYS_IN_UNKNOWN, NULL);
+
+ LY_CHECK_ERR_RET(ret = ly_in_new_filepath(path, 0, &in), LOGERR(ctx, ret, "Unable to create input handler for filepath %s.", path), NULL);
+
+ result = lys_parse(ctx, in, format);
+ ly_in_free(in, 0);
+
+ return result;
}
API LY_ERR
diff --git a/src/tree_schema.h b/src/tree_schema.h
index bc573c8..a307e2d 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -126,27 +126,6 @@
} \
} } \
-/**
- * @brief Schema input formats accepted by libyang [parser functions](@ref howtoschemasparsers).
- */
-typedef enum {
- LYS_IN_UNKNOWN = 0, /**< unknown format, used as return value in case of error */
- LYS_IN_YANG = 1, /**< YANG schema input format */
- LYS_IN_YIN = 3 /**< YIN schema input format */
-} LYS_INFORMAT;
-
-/**
- * @brief Schema output formats accepted by libyang [printer functions](@ref howtoschemasprinters).
- */
-typedef enum {
- LYS_OUT_UNKNOWN = 0, /**< unknown format, used as return value in case of error */
- LYS_OUT_YANG = 1, /**< YANG schema output format */
- LYS_OUT_YANG_COMPILED = 2, /**< YANG schema output format of the compiled schema tree */
- LYS_OUT_YIN = 3, /**< YIN schema output format */
-
- LYS_OUT_TREE, /**< Tree schema output format, for more information see the [printers](@ref howtoschemasprinters) page */
-} LYS_OUTFORMAT;
-
#define LY_REV_SIZE 11 /**< revision data string length (including terminating NULL byte) */
#define LYS_UNKNOWN 0x0000 /**< uninitalized unknown statement node */
@@ -1876,57 +1855,6 @@
int lys_feature_value(const struct lys_module *module, const char *feature);
/**
- * @brief Load a schema into the specified context.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] data The string containing the dumped data model in the specified
- * format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lys_module *lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format);
-
-/**
- * @brief Read a schema from file descriptor into the specified context.
- *
- * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
- * in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lys_module *lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format);
-
-/**
- * @brief Read a schema into the specified context from a file.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] path Path to the file with the model in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lys_module *lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format);
-
-/**
- * @brief Search for the schema file in the specified searchpaths.
- *
- * @param[in] searchpaths NULL-terminated array of paths to be searched (recursively). Current working
- * directory is searched automatically (but non-recursively if not in the provided list). Caller can use
- * result of the ly_ctx_get_searchdirs().
- * @param[in] cwd Flag to implicitly search also in the current working directory (non-recursively).
- * @param[in] name Name of the schema to find.
- * @param[in] revision Revision of the schema to find. If NULL, the newest found schema filepath is returned.
- * @param[out] localfile Mandatory output variable containing absolute path of the found schema. If no schema
- * complying the provided restriction is found, NULL is set.
- * @param[out] format Optional output variable containing expected format of the schema document according to the
- * file suffix.
- * @return LY_ERR value (LY_SUCCESS is returned even if the file is not found, then the *localfile is NULL).
- */
-LY_ERR lys_search_localfile(const char * const *searchpaths, int cwd, const char *name, const char *revision, char **localfile, LYS_INFORMAT *format);
-
-/**
* @brief Get next schema tree (sibling) node element that can be instantiated in a data tree. Returned node can
* be from an augment.
*
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 058b19a..f359111 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -27,6 +27,7 @@
#include "dict.h"
#include "log.h"
#include "path.h"
+#include "parser.h"
#include "plugins_exts.h"
#include "plugins_types.h"
#include "plugins_exts_internal.h"
@@ -920,13 +921,17 @@
if (!imp->module->parsed) {
/* try to use filepath if present */
if (imp->module->filepath) {
- mod = (struct lys_module*)lys_parse_path(ctx->ctx, imp->module->filepath,
- !strcmp(&imp->module->filepath[strlen(imp->module->filepath - 4)], ".yin") ? LYS_IN_YIN : LYS_IN_YANG);
- if (mod != imp->module) {
- LOGERR(ctx->ctx, LY_EINT, "Filepath \"%s\" of the module \"%s\" does not match.",
- imp->module->filepath, imp->module->name);
- mod = NULL;
+ struct ly_in *in;
+ if (ly_in_new_filepath(imp->module->filepath, 0, &in)) {
+ LOGINT(ctx->ctx);
+ } else {
+ mod = lys_parse(ctx->ctx, in, !strcmp(&imp->module->filepath[strlen(imp->module->filepath - 4)], ".yin") ? LYS_IN_YIN : LYS_IN_YANG);
+ if (mod != imp->module) {
+ LOGERR(ctx->ctx, LY_EINT, "Filepath \"%s\" of the module \"%s\" does not match.", imp->module->filepath, imp->module->name);
+ mod = NULL;
+ }
}
+ ly_in_free(in, 1);
}
if (!mod) {
if (lysp_load_module(ctx->ctx, imp->module->name, imp->module->revision, 0, 1, &mod)) {
@@ -7024,7 +7029,7 @@
}
LY_ERR
-lys_compile(struct lys_module *mod, int options)
+lys_compile(struct lys_module **mod, int options)
{
struct lysc_ctx ctx = {0};
struct lysc_module *mod_c;
@@ -7037,25 +7042,25 @@
uint32_t i;
LY_ERR ret = LY_SUCCESS;
- LY_CHECK_ARG_RET(NULL, mod, mod->parsed, mod->ctx, LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, mod, *mod, (*mod)->parsed, (*mod)->ctx, LY_EINVAL);
- if (!mod->implemented) {
+ if (!(*mod)->implemented) {
/* just imported modules are not compiled */
return LY_SUCCESS;
}
- sp = mod->parsed;
+ sp = (*mod)->parsed;
- ctx.ctx = mod->ctx;
- ctx.mod = mod;
- ctx.mod_def = mod;
+ ctx.ctx = (*mod)->ctx;
+ ctx.mod = *mod;
+ ctx.mod_def = *mod;
ctx.options = options;
ctx.path_len = 1;
ctx.path[0] = '/';
- mod->compiled = mod_c = calloc(1, sizeof *mod_c);
- LY_CHECK_ERR_RET(!mod_c, LOGMEM(mod->ctx), LY_EMEM);
- mod_c->mod = mod;
+ (*mod)->compiled = mod_c = calloc(1, sizeof *mod_c);
+ LY_CHECK_ERR_RET(!mod_c, LOGMEM((*mod)->ctx), LY_EMEM);
+ mod_c->mod = *mod;
COMPILE_ARRAY_GOTO(&ctx, sp->imports, mod_c->imports, u, lys_compile_import, ret, error);
LY_ARRAY_FOR(sp->includes, u) {
@@ -7064,10 +7069,10 @@
}
/* features */
- if (mod->off_features) {
+ if ((*mod)->off_features) {
/* there is already precompiled array of features */
- mod_c->features = mod->off_features;
- mod->off_features = NULL;
+ mod_c->features = (*mod)->off_features;
+ (*mod)->off_features = NULL;
} else {
/* features are compiled directly into the compiled module structure,
* but it must be done in two steps to allow forward references (via if-feature) between the features themselves */
@@ -7096,7 +7101,7 @@
lysc_update_path(&ctx, NULL, "{identity}");
COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->identities, mod_c->identities, u, lys_compile_identity, ret, error);
if (sp->identities) {
- LY_CHECK_RET(lys_compile_identities_derived(&ctx, sp->identities, mod_c->identities));
+ LY_CHECK_GOTO(ret = lys_compile_identities_derived(&ctx, sp->identities, mod_c->identities), error);
}
lysc_update_path(&ctx, NULL, NULL);
@@ -7159,8 +7164,8 @@
#endif
/* add ietf-netconf-with-defaults "default" metadata to the compiled module */
- if (!strcmp(mod->name, "ietf-netconf-with-defaults")) {
- LY_CHECK_GOTO(ret = lys_compile_ietf_netconf_wd_annotation(&ctx, mod), error);
+ if (!strcmp((*mod)->name, "ietf-netconf-with-defaults")) {
+ LY_CHECK_GOTO(ret = lys_compile_ietf_netconf_wd_annotation(&ctx, *mod), error);
}
ly_set_erase(&ctx.dflts, free);
@@ -7171,8 +7176,8 @@
LY_ARRAY_FREE(augments);
if (ctx.options & LYSC_OPT_FREE_SP) {
- lysp_module_free(mod->parsed);
- ((struct lys_module*)mod)->parsed = NULL;
+ lysp_module_free((*mod)->parsed);
+ (*mod)->parsed = NULL;
}
if (!(ctx.options & LYSC_OPT_INTERNAL)) {
@@ -7185,11 +7190,11 @@
}
}
- ((struct lys_module*)mod)->compiled = mod_c;
+ (*mod)->compiled = mod_c;
return LY_SUCCESS;
error:
- lys_feature_precompile_revert(&ctx, mod);
+ lys_feature_precompile_revert(&ctx, *mod);
ly_set_erase(&ctx.dflts, free);
ly_set_erase(&ctx.xpath, NULL);
ly_set_erase(&ctx.leafrefs, NULL);
@@ -7197,7 +7202,7 @@
ly_set_erase(&ctx.tpdf_chain, NULL);
LY_ARRAY_FREE(augments);
lysc_module_free(mod_c, NULL);
- mod->compiled = NULL;
+ (*mod)->compiled = NULL;
/* revert compilation of modules implemented by dependency */
for (i = 0; i < ctx.ctx->list.count; ++i) {
@@ -7213,5 +7218,10 @@
}
}
+ /* remove the module itself from the context and free it */
+ ly_set_rm(&ctx.ctx->list, *mod, NULL);
+ lys_module_free(*mod, NULL);
+ *mod = NULL;
+
return ret;
}
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 664edd8..fa32f8f 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -32,6 +32,8 @@
#include "hash_table.h"
#include "log.h"
#include "plugins_exts.h"
+#include "parser.h"
+#include "parser_internal.h"
#include "set.h"
#include "tree.h"
#include "tree_schema.h"
@@ -749,14 +751,12 @@
lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement,
struct lys_parser_ctx *main_ctx, const char *main_name, int required, void **result)
{
- int fd;
+ struct ly_in *in;
char *filepath = NULL;
- const char **fp;
LYS_INFORMAT format;
void *mod = NULL;
LY_ERR ret = LY_SUCCESS;
struct lysp_load_module_check_data check_data = {0};
- char rpath[PATH_MAX];
LY_CHECK_RET(lys_search_localfile(ly_ctx_get_searchdirs(ctx), !(ctx->flags & LY_CTX_DISABLE_SEARCHDIR_CWD), name, revision,
&filepath, &format));
@@ -765,31 +765,29 @@
LOGVRB("Loading schema from \"%s\" file.", filepath);
- /* open the file */
- fd = open(filepath, O_RDONLY);
- LY_CHECK_ERR_GOTO(fd < 0, LOGERR(ctx, LY_ESYS, "Unable to open data model file \"%s\" (%s).",
- filepath, strerror(errno)); ret = LY_ESYS, cleanup);
-
+ /* get the (sub)module */
+ LY_CHECK_ERR_GOTO(ret = ly_in_new_filepath(filepath, 0, &in), LOGERR(ctx, ret, "Unable to create input handler for filepath %s.", filepath), cleanup);
check_data.name = name;
check_data.revision = revision;
check_data.path = filepath;
check_data.submoduleof = main_name;
- mod = lys_parse_fd_(ctx, fd, format, implement, main_ctx,
- lysp_load_module_check, &check_data);
- close(fd);
- LY_CHECK_ERR_GOTO(!mod, ly_errcode(ctx), cleanup);
+ if (main_ctx) {
+ mod = lys_parse_mem_submodule(ctx, in->current, format, main_ctx, lysp_load_module_check, &check_data);
+ } else {
+ mod = lys_parse_mem_module(ctx, in->current, format, implement, lysp_load_module_check, &check_data);
+
+ }
+ LY_CHECK_ERR_GOTO(!mod, ly_in_free(in, 1);ly_errcode(ctx), cleanup);
if (main_ctx) {
- fp = &((struct lysp_submodule*)mod)->filepath;
+ lys_parser_fill_filepath(ctx, in, &((struct lysp_submodule*)mod)->filepath);
} else {
- fp = &((struct lys_module*)mod)->filepath;
+ lys_parser_fill_filepath(ctx, in, &((struct lys_module*)mod)->filepath);
}
- if (!(*fp)) {
- if (realpath(filepath, rpath) != NULL) {
- (*fp) = lydict_insert(ctx, rpath, 0);
- } else {
- (*fp) = lydict_insert(ctx, filepath, 0);
- }
+ ly_in_free(in, 1);
+
+ if (mod && implement) {
+ lys_compile((struct lys_module**)&mod, 0);
}
*result = mod;
@@ -858,10 +856,8 @@
if (module_data_free) {
module_data_free((void*)module_data, ctx->imp_clb_data);
}
- if (*mod && implement && lys_compile(*mod, 0)) {
- ly_set_rm(&ctx->list, *mod, NULL);
- lys_module_free(*mod, NULL);
- *mod = NULL;
+ if (*mod && implement) {
+ lys_compile(mod, 0);
}
}
}
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index c4370b6..ddc7966 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -340,12 +340,13 @@
/**
* @brief Compile printable schema into a validated schema linking all the references.
*
- * @param[in, out] mod Schema structure holding pointers to both schema structure types. The ::lys_module#parsed
+ * @param[in, out] mod Pointer to the schema structure holding pointers to both schema structure types. The ::lys_module#parsed
* member is used as input and ::lys_module#compiled is used to hold the result of the compilation.
+ * If the compilation fails, the whole module is removed from context, freed and @p mod is set to NULL!
* @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
* @return LY_ERR value - LY_SUCCESS or LY_EVALID.
*/
-LY_ERR lys_compile(struct lys_module *mod, int options);
+LY_ERR lys_compile(struct lys_module **mod, int options);
/**
* @brief Get address of a node's actions list if any.
@@ -556,81 +557,13 @@
lys_custom_check custom_check, void *check_data);
/**
- * @brief Parse module or submodule from a file descriptor.
+ * @brief Fill filepath value if available in input handler @p in
*
- * The modules are added into the context, submodules not. The latest_revision flag is updated in both cases.
- *
- * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
- * in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @param[in] implement Flag if the schema is supposed to be marked as implemented.
- * @param[in] main_ctx Parser context of the main module in case of parsing submodule. This flag decides if the module
- * or submodule was expected to be parsed.
- * @param[in] custom_check Callback to check the parsed schema before it is accepted.
- * @param[in] check_data Caller's data to pass to the custom_check callback.
- * @return Pointer to the data model structure or NULL on error.
+ * @param[in] ctx Context with dictionary where the filepath value will be stored.
+ * @param[in] in Input handler to examine (filepath is not available for all the input types).
+ * @param[out] filepath Address of the variable where the filepath is stored.
*/
-void *lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct lys_parser_ctx *main_ctx,
- lys_custom_check custom_check, void *check_data);
-
-/**
- * @brief Parse YANG module from a file descriptor.
- *
- * The modules are added into the context. The latest_revision flag is updated.
- *
- * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
- * in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @param[in] implement Flag if the schema is supposed to be marked as implemented.
- * @param[in] custom_check Callback to check the parsed schema before it is accepted.
- * @param[in] check_data Caller's data to pass to the custom_check callback.
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lys_module *lys_parse_fd_module(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement,
- lys_custom_check custom_check, void *check_data);
-
-/**
- * @brief Parse submodule from a file descriptor.
- *
- * The latest_revision flag of submodules is updated.
- *
- * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
- * in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @param[in] main_ctx Parser context of the main module.
- * @param[in] custom_check Callback to check the parsed schema before it is accepted.
- * @param[in] check_data Caller's data to pass to the custom_check callback.
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lysp_submodule *lys_parse_fd_submodule(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, struct lys_parser_ctx *main_ctx,
- lys_custom_check custom_check, void *check_data);
-
-/**
- * @brief Parse YANG module from a filepath.
- *
- * The modules are added into the context. The latest_revision flag is updated.
- *
- * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
- *
- * @param[in] ctx libyang context where to process the data model.
- * @param[in] path Path to the file with the model in the specified format.
- * @param[in] format Format of the input data (YANG or YIN).
- * @param[in] implement Flag if the schema is supposed to be marked as implemented.
- * @param[in] custom_check Callback to check the parsed schema before it is accepted.
- * @param[in] check_data Caller's data to pass to the custom_check callback.
- * @return Pointer to the data model structure or NULL on error.
- */
-struct lys_module *lys_parse_path_(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, int implement,
- lys_custom_check custom_check, void *check_data);
+void lys_parser_fill_filepath(struct ly_ctx *ctx, struct ly_in *in, const char **filepath);
/**
* @brief Load the (sub)module into the context.
@@ -828,53 +761,6 @@
void lys_module_free(struct lys_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv));
/**
- * @brief Parse submodule from YANG data.
- * @param[in,out] ctx Parser context.
- * @param[in] ly_ctx Context of YANG schemas.
- * @param[in] main_ctx Parser context of main module.
- * @param[in] data Input data to be parsed.
- * @param[out] submod Pointer to the parsed submodule structure.
- * @return LY_ERR value - LY_SUCCESS, LY_EINVAL or LY_EVALID.
- */
-LY_ERR yang_parse_submodule(struct lys_yang_parser_ctx **context, struct ly_ctx *ly_ctx, struct lys_parser_ctx *main_ctx,
- const char *data, struct lysp_submodule **submod);
-
-/**
- * @brief Parse module from YANG data.
- * @param[in] ctx Parser context.
- * @param[in] data Input data to be parsed.
- * @param[in, out] mod Prepared module structure where the parsed information, including the parsed
- * module structure, will be filled in.
- * @return LY_ERR value - LY_SUCCESS, LY_EINVAL or LY_EVALID.
- */
-LY_ERR yang_parse_module(struct lys_yang_parser_ctx **context, const char *data, struct lys_module *mod);
-
-/**
- * @brief Parse module from YIN data.
- *
- * @param[in,out] yin_ctx Context created during parsing, is used to finalize lysp_model after it's completly parsed.
- * @param[in] data Input data to be parsed.
- * @param[in,out] mod Prepared module structure where the parsed information, including the parsed
- * module structure, will be filled in.
- *
- * @return LY_ERR values.
- */
-LY_ERR yin_parse_module(struct lys_yin_parser_ctx **yin_ctx, const char *data, struct lys_module *mod);
-
-/**
- * @brief Parse submodule from YIN data.
- *
- * @param[in,out] yin_ctx Context created during parsing, is used to finalize lysp_model after it's completly parsed.
- * @param[in] ctx Libyang context.
- * @param[in] main_ctx Parser context of main module.
- * @param[in,out] data Input data to be parsed.
- * @param[in,out] submod Submodule structure where the parsed information, will be filled in.
- * @return LY_ERR values.
- */
-LY_ERR yin_parse_submodule(struct lys_yin_parser_ctx **yin_ctx, struct ly_ctx *ctx, struct lys_parser_ctx *main_ctx,
- const char *data, struct lysp_submodule **submod);
-
-/**
* @brief Make the specific module implemented, use the provided value as flag.
*
* @param[in] mod Module to make implemented. It is not an error to provide already implemented module, it just does nothing.
diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt
index 39b1854..9a5905a 100644
--- a/tests/utests/CMakeLists.txt
+++ b/tests/utests/CMakeLists.txt
@@ -2,6 +2,7 @@
utest:test_common
utest:test_set
utest:test_hash_table
+ utest:test_inout
utest:test_context
utest:test_xml
utest:test_xpath
@@ -41,6 +42,7 @@
" "
" "
" "
+ " "
" ")
set(tests ${tests} ${local_tests} PARENT_SCOPE)
set(tests_wraps ${tests_wraps} ${local_tests_wraps} PARENT_SCOPE)
diff --git a/tests/utests/schema/test_parser_yang.c b/tests/utests/schema/test_parser_yang.c
index 6311dfc..31b9c34 100644
--- a/tests/utests/schema/test_parser_yang.c
+++ b/tests/utests/schema/test_parser_yang.c
@@ -23,6 +23,7 @@
#include "../../../src/common.h"
#include "../../../src/tree_schema.h"
#include "../../../src/tree_schema_internal.h"
+#include "../../../src/parser_internal.h"
/* originally static functions from tree_schema_free.c and parser_yang.c */
void lysp_ext_instance_free(struct ly_ctx *ctx, struct lysp_ext_instance *ext);
diff --git a/tests/utests/schema/test_parser_yin.c b/tests/utests/schema/test_parser_yin.c
index 809bfd6..d2576b9 100644
--- a/tests/utests/schema/test_parser_yin.c
+++ b/tests/utests/schema/test_parser_yin.c
@@ -24,6 +24,7 @@
#include "../../../src/common.h"
#include "../../../src/tree_schema.h"
#include "../../../src/tree_schema_internal.h"
+#include "../../../src/parser_internal.h"
#include "../../../src/parser_yin.h"
#include "../../../src/xml.h"
#include "../../../src/xpath.h"
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index 9c90ce5..ea6166e 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -20,10 +20,11 @@
#include <stdio.h>
#include <string.h>
-#include "../../src/common.h"
-#include "../../src/tree_schema_internal.h"
-#include "../../src/xpath.h"
-#include "../../src/plugins_types.h"
+#include "../../../src/common.h"
+#include "../../../src/tree_schema_internal.h"
+#include "../../../src/parser_internal.h"
+#include "../../../src/xpath.h"
+#include "../../../src/plugins_types.h"
void lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat);
void yang_parser_ctx_free(struct lys_yang_parser_ctx *ctx);
@@ -96,97 +97,60 @@
}
static void
-reset_mod(struct lys_module *module)
-{
- struct ly_ctx *ctx = module->ctx;
- lysc_module_free(module->compiled, NULL);
- lysp_module_free(module->parsed);
-
- FREE_STRING(ctx, module->name);
- FREE_STRING(ctx, module->ns);
- FREE_STRING(ctx, module->prefix);
- FREE_STRING(ctx, module->filepath);
- FREE_STRING(ctx, module->org);
- FREE_STRING(ctx, module->contact);
- FREE_STRING(ctx, module->dsc);
- FREE_STRING(ctx, module->ref);
- FREE_ARRAY(ctx, module->off_features, lysc_feature_free);
-
- memset(module, 0, sizeof *module);
- module->ctx = ctx;
- module->implemented = 1;
-}
-
-static void
test_module(void **state)
{
*state = test_module;
const char *str;
- struct lys_yang_parser_ctx *ctx = NULL;
- struct lys_module mod = {0};
+ struct ly_ctx *ctx = NULL;
+ struct lys_module *mod = NULL;
struct lysc_feature *f;
struct lysc_iffeature *iff;
str = "module test {namespace urn:test; prefix t;"
"feature f1;feature f2 {if-feature f1;}}";
- assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &mod.ctx));
- reset_mod(&mod);
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
assert_int_equal(LY_EINVAL, lys_compile(NULL, 0));
logbuf_assert("Invalid argument mod (lys_compile()).");
assert_int_equal(LY_EINVAL, lys_compile(&mod, 0));
- logbuf_assert("Invalid argument mod->parsed (lys_compile()).");
- assert_int_equal(LY_SUCCESS, yang_parse_module(&ctx, str, &mod));
- yang_parser_ctx_free(ctx);
- mod.implemented = 0;
+ logbuf_assert("Invalid argument *mod (lys_compile()).");
+ assert_non_null(mod = lys_parse_mem_module(ctx, str, LYS_IN_YANG, 0, NULL, NULL));
+ assert_int_equal(0, mod->implemented);
assert_int_equal(LY_SUCCESS, lys_compile(&mod, 0));
- assert_null(mod.compiled);
- mod.implemented = 1;
+ assert_null(mod->compiled);
+ mod->implemented = 1;
assert_int_equal(LY_SUCCESS, lys_compile(&mod, 0));
- assert_non_null(mod.compiled);
- assert_string_equal("test", mod.name);
- assert_string_equal("urn:test", mod.ns);
- assert_string_equal("t", mod.prefix);
+ assert_non_null(mod->compiled);
+ assert_string_equal("test", mod->name);
+ assert_string_equal("urn:test", mod->ns);
+ assert_string_equal("t", mod->prefix);
/* features */
- assert_non_null(mod.compiled->features);
- assert_int_equal(2, LY_ARRAY_SIZE(mod.compiled->features));
- f = &mod.compiled->features[1];
+ assert_non_null(mod->compiled->features);
+ assert_int_equal(2, LY_ARRAY_SIZE(mod->compiled->features));
+ f = &mod->compiled->features[1];
assert_non_null(f->iffeatures);
assert_int_equal(1, LY_ARRAY_SIZE(f->iffeatures));
iff = &f->iffeatures[0];
assert_non_null(iff->expr);
assert_non_null(iff->features);
assert_int_equal(1, LY_ARRAY_SIZE(iff->features));
- assert_ptr_equal(&mod.compiled->features[0], iff->features[0]);
-
- lysc_module_free(mod.compiled, NULL);
-
- assert_int_equal(LY_SUCCESS, lys_compile(&mod, LYSC_OPT_FREE_SP));
- assert_non_null(mod.compiled);
-
- lysc_module_free(mod.compiled, NULL);
- mod.compiled = NULL;
+ assert_ptr_equal(&mod->compiled->features[0], iff->features[0]);
/* submodules cannot be compiled directly */
str = "submodule test {belongs-to xxx {prefix x;}}";
- assert_int_equal(LY_EINVAL, yang_parse_module(&ctx, str, &mod));
- yang_parser_ctx_free(ctx);
+ assert_null(lys_parse_mem_module(ctx, str, LYS_IN_YANG, 1, NULL, NULL));
logbuf_assert("Input data contains submodule which cannot be parsed directly without its main module.");
- assert_null(mod.parsed);
- reset_mod(&mod);
/* data definition name collision in top level */
- assert_int_equal(LY_SUCCESS, yang_parse_module(&ctx, "module aa {namespace urn:aa;prefix aa;"
- "leaf a {type string;} container a{presence x;}}", &mod));
- yang_parser_ctx_free(ctx);
+ assert_non_null(mod = lys_parse_mem_module(ctx, "module aa {namespace urn:aa;prefix aa;"
+ "leaf a {type string;} container a{presence x;}}", LYS_IN_YANG, 1, NULL, NULL));
assert_int_equal(LY_EVALID, lys_compile(&mod, 0));
logbuf_assert("Duplicate identifier \"a\" of data definition/RPC/action/notification statement. /aa:a");
- assert_null(mod.compiled);
- reset_mod(&mod);
+ assert_null(mod);
*state = NULL;
- ly_ctx_destroy(mod.ctx, NULL);
+ ly_ctx_destroy(ctx, NULL);
}
diff --git a/tests/utests/test_context.c b/tests/utests/test_context.c
index fe06f96..d55e5b1 100644
--- a/tests/utests/test_context.c
+++ b/tests/utests/test_context.c
@@ -325,17 +325,23 @@
/* reloading module in case only the compiled module resists in the context */
mod1 = lys_parse_mem_module(ctx, "module w {namespace urn:w;prefix w;revision 2018-10-24;}", LYS_IN_YANG, 1, NULL, NULL);
assert_non_null(mod1);
- assert_int_equal(LY_SUCCESS, lys_compile(mod1, LYSC_OPT_FREE_SP));
+ assert_int_equal(LY_SUCCESS, lys_compile(&mod1, LYSC_OPT_FREE_SP));
assert_non_null(mod1->compiled);
assert_null(mod1->parsed);
+
mod2 = lys_parse_mem_module(ctx, "module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", LYS_IN_YANG, 1, NULL, NULL);
assert_non_null(mod2);
/* mod1->parsed is necessary to compile mod2 because of possible groupings, typedefs, ... */
ly_ctx_set_module_imp_clb(ctx, NULL, NULL);
- assert_int_equal(LY_ENOTFOUND, lys_compile(mod2, 0));
+ assert_int_equal(LY_ENOTFOUND, lys_compile(&mod2, 0));
logbuf_assert("Unable to reload \"w\" module to import it into \"z\", source data not found.");
+ assert_null(mod2);
+
+ mod2 = lys_parse_mem_module(ctx, "module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", LYS_IN_YANG, 1, NULL, NULL);
+ assert_non_null(mod2);
ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "module w {namespace urn:w;prefix w;revision 2018-10-24;}");
- assert_int_equal(LY_SUCCESS, lys_compile(mod2, 0));
+ assert_int_equal(LY_SUCCESS, lys_compile(&mod2, 0));
+ assert_non_null(mod2);
assert_non_null(mod1->parsed);
assert_string_equal("w", mod1->name);
diff --git a/tests/utests/test_inout.c b/tests/utests/test_inout.c
new file mode 100644
index 0000000..cbec9c7
--- /dev/null
+++ b/tests/utests/test_inout.c
@@ -0,0 +1,438 @@
+/*
+ * @file test_inout.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for input and output handlers functions
+ *
+ * Copyright (c) 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "tests/config.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "../../src/common.h"
+#include "../../src/log.h"
+#include "../../src/printer.h"
+#include "../../src/parser.h"
+
+
+#define BUFSIZE 1024
+char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+
+/* set to 0 to printing error messages to stderr instead of checking them in code */
+#define ENABLE_LOGGER_CHECKING 1
+
+#if ENABLE_LOGGER_CHECKING
+static void
+logger(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+ (void) level; /* unused */
+ if (store) {
+ if (path && path[0]) {
+ snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
+ } else {
+ strncpy(logbuf, msg, BUFSIZE - 1);
+ }
+ if (store > 0) {
+ --store;
+ }
+ }
+}
+#endif
+
+static int
+logger_setup(void **state)
+{
+ (void) state; /* unused */
+
+ ly_set_log_clb(logger, 0);
+
+ return 0;
+}
+
+static int
+logger_teardown(void **state)
+{
+ (void) state; /* unused */
+#if ENABLE_LOGGER_CHECKING
+ if (*state) {
+ fprintf(stderr, "%s\n", logbuf);
+ }
+#endif
+ return 0;
+}
+
+void
+logbuf_clean(void)
+{
+ logbuf[0] = '\0';
+}
+
+#if ENABLE_LOGGER_CHECKING
+# define logbuf_assert(str) assert_string_equal(logbuf, str)
+#else
+# define logbuf_assert(str)
+#endif
+
+static void
+test_input_mem(void **state)
+{
+ struct ly_in *in = NULL;
+ char *str1 = "a", *str2 = "b";
+
+ *state = test_input_mem;
+
+ assert_int_equal(LY_EINVAL, ly_in_new_memory(NULL, NULL));
+ assert_int_equal(LY_EINVAL, ly_in_new_memory(str1, NULL));
+ assert_null(ly_in_memory(NULL, NULL));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in));
+ assert_int_equal(LY_IN_MEMORY, ly_in_type(in));
+ assert_ptr_equal(str1, ly_in_memory(in, str2));
+ assert_ptr_equal(str2, ly_in_memory(in, NULL));
+ assert_ptr_equal(str2, ly_in_memory(in, NULL));
+ ly_in_free(in, 0);
+
+ /* cleanup */
+ *state = NULL;
+}
+
+static void
+test_input_fd(void **state)
+{
+ struct ly_in *in = NULL;
+ int fd1, fd2;
+ struct stat statbuf;
+
+ *state = test_input_fd;
+
+ assert_int_equal(LY_EINVAL, ly_in_new_fd(-1, NULL));
+ assert_int_equal(-1, ly_in_fd(NULL, -1));
+
+ assert_int_not_equal(-1, fd1 = open(__FILE__, O_RDONLY));
+ assert_int_not_equal(-1, fd2 = open(__FILE__, O_RDONLY));
+
+ assert_int_equal(LY_EINVAL, ly_in_new_fd(fd1, NULL));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_fd(fd1, &in));
+ assert_int_equal(LY_IN_FD, ly_in_type(in));
+ assert_ptr_equal(fd1, ly_in_fd(in, fd2));
+ assert_ptr_equal(fd2, ly_in_fd(in, -1));
+ assert_ptr_equal(fd2, ly_in_fd(in, -1));
+ ly_in_free(in, 1);
+ /* fd1 is still open */
+ assert_int_equal(0, fstat(fd1, &statbuf));
+ close(fd1);
+ /* but fd2 was closed by ly_in_free() */
+ errno = 0;
+ assert_int_equal(-1, fstat(fd2, &statbuf));
+ assert_int_equal(errno, EBADF);
+
+ /* cleanup */
+ *state = NULL;
+}
+
+static void
+test_input_file(void **state)
+{
+ struct ly_in *in = NULL;
+ FILE *f1 = NULL, *f2 = NULL;
+
+ *state = test_input_file;
+
+ assert_int_equal(LY_EINVAL, ly_in_new_file(NULL, NULL));
+ assert_null(ly_in_file(NULL, NULL));
+
+ assert_int_not_equal(-1, f1 = fopen(__FILE__, "r"));
+ assert_int_not_equal(-1, f2 = fopen(__FILE__, "r"));
+
+ assert_int_equal(LY_EINVAL, ly_in_new_file(f1, NULL));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_file(f1, &in));
+ assert_int_equal(LY_IN_FILE, ly_in_type(in));
+ assert_ptr_equal(f1, ly_in_file(in, f2));
+ assert_ptr_equal(f2, ly_in_file(in, NULL));
+ assert_ptr_equal(f2, ly_in_file(in, NULL));
+ ly_in_free(in, 1);
+ /* f1 is still open */
+ assert_int_not_equal(-1, fileno(f1));
+ fclose(f1);
+ /* but f2 was closed by ly_in_free() */
+
+ /* cleanup */
+ *state = NULL;
+}
+
+static void
+test_input_filepath(void **state)
+{
+ struct ly_in *in = NULL;
+ const char *path1 = __FILE__, *path2 = __FILE__;
+
+ *state = test_input_filepath;
+
+ assert_int_equal(LY_EINVAL, ly_in_new_filepath(NULL, 0, NULL));
+ assert_int_equal(LY_EINVAL, ly_in_new_filepath(path1, 0, NULL));
+ assert_ptr_equal(((void *)-1), ly_in_filepath(NULL, NULL, 0));
+
+ assert_int_equal(LY_SUCCESS, ly_in_new_filepath(path1, 0, &in));
+ assert_int_equal(LY_IN_FILEPATH, ly_in_type(in));
+ assert_ptr_equal(NULL, ly_in_filepath(in, path2, 0));
+ assert_string_equal(path2, ly_in_filepath(in, NULL, 0));
+ ly_in_free(in, 0);
+
+ /* cleanup */
+ *state = NULL;
+}
+
+static void
+test_output_mem(void **state)
+{
+ struct ly_out *out = NULL;
+ char *buf1 = NULL, *buf2 = NULL;
+
+ *state = test_output_mem;
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out));
+ assert_int_equal(LY_OUT_MEMORY, ly_out_type(out));
+ ly_write(out, "test", 4);
+ assert_ptr_equal(buf1, ly_out_memory(out, &buf2, 0));
+ assert_ptr_equal(buf2, ly_out_memory(out, NULL, 0));
+ assert_ptr_equal(buf2, ly_out_memory(out, &buf1, strlen(buf1)));
+ ly_out_free(out, NULL, 0);
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, strlen(buf1), &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_memory(&buf1, 0, &out));
+ assert_int_equal(10, ly_print(out, "test %s", "print"));
+ assert_string_equal("test print", buf1);
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+ assert_int_equal(8, ly_write(out, "rewrite", 8));
+ assert_string_equal("rewrite", buf1);
+ ly_out_free(out, NULL, 1);
+
+ /* cleanup */
+ *state = NULL;
+}
+
+static void
+test_output_fd(void **state)
+{
+ struct ly_out *out = NULL;
+ int fd1, fd2;
+ char buf[31] = {0};
+ const char *filepath = "/tmp/libyang_test_output";
+
+ *state = test_output_fd;
+
+ assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+ assert_int_equal(LY_OUT_FD, ly_out_type(out));
+ assert_ptr_equal(fd1, ly_out_fd(out, fd2));
+ assert_ptr_equal(fd2, ly_out_fd(out, -1));
+ assert_ptr_equal(fd2, ly_out_fd(out, fd1));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(0, close(fd2));
+ assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+ assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ /* truncate file to start with no data */
+ assert_int_equal(0, ftruncate(fd1, 0));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_fd(fd1, &out));
+ assert_int_equal(10, ly_print(out, "test %s", "print"));
+ ly_print_flush(out);
+ assert_int_equal(10, read(fd2, buf, 30));
+ assert_string_equal("test print", buf);
+ assert_int_equal(0, lseek(fd2, 0, SEEK_SET));
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+ assert_int_equal(8, ly_write(out, "rewrite", 8));
+ ly_print_flush(out);
+ assert_int_equal(8, read(fd2, buf, 30));
+ assert_string_equal("rewrite", buf);
+
+ close(fd2);
+ ly_out_free(out, NULL, 1);
+
+ /* cleanup */
+ *state = NULL;
+}
+
+static void
+test_output_file(void **state)
+{
+ struct ly_out *out = NULL;
+ FILE *f1, *f2;
+ char buf[31] = {0};
+ const char *filepath = "/tmp/libyang_test_output";
+
+ *state = test_output_file;
+
+ assert_int_not_equal(-1, f1 = fopen(filepath, "w"));
+ assert_int_not_equal(-1, f2 = fopen(filepath, "w"));
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+ assert_int_equal(LY_OUT_FILE, ly_out_type(out));
+ assert_ptr_equal(f1, ly_out_file(out, f2));
+ assert_ptr_equal(f2, ly_out_file(out, NULL));
+ assert_ptr_equal(f2, ly_out_file(out, f1));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(0, fclose(f2));
+ assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+ assert_int_not_equal(-1, f1 = fopen(filepath, "w"));
+ assert_int_not_equal(-1, f2 = fopen(filepath, "r"));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_file(f1, &out));
+ assert_int_equal(10, ly_print(out, "test %s", "print"));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f2));
+ assert_string_equal("test print", buf);
+ assert_int_equal(0, fseek(f2, 0, SEEK_SET));
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+ assert_int_equal(8, ly_write(out, "rewrite", 8));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f2));
+ assert_string_equal("rewrite", buf);
+
+ fclose(f2);
+ ly_out_free(out, NULL, 1);
+
+ /* cleanup */
+ *state = NULL;
+}
+
+static void
+test_output_filepath(void **state)
+{
+ struct ly_out *out = NULL;
+ FILE *f1;
+ char buf[31] = {0};
+ const char *fp1 = "/tmp/libyang_test_output";
+ const char *fp2 = "/tmp/libyang_test_output2";
+
+ *state = test_output_filepath;
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+ assert_int_equal(LY_OUT_FILEPATH, ly_out_type(out));
+ assert_ptr_equal(NULL, ly_out_filepath(out, fp2));
+ assert_string_equal(fp2, ly_out_filepath(out, NULL));
+ assert_ptr_equal(NULL, ly_out_filepath(out, fp1));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+ ly_out_free(out, NULL, 1);
+
+ /* writing data */
+ assert_int_not_equal(-1, f1 = fopen(fp1, "r"));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_filepath(fp1, &out));
+ assert_int_equal(10, ly_print(out, "test %s", "print"));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f1));
+ assert_string_equal("test print", buf);
+ assert_int_equal(0, fseek(f1, 0, SEEK_SET));
+ assert_int_equal(LY_SUCCESS, ly_out_reset(out));
+
+ assert_int_equal(8, ly_write(out, "rewrite", 8));
+ ly_print_flush(out);
+ assert_non_null(fgets(buf, 31, f1));
+ assert_string_equal("rewrite", buf);
+
+ fclose(f1);
+ ly_out_free(out, NULL, 1);
+
+ /* cleanup */
+ *state = NULL;
+}
+
+static void
+test_output_clb(void **state)
+{
+ struct ly_out *out = NULL;
+ int fd1, fd2;
+ char buf[31] = {0};
+ const char *filepath = "/tmp/libyang_test_output";
+
+ *state = test_output_clb;
+
+ assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+
+ /* manipulate with the handler */
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb((ssize_t (*)(void *, const void *, size_t))write, (void*)(intptr_t)fd1, &out));
+ assert_int_equal(LY_OUT_CALLBACK, ly_out_type(out));
+ assert_ptr_equal(fd1, ly_out_clb_arg(out, (void*)(intptr_t)fd2));
+ assert_ptr_equal(fd2, ly_out_clb_arg(out, NULL));
+ assert_ptr_equal(fd2, ly_out_clb_arg(out, (void*)(intptr_t)fd1));
+ assert_ptr_equal(write, ly_out_clb(out, (ssize_t (*)(void *, const void *, size_t))write));
+ ly_out_free(out, NULL, 0);
+ assert_int_equal(0, close(fd2));
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb((ssize_t (*)(void *, const void *, size_t))write, (void*)(intptr_t)fd1, &out));
+ ly_out_free(out, (void (*)(void *))close, 0);
+
+ /* writing data */
+ assert_int_not_equal(-1, fd1 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ assert_int_not_equal(-1, fd2 = open(filepath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
+ /* truncate file to start with no data */
+ assert_int_equal(0, ftruncate(fd1, 0));
+
+ assert_int_equal(LY_SUCCESS, ly_out_new_clb((ssize_t (*)(void *, const void *, size_t))write, (void*)(intptr_t)fd1, &out));
+ assert_int_equal(10, ly_print(out, "test %s", "print"));
+ assert_int_equal(10, read(fd2, buf, 30));
+ assert_string_equal("test print", buf);
+
+ close(fd2);
+ ly_out_free(out, (void (*)(void *))close, 0);
+
+ /* cleanup */
+ *state = NULL;
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(test_input_mem, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_input_fd, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_input_file, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_input_filepath, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_output_mem, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_output_fd, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_output_file, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_output_filepath, logger_setup, logger_teardown),
+ cmocka_unit_test_setup_teardown(test_output_clb, logger_setup, logger_teardown),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}