Coverage Report

Created: 2026-01-29 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/moddable/xs/tools/yaml/dumper.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2006-2016 Kirill Simonov
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5
 * this software and associated documentation files (the "Software"), to deal in
6
 * the Software without restriction, including without limitation the rights to
7
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8
 * of the Software, and to permit persons to whom the Software is furnished to do
9
 * so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in all
12
 * copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
 * SOFTWARE.
21
 *
22
 */
23
24
#include "yaml_private.h"
25
26
/*
27
 * API functions.
28
 */
29
30
YAML_DECLARE(int)
31
yaml_emitter_open(yaml_emitter_t *emitter);
32
33
YAML_DECLARE(int)
34
yaml_emitter_close(yaml_emitter_t *emitter);
35
36
YAML_DECLARE(int)
37
yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document);
38
39
/*
40
 * Clean up functions.
41
 */
42
43
static void
44
yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter);
45
46
/*
47
 * Anchor functions.
48
 */
49
50
static void
51
yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index);
52
53
static yaml_char_t *
54
yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id);
55
56
57
/*
58
 * Serialize functions.
59
 */
60
61
static int
62
yaml_emitter_dump_node(yaml_emitter_t *emitter, int index);
63
64
static int
65
yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor);
66
67
static int
68
yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node,
69
        yaml_char_t *anchor);
70
71
static int
72
yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node,
73
        yaml_char_t *anchor);
74
75
static int
76
yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node,
77
        yaml_char_t *anchor);
78
79
/*
80
 * Issue a STREAM-START event.
81
 */
82
83
YAML_DECLARE(int)
84
yaml_emitter_open(yaml_emitter_t *emitter)
85
0
{
86
0
    yaml_event_t event;
87
0
    yaml_mark_t mark = { 0, 0, 0 };
88
89
0
    assert(emitter);            /* Non-NULL emitter object is required. */
90
0
    assert(!emitter->opened);   /* Emitter should not be opened yet. */
91
92
0
    STREAM_START_EVENT_INIT(event, YAML_ANY_ENCODING, mark, mark);
93
94
0
    if (!yaml_emitter_emit(emitter, &event)) {
95
0
        return 0;
96
0
    }
97
98
0
    emitter->opened = 1;
99
100
0
    return 1;
101
0
}
102
103
/*
104
 * Issue a STREAM-END event.
105
 */
106
107
YAML_DECLARE(int)
108
yaml_emitter_close(yaml_emitter_t *emitter)
109
0
{
110
0
    yaml_event_t event;
111
0
    yaml_mark_t mark = { 0, 0, 0 };
112
113
0
    assert(emitter);            /* Non-NULL emitter object is required. */
114
0
    assert(emitter->opened);    /* Emitter should be opened. */
115
116
0
    if (emitter->closed) return 1;
117
118
0
    STREAM_END_EVENT_INIT(event, mark, mark);
119
120
0
    if (!yaml_emitter_emit(emitter, &event)) {
121
0
        return 0;
122
0
    }
123
124
0
    emitter->closed = 1;
125
126
0
    return 1;
127
0
}
128
129
/*
130
 * Dump a YAML document.
131
 */
132
133
YAML_DECLARE(int)
134
yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document)
135
0
{
136
0
    yaml_event_t event;
137
0
    yaml_mark_t mark = { 0, 0, 0 };
138
139
0
    assert(emitter);            /* Non-NULL emitter object is required. */
140
0
    assert(document);           /* Non-NULL emitter object is expected. */
141
142
0
    emitter->document = document;
143
144
0
    if (!emitter->opened) {
145
0
        if (!yaml_emitter_open(emitter)) goto error;
146
0
    }
147
148
0
    if (STACK_EMPTY(emitter, document->nodes)) {
149
0
        if (!yaml_emitter_close(emitter)) goto error;
150
0
        yaml_emitter_delete_document_and_anchors(emitter);
151
0
        return 1;
152
0
    }
153
154
0
    assert(emitter->opened);    /* Emitter should be opened. */
155
156
0
    emitter->anchors = yaml_malloc(sizeof(*(emitter->anchors))
157
0
            * (document->nodes.top - document->nodes.start));
158
0
    if (!emitter->anchors) goto error;
159
0
    memset(emitter->anchors, 0, sizeof(*(emitter->anchors))
160
0
            * (document->nodes.top - document->nodes.start));
161
162
0
    DOCUMENT_START_EVENT_INIT(event, document->version_directive,
163
0
            document->tag_directives.start, document->tag_directives.end,
164
0
            document->start_implicit, mark, mark);
165
0
    if (!yaml_emitter_emit(emitter, &event)) goto error;
166
167
0
    yaml_emitter_anchor_node(emitter, 1);
168
0
    if (!yaml_emitter_dump_node(emitter, 1)) goto error;
169
170
0
    DOCUMENT_END_EVENT_INIT(event, document->end_implicit, mark, mark);
171
0
    if (!yaml_emitter_emit(emitter, &event)) goto error;
172
173
0
    yaml_emitter_delete_document_and_anchors(emitter);
174
175
0
    return 1;
176
177
0
error:
178
179
0
    yaml_emitter_delete_document_and_anchors(emitter);
180
181
0
    return 0;
182
0
}
183
184
/*
185
 * Clean up the emitter object after a document is dumped.
186
 */
187
188
static void
189
yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter)
190
0
{
191
0
    int index;
192
193
0
    if (!emitter->anchors) {
194
0
        yaml_document_delete(emitter->document);
195
0
        emitter->document = NULL;
196
0
        return;
197
0
    }
198
199
0
    for (index = 0; emitter->document->nodes.start + index
200
0
            < emitter->document->nodes.top; index ++) {
201
0
        yaml_node_t node = emitter->document->nodes.start[index];
202
0
        if (!emitter->anchors[index].serialized) {
203
0
            yaml_free(node.tag);
204
0
            if (node.type == YAML_SCALAR_NODE) {
205
0
                yaml_free(node.data.scalar.value);
206
0
            }
207
0
        }
208
0
        if (node.type == YAML_SEQUENCE_NODE) {
209
0
            STACK_DEL(emitter, node.data.sequence.items);
210
0
        }
211
0
        if (node.type == YAML_MAPPING_NODE) {
212
0
            STACK_DEL(emitter, node.data.mapping.pairs);
213
0
        }
214
0
    }
215
216
0
    STACK_DEL(emitter, emitter->document->nodes);
217
0
    yaml_free(emitter->anchors);
218
219
0
    emitter->anchors = NULL;
220
0
    emitter->last_anchor_id = 0;
221
0
    emitter->document = NULL;
222
0
}
223
224
/*
225
 * Check the references of a node and assign the anchor id if needed.
226
 */
227
228
static void
229
yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index)
230
0
{
231
0
    yaml_node_t *node = emitter->document->nodes.start + index - 1;
232
0
    yaml_node_item_t *item;
233
0
    yaml_node_pair_t *pair;
234
235
0
    emitter->anchors[index-1].references ++;
236
237
0
    if (emitter->anchors[index-1].references == 1) {
238
0
        switch (node->type) {
239
0
            case YAML_SEQUENCE_NODE:
240
0
                for (item = node->data.sequence.items.start;
241
0
                        item < node->data.sequence.items.top; item ++) {
242
0
                    yaml_emitter_anchor_node(emitter, *item);
243
0
                }
244
0
                break;
245
0
            case YAML_MAPPING_NODE:
246
0
                for (pair = node->data.mapping.pairs.start;
247
0
                        pair < node->data.mapping.pairs.top; pair ++) {
248
0
                    yaml_emitter_anchor_node(emitter, pair->key);
249
0
                    yaml_emitter_anchor_node(emitter, pair->value);
250
0
                }
251
0
                break;
252
0
            default:
253
0
                break;
254
0
        }
255
0
    }
256
257
0
    else if (emitter->anchors[index-1].references == 2) {
258
0
        emitter->anchors[index-1].anchor = (++ emitter->last_anchor_id);
259
0
    }
260
0
}
261
262
/*
263
 * Generate a textual representation for an anchor.
264
 */
265
266
0
#define ANCHOR_TEMPLATE         "id%03d"
267
0
#define ANCHOR_TEMPLATE_LENGTH  16
268
269
static yaml_char_t *
270
yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id)
271
0
{
272
0
    yaml_char_t *anchor = yaml_malloc(ANCHOR_TEMPLATE_LENGTH);
273
274
0
    if (!anchor) return NULL;
275
276
0
    snprintf((char *)anchor, ANCHOR_TEMPLATE_LENGTH, ANCHOR_TEMPLATE, anchor_id);
277
278
0
    return anchor;
279
0
}
280
281
/*
282
 * Serialize a node.
283
 */
284
285
static int
286
yaml_emitter_dump_node(yaml_emitter_t *emitter, int index)
287
0
{
288
0
    yaml_node_t *node = emitter->document->nodes.start + index - 1;
289
0
    int anchor_id = emitter->anchors[index-1].anchor;
290
0
    yaml_char_t *anchor = NULL;
291
292
0
    if (anchor_id) {
293
0
        anchor = yaml_emitter_generate_anchor(emitter, anchor_id);
294
0
        if (!anchor) return 0;
295
0
    }
296
297
0
    if (emitter->anchors[index-1].serialized) {
298
0
        return yaml_emitter_dump_alias(emitter, anchor);
299
0
    }
300
301
0
    emitter->anchors[index-1].serialized = 1;
302
303
0
    switch (node->type) {
304
0
        case YAML_SCALAR_NODE:
305
0
            return yaml_emitter_dump_scalar(emitter, node, anchor);
306
0
        case YAML_SEQUENCE_NODE:
307
0
            return yaml_emitter_dump_sequence(emitter, node, anchor);
308
0
        case YAML_MAPPING_NODE:
309
0
            return yaml_emitter_dump_mapping(emitter, node, anchor);
310
0
        default:
311
0
            assert(0);      /* Could not happen. */
312
0
            break;
313
0
    }
314
315
0
    return 0;       /* Could not happen. */
316
0
}
317
318
/*
319
 * Serialize an alias.
320
 */
321
322
static int
323
yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor)
324
0
{
325
0
    yaml_event_t event;
326
0
    yaml_mark_t mark  = { 0, 0, 0 };
327
328
0
    ALIAS_EVENT_INIT(event, anchor, mark, mark);
329
330
0
    return yaml_emitter_emit(emitter, &event);
331
0
}
332
333
/*
334
 * Serialize a scalar.
335
 */
336
337
static int
338
yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node,
339
        yaml_char_t *anchor)
340
0
{
341
0
    yaml_event_t event;
342
0
    yaml_mark_t mark  = { 0, 0, 0 };
343
344
0
    int plain_implicit = (strcmp((char *)node->tag,
345
0
                YAML_DEFAULT_SCALAR_TAG) == 0);
346
0
    int quoted_implicit = (strcmp((char *)node->tag,
347
0
                YAML_DEFAULT_SCALAR_TAG) == 0);
348
349
0
    SCALAR_EVENT_INIT(event, anchor, node->tag, node->data.scalar.value,
350
0
            node->data.scalar.length, plain_implicit, quoted_implicit,
351
0
            node->data.scalar.style, mark, mark);
352
353
0
    return yaml_emitter_emit(emitter, &event);
354
0
}
355
356
/*
357
 * Serialize a sequence.
358
 */
359
360
static int
361
yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node,
362
        yaml_char_t *anchor)
363
0
{
364
0
    yaml_event_t event;
365
0
    yaml_mark_t mark  = { 0, 0, 0 };
366
367
0
    int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_SEQUENCE_TAG) == 0);
368
369
0
    yaml_node_item_t *item;
370
371
0
    SEQUENCE_START_EVENT_INIT(event, anchor, node->tag, implicit,
372
0
            node->data.sequence.style, mark, mark);
373
0
    if (!yaml_emitter_emit(emitter, &event)) return 0;
374
375
0
    for (item = node->data.sequence.items.start;
376
0
            item < node->data.sequence.items.top; item ++) {
377
0
        if (!yaml_emitter_dump_node(emitter, *item)) return 0;
378
0
    }
379
380
0
    SEQUENCE_END_EVENT_INIT(event, mark, mark);
381
0
    if (!yaml_emitter_emit(emitter, &event)) return 0;
382
383
0
    return 1;
384
0
}
385
386
/*
387
 * Serialize a mapping.
388
 */
389
390
static int
391
yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node,
392
        yaml_char_t *anchor)
393
0
{
394
0
    yaml_event_t event;
395
0
    yaml_mark_t mark  = { 0, 0, 0 };
396
397
0
    int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_MAPPING_TAG) == 0);
398
399
0
    yaml_node_pair_t *pair;
400
401
0
    MAPPING_START_EVENT_INIT(event, anchor, node->tag, implicit,
402
0
            node->data.mapping.style, mark, mark);
403
0
    if (!yaml_emitter_emit(emitter, &event)) return 0;
404
405
0
    for (pair = node->data.mapping.pairs.start;
406
0
            pair < node->data.mapping.pairs.top; pair ++) {
407
0
        if (!yaml_emitter_dump_node(emitter, pair->key)) return 0;
408
0
        if (!yaml_emitter_dump_node(emitter, pair->value)) return 0;
409
0
    }
410
411
0
    MAPPING_END_EVENT_INIT(event, mark, mark);
412
0
    if (!yaml_emitter_emit(emitter, &event)) return 0;
413
414
0
    return 1;
415
0
}
416