freebsd-dev/usr.sbin/pkg_install/lib/match.c
Garrett Wollman 0d3bcc2e80 Make the threatened fts(3) ABI fix. FTSENT now avoids the use of the struct
hack, thereby allowing future extensions to the structure (e.g., for extended
attributes) without rebreaking the ABI.  FTSENT now contains a pointer to the
parent stream, which fts_compar() can then take advantage of, avoiding the
undefined behavior previously warned about.  As a consequence of this change,
the prototype of the comparison function passed to fts_open() has changed
to reflect the required amount of constness for its use.  All callers in the
tree are updated to use the correct prototype.

Comparison functions can now make use of the new parent pointer to access
the new stream-specific private data pointer, which is intended to assist
creation of reentrant library routines which use fts(3) internally.

Not objected to in spirit by: -arch
2002-09-21 01:28:41 +00:00

344 lines
7.6 KiB
C

/*
* 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 "lib.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 *);
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;
* 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;
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++) {
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;
}
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;
}
/*
* Synopsis is similar to matchinstalled(), but use origin
* as a key for matching packages.
*/
char **
matchbyorigin(const char *origin, int *retval)
{
char **installed;
int i;
static struct store *store = NULL;
store = storecreate(store);
if (store == NULL) {
if (retval != NULL)
*retval = 1;
return NULL;
}
if (retval != NULL)
*retval = 0;
installed = matchinstalled(MATCH_ALL, NULL, retval);
if (installed == NULL)
return NULL;
for (i = 0; installed[i] != NULL; i++) {
FILE *fp;
char *cp, tmp[PATH_MAX];
int cmd;
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;
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;
}
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) {
if (strcmp(origin, cp) == 0)
storeappend(store, installed[i]);
break;
}
}
if (cmd != PLIST_ORIGIN)
warnx("package %s has no origin recorded", installed[i]);
fclose(fp);
}
if (store->used == 0)
return NULL;
else
return store->store;
}
/*
* Return TRUE if the specified package is installed,
* or FALSE otherwise.
*/
int
isinstalledpkg(const char *name)
{
char buf[FILENAME_MAX];
snprintf(buf, sizeof(buf), "%s/%s", LOG_DIR, name);
if (!isdir(buf) || access(buf, R_OK) == FAIL)
return FALSE;
snprintf(buf, sizeof(buf), "%s/%s", buf, CONTENTS_FNAME);
if (!isfile(buf) || access(buf, R_OK) == FAIL)
return FALSE;
return TRUE;
}
/*
* 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)
{
char errbuf[128];
int errcode;
int retval;
regex_t rex;
retval = 0;
errcode = regcomp(&rex, pattern, 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;
}
/*
* 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);
}