diff --git a/git/config.py b/git/config.py index c71bb8ca4..620f7b103 100644 --- a/git/config.py +++ b/git/config.py @@ -245,8 +245,28 @@ def _read(self, fp, fpname): if pos != -1 and optval[pos-1].isspace(): optval = optval[:pos] optval = optval.strip() - if optval == '""': - optval = '' + + # Remove paired unescaped-quotes + unquoted_optval = '' + escaped = False + in_quote = False + for c in optval: + if not escaped and c == '"': + in_quote = not in_quote + else: + escaped = (c == '\\') and not escaped + unquoted_optval += c + + if in_quote: + if not e: + e = cp.ParsingError(fpname) + e.append(lineno, repr(line)) + + optval = unquoted_optval + + optval = optval.replace('\\\\', '\\') # Unescape backslashes + optval = optval.replace(r'\"', '"') # Unescape quotes + optname = self.optionxform(optname.rstrip()) cursect[optname] = optval else: @@ -303,7 +323,11 @@ def write_section(name, section_dict): fp.write("[%s]\n" % name) for (key, value) in section_dict.items(): if key != "__name__": - fp.write("\t%s = %s\n" % (key, str(value).replace('\n', '\n\t'))) + value = str(value) + value = value.replace('\\', '\\\\') # Escape backslashes + value = value.replace('"', r'\"') # Escape quotes + value = value.replace('\n', '\n\t') + fp.write("\t%s = %s\n" % (key, value)) # END if key is not __name__ # END section writing diff --git a/git/test/fixtures/git_config_values b/git/test/fixtures/git_config_values new file mode 100644 index 000000000..5ba039ef8 --- /dev/null +++ b/git/test/fixtures/git_config_values @@ -0,0 +1,4 @@ +[values] + backslash = some\\data + quote = this is a \"quoted value\" + quoted = "all" "your \"quotes\" a"re bel"ong to """"us" diff --git a/git/test/test_config.py b/git/test/test_config.py index d2e199e31..b37db290e 100644 --- a/git/test/test_config.py +++ b/git/test/test_config.py @@ -101,4 +101,28 @@ def test_base(self): # it raises if there is no default though self.failUnlessRaises(NoSectionError, r_config.get_value, "doesnt", "exist") - + def test_values(self): + file_obj = self._to_memcache(fixture_path("git_config_values")) + w_config = GitConfigParser(file_obj, read_only = False) + w_config.write() # enforce writing + orig_value = file_obj.getvalue() + + # Reading must unescape backslashes + backslash = w_config.get('values', 'backslash') + assert backslash == r'some\data' + + # Reading must unescape quotes + quote = w_config.get('values', 'quote') + assert quote == 'this is a "quoted value"' + + # Reading must remove surrounding quotes + quoted = w_config.get('values', 'quoted') + assert quoted == 'all your "quotes" are belong to us' + + # Writing must escape backslashes and quotes + w_config.set('values', 'backslash', backslash) + w_config.set('values', 'quote', quote) + w_config.write() # enforce writing + + # Contents shouldn't differ + assert file_obj.getvalue() == orig_value