source: trunk/packages/libyaml/src/dumper.c @ 1036

Last change on this file since 1036 was 898, checked in by hartmans, 16 years ago

Add pyyaml and libyaml packages
backported from lenny.
There is discussion about how these should go in the repository; these are added in this form
in order to make forward progress.

File size: 9.8 KB
Line 
1
2#include "yaml_private.h"
3
4/*
5 * API functions.
6 */
7
8YAML_DECLARE(int)
9yaml_emitter_open(yaml_emitter_t *emitter);
10
11YAML_DECLARE(int)
12yaml_emitter_close(yaml_emitter_t *emitter);
13
14YAML_DECLARE(int)
15yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document);
16
17/*
18 * Clean up functions.
19 */
20
21static void
22yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter);
23
24/*
25 * Anchor functions.
26 */
27
28static void
29yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index);
30
31static yaml_char_t *
32yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id);
33
34
35/*
36 * Serialize functions.
37 */
38
39static int
40yaml_emitter_dump_node(yaml_emitter_t *emitter, int index);
41
42static int
43yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor);
44
45static int
46yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node,
47        yaml_char_t *anchor);
48
49static int
50yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node,
51        yaml_char_t *anchor);
52
53static int
54yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node,
55        yaml_char_t *anchor);
56
57/*
58 * Issue a STREAM-START event.
59 */
60
61YAML_DECLARE(int)
62yaml_emitter_open(yaml_emitter_t *emitter)
63{
64    yaml_event_t event;
65    yaml_mark_t mark = { 0, 0, 0 };
66
67    assert(emitter);            /* Non-NULL emitter object is required. */
68    assert(!emitter->opened);   /* Emitter should not be opened yet. */
69
70    STREAM_START_EVENT_INIT(event, YAML_ANY_ENCODING, mark, mark);
71
72    if (!yaml_emitter_emit(emitter, &event)) {
73        return 0;
74    }
75
76    emitter->opened = 1;
77
78    return 1;
79}
80
81/*
82 * Issue a STREAM-END event.
83 */
84
85YAML_DECLARE(int)
86yaml_emitter_close(yaml_emitter_t *emitter)
87{
88    yaml_event_t event;
89    yaml_mark_t mark = { 0, 0, 0 };
90
91    assert(emitter);            /* Non-NULL emitter object is required. */
92    assert(emitter->opened);    /* Emitter should be opened. */
93
94    if (emitter->closed) return 1;
95
96    STREAM_END_EVENT_INIT(event, mark, mark);
97
98    if (!yaml_emitter_emit(emitter, &event)) {
99        return 0;
100    }
101
102    emitter->closed = 1;
103
104    return 1;
105}
106
107/*
108 * Dump a YAML document.
109 */
110
111YAML_DECLARE(int)
112yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document)
113{
114    yaml_event_t event;
115    yaml_mark_t mark = { 0, 0, 0 };
116
117    assert(emitter);            /* Non-NULL emitter object is required. */
118    assert(document);           /* Non-NULL emitter object is expected. */
119
120    emitter->document = document;
121
122    if (!emitter->opened) {
123        if (!yaml_emitter_open(emitter)) goto error;
124    }
125
126    if (STACK_EMPTY(emitter, document->nodes)) {
127        if (!yaml_emitter_close(emitter)) goto error;
128        yaml_emitter_delete_document_and_anchors(emitter);
129        return 1;
130    }
131
132    assert(emitter->opened);    /* Emitter should be opened. */
133
134    emitter->anchors = yaml_malloc(sizeof(*(emitter->anchors))
135            * (document->nodes.top - document->nodes.start));
136    if (!emitter->anchors) goto error;
137    memset(emitter->anchors, 0, sizeof(*(emitter->anchors))
138            * (document->nodes.top - document->nodes.start));
139
140    DOCUMENT_START_EVENT_INIT(event, document->version_directive,
141            document->tag_directives.start, document->tag_directives.end,
142            document->start_implicit, mark, mark);
143    if (!yaml_emitter_emit(emitter, &event)) goto error;
144
145    yaml_emitter_anchor_node(emitter, 1);
146    if (!yaml_emitter_dump_node(emitter, 1)) goto error;
147
148    DOCUMENT_END_EVENT_INIT(event, document->end_implicit, mark, mark);
149    if (!yaml_emitter_emit(emitter, &event)) goto error;
150
151    yaml_emitter_delete_document_and_anchors(emitter);
152
153    return 1;
154
155error:
156
157    yaml_emitter_delete_document_and_anchors(emitter);
158
159    return 0;
160}
161
162/*
163 * Clean up the emitter object after a document is dumped.
164 */
165
166static void
167yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter)
168{
169    int index;
170
171    if (!emitter->anchors) {
172        yaml_document_delete(emitter->document);
173        emitter->document = NULL;
174        return;
175    }
176
177    for (index = 0; emitter->document->nodes.start + index
178            < emitter->document->nodes.top; index ++) {
179        yaml_node_t node = emitter->document->nodes.start[index];
180        if (!emitter->anchors[index].serialized) {
181            yaml_free(node.tag);
182            if (node.type == YAML_SCALAR_NODE) {
183                yaml_free(node.data.scalar.value);
184            }
185        }
186        if (node.type == YAML_SEQUENCE_NODE) {
187            STACK_DEL(emitter, node.data.sequence.items);
188        }
189        if (node.type == YAML_MAPPING_NODE) {
190            STACK_DEL(emitter, node.data.mapping.pairs);
191        }
192    }
193
194    STACK_DEL(emitter, emitter->document->nodes);
195    yaml_free(emitter->anchors);
196
197    emitter->anchors = NULL;
198    emitter->last_anchor_id = 0;
199    emitter->document = NULL;
200}
201
202/*
203 * Check the references of a node and assign the anchor id if needed.
204 */
205
206static void
207yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index)
208{
209    yaml_node_t *node = emitter->document->nodes.start + index - 1;
210    yaml_node_item_t *item;
211    yaml_node_pair_t *pair;
212
213    emitter->anchors[index-1].references ++;
214
215    if (emitter->anchors[index-1].references == 1) {
216        switch (node->type) {
217            case YAML_SEQUENCE_NODE:
218                for (item = node->data.sequence.items.start;
219                        item < node->data.sequence.items.top; item ++) {
220                    yaml_emitter_anchor_node(emitter, *item);
221                }
222                break;
223            case YAML_MAPPING_NODE:
224                for (pair = node->data.mapping.pairs.start;
225                        pair < node->data.mapping.pairs.top; pair ++) {
226                    yaml_emitter_anchor_node(emitter, pair->key);
227                    yaml_emitter_anchor_node(emitter, pair->value);
228                }
229                break;
230            default:
231                break;
232        }
233    }
234
235    else if (emitter->anchors[index-1].references == 2) {
236        emitter->anchors[index-1].anchor = (++ emitter->last_anchor_id);
237    }
238}
239
240/*
241 * Generate a textual representation for an anchor.
242 */
243
244#define ANCHOR_TEMPLATE         "id%03d"
245#define ANCHOR_TEMPLATE_LENGTH  16
246
247static yaml_char_t *
248yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id)
249{
250    yaml_char_t *anchor = yaml_malloc(ANCHOR_TEMPLATE_LENGTH);
251
252    if (!anchor) return NULL;
253
254    sprintf((char *)anchor, ANCHOR_TEMPLATE, anchor_id);
255
256    return anchor;
257}
258
259/*
260 * Serialize a node.
261 */
262
263static int
264yaml_emitter_dump_node(yaml_emitter_t *emitter, int index)
265{
266    yaml_node_t *node = emitter->document->nodes.start + index - 1;
267    int anchor_id = emitter->anchors[index-1].anchor;
268    yaml_char_t *anchor = NULL;
269
270    if (anchor_id) {
271        anchor = yaml_emitter_generate_anchor(emitter, anchor_id);
272        if (!anchor) return 0;
273    }
274
275    if (emitter->anchors[index-1].serialized) {
276        return yaml_emitter_dump_alias(emitter, anchor);
277    }
278
279    emitter->anchors[index-1].serialized = 1;
280
281    switch (node->type) {
282        case YAML_SCALAR_NODE:
283            return yaml_emitter_dump_scalar(emitter, node, anchor);
284        case YAML_SEQUENCE_NODE:
285            return yaml_emitter_dump_sequence(emitter, node, anchor);
286        case YAML_MAPPING_NODE:
287            return yaml_emitter_dump_mapping(emitter, node, anchor);
288        default:
289            assert(0);      /* Could not happen. */
290            break;
291    }
292
293    return 0;       /* Could not happen. */
294}
295
296/*
297 * Serialize an alias.
298 */
299
300static int
301yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor)
302{
303    yaml_event_t event;
304    yaml_mark_t mark  = { 0, 0, 0 };
305
306    ALIAS_EVENT_INIT(event, anchor, mark, mark);
307
308    return yaml_emitter_emit(emitter, &event);
309}
310
311/*
312 * Serialize a scalar.
313 */
314
315static int
316yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node,
317        yaml_char_t *anchor)
318{
319    yaml_event_t event;
320    yaml_mark_t mark  = { 0, 0, 0 };
321
322    int plain_implicit = (strcmp((char *)node->tag,
323                YAML_DEFAULT_SCALAR_TAG) == 0);
324    int quoted_implicit = (strcmp((char *)node->tag,
325                YAML_DEFAULT_SCALAR_TAG) == 0);
326
327    SCALAR_EVENT_INIT(event, anchor, node->tag, node->data.scalar.value,
328            node->data.scalar.length, plain_implicit, quoted_implicit,
329            node->data.scalar.style, mark, mark);
330
331    return yaml_emitter_emit(emitter, &event);
332}
333
334/*
335 * Serialize a sequence.
336 */
337
338static int
339yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node,
340        yaml_char_t *anchor)
341{
342    yaml_event_t event;
343    yaml_mark_t mark  = { 0, 0, 0 };
344
345    int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_SEQUENCE_TAG) == 0);
346
347    yaml_node_item_t *item;
348
349    SEQUENCE_START_EVENT_INIT(event, anchor, node->tag, implicit,
350            node->data.sequence.style, mark, mark);
351    if (!yaml_emitter_emit(emitter, &event)) return 0;
352
353    for (item = node->data.sequence.items.start;
354            item < node->data.sequence.items.top; item ++) {
355        if (!yaml_emitter_dump_node(emitter, *item)) return 0;
356    }
357
358    SEQUENCE_END_EVENT_INIT(event, mark, mark);
359    if (!yaml_emitter_emit(emitter, &event)) return 0;
360
361    return 1;
362}
363
364/*
365 * Serialize a mapping.
366 */
367
368static int
369yaml_emitter_dump_mapping(yaml_emitter_t *emitter, yaml_node_t *node,
370        yaml_char_t *anchor)
371{
372    yaml_event_t event;
373    yaml_mark_t mark  = { 0, 0, 0 };
374
375    int implicit = (strcmp((char *)node->tag, YAML_DEFAULT_MAPPING_TAG) == 0);
376
377    yaml_node_pair_t *pair;
378
379    MAPPING_START_EVENT_INIT(event, anchor, node->tag, implicit,
380            node->data.mapping.style, mark, mark);
381    if (!yaml_emitter_emit(emitter, &event)) return 0;
382
383    for (pair = node->data.mapping.pairs.start;
384            pair < node->data.mapping.pairs.top; pair ++) {
385        if (!yaml_emitter_dump_node(emitter, pair->key)) return 0;
386        if (!yaml_emitter_dump_node(emitter, pair->value)) return 0;
387    }
388
389    MAPPING_END_EVENT_INIT(event, mark, mark);
390    if (!yaml_emitter_emit(emitter, &event)) return 0;
391
392    return 1;
393}
394
Note: See TracBrowser for help on using the repository browser.