blob: 6b03c01ee6b479513168cecb13a6f5203bd38f3f [file] [log] [blame]
Radek Krejcid3ca0632019-04-16 16:54:54 +02001/**
2 * @file printer.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
Radek Krejcie7b95092019-05-15 11:03:07 +02004 * @brief Generic libyang printers functions.
Radek Krejcid3ca0632019-04-16 16:54:54 +02005 *
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>
Radek Krejcie7b95092019-05-15 11:03:07 +020019#include <stdio.h>
20#include <stdlib.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020021#include <string.h>
Radek Krejcie7b95092019-05-15 11:03:07 +020022#include <unistd.h>
Michal Vasko9b368d32020-02-14 13:53:31 +010023#include <assert.h>
Radek Krejcid3ca0632019-04-16 16:54:54 +020024
Radek Krejcie7b95092019-05-15 11:03:07 +020025#include "log.h"
Radek Krejcid3ca0632019-04-16 16:54:54 +020026#include "printer_internal.h"
Michal Vasko9b368d32020-02-14 13:53:31 +010027#include "plugins_types.h"
Radek Krejcid3ca0632019-04-16 16:54:54 +020028
29/**
30 * @brief informational structure shared by printers
31 */
32struct ext_substmt_info_s ext_substmt_info[] = {
33 {NULL, NULL, 0}, /**< LYEXT_SUBSTMT_SELF */
34 {"argument", "name", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_ARGUMENT */
35 {"base", "name", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_BASE */
36 {"belongs-to", "module", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_BELONGSTO */
37 {"contact", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_CONTACT */
38 {"default", "value", 0}, /**< LYEXT_SUBSTMT_DEFAULT */
39 {"description", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_DESCRIPTION */
40 {"error-app-tag", "value", 0}, /**< LYEXT_SUBSTMT_ERRTAG */
41 {"error-message", "value", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_ERRMSG */
42 {"key", "value", 0}, /**< LYEXT_SUBSTMT_KEY */
43 {"namespace", "uri", 0}, /**< LYEXT_SUBSTMT_NAMESPACE */
44 {"organization", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_ORGANIZATION */
45 {"path", "value", 0}, /**< LYEXT_SUBSTMT_PATH */
46 {"prefix", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_PREFIX */
47 {"presence", "value", 0}, /**< LYEXT_SUBSTMT_PRESENCE */
48 {"reference", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_REFERENCE */
49 {"revision-date", "date", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REVISIONDATE */
50 {"units", "name", 0}, /**< LYEXT_SUBSTMT_UNITS */
51 {"value", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_VALUE */
52 {"yang-version", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_VERSION */
53 {"modifier", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MODIFIER */
54 {"require-instance", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REQINST */
55 {"yin-element", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_YINELEM */
56 {"config", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_CONFIG */
57 {"mandatory", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MANDATORY */
58 {"ordered-by", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_ORDEREDBY */
59 {"status", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_STATUS */
60 {"fraction-digits", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_DIGITS */
61 {"max-elements", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MAX */
62 {"min-elements", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MIN */
63 {"position", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_POSITION */
64 {"unique", "tag", 0}, /**< LYEXT_SUBSTMT_UNIQUE */
65};
66
Michal Vasko6f4cbb62020-02-28 11:15:47 +010067int
Michal Vasko9b368d32020-02-14 13:53:31 +010068ly_is_default(const struct lyd_node *node)
69{
70 const struct lysc_node_leaf *leaf;
71 const struct lysc_node_leaflist *llist;
72 const struct lyd_node_term *term;
73 size_t i;
74
75 assert(node->schema->nodetype & LYD_NODE_TERM);
76 term = (const struct lyd_node_term *)node;
77
78 if (node->schema->nodetype == LYS_LEAF) {
79 leaf = (const struct lysc_node_leaf *)node->schema;
80 if (!leaf->dflt) {
81 return 0;
82 }
83
84 /* compare with the default value */
85 if (leaf->type->plugin->compare(&term->value, leaf->dflt)) {
86 return 0;
87 }
88 } else {
89 llist = (const struct lysc_node_leaflist *)node->schema;
90 if (!llist->dflts) {
91 return 0;
92 }
93
94 LY_ARRAY_FOR(llist->dflts, i) {
95 /* compare with each possible default value */
96 if (llist->type->plugin->compare(&term->value, llist->dflts[i])) {
97 return 0;
98 }
99 }
100 }
101
102 return 1;
103}
104
105int
106ly_should_print(const struct lyd_node *node, int options)
107{
108 const struct lyd_node *next, *elem;
109
110 if (options & LYDP_WD_TRIM) {
111 /* do not print default nodes */
112 if (node->flags & LYD_DEFAULT) {
113 /* implicit default node/NP container with only default nodes */
114 return 0;
115 } else if (node->schema->nodetype & LYD_NODE_TERM) {
116 if (ly_is_default(node)) {
117 /* explicit default node */
118 return 0;
119 }
120 }
121 } else if ((node->flags & LYD_DEFAULT) && !(options & LYDP_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
122 /* LYP_WD_EXPLICIT
123 * - print only if it contains status data in its subtree */
124 LYD_TREE_DFS_BEGIN(node, next, elem) {
125 if (elem->schema->flags & LYS_CONFIG_R) {
126 return 1;
127 }
128 LYD_TREE_DFS_END(node, next, elem)
129 }
130 return 0;
131 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYDP_KEEPEMPTYCONT)) {
132 /* avoid empty default containers */
133 LYD_TREE_DFS_BEGIN(node, next, elem) {
134 if (elem->schema->nodetype != LYS_CONTAINER) {
135 return 1;
136 }
137 assert(elem->flags & LYD_DEFAULT);
138 LYD_TREE_DFS_END(node, next, elem)
139 }
140 return 0;
141 }
142
143 return 1;
144}
145
Radek Krejcid3ca0632019-04-16 16:54:54 +0200146LY_ERR
147ly_print(struct lyout *out, const char *format, ...)
148{
149 int count = 0;
150 char *msg = NULL, *aux;
151 va_list ap;
152#ifndef HAVE_VDPRINTF
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200153 int fd;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200154 FILE *stream;
155#endif
156
Radek Krejci56cc0872019-04-30 09:22:27 +0200157 LYOUT_CHECK(out, out->status);
158
Radek Krejcid3ca0632019-04-16 16:54:54 +0200159 va_start(ap, format);
160
161 switch (out->type) {
162 case LYOUT_FD:
163#ifdef HAVE_VDPRINTF
164 count = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200165 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200166#else
167 /* Without vdfprintf(), change the printing method to printing to a FILE stream.
168 * To preserve the original file descriptor, duplicate it and use it to open file stream.
169 * Due to a standalone LYOUT_FDSTREAM, ly*_print_fd() functions are supposed to detect the
170 * change and close the stream on their exit. */
171 fd = dup(out->method.fd);
172 if (fd < 0) {
173 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).",
174 out->method.fd, strerror(errno));
175 va_end(ap);
Radek Krejci56cc0872019-04-30 09:22:27 +0200176 out->status = LY_ESYS;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200177 return LY_ESYS;
178 }
179 stream = fdopen(fd, "a");
180 if (!stream) {
181 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).",
182 out->method.fd, strerror(errno));
183 va_end(ap);
Radek Krejci56cc0872019-04-30 09:22:27 +0200184 out->status = LY_ESYS;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200185 return LY_ESYS;
186 }
187 out->method.f = stream;
188 out->type = LYOUT_FDSTREAM;
189#endif
190 /* fall through */
191 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200192 case LYOUT_STREAM:
193 count = vfprintf(out->method.f, format, ap);
194 break;
195 case LYOUT_MEMORY:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200196 if ((count = vasprintf(&msg, format, ap)) < 0) {
197 break;
198 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200199 if (out->method.mem.len + count + 1 > out->method.mem.size) {
200 aux = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
201 if (!aux) {
202 out->method.mem.buf = NULL;
203 out->method.mem.len = 0;
204 out->method.mem.size = 0;
205 LOGMEM(NULL);
206 va_end(ap);
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200207 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200208 }
209 out->method.mem.buf = aux;
210 out->method.mem.size = out->method.mem.len + count + 1;
211 }
212 memcpy(&out->method.mem.buf[out->method.mem.len], msg, count);
213 out->method.mem.len += count;
214 out->method.mem.buf[out->method.mem.len] = '\0';
215 free(msg);
216 break;
217 case LYOUT_CALLBACK:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200218 if ((count = vasprintf(&msg, format, ap)) < 0) {
219 break;
220 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200221 count = out->method.clb.f(out->method.clb.arg, msg, count);
222 free(msg);
223 break;
224 }
225
226 va_end(ap);
227
228 if (count < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200229 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200230 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200231 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200232 } else {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200233 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200234 return LY_SUCCESS;
235 }
236}
237
238void
239ly_print_flush(struct lyout *out)
240{
241 switch (out->type) {
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200242 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200243 case LYOUT_STREAM:
244 fflush(out->method.f);
245 break;
246 case LYOUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200247 fsync(out->method.fd);
248 break;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200249 case LYOUT_MEMORY:
250 case LYOUT_CALLBACK:
251 /* nothing to do */
252 break;
253 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200254
255 free(out->buffered);
256 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200257}
258
259LY_ERR
Radek Krejcie7b95092019-05-15 11:03:07 +0200260ly_write(struct lyout *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200261{
262 int written = 0;
263
Radek Krejci56cc0872019-04-30 09:22:27 +0200264 LYOUT_CHECK(out, out->status);
265
Radek Krejcid3ca0632019-04-16 16:54:54 +0200266 if (out->hole_count) {
267 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200268 if (out->buf_len + len > out->buf_size) {
269 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200270 if (!out->buffered) {
271 out->buf_len = 0;
272 out->buf_size = 0;
273 LOGMEM_RET(NULL);
274 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200275 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200276 }
277
Radek Krejcie7b95092019-05-15 11:03:07 +0200278 memcpy(&out->buffered[out->buf_len], buf, len);
279 out->buf_len += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200280 return LY_SUCCESS;
281 }
282
Radek Krejci897ad2e2019-04-29 16:43:07 +0200283repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200284 switch (out->type) {
285 case LYOUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200286 if (out->method.mem.len + len + 1 > out->method.mem.size) {
287 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + len + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200288 if (!out->method.mem.buf) {
289 out->method.mem.len = 0;
290 out->method.mem.size = 0;
291 LOGMEM_RET(NULL);
292 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200293 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200294 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200295 memcpy(&out->method.mem.buf[out->method.mem.len], buf, len);
296 out->method.mem.len += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200297 out->method.mem.buf[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200298
Radek Krejcie7b95092019-05-15 11:03:07 +0200299 out->printed += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200300 return LY_SUCCESS;
301 case LYOUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200302 written = write(out->method.fd, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200303 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200304 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200305 case LYOUT_STREAM:
Radek Krejcie7b95092019-05-15 11:03:07 +0200306 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200307 break;
308 case LYOUT_CALLBACK:
Radek Krejcie7b95092019-05-15 11:03:07 +0200309 written = out->method.clb.f(out->method.clb.arg, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200310 break;
311 }
312
313 if (written < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200314 if (errno == EAGAIN || errno == EWOULDBLOCK) {
315 goto repeat;
316 }
317 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200318 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200319 return LY_ESYS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200320 } else if ((size_t)written != len) {
321 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (unable to write %u from %u data).", __func__, len - (size_t)written, len);
Radek Krejci56cc0872019-04-30 09:22:27 +0200322 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200323 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200324 } else {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200325 out->printed += written;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200326 return LY_SUCCESS;
327 }
328}
329
330LY_ERR
331ly_write_skip(struct lyout *out, size_t count, size_t *position)
332{
Radek Krejci56cc0872019-04-30 09:22:27 +0200333 LYOUT_CHECK(out, out->status);
334
Radek Krejcid3ca0632019-04-16 16:54:54 +0200335 switch (out->type) {
336 case LYOUT_MEMORY:
337 if (out->method.mem.len + count > out->method.mem.size) {
338 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count);
339 if (!out->method.mem.buf) {
340 out->method.mem.len = 0;
341 out->method.mem.size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200342 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200343 LOGMEM_RET(NULL);
344 }
345 out->method.mem.size = out->method.mem.len + count;
346 }
347
348 /* save the current position */
349 *position = out->method.mem.len;
350
351 /* skip the memory */
352 out->method.mem.len += count;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200353
354 /* update printed bytes counter despite we actually printed just a hole */
355 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200356 break;
357 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200358 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200359 case LYOUT_STREAM:
360 case LYOUT_CALLBACK:
361 /* buffer the hole */
362 if (out->buf_len + count > out->buf_size) {
363 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
364 if (!out->buffered) {
365 out->buf_len = 0;
366 out->buf_size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200367 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200368 LOGMEM_RET(NULL);
369 }
370 out->buf_size = out->buf_len + count;
371 }
372
373 /* save the current position */
374 *position = out->buf_len;
375
376 /* skip the memory */
377 out->buf_len += count;
378
379 /* increase hole counter */
380 ++out->hole_count;
381 }
382
383 return LY_SUCCESS;
384}
385
386LY_ERR
387ly_write_skipped(struct lyout *out, size_t position, const char *buf, size_t count)
388{
389 LY_ERR ret = LY_SUCCESS;
390
Radek Krejci56cc0872019-04-30 09:22:27 +0200391 LYOUT_CHECK(out, out->status);
392
Radek Krejcid3ca0632019-04-16 16:54:54 +0200393 switch (out->type) {
394 case LYOUT_MEMORY:
395 /* write */
396 memcpy(&out->method.mem.buf[position], buf, count);
397 break;
398 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200399 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200400 case LYOUT_STREAM:
401 case LYOUT_CALLBACK:
402 if (out->buf_len < position + count) {
Radek Krejci56cc0872019-04-30 09:22:27 +0200403 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200404 LOGMEM_RET(NULL);
405 }
406
407 /* write into the hole */
408 memcpy(&out->buffered[position], buf, count);
409
410 /* decrease hole counter */
411 --out->hole_count;
412
413 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200414 /* all holes filled, we can write the buffer,
415 * printed bytes counter is updated by ly_write() */
Radek Krejcid3ca0632019-04-16 16:54:54 +0200416 ret = ly_write(out, out->buffered, out->buf_len);
417 out->buf_len = 0;
418 }
419 break;
420 }
421
422 return ret;
423}