Imported Upstream version 0.6.12 11/94111/1 upstream/0.6.12
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 27 Oct 2016 05:54:32 +0000 (14:54 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 27 Oct 2016 05:54:33 +0000 (14:54 +0900)
Change-Id: I57ee1847c5c25f2602e0348213fa145f97cc3434
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
63 files changed:
CMakeLists.txt
NEWS
VERSION.cmake
bindings/CMakeLists.txt
bindings/python/CMakeLists.txt
bindings/solv.i
bindings/tcl/CMakeLists.txt [new file with mode: 0644]
bindings/tcl/solv.tm.in [new file with mode: 0644]
doc/appdata2solv.1
doc/archpkgs2solv.1
doc/archrepo2solv.1
doc/comps2solv.1
doc/deb2solv.1
doc/deltainfoxml2solv.1
doc/dumpsolv.1
doc/filters/xcode.pl
doc/helix2solv.1
doc/installcheck.1
doc/libsolv-bindings.3
doc/libsolv-bindings.txt
doc/libsolv-constantids.3
doc/libsolv-history.3
doc/libsolv-pool.3
doc/libsolv.3
doc/mdk2solv.1
doc/mergesolv.1
doc/repomdxml2solv.1
doc/rpmdb2solv.1
doc/rpmmd2solv.1
doc/rpms2solv.1
doc/susetags2solv.1
doc/testsolv.1
doc/updateinfoxml2solv.1
examples/p5solv
examples/pysolv
examples/rbsolv
examples/solv.c
examples/tclsolv [new file with mode: 0755]
ext/CMakeLists.txt
ext/pool_parserpmrichdep.c
ext/repo_deb.c
ext/repo_mdk.c
ext/repo_rpmdb.c
ext/testcase.c
ext/testcase.h
libsolv.pc.in [new file with mode: 0644]
package/libsolv.changes
package/libsolv.spec.in
src/CMakeLists.txt
src/chksum.c
src/chksum.h
src/cplxdeps.c
src/cplxdeps.h
src/libsolv.ver
src/pool.c
src/pool.h
src/poolarch.c
src/poolid.c
src/repodata.c
src/rules.c
src/solver.c
src/solver_private.h
tools/testsolv.c

index 45422af..2ba307c 100644 (file)
@@ -8,6 +8,7 @@ OPTION (DISABLE_SHARED "Do not build a shared version of the libraries?" OFF)
 OPTION (ENABLE_PERL "Build the perl bindings?" OFF)
 OPTION (ENABLE_PYTHON "Build the python bindings?" OFF)
 OPTION (ENABLE_RUBY "Build the ruby bindings?" OFF)
+OPTION (ENABLE_TCL "Build the Tcl bindings?" OFF)
 
 OPTION (USE_VENDORDIRS "Install the bindings in vendor directories?" OFF)
 
@@ -332,9 +333,9 @@ ENDIF (HAVE_LINKER_AS_NEEDED)
 ADD_SUBDIRECTORY (src)
 ADD_SUBDIRECTORY (ext)
 ADD_SUBDIRECTORY (tools)
-IF (ENABLE_PERL OR ENABLE_PYTHON OR ENABLE_RUBY)
+IF (ENABLE_PERL OR ENABLE_PYTHON OR ENABLE_RUBY OR ENABLE_TCL)
     ADD_SUBDIRECTORY (bindings)
-ENDIF (ENABLE_PERL OR ENABLE_PYTHON OR ENABLE_RUBY)
+ENDIF (ENABLE_PERL OR ENABLE_PYTHON OR ENABLE_RUBY OR ENABLE_TCL)
 ADD_SUBDIRECTORY (examples)
 ADD_SUBDIRECTORY (doc)
 
@@ -349,7 +350,14 @@ MACRO (SPECFILE)
   CONFIGURE_FILE (${CMAKE_SOURCE_DIR}/package/libsolv.spec.in ${CMAKE_BINARY_DIR}/package/libsolv.spec @ONLY)
 ENDMACRO (SPECFILE)
 
+MACRO (PCFILE)
+  MESSAGE (STATUS "Writing pkg-config file...")
+  CONFIGURE_FILE (${CMAKE_SOURCE_DIR}/libsolv.pc.in ${CMAKE_BINARY_DIR}/libsolv.pc @ONLY)
+  INSTALL( FILES ${CMAKE_BINARY_DIR}/libsolv.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
+ENDMACRO (PCFILE)
+
 SPECFILE ()
+PCFILE ()
 
 SET (AUTOBUILD_COMMAND
   COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_BINARY_DIR}/package/*.tar.bz2
diff --git a/NEWS b/NEWS
index 5f21c86..a265cc4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,39 @@
 This file contains the major changes between
 libsolv versions:
 
+Version 0.6.12:
+- new features:
+  * tcl bindings
+- new functions:
+  * solv_chksum_cmp
+
+Version 0.6.11:
+- new functions:
+  * pool_ids2whatprovides
+
+Version 0.6.9:
+- new features:
+  * much improved package choosing code
+  * new testcase dependency format
+  * alternatives introspection
+- new functions:
+  * pool_deb_get_autoinstalled
+  * solver_alternative2str
+  * solver_alternatives_count
+  * solver_get_alternative
+  * solver_rule2pkgrule
+  * testcase_dep2str
+
+Version 0.6.5:
+- new features:
+  * support yum style obsolete handling
+
+Version 0.6.1:
+- API change:
+  repodata_stringify() now returns the string
+- new features:
+  * add BREAK_ORPHANS and KEEP_ORPHANS solver flags
+
 Version 0.6.0:
 - ABI change: cleaned up and reordered knownid.h
 - added support for sha224/sha384/sha512
index fe0632b..086ee95 100644 (file)
@@ -49,5 +49,5 @@ SET(LIBSOLVEXT_SOVERSION "0")
 
 SET(LIBSOLV_MAJOR "0")
 SET(LIBSOLV_MINOR "6")
-SET(LIBSOLV_PATCH "11")
+SET(LIBSOLV_PATCH "12")
 
index 9000773..34b0784 100644 (file)
@@ -14,3 +14,6 @@ ENDIF (ENABLE_PERL)
 IF (ENABLE_RUBY)
     ADD_SUBDIRECTORY (ruby)
 ENDIF (ENABLE_RUBY)
+IF (ENABLE_TCL)
+    ADD_SUBDIRECTORY (tcl)
+ENDIF (ENABLE_TCL)
index 8b5c72c..22ddec3 100644 (file)
@@ -1,8 +1,7 @@
 #SET (PythonLibs_FIND_VERSION 3)
 
 FIND_PACKAGE (PythonLibs)
-SET (PythonInterp_FIND_VERSION ${PYTHONLIBS_VERSION_STRING})
-FIND_PACKAGE (PythonInterp REQUIRED)
+FIND_PACKAGE (PythonInterp ${PYTHONLIBS_VERSION_STRING} REQUIRED)
 EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} -c "from sys import stdout; from distutils import sysconfig; stdout.write(sysconfig.get_python_lib(True))" OUTPUT_VARIABLE PYTHON_INSTALL_DIR)
 
 IF (NOT DEFINED PYTHON_VERSION_MAJOR)
index 4030796..0c37f6c 100644 (file)
@@ -10,9 +10,9 @@
 %markfunc Pool "mark_Pool";
 #endif
 
-/*
- * binaryblob handling
- */
+/**
+ ** binaryblob handling
+ **/
 
 %{
 typedef struct {
@@ -22,7 +22,24 @@ typedef struct {
 %}
 
 %typemap(in,noblock=1,fragment="SWIG_AsCharPtrAndSize") (const unsigned char *str, size_t len) (int res, char *buf = 0, size_t size = 0, int alloc = 0) {
+#if defined(SWIGTCL)
+  {
+    int bal;
+    unsigned char *ba;
+    res = SWIG_TypeError;
+    ba = Tcl_GetByteArrayFromObj($input, &bal);
+    if (ba) {
+      buf = (char *)ba;
+      size = bal;
+      res = SWIG_OK;
+      alloc = SWIG_OLDOBJ;
+    }
+  }
+#else
   res = SWIG_AsCharPtrAndSize($input, &buf, &size, &alloc);
+  if (buf && size)
+    size--;
+#endif
   if (!SWIG_IsOK(res)) {
 #if defined(SWIGPYTHON)
     const void *pybuf = 0;
@@ -49,6 +66,8 @@ typedef struct {
 %typemap(out,noblock=1,fragment="SWIG_FromCharPtrAndSize") BinaryBlob {
 #if defined(SWIGPYTHON) && defined(PYTHON3)
   $result = $1.data ? Py_BuildValue("y#", $1.data, $1.len) : SWIG_Py_Void();
+#elif defined(SWIGTCL)
+  Tcl_SetObjResult(interp, $1.data ? Tcl_NewByteArrayObj($1.data, $1.len) : NULL);
 #else
   $result = SWIG_FromCharPtrAndSize($1.data, $1.len);
 #if defined(SWIGPERL)
@@ -57,10 +76,20 @@ typedef struct {
 #endif
 }
 
+/**
+ ** Queue handling
+ **/
+
+%typemap(arginit) Queue {
+  queue_init(&$1);
+}
+%typemap(freearg) Queue {
+  queue_free(&$1);
+}
+
 #if defined(SWIGPYTHON)
 %typemap(in) Queue {
   /* Check if is a list */
-  queue_init(&$1);
   if (PyList_Check($input)) {
     int size = PyList_Size($input);
     int i = 0;
@@ -68,16 +97,12 @@ typedef struct {
       PyObject *o = PyList_GetItem($input,i);
       int v;
       int e = SWIG_AsVal_int(o, &v);
-      if (!SWIG_IsOK(e)) {
+      if (!SWIG_IsOK(e))
         SWIG_exception_fail(SWIG_ArgError(e), "list must contain only integers");
-        queue_free(&$1);
-        return NULL;
-      }
       queue_push(&$1, v);
     }
   } else {
-    PyErr_SetString(PyExc_TypeError,"not a list");
-    return NULL;
+    SWIG_exception_fail(SWIG_TypeError, "list must contain only integers");
   }
 }
 
@@ -110,13 +135,12 @@ typedef struct {
 
 %enddef
 
-#endif
+#endif  /* SWIGPYTHON */
 
 #if defined(SWIGPERL)
 %typemap(in) Queue {
   AV *av;
   int i, size;
-  queue_init(&$1);
   if (!SvROK($input) || SvTYPE(SvRV($input)) != SVt_PVAV)
     SWIG_croak("Argument $argnum is not an array reference.");
   av = (AV*)SvRV($input);
@@ -145,6 +169,7 @@ typedef struct {
   queue_free(&$1);
   $result = 0;
 }
+
 %define Queue2Array(type, step, con) %{
   int i;
   int cnt = $1.count / step;
@@ -166,31 +191,22 @@ typedef struct {
 %}
 %enddef
 
-#endif
+#endif  /* SWIGPERL */
 
-%typemap(arginit) Queue {
-  queue_init(&$1);
-}
-%typemap(freearg) Queue {
-  queue_free(&$1);
-}
 
 #if defined(SWIGRUBY)
 %typemap(in) Queue {
   int size, i;
-  VALUE *o;
-  queue_init(&$1);
-  size = RARRAY_LEN($input);
+  VALUE *o, ary;
+  ary = rb_Array($input);
+  size = RARRAY_LEN(ary);
   i = 0;
-  o = RARRAY_PTR($input);
+  o = RARRAY_PTR(ary);
   for (i = 0; i < size; i++, o++) {
     int v;
     int e = SWIG_AsVal_int(*o, &v);
     if (!SWIG_IsOK(e))
-      {
-        SWIG_Error(SWIG_RuntimeError, "list must contain only integers");
-        SWIG_fail;
-      }
+      SWIG_exception_fail(SWIG_TypeError, "list must contain only integers");
     queue_push(&$1, v);
   }
 }
@@ -226,9 +242,90 @@ typedef struct {
   $result = o;
 %}
 %enddef
-#endif
 
+#endif  /* SWIGRUBY */
 
+#if defined(SWIGTCL)
+%typemap(in) Queue {
+  /* Check if is a list */
+  int size = 0;
+  int i = 0;
+
+  if (TCL_OK != Tcl_ListObjLength(interp, $input, &size))
+    SWIG_exception_fail(SWIG_TypeError, "argument is not a list");
+  for (i = 0; i < size; i++) {
+    Tcl_Obj *o = NULL;
+    int e, v;
+
+    if (TCL_OK != Tcl_ListObjIndex(interp, $input, i, &o))
+      SWIG_exception_fail(SWIG_IndexError, "failed to retrieve a list member");
+    e = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(o, &v);
+    if (!SWIG_IsOK(e))
+      SWIG_exception_fail(SWIG_ArgError(e), "list must contain only integers");
+    queue_push(&$1, v);
+  }
+}
+
+%typemap(out) Queue {
+  Tcl_Obj *objvx[$1.count];
+  int i;
+
+  for (i = 0; i < $1.count; i++) {
+    objvx[i] = SWIG_From_int($1.elements[i]);
+  }
+  Tcl_SetObjResult(interp, Tcl_NewListObj($1.count, objvx));
+  queue_free(&$1);
+}
+
+%define Queue2Array(type, step, con) %{
+  { /* scope is needed to make the goto of SWIG_exception_fail work */
+    int i;
+    int cnt = $1.count / step;
+    Id *idp = $1.elements;
+    Tcl_Obj *objvx[cnt];
+
+    for (i = 0; i < cnt; i++, idp += step) {
+      Id id = *idp;
+#define result resultx
+#define Tcl_SetObjResult(i, x) resultobj = x
+      type result = con;
+      Tcl_Obj *resultobj;
+      $typemap(out, type)
+      objvx[i] = resultobj;
+#undef Tcl_SetObjResult
+#undef result
+    }
+    queue_free(&$1);
+    Tcl_SetObjResult(interp, Tcl_NewListObj(cnt, objvx));
+  }
+%}
+
+%enddef
+
+%typemap(in) Queue solvejobs {
+  /* Check if is a list */
+  int size = 0;
+  int i = 0;
+
+  if (TCL_OK != Tcl_ListObjLength(interp, $input, &size))
+    SWIG_exception_fail(SWIG_TypeError, "argument is not a list");
+  for (i = 0; i < size; i++) {
+    Tcl_Obj *o = NULL;
+    void *jp;
+    Job *j;
+    int e;
+
+    if (TCL_OK != Tcl_ListObjIndex(interp, $input, i, &o))
+      SWIG_exception_fail(SWIG_IndexError, "failed to retrieve a list member");
+    e = SWIG_ConvertPtr(o, &jp ,SWIGTYPE_p_Job, 0 |  0 );
+    if (!SWIG_IsOK(e))
+      SWIG_exception_fail(SWIG_ArgError(e), "list member is not a Job");
+    j = (Job *)jp;
+    queue_push2(&$1, j->how, j->what);
+  }
+}
+
+#endif  /* SWIGTCL */
 
 
 #if defined(SWIGPERL)
@@ -329,27 +426,38 @@ SWIG_Perl_NewArrayObj(SWIG_MAYBE_PERL_OBJECT void *ptr, swig_type_info *t, int f
 %typemap(out) Repo_solvable_iterator * solvables_iter = Perliterator;
 %typemap(out) Dataiterator * = Perliterator;
 
-#endif
+#endif  /* SWIGPERL */
+
 
+/**
+ ** appdata handling
+ **/
 
 #if defined(SWIGPYTHON)
 typedef PyObject *AppObjectPtr;
+%typemap(in) AppObjectPtr {
+  if ($input)
+    Py_INCREF($input);
+  $1 = $input;
+}
 %typemap(out) AppObjectPtr {
   $result = $1 ? $1 : Py_None;
   Py_INCREF($result);
 }
-#endif
-#if defined(SWIGPERL)
+#elif defined(SWIGPERL)
 typedef SV *AppObjectPtr;
 %typemap(in) AppObjectPtr {
-  $1 = SvROK($input) ? SvRV($input) : 0;
+  if ($input) {
+    $1 = newSV(0);
+    sv_setsv((SV *)$1, $input);
+  } else
+    $1 = (void *)0;
 }
 %typemap(out) AppObjectPtr {
-  $result = $1 ? newRV_inc($1) : newSV(0);
+  $result = sv_2mortal($1 ? SvREFCNT_inc($1) : newSV(0));
   argvi++;
 }
-#endif
-#if defined(SWIGRUBY)
+#elif defined(SWIGRUBY)
 typedef VALUE AppObjectPtr;
 %typemap(in) AppObjectPtr {
   $1 = (void *)$input;
@@ -357,8 +465,23 @@ typedef VALUE AppObjectPtr;
 %typemap(out) AppObjectPtr {
   $result = (VALUE)$1;
 }
+#elif defined(SWIGTCL)
+typedef Tcl_Obj *AppObjectPtr;
+%typemap(in) AppObjectPtr {
+  if ($input)
+    Tcl_IncrRefCount($input);
+  $1 = (void *)$input;
+}
+%typemap(out) AppObjectPtr {
+  Tcl_SetObjResult(interp, $1 ? $1 : Tcl_NewObj());
+}
+#else
+#warning AppObjectPtr not defined for this language!
 #endif
 
+/**
+ ** FILE handling
+ **/
 
 #ifdef SWIGPYTHON
 %include "file.i"
@@ -372,6 +495,8 @@ typedef VALUE AppObjectPtr;
 SWIGINTERN int
 #ifdef SWIGRUBY
 SWIG_AsValSolvFpPtr(VALUE obj, FILE **val) {
+#elif defined(SWIGTCL)
+SWIG_AsValSolvFpPtr SWIG_TCL_DECL_ARGS_2(void *obj, FILE **val) {
 #else
 SWIG_AsValSolvFpPtr(void *obj, FILE **val) {
 #endif
@@ -393,14 +518,24 @@ SWIG_AsValSolvFpPtr(void *obj, FILE **val) {
   return SWIG_TypeError;
 }
 
+#if defined(SWIGTCL)
+#define SWIG_AsValSolvFpPtr(x, y) SWIG_AsValSolvFpPtr SWIG_TCL_CALL_ARGS_2(x, y)
+#endif
+
 }
 
 
+/**
+ ** DepId handling
+ **/
+
 %fragment("SWIG_AsValDepId","header") {
 
 SWIGINTERN int
 #ifdef SWIGRUBY
 SWIG_AsValDepId(VALUE obj, int *val) {
+#elif defined(SWIGTCL)
+SWIG_AsValDepId SWIG_TCL_DECL_ARGS_2(void *obj, int *val) {
 #else
 SWIG_AsValDepId(void *obj, int *val) {
 #endif
@@ -408,7 +543,11 @@ SWIG_AsValDepId(void *obj, int *val) {
   void *vptr = 0;
   int ecode;
   if (!desc) desc = SWIG_TypeQuery("Dep *");
+#ifdef SWIGTCL
+  ecode = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(obj, val);
+#else
   ecode = SWIG_AsVal_int(obj, val);
+#endif
   if (SWIG_IsOK(ecode))
     return ecode;
   if ((SWIG_ConvertPtr(obj, &vptr, desc, 0)) == SWIG_OK) {
@@ -419,21 +558,40 @@ SWIG_AsValDepId(void *obj, int *val) {
   return SWIG_TypeError;
 }
 
+#ifdef SWIGTCL
+#define SWIG_AsValDepId(x, y) SWIG_AsValDepId SWIG_TCL_CALL_ARGS_2(x, y)
+#endif
 }
 
+/**
+ ** Pool disown helper
+ **/
+
 %typemap(out) disown_helper {
-#ifdef SWIGRUBY
+#if defined(SWIGRUBY)
   SWIG_ConvertPtr(self, &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN |  0 );
-#endif
-#ifdef SWIGPYTHON
+#elif defined(SWIGPYTHON)
   SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN |  0 );
-#endif
-#ifdef SWIGPERL
+#elif defined(SWIGPERL)
   SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN |  0 );
+#elif defined(SWIGTCL)
+  SWIG_ConvertPtr(objv[1], &argp1, SWIGTYPE_p_Pool, SWIG_POINTER_DISOWN | 0);
+#else
+#warning disown_helper not implemented for this language, this is likely going to leak memory
 #endif
+
+#ifdef SWIGTCL
+  Tcl_SetObjResult(interp, SWIG_From_int((int)(0)));
+#else
   $result = SWIG_From_int((int)(0));
+#endif
 }
 
+
+/**
+ ** misc stuff
+ **/
+
 %include "typemaps.i"
 
 %typemap(in,numinputs=0,noblock=1) XRule **OUTPUT ($*1_ltype temp) {
@@ -447,6 +605,10 @@ SWIG_AsValDepId(void *obj, int *val) {
 %typemaps_asval(%checkcode(INT32), SWIG_AsValDepId, "SWIG_AsValDepId", DepId);
 
 
+/**
+ ** the C declarations
+ **/
+
 %{
 #include <stdbool.h>
 #include <stdio.h>
@@ -454,6 +616,7 @@ SWIG_AsValDepId(void *obj, int *val) {
 #include <sys/utsname.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <fcntl.h>
 
 /* argh, swig undefs bool for perl */
 #ifndef bool
@@ -501,6 +664,7 @@ typedef int bool;
 #include "repo_autopattern.h"
 #endif
 #include "solv_xfopen.h"
+#include "testcase.h"
 
 /* for old ruby versions */
 #ifndef RARRAY_PTR
@@ -626,8 +790,130 @@ typedef Dataiterator Datamatch;
 
 typedef int disown_helper;
 
+struct myappdata {
+  void *appdata;
+  int disowned;
+};
+
+
 %}
 
+/**
+ ** appdata helpers
+ **/
+
+#ifdef SWIGRUBY
+
+%{
+SWIGINTERN void appdata_disown_helper(void *appdata) {
+}
+SWIGINTERN void appdata_clr_helper(void **appdatap) {
+  *appdatap = 0;
+}
+SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
+  *appdatap = appdata;
+}
+SWIGINTERN void *appdata_get_helper(void *appdata) {
+  return appdata;
+}
+%}
+
+#elif defined(SWIGTCL)
+
+%{
+SWIGINTERN void appdata_disown_helper(void *appdata) {
+}
+SWIGINTERN void appdata_clr_helper(void **appdatap) {
+  if (*appdatap)
+    Tcl_DecrRefCount((Tcl_Obj *)(*appdatap));
+  *appdatap = 0;
+}
+SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
+  appdata_clr_helper(appdatap);
+  *appdatap = appdata;
+}
+SWIGINTERN void *appdata_get_helper(void *appdata) {
+  return appdata;
+}
+%}
+
+#elif defined(SWIGPYTHON)
+
+%{
+SWIGINTERN void appdata_disown_helper(void *appdata) {
+  struct myappdata *myappdata = appdata;
+  if (!myappdata || !myappdata->appdata || myappdata->disowned)
+    return;
+  myappdata->disowned = 1;
+  Py_DECREF((PyObject *)myappdata->appdata);
+}
+SWIGINTERN void appdata_clr_helper(void **appdatap) {
+  struct myappdata *myappdata = *(struct myappdata **)appdatap;
+  if (myappdata && myappdata->appdata && !myappdata->disowned) {
+    Py_DECREF((PyObject *)myappdata->appdata);
+  }
+  *appdatap = solv_free(myappdata);
+}
+SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
+  appdata_clr_helper(appdatap);
+  if (appdata) {
+    struct myappdata *myappdata = *appdatap = solv_calloc(sizeof(struct myappdata), 1);
+    myappdata->appdata = appdata;
+  }
+}
+SWIGINTERN void *appdata_get_helper(void *appdata) {
+  return appdata ? ((struct myappdata *)appdata)->appdata : 0;
+}
+
+%}
+
+#elif defined(SWIGPERL)
+
+%{
+SWIGINTERN void appdata_disown_helper(void *appdata) {
+  struct myappdata *myappdata = appdata;
+  SV *rsv;
+  if (!myappdata || !myappdata->appdata || myappdata->disowned)
+    return;
+  rsv = myappdata->appdata;
+  if (!SvROK(rsv))
+    return;
+  myappdata->appdata = SvRV(rsv);
+  myappdata->disowned = 1;
+  SvREFCNT_dec(rsv);
+}
+SWIGINTERN void appdata_clr_helper(void **appdatap) {
+  struct myappdata *myappdata = *(struct myappdata **)appdatap;
+  if (myappdata && myappdata->appdata && !myappdata->disowned) {
+    SvREFCNT_dec((SV *)myappdata->appdata);
+  }
+  *appdatap = solv_free(myappdata);
+}
+SWIGINTERN void appdata_set_helper(void **appdatap, void *appdata) {
+  appdata_clr_helper(appdatap);
+  if (appdata) {
+    struct myappdata *myappdata = *appdatap = solv_calloc(sizeof(struct myappdata), 1);
+    myappdata->appdata = appdata;
+  }
+}
+SWIGINTERN void *appdata_get_helper(void *appdata) {
+  struct myappdata *myappdata = appdata;
+  if (!myappdata || !myappdata->appdata)
+    return 0;
+  return myappdata->disowned ? newRV_noinc((SV *)myappdata->appdata) : myappdata->appdata;
+}
+
+%}
+
+#else
+#warning appdata helpers not implemented for this language
+#endif
+
+
+/**
+ ** the SWIG declarations defining the API
+ **/
+
 #ifdef SWIGRUBY
 %mixin Dataiterator "Enumerable";
 %mixin Pool_solvable_iterator "Enumerable";
@@ -647,6 +933,11 @@ typedef int Id;
 %constant int REL_GT;
 %constant int REL_LT;
 %constant int REL_ARCH;
+%constant int REL_AND;
+%constant int REL_OR;
+%constant int REL_WITH;
+%constant int REL_COND;
+%constant int REL_ELSE;
 
 typedef struct {
   Pool* const pool;
@@ -708,7 +999,6 @@ typedef struct {
 %nodefaultctor Pool;
 %nodefaultdtor Pool;
 typedef struct {
-  AppObjectPtr appdata;
 } Pool;
 
 %nodefaultctor Repo;
@@ -719,7 +1009,6 @@ typedef struct {
   int priority;
   int subpriority;
   int const nsolvables;
-  AppObjectPtr appdata;
 } Repo;
 
 %nodefaultctor Solver;
@@ -758,9 +1047,14 @@ SolvFp *solvfp_xfopen_fd(const char *fn, int fd, const char *mode = 0);
     SolvFp *sfp;
     FILE *fp;
     fd = dup(fd);
-    fp = fd == -1 ? 0 : solv_xfopen_fd(fn, fd, mode);
-    if (!fp)
+    if (fd == -1)
       return 0;
+    fcntl(fd, F_SETFD, FD_CLOEXEC);
+    fp = solv_xfopen_fd(fn, fd, mode);
+    if (!fp) {
+      close(fd);
+      return 0;
+    }
     sfp = solv_calloc(1, sizeof(SolvFp));
     sfp->fp = fp;
     return sfp;
@@ -771,6 +1065,8 @@ SolvFp *solvfp_xfopen_fd(const char *fn, int fd, const char *mode = 0);
     fp = solv_xfopen(fn, mode);
     if (!fp)
       return 0;
+    if (fileno(fp) != -1)
+      fcntl(fileno(fp), F_SETFD, FD_CLOEXEC);
     sfp = solv_calloc(1, sizeof(SolvFp));
     sfp->fp = fp;
     return sfp;
@@ -833,6 +1129,9 @@ typedef struct {
   int dup() {
     return $self->fp ? dup(fileno($self->fp)) : -1;
   }
+  bool write(const unsigned char *str, size_t len) {
+    return fwrite(str, len, 1, $self->fp) == 1;
+  }
   bool flush() {
     if (!$self->fp)
       return 1;
@@ -846,6 +1145,11 @@ typedef struct {
     $self->fp = 0;
     return ret;
   }
+  void cloexec(bool state) {
+    if (!$self->fp || fileno($self->fp) == -1)
+      return;
+    fcntl(fileno($self->fp), F_SETFD, state ? FD_CLOEXEC : 0);
+  }
 }
 
 %extend Job {
@@ -906,18 +1210,27 @@ typedef struct {
     return pool_isemptyupdatejob($self->pool, $self->how, $self->what);
   }
 
+#if defined(SWIGTCL)
+  %rename("==") __eq__;
+#endif
   bool __eq__(Job *j) {
     return $self->pool == j->pool && $self->how == j->how && $self->what == j->what;
   }
+#if defined(SWIGTCL)
+  %rename("!=") __ne__;
+#endif
   bool __ne__(Job *j) {
     return !Job___eq__($self, j);
   }
-#if defined(SWIGPERL)
+#if defined(SWIGPERL) || defined(SWIGTCL)
   %rename("str") __str__;
 #endif
   const char *__str__() {
     return pool_job2str($self->pool, $self->how, $self->what, 0);
   }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("repr") __repr__;
+#endif
   const char *__repr__() {
     const char *str = pool_job2str($self->pool, $self->how, $self->what, ~0);
     return pool_tmpjoin($self->pool, "<Job ", str, ">");
@@ -994,12 +1307,15 @@ typedef struct {
     return q;
   }
 
-#if defined(SWIGPERL)
+#if defined(SWIGPERL) || defined(SWIGTCL)
   %rename("str") __str__;
 #endif
   const char *__str__() {
     return pool_selection2str($self->pool, &$self->q, 0);
   }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("repr") __repr__;
+#endif
   const char *__repr__() {
     const char *str = pool_selection2str($self->pool, &$self->q, ~0);
     return pool_tmpjoin($self->pool, "<Selection ", str, ">");
@@ -1019,6 +1335,20 @@ typedef struct {
       return 0;
     return solv_chksum_create_from_bin(type, buf);
   }
+  %newobject from_bin;
+  static Chksum *from_bin(Id type, const unsigned char *str, size_t len) {
+    return len == solv_chksum_len(type) ? solv_chksum_create_from_bin(type, str) : 0;
+  }
+#if defined(SWIGPERL)
+  %perlcode {
+    undef *solv::Chksum::from_bin;
+    *solv::Chksum::from_bin = sub {
+      my $pkg = shift;
+      my $self = solvc::Chksum_from_bin(@_);
+      bless $self, $pkg if defined $self;
+    };
+  }
+#endif
   ~Chksum() {
     solv_chksum_free($self, 0);
   }
@@ -1087,24 +1417,22 @@ typedef struct {
     return solv_chksum_type2str(solv_chksum_get_type($self));
   }
 
+#if defined(SWIGTCL)
+  %rename("==") __eq__;
+#endif
   bool __eq__(Chksum *chk) {
-    int l;
-    const unsigned char *b, *bo;
-    if (!chk)
-      return 0;
-    if (solv_chksum_get_type($self) != solv_chksum_get_type(chk))
-      return 0;
-    b = solv_chksum_get($self, &l);
-    bo = solv_chksum_get(chk, 0);
-    return memcmp(b, bo, l) == 0;
+    return solv_chksum_cmp($self, chk);
   }
+#if defined(SWIGTCL)
+  %rename("!=") __ne__;
+#endif
   bool __ne__(Chksum *chk) {
-    return !Chksum___eq__($self, chk);
+    return !solv_chksum_cmp($self, chk);
   }
 #if defined(SWIGRUBY)
   %rename("to_s") __str__;
 #endif
-#if defined(SWIGPERL)
+#if defined(SWIGPERL) || defined(SWIGTCL)
   %rename("str") __str__;
 #endif
   %newobject __str__;
@@ -1117,6 +1445,9 @@ typedef struct {
     solv_free((void *)h);
     return str;
   }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("repr") __repr__;
+#endif
   %newobject __repr__;
   const char *__repr__() {
     const char *h = Chksum___str__($self);
@@ -1172,18 +1503,21 @@ typedef struct {
     return SWIG_IsOK(ecode) ? vresult : 0;
   }
   %}
-  void set_loadcallback(PyObject *callable) {
+  void clr_loadcallback() {
     if ($self->loadcallback == loadcallback) {
       PyObject *obj = $self->loadcallbackdata;
       Py_DECREF(obj);
+      pool_setloadcallback($self, 0, 0);
     }
+  }
+  void set_loadcallback(PyObject *callable) {
+    Pool_clr_loadcallback($self);
     if (callable) {
       Py_INCREF(callable);
+      pool_setloadcallback($self, loadcallback, callable);
     }
-    pool_setloadcallback($self, callable ? loadcallback : 0, callable);
   }
-#endif
-#if defined(SWIGPERL)
+#elif defined(SWIGPERL)
 %{
   SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
     int count;
@@ -1206,16 +1540,20 @@ typedef struct {
     return ret;
   }
 %}
-  void set_loadcallback(SV *callable) {
-    if ($self->loadcallback == loadcallback)
+  void clr_loadcallback() {
+    if ($self->loadcallback == loadcallback) {
       SvREFCNT_dec($self->loadcallbackdata);
-    if (callable)
+      pool_setloadcallback($self, 0, 0);
+    }
+  }
+  void set_loadcallback(SV *callable) {
+    Pool_clr_loadcallback($self);
+    if (callable) {
       SvREFCNT_inc(callable);
-    pool_setloadcallback($self, callable ? loadcallback : 0, callable);
+      pool_setloadcallback($self, loadcallback, callable);
+    }
   }
-#endif
-
-#if defined(SWIGRUBY)
+#elif defined(SWIGRUBY)
 %{
   SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
     XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
@@ -1232,26 +1570,97 @@ typedef struct {
     }
   }
 %}
+  void clr_loadcallback() {
+    pool_setloadcallback($self, 0, 0);
+  }
   %typemap(in, numinputs=0) VALUE callable {
     $1 = rb_block_given_p() ? rb_block_proc() : 0;
   }
   void set_loadcallback(VALUE callable) {
     pool_setloadcallback($self, callable ? loadcallback : 0, (void *)callable);
   }
+#elif defined(SWIGTCL)
+  %{
+  typedef struct {
+    Tcl_Interp *interp;
+    Tcl_Obj *obj;
+  } tcl_callback_t;
+  SWIGINTERN int loadcallback(Pool *pool, Repodata *data, void *d) {
+    tcl_callback_t *callback_var = (tcl_callback_t *)d;
+    Tcl_Interp *interp = callback_var->interp;
+    XRepodata *xd = new_XRepodata(data->repo, data->repodataid);
+    int result, ecode = 0, vresult = 0;
+    Tcl_Obj *objvx[2];
+    objvx[0] = callback_var->obj;
+    objvx[1] = SWIG_NewInstanceObj(SWIG_as_voidptr(xd), SWIGTYPE_p_XRepodata, 0); 
+    Tcl_IncrRefCount(objvx[1]);
+    result = Tcl_EvalObjv(interp, sizeof(objvx)/sizeof(*objvx), objvx, TCL_EVAL_GLOBAL);
+    Tcl_DecrRefCount(objvx[1]);
+    if (result != TCL_OK)
+      return 0; /* exception */
+    ecode = SWIG_AsVal_int(interp, Tcl_GetObjResult(interp), &vresult);
+    return SWIG_IsOK(ecode) ? vresult : 0;
+  }
+  %}
+  void clr_loadcallback() {
+    if ($self->loadcallback == loadcallback) {
+      tcl_callback_t *callback_var = $self->loadcallbackdata;
+      Tcl_DecrRefCount(callback_var->obj);
+      solv_free(callback_var);
+      pool_setloadcallback($self, 0, 0);
+    }
+  }
+  void set_loadcallback(Tcl_Obj *callable, Tcl_Interp *interp) {
+    Pool_clr_loadcallback($self);
+    if (callable) {
+      tcl_callback_t *callback_var = solv_malloc(sizeof(tcl_callback_t));
+      Tcl_IncrRefCount(callable);
+      callback_var->interp = interp;
+      callback_var->obj = callable;
+      pool_setloadcallback($self, loadcallback, callback_var);
+    }
+  }
+#else
+#warning loadcallback not implemented for this language
 #endif
 
   ~Pool() {
-    Pool_set_loadcallback($self, 0);
-    pool_free($self);
+    Pool *pool = $self;
+    Id repoid;
+    Repo *repo;
+    FOR_REPOS(repoid, repo)
+      appdata_clr_helper(&repo->appdata);
+    Pool_clr_loadcallback(pool);
+    appdata_clr_helper(&pool->appdata);
+    pool_free(pool);
   }
   disown_helper free() {
-    Pool_set_loadcallback($self, 0);
-    pool_free($self);
+    Pool *pool = $self;
+    Id repoid;
+    Repo *repo;
+    FOR_REPOS(repoid, repo)
+      appdata_clr_helper(&repo->appdata);
+    Pool_clr_loadcallback(pool);
+    appdata_clr_helper(&pool->appdata);
+    pool_free(pool);
     return 0;
   }
   disown_helper disown() {
     return 0;
   }
+  AppObjectPtr appdata;
+  %{
+  SWIGINTERN void Pool_appdata_set(Pool *pool, AppObjectPtr appdata) {
+    appdata_set_helper(&pool->appdata, appdata);
+  }
+  SWIGINTERN AppObjectPtr Pool_appdata_get(Pool *pool) {
+    return appdata_get_helper(pool->appdata);
+  }
+  %}
+  void appdata_disown() {
+    appdata_disown_helper($self->appdata);
+  }
+
   Id str2id(const char *str, bool create=1) {
     return pool_str2id($self, str, create);
   }
@@ -1369,10 +1778,10 @@ typedef struct {
   SWIGINTERN void Pool_installed_set(Pool *pool, Repo *installed) {
     pool_set_installed(pool, installed);
   }
-  Repo *Pool_installed_get(Pool *pool) {
+  SWIGINTERN Repo *Pool_installed_get(Pool *pool) {
     return pool->installed;
   }
-  const char *Pool_errstr_get(Pool *pool) {
+  SWIGINTERN const char *Pool_errstr_get(Pool *pool) {
     return pool_errstr(pool);
   }
   %}
@@ -1522,6 +1931,7 @@ rb_eval_string(
 #endif
 
   void free(bool reuseids = 0) {
+    appdata_clr_helper(&$self->appdata);
     repo_free($self, reuseids);
   }
   void empty(bool reuseids = 0) {
@@ -1533,6 +1943,17 @@ rb_eval_string(
   bool isempty() {
     return !$self->nsolvables;
   }
+
+  AppObjectPtr appdata;
+  %{
+  SWIGINTERN void Repo_appdata_set(Repo *repo, AppObjectPtr appdata) {
+    appdata_set_helper(&repo->appdata, appdata);
+  }
+  SWIGINTERN AppObjectPtr Repo_appdata_get(Repo *repo) {
+    return appdata_get_helper(repo->appdata);
+  }
+  %}
+
   bool add_solv(const char *name, int flags = 0) {
     FILE *fp = fopen(name, "r");
     int r;
@@ -1755,13 +2176,19 @@ rb_eval_string(
   }
 #endif
 
+#if defined(SWIGTCL)
+  %rename("==") __eq__;
+#endif
   bool __eq__(Repo *repo) {
     return $self == repo;
   }
+#if defined(SWIGTCL)
+  %rename("!=") __ne__;
+#endif
   bool __ne__(Repo *repo) {
     return $self != repo;
   }
-#if defined(SWIGPERL)
+#if defined(SWIGPERL) || defined(SWIGTCL)
   %rename("str") __str__;
 #endif
   %newobject __str__;
@@ -1772,6 +2199,9 @@ rb_eval_string(
     sprintf(buf, "Repo#%d", $self->repoid);
     return solv_strdup(buf);
   }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("repr") __repr__;
+#endif
   %newobject __repr__;
   const char *__repr__() {
     char buf[20];
@@ -2038,6 +2468,9 @@ rb_eval_string(
     *solv::Datamatch::str = *solvc::Datamatch_stringify;
   }
 #endif
+#if defined(SWIGTCL)
+  %rename("stringify") __str__;
+#endif
   const char *__str__() {
     KeyValue kv = $self->kv;
     const char *str = repodata_stringify($self->pool, $self->data, $self->key, &kv, SEARCH_FILES | SEARCH_CHECKSUMS);
@@ -2262,18 +2695,27 @@ rb_eval_string(
   const char *str() {
     return pool_dep2str($self->pool, $self->id);
   }
+#if defined(SWIGTCL)
+  %rename("==") __eq__;
+#endif
   bool __eq__(Dep *s) {
     return $self->pool == s->pool && $self->id == s->id;
   }
+#if defined(SWIGTCL)
+  %rename("!=") __ne__;
+#endif
   bool __ne__(Dep *s) {
     return !Dep___eq__($self, s);
   }
-#if defined(SWIGPERL)
+#if defined(SWIGPERL) || defined(SWIGTCL)
   %rename("str") __str__;
 #endif
   const char *__str__() {
     return pool_dep2str($self->pool, $self->id);
   }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("repr") __repr__;
+#endif
   %newobject __repr__;
   const char *__repr__() {
     char buf[20];
@@ -2500,18 +2942,27 @@ rb_eval_string(
     return pool_evrcmp($self->pool, $self->pool->solvables[$self->id].evr, s2->pool->solvables[s2->id].evr, EVRCMP_COMPARE);
   }
 
+#if defined(SWIGTCL)
+  %rename("==") __eq__;
+#endif
   bool __eq__(XSolvable *s) {
     return $self->pool == s->pool && $self->id == s->id;
   }
+#if defined(SWIGTCL)
+  %rename("!=") __ne__;
+#endif
   bool __ne__(XSolvable *s) {
     return !XSolvable___eq__($self, s);
   }
-#if defined(SWIGPERL)
+#if defined(SWIGPERL) || defined(SWIGTCL)
   %rename("str") __str__;
 #endif
   const char *__str__() {
     return pool_solvid2str($self->pool, $self->id);
   }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("repr") __repr__;
+#endif
   %newobject __repr__;
   const char *__repr__() {
     char buf[20];
@@ -2572,7 +3023,7 @@ rb_eval_string(
       queue_push(&q, i);
     return q;
   }
-#if defined(SWIGPERL)
+#if defined(SWIGPERL) || defined(SWIGTCL)
   %rename("str") __str__;
 #endif
   const char *__str__() {
@@ -2728,7 +3179,7 @@ rb_eval_string(
   %newobject Job;
   Job *Job() {
     Id extraflags = solver_solutionelement_extrajobflags($self->solv, $self->problemid, $self->solutionid);
-    if ($self->type == SOLVER_SOLUTION_JOB || SOLVER_SOLUTION_POOLJOB)
+    if ($self->type == SOLVER_SOLUTION_JOB || $self->type == SOLVER_SOLUTION_POOLJOB)
       return new_Job($self->solv->pool, SOLVER_NOOP, 0);
     if ($self->type == SOLVER_SOLUTION_INFARCH || $self->type == SOLVER_SOLUTION_DISTUPGRADE || $self->type == SOLVER_SOLUTION_BEST)
       return new_Job($self->solv->pool, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_NOTBYUSER|extraflags, $self->p);
@@ -2870,6 +3321,21 @@ rb_eval_string(
       queue_push(&q, i);
     return q;
   }
+#if defined(SWIGTCL)
+  %typemap(out) Queue solve Queue2Array(Problem *, 1, new_Problem(arg1, id));
+  %newobject solve;
+  Queue solve(Queue solvejobs) {
+    Queue q;
+    int i, cnt;
+    queue_init(&q);
+    solver_solve($self, &solvejobs);
+    cnt = solver_problem_count($self);
+    for (i = 1; i <= cnt; i++)
+      queue_push(&q, i);
+    return q;
+  }
+#endif
+
   %newobject transaction;
   Transaction *transaction() {
     return solver_create_transaction($self);
@@ -2882,6 +3348,44 @@ rb_eval_string(
     return reason;
   }
 
+  %newobject describe_weakdep_decision_raw;
+  Queue describe_weakdep_decision_raw(XSolvable *s) {
+    Queue q;
+    queue_init(&q);
+    solver_describe_weakdep_decision($self, s->id, &q);
+    return q;
+  }
+#if defined(SWIGPYTHON)
+  %pythoncode {
+    def describe_weakdep_decision(self, s):
+      d = iter(self.describe_weakdep_decision_raw(s))
+      return [ (t, XSolvable(self.pool, sid), Dep(self.pool, id)) for t, sid, id in zip(d, d, d) ]
+  }
+#endif
+#if defined(SWIGPERL)
+  %perlcode {
+    sub solv::Solver::describe_weakdep_decision {
+      my ($self, $s) = @_;
+      my $pool = $self->{'pool'};
+      my @res;
+      my @d = $self->describe_weakdep_decision_raw($s);
+      push @res, [ splice(@d, 0, 3) ] while @d;
+      return map { [ $_->[0], solv::XSolvable->new($pool, $_->[1]), solv::Dep->new($pool, $_->[2]) ] } @res;
+    }
+  }
+#endif
+#if defined(SWIGRUBY)
+%init %{
+rb_eval_string(
+    "class Solv::Solver\n"
+    "  def describe_weakdep_decision(s)\n"
+    "    self.describe_weakdep_decision_raw(s).each_slice(3).map { |t, sid, id| [ t, Solv::XSolvable.new(self.pool, sid), Solv::Dep.new(self.pool, id)] }\n"
+    "  end\n"
+    "end\n"
+  );
+%}
+#endif
+
   int alternatives_count() {
     return solver_alternatives_count($self);
   }
@@ -2915,6 +3419,10 @@ rb_eval_string(
       queue_push(&q, i);
     return q;
   }
+
+  bool write_testcase(const char *dir) {
+    return testcase_write($self, dir, TESTCASE_RESULT_TRANSACTION | TESTCASE_RESULT_PROBLEMS, 0, 0);
+  }
 }
 
 %extend Transaction {
@@ -3108,12 +3616,21 @@ rb_eval_string(
     return q;
   }
 
+#if defined(SWIGTCL)
+  %rename("==") __eq__;
+#endif
   bool __eq__(XRule *xr) {
     return $self->solv == xr->solv && $self->id == xr->id;
   }
+#if defined(SWIGTCL)
+  %rename("!=") __ne__;
+#endif
   bool __ne__(XRule *xr) {
     return !XRule___eq__($self, xr);
   }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("repr") __repr__;
+#endif
   %newobject __repr__;
   const char *__repr__() {
     char buf[20];
@@ -3224,12 +3741,21 @@ rb_eval_string(
     Repodata *data = repo_id2repodata($self->repo, $self->id);
     repodata_extend_block(data, data->repo->start, data->repo->end - data->repo->start);
   }
+#if defined(SWIGTCL)
+  %rename("==") __eq__;
+#endif
   bool __eq__(XRepodata *xr) {
     return $self->repo == xr->repo && $self->id == xr->id;
   }
+#if defined(SWIGTCL)
+  %rename("!=") __ne__;
+#endif
   bool __ne__(XRepodata *xr) {
     return !XRepodata___eq__($self, xr);
   }
+#if defined(SWIGPERL) || defined(SWIGTCL)
+  %rename("repr") __repr__;
+#endif
   %newobject __repr__;
   const char *__repr__() {
     char buf[20];
@@ -3309,10 +3835,32 @@ rb_eval_string(
     return r;
   }
 
-#if defined(SWIGPERL)
+#if defined(SWIGPERL) || defined(SWIGTCL)
   %rename("str") __str__;
 #endif
   const char *__str__() {
     return solver_alternative2str($self->solv, $self->type, $self->type == SOLVER_ALTERNATIVE_TYPE_RULE ? $self->rid : $self->dep_id, $self->from_id);
   }
 }
+
+#if defined(SWIGTCL)
+%init %{
+  Tcl_Eval(interp,
+"proc solv::iter {varname iter body} {\n"\
+"  while 1 {\n"\
+"    set value [$iter __next__]\n"\
+"    if {$value eq \"NULL\"} { break }\n"\
+"    uplevel [list set $varname $value]\n"\
+"    set code [catch {uplevel $body} result]\n"\
+"    switch -exact -- $code {\n"\
+"      0 {}\n"\
+"      3 { return }\n"\
+"      4 {}\n"\
+"      default { return -code $code $result }\n"\
+"    }\n"\
+"  }\n"\
+"}\n"
+  );
+%}
+#endif
+
diff --git a/bindings/tcl/CMakeLists.txt b/bindings/tcl/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f78de9f
--- /dev/null
@@ -0,0 +1,38 @@
+FIND_PACKAGE (TCL)
+
+SET (SWIG_TCL_FLAGS -namespace -pkgversion ${VERSION})
+
+EXECUTE_PROCESS (
+    COMMAND echo "puts -nonewline [lindex [::tcl::tm::list] end]"
+    COMMAND ${TCL_TCLSH}
+    OUTPUT_VARIABLE TCL_INSTALL_DIR
+)
+
+MESSAGE (STATUS "Tclsh executable: ${TCL_TCLSH}")
+MESSAGE (STATUS "Tcl installation dir: ${TCL_INSTALL_DIR}")
+
+ADD_CUSTOM_COMMAND (
+    OUTPUT solv_tcl.c
+    COMMAND ${SWIG_EXECUTABLE} ${SWIG_FLAGS} -tcl ${SWIG_TCL_FLAGS} -I${CMAKE_SOURCE_DIR}/src -o solv_tcl.c ${CMAKE_SOURCE_DIR}/bindings/solv.i
+    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+    DEPENDS ${CMAKE_SOURCE_DIR}/bindings/solv.i
+)
+
+ADD_DEFINITIONS(-Wno-unused)
+INCLUDE_DIRECTORIES (${TCL_INCLUDE_PATH})
+
+ADD_LIBRARY (bindings_tcl SHARED solv_tcl.c)
+SET_TARGET_PROPERTIES (bindings_tcl PROPERTIES PREFIX "" OUTPUT_NAME "solv-${VERSION}" INSTALL_NAME_DIR "${TCL_INSTALL_DIR}")
+TARGET_LINK_LIBRARIES (bindings_tcl libsolvext libsolv ${TCL_LIBRARY} ${SYSTEM_LIBRARIES})
+INSTALL (TARGETS bindings_tcl LIBRARY DESTINATION ${TCL_INSTALL_DIR})
+
+ADD_CUSTOM_COMMAND (
+    OUTPUT solv.tm
+       COMMAND sed -e "s/__VERSION__/${VERSION}/" ${CMAKE_SOURCE_DIR}/bindings/tcl/solv.tm.in >${CMAKE_CURRENT_BINARY_DIR}/solv.tm
+    DEPENDS ${CMAKE_SOURCE_DIR}/bindings/tcl/solv.tm.in
+    COMMENT "Creating Tcl module to load libsolv"
+)
+ADD_CUSTOM_TARGET (solv_tm ALL DEPENDS solv.tm)
+SET_SOURCE_FILES_PROPERTIES (solv.tm PROPERTIES GENERATED TRUE)
+
+INSTALL (FILES ${CMAKE_CURRENT_BINARY_DIR}/solv.tm DESTINATION ${TCL_INSTALL_DIR} RENAME solv-${VERSION}.tm)
diff --git a/bindings/tcl/solv.tm.in b/bindings/tcl/solv.tm.in
new file mode 100644 (file)
index 0000000..3b94771
--- /dev/null
@@ -0,0 +1,4 @@
+package require Tcl
+
+#package provide solv __VERSION__
+load [::file join [::file dirname [::info script]] "solv-__VERSION__[::info sharedlibextension]"]
index 174e25e..b5fdf9f 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: appdata2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/25/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "APPDATA2SOLV" "1" "09/25/2014" "libsolv" "LIBSOLV"
+.TH "APPDATA2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index e4ef62d..6052aee 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: archpkgs2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/25/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "ARCHPKGS2SOLV" "1" "09/25/2014" "libsolv" "LIBSOLV"
+.TH "ARCHPKGS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 4987821..94f5c41 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: archrepo2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/25/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "ARCHREPO2SOLV" "1" "09/25/2014" "libsolv" "LIBSOLV"
+.TH "ARCHREPO2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index e8ec2f7..c6f8d32 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: comps2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/25/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "COMPS2SOLV" "1" "09/25/2014" "libsolv" "LIBSOLV"
+.TH "COMPS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 4cfa093..95f5091 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: deb2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/25/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "DEB2SOLV" "1" "09/25/2014" "libsolv" "LIBSOLV"
+.TH "DEB2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 844c4fb..11cda74 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: deltainfoxml2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/24/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "DELTAINFOXML2SOLV" "1" "09/24/2014" "libsolv" "LIBSOLV"
+.TH "DELTAINFOXML2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 3e1400e..cb6a136 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: dumpsolv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/22/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "DUMPSOLV" "1" "09/22/2014" "libsolv" "LIBSOLV"
+.TH "DUMPSOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 54b4080..089f511 100755 (executable)
@@ -35,7 +35,11 @@ while(<STDIN>) {
   if (!/^[a-zA-Z0-9_]+$/) {
     $_ = " $_";
     $_ = "$_ ";
-    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;
+    if (s/^ TCL +/ /) {
+      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;
+    }
     # fixup for perl bare words
     s/{<-S><I>([a-zA-Z_][a-zA-Z0-9]*)<-I><S>}/{$1}/g;
     # fixup for callbackfunctions
index 5a8d678..900482f 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: helix2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/24/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "HELIX2SOLV" "1" "09/24/2014" "libsolv" "LIBSOLV"
+.TH "HELIX2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 85e4d89..7ee3e4d 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: installcheck
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/22/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "INSTALLCHECK" "1" "09/22/2014" "libsolv" "LIBSOLV"
+.TH "INSTALLCHECK" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 1f52285..9fe26c1 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: 09/29/2014
+.\"      Date: 08/28/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "LIBSOLV\-BINDINGS" "3" "09/29/2014" "libsolv" "LIBSOLV"
+.TH "LIBSOLV\-BINDINGS" "3" "08/28/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -312,6 +312,99 @@ Note that boolean methods have an added trailing \(lq?\(rq, to be consistent wit
 .if n \{\
 .RE
 .\}
+.SH "TCL SPECIFICS"
+.sp
+Libsolv\(cqs tcl bindings can be loaded with the following statement:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBpackage require solv\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Objects are either created by calling class name prefixed with \(lqnew_\(rq, or they are returned by calling methods on other objects\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBset pool [solv::new_Pool]\fR
+\fBset repo [\fR\fI$pool\fR \fBadd_repo "my_first_repo"]\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Swig provides a \(lqcget\(rq method to read object attributes, and a \(lqconfigure\(rq method to write them:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fI$pool\fR \fBconfigure \-appdata 42\fR
+\fBputs "appdata is [\fR\fI$pool\fR \fBcget \-appdata]"\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+The tcl bindings provide a little helper to work with iterators in a foreach style:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBset iter [\fR\fI$pool\fR \fBsolvables_iter]\fR
+\fBsolv::iter s\fR \fI$iter\fR \fB{ \&.\&.\&. }\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+libsolv\(cqs arrays are mapped to tcl\(cqs lists:
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBset jobs [list\fR \fI$job1 $job2\fR\fB]\fR
+\fBset problems [\fR\fI$solver\fR \fBsolve\fR \fI$jobs\fR\fB]\fR
+\fBputs "We have [llength\fR \fI$problems\fR\fB] problems\&.\&.\&."\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Stringification is done by calling the object\(cqs \(lqstr\(rq method\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBputs [\fR\fI$dep\fR \fBstr]\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+There is one exception: you have to use \(lqstringify\(rq for Datamatch objects, as swig reports a clash with the \(lqstr\(rq attribute\&. Some objects also support a \(lq==\(rq method for equality tests, and a \(lq!=\(rq method\&.
+.sp
+Swig implements all constants as numeric variables, constants belonging to a libsolv class are prefixed with the class name:
+.sp
+.if n \{\
+.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
+.if n \{\
+.RE
+.\}
 .SH "THE SOLV CLASS"
 .sp
 This is the main namespace of the library, you cannot create objects of this type but it contains some useful constants\&.
@@ -916,6 +1009,21 @@ Get/Set fixed jobs stored in the pool\&. Those jobs are automatically appended t
 .\}
 .sp
 Set the callback function called when repository metadata needs to be loaded on demand\&. To make use of this feature, you need to create repodata stubs that tell the library which data is available but not loaded\&. If later on the data needs to be accessed, the callback function is called with a repodata argument\&. You can then load the data (maybe fetching it first from an remote server)\&. The callback should return true if the data has been made available\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+/* bindings only */
+\fI$pool\fR\fB\->appdata_disown()\fR
+\fIpool\fR\fB\&.appdata_disown()\fR
+\fIpool\fR\fB\&.appdata_disown()\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Decrement the reference count of the appdata object\&. This can be used to break circular references (e\&.g\&. if the pool\(cqs appdata value points to some meta data structure that contains a pool handle)\&. If used incorrectly, this method can lead to application crashes, so beware\&. (This method is a no\-op for ruby and tcl\&.)
 .SS "DATA RETRIEVAL METHODS"
 .sp
 In the following functions, the \fIkeyname\fR argument describes what to retrieve\&. For the standard cases you can use the available Id constants\&. For example,
@@ -4892,7 +5000,22 @@ my \fI$chksum\fR \fB= solv::Chksum\->new(\fR\fI$type\fR\fB,\fR \fI$hex\fR\fB)\fR
 .RE
 .\}
 .sp
-Create an already finalized checksum object\&.
+Create an already finalized checksum object from a hex string\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+\fBChksum Chksum_from_bin(Id\fR \fItype\fR\fB, char *\fR\fIbin\fR\fB)\fR
+my \fI$chksum\fR \fB= solv::Chksum\->from_bin(\fR\fI$type\fR\fB,\fR \fI$bin\fR\fB)\fR;
+\fIchksum\fR \fB= solv\&.Chksum\&.from_bin(\fR\fItype\fR\fB,\fR \fIbin\fR\fB)\fR
+\fIchksum\fR \fB= Solv::Chksum\&.from_bin(\fR\fItype\fR\fB,\fR \fIbin\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Create an already finalized checksum object from a binary checksum\&.
 .SS "ATTRIBUTES"
 .sp
 .if n \{\
@@ -4924,7 +5047,7 @@ Return the type of the checksum object\&.
 .RE
 .\}
 .sp
-Add a string to the checksum\&.
+Add a (binary) string to the checksum\&.
 .sp
 .if n \{\
 .RS 4
@@ -5077,7 +5200,7 @@ my \fI$file\fR \fB= solv::xfopen_fd(\fR\fI$path\fR\fB,\fR \fI$fileno\fR\fB)\fR;
 .RE
 .\}
 .sp
-Create a file handle from the specified file descriptor\&. The path argument is only used to select the correct (de\-)compression algorithm, use an empty path if you want to make sure to read/write raw data\&.
+Create a file handle from the specified file descriptor\&. The path argument is only used to select the correct (de\-)compression algorithm, use an empty path if you want to make sure to read/write raw data\&. The file descriptor is dup()ed before the file handle is created\&.
 .SS "METHODS"
 .sp
 .if n \{\
@@ -5099,6 +5222,21 @@ Return file file descriptor of the file\&. If the file is not open, \-1 is retur
 .RS 4
 .\}
 .nf
+\fBvoid cloexec(bool\fR \fIstate\fR\fB)\fR
+\fI$file\fR\fB\->cloexec(\fR\fI$state\fR\fB)\fR
+\fIfile\fR\fB\&.cloexec(\fR\fIstate\fR\fB)\fR
+\fIfile\fR\fB\&.cloexec(\fR\fIstate\fR\fB)\fR
+.fi
+.if n \{\
+.RE
+.\}
+.sp
+Set the close\-on\-exec flag of the file descriptor\&. The xfopen function returns files with close\-on\-exec turned on, so if you want to pass a file to some other process you need to call cloexec(0) before calling exec\&.
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
 \fBint dup()\fR
 my \fI$fileno\fR \fB=\fR \fI$file\fR\fB\->dup()\fR;
 \fIfileno\fR \fB=\fR \fIfile\fR\fB\&.dup()\fR
@@ -5138,7 +5276,7 @@ Flush the file\&. Returns false if there was an error\&. Flushing a closed file
 .RE
 .\}
 .sp
-Close the file\&. This is needed for languages like Ruby, that do not destruct objects right after they are no longer referenced\&. In that case, it is good style to close open files so that the file descriptors are freed right away\&. Returns false if there was an error\&.
+Close the file\&. This is needed for languages like Ruby that do not destruct objects right after they are no longer referenced\&. In that case, it is good style to close open files so that the file descriptors are freed right away\&. Returns false if there was an error\&.
 .SH "THE REPODATA CLASS"
 .sp
 The Repodata stores attributes for packages and the repository itself, each repository can have multiple repodata areas\&. You normally only need to directly access them if you implement lazy downloading of repository data\&. Repodata areas are created by calling the repository\(cqs add_repodata() method or by using repo_add methods without the REPO_REUSE_REPODATA or REPO_USE_LOADING flag\&.
index 2b51635..175d922 100644 (file)
@@ -141,6 +141,52 @@ other ruby modules:
        puts "empty" if repo.isempty?
 
 
+Tcl Specifics
+-------------
+Libsolv's tcl bindings can be loaded with the following statement:
+
+       TCL package require solv
+
+Objects are either created by calling class name prefixed with ``new_'',
+or they are returned by calling methods on other objects.
+
+       TCL set pool [solv::new_Pool]
+       TCL set repo [$pool add_repo "my_first_repo"]
+
+Swig provides a ``cget'' method to read object attributes, and a
+``configure'' method to write them:
+
+       TCL $pool configure -appdata 42
+       TCL puts "appdata is [$pool cget -appdata]"
+
+The tcl bindings provide a little helper to work with iterators in
+a foreach style:
+
+       TCL set iter [$pool solvables_iter]
+       TCL solv::iter s $iter { ... }
+
+libsolv's arrays are mapped to tcl's lists:
+
+        TCL set jobs [list $job1 $job2]
+       TCL set problems [$solver solve $jobs]
+       TCL puts "We have [llength $problems] problems..."
+
+Stringification is done by calling the object's ``str'' method.
+
+       TCL puts [$dep str]
+
+There is one exception: you have to use ``stringify'' for Datamatch
+objects, as swig reports a clash with the ``str'' attribute.
+Some objects also support a ``=='' method for equality tests, and a
+``!='' method.
+
+Swig implements all constants as numeric variables, constants belonging
+to a libsolv class are prefixed with the class name:
+
+       TCL $pool set_flag $solv::Pool_POOL_FLAG_OBSOLETEUSESCOLORS  1
+       TCL puts [$solvable lookup_str $solv::SOLVABLE_SUMMARY]
+       
+
 The Solv Class
 --------------
 This is the main namespace of the library, you cannot create objects of this
@@ -532,6 +578,16 @@ needs to be accessed, the callback function is called with a repodata argument.
 You can then load the data (maybe fetching it first from an remote server).
 The callback should return true if the data has been made available.
 
+       /* bindings only */
+       $pool->appdata_disown()
+       pool.appdata_disown()
+       pool.appdata_disown()
+
+Decrement the reference count of the appdata object. This can be used to break
+circular references (e.g. if the pool's appdata value points to some meta data
+structure that contains a pool handle). If used incorrectly, this method can
+lead to application crashes, so beware. (This method is a no-op for ruby and tcl.)
+
 === DATA RETRIEVAL METHODS ===
 
 In the following functions, the _keyname_ argument describes what to retrieve.
@@ -3029,7 +3085,14 @@ These keys are constants in the *solv* class.
        chksum = solv.Chksum(type, hex)
        chksum = Solv::Chksum.new(type, hex)
 
-Create an already finalized checksum object.
+Create an already finalized checksum object from a hex string.
+
+       Chksum Chksum_from_bin(Id type, char *bin)
+       my $chksum = solv::Chksum->from_bin($type, $bin);
+       chksum = solv.Chksum.from_bin(type, bin)
+       chksum = Solv::Chksum.from_bin(type, bin)
+
+Create an already finalized checksum object from a binary checksum.
 
 === ATTRIBUTES ===
 
@@ -3047,7 +3110,7 @@ Return the type of the checksum object.
        chksum.add(str)
        chksum.add(str)
 
-Add a string to the checksum.
+Add a (binary) string to the checksum.
 
        void add_fp(FILE *fp)
        $chksum->add_fp($file);
@@ -3132,7 +3195,8 @@ stdio library.
 
 Create a file handle from the specified file descriptor. The path argument is
 only used to select the correct (de-)compression algorithm, use an empty path
-if you want to make sure to read/write raw data.
+if you want to make sure to read/write raw data. The file descriptor is dup()ed
+before the file handle is created.
 
 === METHODS ===
 
@@ -3144,6 +3208,16 @@ if you want to make sure to read/write raw data.
 Return file file descriptor of the file. If the file is not open, `-1` is
 returned.
 
+       void cloexec(bool state)
+       $file->cloexec($state)
+       file.cloexec(state)
+       file.cloexec(state)
+
+Set the close-on-exec flag of the file descriptor. The xfopen function
+returns files with close-on-exec turned on, so if you want to pass
+a file to some other process you need to call cloexec(0) before calling
+exec.
+
        int dup()
        my $fileno = $file->dup();
        fileno = file.dup()
@@ -3165,7 +3239,7 @@ always returns true.
        file.close()
        file.close()
 
-Close the file. This is needed for languages like Ruby, that do not destruct
+Close the file. This is needed for languages like Ruby that do not destruct
 objects right after they are no longer referenced. In that case, it is good
 style to close open files so that the file descriptors are freed right away.
 Returns false if there was an error.
index a079793..bf2230a 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: Libsolv-Constantids
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 03/19/2015
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "LIBSOLV\-CONSTANTIDS" "3" "03/19/2015" "libsolv" "LIBSOLV"
+.TH "LIBSOLV\-CONSTANTIDS" "3" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index c4b33fc..872cd4e 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: Libsolv-History
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/20/2013
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "LIBSOLV\-HISTORY" "3" "09/20/2013" "libsolv" "LIBSOLV"
+.TH "LIBSOLV\-HISTORY" "3" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 1d0e84d..79d7f41 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: Libsolv-Pool
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 10/09/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "LIBSOLV\-POOL" "3" "10/09/2014" "libsolv" "LIBSOLV"
+.TH "LIBSOLV\-POOL" "3" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index da81c49..24ab788 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: Libsolv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/20/2013
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "LIBSOLV" "3" "09/20/2013" "libsolv" "LIBSOLV"
+.TH "LIBSOLV" "3" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index a32118a..4400c9b 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: mdk2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/25/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "MDK2SOLV" "1" "09/25/2014" "libsolv" "LIBSOLV"
+.TH "MDK2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index e7cdd26..3ff00e8 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: mergesolv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/22/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "MERGESOLV" "1" "09/22/2014" "libsolv" "LIBSOLV"
+.TH "MERGESOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index d00fa02..e14b974 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: repomdxml2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/24/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "REPOMDXML2SOLV" "1" "09/24/2014" "libsolv" "LIBSOLV"
+.TH "REPOMDXML2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 5057b57..2b4b931 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: rpmdb2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/25/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "RPMDB2SOLV" "1" "09/25/2014" "libsolv" "LIBSOLV"
+.TH "RPMDB2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 249d801..2d8e245 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: rpmmd2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/24/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "RPMMD2SOLV" "1" "09/24/2014" "libsolv" "LIBSOLV"
+.TH "RPMMD2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index c6a2725..e67c6ef 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: rpms2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/24/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "RPMS2SOLV" "1" "09/24/2014" "libsolv" "LIBSOLV"
+.TH "RPMS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 9ec9510..9b07094 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: susetags2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/24/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "SUSETAGS2SOLV" "1" "09/24/2014" "libsolv" "LIBSOLV"
+.TH "SUSETAGS2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index faf97b6..0e71874 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: testsolv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/22/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "TESTSOLV" "1" "09/22/2014" "libsolv" "LIBSOLV"
+.TH "TESTSOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 03c5cc7..20ab3ad 100644 (file)
@@ -2,12 +2,12 @@
 .\"     Title: updateinfoxml2solv
 .\"    Author: [see the "Author" section]
 .\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\"      Date: 09/24/2014
+.\"      Date: 08/26/2015
 .\"    Manual: LIBSOLV
 .\"    Source: libsolv
 .\"  Language: English
 .\"
-.TH "UPDATEINFOXML2SOLV" "1" "09/24/2014" "libsolv" "LIBSOLV"
+.TH "UPDATEINFOXML2SOLV" "1" "08/26/2015" "libsolv" "LIBSOLV"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
index 330ec63..0a1dbb0 100755 (executable)
@@ -42,9 +42,7 @@ sub calc_cookie_ext {
   $chksum->add("1.1");
   $chksum->add($cookie);
   $chksum->add_fstat(fileno($f));
-  my $extcookie = $chksum->raw();
-  substr($extcookie, 0, 1) = chr(1) if ord(substr($extcookie, 0, 1)) == 0;
-  return $extcookie;
+  return $chksum->raw();
 }
 
 sub cachepath {
@@ -67,6 +65,7 @@ sub load {
     $dorefresh = 0 if @s && ($self->{metadata_expire} == -1 || time() - $s[9] < $self->{metadata_expire});
   }
   $self->{cookie} = '';
+  $self->{extcookie} = '';
   if (!$dorefresh && $self->usecachedrepo()) {
     print "repo: '$self->{alias}' cached\n";
     return 1;
@@ -88,7 +87,7 @@ sub download {
   $url =~ s!/$!!;
   $url .= "/$file";
   open(my $f, '+>', undef) || die;
-  fcntl($f, Fcntl::F_SETFD, 0);
+  fcntl($f, Fcntl::F_SETFD, 0);                # turn off CLOEXEC
   my $st = system('curl', '-f', '-s', '-L', '-o', "/dev/fd/" . fileno($f), '--', $url);
   if (POSIX::lseek(fileno($f), 0, POSIX::SEEK_END) == 0 && ($st == 0 || !$chksum)) {
     return undef;
@@ -145,7 +144,7 @@ sub usecachedrepo {
 }
 
 sub writecachedrepo {
-  my ($self, $ext, $info) = @_;
+  my ($self, $ext, $repodata) = @_;
   return if $self->{incomplete};
   mkdir("/var/cache/solv", 0755) unless -d "/var/cache/solv";
   my ($f, $tmpname);
@@ -155,10 +154,10 @@ sub writecachedrepo {
   return unless $f;
   chmod 0444, $f;
   my $ff = solv::xfopen_fd(undef, fileno($f));
-  if (!$info) {
+  if (!$repodata) {
     $self->{handle}->write($ff);
   } elsif ($ext) {
-    $info->write($ff);
+    $repodata->write($ff);
   } else {
      $self->{handle}->write_first_repodata($ff);
   }
@@ -174,12 +173,12 @@ sub writecachedrepo {
     if ($f) {
       if (!$ext) {
        $self->{handle}->empty();
-       die("internal error, cannot reload solv file\n") unless $self->{handle}->add_solv($f, $solv::Repo::SOLV_ADD_NO_STUBS);
+       die("internal error, cannot reload solv file\n") unless $self->{handle}->add_solv($f, $repodata ? 0 : $solv::Repo::SOLV_ADD_NO_STUBS);
       } else {
-       $info->extend_to_repo();
+       $repodata->extend_to_repo();
        my $flags = $solv::Repo::REPO_EXTEND_SOLVABLES;
        $flags |= $solv::Repo::REPO_LOCALPOOL if $ext ne 'DL';
-       $info->add_solv($f, $flags);
+       $repodata->add_solv($f, $flags);
       }
     }
   }
@@ -191,6 +190,34 @@ sub packagespath {
   return '';
 }
 
+my %langtags = (
+  $solv::SOLVABLE_SUMMARY     => $solv::REPOKEY_TYPE_STR,
+  $solv::SOLVABLE_DESCRIPTION => $solv::REPOKEY_TYPE_STR,
+  $solv::SOLVABLE_EULA        => $solv::REPOKEY_TYPE_STR,
+  $solv::SOLVABLE_MESSAGEINS  => $solv::REPOKEY_TYPE_STR,
+  $solv::SOLVABLE_MESSAGEDEL  => $solv::REPOKEY_TYPE_STR,
+  $solv::SOLVABLE_CATEGORY    => $solv::REPOKEY_TYPE_ID,
+);
+
+sub add_ext_keys {
+  my ($self, $ext, $repodata, $handle) = @_;
+  if ($ext eq 'DL') {
+    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOSITORY_DELTAINFO);
+    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_FLEXARRAY);
+  } elsif ($ext eq 'DU') {
+    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::SOLVABLE_DISKUSAGE);
+    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_DIRNUMNUMARRAY);
+  } elsif ($ext eq 'FL') {
+    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::SOLVABLE_FILELIST);
+    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_DIRSTRARRAY);
+  } else {
+    for my $langid (sort { $a <=> $b } keys %langtags) {
+      $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $self->{handle}->{pool}->id2langid($langid, $ext, 1));
+      $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $langtags{$langid});
+    }
+  }
+}
+
 package Repo::rpmmd;
 
 our @ISA = ('Repo::generic');
@@ -222,13 +249,7 @@ sub add_ext {
   $repodata->set_poolstr($handle, $solv::REPOSITORY_REPOMD_TYPE, $what);
   $repodata->set_str($handle, $solv::REPOSITORY_REPOMD_LOCATION, $filename);
   $repodata->set_checksum($handle, $solv::REPOSITORY_REPOMD_CHECKSUM, $chksum);
-  if ($ext eq 'DL') {
-    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOSITORY_DELTAINFO);
-    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_FLEXARRAY);
-  } elsif ($ext eq 'FL') {
-    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::SOLVABLE_FILELIST);
-    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_DIRSTRARRAY);
-  }
+  $self->add_ext_keys($ext, $repodata, $handle);
   $repodata->add_flexarray($solv::SOLVID_META, $solv::REPOSITORY_EXTERNAL, $handle);
 }
 
@@ -328,36 +349,15 @@ sub find {
   return (undef, undef);
 }
 
-my %langtags = (
-  $solv::SOLVABLE_SUMMARY     => $solv::REPOKEY_TYPE_STR,
-  $solv::SOLVABLE_DESCRIPTION => $solv::REPOKEY_TYPE_STR,
-  $solv::SOLVABLE_EULA        => $solv::REPOKEY_TYPE_STR,
-  $solv::SOLVABLE_MESSAGEINS  => $solv::REPOKEY_TYPE_STR,
-  $solv::SOLVABLE_MESSAGEDEL  => $solv::REPOKEY_TYPE_STR,
-  $solv::SOLVABLE_CATEGORY    => $solv::REPOKEY_TYPE_ID,
-);
 
 sub add_ext {
   my ($self, $repodata, $what, $ext) = @_;
   my ($filename, $chksum) = $self->find($what);
+  return unless $filename;
   my $handle = $repodata->new_handle();
   $repodata->set_str($handle, $solv::SUSETAGS_FILE_NAME, $filename);
   $repodata->set_checksum($handle, $solv::SUSETAGS_FILE_CHECKSUM, $chksum);
-  if ($ext eq 'DL') {
-    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOSITORY_DELTAINFO);
-    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_FLEXARRAY);
-  } elsif ($ext eq 'DU') {
-    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::SOLVABLE_DISKUSAGE);
-    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_DIRNUMNUMARRAY);
-  } elsif ($ext eq 'FL') {
-    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::SOLVABLE_FILELIST);
-    $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $solv::REPOKEY_TYPE_DIRSTRARRAY);
-  } else {
-    for my $langid (sort { $a <=> $b } keys %langtags) {
-      $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $self->{handle}->{pool}->id2langid($langid, $ext, 1));
-      $repodata->add_idarray($handle, $solv::REPOSITORY_KEYS, $langtags{$langid});
-    }
-  }
+  $self->add_ext_keys($ext, $repodata, $handle);
   $repodata->add_flexarray($solv::SOLVID_META, $solv::REPOSITORY_EXTERNAL, $handle);
 }
 
@@ -710,7 +710,7 @@ my %newpkgsfps;
 if (@newpkgs) {
   my $downloadsize = 0;
   $downloadsize += $_->lookup_num($solv::SOLVABLE_DOWNLOADSIZE) for @newpkgs;
-  printf "Downloading %d packages, %d K\n", scalar(@newpkgs), $downloadsize;
+  printf "Downloading %d packages, %d K\n", scalar(@newpkgs), $downloadsize / 1024;
   for my $p (@newpkgs) {
     my $repo = $p->{repo}->{appdata};
     my ($location) = $p->lookup_location();
@@ -740,6 +740,7 @@ for my $p ($trans->steps()) {
     print "install ".$p->str()."\n";
     my $f = $newpkgsfps{$p->{id}};
     my $mode = $steptype == $solv::Transaction::SOLVER_TRANSACTION_INSTALL ? '-U' : '-i';
+    $f->cloexec(0);
     system('rpm', $mode, '--force', '--nodeps', '--nodigest', '--nosignature', "/dev/fd/".$f->fileno()) && die("rpm failed: $?\n");
     delete $newpkgsfps{$p->{id}};
   }
index 22ae209..3d6ca07 100755 (executable)
@@ -68,16 +68,7 @@ class repo_generic(dict):
         chksum.add("1.1");
         chksum.add(cookie)
         chksum.add_fstat(f.fileno())
-        extcookie = chksum.raw()
-        if sys.version > '3':
-            # compatibility to c code
-            if extcookie[0] == 0:
-                extcookie[0] = 1
-        else:
-            # compatibility to c code
-            if ord(extcookie[0]) == 0:
-                extcookie[0] = chr(1)
-        return extcookie
+        return chksum.raw()
 
     def cachepath(self, ext = None):
         path = re.sub(r'^\.', '_', self.name)
@@ -100,6 +91,7 @@ class repo_generic(dict):
             except OSError:
                 pass
         self['cookie'] = ''
+        self['extcookie'] = ''
         if not dorefresh and self.usecachedrepo(None):
             print("repo: '%s': cached" % self.name)
             return True
@@ -237,7 +229,7 @@ class repo_generic(dict):
             return False
         return True
 
-    def writecachedrepo(self, ext, info=None):
+    def writecachedrepo(self, ext, repodata=None):
         if 'incomplete' in self:
             return
         tmpname = None
@@ -248,21 +240,21 @@ class repo_generic(dict):
             os.fchmod(fd, 0o444)
             f = os.fdopen(fd, 'wb+')
             f = solv.xfopen_fd(None, f.fileno())
-            if not info:
+            if not repodata:
                 self.handle.write(f)
             elif ext:
-                info.write(f)
-            else:       # rewrite_repos case
+                repodata.write(f)
+            else:       # rewrite_repos case, do not write stubs
                 self.handle.write_first_repodata(f)
             f.flush()
             if self.type != 'system' and not ext:
-                if 'extcookie' not in self:
+                if not self['extcookie']:
                     self['extcookie'] = self.calc_cookie_ext(f, self['cookie'])
-                os.write(f.fileno(), self['extcookie'])
+                f.write(self['extcookie'])
             if not ext:
-                os.write(f.fileno(), self['cookie'])
+                f.write(self['cookie'])
             else:
-                os.write(f.fileno(), self['extcookie'])
+                f.write(self['extcookie'])
             f.close
             if self.handle.iscontiguous():
                 # switch to saved repo to activate paging and save memory
@@ -270,17 +262,20 @@ class repo_generic(dict):
                 if not ext:
                     # main repo
                     self.handle.empty()
-                    if not self.handle.add_solv(nf, solv.Repo.SOLV_ADD_NO_STUBS):
+                    flags = solv.Repo.SOLV_ADD_NO_STUBS
+                    if repodata:
+                        flags = 0       # rewrite repos case, recreate stubs
+                    if not self.handle.add_solv(nf, flags):
                         sys.exit("internal error, cannot reload solv file")
                 else:
                     # extension repodata
                     # need to extend to repo boundaries, as this is how
-                    # info.write() has written the data
-                    info.extend_to_repo()
+                    # repodata.write() has written the data
+                    repodata.extend_to_repo()
                     flags = solv.Repo.REPO_EXTEND_SOLVABLES
                     if ext != 'DL':
                         flags |= solv.Repo.REPO_LOCALPOOL
-                    info.add_solv(nf, flags)
+                    repodata.add_solv(nf, flags)
             os.rename(tmpname, self.cachepath(ext))
         except (OSError, IOError):
             if tmpname:
@@ -307,6 +302,29 @@ class repo_generic(dict):
     def packagespath(self):
         return ''
 
+    def add_ext_keys(self, ext, repodata, handle):
+        if ext == 'DL':
+            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOSITORY_DELTAINFO)
+            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_FLEXARRAY)
+        elif ext == 'DU':
+            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_DISKUSAGE)
+            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRNUMNUMARRAY)
+        elif ext == 'FL':
+            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_FILELIST)
+            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRSTRARRAY)
+        else:
+            for langtag, langtagtype in [
+                (solv.SOLVABLE_SUMMARY, solv.REPOKEY_TYPE_STR),
+                (solv.SOLVABLE_DESCRIPTION, solv.REPOKEY_TYPE_STR),
+                (solv.SOLVABLE_EULA, solv.REPOKEY_TYPE_STR),
+                (solv.SOLVABLE_MESSAGEINS, solv.REPOKEY_TYPE_STR),
+                (solv.SOLVABLE_MESSAGEDEL, solv.REPOKEY_TYPE_STR),
+                (solv.SOLVABLE_CATEGORY, solv.REPOKEY_TYPE_ID)
+            ]:
+                repodata.add_idarray(handle, solv.REPOSITORY_KEYS, self.handle.pool.id2langid(langtag, ext, 1))
+                repodata.add_idarray(handle, solv.REPOSITORY_KEYS, langtagtype)
+        
+
 class repo_repomd(repo_generic):
     def load(self, pool):
         if super(repo_repomd, self).load(pool):
@@ -368,12 +386,7 @@ class repo_repomd(repo_generic):
         repodata.set_poolstr(handle, solv.REPOSITORY_REPOMD_TYPE, what)
         repodata.set_str(handle, solv.REPOSITORY_REPOMD_LOCATION, filename)
         repodata.set_checksum(handle, solv.REPOSITORY_REPOMD_CHECKSUM, chksum)
-        if ext == 'DL':
-            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOSITORY_DELTAINFO)
-            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_FLEXARRAY)
-        elif ext == 'FL':
-            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_FILELIST)
-            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRSTRARRAY)
+        self.add_ext_keys(ext, repodata, handle)
         repodata.add_flexarray(solv.SOLVID_META, solv.REPOSITORY_EXTERNAL, handle)
 
     def add_exts(self):
@@ -469,23 +482,7 @@ class repo_susetags(repo_generic):
         repodata.set_str(handle, solv.SUSETAGS_FILE_NAME, filename)
         if chksum:
             repodata.set_checksum(handle, solv.SUSETAGS_FILE_CHECKSUM, chksum)
-        if ext == 'DU':
-            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_DISKUSAGE)
-            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRNUMNUMARRAY)
-        elif ext == 'FL':
-            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.SOLVABLE_FILELIST)
-            repodata.add_idarray(handle, solv.REPOSITORY_KEYS, solv.REPOKEY_TYPE_DIRSTRARRAY)
-        else:
-            for langtag, langtagtype in [
-                (solv.SOLVABLE_SUMMARY, solv.REPOKEY_TYPE_STR),
-                (solv.SOLVABLE_DESCRIPTION, solv.REPOKEY_TYPE_STR),
-                (solv.SOLVABLE_EULA, solv.REPOKEY_TYPE_STR),
-                (solv.SOLVABLE_MESSAGEINS, solv.REPOKEY_TYPE_STR),
-                (solv.SOLVABLE_MESSAGEDEL, solv.REPOKEY_TYPE_STR),
-                (solv.SOLVABLE_CATEGORY, solv.REPOKEY_TYPE_ID)
-            ]:
-                repodata.add_idarray(handle, solv.REPOSITORY_KEYS, self.handle.pool.id2langid(langtag, ext, 1))
-                repodata.add_idarray(handle, solv.REPOSITORY_KEYS, langtagtype)
+        self.add_ext_keys(ext, repodata, handle)
         repodata.add_flexarray(solv.SOLVID_META, solv.REPOSITORY_EXTERNAL, handle)
         
     def add_exts(self):
@@ -852,7 +849,7 @@ if newpkgs:
     downloadsize = 0
     for p in newpkgs:
         downloadsize += p.lookup_num(solv.SOLVABLE_DOWNLOADSIZE)
-    print("Downloading %d packages, %d K" % (len(newpkgs), downloadsize))
+    print("Downloading %d packages, %d K" % (len(newpkgs), downloadsize / 1024))
     for p in newpkgs:
         repo = p.repo.appdata
         location, medianr = p.lookup_location()
@@ -866,7 +863,7 @@ if newpkgs:
             continue
         if not sysrepo.handle.isempty() and os.access('/usr/bin/applydeltarpm', os.X_OK):
             pname = p.name
-            di = p.repo.Dataiterator(solv.SOLVID_META, solv.DELTA_PACKAGE_NAME, pname, solv.Dataiterator.SEARCH_STRING)
+            di = p.repo.Dataiterator_meta(solv.DELTA_PACKAGE_NAME, pname, solv.Dataiterator.SEARCH_STRING)
             di.prepend_keyname(solv.REPOSITORY_DELTAINFO)
             for d in di:
                 dp = d.parentpos()
@@ -893,6 +890,7 @@ if newpkgs:
                     continue
                 nf = tempfile.TemporaryFile()
                 nf = os.dup(nf.fileno())   # get rid of CLOEXEC
+                f.cloexec(0)
                 st = subprocess.call(['/usr/bin/applydeltarpm', '-a', p.arch, "/dev/fd/%d" % f.fileno(), "/dev/fd/%d" % nf])
                 if st:
                     os.close(nf)
index 470438d..be633f3 100755 (executable)
@@ -44,9 +44,7 @@ class Repo_generic
     chksum.add("1.1")
     chksum.add(cookie)
     chksum.add_fstat(f.fileno)
-    extcookie = chksum.raw()
-    extcookie[0] = 1 if extcookie[0] == 0
-    return extcookie
+    return chksum.raw()
   end
 
   def cachepath(ext = nil)
@@ -68,6 +66,7 @@ class Repo_generic
       end
     end
     @cookie = nil
+    @extcookie = nil
     if !dorefresh && usecachedrepo(nil)
       puts "repo: '#{@name}' cached"
       return true
@@ -152,17 +151,17 @@ class Repo_generic
     return true
   end
 
-  def writecachedrepo(ext, info = nil)
+  def writecachedrepo(ext, repodata = nil)
     return if @incomplete
     begin
       Dir::mkdir("/var/cache/solv", 0755) unless FileTest.directory?("/var/cache/solv")
       f =  Tempfile.new('.newsolv-', '/var/cache/solv')
       f.chmod(0444)
       sf = Solv::xfopen_fd('', f.fileno)
-      if !info
+      if !repodata
        @handle.write(sf)
       elsif ext
-       info.write(sf)
+       repodata.write(sf)
       else
        @handle.write_first_repodata(sf)
       end
@@ -179,12 +178,12 @@ class Repo_generic
        if sf
          if !ext
            @handle.empty()
-           abort("internal error, cannot reload solv file") unless @handle.add_solv(sf, Solv::Repo::SOLV_ADD_NO_STUBS)
+           abort("internal error, cannot reload solv file") unless @handle.add_solv(sf, repodata ? 0 : Solv::Repo::SOLV_ADD_NO_STUBS)
          else
-           info.extend_to_repo()
+           repodata.extend_to_repo()
            flags = Solv::Repo::REPO_EXTEND_SOLVABLES
            flags |= Solv::Repo::REPO_LOCALPOOL if ext != 'DL'
-           info.add_solv(sf, flags)
+           repodata.add_solv(sf, flags)
          end
          sf.close
        end
@@ -214,6 +213,33 @@ class Repo_generic
   def packagespath()
     return ''
   end
+
+  @@langtags = {
+    Solv::SOLVABLE_SUMMARY     => Solv::REPOKEY_TYPE_STR,
+    Solv::SOLVABLE_DESCRIPTION => Solv::REPOKEY_TYPE_STR,
+    Solv::SOLVABLE_EULA        => Solv::REPOKEY_TYPE_STR,
+    Solv::SOLVABLE_MESSAGEINS  => Solv::REPOKEY_TYPE_STR,
+    Solv::SOLVABLE_MESSAGEDEL  => Solv::REPOKEY_TYPE_STR,
+    Solv::SOLVABLE_CATEGORY    => Solv::REPOKEY_TYPE_ID,
+  }
+
+  def add_ext_keys(ext, repodata, h)
+    if ext == 'DL'
+      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOSITORY_DELTAINFO)
+      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_FLEXARRAY)
+    elsif ext == 'DU'
+      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_DISKUSAGE)
+      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRNUMNUMARRAY)
+    elsif ext == 'FL'
+      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_FILELIST)
+      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRSTRARRAY)
+    else
+      @@langtags.sort.each do |langid, langtype|
+       repodata.add_idarray(h, Solv::REPOSITORY_KEYS, @handle.pool.id2langid(langid, ext, true))
+       repodata.add_idarray(h, Solv::REPOSITORY_KEYS, langtype)
+      end
+    end
+  end
 end
 
 class Repo_rpmmd < Repo_generic
@@ -285,13 +311,7 @@ class Repo_rpmmd < Repo_generic
     repodata.set_poolstr(h, Solv::REPOSITORY_REPOMD_TYPE, what)
     repodata.set_str(h, Solv::REPOSITORY_REPOMD_LOCATION, filename)
     repodata.set_checksum(h, Solv::REPOSITORY_REPOMD_CHECKSUM, filechksum)
-    if ext == 'DL'
-      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOSITORY_DELTAINFO)
-      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_FLEXARRAY)
-    elsif ext == 'FL'
-      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_FILELIST)
-      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRSTRARRAY)
-    end
+    add_ext_keys(ext, repodata, h)
     repodata.add_flexarray(Solv::SOLVID_META, Solv::REPOSITORY_EXTERNAL, h)
   end
 
@@ -394,35 +414,12 @@ class Repo_susetags < Repo_generic
     return true
   end
 
-  @@langtags = {
-    Solv::SOLVABLE_SUMMARY     => Solv::REPOKEY_TYPE_STR,
-    Solv::SOLVABLE_DESCRIPTION => Solv::REPOKEY_TYPE_STR,
-    Solv::SOLVABLE_EULA        => Solv::REPOKEY_TYPE_STR,
-    Solv::SOLVABLE_MESSAGEINS  => Solv::REPOKEY_TYPE_STR,
-    Solv::SOLVABLE_MESSAGEDEL  => Solv::REPOKEY_TYPE_STR,
-    Solv::SOLVABLE_CATEGORY    => Solv::REPOKEY_TYPE_ID,
-  }
-
   def add_ext(repodata, what, ext)
     (filename, filechksum) = find(what)
     h = repodata.new_handle()
     repodata.set_str(h, Solv::SUSETAGS_FILE_NAME, filename)
     repodata.set_checksum(h, Solv::SUSETAGS_FILE_CHECKSUM, filechksum)
-    if ext == 'DL'
-      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOSITORY_DELTAINFO)
-      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_FLEXARRAY)
-    elsif ext == 'DU'
-      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_DISKUSAGE)
-      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRNUMNUMARRAY)
-    elsif ext == 'FL'
-      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::SOLVABLE_FILELIST)
-      repodata.add_idarray(h, Solv::REPOSITORY_KEYS, Solv::REPOKEY_TYPE_DIRSTRARRAY)
-    else
-      @@langtags.sort.each do |langid, langtype|
-       repodata.add_idarray(h, Solv::REPOSITORY_KEYS, @handle.pool.id2langid(langid, ext, true))
-       repodata.add_idarray(h, Solv::REPOSITORY_KEYS, langtype)
-      end
-    end
+    add_ext_keys(ext, repodata, h)
     repodata.add_flexarray(Solv::SOLVID_META, Solv::REPOSITORY_EXTERNAL, h)
   end
 
@@ -494,7 +491,7 @@ class Repo_system < Repo_generic
     end
     f = Solv::xfopen(cachepath())
     @handle.add_rpmdb_reffp(f, Solv::Repo::REPO_REUSE_REPODATA)
-    f.close
+    f.close if f
     writecachedrepo(nil)
     return true
   end
@@ -727,7 +724,7 @@ if !newpkgs.empty?
   for p in newpkgs
     downloadsize += p.lookup_num(Solv::SOLVABLE_DOWNLOADSIZE)
   end
-  puts "Downloading #{newpkgs.length} packages, #{downloadsize} K"
+  puts "Downloading #{newpkgs.length} packages, #{downloadsize / 1024} K"
   for p in newpkgs
     repo = p.repo.appdata
     location, medianr = p.lookup_location()
@@ -758,6 +755,7 @@ for p in trans.steps
     f = newpkgsfp.delete(p.id)
     next unless f
     mode = steptype == Solv::Transaction::SOLVER_TRANSACTION_INSTALL ? '-U' : '-i'
+    f.cloexec(0)
     system('rpm', mode, '--force', '--nodeps', '--nodigest', '--nosignature', "/dev/fd/#{f.fileno().to_s}") || abort("rpm failed: #{$? >> 8}")
     f.close
   end
index 8b0d6cc..fc420b3 100644 (file)
@@ -124,8 +124,8 @@ struct repoinfo {
   int metadata_expire;
   char **components;
   int ncomponents;
-
   unsigned char cookie[32];
+  int extcookieset;
   unsigned char extcookie[32];
   int incomplete;
 };
@@ -1133,7 +1133,7 @@ usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie, int mark)
       fclose(fp);
       return 0;
     }
-  if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)))
+  if (cookie && memcmp(cookie, mycookie, sizeof(mycookie)) != 0)
     {
       fclose(fp);
       return 0;
@@ -1165,6 +1165,7 @@ usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie, int mark)
     {
       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 */
@@ -1173,7 +1174,7 @@ usecachedrepo(Repo *repo, const char *repoext, unsigned char *cookie, int mark)
 }
 
 void
-writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *cookie)
+writecachedrepo(Repo *repo, Repodata *repodata, const char *repoext, unsigned char *cookie)
 {
   FILE *fp;
   int i, fd;
@@ -1211,22 +1212,22 @@ writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *
   if (i < repo->end)
     onepiece = 0;
 
-  if (!info)
+  if (!repodata)
     repo_write(repo, fp);
   else if (repoext)
-    repodata_write(info, fp);
+    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;
+      onepiece = 0;    /* don't bother for the added file provides */
     }
 
   if (!repoext && cinfo)
     {
-      if (!cinfo->extcookie[0])
+      if (!cinfo->extcookieset)
        {
          /* create the ext cookie and append it */
          /* we just need some unique ID */
@@ -1234,8 +1235,7 @@ writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *
          if (!fstat(fileno(fp), &stb))
            memset(&stb, 0, sizeof(stb));
          calc_checksum_stat(&stb, REPOKEY_TYPE_SHA256, cookie, cinfo->extcookie);
-         if (cinfo->extcookie[0] == 0)
-           cinfo->extcookie[0] = 1;
+         cinfo->extcookieset = 1;
        }
       if (fwrite(cinfo->extcookie, 32, 1, fp) != 1)
        {
@@ -1281,12 +1281,12 @@ writecachedrepo(Repo *repo, Repodata *info, const char *repoext, unsigned char *
              int flags = REPO_USE_LOADING|REPO_EXTEND_SOLVABLES;
              /* make sure repodata contains complete repo */
              /* (this is how repodata_write saves it) */
-             repodata_extend_block(info, repo->start, repo->end - repo->start);
-             info->state = REPODATA_LOADING;
+             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);
-             info->state = REPODATA_AVAILABLE; /* in case the load failed */
+             repodata->state = REPODATA_AVAILABLE;     /* in case the load failed */
            }
          fclose(fp);
        }
@@ -1400,7 +1400,8 @@ repomd_load_ext(Repo *repo, Repodata *data)
       printf("%s\n", pool_errstr(repo->pool));
       return 0;
     }
-  writecachedrepo(repo, data, ext, cinfo->extcookie);
+  if (cinfo->extcookieset)
+    writecachedrepo(repo, data, ext, cinfo->extcookie);
   return 1;
 }
 
@@ -1546,7 +1547,8 @@ susetags_load_ext(Repo *repo, Repodata *data)
       return 0;
     }
   fclose(fp);
-  writecachedrepo(repo, data, ext, cinfo->extcookie);
+  if (cinfo->extcookieset)
+    writecachedrepo(repo, data, ext, cinfo->extcookie);
   return 1;
 }
 #endif
diff --git a/examples/tclsolv b/examples/tclsolv
new file mode 100755 (executable)
index 0000000..6f819aa
--- /dev/null
@@ -0,0 +1,803 @@
+#!/usr/bin/tclsh
+
+package require solv
+package require inifile
+package require fileutil
+
+set reposdir /etc/zypp/repos.d
+
+### some helpers
+
+proc fileno {file} {
+  if [regexp -- {^file(\d+)$} $file match fd] {
+    return $fd
+  }
+  error "file not open"
+}
+
+set ::globalarray_cnt 0
+
+proc globalarray {} {
+  set name "::globalarray_[incr ::globalarray_cnt]"
+  array set $name [list varName $name]
+  return $name
+}
+
+### generic repo handling (cache reading/writing)
+
+proc repo_calc_cookie_file {selfName filename} {
+  upvar $selfName self
+  set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
+  $chksum add "1.1"
+  $chksum add_stat $filename
+  return [$chksum raw]
+}
+
+proc repo_calc_cookie_fp {selfName fp} {
+  upvar $selfName self
+  set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
+  $chksum add "1.1"
+  $chksum add_fp $fp
+  return [$chksum raw]
+}
+
+proc repo_calc_cookie_ext {selfName f cookie} {
+  upvar $selfName self
+  set chksum [solv::new_Chksum $solv::REPOKEY_TYPE_SHA256]
+  $chksum add "1.1"
+  $chksum add $cookie
+  $chksum add_fstat [$f fileno]
+  return [$chksum raw]
+}
+
+proc repo_cachepath {selfName {ext "-"}} {
+  upvar $selfName self
+  regsub {^\.} $self(name) _ path
+  if {$ext ne "-"} {
+    set path "${path}_$ext.solvx"
+  } else {
+    set path "${path}.solv"
+  }
+  regsub -all / $path _ path
+  return "/var/cache/solv/$path"
+}
+
+proc repo_generic_load {selfName pool} {
+  upvar $selfName self
+  set handle [ $pool add_repo $self(name) ]
+  set self(handle) $handle
+  $handle configure -priority [expr 99 - $self(priority)] -appdata $self(varName)
+  set dorefresh $self(autorefresh)
+  set metadata_expire $self(metadata_expire)
+  catch {
+    if {$metadata_expire == -1 || [clock seconds] - [file mtime [repo_cachepath self]] < $metadata_expire} {
+      set dorefresh 0
+    }
+  }
+  set self(cookie) {}
+  set self(extcookie) {}
+  if { !$dorefresh && [repo_usecachedrepo self] } {
+    puts "repo $self(name): cached"
+    return 1 
+  }
+  return 0 
+}
+
+proc repo_free_handle {selfName} {
+  upvar $selfName self
+  set handle $self(handle)
+  unset self(handle)
+  $handle free 1
+}
+
+proc repo_usecachedrepo {selfName {ext "-"} {mark 0}} {
+  upvar $selfName self
+  set repopath [repo_cachepath self $ext]
+  set code [catch {
+    set f [open $repopath "rb"]
+    seek $f -32 end
+    set fcookie [read $f 32]
+    set cookie [expr {$ext eq "-" ? $self(cookie) : $self(extcookie)}]
+    if {$cookie ne {} && $cookie ne $fcookie} {
+      close $f
+      return 0
+    }
+    set fextcookie {}
+    if {$ext eq "-" && $self(type) ne "system"} {
+      seek $f -64 end
+      set fextcookie [read $f 32]
+    }
+    seek $f 0 start
+    set ff [solv::xfopen_fd {} [fileno $f]]
+    close $f
+    set flags 0
+    if {$ext ne "-"} {
+      set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
+      if {$ext ne "DL"} {
+       set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
+      }
+    }
+    if {! [$self(handle) add_solv $ff $flags]} {
+      $ff close
+      return 0
+    }
+    $ff close
+    if {$self(type) ne "system" && $ext eq "-"} {
+      set self(cookie) $fcookie
+      set self(extcookie) $fextcookie
+    }
+    if {$mark} {
+      catch {
+        ::fileutil::touch -c -m -t [clock seconds] $repopath
+      }
+    }
+    return 1
+  } res]
+  return [expr {$code == 2 ? $res : 0}]
+}
+
+proc repo_writecachedrepo {selfName {ext "-"} {repodata "NULL"}} {
+  upvar $selfName self
+  if [info exists self(incomplete)] {
+    return
+  }
+  file mkdir "/var/cache/solv"
+  ::fileutil::tempdir "/var/cache/solv"
+  set tempfilename [::fileutil::tempfile ".newsolv-"]
+  ::fileutil::tempdirReset
+  set f [solv::xfopen $tempfilename "w+"]
+  file attributes $tempfilename -permissions 0444
+  if {$repodata eq {NULL}} {
+    $self(handle) write $f
+  } else {
+    $repodata write $f
+  }
+  $f flush
+  if {$self(type) ne "system" && $ext eq "-"} {
+    if {$self(extcookie) eq {}} {
+      set self(extcookie) [repo_calc_cookie_ext self $f $self(cookie)]
+    }
+    $f write $self(extcookie)
+  }
+  $f write [expr {$ext eq "-" ? $self(cookie) : $self(extcookie)}]
+  $f close
+  file rename -force -- $tempfilename [repo_cachepath self $ext]
+}
+
+proc repo_download {selfName file uncompress chksum {markincomplete 0}} {
+  upvar $selfName self
+  regsub {/$} $self(baseurl) {} url
+  set url "$url/$file"
+  set tempfilename [::fileutil::tempfile]
+  set f [open $tempfilename rb+]
+  file delete -- $tempfilename
+  if [catch {
+    exec -ignorestderr -- curl -f -s -L $url ">@$f"
+  }] {
+    seek $f 0 end
+    if {($chksum ne "" && $chksum ne "NULL") || [tell $f] != 0} {
+      puts "$file: download error"
+    }
+    close $f
+    return {NULL}
+  }
+  seek $f 0 start
+  if {$chksum ne "" && $chksum ne "NULL"} {
+    set fchksum [solv::new_Chksum [$chksum cget -type]]
+    if {$fchksum eq "" || $fchksum eq "NULL"} {
+      puts "$file: unknown checksum type"
+      if {$markincomplete} {
+       set self(incomplete) 1
+      }
+      close $f
+      return {NULL}
+    }
+    $fchksum add_fd [fileno $f]
+    if {[$fchksum != $chksum]} {
+      puts "$file: checksum mismatch"
+      if {$markincomplete} {
+       set self(incomplete) 1
+      }
+      close $f
+      return {NULL}
+    }
+  }
+  set ff [solv::xfopen_fd [expr {$uncompress ? $file : ""}] [fileno $f]]
+  close $f
+  return $ff
+}
+
+proc repo_generic_add_ext_keys {selfName ext repodata h} {
+  upvar $selfName self
+  if {$ext eq "DL"} {
+    $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOSITORY_DELTAINFO
+    $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_FLEXARRAY
+  } elseif {$ext eq "DU"} {
+    $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::SOLVABLE_DISKUSAGE
+    $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_DIRNUMNUMARRAY
+  } elseif {$ext eq "FL"} {
+    $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::SOLVABLE_FILELIST
+    $repodata add_idarray $h $solv::REPOSITORY_KEYS $solv::REPOKEY_TYPE_DIRSTRARRAY
+  }
+}
+
+### system
+
+proc repo_system_load {selfName pool} {
+  upvar $selfName self
+  set handle [ $pool add_repo $self(name) ]
+  set self(handle) $handle
+  $handle configure -appdata $self(varName)
+  $pool configure -installed $handle
+  puts -nonewline "rpm database: "
+  set self(cookie) [repo_calc_cookie_file self "/var/lib/rpm/Packages"]
+  if [repo_usecachedrepo self] {
+    puts "cached"
+    return 1
+  }
+  puts "reading"
+  set f [solv::xfopen [repo_cachepath self]]
+  $handle add_rpmdb_reffp $f $solv::Repo_REPO_REUSE_REPODATA
+  repo_writecachedrepo self
+}
+
+### repomd
+
+proc repo_repomd_add_ext {selfName repodata what ext} {
+  upvar $selfName self
+  set where [repo_repomd_find self $what]
+  if {$where eq {}} {
+    return
+  }
+  set h [$repodata new_handle]
+  $repodata set_poolstr $h $solv::REPOSITORY_REPOMD_TYPE $what
+  $repodata set_str $h $solv::REPOSITORY_REPOMD_LOCATION [lindex $where 0]
+  $repodata set_checksum $h $solv::REPOSITORY_REPOMD_CHECKSUM [lindex $where 1]
+  repo_generic_add_ext_keys self $ext $repodata $h
+  $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
+}
+
+proc repo_repomd_add_exts {selfName} {
+  upvar $selfName self
+  set repodata [$self(handle) add_repodata 0]
+  repo_repomd_add_ext self $repodata "filelists" "FL"
+  $repodata internalize
+}
+
+proc repo_repomd_find {selfName what} {
+  upvar $selfName self
+  set di [$self(handle) Dataiterator_meta $solv::REPOSITORY_REPOMD_TYPE $what $solv::Dataiterator_SEARCH_STRING]
+  $di prepend_keyname $solv::REPOSITORY_REPOMD
+  solv::iter d $di {
+    set dp [$d parentpos]
+    set filename [$dp lookup_str $solv::REPOSITORY_REPOMD_LOCATION]
+    set checksum [$dp lookup_checksum $solv::REPOSITORY_REPOMD_CHECKSUM]
+    if {$filename ne "" && $checksum eq "NULL"} {
+      puts "no $filename file checksum"
+    } elseif {$filename ne ""} {
+      return [list $filename $checksum]
+    }
+  }
+  return {}
+}
+
+proc repo_repomd_load {selfName pool} {
+  upvar $selfName self
+  if [repo_generic_load self $pool] {
+    return 1
+  }
+  puts -nonewline "rpmmd repo '$self(name)': "
+  set f [repo_download self {repodata/repomd.xml} 0 {}]
+  if {$f eq {NULL}} {
+    puts "no repomd.xml file, skipped"
+    repo_free_handle self
+    return 0
+  }
+  set self(cookie) [repo_calc_cookie_fp self $f]
+  if [repo_usecachedrepo self "-" 1] {
+    puts "cached"
+    return 1
+  }
+  set handle $self(handle)
+  $handle add_repomdxml $f
+  puts "fetching"
+  set primary [repo_repomd_find self primary]
+  if {$primary ne {}} {
+    set f [repo_download self [lindex $primary 0] 1 [lindex $primary 1] 1]
+    if {$f ne {NULL}} {
+      $handle add_rpmmd $f {}
+      $f close
+    }
+    if [info exists self(incomplete)] {
+      return 0
+    }
+  }
+  set updateinfo [repo_repomd_find self primary]
+  if {$updateinfo ne {}} {
+    set f [repo_download self [lindex $updateinfo  0] 1 [lindex $updateinfo 1] 1]
+    if {$f ne {NULL}} {
+      $handle add_updateinfoxml $f
+      $f close
+    }
+  }
+  repo_repomd_add_exts self
+  repo_writecachedrepo self
+  $self(handle) create_stubs
+  return 1
+}
+
+proc repo_repomd_packagespath {selfName} {
+  return ""
+}
+
+proc repo_repomd_load_ext {selfName repodata} {
+  upvar $selfName self
+  switch -- [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_TYPE] {
+    "deltainfo" {
+      set ext DL
+    }
+    "filelists" {
+      set ext FL
+    }
+    default {
+      return 0
+    }
+  }
+  puts -nonewline "\[$self(name):$ext: "
+  flush stdout
+  if [repo_usecachedrepo self $ext] {
+    puts "cached]"
+    return 1
+  }
+  puts "fetching]"
+  set handle $self(handle)
+  set filename [$repodata lookup_str $solv::SOLVID_META $solv::REPOSITORY_REPOMD_LOCATION]
+  set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::REPOSITORY_REPOMD_CHECKSUM]
+  set f [repo_download self $filename 1 $filechecksum]
+  if {$f eq {NULL}} {
+    return 0
+  }
+  if {$ext eq "FL"} {
+    $handle add_rpmmd $f "FL" [ expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES | $solv::Repo_REPO_LOCALPOOL]
+  }
+  $f close
+  repo_writecachedrepo self $ext $repodata
+  return 1
+}
+
+### susetags
+
+proc repo_susetags_add_ext {selfName repodata what ext} {
+  upvar $selfName self
+  set where [repo_susetags_find self $what]
+  if {$where eq {}} {
+    return
+  }
+  set h [$repodata new_handle]
+  $repodata set_str $h $solv::SUSETAGS_FILE_NAME [lindex $where 0]
+  $repodata set_checksum $h $solv::SUSETAGS_FILE_CHECKSUM [lindex $where 1]
+  repo_generic_add_ext_keys self $ext $repodata $h
+  $repodata add_flexarray $solv::SOLVID_META $solv::REPOSITORY_EXTERNAL $h
+}
+
+proc repo_susetags_add_exts {selfName} {
+  upvar $selfName self
+  set repodata [$self(handle) add_repodata 0]
+  repo_susetags_add_ext self $repodata "packages.FL" "FL"
+  repo_susetags_add_ext self $repodata "packages.FL.gz" "FL"
+  $repodata internalize
+}
+
+proc repo_susetags_find {selfName what} {
+  upvar $selfName self
+  set di [$self(handle) Dataiterator_meta $solv::SUSETAGS_FILE_NAME $what $solv::Dataiterator_SEARCH_STRING]
+  $di prepend_keyname $solv::SUSETAGS_FILE
+  solv::iter d $di {
+    set dp [$d parentpos]
+    set checksum [$dp lookup_checksum $solv::SUSETAGS_FILE_CHECKSUM]
+    return [list $what $checksum]
+  }
+  return {}
+}
+
+proc repo_susetags_load {selfName pool} {
+  upvar $selfName self
+  if [repo_generic_load self $pool] {
+    return 1
+  }
+  puts -nonewline "susetags repo '$self(name)': "
+  set f [repo_download self {content} 0 {}]
+  if {$f eq {NULL}} {
+    puts "no content file, skipped"
+    repo_free_handle self
+    return 0
+  }
+  set self(cookie) [repo_calc_cookie_fp self $f]
+  if [repo_usecachedrepo self "-" 1] {
+    puts "cached"
+    return 1
+  }
+  set handle $self(handle)
+  $handle add_content $f
+  puts "fetching"
+  set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
+  set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
+  if {$descrdir eq {NULL}} {
+    set descrdir "suse/setup/descr"
+  }
+  set packages [repo_susetags_find self "packages.gz"]
+  if {$packages eq {}} {
+    set packages [repo_susetags_find self "packages"]
+  }
+  if {$packages ne {}} {
+    set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
+    if {$f ne {NULL}} {
+      $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_SUSETAGS_RECORD_SHARES]
+      $f close
+      set packages [repo_susetags_find self "packages.en.gz"]
+      if {$packages eq {}} {
+       set packages [repo_susetags_find self "packages.en"]
+      }
+      if {$packages ne {}} {
+       set f [repo_download self "$descrdir/[lindex $packages 0]" 1 [lindex $packages 1] 1]
+       if {$f ne {NULL}} {
+          $handle add_susetags $f $defvendorid {} [expr $solv::Repo_REPO_NO_INTERNALIZE | $solv::Repo_REPO_REUSE_REPODATA | $solv::Repo_REPO_EXTEND_SOLVABLES ]
+         $f close
+       }
+      }
+      $handle internalize
+    }
+  }
+  repo_susetags_add_exts self
+  repo_writecachedrepo self
+  $self(handle) create_stubs
+  return 1
+}
+
+proc repo_susetags_packagespath {selfName} {
+  upvar $selfName self
+  set datadir [[$self(handle) cget -meta] lookup_str $solv::SUSETAGS_DATADIR]
+  return [expr {$datadir ne {} ? "$datadir/" : "suse/"}]
+}
+
+proc repo_susetags_load_ext {selfName repodata} {
+  upvar $selfName self
+  set filename [$repodata lookup_str $solv::SOLVID_META $solv::SUSETAGS_FILE_NAME]
+  set ext [string range $filename 9 10]
+  puts -nonewline "\[$self(name):$ext: "
+  flush stdout
+  if [repo_usecachedrepo self $ext] {
+    puts "cached]"
+    return 1
+  }
+  puts "fetching]"
+  set handle $self(handle)
+  set defvendorid [[$handle cget -meta] lookup_id $solv::SUSETAGS_DEFAULTVENDOR]
+  set descrdir [[$handle cget -meta] lookup_str $solv::SUSETAGS_DESCRDIR]
+  if {$descrdir eq {NULL}} {
+    set descrdir "suse/setup/descr"
+  }
+  set filechecksum [$repodata lookup_checksum $solv::SOLVID_META $solv::SUSETAGS_FILE_CHECKSUM]
+  set f [repo_download self "$descrdir/$filename" 1 $filechecksum]
+  if {$f eq {NULL}} {
+    return 0
+  }
+  set flags [expr $solv::Repo_REPO_USE_LOADING | $solv::Repo_REPO_EXTEND_SOLVABLES]
+  if {$ext ne "DL"} {
+    set flags [expr $flags | $solv::Repo_REPO_LOCALPOOL]
+  }
+  $handle add_susetags $f $defvendorid $ext $flags
+  $f close
+  repo_writecachedrepo self $ext $repodata
+  return 1
+}
+
+### unknown
+
+proc repo_unknown_load {selfName pool} {
+  upvar $selfName self
+  puts "unsupported repo '$self(name)': skipped"
+  return 0
+}
+
+### poor man's OO
+
+proc repo_load {selfName pool} {
+  upvar $selfName self
+  "repo_$self(type)_load" self $pool
+}
+
+proc repo_packagespath {selfName} {
+  upvar $selfName self
+  "repo_$self(type)_packagespath" self
+}
+
+proc repo_load_ext {selfName repodata} {
+  upvar $selfName self
+  "repo_$self(type)_load_ext" self $repodata
+}
+
+###
+
+proc load_stub {repodata} {
+  set code [catch {
+    upvar #0 [[$repodata cget -repo] cget -appdata] repo
+    if [info exists repo(handle)] {
+      return [repo_load_ext repo $repodata]
+    }
+    return 0
+  } res]
+  if {$code == 2} {
+    return $res
+  }
+  puts stderr $res
+  return 0
+}
+
+###
+
+set repoNames {}
+foreach reponame [lsort [glob -nocomplain -directory $reposdir *.repo]] {
+  set ini [::ini::open $reponame r]
+  foreach alias [::ini::sections $ini] {
+    upvar #0 [globalarray] repo
+    array set repo {enabled 0 priority 99 autorefresh 1 type rpm-md metadata_expire 900}
+    array set repo [::ini::get $ini $alias]
+    set repo(name) $alias
+    switch -exact -- $repo(type) {
+      rpm-md  { set repo(type) repomd }
+      yast2   { set repo(type) susetags }
+      default { set repo(type) unknown }
+    }
+    lappend repoNames $repo(varName)
+  }
+  ::ini::close $ini
+}
+
+set pool [solv::new_Pool]
+$pool setarch
+$pool set_loadcallback load_stub
+
+upvar #0 [globalarray] sysrepo
+array set sysrepo [list name {@System} type system]
+repo_load sysrepo $pool
+
+foreach repoName $repoNames {
+  upvar 0 $repoName repo
+  if {$repo(enabled)} {
+    repo_load repo $pool
+  }
+}
+
+
+set cmd [lindex $::argv 0]
+set ::argv [lreplace $::argv 0 0]
+
+array set cmdabbrev [ list \
+  in install \
+  rm erase \
+  ls list \
+  ve verify \
+  se search \
+]
+if [info exists cmdabbrev($cmd)] {
+  set cmd $cmdabbrev($cmd)
+}
+
+if {$cmd eq "search"} {
+  set arg [lindex $::argv 0]
+  $pool createwhatprovides
+  set sel [$pool Selection]
+  set di [$pool Dataiterator $solv::SOLVABLE_NAME $arg [ expr $solv::Dataiterator_SEARCH_SUBSTRING | $solv::Dataiterator_SEARCH_NOCASE ]]
+  solv::iter d $di {
+    $sel add_raw $solv::Job_SOLVER_SOLVABLE [$d cget -solvid]
+  }
+  foreach s [$sel solvables] {
+    puts [format { - %s [%s]: %s} [$s str] [[$s cget -repo] cget -name] [$s lookup_str $solv::SOLVABLE_SUMMARY]]
+  }
+  exit
+}
+
+$pool addfileprovides
+$pool createwhatprovides
+
+array set cmdactionmap [ list \
+  install $solv::Job_SOLVER_INSTALL \
+  erase $solv::Job_SOLVER_ERASE \
+  up $solv::Job_SOLVER_UPDATE \
+  dup $solv::Job_SOLVER_DISTUPGRADE \
+  verify $solv::Job_SOLVER_VERIFY \
+  list 0 \
+  info 0 \
+]
+
+set jobs {}
+foreach arg $::argv {
+  set flags [expr $solv::Selection_SELECTION_NAME | $solv::Selection_SELECTION_PROVIDES | $solv::Selection_SELECTION_GLOB | \
+                  $solv::Selection_SELECTION_CANON | $solv::Selection_SELECTION_DOTARCH | $solv::Selection_SELECTION_REL ]
+  switch -glob -- $arg {
+    "/*" {
+      set flags [expr $flags | $solv::Selection_SELECTION_FILELIST ]
+      if {$cmd eq "erase"} {
+        set flags [expr $flags | $solv::Selection_SELECTION_INSTALLED_ONLY ]
+      }
+    }
+  }
+  set sel [$pool select $arg $flags]
+  if [$sel isempty] {
+    set sel [$pool select $arg [expr $flags | $solv::Selection_SELECTION_NOCASE]]
+    if {![$sel isempty]} {
+      puts "\[ignoring case for '$arg']"
+    }
+  }
+  if [$sel isempty] {
+    puts "nothing matches '$arg'"
+    exit 1
+  }
+  if {[$sel flags] & $solv::Selection_SELECTION_FILELIST} {
+    puts "\[using file list match for '$arg']"
+  }
+  if {[$sel flags] & $solv::Selection_SELECTION_PROVIDES} {
+    puts "\[using capability match for '$arg']"
+  }
+  lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
+}
+
+if {$jobs eq {} && ($cmd eq "up" || $cmd eq "dup" || $cmd eq "verify") } {
+  set sel [$pool Selection_all]
+  lappend jobs {*}[$sel jobs $cmdactionmap($cmd)]
+}
+
+if {$jobs eq {}} {
+  puts "no package matched."
+  exit 1
+}
+
+if {$cmd eq "list" || $cmd eq "info"} {
+  foreach job $jobs {
+    foreach s [$job solvables] {
+      if {$cmd eq "info"} {
+        puts [format {Name:        %s} [$s str]]
+        puts [format {Repo:        %s} [[$s cget -repo] cget -name]]
+        puts [format {Summary:     %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
+        set str [$s lookup_str $solv::SOLVABLE_URL]
+       if {$str ne {}} {
+          puts [format {Url:         %s} $str]
+       }
+        set str [$s lookup_str $solv::SOLVABLE_LICENSE]
+       if {$str ne {}} {
+          puts [format {License      %s} $str]
+       }
+        puts [format {Description: %s} [$s lookup_str $solv::SOLVABLE_DESCRIPTION]]
+       puts {}
+      } else {
+        puts [format {  - %s [%s]} [$s str] [[$s cget -repo] cget -name]]
+        puts [format {    %s} [$s lookup_str $solv::SOLVABLE_SUMMARY]]
+      }
+    }
+  }
+  exit
+}
+
+#$pool set_debuglevel 1
+set solver [$pool Solver]
+$solver set_flag $solv::Solver_SOLVER_FLAG_SPLITPROVIDES 1
+if {$cmd eq "erase"} {
+  $solver set_flag $solv::Solver_SOLVER_FLAG_ALLOW_UNINSTALL 1
+}
+
+set problems [$solver solve $jobs]
+if {$problems ne {}} {
+  set pcnt 1
+  foreach problem $problems {
+    puts [format {Problem %d/%d:} $pcnt [llength $problems]]
+    puts [$problem str]
+    incr pcnt
+  }
+  exit 1
+}
+
+set trans [$solver transaction]
+
+if [$trans isempty] {
+  puts "Nothing to do."
+  exit
+}
+
+puts {}
+puts "Transaction summary:"
+puts {}
+foreach cl [$trans classify [expr $solv::Transaction_SOLVER_TRANSACTION_SHOW_OBSOLETES | $solv::Transaction_SOLVER_TRANSACTION_OBSOLETE_IS_UPGRADE]] {
+  switch -- [$cl cget -type] \
+    $solv::Transaction_SOLVER_TRANSACTION_ERASE {
+      puts [format {%d erased packages:} [$cl cget -count]]
+    } \
+    $solv::Transaction_SOLVER_TRANSACTION_INSTALL {
+      puts [format {%d installed packages:} [$cl cget -count]]
+    } \
+    $solv::Transaction_SOLVER_TRANSACTION_REINSTALLED {
+      puts [format {%d reinstalled packages:} [$cl cget -count]]
+    } \
+    $solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED {
+      puts [format {%d downgraded packages:} [$cl cget -count]]
+    } \
+    $solv::Transaction_SOLVER_TRANSACTION_CHANGED {
+      puts [format {%d changed packages:} [$cl cget -count]]
+    } \
+    $solv::Transaction_SOLVER_TRANSACTION_UPGRADED {
+      puts [format {%d upgraded packages:} [$cl cget -count]]
+    } \
+    $solv::Transaction_SOLVER_TRANSACTION_VENDORCHANGE {
+      puts [format {%d vendor changes from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
+    } \
+    $solv::Transaction_SOLVER_TRANSACTION_ARCHCHANGE {
+      puts [format {%d archchanges from '%s' to '%s':} [$cl cget -count] [$cl cget -fromstr] [$cl cget -tostr]]
+    } \
+    default continue
+  foreach p [$cl solvables] {
+    set cltype [$cl cget -type]
+    if {$cltype == $solv::Transaction_SOLVER_TRANSACTION_UPGRADED || $cltype ==$solv::Transaction_SOLVER_TRANSACTION_DOWNGRADED} {
+      set op [$trans othersolvable $p]
+      puts [format {  - %s -> %s} [$p str] [$p str]]
+    } else {
+      puts [format {  - %s} [$p str]]
+    }
+  }
+  puts {}
+}
+puts [format {install size change: %d K} [$trans calc_installsizechange]]
+puts {}
+
+while 1 {
+  puts -nonewline "OK to continue (y/n)? "
+  flush stdout
+  set yn [gets stdin]
+  if {$yn eq "y"} {
+    break
+  }
+  if {$yn eq "n" || $yn eq "q"} {
+    exit
+  }
+}
+
+set newpkgs [$trans newsolvables]
+array set newpkgs_f {}
+if {$newpkgs ne {}} {
+  set downloadsize 0
+  foreach p $newpkgs {
+    set downloadsize [expr $downloadsize + [$p lookup_num $solv::SOLVABLE_DOWNLOADSIZE]]
+  }
+  puts [format {Downloading %d packages, %d K} [llength $newpkgs] [expr $downloadsize / 1024]]
+  foreach p $newpkgs {
+    upvar #0 [[$p cget -repo] cget -appdata] repo
+    set location [$p lookup_location]
+    if {$location eq {}} {
+      continue
+    }
+    set location "[repo_packagespath repo][lindex $location 0]"
+    set checksum [$p lookup_checksum $solv::SOLVABLE_CHECKSUM]
+    set f [repo_download repo $location 0 $checksum]
+    set newpkgs_f([$p cget -id]) $f
+    puts -nonewline "."
+    flush stdout
+  }
+  puts {}
+}
+
+puts "Committing transaction:"
+$trans order
+foreach p [$trans steps] {
+  set steptype [$trans steptype $p $solv::Transaction_SOLVER_TRANSACTION_RPM_ONLY]
+  if {$steptype == $solv::Transaction_SOLVER_TRANSACTION_ERASE} {
+    puts "erase [$p str]"
+    regsub {^[0-9]+:} [$p cget -evr] {} nvr
+    set nvr "[$p cget -name]-$nvr.[$p cget -arch]"
+    exec -ignorestderr -- rpm -e --nodeps --nodigest --nosignature $nvr
+  } elseif {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL || $steptype == $solv::Transaction_SOLVER_TRANSACTION_MULTIINSTALL} {
+    puts "install [$p str]"
+    set f $newpkgs_f([$p cget -id])
+    set mode [expr {$steptype == $solv::Transaction_SOLVER_TRANSACTION_INSTALL ? "-U" : "-i"}]
+    $f cloexec 0
+    exec -ignorestderr -- rpm $mode --force --nodeps --nodigest --nosignature "/dev/fd/[$f fileno]"
+  }
+}
index 360c7dc..ad52495 100644 (file)
@@ -128,6 +128,7 @@ ENDIF (DISABLE_SHARED)
 
 SET_TARGET_PROPERTIES(libsolvext PROPERTIES OUTPUT_NAME "solvext")
 SET_TARGET_PROPERTIES(libsolvext PROPERTIES SOVERSION ${LIBSOLVEXT_SOVERSION})
+SET_TARGET_PROPERTIES(libsolvext PROPERTIES INSTALL_NAME_DIR ${LIB_INSTALL_DIR})
 
 INSTALL (FILES ${libsolvext_HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/solv")
 INSTALL (TARGETS libsolvext LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
index 742823a..d3e559e 100644 (file)
 #include "pool.h"
 #include "pool_parserpmrichdep.h"
 
-#define REL_THEN 0
-#define REL_ELSE 0
-
 static struct RichOpComp {
   const char *n;
   int l;
   Id fl;
 } RichOps[] = {
-  { "&&", 2, REL_AND },
-  { "&", 1, REL_AND },
-  { "AND", 3, REL_AND },
-  { "||", 2, REL_OR },
-  { "|", 1, REL_OR },
-  { "OR", 2, REL_OR },
-  { "IF", 2, REL_COND },
-  { "THEN", 4, REL_THEN },
-  { "?", 1, REL_THEN },
-  { "ELSE", 4, REL_ELSE },
-  { ":", 1, REL_ELSE },
+  { "and",  3, REL_AND },
+  { "or",   2, REL_OR },
+  { "if",   2, REL_COND },
+  { "else", 4, REL_ELSE },
   { NULL, 0, 0},
 };
 
@@ -116,7 +106,7 @@ parseRichDep(Pool *pool, const char **depp, Id chainfl)
   fl = op->fl;
   if (!fl)
     return 0;
-  if (chainfl == REL_THEN && fl == REL_ELSE)
+  if (chainfl == REL_COND && fl == REL_ELSE)
     chainfl = 0;
   if (chainfl && fl != chainfl)
     return 0;
index 26c73b0..35dcc84 100644 (file)
@@ -475,7 +475,7 @@ repo_add_deb(Repo *repo, const char *deb, int flags)
       return 0;
     }
   l = fread(buf, 1, sizeof(buf), fp);
-  if (l < 8 + 60 || strncmp((char *)buf, "!<arch>\ndebian-binary   ", 8 + 16) != 0)
+  if (l < 8 + 60 || (strncmp((char *)buf, "!<arch>\ndebian-binary   ", 8 + 16) != 0 && strncmp((char *)buf, "!<arch>\ndebian-binary/  ", 8 + 16) != 0))
     {
       pool_error(pool, -1, "%s: not a deb package", deb);
       fclose(fp);
@@ -495,7 +495,7 @@ repo_add_deb(Repo *repo, const char *deb, int flags)
       fclose(fp);
       return 0;
     }
-  if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz  ", 16) != 0)
+  if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz  ", 16) != 0 && strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz/ ", 16) != 0)
     {
       pool_error(pool, -1, "%s: control.tar.gz is not second entry", deb);
       fclose(fp);
index 1bf6f17..3d0a91d 100644 (file)
@@ -136,6 +136,8 @@ repo_add_mdk(Repo *repo, FILE *fp, int flags)
          char *arch;
          char *version;
          char *filename;
+         char *disttag = 0;
+         char *distepoch = 0;
          if ((epochstr = strchr(nvra, '@')) != 0)
            {
              char *sizestr;
@@ -146,14 +148,23 @@ repo_add_mdk(Repo *repo, FILE *fp, int flags)
                  *sizestr++ = 0;
                  if ((groupstr = strchr(sizestr, '@')) != 0)
                    {
-                     char *n;
                      *groupstr++ = 0;
-                     if ((n = strchr(groupstr, '@')) != 0)
-                       *n = 0;
+                     if ((disttag = strchr(groupstr, '@')) != 0)
+                       {
+                         *disttag++ = 0;
+                         if ((distepoch = strchr(disttag, '@')) != 0)
+                           {
+                             char *n;
+                             *distepoch++ = 0;
+                             if ((n = strchr(distepoch, '@')) != 0)
+                               *n = 0;
+                           }
+                       }
                      if (*groupstr)
                        repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_GROUP, groupstr);
                    }
-                 repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(sizestr, 0, 10));
+                 if (*sizestr)
+                   repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(sizestr, 0, 10));
                }
            }
           filename = pool_tmpjoin(pool, nvra, ".rpm", 0);
@@ -163,47 +174,21 @@ repo_add_mdk(Repo *repo, FILE *fp, int flags)
              *arch++ = 0;
              s->arch = pool_str2id(pool, arch, 1);
            }
-         /* argh, do we have a distepoch or not, check self-provides */
-         if (s->provides)
+         if (disttag && *disttag)
            {
-             Id id, lastid, *idp = s->repo->idarraydata + s->provides;
-             lastid = 0;
-             for (idp = s->repo->idarraydata + s->provides; (id = *idp) != 0; idp++)
-               {
-                 const char *evr, *name;
-                 int namel;
-                 Reldep *rd;
-                 if (!ISRELDEP(id))
-                   continue;
-                 rd = GETRELDEP(pool, id);
-                 if (rd->flags != REL_EQ)
-                   continue;
-                 name = pool_id2str(pool, rd->name);
-                 namel = strlen(name);
-                 if (strncmp(name, nvra, namel) != 0 || nvra[namel] != '-')
-                   continue;
-                 evr = pool_id2str(pool, rd->evr);
-                 evr = strrchr(evr, '-');
-                 if (evr && strchr(evr, ':') != 0)
-                   lastid = id;
-               }
-             if (lastid)
-               {
-                 /* self provides found, and it contains a distepoch */
-                 /* replace with self-provides distepoch to get rid of the disttag */
-                 char *nvradistepoch = strrchr(nvra, '-');
-                 if (nvradistepoch)
-                   {
-                     Reldep *rd = GETRELDEP(pool, lastid);
-                     const char *evr = pool_id2str(pool, rd->evr);
-                     evr = strrchr(evr, '-');
-                     if (evr && (evr = strchr(evr, ':')) != 0)
-                       {
-                         if (strlen(evr) < strlen(nvradistepoch))
-                           strcpy(nvradistepoch, evr);
-                       }
-                   }
-               }
+             /* strip disttag from release */
+             char *n = strrchr(nvra, '-');
+             if (n && !strncmp(n + 1, disttag, strlen(disttag)))
+               *n = 0;
+           }
+         if (distepoch && *distepoch)
+           {
+             /* add distepoch */
+             int le = strlen(distepoch);
+             int ln = strlen(nvra);
+             nvra[ln++] = ':';
+             memmove(nvra + ln, distepoch, le);        /* may overlap */
+             nvra[le + ln] = 0;
            }
          version = strrchr(nvra, '-');
          if (version)
index 71c69ab..d49f9d8 100644 (file)
 #define DEP_STRONG             (1 << 27)
 #define DEP_PRE_IN             ((1 << 6) | (1 << 9) | (1 << 10))
 #define DEP_PRE_UN             ((1 << 6) | (1 << 11) | (1 << 12))
-#define DEP_RICH               (1 << 29)
 
 #define FILEFLAG_GHOST         (1 <<  6)
 
@@ -537,7 +536,7 @@ makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf,
        if (!strncmp(n[i], "rpmlib(", 7))
          continue;
 #ifdef ENABLE_COMPLEX_DEPS
-      if ((f[i] & (DEP_RICH|DEP_LESS| DEP_EQUAL|DEP_GREATER)) == DEP_RICH && n[i][0] == '(')
+      if ((f[i] & (DEP_LESS|DEP_EQUAL|DEP_GREATER)) == 0 && n[i][0] == '(')
        {
          id = pool_parserpmrichdep(pool, n[i]);
          if (id)
index 251cd2f..e4346fe 100644 (file)
@@ -378,6 +378,7 @@ struct oplist {
   { REL_COND,  "<IF>" },
   { REL_COMPAT,  "compat >=" },
   { REL_KIND,  "<KIND>" },
+  { REL_ELSE, "<ELSE>" },
   { REL_LT, "<" },
   { 0, 0 }
 };
@@ -2006,7 +2007,7 @@ testcase_solverresult(Solver *solv, int resultflags)
 
 
 int
-testcase_write(Solver *solv, char *dir, int resultflags, const char *testcasename, const char *resultname)
+testcase_write(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname)
 {
   Pool *pool = solv->pool;
   Repo *repo;
@@ -2332,7 +2333,7 @@ str2resultflags(Pool *pool, char *s)      /* modifies the string! */
 }
 
 Solver *
-testcase_read(Pool *pool, FILE *fp, char *testcase, Queue *job, char **resultp, int *resultflagsp)
+testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp)
 {
   Solver *solv;
   char *buf, *bufp;
@@ -2750,7 +2751,7 @@ testcase_read(Pool *pool, FILE *fp, char *testcase, Queue *job, char **resultp,
 }
 
 char *
-testcase_resultdiff(char *result1, char *result2)
+testcase_resultdiff(const char *result1, const char *result2)
 {
   Strqueue sq1, sq2, osq;
   char *r;
index 78f78b0..4903e6c 100644 (file)
@@ -32,6 +32,6 @@ extern const char *testcase_getsolverflags(Solver *solv);
 extern int testcase_setsolverflags(Solver *solv, const char *str);
 extern void testcase_resetsolverflags(Solver *solv);
 extern char *testcase_solverresult(Solver *solv, int flags);
-extern int testcase_write(Solver *solv, char *dir, int resultflags, const char *testcasename, const char *resultname);
-extern Solver *testcase_read(Pool *pool, FILE *fp, char *testcase, Queue *job, char **resultp, int *resultflagsp);
-extern char *testcase_resultdiff(char *result1, char *result2);
+extern int testcase_write(Solver *solv, const char *dir, int resultflags, const char *testcasename, const char *resultname);
+extern Solver *testcase_read(Pool *pool, FILE *fp, const char *testcase, Queue *job, char **resultp, int *resultflagsp);
+extern char *testcase_resultdiff(const char *result1, const char *result2);
diff --git a/libsolv.pc.in b/libsolv.pc.in
new file mode 100644 (file)
index 0000000..c82dfc4
--- /dev/null
@@ -0,0 +1,8 @@
+libdir=@LIB_INSTALL_DIR@
+includedir=@INCLUDE_INSTALL_DIR@
+
+Name: libsolv
+Description: Library for solving packages and reading repositories
+Version: @VERSION@
+Libs: -L${libdir} -lsolvext -lsolv
+Cflags: -I${includedir}
index 5a0a59b..b98c3ba 100644 (file)
@@ -1,4 +1,14 @@
 -------------------------------------------------------------------
+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
+- support REL_ELSE as evr of REL_COND
+- bump version to 0.6.12
+
+-------------------------------------------------------------------
 Tue Jun  2 11:41:10 CEST 2015 - mls@suse.de
 
 - add forgotten sha-512 support to data_skip
index f5581b6..c2251ec 100644 (file)
@@ -34,7 +34,7 @@ BuildRoot:      %{_tmppath}/%{name}-%{version}-build
 BuildRequires:  libneon0.26-devel
 %endif
 %if 0%{?fedora_version} || 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600
-BuildRequires:  db4-devel
+BuildRequires:  db-devel
 %endif
 %if 0%{?suse_version}
 %if 0%{?suse_version} < 1030
@@ -166,7 +166,7 @@ CMAKE_FLAGS=
 CMAKE_FLAGS="-DFEDORA=1"
 %endif
 %if 0%{?suse_version}
-CMAKE_FLAGS="-DSUSE=1 -DENABLE_APPDATA=1"
+CMAKE_FLAGS="-DSUSE=1 -DENABLE_APPDATA=1 -DENABLE_COMPS=1"
 %endif
 
 cmake   $CMAKE_FLAGS \
@@ -240,6 +240,7 @@ rm -rf "$RPM_BUILD_ROOT"
 %{_mandir}/man1/helix2solv*
 %endif
 %{_datadir}/cmake/Modules/*
+%{_libdir}/pkgconfig/libsolv.pc
 %{_mandir}/man3/*
 
 %files demo
index f60853e..5d7190a 100644 (file)
@@ -40,6 +40,7 @@ ENDIF (DISABLE_SHARED)
 
 SET_TARGET_PROPERTIES(libsolv PROPERTIES OUTPUT_NAME "solv")
 SET_TARGET_PROPERTIES(libsolv PROPERTIES SOVERSION ${LIBSOLV_SOVERSION})
+SET_TARGET_PROPERTIES(libsolv PROPERTIES INSTALL_NAME_DIR ${LIB_INSTALL_DIR})
 
 INSTALL (FILES ${libsolv_HEADERS} DESTINATION "${INCLUDE_INSTALL_DIR}/solv")
 INSTALL (TARGETS libsolv LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
index 2f27621..935aea8 100644 (file)
@@ -259,3 +259,16 @@ solv_chksum_free(Chksum *chk, unsigned char *cp)
   return 0;
 }
 
+int
+solv_chksum_cmp(Chksum *chk, Chksum *chk2)
+{
+  int len;
+  const unsigned char *res1, *res2;
+  if (chk == chk2)
+    return 1;
+  if (!chk || !chk2 || chk->type != chk2->type)
+    return 0;
+  res1 = solv_chksum_get(chk, &len);
+  res2 = solv_chksum_get(chk2, 0);
+  return memcmp(res1, res2, len) == 0 ? 1 : 0;
+}
index 02f1504..479923a 100644 (file)
@@ -28,6 +28,7 @@ void *solv_chksum_free(Chksum *chk, unsigned char *cp);
 const char *solv_chksum_type2str(Id type);
 Id solv_chksum_str2type(const char *str);
 int solv_chksum_len(Id type);
+int solv_chksum_cmp(Chksum *chk, Chksum *chk2);
 
 #ifdef LIBSOLV_INTERNAL
 
index 1be6868..aadbc48 100644 (file)
@@ -96,10 +96,12 @@ print_depblocks(Pool *pool, Queue *bq, int start)
 #endif
 
 /* invert all literals in the blocks. note that this also turns DNF into CNF and vice versa */
-static void
-invert_depblocks(Pool *pool, Queue *bq, int start)
+static int
+invert_depblocks(Pool *pool, Queue *bq, int start, int r)
 {
   int i, j, end;
+  if (r == 0 || r == 1)
+    return r ? 0 : 1;
   expand_simpledeps(pool, bq, start, 0);
   end = bq->count;
   for (i = j = start; i < end; i++)
@@ -122,6 +124,7 @@ invert_depblocks(Pool *pool, Queue *bq, int start)
        }
       j = i + 1;
     }
+  return -1;
 }
 
 /*
@@ -147,40 +150,129 @@ normalize_dep(Pool *pool, Id dep, Queue *bq, int flags)
       if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_COND)
        {
          int rdflags = rd->flags;
+         Id name = rd->name;
+         Id evr = rd->evr;
          int r, mode;
          
-         /* in inverted mode, COND means AND. otherwise it means OR NOT */
-         if (rdflags == REL_COND && todnf)
-           rdflags = REL_AND;
-         mode = rdflags == REL_AND ? 0 : 1;
+          if (rdflags == REL_COND)
+           {
+             /* check for relly complex ELSE case */
+             if (ISRELDEP(evr))
+               {
+                 Reldep *rd2 = GETRELDEP(pool, evr);
+                 if (rd2->flags == REL_ELSE)
+                   {
+                     int r2;
+                     /* really complex case */
+                     if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_AND_1)
+                       {
+                         /* A OR ~B */
+                         rdflags = REL_COND;
+                         evr = rd2->name;
+                       }
+                     else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_AND_2)
+                       {
+                         /* C OR B */
+                         rdflags = REL_OR;
+                         name = rd2->evr;
+                         evr = rd2->name;
+                       }
+                     else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_1)
+                       {
+                         /* A AND B */
+                         rdflags = REL_AND;
+                         evr = rd2->name;
+                       }
+                     else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_2)
+                       {
+                         /* A AND C */
+                         rdflags = REL_AND;
+                         evr = rd2->evr;
+                       }
+                     else if ((flags & CPLXDEPS_ELSE_MASK) == CPLXDEPS_ELSE_OR_3)
+                       {
+                         /* C AND ~B */
+                         rdflags = REL_ELSE;
+                         name = rd2->evr;
+                         evr = rd2->name;
+                       }
+                     else if (!todnf)
+                       {
+                         /* we want AND: A IF (B ELSE C) -> (A OR ~B) AND (C OR B) */
+                         r = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_AND_1);
+                         if (r == 0 && (flags & CPLXDEPS_DONTFIX) == 0)
+                           return 0;
+                         r2 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_AND_2);
+                         if (r2 == 0 && (flags & CPLXDEPS_DONTFIX) == 0)
+                           {
+                             queue_truncate(bq, bqcnt);
+                             return 0;
+                           }
+                         if (r == -1 || r2 == -1)
+                           return -1;
+                         return r == 1 || r2 == 1 ? 1 : 0;
+                       }
+                     else
+                       {
+                         int r2, r3;
+                         /* we want OR: A IF (B ELSE C) -> (A AND B) OR (A AND C) OR (~B AND C) */
+                         r = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_1);
+                         if (r == 1)
+                           return 1;
+                         r2 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_2);
+                         if (r2 == 1)
+                           {
+                             queue_truncate(bq, bqcnt);
+                             return 1;
+                           }
+                         r3 = normalize_dep(pool, dep, bq, flags | CPLXDEPS_ELSE_OR_3);
+                         if (r3 == 1)
+                           {
+                             queue_truncate(bq, bqcnt);
+                             return 1;
+                           }
+                         if (r == -1 || r2 == -1 || r3 == -1)
+                           return -1;
+                         return 0;
+                       }
+                   }
+               }
+           }
+         mode = rdflags == REL_AND || rdflags == REL_ELSE ? 0 : 1;
 
          /* get blocks of first argument */
-         r = normalize_dep(pool, rd->name, bq, flags);
+         r = normalize_dep(pool, name, bq, flags);
          if (r == 0)
            {
+             if (rdflags == REL_ELSE)
+               return 0;
              if (rdflags == REL_AND && (flags & CPLXDEPS_DONTFIX) == 0)
                return 0;
              if (rdflags == REL_COND)
                {
-                 r = normalize_dep(pool, rd->evr, bq, (flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX);
-                 if (r == 0 || r == 1)
-                   return r == 0 ? 1 : 0;
-                 invert_depblocks(pool, bq, bqcnt);    /* invert block for COND */
-                 return r;
+                 r = normalize_dep(pool, evr, bq, (flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX);
+                 return invert_depblocks(pool, bq, bqcnt, r);  /* invert block for COND */
                }
-             return normalize_dep(pool, rd->evr, bq, flags);
+             return normalize_dep(pool, evr, bq, flags);
            }
          if (r == 1)
            {
-             if (rdflags != REL_AND)
+             if (rdflags == REL_ELSE)
+               {
+                 r = normalize_dep(pool, evr, bq, (flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX);
+                 return invert_depblocks(pool, bq, bqcnt, r);  /* invert block for ELSE */
+               }
+             if (rdflags == REL_OR || rdflags == REL_COND)
                return 1;
-             return normalize_dep(pool, rd->evr, bq, flags);
+             return normalize_dep(pool, evr, bq, flags);
            }
 
          /* get blocks of second argument */
          bqcnt2 = bq->count;
          /* COND is OR with NEG on evr block, so we invert the todnf flag in that case */
-         r = normalize_dep(pool, rd->evr, bq, rdflags == REL_COND ? ((flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX) : flags);
+         r = normalize_dep(pool, evr, bq, rdflags == REL_COND || rdflags == REL_ELSE ? ((flags ^ CPLXDEPS_TODNF) & ~CPLXDEPS_DONTFIX) : flags);
+         if (rdflags == REL_COND || rdflags == REL_ELSE)
+           r = invert_depblocks(pool, bq, bqcnt2, r);  /* invert 2nd block */
          if (r == 0)
            {
              if (rdflags == REL_OR)
@@ -188,19 +280,17 @@ normalize_dep(Pool *pool, Id dep, Queue *bq, int flags)
              if (rdflags == REL_AND && (flags & CPLXDEPS_DONTFIX) != 0)
                return -1;
              queue_truncate(bq, bqcnt);
-             return rdflags == REL_COND ? 1 : 0;
+             return 0;
            }
          if (r == 1)
            {
-             if (rdflags == REL_OR)
+             if (rdflags == REL_COND || rdflags == REL_OR)
                {
                  queue_truncate(bq, bqcnt);
                  return 1;
                }
              return -1;
            }
-         if (rdflags == REL_COND)
-           invert_depblocks(pool, bq, bqcnt2); /* invert 2nd block */
          if (mode == todnf)
            {
              /* simple case: just join em. nothing more to do here. */
@@ -315,12 +405,7 @@ pool_normalize_complex_dep(Pool *pool, Id dep, Queue *bq, int flags)
         expand_simpledeps(pool, bq, bqcnt, 0);
     }
   if ((flags & CPLXDEPS_INVERT) != 0)
-    {
-      if (i == 0 || i == 1)
-       i ^= 1;
-      else
-       invert_depblocks(pool, bq, bqcnt);
-    }
+    i = invert_depblocks(pool, bq, bqcnt, i);
 #ifdef CPLXDEBUG
   if (i == 0)
     printf("NONE\n");
@@ -332,5 +417,38 @@ pool_normalize_complex_dep(Pool *pool, Id dep, Queue *bq, int flags)
   return i;
 }
 
+void
+pool_add_pos_literals_complex_dep(Pool *pool, Id dep, Queue *q, Map *m, int neg)
+{
+  while (ISRELDEP(dep))
+    {
+      Reldep *rd = GETRELDEP(pool, dep);
+      if (rd->flags != REL_AND && rd->flags != REL_OR && rd->flags != REL_COND)
+       break;
+      pool_add_pos_literals_complex_dep(pool, rd->name, q, m, neg);
+      dep = rd->evr;
+      if (rd->flags == REL_COND)
+       {
+         neg = !neg;
+         if (ISRELDEP(dep))
+           {
+             Reldep *rd2 = GETRELDEP(pool, rd->evr);
+             if (rd2->flags == REL_ELSE)
+               {
+                 pool_add_pos_literals_complex_dep(pool, rd2->evr, q, m, !neg);
+                 dep = rd2->name;
+               }
+           }
+       }
+    }
+  if (!neg)
+    {
+      Id p, pp;
+      FOR_PROVIDES(p, pp, dep)
+       if (!MAPTST(m, p))
+         queue_push(q, p);
+    }
+}
+
 #endif /* ENABLE_COMPLEX_DEPS */
 
index b553305..798b485 100644 (file)
@@ -27,6 +27,7 @@ pool_is_complex_dep(Pool *pool, Id dep)
 }
 
 extern int pool_normalize_complex_dep(Pool *pool, Id dep, Queue *bq, int flags);
+extern void pool_add_pos_literals_complex_dep(Pool *pool, Id dep, Queue *q, Map *m, int neg);
 
 #define CPLXDEPS_TODNF   (1 << 0)
 #define CPLXDEPS_EXPAND  (1 << 1)
@@ -34,5 +35,12 @@ extern int pool_normalize_complex_dep(Pool *pool, Id dep, Queue *bq, int flags);
 #define CPLXDEPS_NAME    (1 << 3)
 #define CPLXDEPS_DONTFIX (1 << 4)
 
+#define CPLXDEPS_ELSE_AND_1 (1 << 8)
+#define CPLXDEPS_ELSE_AND_2 (1 << 9)
+#define CPLXDEPS_ELSE_OR_1  (1 << 10)
+#define CPLXDEPS_ELSE_OR_2  (1 << 11)
+#define CPLXDEPS_ELSE_OR_3  (1 << 12)
+#define CPLXDEPS_ELSE_MASK  (0x1f00)
+
 #endif
 
index b8d9764..91186a7 100644 (file)
@@ -248,6 +248,7 @@ SOLV_1.0 {
                solv_bin2hex;
                solv_calloc;
                solv_chksum_add;
+               solv_chksum_cmp;
                solv_chksum_create;
                solv_chksum_create_clone;
                solv_chksum_create_from_bin;
index f78f71a..33293b5 100644 (file)
@@ -421,7 +421,7 @@ pool_shrink_whatprovidesaux(Pool *pool)
        *wp++ = id;
     }
   newoff = wp - pool->whatprovidesauxdata;
-  solv_realloc(pool->whatprovidesauxdata, newoff * sizeof(Id));
+  pool->whatprovidesauxdata = solv_realloc(pool->whatprovidesauxdata, newoff * sizeof(Id));
   POOL_DEBUG(SOLV_DEBUG_STATS, "shrunk whatprovidesauxdata from %d to %d\n", pool->whatprovidesauxdataoff, newoff);
   pool->whatprovidesauxdataoff = newoff;
 }
@@ -1014,10 +1014,21 @@ pool_addrelproviders(Pool *pool, Id d)
 
        case REL_AND:
        case REL_OR:
+       case REL_COND:
+         if (flags == REL_COND)
+           {
+             if (ISRELDEP(evr))
+               {
+                 Reldep *rd2 = GETRELDEP(pool, evr);
+                 evr = rd2->flags == REL_ELSE ? rd2->evr : 0;
+               }
+             else
+               evr = 0;        /* assume cond is true */
+           }
          wp = pool_whatprovides(pool, name);
          if (!pool->whatprovidesdata[wp])
-           wp = pool_whatprovides(pool, evr);
-         else
+           wp = evr ? pool_whatprovides(pool, evr) : 1;
+         else if (evr)
            {
              /* sorted merge */
              pp2 = pool_whatprovides_ptr(pool, evr);
@@ -1043,11 +1054,6 @@ pool_addrelproviders(Pool *pool, Id d)
            }
          break;
 
-       case REL_COND:
-         /* assume the condition is true */
-         wp = pool_whatprovides(pool, name);
-         break;
-
        case REL_NAMESPACE:
          if (name == NAMESPACE_OTHERPROVIDERS)
            {
index 6b98efc..4a2089d 100644 (file)
@@ -222,6 +222,7 @@ struct _Pool {
 #define REL_COMPAT     23
 #define REL_KIND       24      /* for filters only */
 #define REL_MULTIARCH  25      /* debian multiarch annotation */
+#define REL_ELSE       26      /* only as evr part of REL_COND */
 
 #if !defined(__GNUC__) && !defined(__attribute__)
 # define __attribute__(x)
index 79d3bd3..9408983 100644 (file)
@@ -59,7 +59,10 @@ static const char *archpolicies[] = {
   "sparcv9",   "sparcv9:sparcv8:sparc",
   "sparcv8",   "sparcv8:sparc",
   "sparc",     "sparc",
+  "mips",      "mips",
   "mipsel",    "mipsel",
+  "mips64",    "mips64",
+  "mips64el",  "mips64el",
   "m68k",      "m68k",
 #ifdef FEDORA
   "ia32e",     "ia32e:x86_64:athlon:i686:i586:i486:i386",
index 7bcc1f6..2138c42 100644 (file)
@@ -192,6 +192,8 @@ pool_id2rel(const Pool *pool, Id id)
       return " compat >= ";
     case REL_KIND:
       return " KIND ";
+    case REL_ELSE:
+      return " ELSE ";
     default:
       break;
     }
index 4ba1345..c854262 100644 (file)
@@ -1927,19 +1927,19 @@ dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
          return;
        }
       di->repoid = 0;
-      di->data = di->repo->repodata + di->pool->pos.repodataid;
-      di->repodataid = 0;
-      di->solvid = solvid;
-      di->state = di_enterrepo;
-      di->flags |= SEARCH_THISSOLVID;
-      return;
+      if (!di->pool->pos.repodataid && di->pool->pos.solvid == SOLVID_META) {
+       solvid = SOLVID_META;           /* META pos hack */
+      } else {
+        di->data = di->repo->repodata + di->pool->pos.repodataid;
+        di->repodataid = 0;
+      }
     }
-  if (solvid > 0)
+  else if (solvid > 0)
     {
       di->repo = di->pool->solvables[solvid].repo;
       di->repoid = 0;
     }
-  else if (di->repoid > 0)
+  if (di->repoid > 0)
     {
       if (!di->pool->urepos)
        {
@@ -1949,7 +1949,8 @@ dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
       di->repoid = 1;
       di->repo = di->pool->repos[di->repoid];
     }
-  di->repodataid = 1;
+  if (solvid != SOLVID_POS)
+    di->repodataid = 1;
   di->solvid = solvid;
   if (solvid)
     di->flags |= SEARCH_THISSOLVID;
index 67de769..3264e9b 100644 (file)
@@ -52,7 +52,9 @@ dep_possible(Solver *solv, Id dep, Map *m)
       Reldep *rd = GETRELDEP(pool, dep);
       if (rd->flags >= 8)
         {
-         if (rd->flags == REL_AND || rd->flags == REL_COND)
+         if (rd->flags == REL_COND)
+           return 1;
+         if (rd->flags == REL_AND)
            {
              if (!dep_possible(solv, rd->name, m))
                return 0;
@@ -966,13 +968,20 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
        }
 
       /*-----------------------------------------
-       * add recommends to the work queue
+       * add recommends/suggests to the work queue
        */
       if (s->recommends && m)
        {
          recp = s->repo->idarraydata + s->recommends;
          while ((rec = *recp++) != 0)
            {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, rec))
+               {
+                 pool_add_pos_literals_complex_dep(pool, rec, &workq, m, 0);
+                   continue;
+               }
+#endif
              FOR_PROVIDES(p, pp, rec)
                if (!MAPTST(m, p))
                  queue_push(&workq, p);
@@ -983,6 +992,13 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
          sugp = s->repo->idarraydata + s->suggests;
          while ((sug = *sugp++) != 0)
            {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, sug))
+               {
+                 pool_add_pos_literals_complex_dep(pool, sug, &workq, m, 0);
+                   continue;
+               }
+#endif
              FOR_PROVIDES(p, pp, sug)
                if (!MAPTST(m, p))
                  queue_push(&workq, p);
index f551731..b89e0d1 100644 (file)
@@ -117,6 +117,29 @@ solver_dep_fulfilled_alreadyinstalled(Solver *solv, Id dep)
   if (ISRELDEP(dep))
     {
       Reldep *rd = GETRELDEP(pool, dep);
+      if (rd->flags == REL_COND)
+       {
+         int r1, r2;
+         if (ISRELDEP(rd->evr))
+           {
+             Reldep *rd2 = GETRELDEP(pool, rd->evr);
+             if (rd2->flags == REL_ELSE)
+               {
+                 r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd2->name);
+                 if (r1)
+                   {
+                     r2 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+                     return r2 && r1 == 2 ? 2 : r2;
+                   }
+                 return solver_dep_fulfilled_alreadyinstalled(solv, rd2->evr);
+               }
+           }
+         r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
+         r2 = !solver_dep_fulfilled_alreadyinstalled(solv, rd->evr);
+         if (!r1 && !r2)
+           return 0;
+          return r1 == 2 ? 2 : 1;
+       }
       if (rd->flags == REL_AND)
         {
          int r2, r1 = solver_dep_fulfilled_alreadyinstalled(solv, rd->name);
index f8df8c7..fe80881 100644 (file)
@@ -27,7 +27,23 @@ solver_dep_fulfilled(Solver *solv, Id dep)
   if (ISRELDEP(dep))
     {
       Reldep *rd = GETRELDEP(pool, dep);
-      if (rd->flags == REL_AND || rd->flags == REL_COND)
+      if (rd->flags == REL_COND)
+       {
+         if (ISRELDEP(rd->evr))
+           {
+             Reldep *rd2 = GETRELDEP(pool, rd->evr);
+             if (rd2->flags == REL_ELSE)
+               {
+                 if (solver_dep_fulfilled(solv, rd2->name))
+                   return solver_dep_fulfilled(solv, rd->name);
+                 return solver_dep_fulfilled(solv, rd2->evr);
+               }
+           }
+          if (solver_dep_fulfilled(solv, rd->name))
+           return 1;
+         return !solver_dep_fulfilled(solv, rd->evr);
+       }
+      if (rd->flags == REL_AND)
         {
           if (!solver_dep_fulfilled(solv, rd->name))
             return 0;
index 428688f..5c49394 100644 (file)
@@ -73,6 +73,7 @@ main(int argc, char **argv)
   int resultflags = 0;
   int debuglevel = 0;
   int writeresult = 0;
+  char *writetestcase = 0;
   int multijob = 0;
   int rescallback = 0;
   int c;
@@ -82,7 +83,7 @@ main(int argc, char **argv)
   const char *p;
 
   queue_init(&solq);
-  while ((c = getopt(argc, argv, "vmrhl:s:")) >= 0)
+  while ((c = getopt(argc, argv, "vmrhl:s:T:")) >= 0)
     {
       switch (c)
       {
@@ -107,6 +108,9 @@ main(int argc, char **argv)
          else
            queue_push2(&solq, 1, atoi(optarg));
           break;
+        case 'T':
+         writetestcase = optarg;
+          break;
         default:
          usage(1);
           break;
@@ -231,6 +235,8 @@ main(int argc, char **argv)
          else
            {
              int pcnt = solver_solve(solv, &job);
+             if (writetestcase)
+               testcase_write(solv, writetestcase, resultflags, 0, 0);
              if (pcnt && solq.count)
                {
                  int i, taken = 0;