From patchwork Thu Feb 11 11:38:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Chris Wilson X-Patchwork-Id: 12082935 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-21.7 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C50BAC433E9 for ; Thu, 11 Feb 2021 11:39:11 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D6BDF64E16 for ; Thu, 11 Feb 2021 11:39:08 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org D6BDF64E16 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=chris-wilson.co.uk Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=intel-gfx-bounces@lists.freedesktop.org Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 6D2086EE04; Thu, 11 Feb 2021 11:39:08 +0000 (UTC) Received: from fireflyinternet.com (unknown [77.68.26.236]) by gabe.freedesktop.org (Postfix) with ESMTPS id C5DB26EE04 for ; Thu, 11 Feb 2021 11:39:06 +0000 (UTC) X-Default-Received-SPF: pass (skip=forwardok (res=PASS)) x-ip-name=78.156.69.177; Received: from build.alporthouse.com (unverified [78.156.69.177]) by fireflyinternet.com (Firefly Internet (M1)) with ESMTP id 23837356-1500050 for multiple; Thu, 11 Feb 2021 11:38:50 +0000 From: Chris Wilson To: intel-gfx@lists.freedesktop.org Date: Thu, 11 Feb 2021 11:38:48 +0000 Message-Id: <20210211113848.16341-1-chris@chris-wilson.co.uk> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH] lib: Add a YAML emitter X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Jani Nikula , Chris Wilson Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" Provide a library to generate correct YAML for use in structured debugfs or similar information dumps. This will be useful to pull our large information dumps into a forwards compatible, parse-able file-format by forcing some structure upon ourselves! Originally from https://github.com/yaml/libyaml/blob/master/src/emitter.c under a permissive MIT licence. Signed-off-by: Chris Wilson Cc: Jani Nikula Cc: Joonas Lahtinen --- Converting to kerneldoc is about the last major task. It uses an opencoded stack struct which might be nice to convert to a library, maybe just use XArray? It has been dogfooded to convert i915_engine_info (and friends) https://patchwork.freedesktop.org/patch/353209/?series=73403&rev=1 --- include/linux/yaml-events.h | 259 ++++ include/linux/yaml.h | 190 +++ lib/Kconfig | 11 + lib/Makefile | 2 + lib/yaml/Makefile | 8 + lib/yaml/test-yaml.c | 123 ++ lib/yaml/yaml-emitter.c | 2539 +++++++++++++++++++++++++++++++++++ lib/yaml/yaml-emitter.h | 140 ++ lib/yaml/yaml-events.c | 424 ++++++ lib/yaml/yaml-simple.c | 227 ++++ 10 files changed, 3923 insertions(+) create mode 100644 include/linux/yaml-events.h create mode 100644 include/linux/yaml.h create mode 100644 lib/yaml/Makefile create mode 100644 lib/yaml/test-yaml.c create mode 100644 lib/yaml/yaml-emitter.c create mode 100644 lib/yaml/yaml-emitter.h create mode 100644 lib/yaml/yaml-events.c create mode 100644 lib/yaml/yaml-simple.c diff --git a/include/linux/yaml-events.h b/include/linux/yaml-events.h new file mode 100644 index 000000000000..33d1bb227e65 --- /dev/null +++ b/include/linux/yaml-events.h @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2006-2016 Kirill Simonov + * Copyright (c) 2017-2019 Ingy döt Net + * Copyright (c) 2020 Intel Corporation + */ + +#ifndef __LINUX_YAML_EVENTS_H__ +#define __LINUX_YAML_EVENTS_H__ + +#include +#include +#include + +/** The version directive data. */ +struct yaml_version_directive { + int major; + int minor; +}; + +/** The tag directive data. */ +struct yaml_tag_directive { + char *handle; + char *prefix; +}; + +/** The pointer position. */ +struct yaml_mark { + size_t index; + size_t line; + size_t column; +}; + +enum yaml_event_type { + YAML_NO_EVENT = 0, + + YAML_STREAM_START_EVENT, + YAML_STREAM_END_EVENT, + + YAML_DOCUMENT_START_EVENT, + YAML_DOCUMENT_END_EVENT, + + YAML_ALIAS_EVENT, + YAML_SCALAR_EVENT, + + YAML_SEQUENCE_START_EVENT, + YAML_SEQUENCE_END_EVENT, + + YAML_MAPPING_START_EVENT, + YAML_MAPPING_END_EVENT +}; + +struct yaml_event { + enum yaml_event_type type; + struct list_head link; + + union { + /** The stream parameters (for YAML_STREAM_START_EVENT). */ + struct { + } stream_start; + + /** The document parameters (for YAML_DOCUMENT_START_EVENT). */ + struct { + struct yaml_version_directive *version; + + struct { + struct yaml_tag_directive *start; + struct yaml_tag_directive *end; + } tags; + + int implicit; + } document_start; + + /** The document end parameters (for YAML_DOCUMENT_END_EVENT). */ + struct { + int implicit; + } document_end; + + /** The alias parameters (for YAML_ALIAS_EVENT). */ + struct { + char *anchor; + } alias; + + /** The scalar parameters (for YAML_SCALAR_EVENT). */ + struct { + char *anchor; + char *tag; + char *value; + size_t length; + int plain_implicit; + int quoted_implicit; + enum yaml_scalar_style style; + } scalar; + + /** The sequence parameters (for YAML_SEQUENCE_START_EVENT). */ + struct { + char *anchor; + char *tag; + int implicit; + enum yaml_sequence_style style; + } sequence_start; + + /** The mapping parameters (for YAML_MAPPING_START_EVENT). */ + struct { + char *anchor; + char *tag; + int implicit; + enum yaml_mapping_style style; + } mapping_start; + }; + + struct yaml_mark start_mark; + struct yaml_mark end_mark; +}; + +struct yaml_event *yaml_stream_start_event_create(gfp_t gfp); +struct yaml_event *yaml_stream_end_event_create(gfp_t gfp); + +/** + * Create the DOCUMENT-START event. + * + * The @a implicit argument is considered as a stylistic parameter and may be + * ignored by the emitter. + * + * @param[in] version_directive The %YAML directive value or + * @c NULL. + * @param[in] tag_directives_start The beginning of the %TAG + * directives list. + * @param[in] tag_directives_end The end of the %TAG directives + * list. + * @param[in] implicit If the document start indicator is + * implicit. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +struct yaml_event * +yaml_document_start_event_create(struct yaml_version_directive *version, + struct yaml_tag_directive *start, + struct yaml_tag_directive *end, + int implicit, + gfp_t gfp); + +/** + * Create the DOCUMENT-END event. + * + * The @a implicit argument is considered as a stylistic parameter and may be + * ignored by the emitter. + * + * @param[in] implicit If the document end indicator is implicit. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +struct yaml_event * +yaml_document_end_event_create(int implicit, gfp_t gfp); + +/** + * Create an ALIAS event. + * + * @param[in] anchor The anchor value. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +struct yaml_event * +yaml_alias_event_create(const char *anchor, gfp_t gfp); + +/** + * Create a SCALAR event. + * + * The @a style argument may be ignored by the emitter. + * + * Either the @a tag attribute or one of the @a plain_implicit and + * @a quoted_implicit flags must be set. + * + * @param[in] anchor The scalar anchor or @c NULL. + * @param[in] tag The scalar tag or @c NULL. + * @param[in] value The scalar value. + * @param[in] length The length of the scalar value. + * @param[in] plain_implicit If the tag may be omitted for the plain + * style. + * @param[in] quoted_implicit If the tag may be omitted for any + * non-plain style. + * @param[in] style The scalar style. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +struct yaml_event * +yaml_scalar_event_create(const char *anchor, const char *tag, + const char *value, ssize_t length, bool steal, + int plain_implicit, int quoted_implicit, + enum yaml_scalar_style style, + gfp_t gfp); + +/** + * Create a SEQUENCE-START event. + * + * The @a style argument may be ignored by the emitter. + * + * Either the @a tag attribute or the @a implicit flag must be set. + * + * @param[in] anchor The sequence anchor or @c NULL. + * @param[in] tag The sequence tag or @c NULL. + * @param[in] implicit If the tag may be omitted. + * @param[in] style The sequence style. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +struct yaml_event * +yaml_sequence_start_event_create(const char *anchor, + const char *tag, + int implicit, + enum yaml_sequence_style style, + gfp_t gfp); + +/** + * Create a SEQUENCE-END event. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +struct yaml_event * +yaml_sequence_end_event_create(gfp_t gfp); + +/** + * Create a MAPPING-START event. + * + * The @a style argument may be ignored by the emitter. + * + * Either the @a tag attribute or the @a implicit flag must be set. + * + * @param[in] anchor The mapping anchor or @c NULL. + * @param[in] tag The mapping tag or @c NULL. + * @param[in] implicit If the tag may be omitted. + * @param[in] style The mapping style. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +struct yaml_event * +yaml_mapping_start_event_create(const char *anchor, + const char *tag, + int implicit, + enum yaml_mapping_style style, + gfp_t gfp); + +/** + * Create a MAPPING-END event. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +struct yaml_event * +yaml_mapping_end_event_create(gfp_t gfp); + +/** + * Free any memory allocated for an event object. + * + * @param[in,out] event An event object. + */ + +void yaml_event_delete(struct yaml_event *event); + +#endif /* __LINUX_YAML_EVENTS_H__ */ diff --git a/include/linux/yaml.h b/include/linux/yaml.h new file mode 100644 index 000000000000..541095a1669e --- /dev/null +++ b/include/linux/yaml.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2006-2016 Kirill Simonov + * Copyright (c) 2017-2019 Ingy döt Net + * Copyright (c) 2020 Intel Corporation + */ + +#ifndef __LINUX_YAML_H__ +#define __LINUX_YAML_H__ + +#include +#include + +struct seq_file; + +struct yaml_emitter; +struct yaml_event; + +/** Scalar styles. */ +enum yaml_scalar_style { + YAML_ANY_SCALAR_STYLE = 0, /** Let the emitter choose the style. */ + + YAML_PLAIN_SCALAR_STYLE, + + YAML_SINGLE_QUOTED_SCALAR_STYLE, + YAML_DOUBLE_QUOTED_SCALAR_STYLE, + + YAML_LITERAL_SCALAR_STYLE, + YAML_FOLDED_SCALAR_STYLE, +}; + +/** Sequence styles. */ +enum yaml_sequence_style { + YAML_ANY_SEQUENCE_STYLE = 0, /** Let the emitter choose the style. */ + + YAML_BLOCK_SEQUENCE_STYLE, + YAML_FLOW_SEQUENCE_STYLE, +}; + +/** Mapping styles. */ +enum yaml_mapping_style { + YAML_ANY_MAPPING_STYLE = 0, /** Let the emitter choose the style. */ + + YAML_BLOCK_MAPPING_STYLE, + YAML_FLOW_MAPPING_STYLE +}; + +typedef int (*yaml_write_t)(void *data, const u8 *buffer, size_t size); + +/** + * Initialize an emitter. + * + * This function creates a new emitter object. An application is responsible + * for destroying the object using the yaml_emitter_delete() function. + * + * @param[out] emitter An empty parser object. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +struct yaml_emitter *yaml_emitter_create(gfp_t gfp); + +/** + * Destroy an emitter. + * + * @param[in,out] emitter An emitter object. + */ +void yaml_emitter_destroy(struct yaml_emitter *emitter); + +/** + * Set a string output. + * + * The emitter will write the output characters to the @a output buffer of the + * size @a size. The emitter will set @a size_written to the number of written + * bytes. If the buffer is smaller than required, the emitter produces the + * YAML_WRITE_ERROR error. + * + * @param[in,out] emitter An emitter object. + * @param[in] output An output buffer. + * @param[in] size The buffer size. + * @param[in] size_written The pointer to save the number of written + * bytes. + */ +void yaml_emitter_set_string(struct yaml_emitter *emitter, + u8 *output, size_t size, + size_t *size_written); + +void yaml_emitter_set_seq_file(struct yaml_emitter *emitter, + struct seq_file *file); + +void yaml_emitter_set_output(struct yaml_emitter *emitter, + yaml_write_t handler, + void *data); + +/** + * Set if the output should be in the "canonical" format as in the YAML + * specification. + * + * @param[in,out] emitter An emitter object. + * @param[in] canonical If the output is canonical. + */ +void yaml_emitter_set_canonical(struct yaml_emitter *emitter, bool canonical); + +/** + * Set the indentation increment. + * + * @param[in,out] emitter An emitter object. + * @param[in] indent The indentation increment (1 < . < 10). + */ +void yaml_emitter_set_indent(struct yaml_emitter *emitter, int indent); + +/** + * Set the preferred line width. @c -1 means unlimited. + * + * @param[in,out] emitter An emitter object. + * @param[in] width The preferred line width. + */ +void yaml_emitter_set_width(struct yaml_emitter *emitter, int width); + +/** + * Set if unescaped non-ASCII characters are allowed. + * + * @param[in,out] emitter An emitter object. + * @param[in] unicode If unescaped Unicode characters are allowed. + */ +void yaml_emitter_set_unicode(struct yaml_emitter *emitter, bool unicode); + +/** + * Emit an event. + * + * The event object may be generated using the yaml_parser_parse() function. + * The emitter takes the responsibility for the event object and destroys its + * content after it is emitted. The event object is destroyed even if the + * function fails. + * + * @param[in,out] emitter An emitter object. + * @param[in,out] event An event object. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +void yaml_emitter_emit(struct yaml_emitter *emitter, struct yaml_event *event); + +/** + * Flush the accumulated characters to the output. + * + * @param[in,out] emitter An emitter object. + * + * @returns @c 1 if the function succeeded, @c 0 on error. + */ +int yaml_emitter_flush(struct yaml_emitter *emitter, const char **problem); + +gfp_t yaml_emitter_set_gfp(struct yaml_emitter *emitter, gfp_t gfp); + +struct yaml_emitter *yaml_open(yaml_write_t handler, void *data); +struct yaml_emitter *yaml_open_file(struct seq_file *m); +struct yaml_emitter *yaml_open_string(char *buf, size_t len, size_t *written); + +void __yaml_mapping_start(struct yaml_emitter *emitter, + enum yaml_mapping_style style); +static inline void yaml_mapping_start(struct yaml_emitter *emitter) +{ + __yaml_mapping_start(emitter, YAML_ANY_MAPPING_STYLE); +} + +void yaml_mapping_end(struct yaml_emitter *emitter); + +void __yaml_sequence_start(struct yaml_emitter *emitter, + enum yaml_sequence_style style); +static inline void yaml_sequence_start(struct yaml_emitter *emitter) +{ + __yaml_sequence_start(emitter, YAML_ANY_SEQUENCE_STYLE); +} + +void yaml_sequence_end(struct yaml_emitter *emitter); + +__printf(2, 3) +void yaml_alias_printf(struct yaml_emitter *emitter, const char *fmt, ...); + +__printf(2, 3) +void yaml_scalar_printf(struct yaml_emitter *emitter, const char *fmt, ...); + +__printf(3, 4) +void yaml_pair_printf(struct yaml_emitter *emitter, + const char *name, const char *fmt, ...); + +void yaml_ascii85_encode(struct yaml_emitter *emitter, + const void *data, size_t len, bool compressed); + +int yaml_close(struct yaml_emitter *emitter, const char **problem); + +#endif /* __LINUX_YAML_H__ */ diff --git a/lib/Kconfig b/lib/Kconfig index 46806332a8cc..4829719e8b9b 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -663,6 +663,17 @@ config OBJAGG config STRING_SELFTEST tristate "Test string functions" +config YAML + tristate + +config YAML_EMITTER + tristate + select YAML + +config TEST_YAML + tristate "Small test module for libyaml" + depends on YAML + endmenu config GENERIC_IOREMAP diff --git a/lib/Makefile b/lib/Makefile index afeff05fa8c5..0ace23ef51bc 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -285,6 +285,8 @@ obj-$(CONFIG_ASN1) += asn1_decoder.o obj-$(CONFIG_FONT_SUPPORT) += fonts/ +obj-$(CONFIG_YAML) += yaml/ + hostprogs := gen_crc32table hostprogs += gen_crc64table clean-files := crc32table.h diff --git a/lib/yaml/Makefile b/lib/yaml/Makefile new file mode 100644 index 000000000000..f5e8c1795914 --- /dev/null +++ b/lib/yaml/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 + +yaml-objs = yaml-events.o +yaml-objs-$(CONFIG_YAML_EMITTER) += yaml-emitter.o yaml-simple.o +yaml-objs += $(yaml-objs-m) + +obj-$(CONFIG_YAML) += yaml.o +obj-$(CONFIG_TEST_YAML) += test-yaml.o diff --git a/lib/yaml/test-yaml.c b/lib/yaml/test-yaml.c new file mode 100644 index 000000000000..5c3c7a928859 --- /dev/null +++ b/lib/yaml/test-yaml.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL +/* + * Copyright (c) 2020 Intel Corporation + */ +#include +#include +#include +#include + +struct dentry *root; + +#if IS_ENABLED(CONFIG_YAML_EMITTER) +static int y_events_show(struct seq_file *m, void *data) +{ + struct yaml_emitter *emitter; + struct yaml_event *event; + const char *problem; + + emitter = yaml_emitter_create(GFP_KERNEL); + if (!emitter) + return -ENOMEM; + + yaml_emitter_set_seq_file(emitter, m); + + event = yaml_stream_start_event_create(GFP_KERNEL); + yaml_emitter_emit(emitter, event); + + event = yaml_document_start_event_create(NULL, NULL, NULL, true, + GFP_KERNEL); + yaml_emitter_emit(emitter, event); + + event = yaml_mapping_start_event_create(NULL, NULL, true, + YAML_FLOW_MAPPING_STYLE, + GFP_KERNEL); + yaml_emitter_emit(emitter, event); + + event = yaml_scalar_event_create(NULL, NULL, + "General", -1, false, + true, true, YAML_PLAIN_SCALAR_STYLE, + GFP_KERNEL); + yaml_emitter_emit(emitter, event); + + event = yaml_scalar_event_create(NULL, NULL, + "Kenobi", -1, false, + true, true, YAML_PLAIN_SCALAR_STYLE, + GFP_KERNEL); + yaml_emitter_emit(emitter, event); + + event = yaml_mapping_end_event_create(GFP_KERNEL); + yaml_emitter_emit(emitter, event); + + event = yaml_document_end_event_create(true, GFP_KERNEL); + yaml_emitter_emit(emitter, event); + + event = yaml_stream_end_event_create(GFP_KERNEL); + yaml_emitter_emit(emitter, event); + + if (yaml_emitter_flush(emitter, &problem)) + pr_err("test-yaml events failed: %s\n", problem); + + yaml_emitter_destroy(emitter); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(y_events); + +static int y_simple_show(struct seq_file *m, void *data) +{ + struct yaml_emitter *yaml; + const char *problem; + + yaml = yaml_open_file(m); + if (!yaml) + return -ENOMEM; + + yaml_mapping_start(yaml); { + yaml_scalar_printf(yaml, "map"); + yaml_mapping_start(yaml); { + yaml_scalar_printf(yaml, "Hello"); + yaml_mapping_start(yaml); { + yaml_pair_printf(yaml, + "General", "%s", "Kenobi"); + } yaml_mapping_end(yaml); + } yaml_mapping_end(yaml); + + yaml_scalar_printf(yaml, "sequence"); + yaml_sequence_start(yaml); { + yaml_scalar_printf(yaml, "1"); + yaml_scalar_printf(yaml, "2"); + yaml_scalar_printf(yaml, "3"); + yaml_ascii85_encode(yaml, "4", 2, false); + } yaml_sequence_end(yaml); + } yaml_mapping_end(yaml); + + if (yaml_close(yaml, &problem)) + pr_err("test-yaml simple failed: %s\n", problem); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(y_simple); +#endif + +static int __init test_yaml_init(void) +{ + root = debugfs_create_dir("libyaml", NULL); + if (!root) + return -ENOMEM; + +#if IS_ENABLED(CONFIG_YAML_EMITTER) + debugfs_create_file("emit-events", 0444, root, NULL, &y_events_fops); + debugfs_create_file("emit-simple", 0444, root, NULL, &y_simple_fops); +#endif + + return 0; +} + +static void __exit test_yaml_exit(void) +{ + debugfs_remove(root); +} + +module_init(test_yaml_init); +module_exit(test_yaml_exit); +MODULE_LICENSE("GPL"); diff --git a/lib/yaml/yaml-emitter.c b/lib/yaml/yaml-emitter.c new file mode 100644 index 000000000000..ab45070fc933 --- /dev/null +++ b/lib/yaml/yaml-emitter.c @@ -0,0 +1,2539 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2006-2016 Kirill Simonov + * Copyright (c) 2017-2019 Ingy döt Net + * Copyright (c) 2020 Intel Corporation + */ +#include +#include +#include +#include +#include +#include +#include + +#include "yaml-emitter.h" + +#define INITIAL_STACK_SIZE 16 + +struct yaml_string { + const char *start; + const char *end; + const char *pos; +}; + +#define STRING_INIT(str, len) { (str), (str) + (len), (str) } + +#define YAML_STRING(name, str) \ + struct yaml_string name = STRING_INIT(str, strlen(str)) + +#define CHECK_AT(s, octet, offset) \ + ((s).pos[offset] == (char)(octet)) +#define CHECK(s, octet) (CHECK_AT((s), (octet), 0)) + +#define IS_ALPHA_AT(s, offset) \ + (isalnum((s).pos[offset]) || \ + (s).pos[offset] == '_' || \ + (s).pos[offset] == '-') +#define IS_ALPHA(s) IS_ALPHA_AT((s), 0) + +#define IS_ASCII_AT(s, offset) ((s).pos[offset] <= '\x7F') +#define IS_ASCII(s) IS_ASCII_AT((s), 0) + +#define IS_PRINTABLE_AT(s, offset) isprint((s).pos[offset]) +#define IS_PRINTABLE(s) IS_PRINTABLE_AT((s), 0) + +#define IS_Z_AT(s, offset) CHECK_AT((s), '\0', (offset)) +#define IS_Z(s) IS_Z_AT((s), 0) + +#define IS_BOM_AT(s, offset) \ + (CHECK_AT((s), '\xEF', (offset) + 0) && \ + CHECK_AT((s), '\xBB', (offset) + 1) && \ + CHECK_AT((s), '\xBF', (offset) + 2)) /* BOM (#xFEFF) */ +#define IS_BOM(s) IS_BOM_AT(s, 0) + +#define IS_SPACE_AT(s, offset) CHECK_AT((s), ' ', (offset)) +#define IS_SPACE(s) IS_SPACE_AT((s), 0) + +#define IS_TAB_AT(s, offset) CHECK_AT((s), '\t', (offset)) +#define IS_TAB(s) IS_TAB_AT((s), 0) + +#define IS_BLANK_AT(s, offset) \ + (IS_SPACE_AT((s), (offset)) || IS_TAB_AT((s), (offset))) +#define IS_BLANK(s) IS_BLANK_AT((s), 0) + +#define IS_BREAK_AT(s, offset) \ + (CHECK_AT((s), '\r', (offset)) || /* CR (#xD)*/ \ + CHECK_AT((s), '\n', (offset)) || /* LF (#xA) */ \ + (CHECK_AT((s), '\xC2', (offset)) && \ + CHECK_AT((s), '\x85', (offset) + 1)) || /* NEL (#x85) */ \ + (CHECK_AT((s), '\xE2', (offset)) && \ + CHECK_AT((s), '\x80', (offset) + 1) && \ + CHECK_AT((s), '\xA8', (offset) + 2)) || /* LS (#x2028) */ \ + (CHECK_AT((s), '\xE2', (offset)) && \ + CHECK_AT((s), '\x80', (offset) + 1) && \ + CHECK_AT((s), '\xA9', (offset) + 2))) /* PS (#x2029) */ +#define IS_BREAK(s) IS_BREAK_AT((s), 0) + +#define IS_CRLF_AT(s, offset) \ + (CHECK_AT((s), '\r', (offset)) && CHECK_AT((s), '\n', (offset) + 1)) +#define IS_CRLF(s) IS_CRLF_AT((s), 0) + +#define IS_BREAKZ_AT(s, offset) \ + (IS_BREAK_AT((s), (offset)) || IS_Z_AT((s), (offset))) +#define IS_BREAKZ(s) IS_BREAKZ_AT((s), 0) + +#define IS_SPACEZ_AT(s, offset) \ + (IS_SPACE_AT((s), (offset)) || IS_BREAKZ_AT((s), (offset))) +#define IS_SPACEZ(s) IS_SPACEZ_AT((s), 0) + +#define IS_BLANKZ_AT(s, offset) \ + (IS_BLANK_AT((s), (offset)) || IS_BREAKZ_AT((s), (offset))) +#define IS_BLANKZ(s) IS_BLANKZ_AT((s), 0) + +static inline int __utf8_width(const struct yaml_string *s, int idx) +{ + return ((s->pos[idx] & 0x80) == 0x00 ? 1 : + (s->pos[idx] & 0xE0) == 0xC0 ? 2 : + (s->pos[idx] & 0xF0) == 0xE0 ? 3 : + (s->pos[idx] & 0xF8) == 0xF0 ? 4 : + 0); +} + +static inline int utf8_width(const struct yaml_string *s) +{ + return __utf8_width(s, 0); +} + +#define ADVANCE(s) ((s).pos += utf8_width((&s))) + +static bool yaml_stack_extend(void **start, void **top, void **end); + +#define EMPTY(context, stack) ((stack).start == (stack).top) + +#define PUSH(context, stack, value) \ + (((stack).top != (stack).end || \ + !yaml_stack_extend((void **)&(stack).start, \ + (void **)&(stack).top, (void **)&(stack).end)) ? \ + (*((stack).top++) = value, 0) : \ + ((context)->errno = -ENOMEM)) + +#define POP(context, stack) (*(--(stack).top)) + +static int +__set_error(struct yaml_emitter *emitter, const char *problem, int err) +{ + WARN_ON(emitter->errno); + + emitter->errno = err; + emitter->problem = problem; + + return err; +} + +static int set_error(struct yaml_emitter *emitter, const char *problem) +{ + return __set_error(emitter, problem, EINVAL); +} + +static int flush(struct yaml_emitter *emitter) +{ + int err; + + if (unlikely(emitter->errno)) + return emitter->errno; + + emitter->buf.last = emitter->buf.pos; + emitter->buf.pos = emitter->buf.start; + + if (emitter->buf.start == emitter->buf.last) + return 0; + + err = emitter->write_handler(emitter->write_data, + emitter->buf.start, + emitter->buf.last - emitter->buf.start); + if (unlikely(err < 0)) + return __set_error(emitter, "write error", err); + + emitter->buf.last = emitter->buf.start; + emitter->buf.pos = emitter->buf.start; + return 0; +} + +static int maybe_flush(struct yaml_emitter *emitter, int len) +{ + if (emitter->buf.pos + len < emitter->buf.end) + return 0; + + return flush(emitter); +} + +static int put(struct yaml_emitter *emitter, const char value) +{ + int err; + + err = maybe_flush(emitter, 5); + if (err) + return err; + + *emitter->buf.pos++ = value; + emitter->column++; + return 0; +} + +static int put_break(struct yaml_emitter *emitter) +{ + int err; + + err = put(emitter, '\n'); + if (err) + return err; + + emitter->column = 0; + return 0; +} + +static int put_char(struct yaml_emitter *emitter, struct yaml_string *str) +{ + const int width = utf8_width(str); + int err; + + err = maybe_flush(emitter, width); + if (err) + return err; + + memcpy(emitter->buf.pos, str->pos, width); + emitter->buf.pos += width; + str->pos += width; + + emitter->column++; + return 0; +} + +static int put_char_break(struct yaml_emitter *emitter, struct yaml_string *str) +{ + const int width = utf8_width(str); + int err; + + err = maybe_flush(emitter, width); + if (err) + return err; + + memcpy(emitter->buf.pos, str->pos, width); + emitter->buf.pos += width; + str->pos += width; + + emitter->column = 0; + return 0; +} + +static struct yaml_event *next_event(const struct yaml_emitter *emitter) +{ + return list_first_entry(&emitter->events, struct yaml_event, link); +} + +static bool need_more_events(struct yaml_emitter *emitter) +{ + struct yaml_event *event; + int acc; + + if (list_empty(&emitter->events)) + return true; + + acc = 0; + switch (next_event(emitter)->type) { + case YAML_DOCUMENT_START_EVENT: + acc = 1; + break; + case YAML_SEQUENCE_START_EVENT: + acc = 2; + break; + case YAML_MAPPING_START_EVENT: + acc = 3; + break; + + default: + return false; + } + + if (emitter->num_events > acc) + return false; + + acc = 0; + list_for_each_entry(event, &emitter->events, link) { + switch (event->type) { + case YAML_STREAM_START_EVENT: + case YAML_DOCUMENT_START_EVENT: + case YAML_SEQUENCE_START_EVENT: + case YAML_MAPPING_START_EVENT: + acc++; + break; + + case YAML_STREAM_END_EVENT: + case YAML_DOCUMENT_END_EVENT: + case YAML_SEQUENCE_END_EVENT: + case YAML_MAPPING_END_EVENT: + acc--; + break; + + default: + break; + } + + if (!acc) + return false; + } + + return true; +} + +static int +analyze_version_directive(struct yaml_emitter *emitter, + struct yaml_version_directive version) +{ + if (version.major != 1 || version.minor != 1) + return set_error(emitter, "incompatible %YAML directive"); + + return 0; +} + +static int +analyze_tag_directive(struct yaml_emitter *emitter, + struct yaml_tag_directive tag) +{ + YAML_STRING(handle, tag.handle); + YAML_STRING(prefix, tag.prefix); + + if (handle.start == handle.end) + return set_error(emitter, "tag handle must not be empty"); + + if (handle.start[0] != '!') + return set_error(emitter, "tag handle must start with '!'"); + + if (handle.end[-1] != '!') + return set_error(emitter, "tag handle must end with '!'"); + + handle.pos++; + + while (handle.pos < handle.end - 1) { + if (!IS_ALPHA(handle)) + return set_error(emitter, + "tag handle must contain alphanumerical characters only"); + + ADVANCE(handle); + } + + if (prefix.start == prefix.end) + return set_error(emitter, "tag prefix must not be empty"); + + return 0; +} + +static int +analyze_anchor(struct yaml_emitter *emitter, const char *anchor, int alias) +{ + YAML_STRING(string, anchor); + + if (string.start == string.end) { + return set_error(emitter, alias ? + "alias value must not be empty" : + "anchor value must not be empty"); + } + + while (string.pos != string.end) { + if (!IS_ALPHA(string)) { + return set_error(emitter, alias ? + "alias value must contain alphanumerical characters only" : + "anchor value must contain alphanumerical characters only"); + } + ADVANCE(string); + } + + emitter->anchor_data.anchor = string.start; + emitter->anchor_data.anchor_length = string.end - string.start; + emitter->anchor_data.alias = alias; + return 0; +} + +static int analyze_tag(struct yaml_emitter *emitter, const char *str) +{ + YAML_STRING(string, str); + struct yaml_tag_directive *tag; + + if (string.start == string.end) + return set_error(emitter, "tag value must not be empty"); + + for (tag = emitter->tags.start; tag != emitter->tags.top; tag++) { + size_t prefix_length = strlen(tag->prefix); + + if (prefix_length < string.end - string.start && + strncmp(tag->prefix, string.start, prefix_length) == 0) { + emitter->tag_data.handle = tag->handle; + emitter->tag_data.handle_length = strlen(tag->handle); + emitter->tag_data.suffix = string.start + prefix_length; + emitter->tag_data.suffix_length = + (string.end - string.start) - prefix_length; + return 0; + } + } + + emitter->tag_data.suffix = string.start; + emitter->tag_data.suffix_length = string.end - string.start; + return 0; +} + +static int +analyze_scalar(struct yaml_emitter *emitter, const char *value, size_t length) +{ + struct yaml_string string = STRING_INIT(value, length); + + int block_indicators = 0; + int flow_indicators = 0; + int line_breaks = 0; + int special_characters = 0; + + int leading_space = 0; + int leading_break = 0; + int trailing_space = 0; + int trailing_break = 0; + int break_space = 0; + int space_break = 0; + + int preceded_by_whitespace = 0; + int followed_by_whitespace = 0; + int previous_space = 0; + int previous_break = 0; + + emitter->scalar_data.value = value; + emitter->scalar_data.length = length; + + if (string.start == string.end) { + emitter->scalar_data.multiline = 0; + emitter->scalar_data.flow_plain_allowed = 0; + emitter->scalar_data.block_plain_allowed = 1; + emitter->scalar_data.single_quoted_allowed = 1; + emitter->scalar_data.block_allowed = 0; + return 0; + } + + if ((CHECK_AT(string, '-', 0) && + CHECK_AT(string, '-', 1) && + CHECK_AT(string, '-', 2)) || + (CHECK_AT(string, '.', 0) && + CHECK_AT(string, '.', 1) && + CHECK_AT(string, '.', 2))) { + block_indicators = 1; + flow_indicators = 1; + } + + preceded_by_whitespace = 1; + followed_by_whitespace = IS_BLANKZ_AT(string, utf8_width(&string)); + + while (string.pos != string.end) { + if (string.start == string.pos) { + if (CHECK(string, '#') || CHECK(string, ',') || + CHECK(string, '[') || CHECK(string, ']') || + CHECK(string, '{') || CHECK(string, '}') || + CHECK(string, '&') || CHECK(string, '*') || + CHECK(string, '!') || CHECK(string, '|') || + CHECK(string, '>') || CHECK(string, '\'') || + CHECK(string, '"') || CHECK(string, '%') || + CHECK(string, '@') || CHECK(string, '`')) { + flow_indicators = 1; + block_indicators = 1; + } + + if (CHECK(string, '?') || CHECK(string, ':')) { + flow_indicators = 1; + if (followed_by_whitespace) + block_indicators = 1; + } + + if (CHECK(string, '-') && followed_by_whitespace) { + flow_indicators = 1; + block_indicators = 1; + } + } else { + if (CHECK(string, ',') || CHECK(string, '?') || + CHECK(string, '[') || CHECK(string, ']') || + CHECK(string, '{') || CHECK(string, '}')) + flow_indicators = 1; + + if (CHECK(string, ':')) { + flow_indicators = 1; + if (followed_by_whitespace) + block_indicators = 1; + } + + if (CHECK(string, '#') && preceded_by_whitespace) { + flow_indicators = 1; + block_indicators = 1; + } + } + + if (!IS_PRINTABLE(string) || + (!IS_ASCII(string) && !emitter->unicode)) { + special_characters = 1; + } + + if (IS_BREAK(string)) + line_breaks = 1; + + if (IS_SPACE(string)) { + if (string.start == string.pos) + leading_space = 1; + + if (string.pos + utf8_width(&string) == string.end) + trailing_space = 1; + + if (previous_break) + break_space = 1; + + previous_space = 1; + previous_break = 0; + } else if (IS_BREAK(string)) { + if (string.start == string.pos) + leading_break = 1; + + if (string.pos + utf8_width(&string) == string.end) + trailing_break = 1; + + if (previous_space) + space_break = 1; + + previous_space = 0; + previous_break = 1; + } else { + previous_space = 0; + previous_break = 0; + } + + preceded_by_whitespace = IS_BLANKZ(string); + ADVANCE(string); + if (string.pos != string.end) + followed_by_whitespace = + IS_BLANKZ_AT(string, utf8_width(&string)); + } + + emitter->scalar_data.multiline = line_breaks; + + emitter->scalar_data.flow_plain_allowed = 1; + emitter->scalar_data.block_plain_allowed = 1; + emitter->scalar_data.single_quoted_allowed = 1; + emitter->scalar_data.block_allowed = 1; + + if (leading_space || + leading_break || + trailing_space || + trailing_break) { + emitter->scalar_data.flow_plain_allowed = 0; + emitter->scalar_data.block_plain_allowed = 0; + } + + if (trailing_space) + emitter->scalar_data.block_allowed = 0; + + if (break_space) { + emitter->scalar_data.flow_plain_allowed = 0; + emitter->scalar_data.block_plain_allowed = 0; + emitter->scalar_data.single_quoted_allowed = 0; + } + + if (space_break || special_characters) { + emitter->scalar_data.flow_plain_allowed = 0; + emitter->scalar_data.block_plain_allowed = 0; + emitter->scalar_data.single_quoted_allowed = 0; + emitter->scalar_data.block_allowed = 0; + } + + if (line_breaks) { + emitter->scalar_data.flow_plain_allowed = 0; + emitter->scalar_data.block_plain_allowed = 0; + } + + if (flow_indicators) + emitter->scalar_data.flow_plain_allowed = 0; + + if (block_indicators) + emitter->scalar_data.block_plain_allowed = 0; + + return 0; +} + +static int +analyze_event(struct yaml_emitter *emitter, const struct yaml_event *event) +{ + int err; + + emitter->anchor_data.anchor = NULL; + emitter->anchor_data.anchor_length = 0; + emitter->tag_data.handle = NULL; + emitter->tag_data.handle_length = 0; + emitter->tag_data.suffix = NULL; + emitter->tag_data.suffix_length = 0; + emitter->scalar_data.value = NULL; + emitter->scalar_data.length = 0; + + switch (event->type) { + case YAML_ALIAS_EVENT: + return analyze_anchor(emitter, event->alias.anchor, 1); + + case YAML_SCALAR_EVENT: + if (event->scalar.anchor) { + err = analyze_anchor(emitter, event->scalar.anchor, 0); + if (err) + return err; + } + + if (event->scalar.tag && + (emitter->canonical || + (!event->scalar.plain_implicit && + !event->scalar.quoted_implicit))) { + err = analyze_tag(emitter, event->scalar.tag); + if (err) + return err; + } + + return analyze_scalar(emitter, + event->scalar.value, + event->scalar.length); + + case YAML_SEQUENCE_START_EVENT: + if (event->sequence_start.anchor) { + err = analyze_anchor(emitter, + event->sequence_start.anchor, 0); + if (err) + return err; + } + + if (event->sequence_start.tag && + (emitter->canonical || !event->sequence_start.implicit)) { + err = analyze_tag(emitter, + event->sequence_start.tag); + if (err) + return err; + } + + return 0; + + case YAML_MAPPING_START_EVENT: + if (event->mapping_start.anchor) { + err = analyze_anchor(emitter, + event->mapping_start.anchor, 0); + if (err) + return err; + } + + if (event->mapping_start.tag && + (emitter->canonical || !event->mapping_start.implicit)) { + err = analyze_tag(emitter, event->mapping_start.tag); + if (err) + return err; + } + + return 0; + + default: + return 0; + } +} + +static bool check_empty_document(const struct yaml_emitter *emitter) +{ + return false; +} + +static bool check_empty_sequence(const struct yaml_emitter *emitter) +{ + const struct yaml_event *event; + + if (emitter->num_events < 2) + return false; + + event = next_event(emitter); + return (event->type == YAML_SEQUENCE_START_EVENT && + list_next_entry(event, link)->type == YAML_SEQUENCE_END_EVENT); +} + +static bool check_empty_mapping(const struct yaml_emitter *emitter) +{ + const struct yaml_event *event; + + if (emitter->num_events < 2) + return false; + + event = next_event(emitter); + return (event->type == YAML_MAPPING_START_EVENT && + list_next_entry(event, link)->type == YAML_MAPPING_END_EVENT); +} + +static bool check_simple_key(const struct yaml_emitter *emitter) +{ + const struct yaml_event *event = next_event(emitter); + size_t length = 0; + + switch (event->type) { + case YAML_ALIAS_EVENT: + length += emitter->anchor_data.anchor_length; + break; + + case YAML_SCALAR_EVENT: + if (emitter->scalar_data.multiline) + return false; + + length += + emitter->anchor_data.anchor_length + + emitter->tag_data.handle_length + + emitter->tag_data.suffix_length + + emitter->scalar_data.length; + break; + + case YAML_SEQUENCE_START_EVENT: + if (!check_empty_sequence(emitter)) + return false; + + length += + emitter->anchor_data.anchor_length + + emitter->tag_data.handle_length + + emitter->tag_data.suffix_length; + break; + + case YAML_MAPPING_START_EVENT: + if (!check_empty_mapping(emitter)) + return false; + + length += + emitter->anchor_data.anchor_length + + emitter->tag_data.handle_length + + emitter->tag_data.suffix_length; + break; + + default: + return false; + } + + return length <= 128; +} + +static int increase_indent(struct yaml_emitter *emitter, + bool flow, bool indentless) +{ + int err; + + err = PUSH(emitter, emitter->indents, emitter->indent); + if (err) + return err; + + if (emitter->indent < 0) + emitter->indent = flow ? emitter->best_indent : 0; + else if (!indentless) + emitter->indent += emitter->best_indent; + + return 0; +} + +static int write_indent(struct yaml_emitter *emitter) +{ + int indent = (emitter->indent >= 0) ? emitter->indent : 0; + int err; + + if (!emitter->indention || + emitter->column > indent || + (emitter->column == indent && !emitter->whitespace)) { + err = put_break(emitter); + if (err) + return err; + } + + while (emitter->column < indent) { + err = put(emitter, ' '); + if (err) + return err; + } + + emitter->whitespace = 1; + emitter->indention = 1; + return 0; +} + +static int write_indicator(struct yaml_emitter *emitter, + const char *indicator, int need_whitespace, + int is_whitespace, int is_indention) +{ + YAML_STRING(string, indicator); + int err; + + if (need_whitespace && !emitter->whitespace) { + err = put(emitter, ' '); + if (err) + return err; + } + + while (string.pos != string.end) { + err = put_char(emitter, &string); + if (err) + return err; + } + + emitter->whitespace = is_whitespace; + emitter->indention = (emitter->indention && is_indention); + emitter->open_ended = 0; + return 0; +} + +static int write_anchor(struct yaml_emitter *emitter, + const char *value, size_t length) +{ + struct yaml_string string = STRING_INIT(value, length); + int err; + + while (string.pos != string.end) { + err = put_char(emitter, &string); + if (err) + return err; + } + + emitter->whitespace = 0; + emitter->indention = 0; + return 0; +} + +static int write_tag_handle(struct yaml_emitter *emitter, + const char *value, size_t length) +{ + struct yaml_string string = STRING_INIT(value, length); + int err; + + if (!emitter->whitespace) { + err = put(emitter, ' '); + if (err) + return err; + } + + while (string.pos != string.end) { + err = put_char(emitter, &string); + if (err) + return err; + } + + emitter->whitespace = 0; + emitter->indention = 0; + return 0; +} + +static int write_tag_content(struct yaml_emitter *emitter, + const char *value, size_t length, + int need_whitespace) +{ + struct yaml_string string = STRING_INIT(value, length); + int err; + + if (need_whitespace && !emitter->whitespace) { + err = put(emitter, ' '); + if (err) + return err; + } + + while (string.pos != string.end) { + if (IS_ALPHA(string) || + CHECK(string, ';') || + CHECK(string, '/') || + CHECK(string, '?') || + CHECK(string, ':') || + CHECK(string, '@') || + CHECK(string, '&') || + CHECK(string, '=') || + CHECK(string, '+') || + CHECK(string, '$') || + CHECK(string, ',') || + CHECK(string, '_') || + CHECK(string, '.') || + CHECK(string, '~') || + CHECK(string, '*') || + CHECK(string, '\'') || + CHECK(string, '(') || + CHECK(string, ')') || + CHECK(string, '[') || + CHECK(string, ']')) { + err = put_char(emitter, &string); + if (err) + return err; + } else { + int width = utf8_width(&string); + + while (width--) { + unsigned int value = *string.pos++; + int c; + + err = put(emitter, '%'); + if (err) + return err; + + c = value >> 4; + c += c < 10 ? '0' : 'A' - 10; + err = put(emitter, c); + if (err) + return err; + + c = value & 0x0f; + c += c < 10 ? '0' : 'A' - 10; + err = put(emitter, c); + if (err) + return err; + } + } + } + + emitter->whitespace = 0; + emitter->indention = 0; + return 0; +} + +static int write_tag(struct yaml_emitter *emitter) +{ + int err; + + if (!emitter->tag_data.handle && !emitter->tag_data.suffix) + return 0; + + if (emitter->tag_data.handle) { + err = write_tag_handle(emitter, + emitter->tag_data.handle, + emitter->tag_data.handle_length); + if (err) + return err; + + if (emitter->tag_data.suffix) { + err = write_tag_content(emitter, + emitter->tag_data.suffix, + emitter->tag_data.suffix_length, + 0); + if (err) + return err; + } + } else { + err = write_indicator(emitter, "!<", 1, 0, 0); + if (err) + return err; + + err = write_tag_content(emitter, + emitter->tag_data.suffix, + emitter->tag_data.suffix_length, + 0); + if (err) + return err; + + err = write_indicator(emitter, ">", 0, 0, 0); + if (err) + return err; + } + + return 0; +} + +static int write_plain_scalar(struct yaml_emitter *emitter, + const char *value, + size_t length, + int allow_breaks) +{ + struct yaml_string string = STRING_INIT(value, length); + int spaces = 0; + int breaks = 0; + int err; + + if (!emitter->whitespace) { + err = put(emitter, ' '); + if (err) + return err; + } + + while (string.pos != string.end) { + if (IS_SPACE(string)) { + if (allow_breaks && !spaces && + emitter->column > emitter->best_width && + !IS_SPACE_AT(string, 1)) { + err = write_indent(emitter); + if (err) + return err; + ADVANCE(string); + } else { + err = put_char(emitter, &string); + if (err) + return err; + } + spaces = 1; + } else if (IS_BREAK(string)) { + if (!breaks && CHECK(string, '\n')) { + err = put_break(emitter); + if (err) + return err; + } + + err = put_char_break(emitter, &string); + if (err) + return err; + + emitter->indention = 1; + breaks = 1; + } else { + if (breaks) { + err = write_indent(emitter); + if (err) + return err; + } + + err = put_char(emitter, &string); + if (err) + return err; + + emitter->indention = 0; + spaces = 0; + breaks = 0; + } + } + + emitter->whitespace = 0; + emitter->indention = 0; + if (emitter->root_context) + emitter->open_ended = 1; + return 0; +} + +static int +write_single_quoted_scalar(struct yaml_emitter *emitter, + const char *value, + size_t length, + int allow_breaks) +{ + struct yaml_string string = STRING_INIT(value, length); + int spaces = 0; + int breaks = 0; + int err; + + err = write_indicator(emitter, "'", 1, 0, 0); + if (err) + return err; + + while (string.pos != string.end) { + if (IS_SPACE(string)) { + if (allow_breaks && !spaces && + emitter->column > emitter->best_width && + string.pos != string.start && + string.pos != string.end - 1 && + !IS_SPACE_AT(string, 1)) { + err = write_indent(emitter); + if (err) + return err; + ADVANCE(string); + } else { + err = put_char(emitter, &string); + if (err) + return err; + } + spaces = 1; + } else if (IS_BREAK(string)) { + if (!breaks && CHECK(string, '\n')) { + err = put_break(emitter); + if (err) + return err; + } + + err = put_char_break(emitter, &string); + if (err) + return err; + + emitter->indention = 1; + breaks = 1; + } else { + if (breaks) { + err = write_indent(emitter); + if (err) + return err; + } + + if (CHECK(string, '\'')) { + err = put(emitter, '\''); + if (err) + return err; + } + + err = put_char(emitter, &string); + if (err) + return err; + + emitter->indention = 0; + spaces = 0; + breaks = 0; + } + } + + if (breaks) { + err = write_indent(emitter); + if (err) + return err; + } + + err = write_indicator(emitter, "'", 0, 0, 0); + if (err) + return err; + + emitter->whitespace = 0; + emitter->indention = 0; + return 0; +} + +static int +write_double_quoted_scalar(struct yaml_emitter *emitter, + const char *value, size_t length, int allow_breaks) +{ + struct yaml_string string = STRING_INIT(value, length); + int spaces = 0; + int err; + + err = write_indicator(emitter, "\"", 1, 0, 0); + if (err) + return err; + + while (string.pos != string.end) { + if (!IS_PRINTABLE(string) || + (!emitter->unicode && !IS_ASCII(string)) || + IS_BOM(string) || + IS_BREAK(string) || + CHECK(string, '"') || + CHECK(string, '\\')) { + u8 octet = string.pos[0]; + unsigned int width; + unsigned int value; + int k; + + width = (octet & 0x80) == 0x00 ? 1 : + (octet & 0xE0) == 0xC0 ? 2 : + (octet & 0xF0) == 0xE0 ? 3 : + (octet & 0xF8) == 0xF0 ? 4 : 0; + value = (octet & 0x80) == 0x00 ? octet & 0x7F : + (octet & 0xE0) == 0xC0 ? octet & 0x1F : + (octet & 0xF0) == 0xE0 ? octet & 0x0F : + (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; + for (k = 1; k < (int)width; k++) { + octet = string.pos[k]; + value = (value << 6) + (octet & 0x3F); + } + string.pos += width; + + err = put(emitter, '\\'); + if (err) + return err; + + switch (value) { + case 0x00: + err = put(emitter, '0'); + if (err) + return err; + break; + + case 0x07: + err = put(emitter, 'a'); + if (err) + return err; + break; + + case 0x08: + err = put(emitter, 'b'); + if (err) + return err; + break; + + case 0x09: + err = put(emitter, 't'); + if (err) + return err; + break; + + case 0x0A: + err = put(emitter, 'n'); + if (err) + return err; + break; + + case 0x0B: + err = put(emitter, 'v'); + if (err) + return err; + break; + + case 0x0C: + err = put(emitter, 'f'); + if (err) + return err; + break; + + case 0x0D: + err = put(emitter, 'r'); + if (err) + return err; + break; + + case 0x1B: + err = put(emitter, 'e'); + if (err) + return err; + break; + + case 0x22: + err = put(emitter, '\"'); + if (err) + return err; + break; + + case 0x5C: + err = put(emitter, '\\'); + if (err) + return err; + break; + + case 0x85: + err = put(emitter, 'N'); + if (err) + return err; + break; + + case 0xA0: + err = put(emitter, '_'); + if (err) + return err; + break; + + case 0x2028: + err = put(emitter, 'L'); + if (err) + return err; + break; + + case 0x2029: + err = put(emitter, 'P'); + if (err) + return err; + break; + + default: + if (value <= 0xFF) { + err = put(emitter, 'x'); + if (err) + return err; + width = 2; + } else if (value <= 0xFFFF) { + err = put(emitter, 'u'); + if (err) + return err; + width = 4; + } else { + err = put(emitter, 'U'); + if (err) + return err; + width = 8; + } + + for (k = (width - 1) * 4; k >= 0; k -= 4) { + int digit; + + digit = (value >> k) & 0x0F; + digit += digit < 10 ? '0' : 'A' - 10; + err = put(emitter, digit); + if (err) + return err; + } + } + spaces = 0; + } else if (IS_SPACE(string)) { + if (allow_breaks && + !spaces && + emitter->column > emitter->best_width && + string.pos != string.start && + string.pos != string.end - 1) { + err = write_indent(emitter); + if (err) + return err; + + if (IS_SPACE_AT(string, 1)) { + err = put(emitter, '\\'); + if (err) + return err; + } + ADVANCE(string); + } else { + err = put_char(emitter, &string); + if (err) + return err; + } + spaces = 1; + } else { + err = put_char(emitter, &string); + if (err) + return err; + spaces = 0; + } + } + + err = write_indicator(emitter, "\"", 0, 0, 0); + if (err) + return err; + + emitter->whitespace = 0; + emitter->indention = 0; + return 0; +} + +static int +write_block_scalar_hints(struct yaml_emitter *emitter, + struct yaml_string string) +{ + char indent_hint[2]; + const char *chomp_hint = NULL; + int err; + + if (IS_SPACE(string) || IS_BREAK(string)) { + indent_hint[0] = '0' + (char)emitter->best_indent; + indent_hint[1] = '\0'; + err = write_indicator(emitter, indent_hint, 0, 0, 0); + if (err) + return err; + } + + emitter->open_ended = 0; + + string.pos = string.end; + if (string.start == string.pos) { + chomp_hint = "-"; + } else { + do { + string.pos--; + } while ((*string.pos & 0xC0) == 0x80); + + if (!IS_BREAK(string)) { + chomp_hint = "-"; + } else if (string.start == string.pos) { + chomp_hint = "+"; + emitter->open_ended = 1; + } else { + do { + string.pos--; + } while ((*string.pos & 0xC0) == 0x80); + + if (IS_BREAK(string)) { + chomp_hint = "+"; + emitter->open_ended = 1; + } + } + } + + if (chomp_hint) { + err = write_indicator(emitter, chomp_hint, 0, 0, 0); + if (err) + return err; + } + + return 0; +} + +static int +write_literal_scalar(struct yaml_emitter *emitter, + const char *value, size_t length) +{ + struct yaml_string string = STRING_INIT(value, length); + int breaks = 1; + int err; + + err = write_indicator(emitter, "|", 1, 0, 0); + if (err) + return err; + + err = write_block_scalar_hints(emitter, string); + if (err) + return err; + + err = put_break(emitter); + if (err) + return err; + + emitter->indention = 1; + emitter->whitespace = 1; + + while (string.pos != string.end) { + if (IS_BREAK(string)) { + err = put_char_break(emitter, &string); + if (err) + return err; + + emitter->indention = 1; + breaks = 1; + } else { + if (breaks) { + err = write_indent(emitter); + if (err) + return err; + } + + err = put_char(emitter, &string); + if (err) + return err; + + emitter->indention = 0; + breaks = 0; + } + } + + return 0; +} + +static int +write_folded_scalar(struct yaml_emitter *emitter, + const char *value, size_t length) +{ + struct yaml_string string = STRING_INIT(value, length); + int breaks = 1; + int leading_spaces = 1; + int err; + + err = write_indicator(emitter, ">", 1, 0, 0); + if (err) + return err; + + err = write_block_scalar_hints(emitter, string); + if (err) + return err; + + err = put_break(emitter); + if (err) + return err; + + emitter->indention = 1; + emitter->whitespace = 1; + + while (string.pos != string.end) { + if (IS_BREAK(string)) { + if (!breaks && !leading_spaces && CHECK(string, '\n')) { + int k; + + k = 0; + while (IS_BREAK_AT(string, k)) + k += __utf8_width(&string, k); + + if (!IS_BLANKZ_AT(string, k)) { + err = put_break(emitter); + if (err) + return err; + } + } + + err = put_char_break(emitter, &string); + if (err) + return err; + + emitter->indention = 1; + breaks = 1; + } else { + if (breaks) { + err = write_indent(emitter); + if (err) + return err; + + leading_spaces = IS_BLANK(string); + } + + if (!breaks && + IS_SPACE(string) && + !IS_SPACE_AT(string, 1) && + emitter->column > emitter->best_width) { + err = write_indent(emitter); + if (err) + return err; + + ADVANCE(string); + } else { + err = put_char(emitter, &string); + if (err) + return err; + } + emitter->indention = 0; + breaks = 0; + } + } + + return err; +} + +static int +write_scalar(struct yaml_emitter *emitter) +{ + switch (emitter->scalar_data.style) { + case YAML_PLAIN_SCALAR_STYLE: + return write_plain_scalar(emitter, + emitter->scalar_data.value, + emitter->scalar_data.length, + !emitter->simple_key_context); + + case YAML_SINGLE_QUOTED_SCALAR_STYLE: + return write_single_quoted_scalar(emitter, + emitter->scalar_data.value, + emitter->scalar_data.length, + !emitter->simple_key_context); + + case YAML_DOUBLE_QUOTED_SCALAR_STYLE: + return write_double_quoted_scalar(emitter, + emitter->scalar_data.value, + emitter->scalar_data.length, + !emitter->simple_key_context); + + case YAML_LITERAL_SCALAR_STYLE: + return write_literal_scalar(emitter, + emitter->scalar_data.value, + emitter->scalar_data.length); + + case YAML_FOLDED_SCALAR_STYLE: + return write_folded_scalar(emitter, + emitter->scalar_data.value, + emitter->scalar_data.length); + + default: + BUG(); + return 0; + } +} + +static int emit_anchor(struct yaml_emitter *emitter) +{ + int err; + + if (!emitter->anchor_data.anchor) + return 0; + + err = write_indicator(emitter, + emitter->anchor_data.alias ? "*" : "&", + 1, 0, 0); + if (err) + return err; + + return write_anchor(emitter, + emitter->anchor_data.anchor, + emitter->anchor_data.anchor_length); +} + +static int emit_stream_start(struct yaml_emitter *emitter, + const struct yaml_event *event) +{ + if (event->type == YAML_STREAM_START_EVENT) { + if (emitter->best_indent < 2 || emitter->best_indent > 9) + emitter->best_indent = 2; + + if (emitter->best_width >= 0 && + emitter->best_width <= emitter->best_indent * 2) + emitter->best_width = 80; + + if (emitter->best_width < 0) + emitter->best_width = INT_MAX; + + emitter->indent = -1; + + emitter->column = 0; + emitter->whitespace = 1; + emitter->indention = 1; + + emitter->state = EMIT_FIRST_DOCUMENT_START_STATE; + return 0; + } + + return set_error(emitter, "expected STREAM-START"); +} + +static int +append_tag_directive(struct yaml_emitter *emitter, + struct yaml_tag_directive value, + int allow_duplicates) +{ + struct yaml_tag_directive *tag; + struct yaml_tag_directive copy = {}; + int err; + + for (tag = emitter->tags.start; tag != emitter->tags.top; tag++) { + if (strcmp(value.handle, tag->handle) == 0) { + if (allow_duplicates) + return 0; + + return set_error(emitter, "duplicate %TAG directive"); + } + } + + copy.handle = kstrdup(value.handle, GFP_KERNEL); + copy.prefix = kstrdup(value.prefix, GFP_KERNEL); + if (!copy.handle || !copy.prefix) { + emitter->errno = -ENOMEM; + err = -ENOMEM; + goto error; + } + + err = PUSH(emitter, emitter->tags, copy); + if (err) + goto error; + + return 0; + +error: + kfree(copy.handle); + kfree(copy.prefix); + return err; +} + +static int +emit_document_start(struct yaml_emitter *emitter, + const struct yaml_event *event, + int first) +{ + int err; + + if (event->type == YAML_DOCUMENT_START_EVENT) { + struct yaml_tag_directive default_tag_directives[] = { + { "!", "!" }, + { "!!", "tag:yaml.org,2002:" }, + { NULL, NULL } + }; + struct yaml_tag_directive *tag; + int implicit; + + if (event->document_start.version) { + err = analyze_version_directive(emitter, + *event->document_start.version); + if (err) + return err; + } + + for (tag = event->document_start.tags.start; + tag != event->document_start.tags.end; + tag++) { + err = analyze_tag_directive(emitter, *tag); + if (err) + return err; + + err = append_tag_directive(emitter, *tag, 0); + if (err) + return err; + } + + for (tag = default_tag_directives; + tag->handle; tag++) { + err = append_tag_directive(emitter, *tag, 1); + if (err) + return err; + } + + implicit = event->document_start.implicit; + if (!first || emitter->canonical) + implicit = 0; + + if ((event->document_start.version || + event->document_start.tags.start + != event->document_start.tags.end) && + emitter->open_ended) { + err = write_indicator(emitter, "...", 1, 0, 0); + if (err) + return err; + + err = write_indent(emitter); + if (err) + return err; + } + + if (event->document_start.version) { + implicit = 0; + err = write_indicator(emitter, "%YAML", 1, 0, 0); + if (err) + return err; + + err = write_indicator(emitter, "1.1", 1, 0, 0); + if (err) + return err; + + err = write_indent(emitter); + if (err) + return err; + } + + if (event->document_start.tags.start != + event->document_start.tags.end) { + implicit = 0; + for (tag = event->document_start.tags.start; + tag != event->document_start.tags.end; + tag++) { + err = write_indicator(emitter, "%TAG", 1, 0, 0); + if (err) + return err; + + err = write_tag_handle(emitter, + tag->handle, + strlen(tag->handle)); + if (err) + return err; + + err = write_tag_content(emitter, + tag->prefix, + strlen(tag->prefix), + 1); + if (err) + return err; + + err = write_indent(emitter); + if (err) + return err; + } + } + + if (check_empty_document(emitter)) + implicit = 0; + + if (!implicit) { + err = write_indent(emitter); + if (err) + return err; + + err = write_indicator(emitter, "---", 1, 0, 0); + if (err) + return err; + + if (emitter->canonical) { + err = write_indent(emitter); + if (err) + return err; + } + } + + emitter->state = EMIT_DOCUMENT_CONTENT_STATE; + return 0; + } + + if (event->type == YAML_STREAM_END_EVENT) { + err = flush(emitter); + if (err) + return err; + + emitter->state = EMIT_END_STATE; + return 0; + } + + return set_error(emitter, "expected DOCUMENT-START or STREAM-END"); +} + +static int +emit_document_end(struct yaml_emitter *emitter, const struct yaml_event *event) +{ + int err; + + if (event->type == YAML_DOCUMENT_END_EVENT) { + err = write_indent(emitter); + if (err) + return err; + + if (!event->document_end.implicit) { + err = write_indicator(emitter, "...", 1, 0, 0); + if (err) + return err; + + err = write_indent(emitter); + if (err) + return err; + } + + err = flush(emitter); + if (err) + return err; + + emitter->state = EMIT_DOCUMENT_START_STATE; + + while (!EMPTY(emitter, emitter->tags)) { + struct yaml_tag_directive tag = + POP(emitter, emitter->tags); + + kfree(tag.handle); + kfree(tag.prefix); + } + + return 0; + } + + return set_error(emitter, "expected DOCUMENT-END"); +} + +static int +emit_alias(struct yaml_emitter *emitter, const struct yaml_event *event) +{ + int err; + + err = emit_anchor(emitter); + if (err) + return err; + + emitter->state = POP(emitter, emitter->states); + return 0; +} + +static int +select_scalar_style(struct yaml_emitter *emitter, const struct yaml_event *event) +{ + enum yaml_scalar_style style = event->scalar.style; + bool no_tag = !emitter->tag_data.handle && !emitter->tag_data.suffix; + + if (no_tag && + !event->scalar.plain_implicit && + !event->scalar.quoted_implicit) + return set_error(emitter, + "neither tag nor implicit flags are specified"); + + if (style == YAML_ANY_SCALAR_STYLE) + style = YAML_PLAIN_SCALAR_STYLE; + + if (emitter->canonical) + style = YAML_DOUBLE_QUOTED_SCALAR_STYLE; + + if (emitter->simple_key_context && emitter->scalar_data.multiline) + style = YAML_DOUBLE_QUOTED_SCALAR_STYLE; + + if (style == YAML_PLAIN_SCALAR_STYLE) { + if ((emitter->flow_level && !emitter->scalar_data.flow_plain_allowed) || + (!emitter->flow_level && !emitter->scalar_data.block_plain_allowed)) + style = YAML_SINGLE_QUOTED_SCALAR_STYLE; + + if (!emitter->scalar_data.length && + (emitter->flow_level || emitter->simple_key_context)) + style = YAML_SINGLE_QUOTED_SCALAR_STYLE; + + if (no_tag && !event->scalar.plain_implicit) + style = YAML_SINGLE_QUOTED_SCALAR_STYLE; + } + + if (style == YAML_SINGLE_QUOTED_SCALAR_STYLE) { + if (!emitter->scalar_data.single_quoted_allowed) + style = YAML_DOUBLE_QUOTED_SCALAR_STYLE; + } + + if (style == YAML_LITERAL_SCALAR_STYLE || + style == YAML_FOLDED_SCALAR_STYLE) { + if (!emitter->scalar_data.block_allowed || + emitter->flow_level || + emitter->simple_key_context) + style = YAML_DOUBLE_QUOTED_SCALAR_STYLE; + } + + if (no_tag && !event->scalar.quoted_implicit && + style != YAML_PLAIN_SCALAR_STYLE) { + emitter->tag_data.handle = "!"; + emitter->tag_data.handle_length = 1; + } + + emitter->scalar_data.style = style; + return 0; +} + +static int +emit_scalar(struct yaml_emitter *emitter, const struct yaml_event *event) +{ + int err; + + err = select_scalar_style(emitter, event); + if (err) + return err; + + err = emit_anchor(emitter); + if (err) + return err; + + err = write_tag(emitter); + if (err) + return err; + + err = increase_indent(emitter, 1, 0); + if (err) + return err; + + err = write_scalar(emitter); + if (err) + return err; + + emitter->indent = POP(emitter, emitter->indents); + emitter->state = POP(emitter, emitter->states); + return 0; +} + +static int +emit_sequence_start(struct yaml_emitter *emitter, const struct yaml_event *event) +{ + int err; + + err = emit_anchor(emitter); + if (err) + return err; + + err = write_tag(emitter); + if (err) + return err; + + if (emitter->flow_level || emitter->canonical || + event->sequence_start.style == YAML_FLOW_SEQUENCE_STYLE || + check_empty_sequence(emitter)) { + emitter->state = EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE; + } else { + emitter->state = EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE; + } + + return 0; +} + +static int +emit_mapping_start(struct yaml_emitter *emitter, const struct yaml_event *event) +{ + int err; + + err = emit_anchor(emitter); + if (err) + return err; + + err = write_tag(emitter); + if (err) + return err; + + if (emitter->flow_level || + emitter->canonical || + event->mapping_start.style == YAML_FLOW_MAPPING_STYLE || + check_empty_mapping(emitter)) { + emitter->state = EMIT_FLOW_MAPPING_FIRST_KEY_STATE; + } else { + emitter->state = EMIT_BLOCK_MAPPING_FIRST_KEY_STATE; + } + + return 0; +} + +static int +emit_node(struct yaml_emitter *emitter, const struct yaml_event *event, + int root, int sequence, int mapping, int simple_key) +{ + emitter->root_context = root; + emitter->sequence_context = sequence; + emitter->mapping_context = mapping; + emitter->simple_key_context = simple_key; + + switch (event->type) { + case YAML_ALIAS_EVENT: + return emit_alias(emitter, event); + + case YAML_SCALAR_EVENT: + return emit_scalar(emitter, event); + + case YAML_SEQUENCE_START_EVENT: + return emit_sequence_start(emitter, event); + + case YAML_MAPPING_START_EVENT: + return emit_mapping_start(emitter, event); + + default: + return set_error(emitter, + "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS"); + } + + return 0; +} + +static int +emit_document_content(struct yaml_emitter *emitter, const struct yaml_event *event) +{ + int err; + + err = PUSH(emitter, emitter->states, EMIT_DOCUMENT_END_STATE); + if (err) + return err; + + return emit_node(emitter, event, 1, 0, 0, 0); +} + +static int +emit_flow_sequence_item(struct yaml_emitter *emitter, + const struct yaml_event *event, + bool first) +{ + int err; + + if (first) { + err = write_indicator(emitter, "[", 1, 1, 0); + if (err) + return err; + + err = increase_indent(emitter, 1, 0); + if (err) + return err; + + emitter->flow_level++; + } + + if (event->type == YAML_SEQUENCE_END_EVENT) { + emitter->flow_level--; + emitter->indent = POP(emitter, emitter->indents); + + if (emitter->canonical && !first) { + err = write_indicator(emitter, ",", 0, 0, 0); + if (err) + return err; + + err = write_indent(emitter); + if (err) + return err; + } + + err = write_indicator(emitter, "]", 0, 0, 0); + if (err) + return err; + + emitter->state = POP(emitter, emitter->states); + return 0; + } + + if (!first) { + err = write_indicator(emitter, ",", 0, 0, 0); + if (err) + return err; + } + + if (emitter->canonical || emitter->column > emitter->best_width) { + err = write_indent(emitter); + if (err) + return err; + } + + err = PUSH(emitter, emitter->states, EMIT_FLOW_SEQUENCE_ITEM_STATE); + if (err) + return err; + + return emit_node(emitter, event, 0, 1, 0, 0); +} + +static int +emit_flow_mapping_key(struct yaml_emitter *emitter, + const struct yaml_event *event, int first) +{ + int err; + + if (first) { + err = write_indicator(emitter, "{", 1, 1, 0); + if (err) + return err; + + err = increase_indent(emitter, 1, 0); + if (err) + return err; + + emitter->flow_level++; + } + + if (event->type == YAML_MAPPING_END_EVENT) { + emitter->flow_level--; + emitter->indent = POP(emitter, emitter->indents); + + if (emitter->canonical && !first) { + err = write_indicator(emitter, ",", 0, 0, 0); + if (err) + return err; + + err = write_indent(emitter); + if (err) + return err; + } + + err = write_indicator(emitter, "}", 0, 0, 0); + if (err) + return err; + + emitter->state = POP(emitter, emitter->states); + return 0; + } + + if (!first) { + err = write_indicator(emitter, ",", 0, 0, 0); + if (err) + return err; + } + + if (emitter->canonical || emitter->column > emitter->best_width) { + err = write_indent(emitter); + if (err) + return err; + } + + if (!emitter->canonical && check_simple_key(emitter)) { + err = PUSH(emitter, emitter->states, + EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE); + if (err) + return err; + + return emit_node(emitter, event, 0, 0, 1, 1); + } + + err = write_indicator(emitter, "?", 1, 0, 0); + if (err) + return err; + + err = PUSH(emitter, emitter->states, EMIT_FLOW_MAPPING_VALUE_STATE); + if (err) + return err; + + return emit_node(emitter, event, 0, 0, 1, 0); +} + +static int +emit_flow_mapping_value(struct yaml_emitter *emitter, + const struct yaml_event *event, int simple) +{ + int err; + + if (simple) { + err = write_indicator(emitter, ":", 0, 0, 0); + if (err) + return err; + } else { + if (emitter->canonical || + emitter->column > emitter->best_width) { + err = write_indent(emitter); + if (err) + return err; + } + + err = write_indicator(emitter, ":", 1, 0, 0); + if (err) + return err; + } + + err = PUSH(emitter, emitter->states, EMIT_FLOW_MAPPING_KEY_STATE); + if (err) + return err; + + return emit_node(emitter, event, 0, 0, 1, 0); +} + +static int +emit_block_sequence_item(struct yaml_emitter *emitter, + const struct yaml_event *event, int first) +{ + int err; + + if (first) { + err = increase_indent(emitter, 0, + emitter->mapping_context && + !emitter->indention); + if (err) + return err; + } + + if (event->type == YAML_SEQUENCE_END_EVENT) { + emitter->indent = POP(emitter, emitter->indents); + emitter->state = POP(emitter, emitter->states); + return 0; + } + + err = write_indent(emitter); + if (err) + return err; + + err = write_indicator(emitter, "-", 1, 0, 1); + if (err) + return err; + + err = PUSH(emitter, emitter->states, EMIT_BLOCK_SEQUENCE_ITEM_STATE); + if (err) + return err; + + return emit_node(emitter, event, 0, 1, 0, 0); +} + +static int +emit_block_mapping_key(struct yaml_emitter *emitter, + const struct yaml_event *event, int first) +{ + int err; + + if (first) { + err = increase_indent(emitter, 0, 0); + if (err) + return err; + } + + if (event->type == YAML_MAPPING_END_EVENT) { + emitter->indent = POP(emitter, emitter->indents); + emitter->state = POP(emitter, emitter->states); + return 0; + } + + err = write_indent(emitter); + if (err) + return err; + + if (check_simple_key(emitter)) { + err = PUSH(emitter, emitter->states, + EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE); + if (err) + return err; + + return emit_node(emitter, event, 0, 0, 1, 1); + } else { + err = write_indicator(emitter, "?", 1, 0, 1); + if (err) + return err; + + err = PUSH(emitter, emitter->states, + EMIT_BLOCK_MAPPING_VALUE_STATE); + if (err) + return err; + + return emit_node(emitter, event, 0, 0, 1, 0); + } +} + +static int +emit_block_mapping_value(struct yaml_emitter *emitter, + const struct yaml_event *event, int simple) +{ + int err; + + if (simple) { + err = write_indicator(emitter, ":", 0, 0, 0); + if (err) + return err; + } else { + err = write_indent(emitter); + if (err) + return err; + + err = write_indicator(emitter, ":", 1, 0, 1); + if (err) + return err; + } + + err = PUSH(emitter, emitter->states, EMIT_BLOCK_MAPPING_KEY_STATE); + if (err) + return err; + + return emit_node(emitter, event, 0, 0, 1, 0); +} + +static int +emitter_state_machine(struct yaml_emitter *emitter, + const struct yaml_event *event) +{ + switch (emitter->state) { + case EMIT_STREAM_START_STATE: + return emit_stream_start(emitter, event); + + case EMIT_FIRST_DOCUMENT_START_STATE: + return emit_document_start(emitter, event, true); + + case EMIT_DOCUMENT_START_STATE: + return emit_document_start(emitter, event, false); + + case EMIT_DOCUMENT_CONTENT_STATE: + return emit_document_content(emitter, event); + + case EMIT_DOCUMENT_END_STATE: + return emit_document_end(emitter, event); + + case EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: + return emit_flow_sequence_item(emitter, event, true); + + case EMIT_FLOW_SEQUENCE_ITEM_STATE: + return emit_flow_sequence_item(emitter, event, false); + + case EMIT_FLOW_MAPPING_FIRST_KEY_STATE: + return emit_flow_mapping_key(emitter, event, true); + + case EMIT_FLOW_MAPPING_KEY_STATE: + return emit_flow_mapping_key(emitter, event, false); + + case EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: + return emit_flow_mapping_value(emitter, event, true); + + case EMIT_FLOW_MAPPING_VALUE_STATE: + return emit_flow_mapping_value(emitter, event, false); + + case EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: + return emit_block_sequence_item(emitter, event, true); + + case EMIT_BLOCK_SEQUENCE_ITEM_STATE: + return emit_block_sequence_item(emitter, event, false); + + case EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: + return emit_block_mapping_key(emitter, event, true); + + case EMIT_BLOCK_MAPPING_KEY_STATE: + return emit_block_mapping_key(emitter, event, false); + + case EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: + return emit_block_mapping_value(emitter, event, true); + + case EMIT_BLOCK_MAPPING_VALUE_STATE: + return emit_block_mapping_value(emitter, event, false); + + case EMIT_END_STATE: + return set_error(emitter, "expected nothing after STREAM-END"); + + default: + BUG(); + } + + return 0; +} + +void yaml_emitter_emit(struct yaml_emitter *emitter, struct yaml_event *event) +{ + if (!event || !emitter || emitter->errno) { + yaml_event_delete(event); + return; + } + + list_add_tail(&event->link, &emitter->events); + emitter->num_events++; + + while (!need_more_events(emitter)) { + event = next_event(emitter); + + if (analyze_event(emitter, event)) + return; + + if (emitter_state_machine(emitter, event)) + return; + + emitter->num_events--; + list_del(&event->link); + yaml_event_delete(event); + } +} +EXPORT_SYMBOL(yaml_emitter_emit); + +int yaml_emitter_flush(struct yaml_emitter *emitter, const char **problem) +{ + int err; + + if (!emitter) { + if (problem) + *problem = "allocation error"; + return -ENOMEM; + } + + if (emitter->errno) + err = emitter->errno; + else + err = flush(emitter); + if (!err) + return 0; + + if (problem) + *problem = emitter->problem; + return err; +} +EXPORT_SYMBOL(yaml_emitter_flush); + +static bool yaml_stack_extend(void **start, void **top, void **end) +{ + void *new_start; + + if (*end - *start >= INT_MAX / 2) + return false; + + new_start = krealloc(*start, (*end - *start) * 2, GFP_KERNEL); + if (!new_start) + return false; + + *top = new_start + (*top - *start); + *end = new_start + (*end - *start) * 2; + *start = new_start; + + return true; +} + +#define STACK_INIT(context, stack) \ + (((stack).start = kmalloc(INITIAL_STACK_SIZE * sizeof(*(stack). start),\ + gfp)) ? \ + ((stack).top = (stack).start, \ + (stack).end = (stack).start + INITIAL_STACK_SIZE, \ + 1) : 0) +#define STACK_DEL(context, stack) kfree((stack).start) + +struct yaml_emitter *yaml_emitter_create(gfp_t gfp) +{ + struct yaml_emitter *emitter; + + emitter = kzalloc(sizeof(*emitter), gfp); + if (!emitter) + return NULL; + + emitter->gfp = gfp; + INIT_LIST_HEAD(&emitter->events); + + emitter->buf.start = kmalloc(PAGE_SIZE, gfp); + if (!emitter->buf.start) + goto error; + + emitter->buf.pos = emitter->buf.start; + emitter->buf.last = emitter->buf.pos; + emitter->buf.end = emitter->buf.start + PAGE_SIZE; + + if (!STACK_INIT(emitter, emitter->states)) + goto error; + + if (!STACK_INIT(emitter, emitter->indents)) + goto error; + + if (!STACK_INIT(emitter, emitter->tags)) + goto error; + + return emitter; + +error: + yaml_emitter_destroy(emitter); + return NULL; +} +EXPORT_SYMBOL(yaml_emitter_create); + +gfp_t yaml_emitter_set_gfp(struct yaml_emitter *emitter, gfp_t gfp) +{ + gfp_t old = 0; + + if (emitter) { + old = emitter->gfp; + emitter->gfp = gfp; + } + + return old; +} +EXPORT_SYMBOL(yaml_emitter_set_gfp); + +static void delete_events(struct yaml_emitter *emitter) +{ + struct yaml_event *event, *next; + + list_for_each_entry_safe(event, next, &emitter->events, link) + yaml_event_delete(event); +} + +void yaml_emitter_destroy(struct yaml_emitter *emitter) +{ + if (!emitter) + return; + + delete_events(emitter); + + kfree(emitter->buf.start); + STACK_DEL(emitter, emitter->states); + STACK_DEL(emitter, emitter->indents); + while (!EMPTY(empty, emitter->tags)) { + struct yaml_tag_directive tag = + POP(emitter, emitter->tags); + + kfree(tag.handle); + kfree(tag.prefix); + } + STACK_DEL(emitter, emitter->tags); + kfree(emitter->anchors); + + kfree(emitter); +} +EXPORT_SYMBOL(yaml_emitter_destroy); + +static int string_write_handler(void *data, const u8 *buffer, size_t size) +{ + struct yaml_emitter *emitter = data; + typeof(emitter->output.string) *out = &emitter->output.string; + + if (out->size - *out->size_written < size) + size = out->size - *out->size_written; + + memcpy(out->buffer + *out->size_written, buffer, size); + *out->size_written += size; + return 0; +} + +void yaml_emitter_set_string(struct yaml_emitter *emitter, + u8 *output, + size_t size, + size_t *size_written) +{ + if (!emitter) + return; + + emitter->write_handler = string_write_handler; + emitter->write_data = emitter; + + emitter->output.string.buffer = output; + emitter->output.string.size = size; + emitter->output.string.size_written = size_written; + *size_written = 0; +} +EXPORT_SYMBOL(yaml_emitter_set_string); + +static int seq_file_write_handler(void *data, const u8 *buffer, size_t size) +{ + /* Ignore the overflow error; seq will restart with a larger buffer */ + seq_write(data, buffer, size); + return 0; +} + +void yaml_emitter_set_seq_file(struct yaml_emitter *emitter, + struct seq_file *file) +{ + if (!emitter) + return; + + emitter->write_handler = seq_file_write_handler; + emitter->write_data = file; +} +EXPORT_SYMBOL(yaml_emitter_set_seq_file); + +void yaml_emitter_set_width(struct yaml_emitter *emitter, int width) +{ + if (!emitter) + return; + + emitter->best_width = (width >= 0) ? width : -1; +} +EXPORT_SYMBOL(yaml_emitter_set_width); + +void yaml_emitter_set_unicode(struct yaml_emitter *emitter, bool unicode) +{ + if (!emitter) + return; + + emitter->unicode = unicode; +} +EXPORT_SYMBOL(yaml_emitter_set_unicode); + +void yaml_emitter_set_indent(struct yaml_emitter *emitter, int indent) +{ + if (!emitter) + return; + + emitter->best_indent = (1 < indent && indent < 10) ? indent : 2; +} +EXPORT_SYMBOL(yaml_emitter_set_indent); + +void yaml_emitter_set_canonical(struct yaml_emitter *emitter, bool canonical) +{ + if (!emitter) + return; + + emitter->canonical = canonical; +} +EXPORT_SYMBOL(yaml_emitter_set_canonical); + +void yaml_emitter_set_output(struct yaml_emitter *emitter, + yaml_write_t handler, + void *data) +{ + if (!emitter) + return; + + emitter->write_handler = handler; + emitter->write_data = data; +} +EXPORT_SYMBOL(yaml_emitter_set_output); diff --git a/lib/yaml/yaml-emitter.h b/lib/yaml/yaml-emitter.h new file mode 100644 index 000000000000..a8708bc63f29 --- /dev/null +++ b/lib/yaml/yaml-emitter.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2006-2016 Kirill Simonov + * Copyright (c) 2017-2019 Ingy döt Net + * Copyright (c) 2020 Intel Corporation + */ + +#ifndef YAML_EMITTER_H +#define YAML_EMITTER_H + +#include +#include +#include + +enum yaml_emitter_state { + EMIT_STREAM_START_STATE, + EMIT_FIRST_DOCUMENT_START_STATE, + EMIT_DOCUMENT_START_STATE, + EMIT_DOCUMENT_CONTENT_STATE, + EMIT_DOCUMENT_END_STATE, + EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE, + EMIT_FLOW_SEQUENCE_ITEM_STATE, + EMIT_FLOW_MAPPING_FIRST_KEY_STATE, + EMIT_FLOW_MAPPING_KEY_STATE, + EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE, + EMIT_FLOW_MAPPING_VALUE_STATE, + EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE, + EMIT_BLOCK_SEQUENCE_ITEM_STATE, + EMIT_BLOCK_MAPPING_FIRST_KEY_STATE, + EMIT_BLOCK_MAPPING_KEY_STATE, + EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE, + EMIT_BLOCK_MAPPING_VALUE_STATE, + EMIT_END_STATE +}; + +struct yaml_anchors { + int references; + int anchor; + int serialized; +}; + +struct yaml_emitter { + int errno; + const char *problem; + gfp_t gfp; + + yaml_write_t write_handler; + void *write_data; + + union { + struct { + u8 *buffer; + size_t size; + size_t *size_written; + } string; + } output; + + struct { + char *start; + char *end; + char *pos; + char *last; + } buf; + + int canonical; + int best_indent; + int best_width; + int unicode; + + struct { + enum yaml_emitter_state *start; + enum yaml_emitter_state *end; + enum yaml_emitter_state *top; + } states; + + enum yaml_emitter_state state; + + struct list_head events; + int num_events; + + struct { + int *start; + int *end; + int *top; + } indents; + + struct { + struct yaml_tag_directive *start; + struct yaml_tag_directive *end; + struct yaml_tag_directive *top; + } tags; + + int indent; + int flow_level; + + int root_context; + int sequence_context; + int mapping_context; + int simple_key_context; + + int column; + int whitespace; + int indention; + int open_ended; + + /** Anchor analysis. */ + struct { + const char *anchor; + size_t anchor_length; + int alias; + } anchor_data; + + /** Tag analysis. */ + struct { + const char *handle; + size_t handle_length; + const char *suffix; + size_t suffix_length; + } tag_data; + + /** Scalar analysis. */ + struct { + const char *value; + size_t length; + int multiline; + int flow_plain_allowed; + int block_plain_allowed; + int single_quoted_allowed; + int block_allowed; + enum yaml_scalar_style style; + } scalar_data; + + int opened; + int closed; + + struct yaml_anchors *anchors; + int last_anchor_id; +}; + +#endif /* YAML_EMITTER_H */ diff --git a/lib/yaml/yaml-events.c b/lib/yaml/yaml-events.c new file mode 100644 index 000000000000..adc7ed887d6a --- /dev/null +++ b/lib/yaml/yaml-events.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2006-2016 Kirill Simonov + * Copyright (c) 2017-2019 Ingy döt Net + * Copyright (c) 2020 Intel Corporation + */ + +#include +#include +#include +#include +#include + +static bool check_utf8(const char *pos, size_t length) +{ + const char *end = pos + length; + + while (pos < end) { + u8 octet = pos[0]; + unsigned int width; + unsigned int value; + size_t k; + + width = (octet & 0x80) == 0x00 ? 1 : + (octet & 0xE0) == 0xC0 ? 2 : + (octet & 0xF0) == 0xE0 ? 3 : + (octet & 0xF8) == 0xF0 ? 4 : 0; + value = (octet & 0x80) == 0x00 ? octet & 0x7F : + (octet & 0xE0) == 0xC0 ? octet & 0x1F : + (octet & 0xF0) == 0xE0 ? octet & 0x0F : + (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; + if (!width) + return false; + + if (pos + width > end) + return false; + + for (k = 1; k < width; k++) { + octet = pos[k]; + if ((octet & 0xC0) != 0x80) + return false; + + value = (value << 6) + (octet & 0x3F); + } + + if (!(width == 1 || + (width == 2 && value >= 0x80) || + (width == 3 && value >= 0x800) || + (width == 4 && value >= 0x10000))) + return false; + + pos += width; + } + + return true; +} + +static struct yaml_event *yaml_event_create(int type, gfp_t gfp) +{ + struct yaml_event *ev; + + ev = kzalloc(sizeof(*ev), gfp); + if (ev) + ev->type = type; + + return ev; +} + +static void yaml_event_free(struct yaml_event *event) +{ + kfree(event); +} + +struct yaml_event *yaml_stream_start_event_create(gfp_t gfp) +{ + return yaml_event_create(YAML_STREAM_START_EVENT, gfp); +} +EXPORT_SYMBOL(yaml_stream_start_event_create); + +struct yaml_event *yaml_stream_end_event_create(gfp_t gfp) +{ + return yaml_event_create(YAML_STREAM_END_EVENT, gfp); +} +EXPORT_SYMBOL(yaml_stream_end_event_create); + +struct yaml_event * +yaml_document_start_event_create(struct yaml_version_directive *version, + struct yaml_tag_directive *start, + struct yaml_tag_directive *end, + int implicit, + gfp_t gfp) +{ + struct yaml_version_directive *version_copy = NULL; + struct { + struct yaml_tag_directive *start; + struct yaml_tag_directive *end; + struct yaml_tag_directive *top; + } copy = {}; + struct yaml_tag_directive value = {}; + struct yaml_event *event; + + if (!((start && end) || start == end)) + return NULL; + + event = yaml_event_create(YAML_DOCUMENT_START_EVENT, gfp); + if (!event) + return NULL; + + if (version) { + version_copy = kmemdup(version, sizeof(*version), gfp); + if (!version_copy) + goto error; + } + +#if 0 + if (start != end) { + struct yaml_tag_directive *tag; + struct { + enum yaml_error error; + } context; + + if (!STACK_INIT(&context, copy, struct yaml_tag_directive*)) + goto error; + + for (tag = start; tag != end; tag++) { + if (!check_utf8(tag->handle, strlen(tag->handle))) + goto error; + + if (!check_utf8(tag->prefix, strlen(tag->prefix))) + goto error; + + value.handle = kstrdup(tag->handle, gfp); + value.prefix = kstrdup(tag->prefix, gfp); + if (!value.handle || !value.prefix) + goto error; + + if (!PUSH(&context, copy, value)) + goto error; + + value.handle = NULL; + value.prefix = NULL; + } + } +#endif + + event->type = YAML_DOCUMENT_START_EVENT; + event->document_start.version = version_copy; + event->document_start.tags.start = copy.start; + event->document_start.tags.end = copy.top; + event->document_start.implicit = implicit; + + return event; + +error: + kfree(version_copy); +#if 0 + while (!STACK_EMPTY(context, copy)) { + struct yaml_tag_directive value = POP(context, copy); + + kfree(value.handle); + kfree(value.prefix); + } + STACK_DEL(context, copy); +#endif + kfree(value.handle); + kfree(value.prefix); + return NULL; +} +EXPORT_SYMBOL(yaml_document_start_event_create); + +struct yaml_event * +yaml_document_end_event_create(int implicit, gfp_t gfp) +{ + struct yaml_event *event; + + event = yaml_event_create(YAML_DOCUMENT_END_EVENT, gfp); + if (event) + event->document_end.implicit = implicit; + + return event; +} +EXPORT_SYMBOL(yaml_document_end_event_create); + +struct yaml_event * +yaml_alias_event_create(const char *anchor, gfp_t gfp) +{ + struct yaml_event *event; + + if (!anchor || !check_utf8(anchor, strlen(anchor))) + return NULL; + + event = yaml_event_create(YAML_ALIAS_EVENT, gfp); + if (!event) + return NULL; + + event->alias.anchor = kstrdup(anchor, gfp); + if (!event->alias.anchor) { + yaml_event_free(event); + return NULL; + } + + return event; +} +EXPORT_SYMBOL(yaml_alias_event_create); + +struct yaml_event * +yaml_scalar_event_create(const char *anchor, const char *tag, + const char *value, ssize_t length, bool steal, + int plain_implicit, int quoted_implicit, + enum yaml_scalar_style style, + gfp_t gfp) +{ + struct yaml_event *event; + char *anchor_copy = NULL; + char *tag_copy = NULL; + + if (!value) + return NULL; + + if (anchor) { + if (!check_utf8(anchor, strlen(anchor))) + goto error; + + anchor_copy = kstrdup(anchor, gfp); + if (!anchor_copy) + goto error; + } + + if (tag) { + if (!check_utf8(tag, strlen(tag))) + goto error; + + tag_copy = kstrdup(tag, gfp); + if (!tag_copy) + goto error; + } + + if (length < 0) + length = strlen(value); + + if (!check_utf8(value, length)) + goto error; + + event = yaml_event_create(YAML_SCALAR_EVENT, gfp); + if (!event) + goto error; + + if (steal) { + event->scalar.value = (char *)value; + } else { + event->scalar.value = kmemdup(value, length, gfp); + if (!event->scalar.value) { + yaml_event_free(event); + goto error; + } + } + event->scalar.length = length; + + event->scalar.anchor = anchor_copy; + event->scalar.tag = tag_copy; + event->scalar.plain_implicit = plain_implicit; + event->scalar.quoted_implicit = quoted_implicit; + event->scalar.style = style; + return event; + +error: + kfree(anchor_copy); + kfree(tag_copy); + return NULL; +} +EXPORT_SYMBOL(yaml_scalar_event_create); + +struct yaml_event * +yaml_sequence_start_event_create(const char *anchor, + const char *tag, + int implicit, + enum yaml_sequence_style style, + gfp_t gfp) +{ + char *anchor_copy = NULL; + char *tag_copy = NULL; + struct yaml_event *event; + + if (anchor) { + if (!check_utf8(anchor, strlen(anchor))) + goto error; + + anchor_copy = kstrdup(anchor, gfp); + if (!anchor_copy) + goto error; + } + + if (tag) { + if (!check_utf8(tag, strlen(tag))) + goto error; + + tag_copy = kstrdup(tag, gfp); + if (!tag_copy) + goto error; + } + + event = yaml_event_create(YAML_SEQUENCE_START_EVENT, gfp); + if (!event) + goto error; + + event->sequence_start.anchor = anchor_copy; + event->sequence_start.tag = tag_copy; + event->sequence_start.implicit = implicit; + event->sequence_start.style = style; + return event; + +error: + kfree(anchor_copy); + kfree(tag_copy); + return NULL; +} +EXPORT_SYMBOL(yaml_sequence_start_event_create); + +struct yaml_event *yaml_sequence_end_event_create(gfp_t gfp) +{ + return yaml_event_create(YAML_SEQUENCE_END_EVENT, gfp); +} +EXPORT_SYMBOL(yaml_sequence_end_event_create); + +struct yaml_event * +yaml_mapping_start_event_create(const char *anchor, + const char *tag, + int implicit, + enum yaml_mapping_style style, + gfp_t gfp) +{ + char *anchor_copy = NULL; + char *tag_copy = NULL; + struct yaml_event *event; + + if (anchor) { + if (!check_utf8(anchor, strlen(anchor))) + goto error; + + anchor_copy = kstrdup(anchor, gfp); + if (!anchor_copy) + goto error; + } + + if (tag) { + if (!check_utf8(tag, strlen(tag))) + goto error; + + tag_copy = kstrdup(tag, gfp); + if (!tag_copy) + goto error; + } + + event = yaml_event_create(YAML_MAPPING_START_EVENT, gfp); + if (!event) + goto error; + + event->mapping_start.anchor = anchor_copy; + event->mapping_start.tag = tag_copy; + event->mapping_start.implicit = implicit; + event->mapping_start.style = style; + return event; + +error: + kfree(anchor_copy); + kfree(tag_copy); + return NULL; +} +EXPORT_SYMBOL(yaml_mapping_start_event_create); + +struct yaml_event *yaml_mapping_end_event_create(gfp_t gfp) +{ + return yaml_event_create(YAML_MAPPING_END_EVENT, gfp); +} +EXPORT_SYMBOL(yaml_mapping_end_event_create); + +void yaml_event_delete(struct yaml_event *event) +{ + struct yaml_tag_directive *tag; + + if (!event) + return; + + switch (event->type) { + case YAML_DOCUMENT_START_EVENT: + kfree(event->document_start.version); + for (tag = event->document_start.tags.start; + tag != event->document_start.tags.end; + tag++) { + kfree(tag->handle); + kfree(tag->prefix); + } + kfree(event->document_start.tags.start); + break; + + case YAML_ALIAS_EVENT: + kfree(event->alias.anchor); + break; + + case YAML_SCALAR_EVENT: + kfree(event->scalar.anchor); + kfree(event->scalar.tag); + kfree(event->scalar.value); + break; + + case YAML_SEQUENCE_START_EVENT: + kfree(event->sequence_start.anchor); + kfree(event->sequence_start.tag); + break; + + case YAML_MAPPING_START_EVENT: + kfree(event->mapping_start.anchor); + kfree(event->mapping_start.tag); + break; + + default: + break; + } + + yaml_event_free(event); +} +EXPORT_SYMBOL(yaml_event_delete); + +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/lib/yaml/yaml-simple.c b/lib/yaml/yaml-simple.c new file mode 100644 index 000000000000..cfde261f09e8 --- /dev/null +++ b/lib/yaml/yaml-simple.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright (c) 2020 Intel Corporation + */ + +#include +#include +#include +#include +#include + +#include "yaml-emitter.h" + +static struct yaml_emitter *__yaml_open(struct yaml_emitter *emitter) +{ + struct yaml_event *event; + + event = yaml_stream_start_event_create(emitter->gfp); + yaml_emitter_emit(emitter, event); + + event = yaml_document_start_event_create(NULL, NULL, NULL, true, + emitter->gfp); + yaml_emitter_emit(emitter, event); + + return emitter; +} + +struct yaml_emitter *yaml_open(yaml_write_t handler, void *data) +{ + struct yaml_emitter *emitter; + + emitter = yaml_emitter_create(GFP_KERNEL); + if (!emitter) + return NULL; + + yaml_emitter_set_output(emitter, handler, data); + return __yaml_open(emitter); +} +EXPORT_SYMBOL(yaml_open); + +struct yaml_emitter *yaml_open_file(struct seq_file *m) +{ + struct yaml_emitter *emitter; + + emitter = yaml_emitter_create(GFP_KERNEL); + if (!emitter) + return NULL; + + yaml_emitter_set_seq_file(emitter, m); + return __yaml_open(emitter); +} +EXPORT_SYMBOL(yaml_open_file); + +struct yaml_emitter *yaml_open_string(char *buf, size_t len, size_t *out) +{ + struct yaml_emitter *emitter; + + emitter = yaml_emitter_create(GFP_KERNEL); + if (!emitter) + return NULL; + + yaml_emitter_set_string(emitter, buf, len, out); + return __yaml_open(emitter); +} +EXPORT_SYMBOL(yaml_open_string); + +void __yaml_mapping_start(struct yaml_emitter *emitter, + enum yaml_mapping_style style) +{ + struct yaml_event *event; + + event = yaml_mapping_start_event_create(NULL, NULL, true, style, + emitter->gfp); + yaml_emitter_emit(emitter, event); +} +EXPORT_SYMBOL(__yaml_mapping_start); + +void yaml_mapping_end(struct yaml_emitter *emitter) +{ + yaml_emitter_emit(emitter, yaml_mapping_end_event_create(emitter->gfp)); +} +EXPORT_SYMBOL(yaml_mapping_end); + +void __yaml_sequence_start(struct yaml_emitter *emitter, + enum yaml_sequence_style style) +{ + struct yaml_event *event; + + event = yaml_sequence_start_event_create(NULL, NULL, true, style, + emitter->gfp); + yaml_emitter_emit(emitter, event); +} +EXPORT_SYMBOL(__yaml_sequence_start); + +void yaml_sequence_end(struct yaml_emitter *emitter) +{ + yaml_emitter_emit(emitter, + yaml_sequence_end_event_create(emitter->gfp)); +} +EXPORT_SYMBOL(yaml_sequence_end); + +void yaml_alias_printf(struct yaml_emitter *emitter, + const char *fmt, ...) +{ + va_list ap; + char *str; + + va_start(ap, fmt); + str = kvasprintf(emitter->gfp, fmt, ap); + va_end(ap); + + yaml_emitter_emit(emitter, yaml_alias_event_create(str, emitter->gfp)); + + kfree(str); +} +EXPORT_SYMBOL(yaml_alias_printf); + +void yaml_scalar_printf(struct yaml_emitter *emitter, + const char *fmt, ...) +{ + struct yaml_event *event; + va_list ap; + char *str; + + va_start(ap, fmt); + str = kvasprintf(emitter->gfp, fmt, ap); + va_end(ap); + + event = yaml_scalar_event_create(NULL, NULL, str, -1, true, + true, true, 0, emitter->gfp); + yaml_emitter_emit(emitter, event); +} +EXPORT_SYMBOL(yaml_scalar_printf); + +void yaml_pair_printf(struct yaml_emitter *emitter, + const char *name, const char *fmt, ...) +{ + struct yaml_event *event; + va_list ap; + char *str; + + event = yaml_scalar_event_create(NULL, NULL, name, -1, false, + true, true, 0, emitter->gfp); + yaml_emitter_emit(emitter, event); + + va_start(ap, fmt); + str = kvasprintf(emitter->gfp, fmt, ap); + va_end(ap); + + event = yaml_scalar_event_create(NULL, NULL, str, -1, true, + true, true, 0, emitter->gfp); + yaml_emitter_emit(emitter, event); +} +EXPORT_SYMBOL(yaml_pair_printf); + +void yaml_ascii85_encode(struct yaml_emitter *emitter, + const void *data, size_t len, bool compressed) +{ + struct yaml_event *event; + const u32 *src = data; + char *buf, *dst; + int i; + + if (!data || !len) + return; + + buf = kmalloc(ascii85_encode_len(len) * 5, emitter->gfp); + if (!buf) + return; + + dst = buf; + while (len > 4) { + u32 in = *src++; + + if (in == 0) { + *dst++ = 'z'; + } else { + for (i = 5; i--; ) { + dst[i] = '!' + in % 85; + in /= 85; + } + dst += 5; + } + len -= 4; + } + + if (len) { + u32 in = 0; + + memcpy(&in, src, len); + + if (in == 0) { + *dst++ = 'z'; + } else { + for (i = 5; i--; ) { + dst[i] = '!' + in % 85; + in /= 85; + } + dst += 5; + } + } + + event = yaml_scalar_event_create(NULL, + compressed ? "ascii85.gz" : "ascii85", + buf, dst - buf, true, + false, false, + YAML_FOLDED_SCALAR_STYLE, + emitter->gfp); + yaml_emitter_emit(emitter, event); +} +EXPORT_SYMBOL(yaml_ascii85_encode); + +int yaml_close(struct yaml_emitter *emitter, const char **problem) +{ + int err; + + yaml_emitter_emit(emitter, + yaml_document_end_event_create(true, GFP_KERNEL)); + yaml_emitter_emit(emitter, + yaml_stream_end_event_create(GFP_KERNEL)); + + err = yaml_emitter_flush(emitter, problem); + yaml_emitter_destroy(emitter); + + return err; +} +EXPORT_SYMBOL(yaml_close);