blob: 6efb0763837156e1fcbcfb834943edad58815cfc [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 Vasko9b368d32020-02-14 13:53:31 +010067/**
68 * @brief Check whether a node value equals to its default one.
69 *
70 * @param[in] node Term node to test.
71 * @return 0 if no,
72 * @return non-zero if yes.
73 */
74static int
75ly_is_default(const struct lyd_node *node)
76{
77 const struct lysc_node_leaf *leaf;
78 const struct lysc_node_leaflist *llist;
79 const struct lyd_node_term *term;
80 size_t i;
81
82 assert(node->schema->nodetype & LYD_NODE_TERM);
83 term = (const struct lyd_node_term *)node;
84
85 if (node->schema->nodetype == LYS_LEAF) {
86 leaf = (const struct lysc_node_leaf *)node->schema;
87 if (!leaf->dflt) {
88 return 0;
89 }
90
91 /* compare with the default value */
92 if (leaf->type->plugin->compare(&term->value, leaf->dflt)) {
93 return 0;
94 }
95 } else {
96 llist = (const struct lysc_node_leaflist *)node->schema;
97 if (!llist->dflts) {
98 return 0;
99 }
100
101 LY_ARRAY_FOR(llist->dflts, i) {
102 /* compare with each possible default value */
103 if (llist->type->plugin->compare(&term->value, llist->dflts[i])) {
104 return 0;
105 }
106 }
107 }
108
109 return 1;
110}
111
112int
113ly_should_print(const struct lyd_node *node, int options)
114{
115 const struct lyd_node *next, *elem;
116
117 if (options & LYDP_WD_TRIM) {
118 /* do not print default nodes */
119 if (node->flags & LYD_DEFAULT) {
120 /* implicit default node/NP container with only default nodes */
121 return 0;
122 } else if (node->schema->nodetype & LYD_NODE_TERM) {
123 if (ly_is_default(node)) {
124 /* explicit default node */
125 return 0;
126 }
127 }
128 } else if ((node->flags & LYD_DEFAULT) && !(options & LYDP_WD_MASK) && !(node->schema->flags & LYS_CONFIG_R)) {
129 /* LYP_WD_EXPLICIT
130 * - print only if it contains status data in its subtree */
131 LYD_TREE_DFS_BEGIN(node, next, elem) {
132 if (elem->schema->flags & LYS_CONFIG_R) {
133 return 1;
134 }
135 LYD_TREE_DFS_END(node, next, elem)
136 }
137 return 0;
138 } else if ((node->flags & LYD_DEFAULT) && (node->schema->nodetype == LYS_CONTAINER) && !(options & LYDP_KEEPEMPTYCONT)) {
139 /* avoid empty default containers */
140 LYD_TREE_DFS_BEGIN(node, next, elem) {
141 if (elem->schema->nodetype != LYS_CONTAINER) {
142 return 1;
143 }
144 assert(elem->flags & LYD_DEFAULT);
145 LYD_TREE_DFS_END(node, next, elem)
146 }
147 return 0;
148 }
149
150 return 1;
151}
152
Radek Krejcid3ca0632019-04-16 16:54:54 +0200153LY_ERR
154ly_print(struct lyout *out, const char *format, ...)
155{
156 int count = 0;
157 char *msg = NULL, *aux;
158 va_list ap;
159#ifndef HAVE_VDPRINTF
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200160 int fd;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200161 FILE *stream;
162#endif
163
Radek Krejci56cc0872019-04-30 09:22:27 +0200164 LYOUT_CHECK(out, out->status);
165
Radek Krejcid3ca0632019-04-16 16:54:54 +0200166 va_start(ap, format);
167
168 switch (out->type) {
169 case LYOUT_FD:
170#ifdef HAVE_VDPRINTF
171 count = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200172 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200173#else
174 /* Without vdfprintf(), change the printing method to printing to a FILE stream.
175 * To preserve the original file descriptor, duplicate it and use it to open file stream.
176 * Due to a standalone LYOUT_FDSTREAM, ly*_print_fd() functions are supposed to detect the
177 * change and close the stream on their exit. */
178 fd = dup(out->method.fd);
179 if (fd < 0) {
180 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).",
181 out->method.fd, strerror(errno));
182 va_end(ap);
Radek Krejci56cc0872019-04-30 09:22:27 +0200183 out->status = LY_ESYS;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200184 return LY_ESYS;
185 }
186 stream = fdopen(fd, "a");
187 if (!stream) {
188 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).",
189 out->method.fd, strerror(errno));
190 va_end(ap);
Radek Krejci56cc0872019-04-30 09:22:27 +0200191 out->status = LY_ESYS;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200192 return LY_ESYS;
193 }
194 out->method.f = stream;
195 out->type = LYOUT_FDSTREAM;
196#endif
197 /* fall through */
198 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200199 case LYOUT_STREAM:
200 count = vfprintf(out->method.f, format, ap);
201 break;
202 case LYOUT_MEMORY:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200203 if ((count = vasprintf(&msg, format, ap)) < 0) {
204 break;
205 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200206 if (out->method.mem.len + count + 1 > out->method.mem.size) {
207 aux = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
208 if (!aux) {
209 out->method.mem.buf = NULL;
210 out->method.mem.len = 0;
211 out->method.mem.size = 0;
212 LOGMEM(NULL);
213 va_end(ap);
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200214 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200215 }
216 out->method.mem.buf = aux;
217 out->method.mem.size = out->method.mem.len + count + 1;
218 }
219 memcpy(&out->method.mem.buf[out->method.mem.len], msg, count);
220 out->method.mem.len += count;
221 out->method.mem.buf[out->method.mem.len] = '\0';
222 free(msg);
223 break;
224 case LYOUT_CALLBACK:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200225 if ((count = vasprintf(&msg, format, ap)) < 0) {
226 break;
227 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200228 count = out->method.clb.f(out->method.clb.arg, msg, count);
229 free(msg);
230 break;
231 }
232
233 va_end(ap);
234
235 if (count < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200236 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200237 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200238 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200239 } else {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200240 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200241 return LY_SUCCESS;
242 }
243}
244
245void
246ly_print_flush(struct lyout *out)
247{
248 switch (out->type) {
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200249 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200250 case LYOUT_STREAM:
251 fflush(out->method.f);
252 break;
253 case LYOUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200254 fsync(out->method.fd);
255 break;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200256 case LYOUT_MEMORY:
257 case LYOUT_CALLBACK:
258 /* nothing to do */
259 break;
260 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200261
262 free(out->buffered);
263 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200264}
265
266LY_ERR
Radek Krejcie7b95092019-05-15 11:03:07 +0200267ly_write(struct lyout *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200268{
269 int written = 0;
270
Radek Krejci56cc0872019-04-30 09:22:27 +0200271 LYOUT_CHECK(out, out->status);
272
Radek Krejcid3ca0632019-04-16 16:54:54 +0200273 if (out->hole_count) {
274 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200275 if (out->buf_len + len > out->buf_size) {
276 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200277 if (!out->buffered) {
278 out->buf_len = 0;
279 out->buf_size = 0;
280 LOGMEM_RET(NULL);
281 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200282 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200283 }
284
Radek Krejcie7b95092019-05-15 11:03:07 +0200285 memcpy(&out->buffered[out->buf_len], buf, len);
286 out->buf_len += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200287 return LY_SUCCESS;
288 }
289
Radek Krejci897ad2e2019-04-29 16:43:07 +0200290repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200291 switch (out->type) {
292 case LYOUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200293 if (out->method.mem.len + len + 1 > out->method.mem.size) {
294 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + len + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200295 if (!out->method.mem.buf) {
296 out->method.mem.len = 0;
297 out->method.mem.size = 0;
298 LOGMEM_RET(NULL);
299 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200300 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200301 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200302 memcpy(&out->method.mem.buf[out->method.mem.len], buf, len);
303 out->method.mem.len += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200304 out->method.mem.buf[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200305
Radek Krejcie7b95092019-05-15 11:03:07 +0200306 out->printed += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200307 return LY_SUCCESS;
308 case LYOUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200309 written = write(out->method.fd, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200310 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200311 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200312 case LYOUT_STREAM:
Radek Krejcie7b95092019-05-15 11:03:07 +0200313 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200314 break;
315 case LYOUT_CALLBACK:
Radek Krejcie7b95092019-05-15 11:03:07 +0200316 written = out->method.clb.f(out->method.clb.arg, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200317 break;
318 }
319
320 if (written < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200321 if (errno == EAGAIN || errno == EWOULDBLOCK) {
322 goto repeat;
323 }
324 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200325 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200326 return LY_ESYS;
Radek Krejcie7b95092019-05-15 11:03:07 +0200327 } else if ((size_t)written != len) {
328 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 +0200329 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200330 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200331 } else {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200332 out->printed += written;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200333 return LY_SUCCESS;
334 }
335}
336
337LY_ERR
338ly_write_skip(struct lyout *out, size_t count, size_t *position)
339{
Radek Krejci56cc0872019-04-30 09:22:27 +0200340 LYOUT_CHECK(out, out->status);
341
Radek Krejcid3ca0632019-04-16 16:54:54 +0200342 switch (out->type) {
343 case LYOUT_MEMORY:
344 if (out->method.mem.len + count > out->method.mem.size) {
345 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count);
346 if (!out->method.mem.buf) {
347 out->method.mem.len = 0;
348 out->method.mem.size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200349 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200350 LOGMEM_RET(NULL);
351 }
352 out->method.mem.size = out->method.mem.len + count;
353 }
354
355 /* save the current position */
356 *position = out->method.mem.len;
357
358 /* skip the memory */
359 out->method.mem.len += count;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200360
361 /* update printed bytes counter despite we actually printed just a hole */
362 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200363 break;
364 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200365 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200366 case LYOUT_STREAM:
367 case LYOUT_CALLBACK:
368 /* buffer the hole */
369 if (out->buf_len + count > out->buf_size) {
370 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
371 if (!out->buffered) {
372 out->buf_len = 0;
373 out->buf_size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200374 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200375 LOGMEM_RET(NULL);
376 }
377 out->buf_size = out->buf_len + count;
378 }
379
380 /* save the current position */
381 *position = out->buf_len;
382
383 /* skip the memory */
384 out->buf_len += count;
385
386 /* increase hole counter */
387 ++out->hole_count;
388 }
389
390 return LY_SUCCESS;
391}
392
393LY_ERR
394ly_write_skipped(struct lyout *out, size_t position, const char *buf, size_t count)
395{
396 LY_ERR ret = LY_SUCCESS;
397
Radek Krejci56cc0872019-04-30 09:22:27 +0200398 LYOUT_CHECK(out, out->status);
399
Radek Krejcid3ca0632019-04-16 16:54:54 +0200400 switch (out->type) {
401 case LYOUT_MEMORY:
402 /* write */
403 memcpy(&out->method.mem.buf[position], buf, count);
404 break;
405 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200406 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200407 case LYOUT_STREAM:
408 case LYOUT_CALLBACK:
409 if (out->buf_len < position + count) {
Radek Krejci56cc0872019-04-30 09:22:27 +0200410 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200411 LOGMEM_RET(NULL);
412 }
413
414 /* write into the hole */
415 memcpy(&out->buffered[position], buf, count);
416
417 /* decrease hole counter */
418 --out->hole_count;
419
420 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200421 /* all holes filled, we can write the buffer,
422 * printed bytes counter is updated by ly_write() */
Radek Krejcid3ca0632019-04-16 16:54:54 +0200423 ret = ly_write(out, out->buffered, out->buf_len);
424 out->buf_len = 0;
425 }
426 break;
427 }
428
429 return ret;
430}