OpenPKG CVS Repository http://cvs.openpkg.org/ ____________________________________________________________________________
Server: cvs.openpkg.org Name: Ralf S. Engelschall Root: /e/openpkg/cvs Email: [EMAIL PROTECTED] Module: openpkg-re Date: 05-Mar-2003 17:21:17 Branch: HEAD Handle: 2003030516211700 Added files: openpkg-re speclint.pl Log: first cut for a forthcoming OpenPKG .spec file lint utility Summary: Revision Changes Path 1.1 +314 -0 openpkg-re/speclint.pl ____________________________________________________________________________ patch -p0 <<'@@ .' Index: openpkg-re/speclint.pl ============================================================================ $ cvs diff -u -r0 -r1.1 speclint.pl --- /dev/null 2003-03-05 17:21:17.000000000 +0100 +++ speclint.pl 2003-03-05 17:21:17.000000000 +0100 @@ -0,0 +1,314 @@ +#!/bin/sh -- # -*- perl -*- +eval 'exec perl -S $0 ${1+"$@"}' + if $running_under_some_shell; +## +## speclint -- OpenPKG .spec File Checker +## Copyright (c) 2003 The OpenPKG Project <http://www.openpkg.org/> +## Copyright (c) 2003 Ralf S. Engelschall <[EMAIL PROTECTED]> +## Copyright (c) 2003 Cable & Wireless Germany <http://www.cw.com/de> +## +## 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. +## +## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +## SUCH DAMAGE. +## + +require 5; +use Getopt::Long; +use IO; +use strict; + +# program information +my $progname = "speclint"; +my $progvers = "0.0.1"; + +# parameters (defaults) +my $version = 0; +my $verbose = 0; +my $help = 0; +my $check = 'all'; +my $tmpdir = ($ENV{TMPDIR} || $ENV{TEMPDIR} || "/tmp") . "/$progname"; +my $rpm = 'rpm'; + +# exception handling support +$SIG{__DIE__} = sub { + my ($err) = @_; + $err =~ s|\s+at\s+.*||s if (not $verbose); + print STDERR "$progname:ERROR: $err ". ($! ? "($!)" : "") . "\n"; + exit(1); +}; + +# command line parsing +Getopt::Long::Configure("bundling"); +my $result = GetOptions( + 'V|version' => \$version, + 'v|verbose' => \$verbose, + 'h|help' => \$help, + 'c|check=s' => \$check, + 't|tmpdir=s' => \$tmpdir, + 'r|rpm=s' => \$rpm, +) || die "option parsing failed"; +if ($help) { + print "Usage: $progname [options] [SPECFILE ...]\n" . + "Available options:\n" . + " -v,--verbose enable verbose run-time mode\n" . + " -h,--help print out this usage page\n" . + " -c,--check=CHECKS select checks to perform (default='all')\n" . + " -r,--rpm=FILE filesystem path to RPM program\n" . + " -t,--tmpdir=PATH filesystem path to temporary directory\n" . + " -V,--version print program version\n" . + exit(0); +} +if ($version) { + print "OpenPKG $progname $progvers\n"; + exit(0); +} + +# verbose message printing +sub msg_verbose { + my ($msg) = @_; + print STDERR "$msg\n" if ($verbose); +} + +# warning message printing +sub msg_warning { + my ($msg) = @_; + print STDERR "$progname:WARNING: $msg\n"; +} + +# error message printing +sub msg_error { + my ($msg) = @_; + print STDERR "$progname:ERROR: $msg\n"; +} + +# determine check list +my @check_list = (qw( + blank + comment + license + header +)); +my @checks = (); +if ($check eq 'all') { + @checks = @check_list; +} +else { + foreach my $c (split(/,/, $check)) { + if (not grep($c, @check_list)) { + die "invalid check \"$c\""; + } + push(@checks, $c); + } +} + +# iterate over all .spec files +foreach my $filename (@ARGV) { + my $io = new IO::File "<$filename" + || "unable to open file \"$filename\" for reading"; + my $spec; { local $/ = undef; $spec = <$io>; } + $io->close; + + foreach my $check (@checks) { + eval "\&check_$check(\$filename, \$spec);"; + } +} +exit(0); + +## _________________________________________________________________ +## +## COMMON SUBROUTINES +## _________________________________________________________________ +## + +sub lines { + my ($txt) = @_; + my $l = 0; + $txt =~ s|\n|$l++, ''|sge; + return $l; +} + +sub lint_message { + my ($type, $file, $done, $this, $msg) = @_; + my $start = &lines($done) + 1; + my $end = $start + &lines($this); + my $pos = $start; + if ($end > $start) { + $pos .= "-". $end; + } + printf("%s: %s:%s: %s\n", $type, $file, $pos, $msg); +} + +sub lint_warning { + my ($file, $done, $this, $msg) = @_; + &lint_message("WARNING", $file, $done, $this, $msg); +} + +sub lint_error { + my ($file, $done, $this, $msg) = @_; + &lint_message("ERROR", $file, $done, $this, $msg); +} + +## _________________________________________________________________ +## +## CHECK "blank": whitespace and blank lines +## _________________________________________________________________ +## + +sub check_blank { + my ($file, $spec) = @_; + + # check for CR-LF combination + my $done = ''; + my $todo = $spec; + while ($todo =~ m/\r\n/s) { + $done .= $`; + &lint_warning($file, $done, $&, "carriage-return (CR, 0x0d) line-feed (NL, 0x0a) combination (expected just line-feed)"); + $todo = $'; + } + + # check for multiple blank lines + $done = ''; + $todo = $spec; + while ($todo =~ m/(\r?\n[ \t]*){3,}/s) { + $done .= $`; + &lint_warning($file, $done, $&, "multiple subsequent blank lines (expected single blank line)"); + $todo = $'; + } + + # check for trailing whitespaces + $done = ''; + $todo = $spec; + while ($todo =~ m/[ \t]+\r?\n/s) { + $done .= $`; + &lint_warning($file, $done, $&, "trailing whitespace (expected none)"); + $todo = $'; + } +} + +## _________________________________________________________________ +## +## CHECK "comment": sharp-comments +## _________________________________________________________________ +## + +sub check_comment { + my ($file, $spec) = @_; + + # check for comment indentation + my $done = ''; + my $todo = $spec; + while ($todo =~ m/^([ \t]*)(#+)([ \t]*)(.*?)$/m) { + $done .= $`; + my $this = $&; + $todo = $'; + my ($lead, $sharp, $pad, $text) = ($1, $2, $3, $4); + if (length($lead) % 2 != 0) { + &lint_warning($file, $done, $this, "incorrect comment indentation (expected a multiple of 2 spaces)"); + } + if (length($lead) > 1 && length($sharp) > 1) { + &lint_warning($file, $done, $this, "indented comment has introduced with multiple sharps (expected single sharp character)"); + } + if (length($pad.$text) > 0 && length($sharp.$pad) % 4 != 0) { + &lint_warning($file, $done, $this, "incorrect comment text padding (expected a multiple of 4 sharps or spaces)"); + } + if (length($pad) == 0 && length($text) > 0) { + &lint_warning($file, $done, $this, "missing leading space before comment text (expected padding spaces)"); + } + if (length($pad) > 0 && length($text) == 0) { + &lint_warning($file, $done, $this, "empty comment text (expected a reasonable text)"); + } + } +} + +## _________________________________________________________________ +## +## CHECK "license": license header +## _________________________________________________________________ +## + +sub check_license { + my ($file, $spec) = @_; + + my $re = ""; + $re .= "##\\n"; + $re .= "## [a-z][a-z0-9-]+\\.spec -- OpenPKG RPM Specification\\n"; + $re .= "## Copyright \\(c\\) 200[0-3]-200[0-3] Cable \\& Wireless Deutschland GmbH\\n"; + $re .= "## Copyright \\(c\\) 200[0-3]-200[0-3] The OpenPKG Project <http://www\\.openpkg\\.org/>\\n"; + $re .= "## Copyright \\(c\\) 200[0-3]-200[0-3] Ralf S\\. Engelschall <[EMAIL PROTECTED]>\\n"; + $re .= "##\\n"; + $re .= "## Permission to use, copy, modify, and distribute this software for\\n"; + $re .= "## any purpose with or without fee is hereby granted, provided that\\n"; + $re .= "## the above copyright notice and this permission notice appear in all\\n"; + $re .= "## copies\\.\\n"; + $re .= "##\\n"; + $re .= "## THIS SOFTWARE IS PROVIDED \\`\\`AS IS'' AND ANY EXPRESSED OR IMPLIED\\n"; + $re .= "## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\\n"; + $re .= "## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED\\.\\n"; + $re .= "## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR\\n"; + $re .= "## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\\n"; + $re .= "## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES \\(INCLUDING, BUT NOT\\n"; + $re .= "## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\\n"; + $re .= "## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION\\) HOWEVER CAUSED AND\\n"; + $re .= "## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\\n"; + $re .= "## OR TORT \\(INCLUDING NEGLIGENCE OR OTHERWISE\\) ARISING IN ANY WAY OUT\\n"; + $re .= "## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\\n"; + $re .= "## SUCH DAMAGE\\.\\n"; + $re .= "##\\n"; + if ($spec !~ m|^$re|os) { + &lint_warning($file, "", "", "invalid license header"); + } +} + +## _________________________________________________________________ +## +## CHECK "header": RPM headers +## _________________________________________________________________ +## + +sub check_header { + my ($file, $spec) = @_; + + my @headers = (qw( + Name Summary URL Vendor Packager Distribution Group License Version Release + Source\d+ Patch\d+ + Prefix BuildRoot + BuildPreReq PreReq + AutoReq AutoReqProv + Provides Conflicts + )); + + # check for comment indentation + my $done = ''; + my $todo = $spec; + while ($todo =~ m/^(\S+):([ \t]*)(.*?)$/m) { + $done .= $`; + my $this = $&; + $todo = $'; + my ($header, $pad, $value) = ($1, $2, $3); + my $ok = 0; + foreach my $h (@headers) { + if ($header =~ m|^$h$|s) { + $ok = 1; + last; + } + } + if (not $ok) { + &lint_error($file, $done, $this, "invalid header \"$header\""); + } + } +} + @@ . ______________________________________________________________________ The OpenPKG Project www.openpkg.org CVS Repository Commit List [EMAIL PROTECTED]