1
|
1 |
|
2 #include "yaml_private.h" |
|
3 |
|
4 /* |
|
5 * API functions. |
|
6 */ |
|
7 |
|
8 YAML_DECLARE(int) |
|
9 yaml_emitter_open(yaml_emitter_t *emitter); |
|
10 |
|
11 YAML_DECLARE(int) |
|
12 yaml_emitter_close(yaml_emitter_t *emitter); |
|
13 |
|
14 YAML_DECLARE(int) |
|
15 yaml_emitter_dump(yaml_emitter_t *emitter, yaml_document_t *document); |
|
16 |
|
17 /* |
|
18 * Clean up functions. |
|
19 */ |
|
20 |
|
21 static void |
|
22 yaml_emitter_delete_document_and_anchors(yaml_emitter_t *emitter); |
|
23 |
|
24 /* |
|
25 * Anchor functions. |
|
26 */ |
|
27 |
|
28 static void |
|
29 yaml_emitter_anchor_node(yaml_emitter_t *emitter, int index); |
|
30 |
|
31 static yaml_char_t * |
|
32 yaml_emitter_generate_anchor(yaml_emitter_t *emitter, int anchor_id); |
|
33 |
|
34 |
|
35 /* |
|
36 * Serialize functions. |
|
37 */ |
|
38 |
|
39 static int |
|
40 yaml_emitter_dump_node(yaml_emitter_t *emitter, int index); |
|
41 |
|
42 static int |
|
43 yaml_emitter_dump_alias(yaml_emitter_t *emitter, yaml_char_t *anchor); |
|
44 |
|
45 static int |
|
46 yaml_emitter_dump_scalar(yaml_emitter_t *emitter, yaml_node_t *node, |
|
47 yaml_char_t *anchor); |
|
48 |
|
49 static int |
|
50 yaml_emitter_dump_sequence(yaml_emitter_t *emitter, yaml_node_t *node, |
|
51 yaml_char_t *anchor); |
|
52 |
|
53 static int |
|
54 yaml_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 |
|
61 YAML_DECLARE(int) |
|
62 yaml_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 |
|
85 YAML_DECLARE(int) |
|
86 yaml_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 |
|
111 YAML_DECLARE(int) |
|
112 yaml_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 |
|
155 error: |
|
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 |
|
166 static void |
|
167 yaml_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 |
|
206 static void |
|
207 yaml_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 |
|
247 static yaml_char_t * |
|
248 yaml_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 |
|
263 static int |
|
264 yaml_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 |
|
300 static int |
|
301 yaml_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 |
|
315 static int |
|
316 yaml_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 |
|
338 static int |
|
339 yaml_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 |
|
368 static int |
|
369 yaml_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 |