diff options
author | eik <eik@FreeBSD.org> | 2004-06-29 18:54:47 +0000 |
---|---|---|
committer | eik <eik@FreeBSD.org> | 2004-06-29 18:54:47 +0000 |
commit | 7923356ae6b0c5daf724f0e5c8844ffec1bb4871 (patch) | |
tree | 96259c70ed86c0b116099b99afb26ba9fecc746e /usr.sbin/pkg_install/lib | |
parent | 649576111eab9cebc8bcde1eb574a9f3644fe2a6 (diff) | |
download | FreeBSD-src-7923356ae6b0c5daf724f0e5c8844ffec1bb4871.zip FreeBSD-src-7923356ae6b0c5daf724f0e5c8844ffec1bb4871.tar.gz |
- match package version numbers with relational operators
- use glob patterns when matching packages by origin
- csh-style {...} choices in glob matching
- pkg_info: new flag -E (list matching package names only)
- pkg_version: new flag -T (test if a given name matches a pattern)
- new flag -X (interpret pattern as an extended regular expression)
PR: 56961
Diffstat (limited to 'usr.sbin/pkg_install/lib')
-rw-r--r-- | usr.sbin/pkg_install/lib/lib.h | 3 | ||||
-rw-r--r-- | usr.sbin/pkg_install/lib/match.c | 221 |
2 files changed, 198 insertions, 26 deletions
diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h index 5690314..50c3d40 100644 --- a/usr.sbin/pkg_install/lib/lib.h +++ b/usr.sbin/pkg_install/lib/lib.h @@ -105,7 +105,7 @@ enum _plist_t { 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; @@ -206,6 +206,7 @@ int real_main(int, char **); 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 **); diff --git a/usr.sbin/pkg_install/lib/match.c b/usr.sbin/pkg_install/lib/match.c index 27db978..0cd9853 100644 --- a/usr.sbin/pkg_install/lib/match.c +++ b/usr.sbin/pkg_install/lib/match.c @@ -37,14 +37,15 @@ struct store { 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 @@ matchinstalled(match_t MatchType, char **patterns, int *retval) 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,96 @@ matchinstalled(match_t MatchType, char **patterns, int *retval) 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. @@ -193,10 +273,8 @@ matchbyorigin(const char *origin, int *retval) 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 +290,7 @@ matchbyorigin(const char *origin, int *retval) 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; } @@ -255,7 +333,7 @@ isinstalledpkg(const char *name) * 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 +342,7 @@ rex_match(const char *pattern, const char *pkgname) 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); @@ -282,6 +360,99 @@ rex_match(const char *pattern, const char *pkgname) } /* + * 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. */ |