The following commit has been merged in the master branch:
commit 58e0ed5e77149b4e1569e448daa06c1345c71e6c
Author: Guillem Jover <guil...@debian.org>
Date:   Sat Feb 4 19:41:26 2012 +0100

    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 <hert...@debian.org>
    Patch-sponsored-by: Linaro Limited
    
    Signed-off-by: Guillem Jover <guil...@debian.org>

diff --git a/lib/dpkg/Makefile.am b/lib/dpkg/Makefile.am
index e2c8aae..f2bccb0 100644
--- a/lib/dpkg/Makefile.am
+++ b/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 \
diff --git a/lib/dpkg/libdpkg.map b/lib/dpkg/libdpkg.map
index 555ab97..bc30b2d 100644
--- a/lib/dpkg/libdpkg.map
+++ b/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;
diff --git a/lib/dpkg/pkg-spec.c b/lib/dpkg/pkg-spec.c
new file mode 100644
index 0000000..6f17e1e
--- /dev/null
+++ b/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 <hert...@debian.org>
+ * Copyright © 2011-2012 Guillem Jover <guil...@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);
+}
diff --git a/lib/dpkg/pkg-spec.h b/lib/dpkg/pkg-spec.h
new file mode 100644
index 0000000..d754da7
--- /dev/null
+++ b/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 <hert...@debian.org>
+ * Copyright © 2011-2012 Guillem Jover <guil...@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
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ad9e259..275eaee 100644
--- a/po/POTFILES.in
+++ b/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

-- 
dpkg's main repository


-- 
To UNSUBSCRIBE, email to debian-dpkg-cvs-requ...@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org

Reply via email to