1582 lines
41 KiB
Bash
Executable File

#!/bin/ksh
#
# $OpenBSD: portcheck,v 1.73 2014/04/27 17:57:14 zhuk Exp $
# Copyright (c) 2013 Vadim Zhukov
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
set -e
set +X
set -u
usage() {
echo "usage: ${0##*/} [-dNP] [-p portsdir] [-x glob]" >&2
echo " ${0##*/} -A [-dP] [-p portsdir] [-x glob] [subdir ...]" >&2
exit 1
}
############################################################
# Parsing command line options
#
existing_port=true
ignore_cvs=true
plist_checks=true
portsdir=
rootrun=false
debugging=false
ignore_list=; unset ignore_list[0]
while getopts "AdNPp:x:" OPT; do
case $OPT in
A)
$existing_port || usage
if ! $rootrun; then
ignore_list[${#ignore_list[@]}]=.cvsignore
ignore_list[${#ignore_list[@]}]=.fslckout
ignore_list[${#ignore_list[@]}]=.git
ignore_list[${#ignore_list[@]}]=.gitignore
ignore_list[${#ignore_list[@]}]=.hg
ignore_list[${#ignore_list[@]}]=.hgignore
ignore_list[${#ignore_list[@]}]=.svn
ignore_list[${#ignore_list[@]}]=FINISHED
ignore_list[${#ignore_list[@]}]=INDEX
ignore_list[${#ignore_list[@]}]=README
ignore_list[${#ignore_list[@]}]=README.md
ignore_list[${#ignore_list[@]}]=bulk
ignore_list[${#ignore_list[@]}]=distfiles
ignore_list[${#ignore_list[@]}]=infrastructure
ignore_list[${#ignore_list[@]}]=lost+found
ignore_list[${#ignore_list[@]}]=mystuff
ignore_list[${#ignore_list[@]}]=openbsd-wip
ignore_list[${#ignore_list[@]}]=packages
ignore_list[${#ignore_list[@]}]=plist
ignore_list[${#ignore_list[@]}]=pobj
ignore_list[${#ignore_list[@]}]=tests
ignore_list[${#ignore_list[@]}]=update
fi
rootrun=true
;;
d)
debugging=true
;;
N)
$rootrun && usage
existing_port=false
ignore_cvs=false
;;
P)
plist_checks=false
;;
p)
portsdir=$OPTARG
;;
x)
set -A ignore_list -- "${ignore_list[@]}" "$OPTARG"
;;
*)
usage
;;
esac
done
if ! $rootrun && [[ -n $portsdir && ${PWD##"$portsdir"} == "$PWD" ]]; then
cat >&2 <<EOE
${0##*/}: current directory does not seem to be under the
specified root directory: $portsdir.
EOE
exit 3
fi
shift $(($OPTIND - 1))
(($# > 0)) && ! $rootrun && usage
(($# == 0)) && set -- .
############################################################
# Detect path to root of directory tree of current port(s) and put it
# in $portsdir, unless it was set by user above. As a last resort, we
# use some heuristics based on the commonly used names.
#
# We also have a $pkgpath variable, that represents subdirectory under
# root ports directory where the port(s) will be imported. In case we
# use heuristics for determining $portsdir, we'll set up $pkgpath, too,
# since we would get this info anyway.
#
# In make_args we write PORTSDIR_PATH override, that allows us to run
# even in ports directory that is not on the PORTSDIR_PATH. This is
# useful, for example, when you check your port on cvs.openbsd.org,
# where you cannot just override mk.conf.
#
pkgpath=
if [[ -z $portsdir ]]; then
IFS=:
testp=/usr/ports/devel/quirks
set -A portsdir_path -- \
$( (cd $testp && make show=PORTSDIR_PATH 2>/dev/null) || true)
unset IFS
if ((${#portsdir_path[@]} > 0)); then
for p in "${portsdir_path[@]}"; do
if [[ -z $portsdir && ${PWD#"$p"} != "$PWD" ]]; then
portsdir=$p
elif [[ -n $portsdir && ${PWD#"$p"} != "$PWD" &&
$p > $portsdir ]]; then
portsdir=$p
fi
done
fi
fi
if [[ -z $portsdir ]]; then
# heuristics mode ON
pkgpath=${PWD##*/ports/*(mystuff/|openbsd-wip/)}
portsdir=${PWD%"/$pkgpath"}
fi
if [[ -z $portsdir ]]; then
cat >&2 <<EOE
${0##*/}: could not detect root ports directory. Please provide
one with -p option.
EOE
exit 2
fi
# This way we can run all checks even on cvs.openbsd.org
set -A make_args -- MASTER_SITE_OPENBSD= \
PORTSDIR_PATH="$portsdir:$(cd /usr/ports && make -V PORTSDIR_PATH || true)"
if $rootrun; then
cd -- "$portsdir"
echo "scanning ports under the $portsdir" >&2
fi
############################################################
# Check and fail routines
#
error=false
err() {
local prefix=
while (($# > 0)); do
printf "$prefix%s" "$1" >&2
prefix=" "
shift
done
echo >&2
error=true
}
err_duplicated() {
err "both $2 and some of its parents has $1"
}
err_coredump_found() {
err "core dump file found: $1"
}
has_subdirs_only() {
$debugging && echo "CALLED: has_subdirs_only($*)" >&2
local dir=$1; shift
ls -A "$dir" | {
local has_files=false has_dirs=false
while read F; do
$ignore_cvs && [[ $F == CVS ]] && continue
ignoring "$dir/$F" && continue
if [[ -d $dir/$F ]]; then
has_dirs=true
else
has_files=true
fi
done
$has_dirs && ! $has_files
}
}
ignoring() {
((${#ignore_list[*]} > 0)) || return 1
local iglob
for iglob in "${ignore_list[@]}"; do
[[ ${1#./} == $iglob ]] && return 0
done
return 1
}
is_vcs_item() {
[[ -d "$1" && ${1##*/} == @(CVS|.fslckout|.git|.hg|.svn) ]]
}
handle_extra_file() {
ignoring "$1" && return 0
# avoid warning, e.g., about ".*"
test -e "$1" || return 0
if is_vcs_item "$1"; then
if ! $ignore_cvs || [[ ${1##*/} != CVS ]]; then
err "VCS item detected: $1"
fi
elif [[ -f $1 && $1 == *.core ]]; then
err_coredump_found "$1"
elif [[ -d $1 ]]; then
err "extra directory: $1"
else
err "extra file: $1"
fi
}
# Make a path to .py[co] file looks like as if it's in the same dir
# as the corresponding .py file, and has same basename. E.g.:
# lib/python3.3/__pycache__/Foo/cpython-33.Bar.pyc
# became:
# lib/python2.7/Foo/Bar.pyc
# which corresponds to:
# lib/python2.7/Foo/Bar.py
normalize_pyco() {
local pyco=$1
[[ $pyco == *.cpython-+([0-9]).py[co] ]] &&
pyco=${pyco%.cpython-+([0-9]).py[co]}.${pyco##*.}
[[ $pyco == */__pycache__/* ]] &&
pyco=${pyco%/__pycache__/*}/${pyco##*/__pycache__/}
printf "%s" "$pyco"
}
# Print out a ref to the particular subport/subpackage, if needed.
# Port FLAVORs could also be handled, if provided.
# Usage: portref directory [subpackage [flavor all_flavors]]
portref() {
local dir=$1; shift
local subpkg= flavor all_flavors=
if (($# > 0)); then
subpkg=$1
shift
fi
if (($# > 0)); then
flavor=$1
all_flavors=$2
shift 2
fi
local ref=
if [[ $dir != . ]]; then
ref="${dir#./}"
[[ -n $subpkg && $subpkg != "-" ]] && ref="$ref,$subpkg"
else
[[ $subpkg != "-" ]] && ref="$subpkg"
fi
if [[ -n $all_flavors ]]; then
[[ -n $ref ]] && ref="$ref, "
if [[ -z $flavor ]]; then
ref="${ref}default FLAVOR"
else
ref="${ref}FLAVOR \"$flavor\""
fi
fi
[[ -n $ref ]] && echo "in $ref: "
}
# Contains last SUBST_CMD. Filled by check_port_dir(), used
# by check_port_hier() to lazily call the check_pkg_dir().
last_subst_cmd=
# Checks made:
# * Whitelist filter of what could be in this directory.
check_port_hier() {
$debugging && echo "CALLED: check_port_hier($*)" >&2
local distinfo_lives_upper pkg_lives_upper plist_lives_upper
local dir=$1; shift
for opt; do
# looks unsafe but we do not pass anything except
# "foo=true" and "foo=false" here
eval "$opt"
done
distinfo_lives_upper=${distinfo_lives_upper:-false}
pkg_lives_upper=${pkg_lives_upper:-false}
plist_lives_upper=${plist_lives_upper:-false}
local distinfo_exists=false
[[ -f $dir/distinfo ]] && distinfo_exists=true
$distinfo_exists && $distinfo_lives_upper &&
err_duplicated distinfo "$dir"
local pkg_exists=false tell_pkg_exists=$pkg_lives_upper
if [[ -d $dir/pkg ]]; then
pkg_exists=true
tell_pkg_exists=true
fi
local plist_exists=false
ls $dir/pkg/PLIST* >/dev/null 2>&1 && plist_exists=true
$plist_lives_upper && $plist_exists &&
err_duplicated "packing list(s)" "$dir"
$distinfo_lives_upper && distinfo_exists=true
$plist_lives_upper && plist_exists=true
local recursive_args
set -A recursive_args -- \
distinfo_lives_upper=$distinfo_exists \
pkg_lives_upper=$tell_pkg_exists \
plist_lives_upper=$plist_exists
local F
for F in "$dir"/* "$dir"/.*; do
F=${F#./}
ignoring "$F" && continue
if is_vcs_item "$F"; then
if ! $ignore_cvs || [[ ${F##*/} != CVS ]]; then
err "VCS item detected: $F"
fi
elif [[ -d $F ]]; then
case "${F##*/}" in
files|patches)
check_${F##*/}_dir "$F"
;;
pkg)
# Do nothing, pkg_exists is already set,
# and we need to read SUBST_CMD first.
;;
patches?(-*))
check_patches_dir "$F"
;;
*)
if ! ([[ -f $F/Makefile ]] ||
ls $F/*.port.mk >/dev/null 2>&1) &&
! has_subdirs_only "$F"; then
# Avoid extra spam
err "not a port directory: $F"
else
local pkgpath_set=false
[[ -n $pkgpath ]] && pkgpath_set=true
check_port_dir "$F" "${recursive_args[@]}"
$pkgpath_set || pkgpath=${pkgpath%/*}
fi
;;
esac
else
case "${F##*/}" in
Makefile?(.inc)|*.port.mk)
check_makefile "$F"
;;
distinfo)
;;
*)
handle_extra_file "$F"
;;
esac
fi
done
$pkg_exists && check_pkg_dir "$dir"/pkg "$last_subst_cmd"
$existing_port ||
egrep -q '^ *SUBDIR[[:space:]]*\+?=' "$dir"/Makefile ||
err missing subdir Makefile
}
# Checks made:
# * Whitelist filter of what could be in this directory.
check_port_dir() {
$debugging && echo "CALLED: check_port_dir($*)" >&2
local dir=$1; shift
local distinfo_lives_upper pkg_lives_upper plist_lives_upper
for opt; do
# looks unsafe but we do not pass anything except
# "foo=true" and "foo=false" here
eval "$opt"
done
distinfo_lives_upper=${distinfo_lives_upper:-false}
pkg_lives_upper=${pkg_lives_upper:-false}
plist_lives_upper=${plist_lives_upper:-false}
check_perms_in_dir "$dir"
if [[ -f $dir/Makefile.inc ]] ||
egrep -sq '^ *SUBDIR[[:space:]]*\+?=' "$dir"/Makefile ||
has_subdirs_only "$dir"; then
check_port_hier "${dir#./}" "${@:-}"
return
fi
local F
local distinfo_exists=false
local mk_exists=false
local pkg_exists=false
local plist_exists=false
local portmk_exists=true
local non_portmk=0
for F in "$dir"/* "$dir"/.*; do
F=${F#./}
ignoring "$F" && continue
case ${F##*/} in
Makefile)
test -f "$F" || err "$F is not a file"
check_makefile "$F"
mk_exists=true
((++non_portmk))
;;
distinfo)
$distinfo_lives_upper && err_duplicated distinfo "$dir"
distinfo_exists=true
test -f "$F" || err "$F is not a file"
((++non_portmk))
;;
*.port.mk)
test -f "$F" || err "$F is not a file"
check_makefile "$F"
portmk_exists=true
;;
systrace.filter)
test -f "$F" || err "$F is not a file"
((++non_portmk))
;;
files|patches)
if [[ -d $F ]]; then
check_${F##*/}_dir "$F"
else
err "$F" is not a directory
fi
((++non_portmk))
;;
pkg)
if [[ -d $F ]]; then
pkg_exists=true
# Actual check to be done later, we need to gather
# additional info through "make show=" call.
ls "$F"/PLIST* >/dev/null 2>&1 &&
plist_exists=true
$plist_lives_upper && $plist_exists &&
err_duplicated "packing list(s)" "$dir"
else
err "$F" is not a directory
fi
((++non_portmk))
;;
*)
handle_extra_file "$F"
;;
esac
done
# examples: lang/clang, www/mozilla
$portmk_exists && ((non_portmk == 0)) && return
$mk_exists || err no Makefile in "$dir"
$pkg_exists || $pkg_lives_upper || err "no pkg/ in $dir"
$distinfo_lives_upper && distinfo_exists=true
$distinfo_exists || $existing_port || err "no distinfo in $dir"
# Now gather and check some info via "make show=...".
# We request all info at once for speed.
local dist_subdir distfiles flavor flavors master_sites
local multi_packages pkgpath_this pseudo_flavor pseudo_flavors
local shared_libs subst_cmd
local perm_pkg_cdrom perm_pkg_ftp perm_dist_ftp
local show_items="DIST_SUBDIR DISTFILES FLAVOR FLAVORS FULLPKGNAME"
local show_items="$show_items MASTER_SITES MULTI_PACKAGES PKGPATH"
local show_items="$show_items PSEUDO_FLAVOR PSEUDO_FLAVORS"
local show_items="$show_items SHARED_LIBS SUBST_CMD"
local show_items="$show_items PERMIT_PACKAGE_CDROM PERMIT_PACKAGE_FTP"
local show_items="$show_items PERMIT_DISTFILES_FTP"
local read_ok=false
local read_failed=false
(cd -- "$dir"; make "${make_args[@]}" show="$show_items" || true) </dev/null |&
read -pr dist_subdir &&
read -pr distfiles &&
read -pr flavor &&
read -pr flavors &&
read -pr fullpkgname &&
read -pr master_sites &&
read -pr multi_packages &&
read -pr pkgpath_this &&
read -pr pseudo_flavor &&
read -pr pseudo_flavors &&
read -pr shared_libs &&
read -pr subst_cmd &&
read -pr perm_pkg_cdrom &&
read -pr perm_pkg_ftp &&
read -pr perm_dist_ftp &&
read_ok=true
if $read_ok; then
exec 3<&p
exec 3<&-
wait
else
error=true
return
fi
pseudo_flavor=$(echo "$pseudo_flavor" | sed -e 's/,/ /g')
pseudo_flavor=${pseudo_flavor##" "}
local f pf found
local check_flavors=
[[ $flavor != "$pseudo_flavor" ]] && unset check_flavors[0]
for f in $flavors; do
for pf in $pseudo_flavors; do
[[ $f == "$pf" ]] && continue 2
done
[[ $f == debug ]] && continue # XXX
check_flavors[${#check_flavors[@]}]=$f
done
check_distfiles "$dir" "$dist_subdir" $distfiles
check_master_sites "$dir" $master_sites
check_permit_dist "$dir" "$perm_pkg_cdrom" "$perm_pkg_ftp" \
"$perm_dist_ftp"
$pkg_exists && check_pkg_dir "$dir"/pkg "$subst_cmd"
$existing_port || check_shlibs_versions "$dir" $shared_libs
for _s in $multi_packages; do
sub_checks "$dir" "$_s" "$fullpkgname" "${check_flavors[@]}"
done
pkgpath=${pkgpath:-"$pkgpath_this"}
last_subst_cmd="$subst_cmd"
}
# Checks made: obvious
check_trailing_whitespace() {
egrep -q '[[:space:]]+$' "$1" &&
err "trailing whitespace in $1"
}
# Checks made: obvious
check_newline_at_eof() {
(( $(tail -1 -- "$1" | wc -l) == 0)) &&
err "no newline at EOF in $1"
}
# Checks made:
# * Every library in SHARED_LIBS has 0.0 version.
check_shlibs_versions() {
$debugging && echo "CALLED: check_shlibs_versions($*)" >&2
local dir=$1; shift
local lib
local libver
local portref=$(portref "$dir")
while (($# > 1)); do
lib=$1
libver=$2
if [[ $libver != 0.0 ]]; then
err "${portref}the $lib shared library has" \
"version $libver instead of 0.0"
fi
shift 2
done
}
# Checks made:
# * Distfiles with useless names go into DIST_SUBDIR or have {url} suffix.
check_distfiles() {
$debugging && echo "CALLED: check_distfiles($*)" >&2
local dir=$1; shift
local dist_subdir=$1; shift
local portref=$(portref "$dir")
# do not care about absent distfiles, this is fine for meta ports
while (($# > 1)); do
# try to catch "version-only" names, but not anything more
if [[ $1 == ?(v)?(.)+([0-9])?(.+([0-9]))*(.+([a-z])) &&
-z $dist_subdir && $1 != *\{*\} ]]; then
err "${portref}badly named distfile $1 without" \
"DIST_SUBDIR or {url} postfix"
fi
shift
done
}
# Checks made:
# * No unreliable (without fixed distfiles) hosting listed in MASTER_SITES.
check_master_sites() {
$debugging && echo "CALLED: check_master_sites($*)" >&2
local dir=$1; shift
local portref=$(portref "$dir")
local name
while (($# > 1)); do
case "$1" in
http?(s)://bitbucket.com/*) name=BitBucket;;
http?(s)://gitorious.com/*) name=Gitorious;;
*) name=;;
esac
[[ -n $name ]] && err "$portref$name does not hold real" \
"releases, please host the distfiles somewhere" \
"else or ask someone to do this for you"
shift
done
}
# Run checks that are FLAVOR/SUBPACKAGE-dependent.
sub_checks() {
$debugging && echo "CALLED: sub_checks($*)" >&2
local dir=$1; shift
local subpkg=$1; shift
local fullpkgname=$1; shift
local flavor
for flavor in "$@"; do
# avoid extra noise
[[ ${flavor#no_} != ${flavor} &&
${subpkg#-} == ${flavor#no_} ]] &&
continue
(
cd -- "$dir"
portref=$(portref "$dir" "$subpkg" "$flavor" "$*")
export SUBPACKAGE="$subpkg" FLAVOR="$flavor"
local wantlib_var=WANTLIB${subpkg%-}
local vars="MODULES PKG_ARCH$subpkg $wantlib_var"
vars="$vars PERMIT_PACKAGE_CDROM${subpkg%-}"
vars="$vars PERMIT_PACKAGE_FTP${subpkg%-}"
make "${make_args[@]}" show="$vars" | {
local modules pkg_arch wantlib
local perm_pkg_cdrom perm_pkg_ftp
read -r modules
read -r pkg_arch
read -r wantlib
read -r perm_pkg_cdrom
read -r perm_pkg_ftp
if [[ $pkg_arch = "*" && -n $wantlib ]]; then
err "${portref}non-empty $wantlib_var for" \
"arch-independent package"
fi
check_wantlib "$portref" "$modules" $wantlib ||
error=true
check_permit_subpkg "$portref" "$subpkg" \
"$perm_pkg_cdrom" "$perm_pkg_ftp" ||
error=true
! $error
} || error=true
if $plist_checks; then
(make "${make_args[@]}" print-plist-with-depends ||
true) </dev/null |&
check_plist "$portref" "$fullpkgname" "$flavor"
wait
fi
! $error
) || error=true
done
wait
}
# Checks made:
# * If package installs system-wide icons, it should have the
# x11/gtk+2,-guic dependency and @exec/@unexec-delete with
# %D/bin/gtk-update-icon-cache -q -t %D/share/icons/$theme
# for each icon theme used in package. If there is an
# index.theme provided, then, instead of gtk-update-icon-cache,
# @unexec-delete should contain the following command:
# rm -f %D/share/icons/$theme/icon-theme.cache
#
# * If package adds a MIME type handler, it should have the
# devel/desktop-file-utils dependency and @exec/@unexec-delete with
# %D/bin/update-desktop-database . Unfortunately, it's hard to tell
# if there is a MIME type handler in .desktop file, so we just
# trigger if any .desktop files are added to
# ${PREFIX}/share/applications/ .
#
# * If package adds a MIME types package, it should have the
# misc/shared-mime-info dependency and @exec/@unexec-delete with
# %D/bin/update-mime-database %D/share/mime
#
# * If package installs .mo files under ${PREFIX}/share/locale/, then
# run-time dependency on devel/gettext should exists.
#
# * Each .py should have corresponding .pyc files, to avoid
# generation of the latter at run-time.
check_plist() {
$debugging && echo "CALLED: check_plist($*)" >&2
local portref=$1; shift
local fullpkgname=$1; shift
local flavor_list=$1; shift
local flavor is_static=false
for flavor in $flavor_list; do
[[ $flavor == static ]] && is_static=true
done
local guic_dep=false
local guic_dep_needed=false
local guic_exec_cnt=0
local guic_unexec_cnt=0
local mime_dep=false
local mime_dep_needed=false
local mime_exec_cnt=0
local mime_unexec_cnt=0
local mimepkg_dep=false
local mimepkg_dep_needed=false
local mimepkg_exec_cnt=0
local mimepkg_unexec_cnt=0
# Lists of icon themes discovered through reading
# @file, @exec and @unexec lines, accordingly.
local icon_themes= exec_icon_themes= unexec_icon_themes=
# List of icon themes that remove cache file
local rm_cache_themes=
local gettext_dep=false
local translation_found=false
# Lists of .py, .pyc and .pyo items found, accordingly
local py_files= pyc_files= pyo_files=
unset py_files[0] pyc_files[0] pyo_files[0]
# Temporary ones
local app l theme varname py
while read -pr l; do
case "$l" in
"@comment "*)
# ignore
;;
share/icons/*/*/*|share/icons/*/@(index.theme|iconrc?(-png)|default.kde4))
# Themes have at least two levels in depth.
#
# We match directories by purpose, this helps to catch
# update-plist fuckups, when directories go into one
# package and actual icons go in another.
guic_dep_needed=true
theme=${l#share/icons/}
theme=${theme%%/*}
# wrap with the '/' characters to avoid erroneous matching
echo "$icon_themes" | fgrep -q "/$theme/" ||
icon_themes="$icon_themes /$theme/"
if [[ "$l" = "share/icons/$theme/index.theme" ]]; then
echo "$rm_cache_themes" | fgrep -q "/$theme/" ||
err "${portref}missing @unexec-delete rm -f" \
"%D/share/icons/$theme/icon-theme.cache"
fi
;;
share/icons/*(*/))
# Do not match intermediate directories to avoid false
# positives.
;;
share/icons/*)
app=${l#share/icons/}
app=${app%%/*}
app=${app%%.*}
err "${portref}installs icon ${l##*/} in ${l%/*}, it" \
"should go in share/$app/icons/ or like instead"
;;
"@depend x11/gtk+2,-guic"*)
guic_dep=true
;;
"@exec %D/bin/gtk-update-icon-cache -q -t %D/share/icons/"*)
theme=${l##*/}
varname=$(echo "$theme" | sed -e 's/[^a-zA-Z_]/_/g')
((++guic_exec_cnt))
eval "((++guic_exec_cnt_$varname))"
exec_icon_themes="$exec_icon_themes /$theme/"
;;
"@unexec-delete %D/bin/gtk-update-icon-cache -q -t %D/share/icons/"*)
theme=${l##*/}
varname=$(echo "$theme" | sed -e 's/[^a-zA-Z_]/_/g')
((++guic_unexec_cnt))
eval "((++guic_unexec_cnt_$varname))"
unexec_icon_themes="$unexec_icon_themes /$theme/"
;;
"@unexec-delete rm -f "%D/share/icons/*/icon-theme.cache)
# as an alternative, port could zap the theme entirely
theme=${l#*/icons/}
theme=${theme%/icon-theme.cache}
varname=$(echo "$theme" | sed -e 's/[^a-zA-Z_]/_/g')
((++guic_unexec_cnt))
eval "((++guic_unexec_cnt_$varname))"
unexec_icon_themes="$unexec_icon_themes /$theme/"
rm_cache_themes="$rm_cache_themes /$theme/"
if echo "$icon_themes" | fgrep -q "/$theme/"; then
err "${portref}the @unexec-delete line removing" \
"%D/share/icons/$theme/icon-theme.cache" \
"does not preceed all of the icon theme" \
"$theme files"
fi
;;
@?(un)exec?(-delete|-update)" %D/bin/gtk-update-icon-cache"*)
err "${portref}incorrect gtk-update-icon-cache" \
"invocation: ${l#@* }"
;;
share/applications/*(*/)*.desktop)
mime_dep_needed=true
;;
"@depend devel/desktop-file-utils"*)
mime_dep=true
;;
"@exec %D/bin/update-desktop-database")
((++mime_exec_cnt))
;;
"@unexec-delete %D/bin/update-desktop-database")
((++mime_unexec_cnt))
;;
@?(un)exec?(-delete|-update)" %D/bin/update-desktop-database"*)
err "${portref}incorrect update-desktop-database" \
"invocation: ${l#@* }"
;;
share/mime/packages/*.xml)
mimepkg_dep_needed=true
;;
"@depend misc/shared-mime-info"*)
mimepkg_dep=true
;;
"@exec %D/bin/update-mime-database %D/share/mime")
((++mimepkg_exec_cnt))
;;
"@unexec-delete %D/bin/update-mime-database %D/share/mime")
((++mimepkg_unexec_cnt))
;;
@?(un)exec?(-delete|-update)" %D/bin/update-mime-database"*)
err "${portref}incorrect update-mime-database" \
"invocation: ${l#@* }"
;;
"@depend devel/gettext"*)
gettext_dep=true
;;
share/locale/*/*/*.mo)
translation_found=true
;;
# XXX KSH arrays are limited to 10239 items
share/@(doc|*(*/)examples)+(/*).py|?(s)bin/*.py)
# ignore
;;
*.py)
py_files[${#py_files[@]}]=$l
;;
*.pyc)
pyc_files[${#pyc_files[@]}]=$(normalize_pyco "$l")
;;
*.pyo)
pyo_files[${#pyo_files[@]}]=$(normalize_pyco "$l")
;;
esac
done
# gtk-update-icon-cache
$guic_dep_needed && ! $guic_dep &&
[[ $fullpkgname != gtk-update-icon-cache-* ]] &&
err "${portref}missing RDEP on x11/gtk+2,-guic"
local cnt
for theme in $icon_themes; do
theme=${theme#/}
theme=${theme%/}
varname=$(echo "$theme" | sed -e 's/[^a-zA-Z_]/_/g')
((guic_exec_cnt--)) || true
((guic_unexec_cnt--)) || true
eval "((guic_exec_cnt_$varname--)) || true"
eval "((guic_unexec_cnt_$varname--)) || true"
eval "cnt=\$guic_exec_cnt_$varname"
if (($cnt > 0)); then
err "${portref}extra @exec of gtk-update-icon-cache" \
"for icon theme $theme"
((guic_exec_cnt--)) || true
elif (($cnt < 0)); then
err "${portref}missing @exec of gtk-update-icon-cache" \
"for icon theme $theme"
fi
eval "cnt=\$guic_unexec_cnt_$varname"
if (($cnt > 0)); then
err "${portref}extra @unexec-delete of gtk-update-icon-cache" \
"for icon theme $theme"
((guic_unexec_cnt--)) || true
elif (($cnt < 0)); then
err "${portref}missing @unexec-delete of gtk-update-icon-cache" \
"for icon theme $theme"
fi
done
for theme in $exec_icon_themes; do
theme=${theme#/}
theme=${theme%/}
echo "$icon_themes" | fgrep -q "/$theme/" ||
err "${portref}doing @exec of gtk-update-icon-cache" \
"for absent icon theme $theme"
done
for theme in $unexec_icon_themes; do
theme=${theme#/}
theme=${theme%/}
echo "$icon_themes" | fgrep -q "/$theme/" ||
err "${portref}doing @unexec-delete of gtk-update-icon-cache" \
"for absent icon theme $theme"
done
((guic_exec_cnt > 0)) &&
err "${portref}extra @exec of gtk-update-icon-cache"
((guic_unexec_cnt > 0)) &&
err "${portref}extra @unexec-delete of gtk-update-icon-cache"
# desktop-file-utils (simplier than previous, isn't it?)
$mime_dep_needed && ! $mime_dep &&
[[ $fullpkgname != desktop-file-utils-* ]] &&
err "${portref}missing RDEP on devel/desktop-file-utils"
if $mime_dep_needed; then
((mime_exec_cnt--)) || true
((mime_unexec_cnt--)) || true
fi
if ((mime_exec_cnt > 0)) &&
[[ $fullpkgname != desktop-file-utils-* ]]; then
err "${portref}extra @exec of update-desktop-database"
elif ((mime_exec_cnt < 0)); then
err "${portref}missing @exec of update-desktop-database"
fi
if ((mime_unexec_cnt > 0)); then
err "${portref}extra @unexec-delete of update-desktop-database"
elif ((mime_unexec_cnt < 0)); then
err "${portref}missing @unexec-delete of update-desktop-database"
fi
# update-mime-database (same as previous)
$mimepkg_dep_needed && ! $mimepkg_dep &&
[[ $fullpkgname != shared-mime-info-* ]] &&
err "${portref}missing RDEP on misc/shared-mime-info"
if $mimepkg_dep_needed; then
((mimepkg_exec_cnt--)) || true
((mimepkg_unexec_cnt--)) || true
fi
if ((mimepkg_exec_cnt > 0)) &&
[[ $fullpkgname != shared-mime-info-* ]]; then
err "${portref}extra @exec of update-mime-database"
elif ((mimepkg_exec_cnt < 0)); then
err "${portref}missing @exec of update-mime-database"
fi
if ((mimepkg_unexec_cnt > 0)); then
err "${portref}extra @unexec-delete of update-mime-database"
elif ((mimepkg_unexec_cnt < 0)); then
err "${portref}missing @unexec-delete of update-mime-database"
fi
# gettext
$translation_found && ! $gettext_dep && ! $is_static &&
[[ $fullpkgname != gettext-* ]] &&
err "${portref}translation file(s) found without" \
"devel/gettext dependency"
# Python modules
((${#py_files[@]} > 0)) && set -sA py_files -- "${py_files[@]}"
((${#pyc_files[@]} > 0)) && set -sA pyc_files -- "${pyc_files[@]}"
((${#pyo_files[@]} > 0)) && set -sA pyo_files -- "${pyo_files[@]}"
local ic=0 io=0
if ((${#py_files[@]} > 0)); then for py in "${py_files[@]}"; do
while [[ $ic -lt ${#pyc_files[@]} ]]; do
[[ ${pyc_files[$ic]} < "$py"c ]] || break
# allowed behaviour
#err "${portref}compiled Python module without" \
# "source, expected: ${pyc_files[$ic]%c}"
((++ic))
done
if [[ $ic -lt ${#pyc_files[@]} &&
${pyc_files[$ic]} == "$py"c ]]; then
((++ic))
else
err "${portref}Python module without" \
"compiled version, consider using" \
"\${MODPY_LIBDIR}/compileall.py: $py"
fi
while [[ $io -lt ${#pyo_files[@]} ]]; do
[[ ${pyo_files[$io]} < "$py"o ]] || break
# allowed behaviour
#err "${portref}optimized Python module without" \
# "source, expected: ${pyo_files[$io]%o}"
((++io))
done
if [[ $io -lt ${#pyo_files[@]} &&
${pyo_files[$io]} == "$py"o ]]; then
((++io))
# too much noise, maybe enable in the future
#else
# err "${portref}Python module without" \
# "optimized version: $py"
fi
done; fi
# allowed behaviour
#while (($ic < ${#pyc_files[@]})); do
# err "${portref}compiled Python module without source," \
# "expected: ${pyc_files[$ic]%c}"
# ((++ic))
#done
# allowed behaviour
#while (($io < ${#pyo_files[@]})); do
# err "${portref}optimized Python module without source," \
# "expected: ${pyo_files[$io]%o}"
# ((++io))
#done
}
# Checks made:
# * devel/gettext and converters/libiconv MODULES are not forgotten.
# * lib/qt[34]/, lib/kde/ and lib/kde4/ prefixes not missing where
# applicable.
check_wantlib() {
local portref="$1"; shift
local modules="$1"; shift
local iconv_wantlib=false
local intl_wantlib=false
local phonon_s_wantlib=false
local gettext_module=false
local iconv_module=false
local qt3_module=false
local qt4_module=false
local kde3_module=false
local kde4_module=false
local phonon_module=false
local v
for v in $modules; do case $v in
devel/gettext) gettext_module=true;;
converters/libiconv) iconv_module=true;;
multimedia/phonon) phonon_module=true;;
x11/qt3) qt3_module=true;;
x11/qt4) qt4_module=true;;
x11/kde) kde3_module=true;;
x11/kde4) kde4_module=true;;
esac; done
for v; do case $v in
iconv?(?(">")=+([0-9])))
iconv_wantlib=true
;;
intl?(?(">")=+([0-9])))
intl_wantlib=true
;;
phonon_s?(?(">")=+([0-9])))
phonon_s_wantlib=true
;;
@(Qt+([A-Za-z0-9])|phonon)?(?('>')=+([0-9])))
err "$portref$v instead of lib/qt4/$v" \
"in WANTLIB"
;;
@(qt-mt|qui|qui-mt)?(?('>')=+([0-9])))
err "$portref$v instead of lib/qt3/$v" \
"in WANTLIB"
;;
@(smbclient|wbclient)?(?('>')=+([0-9])))
err "$portref$v instead of lib/samba/$v" \
"in WANTLIB"
;;
@(DCOP|soundserver_idl|vcard)?(?('>')=+([0-9])))
err "$portref$v instead of \${KDE}/$v" \
"in WANTLIB (check other libs, too!)"
;;
@(kdecore|kdeui|kio)?(?('>')=+([0-9])))
if $kde4_module; then
err "$portref$v instead of \${KDE4LIB}/$v" \
"in WANTLIB (check other libs, too!)"
elif $kde3_module; then
err "$portref$v instead of \${KDE}/$v" \
"in WANTLIB (check other libs, too!)"
else
err "$portref$v WANTLIB without x11/kde*" \
"in MODULES (check other libs, too!)"
fi
;;
esac; done
if $intl_wantlib && ! $gettext_module; then
err "${portref}missing devel/gettext in MODULES"
elif $iconv_wantlib && ! $gettext_module && ! $iconv_module; then
err "${portref}missing converters/libiconv in MODULES"
fi
if $phonon_s_wantlib && ! $phonon_module; then
err "${portref}missing multimedia/phonon in MODULES"
fi
! $error
}
# Checks made:
# * No extra PERMIT_DISTFILES_FTP variables in Makefile.
# * PERMIT_DISTFILES_FTP should not contain just "No" but a reason.
#
# Runs in the port directory.
# XXX does not handle Makefile.inc and other .include cases correctly.
check_permit_dist() {
$debugging && echo "CALLED: check_permit_dist($*)" >&2
local portref=$(portref $1); shift
local perm_pkg_cdrom=$(echo "$1" | tr '[:upper:]' '[:lower:]')
local perm_pkg_ftp=$(echo "$2" | tr '[:upper:]' '[:lower:]')
local perm_dist_ftp=$(echo "$3" | tr '[:upper:]' '[:lower:]')
if [[ ($perm_pkg_cdrom == yes || $perm_pkg_ftp == yes) && \
$perm_dist_ftp == yes ]]; then
egrep -q "^ *PERMIT_DISTFILES_FTP[[:space:]]*=" Makefile &&
err "${portref}extra PERMIT_DISTFILES_FTP line(-s)"
fi
if [[ $perm_dist_ftp == no ]]; then
err "${portref}PERMIT_DISTFILES_FTP should be either" \
"\"Yes\" or a reason for being non-redistributable"
fi
true
}
# Checks made:
# * No extra PERMIT_PACKAGE_FTP variables in Makefile.
# * PERMIT_PACKAGE_* should not contain just "No" but a reason.
#
# Runs in the port directory.
# XXX does not handle Makefile.inc and other .include cases correctly.
check_permit_subpkg() {
$debugging && echo "CALLED: check_permit_subpkg($*)" >&2
local portref=$1; shift
local subpkg=${1%-}; shift
local perm_pkg_cdrom=$(echo "$1" | tr '[:upper:]' '[:lower:]')
local perm_pkg_ftp=$(echo "$2" | tr '[:upper:]' '[:lower:]')
if [[ $perm_pkg_cdrom == yes && $perm_pkg_ftp == yes ]]; then
egrep -q "^ *PERMIT_PACKAGE_FTP${subpkg}[[:space:]]*=" Makefile &&
err "${portref}extra PERMIT_PACKAGE_FTP lines"
fi
if [[ $perm_pkg_cdrom == no ]]; then
err "${portref} PERMIT_PACKAGE_CDROM should be either" \
"\"Yes\" or a reason for being non-redistributable"
fi
if [[ $perm_pkg_ftp == no ]]; then
err "${portref} PERMIT_PACKAGE_FTP should be either" \
"\"Yes\" or a reason for being non-redistributable"
fi
true
}
# Checks made:
# * Directory is not empty
# * No '*.core' files present
check_files_dir() {
$debugging && echo "CALLED: check_files_dir($*)" >&2
find -f "$1" -- -type f | {
local empty=true
local mode
while read F; do
ignoring "$F" && continue
mode=$(stat -f %p "$F" || true)
(( (0$mode & 0111) != 0 )) &&
err "executable file: $F"
empty=false
[[ $F == *.core ]] &&
err_coredump_found "$F"
done
$empty && "there are no files, please remove the $1 directory"
! $error
} || error=true
}
# Checks made:
# * Each patch contains OpenBSD RCS tag.
# * Directory is not empty and consists only of plain files starting
# with 'patch-' and not ending with '.orig'.
check_patches_dir() {
$debugging && echo "CALLED: check_patches_dir($*)" >&2
local empty=true
local F
check_perms_in_dir "$1"
for F in "$1"/* "$1"/.*; do case "${F##*/}" in
patch-*.orig)
handle_extra_file "$F"
;;
patch-*)
empty=false
test -f "$F" ||
err "$F is not a file"
$rootrun || head -n 1 -- "$F" | egrep -q '^\$OpenBSD.*\$$' ||
err "$F does not have \$OpenBSD\$ RCS tag at the top"
;;
*)
handle_extra_file "$F"
;;
esac; done
$empty && err "there are no patches, please remove the $1 directory instead"
}
# Checks made:
# * Directory is not empty and consist only of plain files with fixed names.
# * PFRAG, PLIST, README and .rc files contain appropriate OpenBSD RCS
# tags; other files should NOT contain OpenBSD RCS tag.
# * PFRAG.shared should be merged in PLIST if it contains @lib items only.
# * No trailing whitespace for DESCR, MESSAGE, README, UNMESSAGE and
# .rc files (PLIST and PFRAG are better checked with "make package").
# * See also check_plist_file().
check_pkg_dir() {
$debugging && echo "CALLED: check_pkg_dir($*)" >&2
local dir=$1; shift
local subst_cmd
if (($# > 0)); then
# XXX should find the way to always obtain SUBST_CMD
subst_cmd=$1
shift
fi
local empty=true
local F
check_perms_in_dir "$dir"
dir="${dir#./}"
for F in "$dir"/* "$dir"/.*; do case "${F##*/}" in
DESCR?(-*))
empty=false
[[ -f $F ]] ||
err "$F is not a file"
check_trailing_whitespace "$F"
check_newline_at_eof "$F"
check_long_lines "$F"
check_hardcoded "$F"
[[ -n $subst_cmd ]] && check_subst_vars "$F" "$subst_cmd"
egrep -q '\$OpenBSD.*\$' "$F" &&
err "$F should not contain \$OpenBSD\$ tag"
;;
PFRAG.shared?(-*))
empty=false
[[ -n $subst_cmd ]] && check_subst_vars "$F" "$subst_cmd"
check_plist_file "$F"
awk <"$F" '/^(@comment )?@lib /' | {
local no_a_for_so=false plist=${F##*/} shlibs_found=false
plist=PLIST${plist##PFRAG.+([!-])}
while read l; do
shlibs_found=true
l=${l##"@comment "}
l=${l##"@lib "}
l=${l%%.so.*}.a
fgrep -q -- "$l" "${F%/*}/$plist" || no_a_for_so=true
done
$shlibs_found && ! $no_a_for_so &&
err "$F should be merged in $plist"
! $error
} || error=true
;;
PFRAG.*|PLIST?(-*))
empty=false
[[ -n $subst_cmd ]] && check_subst_vars "$F" "$subst_cmd"
check_plist_file "$F"
;;
README?(-*))
[[ -f $F ]] ||
err "$F is not a file"
[[ -n $subst_cmd ]] && check_subst_vars "$F" "$subst_cmd"
check_trailing_whitespace "$F"
check_newline_at_eof "$F"
check_long_lines "$F"
check_hardcoded "$F"
head -n 1 -- "$F" |
egrep -q '^(#[[:space:]]*)?\$OpenBSD(:.*)?\$$' ||
err "$F does not have \$OpenBSD\$ RCS tag at the top"
;;
*.rc)
[[ -f $F ]] ||
err "$F is not a file"
[[ ${F##*/} == [A-Za-z_]*([A-Za-z0-9_]).rc ]] ||
err "$F name will not work in rc.subr(8)"
check_trailing_whitespace "$F"
check_long_lines "$F"
check_hardcoded "$F"
head -n 5 -- "$F" |
egrep -q '^#[[:space:]]*\$OpenBSD(:.*)?\$$' ||
err "$F does not have \$OpenBSD\$ RCS tag at the top"
;;
MESSAGE?(-*)|UNMESSAGE?(-*))
[[ -f $F ]] ||
err "$F is not a file"
[[ -n $subst_cmd ]] && check_subst_vars "$F" "$subst_cmd"
check_trailing_whitespace "$F"
check_newline_at_eof "$F"
check_long_lines "$F"
check_hardcoded "$F"
egrep -q '\$OpenBSD.*\$' "$F" &&
err "$F should not contain \$OpenBSD\$ tag"
;;
*)
handle_extra_file "$F"
;;
esac; done
$empty && err "$dir directory does not contain either DESCR, PFRAG or PLIST files"
}
# Checks made:
# * There are no hardcoded /usr/local or /var paths in file.
# /var/log, /var/run and /var/tmp are perfectly fine, though.
check_hardcoded() {
$debugging && echo "CALLED: check_hardcoded($*)" >&2
perl -n -e 'BEGIN { $ec=1; }
if (m,/usr/local\b,o) { $ec=0; close ARGV; }
if (m,/var((?:/+[^/\s]+)*)(?:\s.*)?$,o) {
unless ($1 =~ m,^/+(?:log|run|tmp),o) {
$ec=0; close ARGV;
}
}
END { $? = $ec; }' \
"$1" && err "hardcoded paths detected in $1, consider using" \
"SUBST_VARS and TRUEPREFIX/LOCALBASE/LOCALSTATEDIR/VARBASE"
}
# Checks made:
# * There are no lines longer than 80 characters that have at least
# one space (avoids warnings on long URLs etc.).
check_long_lines() {
$debugging && echo "CALLED: check_long_lines($*)" >&2
local file=$1; shift
local n=$(awk <"$file" \
'/[[:space:]]/ && length > 80 { n++ } END { print n+0 }')
(($n > 0 )) &&
err "$n line(s) longer than 80 chars in $file"
}
# Checks made:
# * There is an OpenBSD RCS tag at the top.
# * No items with ${FULLPKGNAME} are allowed, except readme.
# * No empty lines.
check_plist_file() {
$debugging && echo "CALLED: check_plist_file($*)" >&2
[[ -f $1 ]] ||
err "$1 is not a file"
head -n 1 -- "$1" |
egrep -q '^@comment \$OpenBSD.*\$$' ||
err "$1 does not have \$OpenBSD\$ RCS tag at the top"
# Do not match just '${FULLPKGNAME}' because many ports use the
# following trick:
# @cwd ${LOCALBASE}/share/doc/pkg-readmes
# ${FULLPKGNAME}
egrep -v '^(share/doc/pkg-readmes/\$\{FULLPKGNAME\}|@comment .*)$' "$1" |
egrep '.\$\{FULLPKGNAME\}|\$\{FULLPKGNAME\}.' >&2 &&
err "$1 contains item(s) with \${FULLPKGNAME} in it, see above"
egrep -q '^[[:space:]]*$' "$1" && err "$1 contains empty lines"
}
# Checks made:
# * Every variable referenced by ${[A-Z]+} should be in ${SUBST_VARS}.
check_subst_vars() {
$debugging && echo "CALLED: check_subst_vars($*)" >&2
local F=$1; shift
local subst_cmd=$1; shift
# Add variables sometimes referenced in port docs.
eval "$subst_cmd" -DPATH=test -DWRKSRC=test <"$F" |
egrep '\$\{[A-Z]+\}' >&2 &&
err "looks like misspelled variables in $F, see above"
}
# Checks made:
# * Contains OpenBSD RCS tag at the top line.
# * No REVISION marks present in given file (unless in update mode).
# * Each REVISION mark presents only once.
# * BUILD_DEPENDS, MODULES and PERMIT_DISTFILES_FTP are not defined in
# VAR-subpkg manner.
# * No trailing whitespace.
# * SHARED_LIBS are not defined inside ".if" statements.
# * Variables are not assigned via "=" twice outside of .if statemets.
check_makefile() {
$debugging && echo "CALLED: check_makefile($*)" >&2
local F="$1"
check_trailing_whitespace "$F"
check_long_lines "$F"
head -n 1 -- "$F" |
egrep -q '^#[[:space:]]*\$OpenBSD.*\$' ||
err "$F does not have \$OpenBSD\$ RCS tag at the top"
local iflevel=0 l lnum=0 revs= t r mkvars= var duprevfound
# do not unset mkvars, having empty element(-s) is fine
unset revs[0]
local tab="$(print '\t')"
IFS=
while read -r l; do ((++lnum))
unset IFS
set -A t -- $l
duprevfound=false
case $l in
*(" ")REVISION*)
$existing_port ||
err "REVISION mark found at $F:$lnum"
var=${t[0]%=}
if ((${#revs[@]} > 0)); then
for r in "${revs[@]}"; do
if [[ $var == "$r" ]]; then
err "duplicated $r in $F"
# avoid dup error messages
duprevfound=true
break
fi
done
fi
revs[${#revs[@]}]=${t[0]}
;;
*(" ")@(BUILD_DEPENDS|MODULES|PERMIT_DISTFILES_FTP)-*)
err "${l%%-*} is not a subpackageble variable, see $F:$lnum"
;;
*(" ").*(" "|"$tab")if*)
((++iflevel))
;;
*(" ").*(" "|"$tab")endif*)
((iflevel--))
;;
*(" ")SHARED_LIBS*(" "|"$tab")*(+|:|!)=*)
if ((iflevel > 0)); then
err "should not be inside .if block ($F:$lnum): $l"
fi
;;
esac
if [[ $l == *(" ")+([A-Za-z0-9_-])*(" "|"$tab")?(\?)=* ]] &&
((iflevel == 0)) && ! $duprevfound; then
var=${t[0]%?(\?)=*}
for v in "${mkvars[@]}"; do
if [[ $v == "$var" ]]; then
err "duplicated assignment of $v" \
"at $F:$lnum"
break
fi
done
mkvars[${#mkvars[@]}]=$var
fi
IFS=
done <"$F"
unset IFS
}
# Checks made:
# * None of executable bits (111) are set on plain files.
check_perms_in_dir() {
$debugging && echo "CALLED: check_perms_in_dir($*)" >&2
(find -f "$1" -- -maxdepth 1 -type f \
\( -perm -100 -or -perm -010 -or -perm 001 \) \
</dev/null || true) |&
local F
while read -pr F; do
F=${F#./}
ignoring "$F" && continue
err "executable file: ${F#./}"
done
}
############################################################
# Run checks. Also calculate and show pkgpath variable,
# unless we're checking the ports tree root dir.
#
for D; do
if [[ $D == /* ]]; then
err "absolute path $D ignored"
continue
fi
if [[ $D == *(*/)..*(/*) ]]; then
err "too many .. in $D, skipping"
continue
fi
check_port_dir "$D"
done
if ! $rootrun; then
[[ -z $pkgpath ]] && pkgpath=${PWD##"$portsdir/"}
if [[ $pkgpath == "$PWD" ]]; then
cat >&2 <<EOE
${0##*/}: could not determine PKGPATH. Please help me with the -p option.
EOE
exit 2
fi
echo "$pkgpath"
fi
! $error