match.c revision 118313
1/*
2 * FreeBSD install - a package for the installation and maintainance
3 * of non-core utilities.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
14 * Maxim Sobolev
15 * 24 February 2001
16 *
17 * Routines used to query installed packages.
18 *
19 */
20
21#include <sys/cdefs.h>
22__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/lib/match.c 118313 2003-08-01 17:26:41Z bmilekic $");
23
24#include "lib.h"
25#include <err.h>
26#include <fnmatch.h>
27#include <fts.h>
28#include <regex.h>
29
30/*
31 * Simple structure representing argv-like
32 * NULL-terminated list.
33 */
34struct store {
35    int currlen;
36    int used;
37    char **store;
38};
39
40static int rex_match(const char *, const char *);
41struct store *storecreate(struct store *);
42static int storeappend(struct store *, const char *);
43static int fname_cmp(const FTSENT * const *, const FTSENT * const *);
44
45/*
46 * Function to query names of installed packages.
47 * MatchType	- one of MATCH_ALL, MATCH_REGEX, MATCH_GLOB;
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
51 *		  return value).
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.
55 */
56char **
57matchinstalled(match_t MatchType, char **patterns, int *retval)
58{
59    int i, errcode, len;
60    char *matched;
61    const char *paths[2] = {LOG_DIR, NULL};
62    static struct store *store = NULL;
63    FTS *ftsp;
64    FTSENT *f;
65    Boolean *lmatched;
66
67    store = storecreate(store);
68    if (store == NULL) {
69	if (retval != NULL)
70	    *retval = 1;
71	return NULL;
72    }
73
74    if (retval != NULL)
75	*retval = 0;
76
77    if (!isdir(paths[0])) {
78	if (retval != NULL)
79	    *retval = 1;
80	return NULL;
81	/* Not reached */
82    }
83
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__);
90	    if (retval != NULL)
91		*retval = 1;
92	    return NULL;
93    	}
94    } else
95	len = 0;
96
97    for (i = 0; i < len; i++)
98	lmatched[i] = FALSE;
99
100    ftsp = fts_open((char * const *)(uintptr_t)paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp);
101    if (ftsp != NULL) {
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);
105		matched = NULL;
106		errcode = 0;
107		if (MatchType == MATCH_ALL)
108		    matched = f->fts_name;
109		else
110		    for (i = 0; patterns[i]; i++) {
111			switch (MatchType) {
112			case MATCH_REGEX:
113			    errcode = rex_match(patterns[i], f->fts_name);
114			    if (errcode == 1) {
115				matched = f->fts_name;
116				errcode = 0;
117			    }
118			    break;
119			case MATCH_GLOB:
120			    if (fnmatch(patterns[i], f->fts_name, 0) == 0) {
121				matched = f->fts_name;
122				lmatched[i] = TRUE;
123			    }
124			    break;
125			default:
126			    break;
127			}
128			if (matched != NULL || errcode != 0)
129			    break;
130		    }
131		if (errcode == 0 && matched != NULL)
132		    errcode = storeappend(store, matched);
133		if (errcode != 0) {
134		    if (retval != NULL)
135			*retval = 1;
136		    return NULL;
137		    /* Not reached */
138		}
139	    }
140	}
141	fts_close(ftsp);
142    }
143
144    if (MatchType == MATCH_GLOB) {
145	for (i = 0; i < len; i++)
146	    if (lmatched[i] == FALSE)
147		storeappend(store, patterns[i]);
148    }
149
150    if (store->used == 0)
151	return NULL;
152    else
153	return store->store;
154}
155
156/*
157 * Synopsis is similar to matchinstalled(), but use origin
158 * as a key for matching packages.
159 */
160char **
161matchbyorigin(const char *origin, int *retval)
162{
163    char **installed;
164    int i;
165    static struct store *store = NULL;
166
167    store = storecreate(store);
168    if (store == NULL) {
169	if (retval != NULL)
170	    *retval = 1;
171	return NULL;
172    }
173
174    if (retval != NULL)
175	*retval = 0;
176
177    installed = matchinstalled(MATCH_ALL, NULL, retval);
178    if (installed == NULL)
179	return NULL;
180
181    for (i = 0; installed[i] != NULL; i++) {
182	FILE *fp;
183	char *cp, tmp[PATH_MAX];
184	int cmd;
185
186	snprintf(tmp, PATH_MAX, "%s/%s", LOG_DIR, installed[i]);
187	/*
188	 * SPECIAL CASE: ignore empty dirs, since we can can see them
189	 * during port installation.
190	 */
191	if (isemptydir(tmp))
192	    continue;
193	snprintf(tmp, PATH_MAX, "%s/%s", tmp, CONTENTS_FNAME);
194	fp = fopen(tmp, "r");
195	if (fp == NULL) {
196	    warn("%s", tmp);
197	    if (retval != NULL)
198		*retval = 1;
199	    return NULL;
200	}
201
202	cmd = -1;
203	while (fgets(tmp, sizeof(tmp), fp)) {
204	    int len = strlen(tmp);
205
206	    while (len && isspace(tmp[len - 1]))
207		tmp[--len] = '\0';
208	    if (!len)
209		continue;
210	    cp = tmp;
211	    if (tmp[0] != CMD_CHAR)
212		continue;
213	    cmd = plist_cmd(tmp + 1, &cp);
214	    if (cmd == PLIST_ORIGIN) {
215		if (strncmp(origin, cp, strlen(origin)) == 0)
216		    storeappend(store, installed[i]);
217		break;
218	    }
219	}
220	if (cmd != PLIST_ORIGIN)
221	    warnx("package %s has no origin recorded", installed[i]);
222	fclose(fp);
223    }
224
225    if (store->used == 0)
226	return NULL;
227    else
228	return store->store;
229}
230
231/*
232 * Return TRUE if the specified package is installed,
233 * or FALSE otherwise.
234 */
235int
236isinstalledpkg(const char *name)
237{
238    char buf[FILENAME_MAX];
239
240    snprintf(buf, sizeof(buf), "%s/%s", LOG_DIR, name);
241    if (!isdir(buf) || access(buf, R_OK) == FAIL)
242	return FALSE;
243
244    snprintf(buf, sizeof(buf), "%s/%s", buf, CONTENTS_FNAME);
245    if (!isfile(buf) || access(buf, R_OK) == FAIL)
246	return FALSE;
247
248    return TRUE;
249}
250
251/*
252 * Returns 1 if specified pkgname matches RE pattern.
253 * Otherwise returns 0 if doesn't match or -1 if RE
254 * engine reported an error (usually invalid syntax).
255 */
256static int
257rex_match(const char *pattern, const char *pkgname)
258{
259    char errbuf[128];
260    int errcode;
261    int retval;
262    regex_t rex;
263
264    retval = 0;
265
266    errcode = regcomp(&rex, pattern, REG_BASIC | REG_NOSUB);
267    if (errcode == 0)
268	errcode = regexec(&rex, pkgname, 0, NULL, 0);
269
270    if (errcode == 0) {
271	retval = 1;
272    } else if (errcode != REG_NOMATCH) {
273	regerror(errcode, &rex, errbuf, sizeof(errbuf));
274	warnx("%s: %s", pattern, errbuf);
275	retval = -1;
276    }
277
278    regfree(&rex);
279
280    return retval;
281}
282
283/*
284 * Create an empty store, optionally deallocating
285 * any previously allocated space if store != NULL.
286 */
287struct store *
288storecreate(struct store *store)
289{
290    int i;
291
292    if (store == NULL) {
293	store = malloc(sizeof *store);
294	if (store == NULL) {
295	    warnx("%s(): malloc() failed", __func__);
296	    return NULL;
297	}
298	store->currlen = 0;
299	store->store = NULL;
300    } else if (store->store != NULL) {
301	    /* Free previously allocated memory */
302	    for (i = 0; store->store[i] != NULL; i++)
303		free(store->store[i]);
304	    store->store[0] = NULL;
305    }
306    store->used = 0;
307
308    return store;
309}
310
311/*
312 * Append specified element to the provided store.
313 */
314static int
315storeappend(struct store *store, const char *item)
316{
317    if (store->used + 2 > store->currlen) {
318	store->currlen += 16;
319	store->store = reallocf(store->store,
320				store->currlen * sizeof(*(store->store)));
321	if (store->store == NULL) {
322	    store->currlen = 0;
323	    warnx("%s(): reallocf() failed", __func__);
324	    return 1;
325	}
326    }
327
328    asprintf(&(store->store[store->used]), "%s", item);
329    if (store->store[store->used] == NULL) {
330	warnx("%s(): malloc() failed", __func__);
331	return 1;
332    }
333    store->used++;
334    store->store[store->used] = NULL;
335
336    return 0;
337}
338
339static int
340fname_cmp(const FTSENT * const *a, const FTSENT * const *b)
341{
342    return strcmp((*a)->fts_name, (*b)->fts_name);
343}
344