Imported Upstream version 0.6.13 12/94112/1 upstream/0.6.13
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 27 Oct 2016 05:54:40 +0000 (14:54 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 27 Oct 2016 05:54:40 +0000 (14:54 +0900)
Change-Id: I9fed388d0b8658275d9089b22473596379566069
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
61 files changed:
CMakeLists.txt
VERSION.cmake
bindings/solv.i
doc/filters/xcode.pl
doc/libsolv-bindings.3
doc/libsolv-bindings.txt
examples/CMakeLists.txt
examples/solv.c [deleted file]
examples/solv/CMakeLists.txt [new file with mode: 0644]
examples/solv/checksig.c [new file with mode: 0644]
examples/solv/checksig.h [new file with mode: 0644]
examples/solv/deltarpm.c [new file with mode: 0644]
examples/solv/deltarpm.h [new file with mode: 0644]
examples/solv/fastestmirror.c [new file with mode: 0644]
examples/solv/fastestmirror.h [new file with mode: 0644]
examples/solv/fileconflicts.c [new file with mode: 0644]
examples/solv/fileconflicts.h [new file with mode: 0644]
examples/solv/fileprovides.c [new file with mode: 0644]
examples/solv/fileprovides.h [new file with mode: 0644]
examples/solv/mirror.c [new file with mode: 0644]
examples/solv/mirror.h [new file with mode: 0644]
examples/solv/patchjobs.c [new file with mode: 0644]
examples/solv/patchjobs.h [new file with mode: 0644]
examples/solv/repoinfo.c [new file with mode: 0644]
examples/solv/repoinfo.h [new file with mode: 0644]
examples/solv/repoinfo_cache.c [new file with mode: 0644]
examples/solv/repoinfo_cache.h [new file with mode: 0644]
examples/solv/repoinfo_config_debian.c [new file with mode: 0644]
examples/solv/repoinfo_config_debian.h [new file with mode: 0644]
examples/solv/repoinfo_config_urpmi.c [new file with mode: 0644]
examples/solv/repoinfo_config_urpmi.h [new file with mode: 0644]
examples/solv/repoinfo_config_yum.c [new file with mode: 0644]
examples/solv/repoinfo_config_yum.h [new file with mode: 0644]
examples/solv/repoinfo_download.c [new file with mode: 0644]
examples/solv/repoinfo_download.h [new file with mode: 0644]
examples/solv/repoinfo_system_debian.c [new file with mode: 0644]
examples/solv/repoinfo_system_debian.h [new file with mode: 0644]
examples/solv/repoinfo_system_rpm.c [new file with mode: 0644]
examples/solv/repoinfo_system_rpm.h [new file with mode: 0644]
examples/solv/repoinfo_type_debian.c [new file with mode: 0644]
examples/solv/repoinfo_type_debian.h [new file with mode: 0644]
examples/solv/repoinfo_type_mdk.c [new file with mode: 0644]
examples/solv/repoinfo_type_mdk.h [new file with mode: 0644]
examples/solv/repoinfo_type_rpmmd.c [new file with mode: 0644]
examples/solv/repoinfo_type_rpmmd.h [new file with mode: 0644]
examples/solv/repoinfo_type_susetags.c [new file with mode: 0644]
examples/solv/repoinfo_type_susetags.h [new file with mode: 0644]
examples/solv/solv.c [new file with mode: 0644]
ext/repo_autopattern.c
ext/repo_mdk.c
ext/testcase.c
package/libsolv.changes
src/CMakeLists.txt
src/hash.h
src/libsolv.ver
src/order.c [new file with mode: 0644]
src/solver.c
src/solver.h
src/transaction.c
src/transaction.h
tools/installcheck.c

index 2ba307c..c089e21 100644 (file)
@@ -111,13 +111,22 @@ SET (have_system ${have_system}x)
 ENDIF (ARCHLINUX)
 
 IF (MANDRIVA)
-MESSAGE (STATUS "Building for Mandriva/Mageia")
+MESSAGE (STATUS "Building for Mandriva")
 ADD_DEFINITIONS (-DMANDRIVA)
 SET (ENABLE_MDKREPO ON)
 SET (ENABLE_RPMDB ON)
 SET (have_system ${have_system}x)
 ENDIF (MANDRIVA)
 
+IF (MAGEIA)
+MESSAGE (STATUS "Building for Mageia")
+ADD_DEFINITIONS (-DMAGEIA)
+SET (ENABLE_MDKREPO ON)
+SET (ENABLE_RPMDB ON)
+SET (ENABLE_LZMA_COMPRESSION ON)
+SET (have_system ${have_system}x)
+ENDIF (MAGEIA)
+
 IF (HAIKU)
 MESSAGE(STATUS "Building for Haiku")
 FIND_LIBRARY(HAIKU_BE_LIBRARY NAMES be)
index 086ee95..f67959d 100644 (file)
@@ -49,5 +49,5 @@ SET(LIBSOLVEXT_SOVERSION "0")
 
 SET(LIBSOLV_MAJOR "0")
 SET(LIBSOLV_MINOR "6")
-SET(LIBSOLV_PATCH "12")
+SET(LIBSOLV_PATCH "13")
 
index 0c37f6c..823deda 100644 (file)
@@ -1171,6 +1171,7 @@ typedef struct {
   static const Id SOLVER_VERIFY = SOLVER_VERIFY;
   static const Id SOLVER_DROP_ORPHANED = SOLVER_DROP_ORPHANED;
   static const Id SOLVER_USERINSTALLED = SOLVER_USERINSTALLED;
+  static const Id SOLVER_ALLOWUNINSTALL = SOLVER_ALLOWUNINSTALL;
   static const Id SOLVER_JOBMASK = SOLVER_JOBMASK;
   static const Id SOLVER_WEAK = SOLVER_WEAK;
   static const Id SOLVER_ESSENTIAL = SOLVER_ESSENTIAL;
index 089f511..407641f 100755 (executable)
@@ -36,7 +36,7 @@ while(<STDIN>) {
     $_ = " $_";
     $_ = "$_ ";
     if (s/^ TCL +/ /) {
-      s/(\$[a-zA-Z_][a-zA-Z0-9_]*)/<-S><I>$1<-I><S>/g;
+      s/(\$[a-zA-Z_][a-zA-Z0-9_:]*)/<-S><I>$1<-I><S>/g;
     } else {
       s/(?<=[^a-zA-Z_\&:\.\'\";])(?!solv\W|Solv\W|Pool\W)([\$\@a-zA-Z_][a-zA-Z0-9_]*)(?=[^a-zA-Z0-9_\(;\[])(?!::)(?! [^=])/<-S><I>$1<-I><S>/g;
     }
index 9fe26c1..ad8a7e6 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: Libsolv-Bindings
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 08/28/2015
+.\"      Date: 09/21/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "LIBSOLV\-BINDINGS" "3" "08/28/2015" "libsolv" "LIBSOLV"
+.TH "LIBSOLV\-BINDINGS" "3" "09/21/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -399,8 +399,8 @@ Swig implements all constants as numeric variables, constants belonging to a lib
 .RS 4
 .\}
 .nf
-\fI$pool\fR \fBset_flag\fR \fI$solv\fR\fB::Pool_POOL_FLAG_OBSOLETEUSESCOLORS  1\fR
-\fBputs [\fR\fI$solvable\fR \fBlookup_str\fR \fI$solv\fR\fB::SOLVABLE_SUMMARY]\fR
+\fI$pool\fR \fBset_flag\fR \fI$solv::Pool_POOL_FLAG_OBSOLETEUSESCOLORS\fR  \fB1\fR
+\fBputs [\fR\fI$solvable\fR \fBlookup_str\fR \fI$solv::SOLVABLE_SUMMARY\fR\fB]\fR
 .fi
 .if n \{\
 .RE
@@ -3251,6 +3251,11 @@ Fix dependency problems of matching installed packages\&. The default is to igno
 The matching installed packages are considered to be installed by a user, thus not installed to fulfill some dependency\&. This is needed input for the calculation of unneeded packages for jobs that have the SOLVER_CLEANDEPS flag set\&.
 .RE
 .PP
+\fBSOLVER_ALLOWUNINSTALL\fR
+.RS 4
+Allow the solver to deinstall the matching installed packages if they get into the way of resolving a dependency\&. This is like the SOLVER_FLAG_ALLOW_UNINSTALL flag, but limited to a specific set of packages\&.
+.RE
+.PP
 \fBSOLVER_JOBMASK\fR
 .RS 4
 A mask containing all the above action bits\&.
index 175d922..13d73bd 100644 (file)
@@ -1873,6 +1873,11 @@ thus not installed to fulfill some dependency. This is needed input for
 the calculation of unneeded packages for jobs that have the
 SOLVER_CLEANDEPS flag set.
 
+*SOLVER_ALLOWUNINSTALL*::
+Allow the solver to deinstall the matching installed packages if they get
+into the way of resolving a dependency. This is like the
+SOLVER_FLAG_ALLOW_UNINSTALL flag, but limited to a specific set of packages.
+
 *SOLVER_JOBMASK*::
 A mask containing all the above action bits.
 
index d29ea68..703796c 100644 (file)
@@ -1,10 +1,5 @@
-IF (SUSE OR FEDORA OR DEBIAN)
+IF (SUSE OR FEDORA OR DEBIAN OR MANDRIVA OR MAGEIA)
 
-ADD_EXECUTABLE (solv solv.c)
-TARGET_LINK_LIBRARIES (solv libsolvext libsolv ${SYSTEM_LIBRARIES})
+ADD_SUBDIRECTORY (solv)
 
-INSTALL(TARGETS
-    solv
-    DESTINATION ${BIN_INSTALL_DIR})
-
-ENDIF (SUSE OR FEDORA OR DEBIAN)
+ENDIF (SUSE OR FEDORA OR DEBIAN OR MANDRIVA OR MAGEIA)
diff --git a/examples/solv.c b/examples/solv.c
deleted file mode 100644 (file)
index fc420b3..0000000
+++ /dev/null
@@ -1,3415 +0,0 @@
-/*
- * Copyright (c) 2009-2013, Novell Inc.
- *
- * This program is licensed under the BSD license, read LICENSE.BSD
- * for further information
- */
-
-/* solv, a little software installer demoing the sat solver library */
-
-/* things it does:
- * - understands globs for package names / dependencies
- * - understands .arch suffix
- * - installation of commandline packages
- * - repository data caching
- * - on demand loading of secondary repository data
- * - gpg and checksum verification
- * - file conflicts
- * - deltarpm support
- * - fastestmirror implementation
- *
- * things available in the library but missing from solv:
- * - vendor policy loading
- * - soft locks file handling
- * - multi version handling
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <dirent.h>
-#include <fnmatch.h>
-#include <unistd.h>
-#include <zlib.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <sys/utsname.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <time.h>
-#include <sys/time.h>
-#include <sys/dir.h>
-#include <sys/stat.h>
-
-#include <sys/socket.h>
-#include <netdb.h>
-#include <poll.h>
-#include <errno.h>
-
-#include "pool.h"
-#include "poolarch.h"
-#include "repo.h"
-#include "evr.h"
-#include "policy.h"
-#include "util.h"
-#include "solver.h"
-#include "solverdebug.h"
-#include "chksum.h"
-#include "repo_solv.h"
-#include "selection.h"
-
-#include "repo_write.h"
-#ifdef ENABLE_RPMDB
-#include "repo_rpmdb.h"
-#include "pool_fileconflicts.h"
-#endif
-#ifdef ENABLE_PUBKEY
-#include "repo_pubkey.h"
-#endif
-#ifdef ENABLE_DEBIAN
-#include "repo_deb.h"
-#endif
-#ifdef ENABLE_RPMMD
-#include "repo_rpmmd.h"
-#include "repo_repomdxml.h"
-#include "repo_updateinfoxml.h"
-#include "repo_deltainfoxml.h"
-#endif
-#ifdef ENABLE_APPDATA
-#include "repo_appdata.h"
-#endif
-#ifdef ENABLE_SUSEREPO
-#include "repo_products.h"
-#include "repo_susetags.h"
-#include "repo_content.h"
-#endif
-#ifdef SUSE
-#include "repo_autopattern.h"
-#endif
-#include "solv_xfopen.h"
-
-#ifdef FEDORA
-# define REPOINFO_PATH "/etc/yum.repos.d"
-#endif
-#ifdef SUSE
-# define REPOINFO_PATH "/etc/zypp/repos.d"
-# define PRODUCTS_PATH "/etc/products.d"
-# define SOFTLOCKS_PATH "/var/lib/zypp/SoftLocks"
-#endif
-#ifdef ENABLE_APPDATA
-# define APPDATA_PATH "/usr/share/appdata"
-#endif
-
-#define SOLVCACHE_PATH "/var/cache/solv"
-
-#define METADATA_EXPIRE (60 * 15)
-
-struct repoinfo {
-  Repo *repo;
-
-  char *alias;
-  char *name;
-  int enabled;
-  int autorefresh;
-  char *baseurl;
-  char *metalink;
-  char *mirrorlist;
-  char *path;
-  int type;
-  int pkgs_gpgcheck;
-  int repo_gpgcheck;
-  int priority;
-  int keeppackages;
-  int metadata_expire;
-  char **components;
-  int ncomponents;
-  unsigned char cookie[32];
-  int extcookieset;
-  unsigned char extcookie[32];
-  int incomplete;
-};
-
-#ifdef FEDORA
-char *
-yum_substitute(Pool *pool, char *line)
-{
-  char *p, *p2;
-  static char *releaseevr;
-  static char *basearch;
-
-  if (!line)
-    {
-      solv_free(releaseevr);
-      releaseevr = 0;
-      solv_free(basearch);
-      basearch = 0;
-      return 0;
-    }
-  p = line;
-  while ((p2 = strchr(p, '$')) != 0)
-    {
-      if (!strncmp(p2, "$releasever", 11))
-       {
-         if (!releaseevr)
-           {
-             void *rpmstate;
-             Queue q;
-       
-             queue_init(&q);
-             rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
-             rpm_installedrpmdbids(rpmstate, "Providename", "redhat-release", &q);
-             if (q.count)
-               {
-                 void *handle;
-                 char *p;
-                 handle = rpm_byrpmdbid(rpmstate, q.elements[0]);
-                 releaseevr = handle ? rpm_query(handle, SOLVABLE_EVR) : 0;
-                 if (releaseevr && (p = strchr(releaseevr, '-')) != 0)
-                   *p = 0;
-               }
-             rpm_state_free(rpmstate);
-             queue_free(&q);
-             if (!releaseevr)
-               {
-                 fprintf(stderr, "no installed package provides 'redhat-release', cannot determine $releasever\n");
-                 exit(1);
-               }
-           }
-         *p2 = 0;
-         p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
-         p2 = p + (p2 - line);
-         line = p;
-         p = p2 + strlen(releaseevr);
-         continue;
-       }
-      if (!strncmp(p2, "$basearch", 9))
-       {
-         if (!basearch)
-           {
-             struct utsname un;
-             if (uname(&un))
-               {
-                 perror("uname");
-                 exit(1);
-               }
-             basearch = strdup(un.machine);
-             if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
-               basearch[1] = '3';
-           }
-         *p2 = 0;
-         p = pool_tmpjoin(pool, line, basearch, p2 + 9);
-         p2 = p + (p2 - line);
-         line = p;
-         p = p2 + strlen(basearch);
-         continue;
-       }
-      p = p2 + 1;
-    }
-  return line;
-}
-#endif
-
-#define TYPE_UNKNOWN   0
-#define TYPE_SUSETAGS  1
-#define TYPE_RPMMD     2
-#define TYPE_PLAINDIR  3
-#define TYPE_DEBIAN     4
-
-static int
-read_repoinfos_sort(const void *ap, const void *bp)
-{
-  const struct repoinfo *a = ap;
-  const struct repoinfo *b = bp;
-  return strcmp(a->alias, b->alias);
-}
-
-#if defined(SUSE) || defined(FEDORA)
-
-struct repoinfo *
-read_repoinfos(Pool *pool, int *nrepoinfosp)
-{
-  const char *reposdir = REPOINFO_PATH;
-  char buf[4096];
-  char buf2[4096], *kp, *vp, *kpe;
-  DIR *dir;
-  FILE *fp;
-  struct dirent *ent;
-  int l, rdlen;
-  struct repoinfo *repoinfos = 0, *cinfo;
-  int nrepoinfos = 0;
-
-  rdlen = strlen(reposdir);
-  dir = opendir(reposdir);
-  if (!dir)
-    {
-      *nrepoinfosp = 0;
-      return 0;
-    }
-  while ((ent = readdir(dir)) != 0)
-    {
-      if (ent->d_name[0] == '.')
-       continue;
-      l = strlen(ent->d_name);
-      if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
-       continue;
-      snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
-      if ((fp = fopen(buf, "r")) == 0)
-       {
-         perror(buf);
-         continue;
-       }
-      cinfo = 0;
-      while(fgets(buf2, sizeof(buf2), fp))
-       {
-         l = strlen(buf2);
-         if (l == 0)
-           continue;
-         while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
-           buf2[--l] = 0;
-         kp = buf2;
-         while (*kp == ' ' || *kp == '\t')
-           kp++;
-         if (!*kp || *kp == '#')
-           continue;
-#ifdef FEDORA
-         if (strchr(kp, '$'))
-           kp = yum_substitute(pool, kp);
-#endif
-         if (*kp == '[')
-           {
-             vp = strrchr(kp, ']');
-             if (!vp)
-               continue;
-             *vp = 0;
-             repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
-             cinfo = repoinfos + nrepoinfos++;
-             memset(cinfo, 0, sizeof(*cinfo));
-             cinfo->alias = strdup(kp + 1);
-             cinfo->type = TYPE_RPMMD;
-             cinfo->autorefresh = 1;
-             cinfo->priority = 99;
-#ifndef FEDORA
-             cinfo->repo_gpgcheck = 1;
-#endif
-             cinfo->metadata_expire = METADATA_EXPIRE;
-             continue;
-           }
-         if (!cinfo)
-           continue;
-          vp = strchr(kp, '=');
-         if (!vp)
-           continue;
-         for (kpe = vp - 1; kpe >= kp; kpe--)
-           if (*kpe != ' ' && *kpe != '\t')
-             break;
-         if (kpe == kp)
-           continue;
-         vp++;
-         while (*vp == ' ' || *vp == '\t')
-           vp++;
-         kpe[1] = 0;
-         if (!strcmp(kp, "name"))
-           cinfo->name = strdup(vp);
-         else if (!strcmp(kp, "enabled"))
-           cinfo->enabled = *vp == '0' ? 0 : 1;
-         else if (!strcmp(kp, "autorefresh"))
-           cinfo->autorefresh = *vp == '0' ? 0 : 1;
-         else if (!strcmp(kp, "gpgcheck"))
-           cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
-         else if (!strcmp(kp, "repo_gpgcheck"))
-           cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
-         else if (!strcmp(kp, "baseurl"))
-           cinfo->baseurl = strdup(vp);
-         else if (!strcmp(kp, "mirrorlist"))
-           {
-             if (strstr(vp, "metalink"))
-               cinfo->metalink = strdup(vp);
-             else
-               cinfo->mirrorlist = strdup(vp);
-           }
-         else if (!strcmp(kp, "path"))
-           {
-             if (vp && strcmp(vp, "/") != 0)
-               cinfo->path = strdup(vp);
-           }
-         else if (!strcmp(kp, "type"))
-           {
-             if (!strcmp(vp, "yast2"))
-               cinfo->type = TYPE_SUSETAGS;
-             else if (!strcmp(vp, "rpm-md"))
-               cinfo->type = TYPE_RPMMD;
-             else if (!strcmp(vp, "plaindir"))
-               cinfo->type = TYPE_PLAINDIR;
-             else
-               cinfo->type = TYPE_UNKNOWN;
-           }
-         else if (!strcmp(kp, "priority"))
-           cinfo->priority = atoi(vp);
-         else if (!strcmp(kp, "keeppackages"))
-           cinfo->keeppackages = *vp == '0' ? 0 : 1;
-       }
-      fclose(fp);
-      cinfo = 0;
-    }
-  closedir(dir);
-  qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
-  *nrepoinfosp = nrepoinfos;
-  return repoinfos;
-}
-
-#endif
-
-#ifdef DEBIAN
-
-struct repoinfo *
-read_repoinfos(Pool *pool, int *nrepoinfosp)
-{
-  FILE *fp;
-  char buf[4096];
-  char buf2[4096];
-  int l;
-  char *kp, *url, *distro;
-  struct repoinfo *repoinfos = 0, *cinfo;
-  int nrepoinfos = 0;
-  DIR *dir = 0;
-  struct dirent *ent;
-
-  fp = fopen("/etc/apt/sources.list", "r");
-  while (1)
-    {
-      if (!fp)
-       {
-         if (!dir)
-           {
-             dir = opendir("/etc/apt/sources.list.d");
-             if (!dir)
-               break;
-           }
-         if ((ent = readdir(dir)) == 0)
-           {
-             closedir(dir);
-             break;
-           }
-         if (ent->d_name[0] == '.')
-           continue;
-         l = strlen(ent->d_name);
-         if (l < 5 || strcmp(ent->d_name + l - 5, ".list") != 0)
-           continue;
-         snprintf(buf, sizeof(buf), "%s/%s", "/etc/apt/sources.list.d", ent->d_name);
-         if (!(fp = fopen(buf, "r")))
-           continue;
-       }
-      while(fgets(buf2, sizeof(buf2), fp))
-       {
-         l = strlen(buf2);
-         if (l == 0)
-           continue;
-         while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
-           buf2[--l] = 0;
-         kp = buf2;
-         while (*kp == ' ' || *kp == '\t')
-           kp++;
-         if (!*kp || *kp == '#')
-           continue;
-         if (strncmp(kp, "deb", 3) != 0)
-           continue;
-         kp += 3;
-         if (*kp != ' ' && *kp != '\t')
-           continue;
-         while (*kp == ' ' || *kp == '\t')
-           kp++;
-         if (!*kp)
-           continue;
-         url = kp;
-         while (*kp && *kp != ' ' && *kp != '\t')
-           kp++;
-         if (*kp)
-           *kp++ = 0;
-         while (*kp == ' ' || *kp == '\t')
-           kp++;
-         if (!*kp)
-           continue;
-         distro = kp;
-         while (*kp && *kp != ' ' && *kp != '\t')
-           kp++;
-         if (*kp)
-           *kp++ = 0;
-         while (*kp == ' ' || *kp == '\t')
-           kp++;
-         if (!*kp)
-           continue;
-         repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
-         cinfo = repoinfos + nrepoinfos++;
-         memset(cinfo, 0, sizeof(*cinfo));
-         cinfo->baseurl = strdup(url);
-         cinfo->alias = solv_dupjoin(url, "/", distro);
-         cinfo->name = strdup(distro);
-         cinfo->type = TYPE_DEBIAN;
-         cinfo->enabled = 1;
-         cinfo->autorefresh = 1;
-         cinfo->repo_gpgcheck = 1;
-         cinfo->metadata_expire = METADATA_EXPIRE;
-         while (*kp)
-           {
-             char *compo;
-             while (*kp == ' ' || *kp == '\t')
-               kp++;
-             if (!*kp)
-               break;
-             compo = kp;
-             while (*kp && *kp != ' ' && *kp != '\t')
-               kp++;
-             if (*kp)
-               *kp++ = 0;
-             cinfo->components = solv_extend(cinfo->components, cinfo->ncomponents, 1, sizeof(*cinfo->components), 15);
-             cinfo->components[cinfo->ncomponents++] = strdup(compo);
-           }
-       }
-      fclose(fp);
-      fp = 0;
-    }
-  qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), read_repoinfos_sort);
-  *nrepoinfosp = nrepoinfos;
-  return repoinfos;
-}
-
-#endif
-
-
-void
-free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
-{
-  int i, j;
-  for (i = 0; i < nrepoinfos; i++)
-    {
-      struct repoinfo *cinfo = repoinfos + i;
-      solv_free(cinfo->name);
-      solv_free(cinfo->alias);
-      solv_free(cinfo->path);
-      solv_free(cinfo->metalink);
-      solv_free(cinfo->mirrorlist);
-      solv_free(cinfo->baseurl);
-      for (j = 0; j < cinfo->ncomponents; j++)
-        solv_free(cinfo->components[j]);
-      solv_free(cinfo->components);
-    }
-  solv_free(repoinfos);
-}
-
-static inline int
-opentmpfile()
-{
-  char tmpl[100];
-  int fd;
-
-  strcpy(tmpl, "/var/tmp/solvXXXXXX");
-  fd = mkstemp(tmpl);
-  if (fd < 0)
-    {
-      perror("mkstemp");
-      exit(1);
-    }
-  unlink(tmpl);
-  return fd;
-}
-
-static int
-verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
-{
-  char buf[1024];
-  const unsigned char *sum;
-  Chksum *h;
-  int l;
-
-  h = solv_chksum_create(chksumtype);
-  if (!h)
-    {
-      printf("%s: unknown checksum type\n", file);
-      return 0;
-    }
-  while ((l = read(fd, buf, sizeof(buf))) > 0)
-    solv_chksum_add(h, buf, l);
-  lseek(fd, 0, SEEK_SET);
-  l = 0;
-  sum = solv_chksum_get(h, &l);
-  if (memcmp(sum, chksum, l))
-    {
-      printf("%s: checksum mismatch\n", file);
-      solv_chksum_free(h, 0);
-      return 0;
-    }
-  solv_chksum_free(h, 0);
-  return 1;
-}
-
-void
-findfastest(char **urls, int nurls)
-{
-  int i, j, port;
-  int *socks, qc;
-  struct pollfd *fds;
-  char *p, *p2, *q;
-  char portstr[16];
-  struct addrinfo hints, *result;;
-
-  fds = solv_calloc(nurls, sizeof(*fds));
-  socks = solv_calloc(nurls, sizeof(*socks));
-  for (i = 0; i < nurls; i++)
-    {
-      socks[i] = -1;
-      p = strchr(urls[i], '/');
-      if (!p)
-       continue;
-      if (p[1] != '/')
-       continue;
-      p += 2;
-      q = strchr(p, '/');
-      qc = 0;
-      if (q)
-       {
-         qc = *q;
-         *q = 0;
-       }
-      if ((p2 = strchr(p, '@')) != 0)
-       p = p2 + 1;
-      port = 80;
-      if (!strncmp("https:", urls[i], 6))
-       port = 443;
-      else if (!strncmp("ftp:", urls[i], 4))
-       port = 21;
-      if ((p2 = strrchr(p, ':')) != 0)
-       {
-         port = atoi(p2 + 1);
-         if (q)
-           *q = qc;
-         q = p2;
-         qc = *q;
-         *q = 0;
-       }
-      sprintf(portstr, "%d", port);
-      memset(&hints, 0, sizeof(struct addrinfo));
-      hints.ai_family = AF_UNSPEC;
-      hints.ai_socktype = SOCK_STREAM;
-      hints.ai_flags = AI_NUMERICSERV;
-      result = 0;
-      if (!getaddrinfo(p, portstr, &hints, &result))
-       {
-         socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
-         if (socks[i] >= 0)
-           {
-             fcntl(socks[i], F_SETFL, O_NONBLOCK);
-             if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
-               {
-                 if (errno != EINPROGRESS)
-                   {
-                     close(socks[i]);
-                     socks[i] = -1;
-                   }
-               }
-           }
-         freeaddrinfo(result);
-       }
-      if (q)
-       *q = qc;
-    }
-  for (;;)
-    {
-      for (i = j = 0; i < nurls; i++)
-       {
-         if (socks[i] < 0)
-           continue;
-         fds[j].fd = socks[i];
-         fds[j].events = POLLOUT;
-         j++;
-       }
-      if (j < 2)
-       {
-         i = j - 1;
-         break;
-       }
-      if (poll(fds, j, 10000) <= 0)
-       {
-         i = -1;       /* something is wrong */
-         break;
-       }
-      for (i = 0; i < j; i++)
-       if ((fds[i].revents & POLLOUT) != 0)
-         {
-           int soe = 0;
-           socklen_t soel = sizeof(int);
-           if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
-             {
-               /* connect failed, kill socket */
-               for (j = 0; j < nurls; j++)
-                 if (socks[j] == fds[i].fd)
-                   {
-                     close(socks[j]);
-                     socks[j] = -1;
-                   }
-               i = j + 1;
-               break;
-             }
-           break;      /* horray! */
-         }
-      if (i == j + 1)
-       continue;
-      if (i == j)
-        i = -1;                /* something is wrong, no bit was set */
-      break;
-    }
-  /* now i contains the fastest fd index */
-  if (i >= 0)
-    {
-      for (j = 0; j < nurls; j++)
-       if (socks[j] == fds[i].fd)
-         break;
-      if (j != 0)
-       {
-         char *url0 = urls[0];
-         urls[0] = urls[j];
-         urls[j] = url0;
-       }
-    }
-  for (i = j = 0; i < nurls; i++)
-    if (socks[i] >= 0)
-      close(socks[i]);
-  free(socks);
-  free(fds);
-}
-
-char *
-findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
-{
-  char buf[4096], *bp, *ep;
-  char **urls = 0;
-  int nurls = 0;
-  int i;
-
-  if (chksumtypep)
-    *chksumtypep = 0;
-  while((bp = fgets(buf, sizeof(buf), fp)) != 0)
-    {
-      while (*bp == ' ' || *bp == '\t')
-       bp++;
-      if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
-       {
-         bp += 20;
-         if (solv_hex2bin((const char **)&bp, chksump, 32) == 32)
-           *chksumtypep = REPOKEY_TYPE_SHA256;
-         continue;
-       }
-      if (strncmp(bp, "<url", 4))
-       continue;
-      bp = strchr(bp, '>');
-      if (!bp)
-       continue;
-      bp++;
-      ep = strstr(bp, "repodata/repomd.xml</url>");
-      if (!ep)
-       continue;
-      *ep = 0;
-      if (strncmp(bp, "http", 4))
-       continue;
-      urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
-      urls[nurls++] = strdup(bp);
-    }
-  if (nurls)
-    {
-      if (nurls > 1)
-        findfastest(urls, nurls > 5 ? 5 : nurls);
-      bp = urls[0];
-      urls[0] = 0;
-      for (i = 0; i < nurls; i++)
-        solv_free(urls[i]);
-      solv_free(urls);
-      ep = strchr(bp, '/');
-      if ((ep = strchr(ep + 2, '/')) != 0)
-       {
-         *ep = 0;
-         printf("[using mirror %s]\n", bp);
-         *ep = '/';
-       }
-      return bp;
-    }
-  return 0;
-}
-
-char *
-findmirrorlisturl(FILE *fp)
-{
-  char buf[4096], *bp, *ep;
-  int i, l;
-  char **urls = 0;
-  int nurls = 0;
-
-  while((bp = fgets(buf, sizeof(buf), fp)) != 0)
-    {
-      while (*bp == ' ' || *bp == '\t')
-       bp++;
-      if (!*bp || *bp == '#')
-       continue;
-      l = strlen(bp);
-      while (l > 0 && (bp[l - 1] == ' ' || bp[l - 1] == '\t' || bp[l - 1] == '\n'))
-       bp[--l] = 0;
-      urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
-      urls[nurls++] = strdup(bp);
-    }
-  if (nurls)
-    {
-      if (nurls > 1)
-        findfastest(urls, nurls > 5 ? 5 : nurls);
-      bp = urls[0];
-      urls[0] = 0;
-      for (i = 0; i < nurls; i++)
-        solv_free(urls[i]);
-      solv_free(urls);
-      ep = strchr(bp, '/');
-      if ((ep = strchr(ep + 2, '/')) != 0)
-       {
-         *ep = 0;
-         printf("[using mirror %s]\n", bp);
-         *ep = '/';
-       }
-      return bp;
-    }
-  return 0;
-}
-
-static inline int
-iscompressed(const char *name)
-{
-  return solv_xfopen_iscompressed(name) != 0;
-}
-
-FILE *
-curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete)
-{
-  FILE *fp;
-  pid_t pid;
-  int fd, l;
-  int status;
-  char url[4096];
-  const char *baseurl = cinfo->baseurl;
-
-  if (!baseurl)
-    {
-      if (!cinfo->metalink && !cinfo->mirrorlist)
-        return 0;
-      if (file != cinfo->metalink && file != cinfo->mirrorlist)
-       {
-         unsigned char mlchksum[32];
-         Id mlchksumtype;
-         fp = curlfopen(cinfo, cinfo->metalink ? cinfo->metalink : cinfo->mirrorlist, 0, 0, 0, 0);
-         mlchksumtype = 0;
-         if (!fp)
-           return 0;
-         if (cinfo->metalink)
-           cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
-         else
-           cinfo->baseurl = findmirrorlisturl(fp);
-         fclose(fp);
-         if (!cinfo->baseurl)
-           return 0;
-#ifdef FEDORA
-         if (strchr(cinfo->baseurl, '$'))
-           {
-             char *b = yum_substitute(cinfo->repo->pool, cinfo->baseurl);
-             free(cinfo->baseurl);
-             cinfo->baseurl = strdup(b);
-           }
-#endif
-         if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
-           {
-             chksumtype = mlchksumtype;
-             chksum = mlchksum;
-           }
-         return curlfopen(cinfo, file, uncompress, chksum, chksumtype, markincomplete);
-       }
-      snprintf(url, sizeof(url), "%s", file);
-    }
-  else
-    {
-      l = strlen(baseurl);
-      if (l && baseurl[l - 1] == '/')
-       snprintf(url, sizeof(url), "%s%s", baseurl, file);
-      else
-       snprintf(url, sizeof(url), "%s/%s", baseurl, file);
-    }
-  fd = opentmpfile();
-  // printf("url: %s\n", url);
-  if ((pid = fork()) == (pid_t)-1)
-    {
-      perror("fork");
-      exit(1);
-    }
-  if (pid == 0)
-    {
-      if (fd != 1)
-       {
-          dup2(fd, 1);
-         close(fd);
-       }
-      execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
-      perror("curl");
-      _exit(0);
-    }
-  status = 0;
-  while (waitpid(pid, &status, 0) != pid)
-    ;
-  if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
-    {
-      /* empty file */
-      close(fd);
-      return 0;
-    }
-  lseek(fd, 0, SEEK_SET);
-  if (status)
-    {
-      printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
-      if (markincomplete)
-       cinfo->incomplete = 1;
-      close(fd);
-      return 0;
-    }
-  if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
-    {
-      if (markincomplete)
-       cinfo->incomplete = 1;
-      close(fd);
-      return 0;
-    }
-  fcntl(fd, F_SETFD, FD_CLOEXEC);
-  if (uncompress)
-    {
-      if (solv_xfopen_iscompressed(file) < 0)
-       {
-         printf("%s: unsupported compression\n", file);
-         if (markincomplete)
-           cinfo->incomplete = 1;
-         close(fd);
-         return 0;
-       }
-      fp = solv_xfopen_fd(file, fd, "r");
-    }
-  else
-    fp = fdopen(fd, "r");
-  if (!fp)
-    close(fd);
-  return fp;
-}
-
-#ifndef DEBIAN
-
-static void
-cleanupgpg(char *gpgdir)
-{
-  char cmd[256];
-  snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
-  unlink(cmd);
-  snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
-  unlink(cmd);
-  snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
-  unlink(cmd);
-  snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
-  unlink(cmd);
-  snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
-  unlink(cmd);
-  rmdir(gpgdir);
-}
-
-int
-checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
-{
-  char *gpgdir;
-  char *keysfile;
-  const char *pubkey;
-  char cmd[256];
-  FILE *kfp;
-  Solvable *s;
-  Id p;
-  off_t posfp, possigfp;
-  int r, nkeys;
-
-  gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
-  if (!gpgdir)
-    return 0;
-  keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
-  if (!(kfp = fopen(keysfile, "w")) )
-    {
-      cleanupgpg(gpgdir);
-      return 0;
-    }
-  nkeys = 0;
-  for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
-    {
-      if (!s->repo)
-       continue;
-      pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
-      if (!pubkey || !*pubkey)
-       continue;
-      if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
-       break;
-      if (fputc('\n', kfp) == EOF)     /* Just in case... */
-       break;
-      nkeys++;
-    }
-  if (fclose(kfp) || !nkeys)
-    {
-      cleanupgpg(gpgdir);
-      return 0;
-    }
-  snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
-  if (system(cmd))
-    {
-      fprintf(stderr, "key import error\n");
-      cleanupgpg(gpgdir);
-      return 0;
-    }
-  unlink(keysfile);
-  posfp = lseek(fileno(fp), 0, SEEK_CUR);
-  lseek(fileno(fp), 0, SEEK_SET);
-  possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
-  lseek(fileno(sigfp), 0, SEEK_SET);
-  snprintf(cmd, sizeof(cmd), "gpgv -q --homedir %s --keyring %s/pubring.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, gpgdir, fileno(sigfp), fileno(fp));
-  fcntl(fileno(fp), F_SETFD, 0);       /* clear CLOEXEC */
-  fcntl(fileno(sigfp), F_SETFD, 0);    /* clear CLOEXEC */
-  r = system(cmd);
-  lseek(fileno(sigfp), possigfp, SEEK_SET);
-  lseek(fileno(fp), posfp, SEEK_SET);
-  fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
-  fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
-  cleanupgpg(gpgdir);
-  return r == 0 ? 1 : 0;
-}
-
-#else
-
-static int
-checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
-{
-  char cmd[256];
-  int r;
-
-  snprintf(cmd, sizeof(cmd), "gpgv -q --keyring /etc/apt/trusted.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", fileno(sigfp), fileno(fp));
-  fcntl(fileno(fp), F_SETFD, 0);       /* clear CLOEXEC */
-  fcntl(fileno(sigfp), F_SETFD, 0);    /* clear CLOEXEC */
-  r = system(cmd);
-  fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
-  fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
-  return r == 0 ? 1 : 0;
-}
-
-#endif
-
-static Pool *
-read_sigs()
-{
-  Pool *sigpool = pool_create();
-#if defined(ENABLE_PUBKEY) && defined(ENABLE_RPMDB)
-  Repo *repo = repo_create(sigpool, "pubkeys");
-  repo_add_rpmdb_pubkeys(repo, 0);
-#endif
-  return sigpool;
-}
-
-static int
-downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool)
-{
-  FILE *sigfp;
-  sigfp = curlfopen(cinfo, sigurl, 0, 0, 0, 0);
-  if (!sigfp)
-    {
-      printf(" unsigned, skipped\n");
-      return 0;
-    }
-  if (!*sigpool)
-    *sigpool = read_sigs();
-  if (!checksig(*sigpool, fp, sigfp))
-    {
-      printf(" checksig failed, skipped\n");
-      fclose(sigfp);
-      return 0;
-    }
-  fclose(sigfp);
-  return 1;
-}
-
-#define CHKSUM_IDENT "1.1"
-
-void
-calc_checksum_fp(FILE *fp, Id chktype, unsigned char *out)
-{
-  char buf[4096];
-  Chksum *h = solv_chksum_create(chktype);
-  int l;
-
-  solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
-  while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
-    solv_chksum_add(h, buf, l);
-  rewind(fp);
-  solv_chksum_free(h, out);
-}
-
-void
-calc_checksum_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out)
-{
-  Chksum *h = solv_chksum_create(chktype);
-  solv_chksum_add(h, CHKSUM_IDENT, strlen(CHKSUM_IDENT));
-  if (cookie)
-    solv_chksum_add(h, cookie, 32);
-  solv_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
-  solv_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
-  solv_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
-  solv_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
-  solv_chksum_free(h, out);
-}
-
-void
-setarch(Pool *pool)
-{
-  struct utsname un;
-  if (uname(&un))
-    {
-      perror("uname");
-      exit(1);
-    }
-  pool_setarch(pool, un.machine);
-}
-
-char *userhome;
-
-char *
-calccachepath(Repo *repo, const char *repoext, int forcesystemloc)
-{
-  char *q, *p;
-  int l;
-  if (!forcesystemloc && userhome && getuid())
-    p = pool_tmpjoin(repo->pool, userhome, "/.solvcache/", 0);
-  else
-    p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", 0);
-  l = strlen(p);
-  p = pool_tmpappend(repo->pool, p, repo->name, 0);
-  if (repoext)
-    {
-      p = pool_tmpappend(repo->pool, p, "_", repoext);
-      p = pool_tmpappend(repo->pool, p, ".solvx", 0);
-    }
-  else
-    p = pool_tmpappend(repo->pool, p, ".solv", 0);
-  q = p + l;
-  if (*q == '.')
-    *q = '_';
-  for (; *q; q++)
-    if (*q == '/')
-      *q = '_';
-  return p;
-}
-
-int
-usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie, int mark)
-{
-  FILE *fp;
-  unsigned char mycookie[32];
-  unsigned char myextcookie[32];
-  struct repoinfo *cinfo;
-  int flags;
-  int forcesystemloc;
-
-  forcesystemloc = mark & 2 ? 0 : 1;
-  if (mark < 2 && userhome && getuid())
-    {
-      /* first try home location */
-      int res = usecachedrepo(repo, repoext, cookie, mark | 2);
-      if (res)
-       return res;
-    }
-  mark &= 1;
-  cinfo = repo->appdata;
-  if (!(fp = fopen(calccachepath(repo, repoext, forcesystemloc), "r")))
-    return 0;
-  if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
-    {
-      fclose(fp);
-      return 0;
-    }
-  if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)) != 0)
-    {
-      fclose(fp);
-      return 0;
-    }
-  if (cinfo && !repoext)
-    {
-      if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
-       {
-         fclose(fp);
-         return 0;
-       }
-    }
-  rewind(fp);
-
-  flags = 0;
-  if (repoext)
-    {
-      flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
-      if (strcmp(repoext, "DL") != 0)
-        flags |= REPO_LOCALPOOL;       /* no local pool for DL so that we can compare IDs */
-    }
-
-  if (repo_add_solv(repo, fp, flags))
-    {
-      fclose(fp);
-      return 0;
-    }
-  if (cinfo && !repoext)
-    {
-      memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
-      memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
-      cinfo->extcookieset = 1;
-    }
-  if (mark)
-    futimens(fileno(fp), 0);   /* try to set modification time */
-  fclose(fp);
-  return 1;
-}
-
-void
-writecachedrepo(Repo *repo, Repodata *repodata, const char *repoext, unsigned char *cookie)
-{
-  FILE *fp;
-  int i, fd;
-  char *tmpl, *cachedir;
-  struct repoinfo *cinfo;
-  int onepiece;
-
-  cinfo = repo->appdata;
-  if (cinfo && cinfo->incomplete)
-    return;
-  cachedir = userhome && getuid() ? pool_tmpjoin(repo->pool, userhome, "/.solvcache", 0) : SOLVCACHE_PATH;
-  if (access(cachedir, W_OK | X_OK) != 0 && mkdir(cachedir, 0755) == 0)
-    printf("[created %s]\n", cachedir);
-  /* use dupjoin instead of tmpjoin because tmpl must survive repo_write */
-  tmpl = solv_dupjoin(cachedir, "/", ".newsolv-XXXXXX");
-  fd = mkstemp(tmpl);
-  if (fd < 0)
-    {
-      free(tmpl);
-      return;
-    }
-  fchmod(fd, 0444);
-  if (!(fp = fdopen(fd, "w")))
-    {
-      close(fd);
-      unlink(tmpl);
-      free(tmpl);
-      return;
-    }
-
-  onepiece = 1;
-  for (i = repo->start; i < repo->end; i++)
-   if (repo->pool->solvables[i].repo != repo)
-     break;
-  if (i < repo->end)
-    onepiece = 0;
-
-  if (!repodata)
-    repo_write(repo, fp);
-  else if (repoext)
-    repodata_write(repodata, fp);
-  else
-    {
-      int oldnrepodata = repo->nrepodata;
-      repo->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata;   /* XXX: do this right */
-      repo_write(repo, fp);
-      repo->nrepodata = oldnrepodata;
-      onepiece = 0;    /* don't bother for the added file provides */
-    }
-
-  if (!repoext && cinfo)
-    {
-      if (!cinfo->extcookieset)
-       {
-         /* create the ext cookie and append it */
-         /* we just need some unique ID */
-         struct stat stb;
-         if (!fstat(fileno(fp), &stb))
-           memset(&stb, 0, sizeof(stb));
-         calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cookie, cinfo->extcookie);
-         cinfo->extcookieset = 1;
-       }
-      if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
-       {
-         fclose(fp);
-         unlink(tmpl);
-         free(tmpl);
-         return;
-       }
-    }
-  /* append our cookie describing the metadata state */
-  if (fwrite(cookie, 32, 1, fp) != 1)
-    {
-      fclose(fp);
-      unlink(tmpl);
-      free(tmpl);
-      return;
-    }
-  if (fclose(fp))
-    {
-      unlink(tmpl);
-      free(tmpl);
-      return;
-    }
-  if (onepiece)
-    {
-      /* switch to just saved repo to activate paging and save memory */
-      FILE *fp = fopen(tmpl, "r");
-      if (fp)
-       {
-         if (!repoext)
-           {
-             /* main repo */
-             repo_empty(repo, 1);
-             if (repo_add_solv(repo, fp, SOLV_ADD_NO_STUBS))
-               {
-                 /* oops, no way to recover from here */
-                 fprintf(stderr, "internal error\n");
-                 exit(1);
-               }
-           }
-         else
-           {
-             int flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
-             /* make sure repodata contains complete repo */
-             /* (this is how repodata_write saves it) */
-             repodata_extend_block(repodata, repo->start, repo->end - repo->start);
-             repodata->state = REPODATA_LOADING;
-             if (strcmp(repoext, "DL") != 0)
-               flags |= REPO_LOCALPOOL;
-             repo_add_solv(repo, fp, flags);
-             repodata->state = REPODATA_AVAILABLE;     /* in case the load failed */
-           }
-         fclose(fp);
-       }
-    }
-  if (!rename(tmpl, calccachepath(repo, repoext, 0)))
-    unlink(tmpl);
-  free(tmpl);
-}
-
-
-#ifdef ENABLE_RPMMD
-/* repomd helpers */
-
-static inline const char *
-repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
-{
-  Pool *pool = repo->pool;
-  Dataiterator di;
-  const char *filename;
-
-  filename = 0;
-  *chksump = 0;
-  *chksumtypep = 0;
-  dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
-  dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
-  if (dataiterator_step(&di))
-    {
-      dataiterator_setpos_parent(&di);
-      filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
-      *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
-    }
-  dataiterator_free(&di);
-  if (filename && !*chksumtypep)
-    {
-      printf("no %s file checksum!\n", what);
-      filename = 0;
-    }
-  return filename;
-}
-
-int
-repomd_add_ext(Repo *repo, Repodata *data, const char *what)
-{
-  Id chksumtype, handle;
-  const unsigned char *chksum;
-  const char *filename;
-
-  filename = repomd_find(repo, what, &chksum, &chksumtype);
-  if (!filename)
-    return 0;
-  if (!strcmp(what, "prestodelta"))
-    what = "deltainfo";
-  handle = repodata_new_handle(data);
-  repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
-  repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
-  repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
-  if (!strcmp(what, "deltainfo"))
-    {
-      repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
-      repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
-    }
-  if (!strcmp(what, "filelists"))
-    {
-      repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
-      repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
-    }
-  repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
-  return 1;
-}
-
-int
-repomd_load_ext(Repo *repo, Repodata *data)
-{
-  const char *filename, *repomdtype;
-  char ext[3];
-  FILE *fp;
-  struct repoinfo *cinfo;
-  const unsigned char *filechksum;
-  Id filechksumtype;
-  int r = 0;
-
-  cinfo = repo->appdata;
-  repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
-  if (!repomdtype)
-    return 0;
-  if (!strcmp(repomdtype, "filelists"))
-    strcpy(ext, "FL");
-  else if (!strcmp(repomdtype, "deltainfo"))
-    strcpy(ext, "DL");
-  else
-    return 0;
-  printf("[%s:%s", repo->name, ext);
-  if (usecachedrepo(repo, ext, cinfo->extcookie, 0))
-    {
-      printf(" cached]\n"); fflush(stdout);
-      return 1;
-    }
-  printf(" fetching]\n"); fflush(stdout);
-  filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
-  filechksumtype = 0;
-  filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
-  if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
-    return 0;
-  if (!strcmp(ext, "FL"))
-    r = repo_add_rpmmd(repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
-  else if (!strcmp(ext, "DL"))
-    r = repo_add_deltainfoxml(repo, fp, REPO_USE_LOADING);
-  fclose(fp);
-  if (r)
-    {
-      printf("%s\n", pool_errstr(repo->pool));
-      return 0;
-    }
-  if (cinfo->extcookieset)
-    writecachedrepo(repo, data, ext, cinfo->extcookie);
-  return 1;
-}
-
-#endif
-
-
-#ifdef ENABLE_SUSEREPO
-/* susetags helpers */
-
-static inline const char *
-susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
-{
-  Pool *pool = repo->pool;
-  Dataiterator di;
-  const char *filename;
-
-  filename = 0;
-  *chksump = 0;
-  *chksumtypep = 0;
-  dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
-  dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
-  if (dataiterator_step(&di))
-    {
-      dataiterator_setpos_parent(&di);
-      *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
-      filename = what;
-    }
-  dataiterator_free(&di);
-  if (filename && !*chksumtypep)
-    {
-      printf("no %s file checksum!\n", what);
-      filename = 0;
-    }
-  return filename;
-}
-
-static Id susetags_langtags[] = {
-  SOLVABLE_SUMMARY, REPOKEY_TYPE_STR,
-  SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
-  SOLVABLE_EULA, REPOKEY_TYPE_STR,
-  SOLVABLE_MESSAGEINS, REPOKEY_TYPE_STR,
-  SOLVABLE_MESSAGEDEL, REPOKEY_TYPE_STR,
-  SOLVABLE_CATEGORY, REPOKEY_TYPE_ID,
-  0, 0
-};
-
-void
-susetags_add_ext(Repo *repo, Repodata *data)
-{
-  Pool *pool = repo->pool;
-  Dataiterator di;
-  char ext[3];
-  Id handle, filechksumtype;
-  const unsigned char *filechksum;
-  int i;
-
-  dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
-  dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
-  while (dataiterator_step(&di))
-    {
-      if (strncmp(di.kv.str, "packages.", 9) != 0)
-       continue;
-      if (!strcmp(di.kv.str + 9, "gz"))
-       continue;
-      if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
-       continue;
-      ext[0] = di.kv.str[9];
-      ext[1] = di.kv.str[10];
-      ext[2] = 0;
-      if (!strcmp(ext, "en"))
-       continue;
-      if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
-       continue;
-      handle = repodata_new_handle(data);
-      repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
-      if (filechksumtype)
-       repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
-      if (!strcmp(ext, "DU"))
-       {
-         repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
-         repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
-       }
-      else if (!strcmp(ext, "FL"))
-       {
-         repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
-         repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
-       }
-      else
-       {
-         for (i = 0; susetags_langtags[i]; i += 2)
-           {
-             repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, susetags_langtags[i], ext, 1));
-             repodata_add_idarray(data, handle, REPOSITORY_KEYS, susetags_langtags[i + 1]);
-           }
-       }
-      repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
-    }
-  dataiterator_free(&di);
-}
-
-int
-susetags_load_ext(Repo *repo, Repodata *data)
-{
-  const char *filename, *descrdir;
-  Id defvendor;
-  char ext[3];
-  FILE *fp;
-  struct repoinfo *cinfo;
-  const unsigned char *filechksum;
-  Id filechksumtype;
-  int flags;
-
-  cinfo = repo->appdata;
-  filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
-  if (!filename)
-    return 0;
-  /* susetags load */
-  ext[0] = filename[9];
-  ext[1] = filename[10];
-  ext[2] = 0;
-  printf("[%s:%s", repo->name, ext);
-  if (usecachedrepo(repo, ext, cinfo->extcookie, 0))
-    {
-      printf(" cached]\n"); fflush(stdout);
-      return 1;
-    }
-  printf(" fetching]\n"); fflush(stdout);
-  defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
-  descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
-  if (!descrdir)
-    descrdir = "suse/setup/descr";
-  filechksumtype = 0;
-  filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
-  if ((fp = curlfopen(cinfo, pool_tmpjoin(repo->pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 0)) == 0)
-    return 0;
-  flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
-  if (strcmp(ext, "DL") != 0)
-    flags |= REPO_LOCALPOOL;
-  if (repo_add_susetags(repo, fp, defvendor, ext, flags))
-    {
-      fclose(fp);
-      printf("%s\n", pool_errstr(repo->pool));
-      return 0;
-    }
-  fclose(fp);
-  if (cinfo->extcookieset)
-    writecachedrepo(repo, data, ext, cinfo->extcookie);
-  return 1;
-}
-#endif
-
-
-
-/* load callback */
-
-int
-load_stub(Pool *pool, Repodata *data, void *dp)
-{
-  struct repoinfo *cinfo = data->repo->appdata;
-  switch (cinfo->type)
-    {
-#ifdef ENABLE_SUSEREPO
-    case TYPE_SUSETAGS:
-      return susetags_load_ext(data->repo, data);
-#endif
-#ifdef ENABLE_RPMMD
-    case TYPE_RPMMD:
-      return repomd_load_ext(data->repo, data);
-#endif
-    default:
-      return 0;
-    }
-}
-
-static unsigned char installedcookie[32];
-
-#ifdef ENABLE_DEBIAN
-
-const char *
-debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
-{
-  char buf[4096];
-  Id chksumtype;
-  unsigned char *chksum;
-  Id curchksumtype;
-  int l, compl;
-  char *ch, *fn, *bp;
-  char *filename;
-  static char *basearch;
-  char *binarydir;
-  int lbinarydir;
-
-  if (!basearch)
-    {
-      struct utsname un;
-      if (uname(&un))
-       {
-         perror("uname");
-         exit(1);
-       }
-      basearch = strdup(un.machine);
-      if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
-       basearch[1] = '3';
-    }
-  binarydir = solv_dupjoin("binary-", basearch, "/");
-  lbinarydir = strlen(binarydir);
-  compl = strlen(comp);
-  rewind(fp);
-  curchksumtype = 0;
-  filename = 0;
-  chksum = solv_malloc(32);
-  chksumtype = 0;
-  while(fgets(buf, sizeof(buf), fp))
-    {
-      l = strlen(buf);
-      if (l == 0)
-       continue;
-      while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
-       buf[--l] = 0;
-      if (!strncasecmp(buf, "MD5Sum:", 7))
-       {
-         curchksumtype = REPOKEY_TYPE_MD5;
-         continue;
-       }
-      if (!strncasecmp(buf, "SHA1:", 5))
-       {
-         curchksumtype = REPOKEY_TYPE_SHA1;
-         continue;
-       }
-      if (!strncasecmp(buf, "SHA256:", 7))
-       {
-         curchksumtype = REPOKEY_TYPE_SHA256;
-         continue;
-       }
-      if (!curchksumtype)
-       continue;
-      bp = buf;
-      if (*bp++ != ' ')
-       {
-         curchksumtype = 0;
-         continue;
-       }
-      ch = bp;
-      while (*bp && *bp != ' ' && *bp != '\t')
-       bp++;
-      if (!*bp)
-       continue;
-      *bp++ = 0;
-      while (*bp == ' ' || *bp == '\t')
-       bp++;
-      while (*bp && *bp != ' ' && *bp != '\t')
-       bp++;
-      if (!*bp)
-       continue;
-      while (*bp == ' ' || *bp == '\t')
-       bp++;
-      fn = bp;
-      if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
-       continue;
-      bp += compl + 1;
-      if (strncmp(bp, binarydir, lbinarydir))
-       continue;
-      bp += lbinarydir;
-      if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
-       {
-         unsigned char curchksum[32];
-         int curl;
-         if (filename && !strcmp(bp, "Packages"))
-           continue;
-         curl = solv_chksum_len(curchksumtype);
-         if (!curl || (chksumtype && solv_chksum_len(chksumtype) > curl))
-           continue;
-          if (solv_hex2bin((const char **)&ch, curchksum, sizeof(curchksum)) != curl)
-           continue;
-         solv_free(filename);
-         filename = strdup(fn);
-         chksumtype = curchksumtype;
-         memcpy(chksum, curchksum, curl);
-       }
-    }
-  free(binarydir);
-  if (filename)
-    {
-      fn = solv_dupjoin("/", filename, 0);
-      solv_free(filename);
-      filename = solv_dupjoin("dists/", cinfo->name, fn);
-      solv_free(fn);
-    }
-  if (!chksumtype)
-    chksum = solv_free(chksum);
-  *chksump = chksum;
-  *chksumtypep = chksumtype;
-  return filename;
-}
-#endif
-
-void
-read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
-{
-  Repo *repo;
-  struct repoinfo *cinfo;
-  int i;
-  FILE *fp;
-  const char *filename;
-  const unsigned char *filechksum;
-  Id filechksumtype;
-#ifdef ENABLE_SUSEREPO
-  const char *descrdir;
-  int defvendor;
-#endif
-  struct stat stb;
-  Pool *sigpool = 0;
-#if defined(ENABLE_SUSEREPO) || defined(ENABLE_RPMMD)
-  Repodata *data;
-#endif
-  int dorefresh;
-#if defined(ENABLE_DEBIAN)
-  FILE *fpr;
-  int j;
-#endif
-
-  repo = repo_create(pool, "@System");
-  memset(&stb, 0, sizeof(stb));
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-  printf("rpm database:");
-  if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/rpm/Packages"), &stb))
-    memset(&stb, 0, sizeof(stb));
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-  printf("dpgk database:");
-  if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/dpkg/status"), &stb))
-    memset(&stb, 0, sizeof(stb));
-#endif
-  calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, 0, installedcookie);
-  if (usecachedrepo(repo, 0, installedcookie, 0))
-    printf(" cached\n");
-  else
-    {
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-      FILE *ofp = 0;
-#endif
-      printf(" reading\n");
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-# if defined(ENABLE_SUSEREPO) && defined(PRODUCTS_PATH)
-      if (repo_add_products(repo, PRODUCTS_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
-       {
-         fprintf(stderr, "product reading failed: %s\n", pool_errstr(pool));
-         exit(1);
-       }
-# endif
-# if defined(ENABLE_APPDATA)
-      if (repo_add_appdata_dir(repo, APPDATA_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
-       {
-         fprintf(stderr, "appdata reading failed: %s\n", pool_errstr(pool));
-         exit(1);
-       }
-# endif
-      ofp = fopen(calccachepath(repo, 0, 0), "r");
-      if (repo_add_rpmdb_reffp(repo, ofp, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
-       {
-         fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
-         exit(1);
-       }
-      if (ofp)
-        fclose(ofp);
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-      if (repo_add_debdb(repo, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
-       {
-         fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
-         exit(1);
-       }
-#endif
-      repo_internalize(repo);
-      writecachedrepo(repo, 0, 0, installedcookie);
-    }
-  pool_set_installed(pool, repo);
-
-  for (i = 0; i < nrepoinfos; i++)
-    {
-      cinfo = repoinfos + i;
-      if (!cinfo->enabled)
-       continue;
-
-      repo = repo_create(pool, cinfo->alias);
-      cinfo->repo = repo;
-      repo->appdata = cinfo;
-      repo->priority = 99 - cinfo->priority;
-
-      dorefresh = cinfo->autorefresh;
-      if (dorefresh && cinfo->metadata_expire && stat(calccachepath(repo, 0, 0), &stb) == 0)
-       {
-         if (cinfo->metadata_expire == -1 || time(0) - stb.st_mtime < cinfo->metadata_expire)
-           dorefresh = 0;
-       }
-      if (!dorefresh && usecachedrepo(repo, 0, 0, 0))
-       {
-         printf("repo '%s':", cinfo->alias);
-         printf(" cached\n");
-         continue;
-       }
-      switch (cinfo->type)
-       {
-#ifdef ENABLE_RPMMD
-        case TYPE_RPMMD:
-         printf("rpmmd repo '%s':", cinfo->alias);
-         fflush(stdout);
-         if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
-           {
-             printf(" no repomd.xml file, skipped\n");
-             repo_free(repo, 1);
-             cinfo->repo = 0;
-             break;
-           }
-         calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
-         if (usecachedrepo(repo, 0, cinfo->cookie, 1))
-           {
-             printf(" cached\n");
-              fclose(fp);
-             break;
-           }
-         if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "repodata/repomd.xml.asc", &sigpool))
-           {
-             fclose(fp);
-             break;
-           }
-         if (repo_add_repomdxml(repo, fp, 0))
-           {
-             printf("repomd.xml: %s\n", pool_errstr(pool));
-             fclose(fp);
-             break;    /* hopeless */
-           }
-         fclose(fp);
-         printf(" fetching\n");
-         filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
-         if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
-           {
-             if (repo_add_rpmmd(repo, fp, 0, 0))
-               {
-                 printf("primary: %s\n", pool_errstr(pool));
-                 cinfo->incomplete = 1;
-               }
-             fclose(fp);
-           }
-         if (cinfo->incomplete)
-           break;      /* hopeless */
-
-         filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
-         if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
-           {
-             if (repo_add_updateinfoxml(repo, fp, 0))
-               {
-                 printf("updateinfo: %s\n", pool_errstr(pool));
-                 cinfo->incomplete = 1;
-               }
-             fclose(fp);
-           }
-
-#ifdef ENABLE_APPDATA
-         filename = repomd_find(repo, "appdata", &filechksum, &filechksumtype);
-         if (filename && (fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
-           {
-             if (repo_add_appdata(repo, fp, 0))
-               {
-                 printf("appdata: %s\n", pool_errstr(pool));
-                 cinfo->incomplete = 1;
-               }
-             fclose(fp);
-           }
-#endif
-         data = repo_add_repodata(repo, 0);
-         if (!repomd_add_ext(repo, data, "deltainfo"))
-           repomd_add_ext(repo, data, "prestodelta");
-         repomd_add_ext(repo, data, "filelists");
-         repodata_internalize(data);
-         if (!cinfo->incomplete)
-           writecachedrepo(repo, 0, 0, cinfo->cookie);
-         repodata_create_stubs(repo_last_repodata(repo));
-         break;
-#endif
-
-#ifdef ENABLE_SUSEREPO
-        case TYPE_SUSETAGS:
-         printf("susetags repo '%s':", cinfo->alias);
-         fflush(stdout);
-         descrdir = 0;
-         defvendor = 0;
-         if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
-           {
-             printf(" no content file, skipped\n");
-             repo_free(repo, 1);
-             cinfo->repo = 0;
-             break;
-           }
-         calc_checksum_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
-         if (usecachedrepo(repo, 0, cinfo->cookie, 1))
-           {
-             printf(" cached\n");
-             fclose(fp);
-             break;
-           }
-         if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "content.asc", &sigpool))
-           {
-             fclose(fp);
-             break;
-           }
-         if (repo_add_content(repo, fp, 0))
-           {
-             printf("content: %s\n", pool_errstr(pool));
-             fclose(fp);
-             break;    /* hopeless */
-           }
-         fclose(fp);
-         defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
-         descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
-         if (!descrdir)
-           descrdir = "suse/setup/descr";
-         filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
-          if (!filename)
-           filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
-         if (!filename)
-           {
-             printf(" no packages file entry, skipped\n");
-             break;
-           }
-         printf(" fetching\n");
-         if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) == 0)
-           break;      /* hopeless */
-         if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|SUSETAGS_RECORD_SHARES))
-           {
-             printf("packages: %s\n", pool_errstr(pool));
-             fclose(fp);
-             cinfo->incomplete = 1;
-             break;    /* hopeless */
-           }
-         fclose(fp);
-         /* add default language */
-         filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
-          if (!filename)
-           filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
-         if (filename)
-           {
-             if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
-               {
-                 if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
-                   {
-                     printf("packages.en: %s\n", pool_errstr(pool));
-                     cinfo->incomplete = 1;
-                   }
-                 fclose(fp);
-               }
-           }
-         filename = susetags_find(repo, "patterns", &filechksum, &filechksumtype);
-         if (filename)
-           {
-             if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
-               {
-                 char pbuf[256];
-                 while (fgets(pbuf, sizeof(pbuf), fp))
-                   {
-                     int l = strlen(pbuf);
-                     FILE *fp2;
-                     if (l && pbuf[l - 1] == '\n')
-                       pbuf[--l] = 0;
-                     if (!*pbuf || *pbuf == '.' || strchr(pbuf, '/') != 0)
-                       continue;
-                     filename = susetags_find(repo, pbuf, &filechksum, &filechksumtype);
-                     if (filename && (fp2 = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
-                       {
-                         if (repo_add_susetags(repo, fp2, defvendor, 0, REPO_NO_INTERNALIZE))
-                           {
-                             printf("%s: %s\n", pbuf, pool_errstr(pool));
-                             cinfo->incomplete = 1;
-                           }
-                         fclose(fp2);
-                       }
-                   }
-                 fclose(fp);
-               }
-           }
-#ifdef ENABLE_APPDATA
-         filename = susetags_find(repo, "appdata.xml.gz", &filechksum, &filechksumtype);
-          if (!filename)
-           filename = susetags_find(repo, "appdata.xml", &filechksum, &filechksumtype);
-         if (filename && (fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
-           {
-             if (repo_add_appdata(repo, fp, 0))
-               {
-                 printf("appdata: %s\n", pool_errstr(pool));
-                 cinfo->incomplete = 1;
-               }
-             fclose(fp);
-           }
-#endif
-          repo_internalize(repo);
-         data = repo_add_repodata(repo, 0);
-         susetags_add_ext(repo, data);
-         repodata_internalize(data);
-         if (!cinfo->incomplete)
-           writecachedrepo(repo, 0, 0, cinfo->cookie);
-         repodata_create_stubs(repo_last_repodata(repo));
-         break;
-#endif
-
-#if defined(ENABLE_DEBIAN)
-        case TYPE_DEBIAN:
-         printf("debian repo '%s':", cinfo->alias);
-         fflush(stdout);
-         filename = solv_dupjoin("dists/", cinfo->name, "/Release");
-         if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
-           {
-             printf(" no Release file, skipped\n");
-             repo_free(repo, 1);
-             cinfo->repo = 0;
-             free((char *)filename);
-             break;
-           }
-         solv_free((char *)filename);
-         if (cinfo->repo_gpgcheck)
-           {
-             filename = solv_dupjoin("dists/", cinfo->name, "/Release.gpg");
-             if (!downloadchecksig(cinfo, fpr, filename, &sigpool))
-               {
-                 fclose(fpr);
-                 solv_free((char *)filename);
-                 break;
-               }
-             solv_free((char *)filename);
-           }
-         calc_checksum_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
-         if (usecachedrepo(repo, 0, cinfo->cookie, 1))
-           {
-             printf(" cached\n");
-              fclose(fpr);
-             break;
-           }
-         printf(" fetching\n");
-          for (j = 0; j < cinfo->ncomponents; j++)
-           {
-             if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
-               {
-                 printf("[component %s not found]\n", cinfo->components[j]);
-                 continue;
-               }
-             if ((fp = curlfopen(cinfo, filename, iscompressed(filename), filechksum, filechksumtype, 1)) != 0)
-               {
-                 if (repo_add_debpackages(repo, fp, 0))
-                   {
-                     printf("component %s: %s\n", cinfo->components[j], pool_errstr(pool));
-                     cinfo->incomplete = 1;
-                   }
-                 fclose(fp);
-               }
-             solv_free((char *)filechksum);
-             solv_free((char *)filename);
-           }
-         fclose(fpr);
-         if (!cinfo->incomplete)
-           writecachedrepo(repo, 0, 0, cinfo->cookie);
-         break;
-#endif
-
-       default:
-         printf("unsupported repo '%s': skipped\n", cinfo->alias);
-         repo_free(repo, 1);
-         cinfo->repo = 0;
-         break;
-       }
-    }
-  if (sigpool)
-    pool_free(sigpool);
-}
-
-int
-yesno(const char *str)
-{
-  char inbuf[128], *ip;
-
-  for (;;)
-    {
-      printf("%s", str);
-      fflush(stdout);
-      *inbuf = 0;
-      if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
-       {
-         printf("Abort.\n");
-         exit(1);
-       }
-      while (*ip == ' ' || *ip == '\t')
-       ip++;
-      if (*ip == 'q')
-       {
-         printf("Abort.\n");
-         exit(1);
-       }
-      if (*ip == 'y' || *ip == 'n')
-       return *ip == 'y' ? 1 : 0;
-    }
-}
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-
-struct fcstate {
-  FILE **newpkgsfps;
-  Queue *checkq;
-  int newpkgscnt;
-  void *rpmstate;
-};
-
-static void *
-fileconflict_cb(Pool *pool, Id p, void *cbdata)
-{
-  struct fcstate *fcstate = cbdata;
-  Solvable *s;
-  Id rpmdbid;
-  int i;
-  FILE *fp;
-
-  s = pool_id2solvable(pool, p);
-  if (pool->installed && s->repo == pool->installed)
-    {
-      if (!s->repo->rpmdbid)
-       return 0;
-      rpmdbid = s->repo->rpmdbid[p - s->repo->start];
-      if (!rpmdbid)
-       return 0;
-      return rpm_byrpmdbid(fcstate->rpmstate, rpmdbid);
-    }
-  for (i = 0; i < fcstate->newpkgscnt; i++)
-    if (fcstate->checkq->elements[i] == p)
-      break;
-  if (i == fcstate->newpkgscnt)
-    return 0;
-  fp = fcstate->newpkgsfps[i];
-  if (!fp)
-    return 0;
-  rewind(fp);
-  return rpm_byfp(fcstate->rpmstate, fp, pool_solvable2str(pool, s));
-}
-
-
-void
-runrpm(const char *arg, const char *name, int dupfd3, const char *rootdir)
-{
-  pid_t pid;
-  int status;
-
-  if ((pid = fork()) == (pid_t)-1)
-    {
-      perror("fork");
-      exit(1);
-    }
-  if (pid == 0)
-    {
-      if (!rootdir)
-       rootdir = "/";
-      if (dupfd3 != -1 && dupfd3 != 3)
-       {
-         dup2(dupfd3, 3);
-         close(dupfd3);
-       }
-      if (dupfd3 != -1)
-       fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
-      if (strcmp(arg, "-e") == 0)
-        execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
-      else
-        execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
-      perror("rpm");
-      _exit(0);
-    }
-  while (waitpid(pid, &status, 0) != pid)
-    ;
-  if (status)
-    {
-      printf("rpm failed\n");
-      exit(1);
-    }
-}
-
-#endif
-
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-
-void
-rundpkg(const char *arg, const char *name, int dupfd3, const char *rootdir)
-{
-  pid_t pid;
-  int status;
-
-  if ((pid = fork()) == (pid_t)-1)
-    {
-      perror("fork");
-      exit(1);
-    }
-  if (pid == 0)
-    {
-      if (!rootdir)
-       rootdir = "/";
-      if (dupfd3 != -1 && dupfd3 != 3)
-       {
-         dup2(dupfd3, 3);
-         close(dupfd3);
-       }
-      if (dupfd3 != -1)
-       fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
-      if (strcmp(arg, "--install") == 0)
-        execlp("dpkg", "dpkg", "--install", "--root", rootdir, "--force", "all", name, (char *)0);
-      else
-        execlp("dpkg", "dpkg", "--remove", "--root", rootdir, "--force", "all", name, (char *)0);
-      perror("dpkg");
-      _exit(0);
-    }
-  while (waitpid(pid, &status, 0) != pid)
-    ;
-  if (status)
-    {
-      printf("dpkg failed\n");
-      exit(1);
-    }
-}
-
-#endif
-
-#ifdef SUSE
-static Id
-nscallback(Pool *pool, void *data, Id name, Id evr)
-{
-#if 0
-  if (name == NAMESPACE_LANGUAGE)
-    {
-      if (!strcmp(pool_id2str(pool, evr), "ja"))
-       return 1;
-      if (!strcmp(pool_id2str(pool, evr), "de"))
-       return 1;
-      if (!strcmp(pool_id2str(pool, evr), "en"))
-       return 1;
-      if (!strcmp(pool_id2str(pool, evr), "en_US"))
-       return 1;
-    }
-#endif
-  return 0;
-}
-#endif
-
-#ifdef SOFTLOCKS_PATH
-void
-addsoftlocks(Pool *pool, Queue *job)
-{
-  FILE *fp;
-  Id type, id, p, pp;
-  char *bp, *ep, buf[4096];
-
-  if ((fp = fopen(SOFTLOCKS_PATH, "r")) == 0)
-    return;
-  while((bp = fgets(buf, sizeof(buf), fp)) != 0)
-    {
-      while (*bp == ' ' || *bp == '\t')
-       bp++;
-      if (!*bp || *bp == '#')
-       continue;
-      for (ep = bp; *ep; ep++)
-       if (*ep == ' ' || *ep == '\t' || *ep == '\n')
-         break;
-      *ep = 0;
-      type = SOLVER_SOLVABLE_NAME;
-      if (!strncmp(bp, "provides:", 9) && bp[9])
-       {
-         type = SOLVER_SOLVABLE_PROVIDES;
-         bp += 9;
-       }
-      id = pool_str2id(pool, bp, 1);
-      if (pool->installed)
-       {
-         FOR_JOB_SELECT(p, pp, type, id)
-           if (pool->solvables[p].repo == pool->installed)
-             break;
-         if (p)
-           continue;   /* ignore, as it is already installed */
-       }
-      queue_push2(job, SOLVER_LOCK|SOLVER_WEAK|type, id);
-    }
-  fclose(fp);
-}
-#endif
-
-
-#if defined(ENABLE_RPMDB)
-
-static void
-rewrite_repos(Pool *pool, Queue *addedfileprovides, Queue *addedfileprovides_inst)
-{
-  Repo *repo;
-  Repodata *data;
-  Map providedids;
-  Queue fileprovidesq;
-  int i, j, n;
-  struct repoinfo *cinfo;
-
-  map_init(&providedids, pool->ss.nstrings);
-  queue_init(&fileprovidesq);
-  for (i = 0; i < addedfileprovides->count; i++)
-    MAPSET(&providedids, addedfileprovides->elements[i]);
-  FOR_REPOS(i, repo)
-    {
-      /* make sure all repodatas but the first are extensions */
-      if (repo->nrepodata < 2)
-       continue;
-      cinfo = repo->appdata;
-      if (repo != pool->installed && !cinfo)
-       continue;
-      if (cinfo && cinfo->incomplete)
-       continue;
-      data = repo_id2repodata(repo, 1);
-      if (data->loadcallback)
-        continue;
-      for (j = 2; j < repo->nrepodata; j++)
-       {
-         Repodata *edata = repo_id2repodata(repo, j);
-         if (!edata->loadcallback)
-           break;
-       }
-      if (j < repo->nrepodata)
-       continue;       /* found a non-externsion repodata, can't rewrite  */
-      if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
-       {
-         if (repo == pool->installed && addedfileprovides_inst)
-           {
-             for (j = 0; j < addedfileprovides->count; j++)
-               MAPCLR(&providedids, addedfileprovides->elements[j]);
-             for (j = 0; j < addedfileprovides_inst->count; j++)
-               MAPSET(&providedids, addedfileprovides_inst->elements[j]);
-           }
-         n = 0;
-         for (j = 0; j < fileprovidesq.count; j++)
-           if (MAPTST(&providedids, fileprovidesq.elements[j]))
-             n++;
-         if (repo == pool->installed && addedfileprovides_inst)
-           {
-             for (j = 0; j < addedfileprovides_inst->count; j++)
-               MAPCLR(&providedids, addedfileprovides_inst->elements[j]);
-             for (j = 0; j < addedfileprovides->count; j++)
-               MAPSET(&providedids, addedfileprovides->elements[j]);
-             if (n == addedfileprovides_inst->count)
-               continue;       /* nothing new added */
-           }
-         else if (n == addedfileprovides->count)
-           continue;   /* nothing new added */
-       }
-      repodata_set_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, repo == pool->installed && addedfileprovides_inst ? addedfileprovides_inst : addedfileprovides);
-      repodata_internalize(data);
-      writecachedrepo(repo, data, 0, cinfo ? cinfo->cookie : installedcookie);
-    }
-  queue_free(&fileprovidesq);
-  map_free(&providedids);
-}
-
-static void
-addfileprovides(Pool *pool)
-{
-  Queue addedfileprovides;
-  Queue addedfileprovides_inst;
-
-  queue_init(&addedfileprovides);
-  queue_init(&addedfileprovides_inst);
-  pool_addfileprovides_queue(pool, &addedfileprovides, &addedfileprovides_inst);
-  if (addedfileprovides.count || addedfileprovides_inst.count)
-    rewrite_repos(pool, &addedfileprovides, &addedfileprovides_inst);
-  queue_free(&addedfileprovides);
-  queue_free(&addedfileprovides_inst);
-}
-
-#endif
-
-#ifdef SUSE
-static void
-add_autopackages(Pool *pool)
-{
-  int i;
-  Repo *repo;
-  FOR_REPOS(i, repo)
-    repo_add_autopattern(repo, 0);
-}
-#endif
-
-#if defined(SUSE) || defined(FEDORA)
-static void
-add_patchjobs(Pool *pool, Queue *job)
-{
-  Id p, pp;
-  int pruneyou = 0;
-  Map installedmap, multiversionmap;
-  Solvable *s;
-
-  map_init(&multiversionmap, 0);
-  map_init(&installedmap, pool->nsolvables);
-  solver_calculate_multiversionmap(pool, job, &multiversionmap);
-  if (pool->installed)
-    FOR_REPO_SOLVABLES(pool->installed, p, s)
-      MAPSET(&installedmap, p);
-
-  /* install all patches */
-  for (p = 1; p < pool->nsolvables; p++)
-    {
-      const char *type;
-      int r;
-      Id p2;
-
-      s = pool->solvables + p;
-      if (strncmp(pool_id2str(pool, s->name), "patch:", 6) != 0)
-       continue;
-      FOR_PROVIDES(p2, pp, s->name)
-       {
-         Solvable *s2 = pool->solvables + p2;
-         if (s2->name != s->name)
-           continue;
-         r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
-         if (r < 0 || (r == 0 && p > p2))
-           break;
-       }
-      if (p2)
-       continue;
-      type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
-      if (type && !strcmp(type, "optional"))
-       continue;
-      r = solvable_trivial_installable_map(s, &installedmap, 0, &multiversionmap);
-      if (r == -1)
-       continue;
-      if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
-       {
-         if (!pruneyou++)
-           queue_empty(job);
-       }
-      else if (pruneyou)
-       continue;
-      queue_push2(job, SOLVER_SOLVABLE, p);
-    }
-  map_free(&installedmap);
-  map_free(&multiversionmap);
-}
-#endif
-
-#ifdef SUSE
-static void
-showdiskusagechanges(Transaction *trans)
-{
-  DUChanges duc[4];
-  int i;
-
-  /* XXX: use mountpoints here */
-  memset(duc, 0, sizeof(duc));
-  duc[0].path = "/";
-  duc[1].path = "/usr/share/man";
-  duc[2].path = "/sbin";
-  duc[3].path = "/etc";
-  transaction_calc_duchanges(trans, duc, 4);
-  for (i = 0; i < 4; i++)
-    printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
-}
-#endif
-
-#if defined(ENABLE_RPMDB)
-static FILE *
-trydeltadownload(Solvable *s, struct repoinfo *cinfo, const char *loc)
-{
-  Pool *pool = s->repo->pool;
-  Dataiterator di;
-  Id pp;
-  const unsigned char *chksum;
-  Id chksumtype;
-  FILE *retfp = 0;
-  char *matchname = strdup(pool_id2str(pool, s->name));
-
-  dataiterator_init(&di, pool, s->repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
-  dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
-  while (dataiterator_step(&di))
-    {
-      Id baseevr, op;
-
-      dataiterator_setpos_parent(&di);
-      if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
-         pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
-       continue;
-      baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
-      FOR_PROVIDES(op, pp, s->name)
-       {
-         Solvable *os = pool->solvables + op;
-         if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
-           break;
-       }
-      if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
-       {
-         /* base is installed, run sequence check */
-         const char *seq;
-         const char *dloc;
-         const char *archstr;
-         FILE *fp;
-         char cmd[128];
-         int newfd;
-
-         archstr = pool_id2str(pool, s->arch);
-         if (strlen(archstr) > 10 || strchr(archstr, '\'') != 0)
-           continue;
-
-         seq = pool_tmpjoin(pool, pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME), "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR));
-         seq = pool_tmpappend(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM));
-         if (strchr(seq, '\'') != 0)
-           continue;
-#ifdef FEDORA
-         sprintf(cmd, "/usr/bin/applydeltarpm -a '%s' -c -s '", archstr);
-#else
-         sprintf(cmd, "/usr/bin/applydeltarpm -c -s '");
-#endif
-         if (system(pool_tmpjoin(pool, cmd, seq, "'")) != 0)
-           continue;   /* didn't match */
-         /* looks good, download delta */
-         chksumtype = 0;
-         chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
-         if (!chksumtype)
-           continue;   /* no way! */
-         dloc = pool_lookup_deltalocation(pool, SOLVID_POS, 0);
-         if (!dloc)
-           continue;
-#ifdef ENABLE_SUSEREPO
-         if (cinfo->type == TYPE_SUSETAGS)
-           {
-             const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
-             dloc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", dloc);
-           }
-#endif
-         if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
-           continue;
-         /* got it, now reconstruct */
-         newfd = opentmpfile();
-#ifdef FEDORA
-         sprintf(cmd, "applydeltarpm -a '%s' /dev/fd/%d /dev/fd/%d", archstr, fileno(fp), newfd);
-#else
-         sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
-#endif
-         fcntl(fileno(fp), F_SETFD, 0);
-         if (system(cmd))
-           {
-             close(newfd);
-             fclose(fp);
-             continue;
-           }
-         lseek(newfd, 0, SEEK_SET);
-         chksumtype = 0;
-         chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
-         if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
-           {
-             close(newfd);
-             fclose(fp);
-             continue;
-           }
-         retfp = fdopen(newfd, "r");
-         fclose(fp);
-         break;
-       }
-    }
-  dataiterator_free(&di);
-  solv_free(matchname);
-  return retfp;
-}
-#endif
-
-
-#define MODE_LIST        0
-#define MODE_INSTALL     1
-#define MODE_ERASE       2
-#define MODE_UPDATE      3
-#define MODE_DISTUPGRADE 4
-#define MODE_VERIFY      5
-#define MODE_PATCH       6
-#define MODE_INFO        7
-#define MODE_REPOLIST    8
-#define MODE_SEARCH     9
-
-void
-usage(int r)
-{
-  fprintf(stderr, "Usage: solv COMMAND <select>\n");
-  fprintf(stderr, "\n");
-  fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
-  fprintf(stderr, "                  versions from the repositories\n");
-  fprintf(stderr, "    erase:        erase installed packages\n");
-  fprintf(stderr, "    info:         display package information\n");
-  fprintf(stderr, "    install:      install packages\n");
-  fprintf(stderr, "    list:         list packages\n");
-  fprintf(stderr, "    repos:        list enabled repositories\n");
-  fprintf(stderr, "    search:       search name/summary/description\n");
-  fprintf(stderr, "    update:       update installed packages\n");
-  fprintf(stderr, "    verify:       check dependencies of installed packages\n");
-#if defined(SUSE) || defined(FEDORA)
-  fprintf(stderr, "    patch:        install newest patches\n");
-#endif
-  fprintf(stderr, "\n");
-  exit(r);
-}
-
-int
-main(int argc, char **argv)
-{
-  Pool *pool;
-  Repo *commandlinerepo = 0;
-  Id *commandlinepkgs = 0;
-  Id p;
-  struct repoinfo *repoinfos;
-  int nrepoinfos = 0;
-  int mainmode = 0, mode = 0;
-  int i, newpkgs;
-  Queue job, checkq;
-  Solver *solv = 0;
-  Transaction *trans;
-  FILE **newpkgsfps;
-  Queue repofilter;
-  Queue kindfilter;
-  Queue archfilter;
-  int archfilter_src = 0;
-  int cleandeps = 0;
-  int forcebest = 0;
-  char *rootdir = 0;
-  char *keyname = 0;
-  int debuglevel = 0;
-
-  argc--;
-  argv++;
-  userhome = getenv("HOME");
-  if (userhome && userhome[0] != '/')
-    userhome = 0;
-  while (argc && !strcmp(argv[0], "-d"))
-    {
-      debuglevel++;
-      argc--;
-      argv++;
-    }
-  if (!argv[0])
-    usage(1);
-  if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
-    {
-      mainmode = MODE_INSTALL;
-      mode = SOLVER_INSTALL;
-    }
-#if defined(SUSE) || defined(FEDORA)
-  else if (!strcmp(argv[0], "patch"))
-    {
-      mainmode = MODE_PATCH;
-      mode = SOLVER_INSTALL;
-    }
-#endif
-  else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
-    {
-      mainmode = MODE_ERASE;
-      mode = SOLVER_ERASE;
-    }
-  else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "ls"))
-    {
-      mainmode = MODE_LIST;
-      mode = 0;
-    }
-  else if (!strcmp(argv[0], "info"))
-    {
-      mainmode = MODE_INFO;
-      mode = 0;
-    }
-  else if (!strcmp(argv[0], "search") || !strcmp(argv[0], "se"))
-    {
-      mainmode = MODE_SEARCH;
-      mode = 0;
-    }
-  else if (!strcmp(argv[0], "verify"))
-    {
-      mainmode = MODE_VERIFY;
-      mode = SOLVER_VERIFY;
-    }
-  else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
-    {
-      mainmode = MODE_UPDATE;
-      mode = SOLVER_UPDATE;
-    }
-  else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
-    {
-      mainmode = MODE_DISTUPGRADE;
-      mode = SOLVER_DISTUPGRADE;
-    }
-  else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
-    {
-      mainmode = MODE_REPOLIST;
-      mode = 0;
-    }
-  else
-    usage(1);
-
-  for (;;)
-    {
-      if (argc > 2 && !strcmp(argv[1], "--root"))
-       {
-         rootdir = argv[2];
-         argc -= 2;
-         argv += 2;
-       }
-      else if (argc > 1 && !strcmp(argv[1], "--clean"))
-       {
-         cleandeps = 1;
-         argc--;
-         argv++;
-       }
-      else if (argc > 1 && !strcmp(argv[1], "--best"))
-       {
-         forcebest = 1;
-         argc--;
-         argv++;
-       }
-      if (argc > 2 && !strcmp(argv[1], "--keyname"))
-       {
-         keyname = argv[2];
-         argc -= 2;
-         argv += 2;
-       }
-      else
-       break;
-    }
-
-  pool = pool_create();
-  pool_set_rootdir(pool, rootdir);
-
-#if 0
-  {
-    const char *langs[] = {"de_DE", "de", "en"};
-    pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
-  }
-#endif
-
-  pool_setloadcallback(pool, load_stub, 0);
-#ifdef SUSE
-  pool->nscallback = nscallback;
-#endif
-  if (debuglevel)
-    pool_setdebuglevel(pool, debuglevel);
-  setarch(pool);
-  pool_set_flag(pool, POOL_FLAG_ADDFILEPROVIDESFILTERED, 1);
-  repoinfos = read_repoinfos(pool, &nrepoinfos);
-
-  if (mainmode == MODE_REPOLIST)
-    {
-      int j = 1;
-      for (i = 0; i < nrepoinfos; i++)
-       {
-         struct repoinfo *cinfo = repoinfos + i;
-         if (!cinfo->enabled)
-           continue;
-         printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
-       }
-      exit(0);
-    }
-
-  read_repos(pool, repoinfos, nrepoinfos);
-
-  /* setup filters */
-  queue_init(&repofilter);
-  queue_init(&kindfilter);
-  queue_init(&archfilter);
-  while (argc > 1)
-    {
-      if (!strcmp(argv[1], "-i"))
-       {
-         queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO, pool->installed->repoid);
-         argc--;
-         argv++;
-       }
-      else if (argc > 2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "--repo")))
-       {
-         const char *rname = argv[2], *rp;
-         Id repoid = 0;
-         for (rp = rname; *rp; rp++)
-           if (*rp <= '0' || *rp >= '9')
-             break;
-         if (!*rp)
-           {
-             /* repo specified by number */
-             int rnum = atoi(rname);
-             for (i = 0; i < nrepoinfos; i++)
-               {
-                 struct repoinfo *cinfo = repoinfos + i;
-                 if (!cinfo->enabled)
-                   continue;
-                 if (--rnum == 0)
-                   repoid = cinfo->repo->repoid;
-               }
-           }
-         else
-           {
-             /* repo specified by alias */
-             Repo *repo;
-             FOR_REPOS(i, repo)
-               {
-                 if (!strcasecmp(rname, repo->name))
-                   repoid = repo->repoid;
-               }
-           }
-         if (!repoid)
-           {
-             fprintf(stderr, "%s: no such repo\n", rname);
-             exit(1);
-           }
-         /* SETVENDOR is actually wrong but useful */
-         queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO | SOLVER_SETVENDOR, repoid);
-         argc -= 2;
-         argv += 2;
-       }
-      else if (argc > 2 && !strcmp(argv[1], "--arch"))
-       {
-         if (!strcmp(argv[2], "src") || !strcmp(argv[2], "nosrc"))
-           archfilter_src = 1;
-         queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, argv[2], 1), REL_ARCH, 1));
-         argc -= 2;
-         argv += 2;
-       }
-      else if (argc > 2 && (!strcmp(argv[1], "-t") || !strcmp(argv[1], "--type")))
-       {
-         const char *kind = argv[2];
-         if (!strcmp(kind, "srcpackage"))
-           {
-             /* hey! should use --arch! */
-             queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, ARCH_SRC, REL_ARCH, 1));
-             archfilter_src = 1;
-             argc -= 2;
-             argv += 2;
-             continue;
-           }
-         if (!strcmp(kind, "package"))
-           kind = "";
-         if (!strcmp(kind, "all"))
-           queue_push2(&kindfilter, SOLVER_SOLVABLE_ALL, 0);
-         else
-           queue_push2(&kindfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, kind, 1), REL_KIND, 1));
-         argc -= 2;
-         argv += 2;
-       }
-      else
-       break;
-    }
-
-  if (mainmode == MODE_SEARCH)
-    {
-      Queue sel, q;
-      Dataiterator di;
-      if (argc != 2)
-       usage(1);
-      pool_createwhatprovides(pool);
-      queue_init(&sel);
-      dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
-      dataiterator_set_keyname(&di, SOLVABLE_NAME);
-      dataiterator_set_search(&di, 0, 0);
-      while (dataiterator_step(&di))
-       queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
-      dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
-      dataiterator_set_search(&di, 0, 0);
-      while (dataiterator_step(&di))
-       queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
-      dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
-      dataiterator_set_search(&di, 0, 0);
-      while (dataiterator_step(&di))
-       queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
-      dataiterator_free(&di);
-      if (repofilter.count)
-       selection_filter(pool, &sel, &repofilter);
-       
-      queue_init(&q);
-      selection_solvables(pool, &sel, &q);
-      queue_free(&sel);
-      for (i = 0; i < q.count; i++)
-       {
-         Solvable *s = pool_id2solvable(pool, q.elements[i]);
-         printf("  - %s [%s]: %s\n", pool_solvable2str(pool, s), s->repo->name, solvable_lookup_str(s, SOLVABLE_SUMMARY));
-       }
-      queue_free(&q);
-      exit(0);
-    }
-
-  /* process command line packages */
-  if (mainmode == MODE_LIST || mainmode == MODE_INFO || mainmode == MODE_INSTALL)
-    {
-      for (i = 1; i < argc; i++)
-       {
-         int l;
-          l = strlen(argv[i]);
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-         if (l <= 4 || strcmp(argv[i] + l - 4, ".rpm"))
-           continue;
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-         if (l <= 4 || strcmp(argv[i] + l - 4, ".deb"))
-           continue;
-#endif
-         if (access(argv[i], R_OK))
-           {
-             perror(argv[i]);
-             exit(1);
-           }
-         if (!commandlinepkgs)
-           commandlinepkgs = solv_calloc(argc, sizeof(Id));
-         if (!commandlinerepo)
-           commandlinerepo = repo_create(pool, "@commandline");
-         p = 0;
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-         p = repo_add_rpm(commandlinerepo, (const char *)argv[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-         p = repo_add_deb(commandlinerepo, (const char *)argv[i], REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
-#endif
-         if (!p)
-           {
-             fprintf(stderr, "could not add '%s'\n", argv[i]);
-             exit(1);
-           }
-         commandlinepkgs[i] = p;
-       }
-      if (commandlinerepo)
-       repo_internalize(commandlinerepo);
-    }
-
-  // FOR_REPOS(i, repo)
-  //   printf("%s: %d solvables\n", repo->name, repo->nsolvables);
-
-#if defined(ENABLE_RPMDB)
-  if (pool->disttype == DISTTYPE_RPM)
-    addfileprovides(pool);
-#endif
-#ifdef SUSE
-  add_autopackages(pool);
-#endif
-  pool_createwhatprovides(pool);
-
-  if (keyname)
-    keyname = solv_dupjoin("solvable:", keyname, 0);
-  queue_init(&job);
-  for (i = 1; i < argc; i++)
-    {
-      Queue job2;
-      int j, flags, rflags;
-
-      if (commandlinepkgs && commandlinepkgs[i])
-       {
-         queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
-         continue;
-       }
-      queue_init(&job2);
-      flags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_GLOB;
-      flags |= SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL;
-      if (kindfilter.count)
-       flags |= SELECTION_SKIP_KIND;
-      if (mode == MODE_LIST || archfilter_src)
-       flags |= SELECTION_WITH_SOURCE;
-      if (argv[i][0] == '/')
-       flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0);
-      if (!keyname)
-        rflags = selection_make(pool, &job2, argv[i], flags);
-      else
-        rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
-      if (repofilter.count)
-       selection_filter(pool, &job2, &repofilter);
-      if (archfilter.count)
-       selection_filter(pool, &job2, &archfilter);
-      if (kindfilter.count)
-       selection_filter(pool, &job2, &kindfilter);
-      if (!job2.count)
-       {
-         flags |= SELECTION_NOCASE;
-         if (!keyname)
-            rflags = selection_make(pool, &job2, argv[i], flags);
-         else
-           rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
-         if (repofilter.count)
-           selection_filter(pool, &job2, &repofilter);
-         if (archfilter.count)
-           selection_filter(pool, &job2, &archfilter);
-         if (kindfilter.count)
-           selection_filter(pool, &job2, &kindfilter);
-         if (job2.count)
-           printf("[ignoring case for '%s']\n", argv[i]);
-       }
-      if (!job2.count)
-       {
-         fprintf(stderr, "nothing matches '%s'\n", argv[i]);
-         exit(1);
-       }
-      if (rflags & SELECTION_FILELIST)
-        printf("[using file list match for '%s']\n", argv[i]);
-      if (rflags & SELECTION_PROVIDES)
-       printf("[using capability match for '%s']\n", argv[i]);
-      for (j = 0; j < job2.count; j++)
-       queue_push(&job, job2.elements[j]);
-      queue_free(&job2);
-    }
-  keyname = solv_free(keyname);
-
-  if (!job.count && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE || mainmode == MODE_VERIFY || repofilter.count || archfilter.count || kindfilter.count))
-    {
-      queue_push2(&job, SOLVER_SOLVABLE_ALL, 0);
-      if (repofilter.count)
-       selection_filter(pool, &job, &repofilter);
-      if (archfilter.count)
-       selection_filter(pool, &job, &archfilter);
-      if (kindfilter.count)
-       selection_filter(pool, &job, &kindfilter);
-    }
-  queue_free(&repofilter);
-  queue_free(&archfilter);
-  queue_free(&kindfilter);
-
-  if (!job.count && mainmode != MODE_PATCH)
-    {
-      printf("no package matched\n");
-      exit(1);
-    }
-
-  if (mainmode == MODE_LIST || mainmode == MODE_INFO)
-    {
-      /* list mode, no solver needed */
-      Queue q;
-      queue_init(&q);
-      for (i = 0; i < job.count; i += 2)
-       {
-         int j;
-         queue_empty(&q);
-         pool_job2solvables(pool, &q, job.elements[i], job.elements[i + 1]);
-         for (j = 0; j < q.count; j++)
-           {
-             Solvable *s = pool_id2solvable(pool, q.elements[j]);
-             if (mainmode == MODE_INFO)
-               {
-                 const char *str;
-                 printf("Name:        %s\n", pool_solvable2str(pool, s));
-                 printf("Repo:        %s\n", s->repo->name);
-                 printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
-                 str = solvable_lookup_str(s, SOLVABLE_URL);
-                 if (str)
-                   printf("Url:         %s\n", str);
-                 str = solvable_lookup_str(s, SOLVABLE_LICENSE);
-                 if (str)
-                   printf("License:     %s\n", str);
-#if 0
-                 str = solvable_lookup_sourcepkg(s);
-                 if (str)
-                   printf("Source:      %s\n", str);
-#endif
-                 printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
-                 printf("\n");
-               }
-             else
-               {
-#if 1
-                 const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
-#else
-                 const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
-#endif
-                 printf("  - %s [%s]\n", pool_solvable2str(pool, s), s->repo->name);
-                 if (sum)
-                   printf("    %s\n", sum);
-               }
-           }
-       }
-      queue_free(&q);
-      queue_free(&job);
-      pool_free(pool);
-      free_repoinfos(repoinfos, nrepoinfos);
-      solv_free(commandlinepkgs);
-#ifdef FEDORA
-      yum_substitute(pool, 0);
-#endif
-      exit(0);
-    }
-
-#if defined(SUSE) || defined(FEDORA)
-  if (mainmode == MODE_PATCH)
-    add_patchjobs(pool, &job);
-#endif
-
-  // add mode
-  for (i = 0; i < job.count; i += 2)
-    {
-      job.elements[i] |= mode;
-      if (mode == SOLVER_UPDATE && pool_isemptyupdatejob(pool, job.elements[i], job.elements[i + 1]))
-       job.elements[i] ^= SOLVER_UPDATE ^ SOLVER_INSTALL;
-      if (cleandeps)
-        job.elements[i] |= SOLVER_CLEANDEPS;
-      if (forcebest)
-        job.elements[i] |= SOLVER_FORCEBEST;
-    }
-
-  // multiversion test
-  // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1));
-  // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1));
-  // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-extra", 1));
-#if 0
-  queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
-  queue_push2(&job, SOLVER_ERASE|SOLVER_CLEANDEPS|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
-#endif
-
-#ifdef SOFTLOCKS_PATH
-  addsoftlocks(pool, &job);
-#endif
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-rerunsolver:
-#endif
-  solv = solver_create(pool);
-  solver_set_flag(solv, SOLVER_FLAG_SPLITPROVIDES, 1);
-#ifdef FEDORA
-  solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
-#endif
-  if (mainmode == MODE_ERASE)
-    solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1);     /* don't nag */
-  solver_set_flag(solv, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
-
-  for (;;)
-    {
-      Id problem, solution;
-      int pcnt, scnt;
-
-      if (!solver_solve(solv, &job))
-       break;
-      pcnt = solver_problem_count(solv);
-      printf("Found %d problems:\n", pcnt);
-      for (problem = 1; problem <= pcnt; problem++)
-       {
-         int take = 0;
-         printf("Problem %d/%d:\n", problem, pcnt);
-         solver_printprobleminfo(solv, problem);
-         printf("\n");
-         scnt = solver_solution_count(solv, problem);
-         for (solution = 1; solution <= scnt; solution++)
-           {
-             printf("Solution %d:\n", solution);
-             solver_printsolution(solv, problem, solution);
-             printf("\n");
-           }
-         for (;;)
-           {
-             char inbuf[128], *ip;
-             printf("Please choose a solution: ");
-             fflush(stdout);
-             *inbuf = 0;
-             if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
-               {
-                 printf("Abort.\n");
-                 exit(1);
-               }
-             while (*ip == ' ' || *ip == '\t')
-               ip++;
-             if (*ip >= '0' && *ip <= '9')
-               {
-                 take = atoi(ip);
-                 if (take >= 1 && take <= scnt)
-                   break;
-               }
-             if (*ip == 's')
-               {
-                 take = 0;
-                 break;
-               }
-             if (*ip == 'q')
-               {
-                 printf("Abort.\n");
-                 exit(1);
-               }
-           }
-         if (!take)
-           continue;
-         solver_take_solution(solv, problem, take, &job);
-       }
-    }
-
-  trans = solver_create_transaction(solv);
-  if (!trans->steps.count)
-    {
-      printf("Nothing to do.\n");
-      transaction_free(trans);
-      solver_free(solv);
-      queue_free(&job);
-      pool_free(pool);
-      free_repoinfos(repoinfos, nrepoinfos);
-      solv_free(commandlinepkgs);
-#ifdef FEDORA
-      yum_substitute(pool, 0);
-#endif
-      exit(1);
-    }
-
-  /* display transaction to the user and ask for confirmation */
-  printf("\n");
-  printf("Transaction summary:\n\n");
-  transaction_print(trans);
-#if defined(SUSE)
-  showdiskusagechanges(trans);
-#endif
-  printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
-  printf("\n");
-
-  if (!yesno("OK to continue (y/n)? "))
-    {
-      printf("Abort.\n");
-      transaction_free(trans);
-      solver_free(solv);
-      queue_free(&job);
-      pool_free(pool);
-      free_repoinfos(repoinfos, nrepoinfos);
-      solv_free(commandlinepkgs);
-#ifdef FEDORA
-      yum_substitute(pool, 0);
-#endif
-      exit(1);
-    }
-
-  /* download all new packages */
-  queue_init(&checkq);
-  newpkgs = transaction_installedresult(trans, &checkq);
-  newpkgsfps = 0;
-  if (newpkgs)
-    {
-      int downloadsize = 0;
-      for (i = 0; i < newpkgs; i++)
-       {
-         Solvable *s;
-
-         p = checkq.elements[i];
-         s = pool_id2solvable(pool, p);
-         downloadsize += solvable_lookup_sizek(s, SOLVABLE_DOWNLOADSIZE, 0);
-       }
-      printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
-      newpkgsfps = solv_calloc(newpkgs, sizeof(*newpkgsfps));
-      for (i = 0; i < newpkgs; i++)
-       {
-         unsigned int medianr;
-         const char *loc;
-         Solvable *s;
-         struct repoinfo *cinfo;
-         const unsigned char *chksum;
-         Id chksumtype;
-
-         p = checkq.elements[i];
-         s = pool_id2solvable(pool, p);
-         if (s->repo == commandlinerepo)
-           {
-             loc = solvable_lookup_location(s, &medianr);
-             if (!loc)
-               continue;
-             if (!(newpkgsfps[i] = fopen(loc, "r")))
-               {
-                 perror(loc);
-                 exit(1);
-               }
-             putchar('.');
-             continue;
-           }
-         cinfo = s->repo->appdata;
-         if (!cinfo)
-           {
-             printf("%s: no repository information\n", s->repo->name);
-             exit(1);
-           }
-         loc = solvable_lookup_location(s, &medianr);
-         if (!loc)
-            continue;
-#if defined(ENABLE_RPMDB)
-         if (pool->installed && pool->installed->nsolvables)
-           {
-             if ((newpkgsfps[i] = trydeltadownload(s, cinfo, loc)) != 0)
-               {
-                 putchar('d');
-                 fflush(stdout);
-                 continue;             /* delta worked! */
-               }
-           }
-#endif
-#ifdef ENABLE_SUSEREPO
-         if (cinfo->type == TYPE_SUSETAGS)
-           {
-             const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
-             loc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", loc);
-           }
-#endif
-         chksumtype = 0;
-         chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
-         if ((newpkgsfps[i] = curlfopen(cinfo, loc, 0, chksum, chksumtype, 0)) == 0)
-           {
-             printf("\n%s: %s not found in repository\n", s->repo->name, loc);
-             exit(1);
-           }
-         putchar('.');
-         fflush(stdout);
-       }
-      putchar('\n');
-    }
-
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-  /* check for file conflicts */
-  if (newpkgs)
-    {
-      Queue conflicts;
-      struct fcstate fcstate;
-
-      printf("Searching for file conflicts\n");
-      queue_init(&conflicts);
-      fcstate.rpmstate = rpm_state_create(pool, rootdir);
-      fcstate.newpkgscnt = newpkgs;
-      fcstate.checkq = &checkq;
-      fcstate.newpkgsfps = newpkgsfps;
-      pool_findfileconflicts(pool, &checkq, newpkgs, &conflicts, FINDFILECONFLICTS_USE_SOLVABLEFILELIST | FINDFILECONFLICTS_CHECK_DIRALIASING | FINDFILECONFLICTS_USE_ROOTDIR, &fileconflict_cb, &fcstate);
-      fcstate.rpmstate = rpm_state_free(fcstate.rpmstate);
-      if (conflicts.count)
-       {
-         printf("\n");
-         for (i = 0; i < conflicts.count; i += 6)
-           printf("file %s of package %s conflicts with package %s\n", pool_id2str(pool, conflicts.elements[i]), pool_solvid2str(pool, conflicts.elements[i + 1]), pool_solvid2str(pool, conflicts.elements[i + 4]));
-         printf("\n");
-         if (yesno("Re-run solver (y/n/q)? "))
-           {
-             for (i = 0; i < newpkgs; i++)
-               if (newpkgsfps[i])
-                 fclose(newpkgsfps[i]);
-             newpkgsfps = solv_free(newpkgsfps);
-             solver_free(solv);
-             solv = 0;
-             pool_add_fileconflicts_deps(pool, &conflicts);
-             goto rerunsolver;
-           }
-       }
-      queue_free(&conflicts);
-    }
-#endif
-
-  /* and finally commit the transaction */
-  printf("Committing transaction:\n\n");
-  transaction_order(trans, 0);
-  for (i = 0; i < trans->steps.count; i++)
-    {
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-      const char *evr, *evrp, *nvra;
-#endif
-      Solvable *s;
-      int j;
-      FILE *fp;
-      Id type;
-
-      p = trans->steps.elements[i];
-      s = pool_id2solvable(pool, p);
-      type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
-      switch(type)
-       {
-       case SOLVER_TRANSACTION_ERASE:
-         printf("erase %s\n", pool_solvid2str(pool, p));
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-         if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
-           continue;
-         /* strip epoch from evr */
-         evr = evrp = pool_id2str(pool, s->evr);
-         while (*evrp >= '0' && *evrp <= '9')
-           evrp++;
-         if (evrp > evr && evrp[0] == ':' && evrp[1])
-           evr = evrp + 1;
-         nvra = pool_tmpjoin(pool, pool_id2str(pool, s->name), "-", evr);
-         nvra = pool_tmpappend(pool, nvra, ".", pool_id2str(pool, s->arch));
-         runrpm("-e", nvra, -1, rootdir);      /* too bad that --querybynumber doesn't work */
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-         rundpkg("--remove", pool_id2str(pool, s->name), 0, rootdir);
-#endif
-         break;
-       case SOLVER_TRANSACTION_INSTALL:
-       case SOLVER_TRANSACTION_MULTIINSTALL:
-         printf("install %s\n", pool_solvid2str(pool, p));
-         for (j = 0; j < newpkgs; j++)
-           if (checkq.elements[j] == p)
-             break;
-         fp = j < newpkgs ? newpkgsfps[j] : 0;
-         if (!fp)
-           continue;
-         rewind(fp);
-         lseek(fileno(fp), 0, SEEK_SET);
-#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
-         runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp), rootdir);
-#endif
-#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
-         rundpkg("--install", "/dev/fd/3", fileno(fp), rootdir);
-#endif
-         fclose(fp);
-         newpkgsfps[j] = 0;
-         break;
-       default:
-         break;
-       }
-    }
-
-  for (i = 0; i < newpkgs; i++)
-    if (newpkgsfps[i])
-      fclose(newpkgsfps[i]);
-  solv_free(newpkgsfps);
-  queue_free(&checkq);
-  transaction_free(trans);
-  solver_free(solv);
-  queue_free(&job);
-  pool_free(pool);
-  free_repoinfos(repoinfos, nrepoinfos);
-  solv_free(commandlinepkgs);
-#ifdef FEDORA
-  yum_substitute(pool, 0);
-#endif
-  exit(0);
-}
diff --git a/examples/solv/CMakeLists.txt b/examples/solv/CMakeLists.txt
new file mode 100644 (file)
index 0000000..41f45f7
--- /dev/null
@@ -0,0 +1,29 @@
+
+ADD_EXECUTABLE (solv solv.c
+checksig.c
+deltarpm.c
+fastestmirror.c
+fileconflicts.c
+fileprovides.c
+mirror.c
+patchjobs.c
+repoinfo.c
+repoinfo_cache.c
+repoinfo_config_debian.c
+repoinfo_config_yum.c
+repoinfo_config_urpmi.c
+repoinfo_download.c
+repoinfo_system_debian.c
+repoinfo_system_rpm.c
+repoinfo_type_debian.c
+repoinfo_type_mdk.c
+repoinfo_type_rpmmd.c
+repoinfo_type_susetags.c
+)
+
+TARGET_LINK_LIBRARIES (solv libsolvext libsolv ${SYSTEM_LIBRARIES})
+
+INSTALL(TARGETS
+    solv
+    DESTINATION ${BIN_INSTALL_DIR})
+
diff --git a/examples/solv/checksig.c b/examples/solv/checksig.c
new file mode 100644 (file)
index 0000000..ff60c66
--- /dev/null
@@ -0,0 +1,126 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "pool.h"
+#include "repo.h"
+#ifdef ENABLE_PUBKEY
+#include "repo_pubkey.h"
+#endif
+
+#include "checksig.h"
+
+#ifndef DEBIAN
+
+static void
+cleanupgpg(char *gpgdir)
+{
+  char cmd[256];
+  snprintf(cmd, sizeof(cmd), "%s/pubring.gpg", gpgdir);
+  unlink(cmd);
+  snprintf(cmd, sizeof(cmd), "%s/pubring.gpg~", gpgdir);
+  unlink(cmd);
+  snprintf(cmd, sizeof(cmd), "%s/secring.gpg", gpgdir);
+  unlink(cmd);
+  snprintf(cmd, sizeof(cmd), "%s/trustdb.gpg", gpgdir);
+  unlink(cmd);
+  snprintf(cmd, sizeof(cmd), "%s/keys", gpgdir);
+  unlink(cmd);
+  rmdir(gpgdir);
+}
+
+int
+checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
+{
+  char *gpgdir;
+  char *keysfile;
+  const char *pubkey;
+  char cmd[256];
+  FILE *kfp;
+  Solvable *s;
+  Id p;
+  off_t posfp, possigfp;
+  int r, nkeys;
+
+  gpgdir = mkdtemp(pool_tmpjoin(sigpool, "/var/tmp/solvgpg.XXXXXX", 0, 0));
+  if (!gpgdir)
+    return 0;
+  keysfile = pool_tmpjoin(sigpool, gpgdir, "/keys", 0);
+  if (!(kfp = fopen(keysfile, "w")) )
+    {
+      cleanupgpg(gpgdir);
+      return 0;
+    }
+  nkeys = 0;
+  for (p = 1, s = sigpool->solvables + p; p < sigpool->nsolvables; p++, s++)
+    {
+      if (!s->repo)
+       continue;
+      pubkey = solvable_lookup_str(s, SOLVABLE_DESCRIPTION);
+      if (!pubkey || !*pubkey)
+       continue;
+      if (fwrite(pubkey, strlen(pubkey), 1, kfp) != 1)
+       break;
+      if (fputc('\n', kfp) == EOF)     /* Just in case... */
+       break;
+      nkeys++;
+    }
+  if (fclose(kfp) || !nkeys || p < sigpool->nsolvables)
+    {
+      cleanupgpg(gpgdir);
+      return 0;
+    }
+  snprintf(cmd, sizeof(cmd), "gpg2 -q --homedir %s --import %s", gpgdir, keysfile);
+  if (system(cmd))
+    {
+      fprintf(stderr, "key import error\n");
+      cleanupgpg(gpgdir);
+      return 0;
+    }
+  unlink(keysfile);
+  posfp = lseek(fileno(fp), 0, SEEK_CUR);
+  lseek(fileno(fp), 0, SEEK_SET);
+  possigfp = lseek(fileno(sigfp), 0, SEEK_CUR);
+  lseek(fileno(sigfp), 0, SEEK_SET);
+  snprintf(cmd, sizeof(cmd), "gpgv -q --homedir %s --keyring %s/pubring.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", gpgdir, gpgdir, fileno(sigfp), fileno(fp));
+  fcntl(fileno(fp), F_SETFD, 0);       /* clear CLOEXEC */
+  fcntl(fileno(sigfp), F_SETFD, 0);    /* clear CLOEXEC */
+  r = system(cmd);
+  lseek(fileno(sigfp), possigfp, SEEK_SET);
+  lseek(fileno(fp), posfp, SEEK_SET);
+  fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+  fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
+  cleanupgpg(gpgdir);
+  return r == 0 ? 1 : 0;
+}
+
+#else
+
+int
+checksig(Pool *sigpool, FILE *fp, FILE *sigfp)
+{
+  char cmd[256];
+  int r;
+
+  snprintf(cmd, sizeof(cmd), "gpgv -q --keyring /etc/apt/trusted.gpg /dev/fd/%d /dev/fd/%d >/dev/null 2>&1", fileno(sigfp), fileno(fp));
+  fcntl(fileno(fp), F_SETFD, 0);       /* clear CLOEXEC */
+  fcntl(fileno(sigfp), F_SETFD, 0);    /* clear CLOEXEC */
+  r = system(cmd);
+  fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
+  fcntl(fileno(sigfp), F_SETFD, FD_CLOEXEC);
+  return r == 0 ? 1 : 0;
+}
+
+#endif
+
+Pool *
+read_sigs()
+{
+  Pool *sigpool = pool_create();
+#if defined(ENABLE_PUBKEY) && defined(ENABLE_RPMDB)
+  Repo *repo = repo_create(sigpool, "pubkeys");
+  repo_add_rpmdb_pubkeys(repo, 0);
+#endif
+  return sigpool;
+}
diff --git a/examples/solv/checksig.h b/examples/solv/checksig.h
new file mode 100644 (file)
index 0000000..67ebaf8
--- /dev/null
@@ -0,0 +1,3 @@
+extern int checksig(Pool *sigpool, FILE *fp, FILE *sigfp);
+extern Pool *read_sigs();
+
diff --git a/examples/solv/deltarpm.c b/examples/solv/deltarpm.c
new file mode 100644 (file)
index 0000000..551d570
--- /dev/null
@@ -0,0 +1,133 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repoinfo.h"
+#include "repoinfo_download.h"
+
+#include "deltarpm.h"
+
+static inline int
+opentmpfile()
+{
+  char tmpl[100];
+  int fd;
+
+  strcpy(tmpl, "/var/tmp/solvXXXXXX");
+  fd = mkstemp(tmpl);
+  if (fd < 0) 
+    {    
+      perror("mkstemp");
+      exit(1);
+    }    
+  unlink(tmpl);
+  return fd;
+}
+
+FILE *
+trydeltadownload(Solvable *s, const char *loc)
+{
+  Repo *repo = s->repo;
+  Pool *pool = repo->pool;
+  struct repoinfo *cinfo = repo->appdata;
+  Dataiterator di;
+  Id pp;
+  const unsigned char *chksum;
+  Id chksumtype;
+  FILE *retfp = 0;
+  char *matchname = strdup(pool_id2str(pool, s->name));
+
+  dataiterator_init(&di, pool, repo, SOLVID_META, DELTA_PACKAGE_NAME, matchname, SEARCH_STRING);
+  dataiterator_prepend_keyname(&di, REPOSITORY_DELTAINFO);
+  while (dataiterator_step(&di))
+    {
+      Id baseevr, op;
+
+      dataiterator_setpos_parent(&di);
+      if (pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_EVR) != s->evr ||
+         pool_lookup_id(pool, SOLVID_POS, DELTA_PACKAGE_ARCH) != s->arch)
+       continue;
+      baseevr = pool_lookup_id(pool, SOLVID_POS, DELTA_BASE_EVR);
+      FOR_PROVIDES(op, pp, s->name)
+       {
+         Solvable *os = pool->solvables + op;
+         if (os->repo == pool->installed && os->name == s->name && os->arch == s->arch && os->evr == baseevr)
+           break;
+       }
+      if (op && access("/usr/bin/applydeltarpm", X_OK) == 0)
+       {
+         /* base is installed, run sequence check */
+         const char *seq;
+         const char *dloc;
+         const char *archstr;
+         FILE *fp;
+         char cmd[128];
+         int newfd;
+
+         archstr = pool_id2str(pool, s->arch);
+         if (strlen(archstr) > 10 || strchr(archstr, '\'') != 0)
+           continue;
+
+         seq = pool_tmpjoin(pool, pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NAME), "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_EVR));
+         seq = pool_tmpappend(pool, seq, "-", pool_lookup_str(pool, SOLVID_POS, DELTA_SEQ_NUM));
+         if (strchr(seq, '\'') != 0)
+           continue;
+#ifdef FEDORA
+         sprintf(cmd, "/usr/bin/applydeltarpm -a '%s' -c -s '", archstr);
+#else
+         sprintf(cmd, "/usr/bin/applydeltarpm -c -s '");
+#endif
+         if (system(pool_tmpjoin(pool, cmd, seq, "'")) != 0)
+           continue;   /* didn't match */
+         /* looks good, download delta */
+         chksumtype = 0;
+         chksum = pool_lookup_bin_checksum(pool, SOLVID_POS, DELTA_CHECKSUM, &chksumtype);
+         if (!chksumtype)
+           continue;   /* no way! */
+         dloc = pool_lookup_deltalocation(pool, SOLVID_POS, 0);
+         if (!dloc)
+           continue;
+#ifdef ENABLE_SUSEREPO
+         if (cinfo->type == TYPE_SUSETAGS)
+           {
+             const char *datadir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DATADIR);
+             dloc = pool_tmpjoin(pool, datadir ? datadir : "suse", "/", dloc);
+           }
+#endif
+         if ((fp = curlfopen(cinfo, dloc, 0, chksum, chksumtype, 0)) == 0)
+           continue;
+         /* got it, now reconstruct */
+         newfd = opentmpfile();
+#ifdef FEDORA
+         sprintf(cmd, "applydeltarpm -a '%s' /dev/fd/%d /dev/fd/%d", archstr, fileno(fp), newfd);
+#else
+         sprintf(cmd, "applydeltarpm /dev/fd/%d /dev/fd/%d", fileno(fp), newfd);
+#endif
+         fcntl(fileno(fp), F_SETFD, 0);
+         if (system(cmd))
+           {
+             close(newfd);
+             fclose(fp);
+             continue;
+           }
+         lseek(newfd, 0, SEEK_SET);
+         chksumtype = 0;
+         chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
+         if (chksumtype && !verify_checksum(newfd, loc, chksum, chksumtype))
+           {
+             close(newfd);
+             fclose(fp);
+             continue;
+           }
+         retfp = fdopen(newfd, "r");
+         fclose(fp);
+         break;
+       }
+    }
+  dataiterator_free(&di);
+  solv_free(matchname);
+  return retfp;
+}
diff --git a/examples/solv/deltarpm.h b/examples/solv/deltarpm.h
new file mode 100644 (file)
index 0000000..528e5a2
--- /dev/null
@@ -0,0 +1 @@
+extern FILE *trydeltadownload(Solvable *s, const char *loc);
diff --git a/examples/solv/fastestmirror.c b/examples/solv/fastestmirror.c
new file mode 100644 (file)
index 0000000..d2ebd97
--- /dev/null
@@ -0,0 +1,149 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <poll.h>
+#include <errno.h>
+
+#include "util.h"
+
+#include "fastestmirror.h"
+
+void
+findfastest(char **urls, int nurls)
+{
+  int i, j, port;
+  int *socks, qc;
+  struct pollfd *fds;
+  char *p, *p2, *q;
+  char portstr[16];
+  struct addrinfo hints, *result;;
+
+  fds = solv_calloc(nurls, sizeof(*fds));
+  socks = solv_calloc(nurls, sizeof(*socks));
+  for (i = 0; i < nurls; i++)
+    {
+      socks[i] = -1;
+      p = strchr(urls[i], '/');
+      if (!p)
+       continue;
+      if (p[1] != '/')
+       continue;
+      p += 2;
+      q = strchr(p, '/');
+      qc = 0;
+      if (q)
+       {
+         qc = *q;
+         *q = 0;
+       }
+      if ((p2 = strchr(p, '@')) != 0)
+       p = p2 + 1;
+      port = 80;
+      if (!strncmp("https:", urls[i], 6))
+       port = 443;
+      else if (!strncmp("ftp:", urls[i], 4))
+       port = 21;
+      if ((p2 = strrchr(p, ':')) != 0)
+       {
+         port = atoi(p2 + 1);
+         if (q)
+           *q = qc;
+         q = p2;
+         qc = *q;
+         *q = 0;
+       }
+      sprintf(portstr, "%d", port);
+      memset(&hints, 0, sizeof(struct addrinfo));
+      hints.ai_family = AF_UNSPEC;
+      hints.ai_socktype = SOCK_STREAM;
+      hints.ai_flags = AI_NUMERICSERV;
+      result = 0;
+      if (!getaddrinfo(p, portstr, &hints, &result))
+       {
+         socks[i] = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
+         if (socks[i] >= 0)
+           {
+             fcntl(socks[i], F_SETFL, O_NONBLOCK);
+             if (connect(socks[i], result->ai_addr, result->ai_addrlen) == -1)
+               {
+                 if (errno != EINPROGRESS)
+                   {
+                     close(socks[i]);
+                     socks[i] = -1;
+                   }
+               }
+           }
+         freeaddrinfo(result);
+       }
+      if (q)
+       *q = qc;
+    }
+  for (;;)
+    {
+      for (i = j = 0; i < nurls; i++)
+       {
+         if (socks[i] < 0)
+           continue;
+         fds[j].fd = socks[i];
+         fds[j].events = POLLOUT;
+         j++;
+       }
+      if (j < 2)
+       {
+         i = j - 1;
+         break;
+       }
+      if (poll(fds, j, 10000) <= 0)
+       {
+         i = -1;       /* something is wrong */
+         break;
+       }
+      for (i = 0; i < j; i++)
+       if ((fds[i].revents & POLLOUT) != 0)
+         {
+           int soe = 0;
+           socklen_t soel = sizeof(int);
+           if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &soe, &soel) == -1 || soe != 0)
+             {
+               /* connect failed, kill socket */
+               for (j = 0; j < nurls; j++)
+                 if (socks[j] == fds[i].fd)
+                   {
+                     close(socks[j]);
+                     socks[j] = -1;
+                   }
+               i = j + 1;
+               break;
+             }
+           break;      /* horray! */
+         }
+      if (i == j + 1)
+       continue;
+      if (i == j)
+        i = -1;                /* something is wrong, no bit was set */
+      break;
+    }
+  /* now i contains the fastest fd index */
+  if (i >= 0)
+    {
+      for (j = 0; j < nurls; j++)
+       if (socks[j] == fds[i].fd)
+         break;
+      if (j != 0)
+       {
+         char *url0 = urls[0];
+         urls[0] = urls[j];
+         urls[j] = url0;
+       }
+    }
+  for (i = j = 0; i < nurls; i++)
+    if (socks[i] >= 0)
+      close(socks[i]);
+  free(socks);
+  free(fds);
+}
diff --git a/examples/solv/fastestmirror.h b/examples/solv/fastestmirror.h
new file mode 100644 (file)
index 0000000..53251ef
--- /dev/null
@@ -0,0 +1,2 @@
+extern void findfastest(char **urls, int nurls);
+
diff --git a/examples/solv/fileconflicts.c b/examples/solv/fileconflicts.c
new file mode 100644 (file)
index 0000000..982de85
--- /dev/null
@@ -0,0 +1,76 @@
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+#include "pool_fileconflicts.h"
+
+#include "fileconflicts.h"
+
+struct fcstate {
+  FILE **newpkgsfps;
+  Queue *checkq;
+  int newpkgscnt;
+  void *rpmstate;
+};
+
+static void *
+fileconflict_cb(Pool *pool, Id p, void *cbdata)
+{
+  struct fcstate *fcstate = cbdata;
+  Solvable *s;
+  Id rpmdbid;
+  int i;
+  FILE *fp; 
+
+  s = pool_id2solvable(pool, p);
+  if (pool->installed && s->repo == pool->installed)
+    {    
+      if (!s->repo->rpmdbid)
+        return 0;
+      rpmdbid = s->repo->rpmdbid[p - s->repo->start];
+      if (!rpmdbid)
+        return 0;
+      return rpm_byrpmdbid(fcstate->rpmstate, rpmdbid);
+    }    
+  for (i = 0; i < fcstate->newpkgscnt; i++) 
+    if (fcstate->checkq->elements[i] == p)
+      break;
+  if (i == fcstate->newpkgscnt)
+    return 0;
+  fp = fcstate->newpkgsfps[i];
+  if (!fp)
+    return 0;
+  rewind(fp);
+  return rpm_byfp(fcstate->rpmstate, fp, pool_solvable2str(pool, s)); 
+}
+
+int
+checkfileconflicts(Pool *pool, Queue *checkq, int newpkgs, FILE **newpkgsfps, Queue *conflicts)
+{
+  struct fcstate fcstate;
+  int i;
+
+  printf("Searching for file conflicts\n");
+  queue_init(conflicts);
+  fcstate.rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
+  fcstate.newpkgscnt = newpkgs;
+  fcstate.checkq = checkq;
+  fcstate.newpkgsfps = newpkgsfps;
+  pool_findfileconflicts(pool, checkq, newpkgs, conflicts, FINDFILECONFLICTS_USE_SOLVABLEFILELIST | FINDFILECONFLICTS_CHECK_DIRALIASING | FINDFILECONFLICTS_USE_ROOTDIR, &fileconflict_cb, &fcstate);
+  fcstate.rpmstate = rpm_state_free(fcstate.rpmstate);
+  if (conflicts->count)
+    {
+      printf("\n");
+      for (i = 0; i < conflicts->count; i += 6)
+       printf("file %s of package %s conflicts with package %s\n", pool_id2str(pool, conflicts->elements[i]), pool_solvid2str(pool, conflicts->elements[i + 1]), pool_solvid2str(pool, conflicts->elements[i + 4]));
+      printf("\n");
+    }
+  return conflicts->count;
+}
+
+#endif
diff --git a/examples/solv/fileconflicts.h b/examples/solv/fileconflicts.h
new file mode 100644 (file)
index 0000000..e3f31e7
--- /dev/null
@@ -0,0 +1,2 @@
+extern int checkfileconflicts(Pool *pool, Queue *checkq, int newpkgs, FILE **newpkgsfps, Queue *conflicts);
+
diff --git a/examples/solv/fileprovides.c b/examples/solv/fileprovides.c
new file mode 100644 (file)
index 0000000..2654ab6
--- /dev/null
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+
+#include "fileprovides.h"
+
+static void
+rewrite_repos(Pool *pool, Queue *addedfileprovides, Queue *addedfileprovides_inst)
+{
+  Repo *repo;
+  Repodata *data;
+  Map providedids;
+  Queue fileprovidesq;
+  int i, j, n;
+  struct repoinfo *cinfo;
+
+  map_init(&providedids, pool->ss.nstrings);
+  queue_init(&fileprovidesq);
+  for (i = 0; i < addedfileprovides->count; i++)
+    MAPSET(&providedids, addedfileprovides->elements[i]);
+  FOR_REPOS(i, repo)
+    {
+      /* make sure all repodatas but the first are extensions */
+      if (repo->nrepodata < 2)
+       continue;
+      cinfo = repo->appdata;
+      if (!cinfo)
+       continue;       /* cmdline */
+      if (cinfo->incomplete)
+       continue;
+      data = repo_id2repodata(repo, 1);
+      if (data->loadcallback)
+        continue;
+      for (j = 2; j < repo->nrepodata; j++)
+       {
+         Repodata *edata = repo_id2repodata(repo, j);
+         if (!edata->loadcallback)
+           break;
+       }
+      if (j < repo->nrepodata)
+       continue;       /* found a non-extension repodata, can't rewrite  */
+      if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
+       {
+         if (repo == pool->installed && addedfileprovides_inst)
+           {
+             for (j = 0; j < addedfileprovides->count; j++)
+               MAPCLR(&providedids, addedfileprovides->elements[j]);
+             for (j = 0; j < addedfileprovides_inst->count; j++)
+               MAPSET(&providedids, addedfileprovides_inst->elements[j]);
+           }
+         n = 0;
+         for (j = 0; j < fileprovidesq.count; j++)
+           if (MAPTST(&providedids, fileprovidesq.elements[j]))
+             n++;
+         if (repo == pool->installed && addedfileprovides_inst)
+           {
+             for (j = 0; j < addedfileprovides_inst->count; j++)
+               MAPCLR(&providedids, addedfileprovides_inst->elements[j]);
+             for (j = 0; j < addedfileprovides->count; j++)
+               MAPSET(&providedids, addedfileprovides->elements[j]);
+             if (n == addedfileprovides_inst->count)
+               continue;       /* nothing new added */
+           }
+         else if (n == addedfileprovides->count)
+           continue;   /* nothing new added */
+       }
+      repodata_set_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, repo == pool->installed && addedfileprovides_inst ? addedfileprovides_inst : addedfileprovides);
+      repodata_internalize(data);
+      writecachedrepo(cinfo, 0, data);
+    }
+  queue_free(&fileprovidesq);
+  map_free(&providedids);
+}
+
+void
+addfileprovides(Pool *pool)
+{
+  Queue addedfileprovides;
+  Queue addedfileprovides_inst;
+
+  queue_init(&addedfileprovides);
+  queue_init(&addedfileprovides_inst);
+  pool_addfileprovides_queue(pool, &addedfileprovides, &addedfileprovides_inst);
+  if (addedfileprovides.count || addedfileprovides_inst.count)
+    rewrite_repos(pool, &addedfileprovides, &addedfileprovides_inst);
+  queue_free(&addedfileprovides);
+  queue_free(&addedfileprovides_inst);
+}
diff --git a/examples/solv/fileprovides.h b/examples/solv/fileprovides.h
new file mode 100644 (file)
index 0000000..1069d5a
--- /dev/null
@@ -0,0 +1 @@
+void addfileprovides(Pool *pool);
diff --git a/examples/solv/mirror.c b/examples/solv/mirror.c
new file mode 100644 (file)
index 0000000..52dc5ef
--- /dev/null
@@ -0,0 +1,110 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "pool.h"
+#include "util.h"
+#include "fastestmirror.h"
+
+#include "mirror.h"
+
+char *
+findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep)
+{
+  char buf[4096], *bp, *ep;
+  char **urls = 0;
+  int nurls = 0;
+  int i;
+
+  if (chksumtypep)
+    *chksumtypep = 0;
+  while((bp = fgets(buf, sizeof(buf), fp)) != 0)
+    {
+      while (*bp == ' ' || *bp == '\t')
+       bp++;
+      if (chksumtypep && !*chksumtypep && !strncmp(bp, "<hash type=\"sha256\">", 20))
+       {
+         bp += 20;
+         if (solv_hex2bin((const char **)&bp, chksump, 32) == 32)
+           *chksumtypep = REPOKEY_TYPE_SHA256;
+         continue;
+       }
+      if (strncmp(bp, "<url", 4))
+       continue;
+      bp = strchr(bp, '>');
+      if (!bp)
+       continue;
+      bp++;
+      ep = strstr(bp, "repodata/repomd.xml</url>");
+      if (!ep)
+       continue;
+      *ep = 0;
+      if (strncmp(bp, "http", 4))
+       continue;
+      urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
+      urls[nurls++] = strdup(bp);
+    }
+  if (nurls)
+    {
+      if (nurls > 1)
+        findfastest(urls, nurls > 5 ? 5 : nurls);
+      bp = urls[0];
+      urls[0] = 0;
+      for (i = 0; i < nurls; i++)
+        solv_free(urls[i]);
+      solv_free(urls);
+      ep = strchr(bp, '/');
+      if ((ep = strchr(ep + 2, '/')) != 0)
+       {
+         *ep = 0;
+         printf("[using mirror %s]\n", bp);
+         *ep = '/';
+       }
+      return bp;
+    }
+  return 0;
+}
+
+char *
+findmirrorlisturl(FILE *fp)
+{
+  char buf[4096], *bp, *ep;
+  int i, l;
+  char **urls = 0;
+  int nurls = 0;
+
+  while((bp = fgets(buf, sizeof(buf), fp)) != 0)
+    {
+      while (*bp == ' ' || *bp == '\t')
+       bp++;
+      if (!*bp || *bp == '#')
+       continue;
+      l = strlen(bp);
+      while (l > 0 && (bp[l - 1] == ' ' || bp[l - 1] == '\t' || bp[l - 1] == '\n'))
+       bp[--l] = 0;
+      if ((ep = strstr(bp, "url=")) != 0)
+       bp = ep + 4;
+      urls = solv_extend(urls, nurls, 1, sizeof(*urls), 15);
+      urls[nurls++] = strdup(bp);
+    }
+  if (nurls)
+    {
+      if (nurls > 1)
+        findfastest(urls, nurls > 5 ? 5 : nurls);
+      bp = urls[0];
+      urls[0] = 0;
+      for (i = 0; i < nurls; i++)
+        solv_free(urls[i]);
+      solv_free(urls);
+      ep = strchr(bp, '/');
+      if ((ep = strchr(ep + 2, '/')) != 0)
+       {
+         *ep = 0;
+         printf("[using mirror %s]\n", bp);
+         *ep = '/';
+       }
+      return bp;
+    }
+  return 0;
+}
diff --git a/examples/solv/mirror.h b/examples/solv/mirror.h
new file mode 100644 (file)
index 0000000..ed85a39
--- /dev/null
@@ -0,0 +1,2 @@
+char *findmetalinkurl(FILE *fp, unsigned char *chksump, Id *chksumtypep);
+char *findmirrorlisturl(FILE *fp);
diff --git a/examples/solv/patchjobs.c b/examples/solv/patchjobs.c
new file mode 100644 (file)
index 0000000..64ad607
--- /dev/null
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "evr.h"
+#include "solver.h"
+
+void
+add_patchjobs(Pool *pool, Queue *job)
+{
+  Id p, pp;
+  int pruneyou = 0;
+  Map installedmap, multiversionmap;
+  Solvable *s;
+
+  map_init(&multiversionmap, 0);
+  map_init(&installedmap, pool->nsolvables);
+  solver_calculate_multiversionmap(pool, job, &multiversionmap);
+  if (pool->installed)
+    FOR_REPO_SOLVABLES(pool->installed, p, s)
+      MAPSET(&installedmap, p);
+
+  /* install all patches */
+  for (p = 1; p < pool->nsolvables; p++)
+    {
+      const char *type;
+      int r;
+      Id p2;
+
+      s = pool->solvables + p;
+      if (strncmp(pool_id2str(pool, s->name), "patch:", 6) != 0)
+       continue;
+      FOR_PROVIDES(p2, pp, s->name)
+       {
+         Solvable *s2 = pool->solvables + p2;
+         if (s2->name != s->name)
+           continue;
+         r = pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE);
+         if (r < 0 || (r == 0 && p > p2))
+           break;
+       }
+      if (p2)
+       continue;
+      type = solvable_lookup_str(s, SOLVABLE_PATCHCATEGORY);
+      if (type && !strcmp(type, "optional"))
+       continue;
+      r = solvable_trivial_installable_map(s, &installedmap, 0, &multiversionmap);
+      if (r == -1)
+       continue;
+      if (solvable_lookup_bool(s, UPDATE_RESTART) && r == 0)
+       {
+         if (!pruneyou++)
+           queue_empty(job);
+       }
+      else if (pruneyou)
+       continue;
+      queue_push2(job, SOLVER_SOLVABLE, p);
+    }
+  map_free(&installedmap);
+  map_free(&multiversionmap);
+}
diff --git a/examples/solv/patchjobs.h b/examples/solv/patchjobs.h
new file mode 100644 (file)
index 0000000..52edcbe
--- /dev/null
@@ -0,0 +1,2 @@
+extern void add_patchjobs(Pool *pool, Queue *job);
+
diff --git a/examples/solv/repoinfo.c b/examples/solv/repoinfo.c
new file mode 100644 (file)
index 0000000..e08d160
--- /dev/null
@@ -0,0 +1,275 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+#include "repo_rpmdb.h"
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+#include "repo_deb.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+
+#if defined(SUSE) || defined(FEDORA)
+#include "repoinfo_config_yum.h"
+#endif
+#if defined(DEBIAN)
+#include "repoinfo_config_debian.h"
+#endif
+#if defined(MANDRIVA) || defined(MAGEIA)
+#include "repoinfo_config_urpmi.h"
+#endif
+
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+#include "repoinfo_system_rpm.h"
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+#include "repoinfo_system_debian.h"
+#endif
+
+#ifdef ENABLE_RPMMD
+#include "repoinfo_type_rpmmd.h"
+#endif
+#ifdef ENABLE_SUSEREPO
+#include "repoinfo_type_susetags.h"
+#endif
+#ifdef ENABLE_DEBIAN
+#include "repoinfo_type_debian.h"
+#endif
+#ifdef ENABLE_MDKREPO
+#include "repoinfo_type_mdk.h"
+#endif
+
+static int
+repoinfos_sort_cmp(const void *ap, const void *bp)
+{
+  const struct repoinfo *a = ap;
+  const struct repoinfo *b = bp;
+  return strcmp(a->alias, b->alias);
+}
+
+void
+sort_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
+{
+  qsort(repoinfos, nrepoinfos, sizeof(*repoinfos), repoinfos_sort_cmp);
+}
+
+void
+free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos)
+{
+  int i, j;
+  for (i = 0; i < nrepoinfos; i++)
+    {
+      struct repoinfo *cinfo = repoinfos + i;
+      solv_free(cinfo->name);
+      solv_free(cinfo->alias);
+      solv_free(cinfo->path);
+      solv_free(cinfo->metalink);
+      solv_free(cinfo->mirrorlist);
+      solv_free(cinfo->baseurl);
+      for (j = 0; j < cinfo->ncomponents; j++)
+        solv_free(cinfo->components[j]);
+      solv_free(cinfo->components);
+    }
+  solv_free(repoinfos);
+#if defined(SUSE) || defined(FEDORA)
+  yum_substitute((Pool *)0, 0);                /* free data */
+#endif
+}
+
+struct repoinfo *
+read_repoinfos(Pool *pool, int *nrepoinfosp)
+{
+  struct repoinfo *repoinfos = 0;
+#if defined(SUSE) || defined(FEDORA)
+  repoinfos = read_repoinfos_yum(pool, nrepoinfosp);
+#endif
+#if defined(MANDRIVA) || defined(MAGEIA)
+  repoinfos = read_repoinfos_urpmi(pool, nrepoinfosp);
+#endif
+#if defined(DEBIAN)
+  repoinfos = read_repoinfos_debian(pool, nrepoinfosp);
+#endif
+  return repoinfos;
+}
+
+int
+read_installed_repo(struct repoinfo *cinfo, Pool *pool)
+{
+  int r = 1;
+  cinfo->type = TYPE_INSTALLED;
+  cinfo->repo = repo_create(pool, "@System");
+  cinfo->repo->appdata = cinfo;
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+  r = read_installed_rpm(cinfo);
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+  r = read_installed_debian(cinfo);
+#endif
+  pool_set_installed(pool, cinfo->repo);
+  return r;
+}
+
+int
+is_cmdline_package(const char *filename)
+{
+  int l = strlen(filename);
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+  if (l > 4 && !strcmp(filename + l - 4, ".rpm"))
+    return 1;
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+  if (l > 4 && !strcmp(filename + l - 4, ".deb"))
+    return 1;
+#endif
+  return 0;
+}
+
+Id
+add_cmdline_package(Repo *repo, const char *filename)
+{
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+  return repo_add_rpm(repo, filename, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+  return repo_add_deb(repo, filename, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE);
+#endif
+  return 0;
+}
+
+void
+commit_transactionelement(Pool *pool, Id type, Id p, FILE *fp)
+{
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+  commit_transactionelement_rpm(pool, type, p, fp);
+#endif
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+  commit_transactionelement_debian(pool, type, p, fp);
+#endif
+}
+
+void
+add_ext_keys(Repodata *data, Id handle, const char *ext)
+{
+  static Id langtags[] = {
+    SOLVABLE_SUMMARY,     REPOKEY_TYPE_STR,
+    SOLVABLE_DESCRIPTION, REPOKEY_TYPE_STR,
+    SOLVABLE_EULA,        REPOKEY_TYPE_STR,
+    SOLVABLE_MESSAGEINS,  REPOKEY_TYPE_STR,
+    SOLVABLE_MESSAGEDEL,  REPOKEY_TYPE_STR,
+    SOLVABLE_CATEGORY,    REPOKEY_TYPE_ID,
+    0, 0
+  };
+  if (!strcmp(ext, "DL"))
+    {
+      repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOSITORY_DELTAINFO);
+      repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_FLEXARRAY);
+    }
+  else if (!strcmp(ext, "FL"))
+    {
+      repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_FILELIST);
+      repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRSTRARRAY);
+    }
+  else if (!strcmp(ext, "DU"))
+    {
+      repodata_add_idarray(data, handle, REPOSITORY_KEYS, SOLVABLE_DISKUSAGE);
+      repodata_add_idarray(data, handle, REPOSITORY_KEYS, REPOKEY_TYPE_DIRNUMNUMARRAY);
+    }
+  else
+    {
+      Pool *pool = data->repo->pool;
+      int i;
+      for (i = 0; langtags[i]; i += 2)
+       {
+         repodata_add_idarray(data, handle, REPOSITORY_KEYS, pool_id2langid(pool, langtags[i], ext, 1));
+         repodata_add_idarray(data, handle, REPOSITORY_KEYS, langtags[i + 1]);
+       }
+    }
+}
+
+int
+load_stub(Pool *pool, Repodata *data, void *dp)
+{
+  struct repoinfo *cinfo = data->repo->appdata;
+  switch (cinfo->type)
+    {
+#ifdef ENABLE_SUSEREPO
+    case TYPE_SUSETAGS:
+      return susetags_load_ext(data->repo, data);
+#endif
+#ifdef ENABLE_RPMMD
+    case TYPE_RPMMD:
+      return repomd_load_ext(data->repo, data);
+#endif
+#ifdef ENABLE_MDKREPO
+    case TYPE_MDK:
+      return mdk_load_ext(data->repo, data);
+#endif
+    default:
+      /* debian does not have any ext data yet */
+      return 0;
+    }
+}
+
+void
+read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
+{
+  Repo *repo;
+  int i;
+  Pool *sigpool = 0;
+
+  for (i = 0; i < nrepoinfos; i++)
+    {
+      struct repoinfo *cinfo = repoinfos + i;
+      if (!cinfo->enabled)
+       continue;
+
+      repo = repo_create(pool, cinfo->alias);
+      cinfo->repo = repo;
+      repo->appdata = cinfo;
+      repo->priority = 99 - cinfo->priority;
+
+      if ((!cinfo->autorefresh || cinfo->metadata_expire) && usecachedrepo(cinfo, 0, 0))
+       {
+         printf("repo '%s':", cinfo->alias);
+         printf(" cached\n");
+         continue;
+       }
+
+      switch (cinfo->type)
+       {
+#ifdef ENABLE_RPMMD
+        case TYPE_RPMMD:
+         repomd_load(cinfo, &sigpool);
+         break;
+#endif
+#ifdef ENABLE_SUSEREPO
+        case TYPE_SUSETAGS:
+         susetags_load(cinfo, &sigpool);
+         break;
+#endif
+#ifdef ENABLE_DEBIAN
+        case TYPE_DEBIAN:
+         debian_load(cinfo, &sigpool);
+         break;
+#endif
+#ifdef ENABLE_MDKREPO
+        case TYPE_MDK:
+         mdk_load(cinfo, &sigpool);
+         break;
+#endif
+       default:
+         printf("unsupported repo '%s': skipped\n", cinfo->alias);
+         repo_free(repo, 1);
+         cinfo->repo = 0;
+         break;
+       }
+    }
+  if (sigpool)
+    pool_free(sigpool);
+}
+
diff --git a/examples/solv/repoinfo.h b/examples/solv/repoinfo.h
new file mode 100644 (file)
index 0000000..04f94b7
--- /dev/null
@@ -0,0 +1,53 @@
+struct repoinfo {
+  Repo *repo;
+
+  int type;
+  char *alias;
+  char *name;
+  int enabled;
+  int autorefresh;
+  char *baseurl;
+  char *metalink;
+  char *mirrorlist;
+  char *path;
+  int pkgs_gpgcheck;
+  int repo_gpgcheck;
+  int priority;
+  int keeppackages;
+  int metadata_expire;
+  char **components;
+  int ncomponents;
+  int cookieset;
+  unsigned char cookie[32];
+  int extcookieset;
+  unsigned char extcookie[32];
+  int incomplete;
+};
+
+#define TYPE_UNKNOWN    0
+#define TYPE_SUSETAGS   1
+#define TYPE_RPMMD      2
+#define TYPE_PLAINDIR   3
+#define TYPE_DEBIAN     4
+#define TYPE_MDK        5
+
+#define TYPE_INSTALLED  16
+#define TYPE_CMDLINE    17
+
+#define METADATA_EXPIRE (60 * 15)
+
+extern void sort_repoinfos(struct repoinfo *repoinfos, int nrepoinfos);
+extern void free_repoinfos(struct repoinfo *repoinfos, int nrepoinfos);
+extern void read_repos(Pool *pool, struct repoinfo *repoinfos, int nrepoinfos);
+extern struct repoinfo *read_repoinfos(Pool *pool, int *nrepoinfosp);
+
+extern int read_installed_repo(struct repoinfo *cinfo, Pool *pool);
+
+extern int is_cmdline_package(const char *filename);
+extern Id add_cmdline_package(Repo *repo, const char *filename);
+
+extern void commit_transactionelement(Pool *pool, Id type, Id p, FILE *fp);
+
+extern void add_ext_keys(Repodata *data, Id handle, const char *ext);
+extern int load_stub(Pool *pool, Repodata *data, void *dp);
+
diff --git a/examples/solv/repoinfo_cache.c b/examples/solv/repoinfo_cache.c
new file mode 100644 (file)
index 0000000..ec4fe57
--- /dev/null
@@ -0,0 +1,290 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_solv.h"
+#include "repo_write.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+
+#define COOKIE_IDENT "1.1"
+
+#define SOLVCACHE_PATH "/var/cache/solv"
+
+static char *userhome;
+
+void
+set_userhome()
+{
+  userhome = getenv("HOME");
+  if (userhome && userhome[0] != '/') 
+    userhome = 0; 
+}
+
+void
+calc_cookie_fp(FILE *fp, Id chktype, unsigned char *out)
+{
+  char buf[4096];
+  Chksum *h = solv_chksum_create(chktype);
+  int l;
+
+  solv_chksum_add(h, COOKIE_IDENT, strlen(COOKIE_IDENT));
+  while ((l = fread(buf, 1, sizeof(buf), fp)) > 0)
+    solv_chksum_add(h, buf, l);
+  rewind(fp);
+  solv_chksum_free(h, out);
+}
+
+void
+calc_cookie_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out)
+{
+  Chksum *h = solv_chksum_create(chktype);
+  solv_chksum_add(h, COOKIE_IDENT, strlen(COOKIE_IDENT));
+  if (cookie)
+    solv_chksum_add(h, cookie, 32);
+  solv_chksum_add(h, &stb->st_dev, sizeof(stb->st_dev));
+  solv_chksum_add(h, &stb->st_ino, sizeof(stb->st_ino));
+  solv_chksum_add(h, &stb->st_size, sizeof(stb->st_size));
+  solv_chksum_add(h, &stb->st_mtime, sizeof(stb->st_mtime));
+  solv_chksum_free(h, out);
+}
+
+char *
+calc_cachepath(Repo *repo, const char *repoext, int forcesystemloc)
+{
+  char *q, *p;
+  int l;
+  if (!forcesystemloc && userhome && getuid())
+    p = pool_tmpjoin(repo->pool, userhome, "/.solvcache/", 0);
+  else
+    p = pool_tmpjoin(repo->pool, SOLVCACHE_PATH, "/", 0);
+  l = strlen(p);
+  p = pool_tmpappend(repo->pool, p, repo->name, 0);
+  if (repoext)
+    {
+      p = pool_tmpappend(repo->pool, p, "_", repoext);
+      p = pool_tmpappend(repo->pool, p, ".solvx", 0);
+    }
+  else
+    p = pool_tmpappend(repo->pool, p, ".solv", 0);
+  q = p + l;
+  if (*q == '.')
+    *q = '_';
+  for (; *q; q++)
+    if (*q == '/')
+      *q = '_';
+  return p;
+}
+
+int
+usecachedrepo(struct repoinfo *cinfo, const char *repoext, int mark)
+{
+  Repo *repo = cinfo->repo;
+  FILE *fp;
+  unsigned char *cookie = repoext ? cinfo->extcookie : (cinfo->cookieset ? cinfo->cookie : 0);
+  unsigned char mycookie[32];
+  unsigned char myextcookie[32];
+  int flags;
+  int forcesystemloc;
+
+  if (repoext && !cinfo->extcookieset)
+    return 0;  /* huh? */
+  forcesystemloc = mark & 2 ? 0 : 1;
+  if (mark < 2 && userhome && getuid())
+    {
+      /* first try home location */
+      int res = usecachedrepo(cinfo, repoext, mark | 2);
+      if (res)
+       return res;
+    }
+  mark &= 1;
+  if (!(fp = fopen(calc_cachepath(repo, repoext, forcesystemloc), "r")))
+    return 0;
+  if (!repoext && !cinfo->cookieset && cinfo->autorefresh && cinfo->metadata_expire != -1)
+    {
+      struct stat stb;         /* no cookie set yet, check cache expiry time */
+      if (fstat(fileno(fp), &stb) || time(0) - stb.st_mtime >= cinfo->metadata_expire)
+       {
+         fclose(fp);
+         return 0;
+       }
+    }
+  if (fseek(fp, -sizeof(mycookie), SEEK_END) || fread(mycookie, sizeof(mycookie), 1, fp) != 1)
+    {
+      fclose(fp);
+      return 0;
+    }
+  if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)) != 0)
+    {
+      fclose(fp);
+      return 0;
+    }
+  if (cinfo->type != TYPE_INSTALLED && !repoext)
+    {
+      if (fseek(fp, -sizeof(mycookie) * 2, SEEK_END) || fread(myextcookie, sizeof(myextcookie), 1, fp) != 1)
+       {
+         fclose(fp);
+         return 0;
+       }
+    }
+  rewind(fp);
+
+  flags = 0;
+  if (repoext)
+    {
+      flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
+      if (strcmp(repoext, "DL") != 0)
+        flags |= REPO_LOCALPOOL;       /* no local pool for DL so that we can compare IDs */
+    }
+  if (repo_add_solv(repo, fp, flags))
+    {
+      fclose(fp);
+      return 0;
+    }
+  if (cinfo->type != TYPE_INSTALLED && !repoext)
+    {
+      memcpy(cinfo->cookie, mycookie, sizeof(mycookie));
+      cinfo->cookieset = 1;
+      memcpy(cinfo->extcookie, myextcookie, sizeof(myextcookie));
+      cinfo->extcookieset = 1;
+    }
+  if (mark)
+    futimens(fileno(fp), 0);   /* try to set modification time */
+  fclose(fp);
+  return 1;
+}
+
+static void
+switchtowritten(struct repoinfo *cinfo, const char *repoext, Repodata *repodata, char *tmpl)
+{
+  Repo *repo = cinfo->repo;
+  FILE *fp;
+  int i;
+
+  if (!repoext && repodata)
+    return;    /* rewrite case, don't bother for the added fileprovides */
+  for (i = repo->start; i < repo->end; i++)
+   if (repo->pool->solvables[i].repo != repo)
+     break;
+  if (i < repo->end)
+    return;    /* not a simple block */
+      /* switch to just saved repo to activate paging and save memory */
+  fp = fopen(tmpl, "r");
+  if (!fp)
+    return;
+  if (!repoext)
+    {
+      /* main repo */
+      repo_empty(repo, 1);
+      if (repo_add_solv(repo, fp, SOLV_ADD_NO_STUBS))
+       {
+         /* oops, no way to recover from here */
+         fprintf(stderr, "internal error\n");
+         exit(1);
+       }
+    }
+  else
+    {
+      int flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
+      /* make sure repodata contains complete repo */
+      /* (this is how repodata_write saves it) */
+      repodata_extend_block(repodata, repo->start, repo->end - repo->start);
+      repodata->state = REPODATA_LOADING;
+      if (strcmp(repoext, "DL") != 0)
+       flags |= REPO_LOCALPOOL;
+      repo_add_solv(repo, fp, flags);
+      repodata->state = REPODATA_AVAILABLE;    /* in case the load failed */
+    }
+  fclose(fp);
+}
+
+void
+writecachedrepo(struct repoinfo *cinfo, const char *repoext, Repodata *repodata)
+{
+  Repo *repo = cinfo->repo;
+  FILE *fp;
+  int fd;
+  char *tmpl, *cachedir;
+
+  if (cinfo->incomplete || (repoext && !cinfo->extcookieset) || (!repoext && !cinfo->cookieset))
+    return;
+  cachedir = userhome && getuid() ? pool_tmpjoin(repo->pool, userhome, "/.solvcache", 0) : SOLVCACHE_PATH;
+  if (access(cachedir, W_OK | X_OK) != 0 && mkdir(cachedir, 0755) == 0)
+    printf("[created %s]\n", cachedir);
+  /* use dupjoin instead of tmpjoin because tmpl must survive repo_write */
+  tmpl = solv_dupjoin(cachedir, "/", ".newsolv-XXXXXX");
+  fd = mkstemp(tmpl);
+  if (fd < 0)
+    {
+      free(tmpl);
+      return;
+    }
+  fchmod(fd, 0444);
+  if (!(fp = fdopen(fd, "w")))
+    {
+      close(fd);
+      unlink(tmpl);
+      free(tmpl);
+      return;
+    }
+
+  if (!repodata)
+    repo_write(repo, fp);
+  else if (repoext)
+    repodata_write(repodata, fp);
+  else
+    {
+      int oldnrepodata = repo->nrepodata;
+      repo->nrepodata = oldnrepodata > 2 ? 2 : oldnrepodata;   /* XXX: do this right */
+      repo_write(repo, fp);
+      repo->nrepodata = oldnrepodata;
+    }
+
+  if (!repoext && cinfo->type != TYPE_INSTALLED)
+    {
+      if (!cinfo->extcookieset)
+       {
+         /* create the ext cookie and append it */
+         /* we just need some unique ID */
+         struct stat stb;
+         if (fstat(fileno(fp), &stb))
+           memset(&stb, 0, sizeof(stb));
+         calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, cinfo->cookie, cinfo->extcookie);
+         cinfo->extcookieset = 1;
+       }
+      if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
+       {
+         fclose(fp);
+         unlink(tmpl);
+         free(tmpl);
+         return;
+       }
+    }
+  /* append our cookie describing the metadata state */
+  if (fwrite(repoext ? cinfo->extcookie : cinfo->cookie, 32, 1, fp) != 1)
+    {
+      fclose(fp);
+      unlink(tmpl);
+      free(tmpl);
+      return;
+    }
+  if (fclose(fp))
+    {
+      unlink(tmpl);
+      free(tmpl);
+      return;
+    }
+
+  switchtowritten(cinfo, repoext, repodata, tmpl);
+
+  if (!rename(tmpl, calc_cachepath(repo, repoext, 0)))
+    unlink(tmpl);
+  free(tmpl);
+}
+
diff --git a/examples/solv/repoinfo_cache.h b/examples/solv/repoinfo_cache.h
new file mode 100644 (file)
index 0000000..cc74715
--- /dev/null
@@ -0,0 +1,12 @@
+
+struct repoinfo;
+struct stat;
+
+extern void set_userhome(void);
+extern char *calc_cachepath(Repo *repo, const char *repoext, int forcesystemloc);
+extern void calc_cookie_fp(FILE *fp, Id chktype, unsigned char *out);
+extern void calc_cookie_stat(struct stat *stb, Id chktype, unsigned char *cookie, unsigned char *out);
+
+extern int usecachedrepo(struct repoinfo *cinfo, const char *repoext, int mark);
+extern void  writecachedrepo(struct repoinfo *cinfo, const char *repoext, Repodata *repodata);
+
diff --git a/examples/solv/repoinfo_config_debian.c b/examples/solv/repoinfo_config_debian.c
new file mode 100644 (file)
index 0000000..b369970
--- /dev/null
@@ -0,0 +1,127 @@
+#ifdef DEBIAN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#include "repoinfo.h"
+#include "repoinfo_config_debian.h"
+
+
+
+struct repoinfo *
+read_repoinfos_debian(Pool *pool, int *nrepoinfosp)
+{
+  FILE *fp;
+  char buf[4096];
+  char buf2[4096];
+  int l;
+  char *kp, *url, *distro;
+  struct repoinfo *repoinfos = 0, *cinfo;
+  int nrepoinfos = 0;
+  DIR *dir = 0;
+  struct dirent *ent;
+
+  fp = fopen("/etc/apt/sources.list", "r");
+  while (1)
+    {
+      if (!fp)
+       {
+         if (!dir)
+           {
+             dir = opendir("/etc/apt/sources.list.d");
+             if (!dir)
+               break;
+           }
+         if ((ent = readdir(dir)) == 0)
+           {
+             closedir(dir);
+             break;
+           }
+         if (ent->d_name[0] == '.')
+           continue;
+         l = strlen(ent->d_name);
+         if (l < 5 || strcmp(ent->d_name + l - 5, ".list") != 0)
+           continue;
+         snprintf(buf, sizeof(buf), "%s/%s", "/etc/apt/sources.list.d", ent->d_name);
+         if (!(fp = fopen(buf, "r")))
+           continue;
+       }
+      while(fgets(buf2, sizeof(buf2), fp))
+       {
+         l = strlen(buf2);
+         if (l == 0)
+           continue;
+         while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
+           buf2[--l] = 0;
+         kp = buf2;
+         while (*kp == ' ' || *kp == '\t')
+           kp++;
+         if (!*kp || *kp == '#')
+           continue;
+         if (strncmp(kp, "deb", 3) != 0)
+           continue;
+         kp += 3;
+         if (*kp != ' ' && *kp != '\t')
+           continue;
+         while (*kp == ' ' || *kp == '\t')
+           kp++;
+         if (!*kp)
+           continue;
+         url = kp;
+         while (*kp && *kp != ' ' && *kp != '\t')
+           kp++;
+         if (*kp)
+           *kp++ = 0;
+         while (*kp == ' ' || *kp == '\t')
+           kp++;
+         if (!*kp)
+           continue;
+         distro = kp;
+         while (*kp && *kp != ' ' && *kp != '\t')
+           kp++;
+         if (*kp)
+           *kp++ = 0;
+         while (*kp == ' ' || *kp == '\t')
+           kp++;
+         if (!*kp)
+           continue;
+         repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
+         cinfo = repoinfos + nrepoinfos++;
+         memset(cinfo, 0, sizeof(*cinfo));
+         cinfo->baseurl = strdup(url);
+         cinfo->alias = solv_dupjoin(url, "/", distro);
+         cinfo->name = strdup(distro);
+         cinfo->type = TYPE_DEBIAN;
+         cinfo->enabled = 1;
+         cinfo->autorefresh = 1;
+         cinfo->repo_gpgcheck = 1;
+         cinfo->metadata_expire = METADATA_EXPIRE;
+         while (*kp)
+           {
+             char *compo;
+             while (*kp == ' ' || *kp == '\t')
+               kp++;
+             if (!*kp)
+               break;
+             compo = kp;
+             while (*kp && *kp != ' ' && *kp != '\t')
+               kp++;
+             if (*kp)
+               *kp++ = 0;
+             cinfo->components = solv_extend(cinfo->components, cinfo->ncomponents, 1, sizeof(*cinfo->components), 15);
+             cinfo->components[cinfo->ncomponents++] = strdup(compo);
+           }
+       }
+      fclose(fp);
+      fp = 0;
+    }
+  *nrepoinfosp = nrepoinfos;
+  return repoinfos;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_config_debian.h b/examples/solv/repoinfo_config_debian.h
new file mode 100644 (file)
index 0000000..64b3eb6
--- /dev/null
@@ -0,0 +1 @@
+extern struct repoinfo *read_repoinfos_debian(Pool *pool, int *nrepoinfosp);
diff --git a/examples/solv/repoinfo_config_urpmi.c b/examples/solv/repoinfo_config_urpmi.c
new file mode 100644 (file)
index 0000000..a79ea9b
--- /dev/null
@@ -0,0 +1,106 @@
+#if defined(MANDRIVA) || defined(MAGEIA)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pool.h"
+#include "repo.h"
+
+#include "repoinfo.h"
+#include "repoinfo_config_urpmi.h"
+
+
+#define URPMI_CFG "/etc/urpmi/urpmi.cfg"
+
+
+struct repoinfo *
+read_repoinfos_urpmi(Pool *pool, int *nrepoinfosp)
+{
+  char buf[4096], *bp, *arg;
+  FILE *fp;
+  int l, insect = 0;
+  struct repoinfo *cinfo = 0;
+  struct repoinfo *repoinfos = 0;
+  int nrepoinfos = 0;
+
+  if ((fp = fopen(URPMI_CFG, "r")) == 0)
+    {
+      *nrepoinfosp = 0;
+      return 0;
+    }
+  while (fgets(buf, sizeof(buf), fp))
+    {
+      l = strlen(buf);
+      while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
+       buf[--l] = 0;
+      bp = buf;
+      while (l && (*bp == ' ' || *bp == '\t'))
+       {
+         l--;
+         bp++;
+       }
+      if (!l || *bp == '#')
+       continue;
+      if (!insect && bp[l - 1] == '{')
+       {
+         insect++;
+         bp[--l] = 0;
+         if (l > 0)
+           {
+             while (l && (bp[l - 1] == ' ' || bp[l - 1] == '\t'))
+               bp[--l] = 0;
+           }
+         if (l)
+           {
+             char *bbp = bp, *bbp2 = bp;
+             /* unescape */
+             while (*bbp)
+               {
+                 if (*bbp == '\\' && bbp[1])
+                   bbp++;
+                 *bbp2++ = *bbp++;
+               }
+             *bbp2 = 0;
+             repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
+             cinfo = repoinfos + nrepoinfos++;
+             memset(cinfo, 0, sizeof(*cinfo));
+             cinfo->alias = strdup(bp);
+             cinfo->type = TYPE_MDK;
+             cinfo->autorefresh = 1;
+             cinfo->priority = 99;
+             cinfo->enabled = 1;
+             cinfo->metadata_expire = METADATA_EXPIRE;
+           }
+         continue;
+       }
+      if (insect && *bp == '}')
+       {
+         insect--;
+         cinfo = 0;
+         continue;
+       }
+      if (!insect || !cinfo)
+       continue;
+      if ((arg = strchr(bp, ':')) != 0)
+       {
+         *arg++ = 0;
+         while (*arg == ' ' || *arg == '\t')
+           arg++;
+         if (!*arg)
+           arg = 0;
+       }
+      if (strcmp(bp, "ignore") == 0)
+       cinfo->enabled = 0;
+      if (strcmp(bp, "mirrorlist") == 0)
+       cinfo->mirrorlist = solv_strdup(arg);
+      if (strcmp(bp, "with-dir") == 0)
+       cinfo->path = solv_strdup(arg);
+    }
+  fclose(fp);
+  *nrepoinfosp = nrepoinfos;
+  return repoinfos;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_config_urpmi.h b/examples/solv/repoinfo_config_urpmi.h
new file mode 100644 (file)
index 0000000..bb7374f
--- /dev/null
@@ -0,0 +1,2 @@
+extern struct repoinfo *read_repoinfos_urpmi(Pool *pool, int *nrepoinfosp);
+
diff --git a/examples/solv/repoinfo_config_yum.c b/examples/solv/repoinfo_config_yum.c
new file mode 100644 (file)
index 0000000..6e2e66a
--- /dev/null
@@ -0,0 +1,233 @@
+#if defined(SUSE) || defined(FEDORA)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/utsname.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+
+#include "repoinfo.h"
+#include "repoinfo_config_yum.h"
+
+
+#ifdef FEDORA
+# define REPOINFO_PATH "/etc/yum.repos.d"
+#endif
+#ifdef SUSE
+# define REPOINFO_PATH "/etc/zypp/repos.d"
+#endif
+
+char *
+yum_substitute(Pool *pool, char *line)
+{
+  char *p, *p2;
+  static char *releaseevr;
+  static char *basearch;
+
+  if (!line)
+    {
+      solv_free(releaseevr);
+      releaseevr = 0;
+      solv_free(basearch);
+      basearch = 0;
+      return 0;
+    }
+  p = line;
+  while ((p2 = strchr(p, '$')) != 0)
+    {
+      if (!strncmp(p2, "$releasever", 11))
+       {
+         if (!releaseevr)
+           {
+             void *rpmstate;
+             Queue q;
+       
+             queue_init(&q);
+             rpmstate = rpm_state_create(pool, pool_get_rootdir(pool));
+             rpm_installedrpmdbids(rpmstate, "Providename", "redhat-release", &q);
+             if (q.count)
+               {
+                 void *handle;
+                 char *p;
+                 handle = rpm_byrpmdbid(rpmstate, q.elements[0]);
+                 releaseevr = handle ? rpm_query(handle, SOLVABLE_EVR) : 0;
+                 if (releaseevr && (p = strchr(releaseevr, '-')) != 0)
+                   *p = 0;
+               }
+             rpm_state_free(rpmstate);
+             queue_free(&q);
+             if (!releaseevr)
+               {
+                 fprintf(stderr, "no installed package provides 'redhat-release', cannot determine $releasever\n");
+                 exit(1);
+               }
+           }
+         *p2 = 0;
+         p = pool_tmpjoin(pool, line, releaseevr, p2 + 11);
+         p2 = p + (p2 - line);
+         line = p;
+         p = p2 + strlen(releaseevr);
+         continue;
+       }
+      if (!strncmp(p2, "$basearch", 9))
+       {
+         if (!basearch)
+           {
+             struct utsname un;
+             if (uname(&un))
+               {
+                 perror("uname");
+                 exit(1);
+               }
+             basearch = strdup(un.machine);
+             if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
+               basearch[1] = '3';
+           }
+         *p2 = 0;
+         p = pool_tmpjoin(pool, line, basearch, p2 + 9);
+         p2 = p + (p2 - line);
+         line = p;
+         p = p2 + strlen(basearch);
+         continue;
+       }
+      p = p2 + 1;
+    }
+  return line;
+}
+
+struct repoinfo *
+read_repoinfos_yum(Pool *pool, int *nrepoinfosp)
+{
+  const char *reposdir = REPOINFO_PATH;
+  char buf[4096];
+  char buf2[4096], *kp, *vp, *kpe;
+  DIR *dir;
+  FILE *fp;
+  struct dirent *ent;
+  int l, rdlen;
+  struct repoinfo *repoinfos = 0, *cinfo;
+  int nrepoinfos = 0;
+
+  rdlen = strlen(reposdir);
+  dir = opendir(reposdir);
+  if (!dir)
+    {
+      *nrepoinfosp = 0;
+      return 0;
+    }
+  while ((ent = readdir(dir)) != 0)
+    {
+      if (ent->d_name[0] == '.')
+       continue;
+      l = strlen(ent->d_name);
+      if (l < 6 || rdlen + 2 + l >= sizeof(buf) || strcmp(ent->d_name + l - 5, ".repo") != 0)
+       continue;
+      snprintf(buf, sizeof(buf), "%s/%s", reposdir, ent->d_name);
+      if ((fp = fopen(buf, "r")) == 0)
+       {
+         perror(buf);
+         continue;
+       }
+      cinfo = 0;
+      while(fgets(buf2, sizeof(buf2), fp))
+       {
+         l = strlen(buf2);
+         if (l == 0)
+           continue;
+         while (l && (buf2[l - 1] == '\n' || buf2[l - 1] == ' ' || buf2[l - 1] == '\t'))
+           buf2[--l] = 0;
+         kp = buf2;
+         while (*kp == ' ' || *kp == '\t')
+           kp++;
+         if (!*kp || *kp == '#')
+           continue;
+         if (strchr(kp, '$'))
+           kp = yum_substitute(pool, kp);
+         if (*kp == '[')
+           {
+             vp = strrchr(kp, ']');
+             if (!vp)
+               continue;
+             *vp = 0;
+             repoinfos = solv_extend(repoinfos, nrepoinfos, 1, sizeof(*repoinfos), 15);
+             cinfo = repoinfos + nrepoinfos++;
+             memset(cinfo, 0, sizeof(*cinfo));
+             cinfo->alias = strdup(kp + 1);
+             cinfo->type = TYPE_RPMMD;
+             cinfo->autorefresh = 1;
+             cinfo->priority = 99;
+#ifndef FEDORA
+             cinfo->repo_gpgcheck = 1;
+#endif
+             cinfo->metadata_expire = METADATA_EXPIRE;
+             continue;
+           }
+         if (!cinfo)
+           continue;
+          vp = strchr(kp, '=');
+         if (!vp)
+           continue;
+         for (kpe = vp - 1; kpe >= kp; kpe--)
+           if (*kpe != ' ' && *kpe != '\t')
+             break;
+         if (kpe == kp)
+           continue;
+         vp++;
+         while (*vp == ' ' || *vp == '\t')
+           vp++;
+         kpe[1] = 0;
+         if (!strcmp(kp, "name"))
+           cinfo->name = strdup(vp);
+         else if (!strcmp(kp, "enabled"))
+           cinfo->enabled = *vp == '0' ? 0 : 1;
+         else if (!strcmp(kp, "autorefresh"))
+           cinfo->autorefresh = *vp == '0' ? 0 : 1;
+         else if (!strcmp(kp, "gpgcheck"))
+           cinfo->pkgs_gpgcheck = *vp == '0' ? 0 : 1;
+         else if (!strcmp(kp, "repo_gpgcheck"))
+           cinfo->repo_gpgcheck = *vp == '0' ? 0 : 1;
+         else if (!strcmp(kp, "baseurl"))
+           cinfo->baseurl = strdup(vp);
+         else if (!strcmp(kp, "mirrorlist"))
+           {
+             if (strstr(vp, "metalink"))
+               cinfo->metalink = strdup(vp);
+             else
+               cinfo->mirrorlist = strdup(vp);
+           }
+         else if (!strcmp(kp, "path"))
+           {
+             if (vp && strcmp(vp, "/") != 0)
+               cinfo->path = strdup(vp);
+           }
+         else if (!strcmp(kp, "type"))
+           {
+             if (!strcmp(vp, "yast2"))
+               cinfo->type = TYPE_SUSETAGS;
+             else if (!strcmp(vp, "rpm-md"))
+               cinfo->type = TYPE_RPMMD;
+             else if (!strcmp(vp, "plaindir"))
+               cinfo->type = TYPE_PLAINDIR;
+             else if (!strcmp(vp, "mdk"))
+               cinfo->type = TYPE_MDK;
+             else
+               cinfo->type = TYPE_UNKNOWN;
+           }
+         else if (!strcmp(kp, "priority"))
+           cinfo->priority = atoi(vp);
+         else if (!strcmp(kp, "keeppackages"))
+           cinfo->keeppackages = *vp == '0' ? 0 : 1;
+       }
+      fclose(fp);
+      cinfo = 0;
+    }
+  closedir(dir);
+  *nrepoinfosp = nrepoinfos;
+  return repoinfos;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_config_yum.h b/examples/solv/repoinfo_config_yum.h
new file mode 100644 (file)
index 0000000..d7cb4f7
--- /dev/null
@@ -0,0 +1,2 @@
+extern char *yum_substitute(Pool *pool, char *line);
+extern struct repoinfo *read_repoinfos_yum(Pool *pool, int *nrepoinfosp);
diff --git a/examples/solv/repoinfo_download.c b/examples/solv/repoinfo_download.c
new file mode 100644 (file)
index 0000000..5ba3014
--- /dev/null
@@ -0,0 +1,221 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "solv_xfopen.h"
+
+#include "repoinfo.h"
+#include "mirror.h"
+#include "checksig.h"
+#include "repoinfo_download.h"
+
+static inline int
+opentmpfile()
+{
+  char tmpl[100];
+  int fd;
+
+  strcpy(tmpl, "/var/tmp/solvXXXXXX");
+  fd = mkstemp(tmpl);
+  if (fd < 0) 
+    {    
+      perror("mkstemp");
+      exit(1);
+    }    
+  unlink(tmpl);
+  return fd;
+}
+
+int
+verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype)
+{
+  char buf[1024];
+  const unsigned char *sum;
+  Chksum *h;
+  int l;
+
+  h = solv_chksum_create(chksumtype);
+  if (!h)
+    {
+      printf("%s: unknown checksum type\n", file);
+      return 0;
+    }
+  while ((l = read(fd, buf, sizeof(buf))) > 0)
+    solv_chksum_add(h, buf, l);
+  lseek(fd, 0, SEEK_SET);
+  l = 0;
+  sum = solv_chksum_get(h, &l);
+  if (memcmp(sum, chksum, l))
+    {
+      printf("%s: checksum mismatch\n", file);
+      solv_chksum_free(h, 0);
+      return 0;
+    }
+  solv_chksum_free(h, 0);
+  return 1;
+}
+
+FILE *
+curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete)
+{
+  FILE *fp;
+  pid_t pid;
+  int fd;
+  int status;
+  char url[4096];
+  const char *baseurl = cinfo->baseurl;
+
+  if (!baseurl)
+    {
+      if (!cinfo->metalink && !cinfo->mirrorlist)
+        return 0;
+      if (file != cinfo->metalink && file != cinfo->mirrorlist)
+       {
+         unsigned char mlchksum[32];
+         Id mlchksumtype = 0;
+         fp = curlfopen(cinfo, cinfo->metalink ? cinfo->metalink : cinfo->mirrorlist, 0, 0, 0, 0);
+         if (!fp)
+           return 0;
+         if (cinfo->metalink)
+           cinfo->baseurl = findmetalinkurl(fp, mlchksum, &mlchksumtype);
+         else
+           cinfo->baseurl = findmirrorlisturl(fp);
+         fclose(fp);
+         if (!cinfo->baseurl)
+           return 0;
+#ifdef FEDORA
+         if (strchr(cinfo->baseurl, '$'))
+           {
+             char *b = yum_substitute(cinfo->repo->pool, cinfo->baseurl);
+             free(cinfo->baseurl);
+             cinfo->baseurl = strdup(b);
+           }
+#endif
+         if (!chksumtype && mlchksumtype && !strcmp(file, "repodata/repomd.xml"))
+           {
+             chksumtype = mlchksumtype;
+             chksum = mlchksum;
+           }
+         return curlfopen(cinfo, file, uncompress, chksum, chksumtype, markincomplete);
+       }
+      snprintf(url, sizeof(url), "%s", file);
+    }
+  else
+    {
+      const char *path = cinfo->path && strcmp(cinfo->path, "/") != 0 ? cinfo->path : "";
+      int l = strlen(baseurl);
+      int pl = strlen(path);
+      const char *sep = l && baseurl[l - 1] == '/' ? "" : "/";
+      const char *psep = pl && cinfo->path[pl - 1] == '/' ? "" : "/";
+      snprintf(url, sizeof(url), "%s%s%s%s%s", baseurl, sep, path, psep, file);
+    }
+  fd = opentmpfile();
+  // printf("url: %s\n", url);
+  if ((pid = fork()) == (pid_t)-1)
+    {
+      perror("fork");
+      exit(1);
+    }
+  if (pid == 0)
+    {
+      if (fd != 1)
+       {
+          dup2(fd, 1);
+         close(fd);
+       }
+      execlp("curl", "curl", "-f", "-s", "-L", url, (char *)0);
+      perror("curl");
+      _exit(0);
+    }
+  status = 0;
+  while (waitpid(pid, &status, 0) != pid)
+    ;
+  if (lseek(fd, 0, SEEK_END) == 0 && (!status || !chksumtype))
+    {
+      /* empty file */
+      close(fd);
+      return 0;
+    }
+  lseek(fd, 0, SEEK_SET);
+  if (status)
+    {
+      printf("%s: download error %d\n", file, status >> 8 ? status >> 8 : status);
+      if (markincomplete)
+       cinfo->incomplete = 1;
+      close(fd);
+      return 0;
+    }
+  if (chksumtype && !verify_checksum(fd, file, chksum, chksumtype))
+    {
+      if (markincomplete)
+       cinfo->incomplete = 1;
+      close(fd);
+      return 0;
+    }
+  fcntl(fd, F_SETFD, FD_CLOEXEC);
+  if (uncompress)
+    {
+      if (solv_xfopen_iscompressed(file) < 0)
+       {
+         printf("%s: unsupported compression\n", file);
+         if (markincomplete)
+           cinfo->incomplete = 1;
+         close(fd);
+         return 0;
+       }
+      fp = solv_xfopen_fd(file, fd, "r");
+    }
+  else
+    fp = fdopen(fd, "r");
+  if (!fp)
+    close(fd);
+  return fp;
+}
+
+FILE *
+downloadpackage(Solvable *s, const char *loc)
+{
+  const unsigned char *chksum;
+  Id chksumtype;
+  struct repoinfo *cinfo = s->repo->appdata;
+
+#ifdef ENABLE_SUSEREPO
+  if (cinfo->type == TYPE_SUSETAGS)
+    {
+      const char *datadir = repo_lookup_str(cinfo->repo, SOLVID_META, SUSETAGS_DATADIR);
+      loc = pool_tmpjoin(s->repo->pool, datadir ? datadir : "suse", "/", loc);
+    }
+#endif
+  chksumtype = 0;
+  chksum = solvable_lookup_bin_checksum(s, SOLVABLE_CHECKSUM, &chksumtype);
+  return curlfopen(cinfo, loc, 0, chksum, chksumtype, 0);
+}
+
+int
+downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool)
+{
+  FILE *sigfp;
+  sigfp = curlfopen(cinfo, sigurl, 0, 0, 0, 0); 
+  if (!sigfp)
+    {    
+      printf(" unsigned, skipped\n");
+      return 0;
+    }    
+  if (!*sigpool)
+    *sigpool = read_sigs();
+  if (!checksig(*sigpool, fp, sigfp))
+    {    
+      printf(" checksig failed, skipped\n");
+      fclose(sigfp);
+      return 0;
+    }    
+  fclose(sigfp);
+  return 1;
+}
+
diff --git a/examples/solv/repoinfo_download.h b/examples/solv/repoinfo_download.h
new file mode 100644 (file)
index 0000000..2dfeaa0
--- /dev/null
@@ -0,0 +1,7 @@
+int verify_checksum(int fd, const char *file, const unsigned char *chksum, Id chksumtype);
+
+FILE *curlfopen(struct repoinfo *cinfo, const char *file, int uncompress, const unsigned char *chksum, Id chksumtype, int markincomplete);
+
+FILE *downloadpackage(Solvable *s, const char *loc);
+int downloadchecksig(struct repoinfo *cinfo, FILE *fp, const char *sigurl, Pool **sigpool);
+
diff --git a/examples/solv/repoinfo_system_debian.c b/examples/solv/repoinfo_system_debian.c
new file mode 100644 (file)
index 0000000..f01be60
--- /dev/null
@@ -0,0 +1,108 @@
+#if defined(ENABLE_DEBIAN) && defined(DEBIAN)
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_deb.h"
+#include "transaction.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_system_debian.h"
+
+static void
+rundpkg(const char *arg, const char *name, int dupfd3, const char *rootdir)
+{
+  pid_t pid;
+  int status;
+
+  if ((pid = fork()) == (pid_t)-1)
+    {
+      perror("fork");
+      exit(1);
+    }
+  if (pid == 0)
+    {
+      if (!rootdir)
+       rootdir = "/";
+      if (dupfd3 != -1 && dupfd3 != 3)
+       {
+         dup2(dupfd3, 3);
+         close(dupfd3);
+       }
+      if (dupfd3 != -1)
+       fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
+      if (strcmp(arg, "--install") == 0)
+       execlp("dpkg", "dpkg", "--install", "--root", rootdir, "--force", "all", name, (char *)0);
+      else
+       execlp("dpkg", "dpkg", "--remove", "--root", rootdir, "--force", "all", name, (char *)0);
+      perror("dpkg");
+      _exit(0);
+    }
+  while (waitpid(pid, &status, 0) != pid)
+    ;
+  if (status)
+    {
+      printf("dpkg failed\n");
+      exit(1);
+    }
+}
+
+int
+read_installed_debian(struct repoinfo *cinfo)
+{
+  struct stat stb;
+  Repo *repo = cinfo->repo;
+  Pool *pool = repo->pool;
+
+  memset(&stb, 0, sizeof(stb));
+  printf("dpgk database:");
+  if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/dpkg/status"), &stb))
+    memset(&stb, 0, sizeof(stb));
+  calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, 0, cinfo->cookie);
+  cinfo->cookieset = 1;
+  if (usecachedrepo(cinfo, 0, 0))
+    {
+      printf(" cached\n");
+      return 1;
+    }
+  if (repo_add_debdb(repo, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+    {
+      fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
+      return 0;
+    }
+  repo_internalize(repo);
+  writecachedrepo(cinfo, 0, 0);
+  return 1;
+}
+
+void
+commit_transactionelement_debian(Pool *pool, Id type, Id p, FILE *fp)
+{
+  Solvable *s = pool_id2solvable(pool, p);
+  const char *rootdir = pool_get_rootdir(pool);
+
+  switch(type)
+    {   
+    case SOLVER_TRANSACTION_ERASE:
+      rundpkg("--remove", pool_id2str(pool, s->name), 0, rootdir);
+      break;
+    case SOLVER_TRANSACTION_INSTALL:
+    case SOLVER_TRANSACTION_MULTIINSTALL:
+      rewind(fp);
+      lseek(fileno(fp), 0, SEEK_SET);
+      rundpkg("--install", "/dev/fd/3", fileno(fp), rootdir);
+      break;
+    default:
+      break;
+    }   
+}
+
+#endif
diff --git a/examples/solv/repoinfo_system_debian.h b/examples/solv/repoinfo_system_debian.h
new file mode 100644 (file)
index 0000000..c0e2b29
--- /dev/null
@@ -0,0 +1,2 @@
+int  read_installed_debian(struct repoinfo *cinfo);
+void commit_transactionelement_debian(Pool *pool, Id type, Id p, FILE *fp);
diff --git a/examples/solv/repoinfo_system_rpm.c b/examples/solv/repoinfo_system_rpm.c
new file mode 100644 (file)
index 0000000..b385d72
--- /dev/null
@@ -0,0 +1,151 @@
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "repo_rpmdb.h"
+#if defined(ENABLE_SUSEREPO) && defined(SUSE)
+#include "repo_products.h"
+#endif
+#if defined(ENABLE_APPDATA)
+#include "repo_appdata.h"
+#endif
+#include "transaction.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_system_rpm.h"
+
+#ifdef SUSE
+# define PRODUCTS_PATH "/etc/products.d"
+#endif
+#ifdef ENABLE_APPDATA
+# define APPDATA_PATH "/usr/share/appdata"
+#endif
+
+static void
+runrpm(const char *arg, const char *name, int dupfd3, const char *rootdir)
+{
+  pid_t pid;
+  int status;
+
+  if ((pid = fork()) == (pid_t)-1)
+    {
+      perror("fork");
+      exit(1);
+    }
+  if (pid == 0)
+    {
+      if (!rootdir)
+       rootdir = "/";
+      if (dupfd3 != -1 && dupfd3 != 3)
+       {
+         dup2(dupfd3, 3);
+         close(dupfd3);
+       }
+      if (dupfd3 != -1)
+       fcntl(3, F_SETFD, 0);   /* clear CLOEXEC */
+      if (strcmp(arg, "-e") == 0)
+       execlp("rpm", "rpm", arg, "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
+      else
+       execlp("rpm", "rpm", arg, "--force", "--nodeps", "--nodigest", "--nosignature", "--root", rootdir, name, (char *)0);
+      perror("rpm");
+      _exit(0);
+    }
+  while (waitpid(pid, &status, 0) != pid)
+    ;
+  if (status)
+    {
+      printf("rpm failed\n");
+      exit(1);
+    }
+}
+
+int
+read_installed_rpm(struct repoinfo *cinfo)
+{
+  Repo *repo = cinfo->repo;
+  Pool *pool = repo->pool;
+  FILE *ofp = 0;
+  struct stat stb;
+
+  memset(&stb, 0, sizeof(stb));
+  printf("rpm database:");
+  if (stat(pool_prepend_rootdir_tmp(pool, "/var/lib/rpm/Packages"), &stb))
+    memset(&stb, 0, sizeof(stb));
+  calc_cookie_stat(&stb, REPOKEY_TYPE_SHA256, 0, cinfo->cookie);
+  cinfo->cookieset = 1;
+  if (usecachedrepo(cinfo, 0, 0))
+    {
+      printf(" cached\n");
+      return 1;
+    }
+  printf(" reading\n");
+#if defined(ENABLE_SUSEREPO) && defined(PRODUCTS_PATH)
+  if (repo_add_products(repo, PRODUCTS_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+    {
+      fprintf(stderr, "product reading failed: %s\n", pool_errstr(pool));
+      return 0;
+    }
+#endif
+#if defined(ENABLE_APPDATA) && defined(APPDATA_PATH)
+  if (repo_add_appdata_dir(repo, APPDATA_PATH, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+    {
+      fprintf(stderr, "appdata reading failed: %s\n", pool_errstr(pool));
+      return 0;
+    }
+#endif
+  ofp = fopen(calc_cachepath(repo, 0, 0), "r");
+  if (repo_add_rpmdb_reffp(repo, ofp, REPO_REUSE_REPODATA | REPO_NO_INTERNALIZE | REPO_USE_ROOTDIR))
+    {
+      fprintf(stderr, "installed db: %s\n", pool_errstr(pool));
+      return 0;
+    }
+  if (ofp)
+    fclose(ofp);
+  repo_internalize(repo);
+  writecachedrepo(cinfo, 0, 0);
+  return 1;
+}
+
+void
+commit_transactionelement_rpm(Pool *pool, Id type, Id p, FILE *fp)
+{
+  Solvable *s = pool_id2solvable(pool, p);
+  const char *rootdir = pool_get_rootdir(pool);
+  const char *evr, *evrp, *nvra;
+
+  switch(type)
+    {
+    case SOLVER_TRANSACTION_ERASE:
+      if (!s->repo->rpmdbid || !s->repo->rpmdbid[p - s->repo->start])
+       break;
+      /* strip epoch from evr */
+      evr = evrp = pool_id2str(pool, s->evr);
+      while (*evrp >= '0' && *evrp <= '9')
+       evrp++;
+      if (evrp > evr && evrp[0] == ':' && evrp[1])
+       evr = evrp + 1;
+      nvra = pool_tmpjoin(pool, pool_id2str(pool, s->name), "-", evr);
+      nvra = pool_tmpappend(pool, nvra, ".", pool_id2str(pool, s->arch));
+      runrpm("-e", nvra, -1, rootdir);      /* too bad that --querybynumber doesn't work */
+      break;
+    case SOLVER_TRANSACTION_INSTALL:
+    case SOLVER_TRANSACTION_MULTIINSTALL:
+      rewind(fp);
+      lseek(fileno(fp), 0, SEEK_SET);
+      runrpm(type == SOLVER_TRANSACTION_MULTIINSTALL ? "-i" : "-U", "/dev/fd/3", fileno(fp), rootdir);
+      break;
+    default:
+      break;
+    }
+}
+
+#endif
diff --git a/examples/solv/repoinfo_system_rpm.h b/examples/solv/repoinfo_system_rpm.h
new file mode 100644 (file)
index 0000000..db63a8d
--- /dev/null
@@ -0,0 +1,2 @@
+int  read_installed_rpm(struct repoinfo *cinfo);
+void commit_transactionelement_rpm(Pool *pool, Id type, Id p, FILE *fp);
diff --git a/examples/solv/repoinfo_type_debian.c b/examples/solv/repoinfo_type_debian.c
new file mode 100644 (file)
index 0000000..7a15ff4
--- /dev/null
@@ -0,0 +1,202 @@
+#ifdef ENABLE_DEBIAN
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_deb.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_debian.h"
+
+static const char *
+debian_find_component(struct repoinfo *cinfo, FILE *fp, char *comp, const unsigned char **chksump, Id *chksumtypep)
+{
+  char buf[4096];
+  Id chksumtype;
+  unsigned char *chksum;
+  Id curchksumtype;
+  int l, compl;
+  char *ch, *fn, *bp;
+  char *filename;
+  static char *basearch;
+  char *binarydir;
+  int lbinarydir;
+
+  if (!basearch)
+    {
+      struct utsname un;
+      if (uname(&un))
+       {
+         perror("uname");
+         exit(1);
+       }
+      basearch = strdup(un.machine);
+      if (basearch[0] == 'i' && basearch[1] && !strcmp(basearch + 2, "86"))
+       basearch[1] = '3';
+    }
+  binarydir = solv_dupjoin("binary-", basearch, "/");
+  lbinarydir = strlen(binarydir);
+  compl = strlen(comp);
+  rewind(fp);
+  curchksumtype = 0;
+  filename = 0;
+  chksum = solv_malloc(32);
+  chksumtype = 0;
+  while(fgets(buf, sizeof(buf), fp))
+    {
+      l = strlen(buf);
+      if (l == 0)
+       continue;
+      while (l && (buf[l - 1] == '\n' || buf[l - 1] == ' ' || buf[l - 1] == '\t'))
+       buf[--l] = 0;
+      if (!strncasecmp(buf, "MD5Sum:", 7))
+       {
+         curchksumtype = REPOKEY_TYPE_MD5;
+         continue;
+       }
+      if (!strncasecmp(buf, "SHA1:", 5))
+       {
+         curchksumtype = REPOKEY_TYPE_SHA1;
+         continue;
+       }
+      if (!strncasecmp(buf, "SHA256:", 7))
+       {
+         curchksumtype = REPOKEY_TYPE_SHA256;
+         continue;
+       }
+      if (!curchksumtype)
+       continue;
+      bp = buf;
+      if (*bp++ != ' ')
+       {
+         curchksumtype = 0;
+         continue;
+       }
+      ch = bp;
+      while (*bp && *bp != ' ' && *bp != '\t')
+       bp++;
+      if (!*bp)
+       continue;
+      *bp++ = 0;
+      while (*bp == ' ' || *bp == '\t')
+       bp++;
+      while (*bp && *bp != ' ' && *bp != '\t')
+       bp++;
+      if (!*bp)
+       continue;
+      while (*bp == ' ' || *bp == '\t')
+       bp++;
+      fn = bp;
+      if (strncmp(fn, comp, compl) != 0 || fn[compl] != '/')
+       continue;
+      bp += compl + 1;
+      if (strncmp(bp, binarydir, lbinarydir))
+       continue;
+      bp += lbinarydir;
+      if (!strcmp(bp, "Packages") || !strcmp(bp, "Packages.gz"))
+       {
+         unsigned char curchksum[32];
+         int curl;
+         if (filename && !strcmp(bp, "Packages"))
+           continue;
+         curl = solv_chksum_len(curchksumtype);
+         if (!curl || (chksumtype && solv_chksum_len(chksumtype) > curl))
+           continue;
+          if (solv_hex2bin((const char **)&ch, curchksum, sizeof(curchksum)) != curl)
+           continue;
+         solv_free(filename);
+         filename = strdup(fn);
+         chksumtype = curchksumtype;
+         memcpy(chksum, curchksum, curl);
+       }
+    }
+  free(binarydir);
+  if (filename)
+    {
+      fn = solv_dupjoin("/", filename, 0);
+      solv_free(filename);
+      filename = solv_dupjoin("dists/", cinfo->name, fn);
+      solv_free(fn);
+    }
+  if (!chksumtype)
+    chksum = solv_free(chksum);
+  *chksump = chksum;
+  *chksumtypep = chksumtype;
+  return filename;
+}
+
+int
+debian_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+  Repo *repo = cinfo->repo;
+  Pool *pool = repo->pool;
+  const char *filename;
+  const unsigned char *filechksum;
+  Id filechksumtype;
+  FILE *fp, *fpr;
+  int j;
+
+  printf("debian repo '%s':", cinfo->alias);
+  fflush(stdout);
+  filename = solv_dupjoin("dists/", cinfo->name, "/Release");
+  if ((fpr = curlfopen(cinfo, filename, 0, 0, 0, 0)) == 0)
+    {
+      printf(" no Release file\n");
+      free((char *)filename);
+      cinfo->incomplete = 1;
+      return 0;
+    }
+  solv_free((char *)filename);
+  if (cinfo->repo_gpgcheck)
+    {
+      filename = solv_dupjoin("dists/", cinfo->name, "/Release.gpg");
+      if (!downloadchecksig(cinfo, fpr, filename, sigpoolp))
+       {
+         fclose(fpr);
+         solv_free((char *)filename);
+         cinfo->incomplete = 1;
+         return 0;
+       }
+      solv_free((char *)filename);
+    }
+  calc_cookie_fp(fpr, REPOKEY_TYPE_SHA256, cinfo->cookie);
+  cinfo->cookieset = 1;
+  if (usecachedrepo(cinfo, 0, 1))
+    {
+      printf(" cached\n");
+      fclose(fpr);
+      return 1;
+    }
+  printf(" fetching\n");
+  for (j = 0; j < cinfo->ncomponents; j++)
+    {
+      if (!(filename = debian_find_component(cinfo, fpr, cinfo->components[j], &filechksum, &filechksumtype)))
+       {
+         printf("[component %s not found]\n", cinfo->components[j]);
+         continue;
+       }
+      if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+       {
+         if (repo_add_debpackages(repo, fp, 0))
+           {
+             printf("component %s: %s\n", cinfo->components[j], pool_errstr(pool));
+             cinfo->incomplete = 1;
+           }
+         fclose(fp);
+       }
+      solv_free((char *)filechksum);
+      solv_free((char *)filename);
+    }
+  fclose(fpr);
+  writecachedrepo(cinfo, 0, 0);
+  return 1;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_type_debian.h b/examples/solv/repoinfo_type_debian.h
new file mode 100644 (file)
index 0000000..3dfc827
--- /dev/null
@@ -0,0 +1,2 @@
+extern int debian_load(struct repoinfo *cinfo, Pool **sigpoolp);
+
diff --git a/examples/solv/repoinfo_type_mdk.c b/examples/solv/repoinfo_type_mdk.c
new file mode 100644 (file)
index 0000000..96b005f
--- /dev/null
@@ -0,0 +1,213 @@
+#ifdef ENABLE_MDKREPO
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_mdk.h"
+#include "solv_xfopen.h"
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_mdk.h"
+
+static int
+mdk_find(const char *md5sums, const char *what, unsigned char *chksum)
+{
+  const char *sp, *ep;
+  int wl = strlen(what);
+  for (sp = md5sums; (ep = strchr(sp, '\n')) != 0; sp = ep + 1)
+    {
+      int l = ep - sp;
+      if (l <= 34)
+       continue;
+      if (sp[32] != ' ' || sp[33] != ' ')
+       continue;
+      if (wl != l - 34 || strncmp(what, sp + 34, wl) != 0)
+       continue;
+      if (solv_hex2bin(&sp, chksum, 16) != 16)
+       continue;
+      return 1;
+    }
+  return 0;
+}
+
+static char *
+slurp(FILE *fp)
+{
+  int l, ll;
+  char *buf = 0;
+  int bufl = 0;
+
+  for (l = 0; ; l += ll)
+    {
+      if (bufl - l < 4096)
+        {
+          bufl += 4096;
+          buf = solv_realloc(buf, bufl);
+        }
+      ll = fread(buf + l, 1, bufl - l, fp);
+      if (ll < 0)
+        {
+          buf = solv_free(buf);
+          l = 0;
+          break;
+        }
+      if (ll == 0)
+        {
+          buf[l] = 0;
+          break;
+        }
+    }
+  return buf;
+}
+
+int
+mdk_load_ext(Repo *repo, Repodata *data)
+{
+  struct repoinfo *cinfo = repo->appdata;
+  const char *type, *ext, *filename;
+  const unsigned char *filechksum;
+  Id filechksumtype;
+  int r = 0;
+  FILE *fp;
+
+  type = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
+  if (strcmp(type, "filelists") != 0)
+    return 0;
+  ext = "FL";
+  printf("[%s:%s", repo->name, ext);
+  if (usecachedrepo(cinfo, ext, 0))
+    {
+      printf(" cached]\n"); fflush(stdout);
+      return 1;
+    }
+  printf(" fetching]\n"); fflush(stdout);
+  filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
+  filechksumtype = 0;
+  filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
+  if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 0)) == 0)
+    return 0;
+  r = repo_add_mdk_info(repo, fp, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
+  fclose(fp);
+  if (r)
+    {
+      printf("%s\n", pool_errstr(repo->pool));
+      return 0;
+    }
+  writecachedrepo(cinfo, ext, data);
+  return 1;
+}
+
+int
+mdk_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+  Repo *repo = cinfo->repo;
+  Pool *pool = repo->pool;
+  Repodata *data;
+  const char *compression;
+  FILE *fp, *cfp;
+  char *md5sums;
+  unsigned char probe[5];
+  unsigned char md5[16];
+
+  printf("mdk repo '%s':", cinfo->alias);
+  fflush(stdout);
+  if ((fp = curlfopen(cinfo, "media_info/MD5SUM", 0, 0, 0, 0)) == 0)
+    {
+      printf(" no media_info/MD5SUM file\n");
+      cinfo->incomplete = 1;
+      return 0;
+    }
+  calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
+  cinfo->cookieset = 1;
+  if (usecachedrepo(cinfo, 0, 1))
+    {
+      printf(" cached\n");
+      fclose(fp);
+      return 1;
+    }
+  md5sums = slurp(fp);
+  fclose(fp);
+  printf(" fetching\n");
+  if (!mdk_find(md5sums, "synthesis.hdlist.cz", md5))
+    {
+      solv_free(md5sums);
+      cinfo->incomplete = 1;
+      return 0;        /* hopeless */
+    }
+  if ((fp = curlfopen(cinfo, "media_info/synthesis.hdlist.cz", 0, md5, REPOKEY_TYPE_MD5, 1)) == 0)
+    {
+      solv_free(md5sums);
+      cinfo->incomplete = 1;
+      return 0;        /* hopeless */
+    }
+  /* probe compression */
+  if (fread(probe, 5, 1, fp) != 1)
+    {
+      fclose(fp);
+      solv_free(md5sums);
+      cinfo->incomplete = 1;
+      return 0;        /* hopeless */
+    }
+  if (probe[0] == 0xfd && memcmp(probe + 1, "7zXZ", 4) == 0)
+    compression = "synthesis.hdlist.xz";
+  else
+    compression = "synthesis.hdlist.gz";
+  lseek(fileno(fp), 0, SEEK_SET);
+  cfp = solv_xfopen_fd(compression, dup(fileno(fp)), "r");
+  fclose(fp);
+  fp = cfp;
+  if (!fp)
+    {
+      solv_free(md5sums);
+      cinfo->incomplete = 1;
+      return 0;        /* hopeless */
+    }
+  if (repo_add_mdk(repo, fp, REPO_NO_INTERNALIZE))
+    {
+      printf("synthesis.hdlist.cz: %s\n", pool_errstr(pool));
+      fclose(fp);
+      solv_free(md5sums);
+      cinfo->incomplete = 1;
+      return 0;        /* hopeless */
+    }
+  fclose(fp);
+  /* add info, could do this on demand, but always having the summary is nice */
+  if (mdk_find(md5sums, "info.xml.lzma", md5))
+    {
+      if ((fp = curlfopen(cinfo, "media_info/info.xml.lzma", 1, md5, REPOKEY_TYPE_MD5, 1)) != 0)
+       {
+         if (repo_add_mdk_info(repo, fp, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
+           {
+             printf("info.xml.lzma: %s\n", pool_errstr(pool));
+             cinfo->incomplete = 1;
+           }
+         fclose(fp);
+       }
+    }
+  repo_internalize(repo);
+  data = repo_add_repodata(repo, 0);
+  /* setup on-demand loading of filelist data */
+  if (mdk_find(md5sums, "files.xml.lzma", md5))
+    {
+      Id handle = repodata_new_handle(data);
+      /* we mis-use the repomd ids here... need something generic in the future */
+      repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, "filelists");
+      repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, "media_info/files.xml.lzma");
+      repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, REPOKEY_TYPE_MD5, md5);
+      add_ext_keys(data, handle, "FL");
+      repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
+    }
+  solv_free(md5sums);
+  repodata_internalize(data);
+  writecachedrepo(cinfo, 0, 0);
+  repodata_create_stubs(repo_last_repodata(repo));
+  return 1;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_type_mdk.h b/examples/solv/repoinfo_type_mdk.h
new file mode 100644 (file)
index 0000000..e98bc48
--- /dev/null
@@ -0,0 +1,3 @@
+extern int mdk_load(struct repoinfo *cinfo, Pool **sigpoolp);
+extern int mdk_load_ext(Repo *repo, Repodata *data);
+
diff --git a/examples/solv/repoinfo_type_rpmmd.c b/examples/solv/repoinfo_type_rpmmd.c
new file mode 100644 (file)
index 0000000..98c5d36
--- /dev/null
@@ -0,0 +1,207 @@
+#ifdef ENABLE_RPMMD
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_rpmmd.h"
+#include "repo_deltainfoxml.h"
+#include "repo_updateinfoxml.h"
+#include "repo_repomdxml.h"
+#ifdef ENABLE_APPDATA
+#include "repo_appdata.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_rpmmd.h"
+
+
+
+static const char *
+repomd_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
+{
+  Pool *pool = repo->pool;
+  Dataiterator di;
+  const char *filename;
+
+  filename = 0;
+  *chksump = 0;
+  *chksumtypep = 0;
+  dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_REPOMD_TYPE, what, SEARCH_STRING);
+  dataiterator_prepend_keyname(&di, REPOSITORY_REPOMD);
+  if (dataiterator_step(&di))
+    {
+      dataiterator_setpos_parent(&di);
+      filename = pool_lookup_str(pool, SOLVID_POS, REPOSITORY_REPOMD_LOCATION);
+      *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, REPOSITORY_REPOMD_CHECKSUM, chksumtypep);
+    }
+  dataiterator_free(&di);
+  if (filename && !*chksumtypep)
+    {
+      printf("no %s file checksum!\n", what);
+      filename = 0;
+    }
+  return filename;
+}
+
+static void
+repomd_add_ext(Repo *repo, Repodata *data, const char *what, const char *ext)
+{
+  Id chksumtype, handle;
+  const unsigned char *chksum;
+  const char *filename;
+
+  filename = repomd_find(repo, what, &chksum, &chksumtype);
+  if (!filename && !strcmp(what, "deltainfo"))
+    filename = repomd_find(repo, "prestodelta", &chksum, &chksumtype);
+  if (!filename)
+    return;
+  handle = repodata_new_handle(data);
+  repodata_set_poolstr(data, handle, REPOSITORY_REPOMD_TYPE, what);
+  repodata_set_str(data, handle, REPOSITORY_REPOMD_LOCATION, filename);
+  repodata_set_bin_checksum(data, handle, REPOSITORY_REPOMD_CHECKSUM, chksumtype, chksum);
+  add_ext_keys(data, handle, ext);
+  repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
+}
+
+int
+repomd_load_ext(Repo *repo, Repodata *data)
+{
+  const char *filename, *repomdtype;
+  char ext[3];
+  FILE *fp;
+  struct repoinfo *cinfo;
+  const unsigned char *filechksum;
+  Id filechksumtype;
+  int r = 0;
+
+  cinfo = repo->appdata;
+  repomdtype = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_TYPE);
+  if (!repomdtype)
+    return 0;
+  if (!strcmp(repomdtype, "filelists"))
+    strcpy(ext, "FL");
+  else if (!strcmp(repomdtype, "deltainfo"))
+    strcpy(ext, "DL");
+  else
+    return 0;
+  printf("[%s:%s", repo->name, ext);
+  if (usecachedrepo(cinfo, ext, 0))
+    {
+      printf(" cached]\n"); fflush(stdout);
+      return 1;
+    }
+  printf(" fetching]\n"); fflush(stdout);
+  filename = repodata_lookup_str(data, SOLVID_META, REPOSITORY_REPOMD_LOCATION);
+  filechksumtype = 0;
+  filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, REPOSITORY_REPOMD_CHECKSUM, &filechksumtype);
+  if ((fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 0)) == 0)
+    return 0;
+  if (!strcmp(ext, "FL"))
+    r = repo_add_rpmmd(repo, fp, ext, REPO_USE_LOADING|REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
+  else if (!strcmp(ext, "DL"))
+    r = repo_add_deltainfoxml(repo, fp, REPO_USE_LOADING);
+  fclose(fp);
+  if (r)
+    {
+      printf("%s\n", pool_errstr(repo->pool));
+      return 0;
+    }
+  if (cinfo->extcookieset)
+    writecachedrepo(cinfo, ext, data);
+  return 1;
+}
+
+int
+repomd_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+  Repo *repo = cinfo->repo;
+  Pool *pool = repo->pool;
+  Repodata *data;
+  const char *filename;
+  const unsigned char *filechksum;
+  Id filechksumtype;
+  FILE *fp;
+
+  printf("rpmmd repo '%s':", cinfo->alias);
+  fflush(stdout);
+  if ((fp = curlfopen(cinfo, "repodata/repomd.xml", 0, 0, 0, 0)) == 0)
+    {
+      printf(" no repomd.xml file\n");
+      cinfo->incomplete = 1;
+      return 0;
+    }
+  calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
+  cinfo->cookieset = 1;
+  if (usecachedrepo(cinfo, 0, 1))
+    {
+      printf(" cached\n");
+      fclose(fp);
+      return 1;
+    }
+  if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "repodata/repomd.xml.asc", sigpoolp))
+    {
+      fclose(fp);
+      cinfo->incomplete = 1;
+      return 0;
+    }
+  if (repo_add_repomdxml(repo, fp, 0))
+    {
+      printf("repomd.xml: %s\n", pool_errstr(pool));
+      cinfo->incomplete = 1;
+      fclose(fp);
+      return 0;
+    }
+  fclose(fp);
+  printf(" fetching\n");
+  filename = repomd_find(repo, "primary", &filechksum, &filechksumtype);
+  if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+    {
+      if (repo_add_rpmmd(repo, fp, 0, 0))
+       {
+         printf("primary: %s\n", pool_errstr(pool));
+         cinfo->incomplete = 1;
+       }
+      fclose(fp);
+    }
+  if (cinfo->incomplete)
+    return 0;  /* hopeless */
+
+  filename = repomd_find(repo, "updateinfo", &filechksum, &filechksumtype);
+  if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+    {
+      if (repo_add_updateinfoxml(repo, fp, 0))
+       {
+         printf("updateinfo: %s\n", pool_errstr(pool));
+         cinfo->incomplete = 1;
+       }
+      fclose(fp);
+    }
+
+#ifdef ENABLE_APPDATA
+  filename = repomd_find(repo, "appdata", &filechksum, &filechksumtype);
+  if (filename && (fp = curlfopen(cinfo, filename, 1, filechksum, filechksumtype, 1)) != 0)
+    {
+      if (repo_add_appdata(repo, fp, 0))
+       {
+         printf("appdata: %s\n", pool_errstr(pool));
+         cinfo->incomplete = 1;
+       }
+      fclose(fp);
+    }
+#endif
+  data = repo_add_repodata(repo, 0);
+  repomd_add_ext(repo, data, "deltainfo", "DL");
+  repomd_add_ext(repo, data, "filelists", "FL");
+  repodata_internalize(data);
+  writecachedrepo(cinfo, 0, 0);
+  repodata_create_stubs(repo_last_repodata(repo));
+  return 1;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_type_rpmmd.h b/examples/solv/repoinfo_type_rpmmd.h
new file mode 100644 (file)
index 0000000..275dfc1
--- /dev/null
@@ -0,0 +1,2 @@
+extern int repomd_load_ext(Repo *repo, Repodata *data);
+extern int repomd_load(struct repoinfo *cinfo, Pool **sigpoolp);
diff --git a/examples/solv/repoinfo_type_susetags.c b/examples/solv/repoinfo_type_susetags.c
new file mode 100644 (file)
index 0000000..596171c
--- /dev/null
@@ -0,0 +1,274 @@
+#ifdef ENABLE_SUSEREPO
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pool.h"
+#include "repo.h"
+#include "chksum.h"
+#include "repo_content.h"
+#include "repo_susetags.h"
+#ifdef ENABLE_APPDATA
+#include "repo_appdata.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+#include "repoinfo_type_susetags.h"
+
+/* susetags helpers */
+
+static const char *
+susetags_find(Repo *repo, const char *what, const unsigned char **chksump, Id *chksumtypep)
+{
+  Pool *pool = repo->pool;
+  Dataiterator di;
+  const char *filename;
+
+  filename = 0;
+  *chksump = 0;
+  *chksumtypep = 0;
+  dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, what, SEARCH_STRING);
+  dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
+  if (dataiterator_step(&di))
+    {
+      dataiterator_setpos_parent(&di);
+      *chksump = pool_lookup_bin_checksum(pool, SOLVID_POS, SUSETAGS_FILE_CHECKSUM, chksumtypep);
+      filename = what;
+    }
+  dataiterator_free(&di);
+  if (filename && !*chksumtypep)
+    {
+      printf("no %s file checksum!\n", what);
+      filename = 0;
+    }
+  return filename;
+}
+
+void
+susetags_add_ext(Repo *repo, Repodata *data)
+{
+  Pool *pool = repo->pool;
+  Dataiterator di;
+  char ext[3];
+  Id handle, filechksumtype;
+  const unsigned char *filechksum;
+
+  dataiterator_init(&di, pool, repo, SOLVID_META, SUSETAGS_FILE_NAME, 0, 0);
+  dataiterator_prepend_keyname(&di, SUSETAGS_FILE);
+  while (dataiterator_step(&di))
+    {
+      if (strncmp(di.kv.str, "packages.", 9) != 0)
+       continue;
+      if (!strcmp(di.kv.str + 9, "gz"))
+       continue;
+      if (!di.kv.str[9] || !di.kv.str[10] || (di.kv.str[11] && di.kv.str[11] != '.'))
+       continue;
+      ext[0] = di.kv.str[9];
+      ext[1] = di.kv.str[10];
+      ext[2] = 0;
+      if (!strcmp(ext, "en"))
+       continue;
+      if (!susetags_find(repo, di.kv.str, &filechksum, &filechksumtype))
+       continue;
+      handle = repodata_new_handle(data);
+      repodata_set_str(data, handle, SUSETAGS_FILE_NAME, di.kv.str);
+      if (filechksumtype)
+       repodata_set_bin_checksum(data, handle, SUSETAGS_FILE_CHECKSUM, filechksumtype, filechksum);
+      add_ext_keys(data, handle, ext);
+      repodata_add_flexarray(data, SOLVID_META, REPOSITORY_EXTERNAL, handle);
+    }
+  dataiterator_free(&di);
+}
+
+int
+susetags_load_ext(Repo *repo, Repodata *data)
+{
+  const char *filename, *descrdir;
+  Id defvendor;
+  char ext[3];
+  FILE *fp;
+  struct repoinfo *cinfo;
+  const unsigned char *filechksum;
+  Id filechksumtype;
+  int flags;
+
+  cinfo = repo->appdata;
+  filename = repodata_lookup_str(data, SOLVID_META, SUSETAGS_FILE_NAME);
+  if (!filename)
+    return 0;
+  /* susetags load */
+  ext[0] = filename[9];
+  ext[1] = filename[10];
+  ext[2] = 0;
+  printf("[%s:%s", repo->name, ext);
+  if (usecachedrepo(cinfo, ext, 0))
+    {
+      printf(" cached]\n"); fflush(stdout);
+      return 1;
+    }
+  printf(" fetching]\n"); fflush(stdout);
+  defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
+  descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
+  if (!descrdir)
+    descrdir = "suse/setup/descr";
+  filechksumtype = 0;
+  filechksum = repodata_lookup_bin_checksum(data, SOLVID_META, SUSETAGS_FILE_CHECKSUM, &filechksumtype);
+  if ((fp = curlfopen(cinfo, pool_tmpjoin(repo->pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 0)) == 0)
+    return 0;
+  flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
+  if (strcmp(ext, "DL") != 0)
+    flags |= REPO_LOCALPOOL;
+  if (repo_add_susetags(repo, fp, defvendor, ext, flags))
+    {
+      fclose(fp);
+      printf("%s\n", pool_errstr(repo->pool));
+      return 0;
+    }
+  fclose(fp);
+  writecachedrepo(cinfo, ext, data);
+  return 1;
+}
+
+int
+susetags_load(struct repoinfo *cinfo, Pool **sigpoolp)
+{
+  Repo *repo = cinfo->repo;
+  Pool *pool = repo->pool;
+  Repodata *data;
+  const char *filename;
+  const unsigned char *filechksum;
+  Id filechksumtype;
+  FILE *fp;
+  const char *descrdir;
+  int defvendor;
+
+  printf("susetags repo '%s':", cinfo->alias);
+  fflush(stdout);
+  descrdir = 0;
+  defvendor = 0;
+  if ((fp = curlfopen(cinfo, "content", 0, 0, 0, 0)) == 0)
+    {
+      printf(" no content file\n");
+      cinfo->incomplete = 1;
+      return 0;
+    }
+  calc_cookie_fp(fp, REPOKEY_TYPE_SHA256, cinfo->cookie);
+  cinfo->cookieset = 1;
+  if (usecachedrepo(cinfo, 0, 1))
+    {
+      printf(" cached\n");
+      fclose(fp);
+      return 1;
+    }
+  if (cinfo->repo_gpgcheck && !downloadchecksig(cinfo, fp, "content.asc", sigpoolp))
+    {
+      fclose(fp);
+      cinfo->incomplete = 1;
+      return 0;
+    }
+  if (repo_add_content(repo, fp, 0))
+    {
+      printf("content: %s\n", pool_errstr(pool));
+      fclose(fp);
+      cinfo->incomplete = 1;
+      return 0;
+    }
+  fclose(fp);
+  defvendor = repo_lookup_id(repo, SOLVID_META, SUSETAGS_DEFAULTVENDOR);
+  descrdir = repo_lookup_str(repo, SOLVID_META, SUSETAGS_DESCRDIR);
+  if (!descrdir)
+    descrdir = "suse/setup/descr";
+  filename = susetags_find(repo, "packages.gz", &filechksum, &filechksumtype);
+  if (!filename)
+    filename = susetags_find(repo, "packages", &filechksum, &filechksumtype);
+  if (!filename)
+    {
+      printf(" no packages file entry, skipped\n");
+      cinfo->incomplete = 1;
+      return 0;
+    }
+  printf(" fetching\n");
+  if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) == 0)
+    {
+      cinfo->incomplete = 1;
+      return 0;        /* hopeless */
+    }
+  if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|SUSETAGS_RECORD_SHARES))
+    {
+      printf("packages: %s\n", pool_errstr(pool));
+      fclose(fp);
+      cinfo->incomplete = 1;
+      return 0;        /* hopeless */
+    }
+  fclose(fp);
+  /* add default language */
+  filename = susetags_find(repo, "packages.en.gz", &filechksum, &filechksumtype);
+  if (!filename)
+    filename = susetags_find(repo, "packages.en", &filechksum, &filechksumtype);
+  if (filename)
+    {
+      if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+       {
+         if (repo_add_susetags(repo, fp, defvendor, 0, REPO_NO_INTERNALIZE|REPO_REUSE_REPODATA|REPO_EXTEND_SOLVABLES))
+           {
+             printf("packages.en: %s\n", pool_errstr(pool));
+             cinfo->incomplete = 1;
+           }
+         fclose(fp);
+       }
+    }
+  filename = susetags_find(repo, "patterns", &filechksum, &filechksumtype);
+  if (filename)
+    {
+      if ((fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+       {
+         char pbuf[256];
+         while (fgets(pbuf, sizeof(pbuf), fp))
+           {
+             int l = strlen(pbuf);
+             FILE *fp2;
+             if (l && pbuf[l - 1] == '\n')
+               pbuf[--l] = 0;
+             if (!*pbuf || *pbuf == '.' || strchr(pbuf, '/') != 0)
+               continue;
+             filename = susetags_find(repo, pbuf, &filechksum, &filechksumtype);
+             if (filename && (fp2 = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+               {
+                 if (repo_add_susetags(repo, fp2, defvendor, 0, REPO_NO_INTERNALIZE))
+                   {
+                     printf("%s: %s\n", pbuf, pool_errstr(pool));
+                     cinfo->incomplete = 1;
+                   }
+                 fclose(fp2);
+               }
+           }
+         fclose(fp);
+       }
+    }
+#ifdef ENABLE_APPDATA
+  filename = susetags_find(repo, "appdata.xml.gz", &filechksum, &filechksumtype);
+  if (!filename)
+    filename = susetags_find(repo, "appdata.xml", &filechksum, &filechksumtype);
+  if (filename && (fp = curlfopen(cinfo, pool_tmpjoin(pool, descrdir, "/", filename), 1, filechksum, filechksumtype, 1)) != 0)
+    {
+      if (repo_add_appdata(repo, fp, 0))
+       {
+         printf("appdata: %s\n", pool_errstr(pool));
+         cinfo->incomplete = 1;
+       }
+      fclose(fp);
+    }
+#endif
+  repo_internalize(repo);
+  data = repo_add_repodata(repo, 0);
+  susetags_add_ext(repo, data);
+  repodata_internalize(data);
+  writecachedrepo(cinfo, 0, 0);
+  repodata_create_stubs(repo_last_repodata(repo));
+  return 1;
+}
+
+#endif
diff --git a/examples/solv/repoinfo_type_susetags.h b/examples/solv/repoinfo_type_susetags.h
new file mode 100644 (file)
index 0000000..2ad5778
--- /dev/null
@@ -0,0 +1,2 @@
+extern int susetags_load_ext(Repo *repo, Repodata *data);
+extern int susetags_load(struct repoinfo *cinfo, Pool **sigpoolp);
diff --git a/examples/solv/solv.c b/examples/solv/solv.c
new file mode 100644 (file)
index 0000000..d624bf6
--- /dev/null
@@ -0,0 +1,910 @@
+/*
+ * Copyright (c) 2009-2015, SUSE LLC.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/* solv, a little software installer demoing the sat solver library */
+
+/* things it does:
+ * - understands globs for package names / dependencies
+ * - understands .arch suffix
+ * - installation of commandline packages
+ * - repository data caching
+ * - on demand loading of secondary repository data
+ * - gpg and checksum verification
+ * - file conflicts
+ * - deltarpm support
+ * - fastestmirror implementation
+ *
+ * things available in the library but missing from solv:
+ * - vendor policy loading
+ * - multi version handling
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include "pool.h"
+#include "poolarch.h"
+#include "evr.h"
+#include "selection.h"
+#include "repo.h"
+#include "solver.h"
+#include "solverdebug.h"
+#include "transaction.h"
+#ifdef SUSE
+#include "repo_autopattern.h"
+#endif
+
+#include "repoinfo.h"
+#include "repoinfo_cache.h"
+#include "repoinfo_download.h"
+
+#if defined(ENABLE_RPMDB)
+#include "fileprovides.h"
+#include "fileconflicts.h"
+#include "deltarpm.h"
+#endif
+#if defined(SUSE) || defined(FEDORA)
+#include "patchjobs.h"
+#endif
+
+void
+setarch(Pool *pool)
+{
+  struct utsname un;
+  if (uname(&un))
+    {
+      perror("uname");
+      exit(1);
+    }
+  pool_setarch(pool, un.machine);
+}
+
+
+int
+yesno(const char *str)
+{
+  char inbuf[128], *ip;
+
+  for (;;)
+    {
+      printf("%s", str);
+      fflush(stdout);
+      *inbuf = 0;
+      if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
+       {
+         printf("Abort.\n");
+         exit(1);
+       }
+      while (*ip == ' ' || *ip == '\t')
+       ip++;
+      if (*ip == 'q')
+       {
+         printf("Abort.\n");
+         exit(1);
+       }
+      if (*ip == 'y' || *ip == 'n')
+       return *ip == 'y' ? 1 : 0;
+    }
+}
+
+#ifdef SUSE
+static Id
+nscallback(Pool *pool, void *data, Id name, Id evr)
+{
+#if 0
+  if (name == NAMESPACE_LANGUAGE)
+    {
+      if (!strcmp(pool_id2str(pool, evr), "ja"))
+       return 1;
+      if (!strcmp(pool_id2str(pool, evr), "de"))
+       return 1;
+      if (!strcmp(pool_id2str(pool, evr), "en"))
+       return 1;
+      if (!strcmp(pool_id2str(pool, evr), "en_US"))
+       return 1;
+    }
+#endif
+  return 0;
+}
+#endif
+
+#ifdef SUSE
+static void
+add_autopackages(Pool *pool)
+{
+  int i;
+  Repo *repo;
+  FOR_REPOS(i, repo)
+    repo_add_autopattern(repo, 0);
+}
+#endif
+
+#ifdef SUSE
+static void
+showdiskusagechanges(Transaction *trans)
+{
+  DUChanges duc[4];
+  int i;
+
+  /* XXX: use mountpoints here */
+  memset(duc, 0, sizeof(duc));
+  duc[0].path = "/";
+  duc[1].path = "/usr/share/man";
+  duc[2].path = "/sbin";
+  duc[3].path = "/etc";
+  transaction_calc_duchanges(trans, duc, 4);
+  for (i = 0; i < 4; i++)
+    printf("duchanges %s: %d K  %d inodes\n", duc[i].path, duc[i].kbytes, duc[i].files);
+}
+#endif
+
+static Id
+find_repo(const char *name, Pool *pool, struct repoinfo *repoinfos, int nrepoinfos)
+{
+  const char *rp;
+  int i;
+
+  for (rp = name; *rp; rp++)
+    if (*rp <= '0' || *rp >= '9')
+      break;
+  if (!*rp)
+    {
+      /* repo specified by number */
+      int rnum = atoi(name);
+      for (i = 0; i < nrepoinfos; i++)
+       {
+         struct repoinfo *cinfo = repoinfos + i;
+         if (!cinfo->enabled || !cinfo->repo)
+           continue;
+         if (--rnum == 0)
+           return cinfo->repo->repoid;
+       }
+    }
+  else
+    {
+      /* repo specified by alias */
+      Repo *repo;
+      FOR_REPOS(i, repo)
+       {
+         if (!strcasecmp(name, repo->name))
+           return repo->repoid;
+       }
+    }
+  return 0;
+}
+
+
+#define MODE_LIST        0
+#define MODE_INSTALL     1
+#define MODE_ERASE       2
+#define MODE_UPDATE      3
+#define MODE_DISTUPGRADE 4
+#define MODE_VERIFY      5
+#define MODE_PATCH       6
+#define MODE_INFO        7
+#define MODE_REPOLIST    8
+#define MODE_SEARCH     9
+
+void
+usage(int r)
+{
+  fprintf(stderr, "Usage: solv COMMAND <select>\n");
+  fprintf(stderr, "\n");
+  fprintf(stderr, "    dist-upgrade: replace installed packages with\n");
+  fprintf(stderr, "                  versions from the repositories\n");
+  fprintf(stderr, "    erase:        erase installed packages\n");
+  fprintf(stderr, "    info:         display package information\n");
+  fprintf(stderr, "    install:      install packages\n");
+  fprintf(stderr, "    list:         list packages\n");
+  fprintf(stderr, "    repos:        list enabled repositories\n");
+  fprintf(stderr, "    search:       search name/summary/description\n");
+  fprintf(stderr, "    update:       update installed packages\n");
+  fprintf(stderr, "    verify:       check dependencies of installed packages\n");
+#if defined(SUSE) || defined(FEDORA)
+  fprintf(stderr, "    patch:        install newest maintenance updates\n");
+#endif
+  fprintf(stderr, "\n");
+  exit(r);
+}
+
+int
+main(int argc, char **argv)
+{
+  Pool *pool;
+  Repo *commandlinerepo = 0;
+  Id *commandlinepkgs = 0;
+  Id p;
+  struct repoinfo *repoinfos, installedrepoinfo;
+  int nrepoinfos = 0;
+  int mainmode = 0, mode = 0;
+  int i, newpkgs;
+  Queue job, checkq;
+  Solver *solv = 0;
+  Transaction *trans;
+  FILE **newpkgsfps;
+  Queue repofilter;
+  Queue kindfilter;
+  Queue archfilter;
+  int archfilter_src = 0;
+  int cleandeps = 0;
+  int forcebest = 0;
+  char *rootdir = 0;
+  char *keyname = 0;
+  int debuglevel = 0;
+
+  argc--;
+  argv++;
+  while (argc && !strcmp(argv[0], "-d"))
+    {
+      debuglevel++;
+      argc--;
+      argv++;
+    }
+  if (!argv[0])
+    usage(1);
+  if (!strcmp(argv[0], "install") || !strcmp(argv[0], "in"))
+    {
+      mainmode = MODE_INSTALL;
+      mode = SOLVER_INSTALL;
+    }
+#if defined(SUSE) || defined(FEDORA)
+  else if (!strcmp(argv[0], "patch"))
+    {
+      mainmode = MODE_PATCH;
+      mode = SOLVER_INSTALL;
+    }
+#endif
+  else if (!strcmp(argv[0], "erase") || !strcmp(argv[0], "rm"))
+    {
+      mainmode = MODE_ERASE;
+      mode = SOLVER_ERASE;
+    }
+  else if (!strcmp(argv[0], "list") || !strcmp(argv[0], "ls"))
+    {
+      mainmode = MODE_LIST;
+      mode = 0;
+    }
+  else if (!strcmp(argv[0], "info"))
+    {
+      mainmode = MODE_INFO;
+      mode = 0;
+    }
+  else if (!strcmp(argv[0], "search") || !strcmp(argv[0], "se"))
+    {
+      mainmode = MODE_SEARCH;
+      mode = 0;
+    }
+  else if (!strcmp(argv[0], "verify"))
+    {
+      mainmode = MODE_VERIFY;
+      mode = SOLVER_VERIFY;
+    }
+  else if (!strcmp(argv[0], "update") || !strcmp(argv[0], "up"))
+    {
+      mainmode = MODE_UPDATE;
+      mode = SOLVER_UPDATE;
+    }
+  else if (!strcmp(argv[0], "dist-upgrade") || !strcmp(argv[0], "dup"))
+    {
+      mainmode = MODE_DISTUPGRADE;
+      mode = SOLVER_DISTUPGRADE;
+    }
+  else if (!strcmp(argv[0], "repos") || !strcmp(argv[0], "repolist") || !strcmp(argv[0], "lr"))
+    {
+      mainmode = MODE_REPOLIST;
+      mode = 0;
+    }
+  else
+    usage(1);
+
+  for (;;)
+    {
+      if (argc > 2 && !strcmp(argv[1], "--root"))
+       {
+         rootdir = argv[2];
+         argc -= 2;
+         argv += 2;
+       }
+      else if (argc > 1 && !strcmp(argv[1], "--clean"))
+       {
+         cleandeps = 1;
+         argc--;
+         argv++;
+       }
+      else if (argc > 1 && !strcmp(argv[1], "--best"))
+       {
+         forcebest = 1;
+         argc--;
+         argv++;
+       }
+      if (argc > 2 && !strcmp(argv[1], "--keyname"))
+       {
+         keyname = argv[2];
+         argc -= 2;
+         argv += 2;
+       }
+      else
+       break;
+    }
+
+  set_userhome();
+  pool = pool_create();
+  pool_set_rootdir(pool, rootdir);
+
+#if 0
+  {
+    const char *langs[] = {"de_DE", "de", "en"};
+    pool_set_languages(pool, langs, sizeof(langs)/sizeof(*langs));
+  }
+#endif
+
+  pool_setloadcallback(pool, load_stub, 0);
+#ifdef SUSE
+  pool->nscallback = nscallback;
+#endif
+  if (debuglevel)
+    pool_setdebuglevel(pool, debuglevel);
+  setarch(pool);
+  pool_set_flag(pool, POOL_FLAG_ADDFILEPROVIDESFILTERED, 1);
+  repoinfos = read_repoinfos(pool, &nrepoinfos);
+  sort_repoinfos(repoinfos, nrepoinfos);
+
+  if (mainmode == MODE_REPOLIST)
+    {
+      int j = 1;
+      for (i = 0; i < nrepoinfos; i++)
+       {
+         struct repoinfo *cinfo = repoinfos + i;
+         if (!cinfo->enabled)
+           continue;
+         printf("%d: %-20s %s (prio %d)\n", j++, cinfo->alias, cinfo->name, cinfo->priority);
+       }
+      exit(0);
+    }
+  memset(&installedrepoinfo, 0, sizeof(installedrepoinfo));
+  if (!read_installed_repo(&installedrepoinfo, pool))
+    exit(1);
+  read_repos(pool, repoinfos, nrepoinfos);
+
+  /* setup filters */
+  queue_init(&repofilter);
+  queue_init(&kindfilter);
+  queue_init(&archfilter);
+  while (argc > 1)
+    {
+      if (!strcmp(argv[1], "-i"))
+       {
+         queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO, pool->installed->repoid);
+         argc--;
+         argv++;
+       }
+      else if (argc > 2 && (!strcmp(argv[1], "-r") || !strcmp(argv[1], "--repo")))
+       {
+         Id repoid = find_repo(argv[2], pool, repoinfos, nrepoinfos);
+         if (!repoid)
+           {
+             fprintf(stderr, "%s: no such repo\n", argv[2]);
+             exit(1);
+           }
+         /* SETVENDOR is actually wrong but useful */
+         queue_push2(&repofilter, SOLVER_SOLVABLE_REPO | SOLVER_SETREPO | SOLVER_SETVENDOR, repoid);
+         argc -= 2;
+         argv += 2;
+       }
+      else if (argc > 2 && !strcmp(argv[1], "--arch"))
+       {
+         if (!strcmp(argv[2], "src") || !strcmp(argv[2], "nosrc"))
+           archfilter_src = 1;
+         queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, argv[2], 1), REL_ARCH, 1));
+         argc -= 2;
+         argv += 2;
+       }
+      else if (argc > 2 && (!strcmp(argv[1], "-t") || !strcmp(argv[1], "--type")))
+       {
+         const char *kind = argv[2];
+         if (!strcmp(kind, "srcpackage"))
+           {
+             /* hey! should use --arch! */
+             queue_push2(&archfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, ARCH_SRC, REL_ARCH, 1));
+             archfilter_src = 1;
+             argc -= 2;
+             argv += 2;
+             continue;
+           }
+         if (!strcmp(kind, "package"))
+           kind = "";
+         if (!strcmp(kind, "all"))
+           queue_push2(&kindfilter, SOLVER_SOLVABLE_ALL, 0);
+         else
+           queue_push2(&kindfilter, SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, 0, pool_str2id(pool, kind, 1), REL_KIND, 1));
+         argc -= 2;
+         argv += 2;
+       }
+      else
+       break;
+    }
+
+  if (mainmode == MODE_SEARCH)
+    {
+      Queue sel, q;
+      Dataiterator di;
+      if (argc != 2)
+       usage(1);
+      pool_createwhatprovides(pool);
+      queue_init(&sel);
+      dataiterator_init(&di, pool, 0, 0, 0, argv[1], SEARCH_SUBSTRING|SEARCH_NOCASE);
+      dataiterator_set_keyname(&di, SOLVABLE_NAME);
+      dataiterator_set_search(&di, 0, 0);
+      while (dataiterator_step(&di))
+       queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
+      dataiterator_set_keyname(&di, SOLVABLE_SUMMARY);
+      dataiterator_set_search(&di, 0, 0);
+      while (dataiterator_step(&di))
+       queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
+      dataiterator_set_keyname(&di, SOLVABLE_DESCRIPTION);
+      dataiterator_set_search(&di, 0, 0);
+      while (dataiterator_step(&di))
+       queue_push2(&sel, SOLVER_SOLVABLE, di.solvid);
+      dataiterator_free(&di);
+      if (repofilter.count)
+       selection_filter(pool, &sel, &repofilter);
+       
+      queue_init(&q);
+      selection_solvables(pool, &sel, &q);
+      queue_free(&sel);
+      for (i = 0; i < q.count; i++)
+       {
+         Solvable *s = pool_id2solvable(pool, q.elements[i]);
+         printf("  - %s [%s]: %s\n", pool_solvable2str(pool, s), s->repo->name, solvable_lookup_str(s, SOLVABLE_SUMMARY));
+       }
+      queue_free(&q);
+      exit(0);
+    }
+
+  /* process command line packages */
+  if (mainmode == MODE_LIST || mainmode == MODE_INFO || mainmode == MODE_INSTALL)
+    {
+      for (i = 1; i < argc; i++)
+       {
+         if (!is_cmdline_package((const char *)argv[i]))
+           continue;
+         if (access(argv[i], R_OK))
+           {
+             perror(argv[i]);
+             exit(1);
+           }
+         if (!commandlinepkgs)
+           commandlinepkgs = solv_calloc(argc, sizeof(Id));
+         if (!commandlinerepo)
+           commandlinerepo = repo_create(pool, "@commandline");
+         p = add_cmdline_package(commandlinerepo, (const char *)argv[i]);
+         if (!p)
+           {
+             fprintf(stderr, "could not add '%s'\n", argv[i]);
+             exit(1);
+           }
+         commandlinepkgs[i] = p;
+       }
+      if (commandlinerepo)
+       repo_internalize(commandlinerepo);
+    }
+
+#if defined(ENABLE_RPMDB)
+  if (pool->disttype == DISTTYPE_RPM)
+    addfileprovides(pool);
+#endif
+#ifdef SUSE
+  add_autopackages(pool);
+#endif
+  pool_createwhatprovides(pool);
+
+  if (keyname)
+    keyname = solv_dupjoin("solvable:", keyname, 0);
+  queue_init(&job);
+  for (i = 1; i < argc; i++)
+    {
+      Queue job2;
+      int flags, rflags;
+
+      if (commandlinepkgs && commandlinepkgs[i])
+       {
+         queue_push2(&job, SOLVER_SOLVABLE, commandlinepkgs[i]);
+         continue;
+       }
+      queue_init(&job2);
+      flags = SELECTION_NAME|SELECTION_PROVIDES|SELECTION_GLOB;
+      flags |= SELECTION_CANON|SELECTION_DOTARCH|SELECTION_REL;
+      if (kindfilter.count)
+       flags |= SELECTION_SKIP_KIND;
+      if (mode == MODE_LIST || archfilter_src)
+       flags |= SELECTION_WITH_SOURCE;
+      if (argv[i][0] == '/')
+       flags |= SELECTION_FILELIST | (mode == MODE_ERASE ? SELECTION_INSTALLED_ONLY : 0);
+      if (!keyname)
+        rflags = selection_make(pool, &job2, argv[i], flags);
+      else
+        rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
+      if (repofilter.count)
+       selection_filter(pool, &job2, &repofilter);
+      if (archfilter.count)
+       selection_filter(pool, &job2, &archfilter);
+      if (kindfilter.count)
+       selection_filter(pool, &job2, &kindfilter);
+      if (!job2.count)
+       {
+         flags |= SELECTION_NOCASE;
+         if (!keyname)
+            rflags = selection_make(pool, &job2, argv[i], flags);
+         else
+           rflags = selection_make_matchdeps(pool, &job2, argv[i], flags, pool_str2id(pool, keyname, 1), 0);
+         if (repofilter.count)
+           selection_filter(pool, &job2, &repofilter);
+         if (archfilter.count)
+           selection_filter(pool, &job2, &archfilter);
+         if (kindfilter.count)
+           selection_filter(pool, &job2, &kindfilter);
+         if (job2.count)
+           printf("[ignoring case for '%s']\n", argv[i]);
+       }
+      if (!job2.count)
+       {
+         fprintf(stderr, "nothing matches '%s'\n", argv[i]);
+         exit(1);
+       }
+      if (rflags & SELECTION_FILELIST)
+        printf("[using file list match for '%s']\n", argv[i]);
+      if (rflags & SELECTION_PROVIDES)
+       printf("[using capability match for '%s']\n", argv[i]);
+      queue_insertn(&job, job.count, job2.count, job2.elements);
+      queue_free(&job2);
+    }
+  keyname = solv_free(keyname);
+
+  if (!job.count && (mainmode == MODE_UPDATE || mainmode == MODE_DISTUPGRADE || mainmode == MODE_VERIFY || repofilter.count || archfilter.count || kindfilter.count))
+    {
+      queue_push2(&job, SOLVER_SOLVABLE_ALL, 0);
+      if (repofilter.count)
+       selection_filter(pool, &job, &repofilter);
+      if (archfilter.count)
+       selection_filter(pool, &job, &archfilter);
+      if (kindfilter.count)
+       selection_filter(pool, &job, &kindfilter);
+    }
+  queue_free(&repofilter);
+  queue_free(&archfilter);
+  queue_free(&kindfilter);
+
+  if (!job.count && mainmode != MODE_PATCH)
+    {
+      printf("no package matched\n");
+      exit(1);
+    }
+
+  if (mainmode == MODE_LIST || mainmode == MODE_INFO)
+    {
+      /* list mode, no solver needed */
+      Queue q;
+      queue_init(&q);
+      for (i = 0; i < job.count; i += 2)
+       {
+         int j;
+         queue_empty(&q);
+         pool_job2solvables(pool, &q, job.elements[i], job.elements[i + 1]);
+         for (j = 0; j < q.count; j++)
+           {
+             Solvable *s = pool_id2solvable(pool, q.elements[j]);
+             if (mainmode == MODE_INFO)
+               {
+                 const char *str;
+                 printf("Name:        %s\n", pool_solvable2str(pool, s));
+                 printf("Repo:        %s\n", s->repo->name);
+                 printf("Summary:     %s\n", solvable_lookup_str(s, SOLVABLE_SUMMARY));
+                 str = solvable_lookup_str(s, SOLVABLE_URL);
+                 if (str)
+                   printf("Url:         %s\n", str);
+                 str = solvable_lookup_str(s, SOLVABLE_LICENSE);
+                 if (str)
+                   printf("License:     %s\n", str);
+                 printf("Description:\n%s\n", solvable_lookup_str(s, SOLVABLE_DESCRIPTION));
+                 printf("\n");
+               }
+             else
+               {
+#if 1
+                 const char *sum = solvable_lookup_str_lang(s, SOLVABLE_SUMMARY, "de", 1);
+#else
+                 const char *sum = solvable_lookup_str_poollang(s, SOLVABLE_SUMMARY);
+#endif
+                 printf("  - %s [%s]\n", pool_solvable2str(pool, s), s->repo->name);
+                 if (sum)
+                   printf("    %s\n", sum);
+               }
+           }
+       }
+      queue_free(&q);
+      queue_free(&job);
+      pool_free(pool);
+      free_repoinfos(repoinfos, nrepoinfos);
+      solv_free(commandlinepkgs);
+      exit(0);
+    }
+
+#if defined(SUSE) || defined(FEDORA)
+  if (mainmode == MODE_PATCH)
+    add_patchjobs(pool, &job);
+#endif
+
+  // add mode
+  for (i = 0; i < job.count; i += 2)
+    {
+      job.elements[i] |= mode;
+      if (mode == SOLVER_UPDATE && pool_isemptyupdatejob(pool, job.elements[i], job.elements[i + 1]))
+       job.elements[i] ^= SOLVER_UPDATE ^ SOLVER_INSTALL;
+      if (cleandeps)
+        job.elements[i] |= SOLVER_CLEANDEPS;
+      if (forcebest)
+        job.elements[i] |= SOLVER_FORCEBEST;
+    }
+
+  // multiversion test
+  // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae", 1));
+  // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-base", 1));
+  // queue_push2(&job, SOLVER_MULTIVERSION|SOLVER_SOLVABLE_NAME, pool_str2id(pool, "kernel-pae-extra", 1));
+#if 0
+  queue_push2(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
+  queue_push2(&job, SOLVER_ERASE|SOLVER_CLEANDEPS|SOLVER_SOLVABLE_PROVIDES, pool_rel2id(pool, NAMESPACE_LANGUAGE, 0, REL_NAMESPACE, 1));
+#endif
+
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA))
+rerunsolver:
+#endif
+  solv = solver_create(pool);
+  solver_set_flag(solv, SOLVER_FLAG_SPLITPROVIDES, 1);
+#ifdef FEDORA
+  solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
+#endif
+  if (mainmode == MODE_ERASE)
+    solver_set_flag(solv, SOLVER_FLAG_ALLOW_UNINSTALL, 1);     /* don't nag */
+  solver_set_flag(solv, SOLVER_FLAG_BEST_OBEY_POLICY, 1);
+
+  for (;;)
+    {
+      Id problem, solution;
+      int pcnt, scnt;
+
+      if (!solver_solve(solv, &job))
+       break;
+      pcnt = solver_problem_count(solv);
+      printf("Found %d problems:\n", pcnt);
+      for (problem = 1; problem <= pcnt; problem++)
+       {
+         int take = 0;
+         printf("Problem %d/%d:\n", problem, pcnt);
+         solver_printprobleminfo(solv, problem);
+         printf("\n");
+         scnt = solver_solution_count(solv, problem);
+         for (solution = 1; solution <= scnt; solution++)
+           {
+             printf("Solution %d:\n", solution);
+             solver_printsolution(solv, problem, solution);
+             printf("\n");
+           }
+         for (;;)
+           {
+             char inbuf[128], *ip;
+             printf("Please choose a solution: ");
+             fflush(stdout);
+             *inbuf = 0;
+             if (!(ip = fgets(inbuf, sizeof(inbuf), stdin)))
+               {
+                 printf("Abort.\n");
+                 exit(1);
+               }
+             while (*ip == ' ' || *ip == '\t')
+               ip++;
+             if (*ip >= '0' && *ip <= '9')
+               {
+                 take = atoi(ip);
+                 if (take >= 1 && take <= scnt)
+                   break;
+               }
+             if (*ip == 's')
+               {
+                 take = 0;
+                 break;
+               }
+             if (*ip == 'q')
+               {
+                 printf("Abort.\n");
+                 exit(1);
+               }
+           }
+         if (!take)
+           continue;
+         solver_take_solution(solv, problem, take, &job);
+       }
+    }
+
+  trans = solver_create_transaction(solv);
+  if (!trans->steps.count)
+    {
+      printf("Nothing to do.\n");
+      transaction_free(trans);
+      solver_free(solv);
+      queue_free(&job);
+      pool_free(pool);
+      free_repoinfos(repoinfos, nrepoinfos);
+      solv_free(commandlinepkgs);
+      exit(1);
+    }
+
+  /* display transaction to the user and ask for confirmation */
+  printf("\n");
+  printf("Transaction summary:\n\n");
+  transaction_print(trans);
+#if defined(SUSE)
+  showdiskusagechanges(trans);
+#endif
+  printf("install size change: %d K\n", transaction_calc_installsizechange(trans));
+  printf("\n");
+
+  if (!yesno("OK to continue (y/n)? "))
+    {
+      printf("Abort.\n");
+      transaction_free(trans);
+      solver_free(solv);
+      queue_free(&job);
+      pool_free(pool);
+      free_repoinfos(repoinfos, nrepoinfos);
+      solv_free(commandlinepkgs);
+      exit(1);
+    }
+
+  /* download all new packages */
+  queue_init(&checkq);
+  newpkgs = transaction_installedresult(trans, &checkq);
+  newpkgsfps = 0;
+  if (newpkgs)
+    {
+      int downloadsize = 0;
+      for (i = 0; i < newpkgs; i++)
+       {
+         Solvable *s;
+
+         p = checkq.elements[i];
+         s = pool_id2solvable(pool, p);
+         downloadsize += solvable_lookup_sizek(s, SOLVABLE_DOWNLOADSIZE, 0);
+       }
+      printf("Downloading %d packages, %d K\n", newpkgs, downloadsize);
+      newpkgsfps = solv_calloc(newpkgs, sizeof(*newpkgsfps));
+      for (i = 0; i < newpkgs; i++)
+       {
+         const char *loc;
+         Solvable *s;
+         struct repoinfo *cinfo;
+
+         p = checkq.elements[i];
+         s = pool_id2solvable(pool, p);
+         if (s->repo == commandlinerepo)
+           {
+             loc = solvable_lookup_location(s, 0);
+             if (!loc)
+               continue;
+             if (!(newpkgsfps[i] = fopen(loc, "r")))
+               {
+                 perror(loc);
+                 exit(1);
+               }
+             putchar('.');
+             continue;
+           }
+         cinfo = s->repo->appdata;
+         if (!cinfo || cinfo->type == TYPE_INSTALLED)
+           {
+             printf("%s: no repository information\n", s->repo->name);
+             exit(1);
+           }
+         loc = solvable_lookup_location(s, 0);
+         if (!loc)
+            continue;  /* pseudo package? */
+#if defined(ENABLE_RPMDB)
+         if (pool->installed && pool->installed->nsolvables)
+           {
+             if ((newpkgsfps[i] = trydeltadownload(s, loc)) != 0)
+               {
+                 putchar('d');
+                 fflush(stdout);
+                 continue;             /* delta worked! */
+               }
+           }
+#endif
+         if ((newpkgsfps[i] = downloadpackage(s, loc)) == 0)
+           {
+             printf("\n%s: %s not found in repository\n", s->repo->name, loc);
+             exit(1);
+           }
+         putchar('.');
+         fflush(stdout);
+       }
+      putchar('\n');
+    }
+
+#if defined(ENABLE_RPMDB) && (defined(SUSE) || defined(FEDORA) || defined(MANDRIVA) || defined(MAGEIA))
+  /* check for file conflicts */
+  if (newpkgs)
+    {
+      Queue conflicts;
+      queue_init(&conflicts);
+      if (checkfileconflicts(pool, &checkq, newpkgs, newpkgsfps, &conflicts))
+       {
+         if (yesno("Re-run solver (y/n/q)? "))
+           {
+             for (i = 0; i < newpkgs; i++)
+               if (newpkgsfps[i])
+                 fclose(newpkgsfps[i]);
+             newpkgsfps = solv_free(newpkgsfps);
+             solver_free(solv);
+             solv = 0;
+             pool_add_fileconflicts_deps(pool, &conflicts);
+             queue_free(&conflicts);
+             goto rerunsolver;
+           }
+       }
+      queue_free(&conflicts);
+    }
+#endif
+
+  /* and finally commit the transaction */
+  printf("Committing transaction:\n\n");
+  transaction_order(trans, 0);
+  for (i = 0; i < trans->steps.count; i++)
+    {
+      int j;
+      FILE *fp;
+      Id type;
+
+      p = trans->steps.elements[i];
+      type = transaction_type(trans, p, SOLVER_TRANSACTION_RPM_ONLY);
+      switch(type)
+       {
+       case SOLVER_TRANSACTION_ERASE:
+         printf("erase %s\n", pool_solvid2str(pool, p));
+         commit_transactionelement(pool, type, p, 0);
+         break;
+       case SOLVER_TRANSACTION_INSTALL:
+       case SOLVER_TRANSACTION_MULTIINSTALL:
+         printf("install %s\n", pool_solvid2str(pool, p));
+         for (j = 0; j < newpkgs; j++)
+           if (checkq.elements[j] == p)
+             break;
+         fp = j < newpkgs ? newpkgsfps[j] : 0;
+         if (!fp)
+           continue;
+         commit_transactionelement(pool, type, p, fp);
+         fclose(fp);
+         newpkgsfps[j] = 0;
+         break;
+       default:
+         break;
+       }
+    }
+
+  for (i = 0; i < newpkgs; i++)
+    if (newpkgsfps[i])
+      fclose(newpkgsfps[i]);
+  solv_free(newpkgsfps);
+  queue_free(&checkq);
+  transaction_free(trans);
+  solver_free(solv);
+  queue_free(&job);
+  pool_free(pool);
+  free_repoinfos(repoinfos, nrepoinfos);
+  solv_free(commandlinepkgs);
+  exit(0);
+}
index 7edfc6c..4c767e1 100644 (file)
@@ -265,8 +265,13 @@ repo_add_autopattern(Repo *repo, int flags)
            repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ICON, newname);
          else if (!strcmp(pn, "pattern-order()") && evr)
            repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ORDER, newname);
-         else if (!strcmp(pn, "pattern-visible()") && !evr)
-           repodata_set_void(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE);
+         else if (!strcmp(pn, "pattern-visible()"))
+           {
+             if (!evr)
+               repodata_set_void(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE);
+             else
+               repodata_set_str(data, s2 - pool->solvables, SOLVABLE_ISVISIBLE, newname);
+           }
        }
     }
   queue_free(&patq);
index 3d0a91d..345d416 100644 (file)
@@ -123,6 +123,8 @@ repo_add_mdk(Repo *repo, FILE *fp, int flags)
        s->provides = parse_deps(s, buf + 10, 0);
       else if (!strncmp(buf + 1, "requires@", 9))
        s->requires = parse_deps(s, buf + 10, SOLVABLE_PREREQMARKER);
+      else if (!strncmp(buf + 1, "recommends@", 11))
+       s->recommends = parse_deps(s, buf + 10, 0);
       else if (!strncmp(buf + 1, "suggests@", 9))
        s->suggests = parse_deps(s, buf + 10, 0);
       else if (!strncmp(buf + 1, "obsoletes@", 10))
index e4346fe..3c40451 100644 (file)
@@ -33,18 +33,19 @@ static struct job2str {
   Id job;
   const char *str;
 } job2str[] = {
-  { SOLVER_NOOP,          "noop" },
-  { SOLVER_INSTALL,       "install" },
-  { SOLVER_ERASE,         "erase" },
-  { SOLVER_UPDATE,        "update" },
-  { SOLVER_WEAKENDEPS,    "weakendeps" },
-  { SOLVER_MULTIVERSION,  "multiversion" },
-  { SOLVER_MULTIVERSION,  "noobsoletes" },     /* old name */
-  { SOLVER_LOCK,          "lock" },
-  { SOLVER_DISTUPGRADE,   "distupgrade" },
-  { SOLVER_VERIFY,        "verify" },
-  { SOLVER_DROP_ORPHANED, "droporphaned" },
-  { SOLVER_USERINSTALLED, "userinstalled" },
+  { SOLVER_NOOP,           "noop" },
+  { SOLVER_INSTALL,        "install" },
+  { SOLVER_ERASE,          "erase" },
+  { SOLVER_UPDATE,         "update" },
+  { SOLVER_WEAKENDEPS,     "weakendeps" },
+  { SOLVER_MULTIVERSION,   "multiversion" },
+  { SOLVER_MULTIVERSION,   "noobsoletes" },    /* old name */
+  { SOLVER_LOCK,           "lock" },
+  { SOLVER_DISTUPGRADE,    "distupgrade" },
+  { SOLVER_VERIFY,         "verify" },
+  { SOLVER_DROP_ORPHANED,  "droporphaned" },
+  { SOLVER_USERINSTALLED,  "userinstalled" },
+  { SOLVER_ALLOWUNINSTALL, "allowuninstall" },
   { 0, 0 }
 };
 
index b98c3ba..5ddad42 100644 (file)
@@ -1,10 +1,21 @@
 -------------------------------------------------------------------
+Fri Sep 25 11:54:02 CEST 2015 - mls@suse.de
+
+- support a generic string for pattern-visible() [bnc#900769]
+- add a SOLVER_ALLOWUNINSTALL job type
+- add ordercycle introspection
+- fix mkmask handling of a zero size
+- support 'recommends' in repo_mdk.c
+- support filelist parsing in installcheck
+- bump version to 0.6.13
+
+-------------------------------------------------------------------
 Tue Sep  1 13:37:11 CEST 2015 - mls@suse.de
 
 - added tcl bindings
 - improve debian ar archive handling
 - bindings: set the CLOEXEC flags in xfopen
-- bindings: support testcase writing
+- bindings: support testcase writing [bnc#946752]
 - support REL_ELSE as evr of REL_COND
 - bump version to 0.6.12
 
index 5d7190a..a2c0098 100644 (file)
@@ -17,7 +17,7 @@ SET (libsolv_SRCS
     bitmap.c poolarch.c poolvendor.c poolid.c strpool.c dirpool.c
     solver.c solverdebug.c repo_solv.c repo_write.c evr.c pool.c
     queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c
-    transaction.c rules.c problems.c linkedpkg.c cplxdeps.c
+    transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c
     chksum.c md5.c sha1.c sha2.c solvversion.c selection.c)
 
 SET (libsolv_HEADERS
index 1290afa..4f595bb 100644 (file)
@@ -73,14 +73,14 @@ relhash(Id name, Id evr, int flags)
 
 
 /* compute bitmask for value
- * returns smallest (2^n-1) > 2 * num
+ * returns smallest (2^n-1) > 2 * num + 3
  *
  * used for Hashtable 'modulo' operation
  */
 static inline Hashval
 mkmask(unsigned int num)
 {
-  num *= 2;
+  num = num * 2 + 3;
   while (num & (num - 1))
     num &= num - 1;
   return num * 2 - 1;
index 91186a7..9e47117 100644 (file)
@@ -408,6 +408,8 @@ SOLV_1.0 {
                transaction_obs_pkg;
                transaction_order;
                transaction_order_add_choices;
+               transaction_order_get_cycle;
+               transaction_order_get_cycleids;
                transaction_print;
                transaction_type;
        local:
diff --git a/src/order.c b/src/order.c
new file mode 100644 (file)
index 0000000..d560865
--- /dev/null
@@ -0,0 +1,1345 @@
+/*
+ * Copyright (c) 2007-2015, SUSE LLC
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * order.c
+ *
+ * Transaction ordering
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "transaction.h"
+#include "bitmap.h"
+#include "pool.h"
+#include "repo.h"
+#include "util.h"
+
+struct _TransactionElement {
+  Id p;                /* solvable id */
+  Id edges;    /* pointer into edges data */
+  Id mark;
+};
+
+struct _TransactionOrderdata {
+  struct _TransactionElement *tes;
+  int ntes;
+  Id *invedgedata;
+  int ninvedgedata;
+  Queue *cycles;
+};
+
+#define TYPE_BROKEN    (1<<0)
+#define TYPE_CON       (1<<1)
+
+#define TYPE_REQ_P     (1<<2)
+#define TYPE_PREREQ_P  (1<<3)
+
+#define TYPE_REQ       (1<<4)
+#define TYPE_PREREQ    (1<<5)
+
+#define TYPE_CYCLETAIL  (1<<16)
+#define TYPE_CYCLEHEAD  (1<<17)
+
+#define EDGEDATA_BLOCK 127
+
+void
+transaction_clone_orderdata(Transaction *trans, Transaction *srctrans)
+{
+  struct _TransactionOrderdata *od = srctrans->orderdata;
+  if (!od)
+    return;
+  trans->orderdata = solv_calloc(1, sizeof(*trans->orderdata));
+  trans->orderdata->tes = solv_memdup2(od->tes, od->ntes, sizeof(*od->tes));
+  trans->orderdata->ntes = od->ntes;
+  trans->orderdata->invedgedata = solv_memdup2(od->invedgedata, od->ninvedgedata, sizeof(Id));
+  trans->orderdata->ninvedgedata = od->ninvedgedata;
+  if (od->cycles)
+    {
+      trans->orderdata->cycles = solv_calloc(1, sizeof(Queue));
+      queue_init_clone(trans->orderdata->cycles, od->cycles);
+    }
+}
+
+void
+transaction_free_orderdata(Transaction *trans)
+{
+  if (trans->orderdata)
+    {
+      struct _TransactionOrderdata *od = trans->orderdata;
+      od->tes = solv_free(od->tes);
+      od->invedgedata = solv_free(od->invedgedata);
+      if (od->cycles)
+       {
+         queue_free(od->cycles);
+         od->cycles = solv_free(od->cycles);
+       }
+      trans->orderdata = solv_free(trans->orderdata);
+    }
+}
+
+struct orderdata {
+  Transaction *trans;
+  struct _TransactionElement *tes;
+  int ntes;
+  Id *edgedata;
+  int nedgedata;
+  Id *invedgedata;
+
+  Queue cycles;
+  Queue cyclesdata;
+  int ncycles;
+};
+
+static int
+addteedge(struct orderdata *od, int from, int to, int type)
+{
+  int i;
+  struct _TransactionElement *te;
+
+  if (from == to)
+    return 0;
+
+  /* printf("edge %d(%s) -> %d(%s) type %x\n", from, pool_solvid2str(pool, od->tes[from].p), to, pool_solvid2str(pool, od->tes[to].p), type); */
+
+  te = od->tes + from;
+  for (i = te->edges; od->edgedata[i]; i += 2)
+    if (od->edgedata[i] == to)
+      break;
+  /* test of brokenness */
+  if (type == TYPE_BROKEN)
+    return od->edgedata[i] && (od->edgedata[i + 1] & TYPE_BROKEN) != 0 ? 1 : 0;
+  if (od->edgedata[i])
+    {
+      od->edgedata[i + 1] |= type;
+      return 0;
+    }
+  if (i + 1 == od->nedgedata)
+    {
+      /* printf("tail add %d\n", i - te->edges); */
+      if (!i)
+       te->edges = ++i;
+      od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3, sizeof(Id), EDGEDATA_BLOCK);
+    }
+  else
+    {
+      /* printf("extend %d\n", i - te->edges); */
+      od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3 + (i - te->edges), sizeof(Id), EDGEDATA_BLOCK);
+      if (i > te->edges)
+       memcpy(od->edgedata + od->nedgedata, od->edgedata + te->edges, sizeof(Id) * (i - te->edges));
+      i = od->nedgedata + (i - te->edges);
+      te->edges = od->nedgedata;
+    }
+  od->edgedata[i] = to;
+  od->edgedata[i + 1] = type;
+  od->edgedata[i + 2] = 0;     /* end marker */
+  od->nedgedata = i + 3;
+  return 0;
+}
+
+static int
+addedge(struct orderdata *od, Id from, Id to, int type)
+{
+  Transaction *trans = od->trans;
+  Pool *pool = trans->pool;
+  Solvable *s;
+  struct _TransactionElement *te;
+  int i;
+
+  /* printf("addedge %d %d type %d\n", from, to, type); */
+  s = pool->solvables + from;
+  if (s->repo == pool->installed && trans->transaction_installed[from - pool->installed->start])
+    {
+      /* obsolete, map to install */
+      if (trans->transaction_installed[from - pool->installed->start] > 0)
+       from = trans->transaction_installed[from - pool->installed->start];
+      else
+       {
+         int ret = 0;
+         Queue ti;
+         Id tibuf[5];
+
+         queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
+         transaction_all_obs_pkgs(trans, from, &ti);
+         for (i = 0; i < ti.count; i++)
+           ret |= addedge(od, ti.elements[i], to, type);
+         queue_free(&ti);
+         return ret;
+       }
+    }
+  s = pool->solvables + to;
+  if (s->repo == pool->installed && trans->transaction_installed[to - pool->installed->start])
+    {
+      /* obsolete, map to install */
+      if (trans->transaction_installed[to - pool->installed->start] > 0)
+       to = trans->transaction_installed[to - pool->installed->start];
+      else
+       {
+         int ret = 0;
+         Queue ti;
+         Id tibuf[5];
+
+         queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
+         transaction_all_obs_pkgs(trans, to, &ti);
+         for (i = 0; i < ti.count; i++)
+           ret |= addedge(od, from, ti.elements[i], type);
+         queue_free(&ti);
+         return ret;
+       }
+    }
+
+  /* map from/to to te numbers */
+  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+    if (te->p == to)
+      break;
+  if (i == od->ntes)
+    return 0;
+  to = i;
+
+  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+    if (te->p == from)
+      break;
+  if (i == od->ntes)
+    return 0;
+
+  return addteedge(od, i, to, type);
+}
+
+static inline int
+havescripts(Pool *pool, Id solvid)
+{
+  Solvable *s = pool->solvables + solvid;
+  const char *dep;
+  if (s->requires)
+    {
+      Id req, *reqp;
+      int inpre = 0;
+      reqp = s->repo->idarraydata + s->requires;
+      while ((req = *reqp++) != 0)
+       {
+          if (req == SOLVABLE_PREREQMARKER)
+           {
+             inpre = 1;
+             continue;
+           }
+         if (!inpre)
+           continue;
+         dep = pool_id2str(pool, req);
+         if (*dep == '/' && strcmp(dep, "/sbin/ldconfig") != 0)
+           return 1;
+       }
+    }
+  return 0;
+}
+
+static void
+addsolvableedges(struct orderdata *od, Solvable *s)
+{
+  Transaction *trans = od->trans;
+  Pool *pool = trans->pool;
+  Id req, *reqp, con, *conp;
+  Id p, p2, pp2;
+  int i, j, pre, numins;
+  Repo *installed = pool->installed;
+  Solvable *s2;
+  Queue reqq;
+  int provbyinst;
+
+#if 0
+  printf("addsolvableedges %s\n", pool_solvable2str(pool, s));
+#endif
+  p = s - pool->solvables;
+  queue_init(&reqq);
+  if (s->requires)
+    {
+      reqp = s->repo->idarraydata + s->requires;
+      pre = TYPE_REQ;
+      while ((req = *reqp++) != 0)
+       {
+         if (req == SOLVABLE_PREREQMARKER)
+           {
+             pre = TYPE_PREREQ;
+             continue;
+           }
+#if 0
+         if (pre != TYPE_PREREQ && installed && s->repo == installed)
+           {
+             /* ignore normal requires if we're getting obsoleted */
+             if (trans->transaction_installed[p - pool->installed->start])
+               continue;
+           }
+#endif
+         queue_empty(&reqq);
+         numins = 0;   /* number of packages to be installed providing it */
+         provbyinst = 0;       /* provided by kept package */
+         FOR_PROVIDES(p2, pp2, req)
+           {
+             s2 = pool->solvables + p2;
+             if (p2 == p)
+               {
+                 reqq.count = 0;       /* self provides */
+                 break;
+               }
+             if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2))
+               {
+                 provbyinst = 1;
+#if 0
+                 printf("IGNORE inst provides %s by %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s2));
+                 reqq.count = 0;       /* provided by package that stays installed */
+                 break;
+#else
+                 continue;
+#endif
+               }
+             if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2))
+               continue;               /* package stays uninstalled */
+
+             if (s->repo == installed)
+               {
+                 /* s gets uninstalled */
+                 queue_pushunique(&reqq, p2);
+                 if (s2->repo != installed)
+                   numins++;
+               }
+             else
+               {
+                 if (s2->repo == installed)
+                   continue;   /* s2 gets uninstalled */
+                 queue_pushunique(&reqq, p2);
+               }
+           }
+         if (provbyinst)
+           {
+             /* prune to harmless ->inst edges */
+             for (i = j = 0; i < reqq.count; i++)
+               if (pool->solvables[reqq.elements[i]].repo != installed)
+                 reqq.elements[j++] = reqq.elements[i];
+             reqq.count = j;
+           }
+
+         if (numins && reqq.count)
+           {
+             if (s->repo == installed)
+               {
+                 for (i = 0; i < reqq.count; i++)
+                   {
+                     if (pool->solvables[reqq.elements[i]].repo == installed)
+                       {
+                         for (j = 0; j < reqq.count; j++)
+                           {
+                             if (pool->solvables[reqq.elements[j]].repo != installed)
+                               {
+                                 if (trans->transaction_installed[reqq.elements[i] - pool->installed->start] == reqq.elements[j])
+                                   continue;   /* no self edge */
+#if 0
+                                 printf("add interrreq uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, reqq.elements[i]), pool_dep2str(pool, req), pool_solvid2str(pool, reqq.elements[j]));
+#endif
+                                 addedge(od, reqq.elements[i], reqq.elements[j], pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
+                               }
+                           }
+                       }
+                   }
+               }
+             /* no mixed types, remove all deps on uninstalls */
+             for (i = j = 0; i < reqq.count; i++)
+               if (pool->solvables[reqq.elements[i]].repo != installed)
+                 reqq.elements[j++] = reqq.elements[i];
+             reqq.count = j;
+           }
+          if (!reqq.count)
+           continue;
+          for (i = 0; i < reqq.count; i++)
+           {
+             p2 = reqq.elements[i];
+             if (pool->solvables[p2].repo != installed)
+               {
+                 /* all elements of reqq are installs, thus have different TEs */
+                 if (pool->solvables[p].repo != installed)
+                   {
+#if 0
+                     printf("add inst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
+#endif
+                     addedge(od, p, p2, pre);
+                   }
+                 else
+                   {
+#if 0
+                     printf("add uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
+#endif
+                     addedge(od, p, p2, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
+                   }
+               }
+             else
+               {
+                 if (s->repo != installed)
+                   continue;   /* no inst->uninst edges, please! */
+
+                 /* uninst -> uninst edge. Those make trouble. Only add if we must */
+                 if (trans->transaction_installed[p - installed->start] && !havescripts(pool, p))
+                   {
+                     /* p is obsoleted by another package and has no scripts */
+                     /* we assume that the obsoletor is good enough to replace p */
+                     continue;
+                   }
+#if 0
+                 printf("add uninst->uninst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
+#endif
+                 addedge(od, p2, p, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
+               }
+           }
+       }
+    }
+  if (s->conflicts)
+    {
+      conp = s->repo->idarraydata + s->conflicts;
+      while ((con = *conp++) != 0)
+       {
+         FOR_PROVIDES(p2, pp2, con)
+           {
+             if (p2 == p)
+               continue;
+             s2 = pool->solvables + p2;
+             if (!s2->repo)
+               continue;
+             if (s->repo == installed)
+               {
+                 if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
+                   {
+                     /* deinstall p before installing p2 */
+#if 0
+                     printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, con), pool_solvid2str(pool, p));
+#endif
+                     addedge(od, p2, p, TYPE_CON);
+                   }
+               }
+             else
+               {
+                 if (s2->repo == installed && MAPTST(&trans->transactsmap, p2))
+                   {
+                     /* deinstall p2 before installing p */
+#if 0
+                     printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, con), pool_solvid2str(pool, p2));
+#endif
+                     addedge(od, p, p2, TYPE_CON);
+                   }
+               }
+
+           }
+       }
+    }
+  if (s->repo == installed && solvable_lookup_idarray(s, SOLVABLE_TRIGGERS, &reqq) && reqq.count)
+    {
+      /* we're getting deinstalled/updated. Try to do this before our
+       * triggers are hit */
+      for (i = 0; i < reqq.count; i++)
+       {
+         Id tri = reqq.elements[i];
+         FOR_PROVIDES(p2, pp2, tri)
+           {
+             if (p2 == p)
+               continue;
+             s2 = pool->solvables + p2;
+             if (!s2->repo)
+               continue;
+             if (s2->name == s->name)
+               continue;       /* obsoleted anyway */
+             if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
+               {
+                 /* deinstall/update p before installing p2 */
+#if 0
+                 printf("add trigger uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, tri), pool_solvid2str(pool, p));
+#endif
+                 addedge(od, p2, p, TYPE_CON);
+               }
+           }
+       }
+    }
+  queue_free(&reqq);
+}
+
+
+/* break an edge in a cycle */
+static void
+breakcycle(struct orderdata *od, Id *cycle)
+{
+  Pool *pool = od->trans->pool;
+  Id ddegmin, ddegmax, ddeg;
+  int k, l;
+  struct _TransactionElement *te;
+
+  l = 0;
+  ddegmin = ddegmax = 0;
+  for (k = 0; cycle[k + 1]; k += 2)
+    {
+      ddeg = od->edgedata[cycle[k + 1] + 1];
+      if (ddeg > ddegmax)
+       ddegmax = ddeg;
+      if (!k || ddeg < ddegmin)
+       {
+         l = k;
+         ddegmin = ddeg;
+         continue;
+       }
+      if (ddeg == ddegmin)
+       {
+         if (havescripts(pool, od->tes[cycle[l]].p) && !havescripts(pool, od->tes[cycle[k]].p))
+           {
+             /* prefer k, as l comes from a package with contains scriptlets */
+             l = k;
+             continue;
+           }
+         /* same edge value, check for prereq */
+       }
+    }
+
+  /* record brkoen cycle starting with the tail */
+  queue_push(&od->cycles, od->cyclesdata.count);               /* offset into data */
+  queue_push(&od->cycles, k / 2);                              /* cycle elements */
+  queue_push(&od->cycles, od->edgedata[cycle[l + 1] + 1]);     /* broken edge */
+  queue_push(&od->cycles, (ddegmax << 16) | ddegmin);          /* max/min values */
+  od->ncycles++;
+  for (k = l;;)
+    {
+      k += 2;
+      if (!cycle[k + 1])
+       k = 0;
+      queue_push(&od->cyclesdata, cycle[k]);
+      if (k == l)
+       break;
+    }
+  queue_push(&od->cyclesdata, 0);      /* mark end */
+
+  /* break that edge */
+  od->edgedata[cycle[l + 1] + 1] |= TYPE_BROKEN;
+
+#if 1
+  if (ddegmin < TYPE_REQ)
+    return;
+#endif
+
+  /* cycle recorded, print it */
+  if (ddegmin >= TYPE_REQ && (ddegmax & TYPE_PREREQ) != 0)
+    POOL_DEBUG(SOLV_DEBUG_STATS, "CRITICAL ");
+  POOL_DEBUG(SOLV_DEBUG_STATS, "cycle: --> ");
+  for (k = 0; cycle[k + 1]; k += 2)
+    {
+      te = od->tes + cycle[k];
+      if ((od->edgedata[cycle[k + 1] + 1] & TYPE_BROKEN) != 0)
+        POOL_DEBUG(SOLV_DEBUG_STATS, "%s ##%x##> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
+      else
+        POOL_DEBUG(SOLV_DEBUG_STATS, "%s --%x--> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
+    }
+  POOL_DEBUG(SOLV_DEBUG_STATS, "\n");
+}
+
+static inline void
+dump_tes(struct orderdata *od)
+{
+  Pool *pool = od->trans->pool;
+  int i, j;
+  Queue obsq;
+  struct _TransactionElement *te, *te2;
+
+  queue_init(&obsq);
+  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+    {
+      Solvable *s = pool->solvables + te->p;
+      POOL_DEBUG(SOLV_DEBUG_RESULT, "TE %4d: %c%s\n", i, s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
+      if (s->repo != pool->installed)
+        {
+         queue_empty(&obsq);
+         transaction_all_obs_pkgs(od->trans, te->p, &obsq);
+         for (j = 0; j < obsq.count; j++)
+           POOL_DEBUG(SOLV_DEBUG_RESULT, "         -%s\n", pool_solvid2str(pool, obsq.elements[j]));
+       }
+      for (j = te->edges; od->edgedata[j]; j += 2)
+       {
+         te2 = od->tes + od->edgedata[j];
+         if ((od->edgedata[j + 1] & TYPE_BROKEN) == 0)
+           POOL_DEBUG(SOLV_DEBUG_RESULT, "       --%x--> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
+         else
+           POOL_DEBUG(SOLV_DEBUG_RESULT, "       ##%x##> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
+       }
+    }
+}
+
+static void
+reachable(struct orderdata *od, Id i)
+{
+  struct _TransactionElement *te = od->tes + i;
+  int j, k;
+
+  if (te->mark != 0)
+    return;
+  te->mark = 1;
+  for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+    {
+      if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+       continue;
+      if (!od->tes[k].mark)
+        reachable(od, k);
+      if (od->tes[k].mark == 2)
+       {
+         te->mark = 2;
+         return;
+       }
+    }
+  te->mark = -1;
+}
+
+static void
+addcycleedges(struct orderdata *od, Id *cycle, Queue *todo)
+{
+#if 0
+  Transaction *trans = od->trans;
+  Pool *pool = trans->pool;
+#endif
+  struct _TransactionElement *te;
+  int i, j, k, tail;
+  int head;
+
+#if 0
+  printf("addcycleedges\n");
+  for (i = 0; (j = cycle[i]) != 0; i++)
+    printf("cycle %s\n", pool_solvid2str(pool, od->tes[j].p));
+#endif
+
+  /* first add all the tail cycle edges */
+
+  /* see what we can reach from the cycle */
+  queue_empty(todo);
+  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+    te->mark = 0;
+  for (i = 0; (j = cycle[i]) != 0; i++)
+    {
+      od->tes[j].mark = -1;
+      queue_push(todo, j);
+    }
+  while (todo->count)
+    {
+      i = queue_pop(todo);
+      te = od->tes + i;
+      if (te->mark > 0)
+       continue;
+      te->mark = te->mark < 0 ? 2 : 1;
+      for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+       {
+         if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+           continue;
+         if (od->tes[k].mark > 0)
+           continue;   /* no need to visit again */
+         queue_push(todo, k);
+       }
+    }
+  /* now all cycle TEs are marked with 2, all TEs reachable
+   * from the cycle are marked with 1 */
+  tail = cycle[0];
+  od->tes[tail].mark = 1;      /* no need to add edges */
+
+  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+    {
+      if (te->mark)
+       continue;       /* reachable from cycle */
+      for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+       {
+         if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+           continue;
+         if (od->tes[k].mark != 2)
+           continue;
+         /* We found an edge to the cycle. Add an extra edge to the tail */
+         /* the TE was not reachable, so we're not creating a new cycle! */
+#if 0
+         printf("adding TO TAIL cycle edge %d->%d %s->%s!\n", i, tail, pool_solvid2str(pool, od->tes[i].p), pool_solvid2str(pool, od->tes[tail].p));
+#endif
+         j -= te->edges;       /* in case we move */
+         addteedge(od, i, tail, TYPE_CYCLETAIL);
+         j += te->edges;
+         break;        /* one edge is enough */
+       }
+    }
+
+  /* now add all head cycle edges */
+
+  /* reset marks */
+  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+    te->mark = 0;
+  head = 0;
+  for (i = 0; (j = cycle[i]) != 0; i++)
+    {
+      head = j;
+      od->tes[j].mark = 2;
+    }
+  /* first the head to save some time */
+  te = od->tes + head;
+  for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+    {
+      if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+       continue;
+      if (!od->tes[k].mark)
+       reachable(od, k);
+      if (od->tes[k].mark == -1)
+       od->tes[k].mark = -2;   /* no need for another edge */
+    }
+  for (i = 0; cycle[i] != 0; i++)
+    {
+      if (cycle[i] == head)
+       break;
+      te = od->tes + cycle[i];
+      for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
+       {
+         if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
+           continue;
+         /* see if we can reach a cycle TE from k */
+         if (!od->tes[k].mark)
+           reachable(od, k);
+         if (od->tes[k].mark == -1)
+           {
+#if 0
+             printf("adding FROM HEAD cycle edge %d->%d %s->%s [%s]!\n", head, k, pool_solvid2str(pool, od->tes[head].p), pool_solvid2str(pool, od->tes[k].p), pool_solvid2str(pool, od->tes[cycle[i]].p));
+#endif
+             addteedge(od, head, k, TYPE_CYCLEHEAD);
+             od->tes[k].mark = -2;     /* no need to add that one again */
+           }
+       }
+    }
+}
+
+void
+transaction_order(Transaction *trans, int flags)
+{
+  Pool *pool = trans->pool;
+  Queue *tr = &trans->steps;
+  Repo *installed = pool->installed;
+  Id p;
+  Solvable *s;
+  int i, j, k, numte, numedge;
+  struct orderdata od;
+  struct _TransactionElement *te;
+  Queue todo, obsq, samerepoq, uninstq;
+  int cycstart, cycel;
+  Id *cycle;
+  int oldcount;
+  int start, now;
+  Repo *lastrepo;
+  int lastmedia;
+  Id *temedianr;
+
+  start = now = solv_timems(0);
+  POOL_DEBUG(SOLV_DEBUG_STATS, "ordering transaction\n");
+  /* free old data if present */
+  if (trans->orderdata)
+    {
+      struct _TransactionOrderdata *od = trans->orderdata;
+      od->tes = solv_free(od->tes);
+      od->invedgedata = solv_free(od->invedgedata);
+      trans->orderdata = solv_free(trans->orderdata);
+    }
+
+  /* create a transaction element for every active component */
+  numte = 0;
+  for (i = 0; i < tr->count; i++)
+    {
+      p = tr->elements[i];
+      s = pool->solvables + p;
+      if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
+       continue;
+      numte++;
+    }
+  POOL_DEBUG(SOLV_DEBUG_STATS, "transaction elements: %d\n", numte);
+  if (!numte)
+    return;    /* nothing to do... */
+
+  numte++;     /* leave first one zero */
+  memset(&od, 0, sizeof(od));
+  od.trans = trans;
+  od.ntes = numte;
+  od.tes = solv_calloc(numte, sizeof(*od.tes));
+  od.edgedata = solv_extend(0, 0, 1, sizeof(Id), EDGEDATA_BLOCK);
+  od.edgedata[0] = 0;
+  od.nedgedata = 1;
+  queue_init(&od.cycles);
+
+  /* initialize TEs */
+  for (i = 0, te = od.tes + 1; i < tr->count; i++)
+    {
+      p = tr->elements[i];
+      s = pool->solvables + p;
+      if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
+       continue;
+      te->p = p;
+      te++;
+    }
+
+  /* create dependency graph */
+  for (i = 0; i < tr->count; i++)
+    addsolvableedges(&od, pool->solvables + tr->elements[i]);
+
+  /* count edges */
+  numedge = 0;
+  for (i = 1, te = od.tes + i; i < numte; i++, te++)
+    for (j = te->edges; od.edgedata[j]; j += 2)
+      numedge++;
+  POOL_DEBUG(SOLV_DEBUG_STATS, "edges: %d, edge space: %d\n", numedge, od.nedgedata / 2);
+  POOL_DEBUG(SOLV_DEBUG_STATS, "edge creation took %d ms\n", solv_timems(now));
+
+#if 0
+  dump_tes(&od);
+#endif
+
+  now = solv_timems(0);
+  /* kill all cycles */
+  queue_init(&todo);
+  for (i = numte - 1; i > 0; i--)
+    queue_push(&todo, i);
+
+  while (todo.count)
+    {
+      i = queue_pop(&todo);
+      /* printf("- look at TE %d\n", i); */
+      if (i < 0)
+       {
+         i = -i;
+         od.tes[i].mark = 2;   /* done with that one */
+         continue;
+       }
+      te = od.tes + i;
+      if (te->mark == 2)
+       continue;               /* already finished before */
+      if (te->mark == 0)
+       {
+         int edgestovisit = 0;
+         /* new node, visit edges */
+         for (j = te->edges; (k = od.edgedata[j]) != 0; j += 2)
+           {
+             if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
+               continue;
+             if (od.tes[k].mark == 2)
+               continue;       /* no need to visit again */
+             if (!edgestovisit++)
+               queue_push(&todo, -i);  /* end of edges marker */
+             queue_push(&todo, k);
+           }
+         if (!edgestovisit)
+           te->mark = 2;       /* no edges, done with that one */
+         else
+           te->mark = 1;       /* under investigation */
+         continue;
+       }
+      /* oh no, we found a cycle */
+      /* find start of cycle node (<0) */
+      for (j = todo.count - 1; j >= 0; j--)
+       if (todo.elements[j] == -i)
+         break;
+      assert(j >= 0);
+      cycstart = j;
+      /* build te/edge chain */
+      k = cycstart;
+      for (j = k; j < todo.count; j++)
+       if (todo.elements[j] < 0)
+         todo.elements[k++] = -todo.elements[j];
+      cycel = k - cycstart;
+      assert(cycel > 1);
+      /* make room for edges, two extra element for cycle loop + terminating 0 */
+      while (todo.count < cycstart + 2 * cycel + 2)
+       queue_push(&todo, 0);
+      cycle = todo.elements + cycstart;
+      cycle[cycel] = i;                /* close the loop */
+      cycle[2 * cycel + 1] = 0;        /* terminator */
+      for (k = cycel; k > 0; k--)
+       {
+         cycle[k * 2] = cycle[k];
+         te = od.tes + cycle[k - 1];
+         assert(te->mark == 1);
+         te->mark = 0; /* reset investigation marker */
+         /* printf("searching for edge from %d to %d\n", cycle[k - 1], cycle[k]); */
+         for (j = te->edges; od.edgedata[j]; j += 2)
+           if (od.edgedata[j] == cycle[k])
+             break;
+         assert(od.edgedata[j]);
+         cycle[k * 2 - 1] = j;
+       }
+      /* now cycle looks like this: */
+      /* te1 edge te2 edge te3 ... teN edge te1 0 */
+      breakcycle(&od, cycle);
+      /* restart with start of cycle */
+      todo.count = cycstart + 1;
+    }
+  POOL_DEBUG(SOLV_DEBUG_STATS, "cycles broken: %d\n", od.ncycles);
+  POOL_DEBUG(SOLV_DEBUG_STATS, "cycle breaking took %d ms\n", solv_timems(now));
+
+  now = solv_timems(0);
+  /* now go through all broken cycles and create cycle edges to help
+     the ordering */
+   for (i = od.cycles.count - 4; i >= 0; i -= 4)
+     {
+       if (od.cycles.elements[i + 2] >= TYPE_REQ)
+         addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
+     }
+   for (i = od.cycles.count - 4; i >= 0; i -= 4)
+     {
+       if (od.cycles.elements[i + 2] < TYPE_REQ)
+         addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
+     }
+  POOL_DEBUG(SOLV_DEBUG_STATS, "cycle edge creation took %d ms\n", solv_timems(now));
+
+#if 0
+  dump_tes(&od);
+#endif
+  /* all edges are finally set up and there are no cycles, now the easy part.
+   * Create an ordered transaction */
+  now = solv_timems(0);
+  /* first invert all edges */
+  for (i = 1, te = od.tes + i; i < numte; i++, te++)
+    te->mark = 1;      /* term 0 */
+  for (i = 1, te = od.tes + i; i < numte; i++, te++)
+    {
+      for (j = te->edges; od.edgedata[j]; j += 2)
+        {
+         if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
+           continue;
+         od.tes[od.edgedata[j]].mark++;
+       }
+    }
+  j = 1;
+  for (i = 1, te = od.tes + i; i < numte; i++, te++)
+    {
+      te->mark += j;
+      j = te->mark;
+    }
+  POOL_DEBUG(SOLV_DEBUG_STATS, "invedge space: %d\n", j + 1);
+  od.invedgedata = solv_calloc(j + 1, sizeof(Id));
+  for (i = 1, te = od.tes + i; i < numte; i++, te++)
+    {
+      for (j = te->edges; od.edgedata[j]; j += 2)
+        {
+         if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
+           continue;
+         od.invedgedata[--od.tes[od.edgedata[j]].mark] = i;
+       }
+    }
+  for (i = 1, te = od.tes + i; i < numte; i++, te++)
+    te->edges = te->mark;      /* edges now points into invedgedata */
+  od.edgedata = solv_free(od.edgedata);
+  od.nedgedata = j + 1;
+
+  /* now the final ordering */
+  for (i = 1, te = od.tes + i; i < numte; i++, te++)
+    te->mark = 0;
+  for (i = 1, te = od.tes + i; i < numte; i++, te++)
+    for (j = te->edges; od.invedgedata[j]; j++)
+      od.tes[od.invedgedata[j]].mark++;
+
+  queue_init(&samerepoq);
+  queue_init(&uninstq);
+  queue_empty(&todo);
+  for (i = 1, te = od.tes + i; i < numte; i++, te++)
+    if (te->mark == 0)
+      {
+       if (installed && pool->solvables[te->p].repo == installed)
+          queue_push(&uninstq, i);
+       else
+          queue_push(&todo, i);
+      }
+  assert(todo.count > 0 || uninstq.count > 0);
+  oldcount = tr->count;
+  queue_empty(tr);
+
+  queue_init(&obsq);
+
+  lastrepo = 0;
+  lastmedia = 0;
+  temedianr = solv_calloc(numte, sizeof(Id));
+  for (i = 1; i < numte; i++)
+    {
+      Solvable *s = pool->solvables + od.tes[i].p;
+      if (installed && s->repo == installed)
+       j = 1;
+      else
+        j = solvable_lookup_num(s, SOLVABLE_MEDIANR, 1);
+      temedianr[i] = j;
+    }
+  for (;;)
+    {
+      /* select an TE i */
+      if (uninstq.count)
+       i = queue_shift(&uninstq);
+      else if (samerepoq.count)
+       i = queue_shift(&samerepoq);
+      else if (todo.count)
+       {
+         /* find next repo/media */
+         for (j = 0; j < todo.count; j++)
+           {
+             if (!j || temedianr[todo.elements[j]] < lastmedia)
+               {
+                 i = j;
+                 lastmedia = temedianr[todo.elements[j]];
+               }
+           }
+         lastrepo = pool->solvables[od.tes[todo.elements[i]].p].repo;
+
+         /* move all matching TEs to samerepoq */
+         for (i = j = 0; j < todo.count; j++)
+           {
+             int k = todo.elements[j];
+             if (temedianr[k] == lastmedia && pool->solvables[od.tes[k].p].repo == lastrepo)
+               queue_push(&samerepoq, k);
+             else
+               todo.elements[i++] = k;
+           }
+         todo.count = i;
+
+         assert(samerepoq.count);
+         i = queue_shift(&samerepoq);
+       }
+      else
+       break;
+
+      te = od.tes + i;
+      queue_push(tr, te->p);
+#if 0
+printf("do %s [%d]\n", pool_solvid2str(pool, te->p), temedianr[i]);
+#endif
+      s = pool->solvables + te->p;
+      for (j = te->edges; od.invedgedata[j]; j++)
+       {
+         struct _TransactionElement *te2 = od.tes + od.invedgedata[j];
+         assert(te2->mark > 0);
+         if (--te2->mark == 0)
+           {
+             Solvable *s = pool->solvables + te2->p;
+#if 0
+printf("free %s [%d]\n", pool_solvid2str(pool, te2->p), temedianr[od.invedgedata[j]]);
+#endif
+             if (installed && s->repo == installed)
+               queue_push(&uninstq, od.invedgedata[j]);
+             else if (s->repo == lastrepo && temedianr[od.invedgedata[j]] == lastmedia)
+               queue_push(&samerepoq, od.invedgedata[j]);
+             else
+               queue_push(&todo, od.invedgedata[j]);
+           }
+       }
+    }
+  solv_free(temedianr);
+  queue_free(&todo);
+  queue_free(&samerepoq);
+  queue_free(&uninstq);
+  queue_free(&obsq);
+  for (i = 1, te = od.tes + i; i < numte; i++, te++)
+    assert(te->mark == 0);
+
+  /* add back obsoleted packages */
+  transaction_add_obsoleted(trans);
+  assert(tr->count == oldcount);
+
+  POOL_DEBUG(SOLV_DEBUG_STATS, "creating new transaction took %d ms\n", solv_timems(now));
+  POOL_DEBUG(SOLV_DEBUG_STATS, "transaction ordering took %d ms\n", solv_timems(start));
+
+  if ((flags & (SOLVER_TRANSACTION_KEEP_ORDERDATA | SOLVER_TRANSACTION_KEEP_ORDERCYCLES)) != 0)
+    {
+      struct _TransactionOrderdata *tod;
+      trans->orderdata = tod = solv_calloc(1, sizeof(*trans->orderdata));
+      if ((flags & SOLVER_TRANSACTION_KEEP_ORDERCYCLES) != 0)
+       {
+         Queue *cycles = tod->cycles = solv_calloc(1, sizeof(Queue));
+         queue_init_clone(cycles, &od.cyclesdata);
+         /* map from tes to packages */
+         for (i = 0; i < cycles->count; i++)
+           if (cycles->elements[i])
+             cycles->elements[i] = od.tes[cycles->elements[i]].p;
+         queue_insertn(cycles, cycles->count, od.cycles.count, od.cycles.elements);
+         queue_push(cycles, od.cycles.count / 4);
+       }
+      if ((flags & SOLVER_TRANSACTION_KEEP_ORDERDATA) != 0)
+       {
+         tod->tes = od.tes;
+         tod->ntes = numte;
+         tod->invedgedata = od.invedgedata;
+         tod->ninvedgedata = od.nedgedata;
+         od.tes = 0;
+         od.invedgedata = 0;
+       }
+    }
+  solv_free(od.tes);
+  solv_free(od.invedgedata);
+  queue_free(&od.cycles);
+  queue_free(&od.cyclesdata);
+}
+
+
+int
+transaction_order_add_choices(Transaction *trans, Id chosen, Queue *choices)
+{
+  int i, j;
+  struct _TransactionOrderdata *od = trans->orderdata;
+  struct _TransactionElement *te;
+
+  if (!od)
+     return choices->count;
+  if (!chosen)
+    {
+      /* initialization step */
+      for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+       te->mark = 0;
+      for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+       {
+         for (j = te->edges; od->invedgedata[j]; j++)
+           od->tes[od->invedgedata[j]].mark++;
+       }
+      for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+       if (!te->mark)
+         queue_push(choices, te->p);
+      return choices->count;
+    }
+  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
+    if (te->p == chosen)
+      break;
+  if (i == od->ntes)
+    return choices->count;
+  if (te->mark > 0)
+    {
+      /* hey! out-of-order installation! */
+      te->mark = -1;
+    }
+  for (j = te->edges; od->invedgedata[j]; j++)
+    {
+      te = od->tes + od->invedgedata[j];
+      assert(te->mark > 0 || te->mark == -1);
+      if (te->mark > 0 && --te->mark == 0)
+       queue_push(choices, te->p);
+    }
+  return choices->count;
+}
+
+void
+transaction_add_obsoleted(Transaction *trans)
+{
+  Pool *pool = trans->pool;
+  Repo *installed = pool->installed;
+  Id p;
+  Solvable *s;
+  int i, j, k, max;
+  Map done;
+  Queue obsq, *steps;
+
+  if (!installed || !trans->steps.count)
+    return;
+  /* calculate upper bound */
+  max = 0;
+  FOR_REPO_SOLVABLES(installed, p, s)
+    if (MAPTST(&trans->transactsmap, p))
+      max++;
+  if (!max)
+    return;
+  /* make room */
+  steps = &trans->steps;
+  queue_insertn(steps, 0, max, 0);
+
+  /* now add em */
+  map_init(&done, installed->end - installed->start);
+  queue_init(&obsq);
+  for (j = 0, i = max; i < steps->count; i++)
+    {
+      p = trans->steps.elements[i];
+      if (pool->solvables[p].repo == installed)
+       {
+         if (!trans->transaction_installed[p - pool->installed->start])
+           trans->steps.elements[j++] = p;
+         continue;
+       }
+      trans->steps.elements[j++] = p;
+      queue_empty(&obsq);
+      transaction_all_obs_pkgs(trans, p, &obsq);
+      for (k = 0; k < obsq.count; k++)
+       {
+         p = obsq.elements[k];
+         assert(p >= installed->start && p < installed->end);
+         if (!MAPTST(&trans->transactsmap, p)) /* just in case */
+           continue;
+         if (MAPTST(&done, p - installed->start))
+           continue;
+         MAPSET(&done, p - installed->start);
+         trans->steps.elements[j++] = p;
+       }
+    }
+
+  /* free unneeded space */
+  queue_truncate(steps, j);
+  map_free(&done);
+  queue_free(&obsq);
+}
+
+static void
+transaction_check_pkg(Transaction *trans, Id tepkg, Id pkg, Map *ins, Map *seen, int onlyprereq, Id noconfpkg, int depth)
+{
+  Pool *pool = trans->pool;
+  Id p, pp;
+  Solvable *s;
+  int good;
+
+  if (MAPTST(seen, pkg))
+    return;
+  MAPSET(seen, pkg);
+  s = pool->solvables + pkg;
+#if 0
+  printf("- %*s%c%s\n", depth * 2, "", s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
+#endif
+  if (s->requires)
+    {
+      Id req, *reqp;
+      int inpre = 0;
+      reqp = s->repo->idarraydata + s->requires;
+      while ((req = *reqp++) != 0)
+       {
+          if (req == SOLVABLE_PREREQMARKER)
+           {
+             inpre = 1;
+             continue;
+           }
+         if (onlyprereq && !inpre)
+           continue;
+         if (!strncmp(pool_id2str(pool, req), "rpmlib(", 7))
+           continue;
+         good = 0;
+         /* first check kept packages, then freshly installed, then not yet uninstalled */
+         FOR_PROVIDES(p, pp, req)
+           {
+             if (!MAPTST(ins, p))
+               continue;
+             if (MAPTST(&trans->transactsmap, p))
+               continue;
+             good++;
+             transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
+           }
+         if (!good)
+           {
+             FOR_PROVIDES(p, pp, req)
+               {
+                 if (!MAPTST(ins, p))
+                   continue;
+                 if (pool->solvables[p].repo == pool->installed)
+                   continue;
+                 good++;
+                 transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
+               }
+           }
+         if (!good)
+           {
+             FOR_PROVIDES(p, pp, req)
+               {
+                 if (!MAPTST(ins, p))
+                   continue;
+                 good++;
+                 transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
+               }
+           }
+         if (!good)
+           {
+             POOL_DEBUG(SOLV_DEBUG_RESULT, "  %c%s: nothing provides %s needed by %c%s\n", pool->solvables[tepkg].repo == pool->installed ? '-' : '+', pool_solvid2str(pool, tepkg), pool_dep2str(pool, req), s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
+           }
+       }
+    }
+}
+
+void
+transaction_check_order(Transaction *trans)
+{
+  Pool *pool = trans->pool;
+  Solvable *s;
+  Id p, lastins;
+  Map ins, seen;
+  int i;
+
+  POOL_DEBUG(SOLV_DEBUG_RESULT, "\nchecking transaction order...\n");
+  map_init(&ins, pool->nsolvables);
+  map_init(&seen, pool->nsolvables);
+  if (pool->installed)
+    FOR_REPO_SOLVABLES(pool->installed, p, s)
+      MAPSET(&ins, p);
+  lastins = 0;
+  for (i = 0; i < trans->steps.count; i++)
+    {
+      p = trans->steps.elements[i];
+      s = pool->solvables + p;
+      if (s->repo != pool->installed)
+       lastins = p;
+      if (s->repo != pool->installed)
+       MAPSET(&ins, p);
+      if (havescripts(pool, p))
+       {
+         MAPZERO(&seen);
+         transaction_check_pkg(trans, p, p, &ins, &seen, 1, lastins, 0);
+       }
+      if (s->repo == pool->installed)
+       MAPCLR(&ins, p);
+    }
+  map_free(&seen);
+  map_free(&ins);
+  POOL_DEBUG(SOLV_DEBUG_RESULT, "transaction order check done.\n");
+}
+
+void
+transaction_order_get_cycleids(Transaction *trans, Queue *q, int minseverity)
+{
+  struct _TransactionOrderdata *od = trans->orderdata;
+  Queue *cq;
+  int i, cid, ncycles;
+
+  queue_empty(q);
+  if (!od || !od->cycles || !od->cycles->count)
+    return;
+  cq = od->cycles;
+  ncycles = cq->elements[cq->count - 1];
+  i = cq->count - 1 - ncycles * 4;
+  for (cid = 1; cid <= ncycles; cid++, i += 4)
+    {
+      if (minseverity)
+       {
+         int cmin = cq->elements[i + 3] & 0xffff;
+         int cmax = (cq->elements[i + 3] >> 16) & 0xffff;
+         if (minseverity >= SOLVER_ORDERCYCLE_NORMAL && cmin < TYPE_REQ)
+           continue;
+         if (minseverity >= SOLVER_ORDERCYCLE_CRITICAL && (cmax & TYPE_PREREQ) == 0)
+           continue;
+       }
+      queue_push(q, cid);
+    }
+}
+
+int
+transaction_order_get_cycle(Transaction *trans, Id cid, Queue *q)
+{
+  struct _TransactionOrderdata *od = trans->orderdata;
+  Queue *cq;
+  int cmin, cmax, severity;
+  int ncycles;
+
+  queue_empty(q);
+  if (!od || !od->cycles || !od->cycles->count)
+    return SOLVER_ORDERCYCLE_HARMLESS;
+  cq = od->cycles;
+  ncycles = cq->elements[cq->count - 1];
+  if (cid < 1 || cid > ncycles)
+    return SOLVER_ORDERCYCLE_HARMLESS;
+  cid =  cq->count - 1 - 4 * (ncycles - cid + 1);
+  cmin = cq->elements[cid + 3] & 0xffff;
+  cmax = (cq->elements[cid + 3] >> 16) & 0xffff;
+  if (cmin < TYPE_REQ)
+    severity = SOLVER_ORDERCYCLE_HARMLESS;
+  else if ((cmax & TYPE_PREREQ) == 0)
+    severity = SOLVER_ORDERCYCLE_NORMAL;
+  else
+    severity = SOLVER_ORDERCYCLE_CRITICAL;
+  if (q)
+    queue_insertn(q, 0, cq->elements[cid + 1], cq->elements + cq->elements[cid]);
+  return severity;
+}
+
index b89e0d1..3358b6b 100644 (file)
@@ -200,15 +200,25 @@ autouninstall(Solver *solv, Id *problem)
   int lastfeature = 0, lastupdate = 0;
   Id v;
   Id extraflags = -1;
+  Map *m = 0;
 
+  if (!solv->allowuninstall && !solv->allowuninstall_all)
+    {
+      if (!solv->allowuninstallmap.size)
+       return 0;               /* why did we get called? */
+      m = &solv->allowuninstallmap;
+    }
   for (i = 0; (v = problem[i]) != 0; i++)
     {
       if (v < 0)
        extraflags &= solv->job.elements[-v - 1];
       if (v >= solv->updaterules && v < solv->updaterules_end)
        {
+         Rule *r;
+         if (m && !MAPTST(m, v - solv->updaterules))
+           continue;
          /* check if identical to feature rule, we don't like that */
-         Rule *r = solv->rules + solv->featurerules + (v - solv->updaterules);
+         r = solv->rules + solv->featurerules + (v - solv->updaterules);
          if (!r->p)
            {
              /* update rule == feature rule */
@@ -427,8 +437,16 @@ makeruledecisions(Solver *solv)
                v = ri;
              queue_push(&solv->problems, v);
              queue_push(&solv->problems, 0);
-             if (solv->allowuninstall && v >= solv->featurerules && v < solv->updaterules_end)
-               solv->problems.count = oldproblemcount;
+             if (v >= solv->featurerules && v < solv->updaterules_end)
+               {
+                 if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+                   if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+                     {
+                       solv->problems.count = oldproblemcount;
+                       havedisabled = 1;
+                       break;  /* start over */
+                     }
+               }
              solver_disableproblem(solv, v);
              havedisabled = 1;
              break;    /* start over */
@@ -458,8 +476,16 @@ makeruledecisions(Solver *solv)
                v = ri;
              queue_push(&solv->problems, v);
              queue_push(&solv->problems, 0);
-             if (solv->allowuninstall && v >= solv->featurerules && v < solv->updaterules_end)
-               solv->problems.count = oldproblemcount;
+             if (v >= solv->featurerules && v < solv->updaterules_end)
+               {
+                 if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+                   if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+                     {
+                       solv->problems.count = oldproblemcount;
+                       havedisabled = 1;
+                       break;  /* start over */
+                     }
+               }
              solver_disableproblem(solv, v);
              havedisabled = 1;
              break;    /* start over */
@@ -507,8 +533,13 @@ makeruledecisions(Solver *solv)
            }
          queue_push(&solv->problems, 0);
 
-         if (solv->allowuninstall && (v = autouninstall(solv, solv->problems.elements + oldproblemcount + 1)) != 0)
-           solv->problems.count = oldproblemcount;
+         if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+           if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+             {
+               solv->problems.count = oldproblemcount;
+               havedisabled = 1;
+               break;  /* start over */
+             }
 
          for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++)
            solver_disableproblem(solv, solv->problems.elements[i]);
@@ -1245,13 +1276,14 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules)
       return 0;
     }
 
-  if (solv->allowuninstall && (v = autouninstall(solv, solv->problems.elements + oldproblemcount + 1)) != 0)
-    {
-      solv->problems.count = oldproblemcount;
-      solv->learnt_pool.count = oldlearntpoolcount;
-      solver_reset(solv);
-      return 0;
-    }
+  if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+    if (autouninstall(solv, solv->problems.elements + oldproblemcount + 1) != 0)
+      {
+       solv->problems.count = oldproblemcount;
+       solv->learnt_pool.count = oldlearntpoolcount;
+       solver_reset(solv);
+       return 0;
+      }
 
   /* finish proof */
   if (record_proof)
@@ -1672,6 +1704,7 @@ solver_free(Solver *solv)
   map_free(&solv->dupinvolvedmap);
   map_free(&solv->droporphanedmap);
   map_free(&solv->cleandepsmap);
+  map_free(&solv->allowuninstallmap);
 
   solv_free(solv->decisionmap);
   solv_free(solv->rules);
@@ -3359,6 +3392,8 @@ solver_solve(Solver *solv, Queue *job)
   map_zerosize(&solv->dupinvolvedmap);
   solv->droporphanedmap_all = 0;
   map_zerosize(&solv->droporphanedmap);
+  solv->allowuninstall_all = 0;
+  map_zerosize(&solv->allowuninstallmap);
   map_zerosize(&solv->cleandepsmap);
   map_zerosize(&solv->weakrulemap);
   queue_empty(&solv->weakruleq);
@@ -3873,6 +3908,20 @@ solver_solve(Solver *solv, Queue *job)
        case SOLVER_USERINSTALLED:
          POOL_DEBUG(SOLV_DEBUG_JOB, "job: user installed %s\n", solver_select2str(pool, select, what));
          break;
+       case SOLVER_ALLOWUNINSTALL:
+         POOL_DEBUG(SOLV_DEBUG_JOB, "job: allowuninstall %s\n", solver_select2str(pool, select, what));
+         if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && installed && what == installed->repoid))
+           solv->allowuninstall_all = 1;
+         FOR_JOB_SELECT(p, pp, select, what)
+           {
+             s = pool->solvables + p;
+             if (s->repo != installed)
+               continue;
+             if (!solv->allowuninstallmap.size)
+               map_grow(&solv->allowuninstallmap, installed->end - installed->start);
+             MAPSET(&solv->allowuninstallmap, p - installed->start);
+           }
+         break;
        default:
          POOL_DEBUG(SOLV_DEBUG_JOB, "job: unknown job\n");
          break;
@@ -5082,6 +5131,9 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask)
     case SOLVER_USERINSTALLED:
       strstart = "regard ", strend = " as userinstalled";
       break;
+    case SOLVER_ALLOWUNINSTALL:
+      strstart = "allow deinstallation of ";
+      break;
     default:
       strstart = "unknown job ";
       break;
index 1a47ae0..2ae9c8d 100644 (file)
@@ -195,6 +195,10 @@ struct _Solver {
   int keep_orphans;                    /* how to treat orphans */
   int break_orphans;                   /* how to treat orphans */
   Queue *brokenorphanrules;            /* broken rules of orphaned packages */
+
+  Map allowuninstallmap;               /* ok to uninstall those */
+  int allowuninstall_all;
+
 #endif /* LIBSOLV_INTERNAL */
 };
 
@@ -223,7 +227,8 @@ typedef struct _Solver Solver;
 #define SOLVER_DISTUPGRADE             0x0700
 #define SOLVER_VERIFY                  0x0800
 #define SOLVER_DROP_ORPHANED           0x0900
-#define SOLVER_USERINSTALLED            0x0a00
+#define SOLVER_USERINSTALLED           0x0a00
+#define SOLVER_ALLOWUNINSTALL          0x0b00
 
 #define SOLVER_JOBMASK                 0xff00
 
index 49e58e8..ffe1ec2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2009, Novell Inc.
+ * Copyright (c) 2007-2015, SUSE LLC
  *
  * This program is licensed under the BSD license, read LICENSE.BSD
  * for further information
@@ -842,33 +842,6 @@ transaction_calc_duchanges(Transaction *trans, DUChanges *mps, int nmps)
   map_free(&installedmap);
 }
 
-struct _TransactionElement {
-  Id p;                /* solvable id */
-  Id edges;    /* pointer into edges data */
-  Id mark;
-};
-
-struct _TransactionOrderdata {
-  struct _TransactionElement *tes;
-  int ntes;
-  Id *invedgedata;
-  int ninvedgedata;
-};
-
-#define TYPE_BROKEN    (1<<0)
-#define TYPE_CON       (1<<1)
-
-#define TYPE_REQ_P     (1<<2)
-#define TYPE_PREREQ_P  (1<<3)
-
-#define TYPE_REQ       (1<<4)
-#define TYPE_PREREQ    (1<<5)
-
-#define TYPE_CYCLETAIL  (1<<16)
-#define TYPE_CYCLEHEAD  (1<<17)
-
-#define EDGEDATA_BLOCK 127
-
 Transaction *
 transaction_create(Pool *pool)
 {
@@ -891,14 +864,7 @@ transaction_create_clone(Transaction *srctrans)
   map_init_clone(&trans->transactsmap, &srctrans->transactsmap);
   map_init_clone(&trans->multiversionmap, &srctrans->multiversionmap);
   if (srctrans->orderdata)
-    {
-      struct _TransactionOrderdata *od = srctrans->orderdata;
-      trans->orderdata = solv_calloc(1, sizeof(*trans->orderdata));
-      trans->orderdata->tes = solv_memdup2(od->tes, od->ntes, sizeof(*od->tes));
-      trans->orderdata->ntes = od->ntes;
-      trans->orderdata->invedgedata = solv_memdup2(od->invedgedata, od->ninvedgedata, sizeof(Id));
-      trans->orderdata->ninvedgedata = od->ninvedgedata;
-    }
+    transaction_clone_orderdata(trans, srctrans);
   return trans;
 }
 
@@ -910,1210 +876,8 @@ transaction_free(Transaction *trans)
   trans->transaction_installed = solv_free(trans->transaction_installed);
   map_free(&trans->transactsmap);
   map_free(&trans->multiversionmap);
-  transaction_free_orderdata(trans);
-  free(trans);
-}
-
-void
-transaction_free_orderdata(Transaction *trans)
-{
   if (trans->orderdata)
-    {
-      struct _TransactionOrderdata *od = trans->orderdata;
-      od->tes = solv_free(od->tes);
-      od->invedgedata = solv_free(od->invedgedata);
-      trans->orderdata = solv_free(trans->orderdata);
-    }
-}
-
-struct orderdata {
-  Transaction *trans;
-  struct _TransactionElement *tes;
-  int ntes;
-  Id *edgedata;
-  int nedgedata;
-  Id *invedgedata;
-
-  Queue cycles;
-  Queue cyclesdata;
-  int ncycles;
-};
-
-static int
-addteedge(struct orderdata *od, int from, int to, int type)
-{
-  int i;
-  struct _TransactionElement *te;
-
-  if (from == to)
-    return 0;
-
-  /* printf("edge %d(%s) -> %d(%s) type %x\n", from, pool_solvid2str(pool, od->tes[from].p), to, pool_solvid2str(pool, od->tes[to].p), type); */
-
-  te = od->tes + from;
-  for (i = te->edges; od->edgedata[i]; i += 2)
-    if (od->edgedata[i] == to)
-      break;
-  /* test of brokenness */
-  if (type == TYPE_BROKEN)
-    return od->edgedata[i] && (od->edgedata[i + 1] & TYPE_BROKEN) != 0 ? 1 : 0;
-  if (od->edgedata[i])
-    {
-      od->edgedata[i + 1] |= type;
-      return 0;
-    }
-  if (i + 1 == od->nedgedata)
-    {
-      /* printf("tail add %d\n", i - te->edges); */
-      if (!i)
-       te->edges = ++i;
-      od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3, sizeof(Id), EDGEDATA_BLOCK);
-    }
-  else
-    {
-      /* printf("extend %d\n", i - te->edges); */
-      od->edgedata = solv_extend(od->edgedata, od->nedgedata, 3 + (i - te->edges), sizeof(Id), EDGEDATA_BLOCK);
-      if (i > te->edges)
-       memcpy(od->edgedata + od->nedgedata, od->edgedata + te->edges, sizeof(Id) * (i - te->edges));
-      i = od->nedgedata + (i - te->edges);
-      te->edges = od->nedgedata;
-    }
-  od->edgedata[i] = to;
-  od->edgedata[i + 1] = type;
-  od->edgedata[i + 2] = 0;     /* end marker */
-  od->nedgedata = i + 3;
-  return 0;
-}
-
-static int
-addedge(struct orderdata *od, Id from, Id to, int type)
-{
-  Transaction *trans = od->trans;
-  Pool *pool = trans->pool;
-  Solvable *s;
-  struct _TransactionElement *te;
-  int i;
-
-  /* printf("addedge %d %d type %d\n", from, to, type); */
-  s = pool->solvables + from;
-  if (s->repo == pool->installed && trans->transaction_installed[from - pool->installed->start])
-    {
-      /* obsolete, map to install */
-      if (trans->transaction_installed[from - pool->installed->start] > 0)
-       from = trans->transaction_installed[from - pool->installed->start];
-      else
-       {
-         int ret = 0;
-         Queue ti;
-         Id tibuf[5];
-
-         queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
-         transaction_all_obs_pkgs(trans, from, &ti);
-         for (i = 0; i < ti.count; i++)
-           ret |= addedge(od, ti.elements[i], to, type);
-         queue_free(&ti);
-         return ret;
-       }
-    }
-  s = pool->solvables + to;
-  if (s->repo == pool->installed && trans->transaction_installed[to - pool->installed->start])
-    {
-      /* obsolete, map to install */
-      if (trans->transaction_installed[to - pool->installed->start] > 0)
-       to = trans->transaction_installed[to - pool->installed->start];
-      else
-       {
-         int ret = 0;
-         Queue ti;
-         Id tibuf[5];
-
-         queue_init_buffer(&ti, tibuf, sizeof(tibuf)/sizeof(*tibuf));
-         transaction_all_obs_pkgs(trans, to, &ti);
-         for (i = 0; i < ti.count; i++)
-           ret |= addedge(od, from, ti.elements[i], type);
-         queue_free(&ti);
-         return ret;
-       }
-    }
-
-  /* map from/to to te numbers */
-  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
-    if (te->p == to)
-      break;
-  if (i == od->ntes)
-    return 0;
-  to = i;
-
-  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
-    if (te->p == from)
-      break;
-  if (i == od->ntes)
-    return 0;
-
-  return addteedge(od, i, to, type);
-}
-
-static inline int
-havescripts(Pool *pool, Id solvid)
-{
-  Solvable *s = pool->solvables + solvid;
-  const char *dep;
-  if (s->requires)
-    {
-      Id req, *reqp;
-      int inpre = 0;
-      reqp = s->repo->idarraydata + s->requires;
-      while ((req = *reqp++) != 0)
-       {
-          if (req == SOLVABLE_PREREQMARKER)
-           {
-             inpre = 1;
-             continue;
-           }
-         if (!inpre)
-           continue;
-         dep = pool_id2str(pool, req);
-         if (*dep == '/' && strcmp(dep, "/sbin/ldconfig") != 0)
-           return 1;
-       }
-    }
-  return 0;
-}
-
-static void
-addsolvableedges(struct orderdata *od, Solvable *s)
-{
-  Transaction *trans = od->trans;
-  Pool *pool = trans->pool;
-  Id req, *reqp, con, *conp;
-  Id p, p2, pp2;
-  int i, j, pre, numins;
-  Repo *installed = pool->installed;
-  Solvable *s2;
-  Queue reqq;
-  int provbyinst;
-
-#if 0
-  printf("addsolvableedges %s\n", pool_solvable2str(pool, s));
-#endif
-  p = s - pool->solvables;
-  queue_init(&reqq);
-  if (s->requires)
-    {
-      reqp = s->repo->idarraydata + s->requires;
-      pre = TYPE_REQ;
-      while ((req = *reqp++) != 0)
-       {
-         if (req == SOLVABLE_PREREQMARKER)
-           {
-             pre = TYPE_PREREQ;
-             continue;
-           }
-#if 0
-         if (pre != TYPE_PREREQ && installed && s->repo == installed)
-           {
-             /* ignore normal requires if we're getting obsoleted */
-             if (trans->transaction_installed[p - pool->installed->start])
-               continue;
-           }
-#endif
-         queue_empty(&reqq);
-         numins = 0;   /* number of packages to be installed providing it */
-         provbyinst = 0;       /* provided by kept package */
-         FOR_PROVIDES(p2, pp2, req)
-           {
-             s2 = pool->solvables + p2;
-             if (p2 == p)
-               {
-                 reqq.count = 0;       /* self provides */
-                 break;
-               }
-             if (s2->repo == installed && !MAPTST(&trans->transactsmap, p2))
-               {
-                 provbyinst = 1;
-#if 0
-                 printf("IGNORE inst provides %s by %s\n", pool_dep2str(pool, req), pool_solvable2str(pool, s2));
-                 reqq.count = 0;       /* provided by package that stays installed */
-                 break;
-#else
-                 continue;
-#endif
-               }
-             if (s2->repo != installed && !MAPTST(&trans->transactsmap, p2))
-               continue;               /* package stays uninstalled */
-
-             if (s->repo == installed)
-               {
-                 /* s gets uninstalled */
-                 queue_pushunique(&reqq, p2);
-                 if (s2->repo != installed)
-                   numins++;
-               }
-             else
-               {
-                 if (s2->repo == installed)
-                   continue;   /* s2 gets uninstalled */
-                 queue_pushunique(&reqq, p2);
-               }
-           }
-         if (provbyinst)
-           {
-             /* prune to harmless ->inst edges */
-             for (i = j = 0; i < reqq.count; i++)
-               if (pool->solvables[reqq.elements[i]].repo != installed)
-                 reqq.elements[j++] = reqq.elements[i];
-             reqq.count = j;
-           }
-
-         if (numins && reqq.count)
-           {
-             if (s->repo == installed)
-               {
-                 for (i = 0; i < reqq.count; i++)
-                   {
-                     if (pool->solvables[reqq.elements[i]].repo == installed)
-                       {
-                         for (j = 0; j < reqq.count; j++)
-                           {
-                             if (pool->solvables[reqq.elements[j]].repo != installed)
-                               {
-                                 if (trans->transaction_installed[reqq.elements[i] - pool->installed->start] == reqq.elements[j])
-                                   continue;   /* no self edge */
-#if 0
-                                 printf("add interrreq uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, reqq.elements[i]), pool_dep2str(pool, req), pool_solvid2str(pool, reqq.elements[j]));
-#endif
-                                 addedge(od, reqq.elements[i], reqq.elements[j], pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
-                               }
-                           }
-                       }
-                   }
-               }
-             /* no mixed types, remove all deps on uninstalls */
-             for (i = j = 0; i < reqq.count; i++)
-               if (pool->solvables[reqq.elements[i]].repo != installed)
-                 reqq.elements[j++] = reqq.elements[i];
-             reqq.count = j;
-           }
-          if (!reqq.count)
-           continue;
-          for (i = 0; i < reqq.count; i++)
-           {
-             p2 = reqq.elements[i];
-             if (pool->solvables[p2].repo != installed)
-               {
-                 /* all elements of reqq are installs, thus have different TEs */
-                 if (pool->solvables[p].repo != installed)
-                   {
-#if 0
-                     printf("add inst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
-#endif
-                     addedge(od, p, p2, pre);
-                   }
-                 else
-                   {
-#if 0
-                     printf("add uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
-#endif
-                     addedge(od, p, p2, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
-                   }
-               }
-             else
-               {
-                 if (s->repo != installed)
-                   continue;   /* no inst->uninst edges, please! */
-
-                 /* uninst -> uninst edge. Those make trouble. Only add if we must */
-                 if (trans->transaction_installed[p - installed->start] && !havescripts(pool, p))
-                   {
-                     /* p is obsoleted by another package and has no scripts */
-                     /* we assume that the obsoletor is good enough to replace p */
-                     continue;
-                   }
-#if 0
-                 printf("add uninst->uninst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, req), pool_solvid2str(pool, p2));
-#endif
-                 addedge(od, p2, p, pre == TYPE_PREREQ ? TYPE_PREREQ_P : TYPE_REQ_P);
-               }
-           }
-       }
-    }
-  if (s->conflicts)
-    {
-      conp = s->repo->idarraydata + s->conflicts;
-      while ((con = *conp++) != 0)
-       {
-         FOR_PROVIDES(p2, pp2, con)
-           {
-             if (p2 == p)
-               continue;
-             s2 = pool->solvables + p2;
-             if (!s2->repo)
-               continue;
-             if (s->repo == installed)
-               {
-                 if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
-                   {
-                     /* deinstall p before installing p2 */
-#if 0
-                     printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, con), pool_solvid2str(pool, p));
-#endif
-                     addedge(od, p2, p, TYPE_CON);
-                   }
-               }
-             else
-               {
-                 if (s2->repo == installed && MAPTST(&trans->transactsmap, p2))
-                   {
-                     /* deinstall p2 before installing p */
-#if 0
-                     printf("add conflict uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p), pool_dep2str(pool, con), pool_solvid2str(pool, p2));
-#endif
-                     addedge(od, p, p2, TYPE_CON);
-                   }
-               }
-
-           }
-       }
-    }
-  if (s->repo == installed && solvable_lookup_idarray(s, SOLVABLE_TRIGGERS, &reqq) && reqq.count)
-    {
-      /* we're getting deinstalled/updated. Try to do this before our
-       * triggers are hit */
-      for (i = 0; i < reqq.count; i++)
-       {
-         Id tri = reqq.elements[i];
-         FOR_PROVIDES(p2, pp2, tri)
-           {
-             if (p2 == p)
-               continue;
-             s2 = pool->solvables + p2;
-             if (!s2->repo)
-               continue;
-             if (s2->name == s->name)
-               continue;       /* obsoleted anyway */
-             if (s2->repo != installed && MAPTST(&trans->transactsmap, p2))
-               {
-                 /* deinstall/update p before installing p2 */
-#if 0
-                 printf("add trigger uninst->inst edge (%s -> %s -> %s)\n", pool_solvid2str(pool, p2), pool_dep2str(pool, tri), pool_solvid2str(pool, p));
-#endif
-                 addedge(od, p2, p, TYPE_CON);
-               }
-           }
-       }
-    }
-  queue_free(&reqq);
-}
-
-
-/* break an edge in a cycle */
-static void
-breakcycle(struct orderdata *od, Id *cycle)
-{
-  Pool *pool = od->trans->pool;
-  Id ddegmin, ddegmax, ddeg;
-  int k, l;
-  struct _TransactionElement *te;
-
-  l = 0;
-  ddegmin = ddegmax = 0;
-  for (k = 0; cycle[k + 1]; k += 2)
-    {
-      ddeg = od->edgedata[cycle[k + 1] + 1];
-      if (ddeg > ddegmax)
-       ddegmax = ddeg;
-      if (!k || ddeg < ddegmin)
-       {
-         l = k;
-         ddegmin = ddeg;
-         continue;
-       }
-      if (ddeg == ddegmin)
-       {
-         if (havescripts(pool, od->tes[cycle[l]].p) && !havescripts(pool, od->tes[cycle[k]].p))
-           {
-             /* prefer k, as l comes from a package with contains scriptlets */
-             l = k;
-             ddegmin = ddeg;
-             continue;
-           }
-         /* same edge value, check for prereq */
-       }
-    }
-
-  /* record brkoen cycle starting with the tail */
-  queue_push(&od->cycles, od->cyclesdata.count);               /* offset into data */
-  queue_push(&od->cycles, k / 2);                              /* cycle elements */
-  queue_push(&od->cycles, od->edgedata[cycle[l + 1] + 1]);     /* broken edge */
-  od->ncycles++;
-  for (k = l;;)
-    {
-      k += 2;
-      if (!cycle[k + 1])
-       k = 0;
-      queue_push(&od->cyclesdata, cycle[k]);
-      if (k == l)
-       break;
-    }
-  queue_push(&od->cyclesdata, 0);      /* mark end */
-
-  /* break that edge */
-  od->edgedata[cycle[l + 1] + 1] |= TYPE_BROKEN;
-
-#if 1
-  if (ddegmin < TYPE_REQ)
-    return;
-#endif
-
-  /* cycle recorded, print it */
-  if (ddegmin >= TYPE_REQ && (ddegmax & TYPE_PREREQ) != 0)
-    POOL_DEBUG(SOLV_DEBUG_STATS, "CRITICAL ");
-  POOL_DEBUG(SOLV_DEBUG_STATS, "cycle: --> ");
-  for (k = 0; cycle[k + 1]; k += 2)
-    {
-      te = od->tes +  cycle[k];
-      if ((od->edgedata[cycle[k + 1] + 1] & TYPE_BROKEN) != 0)
-        POOL_DEBUG(SOLV_DEBUG_STATS, "%s ##%x##> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
-      else
-        POOL_DEBUG(SOLV_DEBUG_STATS, "%s --%x--> ", pool_solvid2str(pool, te->p), od->edgedata[cycle[k + 1] + 1]);
-    }
-  POOL_DEBUG(SOLV_DEBUG_STATS, "\n");
-}
-
-static inline void
-dump_tes(struct orderdata *od)
-{
-  Pool *pool = od->trans->pool;
-  int i, j;
-  Queue obsq;
-  struct _TransactionElement *te, *te2;
-
-  queue_init(&obsq);
-  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
-    {
-      Solvable *s = pool->solvables + te->p;
-      POOL_DEBUG(SOLV_DEBUG_RESULT, "TE %4d: %c%s\n", i, s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
-      if (s->repo != pool->installed)
-        {
-         queue_empty(&obsq);
-         transaction_all_obs_pkgs(od->trans, te->p, &obsq);
-         for (j = 0; j < obsq.count; j++)
-           POOL_DEBUG(SOLV_DEBUG_RESULT, "         -%s\n", pool_solvid2str(pool, obsq.elements[j]));
-       }
-      for (j = te->edges; od->edgedata[j]; j += 2)
-       {
-         te2 = od->tes + od->edgedata[j];
-         if ((od->edgedata[j + 1] & TYPE_BROKEN) == 0)
-           POOL_DEBUG(SOLV_DEBUG_RESULT, "       --%x--> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
-         else
-           POOL_DEBUG(SOLV_DEBUG_RESULT, "       ##%x##> TE %4d: %s\n", od->edgedata[j + 1], od->edgedata[j], pool_solvid2str(pool, te2->p));
-       }
-    }
-}
-
-#if 1
-static void
-reachable(struct orderdata *od, Id i)
-{
-  struct _TransactionElement *te = od->tes + i;
-  int j, k;
-
-  if (te->mark != 0)
-    return;
-  te->mark = 1;
-  for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
-    {
-      if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
-       continue;
-      if (!od->tes[k].mark)
-        reachable(od, k);
-      if (od->tes[k].mark == 2)
-       {
-         te->mark = 2;
-         return;
-       }
-    }
-  te->mark = -1;
-}
-#endif
-
-static void
-addcycleedges(struct orderdata *od, Id *cycle, Queue *todo)
-{
-#if 0
-  Transaction *trans = od->trans;
-  Pool *pool = trans->pool;
-#endif
-  struct _TransactionElement *te;
-  int i, j, k, tail;
-#if 1
-  int head;
-#endif
-
-#if 0
-  printf("addcycleedges\n");
-  for (i = 0; (j = cycle[i]) != 0; i++)
-    printf("cycle %s\n", pool_solvid2str(pool, od->tes[j].p));
-#endif
-
-  /* first add all the tail cycle edges */
-
-  /* see what we can reach from the cycle */
-  queue_empty(todo);
-  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
-    te->mark = 0;
-  for (i = 0; (j = cycle[i]) != 0; i++)
-    {
-      od->tes[j].mark = -1;
-      queue_push(todo, j);
-    }
-  while (todo->count)
-    {
-      i = queue_pop(todo);
-      te = od->tes + i;
-      if (te->mark > 0)
-       continue;
-      te->mark = te->mark < 0 ? 2 : 1;
-      for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
-       {
-         if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
-           continue;
-         if (od->tes[k].mark > 0)
-           continue;   /* no need to visit again */
-         queue_push(todo, k);
-       }
-    }
-  /* now all cycle TEs are marked with 2, all TEs reachable
-   * from the cycle are marked with 1 */
-  tail = cycle[0];
-  od->tes[tail].mark = 1;      /* no need to add edges */
-
-  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
-    {
-      if (te->mark)
-       continue;       /* reachable from cycle */
-      for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
-       {
-         if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
-           continue;
-         if (od->tes[k].mark != 2)
-           continue;
-         /* We found an edge to the cycle. Add an extra edge to the tail */
-         /* the TE was not reachable, so we're not creating a new cycle! */
-#if 0
-         printf("adding TO TAIL cycle edge %d->%d %s->%s!\n", i, tail, pool_solvid2str(pool, od->tes[i].p), pool_solvid2str(pool, od->tes[tail].p));
-#endif
-         j -= te->edges;       /* in case we move */
-         addteedge(od, i, tail, TYPE_CYCLETAIL);
-         j += te->edges;
-         break;        /* one edge is enough */
-       }
-    }
-
-#if 1
-  /* now add all head cycle edges */
-
-  /* reset marks */
-  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
-    te->mark = 0;
-  head = 0;
-  for (i = 0; (j = cycle[i]) != 0; i++)
-    {
-      head = j;
-      od->tes[j].mark = 2;
-    }
-  /* first the head to save some time */
-  te = od->tes + head;
-  for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
-    {
-      if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
-       continue;
-      if (!od->tes[k].mark)
-       reachable(od, k);
-      if (od->tes[k].mark == -1)
-       od->tes[k].mark = -2;   /* no need for another edge */
-    }
-  for (i = 0; cycle[i] != 0; i++)
-    {
-      if (cycle[i] == head)
-       break;
-      te = od->tes + cycle[i];
-      for (j = te->edges; (k = od->edgedata[j]) != 0; j += 2)
-       {
-         if ((od->edgedata[j + 1] & TYPE_BROKEN) != 0)
-           continue;
-         /* see if we can reach a cycle TE from k */
-         if (!od->tes[k].mark)
-           reachable(od, k);
-         if (od->tes[k].mark == -1)
-           {
-#if 0
-             printf("adding FROM HEAD cycle edge %d->%d %s->%s [%s]!\n", head, k, pool_solvid2str(pool, od->tes[head].p), pool_solvid2str(pool, od->tes[k].p), pool_solvid2str(pool, od->tes[cycle[i]].p));
-#endif
-             addteedge(od, head, k, TYPE_CYCLEHEAD);
-             od->tes[k].mark = -2;     /* no need to add that one again */
-           }
-       }
-    }
-#endif
-}
-
-void
-transaction_order(Transaction *trans, int flags)
-{
-  Pool *pool = trans->pool;
-  Queue *tr = &trans->steps;
-  Repo *installed = pool->installed;
-  Id p;
-  Solvable *s;
-  int i, j, k, numte, numedge;
-  struct orderdata od;
-  struct _TransactionElement *te;
-  Queue todo, obsq, samerepoq, uninstq;
-  int cycstart, cycel;
-  Id *cycle;
-  int oldcount;
-  int start, now;
-  Repo *lastrepo;
-  int lastmedia;
-  Id *temedianr;
-
-  start = now = solv_timems(0);
-  POOL_DEBUG(SOLV_DEBUG_STATS, "ordering transaction\n");
-  /* free old data if present */
-  if (trans->orderdata)
-    {
-      struct _TransactionOrderdata *od = trans->orderdata;
-      od->tes = solv_free(od->tes);
-      od->invedgedata = solv_free(od->invedgedata);
-      trans->orderdata = solv_free(trans->orderdata);
-    }
-
-  /* create a transaction element for every active component */
-  numte = 0;
-  for (i = 0; i < tr->count; i++)
-    {
-      p = tr->elements[i];
-      s = pool->solvables + p;
-      if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
-       continue;
-      numte++;
-    }
-  POOL_DEBUG(SOLV_DEBUG_STATS, "transaction elements: %d\n", numte);
-  if (!numte)
-    return;    /* nothing to do... */
-
-  numte++;     /* leave first one zero */
-  memset(&od, 0, sizeof(od));
-  od.trans = trans;
-  od.ntes = numte;
-  od.tes = solv_calloc(numte, sizeof(*od.tes));
-  od.edgedata = solv_extend(0, 0, 1, sizeof(Id), EDGEDATA_BLOCK);
-  od.edgedata[0] = 0;
-  od.nedgedata = 1;
-  queue_init(&od.cycles);
-
-  /* initialize TEs */
-  for (i = 0, te = od.tes + 1; i < tr->count; i++)
-    {
-      p = tr->elements[i];
-      s = pool->solvables + p;
-      if (installed && s->repo == installed && trans->transaction_installed[p - installed->start])
-       continue;
-      te->p = p;
-      te++;
-    }
-
-  /* create dependency graph */
-  for (i = 0; i < tr->count; i++)
-    addsolvableedges(&od, pool->solvables + tr->elements[i]);
-
-  /* count edges */
-  numedge = 0;
-  for (i = 1, te = od.tes + i; i < numte; i++, te++)
-    for (j = te->edges; od.edgedata[j]; j += 2)
-      numedge++;
-  POOL_DEBUG(SOLV_DEBUG_STATS, "edges: %d, edge space: %d\n", numedge, od.nedgedata / 2);
-  POOL_DEBUG(SOLV_DEBUG_STATS, "edge creation took %d ms\n", solv_timems(now));
-
-#if 0
-  dump_tes(&od);
-#endif
-
-  now = solv_timems(0);
-  /* kill all cycles */
-  queue_init(&todo);
-  for (i = numte - 1; i > 0; i--)
-    queue_push(&todo, i);
-
-  while (todo.count)
-    {
-      i = queue_pop(&todo);
-      /* printf("- look at TE %d\n", i); */
-      if (i < 0)
-       {
-         i = -i;
-         od.tes[i].mark = 2;   /* done with that one */
-         continue;
-       }
-      te = od.tes + i;
-      if (te->mark == 2)
-       continue;               /* already finished before */
-      if (te->mark == 0)
-       {
-         int edgestovisit = 0;
-         /* new node, visit edges */
-         for (j = te->edges; (k = od.edgedata[j]) != 0; j += 2)
-           {
-             if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
-               continue;
-             if (od.tes[k].mark == 2)
-               continue;       /* no need to visit again */
-             if (!edgestovisit++)
-               queue_push(&todo, -i);  /* end of edges marker */
-             queue_push(&todo, k);
-           }
-         if (!edgestovisit)
-           te->mark = 2;       /* no edges, done with that one */
-         else
-           te->mark = 1;       /* under investigation */
-         continue;
-       }
-      /* oh no, we found a cycle */
-      /* find start of cycle node (<0) */
-      for (j = todo.count - 1; j >= 0; j--)
-       if (todo.elements[j] == -i)
-         break;
-      assert(j >= 0);
-      cycstart = j;
-      /* build te/edge chain */
-      k = cycstart;
-      for (j = k; j < todo.count; j++)
-       if (todo.elements[j] < 0)
-         todo.elements[k++] = -todo.elements[j];
-      cycel = k - cycstart;
-      assert(cycel > 1);
-      /* make room for edges, two extra element for cycle loop + terminating 0 */
-      while (todo.count < cycstart + 2 * cycel + 2)
-       queue_push(&todo, 0);
-      cycle = todo.elements + cycstart;
-      cycle[cycel] = i;                /* close the loop */
-      cycle[2 * cycel + 1] = 0;        /* terminator */
-      for (k = cycel; k > 0; k--)
-       {
-         cycle[k * 2] = cycle[k];
-         te = od.tes + cycle[k - 1];
-         assert(te->mark == 1);
-         te->mark = 0; /* reset investigation marker */
-         /* printf("searching for edge from %d to %d\n", cycle[k - 1], cycle[k]); */
-         for (j = te->edges; od.edgedata[j]; j += 2)
-           if (od.edgedata[j] == cycle[k])
-             break;
-         assert(od.edgedata[j]);
-         cycle[k * 2 - 1] = j;
-       }
-      /* now cycle looks like this: */
-      /* te1 edge te2 edge te3 ... teN edge te1 0 */
-      breakcycle(&od, cycle);
-      /* restart with start of cycle */
-      todo.count = cycstart + 1;
-    }
-  POOL_DEBUG(SOLV_DEBUG_STATS, "cycles broken: %d\n", od.ncycles);
-  POOL_DEBUG(SOLV_DEBUG_STATS, "cycle breaking took %d ms\n", solv_timems(now));
-
-  now = solv_timems(0);
-  /* now go through all broken cycles and create cycle edges to help
-     the ordering */
-   for (i = od.cycles.count - 3; i >= 0; i -= 3)
-     {
-       if (od.cycles.elements[i + 2] >= TYPE_REQ)
-         addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
-     }
-   for (i = od.cycles.count - 3; i >= 0; i -= 3)
-     {
-       if (od.cycles.elements[i + 2] < TYPE_REQ)
-         addcycleedges(&od, od.cyclesdata.elements + od.cycles.elements[i], &todo);
-     }
-  POOL_DEBUG(SOLV_DEBUG_STATS, "cycle edge creation took %d ms\n", solv_timems(now));
-
-#if 0
-  dump_tes(&od);
-#endif
-  /* all edges are finally set up and there are no cycles, now the easy part.
-   * Create an ordered transaction */
-  now = solv_timems(0);
-  /* first invert all edges */
-  for (i = 1, te = od.tes + i; i < numte; i++, te++)
-    te->mark = 1;      /* term 0 */
-  for (i = 1, te = od.tes + i; i < numte; i++, te++)
-    {
-      for (j = te->edges; od.edgedata[j]; j += 2)
-        {
-         if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
-           continue;
-         od.tes[od.edgedata[j]].mark++;
-       }
-    }
-  j = 1;
-  for (i = 1, te = od.tes + i; i < numte; i++, te++)
-    {
-      te->mark += j;
-      j = te->mark;
-    }
-  POOL_DEBUG(SOLV_DEBUG_STATS, "invedge space: %d\n", j + 1);
-  od.invedgedata = solv_calloc(j + 1, sizeof(Id));
-  for (i = 1, te = od.tes + i; i < numte; i++, te++)
-    {
-      for (j = te->edges; od.edgedata[j]; j += 2)
-        {
-         if ((od.edgedata[j + 1] & TYPE_BROKEN) != 0)
-           continue;
-         od.invedgedata[--od.tes[od.edgedata[j]].mark] = i;
-       }
-    }
-  for (i = 1, te = od.tes + i; i < numte; i++, te++)
-    te->edges = te->mark;      /* edges now points into invedgedata */
-  od.edgedata = solv_free(od.edgedata);
-  od.nedgedata = j + 1;
-
-  /* now the final ordering */
-  for (i = 1, te = od.tes + i; i < numte; i++, te++)
-    te->mark = 0;
-  for (i = 1, te = od.tes + i; i < numte; i++, te++)
-    for (j = te->edges; od.invedgedata[j]; j++)
-      od.tes[od.invedgedata[j]].mark++;
-
-  queue_init(&samerepoq);
-  queue_init(&uninstq);
-  queue_empty(&todo);
-  for (i = 1, te = od.tes + i; i < numte; i++, te++)
-    if (te->mark == 0)
-      {
-       if (installed && pool->solvables[te->p].repo == installed)
-          queue_push(&uninstq, i);
-       else
-          queue_push(&todo, i);
-      }
-  assert(todo.count > 0 || uninstq.count > 0);
-  oldcount = tr->count;
-  queue_empty(tr);
-
-  queue_init(&obsq);
-
-  lastrepo = 0;
-  lastmedia = 0;
-  temedianr = solv_calloc(numte, sizeof(Id));
-  for (i = 1; i < numte; i++)
-    {
-      Solvable *s = pool->solvables + od.tes[i].p;
-      if (installed && s->repo == installed)
-       j = 1;
-      else
-        j = solvable_lookup_num(s, SOLVABLE_MEDIANR, 1);
-      temedianr[i] = j;
-    }
-  for (;;)
-    {
-      /* select an TE i */
-      if (uninstq.count)
-       i = queue_shift(&uninstq);
-      else if (samerepoq.count)
-       i = queue_shift(&samerepoq);
-      else if (todo.count)
-       {
-         /* find next repo/media */
-         for (j = 0; j < todo.count; j++)
-           {
-             if (!j || temedianr[todo.elements[j]] < lastmedia)
-               {
-                 i = j;
-                 lastmedia = temedianr[todo.elements[j]];
-               }
-           }
-         lastrepo = pool->solvables[od.tes[todo.elements[i]].p].repo;
-
-         /* move all matching TEs to samerepoq */
-         for (i = j = 0; j < todo.count; j++)
-           {
-             int k = todo.elements[j];
-             if (temedianr[k] == lastmedia && pool->solvables[od.tes[k].p].repo == lastrepo)
-               queue_push(&samerepoq, k);
-             else
-               todo.elements[i++] = k;
-           }
-         todo.count = i;
-
-         assert(samerepoq.count);
-         i = queue_shift(&samerepoq);
-       }
-      else
-       break;
-
-      te = od.tes + i;
-      queue_push(tr, te->p);
-#if 0
-printf("do %s [%d]\n", pool_solvid2str(pool, te->p), temedianr[i]);
-#endif
-      s = pool->solvables + te->p;
-      for (j = te->edges; od.invedgedata[j]; j++)
-       {
-         struct _TransactionElement *te2 = od.tes + od.invedgedata[j];
-         assert(te2->mark > 0);
-         if (--te2->mark == 0)
-           {
-             Solvable *s = pool->solvables + te2->p;
-#if 0
-printf("free %s [%d]\n", pool_solvid2str(pool, te2->p), temedianr[od.invedgedata[j]]);
-#endif
-             if (installed && s->repo == installed)
-               queue_push(&uninstq, od.invedgedata[j]);
-             else if (s->repo == lastrepo && temedianr[od.invedgedata[j]] == lastmedia)
-               queue_push(&samerepoq, od.invedgedata[j]);
-             else
-               queue_push(&todo, od.invedgedata[j]);
-           }
-       }
-    }
-  solv_free(temedianr);
-  queue_free(&todo);
-  queue_free(&samerepoq);
-  queue_free(&uninstq);
-  queue_free(&obsq);
-  for (i = 1, te = od.tes + i; i < numte; i++, te++)
-    assert(te->mark == 0);
-
-  /* add back obsoleted packages */
-  transaction_add_obsoleted(trans);
-  assert(tr->count == oldcount);
-
-  POOL_DEBUG(SOLV_DEBUG_STATS, "creating new transaction took %d ms\n", solv_timems(now));
-  POOL_DEBUG(SOLV_DEBUG_STATS, "transaction ordering took %d ms\n", solv_timems(start));
-
-  if ((flags & SOLVER_TRANSACTION_KEEP_ORDERDATA) != 0)
-    {
-      trans->orderdata = solv_calloc(1, sizeof(*trans->orderdata));
-      trans->orderdata->tes = od.tes;
-      trans->orderdata->ntes = numte;
-      trans->orderdata->invedgedata = od.invedgedata;
-      trans->orderdata->ninvedgedata = od.nedgedata;
-    }
-  else
-    {
-      solv_free(od.tes);
-      solv_free(od.invedgedata);
-    }
-  queue_free(&od.cycles);
-  queue_free(&od.cyclesdata);
-}
-
-
-int
-transaction_order_add_choices(Transaction *trans, Id chosen, Queue *choices)
-{
-  int i, j;
-  struct _TransactionOrderdata *od = trans->orderdata;
-  struct _TransactionElement *te;
-
-  if (!od)
-     return choices->count;
-  if (!chosen)
-    {
-      /* initialization step */
-      for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
-       te->mark = 0;
-      for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
-       {
-         for (j = te->edges; od->invedgedata[j]; j++)
-           od->tes[od->invedgedata[j]].mark++;
-       }
-      for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
-       if (!te->mark)
-         queue_push(choices, te->p);
-      return choices->count;
-    }
-  for (i = 1, te = od->tes + i; i < od->ntes; i++, te++)
-    if (te->p == chosen)
-      break;
-  if (i == od->ntes)
-    return choices->count;
-  if (te->mark > 0)
-    {
-      /* hey! out-of-order installation! */
-      te->mark = -1;
-    }
-  for (j = te->edges; od->invedgedata[j]; j++)
-    {
-      te = od->tes + od->invedgedata[j];
-      assert(te->mark > 0 || te->mark == -1);
-      if (te->mark > 0 && --te->mark == 0)
-       queue_push(choices, te->p);
-    }
-  return choices->count;
-}
-
-void
-transaction_add_obsoleted(Transaction *trans)
-{
-  Pool *pool = trans->pool;
-  Repo *installed = pool->installed;
-  Id p;
-  Solvable *s;
-  int i, j, k, max;
-  Map done;
-  Queue obsq, *steps;
-
-  if (!installed || !trans->steps.count)
-    return;
-  /* calculate upper bound */
-  max = 0;
-  FOR_REPO_SOLVABLES(installed, p, s)
-    if (MAPTST(&trans->transactsmap, p))
-      max++;
-  if (!max)
-    return;
-  /* make room */
-  steps = &trans->steps;
-  queue_insertn(steps, 0, max, 0);
-
-  /* now add em */
-  map_init(&done, installed->end - installed->start);
-  queue_init(&obsq);
-  for (j = 0, i = max; i < steps->count; i++)
-    {
-      p = trans->steps.elements[i];
-      if (pool->solvables[p].repo == installed)
-       {
-         if (!trans->transaction_installed[p - pool->installed->start])
-           trans->steps.elements[j++] = p;
-         continue;
-       }
-      trans->steps.elements[j++] = p;
-      queue_empty(&obsq);
-      transaction_all_obs_pkgs(trans, p, &obsq);
-      for (k = 0; k < obsq.count; k++)
-       {
-         p = obsq.elements[k];
-         assert(p >= installed->start && p < installed->end);
-         if (!MAPTST(&trans->transactsmap, p)) /* just in case */
-           continue;
-         if (MAPTST(&done, p - installed->start))
-           continue;
-         MAPSET(&done, p - installed->start);
-         trans->steps.elements[j++] = p;
-       }
-    }
-
-  /* free unneeded space */
-  queue_truncate(steps, j);
-  map_free(&done);
-  queue_free(&obsq);
-}
-
-static void
-transaction_check_pkg(Transaction *trans, Id tepkg, Id pkg, Map *ins, Map *seen, int onlyprereq, Id noconfpkg, int depth)
-{
-  Pool *pool = trans->pool;
-  Id p, pp;
-  Solvable *s;
-  int good;
-
-  if (MAPTST(seen, pkg))
-    return;
-  MAPSET(seen, pkg);
-  s = pool->solvables + pkg;
-#if 0
-  printf("- %*s%c%s\n", depth * 2, "", s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
-#endif
-  if (s->requires)
-    {
-      Id req, *reqp;
-      int inpre = 0;
-      reqp = s->repo->idarraydata + s->requires;
-      while ((req = *reqp++) != 0)
-       {
-          if (req == SOLVABLE_PREREQMARKER)
-           {
-             inpre = 1;
-             continue;
-           }
-         if (onlyprereq && !inpre)
-           continue;
-         if (!strncmp(pool_id2str(pool, req), "rpmlib(", 7))
-           continue;
-         good = 0;
-         /* first check kept packages, then freshly installed, then not yet uninstalled */
-         FOR_PROVIDES(p, pp, req)
-           {
-             if (!MAPTST(ins, p))
-               continue;
-             if (MAPTST(&trans->transactsmap, p))
-               continue;
-             good++;
-             transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
-           }
-         if (!good)
-           {
-             FOR_PROVIDES(p, pp, req)
-               {
-                 if (!MAPTST(ins, p))
-                   continue;
-                 if (pool->solvables[p].repo == pool->installed)
-                   continue;
-                 good++;
-                 transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
-               }
-           }
-         if (!good)
-           {
-             FOR_PROVIDES(p, pp, req)
-               {
-                 if (!MAPTST(ins, p))
-                   continue;
-                 good++;
-                 transaction_check_pkg(trans, tepkg, p, ins, seen, 0, noconfpkg, depth + 1);
-               }
-           }
-         if (!good)
-           {
-             POOL_DEBUG(SOLV_DEBUG_RESULT, "  %c%s: nothing provides %s needed by %c%s\n", pool->solvables[tepkg].repo == pool->installed ? '-' : '+', pool_solvid2str(pool, tepkg), pool_dep2str(pool, req), s->repo == pool->installed ? '-' : '+', pool_solvable2str(pool, s));
-           }
-       }
-    }
+    transaction_free_orderdata(trans);
+  free(trans);
 }
 
-void
-transaction_check_order(Transaction *trans)
-{
-  Pool *pool = trans->pool;
-  Solvable *s;
-  Id p, lastins;
-  Map ins, seen;
-  int i;
-
-  POOL_DEBUG(SOLV_DEBUG_RESULT, "\nchecking transaction order...\n");
-  map_init(&ins, pool->nsolvables);
-  map_init(&seen, pool->nsolvables);
-  if (pool->installed)
-    FOR_REPO_SOLVABLES(pool->installed, p, s)
-      MAPSET(&ins, p);
-  lastins = 0;
-  for (i = 0; i < trans->steps.count; i++)
-    {
-      p = trans->steps.elements[i];
-      s = pool->solvables + p;
-      if (s->repo != pool->installed)
-       lastins = p;
-      if (s->repo != pool->installed)
-       MAPSET(&ins, p);
-      if (havescripts(pool, p))
-       {
-         MAPZERO(&seen);
-         transaction_check_pkg(trans, p, p, &ins, &seen, 1, lastins, 0);
-       }
-      if (s->repo == pool->installed)
-       MAPCLR(&ins, p);
-    }
-  map_free(&seen);
-  map_free(&ins);
-  POOL_DEBUG(SOLV_DEBUG_RESULT, "transaction order check done.\n");
-}
index 36b0c6f..c840838 100644 (file)
@@ -85,12 +85,17 @@ typedef struct _Transaction {
 
 /* order flags */
 #define SOLVER_TRANSACTION_KEEP_ORDERDATA      (1 << 0)
+#define SOLVER_TRANSACTION_KEEP_ORDERCYCLES    (1 << 1)
+
+/* cycle severities */
+#define SOLVER_ORDERCYCLE_HARMLESS             0
+#define SOLVER_ORDERCYCLE_NORMAL               1
+#define SOLVER_ORDERCYCLE_CRITICAL             2
 
 extern Transaction *transaction_create(struct _Pool *pool);
 extern Transaction *transaction_create_decisionq(struct _Pool *pool, Queue *decisionq, Map *multiversionmap);
 extern Transaction *transaction_create_clone(Transaction *srctrans);
 extern void transaction_free(Transaction *trans);
-extern void transaction_free_orderdata(Transaction *trans);
 
 /* if p is installed, returns with pkg(s) obsolete p */
 /* if p is not installed, returns with pkg(s) we obsolete */
@@ -113,6 +118,8 @@ extern int transaction_installedresult(Transaction *trans, Queue *installedq);
 int transaction_calc_installsizechange(Transaction *trans);
 void transaction_calc_duchanges(Transaction *trans, struct _DUChanges *mps, int nmps);
 
+
+
 /* order a transaction */
 extern void transaction_order(Transaction *trans, int flags);
 
@@ -127,6 +134,12 @@ extern void transaction_add_obsoleted(Transaction *trans);
 /* debug function, report problems found in the order */
 extern void transaction_check_order(Transaction *trans);
 
+/* order cycle introspection */
+extern void transaction_order_get_cycleids(Transaction *trans, Queue *q, int minseverity);
+extern int transaction_order_get_cycle(Transaction *trans, Id cid, Queue *q);
+
+extern void transaction_free_orderdata(Transaction *trans);
+extern void transaction_clone_orderdata(Transaction *trans, Transaction *srctrans);
 
 #ifdef __cplusplus
 }
index 3ef67b0..e8be171 100644 (file)
@@ -2,7 +2,7 @@
  */
 
 /*
- * Copyright (c) 2009, Novell Inc.
+ * Copyright (c) 2009-2015, SUSE LLC
  *
  * This program is licensed under the BSD license, read LICENSE.BSD
  * for further information
@@ -49,6 +49,20 @@ usage(char** argv)
   exit(1);
 }
 
+static int
+strlen_comp(const char *str)
+{
+  size_t l = strlen(str);
+  if (l > 3 && !strcmp(str + l - 3, ".gz"))
+    return l - 3;
+  if (l > 3 && !strcmp(str + l - 3, ".xz"))
+    return l - 3;
+  if (l > 4 && !strcmp(str + l - 4, ".bz2"))
+    return l - 4;
+  if (l > 5 && !strcmp(str + l - 4, ".lzma"))
+    return l - 5;
+  return l;
+}
 
 int
 main(int argc, char **argv)
@@ -112,7 +126,7 @@ main(int argc, char **argv)
           ++i;
           continue;
         }
-      l = strlen(argv[i]);
+      l = strlen_comp(argv[i]);
       if (!strcmp(argv[i], "-"))
        fp = stdin;
       else if ((fp = solv_xfopen(argv[i], 0)) == 0)
@@ -126,33 +140,40 @@ main(int argc, char **argv)
         {
         }
 #ifdef ENABLE_SUSEREPO
-      else if (l >= 8 && !strcmp(argv[i] + l - 8, "packages"))
-       {
-         r = repo_add_susetags(repo, fp, 0, 0, 0);
-       }
-      else if (l >= 11 && !strcmp(argv[i] + l - 11, "packages.gz"))
+      else if (l >= 8 && !strncmp(argv[i] + l - 8, "packages", 8))
        {
          r = repo_add_susetags(repo, fp, 0, 0, 0);
        }
 #endif
 #ifdef ENABLE_RPMMD
-      else if (l >= 14 && !strcmp(argv[i] + l - 14, "primary.xml.gz"))
+      else if (l >= 11 && !strncmp(argv[i] + l - 11, "primary.xml", 11))
        {
          r = repo_add_rpmmd(repo, fp, 0, 0);
+          if (!r && i + 1 < argc)
+            {
+              l = strlen_comp(argv[i + 1]);
+              if (l >= 13 && !strncmp(argv[i + 1] + l - 13, "filelists.xml", 13))
+                {
+                  i++;
+                  fclose(fp);
+                  if ((fp = solv_xfopen(argv[i], 0)) == 0)
+                    {
+                      perror(argv[i]);
+                      exit(1);
+                    }
+                  r = repo_add_rpmmd(repo, fp, 0, REPO_EXTEND_SOLVABLES|REPO_LOCALPOOL);
+                }
+            }
        }
 #endif
 #ifdef ENABLE_DEBIAN
-      else if (l >= 8 && !strcmp(argv[i] + l - 8, "Packages"))
-       {
-         r = repo_add_debpackages(repo, fp, 0);
-       }
-      else if (l >= 11 && !strcmp(argv[i] + l - 11, "Packages.gz"))
+      else if (l >= 8 && !strncmp(argv[i] + l - 8, "Packages", 8))
        {
          r = repo_add_debpackages(repo, fp, 0);
        }
 #endif
 #ifdef ENABLE_ARCHREPO
-      else if (l >= 10 && (!strcmp(argv[i] + l - 10, ".db.tar.gz") || !strcmp(argv[i] + l - 10, ".db.tar.xz")))
+      else if (l >= 7 && (!strncmp(argv[i] + l - 7, ".db.tar", 7)))
         {
          r = repo_add_arch_repo(repo, fp, 0);
         }
@@ -281,14 +302,6 @@ main(int argc, char **argv)
              cand.elements[j++] = p;
              continue;
            }
-#if 0
-         Solvable *s = pool->solvables + p;
-         if (!strcmp(pool_id2str(pool, s->name), "libusb-compat-devel"))
-           {
-             cand.elements[j++] = p;
-             continue;
-           }
-#endif
        }
       cand.count = j;
       if (i == j)
@@ -422,15 +435,6 @@ main(int argc, char **argv)
                }
            }
        }
-#if 0
-      else
-       {
-         if (!strcmp(pool_id2str(pool, s->name), "libusb-compat-devel"))
-           {
-             solver_printdecisions(solv);
-           }
-       }
-#endif
     }
   solver_free(solv);
   exit(status);