lyaml.c

1
/*
2
 * lyaml.c, LibYAML binding for Lua
3
 * 
4
 * Copyright (c) 2009, Andrew Danforth <acd@weirdness.net>
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 * 
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 * 
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
 * THE SOFTWARE.
23
 * 
24
 * Portions of this software were inspired by Perl's YAML::LibYAML module by 
25
 * Ingy d�t Net <ingy@cpan.org>
26
 * 
27
 */
28
 
29
#include <string.h>
30
#include <stdlib.h>
31
 
32
#include <lauxlib.h>
33
#include <lua.h>
34
#include <lualib.h>
35
 
36
#include "yaml.h"
37
#include "b64.h"
38
 
39
/* configurable flags */
40
static char Dump_Auto_Array = 1;
41
static char Dump_Error_on_Unsupported = 0;
42
static char Dump_Check_Metatables = 1;
43
static char Load_Set_Metatables = 1;
44
static char Load_Numeric_Scalars = 1;
45
static char Load_Nulls_As_Nil = 0;
46
 
47
#define LUAYAML_TAG_PREFIX "tag:yaml.org,2002:"
48
 
49
#define LUAYAML_KIND_UNKNOWN 0
50
#define LUAYAML_KIND_SEQUENCE 1
51
#define LUAYAML_KIND_MAPPING 2
52
 
53
#define RETURN_ERRMSG(s, msg) do { \
54
      lua_pushstring(s->L, msg); \
55
      s->error = 1; \
56
      return; \
57
   } while(0)
58
 
59
struct lua_yaml_loader {
60
   lua_State *L;
61
   int anchortable_index;
62
   int sequencemt_index;
63
   int mapmt_index;
64
   int document_count;
65
   yaml_parser_t parser;
66
   yaml_event_t event;
67
   char validevent;
68
   char error;
69
};
70
 
71
struct lua_yaml_dumper {
72
   lua_State *L;
73
   int anchortable_index;
74
   unsigned int anchor_number;
75
   yaml_emitter_t emitter;
76
   char error;
77
 
78
   lua_State *outputL;
79
   luaL_Buffer yamlbuf;
80
};
81
 
82
static int l_null(lua_State *);
83
 
84
static void generate_error_message(struct lua_yaml_loader *loader) {
85
   char buf[256];
86
   luaL_Buffer b;
87
 
88
   luaL_buffinit(loader->L, &b);
89
 
90
   luaL_addstring(&b, loader->parser.problem ? loader->parser.problem : "A problem");
91
   snprintf(buf, sizeof(buf), " at document: %d", loader->document_count);
92
   luaL_addstring(&b, buf);
93
 
94
   if (loader->parser.problem_mark.line || loader->parser.problem_mark.column) {
95
      snprintf(buf, sizeof(buf), ", line: %d, column: %d\n",
96
         loader->parser.problem_mark.line + 1,
97
         loader->parser.problem_mark.column + 1);
98
      luaL_addstring(&b, buf);
99
   } else {
100
      luaL_addstring(&b, "\n");
101
   }
102
 
103
   if (loader->parser.context) {
104
      snprintf(buf, sizeof(buf), "%s at line: %d, column: %d\n",
105
         loader->parser.context,
106
         loader->parser.context_mark.line + 1,
107
         loader->parser.context_mark.column + 1);
108
      luaL_addstring(&b, buf);
109
   }
110
 
111
   luaL_pushresult(&b);
112
}
113
 
114
static inline void delete_event(struct lua_yaml_loader *loader) {
115
   if (loader->validevent) {
116
      yaml_event_delete(&loader->event);
117
      loader->validevent = 0;
118
   }
119
}
120
 
121
static inline int do_parse(struct lua_yaml_loader *loader) {
122
   delete_event(loader);
123
   if (yaml_parser_parse(&loader->parser, &loader->event) != 1) {
124
      generate_error_message(loader);
125
      loader->error = 1;
126
      return 0;
127
   }
128
 
129
   loader->validevent = 1;
130
   return 1;
131
}
132
 
133
static int load_node(struct lua_yaml_loader *loader);
134
 
135
static void handle_anchor(struct lua_yaml_loader *loader) {
136
   const char *anchor = (char *)loader->event.data.scalar.anchor;
137
   if (!anchor)
138
      return;
139
 
140
   lua_pushstring(loader->L, anchor);
141
   lua_pushvalue(loader->L, -2);
142
   lua_rawset(loader->L, loader->anchortable_index);
143
}
144
 
145
static void load_map(struct lua_yaml_loader *loader) {
146
   lua_newtable(loader->L);
147
   if (loader->mapmt_index != 0) {
148
      lua_pushvalue(loader->L, loader->mapmt_index);
149
      lua_setmetatable(loader->L, -2);
150
   }
151
 
152
   handle_anchor(loader);
153
   while (1) {
154
      int r;
155
      /* load key */
156
      if (load_node(loader) == 0 || loader->error)
157
         return;
158
 
159
      /* load value */
160
      r = load_node(loader);
161
      if (loader->error)
162
         return;
163
      if (r != 1)
164
         RETURN_ERRMSG(loader, "unanticipated END event");
165
      lua_rawset(loader->L, -3);
166
   }
167
}
168
 
169
static void load_sequence(struct lua_yaml_loader *loader) {
170
   int index = 1;
171
 
172
   lua_newtable(loader->L);
173
   if (loader->sequencemt_index != 0) {
174
      lua_pushvalue(loader->L, loader->sequencemt_index);
175
      lua_setmetatable(loader->L, -2);
176
   }
177
 
178
   handle_anchor(loader);
179
   while (load_node(loader) == 1 && !loader->error)
180
      lua_rawseti(loader->L, -2, index++);
181
}
182
 
183
static void load_scalar(struct lua_yaml_loader *loader) {
184
   const char *str = (char *)loader->event.data.scalar.value;
185
   unsigned int length = loader->event.data.scalar.length;
186
   const char *tag = (char *)loader->event.data.scalar.tag;
187
 
188
   if (tag && !strncmp(tag, LUAYAML_TAG_PREFIX, sizeof(LUAYAML_TAG_PREFIX) - 1)) {
189
      tag += sizeof(LUAYAML_TAG_PREFIX) - 1;
190
 
191
      if (!strcmp(tag, "str")) {
192
         lua_pushlstring(loader->L, str, length);
193
         return;
194
      } else if (!strcmp(tag, "int")) {
195
         lua_pushinteger(loader->L, strtol(str, NULL, 10));
196
         return;
197
      } else if (!strcmp(tag, "float")) {
198
         lua_pushnumber(loader->L, strtod(str, NULL));
199
         return;
200
      } else if (!strcmp(tag, "bool")) {
201
         lua_pushboolean(loader->L, !strcmp(str, "true") || !strcmp(str, "yes"));
202
         return;
203
      } else if (!strcmp(tag, "binary")) {
204
         frombase64(loader->L, (const unsigned char *)str, length);
205
         return;
206
      }
207
   }
208
 
209
   if (loader->event.data.scalar.style == YAML_PLAIN_SCALAR_STYLE) {
210
      if (!strcmp(str, "~")) {
211
         if (Load_Nulls_As_Nil)
212
            lua_pushnil(loader->L);
213
         else
214
            l_null(loader->L);
215
         return;
216
      } else if (!strcmp(str, "true") || !strcmp(str, "yes")) {
217
         lua_pushboolean(loader->L, 1);
218
         return;
219
      } else if (!strcmp(str, "false") || !strcmp(str, "no")) {
220
         lua_pushboolean(loader->L, 0);
221
         return;
222
      }
223
   }
224
 
225
   lua_pushlstring(loader->L, str, length);
226
 
227
   /* plain scalar and Lua can convert it to a number?  make it so... */
228
   if (Load_Numeric_Scalars
229
      && loader->event.data.scalar.style == YAML_PLAIN_SCALAR_STYLE
230
      && lua_isnumber(loader->L, -1)) {
231
      lua_Number n = lua_tonumber(loader->L, -1);
232
      lua_pop(loader->L, 1);
233
      lua_pushnumber(loader->L, n);
234
   }
235
 
236
   handle_anchor(loader);
237
}
238
 
239
static void load_alias(struct lua_yaml_loader *loader) {
240
   char *anchor = (char *)loader->event.data.alias.anchor;
241
   lua_pushstring(loader->L, anchor);
242
   lua_rawget(loader->L, loader->anchortable_index);
243
   if (lua_isnil(loader->L, -1)) {
244
      char buf[256];
245
      snprintf(buf, sizeof(buf), "invalid reference: %s", anchor);
246
      RETURN_ERRMSG(loader, buf);
247
   }
248
}
249
 
250
static int load_node(struct lua_yaml_loader *loader) {
251
   if (!do_parse(loader))
252
      return -1;
253
 
254
   switch (loader->event.type) {
255
      case YAML_DOCUMENT_END_EVENT:
256
      case YAML_MAPPING_END_EVENT:
257
      case YAML_SEQUENCE_END_EVENT:
258
         return 0;
259
 
260
      case YAML_MAPPING_START_EVENT:
261
         load_map(loader);
262
         return 1;
263
 
264
      case YAML_SEQUENCE_START_EVENT:
265
         load_sequence(loader);
266
         return 1;
267
 
268
      case YAML_SCALAR_EVENT:
269
         load_scalar(loader);
270
         return 1;
271
 
272
      case YAML_ALIAS_EVENT:
273
         load_alias(loader);
274
         return 1;
275
 
276
      case YAML_NO_EVENT:
277
         lua_pushliteral(loader->L, "libyaml returned YAML_NO_EVENT");
278
         loader->error = 1;
279
         return -1;
280
 
281
      default:
282
         lua_pushliteral(loader->L, "invalid event");
283
         loader->error = 1;
284
         return -1;
285
   }
286
}
287
 
288
static void load(struct lua_yaml_loader *loader) {
289
   if (!do_parse(loader))
290
      return;
291
 
292
   if (loader->event.type != YAML_STREAM_START_EVENT)
293
      RETURN_ERRMSG(loader, "expected STREAM_START_EVENT");
294
 
295
   while (1) {
296
      if (!do_parse(loader))
297
         return;
298
 
299
      if (loader->event.type == YAML_STREAM_END_EVENT)
300
         return;
301
 
302
      loader->document_count++;
303
      if (load_node(loader) != 1)
304
         RETURN_ERRMSG(loader, "unexpected END event");
305
      if (loader->error)
306
         return;
307
 
308
      if (!do_parse(loader))
309
         return;
310
      if (loader->event.type != YAML_DOCUMENT_END_EVENT)
311
         RETURN_ERRMSG(loader, "expected DOCUMENT_END_EVENT");
312
 
313
      /* reset anchor table */ 
314
      lua_newtable(loader->L);
315
      lua_replace(loader->L, loader->anchortable_index);
316
   }
317
}
318
 
319
static int l_load(lua_State *L) {
320
   struct lua_yaml_loader loader;
321
   int top = lua_gettop(L);
322
 
323
   luaL_argcheck(L, lua_isstring(L, 1), 1, "must provide a string argument");
324
 
325
   loader.L = L;
326
   loader.validevent = 0;
327
   loader.error = 0;
328
   loader.document_count = 0;
329
   loader.mapmt_index = loader.sequencemt_index = 0;
330
 
331
   if (Load_Set_Metatables) {
332
      /* create sequence metatable */
333
      lua_newtable(L);
334
      lua_pushliteral(L, "_yaml");
335
      lua_pushliteral(L, "sequence");
336
      lua_rawset(L, -3);
337
      loader.sequencemt_index = top + 1;
338
 
339
      /* create map metatable */
340
      lua_newtable(L);
341
      lua_pushliteral(L, "_yaml");
342
      lua_pushliteral(L, "map");
343
      lua_rawset(L, -3);
344
      loader.mapmt_index = top + 2;
345
   }
346
 
347
   /* create table used to track anchors */
348
   lua_newtable(L);
349
   loader.anchortable_index = lua_gettop(L);
350
 
351
   yaml_parser_initialize(&loader.parser);
352
   yaml_parser_set_input_string(&loader.parser,
353
      (const unsigned char *)lua_tostring(L, 1), lua_strlen(L, 1));
354
   load(&loader);
355
 
356
   delete_event(&loader);
357
   yaml_parser_delete(&loader.parser);
358
 
359
   if (loader.error)
360
      lua_error(L);
361
 
362
   return loader.document_count;
363
}
364
 
365
static int dump_node(struct lua_yaml_dumper *dumper);
366
 
367
static int is_binary_string(const unsigned char *str, size_t len) {
368
   // this could be optimized to examine an entire word in each loop iteration
369
   while (len-- > 0) {
370
     if (*str++ & 0x80) return 1;
371
   }
372
   return 0;
373
}
374
 
375
static int dump_scalar(struct lua_yaml_dumper *dumper) {
376
   int type = lua_type(dumper->L, -1);
377
   size_t len;
378
   const char *str = NULL;
379
   yaml_char_t *tag = NULL;
380
   yaml_event_t ev;
381
   yaml_scalar_style_t style = YAML_PLAIN_SCALAR_STYLE;
382
   int is_binary = 0;
383
 
384
   if (type == LUA_TSTRING) {
385
      str = lua_tolstring(dumper->L, -1, &len);
386
      if (len <= 5 && (!strcmp(str, "true")
387
         || !strcmp(str, "false")
388
         || !strcmp(str, "~"))) {
389
         style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
390
      } else if (lua_isnumber(dumper->L, -1)) {
391
         /* string is convertible to number, quote it to preserve type */
392
         style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
393
      } else if ((is_binary = is_binary_string((const unsigned char *)str, len))) {
394
         tobase64(dumper->L, -1);
395
         str = lua_tolstring(dumper->L, -1, &len);
396
         tag = (yaml_char_t *) LUAYAML_TAG_PREFIX "binary";
397
      }
398
   } else if (type == LUA_TNUMBER) {
399
      /* have Lua convert number to a string */
400
      str = lua_tolstring(dumper->L, -1, &len);
401
   } else if (type == LUA_TBOOLEAN) {
402
      if (lua_toboolean(dumper->L, -1)) {
403
         str = "true";
404
         len = 4;
405
      } else {
406
         str = "false";
407
         len = 5;
408
      }
409
   }
410
 
411
   yaml_scalar_event_initialize(
412
      &ev, NULL, tag, (unsigned char *)str, len,
413
      !is_binary, !is_binary, style);
414
   if (is_binary) lua_pop(dumper->L, 1);
415
   return yaml_emitter_emit(&dumper->emitter, &ev);
416
}
417
 
418
static yaml_char_t *get_yaml_anchor(struct lua_yaml_dumper *dumper) {
419
   const char *s = "";
420
   lua_pushvalue(dumper->L, -1);
421
   lua_rawget(dumper->L, dumper->anchortable_index);
422
   if (!lua_toboolean(dumper->L, -1)) {
423
      lua_pop(dumper->L, 1);
424
      return NULL;
425
   }
426
 
427
   if (lua_isboolean(dumper->L, -1)) {
428
      /* this element is referenced more than once but has not been named */
429
      char buf[32];
430
      snprintf(buf, sizeof(buf), "%u", dumper->anchor_number++);
431
      lua_pop(dumper->L, 1);
432
      lua_pushvalue(dumper->L, -1);
433
      lua_pushstring(dumper->L, buf);
434
      s = lua_tostring(dumper->L, -1);
435
      lua_rawset(dumper->L, dumper->anchortable_index);
436
   } else {
437
      /* this is an aliased element */
438
      yaml_event_t ev;
439
      yaml_alias_event_initialize(&ev, (yaml_char_t *)lua_tostring(dumper->L, -1));
440
      yaml_emitter_emit(&dumper->emitter, &ev);
441
      lua_pop(dumper->L, 1);
442
   }
443
   return (yaml_char_t *)s;
444
}
445
 
446
static int dump_table(struct lua_yaml_dumper *dumper) {
447
   yaml_event_t ev;
448
   yaml_char_t *anchor = get_yaml_anchor(dumper);
449
 
450
   if (anchor && !*anchor) return 1;
451
 
452
   yaml_mapping_start_event_initialize(&ev, anchor, NULL, 0,
453
      YAML_BLOCK_MAPPING_STYLE);
454
   yaml_emitter_emit(&dumper->emitter, &ev);
455
 
456
   lua_pushnil(dumper->L);
457
   while (lua_next(dumper->L, -2)) {
458
      lua_pushvalue(dumper->L, -2); /* push copy of key on top of stack */
459
      if (!dump_node(dumper) || dumper->error)
460
         return 0;
461
      lua_pop(dumper->L, 1); /* pop copy of key */
462
      if (!dump_node(dumper) || dumper->error)
463
         return 0;
464
      lua_pop(dumper->L, 1);
465
   }
466
 
467
   yaml_mapping_end_event_initialize(&ev);
468
   yaml_emitter_emit(&dumper->emitter, &ev);
469
   return 1;
470
}
471
 
472
static int dump_array(struct lua_yaml_dumper *dumper) {
473
   int i, n = luaL_getn(dumper->L, -1);
474
   yaml_event_t ev;
475
   yaml_char_t *anchor = get_yaml_anchor(dumper);
476
 
477
   if (anchor && !*anchor)
478
      return 1;
479
 
480
   yaml_sequence_start_event_initialize(&ev, anchor, NULL, 0,
481
      YAML_BLOCK_SEQUENCE_STYLE);
482
   yaml_emitter_emit(&dumper->emitter, &ev);
483
 
484
   for (i = 0; i < n; i++) {
485
      lua_rawgeti(dumper->L, -1, i + 1);
486
      if (!dump_node(dumper) || dumper->error)
487
         return 0;
488
      lua_pop(dumper->L, 1);
489
   }
490
 
491
   yaml_sequence_end_event_initialize(&ev);
492
   yaml_emitter_emit(&dumper->emitter, &ev);
493
 
494
   return 1;
495
}
496
 
497
static int figure_table_type(lua_State *L) {
498
   int type = LUAYAML_KIND_UNKNOWN;
499
 
500
   if (lua_getmetatable(L, -1)) {
501
      /* has metatable, look for _yaml key */
502
      lua_pushliteral(L, "_yaml");
503
      lua_rawget(L, -2);
504
      if (lua_isstring(L, -1)) {
505
         const char *s = lua_tostring(L, -1);
506
         if (!strcmp(s, "sequence") || !strcmp(s, "seq"))
507
            type = LUAYAML_KIND_SEQUENCE;
508
         else if (!strcmp(s, "map") || !strcmp(s, "mapping"))
509
            type = LUAYAML_KIND_MAPPING;
510
      }
511
      lua_pop(L, 2); /* pop value and metatable */
512
   }
513
 
514
   return type;
515
}
516
 
517
static int dump_null(struct lua_yaml_dumper *dumper) {
518
   yaml_event_t ev;
519
   yaml_scalar_event_initialize(&ev, NULL, NULL,
520
      (unsigned char *)"~", 1, 1, 1, YAML_PLAIN_SCALAR_STYLE);
521
   return yaml_emitter_emit(&dumper->emitter, &ev);
522
}
523
 
524
static int dump_node(struct lua_yaml_dumper *dumper) {
525
   int type = lua_type(dumper->L, -1);
526
 
527
   if (type == LUA_TSTRING || type == LUA_TBOOLEAN || type == LUA_TNUMBER) {
528
      return dump_scalar(dumper);
529
   } else if (type == LUA_TTABLE) {
530
      int type = LUAYAML_KIND_UNKNOWN;
531
 
532
      if (Dump_Check_Metatables)
533
         type = figure_table_type(dumper->L);
534
 
535
      if (type == LUAYAML_KIND_UNKNOWN && Dump_Auto_Array &&
536
          luaL_getn(dumper->L, -1) > 0) {
537
         type = LUAYAML_KIND_SEQUENCE;
538
      }
539
 
540
      if (type == LUAYAML_KIND_SEQUENCE)
541
         return dump_array(dumper);
542
      return dump_table(dumper);
543
   } else if (type == LUA_TFUNCTION && lua_tocfunction(dumper->L, -1) == l_null) {
544
      return dump_null(dumper);
545
   } else { /* unsupported Lua type */
546
      if (Dump_Error_on_Unsupported) {
547
         char buf[256];
548
         snprintf(buf, sizeof(buf),
549
            "cannot dump object of type: %s", lua_typename(dumper->L, type));
550
         lua_pushstring(dumper->L, buf);
551
         dumper->error = 1;
552
      } else {
553
         return dump_null(dumper);
554
      }
555
   }
556
   return 0;
557
}
558
 
559
static void dump_document(struct lua_yaml_dumper *dumper) {
560
   yaml_event_t ev;
561
 
562
   yaml_document_start_event_initialize(&ev, NULL, NULL, NULL, 0);
563
   yaml_emitter_emit(&dumper->emitter, &ev);
564
 
565
   if (!dump_node(dumper) || dumper->error)
566
      return;
567
 
568
   yaml_document_end_event_initialize(&ev, 1);
569
   yaml_emitter_emit(&dumper->emitter, &ev);
570
}
571
 
572
static int append_output(void *arg, unsigned char *buf, unsigned int len) {
573
   struct lua_yaml_dumper *dumper = (struct lua_yaml_dumper *)arg;
574
   luaL_addlstring(&dumper->yamlbuf, (char *)buf, len);
575
   return 1;
576
}
577
 
578
static void find_references(struct lua_yaml_dumper *dumper) {
579
   int newval = -1, type = lua_type(dumper->L, -1);
580
   if (type != LUA_TTABLE)
581
      return;
582
 
583
   lua_pushvalue(dumper->L, -1); /* push copy of table */
584
   lua_rawget(dumper->L, dumper->anchortable_index);
585
   if (lua_isnil(dumper->L, -1))
586
      newval = 0;
587
   else if (!lua_toboolean(dumper->L, -1))
588
      newval = 1;
589
   lua_pop(dumper->L, 1);
590
   if (newval != -1) {
591
      lua_pushvalue(dumper->L, -1);
592
      lua_pushboolean(dumper->L, newval);
593
      lua_rawset(dumper->L, dumper->anchortable_index);
594
   }
595
   if (newval)
596
      return;
597
 
598
   /* recursively process other table values */
599
   lua_pushnil(dumper->L);
600
   while (lua_next(dumper->L, -2) != 0) {
601
      find_references(dumper); /* find references on value */
602
      lua_pop(dumper->L, 1);
603
      find_references(dumper); /* find references on key */
604
   }
605
}
606
 
607
static int l_dump(lua_State *L) {
608
   struct lua_yaml_dumper dumper;
609
   int i, argcount = lua_gettop(L);
610
   yaml_event_t ev;
611
 
612
   dumper.L = L;
613
   dumper.error = 0;
614
   /* create thread to use for YAML buffer */
615
   dumper.outputL = lua_newthread(L);
616
   luaL_buffinit(dumper.outputL, &dumper.yamlbuf);
617
 
618
   yaml_emitter_initialize(&dumper.emitter);
619
   yaml_emitter_set_unicode(&dumper.emitter, 1);
620
   yaml_emitter_set_width(&dumper.emitter, 2);
621
   yaml_emitter_set_output(&dumper.emitter, &append_output, &dumper);
622
 
623
   yaml_stream_start_event_initialize(&ev, YAML_UTF8_ENCODING);
624
   yaml_emitter_emit(&dumper.emitter, &ev);
625
 
626
   for (i = 0; i < argcount; i++) {
627
      lua_newtable(L);
628
      dumper.anchortable_index = lua_gettop(L);
629
      dumper.anchor_number = 0;
630
      lua_pushvalue(L, i + 1); /* push copy of arg we're processing */
631
      find_references(&dumper);
632
      dump_document(&dumper);
633
      if (dumper.error)
634
         break;
635
      lua_pop(L, 2); /* pop copied arg and anchor table */
636
   }
637
 
638
   yaml_stream_end_event_initialize(&ev);
639
   yaml_emitter_emit(&dumper.emitter, &ev);
640
 
641
   yaml_emitter_flush(&dumper.emitter);
642
   yaml_emitter_delete(&dumper.emitter);
643
 
644
   /* finalize and push YAML buffer */
645
   luaL_pushresult(&dumper.yamlbuf);
646
 
647
   if (dumper.error)
648
      lua_error(L);
649
 
650
   /* move buffer to original thread */
651
   lua_xmove(dumper.outputL, L, 1);
652
   return 1;
653
}
654
 
655
static int handle_config_option(lua_State *L) {
656
   const char *attr;
657
   int i;
658
   static const struct {
659
      const char *attr;
660
      char *val;
661
   } args[] = {
662
      { "dump_auto_array", &Dump_Auto_Array },
663
      { "dump_check_metatables", &Dump_Check_Metatables },
664
      { "dump_error_on_unsupported", &Dump_Error_on_Unsupported },
665
      { "load_set_metatables", &Load_Set_Metatables },
666
      { "load_numeric_scalars", &Load_Numeric_Scalars },
667
      { "load_nulls_as_nil", &Load_Nulls_As_Nil },
668
      { NULL, NULL }
669
   };
670
 
671
   luaL_argcheck(L, lua_isstring(L, -2), 1, "config attribute must be string");
672
   luaL_argcheck(L, lua_isboolean(L, -1) || lua_isnil(L, -1), 1,
673
      "value must be boolean or nil");
674
 
675
   attr = lua_tostring(L, -2);
676
   for (i = 0; args[i].attr; i++) {
677
      if (!strcmp(attr, args[i].attr)) {
678
         if (!lua_isnil(L, -1))
679
            *(args[i].val) = lua_toboolean(L, -1);
680
         lua_pushboolean(L, *(args[i].val));
681
         return 1;
682
      }
683
   }
684
 
685
   luaL_error(L, "unrecognized config option: %s", attr);
686
   return 0; /* never reached */
687
}
688
 
689
static int l_config(lua_State *L) {
690
   if (lua_istable(L, -1)) {
691
      lua_pushnil(L);
692
      while (lua_next(L, -2) != 0) {
693
         handle_config_option(L);
694
         lua_pop(L, 2);
695
      }
696
      return 0;
697
   }
698
 
699
   return handle_config_option(L);
700
}
701
 
702
static int l_null(lua_State *L) {
703
   lua_getglobal(L, "yaml");
704
   lua_pushliteral(L, "null");
705
   lua_rawget(L, -2);
706
   lua_replace(L, -2);
707
 
708
   return 1;
709
}
710
 
711
LUALIB_API int luaopen_yaml(lua_State *L) {
712
   const luaL_reg yamllib[] = {
713
      { "load", l_load },
714
      { "dump", l_dump },
715
      { "configure", l_config },
716
      { "null", l_null },
717
      { NULL, NULL}
718
   };
719
 
720
   luaL_openlib(L, "yaml", yamllib, 0);
721
   return 1;
722
}