1 /* $NetBSD: iterate.c,v 1.7 2009/08/02 17:56:45 joerg Exp $ */
4 * Copyright (c) 2007 Joerg Sonnenberger <joerg@NetBSD.org>.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * Generic iteration function:
49 * - get new entries from srciter, stop on NULL
50 * - call matchiter for those entries, stop on non-null return value.
53 iterate_pkg_generic_src(int (*matchiter
)(const char *, void *),
54 void *match_cookie
, const char *(*srciter
)(void *), void *src_cookie
)
61 while ((entry
= (*srciter
)(src_cookie
)) != NULL
) {
62 if ((retval
= (*matchiter
)(entry
, match_cookie
)) != 0)
69 struct pkg_dir_iter_arg
{
76 pkg_dir_iter(void *cookie
)
78 struct pkg_dir_iter_arg
*arg
= cookie
;
82 while ((dp
= readdir(arg
->dirp
)) != NULL
) {
83 #if defined(DT_UNKNOWN) && defined(DT_DIR)
84 if (arg
->allow_nonfiles
== 0 &&
85 dp
->d_type
!= DT_UNKNOWN
&& dp
->d_type
!= DT_REG
)
88 len
= strlen(dp
->d_name
);
89 /* .tbz or .tgz suffix length + some prefix*/
92 if (arg
->filter_suffix
== 0 ||
93 memcmp(dp
->d_name
+ len
- 4, ".tgz", 4) == 0 ||
94 memcmp(dp
->d_name
+ len
- 4, ".tbz", 4) == 0)
101 * Call matchiter for every package in the directory.
104 iterate_local_pkg_dir(const char *dir
, int filter_suffix
, int allow_nonfiles
,
105 int (*matchiter
)(const char *, void *), void *cookie
)
107 struct pkg_dir_iter_arg arg
;
110 if ((arg
.dirp
= opendir(dir
)) == NULL
)
113 arg
.filter_suffix
= filter_suffix
;
114 arg
.allow_nonfiles
= allow_nonfiles
;
115 retval
= iterate_pkg_generic_src(matchiter
, cookie
, pkg_dir_iter
, &arg
);
117 if (closedir(arg
.dirp
) == -1)
123 pkg_db_iter(void *cookie
)
128 while ((dp
= readdir(dirp
)) != NULL
) {
129 if (strcmp(dp
->d_name
, ".") == 0)
131 if (strcmp(dp
->d_name
, "..") == 0)
133 if (strcmp(dp
->d_name
, "pkgdb.byfile.db") == 0)
135 if (strcmp(dp
->d_name
, ".cookie") == 0)
137 if (strcmp(dp
->d_name
, "pkg-vulnerabilities") == 0)
139 #if defined(DT_UNKNOWN) && defined(DT_DIR)
140 if (dp
->d_type
!= DT_UNKNOWN
&& dp
->d_type
!= DT_DIR
)
149 * Call matchiter for every installed package.
152 iterate_pkg_db(int (*matchiter
)(const char *, void *), void *cookie
)
157 if ((dirp
= opendir(_pkgdb_getPKGDB_DIR())) == NULL
) {
159 return 0; /* No pkgdb directory == empty pkgdb */
163 retval
= iterate_pkg_generic_src(matchiter
, cookie
, pkg_db_iter
, dirp
);
165 if (closedir(dirp
) == -1)
171 match_by_basename(const char *pkg
, void *cookie
)
173 const char *target
= cookie
;
174 const char *pkg_version
;
176 if ((pkg_version
= strrchr(pkg
, '-')) == NULL
) {
177 warnx("Entry %s in pkgdb is not a valid package name", pkg
);
180 if (strncmp(pkg
, target
, pkg_version
- pkg
) == 0 &&
181 pkg
+ strlen(target
) == pkg_version
)
188 match_by_pattern(const char *pkg
, void *cookie
)
190 const char *pattern
= cookie
;
192 return pkg_match(pattern
, pkg
);
195 struct add_matching_arg
{
196 lpkg_head_t
*pkghead
;
198 int (*match_fn
)(const char *pkg
, void *cookie
);
203 match_and_add(const char *pkg
, void *cookie
)
205 struct add_matching_arg
*arg
= cookie
;
208 if ((*arg
->match_fn
)(pkg
, arg
->cookie
) == 1) {
211 lpp
= alloc_lpkg(pkg
);
212 TAILQ_INSERT_TAIL(arg
->pkghead
, lpp
, lp_link
);
218 * Find all installed packages with the given basename and add them
220 * Returns -1 on error, 0 if no match was found and 1 otherwise.
223 add_installed_pkgs_by_basename(const char *pkgbase
, lpkg_head_t
*pkghead
)
225 struct add_matching_arg arg
;
227 arg
.pkghead
= pkghead
;
229 arg
.match_fn
= match_by_basename
;
230 arg
.cookie
= __UNCONST(pkgbase
);
232 if (iterate_pkg_db(match_and_add
, &arg
) == -1) {
233 warnx("could not process pkgdb");
236 return arg
.got_match
;
240 * Match all installed packages against pattern, add the matches to pkghead.
241 * Returns -1 on error, 0 if no match was found and 1 otherwise.
244 add_installed_pkgs_by_pattern(const char *pattern
, lpkg_head_t
*pkghead
)
246 struct add_matching_arg arg
;
248 arg
.pkghead
= pkghead
;
250 arg
.match_fn
= match_by_pattern
;
251 arg
.cookie
= __UNCONST(pattern
);
253 if (iterate_pkg_db(match_and_add
, &arg
) == -1) {
254 warnx("could not process pkgdb");
257 return arg
.got_match
;
260 struct best_installed_match_arg
{
262 char *best_current_match
;
266 match_best_installed(const char *pkg
, void *cookie
)
268 struct best_installed_match_arg
*arg
= cookie
;
270 switch (pkg_order(arg
->pattern
, pkg
, arg
->best_current_match
)) {
274 * Either current package doesn't match or
275 * the older match is better. Nothing to do.
279 /* Current package is better, remember it. */
280 free(arg
->best_current_match
);
281 arg
->best_current_match
= xstrdup(pkg
);
288 * Returns a copy of the name of best matching package.
289 * If no package matched the pattern or an error occured, return NULL.
292 find_best_matching_installed_pkg(const char *pattern
)
294 struct best_installed_match_arg arg
;
296 arg
.pattern
= pattern
;
297 arg
.best_current_match
= NULL
;
299 if (iterate_pkg_db(match_best_installed
, &arg
) == -1) {
300 warnx("could not process pkgdb");
304 return arg
.best_current_match
;
307 struct call_matching_arg
{
309 int (*call_fn
)(const char *pkg
, void *cookie
);
314 match_and_call(const char *pkg
, void *cookie
)
316 struct call_matching_arg
*arg
= cookie
;
318 if (pkg_match(arg
->pattern
, pkg
) == 1) {
319 return (*arg
->call_fn
)(pkg
, arg
->cookie
);
325 * Find all packages that match the given pattern and call the function
326 * for each of them. Iteration stops if the callback return non-0.
327 * Returns -1 on error, 0 if the iteration finished or whatever the
328 * callback returned otherwise.
331 match_installed_pkgs(const char *pattern
, int (*cb
)(const char *, void *),
334 struct call_matching_arg arg
;
336 arg
.pattern
= pattern
;
340 return iterate_pkg_db(match_and_call
, &arg
);
343 struct best_file_match_arg
{
345 char *best_current_match_filtered
;
346 char *best_current_match
;
351 match_best_file(const char *filename
, void *cookie
)
353 struct best_file_match_arg
*arg
= cookie
;
354 const char *active_filename
;
355 char *filtered_filename
;
357 if (arg
->filter_suffix
) {
360 len
= strlen(filename
);
362 (memcmp(filename
+ len
- 4, ".tgz", 4) != 0 &&
363 memcmp(filename
+ len
- 4, ".tbz", 4) != 0)) {
364 warnx("filename %s does not contain a recognized suffix", filename
);
367 filtered_filename
= xmalloc(len
- 4 + 1);
368 memcpy(filtered_filename
, filename
, len
- 4);
369 filtered_filename
[len
- 4] = '\0';
370 active_filename
= filtered_filename
;
372 filtered_filename
= NULL
;
373 active_filename
= filename
;
376 switch (pkg_order(arg
->pattern
, active_filename
, arg
->best_current_match_filtered
)) {
380 * Either current package doesn't match or
381 * the older match is better. Nothing to do.
383 free(filtered_filename
);
386 /* Current package is better, remember it. */
387 free(arg
->best_current_match
);
388 free(arg
->best_current_match_filtered
);
389 arg
->best_current_match
= xstrdup(filename
);
390 if (filtered_filename
!= NULL
)
391 arg
->best_current_match_filtered
= filtered_filename
;
393 arg
->best_current_match_filtered
= xstrdup(active_filename
);
396 errx(EXIT_FAILURE
, "Invalid error from pkg_order");
402 * Returns a copy of the name of best matching file.
403 * If no package matched the pattern or an error occured, return NULL.
406 find_best_matching_file(const char *dir
, const char *pattern
, int filter_suffix
, int allow_nonfiles
)
408 struct best_file_match_arg arg
;
410 arg
.filter_suffix
= filter_suffix
;
411 arg
.pattern
= pattern
;
412 arg
.best_current_match
= NULL
;
413 arg
.best_current_match_filtered
= NULL
;
415 if (iterate_local_pkg_dir(dir
, filter_suffix
, allow_nonfiles
, match_best_file
, &arg
) == -1) {
416 warnx("could not process directory");
419 free(arg
.best_current_match_filtered
);
421 return arg
.best_current_match
;
424 struct call_matching_file_arg
{
426 int (*call_fn
)(const char *pkg
, void *cookie
);
432 match_file_and_call(const char *filename
, void *cookie
)
434 struct call_matching_file_arg
*arg
= cookie
;
435 const char *active_filename
;
436 char *filtered_filename
;
439 if (arg
->filter_suffix
) {
442 len
= strlen(filename
);
444 (memcmp(filename
+ len
- 4, ".tgz", 4) != 0 &&
445 memcmp(filename
+ len
- 4, ".tbz", 4) != 0)) {
446 warnx("filename %s does not contain a recognized suffix", filename
);
449 filtered_filename
= xmalloc(len
- 4 + 1);
450 memcpy(filtered_filename
, filename
, len
- 4);
451 filtered_filename
[len
- 4] = '\0';
452 active_filename
= filtered_filename
;
454 filtered_filename
= NULL
;
455 active_filename
= filename
;
458 ret
= pkg_match(arg
->pattern
, active_filename
);
459 free(filtered_filename
);
462 return (*arg
->call_fn
)(filename
, arg
->cookie
);
468 * Find all packages that match the given pattern and call the function
469 * for each of them. Iteration stops if the callback return non-0.
470 * Returns -1 on error, 0 if the iteration finished or whatever the
471 * callback returned otherwise.
474 match_local_files(const char *dir
, int filter_suffix
, int allow_nonfiles
, const char *pattern
,
475 int (*cb
)(const char *, void *), void *cookie
)
477 struct call_matching_file_arg arg
;
479 arg
.pattern
= pattern
;
482 arg
.filter_suffix
= filter_suffix
;
484 return iterate_local_pkg_dir(dir
, filter_suffix
, allow_nonfiles
, match_file_and_call
, &arg
);