[PATCH] lib: Add a YAML emitter
Chris Wilson
chris at chris-wilson.co.uk
Wed Feb 12 15:28:54 UTC 2020
Provide a library to generate correct YAML for use in structured debugfs
or similar information dumps.
---
include/linux/yaml.h | 760 ++++++++++++++
lib/Kconfig | 4 +
lib/Makefile | 2 +
lib/yaml-emitter.c | 2326 ++++++++++++++++++++++++++++++++++++++++++
lib/yaml-event.c | 404 ++++++++
5 files changed, 3496 insertions(+)
create mode 100644 include/linux/yaml.h
create mode 100644 lib/yaml-emitter.c
create mode 100644 lib/yaml-event.c
diff --git a/include/linux/yaml.h b/include/linux/yaml.h
new file mode 100644
index 000000000000..92816f16e1dd
--- /dev/null
+++ b/include/linux/yaml.h
@@ -0,0 +1,760 @@
+#ifndef __LINUX_YAML_H__
+#define __LINUX_YAML_H__
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+/** The version directive data. */
+typedef struct yaml_version_directive_s {
+ int major;
+ int minor;
+} yaml_version_directive_t;
+
+/** The tag directive data. */
+typedef struct yaml_tag_directive_s {
+ char *handle;
+ char *prefix;
+} yaml_tag_directive_t;
+
+/** Many bad things could happen with the parser and emitter. */
+typedef enum yaml_error_type_e {
+ /** No error is produced. */
+ YAML_NO_ERROR,
+
+ /** Cannot allocate or reallocate a block of memory. */
+ YAML_MEMORY_ERROR,
+
+ /** Cannot read or decode the input stream. */
+ YAML_READER_ERROR,
+ /** Cannot scan the input stream. */
+ YAML_SCANNER_ERROR,
+ /** Cannot parse the input stream. */
+ YAML_PARSER_ERROR,
+ /** Cannot compose a YAML document. */
+ YAML_COMPOSER_ERROR,
+
+ /** Cannot write to the output stream. */
+ YAML_WRITER_ERROR,
+ /** Cannot emit a YAML stream. */
+ YAML_EMITTER_ERROR,
+} yaml_error_type_t;
+
+/** The pointer position. */
+typedef struct yaml_mark_s {
+ size_t index;
+ size_t line;
+ size_t column;
+} yaml_mark_t;
+
+/** Scalar styles. */
+enum yaml_scalar_style {
+ YAML_ANY_SCALAR_STYLE, /** 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, /** Let the emitter choose the style. */
+
+ YAML_BLOCK_SEQUENCE_STYLE,
+ YAML_FLOW_SEQUENCE_STYLE,
+};
+
+/** Mapping styles. */
+typedef enum yaml_mapping_style_e {
+ YAML_ANY_MAPPING_STYLE, /** Let the emitter choose the style. */
+
+ YAML_BLOCK_MAPPING_STYLE,
+ YAML_FLOW_MAPPING_STYLE
+} yaml_mapping_style_t;
+
+/** Token types. */
+typedef enum yaml_token_type_e {
+ YAML_NO_TOKEN,
+
+ YAML_STREAM_START_TOKEN,
+ YAML_STREAM_END_TOKEN,
+
+ YAML_VERSION_DIRECTIVE_TOKEN,
+ YAML_TAG_DIRECTIVE_TOKEN,
+ YAML_DOCUMENT_START_TOKEN,
+ YAML_DOCUMENT_END_TOKEN,
+
+ YAML_BLOCK_SEQUENCE_START_TOKEN,
+ YAML_BLOCK_MAPPING_START_TOKEN,
+ YAML_BLOCK_END_TOKEN,
+
+ YAML_FLOW_SEQUENCE_START_TOKEN,
+ YAML_FLOW_SEQUENCE_END_TOKEN,
+ YAML_FLOW_MAPPING_START_TOKEN,
+ YAML_FLOW_MAPPING_END_TOKEN,
+
+ YAML_BLOCK_ENTRY_TOKEN,
+ YAML_FLOW_ENTRY_TOKEN,
+ YAML_KEY_TOKEN,
+ YAML_VALUE_TOKEN,
+
+ YAML_ALIAS_TOKEN,
+ YAML_ANCHOR_TOKEN,
+ YAML_TAG_TOKEN,
+ YAML_SCALAR_TOKEN
+} yaml_token_type_t;
+
+typedef struct yaml_token_s {
+ yaml_token_type_t type;
+
+ union {
+ /** The stream start (for @c YAML_STREAM_START_TOKEN). */
+ struct {
+ } stream_start;
+
+ /** The alias (for @c YAML_ALIAS_TOKEN). */
+ struct {
+ const char *value;
+ } alias;
+
+ /** The anchor (for @c YAML_ANCHOR_TOKEN). */
+ struct {
+ const char *value;
+ } anchor;
+
+ /** The tag (for @c YAML_TAG_TOKEN). */
+ struct {
+ const char *handle;
+ const char *suffix;
+ } tag;
+
+ /** The scalar value (for @c YAML_SCALAR_TOKEN). */
+ struct {
+ const char *value;
+ size_t length;
+ enum yaml_scalar_style style;
+ } scalar;
+
+ /** The version directive (for @c YAML_VERSION_DIRECTIVE_TOKEN). */
+ struct {
+ int major;
+ int minor;
+ } version_directive;
+
+ /** The tag directive (for @c YAML_TAG_DIRECTIVE_TOKEN). */
+ struct {
+ const char *handle;
+ const char *prefix;
+ } tag_directive;
+
+ } data;
+
+ yaml_mark_t start_mark;
+ yaml_mark_t end_mark;
+} yaml_token_t;
+
+void yaml_token_delete(yaml_token_t *token);
+
+typedef enum yaml_event_type_e {
+ YAML_NO_EVENT,
+
+ 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
+} yaml_event_type_t;
+
+typedef struct yaml_event_s {
+ yaml_event_type_t type;
+ struct list_head link;
+
+ union {
+ /** The stream parameters (for @c YAML_STREAM_START_EVENT). */
+ struct {
+ } stream_start;
+
+ /** The document parameters (for @c YAML_DOCUMENT_START_EVENT). */
+ struct {
+ yaml_version_directive_t *version_directive;
+
+ struct {
+ yaml_tag_directive_t *start;
+ yaml_tag_directive_t *end;
+ } tag_directives;
+
+ int implicit;
+ } document_start;
+
+ /** The document end parameters (for @c YAML_DOCUMENT_END_EVENT). */
+ struct {
+ int implicit;
+ } document_end;
+
+ /** The alias parameters (for @c YAML_ALIAS_EVENT). */
+ struct {
+ char *anchor;
+ } alias;
+
+ /** The scalar parameters (for @c 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 @c YAML_SEQUENCE_START_EVENT). */
+ struct {
+ char *anchor;
+ char *tag;
+ int implicit;
+ enum yaml_sequence_style style;
+ } sequence_start;
+
+ /** The mapping parameters (for @c YAML_MAPPING_START_EVENT). */
+ struct {
+ char *anchor;
+ char *tag;
+ int implicit;
+ yaml_mapping_style_t style;
+ } mapping_start;
+ };
+
+ yaml_mark_t start_mark;
+ yaml_mark_t end_mark;
+} yaml_event_t;
+
+yaml_event_t *yaml_stream_start_event_create(void);
+yaml_event_t *yaml_stream_end_event_create(void);
+
+/**
+ * Create the DOCUMENT-START event.
+ *
+ * The @a implicit argument is considered as a stylistic parameter and may be
+ * ignored by the emitter.
+ *
+ * @param[out] event An empty event object.
+ * @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.
+ */
+
+yaml_event_t *
+yaml_document_start_event_create(yaml_version_directive_t *version_directive,
+ yaml_tag_directive_t *tag_directives_start,
+ yaml_tag_directive_t *tag_directives_end,
+ int implicit);
+
+/**
+ * Create the DOCUMENT-END event.
+ *
+ * The @a implicit argument is considered as a stylistic parameter and may be
+ * ignored by the emitter.
+ *
+ * @param[out] event An empty event object.
+ * @param[in] implicit If the document end indicator is implicit.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+yaml_event_t *
+yaml_document_end_event_create(int implicit);
+
+/**
+ * Create an ALIAS event.
+ *
+ * @param[out] event An empty event object.
+ * @param[in] anchor The anchor value.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+
+yaml_event_t *
+yaml_alias_event_create(const char *anchor);
+
+/**
+ * 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[out] event An empty event object.
+ * @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.
+ */
+
+yaml_event_t *
+yaml_scalar_event_create(const char *anchor, const char *tag,
+ const char *value, int length,
+ int plain_implicit, int quoted_implicit,
+ enum yaml_scalar_style style);
+
+/**
+ * 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[out] event An empty event object.
+ * @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.
+ */
+
+yaml_event_t *
+yaml_sequence_start_event_create(const char *anchor,
+ const char *tag,
+ int implicit,
+ enum yaml_sequence_style style);
+
+/**
+ * Create a SEQUENCE-END event.
+ *
+ * @param[out] event An empty event object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+yaml_event_t *
+yaml_sequence_end_event_create(void);
+
+/**
+ * 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[out] event An empty event object.
+ * @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.
+ */
+yaml_event_t *
+yaml_mapping_start_event_create(const char *anchor,
+ const char *tag,
+ int implicit,
+ yaml_mapping_style_t style);
+
+/**
+ * Create a MAPPING-END event.
+ *
+ * @param[out] event An empty event object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+yaml_event_t *
+yaml_mapping_end_event_create(void);
+
+/**
+ * Free any memory allocated for an event object.
+ *
+ * @param[in,out] event An event object.
+ */
+
+void yaml_event_delete(yaml_event_t *event);
+
+/** @} */
+
+/**
+ * @defgroup nodes Nodes
+ * @{
+ */
+
+/** The tag @c !!null with the only possible value: @c null. */
+#define YAML_NULL_TAG "tag:yaml.org,2002:null"
+/** The tag @c !!bool with the values: @c true and @c false. */
+#define YAML_BOOL_TAG "tag:yaml.org,2002:bool"
+/** The tag @c !!str for string values. */
+#define YAML_STR_TAG "tag:yaml.org,2002:str"
+/** The tag @c !!int for integer values. */
+#define YAML_INT_TAG "tag:yaml.org,2002:int"
+/** The tag @c !!float for float values. */
+#define YAML_FLOAT_TAG "tag:yaml.org,2002:float"
+/** The tag @c !!timestamp for date and time values. */
+#define YAML_TIMESTAMP_TAG "tag:yaml.org,2002:timestamp"
+
+/** The tag @c !!seq is used to denote sequences. */
+#define YAML_SEQ_TAG "tag:yaml.org,2002:seq"
+/** The tag @c !!map is used to denote mapping. */
+#define YAML_MAP_TAG "tag:yaml.org,2002:map"
+
+/** The default scalar tag is @c !!str. */
+#define YAML_DEFAULT_SCALAR_TAG YAML_STR_TAG
+/** The default sequence tag is @c !!seq. */
+#define YAML_DEFAULT_SEQUENCE_TAG YAML_SEQ_TAG
+/** The default mapping tag is @c !!map. */
+#define YAML_DEFAULT_MAPPING_TAG YAML_MAP_TAG
+
+/** Node types. */
+typedef enum yaml_node_type_e {
+ /** An empty node. */
+ YAML_NO_NODE,
+
+ YAML_SCALAR_NODE,
+ YAML_SEQUENCE_NODE,
+ YAML_MAPPING_NODE
+} yaml_node_type_t;
+
+/** The forward definition of a document node structure. */
+typedef struct yaml_node_s yaml_node_t;
+
+/** An element of a sequence node. */
+typedef int yaml_node_item_t;
+
+/** An element of a mapping node. */
+typedef struct yaml_node_pair_s {
+ int key;
+ int value;
+} yaml_node_pair_t;
+
+/** The node structure. */
+struct yaml_node_s {
+ yaml_node_type_t type;
+
+ char *tag;
+
+ union {
+ /** The scalar parameters (for @c YAML_SCALAR_NODE). */
+ struct {
+ char *value;
+ size_t length;
+ enum yaml_scalar_style style;
+ } scalar;
+
+ /** The sequence parameters (for @c YAML_SEQUENCE_NODE). */
+ struct {
+ struct {
+ yaml_node_item_t *start;
+ yaml_node_item_t *end;
+ yaml_node_item_t *top;
+ } items;
+ enum yaml_sequence_style style;
+ } sequence;
+
+ /** The mapping parameters (for @c YAML_MAPPING_NODE). */
+ struct {
+ struct {
+ yaml_node_pair_t *start;
+ yaml_node_pair_t *end;
+ yaml_node_pair_t *top;
+ } pairs;
+ yaml_mapping_style_t style;
+ } mapping;
+
+ } data;
+
+ yaml_mark_t start_mark;
+ yaml_mark_t end_mark;
+};
+typedef int yaml_write_handler_t(void *data, u8 *buffer, size_t size);
+
+/** The emitter states. */
+typedef enum yaml_emitter_state_e {
+ /** Expect STREAM-START. */
+ YAML_EMIT_STREAM_START_STATE,
+ /** Expect the first DOCUMENT-START or STREAM-END. */
+ YAML_EMIT_FIRST_DOCUMENT_START_STATE,
+ /** Expect DOCUMENT-START or STREAM-END. */
+ YAML_EMIT_DOCUMENT_START_STATE,
+ /** Expect the content of a document. */
+ YAML_EMIT_DOCUMENT_CONTENT_STATE,
+ /** Expect DOCUMENT-END. */
+ YAML_EMIT_DOCUMENT_END_STATE,
+ /** Expect the first item of a flow sequence. */
+ YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE,
+ /** Expect an item of a flow sequence. */
+ YAML_EMIT_FLOW_SEQUENCE_ITEM_STATE,
+ /** Expect the first key of a flow mapping. */
+ YAML_EMIT_FLOW_MAPPING_FIRST_KEY_STATE,
+ /** Expect a key of a flow mapping. */
+ YAML_EMIT_FLOW_MAPPING_KEY_STATE,
+ /** Expect a value for a simple key of a flow mapping. */
+ YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE,
+ /** Expect a value of a flow mapping. */
+ YAML_EMIT_FLOW_MAPPING_VALUE_STATE,
+ /** Expect the first item of a block sequence. */
+ YAML_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE,
+ /** Expect an item of a block sequence. */
+ YAML_EMIT_BLOCK_SEQUENCE_ITEM_STATE,
+ /** Expect the first key of a block mapping. */
+ YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE,
+ /** Expect the key of a block mapping. */
+ YAML_EMIT_BLOCK_MAPPING_KEY_STATE,
+ /** Expect a value for a simple key of a block mapping. */
+ YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE,
+ /** Expect a value of a block mapping. */
+ YAML_EMIT_BLOCK_MAPPING_VALUE_STATE,
+ /** Expect nothing. */
+ YAML_EMIT_END_STATE
+} yaml_emitter_state_t;
+
+typedef struct yaml_anchors_s {
+ int references;
+ int anchor;
+ int serialized;
+} yaml_anchors_t;
+
+typedef struct yaml_emitter_s {
+ yaml_error_type_t error;
+ const char *problem;
+
+ yaml_write_handler_t *write_handler;
+ void *write_handler_data;
+
+ union {
+ struct {
+ unsigned char *buffer;
+ size_t size;
+ size_t *size_written;
+ } string;
+ } output;
+
+ struct {
+ char *start;
+ char *end;
+ char *pos;
+ char *last;
+ } buffer;
+
+ int canonical;
+ int best_indent;
+ int best_width;
+ int unicode;
+
+ struct {
+ yaml_emitter_state_t *start;
+ yaml_emitter_state_t *end;
+ yaml_emitter_state_t *top;
+ } states;
+
+ yaml_emitter_state_t state;
+
+ struct list_head events;
+ int num_events;
+
+ struct {
+ int *start;
+ int *end;
+ int *top;
+ } indents;
+
+ struct {
+ yaml_tag_directive_t *start;
+ yaml_tag_directive_t *end;
+ yaml_tag_directive_t *top;
+ } tag_directives;
+
+ int indent;
+ int flow_level;
+
+ int root_context;
+ int sequence_context;
+ int mapping_context;
+ int simple_key_context;
+
+ int line;
+ 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;
+
+ yaml_anchors_t *anchors;
+ int last_anchor_id;
+} yaml_emitter_t;
+
+/**
+ * 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.
+ */
+
+int yaml_emitter_init(yaml_emitter_t *emitter);
+
+/**
+ * Destroy an emitter.
+ *
+ * @param[in,out] emitter An emitter object.
+ */
+
+void yaml_emitter_destroy(yaml_emitter_t *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_output_string(yaml_emitter_t *emitter,
+ u8 *output, size_t size,
+ size_t *size_written);
+
+static inline void yaml_emitter_set_output(yaml_emitter_t *emitter,
+ yaml_write_handler_t *handler,
+ void *data)
+{
+ emitter->write_handler = handler;
+ emitter->write_handler_data = 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.
+ */
+
+static inline void
+yaml_emitter_set_canonical(yaml_emitter_t *emitter, bool canonical)
+{
+ emitter->canonical = canonical;
+}
+
+/**
+ * Set the indentation increment.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] indent The indentation increment (1 < . < 10).
+ */
+static inline void
+yaml_emitter_set_indent(yaml_emitter_t *emitter, int indent)
+{
+ emitter->best_indent = (1 < indent && indent < 10) ? indent : 2;
+}
+
+/**
+ * Set the preferred line width. @c -1 means unlimited.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] width The preferred line width.
+ */
+
+static inline void
+yaml_emitter_set_width(yaml_emitter_t *emitter, int width)
+{
+ emitter->best_width = (width >= 0) ? width : -1;
+}
+
+/**
+ * Set if unescaped non-ASCII characters are allowed.
+ *
+ * @param[in,out] emitter An emitter object.
+ * @param[in] unicode If unescaped Unicode characters are allowed.
+ */
+
+static inline void
+yaml_emitter_set_unicode(yaml_emitter_t *emitter, bool unicode)
+{
+ emitter->unicode = 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.
+ */
+int yaml_emitter_emit(yaml_emitter_t *emitter, yaml_event_t *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(yaml_emitter_t *emitter);
+
+#endif /* __LINUX_YAML_H__ */
diff --git a/lib/Kconfig b/lib/Kconfig
index 0cf875fd627c..c83ec9a3c53e 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -648,6 +648,10 @@ config OBJAGG
config STRING_SELFTEST
tristate "Test string functions"
+config YAML_EMITTER
+ tristate
+ default m
+
endmenu
config GENERIC_IOREMAP
diff --git a/lib/Makefile b/lib/Makefile
index 5d64890d6b6a..6b4d983dd2af 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -241,6 +241,8 @@ obj-$(CONFIG_ASN1) += asn1_decoder.o
obj-$(CONFIG_FONT_SUPPORT) += fonts/
+obj-$(CONFIG_YAML_EMITTER) += yaml-emitter.o yaml-event.o
+
hostprogs := gen_crc32table
hostprogs += gen_crc64table
clean-files := crc32table.h
diff --git a/lib/yaml-emitter.c b/lib/yaml-emitter.c
new file mode 100644
index 000000000000..22ea5e6c14e8
--- /dev/null
+++ b/lib/yaml-emitter.c
@@ -0,0 +1,2326 @@
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/yaml.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)
+
+/*
+ * Check if the character can be printed unescaped.
+ */
+
+#define IS_PRINTABLE_AT(s, offset) isprint((s).pos[offset])
+#define IS_PRINTABLE(s) IS_PRINTABLE_AT((s), 0)
+
+/*
+ * Check if the character at the specified position is NUL.
+ */
+
+#define IS_Z_AT(string,offset) CHECK_AT((string),'\0',(offset))
+
+#define IS_Z(string) IS_Z_AT((string),0)
+
+/*
+ * Check if the character at the specified position is BOM.
+ */
+
+#define IS_BOM_AT(string,offset) \
+ (CHECK_AT((string),'\xEF',(offset)) \
+ && CHECK_AT((string),'\xBB',(offset)+1) \
+ && CHECK_AT((string),'\xBF',(offset)+2)) /* BOM (#xFEFF) */
+
+#define IS_BOM(string) IS_BOM_AT(string,0)
+
+/*
+ * Check if the character at the specified position is space.
+ */
+
+#define IS_SPACE_AT(string,offset) CHECK_AT((string),' ',(offset))
+
+#define IS_SPACE(string) IS_SPACE_AT((string),0)
+
+/*
+ * Check if the character at the specified position is tab.
+ */
+
+#define IS_TAB_AT(string,offset) CHECK_AT((string),'\t',(offset))
+
+#define IS_TAB(string) IS_TAB_AT((string),0)
+
+/*
+ * Check if the character at the specified position is blank (space or tab).
+ */
+
+#define IS_BLANK_AT(string,offset) \
+ (IS_SPACE_AT((string),(offset)) || IS_TAB_AT((string),(offset)))
+
+#define IS_BLANK(string) IS_BLANK_AT((string),0)
+
+/*
+ * Check if the character at the specified position is a line break.
+ */
+
+#define IS_BREAK_AT(string,offset) \
+ (CHECK_AT((string),'\r',(offset)) /* CR (#xD)*/ \
+ || CHECK_AT((string),'\n',(offset)) /* LF (#xA) */ \
+ || (CHECK_AT((string),'\xC2',(offset)) \
+ && CHECK_AT((string),'\x85',(offset)+1)) /* NEL (#x85) */ \
+ || (CHECK_AT((string),'\xE2',(offset)) \
+ && CHECK_AT((string),'\x80',(offset)+1) \
+ && CHECK_AT((string),'\xA8',(offset)+2)) /* LS (#x2028) */ \
+ || (CHECK_AT((string),'\xE2',(offset)) \
+ && CHECK_AT((string),'\x80',(offset)+1) \
+ && CHECK_AT((string),'\xA9',(offset)+2))) /* PS (#x2029) */
+
+#define IS_BREAK(string) IS_BREAK_AT((string),0)
+
+#define IS_CRLF_AT(string,offset) \
+ (CHECK_AT((string),'\r',(offset)) && CHECK_AT((string),'\n',(offset)+1))
+
+#define IS_CRLF(string) IS_CRLF_AT((string),0)
+
+/*
+ * Check if the character is a line break or NUL.
+ */
+
+#define IS_BREAKZ_AT(string,offset) \
+ (IS_BREAK_AT((string),(offset)) || IS_Z_AT((string),(offset)))
+
+#define IS_BREAKZ(string) IS_BREAKZ_AT((string),0)
+
+/*
+ * Check if the character is a line break, space, or NUL.
+ */
+
+#define IS_SPACEZ_AT(string,offset) \
+ (IS_SPACE_AT((string),(offset)) || IS_BREAKZ_AT((string),(offset)))
+
+#define IS_SPACEZ(string) IS_SPACEZ_AT((string),0)
+
+/*
+ * Check if the character is a line break, space, tab, or NUL.
+ */
+
+#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)
+
+/*
+ * Determine the width of the character.
+ */
+
+#define WIDTH_AT(s,offset) \
+ (((s).pos[offset] & 0x80) == 0x00 ? 1 : \
+ ((s).pos[offset] & 0xE0) == 0xC0 ? 2 : \
+ ((s).pos[offset] & 0xF0) == 0xE0 ? 3 : \
+ ((s).pos[offset] & 0xF8) == 0xF0 ? 4 : 0)
+
+#define WIDTH(s) WIDTH_AT((s),0)
+
+#define MOVE(s) ((s).pos += WIDTH((s)))
+
+/*
+ * Copy a character and move the pointers of both strings.
+ */
+
+#define COPY(string_a,string_b) \
+ ((*(string_b).pos & 0x80) == 0x00 ? \
+ (*((string_a).pos++) = *((string_b).pos++)) : \
+ (*(string_b).pos & 0xE0) == 0xC0 ? \
+ (*((string_a).pos++) = *((string_b).pos++), \
+ *((string_a).pos++) = *((string_b).pos++)) : \
+ (*(string_b).pos & 0xF0) == 0xE0 ? \
+ (*((string_a).pos++) = *((string_b).pos++), \
+ *((string_a).pos++) = *((string_b).pos++), \
+ *((string_a).pos++) = *((string_b).pos++)) : \
+ (*(string_b).pos & 0xF8) == 0xF0 ? \
+ (*((string_a).pos++) = *((string_b).pos++), \
+ *((string_a).pos++) = *((string_b).pos++), \
+ *((string_a).pos++) = *((string_b).pos++), \
+ *((string_a).pos++) = *((string_b).pos++)) : 0)
+
+/*
+ * Stack and queue management.
+ */
+
+static int yaml_stack_extend(void **start, void **top, void **end);
+
+#define STACK_EMPTY(context,stack) \
+ ((stack).start == (stack).top)
+
+#define STACK_LIMIT(context,stack,size) \
+ ((stack).top - (stack).start < (size) ? \
+ 1 : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+#define PUSH(context,stack,value) \
+ (((stack).top != (stack).end \
+ || yaml_stack_extend((void **)&(stack).start, \
+ (void **)&(stack).top, (void **)&(stack).end)) ? \
+ (*((stack).top++) = value, \
+ 1) : \
+ ((context)->error = YAML_MEMORY_ERROR, \
+ 0))
+
+#define POP(context,stack) \
+ (*(--(stack).top))
+
+/*
+ * Flush the buffer if needed.
+ */
+
+#define FLUSH(e) \
+ ((e)->buffer.pos + 5 < (e)->buffer.end || yaml_emitter_flush(e))
+
+/*
+ * Put a character to the output buffer.
+ */
+
+#define PUT(e, value) \
+ (FLUSH(e) && (*((e)->buffer.pos++) = (value), (e)->column++, 1))
+
+/*
+ * Put a line break to the output buffer.
+ */
+
+#define __PUT_BREAK(e) \
+ (*((e)->buffer.pos++) = '\n', \
+ (e)->column = 0, \
+ (e)->line++)
+
+#define PUT_BREAK(e) \
+ (FLUSH(e) && (__PUT_BREAK(e), 1))
+
+/*
+ * Copy a character from a string into buffer.
+ */
+
+#define EMIT(emitter, s) \
+ (FLUSH(emitter) \
+ && (COPY(emitter->buffer, s), \
+ emitter->column ++, \
+ 1))
+
+/*
+ * Copy a line break character from a string into buffer.
+ */
+
+#define WRITE_BREAK(emitter, s) \
+ (FLUSH(emitter) \
+ && (CHECK(s,'\n') ? \
+ (__PUT_BREAK(emitter), \
+ s.pos ++, \
+ 1) : \
+ (COPY(emitter->buffer, s), \
+ emitter->column = 0, \
+ emitter->line ++, \
+ 1)))
+
+/*
+ * Utility functions.
+ */
+
+static int
+set_error(yaml_emitter_t *emitter, const char *problem);
+
+/*
+ * Set an emitter error and return 0.
+ */
+
+static int
+set_error(yaml_emitter_t *emitter, const char *problem)
+{
+ emitter->error = YAML_EMITTER_ERROR;
+ emitter->problem = problem;
+
+ return 0;
+}
+
+static yaml_event_t *next_event(const yaml_emitter_t *emitter)
+{
+ return list_first_entry(&emitter->events, yaml_event_t, link);
+}
+
+/*
+ * Check if we need to accumulate more events before emitting.
+ *
+ * We accumulate extra
+ * - 1 event for DOCUMENT-START
+ * - 2 events for SEQUENCE-START
+ * - 3 events for MAPPING-START
+ */
+
+static bool need_more_events(yaml_emitter_t *emitter)
+{
+ yaml_event_t *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;
+}
+
+
+/*
+ * Check if a %YAML directive is valid.
+ */
+
+static int
+analyze_version_directive(yaml_emitter_t *emitter,
+ yaml_version_directive_t version_directive)
+{
+ if (version_directive.major != 1 || version_directive.minor != 1) {
+ return set_error(emitter,
+ "incompatible %YAML directive");
+ }
+
+ return 1;
+}
+
+/*
+ * Check if a %TAG directive is valid.
+ */
+
+static int
+analyze_tag_directive(yaml_emitter_t *emitter,
+ yaml_tag_directive_t tag_directive)
+{
+ YAML_STRING(handle, tag_directive.handle);
+ YAML_STRING(prefix, tag_directive.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");
+ }
+ MOVE(handle);
+ }
+
+ if (prefix.start == prefix.end) {
+ return set_error(emitter,
+ "tag prefix must not be empty");
+ }
+
+ return 1;
+}
+
+/*
+ * Check if an anchor is valid.
+ */
+
+static int
+analyze_anchor(yaml_emitter_t *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");
+ }
+ MOVE(string);
+ }
+
+ emitter->anchor_data.anchor = string.start;
+ emitter->anchor_data.anchor_length = string.end - string.start;
+ emitter->anchor_data.alias = alias;
+
+ return 1;
+}
+
+/*
+ * Check if a tag is valid.
+ */
+
+static int
+analyze_tag(yaml_emitter_t *emitter, const char *tag)
+{
+ YAML_STRING(string, tag);
+ yaml_tag_directive_t *tag_directive;
+
+ if (string.start == string.end) {
+ return set_error(emitter,
+ "tag value must not be empty");
+ }
+
+ for (tag_directive = emitter->tag_directives.start;
+ tag_directive != emitter->tag_directives.top; tag_directive ++) {
+ size_t prefix_length = strlen((char *)tag_directive->prefix);
+ if (prefix_length < (size_t)(string.end - string.start)
+ && strncmp((char *)tag_directive->prefix, (char *)string.start,
+ prefix_length) == 0)
+ {
+ emitter->tag_data.handle = tag_directive->handle;
+ emitter->tag_data.handle_length =
+ strlen((char *)tag_directive->handle);
+ emitter->tag_data.suffix = string.start + prefix_length;
+ emitter->tag_data.suffix_length =
+ (string.end - string.start) - prefix_length;
+ return 1;
+ }
+ }
+
+ emitter->tag_data.suffix = string.start;
+ emitter->tag_data.suffix_length = string.end - string.start;
+
+ return 1;
+}
+
+/*
+ * Check if a scalar is valid.
+ */
+
+static int
+analyze_scalar(yaml_emitter_t *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 1;
+ }
+
+ 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, 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+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 + 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);
+ MOVE(string);
+ if (string.pos != string.end)
+ followed_by_whitespace = IS_BLANKZ_AT(string, 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 1;
+}
+
+/*
+ * Check if the event data is valid.
+ */
+
+static int
+analyze_event(yaml_emitter_t *emitter, const yaml_event_t *event)
+{
+ 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:
+ if (!analyze_anchor(emitter, event->alias.anchor, 1))
+ return 0;
+ return 1;
+
+ case YAML_SCALAR_EVENT:
+ if (event->scalar.anchor) {
+ if (!analyze_anchor(emitter, event->scalar.anchor, 0))
+ return 0;
+ }
+ if (event->scalar.tag && (emitter->canonical ||
+ (!event->scalar.plain_implicit
+ && !event->scalar.quoted_implicit))) {
+ if (!analyze_tag(emitter, event->scalar.tag))
+ return 0;
+ }
+ if (!analyze_scalar(emitter,
+ event->scalar.value,
+ event->scalar.length))
+ return 0;
+ return 1;
+
+ case YAML_SEQUENCE_START_EVENT:
+ if (event->sequence_start.anchor) {
+ if (!analyze_anchor(emitter, event->sequence_start.anchor, 0))
+ return 0;
+ }
+ if (event->sequence_start.tag && (emitter->canonical ||
+ !event->sequence_start.implicit)) {
+ if (!analyze_tag(emitter,
+ event->sequence_start.tag))
+ return 0;
+ }
+ return 1;
+
+ case YAML_MAPPING_START_EVENT:
+ if (event->mapping_start.anchor) {
+ if (!analyze_anchor(emitter,
+ event->mapping_start.anchor, 0))
+ return 0;
+ }
+ if (event->mapping_start.tag &&
+ (emitter->canonical ||
+ !event->mapping_start.implicit)) {
+ if (!analyze_tag(emitter,
+ event->mapping_start.tag))
+ return 0;
+ }
+ return 1;
+
+ default:
+ return 1;
+ }
+}
+
+static bool check_empty_document(const yaml_emitter_t *emitter)
+{
+ return false;
+}
+
+static bool check_empty_sequence(const yaml_emitter_t *emitter)
+{
+ const yaml_event_t *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 yaml_emitter_t *emitter)
+{
+ const yaml_event_t *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 yaml_emitter_t *emitter)
+{
+ const yaml_event_t *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(yaml_emitter_t *emitter, bool flow, bool indentless)
+{
+ if (!PUSH(emitter, emitter->indents, emitter->indent))
+ return 0;
+
+ if (emitter->indent < 0)
+ emitter->indent = flow ? emitter->best_indent : 0;
+ else if (!indentless)
+ emitter->indent += emitter->best_indent;
+
+ return 1;
+}
+
+static int write_indent(yaml_emitter_t *emitter)
+{
+ int indent = (emitter->indent >= 0) ? emitter->indent : 0;
+
+ if (!emitter->indention ||
+ emitter->column > indent ||
+ (emitter->column == indent && !emitter->whitespace)) {
+ if (!PUT_BREAK(emitter)) return 0;
+ }
+
+ while (emitter->column < indent) {
+ if (!PUT(emitter, ' ')) return 0;
+ }
+
+ emitter->whitespace = 1;
+ emitter->indention = 1;
+
+ return 1;
+}
+
+static int write_indicator(yaml_emitter_t *emitter,
+ const char *indicator, int need_whitespace,
+ int is_whitespace, int is_indention)
+{
+ YAML_STRING(string, indicator);
+
+ if (need_whitespace && !emitter->whitespace) {
+ if (!PUT(emitter, ' ')) return 0;
+ }
+
+ while (string.pos != string.end) {
+ if (!EMIT(emitter, string)) return 0;
+ }
+
+ emitter->whitespace = is_whitespace;
+ emitter->indention = (emitter->indention && is_indention);
+ emitter->open_ended = 0;
+
+ return 1;
+}
+
+static int write_anchor(yaml_emitter_t *emitter,
+ const char *value, size_t length)
+{
+ struct yaml_string string = STRING_INIT(value, length);
+
+ while (string.pos != string.end) {
+ if (!EMIT(emitter, string)) return 0;
+ }
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+
+ return 1;
+}
+
+static int write_tag_handle(yaml_emitter_t *emitter,
+ const char *value, size_t length)
+{
+ struct yaml_string string = STRING_INIT(value, length);
+
+ if (!emitter->whitespace) {
+ if (!PUT(emitter, ' ')) return 0;
+ }
+
+ while (string.pos != string.end) {
+ if (!EMIT(emitter, string)) return 0;
+ }
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+
+ return 1;
+}
+
+static int write_tag_content(yaml_emitter_t *emitter,
+ const char *value, size_t length,
+ int need_whitespace)
+{
+ struct yaml_string string = STRING_INIT(value, length);
+
+ if (need_whitespace && !emitter->whitespace) {
+ if (!PUT(emitter, ' ')) return 0;
+ }
+
+ 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, ']')) {
+ if (!EMIT(emitter, string)) return 0;
+ } else {
+ int width = WIDTH(string);
+ unsigned int value;
+ while (width --) {
+ value = *(string.pos++);
+ if (!PUT(emitter, '%')) return 0;
+ if (!PUT(emitter, (value >> 4) +
+ ((value >> 4) < 10 ? '0' : 'A' - 10)))
+ return 0;
+ if (!PUT(emitter,
+ (value & 0x0F) +
+ ((value & 0x0F) < 10 ? '0' : 'A' - 10)))
+ return 0;
+ }
+ }
+ }
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+
+ return 1;
+}
+
+static int write_tag(yaml_emitter_t *emitter)
+{
+ if (!emitter->tag_data.handle && !emitter->tag_data.suffix)
+ return 1;
+
+ if (emitter->tag_data.handle) {
+ if (!write_tag_handle(emitter,
+ emitter->tag_data.handle,
+ emitter->tag_data.handle_length))
+ return 0;
+
+ if (emitter->tag_data.suffix) {
+ if (!write_tag_content(emitter,
+ emitter->tag_data.suffix,
+ emitter->tag_data.suffix_length,
+ 0))
+ return 0;
+ }
+ } else {
+ if (!write_indicator(emitter, "!<", 1, 0, 0))
+ return 0;
+
+ if (!write_tag_content(emitter,
+ emitter->tag_data.suffix,
+ emitter->tag_data.suffix_length,
+ 0))
+ return 0;
+
+ if (!write_indicator(emitter, ">", 0, 0, 0))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int write_plain_scalar(yaml_emitter_t *emitter,
+ const char *value,
+ size_t length,
+ int allow_breaks)
+{
+ struct yaml_string string = STRING_INIT(value, length);
+ int spaces = 0;
+ int breaks = 0;
+
+ if (!emitter->whitespace) {
+ if (!PUT(emitter, ' '))
+ return 0;
+ }
+
+ while (string.pos != string.end) {
+ if (IS_SPACE(string)) {
+ if (allow_breaks && !spaces &&
+ emitter->column > emitter->best_width &&
+ !IS_SPACE_AT(string, 1)) {
+ if (!write_indent(emitter))
+ return 0;
+ MOVE(string);
+ } else {
+ if (!EMIT(emitter, string))
+ return 0;
+ }
+ spaces = 1;
+ } else if (IS_BREAK(string)) {
+ if (!breaks && CHECK(string, '\n')) {
+ if (!PUT_BREAK(emitter))
+ return 0;
+ }
+
+ if (!WRITE_BREAK(emitter, string))
+ return 0;
+
+ emitter->indention = 1;
+ breaks = 1;
+ } else {
+ if (breaks) {
+ if (!write_indent(emitter))
+ return 0;
+ }
+
+ if (!EMIT(emitter, string))
+ return 0;
+
+ emitter->indention = 0;
+ spaces = 0;
+ breaks = 0;
+ }
+ }
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+ if (emitter->root_context)
+ emitter->open_ended = 1;
+
+ return 1;
+}
+
+static int
+write_single_quoted_scalar(yaml_emitter_t *emitter,
+ const char *value,
+ size_t length,
+ int allow_breaks)
+{
+ struct yaml_string string = STRING_INIT(value, length);
+ int spaces = 0;
+ int breaks = 0;
+
+ if (!write_indicator(emitter, "'", 1, 0, 0))
+ return 0;
+
+ 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)) {
+ if (!write_indent(emitter))
+ return 0;
+ MOVE(string);
+ } else {
+ if (!EMIT(emitter, string))
+ return 0;
+ }
+ spaces = 1;
+ } else if (IS_BREAK(string)) {
+ if (!breaks && CHECK(string, '\n')) {
+ if (!PUT_BREAK(emitter))
+ return 0;
+ }
+
+ if (!WRITE_BREAK(emitter, string))
+ return 0;
+
+ emitter->indention = 1;
+ breaks = 1;
+ } else {
+ if (breaks) {
+ if (!write_indent(emitter))
+ return 0;
+ }
+
+ if (CHECK(string, '\'')) {
+ if (!PUT(emitter, '\''))
+ return 0;
+ }
+
+ if (!EMIT(emitter, string))
+ return 0;
+
+ emitter->indention = 0;
+ spaces = 0;
+ breaks = 0;
+ }
+ }
+
+ if (breaks) {
+ if (!write_indent(emitter))
+ return 0;
+ }
+
+ if (!write_indicator(emitter, "'", 0, 0, 0))
+ return 0;
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+
+ return 1;
+}
+
+static int
+write_double_quoted_scalar(yaml_emitter_t *emitter,
+ const char *value, size_t length, int allow_breaks)
+{
+ struct yaml_string string = STRING_INIT(value, length);
+ int spaces = 0;
+
+ if (!write_indicator(emitter, "\"", 1, 0, 0))
+ return 0;
+
+ while (string.pos != string.end) {
+ if (!IS_PRINTABLE(string) ||
+ (!emitter->unicode && !IS_ASCII(string)) ||
+ IS_BOM(string) ||
+ IS_BREAK(string) ||
+ CHECK(string, '"') ||
+ CHECK(string, '\\')) {
+ unsigned char octet;
+ unsigned int width;
+ unsigned int value;
+ int k;
+
+ octet = string.pos[0];
+ 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;
+
+ if (!PUT(emitter, '\\'))
+ return 0;
+
+ switch (value) {
+ case 0x00:
+ if (!PUT(emitter, '0'))
+ return 0;
+ break;
+
+ case 0x07:
+ if (!PUT(emitter, 'a'))
+ return 0;
+ break;
+
+ case 0x08:
+ if (!PUT(emitter, 'b'))
+ return 0;
+ break;
+
+ case 0x09:
+ if (!PUT(emitter, 't'))
+ return 0;
+ break;
+
+ case 0x0A:
+ if (!PUT(emitter, 'n'))
+ return 0;
+ break;
+
+ case 0x0B:
+ if (!PUT(emitter, 'v'))
+ return 0;
+ break;
+
+ case 0x0C:
+ if (!PUT(emitter, 'f'))
+ return 0;
+ break;
+
+ case 0x0D:
+ if (!PUT(emitter, 'r'))
+ return 0;
+ break;
+
+ case 0x1B:
+ if (!PUT(emitter, 'e'))
+ return 0;
+ break;
+
+ case 0x22:
+ if (!PUT(emitter, '\"'))
+ return 0;
+ break;
+
+ case 0x5C:
+ if (!PUT(emitter, '\\'))
+ return 0;
+ break;
+
+ case 0x85:
+ if (!PUT(emitter, 'N'))
+ return 0;
+ break;
+
+ case 0xA0:
+ if (!PUT(emitter, '_'))
+ return 0;
+ break;
+
+ case 0x2028:
+ if (!PUT(emitter, 'L'))
+ return 0;
+ break;
+
+ case 0x2029:
+ if (!PUT(emitter, 'P'))
+ return 0;
+ break;
+
+ default:
+ if (value <= 0xFF) {
+ if (!PUT(emitter, 'x'))
+ return 0;
+ width = 2;
+ } else if (value <= 0xFFFF) {
+ if (!PUT(emitter, 'u'))
+ return 0;
+ width = 4;
+ } else {
+ if (!PUT(emitter, 'U'))
+ return 0;
+ width = 8;
+ }
+ for (k = (width-1)*4; k >= 0; k -= 4) {
+ int digit = (value >> k) & 0x0F;
+ if (!PUT(emitter, digit + (digit < 10 ? '0' : 'A'-10)))
+ return 0;
+ }
+ }
+ 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) {
+ if (!write_indent(emitter))
+ return 0;
+
+ if (IS_SPACE_AT(string, 1)) {
+ if (!PUT(emitter, '\\'))
+ return 0;
+ }
+ MOVE(string);
+ } else {
+ if (!EMIT(emitter, string))
+ return 0;
+ }
+ spaces = 1;
+ } else {
+ if (!EMIT(emitter, string))
+ return 0;
+ spaces = 0;
+ }
+ }
+
+ if (!write_indicator(emitter, "\"", 0, 0, 0))
+ return 0;
+
+ emitter->whitespace = 0;
+ emitter->indention = 0;
+
+ return 1;
+}
+
+static int
+write_block_scalar_hints(yaml_emitter_t *emitter,
+ struct yaml_string string)
+{
+ char indent_hint[2];
+ const char *chomp_hint = NULL;
+
+ if (IS_SPACE(string) || IS_BREAK(string)) {
+ indent_hint[0] = '0' + (char)emitter->best_indent;
+ indent_hint[1] = '\0';
+ if (!write_indicator(emitter, indent_hint, 0, 0, 0))
+ return 0;
+ }
+
+ 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) {
+ if (!write_indicator(emitter, chomp_hint, 0, 0, 0))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+write_literal_scalar(yaml_emitter_t *emitter,
+ const char *value, size_t length)
+{
+ struct yaml_string string = STRING_INIT(value, length);
+ int breaks = 1;
+
+ if (!write_indicator(emitter, "|", 1, 0, 0))
+ return 0;
+
+ if (!write_block_scalar_hints(emitter, string))
+ return 0;
+
+ if (!PUT_BREAK(emitter)) return 0;
+
+ emitter->indention = 1;
+ emitter->whitespace = 1;
+
+ while (string.pos != string.end) {
+ if (IS_BREAK(string)) {
+ if (!WRITE_BREAK(emitter, string))
+ return 0;
+
+ emitter->indention = 1;
+ breaks = 1;
+ } else {
+ if (breaks) {
+ if (!write_indent(emitter))
+ return 0;
+ }
+
+ if (!EMIT(emitter, string))
+ return 0;
+
+ emitter->indention = 0;
+ breaks = 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+write_folded_scalar(yaml_emitter_t *emitter,
+ const char *value, size_t length)
+{
+ struct yaml_string string = STRING_INIT(value, length);
+ int breaks = 1;
+ int leading_spaces = 1;
+
+ if (!write_indicator(emitter, ">", 1, 0, 0))
+ return 0;
+
+ if (!write_block_scalar_hints(emitter, string))
+ return 0;
+
+ if (!PUT_BREAK(emitter))
+ return 0;
+
+ emitter->indention = 1;
+ emitter->whitespace = 1;
+
+ while (string.pos != string.end) {
+ if (IS_BREAK(string)) {
+ if (!breaks && !leading_spaces && CHECK(string, '\n')) {
+ int k = 0;
+ while (IS_BREAK_AT(string, k))
+ k += WIDTH_AT(string, k);
+
+ if (!IS_BLANKZ_AT(string, k))
+ if (!PUT_BREAK(emitter)) return 0;
+ }
+
+ if (!WRITE_BREAK(emitter, string))
+ return 0;
+
+ emitter->indention = 1;
+ breaks = 1;
+ } else {
+ if (breaks) {
+ if (!write_indent(emitter)) return 0;
+ leading_spaces = IS_BLANK(string);
+ }
+
+ if (!breaks &&
+ IS_SPACE(string) &&
+ !IS_SPACE_AT(string, 1) &&
+ emitter->column > emitter->best_width) {
+ if (!write_indent(emitter))
+ return 0;
+
+ MOVE(string);
+ } else {
+ if (!EMIT(emitter, string))
+ return 0;
+ }
+ emitter->indention = 0;
+ breaks = 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+write_scalar(yaml_emitter_t *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(yaml_emitter_t *emitter)
+{
+ if (!emitter->anchor_data.anchor)
+ return 1;
+
+ if (!write_indicator(emitter,
+ (emitter->anchor_data.alias ? "*" : "&"), 1, 0, 0))
+ return 0;
+
+ return write_anchor(emitter,
+ emitter->anchor_data.anchor,
+ emitter->anchor_data.anchor_length);
+}
+
+static int emit_stream_start(yaml_emitter_t *emitter, const yaml_event_t *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->line = 0;
+ emitter->column = 0;
+ emitter->whitespace = 1;
+ emitter->indention = 1;
+
+ emitter->state = YAML_EMIT_FIRST_DOCUMENT_START_STATE;
+
+ return 1;
+ }
+
+ return set_error(emitter, "expected STREAM-START");
+}
+
+static int
+append_tag_directive(yaml_emitter_t *emitter,
+ yaml_tag_directive_t value,
+ int allow_duplicates)
+{
+ yaml_tag_directive_t *tag_directive;
+ yaml_tag_directive_t copy = {};
+
+ for (tag_directive = emitter->tag_directives.start;
+ tag_directive != emitter->tag_directives.top;
+ tag_directive ++) {
+ if (strcmp(value.handle, tag_directive->handle) == 0) {
+ if (allow_duplicates)
+ return 1;
+
+ 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->error = YAML_MEMORY_ERROR;
+ goto error;
+ }
+
+ if (!PUSH(emitter, emitter->tag_directives, copy))
+ goto error;
+
+ return 1;
+
+error:
+ kfree(copy.handle);
+ kfree(copy.prefix);
+ return 0;
+}
+
+static int
+emit_document_start(yaml_emitter_t *emitter,
+ const yaml_event_t *event,
+ int first)
+{
+ if (event->type == YAML_DOCUMENT_START_EVENT) {
+ yaml_tag_directive_t default_tag_directives[] = {
+ {"!", "!"},
+ {"!!", "tag:yaml.org,2002:"},
+ {NULL, NULL}
+ };
+ yaml_tag_directive_t *tag_directive;
+ int implicit;
+
+ if (event->document_start.version_directive) {
+ if (!analyze_version_directive(emitter,
+ *event->document_start.version_directive))
+ return 0;
+ }
+
+ for (tag_directive = event->document_start.tag_directives.start;
+ tag_directive != event->document_start.tag_directives.end;
+ tag_directive ++) {
+ if (!analyze_tag_directive(emitter, *tag_directive))
+ return 0;
+
+ if (!append_tag_directive(emitter, *tag_directive, 0))
+ return 0;
+ }
+
+ for (tag_directive = default_tag_directives;
+ tag_directive->handle; tag_directive ++) {
+ if (!append_tag_directive(emitter, *tag_directive, 1))
+ return 0;
+ }
+
+ implicit = event->document_start.implicit;
+ if (!first || emitter->canonical) {
+ implicit = 0;
+ }
+
+ if ((event->document_start.version_directive ||
+ (event->document_start.tag_directives.start
+ != event->document_start.tag_directives.end)) &&
+ emitter->open_ended) {
+ if (!write_indicator(emitter, "...", 1, 0, 0))
+ return 0;
+
+ if (!write_indent(emitter))
+ return 0;
+ }
+
+ if (event->document_start.version_directive) {
+ implicit = 0;
+ if (!write_indicator(emitter, "%YAML", 1, 0, 0))
+ return 0;
+
+ if (!write_indicator(emitter, "1.1", 1, 0, 0))
+ return 0;
+
+ if (!write_indent(emitter))
+ return 0;
+ }
+
+ if (event->document_start.tag_directives.start
+ != event->document_start.tag_directives.end) {
+ implicit = 0;
+ for (tag_directive = event->document_start.tag_directives.start;
+ tag_directive != event->document_start.tag_directives.end;
+ tag_directive ++) {
+ if (!write_indicator(emitter, "%TAG", 1, 0, 0))
+ return 0;
+
+ if (!write_tag_handle(emitter,
+ tag_directive->handle,
+ strlen(tag_directive->handle)))
+ return 0;
+
+ if (!write_tag_content(emitter,
+ tag_directive->prefix,
+ strlen(tag_directive->prefix),
+ 1))
+ return 0;
+
+ if (!write_indent(emitter))
+ return 0;
+ }
+ }
+
+ if (check_empty_document(emitter))
+ implicit = 0;
+
+ if (!implicit) {
+ if (!write_indent(emitter))
+ return 0;
+
+ if (!write_indicator(emitter, "---", 1, 0, 0))
+ return 0;
+
+ if (emitter->canonical) {
+ if (!write_indent(emitter))
+ return 0;
+ }
+ }
+
+ emitter->state = YAML_EMIT_DOCUMENT_CONTENT_STATE;
+
+ return 1;
+ } else if (event->type == YAML_STREAM_END_EVENT) {
+ if (!yaml_emitter_flush(emitter))
+ return 0;
+
+ emitter->state = YAML_EMIT_END_STATE;
+
+ return 1;
+ }
+
+ return set_error(emitter, "expected DOCUMENT-START or STREAM-END");
+}
+
+static int
+emit_document_end(yaml_emitter_t *emitter, const yaml_event_t *event)
+{
+ if (event->type == YAML_DOCUMENT_END_EVENT) {
+ if (!write_indent(emitter))
+ return 0;
+
+ if (!event->document_end.implicit) {
+ if (!write_indicator(emitter, "...", 1, 0, 0))
+ return 0;
+
+ if (!write_indent(emitter))
+ return 0;
+ }
+
+ if (!yaml_emitter_flush(emitter))
+ return 0;
+
+ emitter->state = YAML_EMIT_DOCUMENT_START_STATE;
+
+ while (!STACK_EMPTY(emitter, emitter->tag_directives)) {
+ yaml_tag_directive_t tag_directive =
+ POP(emitter, emitter->tag_directives);
+ kfree(tag_directive.handle);
+ kfree(tag_directive.prefix);
+ }
+
+ return 1;
+ }
+
+ return set_error(emitter, "expected DOCUMENT-END");
+}
+
+static int
+emit_alias(yaml_emitter_t *emitter, const yaml_event_t *event)
+{
+ if (!emit_anchor(emitter))
+ return 0;
+
+ emitter->state = POP(emitter, emitter->states);
+ return 1;
+}
+
+static int
+select_scalar_style(yaml_emitter_t *emitter, const yaml_event_t *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 1;
+}
+
+static int
+emit_scalar(yaml_emitter_t *emitter, const yaml_event_t *event)
+{
+ if (!select_scalar_style(emitter, event))
+ return 0;
+
+ if (!emit_anchor(emitter))
+ return 0;
+
+ if (!write_tag(emitter))
+ return 0;
+
+ if (!increase_indent(emitter, 1, 0))
+ return 0;
+
+ if (!write_scalar(emitter))
+ return 0;
+
+ emitter->indent = POP(emitter, emitter->indents);
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+}
+
+static int
+emit_sequence_start(yaml_emitter_t *emitter, const yaml_event_t *event)
+{
+ if (!emit_anchor(emitter))
+ return 0;
+
+ if (!write_tag(emitter))
+ return 0;
+
+ if (emitter->flow_level || emitter->canonical ||
+ event->sequence_start.style == YAML_FLOW_SEQUENCE_STYLE ||
+ check_empty_sequence(emitter)) {
+ emitter->state = YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE;
+ } else {
+ emitter->state = YAML_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE;
+ }
+
+ return 1;
+}
+
+static int
+emit_mapping_start(yaml_emitter_t *emitter, const yaml_event_t *event)
+{
+ if (!emit_anchor(emitter))
+ return 0;
+
+ if (!write_tag(emitter))
+ return 0;
+
+ if (emitter->flow_level ||
+ emitter->canonical ||
+ event->mapping_start.style == YAML_FLOW_MAPPING_STYLE ||
+ check_empty_mapping(emitter)) {
+ emitter->state = YAML_EMIT_FLOW_MAPPING_FIRST_KEY_STATE;
+ } else {
+ emitter->state = YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE;
+ }
+
+ return 1;
+}
+
+static int
+emit_node(yaml_emitter_t *emitter, const yaml_event_t *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(yaml_emitter_t *emitter, const yaml_event_t *event)
+{
+ if (!PUSH(emitter, emitter->states, YAML_EMIT_DOCUMENT_END_STATE))
+ return 0;
+
+ return emit_node(emitter, event, 1, 0, 0, 0);
+}
+
+static int
+emit_flow_sequence_item(yaml_emitter_t *emitter,
+ const yaml_event_t *event,
+ bool first)
+{
+ if (first) {
+ if (!write_indicator(emitter, "[", 1, 1, 0))
+ return 0;
+
+ if (!increase_indent(emitter, 1, 0))
+ return 0;
+
+ emitter->flow_level ++;
+ }
+
+ if (event->type == YAML_SEQUENCE_END_EVENT) {
+ emitter->flow_level --;
+ emitter->indent = POP(emitter, emitter->indents);
+
+ if (emitter->canonical && !first) {
+ if (!write_indicator(emitter, ",", 0, 0, 0))
+ return 0;
+
+ if (!write_indent(emitter))
+ return 0;
+ }
+
+ if (!write_indicator(emitter, "]", 0, 0, 0))
+ return 0;
+
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+ }
+
+ if (!first) {
+ if (!write_indicator(emitter, ",", 0, 0, 0))
+ return 0;
+ }
+
+ if (emitter->canonical || emitter->column > emitter->best_width) {
+ if (!write_indent(emitter))
+ return 0;
+ }
+ if (!PUSH(emitter, emitter->states, YAML_EMIT_FLOW_SEQUENCE_ITEM_STATE))
+ return 0;
+
+ return emit_node(emitter, event, 0, 1, 0, 0);
+}
+
+static int
+emit_flow_mapping_key(yaml_emitter_t *emitter,
+ const yaml_event_t *event, int first)
+{
+ if (first) {
+ if (!write_indicator(emitter, "{", 1, 1, 0))
+ return 0;
+
+ if (!increase_indent(emitter, 1, 0))
+ return 0;
+
+ emitter->flow_level ++;
+ }
+
+ if (event->type == YAML_MAPPING_END_EVENT) {
+ emitter->flow_level --;
+ emitter->indent = POP(emitter, emitter->indents);
+ if (emitter->canonical && !first) {
+ if (!write_indicator(emitter, ",", 0, 0, 0))
+ return 0;
+ if (!write_indent(emitter))
+ return 0;
+ }
+ if (!write_indicator(emitter, "}", 0, 0, 0))
+ return 0;
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+ }
+
+ if (!first) {
+ if (!write_indicator(emitter, ",", 0, 0, 0))
+ return 0;
+ }
+
+ if (emitter->canonical || emitter->column > emitter->best_width) {
+ if (!write_indent(emitter))
+ return 0;
+ }
+
+ if (!emitter->canonical && check_simple_key(emitter)) {
+ if (!PUSH(emitter, emitter->states,
+ YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE))
+ return 0;
+
+ return emit_node(emitter, event, 0, 0, 1, 1);
+ } else {
+ if (!write_indicator(emitter, "?", 1, 0, 0))
+ return 0;
+ if (!PUSH(emitter, emitter->states,
+ YAML_EMIT_FLOW_MAPPING_VALUE_STATE))
+ return 0;
+
+ return emit_node(emitter, event, 0, 0, 1, 0);
+ }
+}
+
+static int
+emit_flow_mapping_value(yaml_emitter_t *emitter,
+ const yaml_event_t *event, int simple)
+{
+ if (simple) {
+ if (!write_indicator(emitter, ":", 0, 0, 0))
+ return 0;
+ } else {
+ if (emitter->canonical ||
+ emitter->column > emitter->best_width) {
+ if (!write_indent(emitter))
+ return 0;
+ }
+
+ if (!write_indicator(emitter, ":", 1, 0, 0))
+ return 0;
+ }
+
+ if (!PUSH(emitter, emitter->states, YAML_EMIT_FLOW_MAPPING_KEY_STATE))
+ return 0;
+
+ return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emit_block_sequence_item(yaml_emitter_t *emitter,
+ const yaml_event_t *event, int first)
+{
+ if (first) {
+ if (!increase_indent(emitter, 0,
+ emitter->mapping_context &&
+ !emitter->indention))
+ return 0;
+ }
+
+ if (event->type == YAML_SEQUENCE_END_EVENT) {
+ emitter->indent = POP(emitter, emitter->indents);
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+ }
+
+ if (!write_indent(emitter))
+ return 0;
+
+ if (!write_indicator(emitter, "-", 1, 0, 1))
+ return 0;
+
+ if (!PUSH(emitter, emitter->states, YAML_EMIT_BLOCK_SEQUENCE_ITEM_STATE))
+ return 0;
+
+ return emit_node(emitter, event, 0, 1, 0, 0);
+}
+
+static int
+emit_block_mapping_key(yaml_emitter_t *emitter,
+ const yaml_event_t *event, int first)
+{
+ if (first) {
+ if (!increase_indent(emitter, 0, 0))
+ return 0;
+ }
+
+ if (event->type == YAML_MAPPING_END_EVENT) {
+ emitter->indent = POP(emitter, emitter->indents);
+ emitter->state = POP(emitter, emitter->states);
+
+ return 1;
+ }
+
+ if (!write_indent(emitter))
+ return 0;
+
+ if (check_simple_key(emitter)) {
+ if (!PUSH(emitter, emitter->states,
+ YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE))
+ return 0;
+
+ return emit_node(emitter, event, 0, 0, 1, 1);
+ } else {
+ if (!write_indicator(emitter, "?", 1, 0, 1))
+ return 0;
+
+ if (!PUSH(emitter, emitter->states,
+ YAML_EMIT_BLOCK_MAPPING_VALUE_STATE))
+ return 0;
+
+ return emit_node(emitter, event, 0, 0, 1, 0);
+ }
+}
+
+static int
+emit_block_mapping_value(yaml_emitter_t *emitter,
+ const yaml_event_t *event, int simple)
+{
+ if (simple) {
+ if (!write_indicator(emitter, ":", 0, 0, 0))
+ return 0;
+ } else {
+ if (!write_indent(emitter))
+ return 0;
+
+ if (!write_indicator(emitter, ":", 1, 0, 1))
+ return 0;
+ }
+
+ if (!PUSH(emitter, emitter->states, YAML_EMIT_BLOCK_MAPPING_KEY_STATE))
+ return 0;
+
+ return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emitter_state_machine(yaml_emitter_t *emitter, const yaml_event_t *event)
+{
+ switch (emitter->state) {
+ case YAML_EMIT_STREAM_START_STATE:
+ return emit_stream_start(emitter, event);
+
+ case YAML_EMIT_FIRST_DOCUMENT_START_STATE:
+ return emit_document_start(emitter, event, true);
+
+ case YAML_EMIT_DOCUMENT_START_STATE:
+ return emit_document_start(emitter, event, false);
+
+ case YAML_EMIT_DOCUMENT_CONTENT_STATE:
+ return emit_document_content(emitter, event);
+
+ case YAML_EMIT_DOCUMENT_END_STATE:
+ return emit_document_end(emitter, event);
+
+ case YAML_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
+ return emit_flow_sequence_item(emitter, event, true);
+
+ case YAML_EMIT_FLOW_SEQUENCE_ITEM_STATE:
+ return emit_flow_sequence_item(emitter, event, false);
+
+ case YAML_EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
+ return emit_flow_mapping_key(emitter, event, true);
+
+ case YAML_EMIT_FLOW_MAPPING_KEY_STATE:
+ return emit_flow_mapping_key(emitter, event, false);
+
+ case YAML_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
+ return emit_flow_mapping_value(emitter, event, true);
+
+ case YAML_EMIT_FLOW_MAPPING_VALUE_STATE:
+ return emit_flow_mapping_value(emitter, event, false);
+
+ case YAML_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE:
+ return emit_block_sequence_item(emitter, event, true);
+
+ case YAML_EMIT_BLOCK_SEQUENCE_ITEM_STATE:
+ return emit_block_sequence_item(emitter, event, false);
+
+ case YAML_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE:
+ return emit_block_mapping_key(emitter, event, true);
+
+ case YAML_EMIT_BLOCK_MAPPING_KEY_STATE:
+ return emit_block_mapping_key(emitter, event, false);
+
+ case YAML_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE:
+ return emit_block_mapping_value(emitter, event, true);
+
+ case YAML_EMIT_BLOCK_MAPPING_VALUE_STATE:
+ return emit_block_mapping_value(emitter, event, false);
+
+ case YAML_EMIT_END_STATE:
+ return set_error(emitter, "expected nothing after STREAM-END");
+
+ default:
+ BUG();
+ }
+
+ return 0;
+}
+
+int yaml_emitter_emit(yaml_emitter_t *emitter, yaml_event_t *event)
+{
+ 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 0;
+
+ if (!emitter_state_machine(emitter, event))
+ return 0;
+
+ emitter->num_events--;
+ list_del(&event->link);
+ yaml_event_delete(event);
+ }
+
+ return 1;
+}
+EXPORT_SYMBOL(yaml_emitter_emit);
+
+static int
+set_writer_error(yaml_emitter_t *emitter, const char *problem)
+{
+ emitter->error = YAML_WRITER_ERROR;
+ emitter->problem = problem;
+
+ return 0;
+}
+
+int yaml_emitter_flush(yaml_emitter_t *emitter)
+{
+ emitter->buffer.last = emitter->buffer.pos;
+ emitter->buffer.pos = emitter->buffer.start;
+
+ if (emitter->buffer.start == emitter->buffer.last)
+ return 1;
+
+ if (emitter->write_handler(emitter->write_handler_data,
+ emitter->buffer.start,
+ emitter->buffer.last - emitter->buffer.start)) {
+ emitter->buffer.last = emitter->buffer.start;
+ emitter->buffer.pos = emitter->buffer.start;
+ return 1;
+ }
+
+ return set_writer_error(emitter, "write error");
+}
+EXPORT_SYMBOL(yaml_emitter_flush);
+
+static int yaml_stack_extend(void **start, void **top, void **end)
+{
+ void *new_start;
+
+ if (*end - *start >= INT_MAX / 2)
+ return 0;
+
+ new_start = krealloc(*start, (*end - *start) * 2, GFP_KERNEL);
+ if (!new_start)
+ return 0;
+
+ *top = new_start + (*top - *start);
+ *end = new_start + (*end - *start) * 2;
+ *start = new_start;
+
+ return 1;
+}
+
+#define BUFFER_INIT(context, buffer, size) \
+ (((buffer).start = kmalloc(size, GFP_KERNEL)) ? \
+ ((buffer).last = (buffer).pos = (buffer).start, \
+ (buffer).end = (buffer).start + (size), \
+ 1) : 0)
+#define BUFFER_DEL(context, buffer) kfree((buffer).start)
+
+#define STACK_INIT(context, stack) \
+ (((stack).start = kmalloc(INITIAL_STACK_SIZE * sizeof(*(stack). start),\
+ GFP_KERNEL)) ? \
+ ((stack).top = (stack).start, \
+ (stack).end = (stack).start + INITIAL_STACK_SIZE, \
+ 1) : 0)
+#define STACK_DEL(context, stack) kfree((stack).start)
+
+int yaml_emitter_init(yaml_emitter_t *emitter)
+{
+ memset(emitter, 0, sizeof(*emitter));
+ INIT_LIST_HEAD(&emitter->events);
+
+ if (!BUFFER_INIT(emitter, emitter->buffer, 4096))
+ goto error;
+
+ if (!STACK_INIT(emitter, emitter->states))
+ goto error;
+
+ if (!STACK_INIT(emitter, emitter->indents))
+ goto error;
+
+ if (!STACK_INIT(emitter, emitter->tag_directives))
+ goto error;
+
+ return 1;
+
+error:
+ BUFFER_DEL(emitter, emitter->buffer);
+ STACK_DEL(emitter, emitter->states);
+ STACK_DEL(emitter, emitter->indents);
+ STACK_DEL(emitter, emitter->tag_directives);
+ memset(emitter, 0, sizeof(*emitter));
+ return -ENOMEM;
+}
+
+static void delete_events(yaml_emitter_t *emitter)
+{
+ yaml_event_t *event, *next;
+
+ list_for_each_entry_safe(event, next, &emitter->events, link)
+ yaml_event_delete(event);
+}
+
+void yaml_emitter_destroy(yaml_emitter_t *emitter)
+{
+ delete_events(emitter);
+
+ BUFFER_DEL(emitter, emitter->buffer);
+ STACK_DEL(emitter, emitter->states);
+ STACK_DEL(emitter, emitter->indents);
+ while (!STACK_EMPTY(empty, emitter->tag_directives)) {
+ yaml_tag_directive_t tag_directive = POP(emitter, emitter->tag_directives);
+
+ kfree(tag_directive.handle);
+ kfree(tag_directive.prefix);
+ }
+ STACK_DEL(emitter, emitter->tag_directives);
+ kfree(emitter->anchors);
+
+ memset(emitter, 0, sizeof(yaml_emitter_t));
+}
+
+static int string_write_handler(void *data, u8 *buffer, size_t size)
+{
+ yaml_emitter_t *emitter = data;
+ typeof(emitter->output.string) *out = &emitter->output.string;
+
+ if (out->size - *out->size_written < size) {
+ memcpy(out->buffer + *out->size_written, buffer,
+ out->size - *out->size_written);
+ *out->size_written = out->size;
+ return 0;
+ }
+
+ memcpy(out->buffer + *out->size_written, buffer, size);
+ *out->size_written += size;
+ return 1;
+}
+
+void yaml_emitter_set_output_string(yaml_emitter_t *emitter,
+ u8 *output,
+ size_t size,
+ size_t *size_written)
+{
+ emitter->write_handler = string_write_handler;
+ emitter->write_handler_data = emitter;
+
+ emitter->output.string.buffer = output;
+ emitter->output.string.size = size;
+ emitter->output.string.size_written = size_written;
+ *size_written = 0;
+}
diff --git a/lib/yaml-event.c b/lib/yaml-event.c
new file mode 100644
index 000000000000..8d568df26b14
--- /dev/null
+++ b/lib/yaml-event.c
@@ -0,0 +1,404 @@
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/yaml.h>
+
+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 yaml_event_t *yaml_event_create(int type)
+{
+ yaml_event_t *ev;
+
+ ev = kzalloc(sizeof(*ev), GFP_KERNEL);
+ if (ev)
+ ev->type = type;
+
+ return ev;
+}
+
+static void yaml_event_free(yaml_event_t *event)
+{
+ kfree(event);
+}
+
+yaml_event_t *yaml_stream_start_event_create(void)
+{
+ return yaml_event_create(YAML_STREAM_START_EVENT);
+}
+
+yaml_event_t *yaml_stream_end_event_create(void)
+{
+ return yaml_event_create(YAML_STREAM_END_EVENT);
+}
+
+#if 0
+yaml_event_t *
+yaml_document_start_event_create(yaml_version_directive_t *version_directive,
+ yaml_tag_directive_t *tag_directives_start,
+ yaml_tag_directive_t *tag_directives_end,
+ int implicit)
+{
+ struct {
+ yaml_error_type_t error;
+ } context;
+ yaml_version_directive_t *version_directive_copy = NULL;
+ struct {
+ yaml_tag_directive_t *start;
+ yaml_tag_directive_t *end;
+ yaml_tag_directive_t *top;
+ } tag_directives_copy = {};
+ yaml_tag_directive_t value = {};
+ yaml_event_t *event;
+
+ if (!((tag_directives_start && tag_directives_end) ||
+ tag_directives_start == tag_directives_end))
+ return NULL;
+
+ event = yaml_event_create(YAML_DOCUMENT_START_EVENT);
+ if (!event)
+ return NULL;
+
+ if (version_directive) {
+ version_directive_copy =
+ kmemdup(version_directive,
+ sizeof(*version_directive),
+ GFP_KERNEL);
+ if (!version_directive_copy)
+ goto error;
+ }
+
+ if (tag_directives_start != tag_directives_end) {
+ yaml_tag_directive_t *tag_directive;
+
+ if (!STACK_INIT(&context, tag_directives_copy, yaml_tag_directive_t*))
+ goto error;
+
+ for (tag_directive = tag_directives_start;
+ tag_directive != tag_directives_end; tag_directive++) {
+ if (!check_utf8(tag_directive->handle,
+ strlen(tag_directive->handle)))
+ goto error;
+
+ if (!check_utf8(tag_directive->prefix,
+ strlen(tag_directive->prefix)))
+ goto error;
+
+ value.handle =
+ kstrdup(tag_directive->handle, GFP_KERNEL);
+ value.prefix =
+ kstrdup(tag_directive->prefix, GFP_KERNEL);
+ if (!value.handle || !value.prefix)
+ goto error;
+
+ if (!PUSH(&context, tag_directives_copy, value))
+ goto error;
+
+ value.handle = NULL;
+ value.prefix = NULL;
+ }
+ }
+
+ event->type = YAML_DOCUMENT_START_EVENT;
+ event->document_start.version_directive = version_directive_copy;
+ event->document_start.tag_directives.start = tag_directives_copy.start;
+ event->document_start.tag_directives.end = tag_directives_copy.top;
+ event->document_start.implicit = implicit;
+
+ return event;
+
+error:
+ kfree(version_directive_copy);
+ while (!STACK_EMPTY(context, tag_directives_copy)) {
+ yaml_tag_directive_t value = POP(context, tag_directives_copy);
+
+ kfree(value.handle);
+ kfree(value.prefix);
+ }
+ STACK_DEL(context, tag_directives_copy);
+ kfree(value.handle);
+ kfree(value.prefix);
+ return 0;
+}
+
+yaml_event_t *
+yaml_document_end_event_create(int implicit)
+{
+ yaml_event_t *event;
+
+ event = yaml_event_create(YAML_DOCUMENT_END_EVENT);
+ if (event)
+ event->document_end.implict = implict;
+
+ return event;
+}
+#endif
+
+yaml_event_t *
+yaml_alias_event_create(const char *anchor)
+{
+ char *anchor_copy;
+ yaml_event_t *event;
+
+ if (!anchor)
+ return NULL;
+
+ if (!check_utf8(anchor, strlen(anchor)))
+ return NULL;
+
+ anchor_copy = kstrdup(anchor, GFP_KERNEL);
+ if (!anchor_copy)
+ return NULL;
+
+ event = yaml_event_create(YAML_ALIAS_EVENT);
+ if (!event) {
+ kfree(anchor_copy);
+ return NULL;
+ }
+
+ event->alias.anchor = anchor_copy;
+ return event;
+}
+
+yaml_event_t *
+yaml_scalar_event_create(const char *anchor, const char *tag,
+ const char *value, int length,
+ int plain_implicit, int quoted_implicit,
+ enum yaml_scalar_style style)
+{
+ char *anchor_copy = NULL;
+ char *tag_copy = NULL;
+ char *value_copy = NULL;
+ yaml_event_t *event;
+
+ if (!value)
+ return NULL;
+
+ if (anchor) {
+ if (!check_utf8(anchor, strlen((anchor))))
+ goto error;
+
+ anchor_copy = kstrdup(anchor, GFP_KERNEL);
+ if (!anchor_copy)
+ goto error;
+ }
+
+ if (tag) {
+ if (!check_utf8(tag, strlen(tag)))
+ goto error;
+
+ tag_copy = kstrdup(tag, GFP_KERNEL);
+ if (!tag_copy)
+ goto error;
+ }
+
+ if (length < 0)
+ length = strlen((char *)value);
+
+ if (!check_utf8(value, length))
+ goto error;
+
+ value_copy = kmemdup(value, length + 1, GFP_KERNEL);
+ if (!value_copy)
+ goto error;
+
+ event = yaml_event_create(YAML_SCALAR_EVENT);
+ if (!event)
+ goto error;
+
+ event->scalar.anchor = anchor_copy;
+ event->scalar.tag = tag_copy;
+ event->scalar.value = value_copy;
+ event->scalar.length = length;
+ 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);
+ kfree(value_copy);
+ return NULL;
+}
+
+yaml_event_t *
+yaml_sequence_start_event_create(const char *anchor,
+ const char *tag,
+ int implicit,
+ enum yaml_sequence_style style)
+{
+ char *anchor_copy = NULL;
+ char *tag_copy = NULL;
+ yaml_event_t *event;
+
+ if (anchor) {
+ if (!check_utf8(anchor, strlen(anchor)))
+ goto error;
+
+ anchor_copy = kstrdup(anchor, GFP_KERNEL);
+ if (!anchor_copy)
+ goto error;
+ }
+
+ if (tag) {
+ if (!check_utf8(tag, strlen(tag)))
+ goto error;
+
+ tag_copy = kstrdup(tag, GFP_KERNEL);
+ if (!tag_copy)
+ goto error;
+ }
+
+ event = yaml_event_create(YAML_SEQUENCE_START_EVENT);
+ 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;
+}
+
+yaml_event_t *yaml_sequence_end_event_create(void)
+{
+ return yaml_event_create(YAML_SEQUENCE_END_EVENT);
+}
+
+yaml_event_t *yaml_mapping_start_event_create(const char *anchor,
+ const char *tag,
+ int implicit,
+ yaml_mapping_style_t style)
+{
+ char *anchor_copy = NULL;
+ char *tag_copy = NULL;
+ yaml_event_t *event;
+
+ if (anchor) {
+ if (!check_utf8(anchor, strlen(anchor)))
+ goto error;
+
+ anchor_copy = kstrdup(anchor, GFP_KERNEL);
+ if (!anchor_copy)
+ goto error;
+ }
+
+ if (tag) {
+ if (!check_utf8(tag, strlen(tag)))
+ goto error;
+
+ tag_copy = kstrdup(tag, GFP_KERNEL);
+ if (!tag_copy)
+ goto error;
+ }
+
+ event = yaml_event_create(YAML_MAPPING_START_EVENT);
+ 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;
+}
+
+yaml_event_t *yaml_mapping_end_event_create(void)
+{
+ return yaml_event_create(YAML_MAPPING_END_EVENT);
+}
+
+void yaml_event_delete(yaml_event_t *event)
+{
+ yaml_tag_directive_t *tag_directive;
+
+ switch (event->type) {
+ case YAML_DOCUMENT_START_EVENT:
+ kfree(event->document_start.version_directive);
+ for (tag_directive = event->document_start.tag_directives.start;
+ tag_directive != event->document_start.tag_directives.end;
+ tag_directive++) {
+ kfree(tag_directive->handle);
+ kfree(tag_directive->prefix);
+ }
+ kfree(event->document_start.tag_directives.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);
--
2.25.0
More information about the Intel-gfx-trybot
mailing list