commit: 86d662abd0216a49a6c2abee915e64d5c649cef2 Author: Fabian Groffen <grobian <AT> gentoo <DOT> org> AuthorDate: Sat Apr 20 17:19:06 2019 +0000 Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org> CommitDate: Sat Apr 20 17:19:06 2019 +0000 URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=86d662ab
qcache: rewrite to use new libq/cache This fixes qcache for Prefix and no longer needs q's cache files. Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org> qcache.c | 1012 ++++++++++++++++++++++++-------------------------------------- 1 file changed, 396 insertions(+), 616 deletions(-) diff --git a/qcache.c b/qcache.c index f3252d7..7c5ae42 100644 --- a/qcache.c +++ b/qcache.c @@ -2,7 +2,8 @@ * Copyright 2005-2019 Gentoo Foundation * Distributed under the terms of the GNU General Public License v2 * - * Copyright 2006 Thomas A. Cort - <[email protected]> + * Copyright 2006 Thomas A. Cort - <[email protected]> + * Copyright 2019- Fabian Groffen - <[email protected]> */ #include "main.h" @@ -52,42 +53,24 @@ static const char * const qcache_opts_help[] = { }; #define qcache_usage(ret) usage(ret, QCACHE_FLAGS, qcache_long_opts, qcache_opts_help, NULL, lookup_applet_idx("qcache")) -/********************************************************************/ -/* Structs */ -/********************************************************************/ - typedef struct { - const char *overlay; - const char *category; - const char *package; - const char *ebuild; - portage_cache *cache_data; - unsigned char cur; - unsigned char num; + depend_atom *qatom; + depend_atom *lastatom; + int *keywordsbuf; + size_t keywordsbuflen; + const char *arch; + cache_pkg_cb *runfunc; } qcache_data; -/********************************************************************/ -/* Global Variables */ -/********************************************************************/ - -static set *archs; -static char **archlist; -static int archlist_count; +static set *archs = NULL; +static char **archlist = NULL; +static size_t archlist_count; static size_t arch_longest_len; const char status[3] = {'-', '~', '+'}; -int qcache_skip, qcache_test_arch; -char *qcache_matchpkg = NULL, *qcache_matchcat = NULL; - -/********************************************************************/ -/* Enumerations */ -/********************************************************************/ +int qcache_test_arch = 0; enum { none = 0, testing, stable, minus }; -/********************************************************************/ -/* Keyword functions */ -/********************************************************************/ - /* * int decode_status(char c); * @@ -109,8 +92,6 @@ decode_status(char c) } /* - * int decode_arch(const char *arch); - * * Decode the architecture string * * IN: @@ -138,8 +119,6 @@ decode_arch(const char *arch) } /* - * void print_keywords(char *category, char *ebuild, int *keywords); - * * Prints the keywords to stdout * * IN: @@ -150,31 +129,24 @@ static void print_keywords(const char *category, const char *ebuild, int *keywords) { char **arch = archlist; - int a; - char *package; + size_t a; - package = xstrdup(ebuild); - package[strlen(ebuild)-7] = '\0'; - - printf("%s%s/%s%s%s ", BOLD, category, BLUE, package, NORM); + printf("%s%s/%s%s%s ", BOLD, category, BLUE, ebuild, NORM); for (a = 0; a < archlist_count; a++) { switch (keywords[a]) { case stable: printf("%s%c%s%s ", GREEN, status[keywords[a]], arch[a], NORM); break; case testing: - printf("%s%c%s%s ", YELLOW, status[keywords[a]], arch[0], NORM); + printf("%s%c%s%s ", YELLOW, status[keywords[a]], arch[a], NORM); break; } } printf("\n"); - free(package); } /* - * int read_keywords(char *s, int *keywords); - * * Read the KEYWORDS string and decode the values * * IN: @@ -188,7 +160,8 @@ read_keywords(char *s, int *keywords) { char *arch, delim[2] = { ' ', '\0' }; size_t slen; - int a; + size_t a; + int i; if (!s) return -1; @@ -206,138 +179,18 @@ read_keywords(char *s, int *keywords) arch = strtok(s, delim); do { - a = decode_arch(arch); - if (a == -1) + i = decode_arch(arch); + if (i == -1) continue; - keywords[a] = decode_status(arch[0]); + keywords[i] = decode_status(arch[0]); } while ((arch = strtok(NULL, delim))); return 0; } /* - * portage_cache *qcache_read_cache_file(const char *file); - * - * Read a file from the edb cache and store data in portage_cache. - * - * IN: - * const char *filename - cache file to read - * OUT: - * portage_cache *pkg - cache data - * ERR: - * NULL is returned when an error occurs. - */ -static portage_cache * -qcache_read_cache_file(const char *filename) -{ - struct stat s; - char *buf; - FILE *f; - portage_cache *ret = NULL; - int linelen; - size_t len, buflen; - - if ((f = fopen(filename, "r")) == NULL) - goto err; - - if (fstat(fileno(f), &s) != 0) { - fclose(f); - goto err; - } - - buf = NULL; - len = sizeof(*ret) + s.st_size + 1; - ret = xzalloc(len); - - while ((linelen = getline(&buf, &buflen, f)) >= 0) { - rmspace_len(buf, (size_t)linelen); - - if (strncmp(buf, "DEPEND=", 7) == 0) - ret->DEPEND = xstrdup(buf + 7); - - if (strncmp(buf, "DESCRIPTION=", 12) == 0) - ret->DESCRIPTION = xstrdup(buf + 12); - - if (strncmp(buf, "HOMEPAGE=", 9) == 0) - ret->HOMEPAGE = xstrdup(buf + 9); - - if (strncmp(buf, "INHERITED=", 10) == 0) - ret->INHERITED = xstrdup(buf + 10); - - if (strncmp(buf, "IUSE=", 4) == 0) - ret->IUSE = xstrdup(buf + 4); - - if (strncmp(buf, "KEYWORDS=", 9) == 0) - ret->KEYWORDS = xstrdup(buf + 9); - - if (strncmp(buf, "LICENSE=", 8) == 0) - ret->LICENSE = xstrdup(buf + 8); - - if (strncmp(buf, "PDEPEND=", 8) == 0) - ret->PDEPEND = xstrdup(buf + 8); - - if (strncmp(buf, "PROVIDE=", 8) == 0) - ret->PROVIDE = xstrdup(buf + 8); - - if (strncmp(buf, "RDEPEND=", 8) == 0) - ret->RDEPEND = xstrdup(buf + 8); - - if (strncmp(buf, "RESTRICT=", 9) == 0) - ret->RESTRICT = xstrdup(buf + 9); - - if (strncmp(buf, "SLOT=", 5) == 0) - ret->SLOT = xstrdup(buf + 5); - - if (strncmp(buf, "SRC_URI=", 8) == 0) - ret->SRC_URI = xstrdup(buf + 8); - } - - free(buf); - ret->atom = atom_explode(filename); - fclose(f); - - return ret; - - err: - if (ret) - cache_free(ret); - return NULL; -} - -/* - * void qcache_free_data(portage_cache *cache); - * - * free()'s a portage_cache - * - * IN: - * portage_cache *cache - the portage_cache to be free()'d - */ -static void -qcache_free_data(portage_cache *cache) -{ - int i; - char **c; - - if (!cache) - errf("Cache is empty !"); - - for (i = 0, c = (char**) cache; i < 15; i++) - if (c[i]) - free(c[i]); - - atom_implode(cache->atom); - free(cache); -} - -/********************************************************************/ -/* Comparison functions */ -/********************************************************************/ - -/* - * int qcache_vercmp(const void *x, const void *y); - * * Compare 2 struct dirent d_name strings based with atom_compare_str(). - * Used with dirscan() to sort ebuild filenames by version. + * Used with dirscan() to sort ebuild filenames by version, reversed. * * IN: * 2 (const struct dirent **) with d_name filled in @@ -350,357 +203,163 @@ static int qcache_vercmp(const struct dirent **x, const struct dirent **y) { switch (atom_compare_str((*x)->d_name, (*y)->d_name)) { - case NEWER: return -1; - case OLDER: return 1; - default: return 0; - } -} - -/********************************************************************/ -/* Selection functions */ -/********************************************************************/ - -/* - * int qcache_file_select(const struct dirent *entry); - * - * Selects filenames that do not begin with '.' and are not name "metadata.xml" - * or that file matches ".cpickle" - * - * IN: - * const struct dirent *entry - entry to check - * OUT: - * int - 0 if filename begins with '.' or is "metadata.xml", otherwise 1 - */ -static int -qcache_file_select(const struct dirent *entry) -{ - return !(entry->d_name[0] == '.' || - (strcmp(entry->d_name, "metadata.xml") == 0) || - (strstr(entry->d_name, ".cpickle") != 0)); -} - -/* - * int qcache_ebuild_select(const struct dirent *entry); - * - * Select filenames that end in ".ebuild" - * - * IN: - * const struct dirent *entry - entry to check - * OUT: - * int - 1 if the filename ends in ".ebuild", otherwise 0 - */ -static int -qcache_ebuild_select(const struct dirent *entry) -{ - return ((strlen(entry->d_name) > 7) && - !strcmp(entry->d_name+strlen(entry->d_name)-7, ".ebuild")); -} - -/********************************************************************/ -/* Traversal function */ -/********************************************************************/ - -static void qcache_load_arches(const char *overlay); - -/* - * int qcache_traverse(void (*func)(qcache_data*)); - * - * visit every version of every package of every category in the tree - * - * IN: - * void (*func)(qcache_data*) - function to call - * OUT: - * int - 0 on success. - * ERR: - * exit or return -1 on failure. - */ -static int -qcache_traverse_overlay(void (*func)(qcache_data*), const char *overlay) -{ - qcache_data data = { - .overlay = overlay, - }; - char *catpath, *pkgpath, *ebuildpath, *cachepath; - int i, j, k, len, num_cat, num_pkg, num_ebuild; - struct dirent **categories, **packages, **ebuilds; - - xasprintf(&catpath, "%s/metadata/md5-cache", overlay); - - if (-1 == (num_cat = scandir(catpath, &categories, - qcache_file_select, alphasort))) - { - errp("%s", catpath); - free(catpath); + case EQUAL: return 0; + case NEWER: return -1; + case OLDER: return 1; + default: return strcmp((*x)->d_name, (*y)->d_name); } - - if (!num_cat) - warn("%s is empty!", catpath); - - /* traverse categories */ - for (i = 0; i < num_cat; i++) { - xasprintf(&pkgpath, "%s/%s", overlay, categories[i]->d_name); - - if (-1 == (num_pkg = scandir(pkgpath, &packages, - qcache_file_select, alphasort))) - { - if (errno != ENOENT) - warnp("Found a cache dir, but unable to process %s", pkgpath); - free(categories[i]); - free(pkgpath); - continue; - } - - if (qcache_matchcat) { - if (strcmp(categories[i]->d_name, qcache_matchcat) != 0) { - scandir_free(packages, num_pkg); - free(categories[i]); - free(pkgpath); - continue; - } - } - - /* traverse packages */ - for (j = 0; j < num_pkg; j++) { - xasprintf(&ebuildpath, "%s/%s/%s", - overlay, categories[i]->d_name, packages[j]->d_name); - - if (-1 == (num_ebuild = scandir(ebuildpath, &ebuilds, - qcache_ebuild_select, qcache_vercmp))) - { - /* Do not complain about spurious files */ - if (errno != ENOTDIR) - warnp("%s", ebuildpath); - free(packages[j]); - free(ebuildpath); - continue; - } - - if (qcache_matchpkg) { - if (strcmp(packages[j]->d_name, qcache_matchpkg) != 0) { - scandir_free(ebuilds, num_ebuild); - free(packages[j]); - free(ebuildpath); - continue; - } - } - - qcache_skip = 0; - - /* traverse ebuilds */ - data.num = num_ebuild; - for (k = 0; k < num_ebuild; k++) { - len = xasprintf(&cachepath, "%s/%s/%s", - catpath, categories[i]->d_name, ebuilds[k]->d_name); - cachepath[len - 7] = '\0'; /* remove ".ebuild" */ - - data.category = categories[i]->d_name; - data.package = packages[j]->d_name; - data.ebuild = ebuilds[k]->d_name; - data.cur = k + 1; - data.cache_data = qcache_read_cache_file(cachepath); - - if (data.cache_data != NULL) { - if (!qcache_skip) - func(&data); - - qcache_free_data(data.cache_data); - } else { - static bool warned = false; - if (!warned) { - warned = true; - warnp("unable to read cache '%s'\n" - "\tperhaps you need to `egencache`?", cachepath); - } - } - - free(ebuilds[k]); - free(cachepath); - } - - free(packages[j]); - free(ebuilds); - free(ebuildpath); - } - - free(categories[i]); - free(packages); - free(pkgpath); - } - - free(categories); - free(catpath); - - return 0; } static int -qcache_traverse(void (*func)(qcache_data*)) +qcache_imlate(cache_pkg_ctx *pkg_ctx, void *priv) { - int ret; - size_t n; - const char *overlay; + size_t a; + qcache_data *data = (qcache_data *)priv; - /* Preload all the arches. Not entirely correctly (as arches are bound - * to overlays if set), but oh well. */ - array_for_each(overlays, n, overlay) - qcache_load_arches(overlay); - - ret = 0; - array_for_each(overlays, n, overlay) - ret |= qcache_traverse_overlay(func, overlay); - - func(NULL); - - return ret; -} - -/********************************************************************/ -/* functors */ -/********************************************************************/ - -static void -qcache_imlate(qcache_data *data) -{ - int *keywords; - int a; - - if (!data) - return; - - keywords = xmalloc(sizeof(*keywords) * archlist_count); - - if (read_keywords(data->cache_data->KEYWORDS, keywords) < 0) { - if (verbose) - warn("Failed to read keywords for %s%s/%s%s%s", - BOLD, data->category, BLUE, data->ebuild, NORM); - free(keywords); - return; - } - - switch (keywords[qcache_test_arch]) { + switch (data->keywordsbuf[qcache_test_arch]) { case stable: - qcache_skip = 1; case none: case minus: break; default: - for (a = 0; a < archlist_count && !qcache_skip; ++a) { - if (keywords[a] != stable) + /* match if any of the other arches have stable keywords */ + for (a = 0; a < archlist_count; a++) { + if (data->keywordsbuf[a] != stable) continue; - qcache_skip = 1; - print_keywords(data->category, data->ebuild, keywords); + print_keywords(pkg_ctx->cat_ctx->name, pkg_ctx->name, + data->keywordsbuf); + + return EXIT_SUCCESS; } } - free(keywords); + + return EXIT_FAILURE; } -static void -qcache_not(qcache_data *data) +static int +qcache_not(cache_pkg_ctx *pkg_ctx, void *priv) { - int *keywords; - - if (!data) - return; + qcache_data *data = (qcache_data *)priv; - keywords = xmalloc(sizeof(*keywords) * archlist_count); - - if (read_keywords(data->cache_data->KEYWORDS, keywords) < 0) { - if (verbose) - warn("Failed to read keywords for %s%s/%s%s%s", - BOLD, data->category, BLUE, data->ebuild, NORM); - free(keywords); - return; - } - - if (keywords[qcache_test_arch] == testing || - keywords[qcache_test_arch] == stable) + if (data->keywordsbuf[qcache_test_arch] != testing && + data->keywordsbuf[qcache_test_arch] != stable) { - qcache_skip = 1; - } else if (data->cur == data->num) { - printf("%s%s/%s%s%s\n", BOLD, data->category, BLUE, - data->package, NORM); + print_keywords(pkg_ctx->cat_ctx->name, pkg_ctx->name, + data->keywordsbuf); + return EXIT_SUCCESS; } - free(keywords); + return EXIT_FAILURE; } -static void -qcache_all(qcache_data *data) +static int +qcache_all(cache_pkg_ctx *pkg_ctx, void *priv) { - int *keywords; - - if (!data) - return; - - keywords = xmalloc(sizeof(*keywords) * archlist_count); - - if (read_keywords(data->cache_data->KEYWORDS, keywords) < 0) { - if (verbose) - warn("Failed to read keywords for %s%s/%s%s%s", - BOLD, data->category, BLUE, data->ebuild, NORM); - free(keywords); - return; - } + qcache_data *data = (qcache_data *)priv; - if (keywords[qcache_test_arch] == stable || - keywords[qcache_test_arch] == testing) + if (data->keywordsbuf[qcache_test_arch] == stable || + data->keywordsbuf[qcache_test_arch] == testing) { - qcache_skip = 1; - printf("%s%s/%s%s%s\n", BOLD, data->category, BLUE, - data->package, NORM); + print_keywords(pkg_ctx->cat_ctx->name, pkg_ctx->name, + data->keywordsbuf); + return EXIT_SUCCESS; } - free(keywords); + return EXIT_FAILURE; } -static void -qcache_dropped(qcache_data *data) +static int +qcache_dropped(cache_pkg_ctx *pkg_ctx, void *priv) { - static int possible = 0; - int *keywords, i; - - if (!data) - return; - - if (data->cur == 1) - possible = 0; - - keywords = xmalloc(sizeof(*keywords) * archlist_count); - - if (read_keywords(data->cache_data->KEYWORDS, keywords) < 0) { - if (verbose) - warn("Failed to read keywords for %s%s/%s%s%s", - BOLD, data->category, BLUE, data->ebuild, NORM); - free(keywords); - return; + static bool candidate = false; + static char pkg1[_Q_PATH_MAX]; + static char pkg2[_Q_PATH_MAX]; + static char *lastpkg = pkg1; + static char *curpkg = pkg2; + static char candpkg[_Q_PATH_MAX]; + static int *candkwds = NULL; + static size_t candkwdslen = 0; + + qcache_data *data = (qcache_data *)priv; + size_t i; + char *p; + + /* a keyword is "dropped", if: + * - the keyword is present (stable or testing) in earlier ebuilds + * - there are other stable or testing keywords in the ebuild being + * evaluated + * - the keyword is absent, thus not explicitly removed -keyword */ + + /* mutt-1.10.4: amd64 + * mutt-1.11.1: amd64 ppc64 + * mutt-1.15.1: amd64 <-- this ebuild for ppc64 + * mutt-9999: */ + + p = lastpkg; + lastpkg = curpkg; + curpkg = p; + if (pkg_ctx != NULL) { + snprintf(curpkg, _Q_PATH_MAX, "%s/%s", + pkg_ctx->cat_ctx->name, pkg_ctx->name); + } else { + curpkg[0] = '\0'; } - - if (keywords[qcache_test_arch] == testing || - keywords[qcache_test_arch] == stable) + if (atom_compare_str(lastpkg, curpkg) == NOT_EQUAL) { - qcache_skip = 1; + /* different package, reset */ + candidate = false; + } - if (possible) { - printf("%s%s/%s%s%s\n", BOLD, data->category, BLUE, - data->package, NORM); - } + if (data == NULL) { + if (candkwds != NULL) + free(candkwds); + return EXIT_SUCCESS; + } - free(keywords); - return; + if (candkwdslen < data->keywordsbuflen) { + candkwds = xrealloc(candkwds, + data->keywordsbuflen * sizeof(candkwds[0])); + candkwdslen = data->keywordsbuflen; } - if (!possible) { - /* don't count newer versions with "-*" keywords */ - for (i = 0; i < archlist_count; ++i) { - if (keywords[i] == stable || keywords[i] == testing) { - possible = 1; - break; + /* explicitly removed? */ + if (data->keywordsbuf[qcache_test_arch] == minus) + return EXIT_FAILURE; + + /* got a keyword? */ + if (data->keywordsbuf[qcache_test_arch] == testing || + data->keywordsbuf[qcache_test_arch] == stable) + { + if (candidate) { + p = strchr(candpkg, '/'); + if (p != NULL) { + *p++ = '\0'; + print_keywords(candpkg, p, candkwds); } + candidate = false; } + return EXIT_SUCCESS; /* suppress further hits for this package */ } - free(keywords); + /* do others have keywords? */ + for (i = 0; i < archlist_count; i++) { + if (data->keywordsbuf[i] == stable || data->keywordsbuf[i] == testing) { + /* we don't have a keyword, others do: candidate */ + break; + } + } + if (i == archlist_count) + return EXIT_FAILURE; + + /* keep the "highest" candidate */ + if (!candidate) { + memcpy(candkwds, data->keywordsbuf, + data->keywordsbuflen * sizeof(candkwds[0])); + memcpy(candpkg, curpkg, _Q_PATH_MAX); + candidate = true; + } + return EXIT_FAILURE; } static void @@ -712,36 +371,54 @@ print_seconds_for_earthlings(const unsigned long t) mm = tt % 60; tt /= 60; hh = tt % 24; tt /= 24; dd = tt; - if (dd) printf("%s%u%s day%s, ", GREEN, dd, NORM, (dd == 1 ? "" : "s")); - if (hh) printf("%s%u%s hour%s, ", GREEN, hh, NORM, (hh == 1 ? "" : "s")); - if (mm) printf("%s%u%s minute%s, ", GREEN, mm, NORM, (mm == 1 ? "" : "s")); + if (dd) + printf("%s%u%s day%s, ", GREEN, dd, NORM, (dd == 1 ? "" : "s")); + if (hh) + printf("%s%u%s hour%s, ", GREEN, hh, NORM, (hh == 1 ? "" : "s")); + if (mm) + printf("%s%u%s minute%s, ", GREEN, mm, NORM, (mm == 1 ? "" : "s")); printf("%s%u%s second%s", GREEN, ss, NORM, (ss == 1 ? "" : "s")); } -static void -qcache_stats(qcache_data *data) +static int +qcache_stats(cache_pkg_ctx *pkg_ctx, void *priv) { static time_t runtime; - static set *allcats; - static const char *last_overlay; static int numpkg = 0; static int numebld = 0; - static int numcat; + static int numcat = 0; static int *packages_stable; static int *packages_testing; static int *current_package_keywords; - static int *keywords; - int a; + static const char *lastcat = NULL; + static char lastpkg[_Q_PATH_MAX]; + + size_t a; + depend_atom *atom; + qcache_data *data = (qcache_data *)priv; /* Is this the last time we'll be called? */ if (!data) { char **arch; const char border[] = "------------------------------------------------------------------"; + /* include stats for last package */ + for (a = 0; a < archlist_count; a++) { + switch (current_package_keywords[a]) { + case stable: + packages_stable[a]++; + break; + case testing: + packages_testing[a]++; + default: + break; + } + } + printf("+%.*s+\n", 25, border); printf("| general statistics |\n"); printf("+%.*s+\n", 25, border); - printf("| %s%13s%s | %s%7d%s |\n", + printf("| %s%13s%s | %s%7zd%s |\n", GREEN, "architectures", NORM, BLUE, archlist_count, NORM); printf("| %s%13s%s | %s%7d%s |\n", GREEN, "categories", NORM, BLUE, numcat, NORM); @@ -783,81 +460,49 @@ qcache_stats(qcache_data *data) free(packages_stable); free(packages_testing); - free(keywords); free(current_package_keywords); - free_set(allcats); - return; + return EXIT_SUCCESS; } - if (last_overlay != data->overlay) { - DIR *dir; - struct dirent *de; - char *catpath; - - runtime = time(NULL); - - xasprintf(&catpath, "%s/metadata/md5-cache", data->overlay); - dir = opendir(catpath); - while ((de = readdir(dir))) { - /* Look for all the directories in this path. */ -#if defined(DT_UNKNOWN) && defined(DT_DIR) - if (de->d_type == DT_UNKNOWN) -#endif - { - struct stat s; - char spath[_Q_PATH_MAX * 2]; - snprintf(spath, sizeof(spath), "%s/%s", catpath, de->d_name); - if (lstat(spath, &s) != 0) - continue; - if (!S_ISDIR(s.st_mode)) - continue; - } -#if defined(DT_UNKNOWN) && defined(DT_DIR) - else if (de->d_type != DT_DIR) - continue; -#endif - - if (de->d_name[0] != '.') { - bool ok; - allcats = add_set_unique(de->d_name, allcats, &ok); - if (ok) - ++numcat; - } - } - closedir(dir); - free(catpath); - - last_overlay = data->overlay; - } - - if (!numpkg) { + if (numpkg == 0) { + runtime = time(NULL); packages_stable = xcalloc(archlist_count, sizeof(*packages_stable)); packages_testing = xcalloc(archlist_count, sizeof(*packages_testing)); - keywords = - xcalloc(archlist_count, sizeof(*keywords)); current_package_keywords = xcalloc(archlist_count, sizeof(*current_package_keywords)); } - if (data->cur == 1) { + if (lastcat != pkg_ctx->cat_ctx->name) + numcat++; + lastcat = pkg_ctx->cat_ctx->name; + + atom = atom_explode(pkg_ctx->name); + if (atom && strcmp(lastpkg, atom->PN) != 0) { + for (a = 0; a < archlist_count; a++) { + switch (current_package_keywords[a]) { + case stable: + packages_stable[a]++; + break; + case testing: + packages_testing[a]++; + default: + break; + } + } + numpkg++; + snprintf(lastpkg, sizeof(lastpkg), "%s", atom->PN); memset(current_package_keywords, 0, archlist_count * sizeof(*current_package_keywords)); } - ++numebld; + atom_implode(atom); - memset(keywords, 0, archlist_count * sizeof(*keywords)); - if (read_keywords(data->cache_data->KEYWORDS, keywords) < 0) { - if (verbose) - warn("Failed to read keywords for %s%s/%s%s%s", - BOLD, data->category, BLUE, data->ebuild, NORM); - return; - } + numebld++; - for (a = 0; a < archlist_count; ++a) { - switch (keywords[a]) { + for (a = 0; a < archlist_count; a++) { + switch (data->keywordsbuf[a]) { case stable: current_package_keywords[a] = stable; break; @@ -869,65 +514,140 @@ qcache_stats(qcache_data *data) } } - if (data->cur == data->num) { - for (a = 0; a < archlist_count; ++a) { - switch (current_package_keywords[a]) { - case stable: - packages_stable[a]++; - break; - case testing: - packages_testing[a]++; - default: - break; + return EXIT_FAILURE; +} + +static int +qcache_testing_only(cache_pkg_ctx *pkg_ctx, void *priv) +{ + static bool candidate = false; + static char pkg1[_Q_PATH_MAX]; + static char pkg2[_Q_PATH_MAX]; + static char *lastpkg = pkg1; + static char *curpkg = pkg2; + static char candpkg[_Q_PATH_MAX]; + static int *candkwds = NULL; + static size_t candkwdslen = 0; + + qcache_data *data = (qcache_data *)priv; + char *p; + + p = lastpkg; + lastpkg = curpkg; + curpkg = p; + if (pkg_ctx != NULL) { + snprintf(curpkg, _Q_PATH_MAX, "%s/%s", + pkg_ctx->cat_ctx->name, pkg_ctx->name); + } else { + curpkg[0] = '\0'; + } + if (atom_compare_str(lastpkg, curpkg) == NOT_EQUAL) + { + /* different package, print if candidate */ + if (candidate) { + p = strchr(candpkg, '/'); + if (p != NULL) { + *p++ = '\0'; + print_keywords(candpkg, p, candkwds); } + candidate = false; } } + + if (data == NULL) { + if (candkwds != NULL) + free(candkwds); + return EXIT_SUCCESS; + } + + if (candkwdslen < data->keywordsbuflen) { + candkwds = xrealloc(candkwds, + data->keywordsbuflen * sizeof(candkwds[0])); + candkwdslen = data->keywordsbuflen; + } + + /* explicitly removed or unkeyworded? */ + if (data->keywordsbuf[qcache_test_arch] == minus || + data->keywordsbuf[qcache_test_arch] == none) + return EXIT_FAILURE; + + /* got a stable keyword? */ + if (data->keywordsbuf[qcache_test_arch] == stable) + return EXIT_SUCCESS; /* suppress further hits for this package */ + + /* must be testing at this point */ + + /* keep the "highest" candidate */ + if (!candidate) { + memcpy(candkwds, data->keywordsbuf, + data->keywordsbuflen * sizeof(candkwds[0])); + memcpy(candpkg, curpkg, _Q_PATH_MAX); + candidate = true; + } + return EXIT_FAILURE; } -static void -qcache_testing_only(qcache_data *data) +static int +qcache_results_cb(cache_pkg_ctx *pkg_ctx, void *priv) { - static int possible = 0; int *keywords; + qcache_data *data = (qcache_data *)priv; + char buf[_Q_PATH_MAX]; + depend_atom *patom = NULL; + cache_pkg_meta *meta; + int ret; - if (!data) - return; - - if (data->cur == 1) - possible = 0; + snprintf(buf, sizeof(buf), "%s/%s", + pkg_ctx->cat_ctx->name, pkg_ctx->name); + patom = atom_explode(buf); + if (patom == NULL) + return EXIT_FAILURE; - keywords = xmalloc(sizeof(*keywords) * archlist_count); + if (data->qatom != NULL && + atom_compare(patom, data->qatom) != EQUAL) + { + atom_implode(patom); + return EXIT_FAILURE; + } - if (read_keywords(data->cache_data->KEYWORDS, keywords) < 0) { - if (verbose) - warn("Failed to read keywords for %s%s/%s%s%s", - BOLD, data->category, BLUE, data->ebuild, NORM); - free(keywords); - return; + if (data->lastatom != NULL && + atom_compare(data->lastatom, patom) != NOT_EQUAL) + { + atom_implode(patom); + return EXIT_SUCCESS; } - if (keywords[qcache_test_arch] == stable) { - qcache_skip = 1; - free(keywords); - return; + keywords = data->keywordsbuf; + meta = cache_pkg_read(pkg_ctx); + if (meta == NULL) { + atom_implode(patom); + return EXIT_FAILURE; } - /* the qcache_test_arch must have at least 1 ~arch keyword */ - if (keywords[qcache_test_arch] == testing) - possible = 1; + if (read_keywords(meta->KEYWORDS, keywords) < 0) { + if (verbose) + warn("Failed to read keywords for %s%s/%s%s%s", + BOLD, pkg_ctx->cat_ctx->name, BLUE, pkg_ctx->name, NORM); + atom_implode(patom); + return EXIT_FAILURE; + } - if (data->cur == data->num && possible) { - printf("%s%s/%s%s%s\n", BOLD, data->category, BLUE, - data->package, NORM); + ret = data->runfunc(pkg_ctx, priv); + + if (ret == EXIT_SUCCESS) { + /* store CAT/PN in lastatom */ + patom->P = patom->PN; + patom->PVR = patom->PN; + patom->PR_int = 0; + data->lastatom = patom; + patom = NULL; + } else { + atom_implode(patom); } - free(keywords); + return EXIT_SUCCESS; } -/********************************************************************/ -/* Misc functions */ -/********************************************************************/ - static void qcache_load_arches(const char *overlay) { @@ -937,11 +657,12 @@ qcache_load_arches(const char *overlay) size_t buflen; char *buf; - xasprintf(&filename, "%s/profiles/arch.list", overlay); + xasprintf(&filename, "%s/%s/profiles/arch.list", portroot, overlay); fp = fopen(filename, "re"); if (!fp) goto done; + clear_set(archs); archlist_count = 0; arch_longest_len = 0; buf = NULL; @@ -956,13 +677,17 @@ qcache_load_arches(const char *overlay) bool ok; archs = add_set_unique(buf, archs, &ok); if (ok) { - ++archlist_count; - arch_longest_len = MAX(arch_longest_len, strlen(buf)); + archlist_count++; + buflen = strlen(buf); + if (arch_longest_len < buflen) + arch_longest_len = buflen; } } free(buf); /* materialise into a list */ + if (archlist != NULL) + free(archlist); list_set(archs, &archlist); fclose(fp); @@ -970,30 +695,51 @@ qcache_load_arches(const char *overlay) free(filename); } -/* - * int qcache_free(); - * - * Deallocate variables (archlist) - */ -static void -qcache_free(void) +static int +qcache_traverse(cache_pkg_cb func, void *priv) { - free(archlist); - free_set(archs); -} + int ret; + size_t n; + const char *overlay; + qcache_data *data = (qcache_data *)priv; -/********************************************************************/ -/* main */ -/********************************************************************/ + /* Preload all the arches. Not entirely correctly (as arches are bound + * to overlays if set), but oh well. */ + array_for_each(overlays, n, overlay) + qcache_load_arches(overlay); + + /* allocate memory (once) for the list used by various funcs */ + if (archlist_count > data->keywordsbuflen) { + data->keywordsbuf = xrealloc(data->keywordsbuf, + archlist_count * sizeof(data->keywordsbuf[0])); + data->keywordsbuflen = archlist_count; + } + + qcache_test_arch = decode_arch(data->arch); + if (qcache_test_arch == -1) + return EXIT_FAILURE; + + data->runfunc = func; + ret = 0; + array_for_each(overlays, n, overlay) + ret |= cache_foreach_pkg_sorted(portroot, overlay, + qcache_results_cb, priv, NULL, qcache_vercmp); + + return ret; +} int qcache_main(int argc, char **argv) { - int i, action = 0; + int i; + char action = '\0'; + qcache_data data; + char *pkg = NULL; + char *cat = NULL; while ((i = GETOPT_LONG(QCACHE, qcache, "")) != -1) { switch (i) { - case 'p': qcache_matchpkg = optarg; break; - case 'c': qcache_matchcat = optarg; break; + case 'p': pkg = optarg; break; + case 'c': cat = optarg; break; case 'i': case 'd': case 't': @@ -1010,22 +756,56 @@ int qcache_main(int argc, char **argv) } } + data.arch = NULL; if (optind < argc) - qcache_test_arch = decode_arch(argv[optind]); + data.arch = argv[optind]; - if ((qcache_test_arch == -1 && action != 's') || optind + 1 < argc) + if ((data.arch == NULL && action != 's') || optind + 1 < argc) qcache_usage(EXIT_FAILURE); + if (cat != NULL) { + char buf[_Q_PATH_MAX]; + + snprintf(buf, sizeof(buf), "%s/%s", cat, pkg == NULL ? "" : pkg); + data.qatom = atom_explode(buf); + if (data.qatom == NULL) { + warnf("invalid cat/pkg: %s\n", buf); + return EXIT_FAILURE; + } + } else if (pkg != NULL) { + data.qatom = atom_explode(pkg); + if (data.qatom == NULL) { + warnf("invalid pkg: %s\n", pkg); + return EXIT_FAILURE; + } + } else { + data.qatom = NULL; + } + + archs = create_set(); + data.lastatom = NULL; + data.keywordsbuf = NULL; + data.keywordsbuflen = 0; + switch (action) { - case 'i': return qcache_traverse(qcache_imlate); - case 'd': return qcache_traverse(qcache_dropped); - case 't': return qcache_traverse(qcache_testing_only); - case 's': return qcache_traverse(qcache_stats); - case 'a': return qcache_traverse(qcache_all); - case 'n': return qcache_traverse(qcache_not); + case 'i': i = qcache_traverse(qcache_imlate, &data); break; + case 'd': i = qcache_traverse(qcache_dropped, &data); + i = qcache_dropped(NULL, NULL); break; + case 't': i = qcache_traverse(qcache_testing_only, &data); + i = qcache_testing_only(NULL, NULL); break; + case 's': data.arch = "amd64"; /* doesn't matter, need to be set */ + i = qcache_traverse(qcache_stats, &data); + i = qcache_stats(NULL, NULL); break; + case 'a': i = qcache_traverse(qcache_all, &data); break; + case 'n': i = qcache_traverse(qcache_not, &data); break; + default: i = -2; break; } - qcache_free(); - qcache_usage(EXIT_FAILURE); - return EXIT_FAILURE; + if (data.qatom != NULL) + atom_implode(data.qatom); + free(archlist); + free_set(archs); + if (i == -2) + qcache_usage(EXIT_FAILURE); + return i; }
