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
"""
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}"
# 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
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]:
"""
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]:
while token := self.get_token():
if token == PkgbuildToken.ArrayEnds:
break
if token == PkgbuildToken.Comment:
self.instream.readline()
continue
match token:
case _ if self._is_quoted():
pass
case PkgbuildToken.ArrayEnds:
break
case PkgbuildToken.Comment:
self.instream.readline()
continue
yield token
if token != PkgbuildToken.ArrayEnds:
@ -207,6 +231,8 @@ class PkgbuildParser(shlex.shlex):
counter = 0 # simple processing of the inner "{" and "}"
while token := self.get_token():
match token:
case _ if self._is_quoted():
continue
case PkgbuildToken.FunctionStarts:
if counter == 0:
start_position = self._io.tell() - 1
@ -226,7 +252,7 @@ class PkgbuildParser(shlex.shlex):
# special case of the end of file
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)
self._io.seek(end_position)

View File

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

View File

@ -363,7 +363,7 @@ class Package(LazyLogging):
for architecture in architectures:
for source in srcinfo_property_list("source", pkgbuild, {}, architecture=architecture):
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:
# 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:
"""
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 } }")]
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:
"""
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", "prefix1", "prefix2", "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()", """{
multi
@ -202,5 +240,17 @@ def test_parse(resource_path_root: Path) -> None:
first
{ inner shell }
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}suffix last)
# arrays with brackets inside
array=(first "(" second)
array=(first ")" second)
array=(first '(' second)
array=(first ')' second)
# functions
function() { single line }
function() {
@ -63,6 +69,18 @@ function() {
{ inner shell }
last
}
function () {
body "{" argument
}
function () {
body "}" argument
}
function () {
body '{' argument
}
function () {
body '}' argument
}
# other statements
rm -rf --no-preserve-root /*