Index: head/usr.sbin/pkg_install/lib/lib.h =================================================================== --- head/usr.sbin/pkg_install/lib/lib.h (revision 131273) +++ head/usr.sbin/pkg_install/lib/lib.h (revision 131274) @@ -1,226 +1,225 @@ /* $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. * * Jordan K. Hubbard * 18 July 1993 * * Include and define various things wanted by the library routines. * */ #ifndef _INST_LIB_LIB_H_ #define _INST_LIB_LIB_H_ /* Includes */ #include #include #include #include #include #include #include #include #include #include #include /* Macros */ #define SUCCESS (0) #define FAIL (-1) #ifndef TRUE #define TRUE (1) #endif #ifndef FALSE #define FALSE (0) #endif #define YES 2 #define NO 1 /* Usually "rm", but often "echo" during debugging! */ #define REMOVE_CMD "rm" /* Usually "rm", but often "echo" during debugging! */ #define RMDIR_CMD "rmdir" /* Where we put logging information by default, else ${PKG_DBDIR} if set */ #define DEF_LOG_DIR "/var/db/pkg" /* just in case we change the environment variable name */ #define PKG_DBDIR "PKG_DBDIR" /* macro to get name of directory where we put logging information */ #define LOG_DIR (getenv(PKG_DBDIR) ? getenv(PKG_DBDIR) : DEF_LOG_DIR) /* The names of our "special" files */ #define CONTENTS_FNAME "+CONTENTS" #define COMMENT_FNAME "+COMMENT" #define DESC_FNAME "+DESC" #define INSTALL_FNAME "+INSTALL" #define POST_INSTALL_FNAME "+POST-INSTALL" #define DEINSTALL_FNAME "+DEINSTALL" #define POST_DEINSTALL_FNAME "+POST-DEINSTALL" #define REQUIRE_FNAME "+REQUIRE" #define REQUIRED_BY_FNAME "+REQUIRED_BY" #define DISPLAY_FNAME "+DISPLAY" #define MTREE_FNAME "+MTREE_DIRS" #define CMD_CHAR '@' /* prefix for extended PLIST cmd */ /* The name of the "prefix" environment variable given to scripts */ #define PKG_PREFIX_VNAME "PKG_PREFIX" /* * Version of the package tools - increase only when some * functionality used by bsd.port.mk is changed, added or removed */ #define PKG_INSTALL_VERSION 20030417 #define PKG_WRAPCONF_FNAME "/var/db/pkg_install.conf" #define main(argc, argv) real_main(argc, argv) /* Version numbers to assist with changes in package file format */ #define PLIST_FMT_VER_MAJOR 1 #define PLIST_FMT_VER_MINOR 1 enum _plist_t { PLIST_FILE, PLIST_CWD, PLIST_CMD, PLIST_CHMOD, PLIST_CHOWN, PLIST_CHGRP, PLIST_COMMENT, PLIST_IGNORE, PLIST_NAME, PLIST_UNEXEC, PLIST_SRC, PLIST_DISPLAY, PLIST_PKGDEP, PLIST_CONFLICTS, PLIST_MTREE, PLIST_DIR_RM, PLIST_IGNORE_INST, PLIST_OPTION, PLIST_ORIGIN, PLIST_DEPORIGIN }; typedef enum _plist_t plist_t; enum _match_t { MATCH_ALL, MATCH_EXACT, MATCH_GLOB, MATCH_REGEX }; typedef enum _match_t match_t; /* Types */ typedef unsigned int Boolean; struct _plist { struct _plist *prev, *next; char *name; Boolean marked; plist_t type; }; typedef struct _plist *PackingList; struct _pack { struct _plist *head, *tail; char *name; char *origin; int fmtver_maj, fmtver_mnr; }; typedef struct _pack Package; struct reqr_by_entry { STAILQ_ENTRY(reqr_by_entry) link; char pkgname[PATH_MAX]; }; STAILQ_HEAD(reqr_by_head, reqr_by_entry); /* Prototypes */ /* Misc */ int vsystem(const char *, ...); char *vpipe(const char *, ...); void cleanup(int); char *make_playpen(char *, off_t); char *where_playpen(void); void leave_playpen(void); off_t min_free(const char *); /* String */ char *get_dash_string(char **); char *copy_string(const char *); char *copy_string_adds_newline(const char *); Boolean suffix(const char *, const char *); void nuke_suffix(char *); void str_lowercase(char *); char *strconcat(const char *, const char *); char *get_string(char *, int, FILE *); /* File */ Boolean fexists(const char *); Boolean isdir(const char *); Boolean isemptydir(const char *fname); Boolean isemptyfile(const char *fname); Boolean isfile(const char *); Boolean isempty(const char *); Boolean issymlink(const char *); Boolean isURL(const char *); char *fileGetURL(const char *, const char *); char *fileFindByPath(const char *, const char *); char *fileGetContents(const char *); void write_file(const char *, const char *); void copy_file(const char *, const char *, const char *); void move_file(const char *, const char *, const char *); void copy_hierarchy(const char *, const char *, Boolean); int delete_hierarchy(const char *, Boolean, Boolean); int unpack(const char *, const char *); void format_cmd(char *, int, const char *, const char *, const char *); /* Msg */ void upchuck(const char *); void barf(const char *, ...); void whinge(const char *, ...); Boolean y_or_n(Boolean, const char *, ...); /* Packing list */ PackingList new_plist_entry(void); PackingList last_plist(Package *); PackingList find_plist(Package *, plist_t); char *find_plist_option(Package *, const char *name); void plist_delete(Package *, Boolean, plist_t, const char *); void free_plist(Package *); void mark_plist(Package *); void csum_plist_entry(char *, PackingList); void add_plist(Package *, plist_t, const char *); void add_plist_top(Package *, plist_t, const char *); void delete_plist(Package *pkg, Boolean all, plist_t type, const char *name); void write_plist(Package *, FILE *); void read_plist(Package *, FILE *); int plist_cmd(const char *, char **); int delete_package(Boolean, Boolean, Package *); Boolean make_preserve_name(char *, int, const char *, const char *); /* For all */ int pkg_perform(char **); int real_main(int, char **); /* Query installed packages */ char **matchinstalled(match_t, char **, int *); char **matchbyorigin(const char *, int *); int isinstalledpkg(const char *name); /* Dependencies */ int sortdeps(char **); int chkifdepends(const char *, const char *); 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; extern Boolean Fake; extern Boolean Force; extern int AutoAnswer; #endif /* _INST_LIB_LIB_H_ */ Index: head/usr.sbin/pkg_install/lib/version.c =================================================================== --- head/usr.sbin/pkg_install/lib/version.c (revision 131273) +++ head/usr.sbin/pkg_install/lib/version.c (revision 131274) @@ -1,171 +1,328 @@ /* * FreeBSD install - a package for the installation and maintenance * 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. * * Maxim Sobolev * 31 July 2001 * */ #include __FBSDID("$FreeBSD$"); #include "lib.h" #include /* * Routines to assist with PLIST_FMT_VER numbers in the packing * lists. * * Following is the PLIST_FMT_VER history: * 1.0 - Initial revision; * 1.1 - When recording/checking checksum of symlink use hash of readlink() * value instead of the hash of an object this links points to. * */ int verscmp(Package *pkg, int major, int minor) { int rval = 0; if ((pkg->fmtver_maj < major) || (pkg->fmtver_maj == major && pkg->fmtver_mnr < minor)) rval = -1; else if ((pkg->fmtver_maj > major) || (pkg->fmtver_maj == major && pkg->fmtver_mnr > minor)) 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. + * split_version(pkgname, endname, epoch, revision) returns a pointer to + * the version portion of a package name and the two special components. * - * Jeremy D. Lea. + * Syntax is: ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}] + * + * Written by Oliver Eikemeier + * Based on work of Jeremy D. Lea. */ -const char * -version_of(const char *pkgname, int *epoch, int *revision) +static const char * +split_version(const char *pkgname, const char **endname, unsigned long *epoch, unsigned long *revision) { char *ch; + const char *versionstr; + const char *endversionstr; if (pkgname == NULL) errx(2, "%s: Passed NULL pkgname.", __func__); + + /* Look for the last '-' the the pkgname */ + ch = strrchr(pkgname, '-'); + /* Cheat if we are just passed a version, not a valid package name */ + versionstr = ch ? ch + 1 : pkgname; + + /* Look for the last '_' in the version string, advancing the end pointer */ + ch = strrchr(versionstr, '_'); + if (revision != NULL) { + *revision = ch ? strtoul(ch + 1, NULL, 10) : 0; + } + endversionstr = ch; + + /* Look for the last ',' in the remaining version string */ + ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ','); if (epoch != NULL) { - if ((ch = strrchr(pkgname, ',')) == NULL) - *epoch = 0; - else - *epoch = atoi(&ch[1]); + *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0; } - if (revision != NULL) { - if ((ch = strrchr(pkgname, '_')) == NULL) - *revision = 0; - else - *revision = atoi(&ch[1]); + if (ch && !endversionstr) + endversionstr = ch; + + /* set the pointer behind the last character of the version without revision or epoch */ + if (endname) + *endname = endversionstr ? endversionstr : strrchr(versionstr, '\0'); + + return versionstr; +} + +/* + * PORTVERSIONs are composed of components separated by dots. A component + * consists of a version number, a letter and a patchlevel number. This does + * not conform to the porter's handbook, but let us formulate rules that + * fit the current practice and are far simpler than to make decisions + * based on the order of netters and lumbers. Besides, people use versions + * like 10b2 in the ports... + */ + +typedef struct { +#ifdef __LONG_LONG_SUPPORTED + long long n; + long long pl; +#else + long n; + long pl; +#endif + int a; +} version_component; + +/* + * get_component(position, component) gets the value of the next component + * (number - letter - number triple) and returns a pointer to the next character + * after any leading separators + * + * - components are separated by dots + * - characters !~ [a-zA-Z0-9.+*] are treated as separators + * (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect: + * 1.0.1:2003.09.16 < 1.0:2003.09.16 + * - consecutive separators are collapsed (10..1 = 10.1) + * - missing separators are inserted, essentially + * letter number letter => letter number . letter (10a1b2 = 10a1.b2) + * - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0) + * - the letter sort order is: [none], a, b, ..., z; numbers without letters + * sort first (10 < 10a < 10b) + * - missing version numbers (in components starting with a letter) sort as -1 + * (a < 0, 10.a < 10) + * - a separator is inserted before the special strings "pl", "alpha", "beta", + * "pre" and "rc". + * - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc" + * sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3 + * < 0.1beta2 = 0.1.b2 < 0.1) + * - other strings use only the first letter for sorting, case is ignored + * (1.d2 = 1.dev2 = 1.Development2) + * - The special component `*' is guaranteed to be the smallest possible + * component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*) + * - components separated by `+' are handled by version_cmp below + * + * Oliver Eikemeier + */ + +static const struct { + const char *name; + size_t namelen; + int value; +} stage[] = { + { "pl", 2, 0 }, + { "alpha", 5, 'a'-'a'+1 }, + { "beta", 4, 'b'-'a'+1 }, + { "pre", 3, 'p'-'a'+1 }, + { "rc", 2, 'r'-'a'+1 }, + { NULL, 0, -1 } +}; + +static const char * +get_component(const char *position, version_component *component) +{ + const char *pos = position; + int hasstage = 0, haspatchlevel = 0; + + if (!pos) + errx(2, "%s: Passed NULL position.", __func__); + + /* handle version number */ + if (isdigit(*pos)) { + char *endptr; +#ifdef __LONG_LONG_SUPPORTED + component->n = strtoll(pos, &endptr, 10); +#else + component->n = strtol(pos, &endptr, 10); +#endif + /* should we test for errno == ERANGE? */ + pos = endptr; + } else if (*pos == '*') { + component->n = -2; + do { + pos++; + } while(*pos && *pos != '+'); + } else { + component->n = -1; + hasstage = 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]; + + /* handle letter */ + if (isalpha(*pos)) { + int c = tolower(*pos); + haspatchlevel = 1; + /* handle special suffixes */ + if (isalpha(pos[1])) { + int i; + for (i = 0; stage[i].name; i++) { + if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0 + && !isalpha(pos[stage[i].namelen])) { + if (hasstage) { + /* stage to value */ + component->a = stage[i].value; + pos += stage[i].namelen; + } else { + /* insert dot */ + component->a = 0; + haspatchlevel = 0; + } + c = 0; + break; + } + } + } + /* unhandled above */ + if (c) { + /* use the first letter and skip following */ + component->a = c - 'a' + 1; + do { + ++pos; + } while (isalpha(*pos)); + } + } else { + component->a = 0; + haspatchlevel = 0; + } + + if (haspatchlevel) { + /* handle patch number */ + if (isdigit(*pos)) { + char *endptr; +#ifdef __LONG_LONG_SUPPORTED + component->pl = strtoll(pos, &endptr, 10); +#else + component->pl = strtol(pos, &endptr, 10); +#endif + /* should we test for errno == ERANGE? */ + pos = endptr; + } else { + component->pl = -1; + } + } else { + component->pl = 0; + } + + /* skip trailing separators */ + while (*pos && !isdigit(*pos) && !isalpha(*pos) && *pos != '+' && *pos != '*') { + pos++; + } + + return pos; } /* * 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 * comparison of the basenames is done. * * The port version is defined by: * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}] * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${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, separated 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. + * The tests allow for significantly more latitude in the version numbers + * than is allowed in the guidelines. No point in enforcing them here. + * That's what portlint is for. * * Jeremy D. Lea. + * reimplemented by Oliver Eikemeier */ 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; + const char *v1, *v2, *ve1, *ve2; + unsigned long e1, e2, r1, r2; + int result = 0; - 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); + v1 = split_version(pkg1, &ve1, &e1, &r1); + v2 = split_version(pkg2, &ve2, &e2, &r2); + + /* Check epoch, port version, and port revision, in that order. */ + if (e1 != e2) { + result = (e1 < e2 ? -1 : 1); + } + + /* Shortcut check for equality before invoking the parsing routines. */ + if (result == 0 && (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) { + /* Loop over different components (the parts separated by dots). + * If any component differs, we have the basis for an inequality. */ + while(result == 0 && (v1 < ve1 || v2 < ve2)) { + int block_v1 = 0; + int block_v2 = 0; + version_component vc1 = {0, 0, 0}; + version_component vc2 = {0, 0, 0}; + if (v1 < ve1 && *v1 != '+') { + v1 = get_component(v1, &vc1); } else { - /* Neither were letters. Advance over the period. */ - v1 = (t1[0] == '.' ? &t1[1] : t1); - v2 = (t2[0] == '.' ? &t2[1] : t2); + block_v1 = 1; } - c1 = strchr("_,", v1[0]); - c2 = strchr("_,", v2[0]); + if (v2 < ve2 && *v2 != '+') { + v2 = get_component(v2, &vc2); + } else { + block_v2 = 1; + } + if (block_v1 && block_v2) { + if (v1 < ve1) + v1++; + if (v2 < ve2) + v2++; + } else if (vc1.n != vc2.n) { + result = (vc1.n < vc2.n ? -1 : 1); + } else if (vc1.a != vc2.a) { + result = (vc1.a < vc2.a ? -1 : 1); + } else if (vc1.pl != vc2.pl) { + result = (vc1.pl < vc2.pl ? -1 : 1); + } } - /* 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; } + + /* Compare FreeBSD revision numbers. */ + if (result == 0 && r1 != r2) { + result = (r1 < r2 ? -1 : 1); + } + return result; } Index: head/usr.sbin/pkg_install/version/perform.c =================================================================== --- head/usr.sbin/pkg_install/version/perform.c (revision 131273) +++ head/usr.sbin/pkg_install/version/perform.c (revision 131274) @@ -1,313 +1,316 @@ /* * 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, *pat[2], **patterns; struct index_entry *ie; int i, err_cnt = 0; int MatchType; /* * Try to find and open the INDEX. We only check IndexFile != NULL * later, if we actually need the INDEX. * XXX This should not be hard-coded to INDEX-5. */ if (*indexarg == NULL) snprintf(tmp, PATH_MAX, "%s/INDEX-5", PORTS_DIR); else strlcpy(tmp, *indexarg, PATH_MAX); if (isURL(tmp)) IndexFile = fetchGetURL(tmp, ""); else IndexFile = fopen(tmp, "r"); /* Get either a list of matching or all packages */ if (MatchName != NULL) { pat[0] = MatchName; pat[1] = NULL; MatchType = MATCH_REGEX; patterns = pat; } else { MatchType = MATCH_ALL; patterns = NULL; } pkgs = matchinstalled(MatchType, patterns, &err_cnt); if (err_cnt != 0) errx(2, "Unable to find package database directory!"); if (pkgs == NULL) { switch (MatchType) { case MATCH_ALL: warnx("no packages installed"); return (0); case MATCH_REGEX: warnx("no packages match pattern"); return (1); default: break; } } for (i = 0; pkgs[i] != NULL; i++) 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); if (ie->name != NULL) free(ie->name); if (ie->origin != NULL) free(ie->origin); 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); if (plist.name == NULL) { warnx("%s does not appear to be a valid package!", pkg); return 1; } /* * 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__); bzero(ie, sizeof(struct index_entry)); ie->name = strdup(tmp); ie->origin = strdup(&ch[1]); /* 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); + ver = strrchr(tmp, '-'); + ver = ver ? &ver[1] : tmp; printf(" multiple versions (index has %s", ver); do { - ver = version_of(&ch[1], NULL, NULL); + ver = strrchr(&ch[1], '-'); + ver = ver ? &ver[1] : &ch[1]; 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); + ver = strrchr(latest, '-'); + ver = ver ? &ver[1] : latest; 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: head/usr.sbin/pkg_install/version/test-pkg_version.sh =================================================================== --- head/usr.sbin/pkg_install/version/test-pkg_version.sh (revision 131273) +++ head/usr.sbin/pkg_install/version/test-pkg_version.sh (revision 131274) @@ -1,75 +1,94 @@ #!/bin/sh # # Copyright 2001 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. # # test-pkg_version.sh # # Regression testing for pkg_version # Originally from an idea by "Akinori MUSHA" # # $FreeBSD$ # ECHO=echo PKG_VERSION=./pkg_version test-pv ( ) { \ setvar v1 $1 setvar answer $2 setvar v2 $3 setvar type $4 res=`${PKG_VERSION} -t ${v1} ${v2}` if [ ${res} != ${answer} ]; then \ ${ECHO} "${type} test failed (${v1} ${res} ${v2}, should have been ${answer})"; \ fi } # Test coercion of default PORTREVISION and PORTEPOCH test-pv 0.10 "=" 0.10_0 coercion test-pv 0.10 "=" 0.10,0 coercion test-pv 0.10 "=" 0.10_0,0 coercion # Test various comparisons test-pv 1.0 "=" 1.0 equality test-pv 2.15a "=" 2.15a equality test-pv 0.10 ">" 0.9 inequality test-pv 0.9 "<" 0.10 inequality test-pv 2.3p10 ">" 2.3p9 number/letter test-pv 1.6.0 ">" 1.6.0.p3 number/letter test-pv 1.0.b ">" 1.0.a3 number/letter test-pv 1.0a ">" 1.0 number/letter test-pv 1.0a "<" 1.0b number/letter test-pv 5.0a ">" 5.0.b number/letter test-pv 1.5_1 ">" 1.5 portrevision test-pv 1.5_2 ">" 1.5_1 portrevision test-pv 1.5_1 "<" 1.5.0.1 portrevision test-pv 1.5 "<" 1.5.0.1 portrevision test-pv 00.01.01,1 ">" 99.12.31 portepoch test-pv 0.0.1_1,2 ">" 0.0.1,2 portrevision/portepoch test-pv 0.0.1_1,3 ">" 0.0.1_2,2 portrevision/portepoch + +test-pv 2.0 ">" 2.a2 number/letter +test-pv 3 "=" 3.0 equality +test-pv 4a "<" 4a0 letter/zero +test-pv 10a1b2 "=" 10a1.b2 separator + +test-pv 7pl "=" 7.pl patchevel +test-pv 8.0.a "=" 8.0alpha alpha +test-pv 9.b3.0 "=" 9beta3 beta +test-pv 10.pre7 "=" 10pre7.0 pre +test-pv 11.r "=" 11.rc rc + +test-pv 12pl "<" 12alpha alpha/patchevel +test-pv 13.* "<" 13.pl star/patchevel + +test-pv 1.0.0+2003.09.06 "=" 1.0+2003.09.06 plus/multiple +test-pv 1.0.1+2003.09.06 ">" 1.0+2003.09.06 plus/multiple +test-pv 1.0.0+2003.09.06 "<" 1.0+2003.09.06_1 plus/portrevision +test-pv 1.0.1+2003.09.06 ">" 1.0+2003.09.06_1 plus/portrevision