|
|
@@ -0,0 +1,294 @@
|
|
|
+/*
|
|
|
+ * libdpkg - Debian packaging suite library routines
|
|
|
+ * pkg-spec.c - primitives for pkg specifier handling
|
|
|
+ *
|
|
|
+ * Copyright © 2011 Linaro Limited
|
|
|
+ * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org>
|
|
|
+ * Copyright © 2011-2012 Guillem Jover <guillem@debian.org>
|
|
|
+ *
|
|
|
+ * This is free software; you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
|
+ * (at your option) any later version.
|
|
|
+ *
|
|
|
+ * This 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 General Public License for more details.
|
|
|
+ *
|
|
|
+ * You should have received a copy of the GNU General Public License
|
|
|
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
+ */
|
|
|
+
|
|
|
+#include <config.h>
|
|
|
+#include <compat.h>
|
|
|
+
|
|
|
+#include <stdlib.h>
|
|
|
+#include <fnmatch.h>
|
|
|
+#include <string.h>
|
|
|
+
|
|
|
+#include <dpkg/i18n.h>
|
|
|
+#include <dpkg/dpkg.h>
|
|
|
+#include <dpkg/dpkg-db.h>
|
|
|
+#include <dpkg/arch.h>
|
|
|
+#include <dpkg/pkg-spec.h>
|
|
|
+
|
|
|
+static void
|
|
|
+pkg_spec_blank(struct pkg_spec *ps)
|
|
|
+{
|
|
|
+ ps->name = NULL;
|
|
|
+ ps->arch = NULL;
|
|
|
+
|
|
|
+ ps->name_is_pattern = false;
|
|
|
+ ps->arch_is_pattern = false;
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+pkg_spec_iter_blank(struct pkg_spec *ps)
|
|
|
+{
|
|
|
+ ps->pkg_iter = NULL;
|
|
|
+ ps->pkg_next = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+pkg_spec_init(struct pkg_spec *ps, enum pkg_spec_flags flags)
|
|
|
+{
|
|
|
+ ps->flags = flags;
|
|
|
+
|
|
|
+ pkg_spec_blank(ps);
|
|
|
+ pkg_spec_iter_blank(ps);
|
|
|
+}
|
|
|
+
|
|
|
+const char *
|
|
|
+pkg_spec_is_illegal(struct pkg_spec *ps)
|
|
|
+{
|
|
|
+ static char msg[1024];
|
|
|
+ const char *emsg;
|
|
|
+
|
|
|
+ if (!ps->name_is_pattern &&
|
|
|
+ (emsg = pkg_name_is_illegal(ps->name))) {
|
|
|
+ snprintf(msg, sizeof(msg),
|
|
|
+ _("illegal package name in specifier '%s%s%s': %s"),
|
|
|
+ ps->name, (ps->arch->type != arch_none) ? ":" : "",
|
|
|
+ ps->arch->name, emsg);
|
|
|
+ return msg;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((!ps->arch_is_pattern && ps->arch->type == arch_illegal) ||
|
|
|
+ ps->arch->type == arch_empty) {
|
|
|
+ emsg = dpkg_arch_name_is_illegal(ps->arch->name);
|
|
|
+ snprintf(msg, sizeof(msg),
|
|
|
+ _("illegal architecture name in specifier '%s:%s': %s"),
|
|
|
+ ps->name, ps->arch->name, emsg);
|
|
|
+ return msg;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If we have been requested a single instance, check that the
|
|
|
+ * package does not contain other instances. */
|
|
|
+ if (!ps->arch_is_pattern && ps->flags & psf_arch_def_single) {
|
|
|
+ struct pkgset *set;
|
|
|
+
|
|
|
+ set = pkg_db_find_set(ps->name);
|
|
|
+
|
|
|
+ /* Single instancing only applies with no architecture. */
|
|
|
+ if (ps->arch->type == arch_none &&
|
|
|
+ pkgset_installed_instances(set) > 1) {
|
|
|
+ snprintf(msg, sizeof(msg),
|
|
|
+ _("ambiguous package name '%s' with more "
|
|
|
+ "than one installed instance"), ps->name);
|
|
|
+ return msg;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static const char *
|
|
|
+pkg_spec_prep(struct pkg_spec *ps, char *pkgname, const char *archname)
|
|
|
+{
|
|
|
+ ps->name = pkgname;
|
|
|
+ ps->arch = dpkg_arch_find(archname);
|
|
|
+
|
|
|
+ ps->name_is_pattern = false;
|
|
|
+ ps->arch_is_pattern = false;
|
|
|
+
|
|
|
+ /* Detect if we have patterns and/or illegal names. */
|
|
|
+ if ((ps->flags & psf_patterns) && strpbrk(ps->name, "*[?\\"))
|
|
|
+ ps->name_is_pattern = true;
|
|
|
+
|
|
|
+ if ((ps->flags & psf_patterns) && strpbrk(ps->arch->name, "*[?\\"))
|
|
|
+ ps->arch_is_pattern = true;
|
|
|
+
|
|
|
+ return pkg_spec_is_illegal(ps);
|
|
|
+}
|
|
|
+
|
|
|
+const char *
|
|
|
+pkg_spec_set(struct pkg_spec *ps, const char *pkgname, const char *archname)
|
|
|
+{
|
|
|
+ return pkg_spec_prep(ps, m_strdup(pkgname), archname);
|
|
|
+}
|
|
|
+
|
|
|
+const char *
|
|
|
+pkg_spec_parse(struct pkg_spec *ps, const char *str)
|
|
|
+{
|
|
|
+ char *pkgname, *archname;
|
|
|
+
|
|
|
+ archname = strchr(str, ':');
|
|
|
+ if (archname == NULL) {
|
|
|
+ pkgname = m_strdup(str);
|
|
|
+ } else {
|
|
|
+ pkgname = m_strndup(str, archname - str);
|
|
|
+ archname++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return pkg_spec_prep(ps, pkgname, archname);
|
|
|
+}
|
|
|
+
|
|
|
+static bool
|
|
|
+pkg_spec_match_name(struct pkg_spec *ps, const char *name)
|
|
|
+{
|
|
|
+ if (ps->name_is_pattern)
|
|
|
+ return (fnmatch(ps->name, name, 0) == 0);
|
|
|
+ else
|
|
|
+ return (strcmp(ps->name, name) == 0);
|
|
|
+}
|
|
|
+
|
|
|
+static bool
|
|
|
+pkg_spec_match_arch(struct pkg_spec *ps, struct pkginfo *pkg,
|
|
|
+ const struct dpkg_arch *arch)
|
|
|
+{
|
|
|
+ if (ps->arch_is_pattern)
|
|
|
+ return (fnmatch(ps->arch->name, arch->name, 0) == 0);
|
|
|
+ else if (ps->arch->type != arch_none) /* !arch_is_pattern */
|
|
|
+ return (ps->arch == arch);
|
|
|
+
|
|
|
+ /* No arch specified. */
|
|
|
+ switch (ps->flags & psf_arch_def_mask) {
|
|
|
+ case psf_arch_def_single:
|
|
|
+ return pkgset_installed_instances(pkg->set) <= 1;
|
|
|
+ case psf_arch_def_wildcard:
|
|
|
+ return true;
|
|
|
+ default:
|
|
|
+ internerr("unknown psf_arch_def_* flags %d in pkg_spec",
|
|
|
+ ps->flags & psf_arch_def_mask);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+bool
|
|
|
+pkg_spec_match_pkg(struct pkg_spec *ps, struct pkginfo *pkg,
|
|
|
+ struct pkgbin *pkgbin)
|
|
|
+{
|
|
|
+ return (pkg_spec_match_name(ps, pkg->set->name) &&
|
|
|
+ pkg_spec_match_arch(ps, pkg, pkgbin->arch));
|
|
|
+}
|
|
|
+
|
|
|
+static struct pkginfo *
|
|
|
+pkg_spec_get_pkg(struct pkg_spec *ps)
|
|
|
+{
|
|
|
+ if (ps->arch->type == arch_none)
|
|
|
+ return pkg_db_find_singleton(ps->name);
|
|
|
+ else
|
|
|
+ return pkg_db_find_pkg(ps->name, ps->arch);
|
|
|
+}
|
|
|
+
|
|
|
+struct pkginfo *
|
|
|
+pkg_spec_parse_pkg(const char *str, struct dpkg_error *err)
|
|
|
+{
|
|
|
+ struct pkg_spec ps;
|
|
|
+ struct pkginfo *pkg;
|
|
|
+ const char *emsg;
|
|
|
+
|
|
|
+ pkg_spec_init(&ps, psf_arch_def_single);
|
|
|
+ emsg = pkg_spec_parse(&ps, str);
|
|
|
+ if (emsg) {
|
|
|
+ dpkg_put_error(err, "%s", emsg);
|
|
|
+ pkg = NULL;
|
|
|
+ } else {
|
|
|
+ pkg = pkg_spec_get_pkg(&ps);
|
|
|
+ }
|
|
|
+ pkg_spec_destroy(&ps);
|
|
|
+
|
|
|
+ return pkg;
|
|
|
+}
|
|
|
+
|
|
|
+struct pkginfo *
|
|
|
+pkg_spec_find_pkg(const char *pkgname, const char *archname,
|
|
|
+ struct dpkg_error *err)
|
|
|
+{
|
|
|
+ struct pkg_spec ps;
|
|
|
+ struct pkginfo *pkg;
|
|
|
+ const char *emsg;
|
|
|
+
|
|
|
+ pkg_spec_init(&ps, psf_arch_def_single);
|
|
|
+ emsg = pkg_spec_set(&ps, pkgname, archname);
|
|
|
+ if (emsg) {
|
|
|
+ dpkg_put_error(err, "%s", emsg);
|
|
|
+ pkg = NULL;
|
|
|
+ } else {
|
|
|
+ pkg = pkg_spec_get_pkg(&ps);
|
|
|
+ }
|
|
|
+ pkg_spec_destroy(&ps);
|
|
|
+
|
|
|
+ return pkg;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+pkg_spec_iter_init(struct pkg_spec *ps)
|
|
|
+{
|
|
|
+ if (ps->name_is_pattern)
|
|
|
+ ps->pkg_iter = pkg_db_iter_new();
|
|
|
+ else
|
|
|
+ ps->pkg_next = &pkg_db_find_set(ps->name)->pkg;
|
|
|
+}
|
|
|
+
|
|
|
+static struct pkginfo *
|
|
|
+pkg_spec_iter_next_pkgname(struct pkg_spec *ps)
|
|
|
+{
|
|
|
+ struct pkginfo *pkg;
|
|
|
+
|
|
|
+ while ((pkg = pkg_db_iter_next_pkg(ps->pkg_iter))) {
|
|
|
+ if (pkg_spec_match_pkg(ps, pkg, &pkg->installed))
|
|
|
+ return pkg;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static struct pkginfo *
|
|
|
+pkg_spec_iter_next_pkgarch(struct pkg_spec *ps)
|
|
|
+{
|
|
|
+ struct pkginfo *pkg;
|
|
|
+
|
|
|
+ while ((pkg = ps->pkg_next)) {
|
|
|
+ ps->pkg_next = pkg->arch_next;
|
|
|
+
|
|
|
+ if (pkg_spec_match_arch(ps, pkg, pkg->installed.arch))
|
|
|
+ return pkg;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+struct pkginfo *
|
|
|
+pkg_spec_iter_next_pkg(struct pkg_spec *ps)
|
|
|
+{
|
|
|
+ if (ps->name_is_pattern)
|
|
|
+ return pkg_spec_iter_next_pkgname(ps);
|
|
|
+ else
|
|
|
+ return pkg_spec_iter_next_pkgarch(ps);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+pkg_spec_iter_destroy(struct pkg_spec *ps)
|
|
|
+{
|
|
|
+ pkg_db_iter_free(ps->pkg_iter);
|
|
|
+ pkg_spec_iter_blank(ps);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+pkg_spec_destroy(struct pkg_spec *ps)
|
|
|
+{
|
|
|
+ free(ps->name);
|
|
|
+ pkg_spec_blank(ps);
|
|
|
+ pkg_spec_iter_destroy(ps);
|
|
|
+}
|