1*0590ec0aSjoerg /*	$NetBSD: iterate.c,v 1.1.1.3 2009/08/06 16:55:27 joerg 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 /*
48af21abb5Sjoerg  * Generic iteration function:
49af21abb5Sjoerg  * - get new entries from srciter, stop on NULL
50af21abb5Sjoerg  * - call matchiter for those entries, stop on non-null return value.
51af21abb5Sjoerg  */
52af21abb5Sjoerg int
53af21abb5Sjoerg iterate_pkg_generic_src(int (*matchiter)(const char *, void *),
54af21abb5Sjoerg     void *match_cookie, const char *(*srciter)(void *), void *src_cookie)
55af21abb5Sjoerg {
56af21abb5Sjoerg 	int retval;
57af21abb5Sjoerg 	const char *entry;
58af21abb5Sjoerg 
59af21abb5Sjoerg 	retval = 0;
60af21abb5Sjoerg 
61af21abb5Sjoerg 	while ((entry = (*srciter)(src_cookie)) != NULL) {
62af21abb5Sjoerg 		if ((retval = (*matchiter)(entry, match_cookie)) != 0)
63af21abb5Sjoerg 			break;
64af21abb5Sjoerg 	}
65af21abb5Sjoerg 
66af21abb5Sjoerg 	return retval;
67af21abb5Sjoerg }
68af21abb5Sjoerg 
69af21abb5Sjoerg struct pkg_dir_iter_arg {
70af21abb5Sjoerg 	DIR *dirp;
71af21abb5Sjoerg 	int filter_suffix;
72af21abb5Sjoerg 	int allow_nonfiles;
73af21abb5Sjoerg };
74af21abb5Sjoerg 
75af21abb5Sjoerg static const char *
76af21abb5Sjoerg pkg_dir_iter(void *cookie)
77af21abb5Sjoerg {
78af21abb5Sjoerg 	struct pkg_dir_iter_arg *arg = cookie;
79af21abb5Sjoerg 	struct dirent *dp;
80af21abb5Sjoerg 	size_t len;
81af21abb5Sjoerg 
82af21abb5Sjoerg 	while ((dp = readdir(arg->dirp)) != NULL) {
83af21abb5Sjoerg #if defined(DT_UNKNOWN) && defined(DT_DIR)
84af21abb5Sjoerg 		if (arg->allow_nonfiles == 0 &&
85af21abb5Sjoerg 		    dp->d_type != DT_UNKNOWN && dp->d_type != DT_REG)
86af21abb5Sjoerg 			continue;
87af21abb5Sjoerg #endif
88af21abb5Sjoerg 		len = strlen(dp->d_name);
89af21abb5Sjoerg 		/* .tbz or .tgz suffix length + some prefix*/
90af21abb5Sjoerg 		if (len < 5)
91af21abb5Sjoerg 			continue;
92af21abb5Sjoerg 		if (arg->filter_suffix == 0 ||
93af21abb5Sjoerg 		    memcmp(dp->d_name + len - 4, ".tgz", 4) == 0 ||
94af21abb5Sjoerg 		    memcmp(dp->d_name + len - 4, ".tbz", 4) == 0)
95af21abb5Sjoerg 			return dp->d_name;
96af21abb5Sjoerg 	}
97af21abb5Sjoerg 	return NULL;
98af21abb5Sjoerg }
99af21abb5Sjoerg 
100af21abb5Sjoerg /*
101af21abb5Sjoerg  * Call matchiter for every package in the directory.
102af21abb5Sjoerg  */
103af21abb5Sjoerg int
104af21abb5Sjoerg iterate_local_pkg_dir(const char *dir, int filter_suffix, int allow_nonfiles,
105af21abb5Sjoerg     int (*matchiter)(const char *, void *), void *cookie)
106af21abb5Sjoerg {
107af21abb5Sjoerg 	struct pkg_dir_iter_arg arg;
108af21abb5Sjoerg 	int retval;
109af21abb5Sjoerg 
110af21abb5Sjoerg 	if ((arg.dirp = opendir(dir)) == NULL)
111af21abb5Sjoerg 		return -1;
112af21abb5Sjoerg 
113af21abb5Sjoerg 	arg.filter_suffix = filter_suffix;
114af21abb5Sjoerg 	arg.allow_nonfiles = allow_nonfiles;
115af21abb5Sjoerg 	retval = iterate_pkg_generic_src(matchiter, cookie, pkg_dir_iter, &arg);
116af21abb5Sjoerg 
117af21abb5Sjoerg 	if (closedir(arg.dirp) == -1)
118af21abb5Sjoerg 		return -1;
119af21abb5Sjoerg 	return retval;
120af21abb5Sjoerg }
121af21abb5Sjoerg 
122af21abb5Sjoerg static const char *
123af21abb5Sjoerg pkg_db_iter(void *cookie)
124af21abb5Sjoerg {
125af21abb5Sjoerg 	DIR *dirp = cookie;
126af21abb5Sjoerg 	struct dirent *dp;
127af21abb5Sjoerg 
128af21abb5Sjoerg 	while ((dp = readdir(dirp)) != NULL) {
129af21abb5Sjoerg 		if (strcmp(dp->d_name, ".") == 0)
130af21abb5Sjoerg 			continue;
131af21abb5Sjoerg 		if (strcmp(dp->d_name, "..") == 0)
132af21abb5Sjoerg 			continue;
133af21abb5Sjoerg 		if (strcmp(dp->d_name, "pkgdb.byfile.db") == 0)
134af21abb5Sjoerg 			continue;
135af21abb5Sjoerg 		if (strcmp(dp->d_name, ".cookie") == 0)
136af21abb5Sjoerg 			continue;
137af21abb5Sjoerg 		if (strcmp(dp->d_name, "pkg-vulnerabilities") == 0)
138af21abb5Sjoerg 			continue;
139af21abb5Sjoerg #if defined(DT_UNKNOWN) && defined(DT_DIR)
140af21abb5Sjoerg 		if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_DIR)
141af21abb5Sjoerg 			continue;
142af21abb5Sjoerg #endif
143af21abb5Sjoerg 		return dp->d_name;
144af21abb5Sjoerg 	}
145af21abb5Sjoerg 	return NULL;
146af21abb5Sjoerg }
147af21abb5Sjoerg 
148af21abb5Sjoerg /*
149af21abb5Sjoerg  * Call matchiter for every installed package.
150af21abb5Sjoerg  */
151af21abb5Sjoerg int
152af21abb5Sjoerg iterate_pkg_db(int (*matchiter)(const char *, void *), void *cookie)
153af21abb5Sjoerg {
154af21abb5Sjoerg 	DIR *dirp;
155af21abb5Sjoerg 	int retval;
156af21abb5Sjoerg 
157af21abb5Sjoerg 	if ((dirp = opendir(_pkgdb_getPKGDB_DIR())) == NULL) {
158af21abb5Sjoerg 		if (errno == ENOENT)
159af21abb5Sjoerg 			return 0; /* No pkgdb directory == empty pkgdb */
160af21abb5Sjoerg 		return -1;
161af21abb5Sjoerg 	}
162af21abb5Sjoerg 
163af21abb5Sjoerg 	retval = iterate_pkg_generic_src(matchiter, cookie, pkg_db_iter, dirp);
164af21abb5Sjoerg 
165af21abb5Sjoerg 	if (closedir(dirp) == -1)
166af21abb5Sjoerg 		return -1;
167af21abb5Sjoerg 	return retval;
168af21abb5Sjoerg }
169af21abb5Sjoerg 
170af21abb5Sjoerg static int
171af21abb5Sjoerg match_by_basename(const char *pkg, void *cookie)
172af21abb5Sjoerg {
173af21abb5Sjoerg 	const char *target = cookie;
174af21abb5Sjoerg 	const char *pkg_version;
175af21abb5Sjoerg 
176af21abb5Sjoerg 	if ((pkg_version = strrchr(pkg, '-')) == NULL) {
177af21abb5Sjoerg 		warnx("Entry %s in pkgdb is not a valid package name", pkg);
178af21abb5Sjoerg 		return 0;
179af21abb5Sjoerg 	}
180af21abb5Sjoerg 	if (strncmp(pkg, target, pkg_version - pkg) == 0 &&
181*0590ec0aSjoerg 	    pkg + strlen(target) == pkg_version)
182af21abb5Sjoerg 		return 1;
183af21abb5Sjoerg 	else
184af21abb5Sjoerg 		return 0;
185af21abb5Sjoerg }
186af21abb5Sjoerg 
187af21abb5Sjoerg static int
188af21abb5Sjoerg match_by_pattern(const char *pkg, void *cookie)
189af21abb5Sjoerg {
190af21abb5Sjoerg 	const char *pattern = cookie;
191af21abb5Sjoerg 
192af21abb5Sjoerg 	return pkg_match(pattern, pkg);
193af21abb5Sjoerg }
194af21abb5Sjoerg 
195af21abb5Sjoerg struct add_matching_arg {
196af21abb5Sjoerg 	lpkg_head_t *pkghead;
197*0590ec0aSjoerg 	int got_match;
198af21abb5Sjoerg 	int (*match_fn)(const char *pkg, void *cookie);
199af21abb5Sjoerg 	void *cookie;
200af21abb5Sjoerg };
201af21abb5Sjoerg 
202af21abb5Sjoerg static int
203af21abb5Sjoerg match_and_add(const char *pkg, void *cookie)
204af21abb5Sjoerg {
205af21abb5Sjoerg 	struct add_matching_arg *arg = cookie;
206af21abb5Sjoerg 	lpkg_t *lpp;
207af21abb5Sjoerg 
208af21abb5Sjoerg 	if ((*arg->match_fn)(pkg, arg->cookie) == 1) {
209af21abb5Sjoerg 		arg->got_match = 1;
210af21abb5Sjoerg 
211af21abb5Sjoerg 		lpp = alloc_lpkg(pkg);
212af21abb5Sjoerg 		TAILQ_INSERT_TAIL(arg->pkghead, lpp, lp_link);
213af21abb5Sjoerg 	}
214af21abb5Sjoerg 	return 0;
215af21abb5Sjoerg }
216af21abb5Sjoerg 
217af21abb5Sjoerg /*
218af21abb5Sjoerg  * Find all installed packages with the given basename and add them
219af21abb5Sjoerg  * to pkghead.
220af21abb5Sjoerg  * Returns -1 on error, 0 if no match was found and 1 otherwise.
221af21abb5Sjoerg  */
222af21abb5Sjoerg int
223af21abb5Sjoerg add_installed_pkgs_by_basename(const char *pkgbase, lpkg_head_t *pkghead)
224af21abb5Sjoerg {
225af21abb5Sjoerg 	struct add_matching_arg arg;
226af21abb5Sjoerg 
227af21abb5Sjoerg 	arg.pkghead = pkghead;
228af21abb5Sjoerg 	arg.got_match = 0;
229af21abb5Sjoerg 	arg.match_fn = match_by_basename;
230af21abb5Sjoerg 	arg.cookie = __UNCONST(pkgbase);
231af21abb5Sjoerg 
232af21abb5Sjoerg 	if (iterate_pkg_db(match_and_add, &arg) == -1) {
233af21abb5Sjoerg 		warnx("could not process pkgdb");
234af21abb5Sjoerg 		return -1;
235af21abb5Sjoerg 	}
236af21abb5Sjoerg 	return arg.got_match;
237af21abb5Sjoerg }
238af21abb5Sjoerg 
239af21abb5Sjoerg /*
240af21abb5Sjoerg  * Match all installed packages against pattern, add the matches to pkghead.
241af21abb5Sjoerg  * Returns -1 on error, 0 if no match was found and 1 otherwise.
242af21abb5Sjoerg  */
243af21abb5Sjoerg int
244af21abb5Sjoerg add_installed_pkgs_by_pattern(const char *pattern, lpkg_head_t *pkghead)
245af21abb5Sjoerg {
246af21abb5Sjoerg 	struct add_matching_arg arg;
247af21abb5Sjoerg 
248af21abb5Sjoerg 	arg.pkghead = pkghead;
249af21abb5Sjoerg 	arg.got_match = 0;
250af21abb5Sjoerg 	arg.match_fn = match_by_pattern;
251af21abb5Sjoerg 	arg.cookie = __UNCONST(pattern);
252af21abb5Sjoerg 
253af21abb5Sjoerg 	if (iterate_pkg_db(match_and_add, &arg) == -1) {
254af21abb5Sjoerg 		warnx("could not process pkgdb");
255af21abb5Sjoerg 		return -1;
256af21abb5Sjoerg 	}
257af21abb5Sjoerg 	return arg.got_match;
258af21abb5Sjoerg }
259af21abb5Sjoerg 
260af21abb5Sjoerg struct best_installed_match_arg {
261af21abb5Sjoerg 	const char *pattern;
262af21abb5Sjoerg 	char *best_current_match;
263af21abb5Sjoerg };
264af21abb5Sjoerg 
265af21abb5Sjoerg static int
266af21abb5Sjoerg match_best_installed(const char *pkg, void *cookie)
267af21abb5Sjoerg {
268af21abb5Sjoerg 	struct best_installed_match_arg *arg = cookie;
269af21abb5Sjoerg 
270af21abb5Sjoerg 	switch (pkg_order(arg->pattern, pkg, arg->best_current_match)) {
271af21abb5Sjoerg 	case 0:
272af21abb5Sjoerg 	case 2:
273af21abb5Sjoerg 		/*
274af21abb5Sjoerg 		 * Either current package doesn't match or
275af21abb5Sjoerg 		 * the older match is better. Nothing to do.
276af21abb5Sjoerg 		 */
277af21abb5Sjoerg 		break;
278af21abb5Sjoerg 	case 1:
279af21abb5Sjoerg 		/* Current package is better, remember it. */
280af21abb5Sjoerg 		free(arg->best_current_match);
281d66ee6c3Sjoerg 		arg->best_current_match = xstrdup(pkg);
282af21abb5Sjoerg 		break;
283af21abb5Sjoerg 	}
284af21abb5Sjoerg 	return 0;
285af21abb5Sjoerg }
286af21abb5Sjoerg 
287af21abb5Sjoerg /*
288af21abb5Sjoerg  * Returns a copy of the name of best matching package.
289af21abb5Sjoerg  * If no package matched the pattern or an error occured, return NULL.
290af21abb5Sjoerg  */
291af21abb5Sjoerg char *
292af21abb5Sjoerg find_best_matching_installed_pkg(const char *pattern)
293af21abb5Sjoerg {
294af21abb5Sjoerg 	struct best_installed_match_arg arg;
295af21abb5Sjoerg 
296af21abb5Sjoerg 	arg.pattern = pattern;
297af21abb5Sjoerg 	arg.best_current_match = NULL;
298af21abb5Sjoerg 
299af21abb5Sjoerg 	if (iterate_pkg_db(match_best_installed, &arg) == -1) {
300af21abb5Sjoerg 		warnx("could not process pkgdb");
301af21abb5Sjoerg 		return NULL;
302af21abb5Sjoerg 	}
303af21abb5Sjoerg 
304af21abb5Sjoerg 	return arg.best_current_match;
305af21abb5Sjoerg }
306af21abb5Sjoerg 
307af21abb5Sjoerg struct call_matching_arg {
308af21abb5Sjoerg 	const char *pattern;
309af21abb5Sjoerg 	int (*call_fn)(const char *pkg, void *cookie);
310af21abb5Sjoerg 	void *cookie;
311af21abb5Sjoerg };
312af21abb5Sjoerg 
313af21abb5Sjoerg static int
314af21abb5Sjoerg match_and_call(const char *pkg, void *cookie)
315af21abb5Sjoerg {
316af21abb5Sjoerg 	struct call_matching_arg *arg = cookie;
317af21abb5Sjoerg 
318af21abb5Sjoerg 	if (pkg_match(arg->pattern, pkg) == 1) {
319af21abb5Sjoerg 		return (*arg->call_fn)(pkg, arg->cookie);
320af21abb5Sjoerg 	} else
321af21abb5Sjoerg 		return 0;
322af21abb5Sjoerg }
323af21abb5Sjoerg 
324af21abb5Sjoerg /*
325af21abb5Sjoerg  * Find all packages that match the given pattern and call the function
326af21abb5Sjoerg  * for each of them. Iteration stops if the callback return non-0.
327af21abb5Sjoerg  * Returns -1 on error, 0 if the iteration finished or whatever the
328af21abb5Sjoerg  * callback returned otherwise.
329af21abb5Sjoerg  */
330af21abb5Sjoerg int
331af21abb5Sjoerg match_installed_pkgs(const char *pattern, int (*cb)(const char *, void *),
332af21abb5Sjoerg     void *cookie)
333af21abb5Sjoerg {
334af21abb5Sjoerg 	struct call_matching_arg arg;
335af21abb5Sjoerg 
336af21abb5Sjoerg 	arg.pattern = pattern;
337af21abb5Sjoerg 	arg.call_fn = cb;
338af21abb5Sjoerg 	arg.cookie = cookie;
339af21abb5Sjoerg 
340af21abb5Sjoerg 	return iterate_pkg_db(match_and_call, &arg);
341af21abb5Sjoerg }
342af21abb5Sjoerg 
343af21abb5Sjoerg struct best_file_match_arg {
344af21abb5Sjoerg 	const char *pattern;
345af21abb5Sjoerg 	char *best_current_match_filtered;
346af21abb5Sjoerg 	char *best_current_match;
347af21abb5Sjoerg 	int filter_suffix;
348af21abb5Sjoerg };
349af21abb5Sjoerg 
350af21abb5Sjoerg static int
351af21abb5Sjoerg match_best_file(const char *filename, void *cookie)
352af21abb5Sjoerg {
353af21abb5Sjoerg 	struct best_file_match_arg *arg = cookie;
354af21abb5Sjoerg 	const char *active_filename;
355af21abb5Sjoerg 	char *filtered_filename;
356af21abb5Sjoerg 
357af21abb5Sjoerg 	if (arg->filter_suffix) {
358af21abb5Sjoerg 		size_t len;
359af21abb5Sjoerg 
360af21abb5Sjoerg 		len = strlen(filename);
361af21abb5Sjoerg 		if (len < 5 ||
362af21abb5Sjoerg 		    (memcmp(filename + len - 4, ".tgz", 4) != 0 &&
363af21abb5Sjoerg 		     memcmp(filename + len - 4, ".tbz", 4) != 0)) {
364af21abb5Sjoerg 			warnx("filename %s does not contain a recognized suffix", filename);
365af21abb5Sjoerg 			return -1;
366af21abb5Sjoerg 		}
367d66ee6c3Sjoerg 		filtered_filename = xmalloc(len - 4 + 1);
368af21abb5Sjoerg 		memcpy(filtered_filename, filename, len - 4);
369af21abb5Sjoerg 		filtered_filename[len - 4] = '\0';
370af21abb5Sjoerg 		active_filename = filtered_filename;
371af21abb5Sjoerg 	} else {
372af21abb5Sjoerg 		filtered_filename = NULL;
373af21abb5Sjoerg 		active_filename = filename;
374af21abb5Sjoerg 	}
375af21abb5Sjoerg 
376af21abb5Sjoerg 	switch (pkg_order(arg->pattern, active_filename, arg->best_current_match_filtered)) {
377af21abb5Sjoerg 	case 0:
378af21abb5Sjoerg 	case 2:
379af21abb5Sjoerg 		/*
380af21abb5Sjoerg 		 * Either current package doesn't match or
381af21abb5Sjoerg 		 * the older match is better. Nothing to do.
382af21abb5Sjoerg 		 */
383af21abb5Sjoerg 		free(filtered_filename);
384af21abb5Sjoerg 		return 0;
385af21abb5Sjoerg 	case 1:
386af21abb5Sjoerg 		/* Current package is better, remember it. */
387af21abb5Sjoerg 		free(arg->best_current_match);
388af21abb5Sjoerg 		free(arg->best_current_match_filtered);
389d66ee6c3Sjoerg 		arg->best_current_match = xstrdup(filename);
390af21abb5Sjoerg 		if (filtered_filename != NULL)
391af21abb5Sjoerg 			arg->best_current_match_filtered = filtered_filename;
392d66ee6c3Sjoerg 		else
393d66ee6c3Sjoerg 			arg->best_current_match_filtered = xstrdup(active_filename);
394af21abb5Sjoerg 		return 0;
395af21abb5Sjoerg 	default:
396af21abb5Sjoerg 		errx(EXIT_FAILURE, "Invalid error from pkg_order");
397*0590ec0aSjoerg 		/* NOTREACHED */
398af21abb5Sjoerg 	}
399af21abb5Sjoerg }
400af21abb5Sjoerg 
401af21abb5Sjoerg /*
402af21abb5Sjoerg  * Returns a copy of the name of best matching file.
403af21abb5Sjoerg  * If no package matched the pattern or an error occured, return NULL.
404af21abb5Sjoerg  */
405af21abb5Sjoerg char *
406af21abb5Sjoerg find_best_matching_file(const char *dir, const char *pattern, int filter_suffix, int allow_nonfiles)
407af21abb5Sjoerg {
408af21abb5Sjoerg 	struct best_file_match_arg arg;
409af21abb5Sjoerg 
410af21abb5Sjoerg 	arg.filter_suffix = filter_suffix;
411af21abb5Sjoerg 	arg.pattern = pattern;
412af21abb5Sjoerg 	arg.best_current_match = NULL;
413af21abb5Sjoerg 	arg.best_current_match_filtered = NULL;
414af21abb5Sjoerg 
415af21abb5Sjoerg 	if (iterate_local_pkg_dir(dir, filter_suffix, allow_nonfiles, match_best_file, &arg) == -1) {
416af21abb5Sjoerg 		warnx("could not process directory");
417af21abb5Sjoerg 		return NULL;
418af21abb5Sjoerg 	}
419af21abb5Sjoerg 	free(arg.best_current_match_filtered);
420af21abb5Sjoerg 
421af21abb5Sjoerg 	return arg.best_current_match;
422af21abb5Sjoerg }
423af21abb5Sjoerg 
424af21abb5Sjoerg struct call_matching_file_arg {
425af21abb5Sjoerg 	const char *pattern;
426af21abb5Sjoerg 	int (*call_fn)(const char *pkg, void *cookie);
427af21abb5Sjoerg 	void *cookie;
428af21abb5Sjoerg 	int filter_suffix;
429af21abb5Sjoerg };
430af21abb5Sjoerg 
431af21abb5Sjoerg static int
432af21abb5Sjoerg match_file_and_call(const char *filename, void *cookie)
433af21abb5Sjoerg {
434af21abb5Sjoerg 	struct call_matching_file_arg *arg = cookie;
435af21abb5Sjoerg 	const char *active_filename;
436af21abb5Sjoerg 	char *filtered_filename;
437af21abb5Sjoerg 	int ret;
438af21abb5Sjoerg 
439af21abb5Sjoerg 	if (arg->filter_suffix) {
440af21abb5Sjoerg 		size_t len;
441af21abb5Sjoerg 
442af21abb5Sjoerg 		len = strlen(filename);
443af21abb5Sjoerg 		if (len < 5 ||
444af21abb5Sjoerg 		    (memcmp(filename + len - 4, ".tgz", 4) != 0 &&
445af21abb5Sjoerg 		     memcmp(filename + len - 4, ".tbz", 4) != 0)) {
446af21abb5Sjoerg 			warnx("filename %s does not contain a recognized suffix", filename);
447af21abb5Sjoerg 			return -1;
448af21abb5Sjoerg 		}
449d66ee6c3Sjoerg 		filtered_filename = xmalloc(len - 4 + 1);
450af21abb5Sjoerg 		memcpy(filtered_filename, filename, len - 4);
451af21abb5Sjoerg 		filtered_filename[len - 4] = '\0';
452af21abb5Sjoerg 		active_filename = filtered_filename;
453af21abb5Sjoerg 	} else {
454af21abb5Sjoerg 		filtered_filename = NULL;
455af21abb5Sjoerg 		active_filename = filename;
456af21abb5Sjoerg 	}
457af21abb5Sjoerg 
458af21abb5Sjoerg 	ret = pkg_match(arg->pattern, active_filename);
459af21abb5Sjoerg 	free(filtered_filename);
460af21abb5Sjoerg 
461af21abb5Sjoerg 	if (ret == 1)
462af21abb5Sjoerg 		return (*arg->call_fn)(filename, arg->cookie);
463af21abb5Sjoerg 	else
464af21abb5Sjoerg 		return 0;
465af21abb5Sjoerg }
466af21abb5Sjoerg 
467af21abb5Sjoerg /*
468af21abb5Sjoerg  * Find all packages that match the given pattern and call the function
469af21abb5Sjoerg  * for each of them. Iteration stops if the callback return non-0.
470af21abb5Sjoerg  * Returns -1 on error, 0 if the iteration finished or whatever the
471af21abb5Sjoerg  * callback returned otherwise.
472af21abb5Sjoerg  */
473af21abb5Sjoerg int
474af21abb5Sjoerg match_local_files(const char *dir, int filter_suffix, int allow_nonfiles, const char *pattern,
475af21abb5Sjoerg     int (*cb)(const char *, void *), void *cookie)
476af21abb5Sjoerg {
477af21abb5Sjoerg 	struct call_matching_file_arg arg;
478af21abb5Sjoerg 
479af21abb5Sjoerg 	arg.pattern = pattern;
480af21abb5Sjoerg 	arg.call_fn = cb;
481af21abb5Sjoerg 	arg.cookie = cookie;
482af21abb5Sjoerg 	arg.filter_suffix = filter_suffix;
483af21abb5Sjoerg 
484af21abb5Sjoerg 	return iterate_local_pkg_dir(dir, filter_suffix, allow_nonfiles, match_file_and_call, &arg);
485af21abb5Sjoerg }
486