/* $Id: packdep.c,v 1.13 2002/06/23 20:28:31 richdawe Exp $ */ /* * packdep.c - Package dependency functions for pakke * Copyright (C) 2000-2002 by Richard Dawe * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "common.h" #include #include #include #include /* Browser context for dep_exists_browser() */ typedef struct { const PACKAGE_INFO *package; int dep_type; PACKAGE_INFO **matched_package; PACKAGE_DEP **matched_dep; int matched_dep_idx; int matched_dep_max; } dep_exists_context_t; /* ------------ * - find_dep - * ------------ */ /* This package browser adds cross-references to a package list. It does this * by matching dependencies with package list entries that satisfy them. */ static int find_dep(PACKAGE_INFO *p, void *context) { PACKAGE_INFO * pack_list = (PACKAGE_INFO *)context; PACKAGE_INFO * pack_ptr; PACKAGE_DEP ** deps; PACKAGE_DEP * dep; int matched_provision = 0; int ret; /* When searching for provides, these contain pointers to the dependencies * of the current package from the package list. */ PACKAGE_DEP ** deps_p; PACKAGE_DEP * dep_p; for (deps = p->deps; (deps != NULL) && (*deps != NULL); deps++) { dep = *deps; for (pack_ptr = pack_list; pack_ptr != NULL; pack_ptr = pack_ptr->q_forw) { /* Check that we're not trying to match the package with itself. */ if (pack_ptr == p) continue; /* * The following describes how the dependencies are matched to * the packages that satisfy them. The dependencies fall into * the following categories: * * - 'provides' dependencies are not really dependencies, so they are * always considered to be satisfied. * * - 'requires' dependencies can depend on a real package or a virtual * package. A virtual package contains 'provides'. This means checking * 'requires' dependencies is carried out in two steps - virtual * packages (by looking at 'provides' dependencies) and then real * packages (by looking at the name, version of the package). * * When looking for a match on a package or 'provides' dependency: * * - the names are compared; * - if no version information is specified in the dependency, then * a name match is enough, otherwise the versions are also compared. * * For 'provides' dependencies, the qualifier is also checked. A package * may require a particular subset of functionality provided. */ /* - Requires dependencies - */ /* Requires dependencies may require something that is provided using * 'provides'. So, scan the 'provides' list of the package. */ matched_provision = 0; for (deps_p = pack_ptr->deps; (deps_p != NULL) && (*deps_p != NULL); deps_p++) { dep_p = *deps_p; /* Must be a provide */ if (dep_p->dep_type != PACKAGE_DEP_PROVIDES) continue; /* Names must match */ if (package_namecmp(dep_p->name, dep->name) != 0) continue; /* Version info? */ if ((dep->op == DEP_OP_NONE) && !dep->version.has_version) { /* No => match */ dep->dep = pack_ptr; matched_provision = 1; break; } /* Compare versions */ ret = package_vercmp(&dep_p->version, &dep->version); /* Check whether the dependency is satisfied by the version * comparison & operator combination. */ switch(dep->op) { case DEP_OP_EQUAL: ret = (ret == 0); break; case DEP_OP_LESSEQUAL: ret = (ret <= 0); break; case DEP_OP_GREATEREQUAL: ret = (ret >= 0); break; case DEP_OP_NOTEQUAL: ret = (ret != 0); break; case DEP_OP_LESS: ret = (ret < 0); break; case DEP_OP_GREATER: ret = (ret > 0); break; /* Fail by default */ default: ret = 0; break; } if (!ret) continue; /* Qualifier? */ if (dep_p->qualifier == NULL) { /* None required, full feature */ dep->dep = pack_ptr; matched_provision = 1; break; } else { if (package_namecmp(dep_p->qualifier, dep->qualifier) == 0) { dep->dep = pack_ptr; matched_provision = 1; break; } } } if (matched_provision) { /* A provision satisfied this dependency, so move onto next dep. */ continue; } /* - Normal dependencies, aka "the rest" ;) - */ /* Skip provides */ if (dep->dep_type == PACKAGE_DEP_PROVIDES) continue; /* Names must match */ if (package_namecmp(pack_ptr->name, dep->name) != 0) continue; /* Version info? */ if ((dep->op == DEP_OP_NONE) && !dep->version.has_version) { /* No => match */ dep->dep = pack_ptr; break; } /* Compare versions */ ret = package_vercmp(&pack_ptr->version, &dep->version); /* Check whether the dependency is satisfied by the version * comparison & operator combination. */ switch(dep->op) { case DEP_OP_EQUAL: ret = (ret == 0); break; case DEP_OP_LESSEQUAL: ret = (ret <= 0); break; case DEP_OP_GREATEREQUAL: ret = (ret >= 0); break; case DEP_OP_NOTEQUAL: ret = (ret != 0); break; case DEP_OP_LESS: ret = (ret < 0); break; case DEP_OP_GREATER: ret = (ret > 0); break; /* Fail by default */ default: ret = 0; break; } if (!ret) continue; /* Match found */ dep->dep = pack_ptr; break; } } /* Done */ return 0; } /* ----------------- * - packlist_xref - * ----------------- */ /* Generate cross-references between packages in the package list. */ int packlist_xref (PACKAGE_INFO *packages) { packlist_browse(packages, find_dep, packages); return(1); } /* ---------------- * - package_xref - * ---------------- */ /* Generate cross-references between package and packages in the package list * 'packages'. */ int package_xref (PACKAGE_INFO *packages, PACKAGE_INFO *package) { /* Pass the package list in as the context. 'package' is the package list * to cross-reference, a single entry list. */ packlist_browse(package, find_dep, packages); return(1); } /* ---------------------- * - package_check_deps - * ---------------------- */ /* * This returns 1 when the spec'd dependencies are satisfied, 0 otherwise. * Up to failed_dep_max failures are returned in the (user supplied) array * failed. These dependency structures have pointers pointing to the packages * in 'packages' that failed, for later diagnosis/analysis. * * This function should only be called after packlist_xref() has been called * on the package list. */ int package_check_deps (const PACKAGE_INFO *package, const int dep_type, PACKAGE_DEP **failed_dep, const int failed_dep_max) { PACKAGE_DEP **deps = NULL; int failed_dep_idx = 0; int ret = 1; /* OK by default */ int j; /* pflag = whether the packages in the dep should be present or not: * 1 = yes, 0 = no. */ int pflag; /* Clear the failed arrays */ for (j = 0; j < failed_dep_max; j++) { failed_dep[j] = NULL; } /* Check what dep_type we have. If the dep_type requires no dependency * checking, return appropriate status. Set the presence flag, based on * the dep_type. */ switch(dep_type) { case PACKAGE_DEP_REQUIRES: case PACKAGE_DEP_DEPENDS_ON: pflag = 1; break; case PACKAGE_DEP_CONFLICTS_WITH: case PACKAGE_DEP_REPLACES: case PACKAGE_DEP_INSTALL_BEFORE: pflag = 0; break; case PACKAGE_DEP_NONE: case PACKAGE_DEP_INSTALL_AFTER: case PACKAGE_DEP_PROVIDES: /* Handle no-op dep_types immediately. */ return(1); break; default: /* Don't understand dep_type => fail. */ return(0); break; } deps = ((PACKAGE_INFO *) package)->deps; /* No deps => success */ if (deps == NULL) return(1); /* Check the dependencies. */ for (j = 0; deps[j] != NULL; j++) { /* Right dep type? */ if (deps[j]->dep_type != dep_type) continue; if ( ((deps[j]->dep != NULL) && !pflag) /* present & shouldn't be */ || ((deps[j]->dep == NULL) && pflag) /* not present & should be */) { /* Add to the list of failed deps, if there's room. */ if (failed_dep_idx < failed_dep_max) { ret = 0; failed_dep[failed_dep_idx] = deps[j]; failed_dep_idx++; } else { /* No more room, so abort */ break; } } } /* Done */ return(ret); } /* ---------------------- * - dep_exists_browser - * ---------------------- */ static int dep_exists_browser (PACKAGE_INFO *p, void *context_in) { dep_exists_context_t *context = (dep_exists_context_t *) context_in; int j; /* Skip package that we're checking for. */ if (p == context->package) return(0); /* No room, so abort */ if (context->matched_dep_idx > context->matched_dep_max) return(0); /* Check deps of the right type */ for (j = 0; p->deps[j] != NULL; j++) { if (p->deps[j]->dep_type != context->dep_type) continue; if (p->deps[j]->dep != context->package) continue; /* Found a match */ if (context->matched_dep_idx > context->matched_dep_max) { /* No room, so abort */ break; } context->matched_package[context->matched_dep_idx] = p; context->matched_dep[context->matched_dep_idx] = p->deps[j]; context->matched_dep_idx++; } return(0); } /* ---------------------- * - package_dep_exists - * ---------------------- */ /* * This function checks whether any of the packages in 'packages' has * a dependency on 'package'. It does this by comparing pointers, so * 'packages' must be a cross-referenced package list. Any matches are * put in 'matched_dep'. * * If a dependency with type 'dep_type' exists, 1 is returned, else 0. */ int package_dep_exists (PACKAGE_INFO *packages, PACKAGE_INFO *package, const int dep_type, PACKAGE_INFO **matched_package, PACKAGE_DEP **matched_dep, const int matched_dep_max) { dep_exists_context_t context; int i; /* Clear the matched arrays */ for (i = 0; i < matched_dep_max; i++) { matched_package[i] = NULL; matched_dep[i] = NULL; } /* Set up context */ memset(&context, 0, sizeof(context)); context.package = package; context.dep_type = dep_type; context.matched_package = matched_package; context.matched_dep = matched_dep; context.matched_dep_idx = 0; context.matched_dep_max = matched_dep_max; /* Use a browser function to enumerate all packages. */ packlist_browse(packages, dep_exists_browser, (void *) &context); /* Got any matches? */ if (matched_dep[0] != NULL) return(1); return(0); }