[arch-projects] [namcap] [PATCH] Add handling for compression into namcap.py

meganomic meganomic at pm.me
Tue Dec 15 09:51:13 UTC 2020


Fixed some potential bugs and made it all around better. I'm working on upstreaming support in xtarfile for LZ4 compression so hopefully the special handling for that can be removed soon. I also fixed a bug in xtarfile that prevents .tar files from being opened. So once the new version hits the repos 'import tarfile' can be removed.

diff --git a/namcap b/namcap
index ea0bc94..019b077 100755
--- a/namcap
+++ b/namcap
@@ -1,39 +1,2 @@
 #!/bin/bash
-
-args=''
-tmp=$(mktemp -d --tmpdir namcap.XXXXXXXXXX)
-cleanup() {
-	rm -rf "${tmp}"
-}
-trap 'cleanup' 0
-
-for arg in "${@}"; do
-	if echo "${arg}" | grep -q -E "^.+\.pkg\.tar\..+$" && [ -f "${arg}" ]; then
-
-		extra_opts=''
-		case "${arg##*.}" in
-			gz|z|Z) cmd='gzip' ;;
-			bz2|bz) cmd='bzip2' ;;
-			xz)     cmd='xz' ;;
-			lzo)    cmd='lzop' ;;
-			lrz)    cmd='lrzip'
-				extra_opts="-q -o -" ;;
-			lz4)    cmd='lz4'
-				extra_opts="-q" ;;
-			lz)     cmd='lzip'
-				extra_opts="-q" ;;
-			zst)    cmd='zstd'
-				extra_opts="-q" ;;
-			*)      echo 'Unsupported compression'; exit 1;;
-		esac
-
-		tar="${tmp}/$(basename "${arg%.*}")"
-		$cmd -dcf $extra_opts "${arg}" > "${tar}"
-
-		args="${args} ${tar}"
-	else
-		args="${args} ${arg}"
-	fi
-done
-
-/usr/bin/env python3 -m namcap ${args}
+/usr/bin/env python3 -m namcap ${@}
diff --git a/namcap.py b/namcap.py
index a7f532a..78662db 100755
--- a/namcap.py
+++ b/namcap.py
@@ -22,7 +22,11 @@
 import getopt
 import os
 import sys
+import subprocess
+import tempfile
+import pathlib
 import tarfile
+import xtarfile

 import Namcap.depends
 import Namcap.tags
@@ -49,18 +53,6 @@ def usage():

 	sys.exit(2)

-def open_package(filename):
-	try:
-		tar = tarfile.open(filename, "r")
-		if '.PKGINFO' not in tar.getnames():
-			tar.close()
-			return None
-	except IOError:
-		if tar:
-			tar.close()
-		return None
-	return tar
-
 def check_rules_exclude(optlist):
 	'''Check if the -r (--rules) and the -r (--exclude) options
 	are being used at same time'''
@@ -76,13 +68,10 @@ def show_messages(name, key, messages):
 	for msg in messages:
 		print("%s %s: %s" % (name, key, Namcap.tags.format_message(msg)))

-def process_realpackage(package, modules):
+def process_realpackage(pkgtar, package, modules):
 	"""Runs namcap checks over a package tarball"""
-	extracted = 0
-	pkgtar = open_package(package)
-
-	if not pkgtar:
-		print("Error: %s is empty or is not a valid package" % package)
+	if '.PKGINFO' not in pkgtar.getnames():
+		raise TypeError('Error: %s is not a valid package' % package)
 		return 1

 	pkginfo = Namcap.package.load_from_tarball(package)
@@ -98,7 +87,7 @@ def process_realpackage(package, modules):
 			rule.analyze(pkginfo, pkgtar)
 		else:
 			show_messages(pkginfo["name"], 'E',
-			              [('error-running-rule %s', i)])
+										[('error-running-rule %s', i)])

 		# Output the three types of messages
 		show_messages(pkginfo["name"], 'E', rule.errors)
@@ -237,15 +226,71 @@ if len(active_modules) == 0:

 # Go through each package, get the info, and apply the rules
 for package in packages:
-	if not os.access(package, os.R_OK):
+	pkgpath = pathlib.Path(package)
+
+	if not pkgpath.is_file():
 		print("Error: Problem reading %s" % package)
 		usage()
-
-	if os.path.isfile(package) and tarfile.is_tarfile(package):
-		process_realpackage(package, active_modules)
-	elif 'PKGBUILD' in package:
+		sys.exit(1)
+
+	if pkgpath.with_suffix('').suffix == '.tar' or pkgpath.suffix == '.tar':
+		try: # Try using xtarfile first
+			with xtarfile.open(package, "r") as pkgtar:
+				process_realpackage(pkgtar, package, active_modules)
+
+		# If xtarfile can't handle the compression do it with external software
+		except NotImplementedError:
+			if pkgpath.suffix == '.lzo':
+				cmd = ['lzop', '-cdqf', pkgpath.as_posix()]
+			elif pkgpath.suffix == '.lrz':
+				cmd = ['lrzip', '-dqfo', '-', pkgpath.as_posix()]
+			elif pkgpath.suffix == '.lz4':
+				cmd = ['lz4', '-cdqf', pkgpath.as_posix()]
+			elif pkgpath.suffix == '.lz':
+				cmd = ['lzip', '-cdqf', pkgpath.as_posix()]
+			else:
+				print("Unsupported compression format:", package)
+				sys.exit(1)
+
+			# Run decompression and put the .tar file in a temporary directory
+			try:
+				tmpdir = tempfile.TemporaryDirectory(prefix='namcap.')
+			except Exception as err:
+				print("Unable to create temporary storage:", err)
+				sys.exit(1)
+
+			tmpfilepath = pathlib.Path(tmpdir.name).joinpath(pkgpath.with_suffix('').name)
+
+			try:
+				with open(tmpfilepath, 'wb') as outfile:
+					p = subprocess.run(cmd, stdout=outfile)
+					p.check_returncode() # Make sure it actually ran without errors
+			except FileNotFoundError as err:
+				print(err, "\nThe required compression software %s wasn't found." % cmd[0], "\nInstall with pacman -S", cmd[0])
+				sys.exit(1)
+			except Exception as err:
+				print("Unable to decompress:", err)
+				sys.exit(1)
+
+			try:
+				with tarfile.open(tmpfilepath) as pkgtar:
+					process_realpackage(pkgtar, package, active_modules)
+			except Exception as err:
+				print("File doesn't exist or isn't a tar file:", package, err)
+				sys.exit(1)
+
+		except TypeError as err: # The tar file doesn't contain a .PKGINFO file
+			print(err)
+			sys.exit(1)
+		except Exception as err:
+			print("Something went horrible wrong:", err)
+			sys.exit(1)
+
+	elif pkgpath.name == 'PKGBUILD':
 		process_pkgbuild(package, active_modules)
+
 	else:
 		print("Error: %s not package or PKGBUILD" % package)
+		sys.exit(1)

 # vim: set ts=4 sw=4 noet:


More information about the arch-projects mailing list