1 /*
2 * emitter.c, LibYAML emitter binding for Lua
3 * Written by Gary V. Vaughan, 2013
4 *
5 * Copyright (C) 2013-2022 Gary V. Vaughan
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25
26 #include <assert.h>
27
28 #include "lyaml.h"
29
30
31 typedef struct {
32 yaml_emitter_t emitter;
33
34 /* output accumulator */
35 lua_State *outputL;
36 luaL_Buffer yamlbuff;
37
38 /* error handling */
39 lua_State *errL;
40 luaL_Buffer errbuff;
41 int error;
42 } lyaml_emitter;
43
44
45 /* Emit a STREAM_START event. */
46 static int
emit_STREAM_START(lua_State * L,lyaml_emitter * emitter)47 emit_STREAM_START (lua_State *L, lyaml_emitter *emitter)
48 {
49 yaml_event_t event;
50 yaml_encoding_t yaml_encoding;
51 const char *encoding = NULL;
52
53 RAWGET_STRDUP (encoding); lua_pop (L, 1);
54
55 #define MENTRY(_s) (STREQ (encoding, #_s)) { yaml_encoding = YAML_##_s##_ENCODING; }
56 if (encoding == NULL) { yaml_encoding = YAML_ANY_ENCODING; } else
57 if MENTRY( UTF8 ) else
58 if MENTRY( UTF16LE ) else
59 if MENTRY( UTF16BE ) else
60 {
61 emitter->error++;
62 luaL_addsize (&emitter->errbuff,
63 sprintf (luaL_prepbuffer (&emitter->errbuff),
64 "invalid stream encoding '%s'", encoding));
65 }
66 #undef MENTRY
67
68 if (encoding) free ((void *) encoding);
69
70 if (emitter->error != 0)
71 return 0;
72
73 yaml_stream_start_event_initialize (&event, yaml_encoding);
74 return yaml_emitter_emit (&emitter->emitter, &event);
75 }
76
77
78 /* Emit a STREAM_END event. */
79 static int
emit_STREAM_END(lua_State * L,lyaml_emitter * emitter)80 emit_STREAM_END (lua_State *L, lyaml_emitter *emitter)
81 {
82 yaml_event_t event;
83 yaml_stream_end_event_initialize (&event);
84 return yaml_emitter_emit (&emitter->emitter, &event);
85 }
86
87
88 /* Emit a DOCUMENT_START event. */
89 static int
emit_DOCUMENT_START(lua_State * L,lyaml_emitter * emitter)90 emit_DOCUMENT_START (lua_State *L, lyaml_emitter *emitter)
91 {
92 yaml_event_t event;
93 yaml_version_directive_t version_directive, *Pversion_directive = NULL;
94 yaml_tag_directive_t *tag_directives_start = NULL, *tag_directives_end = NULL;
95 int implicit = 0;
96
97 RAWGET_PUSHTABLE ("version_directive");
98 if (lua_type (L, -1) == LUA_TTABLE)
99 {
100 RAWGETS_INTEGER (version_directive.major, "major");
101 ERROR_IFNIL ("version_directive missing key 'major'");
102 if (emitter->error == 0)
103 {
104 RAWGETS_INTEGER (version_directive.minor, "minor");
105 ERROR_IFNIL ("version_directive missing key 'minor'");
106 }
107 Pversion_directive = &version_directive;
108 }
109 lua_pop (L, 1); /* pop version_directive rawget */
110
111 RAWGET_PUSHTABLE ("tag_directives");
112 if (lua_type (L, -1) == LUA_TTABLE)
113 {
114 size_t bytes = lua_objlen (L, -1) * sizeof (yaml_tag_directive_t);
115
116 tag_directives_start = (yaml_tag_directive_t *) malloc (bytes);
117 tag_directives_end = tag_directives_start;
118
119 lua_pushnil (L); /* first key */
120 while (lua_next (L, -2) != 0)
121 {
122 RAWGETS_STRDUP (tag_directives_end->handle, "handle");
123 ERROR_IFNIL ("tag_directives item missing key 'handle'");
124 lua_pop (L, 1); /* pop handle */
125
126 RAWGETS_STRDUP (tag_directives_end->prefix, "prefix");
127 ERROR_IFNIL ("tag_directives item missing key 'prefix'");
128 lua_pop (L, 1); /* pop prefix */
129
130 tag_directives_end += 1;
131
132 /* pop tag_directives list elewent, leave key for next iteration */
133 lua_pop (L, 1);
134 }
135 }
136 lua_pop (L, 1); /* pop lua_rawget "tag_directives" result */
137
138 RAWGET_BOOLEAN (implicit); lua_pop (L, 1);
139
140 if (emitter->error != 0)
141 return 0;
142
143 yaml_document_start_event_initialize (&event, Pversion_directive,
144 tag_directives_start, tag_directives_end, implicit);
145 return yaml_emitter_emit (&emitter->emitter, &event);
146 }
147
148
149 /* Emit a DOCUMENT_END event. */
150 static int
emit_DOCUMENT_END(lua_State * L,lyaml_emitter * emitter)151 emit_DOCUMENT_END (lua_State *L, lyaml_emitter *emitter)
152 {
153 yaml_event_t event;
154 int implicit = 0;
155
156 RAWGET_BOOLEAN (implicit);
157
158 yaml_document_end_event_initialize (&event, implicit);
159 return yaml_emitter_emit (&emitter->emitter, &event);
160 }
161
162
163 /* Emit a MAPPING_START event. */
164 static int
emit_MAPPING_START(lua_State * L,lyaml_emitter * emitter)165 emit_MAPPING_START (lua_State *L, lyaml_emitter *emitter)
166 {
167 yaml_event_t event;
168 yaml_mapping_style_t yaml_style;
169 yaml_char_t *anchor = NULL, *tag = NULL;
170 int implicit = 1;
171 const char *style = NULL;
172
173 RAWGET_STRDUP (style); lua_pop (L, 1);
174
175 #define MENTRY(_s) (STREQ (style, #_s)) { yaml_style = YAML_##_s##_MAPPING_STYLE; }
176 if (style == NULL) { yaml_style = YAML_ANY_MAPPING_STYLE; } else
177 if MENTRY( BLOCK ) else
178 if MENTRY( FLOW ) else
179 {
180 emitter->error++;
181 luaL_addsize (&emitter->errbuff,
182 sprintf (luaL_prepbuffer (&emitter->errbuff),
183 "invalid mapping style '%s'", style));
184 }
185 #undef MENTRY
186
187 if (style) free ((void *) style);
188
189 RAWGET_YAML_CHARP (anchor); lua_pop (L, 1);
190 RAWGET_YAML_CHARP (tag); lua_pop (L, 1);
191 RAWGET_BOOLEAN (implicit); lua_pop (L, 1);
192
193 yaml_mapping_start_event_initialize (&event, anchor, tag, implicit, yaml_style);
194 return yaml_emitter_emit (&emitter->emitter, &event);
195 }
196
197
198 /* Emit a MAPPING_END event. */
199 static int
emit_MAPPING_END(lua_State * L,lyaml_emitter * emitter)200 emit_MAPPING_END (lua_State *L, lyaml_emitter *emitter)
201 {
202 yaml_event_t event;
203 yaml_mapping_end_event_initialize (&event);
204 return yaml_emitter_emit (&emitter->emitter, &event);
205 }
206
207
208 /* Emit a SEQUENCE_START event. */
209 static int
emit_SEQUENCE_START(lua_State * L,lyaml_emitter * emitter)210 emit_SEQUENCE_START (lua_State *L, lyaml_emitter *emitter)
211 {
212 yaml_event_t event;
213 yaml_sequence_style_t yaml_style;
214 yaml_char_t *anchor = NULL, *tag = NULL;
215 int implicit = 1;
216 const char *style = NULL;
217
218 RAWGET_STRDUP (style); lua_pop (L, 1);
219
220 #define MENTRY(_s) (STREQ (style, #_s)) { yaml_style = YAML_##_s##_SEQUENCE_STYLE; }
221 if (style == NULL) { yaml_style = YAML_ANY_SEQUENCE_STYLE; } else
222 if MENTRY( BLOCK ) else
223 if MENTRY( FLOW ) else
224 {
225 emitter->error++;
226 luaL_addsize (&emitter->errbuff,
227 sprintf (luaL_prepbuffer (&emitter->errbuff),
228 "invalid sequence style '%s'", style));
229 }
230 #undef MENTRY
231
232 if (style) free ((void *) style);
233
234 RAWGET_YAML_CHARP (anchor); lua_pop (L, 1);
235 RAWGET_YAML_CHARP (tag); lua_pop (L, 1);
236 RAWGET_BOOLEAN (implicit); lua_pop (L, 1);
237
238 yaml_sequence_start_event_initialize (&event, anchor, tag, implicit, yaml_style);
239 return yaml_emitter_emit (&emitter->emitter, &event);
240 }
241
242
243 /* Emit a SEQUENCE_END event. */
244 static int
emit_SEQUENCE_END(lua_State * L,lyaml_emitter * emitter)245 emit_SEQUENCE_END (lua_State *L, lyaml_emitter *emitter)
246 {
247 yaml_event_t event;
248 yaml_sequence_end_event_initialize (&event);
249 return yaml_emitter_emit (&emitter->emitter, &event);
250 }
251
252
253 /* Emit a SCALAR event. */
254 static int
emit_SCALAR(lua_State * L,lyaml_emitter * emitter)255 emit_SCALAR (lua_State *L, lyaml_emitter *emitter)
256 {
257 yaml_event_t event;
258 yaml_scalar_style_t yaml_style;
259 yaml_char_t *anchor = NULL, *tag = NULL, *value;
260 int length = 0, plain_implicit = 1, quoted_implicit = 1;
261 const char *style = NULL;
262
263 RAWGET_STRDUP (style); lua_pop (L, 1);
264
265 #define MENTRY(_s) (STREQ (style, #_s)) { yaml_style = YAML_##_s##_SCALAR_STYLE; }
266 if (style == NULL) { yaml_style = YAML_ANY_SCALAR_STYLE; } else
267 if MENTRY( PLAIN ) else
268 if MENTRY( SINGLE_QUOTED ) else
269 if MENTRY( DOUBLE_QUOTED ) else
270 if MENTRY( LITERAL ) else
271 if MENTRY( FOLDED ) else
272 {
273 emitter->error++;
274 luaL_addsize (&emitter->errbuff,
275 sprintf (luaL_prepbuffer (&emitter->errbuff),
276 "invalid scalar style '%s'", style));
277 }
278 #undef MENTRY
279
280 if (style) free ((void *) style);
281
282 RAWGET_YAML_CHARP (anchor); lua_pop (L, 1);
283 RAWGET_YAML_CHARP (tag); lua_pop (L, 1);
284 RAWGET_YAML_CHARP (value); length = lua_objlen (L, -1); lua_pop (L, 1);
285 RAWGET_BOOLEAN (plain_implicit);
286 RAWGET_BOOLEAN (quoted_implicit);
287
288 yaml_scalar_event_initialize (&event, anchor, tag, value, length,
289 plain_implicit, quoted_implicit, yaml_style);
290 return yaml_emitter_emit (&emitter->emitter, &event);
291 }
292
293
294 /* Emit an ALIAS event. */
295 static int
emit_ALIAS(lua_State * L,lyaml_emitter * emitter)296 emit_ALIAS (lua_State *L, lyaml_emitter *emitter)
297 {
298 yaml_event_t event;
299 yaml_char_t *anchor;
300
301 RAWGET_YAML_CHARP (anchor);
302
303 yaml_alias_event_initialize (&event, anchor);
304 return yaml_emitter_emit (&emitter->emitter, &event);
305 }
306
307
308 static int
emit(lua_State * L)309 emit (lua_State *L)
310 {
311 lyaml_emitter *emitter;
312 int yaml_ok = 0;
313 int finalize = 0;
314
315 luaL_argcheck (L, lua_istable (L, 1), 1, "expected table");
316
317 emitter = (lyaml_emitter *) lua_touserdata (L, lua_upvalueindex (1));
318
319 {
320 const char *type;
321
322 RAWGET_STRDUP (type); lua_pop (L, 1);
323
324 if (type == NULL)
325 {
326 emitter->error++;
327 luaL_addstring (&emitter->errbuff, "no type field in event table");
328 }
329 #define MENTRY(_s) (STREQ (type, #_s)) { yaml_ok = emit_##_s (L, emitter); }
330 /* Minimize comparisons by putting more common types earlier. */
331 else if MENTRY( SCALAR )
332 else if MENTRY( MAPPING_START )
333 else if MENTRY( MAPPING_END )
334 else if MENTRY( SEQUENCE_START )
335 else if MENTRY( SEQUENCE_END )
336 else if MENTRY( DOCUMENT_START )
337 else if MENTRY( DOCUMENT_END )
338 else if MENTRY( STREAM_START )
339 else if MENTRY( STREAM_END )
340 else if MENTRY( ALIAS )
341 #undef MENTRY
342 else
343 {
344 emitter->error++;
345 luaL_addsize (&emitter->errbuff,
346 sprintf (luaL_prepbuffer (&emitter->errbuff),
347 "invalid event type '%s'", type));
348 }
349
350 /* If the stream has finished, finalize the YAML output. */
351 if (type && STREQ (type, "STREAM_END"))
352 finalize = 1;
353
354 if (type) free ((void *) type);
355 }
356
357 /* Copy any yaml_emitter_t errors into the error buffer. */
358 if (!emitter->error && !yaml_ok)
359 {
360 if (emitter->emitter.problem)
361 luaL_addstring (&emitter->errbuff, emitter->emitter.problem);
362 else
363 luaL_addstring (&emitter->errbuff, "LibYAML call failed");
364 emitter->error++;
365 }
366
367 /* Report errors back to the caller as `false, "error message"`. */
368 if (emitter->error != 0)
369 {
370 assert (emitter->error == 1); /* bail on uncaught additional errors */
371 lua_pushboolean (L, 0);
372 luaL_pushresult (&emitter->errbuff);
373 lua_xmove (emitter->errL, L, 1);
374 return 2;
375 }
376
377 /* Return `true, "YAML string"` after accepting a STREAM_END event. */
378 if (finalize)
379 {
380 lua_pushboolean (L, 1);
381 luaL_pushresult (&emitter->yamlbuff);
382 lua_xmove (emitter->outputL, L, 1);
383 return 2;
384 }
385
386 /* Otherwise, just report success to the caller as `true`. */
387 lua_pushboolean (L, 1);
388 return 1;
389 }
390
391
392 static int
append_output(void * arg,unsigned char * buff,size_t len)393 append_output (void *arg, unsigned char *buff, size_t len)
394 {
395 lyaml_emitter *emitter = (lyaml_emitter *) arg;
396 luaL_addlstring (&emitter->yamlbuff, (char *) buff, len);
397 return 1;
398 }
399
400
401 static int
emitter_gc(lua_State * L)402 emitter_gc (lua_State *L)
403 {
404 lyaml_emitter *emitter = (lyaml_emitter *) lua_touserdata (L, 1);
405
406 if (emitter)
407 yaml_emitter_delete (&emitter->emitter);
408
409 return 0;
410 }
411
412
413 int
Pemitter(lua_State * L)414 Pemitter (lua_State *L)
415 {
416 lyaml_emitter *emitter;
417
418 lua_newtable (L); /* object table */
419
420 /* Create a user datum to store the emitter. */
421 emitter = (lyaml_emitter *) lua_newuserdata (L, sizeof (*emitter));
422 emitter->error = 0;
423
424 /* Initialize the emitter. */
425 if (!yaml_emitter_initialize (&emitter->emitter))
426 {
427 if (!emitter->emitter.problem)
428 emitter->emitter.problem = "cannot initialize emitter";
429 return luaL_error (L, "%s", emitter->emitter.problem);
430 }
431 yaml_emitter_set_unicode (&emitter->emitter, 1);
432 yaml_emitter_set_width (&emitter->emitter, 2);
433 yaml_emitter_set_output (&emitter->emitter, &append_output, emitter);
434
435 /* Set it's metatable, and ensure it is garbage collected properly. */
436 luaL_newmetatable (L, "lyaml.emitter");
437 lua_pushcfunction (L, emitter_gc);
438 lua_setfield (L, -2, "__gc");
439 lua_setmetatable (L, -2);
440
441 /* Set the emit method of object as a closure over the user datum, and
442 return the whole object. */
443 lua_pushcclosure (L, emit, 1);
444 lua_setfield (L, -2, "emit");
445
446 /* Set up a separate thread to collect error messages; save the thread
447 in the returned table so that it's not garbage collected when the
448 function call stack for Pemitter is cleaned up. */
449 emitter->errL = lua_newthread (L);
450 luaL_buffinit (emitter->errL, &emitter->errbuff);
451 lua_setfield (L, -2, "errthread");
452
453 /* Create a thread for the YAML buffer. */
454 emitter->outputL = lua_newthread (L);
455 luaL_buffinit (emitter->outputL, &emitter->yamlbuff);
456 lua_setfield (L, -2, "outputthread");
457
458 return 1;
459 }
460