# HG changeset patch # User Matthew Wild # Date 1324077688 0 # Node ID 3ebc0316f54f2fcd7c25a7b7b41ab9ec7a415e70 Initial commit with my changes. Originally imported from yaml-2.0.tar.gz: http://files.luaforge.net/releases/yaml/yaml/0.2/yaml-0.2.tar.gz diff -r 000000000000 -r 3ebc0316f54f HISTORY --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/HISTORY Fri Dec 16 23:21:28 2011 +0000 @@ -0,0 +1,16 @@ +0.1: May 12 2009 + * initial release + +0.2: November 23 2009 + * updated libyaml to version 0.1.3 + * now properly dumps and loads strings containing binary data using base64 + * dumped strings are quoted when they could be loaded as numbers + * nulls are loaded as yaml.null, a function that returns itself and can + be used to test for equality + * load now also recognizes 'yes' as a boolean truth value + * zero length scalars are not converted to nil + +UNRELEASED (Matthew Wild): + * Removed libyaml from source tree + * Modified Makefile to Debian/Ubuntu paths + * Link to system libyaml in Makefile diff -r 000000000000 -r 3ebc0316f54f LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Fri Dec 16 23:21:28 2011 +0000 @@ -0,0 +1,19 @@ +Copyright (c) 2009, Andrew Danforth + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff -r 000000000000 -r 3ebc0316f54f Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Fri Dec 16 23:21:28 2011 +0000 @@ -0,0 +1,34 @@ +# Default installation prefix +PREFIX=/usr + +# System's libraries directory (where binary libraries are installed) +LUA_LIBDIR=$(PREFIX)/lib/lua/5.1 + +# System's lua directory (where Lua libraries are installed) +LUA_DIR=$(PREFIX)/share/lua/5.1 + +LUAINC=$(PREFIX)/include/lua5.1 +LUALIB=$(PREFIX)/lib + +CC=gcc +# -fexceptions is necessary if your Lua was built with a C++ compiler and +# uses exceptions internally; can be removed +CFLAGS=-O2 -Wall $(INC) -shared -fPIC -fexceptions +LDFLAGS=-shared -L$(LUALIB) -lyaml +INC=-I$(LUAINC) + +OBJS=lyaml.o b64.o + +all: yaml.so + +install: + cp -f yaml.so $(LUA_LIBDIR) + +uninstall: + rm -f $(LUA_LIBDIR)/yaml.so + +yaml.so: $(OBJS) + $(CC) -o $@ $(LDFLAGS) $(OBJS) + +clean: + rm -f $(OBJS) yaml.so core core.* a.out diff -r 000000000000 -r 3ebc0316f54f README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Fri Dec 16 23:21:28 2011 +0000 @@ -0,0 +1,39 @@ +NAME + yaml - Lua YAML serialization using LibYAML + +SYNOPSIS + require 'yaml' + + serialized = yaml.dump({ 1, 2, 3, 4 }) + content = yaml.load(serialized) + +DESCRIPTION + This module is a Lua binding for Kirill Siminov's excellent LibYAML. + LibYAML is generally considered to be the best C YAML 1.1 implementation. + LibYAML 0.1.3 is included as part of this release. + + This module defines the functions dump, load, and configure within the + global yaml table. + + Portions of this software were inspired by Perl's YAML::LibYAML module by + Ingy döt Net. + +SEE ALSO + * LibYAML (http://pyyaml.org/wiki/LibYAML) + * luayaml: libsyck YAML binding (http://luaforge.net/projects/luayaml) + * YAML::LibYAML (http://search.cpan.org/~nuffin/YAML-LibYAML) + +AUTHOR + Andrew Danforth + + If you are using this module successfully I would love to hear from you. + +COPYRIGHT + Copyright (c) 2009, Andrew Danforth + +THANKS + Thanks to the following people for suggestions and patches: + + Peter Mawhorter + Cyril Romain + Adrian Sampson diff -r 000000000000 -r 3ebc0316f54f TODO --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TODO Fri Dec 16 23:21:28 2011 +0000 @@ -0,0 +1,3 @@ +* allow creating dump/load objects with internal configuration settings +* better error checking when using LibYAML dump functions +* potentially support additional Lua types (functions?) diff -r 000000000000 -r 3ebc0316f54f b64.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/b64.c Fri Dec 16 23:21:28 2011 +0000 @@ -0,0 +1,94 @@ +#include +#include + +#include "b64.h" + +int frombase64(lua_State *L, const unsigned char *str, unsigned int len) { + int d = 0, dlast = 0, phase = 0; + unsigned char c; + static int table[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ + }; + luaL_Buffer b; + + luaL_buffinit(L, &b); + for (; len--; ++str) { + d = table[(int)*str]; + if (d == -1) continue; + switch(phase) { + case 0: + ++phase; + break; + case 1: + c = ((dlast << 2) | ((d & 0x30) >> 4)); + luaL_addchar(&b, c); + ++phase; + break; + case 2: + c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2)); + luaL_addchar(&b, c); + ++phase; + break; + case 3: + c = (((dlast & 0x03 ) << 6) | d); + luaL_addchar(&b, c); + phase = 0; + break; + } + dlast = d; + } + luaL_pushresult(&b); + return 1; +} + +static void b64_encode(luaL_Buffer *b, unsigned int c1, unsigned int c2, unsigned int c3, int n) { + static const char code[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + unsigned long tuple = c3 + 256UL * (c2 + 256UL * c1); + int i; + char s[4]; + + for (i = 0; i < 4; i++) { + s[3-i] = code[tuple % 64]; + tuple /= 64; + } + for (i = n+1; i < 4; i++) s[i] = '='; + luaL_addlstring(b, s, 4); +} + +int tobase64(lua_State *L, int pos) { + size_t l; + const unsigned char *s = (const unsigned char*)luaL_checklstring(L, pos, &l); + luaL_Buffer b; + int n; + + luaL_buffinit(L, &b); + for (n = l / 3; n--; s += 3) + b64_encode(&b, s[0], s[1], s[2], 3); + + switch (l % 3) { + case 1: + b64_encode(&b, s[0], 0, 0, 1); + break; + case 2: + b64_encode(&b, s[0], s[1], 0, 2); + break; + } + luaL_pushresult(&b); + return 1; +} diff -r 000000000000 -r 3ebc0316f54f b64.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/b64.h Fri Dec 16 23:21:28 2011 +0000 @@ -0,0 +1,4 @@ +#include + +int frombase64(lua_State *, const unsigned char *, unsigned int); +int tobase64(lua_State *, int); diff -r 000000000000 -r 3ebc0316f54f lyaml.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lyaml.c Fri Dec 16 23:21:28 2011 +0000 @@ -0,0 +1,722 @@ +/* + * lyaml.c, LibYAML binding for Lua + * + * Copyright (c) 2009, Andrew Danforth + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Portions of this software were inspired by Perl's YAML::LibYAML module by + * Ingy döt Net + * + */ + +#include +#include + +#include +#include +#include + +#include "yaml.h" +#include "b64.h" + +/* configurable flags */ +static char Dump_Auto_Array = 1; +static char Dump_Error_on_Unsupported = 0; +static char Dump_Check_Metatables = 1; +static char Load_Set_Metatables = 1; +static char Load_Numeric_Scalars = 1; +static char Load_Nulls_As_Nil = 0; + +#define LUAYAML_TAG_PREFIX "tag:yaml.org,2002:" + +#define LUAYAML_KIND_UNKNOWN 0 +#define LUAYAML_KIND_SEQUENCE 1 +#define LUAYAML_KIND_MAPPING 2 + +#define RETURN_ERRMSG(s, msg) do { \ + lua_pushstring(s->L, msg); \ + s->error = 1; \ + return; \ + } while(0) + +struct lua_yaml_loader { + lua_State *L; + int anchortable_index; + int sequencemt_index; + int mapmt_index; + int document_count; + yaml_parser_t parser; + yaml_event_t event; + char validevent; + char error; +}; + +struct lua_yaml_dumper { + lua_State *L; + int anchortable_index; + unsigned int anchor_number; + yaml_emitter_t emitter; + char error; + + lua_State *outputL; + luaL_Buffer yamlbuf; +}; + +static int l_null(lua_State *); + +static void generate_error_message(struct lua_yaml_loader *loader) { + char buf[256]; + luaL_Buffer b; + + luaL_buffinit(loader->L, &b); + + luaL_addstring(&b, loader->parser.problem ? loader->parser.problem : "A problem"); + snprintf(buf, sizeof(buf), " at document: %d", loader->document_count); + luaL_addstring(&b, buf); + + if (loader->parser.problem_mark.line || loader->parser.problem_mark.column) { + snprintf(buf, sizeof(buf), ", line: %d, column: %d\n", + loader->parser.problem_mark.line + 1, + loader->parser.problem_mark.column + 1); + luaL_addstring(&b, buf); + } else { + luaL_addstring(&b, "\n"); + } + + if (loader->parser.context) { + snprintf(buf, sizeof(buf), "%s at line: %d, column: %d\n", + loader->parser.context, + loader->parser.context_mark.line + 1, + loader->parser.context_mark.column + 1); + luaL_addstring(&b, buf); + } + + luaL_pushresult(&b); +} + +static inline void delete_event(struct lua_yaml_loader *loader) { + if (loader->validevent) { + yaml_event_delete(&loader->event); + loader->validevent = 0; + } +} + +static inline int do_parse(struct lua_yaml_loader *loader) { + delete_event(loader); + if (yaml_parser_parse(&loader->parser, &loader->event) != 1) { + generate_error_message(loader); + loader->error = 1; + return 0; + } + + loader->validevent = 1; + return 1; +} + +static int load_node(struct lua_yaml_loader *loader); + +static void handle_anchor(struct lua_yaml_loader *loader) { + const char *anchor = (char *)loader->event.data.scalar.anchor; + if (!anchor) + return; + + lua_pushstring(loader->L, anchor); + lua_pushvalue(loader->L, -2); + lua_rawset(loader->L, loader->anchortable_index); +} + +static void load_map(struct lua_yaml_loader *loader) { + lua_newtable(loader->L); + if (loader->mapmt_index != 0) { + lua_pushvalue(loader->L, loader->mapmt_index); + lua_setmetatable(loader->L, -2); + } + + handle_anchor(loader); + while (1) { + int r; + /* load key */ + if (load_node(loader) == 0 || loader->error) + return; + + /* load value */ + r = load_node(loader); + if (loader->error) + return; + if (r != 1) + RETURN_ERRMSG(loader, "unanticipated END event"); + lua_rawset(loader->L, -3); + } +} + +static void load_sequence(struct lua_yaml_loader *loader) { + int index = 1; + + lua_newtable(loader->L); + if (loader->sequencemt_index != 0) { + lua_pushvalue(loader->L, loader->sequencemt_index); + lua_setmetatable(loader->L, -2); + } + + handle_anchor(loader); + while (load_node(loader) == 1 && !loader->error) + lua_rawseti(loader->L, -2, index++); +} + +static void load_scalar(struct lua_yaml_loader *loader) { + const char *str = (char *)loader->event.data.scalar.value; + unsigned int length = loader->event.data.scalar.length; + const char *tag = (char *)loader->event.data.scalar.tag; + + if (tag && !strncmp(tag, LUAYAML_TAG_PREFIX, sizeof(LUAYAML_TAG_PREFIX) - 1)) { + tag += sizeof(LUAYAML_TAG_PREFIX) - 1; + + if (!strcmp(tag, "str")) { + lua_pushlstring(loader->L, str, length); + return; + } else if (!strcmp(tag, "int")) { + lua_pushinteger(loader->L, strtol(str, NULL, 10)); + return; + } else if (!strcmp(tag, "float")) { + lua_pushnumber(loader->L, strtod(str, NULL)); + return; + } else if (!strcmp(tag, "bool")) { + lua_pushboolean(loader->L, !strcmp(str, "true") || !strcmp(str, "yes")); + return; + } else if (!strcmp(tag, "binary")) { + frombase64(loader->L, (const unsigned char *)str, length); + return; + } + } + + if (loader->event.data.scalar.style == YAML_PLAIN_SCALAR_STYLE) { + if (!strcmp(str, "~")) { + if (Load_Nulls_As_Nil) + lua_pushnil(loader->L); + else + l_null(loader->L); + return; + } else if (!strcmp(str, "true") || !strcmp(str, "yes")) { + lua_pushboolean(loader->L, 1); + return; + } else if (!strcmp(str, "false") || !strcmp(str, "no")) { + lua_pushboolean(loader->L, 0); + return; + } + } + + lua_pushlstring(loader->L, str, length); + + /* plain scalar and Lua can convert it to a number? make it so... */ + if (Load_Numeric_Scalars + && loader->event.data.scalar.style == YAML_PLAIN_SCALAR_STYLE + && lua_isnumber(loader->L, -1)) { + lua_Number n = lua_tonumber(loader->L, -1); + lua_pop(loader->L, 1); + lua_pushnumber(loader->L, n); + } + + handle_anchor(loader); +} + +static void load_alias(struct lua_yaml_loader *loader) { + char *anchor = (char *)loader->event.data.alias.anchor; + lua_pushstring(loader->L, anchor); + lua_rawget(loader->L, loader->anchortable_index); + if (lua_isnil(loader->L, -1)) { + char buf[256]; + snprintf(buf, sizeof(buf), "invalid reference: %s", anchor); + RETURN_ERRMSG(loader, buf); + } +} + +static int load_node(struct lua_yaml_loader *loader) { + if (!do_parse(loader)) + return -1; + + switch (loader->event.type) { + case YAML_DOCUMENT_END_EVENT: + case YAML_MAPPING_END_EVENT: + case YAML_SEQUENCE_END_EVENT: + return 0; + + case YAML_MAPPING_START_EVENT: + load_map(loader); + return 1; + + case YAML_SEQUENCE_START_EVENT: + load_sequence(loader); + return 1; + + case YAML_SCALAR_EVENT: + load_scalar(loader); + return 1; + + case YAML_ALIAS_EVENT: + load_alias(loader); + return 1; + + case YAML_NO_EVENT: + lua_pushliteral(loader->L, "libyaml returned YAML_NO_EVENT"); + loader->error = 1; + return -1; + + default: + lua_pushliteral(loader->L, "invalid event"); + loader->error = 1; + return -1; + } +} + +static void load(struct lua_yaml_loader *loader) { + if (!do_parse(loader)) + return; + + if (loader->event.type != YAML_STREAM_START_EVENT) + RETURN_ERRMSG(loader, "expected STREAM_START_EVENT"); + + while (1) { + if (!do_parse(loader)) + return; + + if (loader->event.type == YAML_STREAM_END_EVENT) + return; + + loader->document_count++; + if (load_node(loader) != 1) + RETURN_ERRMSG(loader, "unexpected END event"); + if (loader->error) + return; + + if (!do_parse(loader)) + return; + if (loader->event.type != YAML_DOCUMENT_END_EVENT) + RETURN_ERRMSG(loader, "expected DOCUMENT_END_EVENT"); + + /* reset anchor table */ + lua_newtable(loader->L); + lua_replace(loader->L, loader->anchortable_index); + } +} + +static int l_load(lua_State *L) { + struct lua_yaml_loader loader; + int top = lua_gettop(L); + + luaL_argcheck(L, lua_isstring(L, 1), 1, "must provide a string argument"); + + loader.L = L; + loader.validevent = 0; + loader.error = 0; + loader.document_count = 0; + loader.mapmt_index = loader.sequencemt_index = 0; + + if (Load_Set_Metatables) { + /* create sequence metatable */ + lua_newtable(L); + lua_pushliteral(L, "_yaml"); + lua_pushliteral(L, "sequence"); + lua_rawset(L, -3); + loader.sequencemt_index = top + 1; + + /* create map metatable */ + lua_newtable(L); + lua_pushliteral(L, "_yaml"); + lua_pushliteral(L, "map"); + lua_rawset(L, -3); + loader.mapmt_index = top + 2; + } + + /* create table used to track anchors */ + lua_newtable(L); + loader.anchortable_index = lua_gettop(L); + + yaml_parser_initialize(&loader.parser); + yaml_parser_set_input_string(&loader.parser, + (const unsigned char *)lua_tostring(L, 1), lua_strlen(L, 1)); + load(&loader); + + delete_event(&loader); + yaml_parser_delete(&loader.parser); + + if (loader.error) + lua_error(L); + + return loader.document_count; +} + +static int dump_node(struct lua_yaml_dumper *dumper); + +static int is_binary_string(const unsigned char *str, size_t len) { + // this could be optimized to examine an entire word in each loop iteration + while (len-- > 0) { + if (*str++ & 0x80) return 1; + } + return 0; +} + +static int dump_scalar(struct lua_yaml_dumper *dumper) { + int type = lua_type(dumper->L, -1); + size_t len; + const char *str = NULL; + yaml_char_t *tag = NULL; + yaml_event_t ev; + yaml_scalar_style_t style = YAML_PLAIN_SCALAR_STYLE; + int is_binary = 0; + + if (type == LUA_TSTRING) { + str = lua_tolstring(dumper->L, -1, &len); + if (len <= 5 && (!strcmp(str, "true") + || !strcmp(str, "false") + || !strcmp(str, "~"))) { + style = YAML_SINGLE_QUOTED_SCALAR_STYLE; + } else if (lua_isnumber(dumper->L, -1)) { + /* string is convertible to number, quote it to preserve type */ + style = YAML_SINGLE_QUOTED_SCALAR_STYLE; + } else if ((is_binary = is_binary_string((const unsigned char *)str, len))) { + tobase64(dumper->L, -1); + str = lua_tolstring(dumper->L, -1, &len); + tag = (yaml_char_t *) LUAYAML_TAG_PREFIX "binary"; + } + } else if (type == LUA_TNUMBER) { + /* have Lua convert number to a string */ + str = lua_tolstring(dumper->L, -1, &len); + } else if (type == LUA_TBOOLEAN) { + if (lua_toboolean(dumper->L, -1)) { + str = "true"; + len = 4; + } else { + str = "false"; + len = 5; + } + } + + yaml_scalar_event_initialize( + &ev, NULL, tag, (unsigned char *)str, len, + !is_binary, !is_binary, style); + if (is_binary) lua_pop(dumper->L, 1); + return yaml_emitter_emit(&dumper->emitter, &ev); +} + +static yaml_char_t *get_yaml_anchor(struct lua_yaml_dumper *dumper) { + const char *s = ""; + lua_pushvalue(dumper->L, -1); + lua_rawget(dumper->L, dumper->anchortable_index); + if (!lua_toboolean(dumper->L, -1)) { + lua_pop(dumper->L, 1); + return NULL; + } + + if (lua_isboolean(dumper->L, -1)) { + /* this element is referenced more than once but has not been named */ + char buf[32]; + snprintf(buf, sizeof(buf), "%u", dumper->anchor_number++); + lua_pop(dumper->L, 1); + lua_pushvalue(dumper->L, -1); + lua_pushstring(dumper->L, buf); + s = lua_tostring(dumper->L, -1); + lua_rawset(dumper->L, dumper->anchortable_index); + } else { + /* this is an aliased element */ + yaml_event_t ev; + yaml_alias_event_initialize(&ev, (yaml_char_t *)lua_tostring(dumper->L, -1)); + yaml_emitter_emit(&dumper->emitter, &ev); + lua_pop(dumper->L, 1); + } + return (yaml_char_t *)s; +} + +static int dump_table(struct lua_yaml_dumper *dumper) { + yaml_event_t ev; + yaml_char_t *anchor = get_yaml_anchor(dumper); + + if (anchor && !*anchor) return 1; + + yaml_mapping_start_event_initialize(&ev, anchor, NULL, 0, + YAML_BLOCK_MAPPING_STYLE); + yaml_emitter_emit(&dumper->emitter, &ev); + + lua_pushnil(dumper->L); + while (lua_next(dumper->L, -2)) { + lua_pushvalue(dumper->L, -2); /* push copy of key on top of stack */ + if (!dump_node(dumper) || dumper->error) + return 0; + lua_pop(dumper->L, 1); /* pop copy of key */ + if (!dump_node(dumper) || dumper->error) + return 0; + lua_pop(dumper->L, 1); + } + + yaml_mapping_end_event_initialize(&ev); + yaml_emitter_emit(&dumper->emitter, &ev); + return 1; +} + +static int dump_array(struct lua_yaml_dumper *dumper) { + int i, n = luaL_getn(dumper->L, -1); + yaml_event_t ev; + yaml_char_t *anchor = get_yaml_anchor(dumper); + + if (anchor && !*anchor) + return 1; + + yaml_sequence_start_event_initialize(&ev, anchor, NULL, 0, + YAML_BLOCK_SEQUENCE_STYLE); + yaml_emitter_emit(&dumper->emitter, &ev); + + for (i = 0; i < n; i++) { + lua_rawgeti(dumper->L, -1, i + 1); + if (!dump_node(dumper) || dumper->error) + return 0; + lua_pop(dumper->L, 1); + } + + yaml_sequence_end_event_initialize(&ev); + yaml_emitter_emit(&dumper->emitter, &ev); + + return 1; +} + +static int figure_table_type(lua_State *L) { + int type = LUAYAML_KIND_UNKNOWN; + + if (lua_getmetatable(L, -1)) { + /* has metatable, look for _yaml key */ + lua_pushliteral(L, "_yaml"); + lua_rawget(L, -2); + if (lua_isstring(L, -1)) { + const char *s = lua_tostring(L, -1); + if (!strcmp(s, "sequence") || !strcmp(s, "seq")) + type = LUAYAML_KIND_SEQUENCE; + else if (!strcmp(s, "map") || !strcmp(s, "mapping")) + type = LUAYAML_KIND_MAPPING; + } + lua_pop(L, 2); /* pop value and metatable */ + } + + return type; +} + +static int dump_null(struct lua_yaml_dumper *dumper) { + yaml_event_t ev; + yaml_scalar_event_initialize(&ev, NULL, NULL, + (unsigned char *)"~", 1, 1, 1, YAML_PLAIN_SCALAR_STYLE); + return yaml_emitter_emit(&dumper->emitter, &ev); +} + +static int dump_node(struct lua_yaml_dumper *dumper) { + int type = lua_type(dumper->L, -1); + + if (type == LUA_TSTRING || type == LUA_TBOOLEAN || type == LUA_TNUMBER) { + return dump_scalar(dumper); + } else if (type == LUA_TTABLE) { + int type = LUAYAML_KIND_UNKNOWN; + + if (Dump_Check_Metatables) + type = figure_table_type(dumper->L); + + if (type == LUAYAML_KIND_UNKNOWN && Dump_Auto_Array && + luaL_getn(dumper->L, -1) > 0) { + type = LUAYAML_KIND_SEQUENCE; + } + + if (type == LUAYAML_KIND_SEQUENCE) + return dump_array(dumper); + return dump_table(dumper); + } else if (type == LUA_TFUNCTION && lua_tocfunction(dumper->L, -1) == l_null) { + return dump_null(dumper); + } else { /* unsupported Lua type */ + if (Dump_Error_on_Unsupported) { + char buf[256]; + snprintf(buf, sizeof(buf), + "cannot dump object of type: %s", lua_typename(dumper->L, type)); + lua_pushstring(dumper->L, buf); + dumper->error = 1; + } else { + return dump_null(dumper); + } + } + return 0; +} + +static void dump_document(struct lua_yaml_dumper *dumper) { + yaml_event_t ev; + + yaml_document_start_event_initialize(&ev, NULL, NULL, NULL, 0); + yaml_emitter_emit(&dumper->emitter, &ev); + + if (!dump_node(dumper) || dumper->error) + return; + + yaml_document_end_event_initialize(&ev, 1); + yaml_emitter_emit(&dumper->emitter, &ev); +} + +static int append_output(void *arg, unsigned char *buf, unsigned int len) { + struct lua_yaml_dumper *dumper = (struct lua_yaml_dumper *)arg; + luaL_addlstring(&dumper->yamlbuf, (char *)buf, len); + return 1; +} + +static void find_references(struct lua_yaml_dumper *dumper) { + int newval = -1, type = lua_type(dumper->L, -1); + if (type != LUA_TTABLE) + return; + + lua_pushvalue(dumper->L, -1); /* push copy of table */ + lua_rawget(dumper->L, dumper->anchortable_index); + if (lua_isnil(dumper->L, -1)) + newval = 0; + else if (!lua_toboolean(dumper->L, -1)) + newval = 1; + lua_pop(dumper->L, 1); + if (newval != -1) { + lua_pushvalue(dumper->L, -1); + lua_pushboolean(dumper->L, newval); + lua_rawset(dumper->L, dumper->anchortable_index); + } + if (newval) + return; + + /* recursively process other table values */ + lua_pushnil(dumper->L); + while (lua_next(dumper->L, -2) != 0) { + find_references(dumper); /* find references on value */ + lua_pop(dumper->L, 1); + find_references(dumper); /* find references on key */ + } +} + +static int l_dump(lua_State *L) { + struct lua_yaml_dumper dumper; + int i, argcount = lua_gettop(L); + yaml_event_t ev; + + dumper.L = L; + dumper.error = 0; + /* create thread to use for YAML buffer */ + dumper.outputL = lua_newthread(L); + luaL_buffinit(dumper.outputL, &dumper.yamlbuf); + + yaml_emitter_initialize(&dumper.emitter); + yaml_emitter_set_unicode(&dumper.emitter, 1); + yaml_emitter_set_width(&dumper.emitter, 2); + yaml_emitter_set_output(&dumper.emitter, &append_output, &dumper); + + yaml_stream_start_event_initialize(&ev, YAML_UTF8_ENCODING); + yaml_emitter_emit(&dumper.emitter, &ev); + + for (i = 0; i < argcount; i++) { + lua_newtable(L); + dumper.anchortable_index = lua_gettop(L); + dumper.anchor_number = 0; + lua_pushvalue(L, i + 1); /* push copy of arg we're processing */ + find_references(&dumper); + dump_document(&dumper); + if (dumper.error) + break; + lua_pop(L, 2); /* pop copied arg and anchor table */ + } + + yaml_stream_end_event_initialize(&ev); + yaml_emitter_emit(&dumper.emitter, &ev); + + yaml_emitter_flush(&dumper.emitter); + yaml_emitter_delete(&dumper.emitter); + + /* finalize and push YAML buffer */ + luaL_pushresult(&dumper.yamlbuf); + + if (dumper.error) + lua_error(L); + + /* move buffer to original thread */ + lua_xmove(dumper.outputL, L, 1); + return 1; +} + +static int handle_config_option(lua_State *L) { + const char *attr; + int i; + static const struct { + const char *attr; + char *val; + } args[] = { + { "dump_auto_array", &Dump_Auto_Array }, + { "dump_check_metatables", &Dump_Check_Metatables }, + { "dump_error_on_unsupported", &Dump_Error_on_Unsupported }, + { "load_set_metatables", &Load_Set_Metatables }, + { "load_numeric_scalars", &Load_Numeric_Scalars }, + { "load_nulls_as_nil", &Load_Nulls_As_Nil }, + { NULL, NULL } + }; + + luaL_argcheck(L, lua_isstring(L, -2), 1, "config attribute must be string"); + luaL_argcheck(L, lua_isboolean(L, -1) || lua_isnil(L, -1), 1, + "value must be boolean or nil"); + + attr = lua_tostring(L, -2); + for (i = 0; args[i].attr; i++) { + if (!strcmp(attr, args[i].attr)) { + if (!lua_isnil(L, -1)) + *(args[i].val) = lua_toboolean(L, -1); + lua_pushboolean(L, *(args[i].val)); + return 1; + } + } + + luaL_error(L, "unrecognized config option: %s", attr); + return 0; /* never reached */ +} + +static int l_config(lua_State *L) { + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + handle_config_option(L); + lua_pop(L, 2); + } + return 0; + } + + return handle_config_option(L); +} + +static int l_null(lua_State *L) { + lua_getglobal(L, "yaml"); + lua_pushliteral(L, "null"); + lua_rawget(L, -2); + lua_replace(L, -2); + + return 1; +} + +LUALIB_API int luaopen_yaml(lua_State *L) { + const luaL_reg yamllib[] = { + { "load", l_load }, + { "dump", l_dump }, + { "configure", l_config }, + { "null", l_null }, + { NULL, NULL} + }; + + luaL_openlib(L, "yaml", yamllib, 0); + return 1; +}