blob: 45f175006e16909cd5af1bef4d1cc69f4217e8ee [file] [log] [blame]
Radek Krejci5aeea3a2018-09-05 13:29:36 +02001/**
2 * @file log.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Logger routines implementations
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 */
14
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
Radek Krejcif8dc59a2020-11-25 13:47:44 +010016#define _POSIX_C_SOURCE 200809L /* strdup */
Radek Krejci535ea9f2020-05-29 16:01:05 +020017
18#include "log.h"
Radek Krejcib7db73a2018-10-24 14:18:40 +020019
Radek Krejci5aeea3a2018-09-05 13:29:36 +020020#include <assert.h>
Radek Krejcic04f0a22018-09-21 15:49:45 +020021#include <inttypes.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020022#include <pthread.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020023#include <stdarg.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020024#include <stdint.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020025#include <stdio.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020026#include <stdlib.h>
27#include <string.h>
Radek Krejci5aeea3a2018-09-05 13:29:36 +020028
Radek Krejci535ea9f2020-05-29 16:01:05 +020029#include "common.h"
Radek Krejciaa45bda2020-07-20 07:43:38 +020030#include "compat.h"
Radek Krejci0935f412019-08-20 16:15:18 +020031#include "plugins_exts.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020032#include "tree_data.h"
33#include "tree_schema.h"
Radek Krejci5aeea3a2018-09-05 13:29:36 +020034
Radek Krejci52b6d512020-10-12 12:33:17 +020035volatile LY_LOG_LEVEL ly_ll = LY_LLWRN;
Radek Krejci1deb5be2020-08-26 16:43:36 +020036volatile uint32_t ly_log_opts = LY_LOLOG | LY_LOSTORE_LAST;
Michal Vaskod8085612020-08-21 12:55:23 +020037static ly_log_clb log_clb;
Radek Krejci857189e2020-09-01 13:26:36 +020038static volatile ly_bool path_flag = 1;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020039#ifndef NDEBUG
Radek Krejci68433c92020-10-12 17:03:55 +020040volatile uint32_t ly_ldbg_groups = 0;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020041#endif
42
Radek Krejci94aa9942018-09-07 17:12:17 +020043/* how many bytes add when enlarging buffers */
44#define LY_BUF_STEP 128
45
Radek Krejcid33273d2018-10-25 14:55:52 +020046API LY_ERR
47ly_errcode(const struct ly_ctx *ctx)
48{
49 struct ly_err_item *i;
50
Radek Krejci572ee602020-09-16 14:35:08 +020051 i = ly_err_last(ctx);
Radek Krejcid33273d2018-10-25 14:55:52 +020052 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020053 return i->no;
Radek Krejcid33273d2018-10-25 14:55:52 +020054 }
55
56 return LY_SUCCESS;
57}
58
Radek Krejci5aeea3a2018-09-05 13:29:36 +020059API LY_VECODE
60ly_vecode(const struct ly_ctx *ctx)
61{
62 struct ly_err_item *i;
63
Radek Krejci572ee602020-09-16 14:35:08 +020064 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020065 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020066 return i->vecode;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020067 }
68
69 return LYVE_SUCCESS;
70}
71
72API const char *
73ly_errmsg(const struct ly_ctx *ctx)
74{
75 struct ly_err_item *i;
76
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020077 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020078
Radek Krejci572ee602020-09-16 14:35:08 +020079 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020080 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020081 return i->msg;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020082 }
83
84 return NULL;
85}
86
87API const char *
88ly_errpath(const struct ly_ctx *ctx)
89{
90 struct ly_err_item *i;
91
Michal Vaskob3d0d6b2018-09-07 10:17:33 +020092 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020093
Radek Krejci572ee602020-09-16 14:35:08 +020094 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +020095 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +020096 return i->path;
Radek Krejci5aeea3a2018-09-05 13:29:36 +020097 }
98
99 return NULL;
100}
101
102API const char *
103ly_errapptag(const struct ly_ctx *ctx)
104{
105 struct ly_err_item *i;
106
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200107 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200108
Radek Krejci572ee602020-09-16 14:35:08 +0200109 i = ly_err_last(ctx);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200110 if (i) {
Radek Krejci572ee602020-09-16 14:35:08 +0200111 return i->apptag;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200112 }
113
114 return NULL;
115}
116
117API struct ly_err_item *
Radek Krejcie7b95092019-05-15 11:03:07 +0200118ly_err_new(LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
119{
120 struct ly_err_item *eitem;
121
122 eitem = malloc(sizeof *eitem);
123 LY_CHECK_ERR_RET(!eitem, LOGMEM(NULL), NULL);
124 eitem->prev = eitem;
125 eitem->next = NULL;
126
127 /* fill in the information */
128 eitem->level = level;
129 eitem->no = no;
130 eitem->vecode = vecode;
131 eitem->msg = msg;
132 eitem->path = path;
133 eitem->apptag = apptag;
134
135 return eitem;
136}
137
138API struct ly_err_item *
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200139ly_err_first(const struct ly_ctx *ctx)
140{
Michal Vaskob3d0d6b2018-09-07 10:17:33 +0200141 LY_CHECK_ARG_RET(NULL, ctx, NULL);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200142
143 return pthread_getspecific(ctx->errlist_key);
144}
145
Radek Krejci572ee602020-09-16 14:35:08 +0200146API struct ly_err_item *
147ly_err_last(const struct ly_ctx *ctx)
148{
149 const struct ly_err_item *e;
150
151 LY_CHECK_ARG_RET(NULL, ctx, NULL);
152
153 e = pthread_getspecific(ctx->errlist_key);
154 return e ? e->prev : NULL;
155}
156
Radek Krejcie7b95092019-05-15 11:03:07 +0200157API void
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200158ly_err_free(void *ptr)
159{
160 struct ly_err_item *i, *next;
161
162 /* clean the error list */
163 for (i = (struct ly_err_item *)ptr; i; i = next) {
164 next = i->next;
165 free(i->msg);
166 free(i->path);
167 free(i->apptag);
168 free(i);
169 }
170}
171
172API void
173ly_err_clean(struct ly_ctx *ctx, struct ly_err_item *eitem)
174{
175 struct ly_err_item *i, *first;
176
177 first = ly_err_first(ctx);
178 if (first == eitem) {
179 eitem = NULL;
180 }
181 if (eitem) {
182 /* disconnect the error */
Radek Krejci1e008d22020-08-17 11:37:37 +0200183 for (i = first; i && (i->next != eitem); i = i->next) {}
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200184 assert(i);
185 i->next = NULL;
186 first->prev = i;
187 /* free this err and newer */
188 ly_err_free(eitem);
189 } else {
190 /* free all err */
191 ly_err_free(first);
192 pthread_setspecific(ctx->errlist_key, NULL);
193 }
194}
195
196API LY_LOG_LEVEL
Radek Krejci52b6d512020-10-12 12:33:17 +0200197ly_log_level(LY_LOG_LEVEL level)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200198{
Radek Krejci52b6d512020-10-12 12:33:17 +0200199 LY_LOG_LEVEL prev = ly_ll;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200200
Radek Krejci52b6d512020-10-12 12:33:17 +0200201 ly_ll = level;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200202 return prev;
203}
204
Radek Krejci1deb5be2020-08-26 16:43:36 +0200205API uint32_t
206ly_log_options(uint32_t opts)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200207{
Radek Krejci1deb5be2020-08-26 16:43:36 +0200208 uint32_t prev = ly_log_opts;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200209
210 ly_log_opts = opts;
211 return prev;
212}
213
Radek Krejciebdaed02020-11-09 13:05:06 +0100214API uint32_t
Radek Krejci68433c92020-10-12 17:03:55 +0200215ly_log_dbg_groups(uint32_t dbg_groups)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200216{
217#ifndef NDEBUG
Radek Krejciebdaed02020-11-09 13:05:06 +0100218 uint32_t prev = ly_ldbg_groups;
219
Radek Krejci68433c92020-10-12 17:03:55 +0200220 ly_ldbg_groups = dbg_groups;
Radek Krejciebdaed02020-11-09 13:05:06 +0100221 return prev;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200222#else
223 (void)dbg_groups;
Radek Krejciebdaed02020-11-09 13:05:06 +0100224 return 0;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200225#endif
226}
227
228API void
Radek Krejci857189e2020-09-01 13:26:36 +0200229ly_set_log_clb(ly_log_clb clb, ly_bool path)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200230{
Michal Vaskod8085612020-08-21 12:55:23 +0200231 log_clb = clb;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200232 path_flag = path;
233}
234
Michal Vaskod8085612020-08-21 12:55:23 +0200235API ly_log_clb
236ly_get_log_clb(void)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200237{
Michal Vaskod8085612020-08-21 12:55:23 +0200238 return log_clb;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200239}
240
241static LY_ERR
242log_store(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *msg, char *path, char *apptag)
243{
244 struct ly_err_item *eitem, *last;
245
246 assert(ctx && (level < LY_LLVRB));
247
248 eitem = pthread_getspecific(ctx->errlist_key);
249 if (!eitem) {
250 /* if we are only to fill in path, there must have been an error stored */
251 assert(msg);
252 eitem = malloc(sizeof *eitem);
253 LY_CHECK_GOTO(!eitem, mem_fail);
254 eitem->prev = eitem;
255 eitem->next = NULL;
256
257 pthread_setspecific(ctx->errlist_key, eitem);
258 } else if (!msg) {
259 /* only filling the path */
260 assert(path);
261
262 /* find last error */
263 eitem = eitem->prev;
264 do {
265 if (eitem->level == LY_LLERR) {
266 /* fill the path */
267 free(eitem->path);
268 eitem->path = path;
269 return LY_SUCCESS;
270 }
271 eitem = eitem->prev;
272 } while (eitem->prev->next);
273 /* last error was not found */
274 assert(0);
Michal Vaskoed94a292019-11-06 15:43:41 +0100275 } else if ((ly_log_opts & LY_LOSTORE_LAST) == LY_LOSTORE_LAST) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200276 /* overwrite last message */
277 free(eitem->msg);
278 free(eitem->path);
279 free(eitem->apptag);
280 } else {
281 /* store new message */
282 last = eitem->prev;
283 eitem->prev = malloc(sizeof *eitem);
284 LY_CHECK_GOTO(!eitem->prev, mem_fail);
285 eitem = eitem->prev;
286 eitem->prev = last;
287 eitem->next = NULL;
288 last->next = eitem;
289 }
290
291 /* fill in the information */
292 eitem->level = level;
293 eitem->no = no;
294 eitem->vecode = vecode;
295 eitem->msg = msg;
296 eitem->path = path;
297 eitem->apptag = apptag;
298 return LY_SUCCESS;
299
300mem_fail:
301 LOGMEM(NULL);
302 free(msg);
303 free(path);
304 free(apptag);
305 return LY_EMEM;
306}
307
308static void
309log_vprintf(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, LY_VECODE vecode, char *path,
Radek Krejci0f969882020-08-21 16:56:47 +0200310 const char *format, va_list args)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200311{
312 char *msg = NULL;
Radek Krejci857189e2020-09-01 13:26:36 +0200313 ly_bool free_strs;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200314
Radek Krejci52b6d512020-10-12 12:33:17 +0200315 if (level > ly_ll) {
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200316 /* do not print or store the message */
317 free(path);
318 return;
319 }
320
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200321 /* store the error/warning (if we need to store errors internally, it does not matter what are the user log options) */
Michal Vaskoed94a292019-11-06 15:43:41 +0100322 if ((level < LY_LLVRB) && ctx && (ly_log_opts & LY_LOSTORE)) {
Michal Vasko004d3152020-06-11 19:59:22 +0200323 assert(format);
324 if (vasprintf(&msg, format, args) == -1) {
325 LOGMEM(ctx);
326 free(path);
327 return;
328 }
Radek Krejcic9e64a62020-09-18 20:08:12 +0200329 if (((no & ~LY_EPLUGIN) == LY_EVALID) && (vecode == LYVE_SUCCESS)) {
330 /* assume we are inheriting the error, so inherit vecode as well */
331 vecode = ly_vecode(ctx);
332 }
Michal Vasko004d3152020-06-11 19:59:22 +0200333 if (log_store(ctx, level, no, vecode, msg, path, NULL)) {
334 return;
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200335 }
336 free_strs = 0;
337 } else {
338 if (vasprintf(&msg, format, args) == -1) {
339 LOGMEM(ctx);
340 free(path);
341 return;
342 }
343 free_strs = 1;
344 }
345
346 /* if we are only storing errors internally, never print the message (yet) */
Michal Vaskoed94a292019-11-06 15:43:41 +0100347 if (ly_log_opts & LY_LOLOG) {
Michal Vaskod8085612020-08-21 12:55:23 +0200348 if (log_clb) {
349 log_clb(level, msg, path);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200350 } else {
351 fprintf(stderr, "libyang[%d]: %s%s", level, msg, path ? " " : "\n");
352 if (path) {
353 fprintf(stderr, "(path: %s)\n", path);
354 }
355 }
356 }
357
358 if (free_strs) {
359 free(path);
360 free(msg);
361 }
362}
363
Radek Krejci4ab61562018-09-05 15:00:37 +0200364#ifndef NDEBUG
365
366void
Radek Krejci1deb5be2020-08-26 16:43:36 +0200367ly_log_dbg(uint32_t group, const char *format, ...)
Radek Krejci4ab61562018-09-05 15:00:37 +0200368{
369 char *dbg_format;
370 const char *str_group;
371 va_list ap;
372
Radek Krejci68433c92020-10-12 17:03:55 +0200373 if (!(ly_ldbg_groups & group)) {
Radek Krejci4ab61562018-09-05 15:00:37 +0200374 return;
375 }
376
377 switch (group) {
378 case LY_LDGDICT:
379 str_group = "DICT";
380 break;
Radek Krejci4ab61562018-09-05 15:00:37 +0200381 case LY_LDGXPATH:
382 str_group = "XPATH";
383 break;
Radek Krejci4ab61562018-09-05 15:00:37 +0200384 default:
385 LOGINT(NULL);
386 return;
387 }
388
389 if (asprintf(&dbg_format, "%s: %s", str_group, format) == -1) {
390 LOGMEM(NULL);
391 return;
392 }
393
394 va_start(ap, format);
395 log_vprintf(NULL, LY_LLDBG, 0, 0, NULL, dbg_format, ap);
396 va_end(ap);
397}
398
399#endif
400
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200401void
402ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...)
403{
404 va_list ap;
405
406 va_start(ap, format);
407 log_vprintf(ctx, level, no, 0, NULL, format, ap);
408 va_end(ap);
409}
410
Radek Krejci94aa9942018-09-07 17:12:17 +0200411static LY_ERR
Radek Krejcic04f0a22018-09-21 15:49:45 +0200412ly_vlog_build_path(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, char **path)
Radek Krejci94aa9942018-09-07 17:12:17 +0200413{
Radek Krejcic04f0a22018-09-21 15:49:45 +0200414 int rc;
Radek Krejci94aa9942018-09-07 17:12:17 +0200415
Radek Krejcic04f0a22018-09-21 15:49:45 +0200416 switch (elem_type) {
417 case LY_VLOG_STR:
Michal Vaskof6e51882019-12-16 09:59:45 +0100418 *path = strdup(elem);
Radek Krejcic04f0a22018-09-21 15:49:45 +0200419 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
420 break;
421 case LY_VLOG_LINE:
Michal Vasko22df3f02020-08-24 13:29:22 +0200422 rc = asprintf(path, "Line number %" PRIu64 ".", *((uint64_t *)elem));
Radek Krejcic04f0a22018-09-21 15:49:45 +0200423 LY_CHECK_ERR_RET(rc == -1, LOGMEM(ctx), LY_EMEM);
424 break;
Michal Vaskof6e51882019-12-16 09:59:45 +0100425 case LY_VLOG_LYSC:
426 *path = lysc_path(elem, LYSC_PATH_LOG, NULL, 0);
427 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
428 break;
Michal Vasko9b368d32020-02-14 13:53:31 +0100429 case LY_VLOG_LYD:
430 *path = lyd_path(elem, LYD_PATH_LOG, NULL, 0);
431 LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
432 break;
Radek Krejcic04f0a22018-09-21 15:49:45 +0200433 default:
434 /* shouldn't be here */
435 LOGINT_RET(ctx);
Radek Krejci94aa9942018-09-07 17:12:17 +0200436 }
437
Radek Krejci94aa9942018-09-07 17:12:17 +0200438 return LY_SUCCESS;
439}
440
441void
442ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
443{
444 va_list ap;
Michal Vasko22df3f02020-08-24 13:29:22 +0200445 char *path = NULL;
Radek Krejci94aa9942018-09-07 17:12:17 +0200446 const struct ly_err_item *first;
447
448 if (path_flag && (elem_type != LY_VLOG_NONE)) {
449 if (elem_type == LY_VLOG_PREV) {
450 /* use previous path */
451 first = ly_err_first(ctx);
452 if (first && first->prev->path) {
453 path = strdup(first->prev->path);
454 }
455 } else {
456 /* print path */
457 if (!elem) {
458 /* top-level */
459 path = strdup("/");
460 } else {
Radek Krejcic04f0a22018-09-21 15:49:45 +0200461 ly_vlog_build_path(ctx, elem_type, elem, &path);
Radek Krejci94aa9942018-09-07 17:12:17 +0200462 }
463 }
464 }
465
466 va_start(ap, format);
467 log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
468 /* path is spent and should not be freed! */
469 va_end(ap);
470}
471
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200472API void
Radek Krejci0935f412019-08-20 16:15:18 +0200473lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
474{
475 va_list ap;
476 char *plugin_msg;
477 int ret;
478
Radek Krejci52b6d512020-10-12 12:33:17 +0200479 if (ly_ll < level) {
Radek Krejci0935f412019-08-20 16:15:18 +0200480 return;
481 }
482 ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
483 if (ret == -1) {
Radek Krejci28681fa2019-09-06 13:08:45 +0200484 LOGMEM(ext->module->ctx);
Radek Krejci0935f412019-08-20 16:15:18 +0200485 return;
486 }
487
488 va_start(ap, format);
Radek Krejcia4614e62020-05-15 14:19:28 +0200489 log_vprintf(ext->module->ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0) | err_no, LYVE_OTHER, path ? strdup(path) : NULL, plugin_msg, ap);
Radek Krejci0935f412019-08-20 16:15:18 +0200490 va_end(ap);
491
492 free(plugin_msg);
493}
494
Michal Vasko177d0ed2020-11-23 16:43:03 +0100495/**
496 * @brief Exact same functionality as ::ly_err_print() but has variable arguments so va_start() can
497 * be used and an empty va_list created.
498 */
499static void
500_ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem, ...)
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200501{
Michal Vasko177d0ed2020-11-23 16:43:03 +0100502 va_list ap;
503 char *path_dup = NULL;
504
505 LY_CHECK_ARG_RET(ctx, eitem, );
506
507 if (eitem->path) {
508 /* duplicate path because it will be freed */
509 path_dup = strdup(eitem->path);
510 LY_CHECK_ERR_RET(!path_dup, LOGMEM(ctx), );
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200511 }
Michal Vasko177d0ed2020-11-23 16:43:03 +0100512
513 va_start(ap, eitem);
514 log_vprintf(ctx, eitem->level, eitem->no, eitem->vecode, eitem->path, eitem->msg, ap);
515 va_end(ap);
516
517 if (path_dup) {
518 eitem->path = path_dup;
519 }
520}
521
522API void
523ly_err_print(const struct ly_ctx *ctx, struct ly_err_item *eitem)
524{
525 _ly_err_print(ctx, eitem);
Radek Krejci5aeea3a2018-09-05 13:29:36 +0200526}