summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJames Bowes <jbowes@redhat.com>2008-06-20 19:04:06 -0400
committerJames Bowes <jbowes@redhat.com>2008-06-20 19:04:47 -0400
commitc778afefaa189c6aa8437fc00512f140a6a68efe (patch)
tree2b62b297754d70db03ff990c5d196f696aa34976 /src
parent08973b4c9c22a4dcbed220daa38fcf510ba9146d (diff)
parente557f97da15b1682b2e9854672b019485bc48362 (diff)
Merge branch 'krh/master'
Conflicts: librazor/razor.h librazor/rpm.c razor.c src/main.c
Diffstat (limited to 'src')
-rw-r--r--src/.gitignore8
-rw-r--r--src/Makefile.am26
-rw-r--r--src/import-rpmdb.c170
-rw-r--r--src/import-yum.c352
-rw-r--r--src/main.c858
-rw-r--r--src/rpm.c895
-rw-r--r--src/test-driver.c463
-rw-r--r--src/test.xml858
8 files changed, 3630 insertions, 0 deletions
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..c9d9664
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,8 @@
+.deps
+.libs
+razor
+rpm
+*.repo
+*.xml.gz
+install
+rpms
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..58f7788
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,26 @@
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = \
+ $(CURL_CFLAGS) \
+ $(EXPAT_CFLAGS) \
+ $(RPM_CFLAGS) \
+ -I$(top_builddir)/src -I$(top_srcdir)/src \
+ -I$(top_srcdir)/librazor \
+ -DPACKAGE_LIBEXEC_DIR=\""$(libexecdir)"\" \
+ -DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
+ -DPACKAGE_DATA_DIR=\""$(datadir)"\" \
+ -DPACKAGE_BIN_DIR=\""$(bindir)"\" \
+ -DPACKAGE_LOCALSTATEDIR=\""$(localstatedir)"\" \
+ -DPACKAGE_LOCALE_DIR=\""$(localedir)"\"
+
+bin_PROGRAMS = razor rpm
+
+razor_SOURCES = main.c import-rpmdb.c import-yum.c
+razor_LDADD = $(RPM_LIBS) $(EXPAT_LIBS) $(CURL_LIBS) $(top_builddir)/librazor/librazor.la
+
+rpm_SOURCES = rpm.c
+rpm_LDADD = $(top_builddir)/librazor/librazor.la
+
+clean-local :
+ rm -f *~
+
diff --git a/src/import-rpmdb.c b/src/import-rpmdb.c
new file mode 100644
index 0000000..a373786
--- /dev/null
+++ b/src/import-rpmdb.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com>
+ * Copyright (C) 2008 Red Hat, Inc
+ *
+ * This program 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 program 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <rpm/rpmlib.h>
+#include <rpm/rpmdb.h>
+
+#include "razor.h"
+
+union rpm_entry {
+ void *p;
+ char *string;
+ char **list;
+ uint_32 *flags;
+ uint_32 integer;
+};
+
+static uint32_t
+rpm_to_razor_flags(uint32_t flags)
+{
+ uint32_t razor_flags;
+
+ razor_flags = 0;
+ if (flags & RPMSENSE_LESS)
+ razor_flags |= RAZOR_PROPERTY_LESS;
+ if (flags & RPMSENSE_EQUAL)
+ razor_flags |= RAZOR_PROPERTY_EQUAL;
+ if (flags & RPMSENSE_GREATER)
+ razor_flags |= RAZOR_PROPERTY_GREATER;
+
+ if (flags & RPMSENSE_SCRIPT_PRE)
+ razor_flags |= RAZOR_PROPERTY_PRE;
+ if (flags & RPMSENSE_SCRIPT_POST)
+ razor_flags |= RAZOR_PROPERTY_POST;
+ if (flags & RPMSENSE_SCRIPT_PREUN)
+ razor_flags |= RAZOR_PROPERTY_PREUN;
+ if (flags & RPMSENSE_SCRIPT_POSTUN)
+ razor_flags |= RAZOR_PROPERTY_POSTUN;
+
+ return razor_flags;
+}
+
+static void
+add_properties(struct razor_importer *importer,
+ uint32_t type_flags,
+ Header h, int_32 name_tag, int_32 version_tag, int_32 flags_tag)
+{
+ union rpm_entry names, versions, flags;
+ int_32 i, type, count;
+
+ headerGetEntry(h, name_tag, &type, &names.p, &count);
+ headerGetEntry(h, version_tag, &type, &versions.p, &count);
+ headerGetEntry(h, flags_tag, &type, &flags.p, &count);
+
+ for (i = 0; i < count; i++)
+ razor_importer_add_property(importer,
+ names.list[i],
+ rpm_to_razor_flags (flags.flags[i]) | type_flags,
+ versions.list[i]);
+}
+
+struct razor_set *
+razor_set_create_from_rpmdb(void)
+{
+ struct razor_importer *importer;
+ rpmdbMatchIterator iter;
+ Header h;
+ int_32 type, count, i;
+ union rpm_entry name, epoch, version, release, arch;
+ union rpm_entry summary, description, url, license;
+ union rpm_entry basenames, dirnames, dirindexes;
+ char filename[PATH_MAX], evr[128], buf[16];
+ rpmdb db;
+
+ rpmReadConfigFiles(NULL, NULL);
+
+ if (rpmdbOpen("", &db, O_RDONLY, 0644) != 0) {
+ fprintf(stderr, "cannot open rpm database\n");
+ exit(1);
+ }
+
+ importer = razor_importer_new();
+
+ iter = rpmdbInitIterator(db, 0, NULL, 0);
+ while (h = rpmdbNextIterator(iter), h != NULL) {
+ headerGetEntry(h, RPMTAG_NAME, &type, &name.p, &count);
+ headerGetEntry(h, RPMTAG_EPOCH, &type, &epoch.p, &count);
+ headerGetEntry(h, RPMTAG_VERSION, &type, &version.p, &count);
+ headerGetEntry(h, RPMTAG_RELEASE, &type, &release.p, &count);
+ headerGetEntry(h, RPMTAG_ARCH, &type, &arch.p, &count);
+ headerGetEntry(h, RPMTAG_SUMMARY, &type, &summary.p, &count);
+ headerGetEntry(h, RPMTAG_DESCRIPTION, &type, &description.p,
+ &count);
+ headerGetEntry(h, RPMTAG_URL, &type, &url.p, &count);
+ headerGetEntry(h, RPMTAG_LICENSE, &type, &license.p, &count);
+
+ if (epoch.flags != NULL) {
+ snprintf(buf, sizeof buf, "%u", *epoch.flags);
+ razor_build_evr(evr, sizeof evr,
+ buf, version.string, release.string);
+ } else {
+ razor_build_evr(evr, sizeof evr,
+ NULL, version.string, release.string);
+ }
+
+ razor_importer_begin_package(importer,
+ name.string, evr, arch.string);
+ razor_importer_add_details(importer, summary.string,
+ description.string, url.string,
+ license.string);
+
+ add_properties(importer, RAZOR_PROPERTY_REQUIRES, h,
+ RPMTAG_REQUIRENAME,
+ RPMTAG_REQUIREVERSION,
+ RPMTAG_REQUIREFLAGS);
+
+ add_properties(importer, RAZOR_PROPERTY_PROVIDES, h,
+ RPMTAG_PROVIDENAME,
+ RPMTAG_PROVIDEVERSION,
+ RPMTAG_PROVIDEFLAGS);
+
+ add_properties(importer, RAZOR_PROPERTY_OBSOLETES, h,
+ RPMTAG_OBSOLETENAME,
+ RPMTAG_OBSOLETEVERSION,
+ RPMTAG_OBSOLETEFLAGS);
+
+ add_properties(importer, RAZOR_PROPERTY_CONFLICTS, h,
+ RPMTAG_CONFLICTNAME,
+ RPMTAG_CONFLICTVERSION,
+ RPMTAG_CONFLICTFLAGS);
+
+ headerGetEntry(h, RPMTAG_BASENAMES, &type,
+ &basenames.p, &count);
+ headerGetEntry(h, RPMTAG_DIRNAMES, &type,
+ &dirnames.p, &count);
+ headerGetEntry(h, RPMTAG_DIRINDEXES, &type,
+ &dirindexes.p, &count);
+ for (i = 0; i < count; i++) {
+ snprintf(filename, sizeof filename, "%s%s",
+ dirnames.list[dirindexes.flags[i]],
+ basenames.list[i]);
+ razor_importer_add_file(importer, filename);
+ }
+
+ razor_importer_finish_package(importer);
+ }
+
+ rpmdbClose(db);
+
+ return razor_importer_finish(importer);
+}
diff --git a/src/import-yum.c b/src/import-yum.c
new file mode 100644
index 0000000..8132eb8
--- /dev/null
+++ b/src/import-yum.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com>
+ * Copyright (C) 2008 Red Hat, Inc
+ *
+ * This program 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 program 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <expat.h>
+#include <zlib.h>
+#include "razor.h"
+
+/* Import a yum filelist as a razor package set. */
+
+enum {
+ YUM_STATE_BEGIN,
+ YUM_STATE_PACKAGE_NAME,
+ YUM_STATE_PACKAGE_ARCH,
+ YUM_STATE_SUMMARY,
+ YUM_STATE_DESCRIPTION,
+ YUM_STATE_URL,
+ YUM_STATE_LICENSE,
+ YUM_STATE_CHECKSUM,
+ YUM_STATE_REQUIRES,
+ YUM_STATE_PROVIDES,
+ YUM_STATE_OBSOLETES,
+ YUM_STATE_CONFLICTS,
+ YUM_STATE_FILE
+};
+
+struct yum_context {
+ XML_Parser primary_parser;
+ XML_Parser filelists_parser;
+ XML_Parser current_parser;
+
+ struct razor_importer *importer;
+ struct import_property_context *current_property_context;
+ char name[256], arch[64], summary[512], description[4096];
+ char url[256], license[64], buffer[512], *p;
+ char pkgid[128];
+ uint32_t property_type;
+ int state;
+};
+
+static uint32_t
+yum_to_razor_relation (const char *flags)
+{
+ if (flags[0] == 'L') {
+ if (flags[1] == 'T')
+ return RAZOR_PROPERTY_LESS;
+ else
+ return RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL;
+ } else if (flags[0] == 'G') {
+ if (flags[1] == 'T')
+ return RAZOR_PROPERTY_GREATER;
+ else
+ return RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL;
+ } else
+ return RAZOR_PROPERTY_EQUAL;
+}
+
+static void
+yum_primary_start_element(void *data, const char *name, const char **atts)
+{
+ struct yum_context *ctx = data;
+ const char *n, *epoch, *version, *release;
+ char buffer[128];
+ uint32_t pre, relation, flags;
+ int i;
+
+ if (strcmp(name, "name") == 0) {
+ ctx->state = YUM_STATE_PACKAGE_NAME;
+ ctx->p = ctx->name;
+ } else if (strcmp(name, "arch") == 0) {
+ ctx->state = YUM_STATE_PACKAGE_ARCH;
+ ctx->p = ctx->arch;
+ } else if (strcmp(name, "version") == 0) {
+ epoch = NULL;
+ version = NULL;
+ release = NULL;
+ for (i = 0; atts[i]; i += 2) {
+ if (strcmp(atts[i], "epoch") == 0)
+ epoch = atts[i + 1];
+ else if (strcmp(atts[i], "ver") == 0)
+ version = atts[i + 1];
+ else if (strcmp(atts[i], "rel") == 0)
+ release = atts[i + 1];
+ }
+ if (version == NULL || release == NULL) {
+ fprintf(stderr, "invalid version tag, "
+ "missing version or release attribute\n");
+ return;
+ }
+
+ razor_build_evr(buffer, sizeof buffer, epoch, version, release);
+ razor_importer_begin_package(ctx->importer,
+ ctx->name, buffer, ctx->arch);
+ } else if (strcmp(name, "summary") == 0) {
+ ctx->p = ctx->summary;
+ ctx->state = YUM_STATE_SUMMARY;
+ } else if (strcmp(name, "description") == 0) {
+ ctx->p = ctx->description;
+ ctx->state = YUM_STATE_DESCRIPTION;
+ } else if (strcmp(name, "url") == 0) {
+ ctx->p = ctx->url;
+ ctx->state = YUM_STATE_URL;
+ } else if (strcmp(name, "checksum") == 0) {
+ ctx->p = ctx->pkgid;
+ ctx->state = YUM_STATE_CHECKSUM;
+ } else if (strcmp(name, "rpm:license") == 0) {
+ ctx->p = ctx->license;
+ ctx->state = YUM_STATE_LICENSE;
+ } else if (strcmp(name, "rpm:requires") == 0) {
+ ctx->state = YUM_STATE_REQUIRES;
+ ctx->property_type = RAZOR_PROPERTY_REQUIRES;
+ } else if (strcmp(name, "rpm:provides") == 0) {
+ ctx->state = YUM_STATE_PROVIDES;
+ ctx->property_type = RAZOR_PROPERTY_PROVIDES;
+ } else if (strcmp(name, "rpm:obsoletes") == 0) {
+ ctx->state = YUM_STATE_OBSOLETES;
+ ctx->property_type = RAZOR_PROPERTY_OBSOLETES;
+ } else if (strcmp(name, "rpm:conflicts") == 0) {
+ ctx->state = YUM_STATE_CONFLICTS;
+ ctx->property_type = RAZOR_PROPERTY_CONFLICTS;
+ } else if (strcmp(name, "rpm:entry") == 0 &&
+ ctx->state != YUM_STATE_BEGIN) {
+ n = NULL;
+ epoch = NULL;
+ version = NULL;
+ release = NULL;
+ relation = RAZOR_PROPERTY_EQUAL;
+ pre = 0;
+ for (i = 0; atts[i]; i += 2) {
+ if (strcmp(atts[i], "name") == 0)
+ n = atts[i + 1];
+ else if (strcmp(atts[i], "epoch") == 0)
+ epoch = atts[i + 1];
+ else if (strcmp(atts[i], "ver") == 0)
+ version = atts[i + 1];
+ else if (strcmp(atts[i], "rel") == 0)
+ release = atts[i + 1];
+ else if (strcmp(atts[i], "flags") == 0)
+ relation = yum_to_razor_relation(atts[i + 1]);
+ else if (strcmp(atts[i], "pre") == 0)
+ pre =
+ RAZOR_PROPERTY_PRE |
+ RAZOR_PROPERTY_POST |
+ RAZOR_PROPERTY_PREUN |
+ RAZOR_PROPERTY_POSTUN;
+ }
+
+ if (n == NULL) {
+ fprintf(stderr, "invalid rpm:entry, "
+ "missing name or version attributes\n");
+ return;
+ }
+
+ razor_build_evr(buffer, sizeof buffer, epoch, version, release);
+ flags = ctx->property_type | relation | pre;
+ razor_importer_add_property(ctx->importer, n, flags, buffer);
+ }
+}
+
+static void
+yum_primary_end_element (void *data, const char *name)
+{
+ struct yum_context *ctx = data;
+
+ switch (ctx->state) {
+ case YUM_STATE_PACKAGE_NAME:
+ case YUM_STATE_PACKAGE_ARCH:
+ case YUM_STATE_SUMMARY:
+ case YUM_STATE_DESCRIPTION:
+ case YUM_STATE_URL:
+ case YUM_STATE_LICENSE:
+ case YUM_STATE_CHECKSUM:
+ case YUM_STATE_FILE:
+ ctx->state = YUM_STATE_BEGIN;
+ break;
+ }
+
+ if (strcmp(name, "package") == 0) {
+ razor_importer_add_details(ctx->importer, ctx->summary,
+ ctx->description, ctx->url,
+ ctx->license);
+
+ XML_StopParser(ctx->current_parser, XML_TRUE);
+ ctx->current_parser = ctx->filelists_parser;
+ }
+}
+
+static void
+yum_character_data (void *data, const XML_Char *s, int len)
+{
+ struct yum_context *ctx = data;
+
+ switch (ctx->state) {
+ case YUM_STATE_PACKAGE_NAME:
+ case YUM_STATE_PACKAGE_ARCH:
+ case YUM_STATE_SUMMARY:
+ case YUM_STATE_DESCRIPTION:
+ case YUM_STATE_URL:
+ case YUM_STATE_LICENSE:
+ case YUM_STATE_CHECKSUM:
+ case YUM_STATE_FILE:
+ memcpy(ctx->p, s, len);
+ ctx->p += len;
+ *ctx->p = '\0';
+ break;
+ }
+}
+
+static void
+yum_filelists_start_element(void *data, const char *name, const char **atts)
+{
+ struct yum_context *ctx = data;
+ const char *pkg, *pkgid;
+ int i;
+
+ if (strcmp(name, "package") == 0) {
+ pkg = NULL;
+ pkgid = NULL;
+ for (i = 0; atts[i]; i += 2) {
+ if (strcmp(atts[i], "name") == 0)
+ pkg = atts[i + 1];
+ else if (strcmp(atts[i], "pkgid") == 0)
+ pkgid = atts[i + 1];
+ }
+ if (strcmp(pkgid, ctx->pkgid) != 0)
+ fprintf(stderr, "primary.xml and filelists.xml "
+ "mismatch for %s: %s vs %s",
+ pkg, pkgid, ctx->pkgid);
+ } else if (strcmp(name, "file") == 0) {
+ ctx->state = YUM_STATE_FILE;
+ ctx->p = ctx->buffer;
+ }
+}
+
+
+static void
+yum_filelists_end_element (void *data, const char *name)
+{
+ struct yum_context *ctx = data;
+
+ ctx->state = YUM_STATE_BEGIN;
+ if (strcmp(name, "package") == 0) {
+ XML_StopParser(ctx->current_parser, XML_TRUE);
+ ctx->current_parser = ctx->primary_parser;
+ razor_importer_finish_package(ctx->importer);
+ } else if (strcmp(name, "file") == 0)
+ razor_importer_add_file(ctx->importer, ctx->buffer);
+
+}
+
+#define XML_BUFFER_SIZE 4096
+
+struct razor_set *
+razor_set_create_from_yum(void)
+{
+ struct yum_context ctx;
+ void *buf;
+ int len, ret;
+ gzFile primary, filelists;
+ XML_ParsingStatus status;
+
+ ctx.importer = razor_importer_new();
+ ctx.state = YUM_STATE_BEGIN;
+
+ ctx.primary_parser = XML_ParserCreate(NULL);
+ XML_SetUserData(ctx.primary_parser, &ctx);
+ XML_SetElementHandler(ctx.primary_parser,
+ yum_primary_start_element,
+ yum_primary_end_element);
+ XML_SetCharacterDataHandler(ctx.primary_parser,
+ yum_character_data);
+
+ ctx.filelists_parser = XML_ParserCreate(NULL);
+ XML_SetUserData(ctx.filelists_parser, &ctx);
+ XML_SetElementHandler(ctx.filelists_parser,
+ yum_filelists_start_element,
+ yum_filelists_end_element);
+ XML_SetCharacterDataHandler(ctx.filelists_parser,
+ yum_character_data);
+
+ primary = gzopen("primary.xml.gz", "rb");
+ if (primary == NULL)
+ return NULL;
+ filelists = gzopen("filelists.xml.gz", "rb");
+ if (filelists == NULL)
+ return NULL;
+
+ ctx.current_parser = ctx.primary_parser;
+
+ do {
+ XML_GetParsingStatus(ctx.current_parser, &status);
+ switch (status.parsing) {
+ case XML_SUSPENDED:
+ ret = XML_ResumeParser(ctx.current_parser);
+ break;
+ case XML_PARSING:
+ case XML_INITIALIZED:
+ buf = XML_GetBuffer(ctx.current_parser,
+ XML_BUFFER_SIZE);
+ if (ctx.current_parser == ctx.primary_parser)
+ len = gzread(primary, buf, XML_BUFFER_SIZE);
+ else
+ len = gzread(filelists, buf, XML_BUFFER_SIZE);
+ if (len < 0) {
+ fprintf(stderr,
+ "couldn't read input: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ XML_ParseBuffer(ctx.current_parser, len, len == 0);
+ break;
+ case XML_FINISHED:
+ break;
+ }
+ } while (status.parsing != XML_FINISHED);
+
+
+ XML_ParserFree(ctx.primary_parser);
+ XML_ParserFree(ctx.filelists_parser);
+
+ gzclose(primary);
+ gzclose(filelists);
+
+ return razor_importer_finish(ctx.importer);
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..15b3b6e
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,858 @@
+/*
+ * Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com>
+ * Copyright (C) 2008 Red Hat, Inc
+ *
+ * This program 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 program 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <curl/curl.h>
+#include <fnmatch.h>
+#include <errno.h>
+#include "razor.h"
+
+static const char system_repo_filename[] = "system.repo";
+static const char next_repo_filename[] = "system-next.repo";
+static const char rawhide_repo_filename[] = "rawhide.repo";
+static const char updated_repo_filename[] = "system-updated.repo";
+static const char install_root[] = "install";
+static const char *repo_filename = system_repo_filename;
+static const char *yum_url;
+
+static int
+command_list(int argc, const char *argv[])
+{
+ struct razor_set *set;
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *pattern, *name, *version, *arch;
+ int only_names = 0, i = 0;
+
+ if (i < argc && strcmp(argv[i], "--only-names") == 0) {
+ only_names = 1;
+ i++;
+ }
+
+ pattern = argv[i];
+ set = razor_set_open(repo_filename);
+ pi = razor_package_iterator_create(set);
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch)) {
+ if (pattern && fnmatch(pattern, name, 0) != 0)
+ continue;
+
+ if (only_names)
+ printf("%s\n", name);
+ else
+ printf("%s-%s.%s\n", name, version, arch);
+ }
+ razor_package_iterator_destroy(pi);
+ razor_set_destroy(set);
+
+ return 0;
+}
+
+static int
+list_properties(const char *package_name, uint32_t type)
+{
+ struct razor_set *set;
+ struct razor_property *property;
+ struct razor_package *package;
+ struct razor_property_iterator *pi;
+ const char *name, *version;
+ uint32_t flags;
+
+ set = razor_set_open(repo_filename);
+ if (package_name)
+ package = razor_set_get_package(set, package_name);
+ else
+ package = NULL;
+
+ pi = razor_property_iterator_create(set, package);
+ while (razor_property_iterator_next(pi, &property,
+ &name, &flags, &version)) {
+ if ((flags & RAZOR_PROPERTY_TYPE_MASK) != type)
+ continue;
+ printf("%s", name);
+ if (version[0] != '\0')
+ printf(" %s %s",
+ razor_property_relation_to_string(property),
+ version);
+
+ if (flags & ~(RAZOR_PROPERTY_RELATION_MASK | RAZOR_PROPERTY_TYPE_MASK)) {
+ printf(" [");
+ if (flags & RAZOR_PROPERTY_PRE)
+ printf(" pre");
+ if (flags & RAZOR_PROPERTY_POST)
+ printf(" post");
+ if (flags & RAZOR_PROPERTY_PREUN)
+ printf(" preun");
+ if (flags & RAZOR_PROPERTY_POSTUN)
+ printf(" postun");
+ printf(" ]");
+ }
+ printf("\n");
+ }
+ razor_property_iterator_destroy(pi);
+
+ razor_set_destroy(set);
+
+ return 0;
+}
+
+static int
+command_list_requires(int argc, const char *argv[])
+{
+ return list_properties(argv[0], RAZOR_PROPERTY_REQUIRES);
+}
+
+static int
+command_list_provides(int argc, const char *argv[])
+{
+ return list_properties(argv[0], RAZOR_PROPERTY_PROVIDES);
+}
+
+static int
+command_list_obsoletes(int argc, const char *argv[])
+{
+ return list_properties(argv[0], RAZOR_PROPERTY_OBSOLETES);
+}
+
+static int
+command_list_conflicts(int argc, const char *argv[])
+{
+ return list_properties(argv[0], RAZOR_PROPERTY_CONFLICTS);
+}
+
+static int
+command_list_files(int argc, const char *argv[])
+{
+ struct razor_set *set;
+
+ set = razor_set_open(repo_filename);
+ razor_set_open_files(set, "system-files.repo");
+ if (set == NULL)
+ return 1;
+ razor_set_list_files(set, argv[0]);
+ razor_set_destroy(set);
+
+ return 0;
+}
+
+static int
+command_list_file_packages(int argc, const char *argv[])
+{
+ struct razor_set *set;
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *name, *version, *arch;
+
+ set = razor_set_open(repo_filename);
+ razor_set_open_files(set, "system-files.repo");
+ if (set == NULL)
+ return 1;
+
+ pi = razor_package_iterator_create_for_file(set, argv[0]);
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch))
+ printf("%s-%s\n", name, version);
+ razor_package_iterator_destroy(pi);
+
+ razor_set_destroy(set);
+
+ return 0;
+}
+
+static int
+command_list_package_files(int argc, const char *argv[])
+{
+ struct razor_set *set;
+
+ set = razor_set_open(repo_filename);
+ razor_set_open_files(set, "system-files.repo");
+ if (set == NULL)
+ return 1;
+ razor_set_list_package_files(set, argv[0]);
+ razor_set_destroy(set);
+
+ return 0;
+}
+
+static void
+list_packages_for_property(struct razor_set *set,
+ struct razor_property *property)
+{
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *name, *version, *arch;
+
+ pi = razor_package_iterator_create_for_property(set, property);
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch))
+ printf("%s-%s.%s\n", name, version, arch);
+ razor_package_iterator_destroy(pi);
+}
+
+static int
+list_property_packages(const char *ref_name,
+ const char *ref_version,
+ uint32_t type)
+{
+ struct razor_set *set;
+ struct razor_property *property;
+ struct razor_property_iterator *pi;
+ const char *name, *version;
+ uint32_t flags;
+
+ if (ref_name == NULL)
+ return 0;
+
+ set = razor_set_open(repo_filename);
+ if (set == NULL)
+ return 1;
+
+ pi = razor_property_iterator_create(set, NULL);
+ while (razor_property_iterator_next(pi, &property,
+ &name, &flags, &version)) {
+ if (strcmp(ref_name, name) != 0)
+ continue;
+ if (ref_version &&
+ (flags & RAZOR_PROPERTY_RELATION_MASK) == RAZOR_PROPERTY_EQUAL &&
+ strcmp(ref_version, version) != 0)
+ continue;
+ if ((flags & RAZOR_PROPERTY_TYPE_MASK) != type)
+ continue;
+
+ list_packages_for_property(set, property);
+ }
+ razor_property_iterator_destroy(pi);
+
+ return 0;
+}
+
+static int
+command_what_requires(int argc, const char *argv[])
+{
+ return list_property_packages(argv[0], argv[1],
+ RAZOR_PROPERTY_REQUIRES);
+}
+
+static int
+command_what_provides(int argc, const char *argv[])
+{
+ return list_property_packages(argv[0], argv[1],
+ RAZOR_PROPERTY_PROVIDES);
+}
+
+static int
+show_progress(void *clientp,
+ double dltotal, double dlnow, double ultotal, double ulnow)
+{
+ const char *file = clientp;
+
+ if (!dlnow < dltotal)
+ fprintf(stderr, "\rdownloading %s, %dkB/%dkB",
+ file, (int) dlnow / 1024, (int) dltotal / 1024);
+
+ return 0;
+}
+
+static int
+download_if_missing(const char *url, const char *file)
+{
+ CURL *curl;
+ struct stat buf;
+ char error[256];
+ FILE *fp;
+ CURLcode res;
+ long response;
+
+ curl = curl_easy_init();
+ if (curl == NULL)
+ return 1;
+
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, show_progress);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, file);
+
+ if (stat(file, &buf) < 0) {
+ fp = fopen(file, "w");
+ if (fp == NULL) {
+ fprintf(stderr,
+ "failed to open %s for writing\n", file);
+ return -1;
+ }
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ res = curl_easy_perform(curl);
+ fclose(fp);
+ if (res != CURLE_OK) {
+ fprintf(stderr, "curl error: %s\n", error);
+ unlink(file);
+ return -1;
+ }
+ res = curl_easy_getinfo(curl,
+ CURLINFO_RESPONSE_CODE, &response);
+ if (res != CURLE_OK) {
+ fprintf(stderr, "curl error: %s\n", error);
+ unlink(file);
+ return -1;
+ }
+ if (response != 200) {
+ fprintf(stderr, " - failed %ld\n", response);
+ unlink(file);
+ return -1;
+ }
+ fprintf(stderr, "\n");
+ }
+
+ curl_easy_cleanup(curl);
+
+ return 0;
+}
+
+#define YUM_URL "http://download.fedora.redhat.com" \
+ "/pub/fedora/linux/development/i386/os"
+
+static int
+command_import_yum(int argc, const char *argv[])
+{
+ struct razor_set *set;
+ char buffer[512];
+
+ printf("downloading from %s.\n", yum_url);
+ snprintf(buffer, sizeof buffer,
+ "%s/repodata/primary.xml.gz", yum_url);
+ if (download_if_missing(buffer, "primary.xml.gz") < 0)
+ return -1;
+ snprintf(buffer, sizeof buffer,
+ "%s/repodata/filelists.xml.gz", yum_url);
+ if (download_if_missing(buffer, "filelists.xml.gz") < 0)
+ return -1;
+
+ set = razor_set_create_from_yum();
+ if (set == NULL)
+ return 1;
+ razor_set_write(set, rawhide_repo_filename, RAZOR_REPO_FILE_MAIN);
+ razor_set_write(set, "rawhide-details.repo", RAZOR_REPO_FILE_DETAILS);
+ razor_set_write(set, "rawhide-files.repo", RAZOR_REPO_FILE_FILES);
+ razor_set_destroy(set);
+ printf("wrote %s\n", rawhide_repo_filename);
+
+ return 0;
+}
+
+static int
+command_import_rpmdb(int argc, const char *argv[])
+{
+ struct razor_set *set;
+
+ set = razor_set_create_from_rpmdb();
+ if (set == NULL)
+ return 1;
+ razor_set_write(set, repo_filename, RAZOR_REPO_FILE_MAIN);
+ razor_set_write(set, "system-details.repo", RAZOR_REPO_FILE_DETAILS);
+ razor_set_write(set, "system-files.repo", RAZOR_REPO_FILE_FILES);
+ razor_set_destroy(set);
+ printf("wrote %s\n", repo_filename);
+
+ return 0;
+}
+
+static int
+mark_packages_for_update(struct razor_transaction *trans,
+ struct razor_set *set, const char *pattern)
+{
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *name, *version, *arch;
+ int matches = 0;
+
+ pi = razor_package_iterator_create(set);
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch)) {
+ if (pattern && fnmatch(pattern, name, 0) == 0) {
+ razor_transaction_update_package(trans, package);
+ matches++;
+ }
+ }
+ razor_package_iterator_destroy(pi);
+
+ return matches;
+}
+
+static int
+mark_packages_for_removal(struct razor_transaction *trans,
+ struct razor_set *set, const char *pattern)
+{
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *name, *version, *arch;
+ int matches = 0;
+
+ pi = razor_package_iterator_create(set);
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch)) {
+ if (pattern && fnmatch(pattern, name, 0) == 0) {
+ razor_transaction_remove_package(trans, package);
+ matches++;
+ }
+ }
+ razor_package_iterator_destroy(pi);
+
+ return matches;
+}
+
+static int
+command_update(int argc, const char *argv[])
+{
+ struct razor_set *set, *upstream;
+ struct razor_transaction *trans;
+ int i, errors;
+
+ set = razor_set_open(repo_filename);
+ upstream = razor_set_open(rawhide_repo_filename);
+ if (set == NULL || upstream == NULL)
+ return 1;
+
+ trans = razor_transaction_create(set, upstream);
+ if (argc == 0)
+ razor_transaction_update_all(trans);
+ for (i = 0; i < argc; i++) {
+ if (mark_packages_for_update(trans, set, argv[i]) == 0) {
+ fprintf(stderr, "no match for %s\n", argv[i]);
+ return 1;
+ }
+ }
+
+ errors = razor_transaction_resolve(trans);
+ if (errors)
+ return 1;
+
+ set = razor_transaction_finish(trans);
+ razor_set_write(set, updated_repo_filename, RAZOR_REPO_FILE_MAIN);
+ razor_set_destroy(set);
+ razor_set_destroy(upstream);
+ printf("wrote system-updated.repo\n");
+
+ return 0;
+}
+
+static int
+command_remove(int argc, const char *argv[])
+{
+ struct razor_set *set, *upstream;
+ struct razor_transaction *trans;
+ int i, errors;
+
+ set = razor_set_open(repo_filename);
+ if (set == NULL)
+ return 1;
+
+ upstream = razor_set_create();
+ trans = razor_transaction_create(set, upstream);
+ for (i = 0; i < argc; i++) {
+ if (mark_packages_for_removal(trans, set, argv[i]) == 0) {
+ fprintf(stderr, "no match for %s\n", argv[i]);
+ return 1;
+ }
+ }
+
+ errors = razor_transaction_resolve(trans);
+ if (errors)
+ return 1;
+
+ set = razor_transaction_finish(trans);
+ razor_set_write(set, updated_repo_filename, RAZOR_REPO_FILE_MAIN);
+ razor_set_destroy(set);
+ razor_set_destroy(upstream);
+ printf("wrote system-updated.repo\n");
+
+ return 0;
+}
+
+static void
+print_diff(const char *name,
+ const char *old_version, const char *new_version, const char *arch,
+ void *data)
+{
+ if (old_version)
+ printf("removing %s %s\n", name, old_version);
+ else
+ printf("install %s %s\n", name, new_version);
+}
+
+static int
+command_diff(int argc, const char *argv[])
+{
+ struct razor_set *set, *updated;
+
+ set = razor_set_open(repo_filename);
+ updated = razor_set_open(updated_repo_filename);
+ if (set == NULL || updated == NULL)
+ return 1;
+
+ razor_set_diff(set, updated, print_diff, NULL);
+
+ razor_set_destroy(set);
+ razor_set_destroy(updated);
+
+ return 0;
+}
+
+static int
+command_import_rpms(int argc, const char *argv[])
+{
+ DIR *dir;
+ struct dirent *de;
+ struct razor_importer *importer;
+ struct razor_set *set;
+ struct razor_rpm *rpm;
+ int len;
+ char filename[256];
+ const char *dirname = argv[0];
+
+ if (dirname == NULL) {
+ fprintf(stderr, "usage: razor import-rpms DIR\n");
+ return -1;
+ }
+
+ dir = opendir(dirname);
+ if (dir == NULL) {
+ fprintf(stderr, "couldn't read dir %s\n", dirname);
+ return -1;
+ }
+
+ importer = razor_importer_new();
+
+ while (de = readdir(dir), de != NULL) {
+ len = strlen(de->d_name);
+ if (len < 5 || strcmp(de->d_name + len - 4, ".rpm") != 0)
+ continue;
+ snprintf(filename, sizeof filename,
+ "%s/%s", dirname, de->d_name);
+ rpm = razor_rpm_open(filename);
+ if (rpm == NULL) {
+ fprintf(stderr,
+ "failed to open rpm \"%s\"\n", filename);
+ continue;
+ }
+ if (razor_importer_add_rpm(importer, rpm)) {
+ fprintf(stderr, "couldn't import %s\n", filename);
+ break;
+ }
+ razor_rpm_close(rpm);
+ }
+
+ if (de != NULL) {
+ razor_importer_destroy(importer);
+ return -1;
+ }
+
+ set = razor_importer_finish(importer);
+
+ razor_set_write(set, repo_filename, RAZOR_REPO_FILE_MAIN);
+ razor_set_destroy(set);
+ printf("wrote %s\n", repo_filename);
+
+ return 0;
+}
+
+static void
+download_package(const char *name,
+ const char *old_version,
+ const char *new_version,
+ const char *arch,
+ void *data)
+{
+ char file[PATH_MAX], url[256];
+ const char *v;
+ int *errors = data;
+
+ if (old_version)
+ return;
+
+ /* Skip epoch */
+ v = strchr(new_version, ':');
+ if (v != NULL)
+ v = v + 1;
+ else
+ v = new_version;
+
+ snprintf(url, sizeof url,
+ "%s/Packages/%s-%s.%s.rpm", yum_url, name, v, arch);
+ snprintf(file, sizeof file,
+ "rpms/%s-%s.%s.rpm", name, v, arch);
+ if (download_if_missing(url, file) < 0)
+ (*errors)++;
+}
+
+static void
+install_package(const char *name,
+ const char *old_version,
+ const char *new_version,
+ const char *arch,
+ void *data)
+{
+ const char *v, *root = data;
+ char file[PATH_MAX];
+ struct razor_rpm *rpm;
+
+ if (old_version) {
+ printf("removing %s %s not handled\n", name, old_version);
+ return;
+ }
+
+ /* Skip epoch */
+ v = strchr(new_version, ':');
+ if (v != NULL)
+ v = v + 1;
+ else
+ v = new_version;
+
+ printf("install %s %s\n", name, v);
+ snprintf(file, sizeof file, "rpms/%s-%s.%s.rpm", name, v, arch);
+
+ rpm = razor_rpm_open(file);
+ if (rpm == NULL) {
+ fprintf(stderr, "failed to open rpm %s\n", file);
+ return;
+ }
+ if (razor_rpm_install(rpm, root) < 0) {
+ fprintf(stderr,
+ "failed to install rpm %s\n", file);
+ return;
+ }
+ razor_rpm_close(rpm);
+}
+
+static int
+command_install(int argc, const char *argv[])
+{
+ struct razor_root *root;
+ struct razor_set *upstream, *next;
+ struct razor_transaction *trans;
+ int i = 0, errors, dependencies = 1;
+
+ if (i < argc && strcmp(argv[i], "--no-dependencies") == 0) {
+ dependencies = 0;
+ i++;
+ }
+
+ root = razor_root_open(install_root, RAZOR_ROOT_OPEN_WRITE);
+ upstream = razor_set_open(rawhide_repo_filename);
+ trans = razor_root_create_transaction(root, upstream);
+
+ for (; i < argc; i++) {
+ if (mark_packages_for_update(trans, upstream, argv[i]) == 0) {
+ fprintf(stderr, "no package matched %s\n", argv[i]);
+ razor_root_close(root);
+ return 1;
+ }
+ }
+
+ if (dependencies) {
+ razor_transaction_resolve(trans);
+ if (razor_transaction_describe(trans) > 0) {
+ razor_root_close(root);
+ return 1;
+ }
+ }
+
+ next = razor_transaction_finish(trans);
+
+ razor_root_update(root, next);
+
+ if (mkdir("rpms", 0777) && errno != EEXIST) {
+ fprintf(stderr, "failed to create rpms directory.\n");
+ razor_root_close(root);
+ return 1;
+ }
+
+ errors = 0;
+ razor_root_diff(root, download_package, &errors);
+ if (errors > 0) {
+ fprintf(stderr, "failed to download %d packages\n", errors);
+ razor_root_close(root);
+ return 1;
+ }
+
+ /* FIXME: We need to figure out the right install order here,
+ * so the post and pre scripts can run. */
+ razor_root_diff(root, install_package, (void *) install_root);
+
+ razor_set_destroy(next);
+ razor_set_destroy(upstream);
+
+ return razor_root_commit(root);
+}
+
+static int
+command_init(int argc, const char *argv[])
+{
+ return razor_root_create(install_root);
+}
+
+static int
+command_download(int argc, const char *argv[])
+{
+ struct razor_set *set;
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *pattern = argv[0], *name, *version, *arch;
+ char url[256], file[256];
+ int matches = 0;
+
+ if (mkdir("rpms", 0777) && errno != EEXIST) {
+ fprintf(stderr, "failed to create rpms directory.\n");
+ return 1;
+ }
+
+ set = razor_set_open(rawhide_repo_filename);
+ pi = razor_package_iterator_create(set);
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch)) {
+ if (pattern && fnmatch(pattern, name, 0) != 0)
+ continue;
+
+ matches++;
+ snprintf(url, sizeof url,
+ "%s/Packages/%s-%s.%s.rpm",
+ yum_url, name, version, arch);
+ snprintf(file, sizeof file,
+ "rpms/%s-%s.%s.rpm", name, version, arch);
+ download_if_missing(url, file);
+ }
+ razor_package_iterator_destroy(pi);
+ razor_set_destroy(set);
+
+ if (matches == 0)
+ fprintf(stderr, "no packages matched \"%s\"\n", pattern);
+ else if (matches == 1)
+ fprintf(stderr, "downloaded 1 package\n");
+ else
+ fprintf(stderr, "downloaded %d packages\n", matches);
+
+ return 0;
+}
+
+static int
+command_info(int argc, const char *argv[])
+{
+ struct razor_set *set;
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *pattern = argv[0], *name, *version, *arch;
+ const char *summary, *description, *url, *license;
+
+ set = razor_set_open(repo_filename);
+ razor_set_open_details(set, "system-details.repo");
+ pi = razor_package_iterator_create(set);
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch)) {
+ if (pattern && fnmatch(pattern, name, 0) != 0)
+ continue;
+
+ razor_package_get_details (set, package, &summary, &description,
+ &url, &license);
+
+ printf ("Name: %s\n", name);
+ printf ("Arch: %s\n", arch);
+ printf ("Version: %s\n", version);
+ printf ("URL: %s\n", url);
+ printf ("License: %s\n", license);
+ printf ("Summary: %s\n", summary);
+ printf ("Description:\n");
+ printf ("%s\n", description);
+ printf ("\n");
+ }
+ razor_package_iterator_destroy(pi);
+ razor_set_destroy(set);
+
+ return 0;
+}
+
+static struct {
+ const char *name;
+ const char *description;
+ int (*func)(int argc, const char *argv[]);
+} razor_commands[] = {
+ { "list", "list all packages", command_list },
+ { "list-requires", "list all requires for the given package", command_list_requires },
+ { "list-provides", "list all provides for the given package", command_list_provides },
+ { "list-obsoletes", "list all obsoletes for the given package", command_list_obsoletes },
+ { "list-conflicts", "list all conflicts for the given package", command_list_conflicts },
+ { "list-files", "list files for package set", command_list_files },
+ { "list-file-packages", "list packages owning file", command_list_file_packages },
+ { "list-package-files", "list files in package", command_list_package_files },
+ { "what-requires", "list the packages that have the given requires", command_what_requires },
+ { "what-provides", "list the packages that have the given provides", command_what_provides },
+ { "import-yum", "import yum metadata files", command_import_yum },
+ { "import-rpmdb", "import the system rpm database", command_import_rpmdb },
+ { "import-rpms", "import rpms from the given directory", command_import_rpms },
+ { "update", "update all or specified packages", command_update },
+ { "remove", "remove specified packages", command_remove },
+ { "diff", "show diff between two package sets", command_diff },
+ { "install", "install rpm", command_install },
+ { "init", "init razor root", command_init },
+ { "download", "download packages", command_download },
+ { "info", "display package details", command_info }
+};
+
+static int
+usage(void)
+{
+ int i;
+
+ printf("usage:\n");
+ for (i = 0; i < ARRAY_SIZE(razor_commands); i++)
+ printf(" %-20s%s\n",
+ razor_commands[i].name, razor_commands[i].description);
+
+ return 1;
+}
+
+int
+main(int argc, const char *argv[])
+{
+ char *repo;
+ int i;
+
+ repo = getenv("RAZOR_REPO");
+ if (repo != NULL)
+ repo_filename = repo;
+
+ yum_url = getenv("YUM_URL");
+ if (yum_url == NULL)
+ yum_url = YUM_URL;
+
+ if (argc < 2)
+ return usage();
+
+ for (i = 0; i < ARRAY_SIZE(razor_commands); i++)
+ if (strcmp(razor_commands[i].name, argv[1]) == 0)
+ return razor_commands[i].func(argc - 2, argv + 2);
+
+ return usage();
+}
diff --git a/src/rpm.c b/src/rpm.c
new file mode 100644
index 0000000..7ad0d6d
--- /dev/null
+++ b/src/rpm.c
@@ -0,0 +1,895 @@
+/*
+ * Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com>
+ * Copyright (C) 2008 Red Hat, Inc
+ *
+ * This program 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 program 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <dirent.h>
+#include "razor.h"
+
+enum option_type {
+ OPTION_LAST,
+ OPTION_GROUP,
+ OPTION_BOOL,
+ OPTION_STRING
+};
+
+struct option {
+ enum option_type type;
+ const char *name;
+ char short_name;
+ const char *arg_name;
+ const char *description;
+ void *data;
+};
+
+/* A note about all these options: rpm allows options to mean
+ * different things depending on what other options are present on the
+ * command line. For example, if -q or --query is present, -i no
+ * longer means install, but info. The way we handle this is by
+ * setting all the options that may match (ie if -i is given we set
+ * install and info), and then look at the relevent one depending on
+ * what else in on the command line. */
+
+static int option_all, option_whatrequires, option_whatprovides;
+static int option_package;
+
+static const struct option query_options[] = {
+ { OPTION_BOOL, "configfiles", 'c', NULL, "list all configuration files", NULL },
+ { OPTION_BOOL, "docfiles", 'd', NULL, "list all documentation files", NULL },
+ { OPTION_BOOL, "dump", 0, NULL, "dump basic file information", NULL },
+ { OPTION_BOOL, "list", 0, NULL, "list files in package", NULL },
+ { OPTION_STRING, "queryformat", 0, "QUERYFORMAT", "use the following query format", NULL },
+ { OPTION_BOOL, "state", 's', NULL, "display the states of the listed files", NULL },
+ { OPTION_BOOL, "all", 'a', NULL, "query/verify all packages", &option_all },
+ { OPTION_BOOL, "file", 'f', NULL, "query/verify package(s) owning file", NULL },
+ { OPTION_BOOL, "group", 'g', NULL, "query/verify package(s) in group", NULL },
+ { OPTION_BOOL, "package", 'p', NULL, "query/verify a package file", &option_package },
+ { OPTION_BOOL, "ftswalk", 'W', NULL, "query/verify package(s) from TOP file tree walk", NULL },
+ { OPTION_BOOL, "pkgid", 0, NULL, "query/verify package(s) with package identifier", NULL },
+ { OPTION_BOOL, "hdrid", 0, NULL, "query/verify package(s) with header identifier", NULL },
+ { OPTION_BOOL, "fileid", 0, NULL, "query/verify package(s) with file identifier", NULL },
+ { OPTION_BOOL, "specfile", 0, NULL, "query a spec file", NULL, },
+ { OPTION_BOOL, "triggeredby", 0, NULL, "query the package(s) triggered by the package", NULL },
+ { OPTION_BOOL, "whatrequires", 0, NULL, "query/verify the package(s) which require a dependency", &option_whatrequires },
+ { OPTION_BOOL, "whatprovides", 0, NULL, "query/verify the package(s) which provide a dependency", &option_whatprovides },
+ { OPTION_BOOL, "nomanifest", 0, NULL, "do not process non-package files as manifests", NULL },
+ { }
+};
+
+static int option_nodeps;
+
+static const struct option verify_options[] = {
+ { OPTION_BOOL, "nomd5", 0, NULL, "don't verify MD5 digest of files", NULL },
+ { OPTION_BOOL, "nofiles", 0, NULL, "don't verify files in package", NULL },
+ { OPTION_BOOL, "nodeps", 0, NULL, "don't verify package dependencies", &option_nodeps },
+ { OPTION_BOOL, "noscript", 0, NULL, "don't execute verify script(s)", NULL, },
+ { OPTION_BOOL, "all", 'a', NULL, "query/verify all packages", &option_all },
+ { OPTION_BOOL, "file", 'f', NULL, "query/verify package(s) owning file", NULL },
+ { OPTION_BOOL, "group", 'g', NULL, "query/verify package(s) in group", NULL },
+ { OPTION_BOOL, "package", 'p', NULL, "query/verify a package file", &option_package },
+ { OPTION_BOOL, "ftswalk", 'W', NULL, "query/verify package(s) from TOP file tree walk", NULL },
+ { OPTION_BOOL, "pkgid", 0, NULL, "query/verify package(s) with package identifier", NULL },
+ { OPTION_BOOL, "hdrid", 0, NULL, "query/verify package(s) with header identifier", NULL },
+ { OPTION_BOOL, "fileid", 0, NULL, "query/verify package(s) with file identifier", NULL },
+ { OPTION_BOOL, "specfile", 0, NULL, "query a spec file", NULL },
+ { OPTION_BOOL, "triggeredby", 0, NULL, "query the package(s) triggered by the package", NULL },
+ { OPTION_BOOL, "whatrequires", 0, NULL, "query/verify the package(s) which require a dependency", &option_whatrequires },
+ { OPTION_BOOL, "whatprovides", 0, NULL, "query/verify the package(s) which provide a dependency", &option_whatprovides },
+ { OPTION_BOOL, "nomanifest", 0, NULL, "do not process non-package files as manifests", NULL },
+ { }
+};
+
+static const struct option ftw_options[] = {
+ { OPTION_BOOL, "comfollow", 0, NULL, "FTS_COMFOLLOW: follow command line symlinks", NULL },
+ { OPTION_BOOL, "logical", 0, NULL, "FTS_LOGICAL: logical walk", NULL },
+ { OPTION_BOOL, "nochdir", 0, NULL, "FTS_NOCHDIR: don't change directories", NULL },
+ { OPTION_BOOL, "nostat", 0, NULL, "FTS_NOSTAT: don't get stat info", NULL },
+ { OPTION_BOOL, "physical", 0, NULL, "FTS_PHYSICAL: physical walk", NULL },
+ { OPTION_BOOL, "seedot", 0, NULL, "FTS_SEEDOT: return dot and dot-dot", NULL },
+ { OPTION_BOOL, "xdev", 0, NULL, "FTS_XDEV: don't cross devices", NULL },
+ { OPTION_BOOL, "whiteout", 0, NULL, "FTS_WHITEOUT: return whiteout information", NULL },
+ { }
+};
+
+static const struct option signature_options[] = {
+ { OPTION_BOOL, "addsign", 0, NULL, "sign package(s) (identical to --resign)", NULL, },
+ { OPTION_BOOL, "checksig", 'K', NULL, "verify package signature(s)", NULL, },
+ { OPTION_BOOL, "delsign", 0, NULL, "delete package signatures", NULL, },
+ { OPTION_BOOL, "import", 0, NULL, "import an armored public key", NULL, },
+ { OPTION_BOOL, "resign", 0, NULL, "sign package(s) (identical to --addsign)", NULL, },
+ { OPTION_BOOL, "nodigest", 0, NULL, "don't verify package digest(s)", NULL, },
+ { OPTION_BOOL, "nosignature", 0, NULL, "don't verify package signature(s)", NULL },
+ { }
+};
+
+static int option_initdb;
+
+static const struct option database_options[] = {
+ { OPTION_BOOL, "initdb", 0, NULL, "initialize database", &option_initdb },
+ { OPTION_BOOL, "rebuilddb", 0, NULL, "rebuild database inverted lists from installed package headers", NULL },
+ { }
+};
+
+static int option_erase, option_install, option_upgrade, option_justdb;
+static int option_test;
+
+static const struct option install_options[] = {
+ { OPTION_BOOL, "aid", 0, NULL, "add suggested packages to transaction", NULL, },
+ { OPTION_BOOL, "allfiles", 0, NULL, "install all files, even configurations which might otherwise be skipped", NULL, },
+ { OPTION_BOOL, "allmatches", 0, NULL, "remove all packages which match <package> (normally an error is generated if <package> specified multiple packages)", NULL, },
+ { OPTION_BOOL, "badreloc", 0, NULL, "relocate files in non-relocatable package", NULL },
+ { OPTION_BOOL, "erase", 'e', "<package>", "erase (uninstall) package", &option_erase },
+ { OPTION_BOOL, "excludedocs", 0, NULL, "do not install documentation", NULL, },
+ { OPTION_BOOL, "excludepath", 0, "<path>", "skip files with leading component <path> ", NULL, },
+ { OPTION_BOOL, "fileconflicts", 0, NULL, "detect file conflicts between packages", NULL, },
+ { OPTION_BOOL, "force", 0, NULL, "short hand for --replacepkgs --replacefiles", NULL },
+ { OPTION_BOOL, "freshen", 'F', "<packagefile>+", "upgrade package(s) if already installed", NULL },
+ { OPTION_BOOL, "hash", 'h', NULL, "print hash marks as package installs (good with -v)", NULL },
+ { OPTION_BOOL, "ignorearch", 0, NULL, "don't verify package architecture", NULL, },
+ { OPTION_BOOL, "ignoreos", 0, NULL, "don't verify package operating system", NULL, },
+ { OPTION_BOOL, "ignoresize", 0, NULL, "don't check disk space before installing", NULL },
+ { OPTION_BOOL, "install", 'i', NULL, "install package(s)", &option_install },
+ { OPTION_BOOL, "justdb", 0, NULL, "update the database, but do not modify the filesystem", &option_justdb, },
+ { OPTION_BOOL, "nodeps", 0, NULL, "do not verify package dependencies", &option_nodeps, },
+ { OPTION_BOOL, "nomd5", 0, NULL, "don't verify MD5 digest of files", NULL, },
+ { OPTION_BOOL, "nocontexts", 0, NULL, "don't install file security contexts", NULL, },
+ { OPTION_BOOL, "noorder", 0, NULL, "do not reorder package installation to satisfy dependencies", NULL, },
+ { OPTION_BOOL, "nosuggest", 0, NULL, "do not suggest missing dependency resolution(s)", NULL, },
+ { OPTION_BOOL, "noscripts", 0, NULL, "do not execute package scriptlet(s)", NULL, },
+ { OPTION_BOOL, "notriggers", 0, NULL, "do not execute any scriptlet(s) triggered by this package", NULL, },
+ { OPTION_BOOL, "oldpackage", 0, NULL, "upgrade to an old version of the package (--force on upgrades does this automatically)", NULL },
+ { OPTION_BOOL, "percent", 0, NULL, "print percentages as package installs", NULL, },
+ { OPTION_STRING, "prefix", 0, "<dir>", "relocate the package to <dir>, if relocatable", NULL, },
+ { OPTION_STRING, "relocate", 0, "<old>=<new>", "relocate files from path <old> to <new>", NULL, },
+ { OPTION_BOOL, "repackage", 0, NULL, "save erased package files by repackaging", NULL, },
+ { OPTION_BOOL, "replacefiles", 0, NULL, "ignore file conflicts between packages", NULL, },
+ { OPTION_BOOL, "replacepkgs", 0, NULL, "reinstall if the package is already present", NULL, },
+ { OPTION_BOOL, "test", 0, NULL, "don't install, but tell if it would work or not", &option_test },
+ { OPTION_BOOL, "upgrade", 'U', "<packagefile>+", "upgrade package(s)", &option_upgrade },
+ { }
+};
+
+static int option_version;
+static const char *option_root = "install";
+
+static const struct option common_options[] = {
+ { OPTION_STRING, "define", 'D', "MACRO EXPR", "define MACRO with value EXPR", NULL, },
+ { OPTION_STRING, "eval", 'E', "EXPR", "print macro expansion of EXPR", NULL },
+ { OPTION_STRING, "macros", 0, "<FILE:...>", "read <FILE:...> instead of default file(s)", NULL },
+ { OPTION_BOOL, "nodigest", 0, NULL, "don't verify package digest(s)", NULL, },
+ { OPTION_BOOL, "nosignature", 0, NULL, "don't verify package signature(s)", NULL, },
+ { OPTION_STRING, "rcfile", 0, "<FILE:...>", "read <FILE:...> instead of default file(s)", NULL },
+ { OPTION_STRING, "root", 'r', "ROOT", "use ROOT as top level directory (default: \"/\")", &option_root },
+ { OPTION_BOOL, "querytags", 0, NULL, "display known query tags", NULL, },
+ { OPTION_BOOL, "showrc", 0, NULL, "display final rpmrc and macro configuration", NULL, },
+ { OPTION_BOOL, "quiet", 0, NULL, "provide less detailed output", NULL },
+ { OPTION_BOOL, "verbose", 'v', NULL, "provide more detailed output", NULL },
+ { OPTION_BOOL, "version", 0, NULL, "print the version of rpm being used", &option_version },
+ { }
+};
+
+static int option_conflicts, option_obsoletes, option_requires;
+static int option_provides, option_info, option_changelog;
+
+static const struct option alias_options[] = {
+ { OPTION_BOOL, "scripts", 0, NULL, "list install/erase scriptlets from package(s)", NULL, },
+ { OPTION_BOOL, "setperms", 0, NULL, "set permissions of files in a package", NULL, },
+ { OPTION_BOOL, "setugids", 0, NULL, "set user/group ownership of files in a package", NULL, },
+ { OPTION_BOOL, "conflicts", 0, NULL, "list capabilities this package conflicts with", &option_conflicts, },
+ { OPTION_BOOL, "obsoletes", 0, NULL, "list other packages removed by installing this package", &option_obsoletes, },
+ { OPTION_BOOL, "provides", 0, NULL, "list capabilities that this package provides", &option_provides, },
+ { OPTION_BOOL, "requires", 0, NULL, "list capabilities required by package(s)", &option_requires, },
+ { OPTION_BOOL, "info", 'i', NULL, "list descriptive information from package(s)", &option_info, },
+ { OPTION_BOOL, "changelog", 0, NULL, "list change logs for this package", &option_changelog, },
+ { OPTION_BOOL, "xml", 0, NULL, "list metadata in xml", NULL, },
+ { OPTION_BOOL, "triggers", 0, NULL, "list trigger scriptlets from package(s)", NULL, },
+ { OPTION_BOOL, "last", 0, NULL, "list package(s) by install time, most recent first", NULL, },
+ { OPTION_BOOL, "dupes", 0, NULL, "list duplicated packages", NULL, },
+ { OPTION_BOOL, "filesbypkg", 0, NULL, "list all files from each package", NULL, },
+ { OPTION_BOOL, "fileclass", 0, NULL, "list file names with classes", NULL, },
+ { OPTION_BOOL, "filecolor", 0, NULL, "list file names with colors", NULL, },
+ { OPTION_BOOL, "filecontext", 0, NULL, "list file names with security context from header", NULL, },
+ { OPTION_BOOL, "fscontext", 0, NULL, "list file names with security context from file system", NULL, },
+ { OPTION_BOOL, "recontext", 0, NULL, "list file names with security context from policy RE", NULL, },
+ { OPTION_BOOL, "fileprovide", 0, NULL, "list file names with provides", NULL, },
+ { OPTION_BOOL, "filerequire", 0, NULL, "list file names with requires", NULL, },
+ { OPTION_BOOL, "redhatprovides", 0, NULL, "find package name that contains a provided capability (needs rpmdb-redhat package installed)", NULL, },
+ { OPTION_BOOL, "redhatrequires", 0, NULL, "find package name that contains a required capability (needs rpmdb-redhat package installed)", NULL, },
+ { OPTION_STRING, "buildpolicy", 0, "<policy>", "set buildroot <policy> (e.g. compress man pages)", NULL, },
+ { OPTION_BOOL, "with", 0, "<option>", "enable configure <option> for build", NULL, },
+ { OPTION_BOOL, "without", 0, "<option>", "disable configure <option> for build", NULL },
+ { }
+};
+
+static int option_help, option_usage;
+
+static const struct option help_options[] = {
+ { OPTION_BOOL, "help", '?', NULL, "Show this help message", &option_help },
+ { OPTION_BOOL, "usage", 0, NULL, "Display brief usage message", &option_usage},
+ { }
+};
+
+static int option_query, option_verify;
+
+static const struct option rpm_options[] = {
+ { OPTION_BOOL, "query", 'q', NULL, "Query rpm database", &option_query },
+ { OPTION_BOOL, "verify", 'V', NULL, "Verify rpm database", &option_verify },
+ { OPTION_GROUP, NULL, 0, NULL, "Query options (with -q or --query):", &query_options },
+ { OPTION_GROUP, NULL, 0, NULL, "Verify options (with -V or --verify):", &verify_options },
+ { OPTION_GROUP, NULL, 0, NULL, "File tree walk options (with --ftswalk):", &ftw_options },
+ { OPTION_GROUP, NULL, 0, NULL, "Signature options:", &signature_options },
+ { OPTION_GROUP, NULL, 0, NULL, "Database options:", &database_options },
+ { OPTION_GROUP, NULL, 0, NULL, "Install/Upgrade/Erase options:", &install_options },
+ { OPTION_GROUP, NULL, 0, NULL, "Common options for all rpm modes and executables:", &common_options },
+ { OPTION_GROUP, NULL, 0, NULL, "Options implemented via popt alias/exec:", &alias_options },
+ { OPTION_GROUP, NULL, 0, NULL, "Help options", &help_options },
+ { }
+};
+
+static const char system_repo_filename[] = "system.repo";
+static const char *repo_filename = system_repo_filename;
+
+static void
+command_initdb(int argc, const char *argv[])
+{
+ razor_root_create(option_root);
+}
+
+static struct razor_property *
+add_property_packages(struct razor_set *set,
+ struct razor_package_query *query,
+ const char *ref_name,
+ const char *ref_version,
+ uint32_t ref_type)
+{
+ struct razor_property *property;
+ struct razor_property_iterator *pi;
+ struct razor_package_iterator *pkgi;
+ const char *name, *version;
+ uint32_t flags;
+
+ pi = razor_property_iterator_create(set, NULL);
+ while (razor_property_iterator_next(pi, &property, &name,
+ &flags, &version)) {
+ if (strcmp(ref_name, name) != 0)
+ continue;
+ if (ref_version &&
+ (flags & RAZOR_PROPERTY_RELATION_MASK) == RAZOR_PROPERTY_EQUAL &&
+ strcmp(ref_version, version) != 0)
+ continue;
+ if ((flags & RAZOR_PROPERTY_TYPE_MASK) != ref_type)
+ continue;
+
+ pkgi = razor_package_iterator_create_for_property(set,
+ property);
+ razor_package_query_add_iterator(query, pkgi);
+ razor_package_iterator_destroy(pkgi);
+ }
+ razor_property_iterator_destroy(pi);
+
+ return property;
+}
+
+static int
+strcmpp(const void *p1, const void *p2)
+{
+ return strcmp(*(char * const *) p1, *(char * const *) p2);
+}
+
+static void
+add_command_line_packages(struct razor_set *set,
+ struct razor_package_query *query,
+ int argc, const char **argv)
+{
+ struct razor_package *package;
+ struct razor_package_iterator *pi;
+ const char *name, *version, *arch;
+ int i, cmp, errors;
+
+ qsort(argv, argc, sizeof(*argv), strcmpp);
+ i = 0;
+ errors = 0;
+
+ pi = razor_package_iterator_create(set);
+
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch)) {
+ while (cmp = strcmp(argv[i], name), cmp < 0 && i < argc) {
+ fprintf(stderr, "error: package %s is not installed\n",
+ argv[i]);
+ errors++;
+ i++;
+ }
+
+ if (cmp == 0) {
+ razor_package_query_add_package(query, package);
+ i++;
+ }
+ }
+
+ razor_package_iterator_destroy(pi);
+
+ if (errors)
+ exit(1);
+}
+
+static struct razor_package_iterator *
+get_query_packages(struct razor_set *set, int argc, const char *argv[])
+{
+ struct razor_package_query *query;
+ struct razor_package_iterator *pi;
+ int i;
+
+ if (option_all + option_whatprovides + option_whatrequires > 1) {
+ printf("only one type of query/verify "
+ "may be performed at a time\n");
+ exit(1);
+ }
+
+ query = razor_package_query_create(set);
+
+ if (option_all) {
+ pi = razor_package_iterator_create(set);
+ razor_package_query_add_iterator(query, pi);
+ razor_package_iterator_destroy(pi);
+ } else if (option_whatrequires) {
+ for (i = 0; i < argc; i++)
+ add_property_packages(set, query,
+ argv[i], NULL,
+ RAZOR_PROPERTY_REQUIRES);
+ } else if (option_whatprovides) {
+ for (i = 0; i < argc; i++)
+ add_property_packages(set, query,
+ argv[i], NULL,
+ RAZOR_PROPERTY_PROVIDES);
+ } else if (argc > 0) {
+ add_command_line_packages(set, query, argc, argv);
+ } else {
+ printf("no arguments given for query/verify\n");
+ exit(1);
+ }
+
+ return razor_package_query_finish(query);
+}
+
+static void
+print_package_properties(struct razor_set *set,
+ struct razor_package *package,
+ uint32_t ref_type)
+{
+ struct razor_property *property;
+ struct razor_property_iterator *pi;
+ const char *name, *version;
+ uint32_t flags;
+
+ pi = razor_property_iterator_create(set, package);
+ while (razor_property_iterator_next(pi, &property,
+ &name, &flags, &version)) {
+ if ((flags & RAZOR_PROPERTY_TYPE_MASK) != ref_type)
+ continue;
+ if (version[0] == '\0')
+ printf("%s\n", name);
+ else
+ printf("%s %s %s\n", name,
+ razor_property_relation_to_string(property),
+ version);
+ }
+ razor_property_iterator_destroy(pi);
+}
+
+static void
+print_package_info(struct razor_set *set, struct razor_package *package)
+{
+ printf("FIXME: Package info not tracked.\n");
+}
+
+static void
+print_package_changelog(struct razor_set *set, struct razor_package *package)
+{
+ printf("FIXME: Package changelog not tracked.\n");
+}
+
+static struct razor_set *
+create_set_from_command_line(int argc, const char *argv[])
+{
+ struct razor_importer *importer;
+ struct razor_rpm *rpm;
+ int i;
+
+ importer = razor_importer_new();
+
+ for (i = 0; i < argc; i++) {
+ rpm = razor_rpm_open(argv[i]);
+ if (rpm == NULL)
+ continue;
+ if (razor_importer_add_rpm(importer, rpm))
+ printf("couldn't import %s\n", argv[i]);
+
+ razor_rpm_close(rpm);
+ }
+
+ return razor_importer_finish(importer);
+}
+
+static void
+command_query(int argc, const char *argv[])
+{
+ struct razor_set *set;
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *name, *version, *arch;
+
+ if (option_package) {
+ set = create_set_from_command_line(argc, argv);
+ argc = 0;
+ option_all = 1;
+ } else {
+ set = razor_root_open_read_only(option_root);
+ }
+
+ pi = get_query_packages(set, argc, argv);
+
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch)) {
+ if (option_conflicts)
+ print_package_properties(set, package,
+ RAZOR_PROPERTY_CONFLICTS);
+ if (option_obsoletes)
+ print_package_properties(set, package,
+ RAZOR_PROPERTY_OBSOLETES);
+ if (option_requires)
+ print_package_properties(set, package,
+ RAZOR_PROPERTY_REQUIRES);
+ if (option_provides)
+ print_package_properties(set, package,
+ RAZOR_PROPERTY_PROVIDES);
+ if (option_info)
+ print_package_info(set, package);
+ if (option_changelog)
+ print_package_changelog(set, package);
+
+ if (option_conflicts + option_obsoletes +
+ option_requires + option_provides +
+ option_info + option_changelog == 0)
+ printf("%s-%s.%s\n", name, version, arch);
+ }
+
+ razor_package_iterator_destroy(pi);
+
+ razor_set_destroy(set);
+
+ return;
+}
+
+static void
+command_verify(int argc, const char *argv[])
+{
+ struct razor_set *set;
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *name, *version, *arch;
+
+ if (option_package) {
+ set = create_set_from_command_line(argc, argv);
+ argc = 0;
+ option_all = 1;
+ } else {
+ set = razor_root_open_read_only(option_root);
+ }
+
+ pi = get_query_packages(set, argc, argv);
+
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch)) {
+ printf("verify %s-%s.%s - not implemented\n",
+ name, version, arch);
+ }
+
+ razor_package_iterator_destroy(pi);
+}
+
+static void
+remove_package(const char *name,
+ const char *old_version, const char *new_version,
+ const char *arch, void *data)
+{
+ if (old_version)
+ printf("remove %s-%s.%s\n", name, old_version, arch);
+}
+
+static void
+command_erase(int argc, const char *argv[])
+{
+ struct razor_set *set, *upstream, *next;
+ struct razor_transaction *trans;
+ struct razor_package_query *query;
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *name, *version, *arch;
+
+ if (argc == 0) {
+ printf("no packages given for erase\n");
+ exit(1);
+ }
+
+ set = razor_set_open(repo_filename);
+ upstream = razor_set_create();
+
+ trans = razor_transaction_create(set, upstream);
+
+ query = razor_package_query_create(set);
+ add_command_line_packages(set, query, argc, argv);
+
+ pi = razor_package_query_finish(query);
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch))
+ razor_transaction_remove_package(trans, package);
+ razor_package_iterator_destroy(pi);
+
+ if (!option_nodeps && razor_transaction_describe(trans) > 0) {
+ printf("unsatisfied dependencies.\n");
+ exit(1);
+ }
+
+ if (option_test)
+ exit(0);
+
+ next = razor_transaction_finish(trans);
+
+ if (!option_justdb)
+ razor_set_diff(set, next, remove_package, NULL);
+
+ razor_set_destroy(set);
+ razor_set_destroy(upstream);
+
+ razor_set_destroy(next);
+}
+
+static void
+install_package(const char *name,
+ const char *old_version, const char *new_version,
+ const char *arch, void *data)
+{
+ if (new_version)
+ printf("install %s-%s.%s\n", name, new_version, arch);
+}
+
+static void
+command_install(int argc, const char *argv[])
+{
+ struct razor_set *set, *upstream, *next;
+ struct razor_transaction *trans;
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *name, *version, *arch;
+
+ if (argc == 0) {
+ printf("no packages given for install\n");
+ exit(1);
+ }
+
+ set = razor_set_open(repo_filename);
+ upstream = create_set_from_command_line(argc, argv);
+
+ trans = razor_transaction_create(set, upstream);
+
+ pi = razor_package_iterator_create(upstream);
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch))
+ razor_transaction_install_package(trans, package);
+ razor_package_iterator_destroy(pi);
+
+ if (!option_nodeps && razor_transaction_describe(trans) > 0) {
+ printf("unsatisfied dependencies.\n");
+ exit(1);
+ }
+
+ if (option_test)
+ exit(0);
+
+ next = razor_transaction_finish(trans);
+
+ if (!option_justdb)
+ razor_set_diff(set, next, install_package, NULL);
+
+ razor_set_destroy(set);
+ razor_set_destroy(upstream);
+
+ razor_set_destroy(next);
+}
+
+static void
+update_package(const char *name,
+ const char *old_version, const char *new_version,
+ const char *arch, void *data)
+{
+ if (old_version)
+ printf("remove %s-%s.%s\n", name, old_version, arch);
+ if (new_version)
+ printf("install %s-%s.%s\n", name, new_version, arch);
+}
+
+static void
+command_update(int argc, const char *argv[])
+{
+ struct razor_set *set, *upstream, *next;
+ struct razor_transaction *trans;
+ struct razor_package_iterator *pi;
+ struct razor_package *package;
+ const char *name, *version, *arch;
+
+ if (argc == 0) {
+ printf("no packages given for update\n");
+ exit(1);
+ }
+
+ set = razor_set_open(repo_filename);
+ upstream = create_set_from_command_line(argc, argv);
+
+ trans = razor_transaction_create(set, upstream);
+
+ pi = razor_package_iterator_create(upstream);
+ while (razor_package_iterator_next(pi, &package,
+ &name, &version, &arch))
+ razor_transaction_update_package(trans, package);
+ razor_package_iterator_destroy(pi);
+
+ if (!option_nodeps && razor_transaction_describe(trans) > 0) {
+ printf("unsatisfied dependencies.\n");
+ exit(1);
+ }
+
+ if (option_test)
+ exit(0);
+
+ next = razor_transaction_finish(trans);
+
+ if (!option_justdb)
+ razor_set_diff(set, next, update_package, NULL);
+
+ razor_set_destroy(set);
+ razor_set_destroy(upstream);
+
+ razor_set_destroy(next);
+}
+
+static int
+for_each_option(const struct option *options,
+ const char *name, char short_name,
+ void (*fn)(const struct option *o,
+ const char *name, char short_name,
+ void *data), void *data)
+{
+ int i, count = 0;
+
+ for (i = 0; options[i].type != OPTION_LAST; i++) {
+ switch (options[i].type) {
+ case OPTION_GROUP:
+ count += for_each_option(options[i].data,
+ name, short_name, fn, data);
+ break;
+
+ case OPTION_BOOL:
+ case OPTION_STRING:
+ if (name && strcmp(options[i].name, name) == 0) {
+ fn(&options[i], name, 0, data);
+ count++;
+ break;
+ }
+
+ if (short_name &&
+ short_name == options[i].short_name) {
+ fn(&options[i], NULL, short_name, data);
+ count++;
+ break;
+ }
+ break;
+
+ case OPTION_LAST:
+ break;
+ }
+ }
+
+ return count;
+}
+
+static void
+handle_option(const struct option *o,
+ const char *name, char short_name, void *data)
+{
+ if (o->data == NULL) {
+ if (name)
+ printf("option --%s not supported\n", name);
+ else
+ printf("option -%c not supported\n", short_name);
+ return;
+ }
+
+ switch (o->type) {
+ case OPTION_BOOL:
+ *(int *) o->data = 1;
+ break;
+
+ case OPTION_STRING:
+ *(const char **) o->data = name + strlen(o->name) + 1;
+ break;
+
+ case OPTION_LAST:
+ case OPTION_GROUP:
+ /* Shouldn't happen. */
+ break;
+ }
+}
+
+static int
+parse_options(const struct option *options, int argc, const char **argv)
+{
+ int i, j, k;
+
+ for (i = 1, j = 0; i < argc; i++) {
+ if (argv[i][0] != '-') {
+ argv[j++] = argv[i];
+ continue;
+ }
+
+ if (argv[i][1] == '-') {
+ if (for_each_option(options, &argv[i][2], 0,
+ handle_option, NULL) == 0) {
+ printf("unknown option: %s\n", argv[i]);
+ exit(1);
+ }
+ continue;
+ }
+
+ for (k = 1; argv[i][k]; k++) {
+ if (for_each_option(options, NULL, argv[i][k],
+ handle_option, NULL) == 0) {
+ printf("unknown option: -%c\n", argv[i][k]);
+ exit(1);
+ }
+ }
+ }
+
+ return j;
+}
+
+static void
+print_options_help(const struct option *options)
+{
+ int i;
+
+ for (i = 0; options[i].type != OPTION_LAST; i++) {
+ switch (options[i].type) {
+ case OPTION_GROUP:
+ printf("%s\n", options[i].description);
+ print_options_help(options[i].data);
+ printf("\n");
+ break;
+
+ case OPTION_BOOL:
+ case OPTION_STRING:
+ printf(" ");
+ if (options[i].short_name)
+ printf("-%c", options[i].short_name);
+ if (options[i].short_name && options[i].name)
+ printf(", ");
+ if (options[i].name)
+ printf("--%s", options[i].name);
+ if (options[i].arg_name)
+ printf("=%s", options[i].arg_name);
+ if (options[i].description)
+ printf("\t\t%s", options[i].description);
+ printf("\n");
+ break;
+
+ case OPTION_LAST:
+ break;
+ }
+ }
+}
+
+static void
+print_options_usage(const struct option *options)
+{
+ int i;
+
+ for (i = 0; options[i].type != OPTION_LAST; i++) {
+ switch (options[i].type) {
+ case OPTION_GROUP:
+ print_options_usage(options[i].data);
+ break;
+
+ case OPTION_BOOL:
+ printf("[");
+ if (options[i].short_name)
+ printf("-%c", options[i].short_name);
+ if (options[i].short_name && options[i].name)
+ printf("|");
+ if (options[i].name)
+ printf("--%s", options[i].name);
+ printf("] ");
+ break;
+
+ case OPTION_STRING:
+ printf("[");
+ if (options[i].short_name)
+ printf("-%c", options[i].short_name);
+ if (options[i].short_name && options[i].name)
+ printf("|");
+ if (options[i].name)
+ printf("--%s", options[i].name);
+ if (options[i].arg_name)
+ printf("=%s", options[i].arg_name);
+ printf("] ");
+ break;
+
+
+ break;
+
+ case OPTION_LAST:
+ break;
+ }
+ }
+}
+
+int
+main(int argc, const char *argv[])
+{
+ argc = parse_options(rpm_options, argc, argv);
+
+ if (option_version) {
+ printf("razor rpm version hoopla.\n");
+ exit(0);
+ }
+
+ if (option_help) {
+ printf("Usage: rpm [OPTION...]\n");
+ print_options_help(rpm_options);
+ exit(0);
+ }
+
+ if (option_usage) {
+ printf("Usage: rpm [OPTION...]\n");
+ print_options_usage(rpm_options);
+ printf("\n");
+ exit(0);
+ }
+
+ if (option_initdb) {
+ command_initdb(argc, argv);
+ } else if (option_verify) {
+ command_verify(argc, argv);
+ } else if (option_query) {
+ command_query(argc, argv);
+ } else if (option_install) {
+ command_install(argc, argv);
+ } else if (option_upgrade) {
+ command_update(argc, argv);
+ } else if (option_erase) {
+ command_erase(argc, argv);
+ } else {
+ print_options_usage(rpm_options);
+ printf("\n");
+ exit(0);
+ }
+
+ return 0;
+}
diff --git a/src/test-driver.c b/src/test-driver.c
new file mode 100644
index 0000000..91bc62c
--- /dev/null
+++ b/src/test-driver.c
@@ -0,0 +1,463 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <expat.h>
+
+#include "razor.h"
+
+#define XML_BUFFER_SIZE 4096
+
+static void
+parse_xml_file(const char *filename,
+ XML_StartElementHandler start,
+ XML_EndElementHandler end,
+ void *data)
+{
+ XML_Parser parser;
+ char *buffer;
+ int fd, len, err;
+
+ parser = XML_ParserCreate(NULL);
+ XML_SetElementHandler(parser, start, end);
+ XML_SetUserData(parser, data);
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "failed to open %s: %m\n", filename);
+ exit(-1);
+ }
+
+ while (1) {
+ buffer = XML_GetBuffer(parser, XML_BUFFER_SIZE);
+ len = read(fd, buffer, XML_BUFFER_SIZE);
+ if (len == 0)
+ break;
+ err = XML_ParseBuffer(parser, len, len == 0);
+ if (err == XML_STATUS_ERROR) {
+ fprintf(stderr, "parse error at line %lu:\n%s\n",
+ XML_GetCurrentLineNumber(parser),
+ XML_ErrorString(XML_GetErrorCode(parser)));
+ exit(-1);
+ }
+ }
+
+ if (fd < 0) {
+ fprintf(stderr, "read: %m\n");
+ exit(-1);
+ }
+
+ close(fd);
+}
+
+struct test_context {
+ struct razor_set *system_set, *repo_set, *result_set;
+
+ struct razor_importer *importer;
+ struct razor_set **importer_set;
+
+ struct razor_transaction *trans;
+
+ char *install_pkgs[3], *remove_pkgs[3];
+ int n_install_pkgs, n_remove_pkgs;
+
+ int unsat;
+ int in_result;
+
+ int debug, errors;
+};
+
+static void
+get_atts(const char **atts, ...)
+{
+ va_list ap;
+ const char *name, **ptr;
+ int i;
+
+ va_start(ap, atts);
+ while (name = va_arg(ap, const char *), name != NULL) {
+ ptr = va_arg(ap, const char **);
+ *ptr = NULL;
+ for (i = 0; atts[i]; i += 2) {
+ if (strcmp(atts[i], name) == 0)
+ *ptr = atts[i + 1];
+ }
+ }
+ va_end(ap);
+}
+
+static enum razor_version_relation
+parse_relation (const char *rel_str)
+{
+ if (!rel_str)
+ return -1;
+ if (rel_str[0] == 'L')
+ return rel_str[1] == 'E' ? RAZOR_VERSION_LESS_OR_EQUAL : RAZOR_VERSION_LESS;
+ else if (rel_str[0] == 'G')
+ return rel_str[1] == 'E' ? RAZOR_VERSION_GREATER_OR_EQUAL : RAZOR_VERSION_GREATER;
+ else if (rel_str[0] == 'E' || rel_str[1] == 'Q')
+ return RAZOR_VERSION_EQUAL;
+ else
+ return -1;
+}
+
+static void
+start_test(struct test_context *ctx, const char **atts)
+{
+ const char *name = NULL;
+
+ get_atts(atts, "name", &name, NULL);
+ if (!name) {
+ fprintf(stderr, "Test with no name\n");
+ exit(1);
+ }
+ printf("%s\n", name);
+}
+
+static void
+end_test(struct test_context *ctx)
+{
+ if (ctx->system_set) {
+ razor_set_destroy(ctx->system_set);
+ ctx->system_set = NULL;
+ }
+ if (ctx->repo_set) {
+ razor_set_destroy(ctx->repo_set);
+ ctx->repo_set = NULL;
+ }
+ if (ctx->result_set) {
+ razor_set_destroy(ctx->result_set);
+ ctx->result_set = NULL;
+ }
+ if (ctx->trans) {
+ razor_transaction_destroy(ctx->trans);
+ ctx->trans = NULL;
+ }
+}
+
+static void
+start_set(struct test_context *ctx, const char **atts)
+{
+ const char *name = NULL;
+
+ ctx->importer = razor_importer_new();
+ get_atts(atts, "name", &name, NULL);
+ if (!name)
+ ctx->importer_set = &ctx->result_set;
+ else if (!strcmp(name, "system"))
+ ctx->importer_set = &ctx->system_set;
+ else if (!strcmp(name, "repo"))
+ ctx->importer_set = &ctx->repo_set;
+ else {
+ fprintf(stderr, " bad set name '%s'\n", name);
+ exit(1);
+ }
+}
+
+static void
+end_set(struct test_context *ctx)
+{
+ *ctx->importer_set = razor_importer_finish(ctx->importer);
+ ctx->importer = NULL;
+}
+
+static void
+start_package(struct test_context *ctx, const char **atts)
+{
+ const char *name = NULL, *version = NULL, *arch = NULL;
+
+ get_atts(atts, "name", &name,
+ "version", &version,
+ "arch", &arch,
+ NULL);
+
+ if (!name) {
+ fprintf(stderr, " package with no name\n");
+ exit(1);
+ }
+
+ razor_importer_begin_package(ctx->importer, name, version, arch);
+ razor_importer_add_property(ctx->importer, name,
+ RAZOR_VERSION_EQUAL, version,
+ RAZOR_PROPERTY_PROVIDES);
+}
+
+static void
+end_package(struct test_context *ctx)
+{
+ razor_importer_finish_package(ctx->importer);
+}
+
+static void
+add_property(struct test_context *ctx, enum razor_property_type type, const char *name, enum razor_version_relation rel, const char *version)
+{
+ razor_importer_add_property(ctx->importer, name,
+ rel, version, type);
+}
+
+static void
+check_unsatisfiable_property(struct test_context *ctx,
+ enum razor_property_type type,
+ const char *name,
+ enum razor_version_relation rel,
+ const char *version)
+{
+ static const char *relation_string[] = { "<", "<=", "=", ">=", ">" };
+
+ if (!version)
+ version = "";
+
+ if (razor_transaction_unsatisfied_property(ctx->trans,
+ name, rel, version, type))
+ return;
+
+ fprintf(stderr, " didn't get unsatisfiable '%s %s %s'\n",
+ name, relation_string[rel], version);
+ ctx->errors++;
+}
+
+static void
+start_property(struct test_context *ctx, enum razor_property_type type, const char **atts)
+{
+ const char *name = NULL, *rel_str = NULL, *version = NULL;
+ enum razor_version_relation rel;
+
+ get_atts(atts, "name", &name, "relation", &rel_str, "version", &version, NULL);
+ if (name == NULL) {
+ fprintf(stderr, " no name specified for property\n");
+ exit(1);
+ }
+ if (version) {
+ rel = parse_relation(rel_str);
+ if (rel == -1) {
+ fprintf(stderr, " bad or missing version relation for property %s\n", name);
+ exit(1);
+ }
+ } else
+ rel = RAZOR_VERSION_EQUAL;
+
+ if (ctx->unsat)
+ check_unsatisfiable_property(ctx, type, name, rel, version);
+ else
+ add_property(ctx, type, name, rel, version);
+}
+
+static void
+start_transaction(struct test_context *ctx, const char **atts)
+{
+ ctx->n_install_pkgs = 0;
+ ctx->n_remove_pkgs = 0;
+}
+
+static void
+end_transaction(struct test_context *ctx)
+{
+ struct razor_package *pkg;
+ int errors, i;
+
+ ctx->trans = razor_transaction_create(ctx->system_set, ctx->repo_set);
+ for (i = 0; i < ctx->n_install_pkgs; i++) {
+ pkg = razor_set_get_package(ctx->repo_set,
+ ctx->install_pkgs[i]);
+ razor_transaction_install_package(ctx->trans, pkg);
+ }
+ for (i = 0; i < ctx->n_remove_pkgs; i++) {
+ pkg = razor_set_get_package(ctx->system_set,
+ ctx->remove_pkgs[i]);
+ if (!pkg)
+ pkg = razor_set_get_package(ctx->repo_set,
+ ctx->remove_pkgs[i]);
+
+ razor_transaction_remove_package(ctx->trans, pkg);
+ }
+
+ errors = razor_transaction_resolve(ctx->trans);
+ printf("\n");
+
+ while (ctx->n_install_pkgs--)
+ free(ctx->install_pkgs[ctx->n_install_pkgs]);
+ while (ctx->n_remove_pkgs--)
+ free(ctx->remove_pkgs[ctx->n_remove_pkgs]);
+
+ if (!errors) {
+ struct razor_set *new;
+ new = razor_transaction_finish(ctx->trans);
+ ctx->trans = NULL;
+ ctx->system_set = new;
+ }
+}
+
+static void
+start_install_or_update(struct test_context *ctx, const char **atts)
+{
+ const char *name = NULL;
+
+ get_atts(atts, "name", &name, NULL);
+ if (!name) {
+ fprintf(stderr, " install/update with no name\n");
+ exit(1);
+ }
+
+ ctx->install_pkgs[ctx->n_install_pkgs++] = strdup(name);
+}
+
+static void
+start_remove(struct test_context *ctx, const char **atts)
+{
+ const char *name = NULL;
+
+ get_atts(atts, "name", &name, NULL);
+ if (!name) {
+ fprintf(stderr, " remove with no name\n");
+ exit(1);
+ }
+
+ ctx->remove_pkgs[ctx->n_remove_pkgs++] = strdup(name);
+}
+
+static void
+start_result(struct test_context *ctx, const char **atts)
+{
+ ctx->in_result = 1;
+}
+
+static void
+diff_callback(const char *name,
+ const char *old_version,
+ const char *new_version,
+ const char *arch,
+ void *data)
+{
+ struct test_context *ctx = data;
+
+ ctx->errors++;
+ if (old_version) {
+ fprintf(stderr, " result set should not contain %s %s\n",
+ name, old_version);
+ } else {
+ fprintf(stderr, " result set should contain %s %s\n",
+ name, new_version);
+ }
+}
+
+static void
+end_result(struct test_context *ctx)
+{
+ ctx->in_result = 0;
+
+ if (ctx->result_set) {
+ if (!ctx->system_set)
+ ctx->system_set = razor_set_create();
+ razor_set_diff(ctx->system_set, ctx->result_set,
+ diff_callback, ctx);
+ }
+}
+
+static void
+start_unsatisfiable(struct test_context *ctx, const char **atts)
+{
+ if (ctx->result_set) {
+ fprintf(stderr, "Expected to fail, but didn't\n");
+ exit(1);
+ }
+
+ ctx->unsat = 1;
+}
+
+static void
+end_unsatisfiable(struct test_context *ctx)
+{
+ ctx->unsat = 0;
+}
+
+static void
+start_test_element(void *data, const char *element, const char **atts)
+{
+ struct test_context *ctx = data;
+
+ if (strcmp(element, "tests") == 0) {
+ ;
+ } else if (strcmp(element, "test") == 0) {
+ start_test(ctx, atts);
+ } else if (strcmp(element, "set") == 0) {
+ start_set(ctx, atts);
+ } else if (strcmp(element, "transaction") == 0) {
+ start_transaction(ctx, atts);
+ } else if (strcmp(element, "install") == 0) {
+ start_install_or_update(ctx, atts);
+ } else if (strcmp(element, "install") == 0) {
+ start_install_or_update(ctx, atts);
+ } else if (strcmp(element, "remove") == 0) {
+ start_remove(ctx, atts);
+ } else if (strcmp(element, "result") == 0) {
+ start_result(ctx, atts);
+ } else if (strcmp(element, "unsatisfiable") == 0) {
+ start_unsatisfiable(ctx, atts);
+ } else if (strcmp(element, "package") == 0) {
+ start_package(ctx, atts);
+ } else if (strcmp(element, "requires") == 0) {
+ start_property(ctx, RAZOR_PROPERTY_REQUIRES, atts);
+ } else if (strcmp(element, "provides") == 0) {
+ start_property(ctx, RAZOR_PROPERTY_PROVIDES, atts);
+ } else if (strcmp(element, "conflicts") == 0) {
+ start_property(ctx, RAZOR_PROPERTY_CONFLICTS, atts);
+ } else if (strcmp(element, "obsoletes") == 0) {
+ start_property(ctx, RAZOR_PROPERTY_OBSOLETES, atts);
+ } else {
+ fprintf(stderr, "Unrecognized element '%s'\n", element);
+ exit(1);
+ }
+}
+
+static void
+end_test_element (void *data, const char *element)
+{
+ struct test_context *ctx = data;
+
+ if (strcmp(element, "test") == 0) {
+ end_test(ctx);
+ } else if (strcmp(element, "set") == 0) {
+ end_set(ctx);
+ } else if (strcmp(element, "package") == 0) {
+ end_package(ctx);
+ } else if (strcmp(element, "transaction") == 0) {
+ end_transaction(ctx);
+ } else if (strcmp(element, "result") == 0) {
+ end_result(ctx);
+ } else if (strcmp(element, "unsatisfiable") == 0) {
+ end_unsatisfiable(ctx);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct test_context ctx;
+ const char *test_file;
+
+ memset(&ctx, 0, sizeof ctx);
+
+ if (argc > 3) {
+ fprintf(stderr, "usage: %s [-d] [TESTS-FILE]\n", argv[0]);
+ exit(-1);
+ }
+
+ if (argc >= 2 && !strcmp (argv[1], "-d")) {
+ ctx.debug = 1;
+ argc--;
+ argv++;
+ }
+ if (argc == 2)
+ test_file = argv[1];
+ else
+ test_file = "test.xml";
+
+ parse_xml_file(test_file, start_test_element, end_test_element, &ctx);
+
+ if (ctx.errors) {
+ fprintf(stderr, "\n%d errors\n", ctx.errors);
+ return 1;
+ } else
+ return 0;
+}
diff --git a/src/test.xml b/src/test.xml
new file mode 100644
index 0000000..f24cf96
--- /dev/null
+++ b/src/test.xml
@@ -0,0 +1,858 @@
+<tests>
+
+ <test name="testEmpty">
+ <set name="system"/>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ <remove name="zsh"/>
+ </transaction>
+ <result>
+ <set/>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageNoRequires">
+ <set name="system"/>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireNotProvided">
+ <set name="system"/>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <unsatisfiable>
+ <requires name="zip"/>
+ </unsatisfiable>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireInstalled">
+ <set name="system">
+ <package name="zip" version="1-1" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="1-1" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireInstalledRequireNotProvided">
+ <set name="system">
+ <package name="zip" version="1-2" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip"/>
+ <!-- This may be a bug in the python test: it looks
+ like they meant to have zip require zap, not zsh.
+ But the install succeeds if you do that...
+ -->
+ <requires name="zap"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <unsatisfiable>
+ <requires name="zap"/>
+ </unsatisfiable>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireInstalledRequireInstall">
+ <set name="system">
+ <package name="zip" version="1-3" arch="i386">
+ <requires name="zap"/>
+ </package>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip"/>
+ </package>
+ <package name="zap" version="1-2" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ <install name="zap"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zap" version="1-2" arch="i386"/>
+ <package name="zip" version="1-3" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer1NotProvided">
+ <set name="system">
+ <package name="zip" version="1.0-2" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="1.3-2"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <unsatisfiable>
+ <requires name="zip" relation="EQ" version="1.3-2"/>
+ </unsatisfiable>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer1Installed">
+ <set name="system">
+ <package name="zip" version="1.3-2" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="1.3-2"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="1.3-2" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer2NotProvided">
+ <set name="system">
+ <package name="zip" version="1.3-2" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="1.3-4"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <unsatisfiable>
+ <requires name="zip" relation="EQ" version="1.3-4"/>
+ </unsatisfiable>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer2Installed">
+ <set name="system">
+ <package name="zip" version="1.3-4" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="1.3-4"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="1.3-4" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer3NotProvided">
+ <set name="system">
+ <package name="zip" version="0:1.3-4" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="GE" version="1:1.3-4"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <unsatisfiable>
+ <requires name="zip" relation="GE" version="1:1.3-4"/>
+ </unsatisfiable>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer3Installed">
+ <set name="system">
+ <package name="zip" version="2:1.3-4" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="GE" version="2:1.3-4"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="2:1.3-4" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer4NotProvided">
+ <set name="system">
+ <package name="zip" version="2:1.3-4" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="LT" version="2:1.3-4"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <unsatisfiable>
+ <requires name="zip" relation="LT" version="2:1.3-4"/>
+ </unsatisfiable>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer4_1Installed">
+ <set name="system">
+ <package name="zip" version="2:1.0-4" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="LT" version="2:1.3-4"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="2:1.0-4" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer4_2Installed">
+ <set name="system">
+ <package name="zip" version="2:1.3-3" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="LT" version="2:1.3-4"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="2:1.3-3" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer4_3Installed">
+ <set name="system">
+ <package name="zip" version="1.3-4" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="LT" version="2:1.3-4"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="1.3-4" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer4_4Installed">
+ <set name="system">
+ <package name="zip" version="1:1.3-4" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="LT" version="2:1.3-4"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="1:1.3-4" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireVer4_5Installed">
+ <set name="system">
+ <package name="zip" version="2:0.3-4" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="LT" version="2:1.3-4"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="2:0.3-4" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireXtraBadVer">
+ <set name="system">
+ <package name="zip" version="2:1.3-4" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="2:1.3-4"/>
+ <requires name="zap" relation="EQ" version="2:1.3-4"/>
+ </package>
+ <package name="zap" version="0:1.3-4" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <unsatisfiable>
+ <requires name="zap" relation="EQ" version="2:1.3-4"/>
+ </unsatisfiable>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireXtra">
+ <set name="system">
+ <package name="zip" version="2:1.3-4" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="2:1.3-4"/>
+ <requires name="zap" relation="EQ" version="4:2.6-8"/>
+ </package>
+ <package name="zap" version="4:2.6-8" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zap" version="4:2.6-8" arch="i386"/>
+ <package name="zip" version="2:1.3-4" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireInstalledRequireXtra">
+ <set name="system">
+ <package name="zip" version="2:1.3-4" arch="i386">
+ <requires name="zap" relation="EQ" version="4:2.6-8"/>
+ </package>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="2:1.3-4"/>
+ </package>
+ <!-- This may be a bug in the python test; it doesn't
+ actually matter whether or not zap is available
+ -->
+ <package name="zap" version="4:2.6-8" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="2:1.3-4" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireUpgradeRequireXtraErr">
+ <set name="system">
+ <package name="zip" version="2:1.3-4" arch="i386">
+ <requires name="zap" relation="EQ" version="2:1.3-3"/>
+ </package>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="4:2.6-8"/>
+ </package>
+ <package name="zip" version="4:2.6-8" arch="i386">
+ <requires name="zap" relation="EQ" version="2:1.3-4"/>
+ </package>
+ <package name="zap" version="2:1.3-4" arch="i386">
+ <requires name="zsh" relation="EQ" version="2:4-8"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <unsatisfiable>
+ <requires name="zsh" relation="EQ" version="2:4-8"/>
+ </unsatisfiable>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireUpgradeRequireXtraOk">
+ <set name="system">
+ <package name="zip" version="2:1.3-4" arch="i386">
+ <requires name="zap" relation="EQ" version="2:1.3-3"/>
+ </package>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="4:2.6-8"/>
+ </package>
+ <package name="zip" version="4:2.6-8" arch="i386">
+ <requires name="zap" relation="EQ" version="2:1.3-4"/>
+ </package>
+ <package name="zap" version="2:1.3-4" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zap" version="2:1.3-4" arch="i386"/>
+ <package name="zip" version="4:2.6-8" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireMultiXtra">
+ <set name="system"/>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="4:2.6-8"/>
+ </package>
+ <package name="zip" version="4:2.6-8" arch="i386">
+ <requires name="zap" relation="EQ" version="2:1.3-4"/>
+ </package>
+ <package name="zap" version="2:1.3-4" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zap" version="2:1.3-4" arch="i386"/>
+ <package name="zip" version="4:2.6-8" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireInstalledMultiLib">
+ <set name="system">
+ <package name="zip" version="1-3" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="x86_64">
+ <requires name="zip"/>
+ </package>
+ <package name="zip" version="1-3" arch="x86_64"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="1-3" arch="i386"/>
+ <package name="zsh" version="1-1" arch="x86_64"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireXtra1MultiLib">
+ <set name="system"/>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="x86_64">
+ <requires name="zip"/>
+ </package>
+ <package name="zip" version="1-3" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="1-3" arch="i386"/>
+ <package name="zsh" version="1-1" arch="x86_64"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireXtra2_64MultiLib">
+ <set name="system"/>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="x86_64">
+ <requires name="zip"/>
+ </package>
+ <package name="zip" version="1-3" arch="i386"/>
+ <package name="zip" version="1-3" arch="x86_64"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zsh" version="1-1" arch="x86_64"/>
+ <package name="zip" version="1-3" arch="x86_64"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testInstallSinglePackageRequireXtra2_32MultiLib">
+ <set name="system"/>
+ <set name="repo">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip"/>
+ </package>
+ <package name="zip" version="1-3" arch="i386"/>
+ <package name="zip" version="1-3" arch="x86_64"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="1-3" arch="i386"/>
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateSinglePackage">
+ <set name="system">
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-3" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zsh" version="1-3" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateForDependency">
+ <set name="system">
+ <package name="zip" version="0:1-1" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="0:1-1" arch="i386">
+ <requires name="zip" relation="EQ" version="0:2-1"/>
+ </package>
+ <package name="zip" version="0:2-1" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="0:2-1" arch="i386"/>
+ <package name="zsh" version="0:1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateSplitPackage">
+ <set name="system">
+ <package name="zip" version="0:1-1" arch="i386">
+ <provides name="libzip" relation="EQ" version="0:1-1"/>
+ </package>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="0:1-1" arch="i386">
+ <requires name="libzip" relation="EQ" version="0:2-1"/>
+ </package>
+ <package name="zip" version="0:2-1" arch="i386">
+ <requires name="zip-libs" relation="EQ" version="0:2-1"/>
+ </package>
+ <package name="zip-libs" version="0:2-1" arch="i386">
+ <provides name="libzip" relation="EQ" version="0:2-1"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="0:1-1" arch="i386"/>
+ <package name="zip-libs" version="0:2-1" arch="i386"/>
+ <package name="zsh" version="0:1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateSinglePackageNewRequires">
+ <set name="system">
+ <package name="zsh" version="1-1" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-3" arch="i386">
+ <requires name="zip"/>
+ </package>
+ <package name="zip" version="1-3" arch="x86_64"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zsh" version="1-3" arch="i386"/>
+ <package name="zip" version="1-3" arch="x86_64"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateSinglePackageOldRequires">
+ <set name="system">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip"/>
+ </package>
+ <package name="zip" version="1-3" arch="x86_64"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-3" arch="i386">
+ <requires name="zip"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="1-3" arch="x86_64"/>
+ <package name="zsh" version="1-3" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateSinglePackageOldRequiresGone">
+ <set name="system">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip"/>
+ </package>
+ <package name="zip" version="1-3" arch="x86_64"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-3" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="1-3" arch="x86_64"/>
+ <package name="zsh" version="1-3" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateSinglePackageObsoletesOldRequirement">
+ <set name="system">
+ <package name="zsh" version="1-1" arch="i386">
+ <requires name="zip"/>
+ </package>
+ <package name="zip" version="1-1" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="1-3" arch="i386">
+ <!-- depsolvetests.py actually adds the obsoletes
+ to the installed zsh rather than the new zsh,
+ but I'm assuming that's a bug -->
+ <obsoletes name="zip"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zsh" version="1-3" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateForConflict">
+ <set name="system">
+ <package name="zip" version="0:1-1" arch="i386"/>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="0:1-1" arch="i386">
+ <conflicts name="zip" relation="LE" version="0:1-1"/>
+ </package>
+ <package name="zip" version="0:2-1" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="0:2-1" arch="i386"/>
+ <package name="zsh" version="0:1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateForConflict2">
+ <set name="system">
+ <package name="zip" version="0:1-1" arch="i386">
+ <conflicts name="zsh" relation="LE" version="0:1-1"/>
+ </package>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="0:1-1" arch="i386"/>
+ <package name="zip" version="0:2-1" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zsh" version="0:1-1" arch="i386"/>
+ <package name="zip" version="0:2-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateForConflictProvide">
+ <set name="system">
+ <package name="zip" version="0:1-1" arch="i386">
+ <provides name="zippy" relation="EQ" version="0:1-1"/>
+ </package>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="0:1-1" arch="i386">
+ <conflicts name="zippy" relation="LE" version="0:1-1"/>
+ </package>
+ <package name="zip" version="0:2-1" arch="i386"/>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="0:2-1" arch="i386"/>
+ <package name="zsh" version="0:1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testUpdateForConflictProvide2">
+ <set name="system">
+ <package name="zip" version="0:1-1" arch="i386">
+ <conflicts name="zippy" relation="GT" version="0:1-1"/>
+ <conflicts name="zippy" relation="LT" version="0:1-1"/>
+ </package>
+ </set>
+ <set name="repo">
+ <package name="zsh" version="0:1-1" arch="i386">
+ <provides name="zippy" relation="EQ" version="0:2-1"/>
+ </package>
+ <package name="zip" version="0:2-1" arch="i386">
+ <conflicts name="zippy" relation="GT" version="0:2-1"/>
+ <conflicts name="zippy" relation="LT" version="0:2-1"/>
+ </package>
+ </set>
+ <transaction>
+ <install name="zsh"/>
+ </transaction>
+ <result>
+ <set>
+ <package name="zip" version="0:2-1" arch="i386"/>
+ <package name="zsh" version="0:1-1" arch="i386"/>
+ </set>
+ </result>
+ </test>
+
+ <test name="testEraseSinglePackage">
+ <set name="system">
+ <package name="zsh" version="0:1-1" arch="i386"/>
+ </set>
+ <set name="repo"/>
+ <transaction>
+ <remove name="zsh"/>
+ </transaction>
+ <result>
+ <set/>
+ </result>
+ </test>
+
+ <test name="testEraseSinglePackageRequiredByOneInstalled">
+ <set name="system">
+ <package name="zippy" version="0:1-1" arch="i386">
+ <requires name="zsh"/>
+ </package>
+ <package name="zsh" version="0:1-1" arch="i386"/>
+ </set>
+ <set name="repo"/>
+ <transaction>
+ <remove name="zsh"/>
+ </transaction>
+ <result>
+ <set/>
+ </result>
+ </test>
+</tests>