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