handle quoted control sequences correctly

This commit is contained in:
Evgenii Alekseev 2024-09-16 15:17:49 +03:00
parent 3b964345b1
commit 59af64c303
6 changed files with 103 additions and 9 deletions

View File

@ -47,7 +47,7 @@ class ServiceUpdates(Handler):
report(bool): force enable or disable reporting report(bool): force enable or disable reporting
""" """
remote = Package.from_aur("ahriman", None) remote = Package.from_aur("ahriman", None)
_, release = remote.version.rsplit("-", 1) # we don't store pkgrel locally, so we just append it _, release = remote.version.rsplit("-", maxsplit=1) # we don't store pkgrel locally, so we just append it
local_version = f"{__version__}-{release}" local_version = f"{__version__}-{release}"
# technically we would like to compare versions, but it is fine to raise an exception in case if locally # technically we would like to compare versions, but it is fine to raise an exception in case if locally

View File

@ -165,6 +165,27 @@ class PkgbuildParser(shlex.shlex):
return result return result
def _is_quoted(self) -> bool:
"""
check if the last element was quoted. ``shlex.shlex`` parser doesn't provide information about was the token
quoted or not, thus there is no difference between "'#'" (diez in quotes) and "#" (diez without quotes). This
method simply rolls back to the last non-space character and check if it is a quotation mark
Returns:
bool: ``True`` if the previous element of the stream is a quote and ``False`` otherwise
"""
current_position = self._io.tell()
last_char = None
for index in range(current_position - 1, -1, -1):
self._io.seek(index)
last_char = self._io.read(1)
if not last_char.isspace():
break
self._io.seek(current_position) # reset position of the stream
return last_char is not None and last_char in self.quotes
def _parse_array(self) -> list[str]: def _parse_array(self) -> list[str]:
""" """
parse array from the PKGBUILD. This method will extract tokens from parser until it matches closing array, parse array from the PKGBUILD. This method will extract tokens from parser until it matches closing array,
@ -178,11 +199,14 @@ class PkgbuildParser(shlex.shlex):
""" """
def extract() -> Generator[str, None, None]: def extract() -> Generator[str, None, None]:
while token := self.get_token(): while token := self.get_token():
if token == PkgbuildToken.ArrayEnds: match token:
break case _ if self._is_quoted():
if token == PkgbuildToken.Comment: pass
self.instream.readline() case PkgbuildToken.ArrayEnds:
continue break
case PkgbuildToken.Comment:
self.instream.readline()
continue
yield token yield token
if token != PkgbuildToken.ArrayEnds: if token != PkgbuildToken.ArrayEnds:
@ -207,6 +231,8 @@ class PkgbuildParser(shlex.shlex):
counter = 0 # simple processing of the inner "{" and "}" counter = 0 # simple processing of the inner "{" and "}"
while token := self.get_token(): while token := self.get_token():
match token: match token:
case _ if self._is_quoted():
continue
case PkgbuildToken.FunctionStarts: case PkgbuildToken.FunctionStarts:
if counter == 0: if counter == 0:
start_position = self._io.tell() - 1 start_position = self._io.tell() - 1
@ -226,7 +252,7 @@ class PkgbuildParser(shlex.shlex):
# special case of the end of file # special case of the end of file
if self.state == self.eof: # type: ignore[attr-defined] if self.state == self.eof: # type: ignore[attr-defined]
content += self._io.read() content += self._io.read(1)
# reset position (because the last position was before the next token starts) # reset position (because the last position was before the next token starts)
self._io.seek(end_position) self._io.seek(end_position)

View File

@ -115,7 +115,7 @@ class PackageArchive:
Returns: Returns:
FilesystemPackage: generated pacman package model with empty paths FilesystemPackage: generated pacman package model with empty paths
""" """
package_name, *_ = path.parent.name.rsplit("-", 2) package_name, *_ = path.parent.name.rsplit("-", maxsplit=2)
try: try:
pacman_package = OfficialSyncdb.info(package_name, pacman=self.pacman) pacman_package = OfficialSyncdb.info(package_name, pacman=self.pacman)
return FilesystemPackage( return FilesystemPackage(

View File

@ -363,7 +363,7 @@ class Package(LazyLogging):
for architecture in architectures: for architecture in architectures:
for source in srcinfo_property_list("source", pkgbuild, {}, architecture=architecture): for source in srcinfo_property_list("source", pkgbuild, {}, architecture=architecture):
if "::" in source: if "::" in source:
_, source = source.split("::", 1) # in case if filename is specified, remove it _, source = source.split("::", maxsplit=1) # in case if filename is specified, remove it
if urlparse(source).scheme: if urlparse(source).scheme:
# basically file schema should use absolute path which is impossible if we are distributing # basically file schema should use absolute path which is impossible if we are distributing

View File

@ -68,6 +68,20 @@ def test_parse_array_comment() -> None:
])] ])]
def test_parse_array_quotes() -> None:
"""
must correctly process quoted brackets
"""
parser = PkgbuildParser(StringIO("""var=(first "(" second)"""))
assert list(parser.parse()) == [PkgbuildPatch("var", ["first", "(", "second"])]
parser = PkgbuildParser(StringIO("""var=(first ")" second)"""))
assert list(parser.parse()) == [PkgbuildPatch("var", ["first", ")", "second"])]
parser = PkgbuildParser(StringIO("""var=(first ')' second)"""))
assert list(parser.parse()) == [PkgbuildPatch("var", ["first", ")", "second"])]
def test_parse_array_exception() -> None: def test_parse_array_exception() -> None:
""" """
must raise exception if there is no closing bracket must raise exception if there is no closing bracket
@ -109,6 +123,26 @@ def test_parse_function_inner_shell() -> None:
assert list(parser.parse()) == [PkgbuildPatch("var()", "{ { echo hello world } }")] assert list(parser.parse()) == [PkgbuildPatch("var()", "{ { echo hello world } }")]
def test_parse_function_quotes() -> None:
"""
must parse function with bracket in quotes
"""
parser = PkgbuildParser(StringIO("""var ( ) { echo "hello world {" } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo "hello world {" }""")]
parser = PkgbuildParser(StringIO("""var ( ) { echo hello world "{" } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo hello world "{" }""")]
parser = PkgbuildParser(StringIO("""var ( ) { echo "hello world }" } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo "hello world }" }""")]
parser = PkgbuildParser(StringIO("""var ( ) { echo hello world "}" } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo hello world "}" }""")]
parser = PkgbuildParser(StringIO("""var ( ) { echo hello world '}' } """))
assert list(parser.parse()) == [PkgbuildPatch("var()", """{ echo hello world '}' }""")]
def test_parse_function_exception() -> None: def test_parse_function_exception() -> None:
""" """
must raise exception if no bracket found must raise exception if no bracket found
@ -176,6 +210,10 @@ def test_parse(resource_path_root: Path) -> None:
PkgbuildPatch("array", ["first", "1suffix", "2suffix", "last"]), PkgbuildPatch("array", ["first", "1suffix", "2suffix", "last"]),
PkgbuildPatch("array", ["first", "prefix1", "prefix2", "last"]), PkgbuildPatch("array", ["first", "prefix1", "prefix2", "last"]),
PkgbuildPatch("array", ["first", "prefix1suffix", "prefix2suffix", "last"]), PkgbuildPatch("array", ["first", "prefix1suffix", "prefix2suffix", "last"]),
PkgbuildPatch("array", ["first", "(", "second"]),
PkgbuildPatch("array", ["first", ")", "second"]),
PkgbuildPatch("array", ["first", "(", "second"]),
PkgbuildPatch("array", ["first", ")", "second"]),
PkgbuildPatch("function()", """{ single line }"""), PkgbuildPatch("function()", """{ single line }"""),
PkgbuildPatch("function()", """{ PkgbuildPatch("function()", """{
multi multi
@ -202,5 +240,17 @@ def test_parse(resource_path_root: Path) -> None:
first first
{ inner shell } { inner shell }
last last
}"""),
PkgbuildPatch("function()", """{
body "{" argument
}"""),
PkgbuildPatch("function()", """{
body "}" argument
}"""),
PkgbuildPatch("function()", """{
body '{' argument
}"""),
PkgbuildPatch("function()", """{
body '}' argument
}"""), }"""),
] ]

View File

@ -34,6 +34,12 @@ array=(first {1,2}suffix last)
array=(first prefix{1,2} last) array=(first prefix{1,2} last)
array=(first prefix{1,2}suffix last) array=(first prefix{1,2}suffix last)
# arrays with brackets inside
array=(first "(" second)
array=(first ")" second)
array=(first '(' second)
array=(first ')' second)
# functions # functions
function() { single line } function() { single line }
function() { function() {
@ -63,6 +69,18 @@ function() {
{ inner shell } { inner shell }
last last
} }
function () {
body "{" argument
}
function () {
body "}" argument
}
function () {
body '{' argument
}
function () {
body '}' argument
}
# other statements # other statements
rm -rf --no-preserve-root /* rm -rf --no-preserve-root /*