C replacement for the Perl5 code.

Submitted by:	reg
This commit is contained in:
Mark Murray 2002-06-24 16:03:24 +00:00
parent 8ba3d077ff
commit 8360b6af14
9 changed files with 570 additions and 680 deletions

View File

@ -203,6 +203,8 @@ int requiredby(const char *, struct reqr_by_head **, Boolean, Boolean);
/* Version */
int verscmp(Package *, int, int);
const char *version_of(const char *, int *, int *);
int version_cmp(const char *, const char *);
/* Externs */
extern Boolean Verbose;

View File

@ -14,6 +14,15 @@
* Maxim Sobolev
* 31 July 2001
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "lib.h"
#include <err.h>
/*
* Routines to assist with PLIST_FMT_VER numbers in the packing
* lists.
*
@ -23,13 +32,6 @@
* value insted of the hash of an object this links points to.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "lib.h"
#include <err.h>
int
verscmp(Package *pkg, int major, int minor)
{
@ -44,3 +46,126 @@ verscmp(Package *pkg, int major, int minor)
return rval;
}
/*
* version_of(pkgname, epoch, revision) returns a pointer to the version
* portion of a package name and the two special components.
*
* Jeremy D. Lea.
*/
const char *
version_of(const char *pkgname, int *epoch, int *revision)
{
char *ch;
if (pkgname == NULL)
errx(2, "%s: Passed NULL pkgname.", __func__);
if (epoch != NULL) {
if ((ch = strrchr(pkgname, ',')) == NULL)
*epoch = 0;
else
*epoch = atoi(&ch[1]);
}
if (revision != NULL) {
if ((ch = strrchr(pkgname, '_')) == NULL)
*revision = 0;
else
*revision = atoi(&ch[1]);
}
/* Cheat if we are just passed a version, not a valid package name */
if ((ch = strrchr(pkgname, '-')) == NULL)
return pkgname;
else
return &ch[1];
}
/*
* version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
* components of pkg1 is less than, equal to or greater than pkg2. No
* comparision of the basenames is done.
*
* The port verison is defined by:
* ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
* ${PORTEPOCH} supercedes ${PORTVERSION} supercedes ${PORTREVISION}.
* See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
* for more information.
*
* The epoch and revision are defined to be a single number, while the rest
* of the version should conform to the porting guidelines. It can contain
* multiple components, seperated by a period, including letters.
*
* The tests below allow for significantly more latitude in the version
* numbers than is allowed in the guidelines. No point in wasting user's
* time enforcing them here. That's what flamewars are for.
*
* Jeremy D. Lea.
*/
int
version_cmp(const char *pkg1, const char *pkg2)
{
const char *c1, *c2, *v1, *v2;
char *t1, *t2;
int e1, e2, r1, r2, n1, n2;
v1 = version_of(pkg1, &e1, &r1);
v2 = version_of(pkg2, &e2, &r2);
/* Minor optimisation. */
if (strcmp(v1, v2) == 0)
return 0;
/* First compare epoch. */
if (e1 != e2)
return (e1 < e2 ? -1 : 1);
else {
/*
* We walk down the versions, trying to convert to numbers.
* We terminate when we reach an underscore, a comma or the
* string terminator, thanks to a nasty trick with strchr().
* strtol() conveniently gobbles up the chars it converts.
*/
c1 = strchr("_,", v1[0]);
c2 = strchr("_,", v2[0]);
while (c1 == NULL && c2 == NULL) {
n1 = strtol(v1, &t1, 10);
n2 = strtol(v2, &t2, 10);
if (n1 != n2)
return (n1 < n2 ? -1 : 1);
/*
* The numbers are equal, check for letters. Assume they're
* letters purely because strtol() didn't chomp them.
*/
c1 = strchr("_,.", t1[0]);
c2 = strchr("_,.", t2[0]);
if (c1 == NULL && c2 == NULL) {
/* Both have letters. Compare them. */
if (t1[0] != t2[0])
return (t1[0] < t2[0] ? -1 : 1);
/* Boring. The letters are equal. Carry on. */
v1 = &t1[1], v2 = &t2[1];
} else if (c1 == NULL) {
/*
* Letters are strange. After a number, a letter counts
* as greater, but after a period it's less.
*/
return (isdigit(v1[0]) ? 1 : -1);
} else if (c2 == NULL) {
return (isdigit(v2[0]) ? -1 : 1);
} else {
/* Neither were letters. Advance over the period. */
v1 = (t1[0] == '.' ? &t1[1] : t1);
v2 = (t2[0] == '.' ? &t2[1] : t2);
}
c1 = strchr("_,", v1[0]);
c2 = strchr("_,", v2[0]);
}
/* If we got here, check if one version has something left. */
if (c1 == NULL)
return (isdigit(v1[0]) ? 1 : -1);
if (c2 == NULL)
return (isdigit(v2[0]) ? -1 : 1);
/* We've run out of version. Try the revision... */
if (r1 != r2)
return (r1 < r2 ? -1 : 1);
else
return 0;
}
}

View File

@ -1,7 +1,19 @@
# $FreeBSD$
SCRIPTS= pkg_version.pl
MAN= pkg_version.1
PROG= pkg_version
SRCS= main.c perform.c
CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
WARNS?= 2
DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
LDADD= ${LIBINSTALL} -lfetch -lmd
.if !defined(NOCRYPT) && !defined(NOSECURE) && !defined(NO_OPENSSL)
DPADD+= ${LIBSSL} ${LIBCRYPTO}
LDADD+= -lssl -lcrypto
.endif
test:
./test-pkg_version.sh

View File

@ -0,0 +1,89 @@
/*
* FreeBSD install - a package for the installation and maintainance
* of non-core utilities.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Jeremy D. Lea.
* 11 May 2002
*
* This is the version module. Based on pkg_version.pl by Bruce A. Mah.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "lib.h"
#include "version.h"
#include <err.h>
static char Options[] = "dhl:L:s:tv";
char *LimitChars = NULL;
char *PreventChars = NULL;
char *MatchName = NULL;
static void usage __P((void));
int
main(int argc, char **argv)
{
int ch, cmp = 0;
if (argc == 4 && !strcmp(argv[1], "-t")) {
cmp = version_cmp(argv[2], argv[3]);
printf(cmp > 0 ? ">\n" : (cmp < 0 ? "<\n" : "=\n"));
exit(0);
}
else while ((ch = getopt(argc, argv, Options)) != -1) {
switch(ch) {
case 'v':
Verbose = TRUE;
break;
case 'l':
LimitChars = optarg;
break;
case 'L':
PreventChars = optarg;
break;
case 's':
MatchName = optarg;
break;
case 't':
errx(2, "Invalid -t usage.");
break;
case 'h':
case '?':
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
return pkg_perform(argv);
}
static void
usage()
{
fprintf(stderr, "%s\n%s\n",
"usage: pkg_version [-hv] [-l limchar] [-L limchar] [-s string] index",
" pkg_version -t v1 v2");
exit(1);
}

View File

@ -0,0 +1,281 @@
/*
* FreeBSD install - a package for the installation and maintainance
* of non-core utilities.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Jeremy D. Lea.
* 11 May 2002
*
* This is the version module. Based on pkg_version.pl by Bruce A. Mah.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "lib.h"
#include "version.h"
#include <err.h>
#include <fetch.h>
#include <signal.h>
FILE *IndexFile;
struct index_head Index = SLIST_HEAD_INITIALIZER(Index);
static int pkg_do(char *);
static void show_version(const char *, const char *, const char *);
/*
* This is the traditional pkg_perform, except that the argument is _not_
* a list of packages. It is the index file from the command line.
*
* We loop over the installed packages, matching them with the -s flag
* if needed and calling pkg_do(). Before hand we set up a few things,
* and after we tear them down...
*/
int
pkg_perform(char **indexarg)
{
char tmp[PATH_MAX], **pkgs;
struct index_entry *ie;
int i, err_cnt = 0;
/*
* Try to find and open the INDEX. We only check IndexFile != NULL
* later, if we actually need the INDEX.
*/
if (*indexarg == NULL)
snprintf(tmp, PATH_MAX, "%s/INDEX", PORTS_DIR);
else
strlcpy(tmp, *indexarg, PATH_MAX);
if (isURL(tmp))
IndexFile = fetchGetURL(tmp, "");
else
IndexFile = fopen(tmp, "r");
/* Get a list of all the installed packages */
pkgs = matchinstalled(MATCH_ALL, NULL, &err_cnt);
if (err_cnt != 0)
errx(2, "Unable to find package database directory!");
i = -1;
while (pkgs[++i] != NULL) {
if (MatchName == NULL || strstr(pkgs[i], MatchName))
err_cnt += pkg_do(pkgs[i]);
}
/* If we opened the INDEX in pkg_do(), clean up. */
while (!SLIST_EMPTY(&Index)) {
ie = SLIST_FIRST(&Index);
SLIST_REMOVE_HEAD(&Index, next);
free(ie);
}
if (IndexFile != NULL)
fclose(IndexFile);
return err_cnt;
}
/*
* Traditional pkg_do(). We take the package name we are passed and
* first slurp in the CONTENTS file, getting name and origin, then
* we look for it's corresponding Makefile. If that fails we pull in
* the INDEX, and check there.
*/
static int
pkg_do(char *pkg)
{
char *ch, tmp[PATH_MAX], tmp2[PATH_MAX], *latest = NULL;
Package plist;
struct index_entry *ie;
FILE *fp;
size_t len;
/* Suck in the contents list. */
plist.head = plist.tail = NULL;
plist.name = plist.origin = NULL;
snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, pkg, CONTENTS_FNAME);
fp = fopen(tmp, "r");
if (!fp) {
warnx("unable to open %s file", CONTENTS_FNAME);
return 1;
}
read_plist(&plist, fp);
fclose(fp);
/*
* First we check if the installed package has an origin, and try
* looking for it's Makefile. If we find the Makefile we get the
* latest version from there. If we fail, we start looking in the
* INDEX, first matching the origin and then the package name.
*/
if (plist.origin != NULL) {
snprintf(tmp, PATH_MAX, "%s/%s", PORTS_DIR, plist.origin);
if (isdir(tmp) && chdir(tmp) != FAIL && isfile("Makefile")) {
if ((latest = vpipe("make -V PKGNAME", tmp)) == NULL)
warnx("Failed to get PKGNAME from %s/Makefile!", tmp);
else
show_version(plist.name, latest, "port");
}
}
if (latest == NULL) {
/* We only pull in the INDEX once, if needed. */
if (SLIST_EMPTY(&Index)) {
if (!IndexFile)
errx(2, "Unable to open INDEX in %s.", __func__);
while ((ch = fgetln(IndexFile, &len)) != NULL) {
/*
* Don't use strlcpy() because fgetln() doesn't
* return a valid C string.
*/
strncpy(tmp, ch, MIN(len, PATH_MAX));
tmp[PATH_MAX-1] = '\0';
/* The INDEX has pkgname|portdir|... */
if ((ch = strchr(tmp, '|')) != NULL)
ch[0] = '\0';
if (ch != NULL && (ch = strchr(&ch[1], '|')) != NULL)
ch[0] = '\0';
/* Look backwards for the last two dirs = origin */
while (ch != NULL && *--ch != '/')
if (ch[0] == '\0')
ch = NULL;
while (ch != NULL && *--ch != '/')
if (ch[0] == '\0')
ch = NULL;
if (ch == NULL)
errx(2, "The INDEX does not appear to be valid!");
if ((ie = malloc(sizeof(struct index_entry))) == NULL)
errx(2, "Unable to allocate memory in %s.", __func__);
strlcpy(ie->name, tmp, PATH_MAX);
strlcpy(ie->origin, &ch[1], PATH_MAX);
/* Who really cares if we reverse the index... */
SLIST_INSERT_HEAD(&Index, ie, next);
}
}
/* Now that we've slurped in the INDEX... */
SLIST_FOREACH(ie, &Index, next) {
if (plist.origin != NULL) {
if (strcmp(plist.origin, ie->origin) == 0)
latest = strdup(ie->name);
} else {
strlcpy(tmp, ie->name, PATH_MAX);
strlcpy(tmp2, plist.name, PATH_MAX);
/* Chop off the versions and compare. */
if ((ch = strrchr(tmp, '-')) == NULL)
errx(2, "The INDEX does not appear to be valid!");
ch[0] = '\0';
if ((ch = strrchr(tmp2, '-')) == NULL)
warnx("%s is not a valid package!", plist.name);
else
ch[0] = '\0';
if (strcmp(tmp2, tmp) == 0) {
if (latest != NULL) {
/* Multiple matches */
snprintf(tmp, PATH_MAX, "%s|%s", latest, ie->name);
free(latest);
latest = strdup(tmp);
} else
latest = strdup(ie->name);
}
}
}
if (latest == NULL)
show_version(plist.name, NULL, plist.origin);
else
show_version(plist.name, latest, "index");
}
if (latest != NULL)
free(latest);
free_plist(&plist);
return 0;
}
#define OUTPUT(c) ((PreventChars != NULL && !strchr(PreventChars, (c))) || \
(LimitChars != NULL && strchr(LimitChars, (c))) || \
(PreventChars == NULL && LimitChars == NULL))
/*
* Do the work of comparing and outputing. Ugly, but well that's what
* You get when you try to match perl output in C ;-).
*/
void
show_version(const char *installed, const char *latest, const char *source)
{
char *ch, tmp[PATH_MAX];
const char *ver;
int cmp = 0;
if (!installed || strlen(installed) == 0)
return;
strlcpy(tmp, installed, PATH_MAX);
if (!Verbose) {
if ((ch = strrchr(tmp, '-')) != NULL)
ch[0] = '\0';
}
if (latest == NULL) {
if (source == NULL && OUTPUT('!')) {
printf("%-34s !", tmp);
if (Verbose)
printf(" Comparison failed");
printf("\n");
} else if (source != NULL && OUTPUT('?')) {
printf("%-34s ?", tmp);
if (Verbose)
printf(" orphaned: %s", source);
printf("\n");
}
} else if (strchr(latest,'|') != NULL) {
if (OUTPUT('*')) {
printf("%-34s *", tmp);
if (Verbose) {
strlcpy(tmp, latest, PATH_MAX);
ch = strchr(tmp, '|');
ch[0] = '\0';
ver = version_of(tmp, NULL, NULL);
printf(" multiple versions (index has %s", ver);
do {
ver = version_of(&ch[1], NULL, NULL);
if ((ch = strchr(&ch[1], '|')) != NULL)
ch[0] = '\0';
printf(", %s", ver);
} while (ch != NULL);
printf(")");
}
printf("\n");
}
} else {
cmp = version_cmp(installed, latest);
ver = version_of(latest, NULL, NULL);
if (cmp < 0 && OUTPUT('<')) {
printf("%-34s <", tmp);
if (Verbose)
printf(" needs updating (%s has %s)", source, ver);
printf("\n");
} else if (cmp == 0 && OUTPUT('=')) {
printf("%-34s =", tmp);
if (Verbose)
printf(" up-to-date with %s", source);
printf("\n");
} else if (cmp > 0 && OUTPUT('>')) {
printf("%-34s >", tmp);
if (Verbose)
printf(" succeeds %s (%s has %s)", source, source, ver);
printf("\n");
}
}
}
void
cleanup(int sig)
{
if (sig)
exit(1);
}

View File

@ -32,7 +32,7 @@
.Nd summarize installed versions of packages
.Sh SYNOPSIS
.Nm
.Op Fl cdhv
.Op Fl hv
.Op Fl l Ar limchar
.Op Fl L Ar limchar
.Op Fl s Ar string
@ -110,23 +110,6 @@ The
.Nm
utility supports several command-line arguments:
.Bl -tag -width indent
.It Fl c
Enable commands output. Commands output includes the commands you should
type to update your installed packages to the latest versions in the ports
system.
This feature does
.Bf Em
not
.Ef
constitute an automated packages updating system.
The output of this command
.Bf Em
must
.Ef
be edited, in order to avoid destroying dependencies between installed
packages.
.It Fl d
Enable debugging output.
.It Fl h
Print help message.
.It Fl l
@ -201,42 +184,19 @@ the version numbers in the on-line ports collection:
.Pp
.Dl % pkg_version ftp://ftp.FreeBSD.org/pub/FreeBSD/branches/-current/ports/INDEX
.Pp
The command below generates a file of commands to run to update the installed
files.
These commands must
.Bf Em
not
.Ef
be run without suitable editing.
They should be treated as suggestions, and may need to be reordered
to account for dependencies between installed packages, or may need to
be disregarded if multiple versions of an installed package can coexist.
Blindly running the output of this command may leave a system in an
unusable state.
.Pp
.Dl % pkg_version -c > do_update
.Pp
The following command compares two package version strings:
.Pp
.Dl % pkg_version -t 1.5 1.5.1
.Sh AUTHORS
.An Bruce A. Mah Aq bmah@FreeBSD.org
The
.Nm
utility was written by
.An Jeremy D. Lea Aq reg@FreeBSD.org ,
partially based on a Perl script written by
.An Bruce A. Mah Aq bmah@FreeBSD.org .
.Sh CONTRIBUTORS
.An Nik Clayton Aq nik@FreeBSD.org ,
.An Dominic Mitchell Aq dom@palmerharvey.co.uk ,
.An Mark Ovens Aq marko@FreeBSD.org ,
.An Doug Barton Aq DougB@gorean.org ,
.An Akinori MUSHA Aq knu@FreeBSD.org
.Sh BUGS
The commands output feature is
.Bf Em
not
.Ef
an automated ports/packages updating system.
It does not even attempt to handle dependencies between installed
packages correctly, and can produce incorrect results if multiple
versions of a package can coexist on a system.
.Pp
Commands output assumes you install new software using the ports system,
rather than using
.Xr pkg_add 1 .

View File

@ -1,623 +0,0 @@
#! /usr/bin/perl
#
# Copyright 1998 Bruce A. Mah
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS 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 DEVELOPERS 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.
#
# pkg_version.pl
#
# A package version-checking utility for FreeBSD.
#
# $FreeBSD$
#
use Cwd;
use Getopt::Std;
#
# Configuration global variables
#
$AllCurrentPackagesCommand = '/usr/sbin/pkg_info -aI';
$SelectedCurrentPackagesCommand = '/usr/sbin/pkg_info -I';
$CatProgram = "cat ";
$FetchProgram = "fetch -o - ";
$OriginCommand = '/usr/sbin/pkg_info -qo';
$GetPkgNameCommand = 'make -V PKGNAME';
#$IndexFile = "ftp://ftp.freebsd.org/pub/FreeBSD/branches/-current/ports/INDEX";
$PortsDirectory = $ENV{PORTSDIR} || '/usr/ports';
$IndexFile = "$PortsDirectory/INDEX";
$ShowCommandsFlag = 0;
$DebugFlag = 0;
$VerboseFlag = 0;
$CommentChar = "#";
$LimitFlag = "";
$PreventFlag = "";
#
# CompareNumbers
#
# Try to figure out the relationship between two program version numbers.
# Detecting equality is easy, but determining order is a little difficult.
# This function returns -1, 0, or 1, in the same manner as <=> or cmp.
#
sub CompareNumbers {
my ($v1, $v2) = @_;
# Short-cut in case of equality
if ($v1 eq $v2) {
return 0;
}
# Loop over different components (the parts separated by dots).
# If any component differs, we have the basis for an inequality.
my @s1 = split(/\./, $v1);
my @s2 = split(/\./, $v2);
my ($c1, $c2);
do {
last unless @s1 || @s2;
$c1 = shift @s1;
$c2 = shift @s2;
} while ($c1 eq $c2);
# Look at the first components of the arrays that are left.
# These will determine the result of the comparison.
# Note that if either version doesn't have any components left,
# it's implicitly treated as a "0".
# Our next set of checks looks to see if either component has a
# leading letter (there should be at most one leading letter per
# component, so that "4.0b1" is allowed, but "4.0beta1" is not).
if ($c1 =~ /^\D/) {
if ($c2 =~ /^\D/) {
# Both have a leading letter, so do an alpha comparison
# on the letters. This isn't ideal, since we're assuming
# that "1.0.b4" > "1.0.a2". But it's about the best we can do,
# without encoding some explicit policy.
my ($letter1, $letter2);
$letter1 = substr($c1, 0, 1);
$letter2 = substr($c2, 0, 1);
if ($letter1 ne $letter2) {
return $letter1 cmp $letter2;
}
else {
# The letters matched equally. Delete the leading
# letters and invoke ourselves on the remainining
# characters, which according to the Porters Handbook
# must be digits, so for example, "1.0.a9" < "1.0.a10".
substr($c1, 0, 1) = "";
substr($c2, 0, 1) = "";
return &CompareNumbers($c1, $c2);
}
}
else {
# $c1 begins with a letter, but $c2 doesn't. Let $c2
# win the comparison, so that "1.0.b1" < "1.0.1".
return -1;
}
}
else {
if ($c2 =~ /^\D/) {
# $c2 begins with a letter but $c1 doesn't. Let $c1
# win the comparison, as above.
return 1;
}
else {
# Neither component begins with a leading letter.
# See if either component has no characters left. If so,
# let the other component win.
if ($c1 eq "") {
return -1;
}
if ($c2 eq "") {
return 1;
}
# Check for numeric inequality. We assume here that (for example)
# "3.09" < "3.10", and that we aren't going to be asked to
# decide between "3.010" and "3.10".
if ($c1 != $c2) {
return $c1 <=> $c2;
}
# String comparison, given numeric equality. This
# handles comparisons of the form "3.4j" < "3.4k". This form
# technically isn't allowed by the Porter's Handbook, but a
# number of ports in the FreeBSD Ports Collection as of this
# writing use it (graphics/jpeg and graphics/xv). So we need
# to support it.
#
# What we actually do is to strip off the leading digits and
# invoke ourselves on the remainder. This allows us to handle
# comparisons of the form "1.1p1" < "1.1p2". Again, not
# technically allowed by the Porters Handbook, but lots of ports
# use it.
else {
$c1 =~ s/\d+//;
$c2 =~ s/\d+//;
if ($c1 eq $c2) {
return 0;
}
elsif ($c1 eq "") {
return -1;
}
elsif ($c2 eq "") {
return 1;
}
else {
return &CompareNumbers($c1, $c2);
}
}
}
}
}
#
# CompareVersions
#
# Try to figure out the relationship between two program "full
# versions", which is defined as the
# ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
# part of a package's name.
#
# Key points: ${PORTEPOCH} supercedes ${PORTVERSION}
# supercedes ${PORTREVISION}. See the commit log for revision
# 1.349 of ports/Mk/bsd.port.mk for more information.
#
sub CompareVersions {
local($fv1, $fv2, $v1, $v2, $r1, $r2, $e1, $e2, $rc);
$fv1 = $_[0];
$fv2 = $_[1];
# Shortcut check for equality before invoking the parsing
# routines.
if ($fv1 eq $fv2) {
return 0;
}
else {
($v1, $r1, $e1) = &GetVersionComponents($fv1);
($v2, $r2, $e2) = &GetVersionComponents($fv2);
# Port revision and port epoch numbers default to zero if not
# specified.
if ($r1 eq "") {
$r1 = "0";
}
if ($r2 eq "") {
$r2 = "0";
}
if ($e1 eq "") {
$e1 = "0";
}
if ($e2 eq "") {
$e2 = "0";
}
# Check epoch, port version, and port revision, in that
# order.
$rc = &CompareNumbers($e1, $e2);
if ($rc == 0) {
$rc = &CompareNumbers($v1, $v2);
if ($rc == 0) {
$rc = &CompareNumbers($r1, $r2);
}
}
return $rc;
}
}
#
# GetVersionComponents
#
# Parse out the version number, revision number, and epoch number
# of a port's version string and return them as a three-element array.
#
# Syntax is: ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
#
sub GetVersionComponents {
local ($fullversion, $version, $revision, $epoch);
$fullversion = $_[0];
$fullversion =~ /([^_,]+)/;
$version = $1;
if ($fullversion =~ /_([^_,]+)/) {
$revision = $1;
}
if ($fullversion =~ /,([^_,]+)/) {
$epoch = $1;
}
return($version, $revision, $epoch);
}
#
# GetNameAndVersion
#
# Get the name and version number of a package. Returns a two element
# array, first element is name, second element is full version string.,
#
sub GetNameAndVersion {
local($fullname, $name, $fullversion);
$fullname = $_[0];
# If no hyphens then no version numbers
return ($fullname, "", "", "", "") if $fullname !~ /-/;
# Match (and group) everything after hyphen(s). Because the
# regexp is 'greedy', the first .* will try and match everything up
# to (but not including) the last hyphen
$fullname =~ /(.+)-(.+)/;
$name = $1;
$fullversion = $2;
return ($name, $fullversion);
}
#
# PrintHelp
#
# Print usage information
#
sub PrintHelp {
print <<"EOF"
Usage: pkg_version [-c] [-d] [-h] [-l limchar] [-L limchar] [-s string]
[-v] [index]
pkg_version [-d debug] -t v1 v2
-c Show commands to update installed packages
-d Enable debugging output
-h Help (this message)
-l limchar Limit output to status flags that match
-L limchar Limit output to status flags that DON\'T match
-s string Limit output to packages matching a string
-v Verbose output
index URL or filename of index file
(Default is $IndexFile)
-t v1 v2 Test two version strings
EOF
}
#
# Parse command-line arguments, deal with them
#
if (!getopts('cdhl:L:s:tv') || ($opt_h)) {
&PrintHelp();
exit;
}
if ($opt_c) {
$ShowCommandsFlag = $opt_c;
$LimitFlag = "<?"; # note that if the user specifies -l, we
# deal with this *after* setting a default
# for $LimitFlag
}
if ($opt_d) {
$DebugFlag = $opt_d;
}
if ($opt_l) {
$LimitFlag = $opt_l;
}
if ($opt_L) {
$PreventFlag = $opt_L;
}
if ($opt_t) {
$TestFlag = 1;
}
if ($opt_s) {
$StringFlag = $opt_s;
}
if ($opt_v) {
$VerboseFlag = 1;
}
if ($#ARGV >= 0) {
if ($TestFlag) {
($test1, $test2) = @ARGV;
}
else {
$IndexFile = $ARGV[0];
}
}
# Handle test flag now
if ($TestFlag) {
my $cmp = CompareVersions($test1, $test2);
if ($cmp < 0) {
print "<\n";
}
elsif ($cmp == 0) {
print "=\n";
}
else {
print ">\n";
}
exit(0);
}
# Determine what command to use to retrieve the index file.
if ($IndexFile =~ m-^((http|ftp)://|file:/)-) {
$IndexPackagesCommand = $FetchProgram . $IndexFile;
}
else {
$IndexPackagesCommand = $CatProgram . $IndexFile;
}
#
# Get the current list of installed packages
#
if ($StringFlag) {
if ($DebugFlag) {
print STDERR "$SelectedCurrentPackagesCommand *$StringFlag*\n";
}
open CURRENT, "$SelectedCurrentPackagesCommand \\*$StringFlag\\*|";
} else {
if ($DebugFlag) {
print STDERR "$AllCurrentPackagesCommand\n";
}
open CURRENT, "$AllCurrentPackagesCommand|";
}
while (<CURRENT>) {
($packageString, $rest) = split;
($packageName, $packageFullversion) = &GetNameAndVersion($packageString);
$currentPackages{$packageString}{'name'} = $packageName;
$currentPackages{$packageString}{'fullversion'} = $packageFullversion;
}
close CURRENT;
#
# Iterate over installed packages, get origin directory (if it
# exists) and PORTVERSION
#
$dir = cwd();
foreach $packageString (sort keys %currentPackages) {
open ORIGIN, "$OriginCommand $packageString|";
$origin = <ORIGIN>;
close ORIGIN;
# If there is an origin variable for this package, then store it.
if ($origin ne "") {
chomp $origin;
# Try to get the version out of the makefile.
# The chdir needs to be successful or our make -V invocation
# will fail.
unless (chdir "$PortsDirectory/$origin" and -r "Makefile") {
$currentPackages{$packageString}->{orphaned} = $origin;
next;
}
open PKGNAME, "$GetPkgNameCommand|";
$pkgname = <PKGNAME>;
close PKGNAME;
if ($pkgname ne "") {
chomp $pkgname;
$pkgname =~ /(.+)-(.+)/;
$portversion = $2;
$currentPackages{$packageString}{'origin'} = $origin;
$currentPackages{$packageString}{'portversion'} = $portversion;
}
}
}
chdir "$dir";
#
# Slurp in the index file
#
if ($DebugFlag) {
print STDERR "$IndexPackagesCommand\n";
}
open INDEX, "$IndexPackagesCommand|";
while (<INDEX>) {
($packageString, $packagePath, $rest) = split(/\|/);
($packageName, $packageFullversion) = &GetNameAndVersion($packageString);
$indexPackages{$packageName}{'name'} = $packageName;
$indexPackages{$packageName}{'path'} = $packagePath;
if (defined $indexPackages{$packageName}{'fullversion'}) {
$indexPackages{$packageName}{'fullversion'} .= "|" . $packageFullversion;
}
else {
$indexPackages{$packageName}{'fullversion'} = $packageFullversion;
}
$indexPackages{$packageName}{'refcount'}++;
}
close INDEX;
#
# If we're doing commands output, cripple the output so that users
# can't just pipe the output to sh(1) and expect this to work.
#
if ($ShowCommandsFlag) {
print<<EOF
echo "The commands output of pkg_version cannot be executed without editing."
echo "You MUST save this output to a file and then edit it, taking into"
echo "account package dependencies and the fact that some packages cannot"
echo "or should not be upgraded."
exit 1
EOF
}
#
# Produce reports
#
# Prior versions of pkg_version used commas (",") as delimiters
# when there were multiple versions of a package installed.
# The new package version number syntax uses commas as well,
# so we've used vertical bars ("|") internally, and convert them
# to commas before we output anything so the reports look the
# same as they did before.
#
foreach $packageString (sort keys %currentPackages) {
$~ = "STDOUT_VERBOSE" if $VerboseFlag;
$~ = "STDOUT_COMMANDS" if $ShowCommandsFlag;
$packageNameVer = $packageString;
$packageName = $currentPackages{$packageString}{'name'};
$currentVersion = $currentPackages{$packageString}{'fullversion'};
if ($currentPackages{$packageString}->{orphaned}) {
next if $ShowCommandsFlag;
$versionCode = "?";
$Comment = "orphaned: $currentPackages{$packageString}->{orphaned}";
} elsif (defined $currentPackages{$packageString}{'portversion'}) {
$portVersion = $currentPackages{$packageString}{'portversion'};
$portPath = "$PortsDirectory/$currentPackages{$packageString}{'origin'}";
# Do the comparison
$rc = &CompareVersions($currentVersion, $portVersion);
if ($rc == 0) {
$versionCode = "=";
$Comment = "up-to-date with port";
}
elsif ($rc < 0) {
$versionCode = "<";
$Comment = "needs updating (port has $portVersion)";
}
elsif ($rc > 0) {
$versionCode = ">";
$Comment = "succeeds port (port has $portVersion)";
}
else {
$versionCode = "!";
$Comment = "Comparison failed";
}
}
elsif (defined $indexPackages{$packageName}{'fullversion'}) {
$indexVersion = $indexPackages{$packageName}{'fullversion'};
$indexRefcount = $indexPackages{$packageName}{'refcount'};
$portPath = $indexPackages{$packageName}{'path'};
if ($indexRefcount > 1) {
$versionCode = "*";
$Comment = "multiple versions (index has $indexVersion)";
$Comment =~ s/\|/,/g;
}
else {
# Do the comparison
$rc =
&CompareVersions($currentVersion, $indexVersion);
if ($rc == 0) {
$versionCode = "=";
$Comment = "up-to-date with index";
}
elsif ($rc < 0) {
$versionCode = "<";
$Comment = "needs updating (index has $indexVersion)"
}
elsif ($rc > 0) {
$versionCode = ">";
$Comment = "succeeds index (index has $indexVersion)";
}
else {
$versionCode = "!";
$Comment = "Comparison failed";
}
}
}
else {
next if $ShowCommandsFlag;
$versionCode = "?";
$Comment = "unknown in index";
}
# Having figured out what to print, now determine, based on the
# $LimitFlag and $PreventFlag variables, if we should print or not.
if ((not $LimitFlag) and (not $PreventFlag)) {
write;
} elsif ($PreventFlag) {
if ($versionCode !~ m/[$PreventFlag]/o) {
if (not $LimitFlag) {
write;
} else {
write if $versionCode =~ m/[$LimitFlag]/o;
}
}
} else {
# Must mean that there is a LimitFlag
write if $versionCode =~ m/[$LimitFlag]/o;
}
}
exit 0;
#
# Formats
#
# $CommentChar is in the formats because you can't put a literal '#' in
# a format specification
# General report (no output flags)
format STDOUT =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<
$packageName, $versionCode
.
;
# Verbose report (-v flag)
format STDOUT_VERBOSE =
@<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$packageNameVer, $versionCode, $Comment
.
;
# Report that includes commands to update program (-c flag)
format STDOUT_COMMANDS =
@<
$CommentChar
@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$CommentChar, $packageName
@< @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$CommentChar, $Comment
@<
$CommentChar
cd @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$portPath
make clean && make && pkg_delete -f @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$packageNameVer
make install clean
.
;

View File

@ -33,7 +33,7 @@
#
ECHO=echo
PKG_VERSION=./pkg_version.pl
PKG_VERSION=./pkg_version
test-pv ( ) { \
setvar v1 $1

View File

@ -0,0 +1,44 @@
/* $FreeBSD$ */
/*
* FreeBSD install - a package for the installation and maintainance
* of non-core utilities.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Jeremy D. Lea.
* 11 May 2002
*
* This is the version module. Based on pkg_version.pl by Bruce A. Mah.
*
*/
#ifndef _INST_VERSION_H_INCLUDE
#define _INST_VERSION_H_INCLUDE
/* Where the ports lives by default */
#define DEF_PORTS_DIR "/usr/ports"
/* just in case we change the environment variable name */
#define PORTSDIR "PORTSDIR"
/* macro to get name of directory where we put logging information */
#define PORTS_DIR (getenv(PORTSDIR) ? getenv(PORTSDIR) : DEF_PORTS_DIR)
struct index_entry {
SLIST_ENTRY(index_entry) next;
char name[PATH_MAX];
char origin[PATH_MAX];
};
SLIST_HEAD(index_head, index_entry);
extern char *LimitChars;
extern char *PreventChars;
extern char *MatchName;
#endif /* _INST_VERSION_H_INCLUDE */