1*a24fe59cSnia /*	$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 /*
482117e12eSwiz  * We define a couple of different caches to hold frequently accessed data.
492117e12eSwiz  *
502117e12eSwiz  * Firstly, we cache the results of readdir() on the package database directory
512117e12eSwiz  * when using iterate_pkg_db_cached().  This helps a lot during recursive calls
522117e12eSwiz  * and avoids exponential system calls, but is not suitable for situations
532117e12eSwiz  * where the database directory may be updated, for example during installs.
542117e12eSwiz  * In those situations the regular iterate_pkg_db() must be used.
552117e12eSwiz  *
562117e12eSwiz  * Secondly, we have a cache for matches of pattern lookups, avoiding expensive
572117e12eSwiz  * pkg_match() calls each time.
582117e12eSwiz  */
592117e12eSwiz struct pkg_db_list {
602117e12eSwiz 	char *pkgname;
612117e12eSwiz 	SLIST_ENTRY(pkg_db_list) entries;
622117e12eSwiz };
632117e12eSwiz SLIST_HEAD(pkg_db_list_head, pkg_db_list);
642117e12eSwiz 
652117e12eSwiz struct pkg_match_list {
662117e12eSwiz 	char *pattern;
672117e12eSwiz 	char *pkgname;
682117e12eSwiz 	SLIST_ENTRY(pkg_match_list) entries;
692117e12eSwiz };
702117e12eSwiz SLIST_HEAD(pkg_match_list_head, pkg_match_list);
712117e12eSwiz 
722117e12eSwiz static struct pkg_db_list_head pkg_list_cache;
732117e12eSwiz static struct pkg_match_list_head pkg_match_cache[PKG_HASH_SIZE];
742117e12eSwiz 
752117e12eSwiz /*
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 
1982117e12eSwiz struct pkg_db_iter_arg {
1992117e12eSwiz 	struct pkg_db_list_head head;
2002117e12eSwiz 	struct pkg_db_list *list;
2012117e12eSwiz };
2022117e12eSwiz 
2032117e12eSwiz static const char *
pkg_db_iter_cached(void * cookie)2042117e12eSwiz pkg_db_iter_cached(void *cookie)
2052117e12eSwiz {
2062117e12eSwiz 	struct pkg_db_iter_arg *arg = cookie;
2072117e12eSwiz 
2082117e12eSwiz 	if (arg->list == NULL)
2092117e12eSwiz 		arg->list = SLIST_FIRST(&arg->head);
2102117e12eSwiz 	else
2112117e12eSwiz 		arg->list = SLIST_NEXT(arg->list, entries);
2122117e12eSwiz 
2132117e12eSwiz 	if (arg->list != NULL)
2142117e12eSwiz 		return arg->list->pkgname;
2152117e12eSwiz 
2162117e12eSwiz 	return NULL;
2172117e12eSwiz }
2182117e12eSwiz 
2192117e12eSwiz /*
2202117e12eSwiz  * Call matchiter for every installed package, using cached data to
2212117e12eSwiz  * significantly increase performance during recursive calls.
2222117e12eSwiz  *
2232117e12eSwiz  * This is not suitable for every situation, for example when finding new
2242117e12eSwiz  * matches after package installation/removal.  In those situations the
2252117e12eSwiz  * regular iterate_pkg_db() must be used.
2262117e12eSwiz  */
2272117e12eSwiz static int
iterate_pkg_db_cached(int (* matchiter)(const char *,void *),void * cookie)2282117e12eSwiz iterate_pkg_db_cached(int (*matchiter)(const char *, void *), void *cookie)
2292117e12eSwiz {
2302117e12eSwiz 	DIR *dirp;
2312117e12eSwiz 	struct pkg_db_iter_arg arg;
2322117e12eSwiz 	struct pkg_db_list *pkg;
2332117e12eSwiz 	const char *pkgdir;
2342117e12eSwiz 	int retval;
2352117e12eSwiz 
2362117e12eSwiz 	if (SLIST_EMPTY(&pkg_list_cache)) {
2372117e12eSwiz 		SLIST_INIT(&pkg_list_cache);
2382117e12eSwiz 
2392117e12eSwiz 		if ((dirp = opendir(pkgdb_get_dir())) == NULL) {
2402117e12eSwiz 			if (errno == ENOENT)
2412117e12eSwiz 				return 0; /* Empty pkgdb */
2422117e12eSwiz 			return -1;
2432117e12eSwiz 		}
2442117e12eSwiz 
2452117e12eSwiz 		while ((pkgdir = pkg_db_iter(dirp)) != NULL) {
2462117e12eSwiz 			pkg = xmalloc(sizeof(struct pkg_db_list));
2472117e12eSwiz 			pkg->pkgname = xstrdup(pkgdir);
2482117e12eSwiz 			SLIST_INSERT_HEAD(&pkg_list_cache, pkg, entries);
2492117e12eSwiz 		}
2502117e12eSwiz 
2512117e12eSwiz 		if (closedir(dirp) == -1)
2522117e12eSwiz 			return -1;
2532117e12eSwiz 	}
2542117e12eSwiz 
2552117e12eSwiz 	arg.head = pkg_list_cache;
2562117e12eSwiz 	arg.list = NULL;
2572117e12eSwiz 
2582117e12eSwiz 	retval = iterate_pkg_generic_src(matchiter, cookie,
2592117e12eSwiz 	    pkg_db_iter_cached, &arg);
2602117e12eSwiz 
2612117e12eSwiz 	return retval;
2622117e12eSwiz }
2632117e12eSwiz 
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.
3842117e12eSwiz  *
3852117e12eSwiz  * If use_cached is set, return a cached match entry if it exists, and also use
3862117e12eSwiz  * the iterate_pkg_db cache, otherwise clear any matching cache entry and use
3872117e12eSwiz  * regular iterate_pkg_db().
388af21abb5Sjoerg  */
389af21abb5Sjoerg char *
find_best_matching_installed_pkg(const char * pattern,int use_cached)3902117e12eSwiz find_best_matching_installed_pkg(const char *pattern, int use_cached)
391af21abb5Sjoerg {
392af21abb5Sjoerg 	struct best_installed_match_arg arg;
3932117e12eSwiz 	struct pkg_match_list *pkg;
3942117e12eSwiz 	int idx = PKG_HASH_ENTRY(pattern), rv;
3952117e12eSwiz 
3962117e12eSwiz 	if (pattern == NULL)
3972117e12eSwiz 		return NULL;
3982117e12eSwiz 
3992117e12eSwiz 	SLIST_FOREACH(pkg, &pkg_match_cache[idx], entries) {
4002117e12eSwiz 		if (strcmp(pattern, pkg->pattern) == 0) {
4012117e12eSwiz 			if (use_cached)
4022117e12eSwiz 				return xstrdup(pkg->pkgname);
4032117e12eSwiz 			SLIST_REMOVE(&pkg_match_cache[idx], pkg,
4042117e12eSwiz 			    pkg_match_list, entries);
4052117e12eSwiz 			free(pkg->pattern);
4062117e12eSwiz 			free(pkg->pkgname);
4072117e12eSwiz 			free(pkg);
4082117e12eSwiz 			break;
4092117e12eSwiz 		}
4102117e12eSwiz 	}
411af21abb5Sjoerg 
412af21abb5Sjoerg 	arg.pattern = pattern;
413af21abb5Sjoerg 	arg.best_current_match = NULL;
414af21abb5Sjoerg 
4152117e12eSwiz 	if (use_cached)
4162117e12eSwiz 		rv = iterate_pkg_db_cached(match_best_installed, &arg);
4172117e12eSwiz 	else
4182117e12eSwiz 		rv = iterate_pkg_db(match_best_installed, &arg);
4192117e12eSwiz 
4202117e12eSwiz 	if (rv == -1) {
421af21abb5Sjoerg 		warnx("could not process pkgdb");
422af21abb5Sjoerg 		return NULL;
423af21abb5Sjoerg 	}
424af21abb5Sjoerg 
4252117e12eSwiz 	if (arg.best_current_match != NULL) {
4262117e12eSwiz 		pkg = xmalloc(sizeof(struct pkg_match_list));
4272117e12eSwiz 		pkg->pattern = xstrdup(pattern);
4282117e12eSwiz 		pkg->pkgname = xstrdup(arg.best_current_match);
4292117e12eSwiz 		SLIST_INSERT_HEAD(&pkg_match_cache[idx],
4302117e12eSwiz 		    pkg, entries);
4312117e12eSwiz 	}
4322117e12eSwiz 
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