diff -u pkg_install/add/add.h pkg_install/add/add.h --- pkg_install/add/add.h Wed Oct 10 08:58:41 2001 +++ pkg_install/add/add.h Fri Apr 30 13:31:09 2004 @@ -34,6 +34,7 @@ extern char *Group; extern char *Directory; extern char *PkgName; +extern char *PkgAddCmd; extern char FirstPen[]; extern add_mode_t AddMode; diff -u pkg_install/add/extract.c pkg_install/add/extract.c --- pkg_install/add/extract.c Sat Jan 24 18:32:43 2004 +++ pkg_install/add/extract.c Fri Apr 30 12:37:02 2004 @@ -27,14 +27,14 @@ #include "add.h" -#define STARTSTRING "tar cf -" +#define STARTSTRING "/usr/bin/tar cf -" #define TOOBIG(str) \ (((int)strlen(str) + FILENAME_MAX + where_count > maxargs) ||\ ((int)strlen(str) + FILENAME_MAX + perm_count > maxargs)) #define PUSHOUT(todir) /* push out string */ \ if (where_count > (int)sizeof(STARTSTRING)-1) { \ - strcat(where_args, "|tar --unlink -xpf - -C "); \ + strcat(where_args, "|/usr/bin/tar --unlink -xpf - -C "); \ strcat(where_args, todir); \ if (system(where_args)) { \ cleanup(0); \ @@ -85,7 +85,7 @@ ++(pos); \ } while (0) -int +static int add_arg(char *buf, int len, const char *str) { int i = 0; diff -u pkg_install/add/futil.c pkg_install/add/futil.c --- pkg_install/add/futil.c Mon Apr 1 11:39:05 2002 +++ pkg_install/add/futil.c Fri Apr 30 12:48:46 2004 @@ -50,7 +50,7 @@ } } else { - if (vsystem("mkdir %s", dir)) { + if (vsystem("/bin/mkdir %s", dir)) { if (cp2) *cp2 = '/'; return FAIL; @@ -78,20 +78,20 @@ cd_to = dir; if (Mode) - if (vsystem("cd %s && chmod -R %s %s", cd_to, Mode, arg)) + if (vsystem("cd %s && /bin/chmod -R %s %s", cd_to, Mode, arg)) warnx("couldn't change modes of '%s' to '%s'", arg, Mode); if (Owner && Group) { - if (vsystem("cd %s && chown -R %s:%s %s", cd_to, Owner, Group, arg)) + if (vsystem("cd %s && /usr/sbin/chown -R %s:%s %s", cd_to, Owner, Group, arg)) warnx("couldn't change owner/group of '%s' to '%s:%s'", arg, Owner, Group); return; } if (Owner) { - if (vsystem("cd %s && chown -R %s %s", cd_to, Owner, arg)) + if (vsystem("cd %s && /usr/sbin/chown -R %s %s", cd_to, Owner, arg)) warnx("couldn't change owner of '%s' to '%s'", arg, Owner); return; } else if (Group) - if (vsystem("cd %s && chgrp -R %s %s", cd_to, Group, arg)) + if (vsystem("cd %s && /usr/bin/chgrp -R %s %s", cd_to, Group, arg)) warnx("couldn't change group of '%s' to '%s'", arg, Group); } diff -u pkg_install/add/main.c pkg_install/add/main.c --- pkg_install/add/main.c Sun Mar 14 11:12:32 2004 +++ pkg_install/add/main.c Fri Apr 30 13:31:09 2004 @@ -39,6 +39,7 @@ char *Owner = NULL; char *Group = NULL; char *PkgName = NULL; +char *PkgAddCmd = NULL; char *Directory = NULL; char FirstPen[FILENAME_MAX]; add_mode_t AddMode = NORMAL; @@ -84,7 +85,9 @@ char **start; char *cp, *packagesite = NULL, *remotepkg = NULL, *ptr; static char temppackageroot[MAXPATHLEN]; + static char pkgaddpath[MAXPATHLEN]; + PkgAddCmd = realpath(argv[0], pkgaddpath); start = argv; while ((ch = getopt(argc, argv, Options)) != -1) { switch(ch) { diff -u pkg_install/add/perform.c pkg_install/add/perform.c --- pkg_install/add/perform.c Thu Apr 17 11:56:05 2003 +++ pkg_install/add/perform.c Fri Apr 30 13:34:31 2004 @@ -71,7 +71,7 @@ int code; PackingList p; struct stat sb; - int inPlace, conflictsfound, i, errcode; + int inPlace, conflictsfound, errcode; /* support for separate pre/post install scripts */ int new_m = 0; char pre_script[FILENAME_MAX] = INSTALL_FNAME; @@ -167,7 +167,7 @@ if (!isdir(p->name) && !Fake) { if (Verbose) printf("Desired prefix of %s does not exist, creating..\n", p->name); - vsystem("mkdir -p %s", p->name); + vsystem("/bin/mkdir -p %s", p->name); if (chdir(p->name) == -1) { warn("unable to change directory to '%s'", p->name); goto bomb; @@ -243,7 +243,7 @@ * See if we're already registered either with the same name (the same * version) or some other version with the same origin. */ - if ((isinstalledpkg(Plist.name) || + if ((isinstalledpkg(Plist.name) > 0 || matchbyorigin(Plist.origin, NULL) != NULL) && !Force) { warnx("package '%s' or its older version already installed", Plist.name); @@ -254,13 +254,14 @@ /* Now check the packing list for conflicts */ for (p = Plist.head; p != NULL; p = p->next) { if (p->type == PLIST_CONFLICTS) { + int i; conflict[0] = strdup(p->name); conflict[1] = NULL; matched = matchinstalled(MATCH_GLOB, conflict, &errcode); free(conflict[0]); if (errcode == 0 && matched != NULL) for (i = 0; matched[i] != NULL; i++) - if (isinstalledpkg(matched[i])) { + if (isinstalledpkg(matched[i]) > 0) { warnx("package '%s' conflicts with %s", Plist.name, matched[i]); conflictsfound = 1; @@ -291,7 +292,7 @@ printf(" with '%s' origin", deporigin); printf(".\n"); } - if (!isinstalledpkg(p->name) && + if (isinstalledpkg(p->name) <= 0 && !(deporigin != NULL && matchbyorigin(deporigin, NULL) != NULL)) { char path[FILENAME_MAX], *cp = NULL; @@ -310,7 +311,7 @@ if (cp) { if (Verbose) printf("Loading it from %s.\n", cp); - if (vsystem("pkg_add %s'%s'", Verbose ? "-v " : "", cp)) { + if (vsystem("%s %s'%s'", PkgAddCmd, Verbose ? "-v " : "", cp)) { warnx("autoload of dependency '%s' failed%s", cp, Force ? " (proceeding anyway)" : "!"); if (!Force) @@ -333,7 +334,7 @@ if (!Force) ++code; } - else if (vsystem("(pwd; cat +CONTENTS) | pkg_add %s-S", Verbose ? "-v " : "")) { + else if (vsystem("(pwd; cat +CONTENTS) | %s %s-S", PkgAddCmd, Verbose ? "-v " : "")) { warnx("pkg_add of dependency '%s' failed%s", p->name, Force ? " (proceeding anyway)" : "!"); if (!Force) @@ -364,7 +365,7 @@ /* Look for the requirements file */ if (fexists(REQUIRE_FNAME)) { - vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */ + vsystem("/bin/chmod +x %s", REQUIRE_FNAME); /* be sure */ if (Verbose) printf("Running requirements file first for %s..\n", Plist.name); if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, Plist.name)) { @@ -397,7 +398,7 @@ /* If we're really installing, and have an installation file, run it */ if (!NoInstall && fexists(pre_script)) { - vsystem("chmod +x %s", pre_script); /* make sure */ + vsystem("/bin/chmod +x %s", pre_script); /* make sure */ if (Verbose) printf("Running pre-install for %s..\n", Plist.name); if (!Fake && vsystem("./%s %s %s", pre_script, Plist.name, pre_arg)) { @@ -426,7 +427,7 @@ /* Run the installation script one last time? */ if (!NoInstall && fexists(post_script)) { - vsystem("chmod +x %s", post_script); /* make sure */ + vsystem("/bin/chmod +x %s", post_script); /* make sure */ if (Verbose) printf("Running post-install for %s..\n", Plist.name); if (!Fake && vsystem("./%s %s %s", post_script, Plist.name, post_arg)) { @@ -456,7 +457,7 @@ goto success; /* close enough for government work */ } /* Make sure pkg_info can read the entry */ - vsystem("chmod a+rx %s", LogDir); + vsystem("/bin/chmod a+rx %s", LogDir); move_file(".", DESC_FNAME, LogDir); move_file(".", COMMENT_FNAME, LogDir); if (fexists(INSTALL_FNAME)) diff -u pkg_install/create/create.h pkg_install/create/create.h --- pkg_install/create/create.h Thu Apr 17 11:56:05 2003 +++ pkg_install/create/create.h Fri Apr 30 18:02:30 2004 @@ -34,6 +34,7 @@ extern char *Contents; extern char *Require; extern char *SrcDir; +extern char *BaseDir; extern char *ExcludeFrom; extern char *Mtree; extern char *Pkgdeps; @@ -47,6 +48,7 @@ enum zipper {NONE, GZIP, BZIP, BZIP2 }; extern enum zipper Zipper; +void add_cksum(Package *, PackingList, const char *); void check_list(const char *, Package *); int pkg_perform(char **); void copy_plist(const char *, Package *); diff -u pkg_install/create/main.c pkg_install/create/main.c --- pkg_install/create/main.c Thu Apr 17 11:56:05 2003 +++ pkg_install/create/main.c Thu Apr 29 15:57:30 2004 @@ -16,12 +16,13 @@ #include "lib.h" #include "create.h" -static char Options[] = "YNOhjvyzf:p:P:C:c:d:i:I:k:K:r:t:X:D:m:s:o:b:"; +static char Options[] = "YNOhjvyzf:p:P:C:c:d:i:I:k:K:r:t:X:D:m:s:S:o:b:"; char *Prefix = NULL; char *Comment = NULL; char *Desc = NULL; char *SrcDir = NULL; +char *BaseDir = NULL; char *Display = NULL; char *Install = NULL; char *PostInstall = NULL; @@ -75,6 +76,10 @@ SrcDir = optarg; break; + case 'S': + BaseDir = optarg; + break; + case 'f': Contents = optarg; break; @@ -199,12 +204,13 @@ static void usage() { - fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n", -"usage: pkg_create [-YNOhvy] [-P pkgs] [-C conflicts] [-p prefix] [-f contents] ", + fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n", +"usage: pkg_create [-YNOhvyz] [-P pkgs] [-C conflicts] [-p prefix] ", " [-i iscript] [-I piscript] [-k dscript] [-K pdscript] ", " [-r rscript] [-t template] [-X excludefile] ", " [-D displayfile] [-m mtreefile] [-o origin] ", +" [-s srcdir] [-S basedir] ", " -c comment -d description -f packlist pkg-filename", -" pkg_create [-YNhvy] -b pkg-name [pkg-filename]"); +" pkg_create [-YNhvyz] -b pkg-name [pkg-filename]"); exit(1); } diff -u pkg_install/create/perform.c pkg_install/create/perform.c --- pkg_install/create/perform.c Thu Apr 17 11:56:05 2003 +++ pkg_install/create/perform.c Fri Apr 30 18:12:42 2004 @@ -222,45 +222,54 @@ write_file(COMMENT_FNAME, Comment); add_plist(&plist, PLIST_IGNORE, NULL); add_plist(&plist, PLIST_FILE, COMMENT_FNAME); + add_cksum(&plist, plist.tail, COMMENT_FNAME); write_file(DESC_FNAME, Desc); add_plist(&plist, PLIST_IGNORE, NULL); add_plist(&plist, PLIST_FILE, DESC_FNAME); + add_cksum(&plist, plist.tail, DESC_FNAME); if (Install) { copy_file(home, Install, INSTALL_FNAME); add_plist(&plist, PLIST_IGNORE, NULL); add_plist(&plist, PLIST_FILE, INSTALL_FNAME); + add_cksum(&plist, plist.tail, INSTALL_FNAME); } if (PostInstall) { copy_file(home, PostInstall, POST_INSTALL_FNAME); add_plist(&plist, PLIST_IGNORE, NULL); add_plist(&plist, PLIST_FILE, POST_INSTALL_FNAME); + add_cksum(&plist, plist.tail, POST_INSTALL_FNAME); } if (DeInstall) { copy_file(home, DeInstall, DEINSTALL_FNAME); add_plist(&plist, PLIST_IGNORE, NULL); add_plist(&plist, PLIST_FILE, DEINSTALL_FNAME); + add_cksum(&plist, plist.tail, DEINSTALL_FNAME); } if (PostDeInstall) { copy_file(home, PostDeInstall, POST_DEINSTALL_FNAME); add_plist(&plist, PLIST_IGNORE, NULL); add_plist(&plist, PLIST_FILE, POST_DEINSTALL_FNAME); + add_cksum(&plist, plist.tail, POST_DEINSTALL_FNAME); } if (Require) { copy_file(home, Require, REQUIRE_FNAME); add_plist(&plist, PLIST_IGNORE, NULL); add_plist(&plist, PLIST_FILE, REQUIRE_FNAME); + add_cksum(&plist, plist.tail, REQUIRE_FNAME); } if (Display) { copy_file(home, Display, DISPLAY_FNAME); add_plist(&plist, PLIST_IGNORE, NULL); add_plist(&plist, PLIST_FILE, DISPLAY_FNAME); + add_cksum(&plist, plist.tail, DISPLAY_FNAME); add_plist(&plist, PLIST_DISPLAY, DISPLAY_FNAME); } if (Mtree) { copy_file(home, Mtree, MTREE_FNAME); add_plist(&plist, PLIST_IGNORE, NULL); add_plist(&plist, PLIST_FILE, MTREE_FNAME); + add_cksum(&plist, plist.tail, MTREE_FNAME); add_plist(&plist, PLIST_MTREE, MTREE_FNAME); } @@ -384,6 +393,8 @@ for (p = plist->head; p; p = p->next) { if (p->type == PLIST_FILE) fprintf(totar, "%s\n", p->name); + else if (p->type == PLIST_CWD && BaseDir && p->name && p->name[0] == '/') + fprintf(totar, "-C\n%s%s\n", BaseDir, p->name); else if (p->type == PLIST_CWD || p->type == PLIST_SRC) fprintf(totar, "-C\n%s\n", p->name); else if (p->type == PLIST_IGNORE) diff -u pkg_install/create/pkg_create.1 pkg_install/create/pkg_create.1 --- pkg_install/create/pkg_create.1 Tue Aug 26 16:49:11 2003 +++ pkg_install/create/pkg_create.1 Mon Apr 26 20:31:00 2004 @@ -35,13 +35,13 @@ .Op Fl C Ar conflicts .Op Fl P Ar pkgs .Op Fl p Ar prefix -.Op Fl f Ar contents .Op Fl i Ar iscript .Op Fl I Ar piscript .Op Fl k Ar dscript .Op Fl K Ar pdscript .Op Fl r Ar rscript .Op Fl s Ar srcdir +.Op Fl S Ar basedir .Op Fl t Ar template .Op Fl X Ar excludefile .Op Fl D Ar displayfile @@ -224,6 +224,11 @@ will override the value of .Cm @cwd during package creation. +.It Fl S Ar basedir +.Ar basedir +will be prefixed to all +.Cm @cwd +during package creation. .It Fl t Ar template Use .Ar template @@ -545,6 +550,7 @@ .An Jordan Hubbard .Sh CONTRIBUTORS .An John Kohl Aq jtk@rational.com +.An Oliver Eikemeier Aq eik@FreeBSD.org .Sh BUGS Hard links between files in a distribution must be bracketed by .Cm @cwd diff -u pkg_install/create/pl.c pkg_install/create/pl.c --- pkg_install/create/pl.c Wed Dec 17 14:36:05 2003 +++ pkg_install/create/pl.c Fri Apr 30 18:17:41 2004 @@ -27,13 +27,43 @@ #include #include +/* Add an MD5 checksum entry for a file or link */ +void +add_cksum(Package *pkg, PackingList p, const char *fname) +{ + char *cp = NULL, buf[33]; + + if (issymlink(fname)) { + int len; + char lnk[FILENAME_MAX]; + + if ((len = readlink(fname, lnk, FILENAME_MAX)) > 0) + cp = MD5Data((unsigned char *)lnk, len, buf); + } else if (isfile(fname)) { + /* Don't record MD5 checksum for device nodes and such */ + cp = MD5File(fname, buf); + } + + if (cp != NULL) { + PackingList tmp = new_plist_entry(); + + tmp->name = copy_string(strconcat("MD5:", cp)); + tmp->type = PLIST_COMMENT; + tmp->next = p->next; + tmp->prev = p; + p->next = tmp; + if (pkg->tail == p) + pkg->tail = tmp; + } +} + /* Check a list for files that require preconversion */ void check_list(const char *home, Package *pkg) { const char *where = home; const char *there = NULL; - char *cp, name[FILENAME_MAX], buf[33]; + char name[FILENAME_MAX]; PackingList p; for (p = pkg->head; p != NULL; p = p->next) @@ -51,31 +81,13 @@ break; case PLIST_FILE: - cp = NULL; - sprintf(name, "%s/%s", there ? there : where, p->name); - if (issymlink(name)) { - int len; - char lnk[FILENAME_MAX]; - - if ((len = readlink(name, lnk, FILENAME_MAX)) > 0) - cp = MD5Data((unsigned char *)lnk, len, buf); - } else if (isfile(name)) { - /* Don't record MD5 checksum for device nodes and such */ - cp = MD5File(name, buf); - } - - if (cp != NULL) { - PackingList tmp = new_plist_entry(); + if (there) + snprintf(name, sizeof(name), "%s/%s", there, p->name); + else + snprintf(name, sizeof(name), "%s%s/%s", + BaseDir && where && where[0] == '/' ? BaseDir : "", where, p->name); - tmp->name = copy_string(strconcat("MD5:", cp)); - tmp->type = PLIST_COMMENT; - tmp->next = p->next; - tmp->prev = p; - p->next = tmp; - if (pkg->tail == p) - pkg->tail = tmp; - p = tmp; - } + add_cksum(pkg, p, name); break; default: break; @@ -91,18 +103,18 @@ /* try making the container directory */ char *cp = strrchr(to, '/'); if (cp) - vsystem("mkdir -p %.*s", cp - to, + vsystem("/bin/mkdir -p %.*s", cp - to, to); return link(from, to); } return -1; } -#define STARTSTRING "tar cf -" +#define STARTSTRING "/usr/bin/tar cf -" #define TOOBIG(str) (int)strlen(str) + 6 + (int)strlen(home) + where_count > maxargs #define PUSHOUT() /* push out string */ \ if (where_count > (int)sizeof(STARTSTRING)-1) { \ - strcat(where_args, "|tar xpf -"); \ + strcat(where_args, "|/usr/bin/tar xpf -"); \ if (system(where_args)) { \ cleanup(0); \ errx(2, "%s: can't invoke tar pipeline", __func__); \ @@ -217,7 +229,11 @@ if (p->name[0] == '/') mythere = root; else mythere = there; - sprintf(fn, "%s/%s", mythere ? mythere : where, p->name); + if (mythere) + snprintf(fn, sizeof(fn), "%s/%s", mythere, p->name); + else + snprintf(fn, sizeof(fn), "%s%s/%s", + BaseDir && where && where[0] == '/' ? BaseDir : "", where, p->name); if (lstat(fn, &stb) == 0 && stb.st_dev == curdir && S_ISREG(stb.st_mode)) { /* diff -u pkg_install/delete/main.c pkg_install/delete/main.c --- pkg_install/delete/main.c Mon Apr 1 11:39:06 2002 +++ pkg_install/delete/main.c Sun Jan 25 15:59:59 2004 @@ -28,7 +28,7 @@ #include "lib.h" #include "delete.h" -static char Options[] = "adDfGhinp:rvx"; +static char Options[] = "adDfGhinp:rvxX"; char *Prefix = NULL; Boolean CleanDirs = FALSE; @@ -88,6 +88,10 @@ MatchType = MATCH_REGEX; break; + case 'X': + MatchType = MATCH_EREGEX; + break; + case 'i': Interactive = TRUE; break; @@ -151,7 +155,7 @@ usage() { fprintf(stderr, "%s\n%s\n", - "usage: pkg_delete [-dDfGinrvx] [-p prefix] pkg-name ...", + "usage: pkg_delete [-dDfGinrvxX] [-p prefix] pkg-name ...", " pkg_delete -a [flags]"); exit(1); } diff -u pkg_install/delete/perform.c pkg_install/delete/perform.c --- pkg_install/delete/perform.c Tue Feb 25 16:01:54 2003 +++ pkg_install/delete/perform.c Fri Apr 30 12:48:46 2004 @@ -64,6 +64,7 @@ case MATCH_ALL: warnx("no packages installed"); return 0; + case MATCH_EREGEX: case MATCH_REGEX: warnx("no packages match pattern(s)"); return 1; @@ -124,6 +125,7 @@ char *deporigin, **depnames, home[FILENAME_MAX]; PackingList p; int i, len; + int isinstalled; /* support for separate pre/post install scripts */ int new_m = 0; const char *pre_script = DEINSTALL_FNAME; @@ -140,9 +142,26 @@ if (Plist.head) free_plist(&Plist); - if (!isinstalledpkg(pkg)) { + sprintf(LogDir, "%s/%s", LOG_DIR, pkg); + + isinstalled = isinstalledpkg(pkg); + if (isinstalled == 0) { warnx("no such package '%s' installed", pkg); return 1; + } else if (isinstalled < 0) { + warnx("the package info for package '%s' is corrupt%s", + pkg, Force ? " (but I'll delete it anyway)" : " (use -f to force removal)"); + if (!Force) + return 1; + if (!Fake) { + if (vsystem("%s -rf %s", REMOVE_CMD, LogDir)) { + warnx("couldn't remove log entry in %s, deinstall failed", LogDir); + } else { + warnx("couldn't completely deinstall package '%s',\n" + "only the log entry in %s was removed", pkg, LogDir); + } + } + return 0; } if (!getcwd(home, FILENAME_MAX)) { @@ -150,8 +169,6 @@ errx(2, "%s: unable to get current working directory!", __func__); } - sprintf(LogDir, "%s/%s", LOG_DIR, pkg); - if (chdir(LogDir) == FAIL) { warnx("unable to change directory to %s! deinstall failed", LogDir); return 1; @@ -207,7 +224,7 @@ if (fexists(REQUIRE_FNAME)) { if (Verbose) printf("Executing 'require' script.\n"); - vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */ + vsystem("/bin/chmod +x %s", REQUIRE_FNAME); /* be sure */ if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) { warnx("package %s fails requirements %s", pkg, Force ? "" : "- not deleted"); @@ -237,7 +254,7 @@ if (Fake) printf("Would execute de-install script at this point.\n"); else { - vsystem("chmod +x %s", pre_script); /* make sure */ + vsystem("/bin/chmod +x %s", pre_script); /* make sure */ if (vsystem("./%s %s %s", pre_script, pkg, pre_arg)) { warnx("deinstall script returned error status"); if (!Force) @@ -270,7 +287,7 @@ if (Fake) printf("Would execute post-deinstall script at this point.\n"); else { - vsystem("chmod +x %s", post_script); /* make sure */ + vsystem("/bin/chmod +x %s", post_script); /* make sure */ if (vsystem("./%s %s %s", post_script, pkg, post_arg)) { warnx("post-deinstall script returned error status"); if (!Force) diff -u pkg_install/delete/pkg_delete.1 pkg_install/delete/pkg_delete.1 --- pkg_install/delete/pkg_delete.1 Sat Apr 20 14:26:57 2002 +++ pkg_install/delete/pkg_delete.1 Sun Jan 25 16:08:16 2004 @@ -25,7 +25,7 @@ .Nd a utility for deleting previously installed software package distributions .Sh SYNOPSIS .Nm -.Op Fl dDfGinrvx +.Op Fl dDfGinrvxX .Op Fl p Ar prefix .Ar pkg-name ... .Nm @@ -119,6 +119,12 @@ .Nm deletes all packages that match at least one regular expression from the list. +.It Fl X +Like +.Fl x , +but treats the +.Ar pkg-name +as an extended regular expression. .It Fl r Recursive removal. In addition to specified packages, delete all packages that depend on those packages as well. @@ -271,6 +277,7 @@ .Sh AUTHORS .An Jordan Hubbard .Sh CONTRIBUTORS -.An John Kohl Aq jtk@rational.com +.An John Kohl Aq jtk@rational.com , +.An Oliver Eikemeier Aq eik@FreeBSD.org .Sh BUGS Sure to be some. diff -u pkg_install/info/info.h pkg_install/info/info.h --- pkg_install/info/info.h Tue Mar 25 02:22:42 2003 +++ pkg_install/info/info.h Sun Jan 25 15:59:59 2004 @@ -50,6 +50,8 @@ #define SHOW_CKSUM 0x04000 #define SHOW_FMTREV 0x08000 #define SHOW_PTREV 0x10000 +#define SHOW_DEPEND 0x20000 +#define SHOW_PKGNAME 0x40000 struct which_entry { TAILQ_ENTRY(which_entry) next; diff -u pkg_install/info/main.c pkg_install/info/main.c --- pkg_install/info/main.c Mon Apr 12 12:07:47 2004 +++ pkg_install/info/main.c Mon Apr 12 12:38:43 2004 @@ -26,7 +26,7 @@ #include "info.h" #include -static char Options[] = "abcdDe:fgGhiIkl:LmoO:pPqQrRst:vVW:x"; +static char Options[] = "abcdDe:EfgGhiIjkl:LmoO:pPqQrRst:vVW:xX"; int Flags = 0; match_t MatchType = MATCH_GLOB; @@ -75,6 +75,10 @@ SHOW_DEINSTALL | SHOW_REQUIRE | SHOW_DISPLAY | SHOW_MTREE; break; + case 'E': + Flags |= SHOW_PKGNAME; + break; + case 'I': Flags |= SHOW_INDEX; break; @@ -111,12 +115,16 @@ Flags |= SHOW_INSTALL; break; + case 'j': + Flags |= SHOW_REQUIRE; + break; + case 'k': Flags |= SHOW_DEINSTALL; break; case 'r': - Flags |= SHOW_REQUIRE; + Flags |= SHOW_DEPEND; break; case 'R': @@ -170,6 +178,10 @@ MatchType = MATCH_REGEX; break; + case 'X': + MatchType = MATCH_EREGEX; + break; + case 'e': CheckPkg = optarg; break; @@ -220,7 +232,7 @@ * Don't try to apply heuristics if arguments are regexs or if * the argument refers to an existing file. */ - if (MatchType != MATCH_REGEX && !isfile(*argv)) + if (MatchType != MATCH_REGEX && MatchType != MATCH_EREGEX && !isfile(*argv)) while ((pkgs_split = strrchr(*argv, (int)'/')) != NULL) { *pkgs_split++ = '\0'; /* @@ -250,7 +262,7 @@ usage() { fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n", - "usage: pkg_info [-bcdDfgGiIkLmopPqQrRsvVx] [-e package] [-l prefix]", + "usage: pkg_info [-bcdDEfgGiIjkLmopPqQrRsvVxX] [-e package] [-l prefix]", " [-t template] -a | pkg-name ...", " pkg_info [-qQ] -W filename", " pkg_info [-qQ] -O origin", diff -u pkg_install/info/perform.c pkg_install/info/perform.c --- pkg_install/info/perform.c Tue Mar 25 01:51:41 2003 +++ pkg_install/info/perform.c Sun Jan 25 15:59:59 2004 @@ -31,6 +31,7 @@ static int cmp_path(const char *, const char *, const char *); static char *abspath(const char *); static int find_pkgs_by_origin(const char *); +static int matched_packages(char **pkgs); int pkg_perform(char **pkgs) @@ -42,8 +43,10 @@ signal(SIGINT, cleanup); /* Overriding action? */ - if (CheckPkg) { - return isinstalledpkg(CheckPkg) == TRUE ? 0 : 1; + if (Flags & SHOW_PKGNAME) { + return matched_packages(pkgs); + } else if (CheckPkg) { + return isinstalledpkg(CheckPkg) > 0 ? 0 : 1; /* Not reached */ } else if (!TAILQ_EMPTY(whead)) { return find_pkg(whead); @@ -67,6 +70,7 @@ return 0; /* Not reached */ case MATCH_REGEX: + case MATCH_EREGEX: warnx("no packages match pattern(s)"); return 1; /* Not reached */ @@ -88,7 +92,7 @@ { Boolean installed = FALSE, isTMP = FALSE; char log_dir[FILENAME_MAX]; - char fname[FILENAME_MAX], extrlist[FILENAME_MAX]; + char fname[FILENAME_MAX]; Package plist; FILE *fp; struct stat sb; @@ -131,29 +135,19 @@ goto bail; } Home = make_playpen(PlayPen, sb.st_size / 2); - snprintf(extrlist, sizeof(extrlist), "--fast-read %s %s %s", - CONTENTS_FNAME, COMMENT_FNAME, DESC_FNAME); - if (Flags & SHOW_DISPLAY) - snprintf(extrlist, sizeof(extrlist), "%s %s", extrlist, - DISPLAY_FNAME); - if (Flags & SHOW_INSTALL) - snprintf(extrlist, sizeof(extrlist), "%s %s %s", extrlist, - INSTALL_FNAME, POST_INSTALL_FNAME); - if (Flags & SHOW_DEINSTALL) - snprintf(extrlist, sizeof(extrlist), "%s %s %s", extrlist, - DEINSTALL_FNAME, POST_DEINSTALL_FNAME); - if (Flags & SHOW_MTREE) - snprintf(extrlist, sizeof(extrlist), "%s %s", extrlist, - MTREE_FNAME); - if (unpack(fname, extrlist)) { + if (unpack(fname, "'+*'")) { warnx("error during unpacking, no info for '%s' available", pkg); code = 1; goto bail; } } - /* It's not an ininstalled package, try and find it among the installed */ + /* It's not an uninstalled package, try and find it among the installed */ else { - if (!isinstalledpkg(pkg)) { + int isinstalled = isinstalledpkg(pkg); + if (isinstalled < 0) { + warnx("the package info for package '%s' is corrupt", pkg); + return 1; + } else if (isinstalled == 0) { warnx("can't find package '%s' installed or in a file!", pkg); return 1; } @@ -195,7 +189,7 @@ printf("%s%s:", InfoPrefix, pkg); if (Flags & SHOW_COMMENT) show_file("Comment:\n", COMMENT_FNAME); - if (Flags & SHOW_REQUIRE) + if (Flags & SHOW_DEPEND) show_plist("Depends on:\n", &plist, PLIST_PKGDEP, FALSE); if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME)) show_file("Required by:\n", REQUIRED_BY_FNAME); @@ -205,6 +199,8 @@ show_file("Install notice:\n", DISPLAY_FNAME); if (Flags & SHOW_PLIST) show_plist("Packing list:\n", &plist, (plist_t)0, TRUE); + if (Flags & SHOW_REQUIRE && fexists(REQUIRE_FNAME)) + show_file("Requirements script:\n", REQUIRE_FNAME); if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME)) show_file("Install script:\n", INSTALL_FNAME); if ((Flags & SHOW_INSTALL) && fexists(POST_INSTALL_FNAME)) @@ -448,6 +444,30 @@ for (i = 0; matched[i] != NULL; i++) puts(matched[i]); + + return 0; +} + +/* + * List only the matching package names. + * Mainly intended for scripts. + */ +static int +matched_packages(char **pkgs) +{ + char **matched; + int i, errcode; + + matched = matchinstalled(MatchType == MATCH_GLOB ? MATCH_NGLOB : MatchType, pkgs, &errcode); + + if (errcode != 0 || matched == NULL) + return 1; + + for (i = 0; matched[i]; i++) + if (!Quiet) + printf("%s\n", matched[i]); + else if (QUIET) + printf("%s%s\n", InfoPrefix, matched[i]); return 0; } diff -u pkg_install/info/pkg_info.1 pkg_install/info/pkg_info.1 --- pkg_install/info/pkg_info.1 Mon Apr 12 12:07:47 2004 +++ pkg_install/info/pkg_info.1 Mon Apr 12 12:38:43 2004 @@ -25,7 +25,7 @@ .Nd a utility for displaying information on software packages .Sh SYNOPSIS .Nm -.Op Fl bcdDfgGiIkLmopPqQrRsvVx +.Op Fl bcdDEfgGijIkLmopPqQrRsvVxX .Op Fl e Ar package .Op Fl l Ar prefix .Op Fl t Ar template @@ -54,6 +54,15 @@ The named packages are described. A package name may either be the name of an installed package, the pathname to a package distribution file or a URL to an FTP available package. +Package version numbers can also be matched in a relational manner using the +.Pa \*[Ge], \*[Le], \*[Gt] +and +.Pa \*[Lt] +operators. For example, +.Pa pkg_info 'portupgrade\*[Ge]20030723' +will match versions 20030723 and later of the +.Pa portupgrade +package. .It Fl a Show all currently installed packages. .It Fl b @@ -90,10 +99,12 @@ .It Fl I Show an index line for each package. This option takes precedence over all other package formatting options. +.It Fl j +Show the requirements script (if any) for each package. .It Fl k Show the de-install script (if any) for each package. .It Fl r -Show the requirements script (if any) for each package. +Show the list of packages on which each package depends. .It Fl R Show the list of installed packages which require each package. .It Fl m @@ -140,12 +151,22 @@ .Nm displays information about all packages that match at least one regular expression from the list. +.It Fl X +Like +.Fl x , +but treats the +.Ar pkg-name +as an extended regular expression. .It Fl e Ar pkg-name If the package identified by .Ar pkg-name is currently installed, return 0, otherwise return 1. This option allows you to easily test for the presence of another (perhaps prerequisite) package from a script. +.It Fl E +Show only matching package names. This option takes +precedence over all other package formatting options. +If any packages match, return 0, otherwise return 1. .It Fl l Ar str Prefix each information category header (see .Fl q ) @@ -236,6 +257,7 @@ .Sh AUTHORS .An Jordan Hubbard .Sh CONTRIBUTORS -.An John Kohl Aq jtk@rational.com +.An John Kohl Aq jtk@rational.com , +.An Oliver Eikemeier Aq eik@FreeBSD.org .Sh BUGS Sure to be some. diff -u pkg_install/lib/deps.c pkg_install/lib/deps.c --- pkg_install/lib/deps.c Tue May 14 23:42:37 2002 +++ pkg_install/lib/deps.c Sun Jan 25 15:59:59 2004 @@ -97,7 +97,7 @@ errcode = 0; /* Check that pkgname2 is actually installed */ - if (!isinstalledpkg(pkgname2)) + if (isinstalledpkg(pkgname2) <= 0) goto exit; errcode = requiredby(pkgname2, &rb_list, FALSE, TRUE); @@ -153,7 +153,7 @@ free(rb_entry); } - if (!isinstalledpkg(pkgname)) { + if (isinstalledpkg(pkgname) <= 0) { if (strict == TRUE) warnx("no such package '%s' installed", pkgname); return -1; @@ -173,7 +173,7 @@ while (fgets(fbuf, sizeof(fbuf), fp) != NULL) { if (fbuf[strlen(fbuf) - 1] == '\n') fbuf[strlen(fbuf) - 1] = '\0'; - if (filter == TRUE && !isinstalledpkg(fbuf)) { + if (filter == TRUE && isinstalledpkg(fbuf) <= 0) { if (strict == TRUE) warnx("package '%s' is recorded in the '%s' but isn't " "actually installed", fbuf, fname); diff -u pkg_install/lib/file.c pkg_install/lib/file.c --- pkg_install/lib/file.c Mon Apr 12 12:07:47 2004 +++ pkg_install/lib/file.c Fri Apr 30 12:44:21 2004 @@ -268,9 +268,9 @@ char cmd[FILENAME_MAX]; if (fname[0] == '/') - snprintf(cmd, FILENAME_MAX, "cp -r %s %s", fname, to); + snprintf(cmd, FILENAME_MAX, "/bin/cp -r %s %s", fname, to); else - snprintf(cmd, FILENAME_MAX, "cp -r %s/%s %s", dir, fname, to); + snprintf(cmd, FILENAME_MAX, "/bin/cp -r %s/%s %s", dir, fname, to); if (vsystem(cmd)) { cleanup(0); errx(2, "%s: could not perform '%s'", __func__, cmd); @@ -283,9 +283,9 @@ char cmd[FILENAME_MAX]; if (fname[0] == '/') - snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to); + snprintf(cmd, FILENAME_MAX, "/bin/mv %s %s", fname, to); else - snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to); + snprintf(cmd, FILENAME_MAX, "/bin/mv %s/%s %s", dir, fname, to); if (vsystem(cmd)) { cleanup(0); errx(2, "%s: could not perform '%s'", __func__, cmd); @@ -309,11 +309,11 @@ /* If absolute path, use it */ if (*fname == '/') dir = "/"; - snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -", + snprintf(cmd, FILENAME_MAX * 3, "/usr/bin/tar cf - -C %s %s | /usr/bin/tar xpf -", dir, fname); } else - snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s", + snprintf(cmd, FILENAME_MAX * 3, "/usr/bin/tar cf - %s | /usr/bin/tar xpf - -C %s", fname, dir); #ifdef DEBUG printf("Using '%s' to copy trees.\n", cmd); @@ -350,7 +350,7 @@ else /* XXX: need to handle .tgz also */ comp = "-j"; - if (vsystem("tar -xp %s -f '%s' %s", comp, pkg, flist ? flist : "")) { + if (vsystem("/usr/bin/tar -xp %s -f '%s' %s", comp, pkg, flist ? flist : "")) { warnx("tar extract of %s failed!", pkg); return 1; } diff -u pkg_install/lib/lib.h pkg_install/lib/lib.h --- pkg_install/lib/lib.h Mon May 26 19:12:22 2003 +++ pkg_install/lib/lib.h Sat May 1 05:01:03 2004 @@ -52,10 +52,10 @@ #define NO 1 /* Usually "rm", but often "echo" during debugging! */ -#define REMOVE_CMD "rm" +#define REMOVE_CMD "/bin/rm" /* Usually "rm", but often "echo" during debugging! */ -#define RMDIR_CMD "rmdir" +#define RMDIR_CMD "/bin/rmdir" /* Where we put logging information by default, else ${PKG_DBDIR} if set */ #define DEF_LOG_DIR "/var/db/pkg" @@ -77,6 +77,12 @@ #define DISPLAY_FNAME "+DISPLAY" #define MTREE_FNAME "+MTREE_DIRS" +#if defined(__FreeBSD_version) && __FreeBSD_version >= 500036 +#define INDEX_FNAME "INDEX-5" +#else +#define INDEX_FNAME "INDEX" +#endif + #define CMD_CHAR '@' /* prefix for extended PLIST cmd */ /* The name of the "prefix" environment variable given to scripts */ @@ -86,7 +92,7 @@ * 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_INSTALL_VERSION 20040501 #define PKG_WRAPCONF_FNAME "/var/db/pkg_install.conf" #define main(argc, argv) real_main(argc, argv) @@ -105,7 +111,7 @@ typedef enum _plist_t plist_t; enum _match_t { - MATCH_ALL, MATCH_EXACT, MATCH_GLOB, MATCH_REGEX + MATCH_ALL, MATCH_EXACT, MATCH_GLOB, MATCH_NGLOB, MATCH_EREGEX, MATCH_REGEX }; typedef enum _match_t match_t; @@ -122,8 +128,8 @@ struct _pack { struct _plist *head, *tail; - char *name; - char *origin; + const char *name; + const char *origin; int fmtver_maj, fmtver_mnr; }; typedef struct _pack Package; @@ -206,6 +212,7 @@ char **matchinstalled(match_t, char **, int *); char **matchbyorigin(const char *, int *); int isinstalledpkg(const char *name); +int pattern_match(match_t MatchType, char *pattern, const char *pkgname); /* Dependencies */ int sortdeps(char **); @@ -214,7 +221,6 @@ /* Version */ int verscmp(Package *, int, int); -const char *version_of(const char *, int *, int *); int version_cmp(const char *, const char *); /* Externs */ diff -u pkg_install/lib/match.c pkg_install/lib/match.c --- pkg_install/lib/match.c Tue Dec 23 16:01:12 2003 +++ pkg_install/lib/match.c Sat May 1 08:24:24 2004 @@ -37,14 +37,15 @@ char **store; }; -static int rex_match(const char *, const char *); +static int rex_match(const char *, const char *, int); +static int csh_match(const char *, const char *, int); struct store *storecreate(struct store *); static int storeappend(struct store *, const char *); static int fname_cmp(const FTSENT * const *, const FTSENT * const *); /* * Function to query names of installed packages. - * MatchType - one of MATCH_ALL, MATCH_REGEX, MATCH_GLOB; + * MatchType - one of MATCH_ALL, MATCH_EREGEX, MATCH_REGEX, MATCH_GLOB, MATCH_NGLOB; * patterns - NULL-terminated list of glob or regex patterns * (could be NULL for MATCH_ALL); * retval - return value (could be NULL if you don't want/need @@ -108,22 +109,11 @@ matched = f->fts_name; else for (i = 0; patterns[i]; i++) { - switch (MatchType) { - case MATCH_REGEX: - errcode = rex_match(patterns[i], f->fts_name); - if (errcode == 1) { - matched = f->fts_name; - errcode = 0; - } - break; - case MATCH_GLOB: - if (fnmatch(patterns[i], f->fts_name, 0) == 0) { - matched = f->fts_name; - lmatched[i] = TRUE; - } - break; - default: - break; + errcode = pattern_match(MatchType, patterns[i], f->fts_name); + if (errcode == 1) { + matched = f->fts_name; + lmatched[i] = TRUE; + errcode = 0; } if (matched != NULL || errcode != 0) break; @@ -153,6 +143,88 @@ return store->store; } +int +pattern_match(match_t MatchType, char *pattern, const char *pkgname) +{ + int errcode = 0; + const char *fname = pkgname; + char basefname[PATH_MAX]; + char condchar = '\0'; + char *condition; + + /* do we have an appended condition? */ + condition = strpbrk(pattern, "<>=!"); + if (condition) { + const char *ch; + /* yes, isolate the pattern from the condition ... */ + condchar = *condition; + *condition = '\0'; + /* ... and compare the name without version */ + ch = strrchr(fname, '-'); + if (ch && ch - fname < PATH_MAX) { + strlcpy(basefname, fname, ch - fname + 1); + fname = basefname; + } + } + + switch (MatchType) { + case MATCH_EREGEX: + case MATCH_REGEX: + errcode = rex_match(pattern, fname, MatchType == MATCH_EREGEX ? 1 : 0); + break; + case MATCH_NGLOB: + case MATCH_GLOB: + errcode = (csh_match(pattern, fname, 0) == 0) ? 1 : 0; + break; + default: + break; + } + + /* loop over all appended conditions */ + while (condition) { + /* restore the pattern */ + *condition = condchar; + /* parse the condition (fun with bits) */ + if (errcode == 1) { + char *nextcondition; + /* compare version numbers */ + int match = 0; + if (*++condition == '=') { + match = 2; + condition++; + } + switch(condchar) { + case '<': + match |= 1; + break; + case '>': + match |= 4; + break; + case '=': + match |= 2; + break; + case '!': + match = 5; + break; + } + /* isolate the version number from the next condition ... */ + nextcondition = strpbrk(condition, "<>=!"); + if (nextcondition) { + condchar = *nextcondition; + *nextcondition = '\0'; + } + /* and compare the versions (version_cmp removes the filename for us) */ + if ((match & (1 << (version_cmp(pkgname, condition) + 1))) == 0) + errcode = 0; + condition = nextcondition; + } else { + break; + } + } + + return errcode; +} + /* * Synopsis is similar to matchinstalled(), but use origin * as a key for matching packages. @@ -193,10 +265,8 @@ snprintf(tmp, PATH_MAX, "%s/%s", tmp, CONTENTS_FNAME); fp = fopen(tmp, "r"); if (fp == NULL) { - warn("%s", tmp); - if (retval != NULL) - *retval = 1; - return NULL; + warnx("the package info for package '%s' is corrupt", installed[i]); + continue; } cmd = -1; @@ -212,7 +282,7 @@ continue; cmd = plist_cmd(tmp + 1, &cp); if (cmd == PLIST_ORIGIN) { - if (strcmp(origin, cp) == 0) + if (csh_match(origin, cp, FNM_PATHNAME) == 0) storeappend(store, installed[i]); break; } @@ -229,8 +299,9 @@ } /* - * Return TRUE if the specified package is installed, - * or FALSE otherwise. + * + * Return 1 if the specified package is installed, + * 0 if not, and -1 if an error occured. */ int isinstalledpkg(const char *name) @@ -240,13 +311,13 @@ snprintf(buf, sizeof(buf), "%s/%s", LOG_DIR, name); if (!isdir(buf) || access(buf, R_OK) == FAIL) - return FALSE; + return 0; snprintf(buf2, sizeof(buf2), "%s/%s", buf, CONTENTS_FNAME); if (!isfile(buf2) || access(buf2, R_OK) == FAIL) - return FALSE; + return -1; - return TRUE; + return 1; } /* @@ -255,7 +326,7 @@ * engine reported an error (usually invalid syntax). */ static int -rex_match(const char *pattern, const char *pkgname) +rex_match(const char *pattern, const char *pkgname, int extended) { char errbuf[128]; int errcode; @@ -264,7 +335,7 @@ retval = 0; - errcode = regcomp(&rex, pattern, REG_BASIC | REG_NOSUB); + errcode = regcomp(&rex, pattern, (extended ? REG_EXTENDED : REG_BASIC) | REG_NOSUB); if (errcode == 0) errcode = regexec(&rex, pkgname, 0, NULL, 0); @@ -279,6 +350,99 @@ regfree(&rex); return retval; +} + +/* + * Match string by a csh-style glob pattern. Returns 0 on + * match and FNM_NOMATCH otherwise, to be compatible with + * fnmatch(3). + */ +static int +csh_match(const char *pattern, const char *string, int flags) +{ + int ret = FNM_NOMATCH; + + + const char *nextchoice = pattern; + const char *current = NULL; + + int prefixlen = -1; + int currentlen = 0; + + int level = 0; + + do { + const char *pos = nextchoice; + const char *postfix = NULL; + + Boolean quoted = FALSE; + + nextchoice = NULL; + + do { + const char *eb; + if (!*pos) { + postfix = pos; + } else if (quoted) { + quoted = FALSE; + } else { + switch (*pos) { + case '{': + ++level; + if (level == 1) { + current = pos+1; + prefixlen = pos-pattern; + } + break; + case ',': + if (level == 1 && !nextchoice) { + nextchoice = pos+1; + currentlen = pos-current; + } + break; + case '}': + if (level == 1) { + postfix = pos+1; + if (!nextchoice) + currentlen = pos-current; + } + level--; + break; + case '[': + eb = pos+1; + if (*eb == '!' || *eb == '^') + eb++; + if (*eb == ']') + eb++; + while(*eb && *eb != ']') + eb++; + if (*eb) + pos=eb; + break; + case '\\': + quoted = TRUE; + break; + default: + ; + } + } + pos++; + } while (!postfix); + + if (current) { + char buf[FILENAME_MAX]; + snprintf(buf, sizeof(buf), "%.*s%.*s%s", prefixlen, pattern, currentlen, current, postfix); + ret = csh_match(buf, string, flags); + if (ret) { + current = nextchoice; + level = 1; + } else + current = NULL; + } else + ret = fnmatch(pattern, string, flags); + } while (current); + + return ret; } /* diff -u pkg_install/lib/pen.c pkg_install/lib/pen.c --- pkg_install/lib/pen.c Sat May 11 06:17:55 2002 +++ pkg_install/lib/pen.c Fri Apr 30 12:29:47 2004 @@ -156,7 +156,7 @@ Previous[0] = '\0'; } if (PenLocation[0]) { - if (PenLocation[0] == '/' && vsystem("rm -rf %s", PenLocation)) + if (PenLocation[0] == '/' && vsystem("/bin/rm -rf %s", PenLocation)) warnx("couldn't remove temporary dir '%s'", PenLocation); popPen(PenLocation); } diff -u pkg_install/lib/version.c pkg_install/lib/version.c --- pkg_install/lib/version.c Sun Aug 11 15:05:30 2002 +++ pkg_install/lib/version.c Mon Apr 12 13:21:54 2004 @@ -48,35 +48,157 @@ } /* - * version_of(pkgname, epoch, revision) returns a pointer to the version + * 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) + * - only the first letter is significant (except for the special string "pl"), + * and case is ignored (1.a2 = 1.alpha2 = 1.Anything2) + * - the letter sort order is: pl, a, b, ..., z + * - missing letters sort like "pl" (5 = 5pl0, 10 < 10a) + * - missing version numbers sort as -1 (a2 < 0.1, 10.a2 < 10.0) + * - missing components are assumed to be 0 (10 = 10.0 = 10.0.0) + * - components separated by `+' are handled by version_cmp below + * + * Oliver Eikemeier + */ +static const char * +get_component(const char *position, version_component *component) +{ + const char *pos = position; + + 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; + pos++; + } else { + component->n = -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); + /* convert letter to value, order is pl, a, ..., z */ + if (c == 'p' && tolower(pos[1]) == 'l' && !isalpha(pos[2])) { + /* special string 'pl' */ + component->a = 0; + pos += 2; + } else { + /* use the first letter and skip following */ + component->a = c - 'a' + 1; + do { + ++pos; + } while (isalpha(*pos)); + } + + /* 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 = 0; + } + } else { + component->a = 0; + component->pl = 0; + } + + /* skip trailing separators */ + while (*pos && !isdigit(*pos) && !isalpha(*pos) && *pos != '+' && *pos != '*') { + pos++; + } + + return pos; } /* @@ -94,78 +216,65 @@ * 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; - - 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); + const char *v1, *v2, *ve1, *ve2; + unsigned long e1, e2, r1, r2; + int result = 0; + + 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; + } + 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); } - 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; } + + /* Compare FreeBSD revision numbers. */ + if (result == 0 && r1 != r2) { + result = (r1 < r2 ? -1 : 1); + } + return result; } diff -u pkg_install/version/main.c pkg_install/version/main.c --- pkg_install/version/main.c Mon Jun 24 18:03:24 2002 +++ pkg_install/version/main.c Sun Jan 25 15:59:59 2004 @@ -25,11 +25,12 @@ #include "version.h" #include -static char Options[] = "dhl:L:s:tv"; +static char Options[] = "dhl:L:s:XtTv"; char *LimitChars = NULL; char *PreventChars = NULL; char *MatchName = NULL; +Boolean RegexExtended = FALSE; static void usage __P((void)); @@ -43,6 +44,10 @@ printf(cmp > 0 ? ">\n" : (cmp < 0 ? "<\n" : "=\n")); exit(0); } + else if (argc == 4 && !strcmp(argv[1], "-T")) { + cmp = pattern_match(MATCH_GLOB, argv[3], argv[2]); + exit(cmp == 1 ? 0 : 1); + } else while ((ch = getopt(argc, argv, Options)) != -1) { switch(ch) { case 'v': @@ -65,6 +70,14 @@ errx(2, "Invalid -t usage."); break; + case 'T': + errx(2, "Invalid -T usage."); + break; + + case 'X': + RegexExtended = TRUE; + break; + case 'h': case '?': default: @@ -82,8 +95,9 @@ 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"); + fprintf(stderr, "%s\n%s\n%s\n", + "usage: pkg_version [-hv] [-l limchar] [-L limchar] [[-X] -s string] index", + " pkg_version -t v1 v2", + " pkg_version -T name pattern"); exit(1); } diff -u pkg_install/version/perform.c pkg_install/version/perform.c --- pkg_install/version/perform.c Fri Apr 4 16:40:49 2003 +++ pkg_install/version/perform.c Fri Apr 30 13:15:45 2004 @@ -52,10 +52,9 @@ /* * 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); + snprintf(tmp, PATH_MAX, "%s/%s", PORTS_DIR, INDEX_FNAME); else strlcpy(tmp, *indexarg, PATH_MAX); if (isURL(tmp)) @@ -67,7 +66,7 @@ if (MatchName != NULL) { pat[0] = MatchName; pat[1] = NULL; - MatchType = MATCH_REGEX; + MatchType = RegexExtended ? MATCH_EREGEX : MATCH_REGEX; patterns = pat; } else { @@ -83,6 +82,7 @@ case MATCH_ALL: warnx("no packages installed"); return (0); + case MATCH_EREGEX: case MATCH_REGEX: warnx("no packages match pattern"); return (1); @@ -131,7 +131,7 @@ 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); + warnx("the package info for package '%s' is corrupt", pkg); return 1; } read_plist(&plist, fp); @@ -150,7 +150,7 @@ 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) + if ((latest = vpipe("/usr/bin/make -V PKGNAME", tmp)) == NULL) warnx("Failed to get PKGNAME from %s/Makefile!", tmp); else show_version(plist.name, latest, "port"); @@ -271,10 +271,12 @@ 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); @@ -285,7 +287,8 @@ } } 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) diff -u pkg_install/version/pkg_version.1 pkg_install/version/pkg_version.1 --- pkg_install/version/pkg_version.1 Mon Jan 27 03:05:59 2003 +++ pkg_install/version/pkg_version.1 Sun Jan 25 16:07:45 2004 @@ -35,7 +35,10 @@ .Op Fl hv .Op Fl l Ar limchar .Op Fl L Ar limchar -.Op Fl s Ar string +.Oo +.Op Fl X +.Fl s Ar string +.Oc .Op Ar index .Nm .Op Fl t Ar version1 version2 @@ -134,6 +137,10 @@ .It Fl s Limit the output to those packages whose names match a given .Ar string . +.It Fl X +Interpret +.Ar string +as a extended regular expression. .It Fl t Test a pair of version number strings and exit. The output consists of one of the single characters @@ -203,4 +210,5 @@ .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 +.An Akinori MUSHA Aq knu@FreeBSD.org , +.An Oliver Eikemeier Aq eik@FreeBSD.org diff -u pkg_install/version/test-pkg_version.sh pkg_install/version/test-pkg_version.sh --- pkg_install/version/test-pkg_version.sh Mon Jun 24 18:03:24 2002 +++ pkg_install/version/test-pkg_version.sh Mon Feb 16 14:27:24 2004 @@ -73,3 +73,13 @@ 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 equality +test-pv 10a1b2 "=" 10a1.b2 separator +test-pv 14 "=" 14pl0 patchlevel +test-pv 14 "<" 14pl1 inequality + +test-pv 1.0.0+2003.09.06 "=" 1.0+2003.09.06 multiple +test-pv 1.0.1+2003.09.06 ">" 1.0+2003.09.06 multiple diff -u pkg_install/version/version.h pkg_install/version/version.h --- pkg_install/version/version.h Mon Sep 9 10:00:32 2002 +++ pkg_install/version/version.h Sun Jan 25 15:59:59 2004 @@ -40,5 +40,6 @@ extern char *LimitChars; extern char *PreventChars; extern char *MatchName; +extern Boolean RegexExtended; #endif /* _INST_VERSION_H_INCLUDE */