aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/libfyaml/src/lib/fy-emit.c
diff options
context:
space:
mode:
authorDaniil Cherednik <dan.cherednik@gmail.com>2023-05-05 11:09:01 +0300
committerDaniil Cherednik <dan.cherednik@gmail.com>2023-05-05 11:09:01 +0300
commitb5a989b16cafa8a3b3bc076f1097a0eda6f48c06 (patch)
tree4da744117a5aab37758921fa43b95a3068e5aec1 /contrib/libs/libfyaml/src/lib/fy-emit.c
parentfc1cffcfa7f0497a1f97b384a24bcbf23362f3be (diff)
downloadydb-b5a989b16cafa8a3b3bc076f1097a0eda6f48c06.tar.gz
Ydb stable 23-1-2623.1.26
x-stable-origin-commit: 22184a7e157553d447f17a2dffc4ea2d32dfd74d
Diffstat (limited to 'contrib/libs/libfyaml/src/lib/fy-emit.c')
-rw-r--r--contrib/libs/libfyaml/src/lib/fy-emit.c3554
1 files changed, 3554 insertions, 0 deletions
diff --git a/contrib/libs/libfyaml/src/lib/fy-emit.c b/contrib/libs/libfyaml/src/lib/fy-emit.c
new file mode 100644
index 0000000000..ad0a18dd57
--- /dev/null
+++ b/contrib/libs/libfyaml/src/lib/fy-emit.c
@@ -0,0 +1,3554 @@
+/*
+ * fy-emit.c - Internal YAML emitter methods
+ *
+ * Copyright (c) 2019 Pantelis Antoniou <pantelis.antoniou@konsulko.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <limits.h>
+#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
+#include <unistd.h>
+#elif defined (_MSC_VER)
+#define STDOUT_FILENO _fileno(stdin)
+#endif
+#include <ctype.h>
+#include <errno.h>
+
+#include <libfyaml.h>
+
+#include "fy-parse.h"
+#include "fy-emit.h"
+
+/* fwd decl */
+void fy_emit_write(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int len);
+void fy_emit_printf(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *fmt, ...)
+ FY_ATTRIBUTE(format(printf, 3, 4));
+
+static inline bool fy_emit_is_json_mode(const struct fy_emitter *emit)
+{
+ enum fy_emitter_cfg_flags flags;
+
+ if (emit->force_json)
+ return true;
+
+ flags = emit->cfg.flags & FYECF_MODE(FYECF_MODE_MASK);
+ return flags == FYECF_MODE_JSON || flags == FYECF_MODE_JSON_TP || flags == FYECF_MODE_JSON_ONELINE;
+}
+
+static inline bool fy_emit_is_flow_mode(const struct fy_emitter *emit)
+{
+ enum fy_emitter_cfg_flags flags = emit->cfg.flags & FYECF_MODE(FYECF_MODE_MASK);
+
+ return flags == FYECF_MODE_FLOW || flags == FYECF_MODE_FLOW_ONELINE;
+}
+
+static inline bool fy_emit_is_block_mode(const struct fy_emitter *emit)
+{
+ enum fy_emitter_cfg_flags flags = emit->cfg.flags & FYECF_MODE(FYECF_MODE_MASK);
+
+ return flags == FYECF_MODE_BLOCK || flags == FYECF_MODE_DEJSON || flags == FYECF_MODE_PRETTY;
+}
+
+static inline bool fy_emit_is_oneline(const struct fy_emitter *emit)
+{
+ enum fy_emitter_cfg_flags flags = emit->cfg.flags & FYECF_MODE(FYECF_MODE_MASK);
+
+ return flags == FYECF_MODE_FLOW_ONELINE || flags == FYECF_MODE_JSON_ONELINE;
+}
+
+static inline bool fy_emit_is_dejson_mode(const struct fy_emitter *emit)
+{
+ enum fy_emitter_cfg_flags flags = emit->cfg.flags & FYECF_MODE(FYECF_MODE_MASK);
+
+ return flags == FYECF_MODE_DEJSON;
+}
+
+static inline bool fy_emit_is_pretty_mode(const struct fy_emitter *emit)
+{
+ enum fy_emitter_cfg_flags flags = emit->cfg.flags & FYECF_MODE(FYECF_MODE_MASK);
+
+ return flags == FYECF_MODE_PRETTY;
+}
+
+static inline int fy_emit_indent(struct fy_emitter *emit)
+{
+ int indent;
+
+ indent = (emit->cfg.flags & FYECF_INDENT(FYECF_INDENT_MASK)) >> FYECF_INDENT_SHIFT;
+ return indent ? indent : 2;
+}
+
+static inline int fy_emit_width(struct fy_emitter *emit)
+{
+ int width;
+
+ width = (emit->cfg.flags & FYECF_WIDTH(FYECF_WIDTH_MASK)) >> FYECF_WIDTH_SHIFT;
+ if (width == 0)
+ return 80;
+ if (width == FYECF_WIDTH_MASK)
+ return INT_MAX;
+ return width;
+}
+
+static inline bool fy_emit_output_comments(struct fy_emitter *emit)
+{
+ return !!(emit->cfg.flags & FYECF_OUTPUT_COMMENTS);
+}
+
+static int fy_emit_node_check_json(struct fy_emitter *emit, struct fy_node *fyn)
+{
+ struct fy_document *fyd;
+ struct fy_node *fyni;
+ struct fy_node_pair *fynp, *fynpi;
+ int ret;
+
+ if (!fyn)
+ return 0;
+
+ fyd = fyn->fyd;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ FYD_TOKEN_ERROR_CHECK(fyd, fyn->scalar, FYEM_INTERNAL,
+ !fy_node_is_alias(fyn), err_out,
+ "aliases not allowed in JSON emit mode");
+ break;
+
+ case FYNT_SEQUENCE:
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni;
+ fyni = fy_node_next(&fyn->sequence, fyni)) {
+ ret = fy_emit_node_check_json(emit, fyni);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ case FYNT_MAPPING:
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp; fynp = fynpi) {
+
+ fynpi = fy_node_pair_next(&fyn->mapping, fynp);
+
+ ret = fy_emit_node_check_json(emit, fynp->key);
+ if (ret)
+ return ret;
+ ret = fy_emit_node_check_json(emit, fynp->value);
+ if (ret)
+ return ret;
+ }
+ break;
+ }
+ return 0;
+err_out:
+ return -1;
+}
+
+static int fy_emit_node_check(struct fy_emitter *emit, struct fy_node *fyn)
+{
+ int ret;
+
+ if (!fyn)
+ return 0;
+
+ if (fy_emit_is_json_mode(emit) && !emit->source_json) {
+ ret = fy_emit_node_check_json(emit, fyn);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+void fy_emit_node_internal(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent, bool is_key);
+void fy_emit_scalar(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent, bool is_key);
+void fy_emit_sequence(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent);
+void fy_emit_mapping(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent);
+
+void fy_emit_write(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int len)
+{
+ int c, w;
+ const char *m, *e;
+ int outlen;
+
+ if (!len)
+ return;
+
+ outlen = emit->cfg.output(emit, type, str, len, emit->cfg.userdata);
+ if (outlen != len)
+ emit->output_error = true;
+
+ e = str + len;
+ while ((c = fy_utf8_get(str, (e - str), &w)) >= 0) {
+
+ /* special handling for MSDOS */
+ if (c == '\r' && (e - str) > 1 && str[1] == '\n') {
+ str += 2;
+ emit->column = 0;
+ emit->line++;
+ continue;
+ }
+
+ /* regular line break */
+ if (fy_is_lb_r_n(c)) {
+ emit->column = 0;
+ emit->line++;
+ str += w;
+ continue;
+ }
+
+ /* completely ignore ANSI color escape sequences */
+ if (c == '\x1b' && (e - str) > 2 && str[1] == '[' &&
+ (m = memchr(str, 'm', e - str)) != NULL) {
+ str = m + 1;
+ continue;
+ }
+
+ emit->column++;
+ str += w;
+ }
+}
+
+void fy_emit_puts(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str)
+{
+ fy_emit_write(emit, type, str, strlen(str));
+}
+
+void fy_emit_putc(struct fy_emitter *emit, enum fy_emitter_write_type type, int c)
+{
+ char buf[FY_UTF8_FORMAT_BUFMIN];
+
+ fy_utf8_format(c, buf, fyue_none);
+ fy_emit_puts(emit, type, buf);
+}
+
+void fy_emit_vprintf(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *fmt, va_list ap)
+{
+ char *str;
+ int size;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+
+ size = vsnprintf(NULL, 0, fmt, ap);
+ if (size < 0)
+ return;
+
+ str = FY_ALLOCA(size + 1);
+ size = vsnprintf(str, size + 1, fmt, ap2);
+ if (size < 0)
+ return;
+
+ fy_emit_write(emit, type, str, size);
+}
+
+void fy_emit_printf(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fy_emit_vprintf(emit, type, fmt, ap);
+ va_end(ap);
+}
+
+void fy_emit_write_ws(struct fy_emitter *emit)
+{
+ fy_emit_putc(emit, fyewt_whitespace, ' ');
+ emit->flags |= FYEF_WHITESPACE;
+}
+
+void fy_emit_write_indent(struct fy_emitter *emit, int indent)
+{
+ int len;
+ char *ws;
+
+ indent = indent > 0 ? indent : 0;
+
+ if (!fy_emit_indentation(emit) || emit->column > indent ||
+ (emit->column == indent && !fy_emit_whitespace(emit)))
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+
+ if (emit->column < indent) {
+ len = indent - emit->column;
+ ws = FY_ALLOCA(len + 1);
+ memset(ws, ' ', len);
+ ws[len] = '\0';
+ fy_emit_write(emit, fyewt_indent, ws, len);
+ }
+
+ emit->flags |= FYEF_WHITESPACE | FYEF_INDENTATION;
+}
+
+enum document_indicator {
+ di_question_mark,
+ di_colon,
+ di_dash,
+ di_left_bracket,
+ di_right_bracket,
+ di_left_brace,
+ di_right_brace,
+ di_comma,
+ di_bar,
+ di_greater,
+ di_single_quote_start,
+ di_single_quote_end,
+ di_double_quote_start,
+ di_double_quote_end,
+ di_ambersand,
+ di_star,
+};
+
+void fy_emit_write_indicator(struct fy_emitter *emit,
+ enum document_indicator indicator,
+ int flags, int indent,
+ enum fy_emitter_write_type wtype)
+{
+ switch (indicator) {
+
+ case di_question_mark:
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+ fy_emit_putc(emit, wtype, '?');
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_OPEN_ENDED);
+ break;
+
+ case di_colon:
+ if (!(flags & DDNF_SIMPLE)) {
+ if (!emit->flow_level && !fy_emit_is_oneline(emit))
+ fy_emit_write_indent(emit, indent);
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+ }
+ fy_emit_putc(emit, wtype, ':');
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_OPEN_ENDED);
+ break;
+
+ case di_dash:
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+ fy_emit_putc(emit, wtype, '-');
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_OPEN_ENDED);
+ break;
+
+ case di_left_bracket:
+ case di_left_brace:
+ emit->flow_level++;
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+ fy_emit_putc(emit, wtype, indicator == di_left_bracket ? '[' : '{');
+ emit->flags |= FYEF_WHITESPACE;
+ emit->flags &= ~(FYEF_INDENTATION | FYEF_OPEN_ENDED);
+ break;
+
+ case di_right_bracket:
+ case di_right_brace:
+ emit->flow_level--;
+ fy_emit_putc(emit, wtype, indicator == di_right_bracket ? ']' : '}');
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_INDENTATION | FYEF_OPEN_ENDED);
+ break;
+
+ case di_comma:
+ fy_emit_putc(emit, wtype, ',');
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_INDENTATION | FYEF_OPEN_ENDED);
+ break;
+
+ case di_bar:
+ case di_greater:
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+ fy_emit_putc(emit, wtype, indicator == di_bar ? '|' : '>');
+ emit->flags &= ~(FYEF_INDENTATION | FYEF_WHITESPACE | FYEF_OPEN_ENDED);
+ break;
+
+ case di_single_quote_start:
+ case di_double_quote_start:
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+ fy_emit_putc(emit, wtype, indicator == di_single_quote_start ? '\'' : '"');
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_INDENTATION | FYEF_OPEN_ENDED);
+ break;
+
+ case di_single_quote_end:
+ case di_double_quote_end:
+ fy_emit_putc(emit, wtype, indicator == di_single_quote_end ? '\'' : '"');
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_INDENTATION | FYEF_OPEN_ENDED);
+ break;
+
+ case di_ambersand:
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+ fy_emit_putc(emit, wtype, '&');
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_INDENTATION);
+ break;
+
+ case di_star:
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+ fy_emit_putc(emit, wtype, '*');
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_INDENTATION);
+ break;
+ }
+}
+
+int fy_emit_increase_indent(struct fy_emitter *emit, int flags, int indent)
+{
+ if (indent < 0)
+ return (flags & DDNF_FLOW) ? fy_emit_indent(emit) : 0;
+
+ if (!(flags & DDNF_INDENTLESS))
+ return indent + fy_emit_indent(emit);
+
+ return indent;
+}
+
+void fy_emit_write_comment(struct fy_emitter *emit, int flags, int indent, const char *str, size_t len, struct fy_atom *handle)
+{
+ const char *s, *e, *sr;
+ int c, w;
+ bool breaks;
+
+ if (!str || !len)
+ return;
+
+ if (len == (size_t)-1)
+ len = strlen(str);
+
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+ indent = emit->column;
+
+ s = str;
+ e = str + len;
+
+ sr = s; /* start of normal output run */
+ breaks = false;
+ while (s < e && (c = fy_utf8_get(s, e - s, &w)) > 0) {
+
+ if (fy_is_lb_m(c, fy_atom_lb_mode(handle))) {
+
+ /* output run */
+ fy_emit_write(emit, fyewt_comment, sr, s - sr);
+ sr = s + w;
+ fy_emit_write_indent(emit, indent);
+ emit->flags |= FYEF_INDENTATION;
+ breaks = true;
+ } else {
+
+ if (breaks) {
+ fy_emit_write(emit, fyewt_comment, sr, s - sr);
+ sr = s;
+ fy_emit_write_indent(emit, indent);
+ }
+ emit->flags &= ~FYEF_INDENTATION;
+ breaks = false;
+ }
+
+ s += w;
+ }
+
+ /* dump what's remaining */
+ fy_emit_write(emit, fyewt_comment, sr, s - sr);
+
+ emit->flags |= (FYEF_WHITESPACE | FYEF_INDENTATION);
+}
+
+struct fy_atom *fy_emit_token_comment_handle(struct fy_emitter *emit, struct fy_token *fyt, enum fy_comment_placement placement)
+{
+ struct fy_atom *handle;
+
+ handle = fy_token_comment_handle(fyt, placement, false);
+ return handle && fy_atom_is_set(handle) ? handle : NULL;
+}
+
+void fy_emit_document_start_indicator(struct fy_emitter *emit)
+{
+ /* do not emit twice */
+ if (emit->flags & FYEF_HAD_DOCUMENT_START)
+ return;
+
+ /* do not try to emit if it's json mode */
+ if (fy_emit_is_json_mode(emit))
+ goto no_doc_emit;
+
+ /* output linebreak anyway */
+ if (emit->column)
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+
+ /* stripping doc indicators, do not emit */
+ if (emit->cfg.flags & FYECF_STRIP_DOC)
+ goto no_doc_emit;
+
+ /* ok, emit document start indicator */
+ fy_emit_puts(emit, fyewt_document_indicator, "---");
+ emit->flags &= ~FYEF_WHITESPACE;
+ emit->flags |= FYEF_HAD_DOCUMENT_START;
+ return;
+
+no_doc_emit:
+ emit->flags &= ~FYEF_HAD_DOCUMENT_START;
+}
+
+struct fy_token *fy_node_value_token(struct fy_node *fyn)
+{
+ struct fy_token *fyt;
+
+ if (!fyn)
+ return NULL;
+
+ switch (fyn->type) {
+ case FYNT_SCALAR:
+ fyt = fyn->scalar;
+ break;
+ case FYNT_SEQUENCE:
+ fyt = fyn->sequence_start;
+ break;
+ case FYNT_MAPPING:
+ fyt = fyn->mapping_start;
+ break;
+ default:
+ fyt = NULL;
+ break;
+ }
+
+ return fyt;
+}
+
+bool fy_emit_token_has_comment(struct fy_emitter *emit, struct fy_token *fyt, enum fy_comment_placement placement)
+{
+ return fy_emit_token_comment_handle(emit, fyt, placement) ? true : false;
+}
+
+bool fy_emit_node_has_comment(struct fy_emitter *emit, struct fy_node *fyn, enum fy_comment_placement placement)
+{
+ return fy_emit_token_has_comment(emit, fy_node_value_token(fyn), placement);
+}
+
+void fy_emit_token_comment(struct fy_emitter *emit, struct fy_token *fyt, int flags, int indent,
+ enum fy_comment_placement placement)
+{
+ struct fy_atom *handle;
+ char *text;
+ const char *t;
+ int len;
+
+ handle = fy_emit_token_comment_handle(emit, fyt, placement);
+ if (!handle)
+ return;
+
+ len = fy_atom_format_text_length(handle);
+ if (len < 0)
+ return;
+
+ text = FY_ALLOCA(len + 1);
+
+ if (placement == fycp_top || placement == fycp_bottom) {
+ fy_emit_write_indent(emit, indent);
+ emit->flags |= FYEF_WHITESPACE;
+ }
+
+ t = fy_atom_format_text(handle, text, len + 1);
+
+ fy_emit_write_comment(emit, flags, indent, t, len, handle);
+
+ emit->flags &= ~FYEF_INDENTATION;
+
+ if (placement == fycp_top || placement == fycp_bottom) {
+ fy_emit_write_indent(emit, indent);
+ emit->flags |= FYEF_WHITESPACE;
+ }
+}
+
+void fy_emit_node_comment(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent,
+ enum fy_comment_placement placement)
+{
+ struct fy_token *fyt;
+
+ if (!fy_emit_output_comments(emit) || (unsigned int)placement >= fycp_max)
+ return;
+
+ fyt = fy_node_value_token(fyn);
+ if (!fyt)
+ return;
+
+ fy_emit_token_comment(emit, fyt, flags, indent, placement);
+}
+
+void fy_emit_common_node_preamble(struct fy_emitter *emit,
+ struct fy_token *fyt_anchor,
+ struct fy_token *fyt_tag,
+ int flags, int indent)
+{
+ const char *anchor = NULL;
+ const char *tag = NULL;
+ const char *td_prefix __FY_DEBUG_UNUSED__;
+ const char *td_handle;
+ size_t td_prefix_size, td_handle_size;
+ size_t tag_len = 0, anchor_len = 0;
+ bool json_mode = false;
+
+ json_mode = fy_emit_is_json_mode(emit);
+
+ if (!json_mode) {
+ if (!(emit->cfg.flags & FYECF_STRIP_LABELS)) {
+ if (fyt_anchor)
+ anchor = fy_token_get_text(fyt_anchor, &anchor_len);
+ }
+
+ if (!(emit->cfg.flags & FYECF_STRIP_TAGS)) {
+ if (fyt_tag)
+ tag = fy_token_get_text(fyt_tag, &tag_len);
+ }
+
+ if (anchor) {
+ fy_emit_write_indicator(emit, di_ambersand, flags, indent, fyewt_anchor);
+ fy_emit_write(emit, fyewt_anchor, anchor, anchor_len);
+ }
+
+ if (tag) {
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+
+ td_handle = fy_tag_token_get_directive_handle(fyt_tag, &td_handle_size);
+ assert(td_handle);
+ td_prefix = fy_tag_token_get_directive_prefix(fyt_tag, &td_prefix_size);
+ assert(td_prefix);
+
+ if (!td_handle_size)
+ fy_emit_printf(emit, fyewt_tag, "!<%.*s>", (int)tag_len, tag);
+ else
+ fy_emit_printf(emit, fyewt_tag, "%.*s%.*s",
+ (int)td_handle_size, td_handle,
+ (int)(tag_len - td_prefix_size), tag + td_prefix_size);
+
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_INDENTATION);
+ }
+ }
+
+ /* content for root always starts on a new line */
+ if ((flags & DDNF_ROOT) && emit->column != 0 &&
+ !(emit->flags & FYEF_HAD_DOCUMENT_START)) {
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION;
+ }
+}
+
+void fy_emit_node_internal(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent, bool is_key)
+{
+ enum fy_node_type type;
+ struct fy_anchor *fya;
+ struct fy_token *fyt_anchor = NULL;
+
+ if (!(emit->cfg.flags & FYECF_STRIP_LABELS)) {
+ fya = fy_document_lookup_anchor_by_node(emit->fyd, fyn);
+ if (fya)
+ fyt_anchor = fya->anchor;
+ }
+
+ fy_emit_common_node_preamble(emit, fyt_anchor, fyn->tag, flags, indent);
+
+ type = fyn ? fyn->type : FYNT_SCALAR;
+
+ if (type != FYNT_SCALAR && (flags & DDNF_ROOT) && emit->column != 0) {
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION;
+ }
+
+ switch (type) {
+ case FYNT_SCALAR:
+ /* if we're pretty and root at column 0 (meaning it's a single scalar document) output --- */
+ if ((flags & DDNF_ROOT) && fy_emit_is_pretty_mode(emit) && !emit->column &&
+ !fy_emit_is_flow_mode(emit) && !(flags & DDNF_FLOW))
+ fy_emit_document_start_indicator(emit);
+ fy_emit_scalar(emit, fyn, flags, indent, is_key);
+ break;
+ case FYNT_SEQUENCE:
+ FYD_TOKEN_ERROR_CHECK(fyn->fyd, fyn->sequence_start, FYEM_INTERNAL,
+ !is_key || !fy_emit_is_json_mode(emit), err_out,
+ "JSON does not allow sequences as keys");
+ fy_emit_sequence(emit, fyn, flags, indent);
+ break;
+ case FYNT_MAPPING:
+ FYD_TOKEN_ERROR_CHECK(fyn->fyd, fyn->mapping_start, FYEM_INTERNAL,
+ !is_key || !fy_emit_is_json_mode(emit), err_out,
+ "JSON does not allow mappings as keys");
+ fy_emit_mapping(emit, fyn, flags, indent);
+ break;
+ }
+err_out:
+ /* nothing */
+ return;
+}
+
+void fy_emit_token_write_plain(struct fy_emitter *emit, struct fy_token *fyt, int flags, int indent)
+{
+ bool allow_breaks, should_indent, spaces, breaks;
+ int c;
+ enum fy_emitter_write_type wtype;
+ const char *str = NULL;
+ size_t len = 0;
+ struct fy_atom *atom;
+ struct fy_atom_iter iter;
+
+ /* null and not json mode */
+ if (!fyt && !fy_emit_is_json_mode(emit))
+ goto out;
+
+ wtype = (flags & DDNF_SIMPLE_SCALAR_KEY) ? fyewt_plain_scalar_key : fyewt_plain_scalar;
+
+ atom = fy_token_atom(fyt);
+
+ /* null and json mode */
+ if (fy_emit_is_json_mode(emit) && (!fyt || !atom || atom->size0)) {
+ fy_emit_puts(emit, wtype, "null");
+ goto out;
+ }
+
+ /* simple case first (90% of cases) */
+ str = fy_token_get_direct_output(fyt, &len);
+ if (str && fy_token_atom_style(fyt) == FYAS_PLAIN) {
+ fy_emit_write(emit, wtype, str, len);
+ goto out;
+ }
+
+ if (!atom)
+ goto out;
+
+ allow_breaks = !(flags & DDNF_SIMPLE) && !fy_emit_is_json_mode(emit) && !fy_emit_is_oneline(emit);
+
+ spaces = false;
+ breaks = false;
+
+ fy_atom_iter_start(atom, &iter);
+ fy_emit_accum_start(&emit->ea, emit->column, fy_token_atom_lb_mode(fyt));
+ while ((c = fy_atom_iter_utf8_get(&iter)) > 0) {
+
+ if (fy_is_ws(c)) {
+
+ should_indent = allow_breaks && !spaces &&
+ fy_emit_accum_column(&emit->ea) > fy_emit_width(emit);
+
+ if (should_indent && !fy_is_ws(fy_atom_iter_utf8_peek(&iter))) {
+ fy_emit_output_accum(emit, wtype, &emit->ea);
+ emit->flags &= ~FYEF_INDENTATION;
+ fy_emit_write_indent(emit, indent);
+ } else
+ fy_emit_accum_utf8_put(&emit->ea, c);
+
+ spaces = true;
+
+ } else if (fy_is_lb_m(c, fy_token_atom_lb_mode(fyt))) {
+
+ /* blergh */
+ if (!allow_breaks)
+ break;
+
+ /* output run */
+ if (!breaks) {
+ fy_emit_output_accum(emit, wtype, &emit->ea);
+ fy_emit_write_indent(emit, indent);
+ }
+
+ emit->flags &= ~FYEF_INDENTATION;
+ fy_emit_write_indent(emit, indent);
+
+ breaks = true;
+
+ } else {
+
+ if (breaks)
+ fy_emit_write_indent(emit, indent);
+
+ fy_emit_accum_utf8_put(&emit->ea, c);
+
+ emit->flags &= ~FYEF_INDENTATION;
+
+ spaces = false;
+ breaks = false;
+ }
+ }
+ fy_emit_output_accum(emit, wtype, &emit->ea);
+ fy_emit_accum_finish(&emit->ea);
+ fy_atom_iter_finish(&iter);
+
+out:
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_INDENTATION);
+}
+
+void fy_emit_token_write_alias(struct fy_emitter *emit, struct fy_token *fyt, int flags, int indent)
+{
+ const char *str = NULL;
+ size_t len = 0;
+ struct fy_atom_iter iter;
+ int c;
+
+ if (!fyt)
+ return;
+
+ fy_emit_write_indicator(emit, di_star, flags, indent, fyewt_alias);
+
+ /* try direct output first (99% of cases) */
+ str = fy_token_get_direct_output(fyt, &len);
+ if (str) {
+ fy_emit_write(emit, fyewt_alias, str, len);
+ return;
+ }
+
+ /* corner case, use iterator */
+ fy_atom_iter_start(fy_token_atom(fyt), &iter);
+ fy_emit_accum_start(&emit->ea, emit->column, fy_token_atom_lb_mode(fyt));
+ while ((c = fy_atom_iter_utf8_get(&iter)) > 0)
+ fy_emit_accum_utf8_put(&emit->ea, c);
+ fy_emit_output_accum(emit, fyewt_alias, &emit->ea);
+ fy_emit_accum_finish(&emit->ea);
+ fy_atom_iter_finish(&iter);
+}
+
+void fy_emit_token_write_quoted(struct fy_emitter *emit, struct fy_token *fyt, int flags, int indent, char qc)
+{
+ bool allow_breaks, spaces, breaks;
+ int c, i, w, digit;
+ enum fy_emitter_write_type wtype;
+ const char *str = NULL;
+ size_t len = 0;
+ bool should_indent, done_esc;
+ struct fy_atom *atom;
+ struct fy_atom_iter iter;
+ enum fy_atom_style target_style;
+ uint32_t hi_surrogate, lo_surrogate;
+ uint8_t non_utf8[4];
+ size_t non_utf8_len, k;
+
+ wtype = qc == '\'' ?
+ ((flags & DDNF_SIMPLE_SCALAR_KEY) ?
+ fyewt_single_quoted_scalar_key : fyewt_single_quoted_scalar) :
+ ((flags & DDNF_SIMPLE_SCALAR_KEY) ?
+ fyewt_double_quoted_scalar_key : fyewt_double_quoted_scalar);
+
+ fy_emit_write_indicator(emit,
+ qc == '\'' ? di_single_quote_start : di_double_quote_start,
+ flags, indent, wtype);
+
+ /* XXX check whether this is ever the case */
+ if (!fyt)
+ goto out;
+
+ /* note that if the original target style and the target differ
+ * we can note use direct output
+ */
+ target_style = qc == '"' ? FYAS_DOUBLE_QUOTED : FYAS_SINGLE_QUOTED;
+
+ /* simple case of direct output (large amount of cases) */
+ str = fy_token_get_direct_output(fyt, &len);
+ if (str && fy_token_atom_style(fyt) == target_style) {
+ fy_emit_write(emit, wtype, str, len);
+ goto out;
+ }
+
+ /* no atom? i.e. empty */
+ atom = fy_token_atom(fyt);
+ if (!atom)
+ goto out;
+
+ allow_breaks = !(flags & DDNF_SIMPLE) && !fy_emit_is_json_mode(emit) && !fy_emit_is_oneline(emit);
+
+ spaces = false;
+ breaks = false;
+
+ fy_atom_iter_start(atom, &iter);
+ fy_emit_accum_start(&emit->ea, emit->column, fy_token_atom_lb_mode(fyt));
+ for (;;) {
+ non_utf8_len = sizeof(non_utf8);
+ c = fy_atom_iter_utf8_quoted_get(&iter, &non_utf8_len, non_utf8);
+ if (c < 0)
+ break;
+
+ if (c == 0 && non_utf8_len > 0) {
+ for (k = 0; k < non_utf8_len; k++) {
+ c = (int)non_utf8[k] & 0xff;
+ fy_emit_accum_utf8_put(&emit->ea, '\\');
+ fy_emit_accum_utf8_put(&emit->ea, 'x');
+ digit = ((unsigned int)c >> 4) & 15;
+ fy_emit_accum_utf8_put(&emit->ea,
+ digit <= 9 ? ('0' + digit) : ('A' + digit - 10));
+ digit = (unsigned int)c & 15;
+ fy_emit_accum_utf8_put(&emit->ea,
+ digit <= 9 ? ('0' + digit) : ('A' + digit - 10));
+ }
+ continue;
+ }
+
+ if (fy_is_space(c) || (qc == '\'' && fy_is_ws(c))) {
+ should_indent = allow_breaks && !spaces &&
+ fy_emit_accum_column(&emit->ea) > fy_emit_width(emit);
+
+ if (should_indent &&
+ ((qc == '\'' && fy_is_ws(fy_atom_iter_utf8_peek(&iter))) ||
+ qc == '"')) {
+ fy_emit_output_accum(emit, wtype, &emit->ea);
+
+ if (qc == '"' && fy_is_ws(fy_atom_iter_utf8_peek(&iter)))
+ fy_emit_putc(emit, wtype, '\\');
+
+ emit->flags &= ~FYEF_INDENTATION;
+ fy_emit_write_indent(emit, indent);
+ } else
+ fy_emit_accum_utf8_put(&emit->ea, c);
+
+ spaces = true;
+ breaks = false;
+
+ } else if (qc == '\'' && fy_is_lb_m(c, fy_token_atom_lb_mode(fyt))) {
+
+ /* blergh */
+ if (!allow_breaks)
+ break;
+
+ /* output run */
+ if (!breaks) {
+ fy_emit_output_accum(emit, wtype, &emit->ea);
+ fy_emit_write_indent(emit, indent);
+ }
+
+ emit->flags &= ~FYEF_INDENTATION;
+ fy_emit_write_indent(emit, indent);
+
+ breaks = true;
+ } else {
+ /* output run */
+ if (breaks) {
+ fy_emit_output_accum(emit, wtype, &emit->ea);
+ fy_emit_write_indent(emit, indent);
+ }
+
+ /* escape */
+ if (qc == '\'' && c == '\'') {
+ fy_emit_accum_utf8_put(&emit->ea, '\'');
+ fy_emit_accum_utf8_put(&emit->ea, '\'');
+ } else if (qc == '"' &&
+ ((!fy_is_printq(c) || c == '"' || c == '\\') ||
+ (fy_emit_is_json_mode(emit) && !fy_is_json_unescaped(c)))) {
+
+ fy_emit_accum_utf8_put(&emit->ea, '\\');
+
+ /* common YAML and JSON escapes */
+ done_esc = false;
+ switch (c) {
+ case '\b':
+ fy_emit_accum_utf8_put(&emit->ea, 'b');
+ done_esc = true;
+ break;
+ case '\f':
+ fy_emit_accum_utf8_put(&emit->ea, 'f');
+ done_esc = true;
+ break;
+ case '\n':
+ fy_emit_accum_utf8_put(&emit->ea, 'n');
+ done_esc = true;
+ break;
+ case '\r':
+ fy_emit_accum_utf8_put(&emit->ea, 'r');
+ done_esc = true;
+ break;
+ case '\t':
+ fy_emit_accum_utf8_put(&emit->ea, 't');
+ done_esc = true;
+ break;
+ case '"':
+ fy_emit_accum_utf8_put(&emit->ea, '"');
+ done_esc = true;
+ break;
+ case '\\':
+ fy_emit_accum_utf8_put(&emit->ea, '\\');
+ done_esc = true;
+ break;
+ }
+
+ if (done_esc)
+ goto done;
+
+ if (!fy_emit_is_json_mode(emit)) {
+ switch (c) {
+ case '\0':
+ fy_emit_accum_utf8_put(&emit->ea, '0');
+ break;
+ case '\a':
+ fy_emit_accum_utf8_put(&emit->ea, 'a');
+ break;
+ case '\v':
+ fy_emit_accum_utf8_put(&emit->ea, 'v');
+ break;
+ case '\e':
+ fy_emit_accum_utf8_put(&emit->ea, 'e');
+ break;
+ case 0x85:
+ fy_emit_accum_utf8_put(&emit->ea, 'N');
+ break;
+ case 0xa0:
+ fy_emit_accum_utf8_put(&emit->ea, '_');
+ break;
+ case 0x2028:
+ fy_emit_accum_utf8_put(&emit->ea, 'L');
+ break;
+ case 0x2029:
+ fy_emit_accum_utf8_put(&emit->ea, 'P');
+ break;
+ default:
+ if ((unsigned int)c <= 0xff) {
+ fy_emit_accum_utf8_put(&emit->ea, 'x');
+ w = 2;
+ } else if ((unsigned int)c <= 0xffff) {
+ fy_emit_accum_utf8_put(&emit->ea, 'u');
+ w = 4;
+ } else if ((unsigned int)c <= 0xffffffff) {
+ fy_emit_accum_utf8_put(&emit->ea, 'U');
+ w = 8;
+ }
+
+ for (i = w - 1; i >= 0; i--) {
+ digit = ((unsigned int)c >> (i * 4)) & 15;
+ fy_emit_accum_utf8_put(&emit->ea,
+ digit <= 9 ? ('0' + digit) : ('A' + digit - 10));
+ }
+ break;
+ }
+
+ } else {
+ /* JSON escapes all others in \uXXXX and \uXXXX\uXXXX */
+ w = 4;
+ if ((unsigned int)c <= 0xffff) {
+ fy_emit_accum_utf8_put(&emit->ea, 'u');
+ for (i = w - 1; i >= 0; i--) {
+ digit = ((unsigned int)c >> (i * 4)) & 15;
+ fy_emit_accum_utf8_put(&emit->ea,
+ digit <= 9 ? ('0' + digit) : ('A' + digit - 10));
+ }
+ } else {
+ hi_surrogate = 0xd800 | ((((c >> 16) & 0x1f) - 1) << 6) | ((c >> 10) & 0x3f);
+ lo_surrogate = 0xdc00 | (c & 0x3ff);
+
+ fy_emit_accum_utf8_put(&emit->ea, 'u');
+ for (i = w - 1; i >= 0; i--) {
+ digit = ((unsigned int)hi_surrogate >> (i * 4)) & 15;
+ fy_emit_accum_utf8_put(&emit->ea,
+ digit <= 9 ? ('0' + digit) : ('A' + digit - 10));
+ }
+
+ fy_emit_accum_utf8_put(&emit->ea, '\\');
+ fy_emit_accum_utf8_put(&emit->ea, 'u');
+ for (i = w - 1; i >= 0; i--) {
+ digit = ((unsigned int)lo_surrogate >> (i * 4)) & 15;
+ fy_emit_accum_utf8_put(&emit->ea,
+ digit <= 9 ? ('0' + digit) : ('A' + digit - 10));
+ }
+ }
+ }
+ } else
+ fy_emit_accum_utf8_put(&emit->ea, c);
+done:
+ emit->flags &= ~FYEF_INDENTATION;
+ spaces = false;
+ breaks = false;
+ }
+ }
+ fy_emit_output_accum(emit, wtype, &emit->ea);
+ fy_emit_accum_finish(&emit->ea);
+ fy_atom_iter_finish(&iter);
+
+out:
+ fy_emit_write_indicator(emit,
+ qc == '\'' ? di_single_quote_end : di_double_quote_end,
+ flags, indent, wtype);
+}
+
+bool fy_emit_token_write_block_hints(struct fy_emitter *emit, struct fy_token *fyt, int flags, int indent, char *chompp)
+{
+ char chomp = '\0';
+ bool explicit_chomp = false;
+ struct fy_atom *atom;
+
+ atom = fy_token_atom(fyt);
+ if (!atom) {
+ emit->flags &= ~FYEF_OPEN_ENDED;
+ chomp = '-';
+ goto out;
+ }
+
+ if (atom->starts_with_ws || atom->starts_with_lb) {
+ fy_emit_putc(emit, fyewt_indicator, '0' + fy_emit_indent(emit));
+ explicit_chomp = true;
+ }
+
+ if (!atom->ends_with_lb) {
+ emit->flags &= ~FYEF_OPEN_ENDED;
+ chomp = '-';
+ goto out;
+ }
+
+ if (atom->trailing_lb) {
+ emit->flags |= FYEF_OPEN_ENDED;
+ chomp = '+';
+ goto out;
+ }
+ emit->flags &= ~FYEF_OPEN_ENDED;
+
+out:
+ if (chomp)
+ fy_emit_putc(emit, fyewt_indicator, chomp);
+ *chompp = chomp;
+ return explicit_chomp;
+}
+
+void fy_emit_token_write_literal(struct fy_emitter *emit, struct fy_token *fyt, int flags, int indent)
+{
+ bool breaks;
+ int c;
+ char chomp;
+ struct fy_atom *atom;
+ struct fy_atom_iter iter;
+
+ fy_emit_write_indicator(emit, di_bar, flags, indent, fyewt_indicator);
+
+ fy_emit_token_write_block_hints(emit, fyt, flags, indent, &chomp);
+ if (flags & DDNF_ROOT)
+ indent += fy_emit_indent(emit);
+
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags |= FYEF_WHITESPACE | FYEF_INDENTATION;
+
+ atom = fy_token_atom(fyt);
+ if (!atom)
+ goto out;
+
+ breaks = true;
+
+ fy_atom_iter_start(atom, &iter);
+ fy_emit_accum_start(&emit->ea, emit->column, fy_token_atom_lb_mode(fyt));
+ while ((c = fy_atom_iter_utf8_get(&iter)) > 0) {
+
+ if (breaks) {
+ fy_emit_write_indent(emit, indent);
+ breaks = false;
+ }
+
+ if (fy_is_lb_m(c, fy_token_atom_lb_mode(fyt))) {
+ fy_emit_output_accum(emit, fyewt_literal_scalar, &emit->ea);
+ emit->flags &= ~FYEF_INDENTATION;
+ breaks = true;
+ } else
+ fy_emit_accum_utf8_put(&emit->ea, c);
+ }
+ fy_emit_output_accum(emit, fyewt_literal_scalar, &emit->ea);
+ fy_emit_accum_finish(&emit->ea);
+ fy_atom_iter_finish(&iter);
+
+out:
+ emit->flags &= ~FYEF_INDENTATION;
+}
+
+void fy_emit_token_write_folded(struct fy_emitter *emit, struct fy_token *fyt, int flags, int indent)
+{
+ bool leading_spaces, breaks;
+ int c, nrbreaks, nrbreakslim;
+ char chomp;
+ struct fy_atom *atom;
+ struct fy_atom_iter iter;
+
+ fy_emit_write_indicator(emit, di_greater, flags, indent, fyewt_indicator);
+
+ fy_emit_token_write_block_hints(emit, fyt, flags, indent, &chomp);
+ if (flags & DDNF_ROOT)
+ indent += fy_emit_indent(emit);
+
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags |= FYEF_WHITESPACE | FYEF_INDENTATION;
+
+ atom = fy_token_atom(fyt);
+ if (!atom)
+ return;
+
+ breaks = true;
+ leading_spaces = true;
+
+ fy_atom_iter_start(atom, &iter);
+ fy_emit_accum_start(&emit->ea, emit->column, fy_token_atom_lb_mode(fyt));
+ while ((c = fy_atom_iter_utf8_get(&iter)) > 0) {
+
+ if (fy_is_lb_m(c, fy_token_atom_lb_mode(fyt))) {
+
+ /* output run */
+ if (!fy_emit_accum_empty(&emit->ea)) {
+ fy_emit_output_accum(emit, fyewt_literal_scalar, &emit->ea);
+ /* do not output a newline (indent) if at the end or
+ * this is a leading spaces line */
+ if (!fy_is_z(fy_atom_iter_utf8_peek(&iter)) && !leading_spaces)
+ fy_emit_write_indent(emit, indent);
+ }
+
+ /* count the number of consecutive breaks */
+ nrbreaks = 1;
+ while (fy_is_lb_m((c = fy_atom_iter_utf8_peek(&iter)), fy_token_atom_lb_mode(fyt))) {
+ nrbreaks++;
+ (void)fy_atom_iter_utf8_get(&iter);
+ }
+
+ /* NOTE: Because the number of indents is tricky
+ * if it's a non blank, non end, it's the number of breaks
+ * if it's a blank, it's the number of breaks minus 1
+ * if it's the end, it's the number of breaks minus 2
+ */
+ nrbreakslim = fy_is_z(c) ? 2 : fy_is_blank(c) ? 1 : 0;
+ while (nrbreaks-- > nrbreakslim) {
+ emit->flags &= ~FYEF_INDENTATION;
+ fy_emit_write_indent(emit, indent);
+ }
+
+ breaks = true;
+
+ } else {
+
+ /* if we had a break, output an indent */
+ if (breaks) {
+ fy_emit_write_indent(emit, indent);
+
+ /* if this line starts with whitespace we need to know */
+ leading_spaces = fy_is_ws(c);
+ }
+
+ if (!breaks && fy_is_space(c) &&
+ !fy_is_space(fy_atom_iter_utf8_peek(&iter)) &&
+ fy_emit_accum_column(&emit->ea) > fy_emit_width(emit)) {
+ fy_emit_output_accum(emit, fyewt_folded_scalar, &emit->ea);
+ emit->flags &= ~FYEF_INDENTATION;
+ fy_emit_write_indent(emit, indent);
+ } else
+ fy_emit_accum_utf8_put(&emit->ea, c);
+
+ breaks = false;
+ }
+ }
+ fy_emit_output_accum(emit, fyewt_folded_scalar, &emit->ea);
+ fy_emit_accum_finish(&emit->ea);
+ fy_atom_iter_finish(&iter);
+}
+
+static enum fy_node_style
+fy_emit_token_scalar_style(struct fy_emitter *emit, struct fy_token *fyt,
+ int flags, int indent, enum fy_node_style style,
+ struct fy_token *fyt_tag)
+{
+ const char *value = NULL;
+ size_t len = 0;
+ bool json, flow, is_json_plain;
+ struct fy_atom *atom;
+ int aflags = -1;
+ const char *tag;
+ size_t tag_len;
+
+ atom = fy_token_atom(fyt);
+
+ flow = fy_emit_is_flow_mode(emit) || (flags & DDNF_FLOW);
+
+ /* check if style is allowed (i.e. no block styles in flow context) */
+ if (flow && (style == FYNS_LITERAL || style == FYNS_FOLDED))
+ style = FYNS_ANY;
+
+ json = fy_emit_is_json_mode(emit);
+
+ /* literal in JSON mode is output as quoted */
+ if (json && (style == FYNS_LITERAL || style == FYNS_FOLDED))
+ return FYNS_DOUBLE_QUOTED;
+
+ /* is this a plain json atom? */
+ is_json_plain = (json || emit->source_json || fy_emit_is_dejson_mode(emit)) &&
+ (!atom || atom->size0 ||
+ !fy_atom_strcmp(atom, "false") ||
+ !fy_atom_strcmp(atom, "true") ||
+ !fy_atom_strcmp(atom, "null") ||
+ fy_atom_is_number(atom));
+
+ if (is_json_plain) {
+ tag = fy_token_get_text(fyt_tag, &tag_len);
+
+ /* XXX hardcoded string tag resultion */
+ if (tag && tag_len &&
+ ((tag_len == 1 && *tag == '!') ||
+ (tag_len == 21 && !memcmp(tag, "tag:yaml.org,2002:str", 21))))
+ return FYNS_DOUBLE_QUOTED;
+ }
+
+ /* JSON NULL, but with plain style */
+ if (json && (style == FYNS_PLAIN || style == FYNS_ANY) && (!atom || (is_json_plain && !atom->size0)))
+ return FYNS_PLAIN;
+
+ if (json)
+ return FYNS_DOUBLE_QUOTED;
+
+ aflags = fy_token_text_analyze(fyt);
+
+ if (flow && (style == FYNS_ANY || style == FYNS_LITERAL || style == FYNS_FOLDED)) {
+
+ if (fyt && !value)
+ value = fy_token_get_text(fyt, &len);
+
+ /* if there's a linebreak, use double quoted style */
+ if (fy_find_any_lb(value, len)) {
+ style = FYNS_DOUBLE_QUOTED;
+ goto out;
+ }
+
+ /* check if there's a non printable */
+ if (!fy_find_non_print(value, len)) {
+ style = FYNS_SINGLE_QUOTED;
+ goto out;
+ }
+
+ /* anything not empty is double quoted here */
+ style = !(aflags & FYTTAF_EMPTY) ? FYNS_PLAIN : FYNS_DOUBLE_QUOTED;
+ }
+
+ /* try to pretify */
+ if (!flow && fy_emit_is_pretty_mode(emit) &&
+ (style == FYNS_ANY || style == FYNS_DOUBLE_QUOTED || style == FYNS_SINGLE_QUOTED)) {
+
+ /* any original style can be a plain, but contains linebreaks, do a literal */
+ if ((aflags & (FYTTAF_CAN_BE_PLAIN | FYTTAF_HAS_LB)) == (FYTTAF_CAN_BE_PLAIN | FYTTAF_HAS_LB)) {
+ style = FYNS_LITERAL;
+ goto out;
+ }
+
+ /* any style, can be just a plain, just make it so */
+ if (style == FYNS_ANY && (aflags & (FYTTAF_CAN_BE_PLAIN | FYTTAF_HAS_LB)) == FYTTAF_CAN_BE_PLAIN) {
+ style = FYNS_PLAIN;
+ goto out;
+ }
+
+ }
+
+ if (!flow && emit->source_json && fy_emit_is_dejson_mode(emit)) {
+ if (is_json_plain || (aflags & (FYTTAF_CAN_BE_PLAIN | FYTTAF_HAS_LB)) == FYTTAF_CAN_BE_PLAIN) {
+ style = FYNS_PLAIN;
+ goto out;
+ }
+ }
+
+out:
+ if (style == FYNS_ANY) {
+ if (fyt)
+ value = fy_token_get_text(fyt, &len);
+
+ style = (aflags & FYTTAF_CAN_BE_PLAIN) ?
+ FYNS_PLAIN : FYNS_DOUBLE_QUOTED;
+ }
+
+ /* special handling for plains on start of line */
+ if ((aflags & FYTTAF_QUOTE_AT_0) && indent == 0 && style == FYNS_PLAIN)
+ style = FYNS_DOUBLE_QUOTED;
+
+ return style;
+}
+
+void fy_emit_token_scalar(struct fy_emitter *emit, struct fy_token *fyt, int flags, int indent,
+ enum fy_node_style style, struct fy_token *fyt_tag)
+{
+ assert(style != FYNS_FLOW && style != FYNS_BLOCK);
+
+ indent = fy_emit_increase_indent(emit, flags, indent);
+
+ if (!fy_emit_whitespace(emit))
+ fy_emit_write_ws(emit);
+
+ style = fy_emit_token_scalar_style(emit, fyt, flags, indent, style, fyt_tag);
+
+ switch (style) {
+ case FYNS_ALIAS:
+ fy_emit_token_write_alias(emit, fyt, flags, indent);
+ break;
+ case FYNS_PLAIN:
+ fy_emit_token_write_plain(emit, fyt, flags, indent);
+ break;
+ case FYNS_DOUBLE_QUOTED:
+ fy_emit_token_write_quoted(emit, fyt, flags, indent, '"');
+ break;
+ case FYNS_SINGLE_QUOTED:
+ fy_emit_token_write_quoted(emit, fyt, flags, indent, '\'');
+ break;
+ case FYNS_LITERAL:
+ fy_emit_token_write_literal(emit, fyt, flags, indent);
+ break;
+ case FYNS_FOLDED:
+ fy_emit_token_write_folded(emit, fyt, flags, indent);
+ break;
+ default:
+ break;
+ }
+}
+
+void fy_emit_scalar(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent, bool is_key)
+{
+ enum fy_node_style style;
+
+ /* default style */
+ style = fyn ? fyn->style : FYNS_ANY;
+
+ /* all JSON keys are double quoted */
+ if (fy_emit_is_json_mode(emit) && is_key)
+ style = FYNS_DOUBLE_QUOTED;
+
+ fy_emit_token_scalar(emit,
+ fyn ? fyn->scalar : NULL,
+ flags, indent,
+ style, fyn->tag);
+}
+
+static void fy_emit_sequence_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc)
+{
+ bool json = fy_emit_is_json_mode(emit);
+ bool oneline = fy_emit_is_oneline(emit);
+ bool was_flow = sc->flow;
+
+ sc->old_indent = sc->indent;
+ if (!json) {
+ if (fy_emit_is_block_mode(emit))
+ sc->flow = sc->empty;
+ else
+ sc->flow = fy_emit_is_flow_mode(emit) || emit->flow_level || sc->flow_token || sc->empty;
+
+ if (sc->flow) {
+ if (!emit->flow_level) {
+ sc->indent = fy_emit_increase_indent(emit, sc->flags, sc->indent);
+ sc->old_indent = sc->indent;
+ }
+
+ sc->flags = (sc->flags | DDNF_FLOW) | (sc->flags & ~DDNF_INDENTLESS);
+ fy_emit_write_indicator(emit, di_left_bracket, sc->flags, sc->indent, fyewt_indicator);
+ } else {
+ sc->flags = (sc->flags & ~DDNF_FLOW);
+ }
+ } else {
+ sc->flags = (sc->flags | DDNF_FLOW) | (sc->flags & ~DDNF_INDENTLESS);
+ fy_emit_write_indicator(emit, di_left_bracket, sc->flags, sc->indent, fyewt_indicator);
+ }
+
+ if (!oneline) {
+ if (was_flow || (sc->flags & (DDNF_ROOT | DDNF_SEQ)))
+ sc->indent = fy_emit_increase_indent(emit, sc->flags, sc->indent);
+ }
+
+ sc->flags &= ~DDNF_ROOT;
+}
+
+static void fy_emit_sequence_epilog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc)
+{
+ if (sc->flow || fy_emit_is_json_mode(emit)) {
+ if (!fy_emit_is_oneline(emit) && !sc->empty)
+ fy_emit_write_indent(emit, sc->old_indent);
+ fy_emit_write_indicator(emit, di_right_bracket, sc->flags, sc->old_indent, fyewt_indicator);
+ }
+}
+
+static void fy_emit_sequence_item_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc,
+ struct fy_token *fyt_value)
+{
+ int tmp_indent;
+
+ sc->flags |= DDNF_SEQ;
+
+ if (!fy_emit_is_oneline(emit))
+ fy_emit_write_indent(emit, sc->indent);
+
+ if (!sc->flow && !fy_emit_is_json_mode(emit))
+ fy_emit_write_indicator(emit, di_dash, sc->flags, sc->indent, fyewt_indicator);
+
+ tmp_indent = sc->indent;
+ if (fy_emit_token_has_comment(emit, fyt_value, fycp_top)) {
+ if (!sc->flow && !fy_emit_is_json_mode(emit))
+ tmp_indent = fy_emit_increase_indent(emit, sc->flags, sc->indent);
+ fy_emit_token_comment(emit, fyt_value, sc->flags, tmp_indent, fycp_top);
+ }
+}
+
+static void fy_emit_sequence_item_epilog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc,
+ bool last, struct fy_token *fyt_value)
+{
+ if ((sc->flow || fy_emit_is_json_mode(emit)) && !last)
+ fy_emit_write_indicator(emit, di_comma, sc->flags, sc->indent, fyewt_indicator);
+
+ fy_emit_token_comment(emit, fyt_value, sc->flags, sc->indent, fycp_right);
+
+ if (last && (sc->flow || fy_emit_is_json_mode(emit)) && !fy_emit_is_oneline(emit) && !sc->empty)
+ fy_emit_write_indent(emit, sc->old_indent);
+
+ sc->flags &= ~DDNF_SEQ;
+}
+
+void fy_emit_sequence(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent)
+{
+ struct fy_node *fyni, *fynin;
+ struct fy_token *fyt_value;
+ bool last;
+ struct fy_emit_save_ctx sct, *sc = &sct;
+
+ memset(sc, 0, sizeof(*sc));
+
+ sc->flags = flags;
+ sc->indent = indent;
+ sc->empty = fy_node_list_empty(&fyn->sequence);
+ sc->flow_token = fyn->style == FYNS_FLOW;
+ sc->flow = !!(flags & DDNF_FLOW);
+ sc->old_indent = sc->indent;
+
+ fy_emit_sequence_prolog(emit, sc);
+
+ for (fyni = fy_node_list_head(&fyn->sequence); fyni; fyni = fynin) {
+
+ fynin = fy_node_next(&fyn->sequence, fyni);
+ last = !fynin;
+ fyt_value = fy_node_value_token(fyni);
+
+ fy_emit_sequence_item_prolog(emit, sc, fyt_value);
+ fy_emit_node_internal(emit, fyni, (sc->flags & ~DDNF_ROOT), sc->indent, false);
+ fy_emit_sequence_item_epilog(emit, sc, last, fyt_value);
+ }
+
+ fy_emit_sequence_epilog(emit, sc);
+}
+
+static void fy_emit_mapping_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc)
+{
+ bool json = fy_emit_is_json_mode(emit);
+ bool oneline = fy_emit_is_oneline(emit);
+
+ sc->old_indent = sc->indent;
+ if (!json) {
+ if (fy_emit_is_block_mode(emit))
+ sc->flow = sc->empty;
+ else
+ sc->flow = fy_emit_is_flow_mode(emit) || emit->flow_level || sc->flow_token || sc->empty;
+
+ if (sc->flow) {
+ if (!emit->flow_level) {
+ sc->indent = fy_emit_increase_indent(emit, sc->flags, sc->indent);
+ sc->old_indent = sc->indent;
+ }
+
+ sc->flags = (sc->flags | DDNF_FLOW) | (sc->flags & ~DDNF_INDENTLESS);
+ fy_emit_write_indicator(emit, di_left_brace, sc->flags, sc->indent, fyewt_indicator);
+ } else {
+ sc->flags &= ~(DDNF_FLOW | DDNF_INDENTLESS);
+ }
+ } else {
+ sc->flags = (sc->flags | DDNF_FLOW) | (sc->flags & ~DDNF_INDENTLESS);
+ fy_emit_write_indicator(emit, di_left_brace, sc->flags, sc->indent, fyewt_indicator);
+ }
+
+ if (!oneline && !sc->empty)
+ sc->indent = fy_emit_increase_indent(emit, sc->flags, sc->indent);
+
+ sc->flags &= ~DDNF_ROOT;
+}
+
+static void fy_emit_mapping_epilog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc)
+{
+ if (sc->flow || fy_emit_is_json_mode(emit)) {
+ if (!fy_emit_is_oneline(emit) && !sc->empty)
+ fy_emit_write_indent(emit, sc->old_indent);
+ fy_emit_write_indicator(emit, di_right_brace, sc->flags, sc->old_indent, fyewt_indicator);
+ }
+}
+
+static void fy_emit_mapping_key_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc,
+ struct fy_token *fyt_key, bool simple_key)
+{
+ sc->flags = DDNF_MAP | (sc->flags & DDNF_FLOW);
+
+ if (simple_key) {
+ sc->flags |= DDNF_SIMPLE;
+ if (fyt_key && fyt_key->type == FYTT_SCALAR)
+ sc->flags |= DDNF_SIMPLE_SCALAR_KEY;
+ } else {
+ /* do not emit the ? in flow modes at all */
+ if (fy_emit_is_flow_mode(emit))
+ sc->flags |= DDNF_SIMPLE;
+ }
+
+ if (!fy_emit_is_oneline(emit))
+ fy_emit_write_indent(emit, sc->indent);
+
+ /* complex? */
+ if (!(sc->flags & DDNF_SIMPLE))
+ fy_emit_write_indicator(emit, di_question_mark, sc->flags, sc->indent, fyewt_indicator);
+}
+
+static void fy_emit_mapping_key_epilog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc,
+ struct fy_token *fyt_key)
+{
+ int tmp_indent;
+
+ /* if the key is an alias, always output an extra whitespace */
+ if (fyt_key && fyt_key->type == FYTT_ALIAS)
+ fy_emit_write_ws(emit);
+
+ sc->flags &= ~DDNF_MAP;
+
+ fy_emit_write_indicator(emit, di_colon, sc->flags, sc->indent, fyewt_indicator);
+
+ tmp_indent = sc->indent;
+ if (fy_emit_token_has_comment(emit, fyt_key, fycp_right)) {
+
+ if (!sc->flow && !fy_emit_is_json_mode(emit))
+ tmp_indent = fy_emit_increase_indent(emit, sc->flags, sc->indent);
+
+ fy_emit_token_comment(emit, fyt_key, sc->flags, tmp_indent, fycp_right);
+ fy_emit_write_indent(emit, tmp_indent);
+ }
+
+ sc->flags = DDNF_MAP | (sc->flags & DDNF_FLOW);
+}
+
+static void fy_emit_mapping_value_prolog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc,
+ struct fy_token *fyt_value)
+{
+ /* nothing */
+}
+
+static void fy_emit_mapping_value_epilog(struct fy_emitter *emit, struct fy_emit_save_ctx *sc,
+ bool last, struct fy_token *fyt_value)
+{
+ if ((sc->flow || fy_emit_is_json_mode(emit)) && !last)
+ fy_emit_write_indicator(emit, di_comma, sc->flags, sc->indent, fyewt_indicator);
+
+ fy_emit_token_comment(emit, fyt_value, sc->flags, sc->indent, fycp_right);
+
+ if (last && (sc->flow || fy_emit_is_json_mode(emit)) && !fy_emit_is_oneline(emit) && !sc->empty)
+ fy_emit_write_indent(emit, sc->old_indent);
+
+ sc->flags &= ~DDNF_MAP;
+}
+
+void fy_emit_mapping(struct fy_emitter *emit, struct fy_node *fyn, int flags, int indent)
+{
+ struct fy_node_pair *fynp, *fynpn, **fynpp = NULL;
+ struct fy_token *fyt_key, *fyt_value;
+ bool last, simple_key, used_malloc = false;
+ int aflags, i, count;
+ struct fy_emit_save_ctx sct, *sc = &sct;
+
+ memset(sc, 0, sizeof(*sc));
+
+ sc->flags = flags;
+ sc->indent = indent;
+ sc->empty = fy_node_pair_list_empty(&fyn->mapping);
+ sc->flow_token = fyn->style == FYNS_FLOW;
+ sc->flow = !!(flags & DDNF_FLOW);
+ sc->old_indent = sc->indent;
+
+ fy_emit_mapping_prolog(emit, sc);
+
+ if (!(emit->cfg.flags & (FYECF_SORT_KEYS | FYECF_STRIP_EMPTY_KV))) {
+ fynp = fy_node_pair_list_head(&fyn->mapping);
+ fynpp = NULL;
+ } else {
+ count = fy_node_mapping_item_count(fyn);
+
+ /* heuristic, avoid allocation for small maps */
+ if (count > 64) {
+ fynpp = malloc((count + 1) * sizeof(*fynpp));
+ fyd_error_check(fyn->fyd, fynpp, err_out,
+ "malloc() failed");
+ used_malloc = true;
+ } else
+ fynpp = FY_ALLOCA((count + 1) * sizeof(*fynpp));
+
+ /* fill (removing empty KVs) */
+ i = 0;
+ for (fynp = fy_node_pair_list_head(&fyn->mapping); fynp;
+ fynp = fy_node_pair_next(&fyn->mapping, fynp)) {
+
+ /* strip key/value pair from the output if it's empty */
+ if ((emit->cfg.flags & FYECF_STRIP_EMPTY_KV) && fy_node_is_empty(fynp->value))
+ continue;
+
+ fynpp[i++] = fynp;
+ }
+ count = i;
+ fynpp[count] = NULL;
+
+ /* sort the keys */
+ if (emit->cfg.flags & FYECF_SORT_KEYS)
+ fy_node_mapping_perform_sort(fyn, NULL, NULL, fynpp, count);
+
+ i = 0;
+ fynp = fynpp[i];
+ }
+
+ for (; fynp; fynp = fynpn) {
+
+ if (!fynpp)
+ fynpn = fy_node_pair_next(&fyn->mapping, fynp);
+ else
+ fynpn = fynpp[++i];
+
+ last = !fynpn;
+ fyt_key = fy_node_value_token(fynp->key);
+ fyt_value = fy_node_value_token(fynp->value);
+
+ FYD_NODE_ERROR_CHECK(fynp->fyd, fynp->key, FYEM_INTERNAL,
+ !fy_emit_is_json_mode(emit) ||
+ (fynp->key && fynp->key->type == FYNT_SCALAR),
+ err_out, "Non scalar keys are not allowed in JSON emit mode");
+
+ simple_key = false;
+ if (fynp->key) {
+ switch (fynp->key->type) {
+ case FYNT_SCALAR:
+ aflags = fy_token_text_analyze(fynp->key->scalar);
+ simple_key = fy_emit_is_json_mode(emit) ||
+ !!(aflags & FYTTAF_CAN_BE_SIMPLE_KEY);
+ break;
+ case FYNT_SEQUENCE:
+ simple_key = fy_node_list_empty(&fynp->key->sequence);
+ break;
+ case FYNT_MAPPING:
+ simple_key = fy_node_pair_list_empty(&fynp->key->mapping);
+ break;
+ }
+ }
+
+ fy_emit_mapping_key_prolog(emit, sc, fyt_key, simple_key);
+ if (fynp->key)
+ fy_emit_node_internal(emit, fynp->key, (sc->flags & ~DDNF_ROOT), sc->indent, true);
+ fy_emit_mapping_key_epilog(emit, sc, fyt_key);
+
+ fy_emit_mapping_value_prolog(emit, sc, fyt_value);
+ if (fynp->value)
+ fy_emit_node_internal(emit, fynp->value, (sc->flags & ~DDNF_ROOT), sc->indent, false);
+ fy_emit_mapping_value_epilog(emit, sc, last, fyt_value);
+ }
+
+ if (fynpp && used_malloc)
+ free(fynpp);
+
+ fy_emit_mapping_epilog(emit, sc);
+
+err_out:
+ return;
+}
+
+int fy_emit_common_document_start(struct fy_emitter *emit,
+ struct fy_document_state *fyds,
+ bool root_tag_or_anchor)
+{
+ struct fy_token *fyt_chk;
+ const char *td_handle, *td_prefix;
+ size_t td_handle_size, td_prefix_size;
+ enum fy_emitter_cfg_flags flags = emit->cfg.flags;
+ enum fy_emitter_cfg_flags vd_flags = flags & FYECF_VERSION_DIR(FYECF_VERSION_DIR_MASK);
+ enum fy_emitter_cfg_flags td_flags = flags & FYECF_TAG_DIR(FYECF_TAG_DIR_MASK);
+ enum fy_emitter_cfg_flags dsm_flags = flags & FYECF_DOC_START_MARK(FYECF_DOC_START_MARK_MASK);
+ bool vd, td, dsm;
+ bool had_non_default_tag = false;
+
+ if (!emit || !fyds || emit->fyds)
+ return -1;
+
+ emit->fyds = fyds;
+
+ vd = ((vd_flags == FYECF_VERSION_DIR_AUTO && fyds->version_explicit) ||
+ vd_flags == FYECF_VERSION_DIR_ON) &&
+ !(emit->cfg.flags & FYECF_STRIP_DOC);
+ td = ((td_flags == FYECF_TAG_DIR_AUTO && fyds->tags_explicit) ||
+ td_flags == FYECF_TAG_DIR_ON) &&
+ !(emit->cfg.flags & FYECF_STRIP_DOC);
+
+ /* if either a version or directive tags exist, and no previous
+ * explicit document end existed, output one now
+ */
+ if (!fy_emit_is_json_mode(emit) && (vd || td) && !(emit->flags & FYEF_HAD_DOCUMENT_END)) {
+ if (emit->column)
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ if (!(emit->cfg.flags & FYECF_STRIP_DOC)) {
+ fy_emit_puts(emit, fyewt_document_indicator, "...");
+ emit->flags &= ~FYEF_WHITESPACE;
+ emit->flags |= FYEF_HAD_DOCUMENT_END;
+ }
+ }
+
+ if (!fy_emit_is_json_mode(emit) && vd) {
+ if (emit->column)
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ fy_emit_printf(emit, fyewt_version_directive, "%%YAML %d.%d",
+ fyds->version.major, fyds->version.minor);
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION;
+ }
+
+ if (!fy_emit_is_json_mode(emit) && td) {
+
+ for (fyt_chk = fy_token_list_first(&fyds->fyt_td); fyt_chk; fyt_chk = fy_token_next(&fyds->fyt_td, fyt_chk)) {
+
+ td_handle = fy_tag_directive_token_handle(fyt_chk, &td_handle_size);
+ td_prefix = fy_tag_directive_token_prefix(fyt_chk, &td_prefix_size);
+ assert(td_handle && td_prefix);
+
+ if (fy_tag_is_default_internal(td_handle, td_handle_size, td_prefix, td_prefix_size))
+ continue;
+
+ had_non_default_tag = true;
+
+ if (emit->column)
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ fy_emit_printf(emit, fyewt_tag_directive, "%%TAG %.*s %.*s",
+ (int)td_handle_size, td_handle,
+ (int)td_prefix_size, td_prefix);
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION;
+ }
+ }
+
+ /* always output document start indicator:
+ * - was explicit
+ * - document has tags
+ * - document has an explicit version
+ * - root exists & has a tag or an anchor
+ */
+ dsm = (dsm_flags == FYECF_DOC_START_MARK_AUTO &&
+ (!fyds->start_implicit ||
+ fyds->tags_explicit || fyds->version_explicit ||
+ had_non_default_tag)) ||
+ dsm_flags == FYECF_DOC_START_MARK_ON;
+
+ /* if there was previous output without document end */
+ if (!dsm && (emit->flags & FYEF_HAD_DOCUMENT_OUTPUT) &&
+ !(emit->flags & FYEF_HAD_DOCUMENT_END))
+ dsm = true;
+
+ /* output document start indicator if we should */
+ if (dsm)
+ fy_emit_document_start_indicator(emit);
+
+ /* clear that in any case */
+ emit->flags &= ~FYEF_HAD_DOCUMENT_END;
+
+ return 0;
+}
+
+int fy_emit_document_start(struct fy_emitter *emit, struct fy_document *fyd,
+ struct fy_node *fyn_root)
+{
+ struct fy_node *root;
+ bool root_tag_or_anchor;
+ int ret;
+
+ if (!emit || !fyd || !fyd->fyds)
+ return -1;
+
+ root = fyn_root ? fyn_root : fy_document_root(fyd);
+
+ root_tag_or_anchor = root && (root->tag || fy_document_lookup_anchor_by_node(fyd, root));
+
+ ret = fy_emit_common_document_start(emit, fyd->fyds, root_tag_or_anchor);
+ if (ret)
+ return ret;
+
+ emit->fyd = fyd;
+
+ return 0;
+}
+
+int fy_emit_common_document_end(struct fy_emitter *emit, bool override_state, bool implicit_override)
+{
+ const struct fy_document_state *fyds;
+ enum fy_emitter_cfg_flags flags = emit->cfg.flags;
+ enum fy_emitter_cfg_flags dem_flags = flags & FYECF_DOC_END_MARK(FYECF_DOC_END_MARK_MASK);
+ bool implicit, dem;
+
+ if (!emit || !emit->fyds)
+ return -1;
+
+ fyds = emit->fyds;
+
+ implicit = fyds->end_implicit;
+ if (override_state)
+ implicit = implicit_override;
+
+ dem = ((dem_flags == FYECF_DOC_END_MARK_AUTO && !implicit) ||
+ dem_flags == FYECF_DOC_END_MARK_ON) &&
+ !(emit->cfg.flags & FYECF_STRIP_DOC);
+
+ if (!(emit->cfg.flags & FYECF_NO_ENDING_NEWLINE)) {
+ if (emit->column != 0) {
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION;
+ }
+
+ if (!fy_emit_is_json_mode(emit) && dem) {
+ fy_emit_puts(emit, fyewt_document_indicator, "...");
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION;
+ emit->flags |= FYEF_HAD_DOCUMENT_END;
+ } else
+ emit->flags &= ~FYEF_HAD_DOCUMENT_END;
+ } else {
+ if (!fy_emit_is_json_mode(emit) && dem) {
+ if (emit->column != 0) {
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION;
+ }
+ fy_emit_puts(emit, fyewt_document_indicator, "...");
+ emit->flags &= ~(FYEF_WHITESPACE | FYEF_INDENTATION);
+ emit->flags |= FYEF_HAD_DOCUMENT_END;
+ } else
+ emit->flags &= ~FYEF_HAD_DOCUMENT_END;
+ }
+
+ /* mark that we did output a document earlier */
+ emit->flags |= FYEF_HAD_DOCUMENT_OUTPUT;
+
+ /* stop our association with the document */
+ emit->fyds = NULL;
+
+ return 0;
+}
+
+int fy_emit_document_end(struct fy_emitter *emit)
+{
+ int ret;
+
+ ret = fy_emit_common_document_end(emit, false, false);
+ if (ret)
+ return ret;
+
+ emit->fyd = NULL;
+ return 0;
+}
+
+int fy_emit_common_explicit_document_end(struct fy_emitter *emit)
+{
+ if (!emit)
+ return -1;
+
+ if (emit->column != 0) {
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION;
+ }
+
+ if (!fy_emit_is_json_mode(emit)) {
+ fy_emit_puts(emit, fyewt_document_indicator, "...");
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION;
+ emit->flags |= FYEF_HAD_DOCUMENT_END;
+ } else
+ emit->flags &= ~FYEF_HAD_DOCUMENT_END;
+
+ /* mark that we did output a document earlier */
+ emit->flags |= FYEF_HAD_DOCUMENT_OUTPUT;
+
+ /* stop our association with the document */
+ emit->fyds = NULL;
+
+ return 0;
+}
+
+int fy_emit_explicit_document_end(struct fy_emitter *emit)
+{
+ int ret;
+
+ ret = fy_emit_common_explicit_document_end(emit);
+ if (ret)
+ return ret;
+
+ emit->fyd = NULL;
+ return 0;
+}
+
+void fy_emit_reset(struct fy_emitter *emit, bool reset_events)
+{
+ struct fy_eventp *fyep;
+
+ emit->line = 0;
+ emit->column = 0;
+ emit->flow_level = 0;
+ emit->output_error = 0;
+ /* start as if there was a previous document with an explicit end */
+ /* this allows implicit documents start without an indicator */
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION | FYEF_HAD_DOCUMENT_END;
+
+ emit->state = FYES_NONE;
+
+ /* reset the accumulator */
+ fy_emit_accum_reset(&emit->ea);
+
+ /* streaming mode indent */
+ emit->s_indent = -1;
+ /* streaming mode flags */
+ emit->s_flags = DDNF_ROOT;
+
+ emit->state_stack_top = 0;
+ emit->sc_stack_top = 0;
+
+ /* and release any queued events */
+ if (reset_events) {
+ while ((fyep = fy_eventp_list_pop(&emit->queued_events)) != NULL)
+ fy_eventp_release(fyep);
+ }
+}
+
+int fy_emit_setup(struct fy_emitter *emit, const struct fy_emitter_cfg *cfg)
+{
+ struct fy_diag *diag;
+
+ if (!cfg)
+ return -1;
+
+ memset(emit, 0, sizeof(*emit));
+
+ emit->cfg = *cfg;
+ if (!emit->cfg.output)
+ emit->cfg.output = fy_emitter_default_output;
+
+ diag = cfg->diag;
+
+ if (!diag) {
+ diag = fy_diag_create(NULL);
+ if (!diag)
+ return -1;
+ } else
+ fy_diag_ref(diag);
+
+ emit->diag = diag;
+
+ fy_emit_accum_init(&emit->ea, emit->ea_inplace_buf, sizeof(emit->ea_inplace_buf), 0, fylb_cr_nl);
+ fy_eventp_list_init(&emit->queued_events);
+
+ emit->state_stack = emit->state_stack_inplace;
+ emit->state_stack_alloc = sizeof(emit->state_stack_inplace)/sizeof(emit->state_stack_inplace[0]);
+
+ emit->sc_stack = emit->sc_stack_inplace;
+ emit->sc_stack_alloc = sizeof(emit->sc_stack_inplace)/sizeof(emit->sc_stack_inplace[0]);
+
+ fy_eventp_list_init(&emit->recycled_eventp);
+ fy_token_list_init(&emit->recycled_token);
+
+ /* suppress recycling if we must */
+ emit->suppress_recycling_force = getenv("FY_VALGRIND") && !getenv("FY_VALGRIND_RECYCLING");
+ emit->suppress_recycling = emit->suppress_recycling_force;
+
+ if (!emit->suppress_recycling) {
+ emit->recycled_eventp_list = &emit->recycled_eventp;
+ emit->recycled_token_list = &emit->recycled_token;
+ } else {
+ emit->recycled_eventp_list = NULL;
+ emit->recycled_token_list = NULL;
+ }
+
+ fy_emit_reset(emit, false);
+
+ return 0;
+}
+
+void fy_emit_cleanup(struct fy_emitter *emit)
+{
+ struct fy_eventp *fyep;
+ struct fy_token *fyt;
+
+ /* call the finalizer if it exists */
+ if (emit->finalizer)
+ emit->finalizer(emit);
+
+ while ((fyt = fy_token_list_pop(&emit->recycled_token)) != NULL)
+ fy_token_free(fyt);
+
+ while ((fyep = fy_eventp_list_pop(&emit->recycled_eventp)) != NULL)
+ fy_eventp_free(fyep);
+
+ if (!emit->fyd && emit->fyds)
+ fy_document_state_unref(emit->fyds);
+
+ fy_emit_accum_cleanup(&emit->ea);
+
+ while ((fyep = fy_eventp_list_pop(&emit->queued_events)) != NULL)
+ fy_eventp_release(fyep);
+
+ if (emit->state_stack && emit->state_stack != emit->state_stack_inplace)
+ free(emit->state_stack);
+
+ if (emit->sc_stack && emit->sc_stack != emit->sc_stack_inplace)
+ free(emit->sc_stack);
+
+ fy_diag_unref(emit->diag);
+}
+
+int fy_emit_node_no_check(struct fy_emitter *emit, struct fy_node *fyn)
+{
+ if (fyn)
+ fy_emit_node_internal(emit, fyn, DDNF_ROOT, -1, false);
+ return 0;
+}
+
+int fy_emit_node(struct fy_emitter *emit, struct fy_node *fyn)
+{
+ int ret;
+
+ ret = fy_emit_node_check(emit, fyn);
+ if (ret)
+ return ret;
+
+ return fy_emit_node_no_check(emit, fyn);
+}
+
+int fy_emit_root_node_no_check(struct fy_emitter *emit, struct fy_node *fyn)
+{
+ if (!emit || !fyn)
+ return -1;
+
+ /* top comment first */
+ fy_emit_node_comment(emit, fyn, DDNF_ROOT, -1, fycp_top);
+
+ fy_emit_node_internal(emit, fyn, DDNF_ROOT, -1, false);
+
+ /* right comment next */
+ fy_emit_node_comment(emit, fyn, DDNF_ROOT, -1, fycp_right);
+
+ /* bottom comment last */
+ fy_emit_node_comment(emit, fyn, DDNF_ROOT, -1, fycp_bottom);
+
+ return 0;
+}
+
+int fy_emit_root_node(struct fy_emitter *emit, struct fy_node *fyn)
+{
+ int ret;
+
+ if (!emit || !fyn)
+ return -1;
+
+ ret = fy_emit_node_check(emit, fyn);
+ if (ret)
+ return ret;
+
+ return fy_emit_root_node_no_check(emit, fyn);
+}
+
+void fy_emit_prepare_document_state(struct fy_emitter *emit, struct fy_document_state *fyds)
+{
+ if (!emit || !fyds)
+ return;
+
+ /* if the original document was JSON and the mode is ORIGINAL turn on JSON mode */
+ emit->source_json = fyds && fyds->json_mode;
+ emit->force_json = (emit->cfg.flags & FYECF_MODE(FYECF_MODE_MASK)) == FYECF_MODE_ORIGINAL &&
+ emit->source_json;
+}
+
+int fy_emit_document_no_check(struct fy_emitter *emit, struct fy_document *fyd)
+{
+ int rc;
+
+ rc = fy_emit_document_start(emit, fyd, NULL);
+ if (rc)
+ return rc;
+
+ rc = fy_emit_root_node_no_check(emit, fyd->root);
+ if (rc)
+ return rc;
+
+ rc = fy_emit_document_end(emit);
+
+ return rc;
+}
+
+int fy_emit_document(struct fy_emitter *emit, struct fy_document *fyd)
+{
+ int ret;
+
+ if (!emit)
+ return -1;
+
+ if (fyd) {
+ fy_emit_prepare_document_state(emit, fyd->fyds);
+
+ if (fyd->root) {
+ ret = fy_emit_node_check(emit, fyd->root);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return fy_emit_document_no_check(emit, fyd);
+}
+
+struct fy_emitter *fy_emitter_create(const struct fy_emitter_cfg *cfg)
+{
+ struct fy_emitter *emit;
+ int rc;
+
+ if (!cfg)
+ return NULL;
+
+ emit = malloc(sizeof(*emit));
+ if (!emit)
+ return NULL;
+
+ rc = fy_emit_setup(emit, cfg);
+ if (rc) {
+ free(emit);
+ return NULL;
+ }
+
+ return emit;
+}
+
+void fy_emitter_destroy(struct fy_emitter *emit)
+{
+ if (!emit)
+ return;
+
+ fy_emit_cleanup(emit);
+
+ free(emit);
+}
+
+const struct fy_emitter_cfg *fy_emitter_get_cfg(struct fy_emitter *emit)
+{
+ if (!emit)
+ return NULL;
+
+ return &emit->cfg;
+}
+
+struct fy_diag *fy_emitter_get_diag(struct fy_emitter *emit)
+{
+ if (!emit || !emit->diag)
+ return NULL;
+ return fy_diag_ref(emit->diag);
+}
+
+int fy_emitter_set_diag(struct fy_emitter *emit, struct fy_diag *diag)
+{
+ struct fy_diag_cfg dcfg;
+
+ if (!emit)
+ return -1;
+
+ /* default? */
+ if (!diag) {
+ fy_diag_cfg_default(&dcfg);
+ diag = fy_diag_create(&dcfg);
+ if (!diag)
+ return -1;
+ }
+
+ fy_diag_unref(emit->diag);
+ emit->diag = fy_diag_ref(diag);
+
+ return 0;
+}
+
+void fy_emitter_set_finalizer(struct fy_emitter *emit,
+ void (*finalizer)(struct fy_emitter *emit))
+{
+ if (!emit)
+ return;
+ emit->finalizer = finalizer;
+}
+
+struct fy_emit_buffer_state {
+ char **bufp;
+ size_t *sizep;
+ char *buf;
+ size_t size;
+ size_t pos;
+ size_t need;
+ bool allocate_buffer;
+};
+
+static int do_buffer_output(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int leni, void *userdata)
+{
+ struct fy_emit_buffer_state *state = emit->cfg.userdata;
+ size_t left, pagesize, size, len;
+ char *bufnew;
+
+ /* convert to unsigned and use that */
+ len = (size_t)leni;
+
+ /* no funky business */
+ if (len < 0)
+ return -1;
+
+ state->need += len;
+ left = state->size - state->pos;
+ if (left < len) {
+ if (!state->allocate_buffer)
+ return 0;
+
+ pagesize = fy_get_pagesize();
+ size = state->need + pagesize - 1;
+ size = size - size % pagesize;
+
+ bufnew = realloc(state->buf, size);
+ if (!bufnew)
+ return -1;
+ state->buf = bufnew;
+ state->size = size;
+ left = state->size - state->pos;
+
+ }
+
+ if (len > left)
+ len = left;
+ if (state->buf)
+ memcpy(state->buf + state->pos, str, len);
+ state->pos += len;
+
+ return len;
+}
+
+static void
+fy_emitter_str_finalizer(struct fy_emitter *emit)
+{
+ struct fy_emit_buffer_state *state;
+
+ if (!emit || !(state = emit->cfg.userdata))
+ return;
+
+ /* if the buffer is allowed to allocate_buffer... */
+ if (state->allocate_buffer && state->buf)
+ free(state->buf);
+ free(state);
+
+ emit->cfg.userdata = NULL;
+}
+
+static struct fy_emitter *
+fy_emitter_create_str_internal(enum fy_emitter_cfg_flags flags, char **bufp, size_t *sizep, bool allocate_buffer)
+{
+ struct fy_emitter *emit;
+ struct fy_emitter_cfg emit_cfg;
+ struct fy_emit_buffer_state *state;
+
+ state = malloc(sizeof(*state));
+ if (!state)
+ return NULL;
+
+ /* if any of these NULL, it's a allocation case */
+ if ((!bufp || !sizep) && !allocate_buffer)
+ return NULL;
+
+ if (bufp && sizep) {
+ state->bufp = bufp;
+ state->buf = *bufp;
+ state->sizep = sizep;
+ state->size = *sizep;
+ } else {
+ state->bufp = NULL;
+ state->buf = NULL;
+ state->sizep = NULL;
+ state->size = 0;
+ }
+ state->pos = 0;
+ state->need = 0;
+ state->allocate_buffer = allocate_buffer;
+
+ memset(&emit_cfg, 0, sizeof(emit_cfg));
+ emit_cfg.output = do_buffer_output;
+ emit_cfg.userdata = state;
+ emit_cfg.flags = flags;
+
+ emit = fy_emitter_create(&emit_cfg);
+ if (!emit)
+ goto err_out;
+
+ /* set finalizer to cleanup */
+ fy_emitter_set_finalizer(emit, fy_emitter_str_finalizer);
+
+ return emit;
+
+err_out:
+ if (state)
+ free(state);
+ return NULL;
+}
+
+static int
+fy_emitter_collect_str_internal(struct fy_emitter *emit, char **bufp, size_t *sizep)
+{
+ struct fy_emit_buffer_state *state;
+ char *buf;
+ int rc;
+
+ state = emit->cfg.userdata;
+ assert(state);
+
+ /* if NULL, then use the values stored on the state */
+ if (!bufp)
+ bufp = state->bufp;
+ if (!sizep)
+ sizep = state->sizep;
+
+ /* terminating zero */
+ rc = do_buffer_output(emit, fyewt_terminating_zero, "\0", 1, state);
+ if (rc != 1)
+ goto err_out;
+
+ state->size = state->need;
+
+ if (state->allocate_buffer) {
+ /* resize */
+ buf = realloc(state->buf, state->size);
+ /* very likely since we shrink the buffer, but make sure we don't error out */
+ if (buf)
+ state->buf = buf;
+ }
+
+ /* retreive the buffer and size */
+ *sizep = state->size;
+ *bufp = state->buf;
+
+ /* reset the buffer, ownership now to the caller */
+ state->buf = NULL;
+ state->size = 0;
+ state->pos = 0;
+ state->bufp = NULL;
+ state->sizep = NULL;
+
+ return 0;
+
+err_out:
+ *bufp = NULL;
+ *sizep = 0;
+ return -1;
+}
+
+static int fy_emit_str_internal(struct fy_document *fyd,
+ enum fy_emitter_cfg_flags flags,
+ struct fy_node *fyn, char **bufp, size_t *sizep,
+ bool allocate_buffer)
+{
+ struct fy_emitter *emit = NULL;
+ int rc = -1;
+
+ emit = fy_emitter_create_str_internal(flags, bufp, sizep, allocate_buffer);
+ if (!emit)
+ goto out_err;
+
+ if (fyd) {
+ fy_emit_prepare_document_state(emit, fyd->fyds);
+ rc = 0;
+ if (fyd->root)
+ rc = fy_emit_node_check(emit, fyd->root);
+ if (!rc)
+ rc = fy_emit_document_no_check(emit, fyd);
+ } else {
+ rc = fy_emit_node_check(emit, fyn);
+ if (!rc)
+ rc = fy_emit_node_no_check(emit, fyn);
+ }
+
+ if (rc)
+ goto out_err;
+
+ rc = fy_emitter_collect_str_internal(emit, NULL, NULL);
+ if (rc)
+ goto out_err;
+
+ /* OK, all done */
+
+out_err:
+ fy_emitter_destroy(emit);
+ return rc;
+}
+
+int fy_emit_document_to_buffer(struct fy_document *fyd, enum fy_emitter_cfg_flags flags, char *buf, size_t size)
+{
+ int rc;
+
+ rc = fy_emit_str_internal(fyd, flags, NULL, &buf, &size, false);
+ if (rc != 0)
+ return -1;
+ return size;
+}
+
+char *fy_emit_document_to_string(struct fy_document *fyd, enum fy_emitter_cfg_flags flags)
+{
+ char *buf;
+ size_t size;
+ int rc;
+
+ buf = NULL;
+ size = 0;
+ rc = fy_emit_str_internal(fyd, flags, NULL, &buf, &size, true);
+ if (rc != 0)
+ return NULL;
+ return buf;
+}
+
+struct fy_emitter *
+fy_emit_to_buffer(enum fy_emitter_cfg_flags flags, char *buf, size_t size)
+{
+ if (!buf)
+ return NULL;
+
+ return fy_emitter_create_str_internal(flags, &buf, &size, false);
+}
+
+char *
+fy_emit_to_buffer_collect(struct fy_emitter *emit, size_t *sizep)
+{
+ int rc;
+ char *buf;
+
+ if (!emit || !sizep)
+ return NULL;
+
+ rc = fy_emitter_collect_str_internal(emit, &buf, sizep);
+ if (rc) {
+ *sizep = 0;
+ return NULL;
+ }
+ return buf;
+}
+
+struct fy_emitter *
+fy_emit_to_string(enum fy_emitter_cfg_flags flags)
+{
+ return fy_emitter_create_str_internal(flags, NULL, NULL, true);
+}
+
+char *
+fy_emit_to_string_collect(struct fy_emitter *emit, size_t *sizep)
+{
+ int rc;
+ char *buf;
+
+ if (!emit || !sizep)
+ return NULL;
+
+ rc = fy_emitter_collect_str_internal(emit, &buf, sizep);
+ if (rc) {
+ *sizep = 0;
+ return NULL;
+ }
+ return buf;
+}
+
+static int do_file_output(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int leni, void *userdata)
+{
+ FILE *fp = userdata;
+ size_t len;
+
+ len = (size_t)leni;
+
+ /* no funky stuff */
+ if (len < 0)
+ return -1;
+
+ return fwrite(str, 1, len, fp);
+}
+
+int fy_emit_document_to_fp(struct fy_document *fyd, enum fy_emitter_cfg_flags flags,
+ FILE *fp)
+{
+ struct fy_emitter emit_state, *emit = &emit_state;
+ struct fy_emitter_cfg emit_cfg;
+ int rc;
+
+ if (!fp)
+ return -1;
+
+ memset(&emit_cfg, 0, sizeof(emit_cfg));
+ emit_cfg.output = do_file_output;
+ emit_cfg.userdata = fp;
+ emit_cfg.flags = flags;
+ fy_emit_setup(emit, &emit_cfg);
+
+ fy_emit_prepare_document_state(emit, fyd->fyds);
+
+ rc = 0;
+ if (fyd->root)
+ rc = fy_emit_node_check(emit, fyd->root);
+
+ rc = fy_emit_document_no_check(emit, fyd);
+
+ fy_emit_cleanup(emit);
+
+ return rc ? rc : 0;
+}
+
+int fy_emit_document_to_file(struct fy_document *fyd,
+ enum fy_emitter_cfg_flags flags,
+ const char *filename)
+{
+ FILE *fp;
+ int rc;
+
+ fp = filename ? fopen(filename, "wa") : stdout;
+ if (!fp)
+ return -1;
+
+ rc = fy_emit_document_to_fp(fyd, flags, fp);
+
+ if (fp != stdout)
+ fclose(fp);
+
+ return rc ? rc : 0;
+}
+
+static int do_fd_output(struct fy_emitter *emit, enum fy_emitter_write_type type, const char *str, int leni, void *userdata)
+{
+ size_t len;
+ int fd;
+ ssize_t wrn;
+ int total;
+
+ len = (size_t)leni;
+
+ /* no funky stuff */
+ if (len < 0)
+ return -1;
+
+ /* get the file descriptor */
+ fd = (int)(uintptr_t)userdata;
+ if (fd < 0)
+ return -1;
+
+ /* loop output to fd */
+ total = 0;
+ while (len > 0) {
+
+ do {
+ wrn = write(fd, str, len);
+ } while (wrn == -1 && errno == EAGAIN);
+
+ if (wrn == -1)
+ return -1;
+
+ if (wrn == 0)
+ return total;
+
+ len -= wrn;
+ str += wrn;
+ total += wrn;
+ }
+
+ return total;
+}
+
+int fy_emit_document_to_fd(struct fy_document *fyd, enum fy_emitter_cfg_flags flags, int fd)
+{
+ struct fy_emitter emit_state, *emit = &emit_state;
+ struct fy_emitter_cfg emit_cfg;
+ int rc;
+
+ if (fd < 0)
+ return -1;
+
+ memset(&emit_cfg, 0, sizeof(emit_cfg));
+ emit_cfg.output = do_fd_output;
+ emit_cfg.userdata = (void *)(uintptr_t)fd;
+ emit_cfg.flags = flags;
+ fy_emit_setup(emit, &emit_cfg);
+
+ fy_emit_prepare_document_state(emit, fyd->fyds);
+
+ rc = 0;
+ if (fyd->root)
+ rc = fy_emit_node_check(emit, fyd->root);
+
+ rc = fy_emit_document_no_check(emit, fyd);
+
+ fy_emit_cleanup(emit);
+
+ return rc ? rc : 0;
+}
+
+int fy_emit_node_to_buffer(struct fy_node *fyn, enum fy_emitter_cfg_flags flags, char *buf, size_t size)
+{
+ int rc;
+
+ rc = fy_emit_str_internal(NULL, flags, fyn, &buf, &size, false);
+ if (rc != 0)
+ return -1;
+ return size;
+}
+
+char *fy_emit_node_to_string(struct fy_node *fyn, enum fy_emitter_cfg_flags flags)
+{
+ char *buf;
+ size_t size;
+ int rc;
+
+ buf = NULL;
+ size = 0;
+ rc = fy_emit_str_internal(NULL, flags, fyn, &buf, &size, true);
+ if (rc != 0)
+ return NULL;
+ return buf;
+}
+
+static bool fy_emit_ready(struct fy_emitter *emit)
+{
+ struct fy_eventp *fyep;
+ int need, count, level;
+
+ /* no events in the list, not ready */
+ fyep = fy_eventp_list_head(&emit->queued_events);
+ if (!fyep)
+ return false;
+
+ /* some events need more than one */
+ switch (fyep->e.type) {
+ case FYET_DOCUMENT_START:
+ need = 1;
+ break;
+ case FYET_SEQUENCE_START:
+ need = 2;
+ break;
+ case FYET_MAPPING_START:
+ need = 3;
+ break;
+ default:
+ need = 0;
+ break;
+ }
+
+ /* if we don't need any more, that's enough */
+ if (!need)
+ return true;
+
+ level = 0;
+ count = 0;
+ for (; fyep; fyep = fy_eventp_next(&emit->queued_events, fyep)) {
+ count++;
+
+ if (count > need)
+ return true;
+
+ switch (fyep->e.type) {
+ case FYET_STREAM_START:
+ case FYET_DOCUMENT_START:
+ case FYET_SEQUENCE_START:
+ case FYET_MAPPING_START:
+ level++;
+ break;
+ case FYET_STREAM_END:
+ case FYET_DOCUMENT_END:
+ case FYET_SEQUENCE_END:
+ case FYET_MAPPING_END:
+ level--;
+ break;
+ default:
+ break;
+ }
+
+ if (!level)
+ return true;
+ }
+
+ return false;
+}
+
+extern const char *fy_event_type_txt[];
+
+const char *fy_emitter_state_txt[] = {
+ [FYES_NONE] = "NONE",
+ [FYES_STREAM_START] = "STREAM_START",
+ [FYES_FIRST_DOCUMENT_START] = "FIRST_DOCUMENT_START",
+ [FYES_DOCUMENT_START] = "DOCUMENT_START",
+ [FYES_DOCUMENT_CONTENT] = "DOCUMENT_CONTENT",
+ [FYES_DOCUMENT_END] = "DOCUMENT_END",
+ [FYES_SEQUENCE_FIRST_ITEM] = "SEQUENCE_FIRST_ITEM",
+ [FYES_SEQUENCE_ITEM] = "SEQUENCE_ITEM",
+ [FYES_MAPPING_FIRST_KEY] = "MAPPING_FIRST_KEY",
+ [FYES_MAPPING_KEY] = "MAPPING_KEY",
+ [FYES_MAPPING_SIMPLE_VALUE] = "MAPPING_SIMPLE_VALUE",
+ [FYES_MAPPING_VALUE] = "MAPPING_VALUE",
+ [FYES_END] = "END",
+};
+
+struct fy_eventp *
+fy_emit_next_event(struct fy_emitter *emit)
+{
+ if (!fy_emit_ready(emit))
+ return NULL;
+
+ return fy_eventp_list_pop(&emit->queued_events);
+}
+
+struct fy_eventp *
+fy_emit_peek_next_event(struct fy_emitter *emit)
+{
+ if (!fy_emit_ready(emit))
+ return NULL;
+
+ return fy_eventp_list_head(&emit->queued_events);
+}
+
+bool fy_emit_streaming_sequence_empty(struct fy_emitter *emit)
+{
+ struct fy_eventp *fyepn;
+ struct fy_event *fyen;
+
+ fyepn = fy_emit_peek_next_event(emit);
+ fyen = fyepn ? &fyepn->e : NULL;
+
+ return !fyen || fyen->type == FYET_SEQUENCE_END;
+}
+
+bool fy_emit_streaming_mapping_empty(struct fy_emitter *emit)
+{
+ struct fy_eventp *fyepn;
+ struct fy_event *fyen;
+
+ fyepn = fy_emit_peek_next_event(emit);
+ fyen = fyepn ? &fyepn->e : NULL;
+
+ return !fyen || fyen->type == FYET_MAPPING_END;
+}
+
+static void fy_emit_goto_state(struct fy_emitter *emit, enum fy_emitter_state state)
+{
+ if (emit->state == state)
+ return;
+
+ emit->state = state;
+}
+
+static int fy_emit_push_state(struct fy_emitter *emit, enum fy_emitter_state state)
+{
+ enum fy_emitter_state *states;
+
+ if (emit->state_stack_top >= emit->state_stack_alloc) {
+ states = realloc(emit->state_stack == emit->state_stack_inplace ? NULL : emit->state_stack,
+ sizeof(emit->state_stack[0]) * emit->state_stack_alloc * 2);
+ if (!states)
+ return -1;
+
+ if (emit->state_stack == emit->state_stack_inplace)
+ memcpy(states, emit->state_stack, sizeof(emit->state_stack[0]) * emit->state_stack_top);
+ emit->state_stack = states;
+ emit->state_stack_alloc *= 2;
+ }
+ emit->state_stack[emit->state_stack_top++] = state;
+
+ return 0;
+}
+
+enum fy_emitter_state fy_emit_pop_state(struct fy_emitter *emit)
+{
+ if (!emit->state_stack_top)
+ return FYES_NONE;
+
+ return emit->state_stack[--emit->state_stack_top];
+}
+
+int fy_emit_push_sc(struct fy_emitter *emit, struct fy_emit_save_ctx *sc)
+{
+ struct fy_emit_save_ctx *scs;
+
+ if (emit->sc_stack_top >= emit->sc_stack_alloc) {
+ scs = realloc(emit->sc_stack == emit->sc_stack_inplace ? NULL : emit->sc_stack,
+ sizeof(emit->sc_stack[0]) * emit->sc_stack_alloc * 2);
+ if (!scs)
+ return -1;
+
+ if (emit->sc_stack == emit->sc_stack_inplace)
+ memcpy(scs, emit->sc_stack, sizeof(emit->sc_stack[0]) * emit->sc_stack_top);
+ emit->sc_stack = scs;
+ emit->sc_stack_alloc *= 2;
+ }
+ emit->sc_stack[emit->sc_stack_top++] = *sc;
+
+ return 0;
+}
+
+int fy_emit_pop_sc(struct fy_emitter *emit, struct fy_emit_save_ctx *sc)
+{
+ if (!emit->sc_stack_top)
+ return -1;
+
+ *sc = emit->sc_stack[--emit->sc_stack_top];
+
+ return 0;
+}
+
+static int fy_emit_streaming_node(struct fy_emitter *emit, struct fy_eventp *fyep, int flags)
+{
+ struct fy_event *fye = &fyep->e;
+ struct fy_emit_save_ctx *sc = &emit->s_sc;
+ enum fy_node_style style;
+ int ret, s_flags, s_indent;
+
+ if (fye->type != FYET_ALIAS && fye->type != FYET_SCALAR &&
+ (emit->s_flags & DDNF_ROOT) && emit->column != 0) {
+ fy_emit_putc(emit, fyewt_linebreak, '\n');
+ emit->flags = FYEF_WHITESPACE | FYEF_INDENTATION;
+ }
+
+ emit->s_flags = flags;
+
+ switch (fye->type) {
+ case FYET_ALIAS:
+ fy_emit_token_write_alias(emit, fye->alias.anchor, emit->s_flags, emit->s_indent);
+ fy_emit_goto_state(emit, fy_emit_pop_state(emit));
+ break;
+
+ case FYET_SCALAR:
+ /* if we're pretty and at column 0 (meaning it's a single scalar document) output --- */
+ if ((emit->s_flags & DDNF_ROOT) && fy_emit_is_pretty_mode(emit) && !emit->column &&
+ !fy_emit_is_flow_mode(emit) && !(emit->s_flags & DDNF_FLOW))
+ fy_emit_document_start_indicator(emit);
+ fy_emit_common_node_preamble(emit, fye->scalar.anchor, fye->scalar.tag, emit->s_flags, emit->s_indent);
+ style = fye->scalar.value ?
+ fy_node_style_from_scalar_style(fye->scalar.value->scalar.style) :
+ FYNS_PLAIN;
+ fy_emit_token_scalar(emit, fye->scalar.value, emit->s_flags, emit->s_indent, style, fye->scalar.tag);
+ fy_emit_goto_state(emit, fy_emit_pop_state(emit));
+ break;
+
+ case FYET_SEQUENCE_START:
+
+ /* save this context */
+ ret = fy_emit_push_sc(emit, sc);
+ if (ret)
+ return ret;
+
+ s_flags = emit->s_flags;
+ s_indent = emit->s_indent;
+
+ fy_emit_common_node_preamble(emit, fye->sequence_start.anchor, fye->sequence_start.tag, emit->s_flags, emit->s_indent);
+
+ /* create new context */
+ memset(sc, 0, sizeof(*sc));
+ sc->flags = emit->s_flags & (DDNF_ROOT | DDNF_SEQ | DDNF_MAP);
+ sc->indent = emit->s_indent;
+ sc->empty = fy_emit_streaming_sequence_empty(emit);
+ sc->flow_token = fye->sequence_start.sequence_start &&
+ fye->sequence_start.sequence_start->type == FYTT_FLOW_SEQUENCE_START;
+ sc->flow = !!(s_flags & DDNF_FLOW);
+ sc->old_indent = sc->indent;
+ sc->s_flags = s_flags;
+ sc->s_indent = s_indent;
+ sc->s_flags = s_flags;
+ sc->s_indent = s_indent;
+
+ fy_emit_sequence_prolog(emit, sc);
+ sc->flags &= ~DDNF_MAP;
+ sc->flags |= DDNF_SEQ;
+
+ emit->s_flags = sc->flags;
+ emit->s_indent = sc->indent;
+
+ fy_emit_goto_state(emit, FYES_SEQUENCE_FIRST_ITEM);
+ break;
+
+ case FYET_MAPPING_START:
+ /* save this context */
+ ret = fy_emit_push_sc(emit, sc);
+ if (ret)
+ return ret;
+
+ s_flags = emit->s_flags;
+ s_indent = emit->s_indent;
+
+ fy_emit_common_node_preamble(emit, fye->mapping_start.anchor, fye->mapping_start.tag, emit->s_flags, emit->s_indent);
+
+ /* create new context */
+ memset(sc, 0, sizeof(*sc));
+ sc->flags = emit->s_flags & (DDNF_ROOT | DDNF_SEQ | DDNF_MAP);
+ sc->indent = emit->s_indent;
+ sc->empty = fy_emit_streaming_mapping_empty(emit);
+ sc->flow_token = fye->mapping_start.mapping_start &&
+ fye->mapping_start.mapping_start->type == FYTT_FLOW_MAPPING_START;
+ sc->flow = !!(s_flags & DDNF_FLOW);
+ sc->old_indent = sc->indent;
+ sc->s_flags = s_flags;
+ sc->s_indent = s_indent;
+ sc->s_flags = s_flags;
+ sc->s_indent = s_indent;
+
+ fy_emit_mapping_prolog(emit, sc);
+ sc->flags &= ~DDNF_SEQ;
+ sc->flags |= DDNF_MAP;
+
+ emit->s_flags = sc->flags;
+ emit->s_indent = sc->indent;
+
+ fy_emit_goto_state(emit, FYES_MAPPING_FIRST_KEY);
+ break;
+
+ default:
+ fy_error(emit->diag, "%s: expected ALIAS|SCALAR|SEQUENCE_START|MAPPING_START", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int fy_emit_handle_stream_start(struct fy_emitter *emit, struct fy_eventp *fyep)
+{
+ struct fy_event *fye = &fyep->e;
+
+ if (fye->type != FYET_STREAM_START) {
+ fy_error(emit->diag, "%s: expected FYET_STREAM_START", __func__);
+ return -1;
+ }
+
+ fy_emit_reset(emit, false);
+
+ fy_emit_goto_state(emit, FYES_FIRST_DOCUMENT_START);
+
+ return 0;
+}
+
+static int fy_emit_handle_document_start(struct fy_emitter *emit, struct fy_eventp *fyep, bool first)
+{
+ struct fy_event *fye = &fyep->e;
+ struct fy_document_state *fyds;
+
+ if (fye->type != FYET_DOCUMENT_START &&
+ fye->type != FYET_STREAM_END) {
+ fy_error(emit->diag, "%s: expected FYET_DOCUMENT_START|FYET_STREAM_END", __func__);
+ return -1;
+ }
+
+ if (fye->type == FYET_STREAM_END) {
+ fy_emit_goto_state(emit, FYES_END);
+ return 0;
+ }
+
+ /* transfer ownership to the emitter */
+ fyds = fye->document_start.document_state;
+ fye->document_start.document_state = NULL;
+
+ /* prepare (i.e. adapt to the document state) */
+ fy_emit_prepare_document_state(emit, fyds);
+
+ fy_emit_common_document_start(emit, fyds, false);
+
+ fy_emit_goto_state(emit, FYES_DOCUMENT_CONTENT);
+
+ return 0;
+}
+
+static int fy_emit_handle_document_end(struct fy_emitter *emit, struct fy_eventp *fyep)
+{
+ struct fy_document_state *fyds;
+ struct fy_event *fye = &fyep->e;
+ int ret;
+
+ if (fye->type != FYET_DOCUMENT_END) {
+ fy_error(emit->diag, "%s: expected FYET_DOCUMENT_END", __func__);
+ return -1;
+ }
+
+ fyds = emit->fyds;
+
+ ret = fy_emit_common_document_end(emit, true, fye->document_end.implicit);
+ if (ret)
+ return ret;
+
+ fy_document_state_unref(fyds);
+
+ fy_emit_reset(emit, false);
+ fy_emit_goto_state(emit, FYES_DOCUMENT_START);
+ return 0;
+}
+
+static int fy_emit_handle_document_content(struct fy_emitter *emit, struct fy_eventp *fyep)
+{
+ struct fy_event *fye = &fyep->e;
+ int ret;
+
+ /* empty document? */
+ if (fye->type == FYET_DOCUMENT_END)
+ return fy_emit_handle_document_end(emit, fyep);
+
+ ret = fy_emit_push_state(emit, FYES_DOCUMENT_END);
+ if (ret)
+ return ret;
+
+ return fy_emit_streaming_node(emit, fyep, DDNF_ROOT);
+}
+
+static int fy_emit_handle_sequence_item(struct fy_emitter *emit, struct fy_eventp *fyep, bool first)
+{
+ struct fy_event *fye = &fyep->e;
+ struct fy_emit_save_ctx *sc = &emit->s_sc;
+ struct fy_token *fyt_item = NULL;
+ int ret;
+
+ fy_token_unref_rl(emit->recycled_token_list, sc->fyt_last_value);
+ sc->fyt_last_value = NULL;
+
+ switch (fye->type) {
+ case FYET_SEQUENCE_END:
+ fy_emit_sequence_item_epilog(emit, sc, true, sc->fyt_last_value);
+
+ /* emit epilog */
+ fy_emit_sequence_epilog(emit, sc);
+ /* pop state */
+ ret = fy_emit_pop_sc(emit, sc);
+ /* pop state */
+ fy_emit_goto_state(emit, fy_emit_pop_state(emit));
+
+ /* restore indent and flags */
+ emit->s_indent = sc->s_indent;
+ emit->s_flags = sc->s_flags;
+ return ret;
+
+ case FYET_ALIAS:
+ fyt_item = fye->alias.anchor;
+ break;
+ case FYET_SCALAR:
+ fyt_item = fye->scalar.value;
+ break;
+ case FYET_SEQUENCE_START:
+ fyt_item = fye->sequence_start.sequence_start;
+ break;
+ case FYET_MAPPING_START:
+ fyt_item = fye->mapping_start.mapping_start;
+ break;
+ default:
+ fy_error(emit->diag, "%s: expected SEQUENCE_END|ALIAS|SCALAR|SEQUENCE_START|MAPPING_START", __func__);
+ return -1;
+ }
+
+ ret = fy_emit_push_state(emit, FYES_SEQUENCE_ITEM);
+ if (ret)
+ return ret;
+
+ /* reset indent and flags for each item */
+ emit->s_indent = sc->indent;
+ emit->s_flags = sc->flags;
+
+ if (!first)
+ fy_emit_sequence_item_epilog(emit, sc, false, sc->fyt_last_value);
+
+ sc->fyt_last_value = fyt_item;
+
+ fy_emit_sequence_item_prolog(emit, sc, fyt_item);
+
+ ret = fy_emit_streaming_node(emit, fyep, sc->flags);
+
+ switch (fye->type) {
+ case FYET_ALIAS:
+ fye->alias.anchor = NULL; /* take ownership */
+ break;
+ case FYET_SCALAR:
+ fye->scalar.value = NULL; /* take ownership */
+ break;
+ case FYET_SEQUENCE_START:
+ fye->sequence_start.sequence_start = NULL; /* take ownership */
+ break;
+ case FYET_MAPPING_START:
+ fye->mapping_start.mapping_start = NULL; /* take ownership */
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int fy_emit_handle_mapping_key(struct fy_emitter *emit, struct fy_eventp *fyep, bool first)
+{
+ struct fy_event *fye = &fyep->e;
+ struct fy_emit_save_ctx *sc = &emit->s_sc;
+ struct fy_token *fyt_key = NULL;
+ int ret, aflags;
+ bool simple_key;
+
+ fy_token_unref_rl(emit->recycled_token_list, sc->fyt_last_key);
+ sc->fyt_last_key = NULL;
+ fy_token_unref_rl(emit->recycled_token_list, sc->fyt_last_value);
+ sc->fyt_last_value = NULL;
+
+ simple_key = false;
+
+ switch (fye->type) {
+ case FYET_MAPPING_END:
+ fy_emit_mapping_value_epilog(emit, sc, true, sc->fyt_last_value);
+
+ /* emit epilog */
+ fy_emit_mapping_epilog(emit, sc);
+ /* pop state */
+ ret = fy_emit_pop_sc(emit, sc);
+ /* pop state */
+ fy_emit_goto_state(emit, fy_emit_pop_state(emit));
+
+ /* restore indent and flags */
+ emit->s_indent = sc->s_indent;
+ emit->s_flags = sc->s_flags;
+ return ret;
+
+ case FYET_ALIAS:
+ fyt_key = fye->alias.anchor;
+ simple_key = true;
+ break;
+ case FYET_SCALAR:
+ fyt_key = fye->scalar.value;
+ aflags = fy_token_text_analyze(fyt_key);
+ simple_key = !!(aflags & FYTTAF_CAN_BE_SIMPLE_KEY);
+ break;
+ case FYET_SEQUENCE_START:
+ fyt_key = fye->sequence_start.sequence_start;
+ simple_key = fy_emit_streaming_sequence_empty(emit);
+ break;
+ case FYET_MAPPING_START:
+ fyt_key = fye->mapping_start.mapping_start;
+ simple_key = fy_emit_streaming_mapping_empty(emit);
+ break;
+ default:
+ fy_error(emit->diag, "%s: expected MAPPING_END|ALIAS|SCALAR|SEQUENCE_START|MAPPING_START", __func__);
+ return -1;
+ }
+
+ ret = fy_emit_push_state(emit, FYES_MAPPING_VALUE);
+ if (ret)
+ return ret;
+
+ /* reset indent and flags for each key/value pair */
+ emit->s_indent = sc->indent;
+ emit->s_flags = sc->flags;
+
+ if (!first)
+ fy_emit_mapping_value_epilog(emit, sc, false, sc->fyt_last_value);
+
+ sc->fyt_last_key = fyt_key;
+
+ fy_emit_mapping_key_prolog(emit, sc, fyt_key, simple_key);
+
+ ret = fy_emit_streaming_node(emit, fyep, sc->flags);
+
+ switch (fye->type) {
+ case FYET_ALIAS:
+ fye->alias.anchor = NULL; /* take ownership */
+ break;
+ case FYET_SCALAR:
+ fye->scalar.value = NULL; /* take ownership */
+ break;
+ case FYET_SEQUENCE_START:
+ fye->sequence_start.sequence_start = NULL; /* take ownership */
+ break;
+ case FYET_MAPPING_START:
+ fye->mapping_start.mapping_start = NULL; /* take ownership */
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int fy_emit_handle_mapping_value(struct fy_emitter *emit, struct fy_eventp *fyep, bool simple)
+{
+ struct fy_event *fye = &fyep->e;
+ struct fy_emit_save_ctx *sc = &emit->s_sc;
+ struct fy_token *fyt_value = NULL;
+ int ret;
+
+ switch (fye->type) {
+ case FYET_ALIAS:
+ fyt_value = fye->alias.anchor;
+ break;
+ case FYET_SCALAR:
+ fyt_value = fye->scalar.value; /* take ownership */
+ break;
+ case FYET_SEQUENCE_START:
+ fyt_value = fye->sequence_start.sequence_start;
+ break;
+ case FYET_MAPPING_START:
+ fyt_value = fye->mapping_start.mapping_start;
+ break;
+ default:
+ fy_error(emit->diag, "%s: expected ALIAS|SCALAR|SEQUENCE_START|MAPPING_START", __func__);
+ return -1;
+ }
+
+ ret = fy_emit_push_state(emit, FYES_MAPPING_KEY);
+ if (ret)
+ return ret;
+
+ fy_emit_mapping_key_epilog(emit, sc, sc->fyt_last_key);
+
+ sc->fyt_last_value = fyt_value;
+
+ fy_emit_mapping_value_prolog(emit, sc, fyt_value);
+
+ ret = fy_emit_streaming_node(emit, fyep, sc->flags);
+
+ switch (fye->type) {
+ case FYET_ALIAS:
+ fye->alias.anchor = NULL; /* take ownership */
+ break;
+ case FYET_SCALAR:
+ fye->scalar.value = NULL; /* take ownership */
+ break;
+ case FYET_SEQUENCE_START:
+ fye->sequence_start.sequence_start = NULL; /* take ownership */
+ break;
+ case FYET_MAPPING_START:
+ fye->mapping_start.mapping_start = NULL; /* take ownership */
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int fy_emit_event_from_parser(struct fy_emitter *emit, struct fy_parser *fyp, struct fy_event *fye)
+{
+ struct fy_eventp *fyep;
+ int ret;
+
+ if (!emit || !fye)
+ return -1;
+
+ /* we're using the raw emitter interface, now mark first state */
+ if (emit->state == FYES_NONE)
+ emit->state = FYES_STREAM_START;
+
+ fyep = fy_container_of(fye, struct fy_eventp, e);
+
+ fy_eventp_list_add_tail(&emit->queued_events, fyep);
+
+ ret = 0;
+ while ((fyep = fy_emit_next_event(emit)) != NULL) {
+
+ switch (emit->state) {
+ case FYES_STREAM_START:
+ ret = fy_emit_handle_stream_start(emit, fyep);
+ break;
+
+ case FYES_FIRST_DOCUMENT_START:
+ case FYES_DOCUMENT_START:
+ ret = fy_emit_handle_document_start(emit, fyep,
+ emit->state == FYES_FIRST_DOCUMENT_START);
+ break;
+
+ case FYES_DOCUMENT_CONTENT:
+ ret = fy_emit_handle_document_content(emit, fyep);
+ break;
+
+ case FYES_DOCUMENT_END:
+ ret = fy_emit_handle_document_end(emit, fyep);
+ break;
+
+ case FYES_SEQUENCE_FIRST_ITEM:
+ case FYES_SEQUENCE_ITEM:
+ ret = fy_emit_handle_sequence_item(emit, fyep,
+ emit->state == FYES_SEQUENCE_FIRST_ITEM);
+ break;
+
+ case FYES_MAPPING_FIRST_KEY:
+ case FYES_MAPPING_KEY:
+ ret = fy_emit_handle_mapping_key(emit, fyep,
+ emit->state == FYES_MAPPING_FIRST_KEY);
+ break;
+
+ case FYES_MAPPING_SIMPLE_VALUE:
+ case FYES_MAPPING_VALUE:
+ ret = fy_emit_handle_mapping_value(emit, fyep,
+ emit->state == FYES_MAPPING_SIMPLE_VALUE);
+ break;
+
+ case FYES_END:
+ ret = -1;
+ break;
+
+ default:
+ assert(1); /* Invalid state. */
+ }
+
+ /* always release the event */
+ if (!fyp)
+ fy_eventp_release(fyep);
+ else
+ fy_parse_eventp_recycle(fyp, fyep);
+
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+int fy_emit_event(struct fy_emitter *emit, struct fy_event *fye)
+{
+ return fy_emit_event_from_parser(emit, NULL, fye);
+}
+
+struct fy_document_state *
+fy_emitter_get_document_state(struct fy_emitter *emit)
+{
+ return emit ? emit->fyds : NULL;
+}
+
+int fy_emitter_default_output(struct fy_emitter *fye, enum fy_emitter_write_type type, const char *str, int len, void *userdata)
+{
+ struct fy_emitter_default_output_data d_local, *d;
+ FILE *fp;
+ int ret, w;
+ const char *color = NULL;
+ const char *s, *e;
+
+ d = userdata;
+ if (!d) {
+ /* kinda inneficient but should not matter */
+ d = &d_local;
+ d->fp = stdout;
+ d->colorize = isatty(STDOUT_FILENO);
+ d->visible = false;
+ }
+ fp = d->fp;
+
+ s = str;
+ e = str + len;
+ if (d->colorize) {
+ switch (type) {
+ case fyewt_document_indicator:
+ color = "\x1b[36m";
+ break;
+ case fyewt_tag_directive:
+ case fyewt_version_directive:
+ color = "\x1b[33m";
+ break;
+ case fyewt_indent:
+ if (d->visible) {
+ fputs("\x1b[32m", fp);
+ while (s < e && (w = fy_utf8_width_by_first_octet(((uint8_t)*s))) > 0) {
+ /* open box - U+2423 */
+ fputs("\xe2\x90\xa3", fp);
+ s += w;
+ }
+ fputs("\x1b[0m", fp);
+ return len;
+ }
+ break;
+ case fyewt_indicator:
+ if (len == 1 && (str[0] == '\'' || str[0] == '"'))
+ color = "\x1b[33m";
+ else if (len == 1 && str[0] == '&')
+ color = "\x1b[32;1m";
+ else
+ color = "\x1b[35m";
+ break;
+ case fyewt_whitespace:
+ if (d->visible) {
+ fputs("\x1b[32m", fp);
+ while (s < e && (w = fy_utf8_width_by_first_octet(((uint8_t)*s))) > 0) {
+ /* symbol for space - U+2420 */
+ /* symbol for interpunct - U+00B7 */
+ fputs("\xc2\xb7", fp);
+ s += w;
+ }
+ fputs("\x1b[0m", fp);
+ return len;
+ }
+ break;
+ case fyewt_plain_scalar:
+ color = "\x1b[37;1m";
+ break;
+ case fyewt_single_quoted_scalar:
+ case fyewt_double_quoted_scalar:
+ color = "\x1b[33m";
+ break;
+ case fyewt_literal_scalar:
+ case fyewt_folded_scalar:
+ color = "\x1b[33m";
+ break;
+ case fyewt_anchor:
+ case fyewt_tag:
+ case fyewt_alias:
+ color = "\x1b[32;1m";
+ break;
+ case fyewt_linebreak:
+ if (d->visible) {
+ fputs("\x1b[32m", fp);
+ while (s < e && (w = fy_utf8_width_by_first_octet(((uint8_t)*s))) > 0) {
+ /* symbol for space - ^M */
+ /* fprintf(fp, "^M\n"); */
+ /* down arrow - U+2193 */
+ fputs("\xe2\x86\x93\n", fp);
+ s += w;
+ }
+ fputs("\x1b[0m", fp);
+ return len;
+ }
+ color = NULL;
+ break;
+ case fyewt_terminating_zero:
+ color = NULL;
+ break;
+ case fyewt_plain_scalar_key:
+ case fyewt_single_quoted_scalar_key:
+ case fyewt_double_quoted_scalar_key:
+ color = "\x1b[36;1m";
+ break;
+ case fyewt_comment:
+ color = "\x1b[34;1m";
+ break;
+ }
+ }
+
+ /* don't output the terminating zero */
+ if (type == fyewt_terminating_zero)
+ return len;
+
+ if (color)
+ fputs(color, fp);
+
+ ret = fwrite(str, 1, len, fp);
+
+ if (color)
+ fputs("\x1b[0m", fp);
+
+ return ret;
+}
+
+int fy_document_default_emit_to_fp(struct fy_document *fyd, FILE *fp)
+{
+ struct fy_emitter emit_local, *emit = &emit_local;
+ struct fy_emitter_cfg ecfg_local, *ecfg = &ecfg_local;
+ struct fy_emitter_default_output_data d_local, *d = &d_local;
+ int rc;
+
+ memset(d, 0, sizeof(*d));
+ d->fp = fp;
+ d->colorize = isatty(fileno(fp));
+ d->visible = false;
+
+ memset(ecfg, 0, sizeof(*ecfg));
+ ecfg->diag = fyd->diag;
+ ecfg->userdata = d;
+
+ rc = fy_emit_setup(emit, ecfg);
+ if (rc)
+ goto err_setup;
+
+ fy_emit_prepare_document_state(emit, fyd->fyds);
+
+ rc = 0;
+ if (fyd->root)
+ rc = fy_emit_node_check(emit, fyd->root);
+
+ rc = fy_emit_document_no_check(emit, fyd);
+ if (rc)
+ goto err_emit;
+
+ fy_emit_cleanup(emit);
+
+ return 0;
+
+err_emit:
+ fy_emit_cleanup(emit);
+err_setup:
+ return -1;
+}