mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
395 lines
9.8 KiB
C
395 lines
9.8 KiB
C
|
|
||
|
#include "yaml_private.h"
|
||
|
|
||
|
/*
|
||
|
* API functions.
|
||
|
*/
|
||
|
|
||
|
YAML_DECLARE(int)
|
||
|
yaml_emitter_open(yaml_emitter_t *emitter);
|
||
|
|
||
|
YAML_DECLARE(int)
|
||
|
yaml_emitter_close(yaml_emitter_t *emitter);
|
||
|
|
||
|
YAML_DECLARE(int)
|
||
|
yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document);
|
||
|
|
||
|
/*
|
||
|
* Clean up functions.
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter);
|
||
|
|
||
|
/*
|
||
|
* Anchor functions.
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index);
|
||
|
|
||
|
static yaml_char_t *
|
||
|
yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Serialize functions.
|
||
|
*/
|
||
|
|
||
|
static int
|
||
|
yaml_emitter_dump_node(yaml_emitter_t *emitter, int index);
|
||
|
|
||
|
static int
|
||
|
yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor);
|
||
|
|
||
|
static int
|
||
|
yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node,
|
||
|
yaml_char_t *anchor);
|
||
|
|
||
|
static int
|
||
|
yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node,
|
||
|
yaml_char_t *anchor);
|
||
|
|
||
|
static int
|
||
|
yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node,
|
||
|
yaml_char_t *anchor);
|
||
|
|
||
|
/*
|
||
|
* Issue a STREAM-START event.
|
||
|
*/
|
||
|
|
||
|
YAML_DECLARE(int)
|
||
|
yaml_emitter_open(yaml_emitter_t *emitter)
|
||
|
{
|
||
|
yaml_event_t event;
|
||
|
yaml_mark_t mark = { 0, 0, 0 };
|
||
|
|
||
|
assert(emitter); /* Non-NULL emitter object is required. */
|
||
|
assert(!emitter->opened); /* Emitter should not be opened yet. */
|
||
|
|
||
|
STREAM_START_EVENT_INIT(event, YAML_ANY_ENCODING, mark, mark);
|
||
|
|
||
|
if (!yaml_emitter_emit(emitter, &event)) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
emitter->opened = 1;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Issue a STREAM-END event.
|
||
|
*/
|
||
|
|
||
|
YAML_DECLARE(int)
|
||
|
yaml_emitter_close(yaml_emitter_t *emitter)
|
||
|
{
|
||
|
yaml_event_t event;
|
||
|
yaml_mark_t mark = { 0, 0, 0 };
|
||
|
|
||
|
assert(emitter); /* Non-NULL emitter object is required. */
|
||
|
assert(emitter->opened); /* Emitter should be opened. */
|
||
|
|
||
|
if (emitter->closed) return 1;
|
||
|
|
||
|
STREAM_END_EVENT_INIT(event, mark, mark);
|
||
|
|
||
|
if (!yaml_emitter_emit(emitter, &event)) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
emitter->closed = 1;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Dump a YAML document.
|
||
|
*/
|
||
|
|
||
|
YAML_DECLARE(int)
|
||
|
yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document)
|
||
|
{
|
||
|
yaml_event_t event;
|
||
|
yaml_mark_t mark = { 0, 0, 0 };
|
||
|
|
||
|
assert(emitter); /* Non-NULL emitter object is required. */
|
||
|
assert(document); /* Non-NULL emitter object is expected. */
|
||
|
|
||
|
emitter->document = document;
|
||
|
|
||
|
if (!emitter->opened) {
|
||
|
if (!yaml_emitter_open(emitter)) goto error;
|
||
|
}
|
||
|
|
||
|
if (STACK_EMPTY(emitter, document->nodes)) {
|
||
|
if (!yaml_emitter_close(emitter)) goto error;
|
||
|
yaml_emitter_delete_document_and_anchors(emitter);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
assert(emitter->opened); /* Emitter should be opened. */
|
||
|
|
||
|
emitter->anchors = yaml_malloc(sizeof(*(emitter->anchors))
|
||
|
* (document->nodes.top - document->nodes.start));
|
||
|
if (!emitter->anchors) goto error;
|
||
|
memset(emitter->anchors, 0, sizeof(*(emitter->anchors))
|
||
|
* (document->nodes.top - document->nodes.start));
|
||
|
|
||
|
DOCUMENT_START_EVENT_INIT(event, document->version_directive,
|
||
|
document->tag_directives.start, document->tag_directives.end,
|
||
|
document->start_implicit, mark, mark);
|
||
|
if (!yaml_emitter_emit(emitter, &event)) goto error;
|
||
|
|
||
|
yaml_emitter_anchor_node(emitter, 1);
|
||
|
if (!yaml_emitter_dump_node(emitter, 1)) goto error;
|
||
|
|
||
|
DOCUMENT_END_EVENT_INIT(event, document->end_implicit, mark, mark);
|
||
|
if (!yaml_emitter_emit(emitter, &event)) goto error;
|
||
|
|
||
|
yaml_emitter_delete_document_and_anchors(emitter);
|
||
|
|
||
|
return 1;
|
||
|
|
||
|
error:
|
||
|
|
||
|
yaml_emitter_delete_document_and_anchors(emitter);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Clean up the emitter object after a document is dumped.
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter)
|
||
|
{
|
||
|
int index;
|
||
|
|
||
|
if (!emitter->anchors) {
|
||
|
yaml_document_delete(emitter->document);
|
||
|
emitter->document = NULL;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (index = 0; emitter->document->nodes.start + index
|
||
|
< emitter->document->nodes.top; index ++) {
|
||
|
yaml_node_t node = emitter->document->nodes.start[index];
|
||
|
if (!emitter->anchors[index].serialized) {
|
||
|
yaml_free(node.tag);
|
||
|
if (node.type == YAML_SCALAR_NODE) {
|
||
|
yaml_free(node.data.scalar.value);
|
||
|
}
|
||
|
}
|
||
|
if (node.type == YAML_SEQUENCE_NODE) {
|
||
|
STACK_DEL(emitter, node.data.sequence.items);
|
||
|
}
|
||
|
if (node.type == YAML_MAPPING_NODE) {
|
||
|
STACK_DEL(emitter, node.data.mapping.pairs);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
STACK_DEL(emitter, emitter->document->nodes);
|
||
|
yaml_free(emitter->anchors);
|
||
|
|
||
|
emitter->anchors = NULL;
|
||
|
emitter->last_anchor_id = 0;
|
||
|
emitter->document = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check the references of a node and assign the anchor id if needed.
|
||
|
*/
|
||
|
|
||
|
static void
|
||
|
yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index)
|
||
|
{
|
||
|
yaml_node_t *node = emitter->document->nodes.start + index - 1;
|
||
|
yaml_node_item_t *item;
|
||
|
yaml_node_pair_t *pair;
|
||
|
|
||
|
emitter->anchors[index-1].references ++;
|
||
|
|
||
|
if (emitter->anchors[index-1].references == 1) {
|
||
|
switch (node->type) {
|
||
|
case YAML_SEQUENCE_NODE:
|
||
|
for (item = node->data.sequence.items.start;
|
||
|
item < node->data.sequence.items.top; item ++) {
|
||
|
yaml_emitter_anchor_node(emitter, *item);
|
||
|
}
|
||
|
break;
|
||
|
case YAML_MAPPING_NODE:
|
||
|
for (pair = node->data.mapping.pairs.start;
|
||
|
pair < node->data.mapping.pairs.top; pair ++) {
|
||
|
yaml_emitter_anchor_node(emitter, pair->key);
|
||
|
yaml_emitter_anchor_node(emitter, pair->value);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
else if (emitter->anchors[index-1].references == 2) {
|
||
|
emitter->anchors[index-1].anchor = (++ emitter->last_anchor_id);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Generate a textual representation for an anchor.
|
||
|
*/
|
||
|
|
||
|
#define ANCHOR_TEMPLATE "id%03d"
|
||
|
#define ANCHOR_TEMPLATE_LENGTH 16
|
||
|
|
||
|
static yaml_char_t *
|
||
|
yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id)
|
||
|
{
|
||
|
yaml_char_t *anchor = yaml_malloc(ANCHOR_TEMPLATE_LENGTH);
|
||
|
|
||
|
if (!anchor) return NULL;
|
||
|
|
||
|
sprintf((char *)anchor, ANCHOR_TEMPLATE, anchor_id);
|
||
|
|
||
|
return anchor;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Serialize a node.
|
||
|
*/
|
||
|
|
||
|
static int
|
||
|
yaml_emitter_dump_node(yaml_emitter_t *emitter, int index)
|
||
|
{
|
||
|
yaml_node_t *node = emitter->document->nodes.start + index - 1;
|
||
|
int anchor_id = emitter->anchors[index-1].anchor;
|
||
|
yaml_char_t *anchor = NULL;
|
||
|
|
||
|
if (anchor_id) {
|
||
|
anchor = yaml_emitter_generate_anchor(emitter, anchor_id);
|
||
|
if (!anchor) return 0;
|
||
|
}
|
||
|
|
||
|
if (emitter->anchors[index-1].serialized) {
|
||
|
return yaml_emitter_dump_alias(emitter, anchor);
|
||
|
}
|
||
|
|
||
|
emitter->anchors[index-1].serialized = 1;
|
||
|
|
||
|
switch (node->type) {
|
||
|
case YAML_SCALAR_NODE:
|
||
|
return yaml_emitter_dump_scalar(emitter, node, anchor);
|
||
|
case YAML_SEQUENCE_NODE:
|
||
|
return yaml_emitter_dump_sequence(emitter, node, anchor);
|
||
|
case YAML_MAPPING_NODE:
|
||
|
return yaml_emitter_dump_mapping(emitter, node, anchor);
|
||
|
default:
|
||
|
assert(0); /* Could not happen. */
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return 0; /* Could not happen. */
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Serialize an alias.
|
||
|
*/
|
||
|
|
||
|
static int
|
||
|
yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor)
|
||
|
{
|
||
|
yaml_event_t event;
|
||
|
yaml_mark_t mark = { 0, 0, 0 };
|
||
|
|
||
|
ALIAS_EVENT_INIT(event, anchor, mark, mark);
|
||
|
|
||
|
return yaml_emitter_emit(emitter, &event);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Serialize a scalar.
|
||
|
*/
|
||
|
|
||
|
static int
|
||
|
yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node,
|
||
|
yaml_char_t *anchor)
|
||
|
{
|
||
|
yaml_event_t event;
|
||
|
yaml_mark_t mark = { 0, 0, 0 };
|
||
|
|
||
|
int plain_implicit = (strcmp((char *)node->tag,
|
||
|
YAML_DEFAULT_SCALAR_TAG) == 0);
|
||
|
int quoted_implicit = (strcmp((char *)node->tag,
|
||
|
YAML_DEFAULT_SCALAR_TAG) == 0);
|
||
|
|
||
|
SCALAR_EVENT_INIT(event, anchor, node->tag, node->data.scalar.value,
|
||
|
node->data.scalar.length, plain_implicit, quoted_implicit,
|
||
|
node->data.scalar.style, mark, mark);
|
||
|
|
||
|
return yaml_emitter_emit(emitter, &event);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Serialize a sequence.
|
||
|
*/
|
||
|
|
||
|
static int
|
||
|
yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node,
|
||
|
yaml_char_t *anchor)
|
||
|
{
|
||
|
yaml_event_t event;
|
||
|
yaml_mark_t mark = { 0, 0, 0 };
|
||
|
|
||
|
int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_SEQUENCE_TAG) == 0);
|
||
|
|
||
|
yaml_node_item_t *item;
|
||
|
|
||
|
SEQUENCE_START_EVENT_INIT(event, anchor, node->tag, implicit,
|
||
|
node->data.sequence.style, mark, mark);
|
||
|
if (!yaml_emitter_emit(emitter, &event)) return 0;
|
||
|
|
||
|
for (item = node->data.sequence.items.start;
|
||
|
item < node->data.sequence.items.top; item ++) {
|
||
|
if (!yaml_emitter_dump_node(emitter, *item)) return 0;
|
||
|
}
|
||
|
|
||
|
SEQUENCE_END_EVENT_INIT(event, mark, mark);
|
||
|
if (!yaml_emitter_emit(emitter, &event)) return 0;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Serialize a mapping.
|
||
|
*/
|
||
|
|
||
|
static int
|
||
|
yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node,
|
||
|
yaml_char_t *anchor)
|
||
|
{
|
||
|
yaml_event_t event;
|
||
|
yaml_mark_t mark = { 0, 0, 0 };
|
||
|
|
||
|
int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_MAPPING_TAG) == 0);
|
||
|
|
||
|
yaml_node_pair_t *pair;
|
||
|
|
||
|
MAPPING_START_EVENT_INIT(event, anchor, node->tag, implicit,
|
||
|
node->data.mapping.style, mark, mark);
|
||
|
if (!yaml_emitter_emit(emitter, &event)) return 0;
|
||
|
|
||
|
for (pair = node->data.mapping.pairs.start;
|
||
|
pair < node->data.mapping.pairs.top; pair ++) {
|
||
|
if (!yaml_emitter_dump_node(emitter, pair->key)) return 0;
|
||
|
if (!yaml_emitter_dump_node(emitter, pair->value)) return 0;
|
||
|
}
|
||
|
|
||
|
MAPPING_END_EVENT_INIT(event, mark, mark);
|
||
|
if (!yaml_emitter_emit(emitter, &event)) return 0;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|