From 5eeec8bd4d24882ea443c4441f696257b8cb68c4 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Wed, 23 May 2018 23:13:26 +1000 Subject: [PATCH] Make expansion of strings support quoting of % by doubling up --- src/command_manager.cc | 54 +++++++++++++++++++++++------------------- src/command_manager.hh | 1 + 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/command_manager.cc b/src/command_manager.cc index a4af3e62..dd904bf4 100644 --- a/src/command_manager.cc +++ b/src/command_manager.cc @@ -51,6 +51,11 @@ Codepoint Reader::operator*() const return utf8::codepoint(pos, str.end()); } +Codepoint Reader::peek_next() const +{ + return utf8::codepoint(utf8::next(pos, str.end()), str.end()); +} + Reader& Reader::operator++() { kak_assert(pos < str.end()); @@ -84,15 +89,14 @@ QuotedResult parse_quoted(Reader& reader, Codepoint delimiter) const Codepoint c = *reader; if (c == delimiter) { - str += reader.substr_from(beg); - ++reader; - if (reader and *reader == delimiter) + if (reader.peek_next() != delimiter) { - str += String{c}; - beg = reader.pos+1; - } - else + str += reader.substr_from(beg); + ++reader; return {str, true}; + } + str += (++reader).substr_from(beg); + beg = reader.pos+1; } ++reader; } @@ -343,7 +347,7 @@ Optional CommandParser::read_token(bool throw_on_unterminated) { if (c == '\\') { - auto next = utf8::codepoint(utf8::next(m_reader.pos, m_reader.str.end()), m_reader.str.end()); + auto next = m_reader.peek_next(); if (next == '%' or next == '\'' or next == '"') ++m_reader; } @@ -364,25 +368,21 @@ String expand_impl(StringView str, const Context& context, while (reader) { Codepoint c = *reader; - if (c == '\\') + if (c == '%') { - if (not (++reader)) - throw parse_error{"unterminated escape"}; - c = *reader; - if (c == '%' or c == '\\') + if (reader.peek_next() == '%') + { + res += (++reader).substr_from(beg); + beg = (++reader).pos; + } + else { res += reader.substr_from(beg); - res.back() = c; + res += postprocess(expand_token(parse_percent_token(reader, true), + context, shell_context)); beg = reader.pos; } } - else if (c == '%') - { - res += reader.substr_from(beg); - res += postprocess(expand_token(parse_percent_token(reader, true), - context, shell_context)); - beg = reader.pos; - } else ++reader; } @@ -693,7 +693,6 @@ UnitTest test_command_parsing{[] kak_assert(quoted.terminated == terminated); kak_assert(quoted.content == content); }; - check_quoted("'abc'", true, "abc"); check_quoted("'abc''def", false, "abc'def"); check_quoted("'abc''def'''", true, "abc'def'"); @@ -705,7 +704,6 @@ UnitTest test_command_parsing{[] kak_assert(quoted.terminated == terminated); kak_assert(quoted.content == content); }; - check_balanced("{abc}", '{', '}', true, "abc"); check_balanced("{abc{def}}", '{', '}', true, "abc{def}"); check_balanced("{{abc}{def}", '{', '}', false, "{abc}{def}"); @@ -716,11 +714,19 @@ UnitTest test_command_parsing{[] auto res = parse_unquoted(reader); kak_assert(res == content); }; - check_unquoted("abc def", "abc"); check_unquoted("abc; def", "abc"); check_unquoted("abc\\; def", "abc;"); check_unquoted("abc\\;\\ def", "abc; def"); + + { + CommandParser parser(R"(foo 'bar' "baz" qux)"); + kak_assert(parser.read_token(false)->content == "foo"); + kak_assert(parser.read_token(false)->content == "bar"); + kak_assert(parser.read_token(false)->content == "baz"); + kak_assert(parser.read_token(false)->content == "qux"); + kak_assert(not parser.read_token(false)); + } }}; } diff --git a/src/command_manager.hh b/src/command_manager.hh index b5b49dd9..3270b217 100644 --- a/src/command_manager.hh +++ b/src/command_manager.hh @@ -66,6 +66,7 @@ public: Reader(StringView s) : str{s}, pos{s.begin()}, line_start{s.begin()}, line{} {} Codepoint operator*() const; + Codepoint peek_next() const; Reader& operator++(); explicit operator bool() const { return pos < str.end(); }