Skip to content

Commit c4c411d

Browse files
Fix escaping of options (#476)
Fix escaping of options At first I thought it will be necessary to introduce a new token type and rewrite the code so that option values and positional arguments can consist of multiple tokens, but fortunately this simple fix works as well. Fixes #475. RELEASE NOTES BEGIN We have fixed a bug that caused specfile to traceback when section names with conditional macro expansions containing spaces were present in the spec file. RELEASE NOTES END Reviewed-by: Matej Focko
2 parents 5ff82c8 + 3dd104b commit c4c411d

File tree

2 files changed

+120
-2
lines changed

2 files changed

+120
-2
lines changed

specfile/options.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@ def __str__(self) -> str:
4747
# escape double quotes
4848
value = self.value.replace('"', r"\"")
4949
return f'"{value}"'
50-
# escape quotes and whitespace
51-
return re.sub(r"['\"\s]", r"\\\g<0>", self.value)
50+
# escape quotes and whitespace only in string literals
51+
value = ""
52+
for node in ValueParser.parse(self.value):
53+
if isinstance(node, StringLiteral):
54+
value += re.sub(r"['\"\s]", r"\\\g<0>", str(node))
55+
else:
56+
value += str(node)
57+
return value
5258

5359
def _key(self) -> tuple:
5460
return self.type, self.value

tests/unit/test_options.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,119 @@ def test_options_find_option(optstring, tokens, option, result):
288288
Token(TokenType.QUOTED, '%{name}-%{version}%[%{rc}?"-rc":""]'),
289289
],
290290
),
291+
(
292+
"-q %{?prever:-n %{name}-%{prever}}",
293+
[
294+
Token(TokenType.DEFAULT, "-q"),
295+
Token(TokenType.WHITESPACE, " "),
296+
Token(TokenType.DEFAULT, "%{?prever:-n %{name}-%{prever}}"),
297+
],
298+
),
291299
],
292300
)
293301
def test_options_tokenize(option_string, result):
294302
assert Options.tokenize(option_string) == result
303+
304+
305+
@pytest.mark.parametrize(
306+
"tokens, result",
307+
[
308+
(
309+
[
310+
Token(TokenType.DEFAULT, "-p1"),
311+
Token(TokenType.WHITESPACE, " "),
312+
Token(TokenType.DEFAULT, "-b"),
313+
Token(TokenType.WHITESPACE, " "),
314+
Token(TokenType.DEFAULT, ".test"),
315+
Token(TokenType.WHITESPACE, " "),
316+
Token(TokenType.DEFAULT, "-E"),
317+
],
318+
"-p1 -b .test -E",
319+
),
320+
(
321+
[
322+
Token(TokenType.DEFAULT, "-p"),
323+
Token(TokenType.WHITESPACE, " "),
324+
Token(TokenType.DEFAULT, "28"),
325+
Token(TokenType.WHITESPACE, " "),
326+
Token(TokenType.DEFAULT, "-b"),
327+
Token(TokenType.WHITESPACE, " "),
328+
Token(TokenType.DEFAULT, ".test escape"),
329+
],
330+
"-p 28 -b .test\\ escape",
331+
),
332+
(
333+
[
334+
Token(TokenType.DEFAULT, "-b"),
335+
Token(TokenType.WHITESPACE, " "),
336+
Token(TokenType.DOUBLE_QUOTED, '.test "double quotes"'),
337+
],
338+
'-b ".test \\"double quotes\\""',
339+
),
340+
(
341+
[
342+
Token(TokenType.DEFAULT, "-p1"),
343+
Token(TokenType.WHITESPACE, " "),
344+
Token(TokenType.DEFAULT, "-b"),
345+
Token(TokenType.WHITESPACE, " "),
346+
Token(TokenType.DEFAULT, ".test_whitespace_at_the_end"),
347+
Token(TokenType.WHITESPACE, " "),
348+
Token(TokenType.DEFAULT, "-M"),
349+
Token(TokenType.WHITESPACE, " "),
350+
Token(TokenType.DEFAULT, "2"),
351+
Token(TokenType.WHITESPACE, " "),
352+
],
353+
"-p1 -b .test_whitespace_at_the_end -M 2 ",
354+
),
355+
(
356+
[
357+
Token(TokenType.DEFAULT, "-q"),
358+
Token(TokenType.WHITESPACE, " "),
359+
Token(TokenType.DEFAULT, "-n"),
360+
Token(TokenType.WHITESPACE, " "),
361+
Token(TokenType.DEFAULT, '%{name}-%{version}%[%{rc}?"-rc":""]'),
362+
],
363+
'-q -n %{name}-%{version}%[%{rc}?"-rc":""]',
364+
),
365+
(
366+
[
367+
Token(TokenType.DEFAULT, "-q"),
368+
Token(TokenType.WHITESPACE, " "),
369+
Token(TokenType.DEFAULT, "-n"),
370+
Token(TokenType.WHITESPACE, " "),
371+
Token(TokenType.QUOTED, "%{name}-%{version}"),
372+
],
373+
"-q -n '%{name}-%{version}'",
374+
),
375+
(
376+
[
377+
Token(TokenType.DEFAULT, "-q"),
378+
Token(TokenType.WHITESPACE, " "),
379+
Token(TokenType.DEFAULT, "-n"),
380+
Token(TokenType.WHITESPACE, " "),
381+
Token(TokenType.DOUBLE_QUOTED, "%{name}-%{version}"),
382+
],
383+
'-q -n "%{name}-%{version}"',
384+
),
385+
(
386+
[
387+
Token(TokenType.DEFAULT, "-q"),
388+
Token(TokenType.WHITESPACE, " "),
389+
Token(TokenType.DEFAULT, "-n"),
390+
Token(TokenType.WHITESPACE, " "),
391+
Token(TokenType.QUOTED, '%{name}-%{version}%[%{rc}?"-rc":""]'),
392+
],
393+
'-q -n \'%{name}-%{version}%[%{rc}?"-rc":""]\'',
394+
),
395+
(
396+
[
397+
Token(TokenType.DEFAULT, "-q"),
398+
Token(TokenType.WHITESPACE, " "),
399+
Token(TokenType.DEFAULT, "%{?prever:-n %{name}-%{prever}}"),
400+
],
401+
"-q %{?prever:-n %{name}-%{prever}}",
402+
),
403+
],
404+
)
405+
def test_options_stringify(tokens, result):
406+
assert str(Options(tokens)) == result

0 commit comments

Comments
 (0)