Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 11 Nov 2007 20:09:53 -0600 (CST)
From:      Alex Stangl <alex@stangl.us>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   ports/117991: New port: ports-mgmt/portupdate-scan Outputs relevant portions of UPDATING
Message-ID:  <20071112020953.16D53B828@scout.stangl.us>
Resent-Message-ID: <200711120230.lAC2U1fB057542@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         117991
>Category:       ports
>Synopsis:       New port: ports-mgmt/portupdate-scan  Outputs relevant portions of UPDATING
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-ports-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Nov 12 02:30:00 UTC 2007
>Closed-Date:
>Last-Modified:
>Originator:     Alex Stangl
>Release:        FreeBSD 6.0-RELEASE i386
>Organization:
>Environment:
System: FreeBSD scout.stangl.us 6.0-RELEASE FreeBSD 6.0-RELEASE #0: Thu Nov 3 09:36:13 UTC 2005 root@x64.samsco.home:/usr/obj/usr/src/sys/GENERIC i386


	
>Description:
portupdate-scan simplifies dealing with /usr/ports/UPDATING when you have so
many ports installed that it is difficult to know which sections are relevant.

It reads /usr/ports/UPDATING, attempting for each block to determine whether
the affected ports are installed. It omits blocks that do not apply.
It handles wildcards and other special cases, however it cannot handle
all variants of phrases used on the APPLIES: line.
In uncertain cases, it errs on the side of reporting.

>How-To-Repeat:
	
>Fix:

--- portupdate-scan.shar begins here ---
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	ports-mgmt/portupdate-scan
#	ports-mgmt/portupdate-scan/pkg-descr
#	ports-mgmt/portupdate-scan/Makefile
#	ports-mgmt/portupdate-scan/src
#	ports-mgmt/portupdate-scan/src/portupdate-scan
#	ports-mgmt/portupdate-scan/src/portupdate-scan.8
#
echo c - ports-mgmt/portupdate-scan
mkdir -p ports-mgmt/portupdate-scan > /dev/null 2>&1
echo x - ports-mgmt/portupdate-scan/pkg-descr
sed 's/^X//' >ports-mgmt/portupdate-scan/pkg-descr << 'END-of-ports-mgmt/portupdate-scan/pkg-descr'
Xportupdate-scan simplifies dealing with /usr/ports/UPDATING when you have so
Xmany ports installed that it is difficult to know which sections are relevant.
X
XIt reads /usr/ports/UPDATING, attempting for each block to determine whether
Xthe affected ports are installed. It omits blocks that do not apply.
XIt handles wildcards and other special cases, however it cannot handle
Xall variants of phrases used on the APPLIES: line.
XIn uncertain cases, it errs on the side of reporting.
X
XAlex Stangl <alex@stangl.us>
END-of-ports-mgmt/portupdate-scan/pkg-descr
echo x - ports-mgmt/portupdate-scan/Makefile
sed 's/^X//' >ports-mgmt/portupdate-scan/Makefile << 'END-of-ports-mgmt/portupdate-scan/Makefile'
X# New ports collection makefile for:	portupdate-scan
X# Date created:				08 November 2007
X# Whom:					Alex Stangl <alex@stangl.us>
X#
X# $FreeBSD$
X#
X# This port is self contained in the files directory.
X
XPORTNAME=	portupdate-scan
XPORTVERSION=	0.1
XCATEGORIES=	ports-mgmt
XMASTER_SITES=	# none
XDISTFILES=	# none
X
XMAINTAINER=	alex@stangl.us
XCOMMENT=	Display pertinent parts of {PORTSDIR}/UPDATING
X
XNO_BUILD=	yes
XUSE_PERL5_RUN=	yes
X
XPLIST_FILES=	sbin/portupdate-scan
XSRC=		${.CURDIR}/src
X
XMAN8=		portupdate-scan.8
X
Xdo-install:
X	${INSTALL_SCRIPT} ${SRC}/portupdate-scan ${PREFIX}/sbin/portupdate-scan
X	${INSTALL_MAN} ${SRC}/portupdate-scan.8 ${MAN8PREFIX}/man/man8
X
X.include <bsd.port.mk>
END-of-ports-mgmt/portupdate-scan/Makefile
echo c - ports-mgmt/portupdate-scan/src
mkdir -p ports-mgmt/portupdate-scan/src > /dev/null 2>&1
echo x - ports-mgmt/portupdate-scan/src/portupdate-scan
sed 's/^X//' >ports-mgmt/portupdate-scan/src/portupdate-scan << 'END-of-ports-mgmt/portupdate-scan/src/portupdate-scan'
X#!/usr/bin/perl -w
X
X# Copyright (c) 2007 Alex Stangl <alex@stangl.us>
X#
X# Permission to use, copy, modify, and distribute this software for any
X# purpose with or without fee is hereby granted, provided that the above
X# copyright notice and this permission notice appear in all copies.
X#
X# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
X# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
X# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
X# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X
X# Check /usr/ports/UPDATING file for all sections matching packages
X# that are installed, and outputting those sections.
X# Intended to be used prior to upgrading ports
X# Usage: portupdatescan [-dDhmuvV] [--help] [--version]
X# Created:      2006/12/21	Alex Stangl
X# Last updated: 2007/11/09	Alex Stangl
X
Xuse Text::ParseWords;
Xuse Getopt::Std;
Xuse strict;
X
X# Display usage and exit
Xsub HELP_MESSAGE() {
X	print <<EOF;
XUsage: portupdate-scan [-dhmuvV] [-D portsdir] [--help] [--version]
X  -d             display additional debugging info
X  -D portsdir    override default port directory
X  -h, --help     display this help and exit
X  -m             display detailed port install info for MIXED
X  -u             display information about uninstalled ports (Default off)
X  -v             display verbose information (e.g., more debugging info)
X  -V, --version  output version information and exit
XEOF
X	exit;
X}
X
Xsub VERSION_MESSAGE() {
X	print "portupdate-scan 0.1\n";
X}
X
X# Fetch cmdline args, display usage if appropriate
X$Getopt::Std::STANDARD_HELP_VERSION=1;          # std, not paranoia behavior
X# our($opt_d, $opt_h, $opt_m, $opt_u, $opt_v, $opt_V);
Xmy %opt;            # map of command-line options
XHELP_MESSAGE() unless getopts("dD:hmuvV", \%opt);
XHELP_MESSAGE() if $opt{h};
XVERSION_MESSAGE() && exit if $opt{V};
X
Xmy $portsdir = $opt{D} || "/usr/ports";     # ports directory
X
Xmy $portIndexFile = "$portsdir/INDEX";      # port index file
Xmy $movedFile = "$portsdir/MOVED";          # file w/ port renames/deletes
Xmy $updatingFile = "$portsdir/UPDATING";    # file w/ ports update news
X
Xmy @portlist = `pkg_info -aoq`
X    or die "Error trying to execute pkg_info -aoq: $!";
Xmy %installedPorts =
X    map {chomp($_);$_, 1} @portlist;    # map of installed ports -> 1
X
Xmy %allPorts;       # map of all portnames -> its INDEX line
Xmy %fromTo;         # map of old -> new portname
Xmy %toFrom;         # map of new -> old portname
Xmy %deletedPorts;   # map of deleted ports
Xmy %substmap = (    # map of port name substitutions
X	'xorg' => 'x11/xorg',
X	'Xorg' => 'x11/xorg',
X	'automake' => 'devel/automake*',
X);
Xmy %glob2regexp = ( 		# map glob-style regexp chars to regular expr
X	'*' => '.*',
X	'?' => '.',
X);
X
X# Process a single block from /usr/ports/UPDATING
Xsub processBlock(@) {
X	my ($affects, $remainder, $line);
X	my $index = 0;
X	foreach $line (@_) {
X		if ($line =~ /^\s*AFFECTS:/) {
X			$affects = $line;
X			$remainder = $index;
X		} elsif ($affects and $line =~ /^\s*AUTHOR/) {
X			last;
X		} elsif ($affects) {
X			$affects .= $line;
X		}
X		++$index;
X	}
X
X	return unless defined $remainder;
X	if (checkline($affects, $_[$remainder - 1])) {
X		print "$_[$remainder++]\n" while $remainder < @_;
X	}
X}
X
X# Given a port name, attempt to find and return an existing port of that
X# name, or a different name, taking into account /usr/ports/MOVED
Xsub findPortByName($@) {
X	my $name = $_[0];
X	print "+ findPortByName $name\n" if $opt{d};
X	$_[1]{$name} = 1;
X	return $name if $allPorts{$name};
X	my $retval = checkaliases($name, $_[1], \%fromTo);
X	return $retval if defined $retval;
X	return checkaliases($name, $_[1], \%toFrom);
X}
X
Xsub checkaliases($\@\@) {
X	my $name = $_[0];
X	my ($alias, $retval);
X	foreach $alias (@{$_[2]{$name}}) {
X		$retval = findPortByName($alias, $_[1]) if not exists $_[1]{$alias};
X		print "+ Checking alias $alias for $name\n" if $opt{d};
X		if (defined $retval and $retval ne "") {
X			print "+ returns $retval\n" if $opt{d};
X			return $retval;
X		}
X	}
X}
X
X
X# Recursively parse package name string, possibly containing
X# {a,b,c} or * metacharacters, requiring expansion
Xsub parseexpr($);	# forward declaration needed because of recursion
Xsub parseexpr($) {
X	my @retval;
X	my $sepExp = "(,\\s+and\\s+|,\\s+or\\s+|,\\s+|\\s+and\\s+|\\s+or\\s+|\\s+)";
X	if ($_[0] =~ /{.*,.*}/)
X	{
X		# Handle expression like textproc/{senna,p5-Senna}
X		# to produce textproc/senna, textproc/p5-Senna
X		my ($prefix, $subexpr, $suffix) = ($_[0] =~ /([^{]*){([^}]*)}(.*)/);
X
X		# Parse portname fragment contained within braces
X		# Breaks string at "," and discards quotes and single backslashes
X		my @exprs = &quotewords(",", 0, $subexpr);
X		my $line;
X		foreach $line (@exprs) {
X			push @retval, parseexpr($prefix . $line . $suffix);
X		}
X	} elsif ($_[0] =~ /\[\d{1,4}\]/) {
X		# Handle expressions like net/openldap2[34]-server
X		# to produce net/openldap23-server, net/openldap24-server
X		my ($prefix, $subexpr, $suffix) = ($_[0] =~ /([^[]*)\[([^\]]+)\](.*)/);
X
X		my $char;
X		foreach $char (split //, $subexpr) {
X			push @retval, parseexpr($prefix . $char . $suffix);
X		}
X	} elsif ($_[0] =~ /\[.+\]/) {
X		# Handle expressions like textproc/docproj[-jadetex]
X		# to produce textproc/docproj, textproc/docproj-jadetex
X		my ($prefix, $subexpr, $suffix) = ($_[0] =~ /([^[]*)\[([^\]]+)\](.*)/);
X		push @retval, parseexpr($prefix . $suffix);
X		push @retval, parseexpr($prefix . $subexpr . $suffix);
X	} elsif ($_[0] =~ /$sepExp/) {
X		# Handle "dir1/port1 and dir2/port2" recursively.
X		my ($prefix, $sep, $suffix) = ($_[0] =~ /(.*?)$sepExp(.*)/);
X		push @retval, parseexpr($prefix) if $prefix ne "";
X		push @retval, parseexpr($suffix) if $suffix ne "";
X	} elsif ($_[0] =~ /\*/ or $_[0] =~ /\?/) {
X		push @retval, expandwildcard($_[0]);
X	} else {
X		# Perform canned substitution, if appropriate, else use input string
X		my $subst = $substmap{$_[0]};
X		push @retval, $subst ? parseexpr($subst) : $_[0];
X	}
X	return @retval;
X}
X
X# Parse a single AFFECTS: line
Xsub parseline($) {
X	return ("ALL") if $_[0] =~ /^\s+AFFECTS:\s*everybody\s*$/i
X	    or $_[0] =~ /^\s+AFFECTS:\s*everyone\s*$/i
X	    or $_[0] =~ /^\s+AFFECTS:\s*all\s*$/i;
X	if ($_[0] =~ s/.*([uU]sers|[tT]esters) of[ \t]*(.*)/$2/) {
X		my @retval = parseexpr($_[0]);
X		print "+ parseline returns @retval for $_[0]\n" if $opt{d};
X		return @retval;
X	}
X}
X
X# For arg "PREFIX*SUFFIX", expand by returning all portnames
X# beginning with "PREFIX" and ending with "SUFFIX".
X# If no matching current portnames found, deleted portnames are checked.
X# If no current or deleted portnames match, the expression itself is returned.
Xsub expandwildcard($) {
X	my (@retval, $key);
X	my $globpattern = $_[0];
X	$globpattern = "*/$globpattern" if $globpattern !~ /.+\/.+/;
X	my $pattern = glob2pat($globpattern);
X
X	foreach $key (keys %allPorts) {
X		push @retval, $key if $key =~ /$pattern/;
X	}
X	return @retval if @retval;
X
X	# No current ports matched, so try checking for matching deleted ports
X	foreach $key (keys %deletedPorts) {
X		push @retval, $key if $key =~ /$pattern/;
X	}
X	push @retval, $_[0] if @retval == 0;	# push expr if no expansion
X	return @retval;
X}
X
X# Convert glob-style pattern to standard regular-expression pattern
Xsub glob2pat($) {
X	(my $globstr = $_[0]) =~ s/(.)/$glob2regexp{$1} || "\Q$1"/ge;
X	return '^' . $globstr . '$';
X}
X
X# Possibilities when evaluating a line:
X# 0. Not able to make sense of line (find any known port names)
X# 1. Able to find 1 or more port names, each either
X# 1a. Is installed
X# 1b. Is unknown
X# 1c. Is known, but not installed
X#
X# Conservatively, output line unless all apparent port names on
X# line are known and are not installed.
Xsub checkline($$) {
X	# Copy args for readability and because we will mutate localCopy
X	my ($localCopy, $prevLine) = @_;
X	my @ret = parseline($localCopy);
X	if ($#ret == -1) {
X		print "(NOT RECOGNIZED!) $_[1]\n";
X		return 1;
X	}
X
X	# boolean accumulator flags tracking whether all known, all deleted, etc.
X	my ($allKnown, $allUnknown, $allInstalled, $allNotInstalled, $allDeleted) = (1,1,1,1,1);
X
X	my ($line, @details);
X	foreach $line (@ret) {
X		if ($line eq "ALL") {
X			$allUnknown = $allDeleted = 0;
X			last;
X		}
X		my $alias = findPortByName($line, \());
X		if ($alias) {
X			$allUnknown = $allDeleted = 0;
X			if ($installedPorts{$alias}) {
X				$allDeleted = $allNotInstalled = 0;
X				push @details, logPortFinding("installed", $line, $alias);
X			} else {
X				$allDeleted = $allInstalled = 0;
X				push @details, logPortFinding("NOT installed", $line, $alias);
X			}
X		} elsif ($deletedPorts{$line}) {
X			push @details, logPortFinding("Deleted", $line, $alias);
X			$allUnknown = 0;
X		} else {
X			$allKnown = $allDeleted = 0;
X			push @details, logPortFinding("UNKNOWN", $line, $alias);
X		}
X	}
X
X	if ($allDeleted) {
X		printall(\@details) if $opt{v};
X		return 0 unless $opt{u};
X		print "(ALL Deleted) $_[1]\n";
X		return 1;
X	} elsif ($allUnknown) {
X		printall(\@details) if $opt{v};
X		print "(ALL Unknown) $_[1]\n";
X		return 1;
X	} elsif ($allKnown && $allInstalled) {
X		printall(\@details) if $opt{v};
X		print "(ALL Installed) $_[1]\n";
X		return 1;
X	} elsif ($allKnown && $allNotInstalled) {
X		printall(\@details) if $opt{v};
X		return 0 unless $opt{u};
X		print "(ALL NOT Installed) $_[1]\n";
X		return 1;
X	} else {
X		printall(\@details) if $opt{m} || $opt{v};
X		print "(MIXED) $_[1]\n";
X		return 1;
X	}
X}
X
X# Log detailed port discovery, if verbose enabled
Xsub logPortFinding($$$) {
X	my ($type, $line, $alias) = @_;
X	my $aliasexpr = (not defined $alias or $alias eq $line or $alias eq "") ? "" : " ($alias)";
X	return "+ Found $type $line$aliasexpr\n";
X}
X
Xsub printall(@) {
X	my $line;
X	foreach $line (@{$_[0]}) {
X		print $line;
X	}
X}
X
X# Main entry point. First, open INDEX and read all ports into allPorts
XMAIN:{
X	open(IDX, $portIndexFile) or die "Can't open $portIndexFile: $!";
X	while(<IDX>) {
X		chomp;
X		my ($portLine) = ($_ =~ /^[^|]*\|\/usr\/ports\/([^|]*)/)
X		    or die "$_ is not correctly formed!";
X		$allPorts{$portLine} = $_;
X		print "+ Processed INDEX line $_\n" if $opt{d} and $opt{v};
X		print "+ Generated from INDEX portLine $portLine\n" if $opt{d};
X	}
X	close(IDX);
X		
X	# Now open /usr/ports/MOVED to read ports which have moved
X	open(MOVED, $movedFile) or die "Can't open $movedFile: $!";
X	while(<MOVED>) {
X		chomp;
X		if ($_ !~ /^\s*#/) {
X			my ($from, $to) = ($_ =~ /^([^|]*)\|([^|]*)/)
X			    or die "$_ is not a correctly formed MOVED line";
X
X			if ($to eq "") {
X				$deletedPorts{$from} = 1;
X			} else {
X				push @{$fromTo{$from}}, $to;
X				push @{$toFrom{$to}}, $from;
X			}
X		}
X	}
X	close(MOVED);
X
X	# Parse /usr/ports/UPDATING into logical blocks, pass each to processBlock
X	my $prevLine;
X	my @bufferBlock;
X	open(UPD, $updatingFile) or die "Can't open $updatingFile: $!";
X	while(<UPD>) {
X		chomp;
X		if (/^\s*AFFECTS:/ && @bufferBlock > 0) {
X			processBlock(@bufferBlock);
X			@bufferBlock = ();
X		}
X		push @bufferBlock, $prevLine if defined $prevLine;
X		$prevLine = $_;
X	}
X	close(UPD);
X
X	# Process residue and exit
X	push @bufferBlock, $prevLine;
X	processBlock(@bufferBlock);
X}
END-of-ports-mgmt/portupdate-scan/src/portupdate-scan
echo x - ports-mgmt/portupdate-scan/src/portupdate-scan.8
sed 's/^X//' >ports-mgmt/portupdate-scan/src/portupdate-scan.8 << 'END-of-ports-mgmt/portupdate-scan/src/portupdate-scan.8'
X.\" Man page for portupdate-scan
X.\"
X.\" Copyright (c) 2007 Alex Stangl <alex@stangl.us>
X.\"
X.\" Permission to use, copy, modify, and distribute this software for any
X.\" purpose with or without fee is hereby granted, provided that the above
X.\" copyright notice and this permission notice appear in all copies.
X.\"
X.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
X.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
X.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
X.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
X.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
X.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
X.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
X.Dd November 5, 2007
X.Os
X.Dt PORTUPDATE-SCAN 8
X.Sh NAME
X.Nm portupdate-scan
X.Nd scan /usr/ports/UPDATING, showing sections for installed ports
X.Sh SYNOPSIS
X.Nm
X.Op Fl dhmuVv
X.Op Fl D Ar portdir
X.Op Fl -help
X.Op Fl -version
X.Sh DESCRIPTION
XReads port-related files, including /usr/ports/UPDATING, and outputs
Xthose sections of UPDATING that might be pertinent to this system.
XOnly sections that apply to recognize ports that are known, and not
Xinstalled are omitted.
X.Pp
XThe following options are available:
X.Bl -tag -width indent
X.It Fl D Ar portsdir
Xuse portsdir as ports directory instead of /usr/ports
X.It Fl d
Xoutput additional debugging information
X.It Fl h , -help
Xoutput command usage information and exit
X.It Fl m
Xfor MIXED sections, show breakdown of which ports are installed and not installed. The verbose information from the v flag is a superset of this.
X.It Fl u
Xoutput information on uninstalled ports (default: off)
X.It Fl v
Xoutput more verbose information
X.It Fl V , -version
Xoutput version information and exit
X.El
X.Sh EXIT STATUS
X.Ex -std
X.Sh FILES
X.Bl -tag -width /usr/ports/UPDATING -compact
X.It Pa /usr/ports/UPDATING
Xport update news
X.It Pa /usr/ports/INDEX
Xport index file
X.It Pa /usr/ports/MOVED
Xrecord of port renames and deletions
X.Sh EXAMPLES
X.Pp
XBasic normal operation:
X.Pp
X.Dl portupdate-scan
X.Pp
XTo include details of installed/uninstalled ports for MIXED sections:
X.Pp
X.Dl portupdate-scan -m
X.Sh SEE ALSO
X.Xr ports 7
X.Sh AUTHORS
X.An "Alex Stangl" Aq alex@stangl.us
X.Sh BUGS
XThe concept of machine-interpreting the APPLIES: line in /usr/ports/UPDATING,
Xwhich were intended for human consumption, is dubious. It would be nice to
Xevolve some more robust method for communicating this information.
END-of-ports-mgmt/portupdate-scan/src/portupdate-scan.8
exit
--- portupdate-scan.shar ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20071112020953.16D53B828>