xref: /netbsd-src/external/bsd/pkg_install/dist/lib/iterate.c (revision f46918ca2125b9b1e7ca5a22c07d1414c618e467)
1*f46918caSnia /*	$NetBSD: iterate.c,v 1.4 2021/04/10 19:49:59 nia Exp $	*/
2d66ee6c3Sjoerg 
3af21abb5Sjoerg /*-
4af21abb5Sjoerg  * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
5af21abb5Sjoerg  * All rights reserved.
6af21abb5Sjoerg  *
7af21abb5Sjoerg  * Redistribution and use in source and binary forms, with or without
8af21abb5Sjoerg  * modification, are permitted provided that the following conditions
9af21abb5Sjoerg  * are met:
10af21abb5Sjoerg  *
11af21abb5Sjoerg  * 1. Redistributions of source code must retain the above copyright
12af21abb5Sjoerg  *    notice, this list of conditions and the following disclaimer.
13af21abb5Sjoerg  * 2. Redistributions in binary form must reproduce the above copyright
14af21abb5Sjoerg  *    notice, this list of conditions and the following disclaimer in
15af21abb5Sjoerg  *    the documentation and/or other materials provided with the
16af21abb5Sjoerg  *    distribution.
17af21abb5Sjoerg  *
18af21abb5Sjoerg  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19af21abb5Sjoerg  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20af21abb5Sjoerg  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21af21abb5Sjoerg  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22af21abb5Sjoerg  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23af21abb5Sjoerg  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24af21abb5Sjoerg  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25af21abb5Sjoerg  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26af21abb5Sjoerg  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27af21abb5Sjoerg  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28af21abb5Sjoerg  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29af21abb5Sjoerg  * SUCH DAMAGE.
30af21abb5Sjoerg  */
31af21abb5Sjoerg 
32af21abb5Sjoerg #if HAVE_CONFIG_H
33af21abb5Sjoerg #include "config.h"
34af21abb5Sjoerg #endif
35af21abb5Sjoerg 
36af21abb5Sjoerg #include <nbcompat.h>
37af21abb5Sjoerg 
38af21abb5Sjoerg #if HAVE_ERR_H
39af21abb5Sjoerg #include <err.h>
40af21abb5Sjoerg #endif
41af21abb5Sjoerg #if HAVE_ERRNO_H
42af21abb5Sjoerg #include <errno.h>
43af21abb5Sjoerg #endif
44af21abb5Sjoerg 
45af21abb5Sjoerg #include "lib.h"
46af21abb5Sjoerg 
47af21abb5Sjoerg /*
48c4260505Swiz  * We define a couple of different caches to hold frequently accessed data.
49c4260505Swiz  *
50c4260505Swiz  * Firstly, we cache the results of readdir() on the package database directory
51c4260505Swiz  * when using iterate_pkg_db_cached().  This helps a lot during recursive calls
52c4260505Swiz  * and avoids exponential system calls, but is not suitable for situations
53c4260505Swiz  * where the database directory may be updated, for example during installs.
54c4260505Swiz  * In those situations the regular iterate_pkg_db() must be used.
55c4260505Swiz  *
56c4260505Swiz  * Secondly, we have a cache for matches of pattern lookups, avoiding expensive
57c4260505Swiz  * pkg_match() calls each time.
58c4260505Swiz  */
59c4260505Swiz struct pkg_db_list {
60c4260505Swiz 	char *pkgname;
61c4260505Swiz 	SLIST_ENTRY(pkg_db_list) entries;
62c4260505Swiz };
63c4260505Swiz SLIST_HEAD(pkg_db_list_head, pkg_db_list);
64c4260505Swiz 
65c4260505Swiz struct pkg_match_list {
66c4260505Swiz 	char *pattern;
67c4260505Swiz 	char *pkgname;
68c4260505Swiz 	SLIST_ENTRY(pkg_match_list) entries;
69c4260505Swiz };
70c4260505Swiz SLIST_HEAD(pkg_match_list_head, pkg_match_list);
71c4260505Swiz 
72c4260505Swiz static struct pkg_db_list_head pkg_list_cache;
73c4260505Swiz static struct pkg_match_list_head pkg_match_cache[PKG_HASH_SIZE];
74c4260505Swiz 
75c4260505Swiz /*
76af21abb5Sjoerg  * Generic iteration function:
77af21abb5Sjoerg  * - get new entries from srciter, stop on NULL
78af21abb5Sjoerg  * - call matchiter for those entries, stop on non-null return value.
79af21abb5Sjoerg  */
80af21abb5Sjoerg int
iterate_pkg_generic_src(int (* matchiter)(const char *,void *),void * match_cookie,const char * (* srciter)(void *),void * src_cookie)81af21abb5Sjoerg iterate_pkg_generic_src(int (*matchiter)(const char *, void *),
82af21abb5Sjoerg     void *match_cookie, const char *(*srciter)(void *), void *src_cookie)
83af21abb5Sjoerg {
84af21abb5Sjoerg 	int retval;
85af21abb5Sjoerg 	const char *entry;
86af21abb5Sjoerg 
87af21abb5Sjoerg 	retval = 0;
88af21abb5Sjoerg 
89af21abb5Sjoerg 	while ((entry = (*srciter)(src_cookie)) != NULL) {
90af21abb5Sjoerg 		if ((retval = (*matchiter)(entry, match_cookie)) != 0)
91af21abb5Sjoerg 			break;
92af21abb5Sjoerg 	}
93af21abb5Sjoerg 
94af21abb5Sjoerg 	return retval;
95af21abb5Sjoerg }
96af21abb5Sjoerg 
97af21abb5Sjoerg struct pkg_dir_iter_arg {
98af21abb5Sjoerg 	DIR *dirp;
99af21abb5Sjoerg 	int filter_suffix;
100af21abb5Sjoerg 	int allow_nonfiles;
101af21abb5Sjoerg };
102af21abb5Sjoerg 
103af21abb5Sjoerg static const char *
pkg_dir_iter(void * cookie)104af21abb5Sjoerg pkg_dir_iter(void *cookie)
105af21abb5Sjoerg {
106af21abb5Sjoerg 	struct pkg_dir_iter_arg *arg = cookie;
107af21abb5Sjoerg 	struct dirent *dp;
108af21abb5Sjoerg 	size_t len;
109af21abb5Sjoerg 
110af21abb5Sjoerg 	while ((dp = readdir(arg->dirp)) != NULL) {
111af21abb5Sjoerg #if defined(DT_UNKNOWN) && defined(DT_DIR)
112af21abb5Sjoerg 		if (arg->allow_nonfiles == 0 &&
113af21abb5Sjoerg 		    dp->d_type != DT_UNKNOWN && dp->d_type != DT_REG)
114af21abb5Sjoerg 			continue;
115af21abb5Sjoerg #endif
116af21abb5Sjoerg 		len = strlen(dp->d_name);
117af21abb5Sjoerg 		/* .tbz or .tgz suffix length + some prefix*/
118af21abb5Sjoerg 		if (len < 5)
119af21abb5Sjoerg 			continue;
120af21abb5Sjoerg 		if (arg->filter_suffix == 0 ||
121af21abb5Sjoerg 		    memcmp(dp->d_name + len - 4, ".tgz", 4) == 0 ||
122af21abb5Sjoerg 		    memcmp(dp->d_name + len - 4, ".tbz", 4) == 0)
123af21abb5Sjoerg 			return dp->d_name;
124af21abb5Sjoerg 	}
125af21abb5Sjoerg 	return NULL;
126af21abb5Sjoerg }
127af21abb5Sjoerg 
128af21abb5Sjoerg /*
129af21abb5Sjoerg  * Call matchiter for every package in the directory.
130af21abb5Sjoerg  */
131af21abb5Sjoerg int
iterate_local_pkg_dir(const char * dir,int filter_suffix,int allow_nonfiles,int (* matchiter)(const char *,void *),void * cookie)132af21abb5Sjoerg iterate_local_pkg_dir(const char *dir, int filter_suffix, int allow_nonfiles,
133af21abb5Sjoerg     int (*matchiter)(const char *, void *), void *cookie)
134af21abb5Sjoerg {
135af21abb5Sjoerg 	struct pkg_dir_iter_arg arg;
136af21abb5Sjoerg 	int retval;
137af21abb5Sjoerg 
138af21abb5Sjoerg 	if ((arg.dirp = opendir(dir)) == NULL)
139af21abb5Sjoerg 		return -1;
140af21abb5Sjoerg 
141af21abb5Sjoerg 	arg.filter_suffix = filter_suffix;
142af21abb5Sjoerg 	arg.allow_nonfiles = allow_nonfiles;
143af21abb5Sjoerg 	retval = iterate_pkg_generic_src(matchiter, cookie, pkg_dir_iter, &arg);
144af21abb5Sjoerg 
145af21abb5Sjoerg 	if (closedir(arg.dirp) == -1)
146af21abb5Sjoerg 		return -1;
147af21abb5Sjoerg 	return retval;
148af21abb5Sjoerg }
149af21abb5Sjoerg 
150af21abb5Sjoerg static const char *
pkg_db_iter(void * cookie)151af21abb5Sjoerg pkg_db_iter(void *cookie)
152af21abb5Sjoerg {
153af21abb5Sjoerg 	DIR *dirp = cookie;
154af21abb5Sjoerg 	struct dirent *dp;
155af21abb5Sjoerg 
156af21abb5Sjoerg 	while ((dp = readdir(dirp)) != NULL) {
157af21abb5Sjoerg 		if (strcmp(dp->d_name, ".") == 0)
158af21abb5Sjoerg 			continue;
159af21abb5Sjoerg 		if (strcmp(dp->d_name, "..") == 0)
160af21abb5Sjoerg 			continue;
161af21abb5Sjoerg 		if (strcmp(dp->d_name, "pkgdb.byfile.db") == 0)
162af21abb5Sjoerg 			continue;
163af21abb5Sjoerg 		if (strcmp(dp->d_name, ".cookie") == 0)
164af21abb5Sjoerg 			continue;
165af21abb5Sjoerg 		if (strcmp(dp->d_name, "pkg-vulnerabilities") == 0)
166af21abb5Sjoerg 			continue;
167af21abb5Sjoerg #if defined(DT_UNKNOWN) && defined(DT_DIR)
168af21abb5Sjoerg 		if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_DIR)
169af21abb5Sjoerg 			continue;
170af21abb5Sjoerg #endif
171af21abb5Sjoerg 		return dp->d_name;
172af21abb5Sjoerg 	}
173af21abb5Sjoerg 	return NULL;
174af21abb5Sjoerg }
175af21abb5Sjoerg 
176af21abb5Sjoerg /*
177af21abb5Sjoerg  * Call matchiter for every installed package.
178af21abb5Sjoerg  */
179af21abb5Sjoerg int
iterate_pkg_db(int (* matchiter)(const char *,void *),void * cookie)180af21abb5Sjoerg iterate_pkg_db(int (*matchiter)(const char *, void *), void *cookie)
181af21abb5Sjoerg {
182af21abb5Sjoerg 	DIR *dirp;
183af21abb5Sjoerg 	int retval;
184af21abb5Sjoerg 
1855ac0fc9cSjoerg 	if ((dirp = opendir(pkgdb_get_dir())) == NULL) {
186af21abb5Sjoerg 		if (errno == ENOENT)
187af21abb5Sjoerg 			return 0; /* No pkgdb directory == empty pkgdb */
188af21abb5Sjoerg 		return -1;
189af21abb5Sjoerg 	}
190af21abb5Sjoerg 
191af21abb5Sjoerg 	retval = iterate_pkg_generic_src(matchiter, cookie, pkg_db_iter, dirp);
192af21abb5Sjoerg 
193af21abb5Sjoerg 	if (closedir(dirp) == -1)
194af21abb5Sjoerg 		return -1;
195af21abb5Sjoerg 	return retval;
196af21abb5Sjoerg }
197af21abb5Sjoerg 
198c4260505Swiz struct pkg_db_iter_arg {
199c4260505Swiz 	struct pkg_db_list_head head;
200c4260505Swiz 	struct pkg_db_list *list;
201c4260505Swiz };
202c4260505Swiz 
203c4260505Swiz static const char *
pkg_db_iter_cached(void * cookie)204c4260505Swiz pkg_db_iter_cached(void *cookie)
205c4260505Swiz {
206c4260505Swiz 	struct pkg_db_iter_arg *arg = cookie;
207c4260505Swiz 
208c4260505Swiz 	if (arg->list == NULL)
209c4260505Swiz 		arg->list = SLIST_FIRST(&arg->head);
210c4260505Swiz 	else
211c4260505Swiz 		arg->list = SLIST_NEXT(arg->list, entries);
212c4260505Swiz 
213c4260505Swiz 	if (arg->list != NULL)
214c4260505Swiz 		return arg->list->pkgname;
215c4260505Swiz 
216c4260505Swiz 	return NULL;
217c4260505Swiz }
218c4260505Swiz 
219c4260505Swiz /*
220c4260505Swiz  * Call matchiter for every installed package, using cached data to
221c4260505Swiz  * significantly increase performance during recursive calls.
222c4260505Swiz  *
223c4260505Swiz  * This is not suitable for every situation, for example when finding new
224c4260505Swiz  * matches after package installation/removal.  In those situations the
225c4260505Swiz  * regular iterate_pkg_db() must be used.
226c4260505Swiz  */
227c4260505Swiz static int
iterate_pkg_db_cached(int (* matchiter)(const char *,void *),void * cookie)228c4260505Swiz iterate_pkg_db_cached(int (*matchiter)(const char *, void *), void *cookie)
229c4260505Swiz {
230c4260505Swiz 	DIR *dirp;
231c4260505Swiz 	struct pkg_db_iter_arg arg;
232c4260505Swiz 	struct pkg_db_list *pkg;
233c4260505Swiz 	const char *pkgdir;
234c4260505Swiz 	int retval;
235c4260505Swiz 
236c4260505Swiz 	if (SLIST_EMPTY(&pkg_list_cache)) {
237c4260505Swiz 		SLIST_INIT(&pkg_list_cache);
238c4260505Swiz 
239c4260505Swiz 		if ((dirp = opendir(pkgdb_get_dir())) == NULL) {
240c4260505Swiz 			if (errno == ENOENT)
241c4260505Swiz 				return 0; /* Empty pkgdb */
242c4260505Swiz 			return -1;
243c4260505Swiz 		}
244c4260505Swiz 
245c4260505Swiz 		while ((pkgdir = pkg_db_iter(dirp)) != NULL) {
246c4260505Swiz 			pkg = xmalloc(sizeof(struct pkg_db_list));
247c4260505Swiz 			pkg->pkgname = xstrdup(pkgdir);
248c4260505Swiz 			SLIST_INSERT_HEAD(&pkg_list_cache, pkg, entries);
249c4260505Swiz 		}
250c4260505Swiz 
251c4260505Swiz 		if (closedir(dirp) == -1)
252c4260505Swiz 			return -1;
253c4260505Swiz 	}
254c4260505Swiz 
255c4260505Swiz 	arg.head = pkg_list_cache;
256c4260505Swiz 	arg.list = NULL;
257c4260505Swiz 
258c4260505Swiz 	retval = iterate_pkg_generic_src(matchiter, cookie,
259c4260505Swiz 	    pkg_db_iter_cached, &arg);
260c4260505Swiz 
261c4260505Swiz 	return retval;
262c4260505Swiz }
263c4260505Swiz 
264af21abb5Sjoerg static int
match_by_basename(const char * pkg,void * cookie)265af21abb5Sjoerg match_by_basename(const char *pkg, void *cookie)
266af21abb5Sjoerg {
267af21abb5Sjoerg 	const char *target = cookie;
268af21abb5Sjoerg 	const char *pkg_version;
269af21abb5Sjoerg 
270af21abb5Sjoerg 	if ((pkg_version = strrchr(pkg, '-')) == NULL) {
271af21abb5Sjoerg 		warnx("Entry %s in pkgdb is not a valid package name", pkg);
272af21abb5Sjoerg 		return 0;
273af21abb5Sjoerg 	}
274af21abb5Sjoerg 	if (strncmp(pkg, target, pkg_version - pkg) == 0 &&
2750590ec0aSjoerg 	    pkg + strlen(target) == pkg_version)
276af21abb5Sjoerg 		return 1;
277af21abb5Sjoerg 	else
278af21abb5Sjoerg 		return 0;
279af21abb5Sjoerg }
280af21abb5Sjoerg 
281af21abb5Sjoerg static int
match_by_pattern(const char * pkg,void * cookie)282af21abb5Sjoerg match_by_pattern(const char *pkg, void *cookie)
283af21abb5Sjoerg {
284af21abb5Sjoerg 	const char *pattern = cookie;
285af21abb5Sjoerg 
286af21abb5Sjoerg 	return pkg_match(pattern, pkg);
287af21abb5Sjoerg }
288af21abb5Sjoerg 
289af21abb5Sjoerg struct add_matching_arg {
290af21abb5Sjoerg 	lpkg_head_t *pkghead;
2910590ec0aSjoerg 	int got_match;
292af21abb5Sjoerg 	int (*match_fn)(const char *pkg, void *cookie);
293af21abb5Sjoerg 	void *cookie;
294af21abb5Sjoerg };
295af21abb5Sjoerg 
296af21abb5Sjoerg static int
match_and_add(const char * pkg,void * cookie)297af21abb5Sjoerg match_and_add(const char *pkg, void *cookie)
298af21abb5Sjoerg {
299af21abb5Sjoerg 	struct add_matching_arg *arg = cookie;
300af21abb5Sjoerg 	lpkg_t *lpp;
301af21abb5Sjoerg 
302af21abb5Sjoerg 	if ((*arg->match_fn)(pkg, arg->cookie) == 1) {
303af21abb5Sjoerg 		arg->got_match = 1;
304af21abb5Sjoerg 
305af21abb5Sjoerg 		lpp = alloc_lpkg(pkg);
306af21abb5Sjoerg 		TAILQ_INSERT_TAIL(arg->pkghead, lpp, lp_link);
307af21abb5Sjoerg 	}
308af21abb5Sjoerg 	return 0;
309af21abb5Sjoerg }
310af21abb5Sjoerg 
311af21abb5Sjoerg /*
312af21abb5Sjoerg  * Find all installed packages with the given basename and add them
313af21abb5Sjoerg  * to pkghead.
314af21abb5Sjoerg  * Returns -1 on error, 0 if no match was found and 1 otherwise.
315af21abb5Sjoerg  */
316af21abb5Sjoerg int
add_installed_pkgs_by_basename(const char * pkgbase,lpkg_head_t * pkghead)317af21abb5Sjoerg add_installed_pkgs_by_basename(const char *pkgbase, lpkg_head_t *pkghead)
318af21abb5Sjoerg {
319af21abb5Sjoerg 	struct add_matching_arg arg;
320af21abb5Sjoerg 
321af21abb5Sjoerg 	arg.pkghead = pkghead;
322af21abb5Sjoerg 	arg.got_match = 0;
323af21abb5Sjoerg 	arg.match_fn = match_by_basename;
324af21abb5Sjoerg 	arg.cookie = __UNCONST(pkgbase);
325af21abb5Sjoerg 
326af21abb5Sjoerg 	if (iterate_pkg_db(match_and_add, &arg) == -1) {
327af21abb5Sjoerg 		warnx("could not process pkgdb");
328af21abb5Sjoerg 		return -1;
329af21abb5Sjoerg 	}
330af21abb5Sjoerg 	return arg.got_match;
331af21abb5Sjoerg }
332af21abb5Sjoerg 
333af21abb5Sjoerg /*
334af21abb5Sjoerg  * Match all installed packages against pattern, add the matches to pkghead.
335af21abb5Sjoerg  * Returns -1 on error, 0 if no match was found and 1 otherwise.
336af21abb5Sjoerg  */
337af21abb5Sjoerg int
add_installed_pkgs_by_pattern(const char * pattern,lpkg_head_t * pkghead)338af21abb5Sjoerg add_installed_pkgs_by_pattern(const char *pattern, lpkg_head_t *pkghead)
339af21abb5Sjoerg {
340af21abb5Sjoerg 	struct add_matching_arg arg;
341af21abb5Sjoerg 
342af21abb5Sjoerg 	arg.pkghead = pkghead;
343af21abb5Sjoerg 	arg.got_match = 0;
344af21abb5Sjoerg 	arg.match_fn = match_by_pattern;
345af21abb5Sjoerg 	arg.cookie = __UNCONST(pattern);
346af21abb5Sjoerg 
347af21abb5Sjoerg 	if (iterate_pkg_db(match_and_add, &arg) == -1) {
348af21abb5Sjoerg 		warnx("could not process pkgdb");
349af21abb5Sjoerg 		return -1;
350af21abb5Sjoerg 	}
351af21abb5Sjoerg 	return arg.got_match;
352af21abb5Sjoerg }
353af21abb5Sjoerg 
354af21abb5Sjoerg struct best_installed_match_arg {
355af21abb5Sjoerg 	const char *pattern;
356af21abb5Sjoerg 	char *best_current_match;
357af21abb5Sjoerg };
358af21abb5Sjoerg 
359af21abb5Sjoerg static int
match_best_installed(const char * pkg,void * cookie)360af21abb5Sjoerg match_best_installed(const char *pkg, void *cookie)
361af21abb5Sjoerg {
362af21abb5Sjoerg 	struct best_installed_match_arg *arg = cookie;
363af21abb5Sjoerg 
364af21abb5Sjoerg 	switch (pkg_order(arg->pattern, pkg, arg->best_current_match)) {
365af21abb5Sjoerg 	case 0:
366af21abb5Sjoerg 	case 2:
367af21abb5Sjoerg 		/*
368af21abb5Sjoerg 		 * Either current package doesn't match or
369af21abb5Sjoerg 		 * the older match is better. Nothing to do.
370af21abb5Sjoerg 		 */
371af21abb5Sjoerg 		break;
372af21abb5Sjoerg 	case 1:
373af21abb5Sjoerg 		/* Current package is better, remember it. */
374af21abb5Sjoerg 		free(arg->best_current_match);
375d66ee6c3Sjoerg 		arg->best_current_match = xstrdup(pkg);
376af21abb5Sjoerg 		break;
377af21abb5Sjoerg 	}
378af21abb5Sjoerg 	return 0;
379af21abb5Sjoerg }
380af21abb5Sjoerg 
381af21abb5Sjoerg /*
382af21abb5Sjoerg  * Returns a copy of the name of best matching package.
383af21abb5Sjoerg  * If no package matched the pattern or an error occured, return NULL.
384c4260505Swiz  *
385c4260505Swiz  * If use_cached is set, return a cached match entry if it exists, and also use
386c4260505Swiz  * the iterate_pkg_db cache, otherwise clear any matching cache entry and use
387c4260505Swiz  * regular iterate_pkg_db().
388af21abb5Sjoerg  */
389af21abb5Sjoerg char *
find_best_matching_installed_pkg(const char * pattern,int use_cached)390c4260505Swiz find_best_matching_installed_pkg(const char *pattern, int use_cached)
391af21abb5Sjoerg {
392af21abb5Sjoerg 	struct best_installed_match_arg arg;
393c4260505Swiz 	struct pkg_match_list *pkg;
394c4260505Swiz 	int idx = PKG_HASH_ENTRY(pattern), rv;
395c4260505Swiz 
396c4260505Swiz 	if (pattern == NULL)
397c4260505Swiz 		return NULL;
398c4260505Swiz 
399c4260505Swiz 	SLIST_FOREACH(pkg, &pkg_match_cache[idx], entries) {
400c4260505Swiz 		if (strcmp(pattern, pkg->pattern) == 0) {
401c4260505Swiz 			if (use_cached)
402c4260505Swiz 				return xstrdup(pkg->pkgname);
403c4260505Swiz 			SLIST_REMOVE(&pkg_match_cache[idx], pkg,
404c4260505Swiz 			    pkg_match_list, entries);
405c4260505Swiz 			free(pkg->pattern);
406c4260505Swiz 			free(pkg->pkgname);
407c4260505Swiz 			free(pkg);
408c4260505Swiz 			break;
409c4260505Swiz 		}
410c4260505Swiz 	}
411af21abb5Sjoerg 
412af21abb5Sjoerg 	arg.pattern = pattern;
413af21abb5Sjoerg 	arg.best_current_match = NULL;
414af21abb5Sjoerg 
415c4260505Swiz 	if (use_cached)
416c4260505Swiz 		rv = iterate_pkg_db_cached(match_best_installed, &arg);
417c4260505Swiz 	else
418c4260505Swiz 		rv = iterate_pkg_db(match_best_installed, &arg);
419c4260505Swiz 
420c4260505Swiz 	if (rv == -1) {
421af21abb5Sjoerg 		warnx("could not process pkgdb");
422af21abb5Sjoerg 		return NULL;
423af21abb5Sjoerg 	}
424af21abb5Sjoerg 
425c4260505Swiz 	if (arg.best_current_match != NULL) {
426c4260505Swiz 		pkg = xmalloc(sizeof(struct pkg_match_list));
427c4260505Swiz 		pkg->pattern = xstrdup(pattern);
428c4260505Swiz 		pkg->pkgname = xstrdup(arg.best_current_match);
429c4260505Swiz 		SLIST_INSERT_HEAD(&pkg_match_cache[idx],
430c4260505Swiz 		    pkg, entries);
431c4260505Swiz 	}
432c4260505Swiz 
433af21abb5Sjoerg 	return arg.best_current_match;
434af21abb5Sjoerg }
435af21abb5Sjoerg 
436af21abb5Sjoerg struct call_matching_arg {
437af21abb5Sjoerg 	const char *pattern;
438af21abb5Sjoerg 	int (*call_fn)(const char *pkg, void *cookie);
439af21abb5Sjoerg 	void *cookie;
440af21abb5Sjoerg };
441af21abb5Sjoerg 
442af21abb5Sjoerg static int
match_and_call(const char * pkg,void * cookie)443af21abb5Sjoerg match_and_call(const char *pkg, void *cookie)
444af21abb5Sjoerg {
445af21abb5Sjoerg 	struct call_matching_arg *arg = cookie;
446af21abb5Sjoerg 
447af21abb5Sjoerg 	if (pkg_match(arg->pattern, pkg) == 1) {
448af21abb5Sjoerg 		return (*arg->call_fn)(pkg, arg->cookie);
449af21abb5Sjoerg 	} else
450af21abb5Sjoerg 		return 0;
451af21abb5Sjoerg }
452af21abb5Sjoerg 
453af21abb5Sjoerg /*
454af21abb5Sjoerg  * Find all packages that match the given pattern and call the function
455af21abb5Sjoerg  * for each of them. Iteration stops if the callback return non-0.
456af21abb5Sjoerg  * Returns -1 on error, 0 if the iteration finished or whatever the
457af21abb5Sjoerg  * callback returned otherwise.
458af21abb5Sjoerg  */
459af21abb5Sjoerg int
match_installed_pkgs(const char * pattern,int (* cb)(const char *,void *),void * cookie)460af21abb5Sjoerg match_installed_pkgs(const char *pattern, int (*cb)(const char *, void *),
461af21abb5Sjoerg     void *cookie)
462af21abb5Sjoerg {
463af21abb5Sjoerg 	struct call_matching_arg arg;
464af21abb5Sjoerg 
465af21abb5Sjoerg 	arg.pattern = pattern;
466af21abb5Sjoerg 	arg.call_fn = cb;
467af21abb5Sjoerg 	arg.cookie = cookie;
468af21abb5Sjoerg 
469af21abb5Sjoerg 	return iterate_pkg_db(match_and_call, &arg);
470af21abb5Sjoerg }
471af21abb5Sjoerg 
472af21abb5Sjoerg struct best_file_match_arg {
473af21abb5Sjoerg 	const char *pattern;
474af21abb5Sjoerg 	char *best_current_match_filtered;
475af21abb5Sjoerg 	char *best_current_match;
476af21abb5Sjoerg 	int filter_suffix;
477af21abb5Sjoerg };
478af21abb5Sjoerg 
479af21abb5Sjoerg static int
match_best_file(const char * filename,void * cookie)480af21abb5Sjoerg match_best_file(const char *filename, void *cookie)
481af21abb5Sjoerg {
482af21abb5Sjoerg 	struct best_file_match_arg *arg = cookie;
483af21abb5Sjoerg 	const char *active_filename;
484af21abb5Sjoerg 	char *filtered_filename;
485af21abb5Sjoerg 
486af21abb5Sjoerg 	if (arg->filter_suffix) {
487af21abb5Sjoerg 		size_t len;
488af21abb5Sjoerg 
489af21abb5Sjoerg 		len = strlen(filename);
490af21abb5Sjoerg 		if (len < 5 ||
491af21abb5Sjoerg 		    (memcmp(filename + len - 4, ".tgz", 4) != 0 &&
492af21abb5Sjoerg 		     memcmp(filename + len - 4, ".tbz", 4) != 0)) {
493af21abb5Sjoerg 			warnx("filename %s does not contain a recognized suffix", filename);
494af21abb5Sjoerg 			return -1;
495af21abb5Sjoerg 		}
496d66ee6c3Sjoerg 		filtered_filename = xmalloc(len - 4 + 1);
497af21abb5Sjoerg 		memcpy(filtered_filename, filename, len - 4);
498af21abb5Sjoerg 		filtered_filename[len - 4] = '\0';
499af21abb5Sjoerg 		active_filename = filtered_filename;
500af21abb5Sjoerg 	} else {
501af21abb5Sjoerg 		filtered_filename = NULL;
502af21abb5Sjoerg 		active_filename = filename;
503af21abb5Sjoerg 	}
504af21abb5Sjoerg 
505af21abb5Sjoerg 	switch (pkg_order(arg->pattern, active_filename, arg->best_current_match_filtered)) {
506af21abb5Sjoerg 	case 0:
507af21abb5Sjoerg 	case 2:
508af21abb5Sjoerg 		/*
509af21abb5Sjoerg 		 * Either current package doesn't match or
510af21abb5Sjoerg 		 * the older match is better. Nothing to do.
511af21abb5Sjoerg 		 */
512af21abb5Sjoerg 		free(filtered_filename);
513af21abb5Sjoerg 		return 0;
514af21abb5Sjoerg 	case 1:
515af21abb5Sjoerg 		/* Current package is better, remember it. */
516af21abb5Sjoerg 		free(arg->best_current_match);
517af21abb5Sjoerg 		free(arg->best_current_match_filtered);
518d66ee6c3Sjoerg 		arg->best_current_match = xstrdup(filename);
519af21abb5Sjoerg 		if (filtered_filename != NULL)
520af21abb5Sjoerg 			arg->best_current_match_filtered = filtered_filename;
521d66ee6c3Sjoerg 		else
522d66ee6c3Sjoerg 			arg->best_current_match_filtered = xstrdup(active_filename);
523af21abb5Sjoerg 		return 0;
524af21abb5Sjoerg 	default:
525af21abb5Sjoerg 		errx(EXIT_FAILURE, "Invalid error from pkg_order");
5260590ec0aSjoerg 		/* NOTREACHED */
527af21abb5Sjoerg 	}
528af21abb5Sjoerg }
529af21abb5Sjoerg 
530af21abb5Sjoerg /*
531af21abb5Sjoerg  * Returns a copy of the name of best matching file.
532af21abb5Sjoerg  * If no package matched the pattern or an error occured, return NULL.
533af21abb5Sjoerg  */
534af21abb5Sjoerg char *
find_best_matching_file(const char * dir,const char * pattern,int filter_suffix,int allow_nonfiles)535af21abb5Sjoerg find_best_matching_file(const char *dir, const char *pattern, int filter_suffix, int allow_nonfiles)
536af21abb5Sjoerg {
537af21abb5Sjoerg 	struct best_file_match_arg arg;
538af21abb5Sjoerg 
539af21abb5Sjoerg 	arg.filter_suffix = filter_suffix;
540af21abb5Sjoerg 	arg.pattern = pattern;
541af21abb5Sjoerg 	arg.best_current_match = NULL;
542af21abb5Sjoerg 	arg.best_current_match_filtered = NULL;
543af21abb5Sjoerg 
544af21abb5Sjoerg 	if (iterate_local_pkg_dir(dir, filter_suffix, allow_nonfiles, match_best_file, &arg) == -1) {
545af21abb5Sjoerg 		warnx("could not process directory");
546af21abb5Sjoerg 		return NULL;
547af21abb5Sjoerg 	}
548af21abb5Sjoerg 	free(arg.best_current_match_filtered);
549af21abb5Sjoerg 
550af21abb5Sjoerg 	return arg.best_current_match;
551af21abb5Sjoerg }
552af21abb5Sjoerg 
553af21abb5Sjoerg struct call_matching_file_arg {
554af21abb5Sjoerg 	const char *pattern;
555af21abb5Sjoerg 	int (*call_fn)(const char *pkg, void *cookie);
556af21abb5Sjoerg 	void *cookie;
557af21abb5Sjoerg 	int filter_suffix;
558af21abb5Sjoerg };
559af21abb5Sjoerg 
560af21abb5Sjoerg static int
match_file_and_call(const char * filename,void * cookie)561af21abb5Sjoerg match_file_and_call(const char *filename, void *cookie)
562af21abb5Sjoerg {
563af21abb5Sjoerg 	struct call_matching_file_arg *arg = cookie;
564af21abb5Sjoerg 	const char *active_filename;
565af21abb5Sjoerg 	char *filtered_filename;
566af21abb5Sjoerg 	int ret;
567af21abb5Sjoerg 
568af21abb5Sjoerg 	if (arg->filter_suffix) {
569af21abb5Sjoerg 		size_t len;
570af21abb5Sjoerg 
571af21abb5Sjoerg 		len = strlen(filename);
572af21abb5Sjoerg 		if (len < 5 ||
573af21abb5Sjoerg 		    (memcmp(filename + len - 4, ".tgz", 4) != 0 &&
574af21abb5Sjoerg 		     memcmp(filename + len - 4, ".tbz", 4) != 0)) {
575af21abb5Sjoerg 			warnx("filename %s does not contain a recognized suffix", filename);
576af21abb5Sjoerg 			return -1;
577af21abb5Sjoerg 		}
578d66ee6c3Sjoerg 		filtered_filename = xmalloc(len - 4 + 1);
579af21abb5Sjoerg 		memcpy(filtered_filename, filename, len - 4);
580af21abb5Sjoerg 		filtered_filename[len - 4] = '\0';
581af21abb5Sjoerg 		active_filename = filtered_filename;
582af21abb5Sjoerg 	} else {
583af21abb5Sjoerg 		filtered_filename = NULL;
584af21abb5Sjoerg 		active_filename = filename;
585af21abb5Sjoerg 	}
586af21abb5Sjoerg 
587af21abb5Sjoerg 	ret = pkg_match(arg->pattern, active_filename);
588af21abb5Sjoerg 	free(filtered_filename);
589af21abb5Sjoerg 
590af21abb5Sjoerg 	if (ret == 1)
591af21abb5Sjoerg 		return (*arg->call_fn)(filename, arg->cookie);
592af21abb5Sjoerg 	else
593af21abb5Sjoerg 		return 0;
594af21abb5Sjoerg }
595af21abb5Sjoerg 
596af21abb5Sjoerg /*
597af21abb5Sjoerg  * Find all packages that match the given pattern and call the function
598af21abb5Sjoerg  * for each of them. Iteration stops if the callback return non-0.
599af21abb5Sjoerg  * Returns -1 on error, 0 if the iteration finished or whatever the
600af21abb5Sjoerg  * callback returned otherwise.
601af21abb5Sjoerg  */
602af21abb5Sjoerg int
match_local_files(const char * dir,int filter_suffix,int allow_nonfiles,const char * pattern,int (* cb)(const char *,void *),void * cookie)603af21abb5Sjoerg match_local_files(const char *dir, int filter_suffix, int allow_nonfiles, const char *pattern,
604af21abb5Sjoerg     int (*cb)(const char *, void *), void *cookie)
605af21abb5Sjoerg {
606af21abb5Sjoerg 	struct call_matching_file_arg arg;
607af21abb5Sjoerg 
608af21abb5Sjoerg 	arg.pattern = pattern;
609af21abb5Sjoerg 	arg.call_fn = cb;
610af21abb5Sjoerg 	arg.cookie = cookie;
611af21abb5Sjoerg 	arg.filter_suffix = filter_suffix;
612af21abb5Sjoerg 
613af21abb5Sjoerg 	return iterate_local_pkg_dir(dir, filter_suffix, allow_nonfiles, match_file_and_call, &arg);
614af21abb5Sjoerg }
615