1*5ac0fc9cSjoerg /* $NetBSD: iterate.c,v 1.1.1.4 2010/01/30 21:33:47 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 157*5ac0fc9cSjoerg if ((dirp = opendir(pkgdb_get_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 && 1810590ec0aSjoerg 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; 1970590ec0aSjoerg 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"); 3970590ec0aSjoerg /* 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