xref: /freebsd/contrib/lyaml/ext/yaml/emitter.c (revision 2bc180ef045e5911cce0cea1c2a139cffd2b577a)
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