blob: 8f4a06756a28fd80bc20525bcb738325d9a5f26a [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:
107 count = vasprintf(&msg, format, ap);
108 if (out->method.mem.len + count + 1 > out->method.mem.size) {
109 aux = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
110 if (!aux) {
111 out->method.mem.buf = NULL;
112 out->method.mem.len = 0;
113 out->method.mem.size = 0;
114 LOGMEM(NULL);
115 va_end(ap);
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200116 return LY_EMEM;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200117 }
118 out->method.mem.buf = aux;
119 out->method.mem.size = out->method.mem.len + count + 1;
120 }
121 memcpy(&out->method.mem.buf[out->method.mem.len], msg, count);
122 out->method.mem.len += count;
123 out->method.mem.buf[out->method.mem.len] = '\0';
124 free(msg);
125 break;
126 case LYOUT_CALLBACK:
127 count = vasprintf(&msg, format, ap);
128 count = out->method.clb.f(out->method.clb.arg, msg, count);
129 free(msg);
130 break;
131 }
132
133 va_end(ap);
134
135 if (count < 0) {
136 return LY_EOTHER;
137 } else {
138 return LY_SUCCESS;
139 }
140}
141
142void
143ly_print_flush(struct lyout *out)
144{
145 switch (out->type) {
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200146 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200147 case LYOUT_STREAM:
148 fflush(out->method.f);
149 break;
150 case LYOUT_FD:
151 case LYOUT_MEMORY:
152 case LYOUT_CALLBACK:
153 /* nothing to do */
154 break;
155 }
156}
157
158LY_ERR
159ly_write(struct lyout *out, const char *buf, size_t count)
160{
161 int written = 0;
162
163 if (out->hole_count) {
164 /* we are buffering data after a hole */
165 if (out->buf_len + count > out->buf_size) {
166 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
167 if (!out->buffered) {
168 out->buf_len = 0;
169 out->buf_size = 0;
170 LOGMEM_RET(NULL);
171 }
172 out->buf_size = out->buf_len + count;
173 }
174
175 memcpy(&out->buffered[out->buf_len], buf, count);
176 out->buf_len += count;
177 return LY_SUCCESS;
178 }
179
180 switch (out->type) {
181 case LYOUT_MEMORY:
182 if (out->method.mem.len + count + 1 > out->method.mem.size) {
183 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count + 1);
184 if (!out->method.mem.buf) {
185 out->method.mem.len = 0;
186 out->method.mem.size = 0;
187 LOGMEM_RET(NULL);
188 }
189 out->method.mem.size = out->method.mem.len + count + 1;
190 }
191 memcpy(&out->method.mem.buf[out->method.mem.len], buf, count);
192 out->method.mem.len += count;
193 out->method.mem.buf[out->method.mem.len] = '\0';
194 return LY_SUCCESS;
195 case LYOUT_FD:
196 written = write(out->method.fd, buf, count);
197 break;
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200198 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200199 case LYOUT_STREAM:
200 written = fwrite(buf, sizeof *buf, count, out->method.f);
201 break;
202 case LYOUT_CALLBACK:
203 written = out->method.clb.f(out->method.clb.arg, buf, count);
204 break;
205 }
206
207 if (written < 0) {
208 return LY_EOTHER;
209 } else {
210 return LY_SUCCESS;
211 }
212}
213
214LY_ERR
215ly_write_skip(struct lyout *out, size_t count, size_t *position)
216{
217 switch (out->type) {
218 case LYOUT_MEMORY:
219 if (out->method.mem.len + count > out->method.mem.size) {
220 out->method.mem.buf = ly_realloc(out->method.mem.buf, out->method.mem.len + count);
221 if (!out->method.mem.buf) {
222 out->method.mem.len = 0;
223 out->method.mem.size = 0;
224 LOGMEM_RET(NULL);
225 }
226 out->method.mem.size = out->method.mem.len + count;
227 }
228
229 /* save the current position */
230 *position = out->method.mem.len;
231
232 /* skip the memory */
233 out->method.mem.len += count;
234 break;
235 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200236 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200237 case LYOUT_STREAM:
238 case LYOUT_CALLBACK:
239 /* buffer the hole */
240 if (out->buf_len + count > out->buf_size) {
241 out->buffered = ly_realloc(out->buffered, out->buf_len + count);
242 if (!out->buffered) {
243 out->buf_len = 0;
244 out->buf_size = 0;
245 LOGMEM_RET(NULL);
246 }
247 out->buf_size = out->buf_len + count;
248 }
249
250 /* save the current position */
251 *position = out->buf_len;
252
253 /* skip the memory */
254 out->buf_len += count;
255
256 /* increase hole counter */
257 ++out->hole_count;
258 }
259
260 return LY_SUCCESS;
261}
262
263LY_ERR
264ly_write_skipped(struct lyout *out, size_t position, const char *buf, size_t count)
265{
266 LY_ERR ret = LY_SUCCESS;
267
268 switch (out->type) {
269 case LYOUT_MEMORY:
270 /* write */
271 memcpy(&out->method.mem.buf[position], buf, count);
272 break;
273 case LYOUT_FD:
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200274 case LYOUT_FDSTREAM:
Radek Krejcid3ca0632019-04-16 16:54:54 +0200275 case LYOUT_STREAM:
276 case LYOUT_CALLBACK:
277 if (out->buf_len < position + count) {
278 LOGMEM_RET(NULL);
279 }
280
281 /* write into the hole */
282 memcpy(&out->buffered[position], buf, count);
283
284 /* decrease hole counter */
285 --out->hole_count;
286
287 if (!out->hole_count) {
288 /* all holes filled, we can write the buffer */
289 ret = ly_write(out, out->buffered, out->buf_len);
290 out->buf_len = 0;
291 }
292 break;
293 }
294
295 return ret;
296}
297
298static LY_ERR
299lys_print_(struct lyout *out, const struct lys_module *module, LYS_OUTFORMAT format, int UNUSED(line_length), int UNUSED(options))
300{
301 LY_ERR ret;
302
303 switch (format) {
304 case LYS_OUT_YANG:
305 ret = yang_print_parsed(out, module);
306 break;
307 case LYS_OUT_YANG_COMPILED:
308 ret = yang_print_compiled(out, module);
309 break;
310 /* TODO not yet implemented
311 case LYS_OUT_YIN:
312 lys_disable_deviations((struct lys_module *)module);
313 ret = yin_print_model(out, module);
314 lys_enable_deviations((struct lys_module *)module);
315 break;
316 case LYS_OUT_TREE:
317 ret = tree_print_model(out, module, target_node, line_length, options);
318 break;
319 case LYS_OUT_INFO:
320 ret = info_print_model(out, module, target_node);
321 break;
322 case LYS_OUT_JSON:
323 ret = jsons_print_model(out, module, target_node);
324 break;
325 */
326 default:
327 LOGERR(module->ctx, LY_EINVAL, "Unknown output format.");
328 ret = LY_EINVAL;
329 break;
330 }
331
332 return ret;
333}
334
335API LY_ERR
336lys_print_file(FILE *f, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
337{
338 struct lyout out;
339
340 LY_CHECK_ARG_RET(NULL, f, module, LY_EINVAL);
341
342 memset(&out, 0, sizeof out);
343
344 out.type = LYOUT_STREAM;
345 out.method.f = f;
346
347 return lys_print_(&out, module, format, line_length, options);
348}
349
350API LY_ERR
351lys_print_path(const char *path, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
352{
353 FILE *f;
354 LY_ERR ret;
355
356 LY_CHECK_ARG_RET(NULL, path, module, LY_EINVAL);
357
358 f = fopen(path, "w");
359 if (!f) {
360 LOGERR(module->ctx, LY_ESYS, "Failed to open file \"%s\" (%s).", path, strerror(errno));
361 return LY_ESYS;
362 }
363
364 ret = lys_print_file(f, module, format, line_length, options);
365 fclose(f);
366 return ret;
367}
368
369API LY_ERR
370lys_print_fd(int fd, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
371{
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200372 LY_ERR ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200373 struct lyout out;
374
375 LY_CHECK_ARG_RET(NULL, fd >= 0, module, LY_EINVAL);
376
377 memset(&out, 0, sizeof out);
378
379 out.type = LYOUT_FD;
380 out.method.fd = fd;
381
Radek Krejci4a0ed4a2019-04-18 15:08:34 +0200382 ret = lys_print_(&out, module, format, line_length, options);
383
384 if (out.type == LYOUT_FDSTREAM) {
385 /* close temporary stream based on the given file descriptor */
386 fclose(out.method.f);
387 /* move the original file descriptor to the end of the output file */
388 lseek(fd, 0, SEEK_END);
389 }
390
391 return ret;
Radek Krejcid3ca0632019-04-16 16:54:54 +0200392}
393
394API LY_ERR
395lys_print_mem(char **strp, const struct lys_module *module, LYS_OUTFORMAT format, int line_length, int options)
396{
397 struct lyout out;
398 LY_ERR r;
399
400 LY_CHECK_ARG_RET(NULL, strp, module, LY_EINVAL);
401
402 memset(&out, 0, sizeof out);
403
404 out.type = LYOUT_MEMORY;
405
406 r = lys_print_(&out, module, format, line_length, options);
407
408 *strp = out.method.mem.buf;
409 return r;
410}
411
412API LY_ERR
413lys_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lys_module *module,
414 LYS_OUTFORMAT format, int line_length, int options)
415{
416 struct lyout out;
417
418 LY_CHECK_ARG_RET(NULL, writeclb, module, LY_EINVAL);
419
420 memset(&out, 0, sizeof out);
421
422 out.type = LYOUT_CALLBACK;
423 out.method.clb.f = writeclb;
424 out.method.clb.arg = arg;
425
426 return lys_print_(&out, module, format, line_length, options);
427}