/* * infopkg.c - query the package database * https://rawnix.org * * Copyright (c) 2025 zorz@gmx.com * SPDX-License-Identifier: BSD-2-Clause */ #include "db.h" #include "archive.h" #include "util.h" #include #include #include #include #include #include static void usage(void) { fprintf(stderr, "usage: infopkg [options]\n" " -i list all installed packages\n" " -l list files owned by package or in archive\n" " -o find which package owns a file (regex)\n" " -f print footprint of package archive\n" " -r alternative installation root\n" ); exit(1); } static int pkg_cmp(const void *a, const void *b) { return strcmp((*(pkg_t **)a)->name, (*(pkg_t **)b)->name); } int main(int argc, char **argv) { int opt_installed = 0, opt_list = 0; int opt_owner = 0, opt_footprint = 0; const char *opt_root = NULL, *opt_arg = NULL; const char *root; pkgdb_t *db; pkg_t *p, **sorted; size_t i, j, count; int c; while ((c = getopt(argc, argv, "il:o:f:r:vh")) != -1) { switch (c) { case 'i': opt_installed = 1; break; case 'l': opt_list = 1; opt_arg = optarg; break; case 'o': opt_owner = 1; opt_arg = optarg; break; case 'f': opt_footprint = 1; opt_arg = optarg; break; case 'r': opt_root = optarg; break; case 'v': printf("infopkg (mkpkg) %s\n", MKPKG_VERSION); return 0; case 'h': usage(); /* FALLTHROUGH */ default: usage(); } } if (opt_installed + opt_list + opt_owner + opt_footprint == 0) errx(1, "no operation specified — try -h"); if (opt_installed + opt_list + opt_owner + opt_footprint > 1) errx(1, "only one operation at a time"); /* footprint does not need the database */ if (opt_footprint) { if (!file_exists(opt_arg)) errx(1, "file not found: %s", opt_arg); return arc_footprint(opt_arg); } root = opt_root ? opt_root : (getenv("PKG_ROOT") ? getenv("PKG_ROOT") : "/"); db = db_open(root, 0); /* shared lock — read only */ if (opt_installed) { count = 0; for (p = db->head; p; p = p->next) count++; if (count == 0) { db_close(db); return 0; } sorted = xmalloc(count * sizeof(pkg_t *)); i = 0; for (p = db->head; p; p = p->next) sorted[i++] = p; qsort(sorted, count, sizeof(pkg_t *), pkg_cmp); for (i = 0; i < count; i++) printf("%s %s\n", sorted[i]->name, sorted[i]->version); free(sorted); } else if (opt_list) { p = db_find(db, opt_arg); if (p) { /* installed package — list from database */ strarray_sort(p->files); for (i = 0; i < p->files->count; i++) printf("%s\n", p->files->items[i]); } else if (file_exists(opt_arg)) { /* package archive on disk */ char *name, *version; strarray_t *files; if (arc_list(opt_arg, &name, &version, &files) < 0) { db_close(db); return 1; } strarray_sort(files); for (i = 0; i < files->count; i++) printf("%s\n", files->items[i]); strarray_free(files); free(name); free(version); } else { errx(1, "%s is not installed and not a package file", opt_arg); } } else if (opt_owner) { regex_t re; int found = 0; char filepath[PATH_MAX_LEN]; if (regcomp(&re, opt_arg, REG_EXTENDED | REG_NOSUB) != 0) errx(1, "invalid regex: %s", opt_arg); for (p = db->head; p; p = p->next) { for (j = 0; j < p->files->count; j++) { /* match against /path as stored, with leading / prepended */ snprintf(filepath, sizeof(filepath), "/%s", p->files->items[j]); if (regexec(&re, filepath, 0, NULL, 0) == 0) { printf("%-20s %s\n", p->name, p->files->items[j]); found = 1; } } } regfree(&re); if (!found) puts("no owner found"); } db_close(db); return 0; }