match.c revision 131285
1166551Smarcel/* 2226647Smarcel * FreeBSD install - a package for the installation and maintainance 3166551Smarcel * of non-core utilities. 4166551Smarcel * 5166551Smarcel * Redistribution and use in source and binary forms, with or without 6166551Smarcel * modification, are permitted provided that the following conditions 7166551Smarcel * are met: 8166551Smarcel * 1. Redistributions of source code must retain the above copyright 9166551Smarcel * notice, this list of conditions and the following disclaimer. 10166551Smarcel * 2. Redistributions in binary form must reproduce the above copyright 11166551Smarcel * notice, this list of conditions and the following disclaimer in the 12166551Smarcel * documentation and/or other materials provided with the distribution. 13166551Smarcel * 14166551Smarcel * Maxim Sobolev 15166551Smarcel * 24 February 2001 16166551Smarcel * 17166551Smarcel * Routines used to query installed packages. 18166551Smarcel * 19166551Smarcel */ 20166551Smarcel 21166551Smarcel#include <sys/cdefs.h> 22166551Smarcel__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/lib/match.c 131285 2004-06-29 19:06:42Z eik $"); 23166551Smarcel 24166551Smarcel#include "lib.h" 25166551Smarcel#include <err.h> 26166551Smarcel#include <fnmatch.h> 27166551Smarcel#include <fts.h> 28166551Smarcel#include <regex.h> 29166551Smarcel 30166551Smarcel/* 31166551Smarcel * Simple structure representing argv-like 32166551Smarcel * NULL-terminated list. 33166551Smarcel */ 34166551Smarcelstruct store { 35166551Smarcel int currlen; 36166551Smarcel int used; 37166551Smarcel char **store; 38166551Smarcel}; 39166551Smarcel 40166551Smarcelstatic int rex_match(const char *, const char *, int); 41166551Smarcelstatic int csh_match(const char *, const char *, int); 42166551Smarcelstruct store *storecreate(struct store *); 43166551Smarcelstatic int storeappend(struct store *, const char *); 44219029Snetchildstatic int fname_cmp(const FTSENT * const *, const FTSENT * const *); 45166551Smarcel 46166551Smarcel/* 47166551Smarcel * Function to query names of installed packages. 48166551Smarcel * MatchType - one of MATCH_ALL, MATCH_EREGEX, MATCH_REGEX, MATCH_GLOB, MATCH_NGLOB; 49166551Smarcel * patterns - NULL-terminated list of glob or regex patterns 50166551Smarcel * (could be NULL for MATCH_ALL); 51219029Snetchild * retval - return value (could be NULL if you don't want/need 52219029Snetchild * return value). 53166551Smarcel * Returns NULL-terminated list with matching names. 54166551Smarcel * Names in list returned are dynamically allocated and should 55166551Smarcel * not be altered by the caller. 56166551Smarcel */ 57166551Smarcelchar ** 58178180Smarcelmatchinstalled(match_t MatchType, char **patterns, int *retval) 59178180Smarcel{ 60166551Smarcel int i, errcode, len; 61166551Smarcel char *matched; 62166551Smarcel const char *paths[2] = {LOG_DIR, NULL}; 63166551Smarcel static struct store *store = NULL; 64166551Smarcel FTS *ftsp; 65166551Smarcel FTSENT *f; 66166551Smarcel Boolean *lmatched = NULL; 67166551Smarcel 68166551Smarcel store = storecreate(store); 69166551Smarcel if (store == NULL) { 70166551Smarcel if (retval != NULL) 71166551Smarcel *retval = 1; 72166551Smarcel return NULL; 73166551Smarcel } 74166551Smarcel 75166551Smarcel if (retval != NULL) 76166551Smarcel *retval = 0; 77166551Smarcel 78178180Smarcel if (!isdir(paths[0])) { 79199017Srnoland if (retval != NULL) 80166551Smarcel *retval = 1; 81166551Smarcel return NULL; 82226647Smarcel /* Not reached */ 83166551Smarcel } 84166551Smarcel 85166551Smarcel /* Count number of patterns */ 86166551Smarcel if (patterns != NULL) { 87166551Smarcel for (len = 0; patterns[len]; len++) {} 88166551Smarcel lmatched = alloca(sizeof(*lmatched) * len); 89166551Smarcel if (lmatched == NULL) { 90179763Smarcel warnx("%s(): alloca() failed", __func__); 91179763Smarcel if (retval != NULL) 92223660Sae *retval = 1; 93179763Smarcel return NULL; 94166551Smarcel } 95166551Smarcel } else 96178180Smarcel len = 0; 97166551Smarcel 98166551Smarcel for (i = 0; i < len; i++) 99188429Simp lmatched[i] = FALSE; 100178444Smarcel 101166551Smarcel ftsp = fts_open((char * const *)(uintptr_t)paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp); 102214352Sae if (ftsp != NULL) { 103166551Smarcel while ((f = fts_read(ftsp)) != NULL) { 104188429Simp if (f->fts_info == FTS_D && f->fts_level == 1) { 105166551Smarcel fts_set(ftsp, f, FTS_SKIP); 106166551Smarcel matched = NULL; 107166551Smarcel errcode = 0; 108213135Spjd if (MatchType == MATCH_ALL) 109213135Spjd matched = f->fts_name; 110166551Smarcel else 111166551Smarcel for (i = 0; patterns[i]; i++) { 112166551Smarcel errcode = pattern_match(MatchType, patterns[i], f->fts_name); 113207094Smarcel if (errcode == 1) { 114207094Smarcel matched = f->fts_name; 115214352Sae lmatched[i] = TRUE; 116166551Smarcel errcode = 0; 117166551Smarcel } 118166551Smarcel if (matched != NULL || errcode != 0) 119178180Smarcel break; 120166551Smarcel } 121166551Smarcel if (errcode == 0 && matched != NULL) 122178444Smarcel errcode = storeappend(store, matched); 123166551Smarcel if (errcode != 0) { 124166551Smarcel if (retval != NULL) 125207094Smarcel *retval = 1; 126166551Smarcel return NULL; 127166551Smarcel /* Not reached */ 128166551Smarcel } 129214352Sae } 130213135Spjd } 131166551Smarcel fts_close(ftsp); 132166551Smarcel } 133166551Smarcel 134166551Smarcel if (MatchType == MATCH_GLOB) { 135166551Smarcel for (i = 0; i < len; i++) 136166551Smarcel if (lmatched[i] == FALSE) 137166551Smarcel storeappend(store, patterns[i]); 138166551Smarcel } 139166551Smarcel 140166551Smarcel if (store->used == 0) 141166551Smarcel return NULL; 142217531Sae else 143178180Smarcel return store->store; 144166551Smarcel} 145177510Smarcel 146166551Smarcelint 147200534Srpaulopattern_match(match_t MatchType, char *pattern, const char *pkgname) 148182797Srpaulo{ 149200534Srpaulo int errcode = 0; 150200534Srpaulo const char *fname = pkgname; 151200534Srpaulo char basefname[PATH_MAX]; 152200534Srpaulo char condchar = '\0'; 153200534Srpaulo char *condition; 154218014Sae 155166551Smarcel /* do we have an appended condition? */ 156166551Smarcel condition = strpbrk(pattern, "<>="); 157172940Sjhb if (condition) { 158236023Smarcel const char *ch; 159166551Smarcel /* yes, isolate the pattern from the condition ... */ 160166551Smarcel if (condition > pattern && condition[-1] == '!') 161166551Smarcel condition--; 162172857Smarcel condchar = *condition; 163200534Srpaulo *condition = '\0'; 164200534Srpaulo /* ... and compare the name without version */ 165200534Srpaulo ch = strrchr(fname, '-'); 166166551Smarcel if (ch && ch - fname < PATH_MAX) { 167234417Smarck strlcpy(basefname, fname, ch - fname + 1); 168234417Smarck fname = basefname; 169234417Smarck } 170200539Srpaulo } 171200539Srpaulo 172200539Srpaulo switch (MatchType) { 173200539Srpaulo case MATCH_EREGEX: 174200539Srpaulo case MATCH_REGEX: 175200539Srpaulo errcode = rex_match(pattern, fname, MatchType == MATCH_EREGEX ? 1 : 0); 176200539Srpaulo break; 177200539Srpaulo case MATCH_NGLOB: 178200539Srpaulo case MATCH_GLOB: 179200539Srpaulo errcode = (csh_match(pattern, fname, 0) == 0) ? 1 : 0; 180166551Smarcel break; 181166551Smarcel case MATCH_EXACT: 182166551Smarcel errcode = (strcmp(pattern, fname) == 0) ? 1 : 0; 183200534Srpaulo break; 184200534Srpaulo case MATCH_ALL: 185214352Sae errcode = 1; 186226647Smarcel break; 187200534Srpaulo default: 188226647Smarcel break; 189226647Smarcel } 190226647Smarcel 191226647Smarcel /* loop over all appended conditions */ 192226647Smarcel while (condition) { 193226647Smarcel /* restore the pattern */ 194226647Smarcel *condition = condchar; 195226647Smarcel /* parse the condition (fun with bits) */ 196226647Smarcel if (errcode == 1) { 197226647Smarcel char *nextcondition; 198226647Smarcel /* compare version numbers */ 199236023Smarcel int match = 0; 200226647Smarcel if (*++condition == '=') { 201226647Smarcel match = 2; 202226647Smarcel condition++; 203226647Smarcel } 204226647Smarcel switch(condchar) { 205226647Smarcel case '<': 206226647Smarcel match |= 1; 207226647Smarcel break; 208234417Smarck case '>': 209234417Smarck match |= 4; 210234417Smarck break; 211226647Smarcel case '=': 212226647Smarcel match |= 2; 213226647Smarcel break; 214226647Smarcel case '!': 215226647Smarcel match = 5; 216226647Smarcel break; 217226647Smarcel } 218226647Smarcel /* isolate the version number from the next condition ... */ 219226647Smarcel nextcondition = strpbrk(condition, "<>=!"); 220226647Smarcel if (nextcondition) { 221226647Smarcel condchar = *nextcondition; 222226647Smarcel *nextcondition = '\0'; 223200534Srpaulo } 224200534Srpaulo /* and compare the versions (version_cmp removes the filename for us) */ 225226647Smarcel if ((match & (1 << (version_cmp(pkgname, condition) + 1))) == 0) 226226647Smarcel errcode = 0; 227226647Smarcel condition = nextcondition; 228226647Smarcel } else { 229226647Smarcel break; 230226647Smarcel } 231226647Smarcel } 232226647Smarcel 233226647Smarcel return errcode; 234226647Smarcel} 235226647Smarcel 236226647Smarcel/* 237226647Smarcel * Synopsis is similar to matchinstalled(), but use origin 238226647Smarcel * as a key for matching packages. 239226647Smarcel */ 240226647Smarcelchar ** 241226647Smarcelmatchbyorigin(const char *origin, int *retval) 242226647Smarcel{ 243226647Smarcel char **installed; 244226647Smarcel int i; 245226647Smarcel static struct store *store = NULL; 246226647Smarcel 247226647Smarcel store = storecreate(store); 248226647Smarcel if (store == NULL) { 249226647Smarcel if (retval != NULL) 250226647Smarcel *retval = 1; 251226647Smarcel return NULL; 252226647Smarcel } 253226647Smarcel 254226647Smarcel if (retval != NULL) 255226647Smarcel *retval = 0; 256226647Smarcel 257226647Smarcel installed = matchinstalled(MATCH_ALL, NULL, retval); 258226647Smarcel if (installed == NULL) 259226647Smarcel return NULL; 260226647Smarcel 261226647Smarcel for (i = 0; installed[i] != NULL; i++) { 262226647Smarcel FILE *fp; 263251588Smarcel char *cp, tmp[PATH_MAX]; 264251588Smarcel int cmd; 265251588Smarcel 266251588Smarcel snprintf(tmp, PATH_MAX, "%s/%s", LOG_DIR, installed[i]); 267251588Smarcel /* 268251588Smarcel * SPECIAL CASE: ignore empty dirs, since we can can see them 269251588Smarcel * during port installation. 270251588Smarcel */ 271251588Smarcel if (isemptydir(tmp)) 272251588Smarcel continue; 273226647Smarcel snprintf(tmp, PATH_MAX, "%s/%s", tmp, CONTENTS_FNAME); 274226647Smarcel fp = fopen(tmp, "r"); 275226647Smarcel if (fp == NULL) { 276226647Smarcel warnx("the package info for package '%s' is corrupt", installed[i]); 277226647Smarcel continue; 278226647Smarcel } 279226647Smarcel 280226647Smarcel cmd = -1; 281226647Smarcel while (fgets(tmp, sizeof(tmp), fp)) { 282226647Smarcel int len = strlen(tmp); 283226647Smarcel 284226647Smarcel while (len && isspace(tmp[len - 1])) 285226647Smarcel tmp[--len] = '\0'; 286226647Smarcel if (!len) 287226647Smarcel continue; 288226647Smarcel cp = tmp; 289226647Smarcel if (tmp[0] != CMD_CHAR) 290226647Smarcel continue; 291226647Smarcel cmd = plist_cmd(tmp + 1, &cp); 292226647Smarcel if (cmd == PLIST_ORIGIN) { 293226647Smarcel if (csh_match(origin, cp, FNM_PATHNAME) == 0) 294226647Smarcel storeappend(store, installed[i]); 295226647Smarcel break; 296226647Smarcel } 297251588Smarcel } 298226647Smarcel if (cmd != PLIST_ORIGIN) 299226647Smarcel warnx("package %s has no origin recorded", installed[i]); 300226647Smarcel fclose(fp); 301226647Smarcel } 302226647Smarcel 303226647Smarcel if (store->used == 0) 304226647Smarcel return NULL; 305226647Smarcel else 306226647Smarcel return store->store; 307226647Smarcel} 308226647Smarcel 309226647Smarcel/* 310226647Smarcel * 311226647Smarcel * Return 1 if the specified package is installed, 312226647Smarcel * 0 if not, and -1 if an error occured. 313226647Smarcel */ 314226647Smarcelint 315226647Smarcelisinstalledpkg(const char *name) 316226647Smarcel{ 317226647Smarcel char buf[FILENAME_MAX]; 318226647Smarcel char buf2[FILENAME_MAX]; 319226647Smarcel 320226647Smarcel snprintf(buf, sizeof(buf), "%s/%s", LOG_DIR, name); 321226647Smarcel if (!isdir(buf) || access(buf, R_OK) == FAIL) 322226647Smarcel return 0; 323226647Smarcel 324226647Smarcel snprintf(buf2, sizeof(buf2), "%s/%s", buf, CONTENTS_FNAME); 325226647Smarcel if (!isfile(buf2) || access(buf2, R_OK) == FAIL) 326226647Smarcel return -1; 327226647Smarcel 328226647Smarcel return 1; 329226647Smarcel} 330226647Smarcel 331226647Smarcel/* 332226647Smarcel * Returns 1 if specified pkgname matches RE pattern. 333226647Smarcel * Otherwise returns 0 if doesn't match or -1 if RE 334226647Smarcel * engine reported an error (usually invalid syntax). 335226647Smarcel */ 336226647Smarcelstatic int 337226647Smarcelrex_match(const char *pattern, const char *pkgname, int extended) 338226647Smarcel{ 339226647Smarcel char errbuf[128]; 340226647Smarcel int errcode; 341226647Smarcel int retval; 342226647Smarcel regex_t rex; 343226647Smarcel 344226647Smarcel retval = 0; 345226647Smarcel 346226647Smarcel errcode = regcomp(&rex, pattern, (extended ? REG_EXTENDED : REG_BASIC) | REG_NOSUB); 347226647Smarcel if (errcode == 0) 348226647Smarcel errcode = regexec(&rex, pkgname, 0, NULL, 0); 349226647Smarcel 350226647Smarcel if (errcode == 0) { 351226647Smarcel retval = 1; 352226647Smarcel } else if (errcode != REG_NOMATCH) { 353226647Smarcel regerror(errcode, &rex, errbuf, sizeof(errbuf)); 354251588Smarcel warnx("%s: %s", pattern, errbuf); 355226647Smarcel retval = -1; 356226647Smarcel } 357199017Srnoland 358166551Smarcel regfree(&rex); 359199017Srnoland 360166551Smarcel return retval; 361199017Srnoland} 362166551Smarcel 363166551Smarcel/* 364166551Smarcel * Match string by a csh-style glob pattern. Returns 0 on 365166551Smarcel * match and FNM_NOMATCH otherwise, to be compatible with 366166551Smarcel * fnmatch(3). 367166551Smarcel */ 368166551Smarcelstatic int 369166551Smarcelcsh_match(const char *pattern, const char *string, int flags) 370214352Sae{ 371214352Sae int ret = FNM_NOMATCH; 372214352Sae 373214352Sae 374214352Sae const char *nextchoice = pattern; 375214352Sae const char *current = NULL; 376214352Sae 377214352Sae int prefixlen = -1; 378214352Sae int currentlen = 0; 379166551Smarcel 380166551Smarcel int level = 0; 381166551Smarcel 382199017Srnoland do { 383199017Srnoland const char *pos = nextchoice; 384199017Srnoland const char *postfix = NULL; 385199017Srnoland 386166551Smarcel Boolean quoted = FALSE; 387166551Smarcel 388199017Srnoland nextchoice = NULL; 389166551Smarcel 390199017Srnoland do { 391199017Srnoland const char *eb; 392199017Srnoland if (!*pos) { 393199017Srnoland postfix = pos; 394166551Smarcel } else if (quoted) { 395199017Srnoland quoted = FALSE; 396199017Srnoland } else { 397199017Srnoland switch (*pos) { 398199017Srnoland case '{': 399199017Srnoland ++level; 400166551Smarcel if (level == 1) { 401166551Smarcel current = pos+1; 402166551Smarcel prefixlen = pos-pattern; 403199017Srnoland } 404214352Sae break; 405199017Srnoland case ',': 406199017Srnoland if (level == 1 && !nextchoice) { 407166551Smarcel nextchoice = pos+1; 408199017Srnoland currentlen = pos-current; 409199017Srnoland } 410214352Sae break; 411214352Sae case '}': 412214352Sae if (level == 1) { 413166551Smarcel postfix = pos+1; 414166551Smarcel if (!nextchoice) 415199017Srnoland currentlen = pos-current; 416166551Smarcel } 417199017Srnoland level--; 418199017Srnoland break; 419166551Smarcel case '[': 420199017Srnoland eb = pos+1; 421166551Smarcel if (*eb == '!' || *eb == '^') 422166551Smarcel eb++; 423199017Srnoland if (*eb == ']') 424199017Srnoland eb++; 425166551Smarcel while(*eb && *eb != ']') 426166551Smarcel eb++; 427199017Srnoland if (*eb) 428199017Srnoland pos=eb; 429166551Smarcel break; 430199017Srnoland case '\\': 431166551Smarcel quoted = TRUE; 432166551Smarcel break; 433199017Srnoland default: 434166551Smarcel ; 435166551Smarcel } 436166551Smarcel } 437166551Smarcel pos++; 438199017Srnoland } while (!postfix); 439166551Smarcel 440199017Srnoland if (current) { 441166551Smarcel char buf[FILENAME_MAX]; 442166551Smarcel snprintf(buf, sizeof(buf), "%.*s%.*s%s", prefixlen, pattern, currentlen, current, postfix); 443199017Srnoland ret = csh_match(buf, string, flags); 444199017Srnoland if (ret) { 445199017Srnoland current = nextchoice; 446214352Sae level = 1; 447214352Sae } else 448214352Sae current = NULL; 449214352Sae } else 450199017Srnoland ret = fnmatch(pattern, string, flags); 451199017Srnoland } while (current); 452199017Srnoland 453199017Srnoland return ret; 454199017Srnoland} 455199017Srnoland 456199017Srnoland/* 457199017Srnoland * Create an empty store, optionally deallocating 458166551Smarcel * any previously allocated space if store != NULL. 459166551Smarcel */ 460166551Smarcelstruct store * 461166551Smarcelstorecreate(struct store *store) 462166551Smarcel{ 463166551Smarcel int i; 464166551Smarcel 465166551Smarcel if (store == NULL) { 466166551Smarcel store = malloc(sizeof *store); 467217531Sae if (store == NULL) { 468166551Smarcel warnx("%s(): malloc() failed", __func__); 469166551Smarcel return NULL; 470199017Srnoland } 471199017Srnoland store->currlen = 0; 472199017Srnoland store->store = NULL; 473166551Smarcel } else if (store->store != NULL) { 474166551Smarcel /* Free previously allocated memory */ 475166551Smarcel for (i = 0; store->store[i] != NULL; i++) 476166551Smarcel free(store->store[i]); 477166551Smarcel store->store[0] = NULL; 478166551Smarcel } 479217531Sae store->used = 0; 480217531Sae 481217531Sae return store; 482217531Sae} 483217531Sae 484217531Sae/* 485217531Sae * Append specified element to the provided store. 486217531Sae */ 487217531Saestatic int 488217531Saestoreappend(struct store *store, const char *item) 489217531Sae{ 490217531Sae if (store->used + 2 > store->currlen) { 491217531Sae store->currlen += 16; 492166551Smarcel store->store = reallocf(store->store, 493166551Smarcel store->currlen * sizeof(*(store->store))); 494166551Smarcel if (store->store == NULL) { 495166551Smarcel store->currlen = 0; 496166551Smarcel warnx("%s(): reallocf() failed", __func__); 497166551Smarcel return 1; 498166551Smarcel } 499166551Smarcel } 500166551Smarcel 501166551Smarcel asprintf(&(store->store[store->used]), "%s", item); 502166551Smarcel if (store->store[store->used] == NULL) { 503166551Smarcel warnx("%s(): malloc() failed", __func__); 504166551Smarcel return 1; 505166551Smarcel } 506166551Smarcel store->used++; 507166551Smarcel store->store[store->used] = NULL; 508166551Smarcel 509166551Smarcel return 0; 510179763Smarcel} 511179763Smarcel 512166551Smarcelstatic int 513166551Smarcelfname_cmp(const FTSENT * const *a, const FTSENT * const *b) 514166551Smarcel{ 515166551Smarcel return strcmp((*a)->fts_name, (*b)->fts_name); 516166551Smarcel} 517166551Smarcel