blob: 0c3499567d389fc0f2891e1af3e91abed3ab8192 [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
72 va_start(ap, format);
73
74 switch (out->type) {
75 case LYOUT_FD:
76#ifdef HAVE_VDPRINTF
77 count = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +020078 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +020079#else
80 /* Without vdfprintf(), change the printing method to printing to a FILE stream.
81 * To preserve the original file descriptor, duplicate it and use it to open file stream.
82 * Due to a standalone LYOUT_FDSTREAM, ly*_print_fd() functions are supposed to detect the
83 * change and close the stream on their exit. */
84 fd = dup(out->method.fd);
85 if (fd < 0) {
86 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).",
87 out->method.fd, strerror(errno));
88 va_end(ap);
89 return LY_ESYS;
90 }
91 stream = fdopen(fd, "a");
92 if (!stream) {
93 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).",
94 out->method.fd, strerror(errno));
95 va_end(ap);
96 return LY_ESYS;
97 }
98 out->method.f = stream;
99 out->type = LYOUT_FDSTREAM;
100#endif
101 /* fall through */
102 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200103 case LYOUT_STREAM:
104 count = vfprintf(out->method.f, format, ap);
105 break;
106 case LYOUT_MEMORY:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200107 if ((count = vasprintf(&msg, format, ap)) < 0) {
108 break;
109 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200110 if (out->method.mem.len + count + 1 > out->method.mem.size) {
111 aux = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
112 if (!aux) {
113 out->method.mem.buf = NULL;
114 out->method.mem.len = 0;
115 out->method.mem.size = 0;
116 LOGMEM(NULL);
117 va_end(ap);
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200118 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200119 }
120 out->method.mem.buf = aux;
121 out->method.mem.size = out->method.mem.len + count + 1;
122 }
123 memcpy(&out->method.mem.buf[out->method.mem.len], msg, count);
124 out->method.mem.len += count;
125 out->method.mem.buf[out->method.mem.len] = '\0';
126 free(msg);
127 break;
128 case LYOUT_CALLBACK:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200129 if ((count = vasprintf(&msg, format, ap)) < 0) {
130 break;
131 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200132 count = out->method.clb.f(out->method.clb.arg, msg, count);
133 free(msg);
134 break;
135 }
136
137 va_end(ap);
138
139 if (count < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200140 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
141 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200142 } else {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200143 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200144 return LY_SUCCESS;
145 }
146}
147
148void
149ly_print_flush(struct lyout *out)
150{
151 switch (out->type) {
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200152 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200153 case LYOUT_STREAM:
154 fflush(out->method.f);
155 break;
156 case LYOUT_FD:
157 case LYOUT_MEMORY:
158 case LYOUT_CALLBACK:
159 /* nothing to do */
160 break;
161 }
162}
163
164LY_ERR
165ly_write(struct lyout *out, const char *buf, size_t count)
166{
167 int written = 0;
168
169 if (out->hole_count) {
170 /* we are buffering data after a hole */
171 if (out->buf_len + count > out->buf_size) {
172 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
173 if (!out->buffered) {
174 out->buf_len = 0;
175 out->buf_size = 0;
176 LOGMEM_RET(NULL);
177 }
178 out->buf_size = out->buf_len + count;
179 }
180
181 memcpy(&out->buffered[out->buf_len], buf, count);
182 out->buf_len += count;
183 return LY_SUCCESS;
184 }
185
Radek Krejci897ad2e2019-04-29 16:43:07 +0200186repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200187 switch (out->type) {
188 case LYOUT_MEMORY:
189 if (out->method.mem.len + count + 1 > out->method.mem.size) {
190 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
191 if (!out->method.mem.buf) {
192 out->method.mem.len = 0;
193 out->method.mem.size = 0;
194 LOGMEM_RET(NULL);
195 }
196 out->method.mem.size = out->method.mem.len + count + 1;
197 }
198 memcpy(&out->method.mem.buf[out->method.mem.len], buf, count);
199 out->method.mem.len += count;
200 out->method.mem.buf[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200201
202 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200203 return LY_SUCCESS;
204 case LYOUT_FD:
205 written = write(out->method.fd, buf, count);
206 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200207 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200208 case LYOUT_STREAM:
209 written = fwrite(buf, sizeof *buf, count, out->method.f);
210 break;
211 case LYOUT_CALLBACK:
212 written = out->method.clb.f(out->method.clb.arg, buf, count);
213 break;
214 }
215
216 if (written < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200217 if (errno == EAGAIN || errno == EWOULDBLOCK) {
218 goto repeat;
219 }
220 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
221 return LY_ESYS;
222 } else if ((size_t)written != count) {
223 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__, count - (size_t)written, count);
224 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200225 } else {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200226 out->printed += written;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200227 return LY_SUCCESS;
228 }
229}
230
231LY_ERR
232ly_write_skip(struct lyout *out, size_t count, size_t *position)
233{
234 switch (out->type) {
235 case LYOUT_MEMORY:
236 if (out->method.mem.len + count > out->method.mem.size) {
237 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count);
238 if (!out->method.mem.buf) {
239 out->method.mem.len = 0;
240 out->method.mem.size = 0;
241 LOGMEM_RET(NULL);
242 }
243 out->method.mem.size = out->method.mem.len + count;
244 }
245
246 /* save the current position */
247 *position = out->method.mem.len;
248
249 /* skip the memory */
250 out->method.mem.len += count;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200251
252 /* update printed bytes counter despite we actually printed just a hole */
253 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200254 break;
255 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200256 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200257 case LYOUT_STREAM:
258 case LYOUT_CALLBACK:
259 /* buffer the hole */
260 if (out->buf_len + count > out->buf_size) {
261 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
262 if (!out->buffered) {
263 out->buf_len = 0;
264 out->buf_size = 0;
265 LOGMEM_RET(NULL);
266 }
267 out->buf_size = out->buf_len + count;
268 }
269
270 /* save the current position */
271 *position = out->buf_len;
272
273 /* skip the memory */
274 out->buf_len += count;
275
276 /* increase hole counter */
277 ++out->hole_count;
278 }
279
280 return LY_SUCCESS;
281}
282
283LY_ERR
284ly_write_skipped(struct lyout *out, size_t position, const char *buf, size_t count)
285{
286 LY_ERR ret = LY_SUCCESS;
287
288 switch (out->type) {
289 case LYOUT_MEMORY:
290 /* write */
291 memcpy(&out->method.mem.buf[position], buf, count);
292 break;
293 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200294 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200295 case LYOUT_STREAM:
296 case LYOUT_CALLBACK:
297 if (out->buf_len < position + count) {
298 LOGMEM_RET(NULL);
299 }
300
301 /* write into the hole */
302 memcpy(&out->buffered[position], buf, count);
303
304 /* decrease hole counter */
305 --out->hole_count;
306
307 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200308 /* all holes filled, we can write the buffer,
309 * printed bytes counter is updated by ly_write() */
Radek Krejcid3ca0632019-04-16 16:54:54 +0200310 ret = ly_write(out, out->buffered, out->buf_len);
311 out->buf_len = 0;
312 }
313 break;
314 }
315
316 return ret;
317}
318
319static LY_ERR
320lys_print_(struct lyout *out, const struct lys_module *module, LYS_OUTFORMAT format, int UNUSED(line_length), int UNUSED(options))
321{
322 LY_ERR ret;
323
324 switch (format) {
325 case LYS_OUT_YANG:
326 ret = yang_print_parsed(out, module);
327 break;
328 case LYS_OUT_YANG_COMPILED:
329 ret = yang_print_compiled(out, module);
330 break;
331 /* TODO not yet implemented
332 case LYS_OUT_YIN:
333 lys_disable_deviations((struct lys_module *)module);
334 ret = yin_print_model(out, module);
335 lys_enable_deviations((struct lys_module *)module);
336 break;
337 case LYS_OUT_TREE:
338 ret = tree_print_model(out, module, target_node, line_length, options);
339 break;
340 case LYS_OUT_INFO:
341 ret = info_print_model(out, module, target_node);
342 break;
343 case LYS_OUT_JSON:
344 ret = jsons_print_model(out, module, target_node);
345 break;
346 */
347 default:
348 LOGERR(module->ctx, LY_EINVAL, "Unknown output format.");
349 ret = LY_EINVAL;
350 break;
351 }
352
353 return ret;
354}
355
Radek Krejci897ad2e2019-04-29 16:43:07 +0200356API ssize_t
Radek Krejcid3ca0632019-04-16 16:54:54 +0200357lys_print_file(FILE *f, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
358{
359 struct lyout out;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200360 LY_ERR ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200361
362 LY_CHECK_ARG_RET(NULL, f, module, LY_EINVAL);
363
364 memset(&out, 0, sizeof out);
Radek Krejci897ad2e2019-04-29 16:43:07 +0200365 out.ctx = module->ctx;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200366 out.type = LYOUT_STREAM;
367 out.method.f = f;
368
Radek Krejci897ad2e2019-04-29 16:43:07 +0200369 ret = lys_print_(&out, module, format, line_length, options);
370 if (ret) {
371 /* error */
372 return (-1) * ret;
373 } else {
374 /* success */
375 return (ssize_t)out.printed;
376 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200377}
378
Radek Krejci897ad2e2019-04-29 16:43:07 +0200379API ssize_t
Radek Krejcid3ca0632019-04-16 16:54:54 +0200380lys_print_path(const char *path, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
381{
382 FILE *f;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200383 ssize_t ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200384
385 LY_CHECK_ARG_RET(NULL, path, module, LY_EINVAL);
386
387 f = fopen(path, "w");
388 if (!f) {
389 LOGERR(module->ctx, LY_ESYS, "Failed to open file \"%s\" (%s).", path, strerror(errno));
390 return LY_ESYS;
391 }
392
393 ret = lys_print_file(f, module, format, line_length, options);
394 fclose(f);
395 return ret;
396}
397
Radek Krejci897ad2e2019-04-29 16:43:07 +0200398API ssize_t
Radek Krejcid3ca0632019-04-16 16:54:54 +0200399lys_print_fd(int fd, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
400{
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200401 LY_ERR ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200402 struct lyout out;
403
404 LY_CHECK_ARG_RET(NULL, fd >= 0, module, LY_EINVAL);
405
406 memset(&out, 0, sizeof out);
Radek Krejci897ad2e2019-04-29 16:43:07 +0200407 out.ctx = module->ctx;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200408 out.type = LYOUT_FD;
409 out.method.fd = fd;
410
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200411 ret = lys_print_(&out, module, format, line_length, options);
412
413 if (out.type == LYOUT_FDSTREAM) {
414 /* close temporary stream based on the given file descriptor */
415 fclose(out.method.f);
416 /* move the original file descriptor to the end of the output file */
417 lseek(fd, 0, SEEK_END);
418 }
419
Radek Krejci897ad2e2019-04-29 16:43:07 +0200420 if (ret) {
421 /* error */
422 return (-1) * ret;
423 } else {
424 /* success */
425 return (ssize_t)out.printed;
426 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200427}
428
Radek Krejci897ad2e2019-04-29 16:43:07 +0200429API ssize_t
Radek Krejcid3ca0632019-04-16 16:54:54 +0200430lys_print_mem(char **strp, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
431{
432 struct lyout out;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200433 LY_ERR ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200434
435 LY_CHECK_ARG_RET(NULL, strp, module, LY_EINVAL);
436
437 memset(&out, 0, sizeof out);
Radek Krejci897ad2e2019-04-29 16:43:07 +0200438 out.ctx = module->ctx;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200439 out.type = LYOUT_MEMORY;
440
Radek Krejci897ad2e2019-04-29 16:43:07 +0200441 ret = lys_print_(&out, module, format, line_length, options);
442 if (ret) {
443 /* error */
444 *strp = NULL;
445 return (-1) * ret;
446 } else {
447 /* success */
448 *strp = out.method.mem.buf;
449 return (ssize_t)out.printed;
450 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200451}
452
Radek Krejci897ad2e2019-04-29 16:43:07 +0200453API ssize_t
Radek Krejcid3ca0632019-04-16 16:54:54 +0200454lys_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lys_module *module,
455 LYS_OUTFORMAT format, int line_length, int options)
456{
Radek Krejci897ad2e2019-04-29 16:43:07 +0200457 LY_ERR ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200458 struct lyout out;
459
460 LY_CHECK_ARG_RET(NULL, writeclb, module, LY_EINVAL);
461
462 memset(&out, 0, sizeof out);
Radek Krejci897ad2e2019-04-29 16:43:07 +0200463 out.ctx = module->ctx;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200464 out.type = LYOUT_CALLBACK;
465 out.method.clb.f = writeclb;
466 out.method.clb.arg = arg;
467
Radek Krejci897ad2e2019-04-29 16:43:07 +0200468 ret = lys_print_(&out, module, format, line_length, options);
469 if (ret) {
470 /* error */
471 return (-1) * ret;
472 } else {
473 /* success */
474 return (ssize_t)out.printed;
475 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200476}