summaryrefslogtreecommitdiffstats
path: root/usr.sbin/pkg_install/lib
diff options
context:
space:
mode:
authoreik <eik@FreeBSD.org>2004-06-29 18:54:47 +0000
committereik <eik@FreeBSD.org>2004-06-29 18:54:47 +0000
commit7923356ae6b0c5daf724f0e5c8844ffec1bb4871 (patch)
tree96259c70ed86c0b116099b99afb26ba9fecc746e /usr.sbin/pkg_install/lib
parent649576111eab9cebc8bcde1eb574a9f3644fe2a6 (diff)
downloadFreeBSD-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.h3
-rw-r--r--usr.sbin/pkg_install/lib/match.c221
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.
*/
OpenPOWER on IntegriCloud