blob: 72872b184b53fd4577cf09574251d207ea09beb0 [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>
Radek Krejcid3ca0632019-04-16 16:54:54 +020023
Radek Krejcie7b95092019-05-15 11:03:07 +020024#include "log.h"
Radek Krejcid3ca0632019-04-16 16:54:54 +020025#include "printer_internal.h"
26
27/**
28 * @brief informational structure shared by printers
29 */
30struct ext_substmt_info_s ext_substmt_info[] = {
31 {NULL, NULL, 0}, /**< LYEXT_SUBSTMT_SELF */
32 {"argument", "name", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_ARGUMENT */
33 {"base", "name", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_BASE */
34 {"belongs-to", "module", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_BELONGSTO */
35 {"contact", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_CONTACT */
36 {"default", "value", 0}, /**< LYEXT_SUBSTMT_DEFAULT */
37 {"description", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_DESCRIPTION */
38 {"error-app-tag", "value", 0}, /**< LYEXT_SUBSTMT_ERRTAG */
39 {"error-message", "value", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_ERRMSG */
40 {"key", "value", 0}, /**< LYEXT_SUBSTMT_KEY */
41 {"namespace", "uri", 0}, /**< LYEXT_SUBSTMT_NAMESPACE */
42 {"organization", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_ORGANIZATION */
43 {"path", "value", 0}, /**< LYEXT_SUBSTMT_PATH */
44 {"prefix", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_PREFIX */
45 {"presence", "value", 0}, /**< LYEXT_SUBSTMT_PRESENCE */
46 {"reference", "text", SUBST_FLAG_YIN}, /**< LYEXT_SUBSTMT_REFERENCE */
47 {"revision-date", "date", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REVISIONDATE */
48 {"units", "name", 0}, /**< LYEXT_SUBSTMT_UNITS */
49 {"value", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_VALUE */
50 {"yang-version", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_VERSION */
51 {"modifier", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MODIFIER */
52 {"require-instance", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_REQINST */
53 {"yin-element", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_YINELEM */
54 {"config", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_CONFIG */
55 {"mandatory", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MANDATORY */
56 {"ordered-by", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_ORDEREDBY */
57 {"status", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_STATUS */
58 {"fraction-digits", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_DIGITS */
59 {"max-elements", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MAX */
60 {"min-elements", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_MIN */
61 {"position", "value", SUBST_FLAG_ID}, /**< LYEXT_SUBSTMT_POSITION */
62 {"unique", "tag", 0}, /**< LYEXT_SUBSTMT_UNIQUE */
63};
64
65LY_ERR
66ly_print(struct lyout *out, const char *format, ...)
67{
68 int count = 0;
69 char *msg = NULL, *aux;
70 va_list ap;
71#ifndef HAVE_VDPRINTF
Radek Krejci4a0ed4a2019-04-18 15:08:34 +020072 int fd;
Radek Krejcid3ca0632019-04-16 16:54:54 +020073 FILE *stream;
74#endif
75
Radek Krejci56cc0872019-04-30 09:22:27 +020076 LYOUT_CHECK(out, out->status);
77
Radek Krejcid3ca0632019-04-16 16:54:54 +020078 va_start(ap, format);
79
80 switch (out->type) {
81 case LYOUT_FD:
82#ifdef HAVE_VDPRINTF
83 count = vdprintf(out->method.fd, format, ap);
Radek Krejcid3ca0632019-04-16 16:54:54 +020084 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +020085#else
86 /* Without vdfprintf(), change the printing method to printing to a FILE stream.
87 * To preserve the original file descriptor, duplicate it and use it to open file stream.
88 * Due to a standalone LYOUT_FDSTREAM, ly*_print_fd() functions are supposed to detect the
89 * change and close the stream on their exit. */
90 fd = dup(out->method.fd);
91 if (fd < 0) {
92 LOGERR(NULL, LY_ESYS, "Unable to duplicate provided file descriptor (%d) for printing the output (%s).",
93 out->method.fd, strerror(errno));
94 va_end(ap);
Radek Krejci56cc0872019-04-30 09:22:27 +020095 out->status = LY_ESYS;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +020096 return LY_ESYS;
97 }
98 stream = fdopen(fd, "a");
99 if (!stream) {
100 LOGERR(NULL, LY_ESYS, "Unable to open provided file descriptor (%d) for printing the output (%s).",
101 out->method.fd, strerror(errno));
102 va_end(ap);
Radek Krejci56cc0872019-04-30 09:22:27 +0200103 out->status = LY_ESYS;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200104 return LY_ESYS;
105 }
106 out->method.f = stream;
107 out->type = LYOUT_FDSTREAM;
108#endif
109 /* fall through */
110 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200111 case LYOUT_STREAM:
112 count = vfprintf(out->method.f, format, ap);
113 break;
114 case LYOUT_MEMORY:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200115 if ((count = vasprintf(&msg, format, ap)) < 0) {
116 break;
117 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200118 if (out->method.mem.len + count + 1 > out->method.mem.size) {
119 aux = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
120 if (!aux) {
121 out->method.mem.buf = NULL;
122 out->method.mem.len = 0;
123 out->method.mem.size = 0;
124 LOGMEM(NULL);
125 va_end(ap);
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200126 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200127 }
128 out->method.mem.buf = aux;
129 out->method.mem.size = out->method.mem.len + count + 1;
130 }
131 memcpy(&out->method.mem.buf[out->method.mem.len], msg, count);
132 out->method.mem.len += count;
133 out->method.mem.buf[out->method.mem.len] = '\0';
134 free(msg);
135 break;
136 case LYOUT_CALLBACK:
Radek Krejci897ad2e2019-04-29 16:43:07 +0200137 if ((count = vasprintf(&msg, format, ap)) < 0) {
138 break;
139 }
Radek Krejcid3ca0632019-04-16 16:54:54 +0200140 count = out->method.clb.f(out->method.clb.arg, msg, count);
141 free(msg);
142 break;
143 }
144
145 va_end(ap);
146
147 if (count < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200148 LOGERR(out->ctx, LY_ESYS, "%s: writing data failed (%s).", __func__, strerror(errno));
Radek Krejci56cc0872019-04-30 09:22:27 +0200149 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200150 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200151 } else {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200152 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200153 return LY_SUCCESS;
154 }
155}
156
157void
158ly_print_flush(struct lyout *out)
159{
160 switch (out->type) {
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200161 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200162 case LYOUT_STREAM:
163 fflush(out->method.f);
164 break;
165 case LYOUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200166 fsync(out->method.fd);
167 break;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200168 case LYOUT_MEMORY:
169 case LYOUT_CALLBACK:
170 /* nothing to do */
171 break;
172 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200173
174 free(out->buffered);
175 out->buf_size = out->buf_len = 0;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200176}
177
178LY_ERR
Radek Krejcie7b95092019-05-15 11:03:07 +0200179ly_write(struct lyout *out, const char *buf, size_t len)
Radek Krejcid3ca0632019-04-16 16:54:54 +0200180{
181 int written = 0;
182
Radek Krejci56cc0872019-04-30 09:22:27 +0200183 LYOUT_CHECK(out, out->status);
184
Radek Krejcid3ca0632019-04-16 16:54:54 +0200185 if (out->hole_count) {
186 /* we are buffering data after a hole */
Radek Krejcie7b95092019-05-15 11:03:07 +0200187 if (out->buf_len + len > out->buf_size) {
188 out->buffered = ly_realloc(out->buffered, out->buf_len + len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200189 if (!out->buffered) {
190 out->buf_len = 0;
191 out->buf_size = 0;
192 LOGMEM_RET(NULL);
193 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200194 out->buf_size = out->buf_len + len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200195 }
196
Radek Krejcie7b95092019-05-15 11:03:07 +0200197 memcpy(&out->buffered[out->buf_len], buf, len);
198 out->buf_len += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200199 return LY_SUCCESS;
200 }
201
Radek Krejci897ad2e2019-04-29 16:43:07 +0200202repeat:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200203 switch (out->type) {
204 case LYOUT_MEMORY:
Radek Krejcie7b95092019-05-15 11:03:07 +0200205 if (out->method.mem.len + len + 1 > out->method.mem.size) {
206 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + len + 1);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200207 if (!out->method.mem.buf) {
208 out->method.mem.len = 0;
209 out->method.mem.size = 0;
210 LOGMEM_RET(NULL);
211 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200212 out->method.mem.size = out->method.mem.len + len + 1;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200213 }
Radek Krejcie7b95092019-05-15 11:03:07 +0200214 memcpy(&out->method.mem.buf[out->method.mem.len], buf, len);
215 out->method.mem.len += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200216 out->method.mem.buf[out->method.mem.len] = '\0';
Radek Krejci897ad2e2019-04-29 16:43:07 +0200217
Radek Krejcie7b95092019-05-15 11:03:07 +0200218 out->printed += len;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200219 return LY_SUCCESS;
220 case LYOUT_FD:
Radek Krejcie7b95092019-05-15 11:03:07 +0200221 written = write(out->method.fd, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200222 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200223 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200224 case LYOUT_STREAM:
Radek Krejcie7b95092019-05-15 11:03:07 +0200225 written = fwrite(buf, sizeof *buf, len, out->method.f);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200226 break;
227 case LYOUT_CALLBACK:
Radek Krejcie7b95092019-05-15 11:03:07 +0200228 written = out->method.clb.f(out->method.clb.arg, buf, len);
Radek Krejcid3ca0632019-04-16 16:54:54 +0200229 break;
230 }
231
232 if (written < 0) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200233 if (errno == EAGAIN || errno == EWOULDBLOCK) {
234 goto repeat;
235 }
236 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 Krejcie7b95092019-05-15 11:03:07 +0200239 } else if ((size_t)written != len) {
240 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 +0200241 out->status = LY_ESYS;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200242 return LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200243 } else {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200244 out->printed += written;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200245 return LY_SUCCESS;
246 }
247}
248
249LY_ERR
250ly_write_skip(struct lyout *out, size_t count, size_t *position)
251{
Radek Krejci56cc0872019-04-30 09:22:27 +0200252 LYOUT_CHECK(out, out->status);
253
Radek Krejcid3ca0632019-04-16 16:54:54 +0200254 switch (out->type) {
255 case LYOUT_MEMORY:
256 if (out->method.mem.len + count > out->method.mem.size) {
257 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count);
258 if (!out->method.mem.buf) {
259 out->method.mem.len = 0;
260 out->method.mem.size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200261 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200262 LOGMEM_RET(NULL);
263 }
264 out->method.mem.size = out->method.mem.len + count;
265 }
266
267 /* save the current position */
268 *position = out->method.mem.len;
269
270 /* skip the memory */
271 out->method.mem.len += count;
Radek Krejci897ad2e2019-04-29 16:43:07 +0200272
273 /* update printed bytes counter despite we actually printed just a hole */
274 out->printed += count;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200275 break;
276 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200277 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200278 case LYOUT_STREAM:
279 case LYOUT_CALLBACK:
280 /* buffer the hole */
281 if (out->buf_len + count > out->buf_size) {
282 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
283 if (!out->buffered) {
284 out->buf_len = 0;
285 out->buf_size = 0;
Radek Krejci56cc0872019-04-30 09:22:27 +0200286 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200287 LOGMEM_RET(NULL);
288 }
289 out->buf_size = out->buf_len + count;
290 }
291
292 /* save the current position */
293 *position = out->buf_len;
294
295 /* skip the memory */
296 out->buf_len += count;
297
298 /* increase hole counter */
299 ++out->hole_count;
300 }
301
302 return LY_SUCCESS;
303}
304
305LY_ERR
306ly_write_skipped(struct lyout *out, size_t position, const char *buf, size_t count)
307{
308 LY_ERR ret = LY_SUCCESS;
309
Radek Krejci56cc0872019-04-30 09:22:27 +0200310 LYOUT_CHECK(out, out->status);
311
Radek Krejcid3ca0632019-04-16 16:54:54 +0200312 switch (out->type) {
313 case LYOUT_MEMORY:
314 /* write */
315 memcpy(&out->method.mem.buf[position], buf, count);
316 break;
317 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200318 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200319 case LYOUT_STREAM:
320 case LYOUT_CALLBACK:
321 if (out->buf_len < position + count) {
Radek Krejci56cc0872019-04-30 09:22:27 +0200322 out->status = LY_ESYS;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200323 LOGMEM_RET(NULL);
324 }
325
326 /* write into the hole */
327 memcpy(&out->buffered[position], buf, count);
328
329 /* decrease hole counter */
330 --out->hole_count;
331
332 if (!out->hole_count) {
Radek Krejci897ad2e2019-04-29 16:43:07 +0200333 /* all holes filled, we can write the buffer,
334 * printed bytes counter is updated by ly_write() */
Radek Krejcid3ca0632019-04-16 16:54:54 +0200335 ret = ly_write(out, out->buffered, out->buf_len);
336 out->buf_len = 0;
337 }
338 break;
339 }
340
341 return ret;
342}