pkgsrc-Changes archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

CVS commit: pkgsrc/pkgtools/lintpkgsrc/files



Module Name:    pkgsrc
Committed By:   rillig
Date:           Sat Jul 30 08:18:31 UTC 2022

Modified Files:
        pkgsrc/pkgtools/lintpkgsrc/files: lintpkgsrc.pl

Log Message:
lintpkgsrc: cleanup: sort subs from small to big

This way, perl properly checks the sub prototypes.

No functional change.


To generate a diff of this commit:
cvs rdiff -u -r1.27 -r1.28 pkgsrc/pkgtools/lintpkgsrc/files/lintpkgsrc.pl

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: pkgsrc/pkgtools/lintpkgsrc/files/lintpkgsrc.pl
diff -u pkgsrc/pkgtools/lintpkgsrc/files/lintpkgsrc.pl:1.27 pkgsrc/pkgtools/lintpkgsrc/files/lintpkgsrc.pl:1.28
--- pkgsrc/pkgtools/lintpkgsrc/files/lintpkgsrc.pl:1.27 Sat Jul 30 07:37:03 2022
+++ pkgsrc/pkgtools/lintpkgsrc/files/lintpkgsrc.pl      Sat Jul 30 08:18:31 2022
@@ -1,6 +1,6 @@
 #!@PERL5@
 
-# $NetBSD: lintpkgsrc.pl,v 1.27 2022/07/30 07:37:03 rillig Exp $
+# $NetBSD: lintpkgsrc.pl,v 1.28 2022/07/30 08:18:31 rillig Exp $
 
 # Written by David Brownlee <abs%netbsd.org@localhost>.
 #
@@ -22,321 +22,250 @@ use File::Basename;
 use IPC::Open3;
 use Cwd 'realpath', 'getcwd';
 
-# Buildtime configuration
-my $conf_make = '@MAKE@';
-my $conf_pkgsrcdir = '@PKGSRCDIR@';
-my $conf_prefix = '@PREFIX@';
-my $conf_sysconfdir = '@PKG_SYSCONFDIR@';
-
-my (
-    $pkglist,                  # list of Pkg packages
-    $pkg_installver,           # installed version of pkg_install pseudo-pkg
-    $default_vars,             # Set for Makefiles, inc PACKAGES & PKGSRCDIR
-    %opt,                      # Command line options
-    @matched_prebuiltpackages, # List of obsolete prebuilt package paths
-    @prebuilt_pkgdirs,         # Use to follow symlinks in prebuilt pkgdirs
-    %prebuilt_pkgdir_cache,    # To avoid symlink loops in prebuilt_pkgdirs
-);
+# PkgList is the master list of all packages in pkgsrc.
+#
+package PkgList;
 
-sub usage_and_exit();
-sub listdir($$);
-sub get_default_makefile_vars();
-sub fail($);
-sub parse_makefile_pkgsrc($);
+sub add($@) {
+       my $self = shift;
 
-# Horrible kludge to ensure we have a value for testing in conditionals, but
-# gets removed in the final evaluation
-my $magic_undefined = 'M_a_G_i_C_uNdEfInEd';
+       if (!$self->pkgs($_[0])) {
+               $self->{_pkgs}{ $_[0] } = new Pkgs $_[0];
+       }
+       $self->pkgs($_[0])->add(@_);
+}
 
-sub canonicalize_pkgname($) {
-       my ($pkgname) = @_;
+sub new($) {
+       my $class = shift;
+       my $self = {};
+       bless $self, $class;
+       return $self;
+}
 
-       $pkgname =~ s,^py\d+(?:pth|)-,py-,;
-       $pkgname =~ s,^ruby\d+-,ruby-,;
-       $pkgname =~ s,^php\d+-,php-,;
-       return $pkgname;
+sub numpkgver($) {
+       my $self = shift;
+       scalar($self->pkgver);
 }
 
-# Could speed up by building a cache of package names to paths, then processing
-# each package name once against the tests.
-sub check_prebuilt_packages() {
+sub pkgver($@) {
+       my $self = shift;
 
-       if ($_ eq 'distfiles' || $_ eq 'pkgsrc') {
-               # Skip these subdirs if present
-               $File::Find::prune = 1;
+       if (@_ == 0) {
+               my (@list);
+               foreach my $pkg ($self->pkgs) {
+                       push(@list, $pkg->pkgver);
+               }
+               return (@list);
+       }
 
-       } elsif (/(.+)-(\d.*)\.t[bg]z$/) {
-               my ($pkg, $ver) = ($1, $2);
+       if (defined $self->{_pkgs}{$_[0]}) {
+               return (@_ > 1)
+                   ? $self->{_pkgs}{$_[0]}->pkgver($_[1])
+                   : $self->{_pkgs}{$_[0]}->pkgver();
+       }
+       return;
+}
 
-               $pkg = canonicalize_pkgname($pkg);
+sub pkgs($@) {
+       my $self = shift;
 
-               my ($pkgs);
-               if ($pkgs = $pkglist->pkgs($pkg)) {
-                       my ($pkgver) = $pkgs->pkgver($ver);
+       if (@_) {
+               return $self->{_pkgs}{$_[0]};
+       } else {
+               return (sort { $a->pkg cmp $b->pkg } values %{$self->{_pkgs}});
+       }
+}
 
-                       if (!defined $pkgver) {
-                               if ($opt{p}) {
-                                       print "$File::Find::dir/$_\n";
-                                       push(@matched_prebuiltpackages, "$File::Find::dir/$_");
-                               }
+sub store($) {
+       my $self = shift;
+       my @pkgs = keys %{$self->{_pkgs}};
+       my ($cnt, $subcnt) = $self->count;
 
-                               # Pick probably the last version
-                               $pkgver = $pkgs->latestver;
-                       }
+       print("\$pkgcnt = $cnt;\n");
+       print("\$subpkgcnt = $subcnt;\n");
+       map($self->{_pkgs}{$_}->store, keys %{$self->{_pkgs}});
+}
 
-                       if ($opt{R} && $pkgver->var('RESTRICTED')) {
-                               print "$File::Find::dir/$_\n";
-                               push(@matched_prebuiltpackages, "$File::Find::dir/$_");
-                       }
+sub count($) {
+       my $self = shift;
+       my ($pkgcnt, $pkgsubcnt);
 
-                       if ($opt{O} && $pkgver->var('OSVERSION_SPECIFIC')) {
-                               print "$File::Find::dir/$_\n";
-                               push(@matched_prebuiltpackages, "$File::Find::dir/$_");
-                       }
-               }
+       map {
+               $pkgcnt++;
+               $pkgsubcnt += $self->{_pkgs}{$_}->count;
+       } keys %{$self->{_pkgs}};
+       wantarray ? ($pkgcnt, $pkgsubcnt) : $pkgcnt;
+}
 
-       } elsif (-d $_) {
-               if ($prebuilt_pkgdir_cache{"$File::Find::dir/$_"}) {
-                       $File::Find::prune = 1;
-                       return;
-               }
+# Pkgs is all versions of a given package (eg: apache-1.x and apache-2.x)
+#
+package Pkgs;
 
-               $prebuilt_pkgdir_cache{"$File::Find::dir/$_"} = 1;
-               if (-l $_) {
-                       my ($dest) = readlink($_);
+sub add($@) {
+       my $self = shift;
 
-                       if (substr($dest, 0, 1) ne '/') {
-                               $dest = "$File::Find::dir/$dest";
-                       }
-                       if (!$prebuilt_pkgdir_cache{$dest}) {
-                               push(@prebuilt_pkgdirs, $dest);
-                       }
-               }
-       }
+       $self->{_pkgver}{$_[1]} = new PkgVer @_;
 }
 
-# Dewey decimal verson number matching - or thereabouts
-# Also handles 'nb<N>' suffix (checked iff values otherwise identical)
-#
-sub deweycmp($$$) {
-       my ($match, $test, $val) = @_;
-       my ($cmp, $match_nb, $val_nb);
-
-       $match_nb = $val_nb = 0;
-       if ($match =~ /(.*)nb(.*)/) {
-               # Handle nb<N> suffix
-               $match = $1;
-               $match_nb = $2;
-       }
+sub new($@) {
+       my $class = shift;
+       my $self = {};
 
-       if ($val =~ /(.*)nb(.*)/) {
-               # Handle nb<N> suffix
-               $val = $1;
-               $val_nb = $2;
-       }
+       bless $self, $class;
+       $self->{_pkg} = $_[0];
+       return $self;
+}
 
-       $cmp = deweycmp_extract($match, $val);
+sub versions($) {
+       my $self = shift;
 
-       if (!$cmp) {
-               # Iff otherwise identical, check nb suffix
-               $cmp = deweycmp_extract($match_nb, $val_nb);
-       }
+       return sort { $b cmp $a } keys %{$self->{_pkgver}};
+}
 
-       debug("eval deweycmp $cmp $test 0\n");
-       eval "$cmp $test 0";
+sub pkg($) {
+       my $self = shift;
+       $self->{_pkg};
 }
 
-sub convert_to_standard_dewey(@) {
-       my ($elem, $underscore, @temp);
+sub pkgver($@) {
+       my $self = shift;
 
-       # According to the current implementation in pkg_install/lib/str.c
-       # as of 2002/06/02, '_' before a number, '.', and 'pl' get treated as 0,
-       # while 'rc' and 'pre' get treated as -1; beta as '-2', alpha as '-3'.
-       # Other characters are converted to lower
-       # case and then to a number: a->1, b->2, c->3, etc. Numbers stay the same.
-       # 'nb' is a special case that's already been handled when we are here.
-       foreach $elem (@_) {
-               if ($elem =~ /\d+/) {
-                       push(@temp, $elem);
-               } elsif ($elem =~ /^pl$/ or $elem =~ /^\.$/) {
-                       push(@temp, 0);
-               } elsif ($elem =~ /^_$/) {
-                       push(@temp, 0);
-               } elsif ($elem =~ /^pre$/) {
-                       push(@temp, -1);
-               } elsif ($elem =~ /^rc$/) {
-                       push(@temp, -1);
-               } elsif ($elem =~ /^beta$/) {
-                       push(@temp, -2);
-               } elsif ($elem =~ /^alpha$/) {
-                       push(@temp, -3);
-               } else {
-                       push(@temp, 0);
-                       push(@temp, ord($elem) - ord("a") + 1);
+       if (@_) {
+               if ($self->{_pkgver}{$_[0]}) {
+                       return ($self->{_pkgver}{$_[0]});
                }
+               return;
        }
-       @temp;
+       return sort { $b->ver() cmp $a->ver() } values %{$self->{_pkgver}};
 }
 
-sub deweycmp_extract($$) {
-       my ($match, $val) = @_;
-       my ($cmp, @matchlist, @vallist, $i, $len);
+sub latestver($) {
+       my $self = shift;
 
-       @matchlist = convert_to_standard_dewey(split(/(\D+)/, lc($match)));
-       @vallist = convert_to_standard_dewey(split(/(\D+)/, lc($val)));
-       $cmp = 0;
-       $i = 0;
-       if ($#matchlist > $#vallist) {
-               $len = $#matchlist;
-       } else {
-               $len = $#vallist;
-       }
-       while (!$cmp && ($i++ <= $len)) {
-               if (!@matchlist) {
-                       push(@matchlist, 0);
-               }
-               if (!@vallist) {
-                       push(@vallist, 0);
-               }
-               $cmp = (shift @matchlist <=> shift @vallist);
-       }
-       $cmp;
+       ($self->pkgver())[0];
 }
 
-sub fail($) {
+sub store($) {
+       my $self = shift;
 
-       print STDERR shift(), "\n";
-       exit(3);
+       print("\$pkgnum++;\n");
+       map($self->{_pkgver}{$_}->store, keys %{$self->{_pkgver}});
 }
 
-sub get_default_makefile_vars() {
-
-       chomp($pkg_installver = `pkg_info -V 2>/dev/null || echo 20010302`);
+sub count($) {
+       my $self = shift;
 
-       chomp($_ = `uname -srm`);
-       (
-           $default_vars->{OPSYS},
-           $default_vars->{OS_VERSION},
-           $default_vars->{MACHINE}
-       ) = (split);
-       if (!$default_vars->{MACHINE}) {
-               die('Unable to extract machine from uname');
-       }
+       scalar(keys %{$self->{_pkgver}});
+}
 
-       # Handle systems without uname -p  (NetBSD pre 1.4)
-       chomp($default_vars->{MACHINE_ARCH} = `uname -p 2>/dev/null`);
+# PkgVer is a unique package+version
+#
+package PkgVer;
 
-       if (!$default_vars->{MACHINE_ARCH}
-           && $default_vars->{OS_VERSION} eq 'NetBSD') {
-               chomp($default_vars->{MACHINE_ARCH} = `sysctl -n hw.machine_arch`);
-       }
+sub new($$$) {
+       my $class = shift;
+       my $self = {};
 
-       if (!$default_vars->{MACHINE_ARCH}) {
-               $default_vars->{MACHINE_ARCH} = $default_vars->{MACHINE};
-       }
+       bless $self, $class;
+       $self->{_pkg} = $_[0];
+       $self->{_ver} = $_[1];
+       return $self;
+}
 
-       $default_vars->{OBJECT_FMT} = 'x';
-       $default_vars->{LOWER_OPSYS} = lc($default_vars->{OPSYS});
+sub pkgname($) {
+       my $self = shift;
 
-       if ($opt{P}) {
-               $default_vars->{PKGSRCDIR} = realpath($opt{P});
-       } else {
-               $default_vars->{PKGSRCDIR} = $conf_pkgsrcdir;
-       }
+       $self->pkg . '-' . $self->ver;
+}
 
-       $default_vars->{DESTDIR} = '';
-       $default_vars->{LOCALBASE} = '/usr/pkg';
-       $default_vars->{X11BASE} = '/usr/X11R6';
+sub pkg($) {
+       my $self = shift;
 
-       my ($vars);
-       if (-f '/etc/mk.conf' && ($vars = parse_makefile_vars('/etc/mk.conf', undef))) {
-               foreach my $var (keys %{$vars}) {
-                       $default_vars->{$var} = $vars->{$var};
-               }
-       } elsif (-f "$conf_sysconfdir/mk.conf" &&
-           ($vars = parse_makefile_vars("$conf_sysconfdir/mk.conf", undef))) {
-               foreach my $var (keys %{$vars}) {
-                       $default_vars->{$var} = $vars->{$var};
-               }
-       }
+       $self->{_pkg};
+}
 
-       if ($opt{P}) {
-               $default_vars->{PKGSRCDIR} = realpath($opt{P});
-       }
+sub var($$$) {
+       my $self = shift;
+       my ($key, $val) = @_;
 
-       if ($opt{M}) {
-               $default_vars->{DISTDIR} = realpath($opt{M});
-       } else {
-               $default_vars->{DISTDIR} ||= $default_vars->{PKGSRCDIR} . '/distfiles';
-       }
+       (defined $val)
+           ? ($self->{$key} = $val)
+           : $self->{$key};
+}
 
-       if ($opt{K}) {
-               $default_vars->{PACKAGES} = realpath($opt{K});
-       }
+sub ver($) {
+       my $self = shift;
 
-       # Extract some variables from bsd.pkg.mk
-       my ($mkvars);
-       $mkvars = parse_makefile_vars(
-           "$default_vars->{PKGSRCDIR}/mk/bsd.pkg.mk",
-           "$default_vars->{PKGSRCDIR}/mk/scripts"
-       );
-       foreach my $varname (keys %{$mkvars}) {
-               if ($varname =~ /_REQD$/ || $varname eq 'EXTRACT_SUFX') {
-                       $default_vars->{$varname} = $mkvars->{$varname};
-               }
-       }
+       $self->{_ver};
+}
 
-       $default_vars->{PACKAGES} ||= $default_vars->{PKGSRCDIR} . '/packages';
+sub vars($) {
+       my $self = shift;
+
+       grep(!/^_(pkg|ver)$/, keys %{$self});
 }
 
-# Determine if a package version is current. If not, report correct version
-# if found
-#
-sub invalid_version($) {
-       my ($pkgmatch) = @_;
-       my ($fail, $ok);
-       my (@pkgmatches, @todo);
+sub store($) {
+       my $self = shift;
+       my $data;
 
-       @todo = ($pkgmatch);
+       ($data = $self->{_pkg}) =~ s/([\\\$\@\%\"])/\\$1/g;
+       print("\$pkgver = \$pkglist->add(\"$data\", \"");
 
-       # We handle {} here, everything else in package_globmatch
-       while ($pkgmatch = shift @todo) {
-               if ($pkgmatch =~ /(.*)\{([^{}]+)}(.*)/) {
-                       foreach (split(',', $2)) {
-                               push(@todo, "$1$_$3");
-                       }
-               } else {
-                       push(@pkgmatches, $pkgmatch);
-               }
+       ($data = $self->{_ver}) =~ s/([\\\$\@\%\"])/\\$1/g;
+       print("$data\"); __pkgcount(1);\n");
+
+       foreach ($self->vars) {
+               ($data = $self->{$_}) =~ s/([\\\$\@\%\"])/\\$1/g;
+               print("\$pkgver->var(\"$_\", \"$data\");\n");
        }
+}
 
-       foreach $pkgmatch (@pkgmatches) {
-               my ($pkg, $badver) = package_globmatch($pkgmatch);
+package main;
 
-               if (defined($badver)) {
-                       my ($pkgs);
+# Buildtime configuration
+my $conf_make = '@MAKE@';
+my $conf_pkgsrcdir = '@PKGSRCDIR@';
+my $conf_prefix = '@PREFIX@';
+my $conf_sysconfdir = '@PKG_SYSCONFDIR@';
 
-                       if ($pkgs = $pkglist->pkgs($pkg)) {
-                               $fail .=
-                                   "Version mismatch: '$pkg' $badver vs "
-                                       . join(',', $pkgs->versions) . "\n";
-                       } else {
-                               $fail .= "Unknown package: '$pkg' version $badver\n";
-                       }
-               } else {
+my (
+    $pkglist,                  # list of Pkg packages
+    $pkg_installver,           # installed version of pkg_install pseudo-pkg
+    $default_vars,             # Set for Makefiles, inc PACKAGES & PKGSRCDIR
+    %opt,                      # Command line options
+    @matched_prebuiltpackages, # List of obsolete prebuilt package paths
+    @prebuilt_pkgdirs,         # Use to follow symlinks in prebuilt pkgdirs
+    %prebuilt_pkgdir_cache,    # To avoid symlink loops in prebuilt_pkgdirs
+);
 
-                       # If we find one match, don't bitch about others
-                       $ok = 1;
-               }
+# Horrible kludge to ensure we have a value for testing in conditionals, but
+# gets removed in the final evaluation
+my $magic_undefined = 'M_a_G_i_C_uNdEfInEd';
+
+sub debug(@) {
+
+       ($opt{D}) && print STDERR 'DEBUG: ', @_;
+}
+
+sub verbose(@) {
+
+       if (-t STDERR) {
+               print STDERR @_;
        }
-       $ok && ($fail = undef);
-       $fail;
+}
+
+sub fail($) {
+
+       print STDERR shift(), "\n";
+       exit(3);
 }
 
 # List (recursive) non directory contents of specified directory
 #
 #TODO this entire sub should be replaced with direct calls to
 #     File::Find
+sub listdir($$);
 sub listdir($$) {
        my ($base, $dir) = @_;
        my ($thisdir);
@@ -363,321 +292,236 @@ sub listdir($$) {
        @list;
 }
 
-# Use pkg_info to list installed packages
-#
-sub list_installed_packages() {
-       my (@pkgs);
-
-       open(PKG_INFO, 'pkg_info -e "*" |') || fail("Unable to run pkg_info: $!");
-       while (defined(my $pkg = <PKG_INFO>)) {
-               chomp($pkg);
-               push(@pkgs, canonicalize_pkgname($pkg));
-       }
-       close(PKG_INFO);
-
-       @pkgs;
-}
-
-# List top level pkgsrc categories
-#
-sub list_pkgsrc_categories($) {
-       my ($pkgsrcdir) = @_;
-       my (@categories);
+sub canonicalize_pkgname($) {
+       my ($pkgname) = @_;
 
-       opendir(BASE, $pkgsrcdir) || die("Unable to opendir($pkgsrcdir): $!");
-       @categories =
-           grep(substr($_, 0, 1) ne '.'
-               && $_ ne 'CVS'
-               && -f "$pkgsrcdir/$_/Makefile",
-               readdir(BASE));
-       closedir(BASE);
-       @categories;
+       $pkgname =~ s,^py\d+(?:pth|)-,py-,;
+       $pkgname =~ s,^ruby\d+-,ruby-,;
+       $pkgname =~ s,^php\d+-,php-,;
+       return $pkgname;
 }
 
-# For a given category, list potentially valid pkgdirs
-#
-sub list_pkgsrc_pkgdirs($$) {
-       my ($pkgsrcdir, $cat) = @_;
-       my (@pkgdirs);
+sub convert_to_standard_dewey(@) {
+       my ($elem, $underscore, @temp);
 
-       if (!opendir(CAT, "$pkgsrcdir/$cat")) {
-               die("Unable to opendir($pkgsrcdir/cat): $!");
+       # According to the current implementation in pkg_install/lib/str.c
+       # as of 2002/06/02, '_' before a number, '.', and 'pl' get treated as 0,
+       # while 'rc' and 'pre' get treated as -1; beta as '-2', alpha as '-3'.
+       # Other characters are converted to lower
+       # case and then to a number: a->1, b->2, c->3, etc. Numbers stay the same.
+       # 'nb' is a special case that's already been handled when we are here.
+       foreach $elem (@_) {
+               if ($elem =~ /\d+/) {
+                       push(@temp, $elem);
+               } elsif ($elem =~ /^pl$/ or $elem =~ /^\.$/) {
+                       push(@temp, 0);
+               } elsif ($elem =~ /^_$/) {
+                       push(@temp, 0);
+               } elsif ($elem =~ /^pre$/) {
+                       push(@temp, -1);
+               } elsif ($elem =~ /^rc$/) {
+                       push(@temp, -1);
+               } elsif ($elem =~ /^beta$/) {
+                       push(@temp, -2);
+               } elsif ($elem =~ /^alpha$/) {
+                       push(@temp, -3);
+               } else {
+                       push(@temp, 0);
+                       push(@temp, ord($elem) - ord("a") + 1);
+               }
        }
-       @pkgdirs =
-           sort grep($_ ne 'Makefile'
-               && $_ ne 'pkg'
-               && $_ ne 'CVS'
-               && substr($_, 0, 1) ne '.',
-               readdir(CAT));
-       closedir(CAT);
-       @pkgdirs;
+       @temp;
 }
 
-sub glob2regex($) {
-       my ($glob) = @_;
-       my (@chars, $in_alt);
-       my ($regex);
+sub deweycmp_extract($$) {
+       my ($match, $val) = @_;
+       my ($cmp, @matchlist, @vallist, $i, $len);
 
-       @chars = split(//, $glob);
-       while (defined($_ = shift @chars)) {
-               if ($_ eq '*') {
-                       $regex .= '.*';
-               } elsif ($_ eq '?') {
-                       $regex .= '.';
-               } elsif ($_ eq '+') {
-                       $regex .= '.';
-               } elsif ($_ eq '\\+') {
-                       $regex .= $_ . shift @chars;
-               } elsif ($_ eq '.' || $_ eq '|') {
-                       $regex .= quotemeta;
-               } elsif ($_ eq '{') {
-                       $regex .= '(';
-                       ++$in_alt;
-               } elsif ($_ eq '}') {
-                       if (!$in_alt) {
-                               # Error
-                               return undef;
-                       }
-                       $regex .= ')';
-                       --$in_alt;
-               } elsif ($_ eq ',' && $in_alt) {
-                       $regex .= '|';
-               } else {
-                       $regex .= $_;
+       @matchlist = convert_to_standard_dewey(split(/(\D+)/, lc($match)));
+       @vallist = convert_to_standard_dewey(split(/(\D+)/, lc($val)));
+       $cmp = 0;
+       $i = 0;
+       if ($#matchlist > $#vallist) {
+               $len = $#matchlist;
+       } else {
+               $len = $#vallist;
+       }
+       while (!$cmp && ($i++ <= $len)) {
+               if (!@matchlist) {
+                       push(@matchlist, 0);
+               }
+               if (!@vallist) {
+                       push(@vallist, 0);
                }
+               $cmp = (shift @matchlist <=> shift @vallist);
        }
+       $cmp;
+}
 
-       if ($in_alt) {
-               # Error
-               return undef;
+# Dewey decimal version number matching - or thereabouts
+# Also handles 'nb<N>' suffix (checked iff values otherwise identical)
+#
+sub deweycmp($$$) {
+       my ($match, $test, $val) = @_;
+       my ($cmp, $match_nb, $val_nb);
+
+       $match_nb = $val_nb = 0;
+       if ($match =~ /(.*)nb(.*)/) {
+               # Handle nb<N> suffix
+               $match = $1;
+               $match_nb = $2;
        }
-       if ($regex eq $glob) {
-               return ('');
+
+       if ($val =~ /(.*)nb(.*)/) {
+               # Handle nb<N> suffix
+               $val = $1;
+               $val_nb = $2;
        }
-       if ($opt{D}) {
-               print "glob2regex: $glob -> $regex\n";
+
+       $cmp = deweycmp_extract($match, $val);
+
+       if (!$cmp) {
+               # Iff otherwise identical, check nb suffix
+               $cmp = deweycmp_extract($match_nb, $val_nb);
        }
-       '^' . $regex . '$';
-}
 
-# Perform some (reasonable) subset of 'pkg_info -e' / glob(3)
-# Returns (sometimes best guess at) package name,
-# and either 'problem version' or undef if all OK
-#
-sub package_globmatch($) {
-       my ($pkgmatch) = @_;
-       my ($matchpkgname, $matchver, $regex);
+       debug("eval deweycmp $cmp $test 0\n");
+       eval "$cmp $test 0";
+}
 
-       if ($pkgmatch =~ /^([^*?[]+)(<|>|<=|>=|-)(\d[^*?[{]*)$/) {
+sub parse_expand_vars_dumb($$) {
+       my ($line, $vars) = @_;
 
-               # (package)(cmp)(dewey)
-               my ($test, @pkgvers);
+       while ($line =~ /\$\{([-\w.]+)\}/) {
+               if (defined(${$vars}{$1})) {
+                       $line = $` . ${$vars}{$1} . $';
+               } else {
+                       $line = $` . $magic_undefined . $';
+               }
+       }
+       $line;
+}
 
-               ($matchpkgname, $test, $matchver) = ($1, $2, $3);
-               if (@pkgvers = $pkglist->pkgver($matchpkgname)) {
-                       foreach my $pkgver (@pkgvers) {
-                               if ($test eq '-') {
-                                       if ($pkgver->ver eq $matchver) {
-                                               $matchver = undef;
-                                               last;
-                                       }
-                               } else {
-                                       if (deweycmp($pkgver->ver, $test, $matchver)) {
-                                               $matchver = undef;
-                                               last;
-                                       }
-                               }
-                       }
+sub parse_expand_vars($$) {
+       my ($line, $vars) = @_;
 
-                       if ($matchver && $test ne '-') {
-                               $matchver = "$test$matchver";
-                       }
+       while ($line =~ /\$\{([-\w.]+)\}/) {
+               if (defined(${$vars}{$1})) {
+                       $line = $` . ${$vars}{$1} . $';
+               } else {
+                       $line = $` . $magic_undefined . $';
                }
+       }
+       $line;
+}
 
-       } elsif ($pkgmatch =~ /^([^[]+)-([\d*?{[].*)$/) {
+sub parse_eval_make_false($$) {
+       my ($line, $vars) = @_;
+       my ($false, $test);
 
-               # (package)-(globver)
-               my (@pkgnames);
+       $false = 0;
+       $test = parse_expand_vars_dumb($line, $vars);
 
-               ($matchpkgname, $matchver) = ($1, $2);
+       # XXX This is _so_ wrong - need to parse this correctly
+       $test =~ s/""/\r/g;
+       $test =~ s/"//g; # "
+       $test =~ s/\r/""/g;
 
-               if (defined $pkglist->pkgs($matchpkgname)) {
-                       push(@pkgnames, $matchpkgname);
+       debug("conditional: $test\n");
 
-               } elsif ($regex = glob2regex($matchpkgname)) {
-                       foreach my $pkg ($pkglist->pkgs) {
-                               ($pkg->pkg() =~ /$regex/) && push(@pkgnames, $pkg->pkg());
-                       }
+       # XXX Could do something with target
+       while ($test =~ /(target|empty|make|defined|exists)\s*\(([^()]+)\)/) {
+               my $testname = $1;
+               my $varname = $2;
+               my $var;
+
+               # Implement (some of) make's :M modifier
+               if ($varname =~ /^([^:]+):M(.+)$/) {
+                       $varname = $1;
+                       my $match = $2;
+
+                       $var = $${vars}{$varname};
+                       $var = parse_expand_vars($var, $vars)
+                           if defined $var;
+
+                       $match =~ s/([{.+])/\\$1/g;
+                       $match =~ s/\*/.*/g;
+                       $match =~ s/\?/./g;
+                       $match = '^' . $match . '$';
+                       $var = ($var =~ /$match/)
+                           if defined $var;
+               } else {
+                       $var = $${vars}{$varname};
+                       $var = parse_expand_vars($var, $vars)
+                           if defined $var;
                }
 
-               # Try to convert $matchver into regex version
-               #
-               $regex = glob2regex($matchver);
+               if (defined $var && $var eq $magic_undefined) {
+                       $var = undef;
+               }
 
-               foreach my $pkg (@pkgnames) {
-                       if (defined $pkglist->pkgver($pkg, $matchver)) {
-                               return ($matchver);
-                       }
+               if ($testname eq 'exists') {
+                       $_ = (-e $varname) ? 1 : 0;
 
-                       if ($regex) {
-                               foreach my $ver ($pkglist->pkgs($pkg)->versions) {
-                                       if ($ver =~ /$regex/) {
-                                               $matchver = undef;
-                                               last;
-                                       }
-                               }
-                       }
+               } elsif ($testname eq 'defined') {
+                       $_ = defined($var) ? 1 : 0;
 
-                       $matchver || last;
+               } elsif ($testname eq 'empty') {
+                       $_ = ((not defined($var) or (length($var) == 0)) ? 1 : 0);
+
+               } else {
+                       $_ = 0;
                }
 
-               # last ditch attempt to handle the whole DEPENDS as a glob
-               #
-               if ($matchver && ($regex = glob2regex($pkgmatch))) {
+               $test =~ s/$testname\s*\([^()]+\)/$_/;
+               debug("conditional: update to $test\n");
+       }
 
-                       # (large-glob)
-                       foreach my $pkgver ($pkglist->pkgver) {
-                               if ($pkgver->pkgname =~ /$regex/) {
-                                       $matchver = undef;
-                                       last;
-                               }
-                       }
+       while ($test =~ /([^\s()\|\&]+)\s+(!=|==)\s+([^\s()]+)/) {
+               if ($2 eq '==') {
+                       $_ = ($1 eq $3) ? 1 : 0;
+               } else {
+                       $_ = ($1 ne $3) ? 1 : 0;
+               }
+               $test =~ s/[^\s()\|\&]+\s+(!=|==)\s+[^\s()]+/$_/;
+       }
+
+       if ($test !~ /[^<>\d()\s&|.!]/) {
+               debug("eval test $test\n");
+               $false = eval "($test)?0:1";
+               if (!defined $false) {
+                       fail("Eval failed $line - $test");
                }
+               debug("conditional: evaluated to " . ($false ? 0 : 1) . "\n");
 
        } else {
-               ($matchpkgname, $matchver) = ($pkgmatch, 'missing');
+               $false = 0;
+               debug("conditional: defaulting to 0\n");
        }
-
-       ($matchpkgname, $matchver);
+       $false;
 }
 
-# Parse a pkgsrc package makefile and return the pkgname and set variables
+# Extract variable assignments from Makefile
+# Much unpalatable magic to avoid having to use make (all for speed)
 #
-sub parse_makefile_pkgsrc($) {
-       my ($file) = @_;
-       my ($pkgname, $vars);
-
-       $vars = parse_makefile_vars($file, undef);
-
-       if (!$vars) {
+sub parse_makefile_vars($$) {
+       my ($file, $cwd) = @_;
+       my (
+           $pkgname, %vars, $plus, $value, @data,
+           %incfiles, # Cache of previously included fils
+           %incdirs,  # Directories in which to check for includes
+           @if_false
+       ); # 0:true 1:false 2:nested-false&nomore-elsif
 
-               # Missing Makefile
-               return undef;
+       if (!open(FILE, $file)) {
+               return (undef);
        }
-
-       if (defined $vars->{PKGNAME}) {
-               $pkgname = $vars->{PKGNAME};
-       } elsif (defined $vars->{DISTNAME}) {
-               $pkgname = $vars->{DISTNAME};
-       }
-
-       if (defined $vars->{PKGNAME}) {
-               debug("$file: PKGNAME=$vars->{PKGNAME}\n");
-       }
-       if (defined $vars->{DISTNAME}) {
-               debug("$file: DISTNAME=$vars->{DISTNAME}\n");
-       }
-
-       if (!defined $pkgname || $pkgname =~ /\$/ || $pkgname !~ /(.*)-(\d.*)/) {
-
-               # invoke make here as a last resort
-               my ($pkgsrcdir) = ($file =~ m:(/.*)/:);
-               debug("Running '$conf_make' in '$pkgsrcdir'\n");
-               my $pid = open3(\*WTR, \*RDR, \*ERR,
-                   "cd $pkgsrcdir || exit 1; $conf_make show-vars VARNAMES=PKGNAME");
-               if (!$pid) {
-                       warn "$file: Unable to run make: $!";
-               } else {
-                       close(WTR);
-                       my @errors = <ERR>;
-                       close(ERR);
-                       my ($makepkgname) = <RDR>;
-                       close(RDR);
-                       wait;
-                       chomp @errors;
-                       if (@errors) { warn "\n$file: @errors\n"; }
-
-                       if ($makepkgname =~ /(.*)-(\d.*)/) {
-                               $pkgname = $makepkgname;
-                       }
-               }
-       }
-
-       if (defined $pkgname) {
-               if ($pkgname =~ /^pkg_install-(\d+)$/ && $1 < $pkg_installver) {
-                       $pkgname = "pkg_install-$pkg_installver";
-               }
-
-               $pkgname = canonicalize_pkgname($pkgname);
-
-               if (defined $vars->{PKGREVISION}
-                   and not $vars->{PKGREVISION} =~ /^\s*$/) {
-                       if ($vars->{PKGREVISION} =~ /^\$\{(_(CVS|GIT|HG|SVN)_PKGVERSION):.*\}$/) {
-                               # See wip/mk/*-package.mk.
-                       } elsif ($vars->{PKGREVISION} =~ /\D/) {
-                               print "\nBogus: PKGREVISION $vars->{PKGREVISION} (from $file)\n";
-
-                       } elsif ($vars->{PKGREVISION}) {
-                               $pkgname .= "nb";
-                               $pkgname .= $vars->{PKGREVISION};
-                       }
-               }
-
-               if ($pkgname =~ /\$/) {
-                       print "\nBogus: $pkgname (from $file)\n";
-
-               } elsif ($pkgname =~ /(.*)-(\d.*)/) {
-                       if ($pkglist) {
-                               my ($pkgver) = $pkglist->add($1, $2);
-
-                               debug("add $1 $2\n");
-
-                               foreach my $var (qw(DEPENDS RESTRICTED OSVERSION_SPECIFIC BROKEN)) {
-                                       $pkgver->var($var, $vars->{$var});
-                               }
-
-                               if (defined $vars->{NO_BIN_ON_FTP}) {
-                                       $pkgver->var('RESTRICTED', 'NO_BIN_ON_FTP');
-                               }
-
-                               if ($file =~ m:([^/]+/[^/]+)/Makefile$:) {
-                                       $pkgver->var('dir', $1);
-                               } else {
-                                       $pkgver->var('dir', 'unknown');
-                               }
-                       }
-               } else {
-                       print "Cannot extract $pkgname version ($file)\n";
-               }
-
-               return ($pkgname, $vars);
-
-       } else {
-               return (undef);
-       }
-}
-
-# Extract variable assignments from Makefile
-# Much unpalatable magic to avoid having to use make (all for speed)
-#
-sub parse_makefile_vars($$) {
-       my ($file, $cwd) = @_;
-       my (
-           $pkgname, %vars, $plus, $value, @data,
-           %incfiles, # Cache of previously included fils
-           %incdirs,  # Directories in which to check for includes
-           @if_false
-       ); # 0:true 1:false 2:nested-false&nomore-elsif
-
-       if (!open(FILE, $file)) {
-               return (undef);
-       }
-       @data = map { chomp;
-               $_; } <FILE>;
-       close(FILE);
+       @data = map { chomp;
+               $_; } <FILE>;
+       close(FILE);
 
        $incdirs{"."} = 1;
-       $incdirs{ dirname($file) } = 1;
+       $incdirs{dirname($file)} = 1;
 
        # Some Makefiles depend on these being set
        if ($file eq '/etc/mk.conf') {
@@ -695,7 +539,7 @@ sub parse_makefile_vars($$) {
                $vars{'.CURDIR'} = getcwd;
        }
 
-       $incdirs{ $vars{'.CURDIR'} } = 1;
+       $incdirs{$vars{'.CURDIR'}} = 1;
        if ($opt{L}) {
                print "$file\n";
        }
@@ -876,176 +720,483 @@ sub parse_makefile_vars($$) {
                                $vars{$key} = $_;
                                $loop = 1;
 
-                       } elsif ($vars{$key} =~ m#\$\{([\w.]+):([CS]([^{}])[^{}\3]+\3[^{}\3]*\3[g1]*(|:[^{}]+)|U[^{}]+)\}#) {
-                               my ($left, $subvar, $right) = ($`, $1, $');
-                               my (@patterns) = split(':', $2);
-                               my ($result);
+                       } elsif ($vars{$key} =~ m#\$\{([\w.]+):([CS]([^{}])[^{}\3]+\3[^{}\3]*\3[g1]*(|:[^{}]+)|U[^{}]+)\}#) {
+                               my ($left, $subvar, $right) = ($`, $1, $');
+                               my (@patterns) = split(':', $2);
+                               my ($result);
+
+                               $result = $vars{$subvar};
+                               $result ||= '';
+
+                               # If $vars{$subvar} contains a $ skip it on this pass.
+                               # Hopefully it will get substituted and we can catch it
+                               # next time around.
+                               if (index($result, '${') != -1) {
+                                       next;
+                               }
+
+                               debug("$file: substitutelist $key ($result) $subvar (@patterns)\n");
+                               foreach (@patterns) {
+                                       if (m#(U)(.*)#) {
+                                               $result ||= $2;
+                                       } elsif (m#([CS])(.)([^/@]+)\2([^/@]*)\2([1g]*)#) {
+                                               my ($how, $from, $to, $global) = ($1, $3, $4, $5);
+
+                                               debug("$file: substituteglob $subvar, $how, $from, $to, $global\n");
+                                               if ($how eq 'S') {
+                                                       # Limited substitution - keep ^ and $
+                                                       $from =~ s/([?.{}\]\[*+])/\\$1/g;
+                                               }
+                                               $to =~ s/\\(\d)/\$$1/g; # Change \1 etc to $1
+                                               $to =~ s/\&/\$&/g;      # Change & to $1
+
+                                               my ($notfirst);
+                                               if ($global =~ s/1//) {
+                                                       ($from, $notfirst) = split('\s', $from, 2);
+                                               }
+
+                                               debug("$file: substituteperl $subvar, $how, $from, $to\n");
+                                               debug("eval substitute <$from> <$to> <$global>\n");
+                                               eval "\$result =~ s/$from/$to/$global";
+                                               if (defined $notfirst) {
+                                                       $result .= " $notfirst";
+                                               }
+                                       } else {
+                                               next;
+                                       }
+                               }
+
+                               $vars{$key} = $left . $result . $right;
+                               $loop = 1;
+                       }
+               }
+       }
+
+       foreach my $key (keys %vars) {
+               $vars{$key} =~ s/$magic_undefined//;
+       }
+       \%vars;
+}
+
+sub get_default_makefile_vars() {
+
+       chomp($pkg_installver = `pkg_info -V 2>/dev/null || echo 20010302`);
+
+       chomp($_ = `uname -srm`);
+       (
+           $default_vars->{OPSYS},
+           $default_vars->{OS_VERSION},
+           $default_vars->{MACHINE}
+       ) = (split);
+       if (!$default_vars->{MACHINE}) {
+               die('Unable to extract machine from uname');
+       }
+
+       # Handle systems without uname -p  (NetBSD pre 1.4)
+       chomp($default_vars->{MACHINE_ARCH} = `uname -p 2>/dev/null`);
+
+       if (!$default_vars->{MACHINE_ARCH}
+           && $default_vars->{OS_VERSION} eq 'NetBSD') {
+               chomp($default_vars->{MACHINE_ARCH} = `sysctl -n hw.machine_arch`);
+       }
+
+       if (!$default_vars->{MACHINE_ARCH}) {
+               $default_vars->{MACHINE_ARCH} = $default_vars->{MACHINE};
+       }
+
+       $default_vars->{OBJECT_FMT} = 'x';
+       $default_vars->{LOWER_OPSYS} = lc($default_vars->{OPSYS});
+
+       if ($opt{P}) {
+               $default_vars->{PKGSRCDIR} = realpath($opt{P});
+       } else {
+               $default_vars->{PKGSRCDIR} = $conf_pkgsrcdir;
+       }
+
+       $default_vars->{DESTDIR} = '';
+       $default_vars->{LOCALBASE} = '/usr/pkg';
+       $default_vars->{X11BASE} = '/usr/X11R6';
+
+       my ($vars);
+       if (-f '/etc/mk.conf' && ($vars = parse_makefile_vars('/etc/mk.conf', undef))) {
+               foreach my $var (keys %{$vars}) {
+                       $default_vars->{$var} = $vars->{$var};
+               }
+       } elsif (-f "$conf_sysconfdir/mk.conf" &&
+           ($vars = parse_makefile_vars("$conf_sysconfdir/mk.conf", undef))) {
+               foreach my $var (keys %{$vars}) {
+                       $default_vars->{$var} = $vars->{$var};
+               }
+       }
+
+       if ($opt{P}) {
+               $default_vars->{PKGSRCDIR} = realpath($opt{P});
+       }
+
+       if ($opt{M}) {
+               $default_vars->{DISTDIR} = realpath($opt{M});
+       } else {
+               $default_vars->{DISTDIR} ||= $default_vars->{PKGSRCDIR} . '/distfiles';
+       }
+
+       if ($opt{K}) {
+               $default_vars->{PACKAGES} = realpath($opt{K});
+       }
+
+       # Extract some variables from bsd.pkg.mk
+       my ($mkvars);
+       $mkvars = parse_makefile_vars(
+           "$default_vars->{PKGSRCDIR}/mk/bsd.pkg.mk",
+           "$default_vars->{PKGSRCDIR}/mk/scripts"
+       );
+       foreach my $varname (keys %{$mkvars}) {
+               if ($varname =~ /_REQD$/ || $varname eq 'EXTRACT_SUFX') {
+                       $default_vars->{$varname} = $mkvars->{$varname};
+               }
+       }
+
+       $default_vars->{PACKAGES} ||= $default_vars->{PKGSRCDIR} . '/packages';
+}
+
+# Determine if a package version is current. If not, report correct version
+# if found
+#
+sub invalid_version($) {
+       my ($pkgmatch) = @_;
+       my ($fail, $ok);
+       my (@pkgmatches, @todo);
+
+       @todo = ($pkgmatch);
+
+       # We handle {} here, everything else in package_globmatch
+       while ($pkgmatch = shift @todo) {
+               if ($pkgmatch =~ /(.*)\{([^{}]+)}(.*)/) {
+                       foreach (split(',', $2)) {
+                               push(@todo, "$1$_$3");
+                       }
+               } else {
+                       push(@pkgmatches, $pkgmatch);
+               }
+       }
+
+       foreach $pkgmatch (@pkgmatches) {
+               my ($pkg, $badver) = package_globmatch($pkgmatch);
+
+               if (defined($badver)) {
+                       my ($pkgs);
+
+                       if ($pkgs = $pkglist->pkgs($pkg)) {
+                               $fail .=
+                                   "Version mismatch: '$pkg' $badver vs "
+                                       . join(',', $pkgs->versions) . "\n";
+                       } else {
+                               $fail .= "Unknown package: '$pkg' version $badver\n";
+                       }
+               } else {
+
+                       # If we find one match, don't bitch about others
+                       $ok = 1;
+               }
+       }
+       $ok && ($fail = undef);
+       $fail;
+}
+
+# Use pkg_info to list installed packages
+#
+sub list_installed_packages() {
+       my (@pkgs);
+
+       open(PKG_INFO, 'pkg_info -e "*" |') || fail("Unable to run pkg_info: $!");
+       while (defined(my $pkg = <PKG_INFO>)) {
+               chomp($pkg);
+               push(@pkgs, canonicalize_pkgname($pkg));
+       }
+       close(PKG_INFO);
+
+       @pkgs;
+}
+
+# List top level pkgsrc categories
+#
+sub list_pkgsrc_categories($) {
+       my ($pkgsrcdir) = @_;
+       my (@categories);
+
+       opendir(BASE, $pkgsrcdir) || die("Unable to opendir($pkgsrcdir): $!");
+       @categories =
+           grep(substr($_, 0, 1) ne '.'
+               && $_ ne 'CVS'
+               && -f "$pkgsrcdir/$_/Makefile",
+               readdir(BASE));
+       closedir(BASE);
+       @categories;
+}
+
+# For a given category, list potentially valid pkgdirs
+#
+sub list_pkgsrc_pkgdirs($$) {
+       my ($pkgsrcdir, $cat) = @_;
+       my (@pkgdirs);
+
+       if (!opendir(CAT, "$pkgsrcdir/$cat")) {
+               die("Unable to opendir($pkgsrcdir/cat): $!");
+       }
+       @pkgdirs =
+           sort grep($_ ne 'Makefile'
+               && $_ ne 'pkg'
+               && $_ ne 'CVS'
+               && substr($_, 0, 1) ne '.',
+               readdir(CAT));
+       closedir(CAT);
+       @pkgdirs;
+}
+
+sub glob2regex($) {
+       my ($glob) = @_;
+       my (@chars, $in_alt);
+       my ($regex);
+
+       @chars = split(//, $glob);
+       while (defined($_ = shift @chars)) {
+               if ($_ eq '*') {
+                       $regex .= '.*';
+               } elsif ($_ eq '?') {
+                       $regex .= '.';
+               } elsif ($_ eq '+') {
+                       $regex .= '.';
+               } elsif ($_ eq '\\+') {
+                       $regex .= $_ . shift @chars;
+               } elsif ($_ eq '.' || $_ eq '|') {
+                       $regex .= quotemeta;
+               } elsif ($_ eq '{') {
+                       $regex .= '(';
+                       ++$in_alt;
+               } elsif ($_ eq '}') {
+                       if (!$in_alt) {
+                               # Error
+                               return undef;
+                       }
+                       $regex .= ')';
+                       --$in_alt;
+               } elsif ($_ eq ',' && $in_alt) {
+                       $regex .= '|';
+               } else {
+                       $regex .= $_;
+               }
+       }
+
+       if ($in_alt) {
+               # Error
+               return undef;
+       }
+       if ($regex eq $glob) {
+               return ('');
+       }
+       if ($opt{D}) {
+               print "glob2regex: $glob -> $regex\n";
+       }
+       '^' . $regex . '$';
+}
+
+# Perform some (reasonable) subset of 'pkg_info -e' / glob(3)
+# Returns (sometimes best guess at) package name,
+# and either 'problem version' or undef if all OK
+#
+sub package_globmatch($) {
+       my ($pkgmatch) = @_;
+       my ($matchpkgname, $matchver, $regex);
+
+       if ($pkgmatch =~ /^([^*?[]+)(<|>|<=|>=|-)(\d[^*?[{]*)$/) {
+
+               # (package)(cmp)(dewey)
+               my ($test, @pkgvers);
+
+               ($matchpkgname, $test, $matchver) = ($1, $2, $3);
+               if (@pkgvers = $pkglist->pkgver($matchpkgname)) {
+                       foreach my $pkgver (@pkgvers) {
+                               if ($test eq '-') {
+                                       if ($pkgver->ver eq $matchver) {
+                                               $matchver = undef;
+                                               last;
+                                       }
+                               } else {
+                                       if (deweycmp($pkgver->ver, $test, $matchver)) {
+                                               $matchver = undef;
+                                               last;
+                                       }
+                               }
+                       }
+
+                       if ($matchver && $test ne '-') {
+                               $matchver = "$test$matchver";
+                       }
+               }
+
+       } elsif ($pkgmatch =~ /^([^[]+)-([\d*?{[].*)$/) {
 
-                               $result = $vars{$subvar};
-                               $result ||= '';
+               # (package)-(globver)
+               my (@pkgnames);
 
-                               # If $vars{$subvar} contains a $ skip it on this pass.
-                               # Hopefully it will get substituted and we can catch it
-                               # next time around.
-                               if (index($result, '${') != -1) {
-                                       next;
-                               }
+               ($matchpkgname, $matchver) = ($1, $2);
 
-                               debug("$file: substitutelist $key ($result) $subvar (@patterns)\n");
-                               foreach (@patterns) {
-                                       if (m#(U)(.*)#) {
-                                               $result ||= $2;
-                                       } elsif (m#([CS])(.)([^/@]+)\2([^/@]*)\2([1g]*)#) {
-                                               my ($how, $from, $to, $global) = ($1, $3, $4, $5);
+               if (defined $pkglist->pkgs($matchpkgname)) {
+                       push(@pkgnames, $matchpkgname);
 
-                                               debug("$file: substituteglob $subvar, $how, $from, $to, $global\n");
-                                               if ($how eq 'S') {
-                                                       # Limited substitution - keep ^ and $
-                                                       $from =~ s/([?.{}\]\[*+])/\\$1/g;
-                                               }
-                                               $to =~ s/\\(\d)/\$$1/g; # Change \1 etc to $1
-                                               $to =~ s/\&/\$&/g;      # Change & to $1
+               } elsif ($regex = glob2regex($matchpkgname)) {
+                       foreach my $pkg ($pkglist->pkgs) {
+                               ($pkg->pkg() =~ /$regex/) && push(@pkgnames, $pkg->pkg());
+                       }
+               }
 
-                                               my ($notfirst);
-                                               if ($global =~ s/1//) {
-                                                       ($from, $notfirst) = split('\s', $from, 2);
-                                               }
+               # Try to convert $matchver into regex version
+               #
+               $regex = glob2regex($matchver);
 
-                                               debug("$file: substituteperl $subvar, $how, $from, $to\n");
-                                               debug("eval substitute <$from> <$to> <$global>\n");
-                                               eval "\$result =~ s/$from/$to/$global";
-                                               if (defined $notfirst) {
-                                                       $result .= " $notfirst";
-                                               }
-                                       } else {
-                                               next;
+               foreach my $pkg (@pkgnames) {
+                       if (defined $pkglist->pkgver($pkg, $matchver)) {
+                               return ($matchver);
+                       }
+
+                       if ($regex) {
+                               foreach my $ver ($pkglist->pkgs($pkg)->versions) {
+                                       if ($ver =~ /$regex/) {
+                                               $matchver = undef;
+                                               last;
                                        }
                                }
+                       }
 
-                               $vars{$key} = $left . $result . $right;
-                               $loop = 1;
+                       $matchver || last;
+               }
+
+               # last ditch attempt to handle the whole DEPENDS as a glob
+               #
+               if ($matchver && ($regex = glob2regex($pkgmatch))) {
+
+                       # (large-glob)
+                       foreach my $pkgver ($pkglist->pkgver) {
+                               if ($pkgver->pkgname =~ /$regex/) {
+                                       $matchver = undef;
+                                       last;
+                               }
                        }
                }
-       }
 
-       foreach my $key (keys %vars) {
-               $vars{$key} =~ s/$magic_undefined//;
+       } else {
+               ($matchpkgname, $matchver) = ($pkgmatch, 'missing');
        }
-       \%vars;
+
+       ($matchpkgname, $matchver);
 }
 
-sub parse_expand_vars($$) {
-       my ($line, $vars) = @_;
+# Parse a pkgsrc package makefile and return the pkgname and set variables
+#
+sub parse_makefile_pkgsrc($) {
+       my ($file) = @_;
+       my ($pkgname, $vars);
 
-       while ($line =~ /\$\{([-\w.]+)\}/) {
-               if (defined(${$vars}{$1})) {
-                       $line = $` . ${$vars}{$1} . $';
-               } else {
-                       $line = $` . $magic_undefined . $';
-               }
-       }
-       $line;
-}
+       $vars = parse_makefile_vars($file, undef);
 
-sub parse_expand_vars_dumb($$) {
-       my ($line, $vars) = @_;
+       if (!$vars) {
 
-       while ($line =~ /\$\{([-\w.]+)\}/) {
-               if (defined(${$vars}{$1})) {
-                       $line = $` . ${$vars}{$1} . $';
-               } else {
-                       $line = $` . $magic_undefined . $';
-               }
+               # Missing Makefile
+               return undef;
        }
-       $line;
-}
 
-sub parse_eval_make_false($$) {
-       my ($line, $vars) = @_;
-       my ($false, $test);
+       if (defined $vars->{PKGNAME}) {
+               $pkgname = $vars->{PKGNAME};
+       } elsif (defined $vars->{DISTNAME}) {
+               $pkgname = $vars->{DISTNAME};
+       }
 
-       $false = 0;
-       $test = parse_expand_vars_dumb($line, $vars);
+       if (defined $vars->{PKGNAME}) {
+               debug("$file: PKGNAME=$vars->{PKGNAME}\n");
+       }
+       if (defined $vars->{DISTNAME}) {
+               debug("$file: DISTNAME=$vars->{DISTNAME}\n");
+       }
 
-       # XXX This is _so_ wrong - need to parse this correctly
-       $test =~ s/""/\r/g;
-       $test =~ s/"//g; # "
-       $test =~ s/\r/""/g;
+       if (!defined $pkgname || $pkgname =~ /\$/ || $pkgname !~ /(.*)-(\d.*)/) {
 
-       debug("conditional: $test\n");
+               # invoke make here as a last resort
+               my ($pkgsrcdir) = ($file =~ m:(/.*)/:);
+               debug("Running '$conf_make' in '$pkgsrcdir'\n");
+               my $pid = open3(\*WTR, \*RDR, \*ERR,
+                   "cd $pkgsrcdir || exit 1; $conf_make show-vars VARNAMES=PKGNAME");
+               if (!$pid) {
+                       warn "$file: Unable to run make: $!";
+               } else {
+                       close(WTR);
+                       my @errors = <ERR>;
+                       close(ERR);
+                       my ($makepkgname) = <RDR>;
+                       close(RDR);
+                       wait;
+                       chomp @errors;
+                       if (@errors) { warn "\n$file: @errors\n"; }
 
-       # XXX Could do something with target
-       while ($test =~ /(target|empty|make|defined|exists)\s*\(([^()]+)\)/) {
-               my $testname = $1;
-               my $varname = $2;
-               my $var;
+                       if ($makepkgname =~ /(.*)-(\d.*)/) {
+                               $pkgname = $makepkgname;
+                       }
+               }
+       }
 
-               # Implement (some of) make's :M modifier
-               if ($varname =~ /^([^:]+):M(.+)$/) {
-                       $varname = $1;
-                       my $match = $2;
+       if (defined $pkgname) {
+               if ($pkgname =~ /^pkg_install-(\d+)$/ && $1 < $pkg_installver) {
+                       $pkgname = "pkg_install-$pkg_installver";
+               }
 
-                       $var = $${vars}{$varname};
-                       $var = parse_expand_vars($var, $vars)
-                           if defined $var;
+               $pkgname = canonicalize_pkgname($pkgname);
 
-                       $match =~ s/([{.+])/\\$1/g;
-                       $match =~ s/\*/.*/g;
-                       $match =~ s/\?/./g;
-                       $match = '^' . $match . '$';
-                       $var = ($var =~ /$match/)
-                           if defined $var;
-               } else {
-                       $var = $${vars}{$varname};
-                       $var = parse_expand_vars($var, $vars)
-                           if defined $var;
-               }
+               if (defined $vars->{PKGREVISION}
+                   and not $vars->{PKGREVISION} =~ /^\s*$/) {
+                       if ($vars->{PKGREVISION} =~ /^\$\{(_(CVS|GIT|HG|SVN)_PKGVERSION):.*\}$/) {
+                               # See wip/mk/*-package.mk.
+                       } elsif ($vars->{PKGREVISION} =~ /\D/) {
+                               print "\nBogus: PKGREVISION $vars->{PKGREVISION} (from $file)\n";
 
-               if (defined $var && $var eq $magic_undefined) {
-                       $var = undef;
+                       } elsif ($vars->{PKGREVISION}) {
+                               $pkgname .= "nb";
+                               $pkgname .= $vars->{PKGREVISION};
+                       }
                }
 
-               if ($testname eq 'exists') {
-                       $_ = (-e $varname) ? 1 : 0;
+               if ($pkgname =~ /\$/) {
+                       print "\nBogus: $pkgname (from $file)\n";
 
-               } elsif ($testname eq 'defined') {
-                       $_ = defined($var) ? 1 : 0;
+               } elsif ($pkgname =~ /(.*)-(\d.*)/) {
+                       if ($pkglist) {
+                               my ($pkgver) = $pkglist->add($1, $2);
 
-               } elsif ($testname eq 'empty') {
-                       $_ = ((not defined($var) or (length($var) == 0)) ? 1 : 0);
+                               debug("add $1 $2\n");
 
-               } else {
-                       $_ = 0;
-               }
+                               foreach my $var (qw(DEPENDS RESTRICTED OSVERSION_SPECIFIC BROKEN)) {
+                                       $pkgver->var($var, $vars->{$var});
+                               }
 
-               $test =~ s/$testname\s*\([^()]+\)/$_/;
-               debug("conditional: update to $test\n");
-       }
+                               if (defined $vars->{NO_BIN_ON_FTP}) {
+                                       $pkgver->var('RESTRICTED', 'NO_BIN_ON_FTP');
+                               }
 
-       while ($test =~ /([^\s()\|\&]+)\s+(!=|==)\s+([^\s()]+)/) {
-               if ($2 eq '==') {
-                       $_ = ($1 eq $3) ? 1 : 0;
+                               if ($file =~ m:([^/]+/[^/]+)/Makefile$:) {
+                                       $pkgver->var('dir', $1);
+                               } else {
+                                       $pkgver->var('dir', 'unknown');
+                               }
+                       }
                } else {
-                       $_ = ($1 ne $3) ? 1 : 0;
+                       print "Cannot extract $pkgname version ($file)\n";
                }
-               $test =~ s/[^\s()\|\&]+\s+(!=|==)\s+[^\s()]+/$_/;
-       }
 
-       if ($test !~ /[^<>\d()\s&|.!]/) {
-               debug("eval test $test\n");
-               $false = eval "($test)?0:1";
-               if (!defined $false) {
-                       fail("Eval failed $line - $test");
-               }
-               debug("conditional: evaluated to " . ($false ? 0 : 1) . "\n");
+               return ($pkgname, $vars);
 
        } else {
-               $false = 0;
-               debug("conditional: defaulting to 0\n");
+               return (undef);
        }
-       $false;
 }
 
+
 # chdir() || fail()
 #
 sub safe_chdir($) {
@@ -1057,6 +1208,19 @@ sub safe_chdir($) {
        }
 }
 
+sub load_pkgsrc_makefiles($) {
+
+       open(STORE, "<$_[0]") || die("Cannot read pkgsrc store from $_[0]: $!\n");
+       my ($pkgver);
+       our ($pkgcnt, $pkgnum, $subpkgcnt, $subpkgnum);
+       $pkglist = new PkgList;
+       while (<STORE>) {
+               debug("eval store $_");
+               eval $_;
+       }
+       close(STORE);
+}
+
 # Generate pkgname->category/pkg mapping, optionally check DEPENDS
 #
 sub scan_pkgsrc_makefiles($) {
@@ -1192,7 +1356,7 @@ sub scan_pkgsrc_distfiles_vs_distinfo($$
                                push(@{$sumfiles{ $dist->{sumtype} }}, $distn);
                        }
                }
-           } },
+       } },
            ($pkgdistdir));
 
        if ($check_unref && %bad_distfiles) {
@@ -1246,19 +1410,6 @@ sub scan_pkgsrc_distfiles_vs_distinfo($$
        (sort keys %bad_distfiles);
 }
 
-sub load_pkgsrc_makefiles() {
-
-       open(STORE, "<$_[0]") || die("Cannot read pkgsrc store from $_[0]: $!\n");
-       my ($pkgver);
-       our ($pkgcnt, $pkgnum, $subpkgcnt, $subpkgnum);
-       $pkglist = new PkgList;
-       while (<STORE>) {
-               debug("eval store $_");
-               eval $_;
-       }
-       close(STORE);
-}
-
 sub store_pkgsrc_makefiles($) {
        open(STORE, ">$_[0]") || die("Cannot save pkgsrc store to $_[0]: $!\n");
        my $was = select(STORE);
@@ -1308,219 +1459,64 @@ Modifiers:
        exit;
 }
 
-sub verbose(@) {
-
-       if (-t STDERR) {
-               print STDERR @_;
-       }
-}
-
-sub debug(@) {
+# Could speed up by building a cache of package names to paths, then processing
+# each package name once against the tests.
+sub check_prebuilt_packages() {
 
-       ($opt{D}) && print STDERR 'DEBUG: ', @_;
-}
+       if ($_ eq 'distfiles' || $_ eq 'pkgsrc') {
+               # Skip these subdirs if present
+               $File::Find::prune = 1;
 
-# PkgList is the master list of all packages in pkgsrc.
-#
-package PkgList;
+       } elsif (/(.+)-(\d.*)\.t[bg]z$/) {
+               my ($pkg, $ver) = ($1, $2);
 
-sub add($@) {
-       my $self = shift;
+               $pkg = canonicalize_pkgname($pkg);
 
-       if (!$self->pkgs($_[0])) {
-               $self->{_pkgs}{ $_[0] } = new Pkgs $_[0];
-       }
-       $self->pkgs($_[0])->add(@_);
-}
+               my ($pkgs);
+               if ($pkgs = $pkglist->pkgs($pkg)) {
+                       my ($pkgver) = $pkgs->pkgver($ver);
 
-sub new($) {
-       my $class = shift;
-       my $self = {};
-       bless $self, $class;
-       return $self;
-}
+                       if (!defined $pkgver) {
+                               if ($opt{p}) {
+                                       print "$File::Find::dir/$_\n";
+                                       push(@matched_prebuiltpackages, "$File::Find::dir/$_");
+                               }
 
-sub numpkgver($) {
-       my $self = shift;
-       scalar($self->pkgver);
-}
+                               # Pick probably the last version
+                               $pkgver = $pkgs->latestver;
+                       }
 
-sub pkgver($@) {
-       my $self = shift;
+                       if ($opt{R} && $pkgver->var('RESTRICTED')) {
+                               print "$File::Find::dir/$_\n";
+                               push(@matched_prebuiltpackages, "$File::Find::dir/$_");
+                       }
 
-       if (@_ == 0) {
-               my (@list);
-               foreach my $pkg ($self->pkgs) {
-                       push(@list, $pkg->pkgver);
+                       if ($opt{O} && $pkgver->var('OSVERSION_SPECIFIC')) {
+                               print "$File::Find::dir/$_\n";
+                               push(@matched_prebuiltpackages, "$File::Find::dir/$_");
+                       }
                }
-               return (@list);
-       }
-
-       if (defined $self->{_pkgs}{$_[0]}) {
-               return (@_ > 1)
-                   ? $self->{_pkgs}{$_[0]}->pkgver($_[1])
-                   : $self->{_pkgs}{$_[0]}->pkgver();
-       }
-       return;
-}
-
-sub pkgs($@) {
-       my $self = shift;
-
-       if (@_) {
-               return $self->{_pkgs}{$_[0]};
-       } else {
-               return (sort { $a->pkg cmp $b->pkg } values %{$self->{_pkgs}});
-       }
-}
-
-sub store($) {
-       my $self = shift;
-       my @pkgs = keys %{$self->{_pkgs}};
-       my ($cnt, $subcnt) = $self->count;
-
-       print("\$pkgcnt = $cnt;\n");
-       print("\$subpkgcnt = $subcnt;\n");
-       map($self->{_pkgs}{$_}->store, keys %{$self->{_pkgs}});
-}
-
-sub count($) {
-       my $self = shift;
-       my ($pkgcnt, $pkgsubcnt);
-
-       map {
-               $pkgcnt++;
-               $pkgsubcnt += $self->{_pkgs}{$_}->count;
-       } keys %{$self->{_pkgs}};
-       wantarray ? ($pkgcnt, $pkgsubcnt) : $pkgcnt;
-}
-
-# Pkgs is all versions of a given package (eg: apache-1.x and apache-2.x)
-#
-package Pkgs;
-
-sub add($@) {
-       my $self = shift;
-
-       $self->{_pkgver}{$_[1]} = new PkgVer @_;
-}
-
-sub new($@) {
-       my $class = shift;
-       my $self = {};
-
-       bless $self, $class;
-       $self->{_pkg} = $_[0];
-       return $self;
-}
-
-sub versions($) {
-       my $self = shift;
 
-       return sort { $b cmp $a } keys %{$self->{_pkgver}};
-}
-
-sub pkg($) {
-       my $self = shift;
-       $self->{_pkg};
-}
-
-sub pkgver($@) {
-       my $self = shift;
-
-       if (@_) {
-               if ($self->{_pkgver}{$_[0]}) {
-                       return ($self->{_pkgver}{$_[0]});
+       } elsif (-d $_) {
+               if ($prebuilt_pkgdir_cache{"$File::Find::dir/$_"}) {
+                       $File::Find::prune = 1;
+                       return;
                }
-               return;
-       }
-       return sort { $b->ver() cmp $a->ver() } values %{$self->{_pkgver}};
-}
-
-sub latestver($) {
-       my $self = shift;
-
-       ($self->pkgver())[0];
-}
-
-sub store($) {
-       my $self = shift;
-
-       print("\$pkgnum++;\n");
-       map($self->{_pkgver}{$_}->store, keys %{$self->{_pkgver}});
-}
-
-sub count($) {
-       my $self = shift;
-
-       scalar(keys %{$self->{_pkgver}});
-}
-
-# PkgVer is a unique package+version
-#
-package PkgVer;
-
-sub new($$$) {
-       my $class = shift;
-       my $self = {};
-
-       bless $self, $class;
-       $self->{_pkg} = $_[0];
-       $self->{_ver} = $_[1];
-       return $self;
-}
-
-sub pkgname($) {
-       my $self = shift;
-
-       $self->pkg . '-' . $self->ver;
-}
-
-sub pkg($) {
-       my $self = shift;
-
-       $self->{_pkg};
-}
-
-sub var($$$) {
-       my $self = shift;
-       my ($key, $val) = @_;
-
-       (defined $val)
-           ? ($self->{$key} = $val)
-           : $self->{$key};
-}
-
-sub ver($) {
-       my $self = shift;
-
-       $self->{_ver};
-}
-
-sub vars($) {
-       my $self = shift;
-
-       grep(!/^_(pkg|ver)$/, keys %{$self});
-}
 
-sub store($) {
-       my $self = shift;
-       my $data;
-
-       ($data = $self->{_pkg}) =~ s/([\\\$\@\%\"])/\\$1/g;
-       print("\$pkgver = \$pkglist->add(\"$data\", \"");
-
-       ($data = $self->{_ver}) =~ s/([\\\$\@\%\"])/\\$1/g;
-       print("$data\"); __pkgcount(1);\n");
+               $prebuilt_pkgdir_cache{"$File::Find::dir/$_"} = 1;
+               if (-l $_) {
+                       my ($dest) = readlink($_);
 
-       foreach ($self->vars) {
-               ($data = $self->{$_}) =~ s/([\\\$\@\%\"])/\\$1/g;
-               print("\$pkgver->var(\"$_\", \"$data\");\n");
+                       if (substr($dest, 0, 1) ne '/') {
+                               $dest = "$File::Find::dir/$dest";
+                       }
+                       if (!$prebuilt_pkgdir_cache{$dest}) {
+                               push(@prebuilt_pkgdirs, $dest);
+                       }
+               }
        }
 }
 
-package main;
-
 sub main() {
 
        $ENV{PATH} .=



Home | Main Index | Thread Index | Old Index