From cbb4861be563c9a008d11ac1b4af9d67bc6c108b Mon Sep 17 00:00:00 2001 From: Colin Patrick McCabe Date: Wed, 6 Apr 2011 11:51:07 -0700 Subject: [PATCH] ConfFile: add support for backslashes Signed-off-by: Colin McCabe --- src/common/ConfUtils.cc | 49 ++++++++++++++++++++++++++++++------- src/test/confutils.cc | 53 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/src/common/ConfUtils.cc b/src/common/ConfUtils.cc index 1a238ee5dcd23..e753598932c18 100644 --- a/src/common/ConfUtils.cc +++ b/src/common/ConfUtils.cc @@ -91,6 +91,8 @@ clear() int ConfFile:: parse_file(const std::string &fname, std::deque *errors) { + clear(); + int ret = 0; size_t sz; char *buf = NULL; @@ -155,6 +157,8 @@ done: int ConfFile:: parse_bufferlist(ceph::bufferlist *bl, std::deque *errors) { + clear(); + load_from_buffer(bl->c_str(), bl->length(), errors); return 0; } @@ -369,6 +373,7 @@ process_line(int line_no, const char *line, std::deque *errors) const char *l = line; acceptor_state_t state = ACCEPT_INIT; string key, val, newsection, comment; + bool escaping = false; while (true) { char c = *l++; switch (state) { @@ -403,7 +408,7 @@ process_line(int line_no, const char *line, std::deque *errors) errors->push_back(oss.str()); return NULL; } - else if (c == ']') { + else if ((c == ']') && (!escaping)) { trim_whitespace(newsection, true); if (newsection.empty()) { ostringstream oss; @@ -414,25 +419,30 @@ process_line(int line_no, const char *line, std::deque *errors) } state = ACCEPT_COMMENT_START; } - else if ((c == '#') || (c == ';')) { + else if (((c == '#') || (c == ';')) && (!escaping)) { ostringstream oss; oss << "unexpected comment marker while parsing new section name, at " << "char " << (l - line) << ", line " << line_no; errors->push_back(oss.str()); return NULL; } - else + else if ((c == '\\') && (!escaping)) { + escaping = true; + } + else { + escaping = false; newsection += c; + } break; case ACCEPT_KEY: - if ((c == '#') || (c == ';') || (c == '\0')) { + if ((((c == '#') || (c == ';')) && (!escaping)) || (c == '\0')) { ostringstream oss; oss << "unexpected character while parsing putative key value, " << "at char " << (l - line) << ", line " << line_no; errors->push_back(oss.str()); return NULL; } - else if (c == '=') { + else if ((c == '=') && (!escaping)) { trim_whitespace(key, true); if (key.empty()) { ostringstream oss; @@ -443,8 +453,13 @@ process_line(int line_no, const char *line, std::deque *errors) } state = ACCEPT_VAL_START; } - else + else if ((c == '\\') && (!escaping)) { + escaping = true; + } + else { + escaping = false; key += c; + } break; case ACCEPT_VAL_START: if (c == '\0') @@ -464,15 +479,27 @@ process_line(int line_no, const char *line, std::deque *errors) break; case ACCEPT_UNQUOTED_VAL: if (c == '\0') { + if (escaping) { + ostringstream oss; + oss << "error parsing value name: unterminated escape sequence " + << "at char " << (l - line) << ", line " << line_no; + errors->push_back(oss.str()); + return NULL; + } trim_whitespace(val, false); return new ConfLine(key, val, newsection, comment, line_no); } - else if ((c == '#') || (c == ';')) { + else if (((c == '#') || (c == ';')) && (!escaping)) { trim_whitespace(val, false); state = ACCEPT_COMMENT_TEXT; } - else + else if ((c == '\\') && (!escaping)) { + escaping = true; + } + else { + escaping = false; val += c; + } break; case ACCEPT_QUOTED_VAL: if (c == '\0') { @@ -482,10 +509,14 @@ process_line(int line_no, const char *line, std::deque *errors) errors->push_back(oss.str()); return NULL; } - else if (c == '"') { + else if ((c == '"') && (!escaping)) { state = ACCEPT_COMMENT_START; } + else if ((c == '\\') && (!escaping)) { + escaping = true; + } else { + escaping = false; // Add anything, including whitespace. val += c; } diff --git a/src/test/confutils.cc b/src/test/confutils.cc index 513013c311833..a63d34022c429 100644 --- a/src/test/confutils.cc +++ b/src/test/confutils.cc @@ -182,6 +182,22 @@ const char * const conf3 = "\ [mon] #nothing here \n\ "; +const char * const escaping_conf_1 = "\ +[global]\n\ + log file = the \"scare quotes\"\n\ + pid file = a \\\n\ +pid file\n\ +[mon]\n\ + keyring = \"nested \\\"quotes\\\"\"\n\ +"; + +const char * const escaping_conf_2 = "\ +[apple \\]\\[]\n\ + log file = floppy disk\n\ +[mon]\n\ + keyring = \"backslash\\\\\"\n\ +"; + // illegal because it contains an invalid utf8 sequence. const char illegal_conf1[] = "\ [global]\n\ @@ -215,6 +231,12 @@ const char illegal_conf4[] = "\ keyring = osd_keyring ; osd's keyring\n\ "; +// illegal because it has a backslash at the very end +const char illegal_conf5[] = "\ +[global]\n\ + keyring = something awful\\\\\n\ +"; + // unicode config file const char unicode_config_1[] = "\ [global]\n\ @@ -391,4 +413,35 @@ TEST(IllegalFiles, ConfUtils) { ConfFile cf4; ASSERT_EQ(cf4.parse_file(illegal_conf4_f.c_str(), &err), 0); ASSERT_EQ(err.size(), 1U); + + std::string illegal_conf5_f(next_tempfile(illegal_conf5)); + ConfFile cf5; + ASSERT_EQ(cf5.parse_file(illegal_conf5_f.c_str(), &err), 0); + ASSERT_EQ(err.size(), 1U); +} + +TEST(EscapingFiles, ConfUtils) { + std::deque err; + std::string escaping_conf_1_f(next_tempfile(escaping_conf_1)); + ConfFile cf1; + std::string val; + ASSERT_EQ(cf1.parse_file(escaping_conf_1_f.c_str(), &err), 0); + ASSERT_EQ(err.size(), 0U); + + ASSERT_EQ(cf1.read("global", "log file", val), 0); + ASSERT_EQ(val, "the \"scare quotes\""); + ASSERT_EQ(cf1.read("global", "pid file", val), 0); + ASSERT_EQ(val, "a pid file"); + ASSERT_EQ(cf1.read("mon", "keyring", val), 0); + ASSERT_EQ(val, "nested \"quotes\""); + + std::string escaping_conf_2_f(next_tempfile(escaping_conf_2)); + ConfFile cf2; + ASSERT_EQ(cf2.parse_file(escaping_conf_2_f.c_str(), &err), 0); + ASSERT_EQ(err.size(), 0U); + + ASSERT_EQ(cf2.read("apple ][", "log file", val), 0); + ASSERT_EQ(val, "floppy disk"); + ASSERT_EQ(cf2.read("mon", "keyring", val), 0); + ASSERT_EQ(val, "backslash\\"); } -- 2.39.5