#!/bin/sh

# sbopkglint test, must be sourced by sbopkglint (not run standalone).

# PKG, PRGNAM, VERSION, ARCH are set by sbopkglint. also the current
# directory is the root of the installed package tree.

#######################################################################
# these directories are allowed to exist in the package, but they
# must be mode 0755 and owned by root:root. if a dir from this list
# exists but is empty, that's an error. if a top-level directory
# exists that's *not* in this list (such as /dev), that's an error.
topleveldirs="bin boot etc lib lib64 opt sbin srv usr var run"

# these directories are *required* to exist, and must be mode 0755, root:root.
# if a dir from this list exists but is empty, that's an error. note
# that the install/ dir no longer exists by the time we run (installpkg
# deleted it already).
requireddirs="usr/doc/$PRGNAM-$VERSION"

# these directories *must not* exist. no need to list top-level dirs here,
# the topleveldirs check already catches those.
baddirs="usr/local usr/share/doc usr/share/man usr/etc usr/share/info usr/X11 usr/X11R6"

# these directories may only contain files with +x permissions. in
# other words, no non-executable files may live here. note that
# bindirs is a subset of fileonlydirs.
bindirs="bin usr/bin sbin usr/sbin"

# these directories may exist, but must contain only files or symlinks,
# and must be mode 0755, root:root. I thought usr/share/pixmaps
# belonged here, but quite a few packages create subdirs there for
# images required at runtime that aren't the app icon.
fileonlydirs="$bindirs"

# these directories may exist, but must contain only subdirectories
# (no files, symlinks, devices, etc). "." (the top-level package dir)
# doesn't need to be included here; it's checked separately.
nofiledirs="usr usr/doc usr/share usr/man usr/doc/HTML"

# these directories may exist but must not have executable files
# anywhere under them. I would put usr/doc and etc here, but too many
# packages break that rule. usr/share/applications is listed here,
# even though Slackware's KDE packages (erroneously) install .desktop
# files +x.
noexecdirs="usr/include usr/man usr/share/pixmaps usr/share/icons usr/share/applications usr/share/appdata usr/share/mime usr/share/mime-info usr/share/glib-2.0 usr/doc/HTML"

# these files must exist, unless -s option given.
requiredfiles="usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild"

# these files must not exist.
badfiles="\
usr/info/dir \
usr/info/dir.gz \
usr/lib64/perl5/perllocal.pod \
usr/lib/perl5/perllocal.pod \
usr/share/perl5/perllocal.pod \
usr/share/perl5/vendor_perl/perllocal.pod \
etc/passwd \
etc/passwd.new \
etc/shadow \
etc/shadow.new \
etc/group \
etc/group.new \
etc/gshadow \
etc/gshadow.new \
etc/ld.so.conf"

#######################################################################

# include 'hidden' files/dirs in * wildcard expansion.
shopt -s dotglob

dir_ok() {
	[ -d "$1" ] && \
		[ "$( stat -c '%A %U %G' "$1" )" = "drwxr-xr-x root root" ]
}

dir_empty() {
	[ "$( find "$1" -mindepth 1 -maxdepth 1 )" = "" ]
}

warn_badperms() {
	warn "bad permissions/owner (should be 0755 root:root): $1"
}

for i in *; do
	if [ ! -d "$i" ]; then
		warn "package root dir contains non-directory: $i"
	elif ! echo "$topleveldirs" | grep -q "\\<$i\\>"; then
		warn "package root dir contains non-standard directory: $i"
	elif ! dir_ok "$i"; then
		warn_badperms "$i"
	elif dir_empty "$i"; then
		warn "package contains empty top-level directory: $i"
	fi
done

for i in $requireddirs; do
	if [ ! -d "$i" ]; then
		warn "missing required directory: $i"
	elif ! dir_ok "$i"; then
		warn_badperms "$i"
	fi
done

for i in $baddirs; do
	if [ -d "$i" ]; then
		warn "forbidden directory exists: $i"
	elif [ -e "$i" ]; then
		warn "forbidden directory exists as a non-directory: $i"
	fi
done

# 20220414 bkw: don't complain about broken symlinks here, they get checked elsewhere.
for i in $fileonlydirs; do
	[ -d "$i" ] || continue
	dir_ok "$i" || warn_badperms "$i"
	badstuff="$( find -L "$i" -mindepth 1 -maxdepth 1 \! \( -type l -o -type f \) )"
	[ -n "$badstuff" ] && warn "$i should only contain files, not:" && ls -ld $badstuff
done

for i in $bindirs; do
	[ -d "$i" ] || continue
	badstuff="$( find -L "$i" -mindepth 1 -maxdepth 1 -type f \! -perm /0111 )"
	[ -n "$badstuff" ] && warn "$i should only contain executable files, not:" && ls -ld $badstuff
done

for i in $nofiledirs; do
	[ -d "$i" ] || continue
	dir_ok "$i" || warn_badperms "$i"
	badstuff="$( find -L "$i" -mindepth 1 -maxdepth 1 \! -type d )"
	[ -n "$badstuff" ] && warn "$i should only contain directories, not:" && ls -ld $badstuff
done

if [ -z "$SLACKBUILD_MISSING_OK" ]; then
	for i in $requiredfiles; do
		[ -f "$i" ] || warn "missing required file: $i"
	done
fi

for i in $noexecdirs; do
	[ -d "$i" ] || continue
	found="$( find "$i" -type f -a -perm /0111 )"
	if [ -n "$found" ]; then
		warn "$i should not contain files with executable permission:"
		ls -l $found
	fi
done

for i in $badfiles; do
	[ -e "$i" ] && warn "forbidden file: $i"
done

# 20220414 bkw: absolute symlinks used to be an error, but there are just
# too many packages that use them. so only flag them if they're broken links.
abslinks="$( find . -type l -lname '/*' )"
[ -n "$abslinks" ] && for i in $abslinks; do
  target="$( readlink $i | sed 's,^/*,,' )"
  [ -e "$target" ] || brokenabslinks+="$i "
done

brokenlinks="$( find -L . -type l \! -lname '/*' )"

[ -n "$brokenabslinks" ] && warn "package contains broken absolute symlinks:" && ls -ld $brokenabslinks
[ -n "$brokenlinks" ] && warn "package contains broken relative symlinks:" && ls -ld $brokenlinks
