blob: e6811047197cb0e3bbeccb8160077235ee3279a0 [file] [log] [blame]
Radek Krejcid3ca0632019-04-16 16:54:54 +02001/**
2 * @file printer.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief Wrapper for all libyang printers.
5 *
6 * Copyright (c) 2015 - 2019 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
15#include "common.h"
16
17#include <errno.h>
18#include <stdarg.h>
19#include <string.h>
20
21#include "printer_internal.h"
22
23/**
24 * @brief informational structure shared by printers
25 */
26struct ext_substmt_info_s ext_substmt_info[] = {
27 {NULL, NULL, 0}, /**< LYEXT_SUBSTMT_SELF */
28 {"argument", "name", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_ARGUMENT */
29 {"base", "name", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_BASE */
30 {"belongs-to", "module", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_BELONGSTO */
31 {"contact", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_CONTACT */
32 {"default", "value", 0}, /**< LYEXT_SUBSTMT_DEFAULT */
33 {"description", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_DESCRIPTION */
34 {"error-app-tag", "value", 0}, /**< LYEXT_SUBSTMT_ERRTAG */
35 {"error-message", "value", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_ERRMSG */
36 {"key", "value", 0}, /**< LYEXT_SUBSTMT_KEY */
37 {"namespace", "uri", 0}, /**< LYEXT_SUBSTMT_NAMESPACE */
38 {"organization", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_ORGANIZATION */
39 {"path", "value", 0}, /**< LYEXT_SUBSTMT_PATH */
40 {"prefix", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_PREFIX */
41 {"presence", "value", 0}, /**< LYEXT_SUBSTMT_PRESENCE */
42 {"reference", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_REFERENCE */
43 {"revision-date", "date", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REVISIONDATE */
44 {"units", "name", 0}, /**< LYEXT_SUBSTMT_UNITS */
45 {"value", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_VALUE */
46 {"yang-version", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_VERSION */
47 {"modifier", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MODIFIER */
48 {"require-instance", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REQINST */
49 {"yin-element", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_YINELEM */
50 {"config", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_CONFIG */
51 {"mandatory", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MANDATORY */
52 {"ordered-by", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_ORDEREDBY */
53 {"status", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_STATUS */
54 {"fraction-digits", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_DIGITS */
55 {"max-elements", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MAX */
56 {"min-elements", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MIN */
57 {"position", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_POSITION */
58 {"unique", "tag", 0}, /**< LYEXT_SUBSTMT_UNIQUE */
59};
60
61LY_ERR
62ly_print(struct lyout *out, const char *format, ...)
63{
64 int count = 0;
65 char *msg = NULL, *aux;
66 va_list ap;
67#ifndef HAVE_VDPRINTF
Radek Krejci4a0ed4a2019-04-18 15:08:34 +020068 int fd;
Radek Krejcid3ca0632019-04-16 16:54:54 +020069 FILE *stream;
70#endif
71
Radek Krejci56cc0872019-04-30 09:22:27 +020072 LYOUT_CHECK(out, out->status);
73
Radek Krejcid3ca0632019-04-16 16:54:54 +020074 va_start(ap, format);
75
76 switch (out->type) {
77 case LYOUT_FD:
78#ifdef HAVE_VDPRINTF
79 count = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +020080 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +020081#else
82 /* Without vdfprintf(), change the printing method to printing to a FILE stream.
83 * To preserve the original file descriptor, duplicate it and use it to open file stream.
84 * Due to a standalone LYOUT_FDSTREAM, ly*_print_fd() functions are supposed to detect the
85 * change and close the stream on their exit. */
86 fd = dup(out->method.fd);
87 if (fd < 0) {
88 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).",
89 out->method.fd, strerror(errno));
90 va_end(ap);
Radek Krejci56cc0872019-04-30 09:22:27 +020091 out->status = LY_ESYS;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +020092 return LY_ESYS;
93 }
94 stream = fdopen(fd, "a");
95 if (!stream) {
96 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).",
97 out->method.fd, strerror(errno));
98 va_end(ap);
Radek Krejci56cc0872019-04-30 09:22:27 +020099 out->status = LY_ESYS;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200100 return LY_ESYS;
101 }
102 out->method.f = stream;
103 out->type = LYOUT_FDSTREAM;
104#endif
105 /* fall through */
106 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200107 case LYOUT_STREAM:
108 count = vfprintf(out->method.f, format, ap);
109 break;
110 case LYOUT_MEMORY:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200111 if ((count = vasprintf(&msg, format, ap)) < 0) {
112 break;
113 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200114 if (out->method.mem.len + count + 1 > out->method.mem.size) {
115 aux = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
116 if (!aux) {
117 out->method.mem.buf = NULL;
118 out->method.mem.len = 0;
119 out->method.mem.size = 0;
120 LOGMEM(NULL);
121 va_end(ap);
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200122 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200123 }
124 out->method.mem.buf = aux;
125 out->method.mem.size = out->method.mem.len + count + 1;
126 }
127 memcpy(&out->method.mem.buf[out->method.mem.len], msg, count);
128 out->method.mem.len += count;
129 out->method.mem.buf[out->method.mem.len] = '\0';
130 free(msg);
131 break;
132 case LYOUT_CALLBACK:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200133 if ((count = vasprintf(&msg, format, ap)) < 0) {
134 break;
135 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200136 count = out->method.clb.f(out->method.clb.arg, msg, count);
137 free(msg);
138 break;
139 }
140
141 va_end(ap);
142
143 if (count < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200144 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200145 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200146 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200147 } else {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200148 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200149 return LY_SUCCESS;
150 }
151}
152
153void
154ly_print_flush(struct lyout *out)
155{
156 switch (out->type) {
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200157 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200158 case LYOUT_STREAM:
159 fflush(out->method.f);
160 break;
161 case LYOUT_FD:
162 case LYOUT_MEMORY:
163 case LYOUT_CALLBACK:
164 /* nothing to do */
165 break;
166 }
167}
168
169LY_ERR
170ly_write(struct lyout *out, const char *buf, size_t count)
171{
172 int written = 0;
173
Radek Krejci56cc0872019-04-30 09:22:27 +0200174 LYOUT_CHECK(out, out->status);
175
Radek Krejcid3ca0632019-04-16 16:54:54 +0200176 if (out->hole_count) {
177 /* we are buffering data after a hole */
178 if (out->buf_len + count > out->buf_size) {
179 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
180 if (!out->buffered) {
181 out->buf_len = 0;
182 out->buf_size = 0;
183 LOGMEM_RET(NULL);
184 }
185 out->buf_size = out->buf_len + count;
186 }
187
188 memcpy(&out->buffered[out->buf_len], buf, count);
189 out->buf_len += count;
190 return LY_SUCCESS;
191 }
192
Radek Krejci897ad2e2019-04-29 16:43:07 +0200193repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200194 switch (out->type) {
195 case LYOUT_MEMORY:
196 if (out->method.mem.len + count + 1 > out->method.mem.size) {
197 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
198 if (!out->method.mem.buf) {
199 out->method.mem.len = 0;
200 out->method.mem.size = 0;
201 LOGMEM_RET(NULL);
202 }
203 out->method.mem.size = out->method.mem.len + count + 1;
204 }
205 memcpy(&out->method.mem.buf[out->method.mem.len], buf, count);
206 out->method.mem.len += count;
207 out->method.mem.buf[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200208
209 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200210 return LY_SUCCESS;
211 case LYOUT_FD:
212 written = write(out->method.fd, buf, count);
213 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200214 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200215 case LYOUT_STREAM:
216 written = fwrite(buf, sizeof *buf, count, out->method.f);
217 break;
218 case LYOUT_CALLBACK:
219 written = out->method.clb.f(out->method.clb.arg, buf, count);
220 break;
221 }
222
223 if (written < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200224 if (errno == EAGAIN || errno == EWOULDBLOCK) {
225 goto repeat;
226 }
227 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200228 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200229 return LY_ESYS;
230 } else if ((size_t)written != count) {
231 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__, count - (size_t)written, count);
Radek Krejci56cc0872019-04-30 09:22:27 +0200232 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200233 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200234 } else {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200235 out->printed += written;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200236 return LY_SUCCESS;
237 }
238}
239
240LY_ERR
241ly_write_skip(struct lyout *out, size_t count, size_t *position)
242{
Radek Krejci56cc0872019-04-30 09:22:27 +0200243 LYOUT_CHECK(out, out->status);
244
Radek Krejcid3ca0632019-04-16 16:54:54 +0200245 switch (out->type) {
246 case LYOUT_MEMORY:
247 if (out->method.mem.len + count > out->method.mem.size) {
248 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count);
249 if (!out->method.mem.buf) {
250 out->method.mem.len = 0;
251 out->method.mem.size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200252 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200253 LOGMEM_RET(NULL);
254 }
255 out->method.mem.size = out->method.mem.len + count;
256 }
257
258 /* save the current position */
259 *position = out->method.mem.len;
260
261 /* skip the memory */
262 out->method.mem.len += count;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200263
264 /* update printed bytes counter despite we actually printed just a hole */
265 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200266 break;
267 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200268 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200269 case LYOUT_STREAM:
270 case LYOUT_CALLBACK:
271 /* buffer the hole */
272 if (out->buf_len + count > out->buf_size) {
273 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
274 if (!out->buffered) {
275 out->buf_len = 0;
276 out->buf_size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200277 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200278 LOGMEM_RET(NULL);
279 }
280 out->buf_size = out->buf_len + count;
281 }
282
283 /* save the current position */
284 *position = out->buf_len;
285
286 /* skip the memory */
287 out->buf_len += count;
288
289 /* increase hole counter */
290 ++out->hole_count;
291 }
292
293 return LY_SUCCESS;
294}
295
296LY_ERR
297ly_write_skipped(struct lyout *out, size_t position, const char *buf, size_t count)
298{
299 LY_ERR ret = LY_SUCCESS;
300
Radek Krejci56cc0872019-04-30 09:22:27 +0200301 LYOUT_CHECK(out, out->status);
302
Radek Krejcid3ca0632019-04-16 16:54:54 +0200303 switch (out->type) {
304 case LYOUT_MEMORY:
305 /* write */
306 memcpy(&out->method.mem.buf[position], buf, count);
307 break;
308 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200309 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200310 case LYOUT_STREAM:
311 case LYOUT_CALLBACK:
312 if (out->buf_len < position + count) {
Radek Krejci56cc0872019-04-30 09:22:27 +0200313 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200314 LOGMEM_RET(NULL);
315 }
316
317 /* write into the hole */
318 memcpy(&out->buffered[position], buf, count);
319
320 /* decrease hole counter */
321 --out->hole_count;
322
323 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200324 /* all holes filled, we can write the buffer,
325 * printed bytes counter is updated by ly_write() */
Radek Krejcid3ca0632019-04-16 16:54:54 +0200326 ret = ly_write(out, out->buffered, out->buf_len);
327 out->buf_len = 0;
328 }
329 break;
330 }
331
332 return ret;
333}
334
335static LY_ERR
336lys_print_(struct lyout *out, const struct lys_module *module, LYS_OUTFORMAT format, int UNUSED(line_length), int UNUSED(options))
337{
338 LY_ERR ret;
339
340 switch (format) {
341 case LYS_OUT_YANG:
342 ret = yang_print_parsed(out, module);
343 break;
344 case LYS_OUT_YANG_COMPILED:
345 ret = yang_print_compiled(out, module);
346 break;
347 /* TODO not yet implemented
348 case LYS_OUT_YIN:
349 lys_disable_deviations((struct lys_module *)module);
350 ret = yin_print_model(out, module);
351 lys_enable_deviations((struct lys_module *)module);
352 break;
353 case LYS_OUT_TREE:
354 ret = tree_print_model(out, module, target_node, line_length, options);
355 break;
356 case LYS_OUT_INFO:
357 ret = info_print_model(out, module, target_node);
358 break;
359 case LYS_OUT_JSON:
360 ret = jsons_print_model(out, module, target_node);
361 break;
362 */
363 default:
364 LOGERR(module->ctx, LY_EINVAL, "Unknown output format.");
365 ret = LY_EINVAL;
366 break;
367 }
368
369 return ret;
370}
371
Radek Krejci897ad2e2019-04-29 16:43:07 +0200372API ssize_t
Radek Krejcid3ca0632019-04-16 16:54:54 +0200373lys_print_file(FILE *f, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
374{
375 struct lyout out;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200376 LY_ERR ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200377
378 LY_CHECK_ARG_RET(NULL, f, module, LY_EINVAL);
379
380 memset(&out, 0, sizeof out);
Radek Krejci897ad2e2019-04-29 16:43:07 +0200381 out.ctx = module->ctx;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200382 out.type = LYOUT_STREAM;
383 out.method.f = f;
384
Radek Krejci897ad2e2019-04-29 16:43:07 +0200385 ret = lys_print_(&out, module, format, line_length, options);
386 if (ret) {
387 /* error */
388 return (-1) * ret;
389 } else {
390 /* success */
391 return (ssize_t)out.printed;
392 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200393}
394
Radek Krejci897ad2e2019-04-29 16:43:07 +0200395API ssize_t
Radek Krejcid3ca0632019-04-16 16:54:54 +0200396lys_print_path(const char *path, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
397{
398 FILE *f;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200399 ssize_t ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200400
401 LY_CHECK_ARG_RET(NULL, path, module, LY_EINVAL);
402
403 f = fopen(path, "w");
404 if (!f) {
405 LOGERR(module->ctx, LY_ESYS, "Failed to open file \"%s\" (%s).", path, strerror(errno));
406 return LY_ESYS;
407 }
408
409 ret = lys_print_file(f, module, format, line_length, options);
410 fclose(f);
411 return ret;
412}
413
Radek Krejci897ad2e2019-04-29 16:43:07 +0200414API ssize_t
Radek Krejcid3ca0632019-04-16 16:54:54 +0200415lys_print_fd(int fd, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
416{
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200417 LY_ERR ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200418 struct lyout out;
419
420 LY_CHECK_ARG_RET(NULL, fd >= 0, module, LY_EINVAL);
421
422 memset(&out, 0, sizeof out);
Radek Krejci897ad2e2019-04-29 16:43:07 +0200423 out.ctx = module->ctx;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200424 out.type = LYOUT_FD;
425 out.method.fd = fd;
426
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200427 ret = lys_print_(&out, module, format, line_length, options);
428
429 if (out.type == LYOUT_FDSTREAM) {
430 /* close temporary stream based on the given file descriptor */
431 fclose(out.method.f);
432 /* move the original file descriptor to the end of the output file */
433 lseek(fd, 0, SEEK_END);
434 }
435
Radek Krejci897ad2e2019-04-29 16:43:07 +0200436 if (ret) {
437 /* error */
438 return (-1) * ret;
439 } else {
440 /* success */
441 return (ssize_t)out.printed;
442 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200443}
444
Radek Krejci897ad2e2019-04-29 16:43:07 +0200445API ssize_t
Radek Krejcid3ca0632019-04-16 16:54:54 +0200446lys_print_mem(char **strp, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
447{
448 struct lyout out;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200449 LY_ERR ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200450
451 LY_CHECK_ARG_RET(NULL, strp, module, LY_EINVAL);
452
453 memset(&out, 0, sizeof out);
Radek Krejci897ad2e2019-04-29 16:43:07 +0200454 out.ctx = module->ctx;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200455 out.type = LYOUT_MEMORY;
456
Radek Krejci897ad2e2019-04-29 16:43:07 +0200457 ret = lys_print_(&out, module, format, line_length, options);
458 if (ret) {
459 /* error */
460 *strp = NULL;
461 return (-1) * ret;
462 } else {
463 /* success */
464 *strp = out.method.mem.buf;
465 return (ssize_t)out.printed;
466 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200467}
468
Radek Krejci897ad2e2019-04-29 16:43:07 +0200469API ssize_t
Radek Krejcid3ca0632019-04-16 16:54:54 +0200470lys_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lys_module *module,
471 LYS_OUTFORMAT format, int line_length, int options)
472{
Radek Krejci897ad2e2019-04-29 16:43:07 +0200473 LY_ERR ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200474 struct lyout out;
475
476 LY_CHECK_ARG_RET(NULL, writeclb, module, LY_EINVAL);
477
478 memset(&out, 0, sizeof out);
Radek Krejci897ad2e2019-04-29 16:43:07 +0200479 out.ctx = module->ctx;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200480 out.type = LYOUT_CALLBACK;
481 out.method.clb.f = writeclb;
482 out.method.clb.arg = arg;
483
Radek Krejci897ad2e2019-04-29 16:43:07 +0200484 ret = lys_print_(&out, module, format, line_length, options);
485 if (ret) {
486 /* error */
487 return (-1) * ret;
488 } else {
489 /* success */
490 return (ssize_t)out.printed;
491 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200492}