From: Alexander Turenko <alexander.turenko@tarantool.org> To: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Cc: Alexander Turenko <alexander.turenko@tarantool.org>, tarantool-patches@freelists.org Subject: [tarantool-patches] [PATCH v3 2/2] lua-yaml: fix strings literals encoding in yaml Date: Tue, 5 Feb 2019 06:30:17 +0300 [thread overview] Message-ID: <9c2e78597b5ab1cd74c28eeb36d2adcebe3f09c5.1549336851.git.alexander.turenko@tarantool.org> (raw) In-Reply-To: <cover.1549336851.git.alexander.turenko@tarantool.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
prev parent reply other threads:[~2019-02-05 3:30 UTC|newest] Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-02-05 3:30 [tarantool-patches] [PATCH v3 0/2] lua-yaml null/boolean fixes Alexander Turenko 2019-02-05 3:30 ` [tarantool-patches] [PATCH v3 1/2] lua-yaml: verify arguments count Alexander Turenko 2019-02-05 3:30 ` Alexander Turenko [this message]
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=9c2e78597b5ab1cd74c28eeb36d2adcebe3f09c5.1549336851.git.alexander.turenko@tarantool.org \ --to=alexander.turenko@tarantool.org \ --cc=tarantool-patches@freelists.org \ --cc=v.shpilevoy@tarantool.org \ --subject='Re: [tarantool-patches] [PATCH v3 2/2] lua-yaml: fix strings literals encoding in yaml' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox