From owner-ports-jp@jp.freebsd.org  Thu Aug  7 23:30:00 1997
Received: by jaz.jp.freebsd.org (8.8.7+2.7Wbeta5/8.7.3) id XAA03984
	Thu, 7 Aug 1997 23:30:00 +0900 (JST)
Received: by jaz.jp.freebsd.org (8.8.7+2.7Wbeta5/8.7.3) with ESMTP id XAA03976
	for <ports-jp@jp.freebsd.org>; Thu, 7 Aug 1997 23:29:59 +0900 (JST)
From: itojun@itojun.org
Received: from localhost (itojun@localhost [127.0.0.1]) by itojun.csl.sony.co.jp (8.8.5/3.3W3) with ESMTP id XAA27189 for <ports-jp@jp.freebsd.org>; Thu, 7 Aug 1997 23:29:37 +0900 (JST)
To: ports-jp@jp.freebsd.org
X-Template-Reply-To: itojun@itojun.org
X-Template-Return-Receipt-To: itojun@itojun.org
X-PGP-Fingerprint: F8 24 B4 2C 8C 98 57 FD  90 5F B4 60 79 54 16 E2
References: <199708070743.QAA01418@tippr00.info.eng.niigata-u.ac.jp>
In-reply-to: =?ISO-2022-JP?B?GyRCRU9KVRsoQiAbJEI9YUxpGyhC?= / WATANABE Junya
	 <junya@tippr.info.eng.niigata-u.ac.jp>'s message of 07 Aug 1997 16:42:57
	 +0900.
	<199708070743.QAA01418@tippr00.info.eng.niigata-u.ac.jp>
X-Mailer: comp (MHng project) version 1997/08/04 03:38:46, by Jun-ichiro Itoh
MIME-Version: 1.0
Content-type: text/plain; charset=iso-2022-jp
Content-transfer-encoding: 7bit
Content-ID: <top.870964177.27181@itojun.csl.sony.co.jp>
Date: Thu, 07 Aug 1997 23:29:37 +0900
Message-ID: <27186.870964177@itojun.csl.sony.co.jp>
Reply-To: ports-jp@jp.freebsd.org
Precedence: bulk
X-Distribute: distribute [version 2.1 (Alpha) patchlevel=20]
X-Sequence: ports-jp 1518
Subject: [ports-jp 1518] Re: portlint 1.43 port available on cvs tree
Errors-To: owner-ports-jp@jp.freebsd.org
Sender: owner-ports-jp@jp.freebsd.org

>> 	$B$b$&$$$A$I4V0c$($k$H$P$+$J$N$G3NG'$7$^$9$,!"(BPLIST$B$N=g=xE*$K$O(B
>> @unexec install-info --delete
>> info/hoge.info
>> @exec install-info
>> 	$B$G$($($N$G$9$M(B?
>$B$3$&$G$$$$$H;W$$$^$9!%(B

	$B$3$s$J$H$3$m$G$I$&$G$9$+(B?

itojun



#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.2).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1997-08-07 23:29 JST by <itojun@itojun.csl.sony.co.jp>.
# Source directory was `/export1/itojun/archive/portlint'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#  24694 -rw-r--r-- portlint.pl
#
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
  if test "$gettext_dir" = FAILED && test -f $dir/gettext \
     && ($dir/gettext --version >/dev/null 2>&1)
  then
    set `$dir/gettext --version 2>&1`
    if test "$3" = GNU
    then
      gettext_dir=$dir
    fi
  fi
  if test "$locale_dir" = FAILED && test -f $dir/shar \
     && ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
  then
    locale_dir=`$dir/shar --print-text-domain-dir`
  fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
  echo=echo
else
  TEXTDOMAINDIR=$locale_dir
  export TEXTDOMAINDIR
  TEXTDOMAIN=sharutils
  export TEXTDOMAIN
  echo="$gettext_dir/gettext -s"
fi
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  $echo 'WARNING: not restoring timestamps.  Consider getting and'
  $echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
if mkdir _sh27172; then
  $echo 'x -' 'creating lock directory'
else
  $echo 'failed to create lock directory'
  exit 1
fi
# ============= portlint.pl ==============
if test -f 'portlint.pl' && test "$first_param" != -c; then
  $echo 'x -' SKIPPING 'portlint.pl' '(file already exists)'
else
  $echo 'x -' extracting 'portlint.pl' '(text)'
  sed 's/^X//' << 'SHAR_EOF' > 'portlint.pl' &&
#! /usr/bin/perl
#
# portlint - lint for port directory
# implemented by:
#	Jun-ichiro itojun Itoh <itojun@itojun.org>
#	Yoshishige Arai <ryo2@on.rim.or.jp>
# visit ftp://ftp.foretune.co.jp/pub/tools/portlint/ for latest version.
#
# Copyright(c) 1997 by Jun-ichiro Itoh <itojun@itojun.org>.
# All rights reserved.
# Freely redistributable.  Absolutely no warranty.
#
# $Id: portlint.pl,v 1.44 1997/08/07 14:29:06 itojun Exp $
#
X
$err = $warn = 0;
$extrafile = $parenwarn = $committer = $verbose = $newport = 0;
$portdir = '.';
select(STDERR);
while (@ARGV > 0) {
X	$_ = shift;
X	/^-h/ && do {
X		($prog) = ($0 =~ /([^\/]+)$/);
X		print <<EOF;
usage: $prog [-avN] [port_directory]
X	-a	additional check for scripts/* and pkg/*
X	-b	warn \$(VARIABLE)
X	-c	committer mode
X	-v	verbose mode
X	-N	writing a new port
EOF
X		exit 0;
X	};
X	/^-a/ && do {$extrafile = 1; next;};
X	/^-b/ && do {$parenwarn = 1; next;};
X	/^-c/ && do {$committer = 1; next;};
X	/^-v/ && do {$verbose = 1; next;};
X	/^-N/ && do {$newport = 1; next;};
X	$portdir = $_;
}
X
#
# just for safety.
#
if (! -d $portdir) {
X	print "FATAL: invalid directory $portdir specified.\n";
X	exit 1;
}
X
#
# variables for global checks.
#
$sharedocused = 0;
%manchapters = ();
%manlangs = ();
X
%predefined = ();
foreach $i (split("\n", <<EOF)) {
XXCONTRIB	ftp://ftp.x.org/contrib/
XXCONTRIB	ftp://crl.dec.com/pub/X11/contrib/
GNU		ftp://prep.ai.mit.edu/pub/gnu/
GNU		ftp://wuarchive.wustl.edu/systems/gnu/
PERL_CPAN	ftp://ftp.digital.com/pub/plan/perl/CPAN/modules/by-module/
PERL_CPAN	ftp://ftp.cdrom.com/pub/perl/CPAN/modules/by-module/
TEX_CTAN	ftp://ftp.cdrom.com/pub/tex/ctan/
TEX_CTAN	ftp://wuarchive.wustl.edu/packages/TeX/
TEX_CTAN	ftp://ftp.funet.fi/pub/TeX/CTAN/
TEX_CTAN	ftp://ftp.tex.ac.uk/public/ctan/tex-archive/
TEX_CTAN	ftp://ftp.dante.de/tex-archive/
SUNSITE		ftp://sunsite.unc.edu/pub/Linux/
SUNSITE		ftp://ftp.infomagic.com/pub/mirrors/linux/sunsite/
SUNSITE		ftp://ftp.funet.fi/pub/mirrors/sunsite.unc.edu/pub/Linux/
EOF
X	($j, $k) = split(/\t+/, $i);
X	$predefined{$k} = $j;
}
X
#
# check for files.
#
@checker = ('pkg/PLIST', 'pkg/COMMENT', 'pkg/DESCR', 'Makefile', 'files/md5');
%checker = ('pkg/PLIST', 'checkplist', 'pkg/COMMENT', 'checkdescr',
X	    'pkg/DESCR', 'checkdescr', 'Makefile', 'checkmakefile',
X	    'files/md5', 'TRUE');
if ($extrafile) {
X	foreach $i ((<$portdir/scripts/*>, <$portdir/pkg/*>)) {
X		next if (! -T $i);
X		$i =~ s/^$portdir\///;
X		next if (defined $checker{$i});
X		push(@checker, $i);
X		$checker{$i} = 'checkpathname';
X	}
}
foreach $i (<$portdir/patches/patch-??>) {
X	next if (! -T $i);
X	$i =~ s/^$portdir\///;
X	next if (defined $checker{$i});
X	push(@checker, $i);
X	$checker{$i} = 'checkpatch';
}
foreach $i (@checker) {
X	print "OK: checking $i.\n";
X	if (! -f "$portdir/$i") {
X		&perror("FATAL: no $i in \"$portdir\".");
X	} else {
X		$proc = $checker{$i};
X		&$proc($i) || &perror("Cannot open the file $i\n");
X		if ($i !~ /^patches\//) {
X			&checklastline($i)
X				|| &perror("Cannot open the file $i\n");
X		}
X	}
}
if ($committer) {
X	if (scalar(@_ = <$portdir/work/.*>) || -d "$portdir/work") {
X		&perror("WARN: be sure to cleanup $portdir/work ".
X			"before committing the port.");
X	}
}
if ($err || $warn) {
X	print "$err fatal errors and $warn warnings found.\n"
} else {
X	print "looks fine.\n";
}
exit $err;
X
#
# pkg/COMMENT, pkg/DESCR
#
sub checkdescr {
X	local($file) = @_;
X	local(%maxcnt) = ('pkg/COMMENT', 1, 'pkg/DESCR', 24);
X	local(%errmsg) = ('pkg/COMMENT', "must be one-liner.",
X			  'pkg/DESCR',	"exceeds $maxcnt{'pkg/DESCR'} lines, ".
X					"make it shorter if possible.");
X	local($longlines, $linecnt, $tmp) = (0, 0, "");
X
X	open(IN, "< $portdir/$file") || return 0;
X	while (<IN>) {
X		$linecnt++;
X		$longlines++ if (80 < length($_));
X		$tmp .= $_;
X	}
X	if ($linecnt > $maxcnt{$file}) {
X		&perror("WARN: $file $errmsg{$file}".
X			"(currently $linecnt lines)");
X	} else {
X		print "OK: $file has $linecnt lines.\n" if ($verbose);
X	}
X	if ($longlines > 0) {
X		&perror("WARN: $i includes lines that exceed 80 charactors.");
X	}
X	if ($tmp =~ /[\033\200-\377]/) {
X		&perror("WARN: pkg/DESCR includes iso-8859-1, or ".
X			"other local characters.  $file should be".
X			"plain ascii file.");
X	}
X	close(IN);
}
X
#
# pkg/PLIST
#
sub checkplist {
X	local($file) = @_;
X	local($curdir) = ('/usr/local');	# XXX
X	local($inforemoveseen, $infoinstallseen, $infoseen) = (0, 0, 0);
X	local($infobeforeremove, $infoafterinstall) = (0, 0);
X
X	open(IN, "< $portdir/$file") || return 0;
X	while (<IN>) {
X		if ($_ =~ /[ \t]+\n?$/) {
X			&perror("WARN: whitespace before end of line, ".
X				"on line $. in $file.");
X		}
X
X		# make it easier to handle.
X		$_ =~ s/\s+$//;
X
X		$_ =~ s/\n$//;
X		if ($_ =~ /^\@/) {
X			if ($_ =~ /^\@(cwd|cd)[ \t]+(\S+)/) {
X				$curdir = $2;
X			} elsif ($_ =~ /^\@unexec[ \t]+rmdir/) {
X				&perror("WARN: use \"\@dirrm\" ".
X					"instead of \"\@unexec rmdir\".");
X			} elsif ($_ =~ /^\@exec[ \t]+install-info/) {
X				$infoinstallseen = $.;
X			} elsif ($_ =~ /^\@unexec[ \t]+install-info[ \t]+--delete/) {
X				$inforemoveseen = $.;
X			} elsif ($_ =~ /^\@(exec|unexec|dirrm|comment)/) {
X				; # no check made
X			} else {
X				&perror("WARN: pkg/PLIST $.: ".
X					"unknown PLIST directive \"$_\"");
X			}
X			next;
X		}
X
X		if ($_ =~ /^info\/.*info(-[0-9]+)?$/) {
X			$infoseen = $.;
X			$infoafterinstall++ if ($infoinstallseen);
X			$infobeforeremove++ if (!$inforemoveseen);
X		}
X
X		if ($_ =~ m#man/([^/]+/)?man([1-9ln])/([^\.]+\.[1-9ln])(\.gz)?$#) {
X			if ($4 eq '') {
X				&perror("FATAL: unpacked man file $3 listed ".
X					"on line $. in $file.");
X				$manchapters{$2}++;
X			}
X			if ($1 ne '') {
X				$manlangs{substr($1, 0, length($1) - 1)}++;
X			}
X		}
X
X		if ($curdir !~ m#^/usr/local#
X		 && $curdir !~ m#^/usr/X11R6#) {
X			&perror("WARN: pkg/PLIST $.: installing to ".
X				"directory $curdir discouraged. ".
X				"could you please avoid it?");
X		}
X
X		if ("$curdir/$_" =~ m#^/usr/local/share/doc#) {
X			print "OK: seen installation to share/doc in $file. ".
X				"($curdir/$_)\n" if ($verbose);
X			$sharedocused++;
X		}
X	}
X	if (!$infoseen) {
X		close(IN);
X		return;
X	}
X	if (!$infoinstallseen) {
X		&perror("FATAL: \"@exec install-info\" must be placed ".
X			"after all the info files.");
X	} elsif ($infoafterinstall) {
X		&perror("FATAL: move \"@exec install-info\" line to make sure ".
X			"that it is placed after all the info files. ".
X			"(currently on line $infoinstallseen in $file)");
X	}
X	if (!$inforemoveseen) {
X		&perror("FATAL: \"@unexec install-info --delete\" must ".
X			"be placed before any of the info files listed.");
X	} elsif ($infobeforeremove) {
X		&perror("FATAL: move \"@exec install-info --delete\" ".
X			"line to make sure ".
X			"that it is placed before any of the info files. ".
X			"(currently on line $inforemoveseen in $file)");
X	}
X	close(IN);
}
X
#
# misc files
#
sub checkpathname {
X	local($file) = @_;
X	local($whole);
X
X	open(IN, "< $portdir/$file") || return 0;
X	$whole = '';
X	while (<IN>) {
X		$whole .= $_;
X	}
X	&abspathname($whole, $file);
X	close(IN);
}
X
sub checklastline {
X	local($file) = @_;
X	local($whole);
X
X	open(IN, "< $portdir/$file") || return 0;
X	$whole = '';
X	while (<IN>) {
X		$whole .= $_;
X	}
X	if ($whole !~ /\n$/) {
X		&perror("FATAL: the last line has to be terminated by \\n, ".
X			"in $file.");
X	}
X	if ($whole =~ /\n([ \t]*\n)+$/) {
X		&perror("WARN: $file seems to have unnecessery blank lines.");
X	}
X
X	close(IN);
}
X
sub checkpatch {
X	local($file) = @_;
X	local($whole);
X
X	open(IN, "< $portdir/$file") || return 0;
X	$whole = '';
X	while (<IN>) {
X		$whole .= $_;
X	}
X	if ($committer && $whole =~ /\$[A-Z][^\$]*\$/) {
X		&perror("WARN: $file includes RCS tag. use binary mode (-ko) ".
X			"on commit/import.");
X	}
X
X	close(IN);
}
X
#
# Makefile
#
sub checkmakefile {
X	local($file) = @_;
X	local($rawwhole, $whole, $idx, @sections);
X	local($tmp, $tmp2);
X	local($i, $j, $k);
X	local(@varnames) = ();
X	local($distfiles, $pkgname, $distname, $extractsufx) = ('', '', '', '');
X	local($bogusdistfiles) = (0);
X	local($realwrksrc, $wrksrc, $nowrksubdir) = ('', '', '');
X
X	open(IN, "< $portdir/$file") || return 0;
X	$rawwhole = '';
X	$tmp = 0;
X	while (<IN>) {
X		if ($_ =~ /[ \t]+\n?$/) {
X			&perror("WARN: whitespace before end of line, ".
X				"on line $. in $file.");
X		}
X		if ($_ =~ /^        /) {	# 8 spaces here!
X			&perror("WARN: use tab (not space) to make ".
X				"indentation, on line $. in $file.");
X		}
#
# I'm still not very convinced, for using this kind of magical word.
# 1. This kind of items are not important for Makefile;
#    portlint should not require any additional rule to Makefile.
#    portlint should simply implement items that are declared in Handbook.
# 2. If we have LINTSKIP, we can't stop people using LINTSKIP too much.
#    IMHO it is better to warn the user and let the user think twice,
#    than let the user escape from portlint.
# Uncomment this part if you are willing to use these magical words.
# Thu Jun 26 11:37:56 JST 1997
# -- itojun
#
#		if ($_ =~ /^# LINTSKIP\n?$/) {
#			print "OK: skipping from line $. in $file.\n"
#				if ($verbose);
#			$tmp = 1;
#			next;
#		}
#		if ($_ =~ /^# LINTAGAIN\n?$/) {
#			print "OK: check start again from line $. in $file.\n"
#				if ($verbose);
#			$tmp = 0;
#			next;
#		}
#		if ($_ =~ /# LINTIGNORE/) {
#			print "OK: ignoring line $. in $file.\n" if ($verbose);
#			next;
#		}
#		next if ($tmp);
X		$rawwhole .= $_;
X	}
X	close(IN);
X
X	#
X	# whole file: blank lines.
X	#
X	$whole = "\n" . $rawwhole;
X	print "OK: checking contiguous blank lines in $file.\n"
X		if ($verbose);
X	if ($whole =~ /\n\n\n/) {
X		&perror("FATAL: contiguous blank lines (> 2lines) found ".
X			"in $file.");
X	}
X
X	#
X	# whole file: $(VARIABLE)
X	#
X	if ($parenwarn) {
X		print "OK: checking for \$(VARIABLE).\n" if ($verbose);
X		if ($whole =~ /\$\([\w\d]+\)/) {
X			&perror("WARN: use \${VARIABLE}, instead of ".
X				"\$(VARIABLE).");
X		}
X	}
X
X	#
X	# whole file: IS_INTERACTIVE/NOPORTDOCS
X	#
X	$whole =~ s/\n#[^\n]*/\n/g;
X	$whole =~ s/\n\n+/\n/g;
X	print "OK: checking IS_INTERACTIVE.\n" if ($verbose);
X	if ($whole =~ /\nIS_INTERACTIVE/) {
X		if ($whole !~ /defined\((BATCH|FOR_CDROM)\)/) {
X			&perror("WARN: use of IS_INTERACTIVE discouraged. ".
X				"provide batch mode by using BATCH and/or ".
X				"FOR_CDROM.");
X		}
X	}
X	print "OK: checking for use of NOPORTDOCS.\n" if ($verbose);
X	if ($sharedocused && $whole !~ /defined\(NOPORTDOCS\)/
X	 && $whole !~ m#(\$[\{\(]PREFIX[\}\)]|/usr/local)/share/doc#) {
X		&perror("WARN: use \".if !defined(NOPORTDOCS)\" to wrap ".
X			"installation of files into /usr/local/share/doc.");
X	}
X
X	#
X	# whole file: direct use of command names
X	#
X	print "OK: checking direct use of command names.\n" if ($verbose);
X	foreach $i (split(/\s+/, <<EOF)) {
awk basename cat cp echo false gmake grep gzcat install
ldconfig md5 mkdir mv patch rm rmdir sed setenv touch tr xmkmf
EOF
X		$cmdnames{$i} = "\$\{\U$i\E\}";
X	}
X	$cmdnames{'gunzip'} = '${GUNZIP_CMD}';
X	$cmdnames{'gzip'} = '${GZIP_CMD}';
X	#
X	# ignore parameter string to echo command.
X	# note that we leave the command as is, since we need to check the
X	# use of echo itself.
X	$j = $whole;
X	$j =~ s/([ \t][\@-]?)(echo|\$[\{\(]ECHO[\}\)]|\$[\{\(]ECHO_MSG[\}\)])[ \t]+("(\\'|\\"|[^"])*"|'(\\'|\\"|[^'])*')[ \t]*[;\n]/$1$2;/;
X	foreach $i (keys %cmdnames) {
X		if ($j =~ /[ \t\/]$i[ \t\n;]/) {
X			&perror("WARN: possible direct use of command \"$i\" ".
X				"found. use $cmdnames{$i} instead.");
X		}
X	}
X
X	#
X	# whole file: full path name
X	#
X	&abspathname($whole, $file);
X
X	#
X	# break the makefile into sections.
X	#
X	@sections = split(/\n\n+/, $rawwhole);
X	for ($i = 0; $i < scalar(@sections); $i++) {
X		if ($sections[$i] !~ /\n$/) {
X			$sections[$i] .= "\n";
X		}
X	}
X	$idx = 0;
X
X	#
X	# section 1: comment lines.
X	#
X	print "OK: checking comment section of $file.\n" if ($verbose);
X	@linestocheck = split("\n", <<EOF);
Whom
Version [rR]equired
Date [cC]reated
(New )?[pP]orts [cC]ollection [mM]akefile [fF]or
EOF
X	$tmp = $sections[$idx++];
X	$tmp = "\n" . $tmp;	# to make the begin-of-line check easier
X
X	if ($tmp =~ /\n[^#]/) {
X		&perror("FATAL: non-comment line in comment section of $file.");
X	}
X	foreach $i (@linestocheck) {
X		$j = $i;
X		$j =~ s/\(.*\)\?//g;
X		$j =~ s/\[(.)[^\]]*\]/$1/g;
X		if ($tmp !~ /# $i:[ \t]+\S+/) {
X			&perror("FATAL: no \"$j\" line in ".
X				"comment section of $file.");
X		} else {
X			print "OK: \"$j\" seen in $file.\n" if ($verbose);
X		}
X	}
X	if ($tmp !~ /#\n# \$Id([^\$]*)\$\n/) {
X		&perror("FATAL: no \$Id\$ line in $file comment section.");
X	} else {
X		print "OK: \$Id\$ seen in $file.\n" if ($verbose);
X		if ($1 ne '') {
X			if ($verbose || $newport) {	# XXX
X				&perror("WARN: ".
X				($newport ? 'for new port, '
X					  : 'is it a new port? if so, ').
X				"make \$Id\$ tag in comment section empty ".
X				"to make CVS happy.");
X			}
X		}
X	}
X
X	#
X	# for the rest of the checks, comment lines are not important.
X	#
X	for ($i = 0; $i < scalar(@sections); $i++) {
X		$sections[$i] =~ s/\n#[^\n]*//g;
X		$sections[$i] =~ s/\n\n+/\n/g;
X		$sections[$i] =~ s/\\\n/ /g;
X	}
X
X	#
X	#
X	# section 2: DISTNAME/PKGNAME/...
X	#
X	print "OK: checking first section of $file. (DISTNAME/...)\n"
X		if ($verbose);
X	$tmp = $sections[$idx++];
X
X	# check the order of items.
X	&checkorder('DISTNAME', $tmp, split(/\s+/, <<EOF));
DISTNAME PKGNAME CATEGORIES MASTER_SITES MASTER_SITE_SUBDIR
EXTRACT_SUFX DISTFILES
EOF
X
X	# check the items that has to be there.
X	$tmp = "\n" . $tmp;
X	foreach $i ('DISTNAME', 'CATEGORIES') {
X		if ($tmp !~ /\n$i[+?]?=/) {
X			&perror("FATAL: $i has to be there.");
X		}
X	}
X
X	# check the URL
X	if ($tmp =~ /\nMASTER_SITES[+?]?=[ \t]*([^\n]*)\n/
X	 && $1 !~ /^[ \t]*$/) {
X		print "OK: seen MASTER_SITES, sanity checking URLs.\n"
X			if ($verbose);
X		@sites = split(/\s+/, $1);
X		foreach $i (@sites) {
X			if ($i =~ m#^\w+://#) {
X				if ($i !~ m#/$#) {
X					&perror("FATAL: URL \"$i\" should ".
X						"end with \"/\".");
X				}
X				unless (&is_predefined($i)) {
X					print "OK: URL \"$i\" okey.\n"
X						if ($verbose);
X				}
X			} else {
X				print "OK: non-URL \"$i\" okey.\n"
X					if ($verbose);
X			}
X		}
X	} else {
X		&perror("WARN: no MASTER_SITES found. is it okey?");
X	}
X
X	# check vogus EXTRACT_SUFX.
X	if ($tmp =~ /\nEXTRACT_SUFX[+?]?=[ \t]*([^\n]*)\n/) {
X		print "OK: seen EXTRACT_SUFX, checking value.\n" if ($verbose);
X		$extractsufx = $1;
X		if ($extractsufx eq '.tar.gz') {
X			&perror("WARN: EXTRACT_SUFX is \".tar.gz.\" ".
X				"by default. you don't need to specify it.");
X		}
X	} else {
X		$extractsufx = '.tar.gz';
X	}
X
X	# check DISTFILES and related items.
X	# 
X	# if DISTFILES have only single item, it is better to avoid DISTFILES
X	# and to use combination of DISTNAME and EXTRACT_SUFX.
X	# example:
X	#	DISTFILES=package-1.0.tgz
X	# should be
X	#	DISTNAME=     package-1.0
X	#	EXTRACT_SUFX= .tgz
X	#
X	$distfiles = $1 if ($tmp =~ /\nDISTFILES[+?]?=[ \t]*([^\n]+)\n/);
X	$pkgname = $1 if ($tmp =~ /\nPKGNAME[+?]?=[ \t]*([^\n]+)\n/);
X	$distname = $1 if ($tmp =~ /\nDISTNAME[+?]?=[ \t]*([^\n]+)\n/);
X	if ($pkgname ne '' && $pkgname eq $distname) {
X		&perror("WARN: PKGNAME is \${DISTNAME} by default, ".
X			"you don't need to define PKGNAME.");
X	}
X	print "OK: sanity checking PKGNAME.\n" if ($verbose);
X	$i = ($pkgname eq '') ? $distname : $pkgname;
X	if ($i =~ /-([^-]+)$/) {
X		$j = $`;
X		$k = $1;
X		if ($k =~ /^pl[0-9]*$/
X		 || $k =~ /^[0-9]*[A-Za-z]?[0-9]*(\.[0-9]*[A-Za-z]?[0-9]*)*$/) {
X			print "OK: trailing part of PKGNAME\"-$k\" ".
X				"looks fine.\n" if ($verbose);
X		} else {
X			&perror("FATAL: version number part of PKGNAME ".
X				(($pkgname eq '')
X					? ', which is defined via DISTNAME, '
X					: '').
X				"looks illegal. should modify \"-$k\" to ".
X				"obey the handbook.");
X		}
X	} else {
X		&perror("FATAL: PKGNAME ".
X			(($pkgname eq '')
X				? ', which is defined via DISTNAME, '
X				: '').
X			"must come with version number.");
X	}
X	if ($committer && -f "$portdir/$i.tgz") {
X		&perror("WARN: be sure to remove $portdir/$i.tgz ".
X			"before committing the port.");
X	}
X	if ($distfiles =~ /^\S+$/) {
X		$bogusdistfiles++;
X		print "OK: seen DISTFILES with single item, checking value.\n"
X			if ($verbose);
X		&perror("WARN: use of DISTFILES with single file ".
X			"discouraged. distribution filename should be set by ".
X			"DISTNAME and EXTRACT_SUFX.");
X		if ($distfiles eq $distname . $extractsufx) {
X			&perror("WARN: definition of DISTFILES not necessery. ".
X				"DISTFILES is \${DISTNAME}/\${EXTRACT_SUFX} ".
X				"by default.");
X		}
X
X		# make an advice only in certain cases.
X		if ($pkgname ne '' && $distfiles =~ /^$pkgname([-\.].+)$/) {
X			&perror("WARN: how about \"DISTNAME=$pkgname\"".
X				(($1 eq '.tar.gz')
X					? ""
X					: " and \"EXTRACT_SUFX=$1\"").
X				", instead of DISTFILES?");
X		}
X	}
X
X	push(@varnames, split(/\s+/, <<EOF));
DISTNAME PKGNAME CATEGORIES MASTER_SITES MASTER_SITE_SUBDIR
EXTRACT_SUFX DISTFILES
EOF
X
X	#
X	# section 3: PATCH_SITES/PATCHFILES(optional)
X	#
X	print "OK: checking second section of $file, (PATCH*: optinal).\n"
X		if ($verbose);
X	$tmp = $sections[$idx];
X
X	if ($tmp =~ /(PATCH_SITES|PATCHFILES)/) {
X		&checkearlier($tmp, @varnames);
X
X		if ($tmp =~ /^PATCH_SITES=/) {
X			print "OK: seen PATCH_SITES.\n" if ($verbose);
X			$tmp =~ s/^[^\n]+\n//;
X		}
X		if ($tmp =~ /^PATCHFILES=/) {
X			print "OK: seen PATCHFILES.\n" if ($verbose);
X			$tmp =~ s/^[^\n]+\n//;
X		}
X		if ($tmp ne '') {
X			if ($tmp =~ /^([\w\d]+)/) {
X				$tmp = $1;
X				&perror("WARN: extra item placed in the ".
X					"PATCH_SITES section, ".
X					"for example, \"$tmp\".");
X			} else {
X				&perror("WARN: extra item placed in the ".
X					"PATCH_SITES section.");
X			}
X		}
X
X		push(@varnames, ('PATCH_SITES', 'PATCHFILES'));
X
X		$idx++;
X	}
X
X	#
X	# section 4: MAINTAINER
X	#
X	print "OK: checking third section of $file (MAINTAINER).\n"
X		if ($verbose);
X	$tmp = $sections[$idx++];
X
X	&checkearlier($tmp, @varnames);
X	$tmp = "\n" . $tmp;
X	if ($tmp =~ /\nMAINTAINER=[^\n]+/) {
X		$tmp =~ s/\nMAINTAINER=[^\n]+//;
X	} else {
X		&perror("FATAL: no MAINTAINER listed in $file.");
X	}
X	$tmp =~ s/\n\n+/\n/g;
X	if ($tmp !~ /^\s*$/) {
X		if ($tmp =~ /\n([\w\d]+)/) {
X			$tmp = $1;
X			&perror("FATAL: extra item placed in ".
X				"MAINTAINER section, ".
X				"for example, \"$tmp\".");
X		} else {
X			&perror("FATAL: extra item placed in ".
X				"MAINTAINER section.");
X		}
X	}
X	push(@varnames, 'MAINTAINER');
X
X	#
X	# section 5: *_DEPENDS (may not be there)
X	#
X	print "OK: checking fourth section of $file(*_DEPENDS).\n"
X		if ($verbose);
X	$tmp = $sections[$idx];
X
X	if ($tmp =~ /(LIB_|BUILD_|RUN_|FETCH_)?DEPENDS/) {
X		@linestocheck = split(/\s+/, <<EOF);
LIB_DEPENDS BUILD_DEPENDS RUN_DEPENDS FETCH_DEPENDS DEPENDS
EOF
X
X		&checkearlier($tmp, @varnames);
X
X		foreach $i (@linestocheck) {
X			$tmp =~ s/$i[?+]?=[^\n]+\n//g;
X		}
X		if ($tmp ne '' && $tmp ne $tmp2) {
X			&perror("WARN: extra item placed in ".
X				"*_DEPENDS section.");
X		}
X
X		push(@varnames, @linestocheck);
X
X		$idx++;
X	}
X
X	#
X	# Makefile 6: check the rest of file
X	#
X	print "OK: checking the rest of the $file.\n" if ($verbose);
X	$tmp = join("\n\n", @sections[$idx .. scalar(@sections)-1]);
X
X	&checkearlier($tmp, @varnames);
X
X	$tmp = "\n" . $tmp;	# to make the begin-of-line check easier
X
X	# check WRKSRC/NO_WRKSUBDIR
X	#
X	# do not use DISTFILES/DISTNAME to control over WRKSRC.
X	# DISTNAME is for controlling distribution filename.
X	# example:
X	#	DISTNAME= package
X	#	PKGNAME=  package-1.0
X	#	DISTFILES=package-1.0.tgz
X	# should be
X	#	DISTNAME=    package-1.0
X	#	EXTRACT_SUFX=.tgz
X	#	WRKSRC=      ${WRKDIR}/package
X	#
X	print "OK: checking WRKSRC.\n" if ($verbose);
X	$wrksrc = $nowrksubdir = '';
X	$wrksrc = $1 if ($tmp =~ /\nWRKSRC[+?]?=[ \t]*([^\n]*)\n/);
X	$nowrksubdir = $1 if ($tmp =~ /\nNO_WRKSUBDIR[+?]?=[ \t]*([^\n]*)\n/);
X	if ($nowrksubdir eq '') {
X		$realwrksrc = $wrksrc ? "$wrksrc/$distname"
X				      : "\${WRKDIR}/$distname";
X	} else {
X		$realwrksrc = $wrksrc ? $wrksrc : '${WRKDIR}';
X	}
X	print "OK: WRKSRC seems to be $realwrksrc.\n" if ($verbose);
X
X	if ($nowrksubdir eq '') {
X		print "OK: no NO_WRKSUBDIR, checking value of WRKSRC.\n"
X			if ($verbose);
X		if ($wrksrc eq 'work' || $wrksrc =~ /^$[\{\(]WRKDIR[\}\)]/) {
X			&perror("WARN: WRKSRC is set to meaningless value ".
X				"\"$1\".".
X				($nowrksubdir eq ''
X					? " use \"NO_WRKSUBDIR=yes\" instead."
X					: ""));
X		}
X		if ($bogusdistfiles) {
X			if ($distname ne '' && $wrksrc eq '') {
X			    &perror("WARN: do not use DISTFILES and DISTNAME".
X				"to control WRKSRC. how about ".
X				".\"WRKSRC=\${WRKDIR}/$distname\"?");
X			} else {
X			    &perror("WARN: DISTFILES/DISTNAME affects WRKSRC. ".
X				"take caution when changing them.");
X			}
X		}
X	} else {
X		print "OK: seen NO_WRKSUBDIR, checking value of WRKSRC.\n"
X			if ($verbose);
X		if ($wrksrc eq 'work' || $wrksrc =~ /^$[\{\(]WRKDIR[\}\)]/) {
X			&perror("WARN: definition of WRKSRC not necessery. ".
X				"WRKSRC is \${WRKDIR} by default.");
X		}
X	}
X
X	# check RESTRICTED/NO_CDROM/NO_PACKAGE
X	print "OK: checking RESTRICTED/NO_CDROM/NO_PACKAGE.\n" if ($verbose);
X	if ($committer && $tmp =~ /\n(RESTRICTED|NO_CDROM|NO_PACKAGE)[+?]?=/) {
X		&perror("WARN: \"$1\" found. do not forget to update ".
X			"ports/LEGAL.");
X	}
X
X	# check MAN[1-9LN]
X	print "OK: checking MAN[0-9LN].\n" if ($verbose);
X	foreach $i (keys %manchapters) {
X		if ($tmp !~ /MAN\U$i\E/) {
X			&perror("WARN: how about compressing manual chapter ".
X				"\"$i\" by using MAN\U$i\E?");
X		}
X	}
X	if ($tmp !~ /MANLANG/ && scalar(keys %manlangs)) {
X		$i = (keys %manlangs)[0];
X		&perror("WARN: how about using MANLANG for ".
X			"designating manual language, such as \"$i\"?");
X	}
X
X	1;
}
X
sub perror {
X	local(@msg) = @_;
X	if ($msg[0] =~ /^FATAL/) {
X		$err++;
X	} else {
X		$warn++;
X	}
X	print join("\n", @msg) . "\n";
}
X
sub checkorder {
X	local($section, $str, @order) = @_;
X	local(@items, $i, $j, $k, $invalidorder);
X
X	print "OK: checking the order of $section section.\n" if ($verbose);
X
X	@items = ();
X	foreach $i (split("\n", $tmp)) {
X		$i =~ s/[+?]?=.*$//;
X		push(@items, $i);
X	}
X
X	@items = reverse(@items);
X	$j = -1;
X	$invalidorder = 0;
X	while (scalar(@items)) {
X		$i = pop(@items);
X		$k = 0;
X		while ($k < scalar(@order) && $order[$k] ne $i) {
X			$k++;
X		}
X		if (@order[$k] eq $i) {
X			if ($k < $j) {
X				&perror("FATAL: $i appears out-of-order.");
X				$invalidorder++;
X			} else {
X				print "OK: seen $i, in order.\n" if ($verbose);
X			}
X			$j = $k;
X		} else {
X			&perror("FATAL: extra item \"$i\" placed in the ".
X				"$section section.");
X		}
X	}
X	if ($invalidorder) {
X		&perror("FATAL: order must be " . join('/', @order) . '.');
X	} else {
X		print "OK: $section section is ordered properly.\n"
X			if ($verbose);
X	}
}
X
sub checkearlier {
X	local($str, @varnames) = @_;
X	local($i);
X
X	print "OK: checking items that has to appear earlier.\n" if ($verbose);
X	foreach $i (@varnames) {
X		if ($str =~ /\n$i[?+]?=/) {
X			&perror("WARN: \"$i\" has to appear earlier in $file.");
X		}
X	}
}
X
sub abspathname {
X	local($str, $file) = @_;
X	local($s, $i, %cmdnames);
X	local($pre);
X
X	# ignore parameter string to echo command
X	$str =~ s/[ \t][\@-]?(echo|\$[\{\(]ECHO[\}\)]|\$[\{\(]ECHO_MSG[\}\)])[ \t]+("(\\'|\\"|[^"])*"|'(\\'|\\"|[^"])*')[ \t]*[;\n]//;
X
X	print "OK: checking direct use of full pathnames in $file.\n"
X		if ($verbose);
X	foreach $s (split(/\n+/, $str)) {
X		$i = '';
X		if ($s =~ /(^|[ \t\@'"-])(\/[\w\d])/) {
X			# suspected pathnames are recorded.
X			$i = $2 . $';
X			$pre = $` . $1;
X
X			if ($pre =~ /MASTER_SITE_SUBDIR/) {
X				# MASTER_SITE_SUBDIR lines are okey.
X				$i = '';
X			}
X		}
X		if ($i ne '') {
X			$i =~ s/\s.*$//;
X			$i =~ s/['"].*$//;
X			$i = substr($i, 0, 20) . '...' if (20 < length($i));
X			&perror("WARN: possible use of absolute pathname ".
X				"\"$i\", in $file.");
X		}
X	}
X
X	print "OK: checking direct use of pathnames, phase 1.\n" if ($verbose);
%cmdnames = split(/\n|\t+/, <<EOF);
/usr/opt	\${PORTSDIR} instead
/usr/ports	\${PORTSDIR} instead
/usr/local	\${PREFIX} or \${LOCALBASE}, as appropriate
/usr/X11	\${PREFIX} or \${X11BASE}, as appropriate
EOF
X	foreach $i (keys %cmdnames) {
X		if ($str =~ /$i/) {
X			&perror("WARN: possible direct use of \"$&\" ".
X				"found in $file. if so, use $cmdnames{$i}.");
X		}
X	}
X
X	print "OK: checking direct use of pathnames, phase 2.\n" if ($verbose);
%cmdnames = split(/\n|\t+/, <<EOF);
distfiles	\${DISTDIR} instead
pkg		\${PKGDIR} instead
files		\${FILESDIR} instead
scripts		\${SCRIPTDIR} instead
patches		\${PATCHDIR} instead
work		\${WRKDIR} instead
EOF
X	foreach $i (keys %cmdnames) {
X		if ($str =~ /(\.\/|\$[\{\(]\.CURDIR[\}\)]\/|[ \t])(\b$i)\//) {
X			&perror("WARN: possible direct use of \"$i\" ".
X				"found in $file. if so, use $cmdnames{$i}.");
X		}
X	}
}
X
sub is_predefined {
X	local($url) = @_;
X	local($site);
X	local($subdir);
X	if ($site = (grep($url =~ $_, keys %predefined))[0]) {
X		$url =~ /$site/;
X		$subdir = $';
X		&perror("WARN: how about using ".
X			"\${MASTER_SITE_$predefined{$site}} with ".
X			"\"MASTER_SITE_SUBDIR=$subdir\", instead of \"$url\?");
X		return &TRUE;
X	}
X	undef;
}
X
sub TRUE {1;}
SHAR_EOF
  $shar_touch -am 0807232997 'portlint.pl' &&
  chmod 0644 'portlint.pl' ||
  $echo 'restore of' 'portlint.pl' 'failed'
  if ( md5sum --help 2>&1 | grep 'sage: md5sum \[' ) >/dev/null 2>&1 \
  && ( md5sum --version 2>&1 | grep -v 'textutils 1.12' ) >/dev/null; then
    md5sum -c << SHAR_EOF >/dev/null 2>&1 \
    || $echo 'portlint.pl:' 'MD5 check failed'
57df9c71524974ef3a89d851d15e2393  portlint.pl
SHAR_EOF
  else
    shar_count="`LC_ALL= LC_CTYPE= LANG= wc -c < 'portlint.pl'`"
    test 24694 -eq "$shar_count" ||
    $echo 'portlint.pl:' 'original size' '24694,' 'current size' "$shar_count!"
  fi
fi
rm -fr _sh27172
exit 0
