Splux Linux

source-based Linux using SPS

SPS · log · files · refs · tags · releases · people · languages · GitHub · commit

1b76f98


pkstat: add --count

Date
Commit1b76f98cab1c386fc0bb1e4d51658aa3f61dd49e
GitHubmirror

Parents

Stat

 README.md                  |  3 +-
 bin/pkstat                 | 14 +++++++
 man/pkstat.1               |  7 ++++
 tests/test_pkstat_count.sh | 94 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 117 insertions(+), 1 deletion(-)

Diff

diff --git a/README.md b/README.md
index 13c762c..5b09b38 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,8 @@ SPS is a set of tools rather than one large front end:
 - `mkpkg` builds a binary package from a recipe.
 - `pkin` installs or upgrades one binary package.
 - `pkdel` removes one installed package.
-- `pkstat` reads the installed package database.
+- `pkstat` reads the installed package database (`pkstat --count` prints
+  the number of installed packages).
 - `pkcheck` checks installed files and database consistency.
 - `pkmark` changes whether an installed package is explicit or dependency-only.
 - `setup` is the SPS Linux (Splux) dialog(1) installer (Slackware/FreeBSD
diff --git a/bin/pkstat b/bin/pkstat
index 5f74a1e..9f0c781 100755
--- a/bin/pkstat
+++ b/bin/pkstat
@@ -24,6 +24,7 @@ usage: pkstat [PACKAGE]
        pkstat --explicit
        pkstat --orphans
        pkstat --size PACKAGE
+       pkstat --count
 EOF
 }
 
@@ -194,6 +195,19 @@ case $sps_command in
             }
         ' "$@" | LC_ALL=C sort
         ;;
+    --count)
+        [ "$#" -eq 1 ] || { sps_usage >&2; exit 2; }
+        sps_count=0
+        if [ -d "$SPS_DB/installed" ] && [ ! -L "$SPS_DB/installed" ]; then
+            for sps_record in "$SPS_DB/installed"/*; do
+                [ -d "$sps_record" ] || continue
+                [ ! -L "$sps_record" ] || continue
+                [ -f "$sps_record/meta" ] && [ ! -L "$sps_record/meta" ] || continue
+                sps_count=$((sps_count + 1))
+            done
+        fi
+        printf '%s\n' "$sps_count"
+        ;;
     --size)
         [ "$#" -eq 2 ] || { sps_usage >&2; exit 2; }
         sps_record_for "$2"
diff --git a/man/pkstat.1 b/man/pkstat.1
index 3c2968f..8ad21a5 100644
--- a/man/pkstat.1
+++ b/man/pkstat.1
@@ -9,6 +9,7 @@ pkstat --owns PATH
 pkstat --explicit
 pkstat --orphans
 pkstat --size PACKAGE
+pkstat --count
 .fi
 .SH DESCRIPTION
 .B pkstat
@@ -32,6 +33,12 @@ entire abandoned dependency chains are reported.
 .TP
 .BI --size " package"
 Print the current byte size of regular files in the package manifest.
+.TP
+.B --count
+Print the number of installed packages, one integer and a newline. Counts
+directories under the installed database that contain a
+.B meta
+file. Missing databases print 0.
 .SH EXIT STATUS
 Returns 0 on success, 2 for usage, 3 for missing package/owner, 8 for
 unreadable filesystem state, and 11 for malformed/unavailable database state.
diff --git a/tests/test_pkstat_count.sh b/tests/test_pkstat_count.sh
new file mode 100644
index 0000000..c0e08f0
--- /dev/null
+++ b/tests/test_pkstat_count.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+# pkstat --count: integer of installed records with a regular meta file.
+
+set -eu
+
+case $0 in */*) test_dir=${0%/*} ;; *) test_dir=. ;; esac
+project_dir=$(CDPATH= cd "$test_dir/.." 2>/dev/null && pwd) || exit 1
+
+fail()
+{
+	printf 'test_pkstat_count: %s\n' "$*" >&2
+	exit 1
+}
+
+run_sps()
+{
+	SPS_ROOT=$root \
+	SPS_DB=$db \
+	SPS_CACHE=$cache \
+	SPS_BUILD=$build \
+	SPS_CONFIG=/dev/null \
+	SPS_REPOS_CONFIG=/dev/null \
+	SPS_LIBDIR=$project_dir/lib \
+	"$@"
+}
+
+install_named()
+{
+	package_name=$1
+	stage=$tmp/stage-$package_name
+	rm -rf "$stage"
+	mkdir -p "$stage/.SPS" "$stage/usr/share/$package_name"
+	printf '%s\n' "$package_name" >"$stage/usr/share/$package_name/id"
+	{
+		printf 'format\t1\n'
+		printf 'name\t%s\n' "$package_name"
+		printf 'version\t1.0\n'
+		printf 'release\t1\n'
+		printf 'arch\tany\n'
+		printf 'description\tcount fixture %s\n' "$package_name"
+	} >"$stage/.SPS/meta"
+	: >"$stage/.SPS/files.unsorted"
+	(CDPATH= cd "$stage" && find . ! -name . ! -path './.SPS' ! -path './.SPS/*' -print) |
+		sed 's#^\./##' | while IFS= read -r package_entry; do
+			if [ -d "$stage/$package_entry" ] && [ ! -L "$stage/$package_entry" ]; then
+				printf '%s/\n' "$package_entry"
+			else
+				printf '%s\n' "$package_entry"
+			fi
+		done >"$stage/.SPS/files.unsorted"
+	LC_ALL=C sort "$stage/.SPS/files.unsorted" >"$stage/.SPS/files"
+	rm -f "$stage/.SPS/files.unsorted"
+	: >"$stage/.SPS/hashes"
+	printf 'sha256\t%s\t%s\n' \
+		"$(sha256sum "$stage/usr/share/$package_name/id" | awk '{ print $1 }')" \
+		"usr/share/$package_name/id" >>"$stage/.SPS/hashes"
+	tar -C "$stage" -cf "$tmp/$package_name.pkg.tar" .
+	run_sps "$project_dir/bin/pkin" --dependency "$tmp/$package_name.pkg.tar" \
+		>/dev/null
+}
+
+tmp=$(mktemp -d "${TMPDIR:-/tmp}/sps-test-pkstat-count.XXXXXX") || fail mktemp
+trap 'rm -rf "$tmp"' 0 HUP INT TERM
+root=$tmp/root
+db=$tmp/db
+cache=$tmp/cache
+build=$tmp/build
+mkdir -p "$root" "$cache" "$build"
+
+[ "$(run_sps "$project_dir/bin/pkstat" --count)" = 0 ] ||
+	fail 'empty database must print 0'
+
+install_named one
+[ "$(run_sps "$project_dir/bin/pkstat" --count)" = 1 ] ||
+	fail 'one installed package must print 1'
+
+install_named two
+install_named three
+[ "$(run_sps "$project_dir/bin/pkstat" --count)" = 3 ] ||
+	fail 'three installed packages must print 3'
+
+# Junk next to real records is not a package.
+printf 'not a record\n' >"$db/installed/readme.txt"
+mkdir -p "$db/installed/empty-dir"
+ln -s one "$db/installed/alias"
+ln -s /tmp "$db/installed/outside"
+[ "$(run_sps "$project_dir/bin/pkstat" --count)" = 3 ] ||
+	fail 'files, empty dirs, and symlinks must not count'
+
+run_sps "$project_dir/bin/pkstat" --count >/dev/null
+run_sps "$project_dir/bin/pkstat" --count | grep -qx '3' ||
+	fail 'count must be a single integer line'
+
+printf '%s\n' 'test_pkstat_count: ok'