/* $Id: depends.c,v 1.44 2009/05/07 21:22:52 imil Exp $ */ /* * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Emile "iMil" Heitor . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include "pkg_dry.h" /* order: <=1.0 >=2.0 ==1.2 -2.10 * ^ ^ ^ ^ */ static const char *extchars = "<>=-"; static Plisthead *plisthead = NULL; void free_deptree(Deptreehead *deptreehead) { Pkgdeptree *pdp; if (deptreehead == NULL) return; while (!SLIST_EMPTY(deptreehead)) { pdp = SLIST_FIRST(deptreehead); SLIST_REMOVE_HEAD(deptreehead, next); XFREE(pdp->depname); XFREE(pdp); } } /* match dependency extension following this order: <>=- */ char * match_dep_ext(char *depname, const char *ext) { char *pext, *p, *pdep = NULL; XSTRDUP(pext, ext); p = pext; for (; *pext != 0; pext++) if ((pdep = strrchr(depname, *pext)) != NULL) break; XFREE(p); return pdep; } /* basic full package format detection */ int exact_pkgfmt(const char *pkgname) { char *p; if ((p = strrchr(pkgname, '-')) == NULL) return 0; p++; /* naive assumption, will fail with foo-100bar, hopefully, there's * only a few packages needing to be fully specified */ if (!isdigit((int)*p)) return 0; return 1; } /* end an expression with a delimiter */ char * end_expr(char *str) { Pkglist *pkglist; char *expr, *p; /* enough space to add a delimiter */ XMALLOC(expr, (strlen(str) + 2) * sizeof(char)); XSTRCPY(expr, str); /* really don't want to fight with {foo,bar>=2.0}, rely on pkg_match() * XXX: this makes us load pkg_summary db in full_dep_tree() */ if (strpbrk(expr, "*?[{") != NULL) { SLIST_FOREACH(pkglist, plisthead, next) { if (pkg_match(expr, pkglist->pkgname)) { XFREE(expr); XSTRDUP(expr, pkglist->pkgname); break; } } } /* simple package/dependency case, parse string backwards * until we find a delimiter: foo>=1.0 */ if ((p = match_dep_ext(expr, extchars)) != NULL) { *p++ = DELIMITER; /* terminate expr */ *p = '\0'; } return expr; } /* sqlite callback * DIRECT_DEPS or REVERSE_DEPS result, feeds a Pkgdeptree SLIST * Deptreehead is the head of Pkgdeptree */ static int ddb_rec_direct_deps(void *param, int argc, char **argv, char **colname) { int argvlen, matchlen; char *depname, *pkgname, *p; Pkgdeptree *deptree, *pdp; Deptreehead *pdphead = (Deptreehead *)param; if (argv != NULL) { depname = end_expr(argv[0]); /* foo| */ argvlen = strlen(depname); /* dependency already recorded, do not insert on list */ SLIST_FOREACH(pdp, pdphead, next) { matchlen = strlen(pdp->matchname) + 2; /* +2 = DELIMITER + \0 */ XMALLOC(pkgname, matchlen * sizeof(char)); snprintf(pkgname, matchlen, "%s%c", pdp->matchname, DELIMITER); if (strncmp(pkgname, depname, argvlen) == 0) { XFREE(pkgname); XFREE(depname); /* proceed to next result */ return DDB_OK; } XFREE(pkgname); } if ((p = strrchr(depname, DELIMITER)) != NULL) *p = '\0'; XMALLOC(deptree, sizeof(Pkgdeptree)); XSTRDUP(deptree->depname, argv[0]); XSTRDUP(deptree->matchname, depname); deptree->computed = 0; deptree->level = 0; /* used in LOCAL_REVERSE_DEPS / autoremove.c */ if (argc > 1 && argv[1] != NULL) deptree->pkgkeep = 1; else deptree->pkgkeep = 0; SLIST_INSERT_HEAD(pdphead, deptree, next); XFREE(depname); return DDB_OK; } return DDB_ERR; } /* recursively parse dependencies: this is our central function */ void full_dep_tree(const char *pkgname, const char *depquery, Deptreehead *pdphead, const char *caller, int level) { Pkgdeptree *pdp; char query[BUFSIZ]; snprintf(query, BUFSIZ, depquery, pkgname); if (plisthead == NULL) { if (strcasestr(depquery, "SELECT REMOTE") != NULL) { /* querying remote packages, load remote packages list */ plisthead = rec_pkglist(REMOTE_PKGS_QUERY); /* first package to recurse on and exact pkg name, this is an * exact match due to many versions of the package */ if (exact_pkgfmt(pkgname)) snprintf(query, BUFSIZ, EXACT_DIRECT_DEPS, pkgname); } else /* querying local packages, load local packages list */ plisthead = rec_pkglist(LOCAL_PKGS_QUERY); /* first call, set recursion level to 1 */ level = 1; } if (drydb_doquery(query, ddb_rec_direct_deps, pdphead) == 0) { /* record dependency level for installation */ SLIST_FOREACH(pdp, pdphead, next) if (!pdp->level) { pdp->level = level; } SLIST_FOREACH(pdp, pdphead, next) { if (!pdp->computed) { /* been parsed */ pdp->computed = 1; #if 0 printf("p: %s, l: %d\n", pdp->depname, pdp->level); #endif full_dep_tree(pdp->matchname, depquery, pdphead, __func__, level + 1); } } /* SLIST_FOREACH */ } /* XXX: freed list is allocated for end_expr */ if (strncmp(caller, __func__, strlen(caller)) != 0) { free_pkglist(plisthead); XFREE(plisthead); } } void show_direct_depends(const char *pkgname) { char query[BUFSIZ]; Pkgdeptree *pdp; Deptreehead deptreehead; if ((plisthead = rec_pkglist(REMOTE_PKGS_QUERY)) == NULL) { printf("empty available package list.\n"); return; } if (count_samepkg(plisthead, pkgname) < 2) { SLIST_INIT(&deptreehead); if (exact_pkgfmt(pkgname)) snprintf(query, BUFSIZ, EXACT_DIRECT_DEPS, pkgname); else snprintf(query, BUFSIZ, DIRECT_DEPS, pkgname); if (drydb_doquery(query, ddb_rec_direct_deps, &deptreehead) == 0) { printf("direct dependencies for %s\n", pkgname); SLIST_FOREACH(pdp, &deptreehead, next) { printf("\t%s\n", pdp->depname); } free_deptree(&deptreehead); } } free_pkglist(plisthead); } void show_full_dep_tree(const char *pkgname, const char *depquery, const char *msg) { Pkgdeptree *pdp; Deptreehead deptreehead; /* replacement for SLIST_HEAD() */ int count; plisthead = rec_pkglist(REMOTE_PKGS_QUERY); count = count_samepkg(plisthead, pkgname); /* free plisthead now so it is NULL for full_dep_tree() */ free_pkglist(plisthead); XFREE(plisthead); if (count > 1) return; SLIST_INIT(&deptreehead); printf(msg, pkgname); full_dep_tree(pkgname, depquery, &deptreehead, __func__, 0); SLIST_FOREACH(pdp, &deptreehead, next) printf("\t%s\n", pdp->depname); free_deptree(&deptreehead); }