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