match.c revision 74258
1#ifndef lint
2static const char rcsid[] =
3  "$FreeBSD: head/usr.sbin/pkg_install/lib/match.c 74258 2001-03-14 19:46:43Z sobomax $";
4#endif
5
6/*
7 * FreeBSD install - a package for the installation and maintainance
8 * of non-core utilities.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * Maxim Sobolev
20 * 24 February 2001
21 *
22 * Routines used to query installed packages.
23 *
24 */
25
26#include "lib.h"
27
28#include <err.h>
29#include <fnmatch.h>
30#include <fts.h>
31#include <regex.h>
32
33/*
34 * Simple structure representing argv-like
35 * NULL-terminated list.
36 */
37struct store {
38    int currlen;
39    int used;
40    char **store;
41};
42
43static int rex_match(char *, char *);
44static int storeappend(struct store *, const char *);
45static int fname_cmp(const FTSENT **, const FTSENT **);
46
47/*
48 * Function to query names of installed packages.
49 * MatchType	- one of MATCH_ALL, MATCH_REGEX, MATCH_GLOB;
50 * patterns	- NULL-terminated list of glob or regex patterns
51 *		  (could be NULL for MATCH_ALL);
52 * retval	- return value (could be NULL if you don't want/need
53 *		  return value).
54 * Returns NULL-terminated list with matching names.
55 * Names in list returned are dynamically allocated and should
56 * not be altered by the caller.
57 */
58char **
59matchinstalled(match_t MatchType, char **patterns, int *retval)
60{
61    int i, errcode, len;
62    char *tmp, *matched;
63    char *paths[2];
64    static struct store *store = NULL;
65    FTS *ftsp;
66    FTSENT *f;
67    Boolean *lmatched;
68
69    if (store == NULL) {
70	store = malloc(sizeof *store);
71	if (store == NULL) {
72	    warnx("%s(): malloc() failed", __FUNCTION__);
73	    if (retval != NULL)
74		*retval = 1;
75	    return NULL;
76	}
77	store->currlen = 0;
78	store->store = NULL;
79    } else
80	if (store->store != NULL)
81	    /* Free previously allocated memory */
82	    for (i = 0; store->store[i] != NULL; i++)
83		free(store->store[i]);
84    store->used = 0;
85
86    if (retval != NULL)
87	*retval = 0;
88
89    tmp = getenv(PKG_DBDIR);
90    if (!tmp)
91	tmp = DEF_LOG_DIR;
92    if (!isdir(tmp)) {
93	if (retval != NULL)
94	    *retval = 1;
95	return NULL;
96	/* Not reached */
97    }
98
99    /* Count number of patterns */
100    for (len = 0; patterns[len]; len++) {}
101    lmatched = alloca(sizeof(*lmatched) * len);
102    if (lmatched == NULL) {
103	warnx("%s(): alloca() failed", __FUNCTION__);
104	if (retval != NULL)
105	    *retval = 1;
106	return NULL;
107    }
108    for (i = 0; i < len; i++)
109	lmatched[i] = FALSE;
110
111    paths[0] = tmp;
112    paths[1] = NULL;
113    ftsp = fts_open(paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp);
114    if (ftsp != NULL) {
115	while ((f = fts_read(ftsp)) != NULL) {
116	    if (f->fts_info == FTS_D && f->fts_level == 1) {
117		fts_set(ftsp, f, FTS_SKIP);
118		matched = NULL;
119		errcode = 0;
120		if (MatchType == MATCH_ALL)
121		    matched = f->fts_name;
122		else
123		    for (i = 0; patterns[i]; i++) {
124			switch (MatchType) {
125			case MATCH_REGEX:
126			    errcode = rex_match(patterns[i], f->fts_name);
127			    if (errcode == 1) {
128				matched = f->fts_name;
129				errcode = 0;
130			    }
131			    break;
132			case MATCH_GLOB:
133			    if (fnmatch(patterns[i], f->fts_name, 0) == 0) {
134				matched = f->fts_name;
135				lmatched[i] = TRUE;
136			    }
137			    break;
138			default:
139			    break;
140			}
141			if (matched != NULL || errcode != 0)
142			    break;
143		    }
144		if (errcode == 0 && matched != NULL)
145		    errcode = storeappend(store, matched);
146		if (errcode != 0) {
147		    if (retval != NULL)
148			*retval = 1;
149		    return NULL;
150		    /* Not reached */
151		}
152	    }
153	}
154	fts_close(ftsp);
155    }
156
157    if (MatchType == MATCH_GLOB) {
158	for (i = 0; i < len; i++)
159	    if (lmatched[i] == FALSE)
160		storeappend(store, patterns[i]);
161    }
162
163    if (store->used == 0)
164	return NULL;
165    else
166	return store->store;
167}
168
169/*
170 * Returns 1 if specified pkgname matches RE pattern.
171 * Otherwise returns 0 if doesn't match or -1 if RE
172 * engine reported an error (usually invalid syntax).
173 */
174static int
175rex_match(char *pattern, char *pkgname)
176{
177    char errbuf[128];
178    int errcode;
179    int retval;
180    regex_t rex;
181
182    retval = 0;
183
184    errcode = regcomp(&rex, pattern, REG_BASIC | REG_NOSUB);
185    if (errcode == 0)
186	errcode = regexec(&rex, pkgname, 0, NULL, 0);
187
188    if (errcode == 0) {
189	retval = 1;
190    } else if (errcode != REG_NOMATCH) {
191	regerror(errcode, &rex, errbuf, sizeof(errbuf));
192	warnx("%s: %s", pattern, errbuf);
193	retval = -1;
194    }
195
196    regfree(&rex);
197
198    return retval;
199}
200
201static int
202storeappend(struct store *store, const char *item)
203{
204    char **tmp;
205
206    if (store->used + 2 > store->currlen) {
207	tmp = store->store;
208	store->currlen += 16;
209	store->store = malloc(store->currlen * sizeof(*(store->store)));
210	if (store->store == NULL) {
211	    warnx("%s(): malloc() failed", __FUNCTION__);
212	    return 1;
213	}
214	memcpy(store->store, tmp, store->used * sizeof(*(store->store)));
215	free(tmp);
216    }
217
218    asprintf(&(store->store[store->used]), "%s", item);
219    if (store->store[store->used] == NULL) {
220	warnx("%s(): malloc() failed", __FUNCTION__);
221	return 1;
222    }
223    store->used++;
224    store->store[store->used] = NULL;
225
226    return 0;
227}
228
229static int
230fname_cmp(const FTSENT **a, const FTSENT **b)
231{
232    return strcmp((*a)->fts_name, (*b)->fts_name);
233}
234