From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 776D925098 for ; Mon, 4 Feb 2019 22:30:27 -0500 (EST) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id bCORhNRGoUtI for ; Mon, 4 Feb 2019 22:30:27 -0500 (EST) Received: from smtp46.i.mail.ru (smtp46.i.mail.ru [94.100.177.106]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 2814124FF6 for ; Mon, 4 Feb 2019 22:30:27 -0500 (EST) From: Alexander Turenko Subject: [tarantool-patches] [PATCH v3 2/2] lua-yaml: fix strings literals encoding in yaml Date: Tue, 5 Feb 2019 06:30:17 +0300 Message-Id: <9c2e78597b5ab1cd74c28eeb36d2adcebe3f09c5.1549336851.git.alexander.turenko@tarantool.org> In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: Vladislav Shpilevoy Cc: Alexander Turenko , tarantool-patches@freelists.org yaml.encode() now wraps a string literal whose content is equal to a null or a boolean value representation in YAML into single quotes. Those literals are: 'false', 'no', 'true', 'yes', '~', 'null'. Reverted the a2d7643c commit to use single quotes instead of multiline encoding for 'true' and 'false' string literals. Follows up #3476 Closes #3662 Closes #3583 --- test/app-tap/console.test.lua | 21 +++++++-- third_party/lua-yaml/lyaml.cc | 88 +++++++++++++++++++++++++++-------- 2 files changed, 85 insertions(+), 24 deletions(-) diff --git a/test/app-tap/console.test.lua b/test/app-tap/console.test.lua index 4f915afd7..0586dd1b1 100755 --- a/test/app-tap/console.test.lua +++ b/test/app-tap/console.test.lua @@ -21,7 +21,7 @@ local EOL = "\n...\n" test = tap.test("console") -test:plan(59) +test:plan(66) -- Start console and connect to it local server = console.listen(CONSOLE_SOCKET) @@ -77,11 +77,22 @@ test:is(yaml.decode(client:read(EOL)), '', "clear delimiter") -- -- gh-3476: yaml.encode encodes 'false' and 'true' incorrectly. +-- gh-3662: yaml.encode encodes booleans with multiline format. +-- gh-3583: yaml.encode encodes null incorrectly. -- -test:is(type(yaml.decode(yaml.encode('false'))), 'string') -test:is(type(yaml.decode(yaml.encode('true'))), 'string') -test:is(type(yaml.decode(yaml.encode({a = 'false'})).a), 'string') -test:is(type(yaml.decode(yaml.encode({a = 'false'})).a), 'string') + +test:is(yaml.encode(false), "--- false\n...\n") +test:is(yaml.encode(true), "--- true\n...\n") +test:is(yaml.encode('false'), "--- 'false'\n...\n") +test:is(yaml.encode('true'), "--- 'true'\n...\n") +test:is(yaml.encode(nil), "--- null\n...\n") + +test:is(yaml.decode('false'), false) +test:is(yaml.decode('no'), false) +test:is(yaml.decode('true'), true) +test:is(yaml.decode('yes'), true) +test:is(yaml.decode('~'), nil) +test:is(yaml.decode('null'), nil) box.cfg{ listen=IPROTO_SOCKET; diff --git a/third_party/lua-yaml/lyaml.cc b/third_party/lua-yaml/lyaml.cc index 354cafe86..5afcace1b 100644 --- a/third_party/lua-yaml/lyaml.cc +++ b/third_party/lua-yaml/lyaml.cc @@ -94,6 +94,50 @@ struct lua_yaml_dumper { luaL_Buffer yamlbuf; }; +/** + * Verify whether a string represents a boolean literal in YAML. + * + * Non-standard: only subset of YAML 1.1 boolean literals are + * treated as boolean values. + * + * Return 0 at success, -1 at error. + */ +static inline int +yaml_parse_bool(const char *str, size_t len, bool *out) +{ + if ((len == 5 && memcmp(str, "false", 5) == 0) || + (len == 2 && memcmp(str, "no", 2) == 0)) { + if (out != NULL) + *out = false; + return 0; + } + if ((len == 4 && memcmp(str, "true", 4) == 0) || + (len == 3 && memcmp(str, "yes", 3) == 0)) { + if (out != NULL) + *out = true; + return 0; + } + return -1; +} + +/** + * Verify whether a string represents a null literal in YAML. + * + * Non-standard: don't match an empty string, 'Null' and 'NULL' as + * null. + * + * Return 0 at success, -1 at error. + */ +static inline int +yaml_parse_null(const char *str, size_t len) +{ + if (len == 1 && str[0] == '~') + return 0; + if (len == 4 && memcmp(str, "null", 4) == 0) + return 0; + return -1; +} + static void generate_error_message(struct lua_yaml_loader *loader) { char buf[256]; luaL_Buffer b; @@ -209,7 +253,11 @@ static void load_scalar(struct lua_yaml_loader *loader) { lua_pushnumber(loader->L, dval); return; } else if (!strcmp(tag, "bool")) { - lua_pushboolean(loader->L, !strcmp(str, "true") || !strcmp(str, "yes")); + bool value = false; + int rc = yaml_parse_bool(str, length, &value); + (void) rc; + assert(rc == 0); + lua_pushboolean(loader->L, value); return; } else if (!strcmp(tag, "binary")) { frombase64(loader->L, (const unsigned char *)str, length); @@ -218,20 +266,20 @@ static void load_scalar(struct lua_yaml_loader *loader) { } if (loader->event.data.scalar.style == YAML_PLAIN_SCALAR_STYLE) { - if (!strcmp(str, "~")) { - luaL_pushnull(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); + bool value; + if (!length) { + /* + * Non-standard: an empty value/document is null + * according to the standard, but we decode it as an + * empty string. + */ + lua_pushliteral(loader->L, ""); return; - } else if (!strcmp(str, "null")) { + } else if (yaml_parse_null(str, length) == 0) { luaL_pushnull(loader->L); return; - } else if (!length) { - lua_pushliteral(loader->L, ""); + } else if (yaml_parse_bool(str, length, &value) == 0) { + lua_pushboolean(loader->L, value); return; } @@ -548,7 +596,6 @@ static int yaml_is_flow_mode(struct lua_yaml_dumper *dumper) { (evp->type == YAML_MAPPING_START_EVENT && evp->data.mapping_start.style == YAML_FLOW_MAPPING_STYLE)) { return 1; - break; } } } @@ -597,8 +644,13 @@ static int dump_node(struct lua_yaml_dumper *dumper) return dump_table(dumper, &field); case MP_STR: str = lua_tolstring(dumper->L, -1, &len); - if (lua_isnumber(dumper->L, -1)) { - /* string is convertible to number, quote it to preserve type */ + if ((yaml_parse_null(str, len) == 0) + || (yaml_parse_bool(str, len, NULL) == 0) + || (lua_isnumber(dumper->L, -1))) { + /* + * The string is convertible to a null, a boolean or + * a number, quote it to preserve its type. + */ style = YAML_SINGLE_QUOTED_SCALAR_STYLE; break; } @@ -606,12 +658,10 @@ static int dump_node(struct lua_yaml_dumper *dumper) if (utf8_check_printable(str, len)) { if (yaml_is_flow_mode(dumper)) { style = YAML_SINGLE_QUOTED_SCALAR_STYLE; - } else if (strstr(str, "\n\n") != NULL || strcmp(str, "true") == 0 || - strcmp(str, "false") == 0) { + } else if (strstr(str, "\n\n") != 0) { /* * Tarantool-specific: use literal style for string - * with empty lines and strings representing boolean - * types. + * with empty lines. * Useful for tutorial(). */ style = YAML_LITERAL_SCALAR_STYLE; -- 2.20.1