From owner-ports-jp@jp.freebsd.org  Tue Jun 17 15:48:16 1997
Received: by jaz.jp.freebsd.org (8.8.5+2.7Wbeta5/8.7.3) id PAA27779
	Tue, 17 Jun 1997 15:48:16 +0900 (JST)
Received: by jaz.jp.freebsd.org (8.8.5+2.7Wbeta5/8.7.3) with ESMTP id PAA27774
	for <ports-jp@jp.freebsd.org>; Tue, 17 Jun 1997 15:48:12 +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 PAA03096 for <ports-jp@jp.freebsd.org>; Tue, 17 Jun 1997 15:48:11 +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: <199706170629.PAA23642@lavender.sanpei.org>
In-reply-to: MIHIRA "Sanpei" Yoshiro <sanpei@yy.cs.keio.ac.jp>'s message of
	 Tue, 17 Jun 1997 15:29:02 +0900.
	<199706170629.PAA23642@lavender.sanpei.org>
X-Mailer: comp (MHng project) version 1997/04/30 02:23:09, by Jun-ichiro Itoh
MIME-Version: 1.0
Content-type: text/plain; charset=iso-2022-jp
Content-transfer-encoding: 7bit
Content-ID: <top.866530090.3088@itojun.csl.sony.co.jp>
Date: Tue, 17 Jun 1997 15:48:11 +0900
Message-ID: <3093.866530091@itojun.csl.sony.co.jp>
Reply-To: ports-jp@jp.freebsd.org
Precedence: bulk
X-Distribute: distribute [version 2.1 (Alpha) patchlevel=19]
X-Sequence: ports-jp 1163
Subject: [ports-jp 1163] Re: ng (Nihongo Micro Gnu Emacs)
Errors-To: owner-ports-jp@jp.freebsd.org
Sender: owner-ports-jp@jp.freebsd.org

>$B$A$g$C$H8+$F$_$^$7$?(B. 
>  (3) DISTFILES $B$N>l=j(B
>$B$A$J$_$K(B, (3) $B$O(B portlint $B$G$bH/8+$G$-$^$;$s$G$7$?(B :-<

	$B$3$l$G!"$I$&$@(B! :-)

	Makefile$B$N%A%'%C%/J}K!!"$@$s$@$s1x$/$J$C$F$-$?$J$"(B...

itojun


---
#! /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.
#
# $Id: portlint.pl,v 1.11 1997/06/17 06:47:02 itojun Exp $
#

$err = $warn = 0;
$verbose = 0;
$portdir = '.';
select(STDERR);
while (@ARGV > 0) {
	$_ = shift;
	/^-h/ && do {
	    @tmp = split(/\//, $0); $prog= pop(@tmp);
	    print "usage: $prog [-v] [port_directory]\n\t-v\tverbose mode\n";
	    exit 0;
	};
	/^-v/ && do {$verbose = 1; next;};
	$portdir = $_;
}

#
# just for safety.
#
if (! -d $portdir) {
	print "FATAL: invalid directory $portdir specified.\n";
	exit 1;
}

#
# variables for global checks.
#
$sharedocused = 0;

#
# check for files.
#
@checker = ('pkg/PLIST', 'pkg/COMMENT', 'pkg/DESCR', 'Makefile');
%checker = ('pkg/PLIST', 'checkplist', 'pkg/COMMENT', 'checkdescr',
	    'pkg/DESCR', 'checkdescr', 'Makefile', 'checkmakefile');
foreach $i (@checker) {
	if (! -f "$portdir/$i") {
		&perror("FATAL: no $i in \"$portdir\".");
	} else {
		$proc = $checker{$i};
		&$proc($i) || &perror("Cannot open the file $i\n");
	}
}
if ($err || $warn) {
	print "$err fatal errors and $warn warnings found.\n"
} else {
	print "looks fine.\n";
}
exit $err;

#
# pkg/COMMENT, pkg/DESCR
#
sub checkdescr {
	local($file) = @_;
	local(%maxcnt) = ('pkg/COMMENT', 1, 'pkg/DESCR', 20);
	local(%errmsg) = ('pkg/COMMENT', "must be one-liner.",
			  'pkg/DESCR',	"exceeds $maxcnt{'pkg/DESCR'} lines, ".
					"could you trim it if possible?");
	local($longlines, $linecnt, $tmp) = (0, 0, "");

	print "OK: looking into $file.\n" if ($verbose);
	open(IN, "< $portdir/$file") || return 0;
	while (<IN>) {
		$linecnt++;
		$longlines++ if (80 < length($_));
		$tmp .= $_;
	}
	print "OK: $file has $linecnt lines.\n" if ($verbose);
	if ($linecnt > $maxcnt{$file}) {
		&perror("WARN: $file $errmsg{$file}");
	}
	if ($longlines > 0) {
		&perror("WARN: $i includes lines that exceed 80 charactors.");
	}
	if ($tmp =~ /[\033\200-\377]/) {
		&perror("WARN: pkg/DESCR includes iso-8859-1, or ".
			"other local characters.  $file should be".
			"plain ascii file.");
	}
	close(IN);
}

#
# pkg/PLIST
#
sub checkplist {
	local($file) = @_;
	local($curdir, $linecnt) = ('/usr/local', 0);	# XXX

	print "OK: looking into $file.\n" if ($verbose);
	open(IN, "< $portdir/$file") || return 0;
	while (<IN>) {
		$linecnt++;
		$_ =~ s/\n$//;
		if ($_ =~ /^\@/) {
			if ($_ =~ /^\@(cwd|cd)\s+(\S+)/) {
				$curdir = $2;
			} elsif ($_ =~ /^\@(exec|unexec|dirrm)/) {
				; # no check made
			} else {
				&perror("WARN: pkg/PLIST $linecnt: ".
					"unknown PLIST directive \"$_\"");
			}
			next;
		}

		if ($curdir !~ m#^/usr/local#
		 && $curdir !~ m#^/usr/X11R6#) {
			&perror("WARN: pkg/PLIST $linecnt: installing to ".
				"directory $curdir discouraged. ".
				"could you please avoid it?");
		}

		if ("$curdir/$_" =~ m#^/usr/local/share/doc#) {
			print "OK: seen installation to share/doc. ".
				"($curdir/$_)\n" if ($verbose);
			$sharedocused++;
		}
	}
	close(IN);
}

#
# Makefile
#
sub checkmakefile {
	local($file) = @_;
	local($tmp, $tmp2);
	local($i, $j, $k);
	local(@varnames) = ();
	print "OK: looking into $file.\n" if ($verbose);

	open(IN, "< $portdir/$file") || return 0;

	# Makefile 1: comment lines.
	print "OK: looking into comment section of $file.\n" if ($verbose);
	@linestocheck = split("\n", <<EOF);
Whom
Version [rR]equired
Date [cC]reated
(New )?[pP]orts [cC]ollection [mM]akefile [fF]or
EOF
	$tmp = '';
	while (<IN>) {
		last if (!/^#/);

		$tmp .= $_;
	}
	if ($_ ne "\n") {
		&perror("WARN: no blank line after $file comment section.");
	}

	foreach $i (@linestocheck) {
		$j = $i;
		$j =~ s/\(.*\)\?//g;
		$j =~ s/\[(.)[^\]]*\]/$1/g;
		if ($tmp !~ /# $i:[ 	]+\S+/) {
			&perror("FATAL: no \"$j\" line in ".
				"comment section of $file.");
		} else {
			print "OK: \"$j\" seen in $file.\n" if ($verbose);
		}
	}
	if ($tmp !~ /#\n# \$Id([^\$]*)\$\n/) {
		&perror("FATAL: no \$Id\$ line in $file comment section.");
	} else {
		print "OK: \$Id\$ seen in $file.\n" if ($verbose);
		if ($1 ne '') {
			&perror("WARN: is it a new port? ".
			"if so, make \$Id\$ tag in comment section empty ".
			"to make CVS happy.");
		}
	}

	# Makefile 2: DISTNAME/PKGNAME/...
	print "OK: looking into first section of $file. (DISTNAME/...)\n"
		if ($verbose);
	$tmp = &read_nextblock(*IN);

	# check the order of items.
	&checkorder('DISTNAME', $tmp, split(/\s+/, <<EOF));
DISTNAME PKGNAME CATEGORIES MASTER_SITES MASTER_SITE_SUBDIR
EXTRACT_SUFX DISTFILES
EOF

	# check the items that has to be there.
	$tmp = "\n" . $tmp;
	foreach $i ('DISTNAME', 'CATEGORIES') {
		if ($tmp !~ /\n$i[+?]?=/) {
			&perror("FATAL: $i has to be there.");
		}
	}

	# check the URL
	if ($tmp =~ /\nMASTER_SITES[+?]?=\s*([^\n]*)\n/) {
		print "OK: seen MASTER_SITES, sanity checking URLs.\n"
			if ($verbose);
		@sites = split(/\s+/, $1);
		foreach $i (@sites) {
			if ($i =~ m#^\w+://#) {
				if ($i !~ m#/$#) {
					&perror("FATAL: URL should end with ".
						"\"/\".")
				} else {
					print "OK: URL \"$i\" okey.\n"
						if ($verbose);
				}
			} else {
				print "OK: non-URL \"$i\" okey.\n"
					if ($verbose);
			}
		}
	} else {
		&perror("WARN: no $i found. is it okey?");
	}

	push(@varnames, split(/\s+/, <<EOF));
DISTNAME PKGNAME CATEGORIES MASTER_SITES MASTER_SITE_SUBDIR
EXTRACT_SUFX DISTFILES
EOF

	# Makefile 3: PATCH_SITES/PATCHFILES(optional)
	print "OK: looking into second section of $file, (PATCH*: optinal).\n"
		if ($verbose);
	$tmp = &read_nextblock(*IN);

	&checkearlier($tmp, @varnames);

	if ($tmp !~ /^MAINTAINER=/) {
		if ($tmp =~ /^PATCH_SITES=/) {
			print "OK: seen PATCH_SITES.\n" if ($verbose);
			$tmp =~ s/^[^\n]+\n//;
		}
		if ($tmp =~ /^PATCHFILES=/) {
			print "OK: seen PATCHFILES.\n" if ($verbose);
			$tmp =~ s/^[^\n]+\n//;
		}
		if ($tmp ne '') {
			if ($tmp =~ /^([A-Z0-9_]+)/) {
				$tmp = $1;
			}
			&perror("WARN: extra item placed in the ".
				"PATCH_SITES section, ".
				"for example, \"$tmp\".");
		}
		$tmp = &read_nextblock(*IN);
	}
	push(@varnames, ('PATCH_SITES', 'PATCHFILES'));

	# Makefile 4: MAINTAINER
	print "OK: looking into third section of $file (MAINTAINER).\n"
		if ($verbose);

	&checkearlier($tmp, @varnames);
	$tmp = "\n" . $tmp;
	if ($tmp =~ /\nMAINTAINER=[^\n]+\n/) {
		$tmp =~ s/\nMAINTAINER=[^\n]+//;
	} else {
		&perror("FATAL: no MAINTAINER listed in $file.");
	}
	if ($tmp !~ /^\s+$/) {
		if ($tmp =~ /\n([A-Z0-9_]+)/) {
			$tmp = $1;
		}
		&perror("FATAL: extra item placed in ".
			"MAINTAINER section, ".
			"for example, \"$tmp\".");
	}
	push(@varnames, 'MAINTAINER');

	# Makefile 5: *_DEPENDS (may not be there)
	print "OK: looking into fourth section of $file(*_DEPENDS).\n"
		if ($verbose);
	@linestocheck = split(/\s+/, <<EOF);
LIB_DEPENDS BUILD_DEPENDS RUN_DEPENDS FETCH_DEPENDS DEPENDS
EOF
	$tmp = $tmp2 = &read_nextblock(*IN);

	&checkearlier($tmp, @varnames);

	foreach $i (@linestocheck) {
		$tmp =~ s/$i=[^\n]+\n//g;
	}
	if ($tmp ne '' && $tmp ne $tmp2) {
		&perror("WARN: extra item placed in *_DEPENDS section.");
	}
	push(@varnames, split(/\s+/, <<EOF));
LIB_DEPENDS BUILD_DEPENDS RUN_DEPENDS FETCH_DEPENDS DEPENDS
EOF

	# Makefile 6: check the rest of file
	print "OK: looking into the rest of the $file.\n" if ($verbose);
	$tmp = $tmp2;
	while (<IN>) {
		$tmp .= $_;
	}
	$tmp = "\n" . $tmp;	# to make the begin-of-line check easier

	&checkearlier($tmp, @varnames);

	print "OK: checking IS_INTERACTIVE.\n" if ($verbose);
	if ($tmp =~ /\nIS_INTERACTIVE/) {
		&perror("WARN: use of IS_INTERACTIVE discouraged. ".
			"please try to avoid interactive port.");
	}

	print "OK: checking direct use of command names.\n" if ($verbose);
@cmdnames = split(/\s+/, <<EOF);
awk basename cat cp echo false grep gunzip gzcat
gzip ldconfig mkdir mv rm rmdir sed setenv tr install
EOF
	foreach $i (@cmdnames) {
		if ($tmp =~ /\n[ \t]+([^\n]*[ \t]+)?$i/) {
			&perror("WARN: direct use of \"$i\" discouraged. ".
				"use \$\{\U$i\E\} instead.");
		}
	}

	print "OK: checking for use of NOPORTDOCS.\n" if ($verbose);
	if ($sharedocused && $tmp !~ /defined\(NOPORTDOCS\)/) {
		&perror("WARN: use \".if defined(NOPORTDOCS)\" when you ".
			"install something into /usr/local/share/doc.");
	}


	close(IN);
}

sub read_nextblock {
	local(*F) = @_;
	local($s) = "";
	while (<F>) {
		last if ($_ eq "\n");
		next if ($_ =~ /^#/);
		$s .= $_;
	}
	$s =~ s/\\\n//g;
	$s;
}

sub perror {
	local(@msg) = @_;
	if ($msg[0] =~ /^FATAL/) {
		$err++;
	} else {
		$warn++;
	}
	print join("\n", @msg) . "\n";
}

sub checkorder {
	local($section, $str, @order) = @_;
	local(@items, $i, $j, $k, $invalidorder);

	print "OK: checking the order of $section section.\n" if ($verbose);

	@items = ();
	foreach $i (split("\n", $tmp)) {
		$i =~ s/[+?]?=.*$//;
		push(@items, $i);
	}

	@items = reverse(@items);
	$j = -1;
	$invalidorder = 0;
	while (scalar(@items)) {
		$i = pop(@items);
		$k = 0;
		while ($k < scalar(@order) && $order[$k] ne $i) {
			$k++;
		}
		if (@order[$k] eq $i) {
			if ($k < $j) {
				&perror("FATAL: $i appears out-of-order.");
				$invalidorder++;
			} else {
				print "OK: seen $i, in order.\n" if ($verbose);
			}
			$j = $k;
		} else {
			&perror("FATAL: extra item \"$i\" placed in the ".
				"$section section.");
		}
	}
	if ($invalidorder) {
		&perror("FATAL: order must be " . join('/', @order) . '.');
	} else {
		print "OK: $section section is ordered properly.\n"
			if ($verbose);
	}
}

sub checkearlier {
	local($str, @varnames) = @_;

	print "OK: checking items that has to appear earlier.\n" if ($verbose);
	foreach $i (@varnames) {
		if ($str =~ /\n$i[?+]?=/) {
			&perror("WARN: \"$i\" has to appear earlier in $file.");
		}
	}
}

sub TRUE {1;}
