Просмотр исходного кода

libdpkg: Add functions to handle package specifiers

Package specifiers are package names qualified with an architecture
string ("pkgname:arch").

If the package specifier uses patterns, it's possible to iterate the list
of packages matching the specifier. Otherwise it's possible to retrieve
the corresponding package from the database.

Based-on-patch-by: Raphaël Hertzog <hertzog@debian.org>
Patch-sponsored-by: Linaro Limited

Signed-off-by: Guillem Jover <guillem@debian.org>
Guillem Jover лет назад: 14
Родитель
Сommit
58e0ed5e77
5 измененных файлов с 391 добавлено и 0 удалено
  1. 2 0
      lib/dpkg/Makefile.am
  2. 13 0
      lib/dpkg/libdpkg.map
  3. 294 0
      lib/dpkg/pkg-spec.c
  4. 81 0
      lib/dpkg/pkg-spec.h
  5. 1 0
      po/POTFILES.in

+ 2 - 0
lib/dpkg/Makefile.am

@@ -60,6 +60,7 @@ libdpkg_a_SOURCES = \
 	pkg-list.c \
 	pkg-queue.c \
 	pkg-show.c \
+	pkg-spec.c \
 	progname.c \
 	progress.c \
 	string.c \
@@ -102,6 +103,7 @@ pkginclude_HEADERS = \
 	pkg-list.h \
 	pkg-queue.h \
 	pkg-show.h \
+	pkg-spec.h \
 	progname.h \
 	progress.h \
 	string.h \

+ 13 - 0
lib/dpkg/libdpkg.map

@@ -241,6 +241,19 @@ LIBDPKG_PRIVATE {
 	pkg_format_show;
 	pkg_format_free;
 
+	# Package specifiers
+	pkg_spec_is_illegal;
+	pkg_spec_init;
+	pkg_spec_destroy;
+	pkg_spec_set;
+	pkg_spec_parse;
+	pkg_spec_match_pkg;
+	pkg_spec_parse_pkg;
+	pkg_spec_find_pkg;
+	pkg_spec_iter_init;
+	pkg_spec_iter_next_pkg;
+	pkg_spec_iter_destroy;
+
 	# Package in-core database functions
 	pkg_db_find;
 	pkg_db_find_set;

+ 294 - 0
lib/dpkg/pkg-spec.c

@@ -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);
+}

+ 81 - 0
lib/dpkg/pkg-spec.h

@@ -0,0 +1,81 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * pkg-spec.h - 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/>.
+ */
+
+#ifndef LIBDPKG_PKG_SPEC_H
+#define LIBDPKG_PKG_SPEC_H
+
+#include <stdbool.h>
+
+#include <dpkg/macros.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/error.h>
+#include <dpkg/arch.h>
+
+DPKG_BEGIN_DECLS
+
+struct pkg_spec {
+	char *name;
+	const struct dpkg_arch *arch;
+
+	enum pkg_spec_flags {
+		/* Recognize glob patterns. */
+		psf_patterns = 00001,
+
+		/* How to consider the lack of an arch qualifier. */
+		psf_arch_def_single = 00100,
+		psf_arch_def_wildcard = 00200,
+		psf_arch_def_mask = 00300,
+	} flags;
+
+	/* Members below are private state. */
+
+	bool name_is_pattern;
+	bool arch_is_pattern;
+
+	/* Used for the pkg_db iterator. */
+	struct pkgiterator *pkg_iter;
+	/* Used for the pkgset iterator. */
+	struct pkginfo *pkg_next;
+};
+
+void pkg_spec_init(struct pkg_spec *ps, enum pkg_spec_flags flags);
+void pkg_spec_destroy(struct pkg_spec *ps);
+
+const char *pkg_spec_is_illegal(struct pkg_spec *ps);
+
+const char *pkg_spec_set(struct pkg_spec *ps,
+                         const char *pkgname, const char *archname);
+const char *pkg_spec_parse(struct pkg_spec *ps, const char *str);
+bool pkg_spec_match_pkg(struct pkg_spec *ps,
+                        struct pkginfo *pkg, struct pkgbin *pkgbin);
+
+struct pkginfo *pkg_spec_parse_pkg(const char *str, struct dpkg_error *err);
+struct pkginfo *pkg_spec_find_pkg(const char *pkgname, const char *archname,
+                                  struct dpkg_error *err);
+
+void pkg_spec_iter_init(struct pkg_spec *ps);
+struct pkginfo *pkg_spec_iter_next_pkg(struct pkg_spec *ps);
+void pkg_spec_iter_destroy(struct pkg_spec *ps);
+
+DPKG_END_DECLS
+
+#endif

+ 1 - 0
po/POTFILES.in

@@ -29,6 +29,7 @@ lib/dpkg/pkg-format.c
 lib/dpkg/pkg-list.c
 lib/dpkg/pkg-queue.c
 lib/dpkg/pkg-show.c
+lib/dpkg/pkg-spec.c
 lib/dpkg/progress.c
 lib/dpkg/string.c
 lib/dpkg/subproc.c