/* * Copyright 2005-2018 Gentoo Foundation * Distributed under the terms of the GNU General Public License v2 * * Copyright 2005-2010 Ned Ludd - * Copyright 2005-2014 Mike Frysinger - * Copyright 2005 Martin Schlemmer - */ #ifdef APPLET_qlist #define QLIST_FLAGS "ISRUcDeados" COMMON_FLAGS static struct option const qlist_long_opts[] = { {"installed", no_argument, NULL, 'I'}, {"slots", no_argument, NULL, 'S'}, {"repo", no_argument, NULL, 'R'}, {"umap", no_argument, NULL, 'U'}, {"columns", no_argument, NULL, 'c'}, {"showdebug", no_argument, NULL, 128}, {"exact", no_argument, NULL, 'e'}, {"all", no_argument, NULL, 'a'}, {"dir", no_argument, NULL, 'd'}, {"obj", no_argument, NULL, 'o'}, {"sym", no_argument, NULL, 's'}, /* {"file", a_argument, NULL, 'f'}, */ COMMON_LONG_OPTS }; static const char * const qlist_opts_help[] = { "Just show installed packages", "Display installed packages with slots (use twice for subslots)", "Display installed packages with repository", "Display installed packages with flags used", "Display column view", "Show /usr/lib/debug and /usr/src/debug files", "Exact match (only CAT/PN or PN without PV)", "Show every installed package", "Only show directories", "Only show objects", "Only show symlinks", /* "query filename for pkgname", */ COMMON_OPTS_HELP }; #define qlist_usage(ret) usage(ret, QLIST_FLAGS, qlist_long_opts, qlist_opts_help, NULL, lookup_applet_idx("qlist")) static char * grab_pkg_umap(q_vdb_pkg_ctx *pkg_ctx) { static char umap[BUFSIZ]; static char *use, *iuse; static size_t use_len, iuse_len; int use_argc = 0, iuse_argc = 0; char **use_argv = NULL, **iuse_argv = NULL; queue *ll = NULL; queue *sets = NULL; int i, u; q_vdb_pkg_eat(pkg_ctx, "USE", &use, &use_len); if (!use[0]) return NULL; q_vdb_pkg_eat(pkg_ctx, "IUSE", &iuse, &iuse_len); if (!iuse[0]) return NULL; umap[0] = '\0'; /* reset the buffer */ makeargv(use, &use_argc, &use_argv); /* strip out possible leading +/- flags in IUSE */ for (i = 0; i < (int)strlen(iuse); i++) if (iuse[i] == '+' || iuse[i] == '-') if (i == 0 || iuse[i - 1] == ' ') iuse[i] = ' '; makeargv(iuse, &iuse_argc, &iuse_argv); for (u = 1; u < use_argc; u++) { for (i = 1; i < iuse_argc; i++) { if (strcmp(use_argv[u], iuse_argv[i]) == 0) { strncat(umap, use_argv[u], sizeof(umap)-strlen(umap)-1); strncat(umap, " ", sizeof(umap)-strlen(umap)-1); } } } freeargv(iuse_argc, iuse_argv); freeargv(use_argc, use_argv); /* filter out the dup use flags */ use_argc = 0; use_argv = NULL; makeargv(umap, &use_argc, &use_argv); for (i = 1; i < use_argc; i++) { int ok = 0; sets = del_set(use_argv[i], sets, &ok); sets = add_set(use_argv[i], sets); } umap[0] = '\0'; /* reset the buffer */ for (ll = sets; ll != NULL; ll = ll->next) { strncat(umap, ll->name, sizeof(umap)-strlen(umap)-1); strncat(umap, " ", sizeof(umap)-strlen(umap)-1); } freeargv(use_argc, use_argv); free_sets(sets); /* end filter */ return umap; } static const char * umapstr(char display, q_vdb_pkg_ctx *pkg_ctx) { static char buf[BUFSIZ]; char *umap = NULL; buf[0] = '\0'; if (!display) return buf; if ((umap = grab_pkg_umap(pkg_ctx)) == NULL) return buf; rmspace(umap); if (!strlen(umap)) return buf; snprintf(buf, sizeof(buf), " %s%s%s%s%s", quiet ? "": "(", RED, umap, NORM, quiet ? "": ")"); return buf; } static bool qlist_match(q_vdb_pkg_ctx *pkg_ctx, const char *name, depend_atom **name_atom, bool exact) { const char *catname = pkg_ctx->cat_ctx->name; const char *pkgname = pkg_ctx->name; char buf[_Q_PATH_MAX]; char swap[_Q_PATH_MAX]; const char *uslot; size_t uslot_len = 0; const char *urepo; size_t urepo_len = 0; depend_atom *atom; uslot = strchr(name, ':'); if (uslot) { if (*++uslot == ':') uslot = NULL; else { if (!pkg_ctx->slot) q_vdb_pkg_eat(pkg_ctx, "SLOT", &pkg_ctx->slot, &pkg_ctx->slot_len); uslot_len = strlen(uslot); } } urepo = strstr(name, "::"); if (urepo) { if (!pkg_ctx->repo) q_vdb_pkg_eat(pkg_ctx, "repository", &pkg_ctx->repo, &pkg_ctx->repo_len); urepo += 2; urepo_len = strlen(urepo); if (uslot_len) uslot_len -= (urepo_len + 2); } /* maybe they're using a version range */ switch (name[0]) { case '=': case '>': case '<': case '~': snprintf(buf, sizeof(buf), "%s/%s%c%s%s%s", catname, pkgname, pkg_ctx->slot ? ':' : '\0', pkg_ctx->slot ? : "", pkg_ctx->repo ? "::" : "", pkg_ctx->repo ? : ""); if ((atom = atom_explode(buf)) == NULL) { warn("invalid atom %s", buf); return false; } depend_atom *_atom = NULL; if (!name_atom) name_atom = &_atom; if (!*name_atom) { if ((*name_atom = atom_explode(name)) == NULL) { atom_implode(atom); warn("invalid atom %s", name); return false; } } bool ret = atom_compare(atom, *name_atom) == EQUAL; atom_implode(atom); return ret; } if (uslot) { /* Require exact match on SLOTs. If the user didn't include a subslot, * then ignore it when checking the package's value. */ if (strncmp(pkg_ctx->slot, uslot, uslot_len) != 0 || (pkg_ctx->slot[uslot_len] != '\0' && pkg_ctx->slot[uslot_len] != '/')) return false; } if (urepo) { /* require exact match on repositories */ if (strcmp(pkg_ctx->repo, urepo) != 0) return false; } if (exact) { int i; snprintf(buf, sizeof(buf), "%s/%s:%s::%s", catname, pkgname, pkg_ctx->slot, pkg_ctx->repo); /* exact match: CAT/PN-PVR[:SLOT][::REPO] */ if (strcmp(name, buf) == 0) return true; /* exact match: PN-PVR[:SLOT][::REPO] */ if (strcmp(name, strstr(buf, "/") + 1) == 0) return true; /* let's try exact matching w/out the PV */ if ((atom = atom_explode(buf)) == NULL) { warn("invalid atom %s", buf); return false; } i = snprintf(swap, sizeof(swap), "%s/%s", atom->CATEGORY, atom->PN); if (uslot && i <= (int)sizeof(swap)) i += snprintf(swap + i, sizeof(swap) - i, ":%s", atom->SLOT); if (urepo && i <= (int)sizeof(swap)) i += snprintf(swap + i, sizeof(swap) - i, "::%s", atom->REPO); atom_implode(atom); /* exact match: CAT/PN[:SLOT][::REPO] */ if (strcmp(name, swap) == 0) return true; /* exact match: PN[:SLOT][::REPO] */ if (strcmp(name, strstr(swap, "/") + 1) == 0) return true; } else { size_t ulen = strlen(name); if (urepo) ulen -= (urepo_len + 2); if (uslot) ulen -= (uslot_len + 1); snprintf(buf, sizeof(buf), "%s/%s", catname, pkgname); /* partial leading match: CAT/PN-PVR */ if (strncmp(name, buf, ulen) == 0) return true; /* partial leading match: PN-PVR */ if (strncmp(name, pkgname, ulen) == 0) return true; /* try again but with regexps */ if (rematch(name, buf, REG_EXTENDED) == 0) return true; if (rematch(name, pkgname, REG_EXTENDED) == 0) return true; } return false; } struct qlist_opt_state { int argc; char **argv; depend_atom **atoms; bool exact; bool all; bool just_pkgname; bool show_dir; bool show_obj; bool show_repo; bool show_sym; int show_slots; bool show_umap; bool show_dbg; bool columns; queue *sets; char *buf; size_t buflen; }; static int qlist_cb(q_vdb_pkg_ctx *pkg_ctx, void *priv) { struct qlist_opt_state *state = priv; int i; FILE *fp; const char *catname = pkg_ctx->cat_ctx->name; const char *pkgname = pkg_ctx->name; /* see if this cat/pkg is requested */ for (i = optind; i < state->argc; ++i) if (qlist_match(pkg_ctx, state->argv[i], &state->atoms[i - optind], state->exact)) break; if ((i == state->argc) && (state->argc != optind)) return 0; if (state->just_pkgname) { depend_atom *atom; atom = (verbose ? NULL : atom_explode(pkgname)); if ((state->all + state->just_pkgname) < 2) { if (state->show_slots && !pkg_ctx->slot) { q_vdb_pkg_eat(pkg_ctx, "SLOT", &pkg_ctx->slot, &pkg_ctx->slot_len); /* chop off the subslot if desired */ if (state->show_slots == 1) { char *s = strchr(pkg_ctx->slot, '/'); if (s) *s = '\0'; } } if (state->show_repo && !pkg_ctx->repo) q_vdb_pkg_eat(pkg_ctx, "repository", &pkg_ctx->repo, &pkg_ctx->repo_len); /* display it */ printf("%s%s/%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", BOLD, catname, BLUE, (!state->columns ? (atom ? atom->PN : pkgname) : atom->PN), (state->columns ? " " : ""), (state->columns ? atom->PV : ""), NORM, YELLOW, state->show_slots ? ":" : "", state->show_slots ? pkg_ctx->slot : "", NORM, NORM, GREEN, state->show_repo ? "::" : "", state->show_repo ? pkg_ctx->repo : "", NORM, umapstr(state->show_umap, pkg_ctx)); } if (atom) atom_implode(atom); if (!state->all) return 1; } if (verbose > 1) printf("%s%s/%s%s%s\n%sCONTENTS%s:\n", BOLD, catname, BLUE, pkgname, NORM, DKBLUE, NORM); fp = q_vdb_pkg_fopenat_ro(pkg_ctx, "CONTENTS"); if (fp == NULL) return 1; while (getline(&state->buf, &state->buflen, fp) != -1) { contents_entry *e; e = contents_parse_line(state->buf); if (!e) continue; if (!state->show_dbg) { if ((strncmp(e->name, "/usr/lib/debug", 14) == 0 || strncmp(e->name, "/usr/src/debug", 14) == 0) && (e->name[14] == '/' || e->name[14] == '\0')) continue; } switch (e->type) { case CONTENTS_DIR: if (state->show_dir) printf("%s%s%s/\n", verbose > 1 ? YELLOW : "" , e->name, verbose > 1 ? NORM : ""); break; case CONTENTS_OBJ: if (state->show_obj) printf("%s%s%s\n", verbose > 1 ? WHITE : "" , e->name, verbose > 1 ? NORM : ""); break; case CONTENTS_SYM: if (state->show_sym) { if (verbose) printf("%s%s -> %s%s\n", verbose > 1 ? CYAN : "", e->name, e->sym_target, NORM); else printf("%s\n", e->name); } break; } } fclose(fp); return 1; } int qlist_main(int argc, char **argv) { struct qlist_opt_state state = { .argc = argc, .argv = argv, .exact = false, .all = false, .just_pkgname = false, .show_dir = false, .show_obj = false, .show_repo = false, .show_sym = false, .show_slots = 0, .show_umap = false, .show_dbg = false, .columns = false, .sets = NULL, .buflen = _Q_PATH_MAX, }; int i, ret; while ((i = GETOPT_LONG(QLIST, qlist, "")) != -1) { switch (i) { COMMON_GETOPTS_CASES(qlist) case 'a': state.all = true; /* fall through */ case 'I': state.just_pkgname = true; break; case 'S': state.just_pkgname = true; ++state.show_slots; break; case 'R': state.just_pkgname = state.show_repo = true; break; case 'U': state.just_pkgname = state.show_umap = true; break; case 'e': state.exact = true; break; case 'd': state.show_dir = true; break; case 128: state.show_dbg = true; break; case 'o': state.show_obj = true; break; case 's': state.show_sym = true; break; case 'c': state.columns = true; break; case 'f': break; } } if (state.columns) verbose = 0; /* if not set to zero; atom wont be exploded; segv */ /* default to showing syms and objs */ if (!state.show_dir && !state.show_obj && !state.show_sym) state.show_obj = state.show_sym = true; if (argc == optind && !state.just_pkgname) qlist_usage(EXIT_FAILURE); state.buf = xmalloc(state.buflen); state.atoms = xcalloc(argc - optind, sizeof(*state.atoms)); ret = q_vdb_foreach_pkg_sorted(qlist_cb, &state); free(state.buf); for (i = optind; i < state.argc; ++i) if (state.atoms[i - optind]) atom_implode(state.atoms[i - optind]); free(state.atoms); /* The return value is whether we matched anything. */ return ret ? EXIT_SUCCESS : EXIT_FAILURE; } #else DEFINE_APPLET_STUB(qlist) #endif