diff options
author | grehan <grehan@FreeBSD.org> | 2011-05-24 15:39:34 +0000 |
---|---|---|
committer | grehan <grehan@FreeBSD.org> | 2011-05-24 15:39:34 +0000 |
commit | 31bc5dbbffce22454d760ecdba41585aaed6281b (patch) | |
tree | a24e88e247a8eedca803d65257068e14f16c0eb6 /lib/libpkg/match.c | |
parent | 949e126edfdb544c4b351ec5726c8dc3ff848dda (diff) | |
parent | b3769a4355d61333f6b11fd0ccac65cddb54a3a8 (diff) | |
download | FreeBSD-src-31bc5dbbffce22454d760ecdba41585aaed6281b.zip FreeBSD-src-31bc5dbbffce22454d760ecdba41585aaed6281b.tar.gz |
IFC @ r222256
Diffstat (limited to 'lib/libpkg/match.c')
-rw-r--r-- | lib/libpkg/match.c | 603 |
1 files changed, 0 insertions, 603 deletions
diff --git a/lib/libpkg/match.c b/lib/libpkg/match.c deleted file mode 100644 index ba65442..0000000 --- a/lib/libpkg/match.c +++ /dev/null @@ -1,603 +0,0 @@ -/* - * 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. - * - * Maxim Sobolev - * 24 February 2001 - * - * Routines used to query installed packages. - * - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include "pkg.h" -#include <err.h> -#include <fnmatch.h> -#include <fts.h> -#include <regex.h> - -/* - * Simple structure representing argv-like - * NULL-terminated list. - */ -struct store { - int currlen; - int used; - char **store; -}; - -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_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 - * return value). - * Returns NULL-terminated list with matching names. - * Names in list returned are dynamically allocated and should - * not be altered by the caller. - */ -char ** -matchinstalled(match_t MatchType, char **patterns, int *retval) -{ - int i, errcode, len; - char *matched; - const char *paths[2] = {LOG_DIR, NULL}; - static struct store *store = NULL; - FTS *ftsp; - FTSENT *f; - Boolean *lmatched = NULL; - - store = storecreate(store); - if (store == NULL) { - if (retval != NULL) - *retval = 1; - return NULL; - } - - if (retval != NULL) - *retval = 0; - - if (!isdir(paths[0])) { - if (retval != NULL) - *retval = 1; - return NULL; - /* Not reached */ - } - - /* Count number of patterns */ - if (patterns != NULL) { - for (len = 0; patterns[len]; len++) {} - lmatched = alloca(sizeof(*lmatched) * len); - if (lmatched == NULL) { - warnx("%s(): alloca() failed", __func__); - if (retval != NULL) - *retval = 1; - return NULL; - } - } else - len = 0; - - for (i = 0; i < len; i++) - lmatched[i] = FALSE; - - ftsp = fts_open((char * const *)(uintptr_t)paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp); - if (ftsp != NULL) { - while ((f = fts_read(ftsp)) != NULL) { - if (f->fts_info == FTS_D && f->fts_level == 1) { - fts_set(ftsp, f, FTS_SKIP); - matched = NULL; - errcode = 0; - if (MatchType == MATCH_ALL) - matched = f->fts_name; - else - for (i = 0; patterns[i]; i++) { - 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; - } - if (errcode == 0 && matched != NULL) - errcode = storeappend(store, matched); - if (errcode != 0) { - if (retval != NULL) - *retval = 1; - return NULL; - /* Not reached */ - } - } - } - fts_close(ftsp); - } - - if (MatchType == MATCH_GLOB) { - for (i = 0; i < len; i++) - if (lmatched[i] == FALSE) - storeappend(store, patterns[i]); - } - - if (store->used == 0) - return NULL; - else - 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 ... */ - if (condition > pattern && condition[-1] == '!') - 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; - case MATCH_EXACT: - errcode = (strcmp(pattern, fname) == 0) ? 1 : 0; - break; - case MATCH_ALL: - errcode = 1; - 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. - */ -char *** -matchallbyorigin(const char **origins, int *retval) -{ - char **installed, **allorigins = NULL; - char ***matches = NULL; - int i, j; - - if (retval != NULL) - *retval = 0; - - installed = matchinstalled(MATCH_ALL, NULL, retval); - if (installed == NULL) - return NULL; - - /* Gather origins for all installed packages */ - for (i = 0; installed[i] != NULL; i++) { - FILE *fp; - char *buf, *cp, tmp[PATH_MAX]; - int cmd; - - allorigins = realloc(allorigins, (i + 1) * sizeof(*allorigins)); - allorigins[i] = NULL; - - snprintf(tmp, PATH_MAX, "%s/%s", LOG_DIR, installed[i]); - /* - * SPECIAL CASE: ignore empty dirs, since we can can see them - * during port installation. - */ - if (isemptydir(tmp)) - continue; - strncat(tmp, "/" CONTENTS_FNAME, PATH_MAX); - fp = fopen(tmp, "r"); - if (fp == NULL) { - warnx("the package info for package '%s' is corrupt", installed[i]); - continue; - } - - cmd = -1; - while (fgets(tmp, sizeof(tmp), fp)) { - int len = strlen(tmp); - - while (len && isspace(tmp[len - 1])) - tmp[--len] = '\0'; - if (!len) - continue; - cp = tmp; - if (tmp[0] != CMD_CHAR) - continue; - cmd = plist_cmd(tmp + 1, &cp); - if (cmd == PLIST_ORIGIN) { - asprintf(&buf, "%s", cp); - allorigins[i] = buf; - break; - } - } - if (cmd != PLIST_ORIGIN && 0 != strncmp("bsdpan-", installed[i], 7)) - warnx("package %s has no origin recorded", installed[i]); - fclose(fp); - } - - /* Resolve origins into package names, retaining the sequence */ - for (i = 0; origins[i] != NULL; i++) { - matches = realloc(matches, (i + 1) * sizeof(*matches)); - struct store *store = NULL; - store = storecreate(store); - - for (j = 0; installed[j] != NULL; j++) { - if (allorigins[j]) { - if (csh_match(origins[i], allorigins[j], FNM_PATHNAME) == 0) { - storeappend(store, installed[j]); - } - } - } - if (store->used == 0) - matches[i] = NULL; - else - matches[i] = store->store; - } - - if (allorigins) { - for (i = 0; installed[i] != NULL; i++) - if (allorigins[i]) - free(allorigins[i]); - free(allorigins); - } - - return matches; -} - -/* - * Synopsis is similar to matchinstalled(), but use origin - * as a key for matching packages. - */ -char ** -matchbyorigin(const char *origin, int *retval) -{ - const char *origins[2]; - char ***tmp; - - origins[0] = origin; - origins[1] = NULL; - - tmp = matchallbyorigin(origins, retval); - if (tmp && tmp[0]) { - return tmp[0]; - } else { - return NULL; - } -} - -/* - * Small linked list to memoize results of isinstalledpkg(). A hash table - * would be faster but for n ~= 1000 may be overkill. - */ -struct iip_memo { - LIST_ENTRY(iip_memo) iip_link; - char *iip_name; - int iip_result; -}; -LIST_HEAD(, iip_memo) iip_memo = LIST_HEAD_INITIALIZER(iip_memo); - -/* - * - * Return 1 if the specified package is installed, - * 0 if not, and -1 if an error occured. - */ -int -isinstalledpkg(const char *name) -{ - int result; - char *buf, *buf2; - struct iip_memo *memo; - - LIST_FOREACH(memo, &iip_memo, iip_link) { - if (strcmp(memo->iip_name, name) == 0) - return memo->iip_result; - } - - buf2 = NULL; - asprintf(&buf, "%s/%s", LOG_DIR, name); - if (buf == NULL) - goto errout; - if (!isdir(buf) || access(buf, R_OK) == FAIL) { - result = 0; - } else { - asprintf(&buf2, "%s/%s", buf, CONTENTS_FNAME); - if (buf2 == NULL) - goto errout; - - if (!isfile(buf2) || access(buf2, R_OK) == FAIL) - result = -1; - else - result = 1; - } - - free(buf); - buf = strdup(name); - if (buf == NULL) - goto errout; - free(buf2); - buf2 = NULL; - - memo = malloc(sizeof *memo); - if (memo == NULL) - goto errout; - memo->iip_name = buf; - memo->iip_result = result; - LIST_INSERT_HEAD(&iip_memo, memo, iip_link); - return result; - -errout: - if (buf != NULL) - free(buf); - if (buf2 != NULL) - free(buf2); - return -1; -} - -/* - * Returns 1 if specified pkgname matches RE pattern. - * Otherwise returns 0 if doesn't match or -1 if RE - * engine reported an error (usually invalid syntax). - */ -static int -rex_match(const char *pattern, const char *pkgname, int extended) -{ - char errbuf[128]; - int errcode; - int retval; - regex_t rex; - - retval = 0; - - errcode = regcomp(&rex, pattern, (extended ? REG_EXTENDED : REG_BASIC) | REG_NOSUB); - if (errcode == 0) - errcode = regexec(&rex, pkgname, 0, NULL, 0); - - if (errcode == 0) { - retval = 1; - } else if (errcode != REG_NOMATCH) { - regerror(errcode, &rex, errbuf, sizeof(errbuf)); - warnx("%s: %s", pattern, errbuf); - retval = -1; - } - - 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; -} - -/* - * Create an empty store, optionally deallocating - * any previously allocated space if store != NULL. - */ -struct store * -storecreate(struct store *store) -{ - int i; - - if (store == NULL) { - store = malloc(sizeof *store); - if (store == NULL) { - warnx("%s(): malloc() failed", __func__); - return NULL; - } - store->currlen = 0; - store->store = NULL; - } else if (store->store != NULL) { - /* Free previously allocated memory */ - for (i = 0; store->store[i] != NULL; i++) - free(store->store[i]); - store->store[0] = NULL; - } - store->used = 0; - - return store; -} - -/* - * Append specified element to the provided store. - */ -static int -storeappend(struct store *store, const char *item) -{ - if (store->used + 2 > store->currlen) { - store->currlen += 16; - store->store = reallocf(store->store, - store->currlen * sizeof(*(store->store))); - if (store->store == NULL) { - store->currlen = 0; - warnx("%s(): reallocf() failed", __func__); - return 1; - } - } - - asprintf(&(store->store[store->used]), "%s", item); - if (store->store[store->used] == NULL) { - warnx("%s(): malloc() failed", __func__); - return 1; - } - store->used++; - store->store[store->used] = NULL; - - return 0; -} - -static int -fname_cmp(const FTSENT * const *a, const FTSENT * const *b) -{ - return strcmp((*a)->fts_name, (*b)->fts_name); -} |