Hi,

On Thu, May 09, 2002 at 08:33:22PM +0100, Mark Murray wrote:
> /usr/sbin/pkg_version Jeremy Lea <[EMAIL PROTECTED]> - re

OK, the first revision is attached.  It appears to work for me...  It
needs some spit and polish, and probably a few more people to test.

I've not implemented the -d flag since it sort of became unneeded, and
it's not really the way things are done in the rest of pkg_*.  I've also
not implemented -c.  There were enough warnings that it wasn't really
useful, and portupgrade does a much better job... 

Regards,
  -Jeremy

-- 
FreeBSD - Because the best things in life are free...
                                           http://www.freebsd.org/
Index: lib/lib.h
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pkg_install/lib/lib.h,v
retrieving revision 1.43
diff -u -r1.43 lib.h
--- lib/lib.h   5 May 2002 21:03:25 -0000       1.43
+++ lib/lib.h   14 May 2002 12:41:41 -0000
@@ -201,6 +201,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: /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       14 May 2002 12:41:41 -0000
@@ -14,6 +14,15 @@
  * Maxim Sobolev
  * 31 July 2001
  *
+ */
+ 
+#include <sys/cdefs.h>
+__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 <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: src/usr.sbin/pkg_install/lib/version.c,v 1.2 2002/04/01 09:39:07 
obrien Exp $");
-
-#include "lib.h"
-#include <err.h>
-
 int
 verscmp(Package *pkg, int major, int minor)
 {
@@ -43,4 +45,123 @@
        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. 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: /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    14 May 2002 12:41:41 -0000
@@ -1,7 +1,14 @@
 # $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
 
 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      14 May 2002 12:41:41 -0000
@@ -0,0 +1,90 @@
+/*
+ *
+ * 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 [-chv] [-l limchar] [-L limchar] [-s string] index",
+       "       pkg_info -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   14 May 2002 12:41:41 -0000
@@ -0,0 +1,298 @@
+/*
+ * 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 <fts.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 *);
+static int fname_cmp(const FTSENT **, const FTSENT **);
+
+/*
+ * 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...
+ *
+ * Jeremy D. Lea.
+ */
+int
+pkg_perform(char **indexarg)
+{
+    const char *paths[2] = {LOG_DIR, NULL};
+    char tmp[PATH_MAX];
+    struct index_entry *ie;
+    int err_cnt = 0;
+    FTS *ftsp;
+    FTSENT *f;
+
+    if (!isdir(paths[0]))
+       errx(2, "Unable to find package database directory");
+
+    if (*indexarg == NULL) {
+       strlcpy(tmp, PORTS_DIR, PATH_MAX);
+       strlcat(tmp, "/INDEX", PATH_MAX);
+    } else
+       strlcpy(tmp, *indexarg, PATH_MAX);
+    if (isURL(tmp))
+       IndexFile = fetchGetURL(tmp, "");
+    else
+       IndexFile = fopen(tmp, "r");
+
+    ftsp = fts_open((char * const *)(uintptr_t)paths, FTS_LOGICAL | FTS_NOCHDIR | 
+FTS_NOSTAT, fname_cmp);
+    if (ftsp != NULL) {
+       while ((f = fts_read(ftsp)) != NULL) {
+           if (f->fts_info == FTS_D && f->fts_level == 1) {
+               fts_set(ftsp, f, FTS_SKIP);
+               if (MatchName == NULL || strstr(f->fts_name, MatchName))
+                       err_cnt += pkg_do(f->fts_name);
+           }
+       }
+       fts_close(ftsp);
+    }
+
+    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.
+ *
+ * Jeremy D. Lea.
+ */
+static int
+pkg_do(char *pkg)
+{
+    char *ch, tmp[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;
+    strlcpy(tmp, LOG_DIR, PATH_MAX);
+    strlcat(tmp, "/", PATH_MAX);
+    strlcat(tmp, pkg, PATH_MAX);
+    strlcat(tmp, "/", PATH_MAX);
+    strlcat(tmp, CONTENTS_FNAME, PATH_MAX);
+    fp = fopen(tmp, "r");
+    if (!fp) {
+       warnx("unable to open %s file", CONTENTS_FNAME);
+       return 1;
+    }
+    read_plist(&plist, fp);
+    fclose(fp);
+
+    if (plist.origin != NULL) {
+       strlcpy(tmp, PORTS_DIR, PATH_MAX);
+       strlcat(tmp, "/", PATH_MAX);
+       strlcat(tmp, plist.origin, PATH_MAX);
+       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);
+               return 1;
+           }
+           show_version(plist.name, latest, "port");
+       }               
+    }
+    if (latest == NULL) {
+       /* We only pull in the INDEX once. */
+       if (SLIST_EMPTY(&Index)) {
+           if (!IndexFile) {
+               warnx("Unable to open INDEX.");
+               return 1;
+           }
+           while ((ch = fgetln(IndexFile, &len)) != NULL) {
+               strlcpy(tmp, ch, MIN(len, PATH_MAX));
+               /* The INDEX has pkgname|portdir|... */
+               if ((ch = strchr(tmp, '|')) == NULL)
+                   goto funny;
+               ch[0] = '\0';
+               if ((ch = strchr(&ch[1], '|')) == NULL)
+                   goto funny;
+               ch[0] = '\0';
+               /* Look backwards for the last two dirs = origin */
+               while (*--ch != '/')
+                   if (ch[0] == '\0')
+                       goto funny;
+               while (*--ch != '/')
+                   if (ch[0] == '\0')
+                       goto funny;
+               if ((ie = malloc(sizeof(struct index_entry))) == NULL) {
+                   warnx("Unable to allocate memory.");
+                   return 1;
+               }
+               strlcpy(ie->name, tmp, PATH_MAX);
+               strlcpy(ie->origin, &ch[1], PATH_MAX);
+               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);
+               if ((ch = strrchr(tmp, '-')) == NULL)
+                   goto funny;
+               ch[0] = '\0';
+               if (strstr(plist.name, tmp)) {
+                   if (latest != NULL) {
+                       /* Multiple matches */
+                       strlcpy(tmp, latest, PATH_MAX);
+                       strlcat(tmp, "|", PATH_MAX);
+                       strlcat(tmp, ie->name, PATH_MAX);
+                       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;
+
+ funny:
+    warnx("This is a very funny looking INDEX!");
+    return 1;
+}
+
+#define LIMIT(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 ;-).  The limit characters
+ * flags don't make any sense to me.
+ *
+ * Jeremy D. Lea.
+ */
+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 && LIMIT('!')) {
+           printf("%-34s  !", tmp);
+           if (Verbose)
+               printf("   Comparison failed");
+           printf("\n");
+       } else if (source != NULL && LIMIT('?')) {
+           printf("%-34s  ?", tmp);
+           if (Verbose)
+               printf("   orphaned: %s", source);
+           printf("\n");
+       }
+       return;
+    }
+    if (strchr(latest,'|') != NULL) {
+       if (LIMIT('*')) {
+           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);
+                   printf(", %s", ver);
+               } while ((ch = strchr(&ch[1], '|')) != NULL);
+               printf(")");
+           }
+           printf("\n");
+       }
+       return;
+    }
+    cmp = version_cmp(installed, latest);
+    ver = version_of(latest, NULL, NULL);
+    if (cmp < 0 && LIMIT('<')) {
+       printf("%-34s  <", tmp);
+       if (Verbose)
+           printf("   needs updating (%s has %s)", source, ver);
+       printf("\n");
+    } else if (cmp == 0 && LIMIT('=')) {
+       printf("%-34s  =", tmp);
+       if (Verbose)
+           printf("   up-to-date with %s", source);
+       printf("\n");
+    } else if (cmp > 0 && LIMIT('>')) {
+       printf("%-34s  >", tmp);
+       if (Verbose)
+           printf("   succeeds %s (%s has %s)", source, source, ver);
+       printf("\n");
+    }
+}
+
+void
+cleanup(int sig)
+{
+    if (sig)
+       exit(1);
+}
+
+static int
+fname_cmp(const FTSENT **a, const FTSENT **b)
+{
+    return strcmp((*a)->fts_name, (*b)->fts_name);
+}
Index: version/pkg_version.1
===================================================================
RCS file: /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       14 May 2002 12:41:41 -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,15 @@
 .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 [EMAIL PROTECTED]
+.An Bruce A. Mah Aq [EMAIL PROTECTED] . Converted to C by
+.An Jeremy D. Lea Aq [EMAIL PROTECTED]
 .Sh CONTRIBUTORS
 .An Nik Clayton Aq [EMAIL PROTECTED] ,
 .An Dominic Mitchell Aq [EMAIL PROTECTED] ,
 .An Mark Ovens Aq [EMAIL PROTECTED] ,
 .An Doug Barton Aq [EMAIL PROTECTED] ,
 .An Akinori MUSHA Aq [EMAIL PROTECTED]
-.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: /home/ncvs/src/usr.sbin/pkg_install/version/pkg_version.pl,v
retrieving revision 1.24
diff -u -r1.24 pkg_version.pl
--- version/pkg_version.pl      18 Sep 2001 17:56:44 -0000      1.24
+++ version/pkg_version.pl      14 May 2002 12:41:41 -0000
@@ -1,599 +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.24 2001/09/18 
17:56:44 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.
-           # 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);
-
-       # 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
-
-.
-  ;
Index: version/test-pkg_version.sh
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pkg_install/version/test-pkg_version.sh,v
retrieving revision 1.2
diff -u -r1.2 test-pkg_version.sh
--- version/test-pkg_version.sh 19 Apr 2001 22:17:07 -0000      1.2
+++ version/test-pkg_version.sh 14 May 2002 12:41:41 -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   14 May 2002 12:41:41 -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 */

Reply via email to