lyaml.c

changeset 0
3ebc0316f54f
equal deleted inserted replaced
-1:000000000000 0:3ebc0316f54f
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 }

mercurial