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

libdpkg: Implement a centralized list of architectures

The goal is to avoid having hundreds of strdup'ed architecture strings in
memory when we can instead store some pointers to a new structure. Those
structs are singletons and it's thus now possible to compare the pointers
directly if we want to compare two architectures. Furthermore the struct
allows us to store conveniently some basic information like if the
architecture is the native one, a foreign one, a special one (all/any),
an illegal one, no architecture present or something completely unknown.

Sponsored-by: Linaro Limited

[guillem@debian.org:
 - Add an explicit test for dpkg_arch_reset(), and do not rely on
   pkg_db_reset().
 - Check that the default list has exactly 3 items instead of >= 3.
 - Change call to dpkg_arch_reset() before nffreeall().
 - Rename dpkg_arch_reset() to dpkg_arch_reset_list(). ]

Signed-off-by: Guillem Jover <guillem@debian.org>
Raphaël Hertzog лет назад: 15
Родитель
Сommit
04c0362979
8 измененных файлов с 373 добавлено и 0 удалено
  1. 2 0
      lib/dpkg/Makefile.am
  2. 174 0
      lib/dpkg/arch.c
  3. 51 0
      lib/dpkg/arch.h
  4. 7 0
      lib/dpkg/libdpkg.Versions
  5. 3 0
      lib/dpkg/pkg-db.c
  6. 1 0
      lib/dpkg/test/.gitignore
  7. 1 0
      lib/dpkg/test/Makefile.am
  8. 134 0
      lib/dpkg/test/t-arch.c

+ 2 - 0
lib/dpkg/Makefile.am

@@ -25,6 +25,7 @@ lib_LIBRARIES = libdpkg.a
 libdpkg_a_SOURCES = \
 libdpkg_a_SOURCES = \
 	dlist.h \
 	dlist.h \
 	ar.c \
 	ar.c \
+	arch.c \
 	buffer.c \
 	buffer.c \
 	cleanup.c \
 	cleanup.c \
 	command.c \
 	command.c \
@@ -74,6 +75,7 @@ libdpkg_a_SOURCES = \
 
 
 pkginclude_HEADERS = \
 pkginclude_HEADERS = \
 	ar.h \
 	ar.h \
+	arch.h \
 	buffer.h \
 	buffer.h \
 	command.h \
 	command.h \
 	compress.h \
 	compress.h \

+ 174 - 0
lib/dpkg/arch.c

@@ -0,0 +1,174 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * arch.c - architecture database functions
+ *
+ * Copyright © 2011 Linaro Limited
+ * Copyright © 2011 Raphaël Hertzog <hertzog@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 <assert.h>
+#include <ctype.h>
+#include <string.h>
+
+#include <dpkg/i18n.h>
+#include <dpkg/ehandle.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/arch.h>
+
+/**
+ * Verify if the architecture name is valid.
+ *
+ * Returns NULL if the architecture name is valid. Otherwise it returns a
+ * string describing why it's not valid. Currently it ensures the name
+ * starts with an alphanumeric and is then composed of a combinations of
+ * dashes and alphanumerics.
+ *
+ * The function will abort if you pass it a NULL pointer.
+ *
+ * @param name The architectute name to verify.
+ */
+const char *
+dpkg_arch_name_is_illegal(const char *name)
+{
+	static char buf[150];
+	const char *p = name;
+
+	assert(name);
+	if (!*p)
+		return _("may not be empty string");
+	if (!isalnum(*p))
+		return _("must start with an alphanumeric");
+	while (*++p != '\0')
+		if (!isalnum(*p) && *p != '-')
+			break;
+	if (*p == '\0')
+		return NULL;
+
+	snprintf(buf, sizeof(buf), _("character `%c' not allowed (only "
+	                             "letters, digits and characters `%s')"),
+	         *p, "-");
+	return buf;
+}
+
+/* This is a special architecture used to guarantee we always have a valid
+ * structure to handle. */
+static struct dpkg_arch arch_item_none = {
+	.name = "",
+	.type = arch_none,
+	.next = NULL,
+};
+
+static struct dpkg_arch arch_item_any = {
+	.name = "any",
+	.type = arch_wildcard,
+	.next = NULL,
+};
+static struct dpkg_arch arch_item_all = {
+	.name = "all",
+	.type = arch_all,
+	.next = &arch_item_any,
+};
+static struct dpkg_arch arch_item_native = {
+	.name = ARCHITECTURE,
+	.type = arch_native,
+	.next = &arch_item_all,
+};
+static struct dpkg_arch *arch_list = &arch_item_native;
+
+static struct dpkg_arch *
+dpkg_arch_new(const char *name, enum arch_type type)
+{
+	struct dpkg_arch *new;
+
+	new = nfmalloc(sizeof(*new));
+	new->next = NULL;
+	new->name = nfstrsave(name);
+	new->type = type;
+
+	return new;
+}
+
+/**
+ * Retrieve the struct dpkg_arch for the given architecture.
+ *
+ * Create a new structure for the architecture if it's not yet known from
+ * the system, in that case it will have arch->type == arch_unknown, if the
+ * architecture is illegal it will have arch->type == arch_illegal and if
+ * name is NULL or an empty string then it will have arch->type == arch_none.
+ *
+ * @param name The architecture name.
+ */
+struct dpkg_arch *
+dpkg_arch_find(const char *name)
+{
+	struct dpkg_arch *arch, *last_arch = NULL;
+	enum arch_type type;
+
+	if (name == NULL || name[0] == '\0')
+		return &arch_item_none;
+
+	for (arch = arch_list; arch; arch = arch->next) {
+		if (strcmp(arch->name, name) == 0)
+			return arch;
+		last_arch = arch;
+	}
+
+	if (dpkg_arch_name_is_illegal(name))
+		type = arch_illegal;
+	else
+		type = arch_unknown;
+
+	arch = dpkg_arch_new(name, type);
+	last_arch->next = arch;
+
+	return arch;
+}
+
+/**
+ * Return the struct dpkg_arch corresponding to the native architecture.
+ */
+struct dpkg_arch *
+dpkg_arch_get_native(void)
+{
+	return &arch_item_native;
+}
+
+/**
+ * Return the complete list of architectures.
+ *
+ * In fact it returns the first item of the linked list and you can
+ * traverse the list by following arch->next until it's NULL.
+ */
+struct dpkg_arch *
+dpkg_arch_get_list(void)
+{
+	return arch_list;
+}
+
+/**
+ * Reset the list of architectures.
+ *
+ * Must be called before nffreeall() to ensure we don't point to
+ * unallocated memory.
+ */
+void
+dpkg_arch_reset_list(void)
+{
+	arch_item_any.next = NULL;
+}

+ 51 - 0
lib/dpkg/arch.h

@@ -0,0 +1,51 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * arch.h - architecture database functions
+ *
+ * Copyright © 2011 Linaro Limited
+ * Copyright © 2011 Raphaël Hertzog <hertzog@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_ARCH_H
+#define LIBDPKG_ARCH_H
+
+#include <dpkg/macros.h>
+
+DPKG_BEGIN_DECLS
+
+struct dpkg_arch {
+	struct dpkg_arch *next;
+	const char *name;
+	enum arch_type {
+		arch_none,
+		arch_all,
+		arch_native,
+		arch_foreign,
+		arch_wildcard,
+		arch_unknown,
+		arch_illegal,
+	} type;
+};
+
+const char *dpkg_arch_name_is_illegal(const char *name) DPKG_ATTR_NONNULL(1);
+struct dpkg_arch *dpkg_arch_find(const char *name);
+struct dpkg_arch *dpkg_arch_get_native(void);
+struct dpkg_arch *dpkg_arch_get_list(void);
+void dpkg_arch_reset_list(void);
+
+DPKG_END_DECLS
+
+#endif /* LIBDPKG_ARCH_H */

+ 7 - 0
lib/dpkg/libdpkg.Versions

@@ -179,6 +179,13 @@ LIBDPKG_PRIVATE {
 	versionsatisfied3;
 	versionsatisfied3;
 	parseversion;
 	parseversion;
 
 
+	# Architecture database
+	dpkg_arch_name_is_illegal;
+	dpkg_arch_find;
+	dpkg_arch_get_native;
+	dpkg_arch_get_list;
+	dpkg_arch_reset_list;
+
 	# Package struct handling
 	# Package struct handling
 	pkg_blank;
 	pkg_blank;
 	pkgbin_blank;
 	pkgbin_blank;

+ 3 - 0
lib/dpkg/pkg-db.c

@@ -28,6 +28,7 @@
 #include <dpkg/i18n.h>
 #include <dpkg/i18n.h>
 #include <dpkg/dpkg.h>
 #include <dpkg/dpkg.h>
 #include <dpkg/dpkg-db.h>
 #include <dpkg/dpkg-db.h>
+#include <dpkg/arch.h>
 
 
 /* This must always be a prime for optimal performance.
 /* This must always be a prime for optimal performance.
  * With 4093 buckets, we glean a 20% speedup, for 8191 buckets
  * With 4093 buckets, we glean a 20% speedup, for 8191 buckets
@@ -123,6 +124,8 @@ void
 pkg_db_reset(void)
 pkg_db_reset(void)
 {
 {
   int i;
   int i;
+
+  dpkg_arch_reset_list();
   nffreeall();
   nffreeall();
   npackages= 0;
   npackages= 0;
   for (i=0; i<BINS; i++) bins[i]= NULL;
   for (i=0; i<BINS; i++) bins[i]= NULL;

+ 1 - 0
lib/dpkg/test/.gitignore

@@ -1,4 +1,5 @@
 t-ar
 t-ar
+t-arch
 t-buffer
 t-buffer
 t-command
 t-command
 t-macros
 t-macros

+ 1 - 0
lib/dpkg/test/Makefile.am

@@ -23,6 +23,7 @@ check_PROGRAMS = \
 	t-command \
 	t-command \
 	t-varbuf \
 	t-varbuf \
 	t-ar \
 	t-ar \
+	t-arch \
 	t-version \
 	t-version \
 	t-pkginfo \
 	t-pkginfo \
 	t-pkg-list \
 	t-pkg-list \

+ 134 - 0
lib/dpkg/test/t-arch.c

@@ -0,0 +1,134 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * t-arch.c - test dpkg_arch implementation
+ *
+ * Copyright © 2011 Linaro Limited
+ * Copyright © 2011 Raphaël Hertzog <hertzog@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 <dpkg/test.h>
+#include <dpkg/arch.h>
+
+static void
+test_dpkg_arch_name_is_illegal(void)
+{
+	/* Test invalid architecture names. */
+	test_fail(dpkg_arch_name_is_illegal("") == NULL);
+	test_fail(dpkg_arch_name_is_illegal("-i386") == NULL);
+	test_fail(dpkg_arch_name_is_illegal(" i386") == NULL);
+	test_fail(dpkg_arch_name_is_illegal(":any") == NULL);
+	test_fail(dpkg_arch_name_is_illegal("amd64_test") == NULL);
+	test_fail(dpkg_arch_name_is_illegal("i386:test") == NULL);
+	test_fail(dpkg_arch_name_is_illegal("i386 amd64") == NULL);
+	test_fail(dpkg_arch_name_is_illegal("i386,amd64") == NULL);
+	test_fail(dpkg_arch_name_is_illegal("i386|amd64") == NULL);
+
+	/* Test valid architecture names. */
+	test_pass(dpkg_arch_name_is_illegal("i386") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("amd64") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("hurd-i386") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("kfreebsd-i386") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("kfreebsd-amd64") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("ia64") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("alpha") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("armel") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("hppa") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("mips") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("mipsel") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("powerpc") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("s390") == NULL);
+	test_pass(dpkg_arch_name_is_illegal("sparc") == NULL);
+}
+
+static void
+test_dpkg_arch_get_native(void)
+{
+	struct dpkg_arch *arch;
+
+	arch = dpkg_arch_get_native();
+	test_str(arch->name, ==, ARCHITECTURE);
+	test_pass(arch->type == arch_native);
+}
+
+static void
+test_dpkg_arch_get_list(void)
+{
+	struct dpkg_arch *arch;
+	int count = 1;
+
+	/* Must never return NULL. */
+	arch = dpkg_arch_get_list();
+	test_pass(arch != NULL);
+
+	while ((arch = arch->next))
+		count++;
+
+	/* The default list should contain 3 architectures. */
+	test_pass(count == 3);
+}
+
+static void
+test_dpkg_arch_find(void)
+{
+	struct dpkg_arch *arch;
+
+	/* Test existence and initial values of default architectures. */
+	arch = dpkg_arch_find("all");
+	test_pass(arch->type == arch_all);
+	arch = dpkg_arch_find(ARCHITECTURE);
+	test_pass(arch->type == arch_native);
+	arch = dpkg_arch_find("any");
+	test_pass(arch->type == arch_wildcard);
+
+	/* Empty architectures are marked none. */
+	arch = dpkg_arch_find(NULL);
+	test_pass(arch->type == arch_none);
+	test_str(arch->name, ==, "");
+	arch = dpkg_arch_find("");
+	test_pass(arch->type == arch_none);
+	test_str(arch->name, ==, "");
+
+	/* New valid architectures are marked unknown. */
+	arch = dpkg_arch_find("foobar");
+	test_pass(arch->type == arch_unknown);
+	test_str(arch->name, ==, "foobar");
+
+	/* New illegal architectures are marked illegal. */
+	arch = dpkg_arch_find("a:b");
+	test_pass(arch->type == arch_illegal);
+	test_str(arch->name, ==, "a:b");
+}
+
+static void
+test_dpkg_arch_reset_list(void)
+{
+	dpkg_arch_reset_list();
+
+	test_dpkg_arch_get_list();
+}
+
+void
+test(void)
+{
+	test_dpkg_arch_name_is_illegal();
+	test_dpkg_arch_get_native();
+	test_dpkg_arch_get_list();
+	test_dpkg_arch_find();
+	test_dpkg_arch_reset_list();
+}