Index: lib/lib.h =================================================================== RCS file: /usr/home/ncvs/src/usr.sbin/pkg_install/lib/lib.h,v retrieving revision 1.44 diff -u -r1.44 lib.h --- lib/lib.h 14 May 2002 21:42:37 -0000 1.44 +++ lib/lib.h 1 Jun 2002 11:10:39 -0000 @@ -203,6 +203,8 @@ /* 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; Index: lib/version.c =================================================================== RCS file: /usr/home/ncvs/src/usr.sbin/pkg_install/lib/version.c,v retrieving revision 1.2 diff -u -r1.2 version.c --- lib/version.c 1 Apr 2002 09:39:07 -0000 1.2 +++ lib/version.c 1 Jun 2002 11:10:39 -0000 @@ -14,6 +14,15 @@ * Maxim Sobolev * 31 July 2001 * + */ + +#include +__FBSDID("$FreeBSD: src/usr.sbin/pkg_install/lib/version.c,v 1.2 2002/04/01 09:39:07 obrien Exp $"); + +#include "lib.h" +#include + +/* * 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 -__FBSDID("$FreeBSD: src/usr.sbin/pkg_install/lib/version.c,v 1.2 2002/04/01 09:39:07 obrien Exp $"); - -#include "lib.h" -#include - int verscmp(Package *pkg, int major, int minor) { @@ -43,4 +45,127 @@ rval = 1; 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; + } } Index: version/Makefile =================================================================== RCS file: /usr/home/ncvs/src/usr.sbin/pkg_install/version/Makefile,v retrieving revision 1.10 diff -u -r1.10 Makefile --- version/Makefile 18 Sep 2001 17:58:10 -0000 1.10 +++ version/Makefile 7 Jun 2002 21:42:00 -0000 @@ -1,7 +1,19 @@ # $FreeBSD: src/usr.sbin/pkg_install/version/Makefile,v 1.10 2001/09/18 17:58:10 bmah Exp $ -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+= ${LIBCRYPTO} ${LIBSSL} +LDADD+= -lcrypto -lssl +.endif test: ./test-pkg_version.sh Index: version/main.c =================================================================== RCS file: version/main.c diff -N version/main.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ version/main.c 1 Jun 2002 11:10:39 -0000 @@ -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 +__FBSDID("$FreeBSD$"); + +#include "lib.h" +#include "version.h" +#include + +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); +} Index: version/perform.c =================================================================== RCS file: version/perform.c diff -N version/perform.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ version/perform.c 1 Jun 2002 11:10:39 -0000 @@ -0,0 +1,276 @@ +/* + * 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 +__FBSDID("$FreeBSD$"); + +#include "lib.h" +#include "version.h" +#include +#include +#include + +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) { + strlcpy(tmp, ch, MIN(len, PATH_MAX)); + /* The INDEX has pkgname|portdir|... */ + if ((ch = strchr(tmp, '|')) == NULL) + errx(2, "The INDEX does not appear to be valid!"); + ch[0] = '\0'; + if ((ch = strchr(&ch[1], '|')) == NULL) + errx(2, "The INDEX does not appear to be valid!"); + ch[0] = '\0'; + /* Look backwards for the last two dirs = origin */ + while (*--ch != '/') + if (ch[0] == '\0') + errx(2, "The INDEX does not appear to be valid!"); + while (*--ch != '/') + if (ch[0] == '\0') + 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); +} Index: version/pkg_version.1 =================================================================== RCS file: /usr/home/ncvs/src/usr.sbin/pkg_install/version/pkg_version.1,v retrieving revision 1.21 diff -u -r1.21 pkg_version.1 --- version/pkg_version.1 20 Apr 2002 12:27:02 -0000 1.21 +++ version/pkg_version.1 1 Jun 2002 11:10:39 -0000 @@ -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 @@ .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 @@ .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 . Index: version/pkg_version.pl =================================================================== RCS file: version/pkg_version.pl diff -N version/pkg_version.pl --- version/pkg_version.pl 31 May 2002 15:45:48 -0000 1.25 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -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: src/usr.sbin/pkg_install/version/pkg_version.pl,v 1.25 2002/05/31 15:45:48 bmah Exp $ -# - -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 = "= 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 () { - ($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 = ; - 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 = ; - 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 () { - ($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<{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 - -. - ; Index: version/test-pkg_version.sh =================================================================== RCS file: /usr/home/ncvs/src/usr.sbin/pkg_install/version/test-pkg_version.sh,v retrieving revision 1.3 diff -u -r1.3 test-pkg_version.sh --- version/test-pkg_version.sh 28 May 2002 18:00:44 -0000 1.3 +++ version/test-pkg_version.sh 1 Jun 2002 11:10:39 -0000 @@ -33,7 +33,7 @@ # ECHO=echo -PKG_VERSION=./pkg_version.pl +PKG_VERSION=./pkg_version test-pv ( ) { \ setvar v1 $1 Index: version/version.h =================================================================== RCS file: version/version.h diff -N version/version.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ version/version.h 1 Jun 2002 11:10:39 -0000 @@ -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 */