2 * FreeBSD install - a package for the installation and maintainance
3 * of non-core utilities.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
17 * Routines used to query installed packages.
19 * $FreeBSD: src/usr.sbin/pkg_install/lib/match.c,v 1.19 2004/06/29 19:06:42 eik Exp $
20 * $DragonFly: src/usr.sbin/pkg_install/lib/Attic/match.c,v 1.3 2004/07/30 04:46:13 dillon Exp $
30 * Simple structure representing argv-like
31 * NULL-terminated list.
39 static int rex_match(const char *, const char *, int);
40 static int csh_match(const char *, const char *, int);
41 struct store *storecreate(struct store *);
42 static int storeappend(struct store *, const char *);
43 static int fname_cmp(const FTSENT * const *, const FTSENT * const *);
46 * Function to query names of installed packages.
47 * MatchType - one of MATCH_ALL, MATCH_EREGEX, MATCH_REGEX, MATCH_GLOB, MATCH_NGLOB;
48 * patterns - NULL-terminated list of glob or regex patterns
49 * (could be NULL for MATCH_ALL);
50 * retval - return value (could be NULL if you don't want/need
52 * Returns NULL-terminated list with matching names.
53 * Names in list returned are dynamically allocated and should
54 * not be altered by the caller.
57 matchinstalled(match_t MatchType, char **patterns, int *retval)
61 const char *paths[2] = {LOG_DIR, NULL};
62 static struct store *store = NULL;
65 Boolean *lmatched = NULL;
67 store = storecreate(store);
77 if (!isdir(paths[0])) {
84 /* Count number of patterns */
85 if (patterns != NULL) {
86 for (len = 0; patterns[len]; len++) {}
87 lmatched = alloca(sizeof(*lmatched) * len);
88 if (lmatched == NULL) {
89 warnx("%s(): alloca() failed", __func__);
97 for (i = 0; i < len; i++)
100 ftsp = fts_open((char * const *)(uintptr_t)paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp);
102 while ((f = fts_read(ftsp)) != NULL) {
103 if (f->fts_info == FTS_D && f->fts_level == 1) {
104 fts_set(ftsp, f, FTS_SKIP);
107 if (MatchType == MATCH_ALL)
108 matched = f->fts_name;
110 for (i = 0; patterns[i]; i++) {
111 errcode = pattern_match(MatchType, patterns[i], f->fts_name);
113 matched = f->fts_name;
117 if (matched != NULL || errcode != 0)
120 if (errcode == 0 && matched != NULL)
121 errcode = storeappend(store, matched);
133 if (MatchType == MATCH_GLOB) {
134 for (i = 0; i < len; i++)
135 if (lmatched[i] == FALSE)
136 storeappend(store, patterns[i]);
139 if (store->used == 0)
146 pattern_match(match_t MatchType, char *pattern, const char *pkgname)
149 const char *fname = pkgname;
150 char basefname[PATH_MAX];
151 char condchar = '\0';
154 /* do we have an appended condition? */
155 condition = strpbrk(pattern, "<>=");
158 /* yes, isolate the pattern from the condition ... */
159 if (condition > pattern && condition[-1] == '!')
161 condchar = *condition;
163 /* ... and compare the name without version */
164 ch = strrchr(fname, '-');
165 if (ch && ch - fname < PATH_MAX) {
166 strlcpy(basefname, fname, ch - fname + 1);
174 errcode = rex_match(pattern, fname, MatchType == MATCH_EREGEX ? 1 : 0);
178 errcode = (csh_match(pattern, fname, 0) == 0) ? 1 : 0;
181 errcode = (strcmp(pattern, fname) == 0) ? 1 : 0;
190 /* loop over all appended conditions */
192 /* restore the pattern */
193 *condition = condchar;
194 /* parse the condition (fun with bits) */
197 /* compare version numbers */
199 if (*++condition == '=') {
217 /* isolate the version number from the next condition ... */
218 nextcondition = strpbrk(condition, "<>=!");
220 condchar = *nextcondition;
221 *nextcondition = '\0';
223 /* and compare the versions (version_cmp removes the filename for us) */
224 if ((match & (1 << (version_cmp(pkgname, condition) + 1))) == 0)
226 condition = nextcondition;
236 * Synopsis is similar to matchinstalled(), but use origin
237 * as a key for matching packages.
240 matchbyorigin(const char *origin, int *retval)
244 static struct store *store = NULL;
246 store = storecreate(store);
256 installed = matchinstalled(MATCH_ALL, NULL, retval);
257 if (installed == NULL)
260 for (i = 0; installed[i] != NULL; i++) {
262 char *cp, tmp[PATH_MAX];
265 snprintf(tmp, PATH_MAX, "%s/%s", LOG_DIR, installed[i]);
267 * SPECIAL CASE: ignore empty dirs, since we can can see them
268 * during port installation.
272 snprintf(tmp, PATH_MAX, "%s/%s", tmp, CONTENTS_FNAME);
273 fp = fopen(tmp, "r");
275 warnx("the package info for package '%s' is corrupt", installed[i]);
280 while (fgets(tmp, sizeof(tmp), fp)) {
281 int len = strlen(tmp);
283 while (len && isspace(tmp[len - 1]))
288 if (tmp[0] != CMD_CHAR)
290 cmd = plist_cmd(tmp + 1, &cp);
291 if (cmd == PLIST_ORIGIN) {
292 if (csh_match(origin, cp, FNM_PATHNAME) == 0)
293 storeappend(store, installed[i]);
297 if (cmd != PLIST_ORIGIN)
298 warnx("package %s has no origin recorded", installed[i]);
302 if (store->used == 0)
310 * Return 1 if the specified package is installed,
311 * 0 if not, and -1 if an error occured.
314 isinstalledpkg(const char *name)
316 char buf[FILENAME_MAX];
317 char buf2[FILENAME_MAX];
319 snprintf(buf, sizeof(buf), "%s/%s", LOG_DIR, name);
320 if (!isdir(buf) || access(buf, R_OK) == FAIL)
323 snprintf(buf2, sizeof(buf2), "%s/%s", buf, CONTENTS_FNAME);
324 if (!isfile(buf2) || access(buf2, R_OK) == FAIL)
331 * Returns 1 if specified pkgname matches RE pattern.
332 * Otherwise returns 0 if doesn't match or -1 if RE
333 * engine reported an error (usually invalid syntax).
336 rex_match(const char *pattern, const char *pkgname, int extended)
345 errcode = regcomp(&rex, pattern, (extended ? REG_EXTENDED : REG_BASIC) | REG_NOSUB);
347 errcode = regexec(&rex, pkgname, 0, NULL, 0);
351 } else if (errcode != REG_NOMATCH) {
352 regerror(errcode, &rex, errbuf, sizeof(errbuf));
353 warnx("%s: %s", pattern, errbuf);
363 * Match string by a csh-style glob pattern. Returns 0 on
364 * match and FNM_NOMATCH otherwise, to be compatible with
368 csh_match(const char *pattern, const char *string, int flags)
370 int ret = FNM_NOMATCH;
373 const char *nextchoice = pattern;
374 const char *current = NULL;
382 const char *pos = nextchoice;
383 const char *postfix = NULL;
385 Boolean quoted = FALSE;
401 prefixlen = pos-pattern;
405 if (level == 1 && !nextchoice) {
407 currentlen = pos-current;
414 currentlen = pos-current;
420 if (*eb == '!' || *eb == '^')
424 while(*eb && *eb != ']')
440 char buf[FILENAME_MAX];
441 snprintf(buf, sizeof(buf), "%.*s%.*s%s", prefixlen, pattern, currentlen, current, postfix);
442 ret = csh_match(buf, string, flags);
444 current = nextchoice;
449 ret = fnmatch(pattern, string, flags);
456 * Create an empty store, optionally deallocating
457 * any previously allocated space if store != NULL.
460 storecreate(struct store *store)
465 store = malloc(sizeof *store);
467 warnx("%s(): malloc() failed", __func__);
472 } else if (store->store != NULL) {
473 /* Free previously allocated memory */
474 for (i = 0; store->store[i] != NULL; i++)
475 free(store->store[i]);
476 store->store[0] = NULL;
484 * Append specified element to the provided store.
487 storeappend(struct store *store, const char *item)
489 if (store->used + 2 > store->currlen) {
490 store->currlen += 16;
491 store->store = reallocf(store->store,
492 store->currlen * sizeof(*(store->store)));
493 if (store->store == NULL) {
495 warnx("%s(): reallocf() failed", __func__);
500 asprintf(&(store->store[store->used]), "%s", item);
501 if (store->store[store->used] == NULL) {
502 warnx("%s(): malloc() failed", __func__);
506 store->store[store->used] = NULL;
512 fname_cmp(const FTSENT * const *a, const FTSENT * const *b)
514 return strcmp((*a)->fts_name, (*b)->fts_name);