]> git.ipfire.org Git - pakfire.git/commitdiff
libpakfire: Allow accessing possible solutions for problems
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 20 May 2017 21:01:55 +0000 (23:01 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 20 May 2017 21:01:55 +0000 (23:01 +0200)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/_pakfire/_pakfiremodule.c
src/_pakfire/problem.c
src/_pakfire/solution.c
src/_pakfire/solution.h
src/libpakfire/include/pakfire/problem.h
src/libpakfire/include/pakfire/solution.h [new file with mode: 0644]
src/libpakfire/include/pakfire/types.h
src/libpakfire/libpakfire.sym
src/libpakfire/problem.c
src/libpakfire/solution.c [new file with mode: 0644]

index 4e48a6b1a44ec7289dc6d0a34209f41995f32bb4..f67987d1766fe90cb06f63efc9b8664c56058054 100644 (file)
@@ -248,6 +248,7 @@ libpakfire_la_SOURCES = \
        src/libpakfire/repocache.c \
        src/libpakfire/request.c \
        src/libpakfire/selector.c \
+       src/libpakfire/solution.c \
        src/libpakfire/solver.c \
        src/libpakfire/step.c \
        src/libpakfire/transaction.c \
@@ -272,6 +273,7 @@ pkginclude_HEADERS += \
        src/libpakfire/include/pakfire/repocache.h \
        src/libpakfire/include/pakfire/request.h \
        src/libpakfire/include/pakfire/selector.h \
+       src/libpakfire/include/pakfire/solution.h \
        src/libpakfire/include/pakfire/solver.h \
        src/libpakfire/include/pakfire/step.h \
        src/libpakfire/include/pakfire/transaction.h \
index 8de3f47c7b316158c8f8ec861d778c5377b02419..43d8b6f9920057777c6d876a80f75d2b04bdf85e 100644 (file)
@@ -99,10 +99,6 @@ static PyMethodDef Solvable_methods[] = {
        { NULL, NULL, 0, NULL }
 };
 
-static PyMethodDef Solution_methods[] = {
-       { NULL, NULL, 0, NULL }
-};
-
 static struct PyModuleDef moduledef = {
        .m_base = PyModuleDef_HEAD_INIT,
        .m_name = "_pakfire",
@@ -176,9 +172,9 @@ PyMODINIT_FUNC PyInit__pakfire(void) {
        PyModule_AddObject(module, "Request", (PyObject *)&RequestType);
 
        // Solution
-       SolutionType.tp_methods = Solution_methods;
        if (PyType_Ready(&SolutionType) < 0)
                return NULL;
+
        Py_INCREF(&SolutionType);
        PyModule_AddObject(module, "Solution", (PyObject *)&SolutionType);
 
index 0f00f1020c33ec2382c0881140d90e45a353691f..bd01ff9c024b3512f590a91c1e2c3a138bc0a180 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "pool.h"
 #include "problem.h"
+#include "solution.h"
 
 static ProblemObject* Problem_new_core(PyTypeObject* type, PakfireProblem problem) {
        ProblemObject* self = (ProblemObject *)type->tp_alloc(type, 0);
@@ -77,11 +78,33 @@ static PyObject* Problem_string(ProblemObject* self) {
        return PyUnicode_FromString(string);
 }
 
+static PyObject* Problem_get_solutions(ProblemObject* self) {
+       PyObject* list = PyList_New(0);
+
+       PakfireSolution solution = pakfire_problem_get_solutions(self->problem);
+       while (solution) {
+               PyObject* s = new_solution(solution);
+               PyList_Append(list, s);
+               Py_DECREF(s);
+
+               solution = pakfire_solution_next(solution);
+       }
+
+       return list;
+}
+
 static struct PyMethodDef Problem_methods[] = {
        { NULL }
 };
 
 static struct PyGetSetDef Problem_getsetters[] = {
+       {
+               "solutions",
+               (getter)Problem_get_solutions,
+               NULL,
+               NULL,
+               NULL,
+       },
        { NULL },
 };
 
index c1eac65ae02f4d8b9304b2ee66b5fae0368d2e6a..34887dbe02e50275c622e28af2e75f4d10a0d80d 100644 (file)
@@ -1,7 +1,7 @@
 /*#############################################################################
 #                                                                             #
 # Pakfire - The IPFire package management system                              #
-# Copyright (C) 2011 Pakfire development team                                 #
+# Copyright (C) 2017 Pakfire development team                                 #
 #                                                                             #
 # This program is free software: you can redistribute it and/or modify        #
 # it under the terms of the GNU General Public License as published by        #
 #############################################################################*/
 
 #include <Python.h>
+#include <assert.h>
 
-#include <solv/policy.h>
-#include <solv/solverdebug.h>
+#include <pakfire/errno.h>
+#include <pakfire/solution.h>
 
-#include "constants.h"
-#include "problem.h"
+#include "pool.h"
 #include "solution.h"
 
-PyTypeObject SolutionType = {
-       PyVarObject_HEAD_INIT(NULL, 0)
-       tp_name: "_pakfire.Solution",
-       tp_basicsize: sizeof(SolutionObject),
-       tp_flags: Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
-       tp_new: Solution_new,
-       tp_dealloc: (destructor)Solution_dealloc,
-       tp_doc: "Sat Solution objects",
-       tp_str: (reprfunc)Solution_string,
-};
-
-PyObject *Solution_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
-       SolutionObject *self;
-       ProblemObject *problem;
-       Id id;
-
-       if (!PyArg_ParseTuple(args, "Oi", &problem, &id)) {
-               /* XXX raise exception */
-               return NULL;
-       }
-
-       self = (SolutionObject *)type->tp_alloc(type, 0);
-       if (self != NULL) {
-#if 0
-               self->_solver = problem->_solver;
-               self->problem_id = problem->_id;
-#endif
-               self->id = id;
+static SolutionObject* Solution_new_core(PyTypeObject* type, PakfireSolution solution) {
+       SolutionObject* self = (SolutionObject *)type->tp_alloc(type, 0);
+       if (self) {
+               self->solution = solution;
        }
 
-       return (PyObject *)self;
+       return self;
 }
 
-PyObject *Solution_dealloc(SolutionObject *self) {
-       Py_TYPE(self)->tp_free((PyObject *)self);
+PyObject* new_solution(PakfireSolution solution) {
+       SolutionObject* s = Solution_new_core(&SolutionType, solution);
 
-       Py_RETURN_NONE;
+       return (PyObject*)s;
 }
 
-PyObject *Solution_string(SolutionObject *self) {
-       Pool *pool = self->_solver->pool;
-       Solver *solver = self->_solver;
-       char str[STRING_SIZE];
-
-       Solvable *s, *sd;
-       Id p, rp;
-       Id how, what, select;
-
-       Id element = 0;
-       while ((element = solver_next_solutionelement(solver, self->problem_id, self->id, element, &p, &rp)) != 0) {
-               if (p == SOLVER_SOLUTION_JOB) {
-                       how = solver->job.elements[rp - 1];
-                       what = solver->job.elements[rp];
-                       select = how & SOLVER_SELECTMASK;
-
-                       switch (how & SOLVER_JOBMASK) {
-                               case SOLVER_INSTALL:
-                                       if (select == SOLVER_SOLVABLE && pool->installed && pool->solvables[what].repo == pool->installed)
-                                               snprintf(str, STRING_SIZE - 1, _("do not keep %s installed"),
-                                                       pool_solvid2str(pool, what));
-                                       else if (select == SOLVER_SOLVABLE_PROVIDES)
-                                               snprintf(str, STRING_SIZE - 1, _("do not install a solvable %s"),
-                                                       solver_select2str(pool, select, what));
-                                       else
-                                               snprintf(str, STRING_SIZE - 1, _("do not install %s"),
-                                                       solver_select2str(pool, select, what));
-                                       break;
-
-                               case SOLVER_ERASE:
-                                       if (select == SOLVER_SOLVABLE && !(pool->installed && pool->solvables[what].repo == pool->installed))
-                                               snprintf(str, STRING_SIZE - 1, _("do not forbid installation of %s"),
-                                                       pool_solvid2str(pool, what));
-                                       else if (select == SOLVER_SOLVABLE_PROVIDES)
-                                               snprintf(str, STRING_SIZE - 1, _("do not deinstall all solvables %s"),
-                                                       solver_select2str(pool, select, what));
-                                       else
-                                               snprintf(str, STRING_SIZE - 1, _("do not deinstall %s"),
-                                                       solver_select2str(pool, select, what));
-                                       break;
-
-                               case SOLVER_UPDATE:
-                                       snprintf(str, STRING_SIZE - 1, _("do not install most recent version of %s"),
-                                               solver_select2str(pool, select, what));
-                                       break;
-
-                               case SOLVER_LOCK:
-                                       snprintf(str, STRING_SIZE - 1, _("do not lock %s"),
-                                               solver_select2str(pool, select, what));
-                                       break;
+static PyObject* Solution_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
+       SolutionObject* self = Solution_new_core(type, NULL);
 
-                               default:
-                                       snprintf(str, STRING_SIZE - 1, _("do something different"));
-                                       break;
-                       }
-
-               } else if (p == SOLVER_SOLUTION_INFARCH) {
-                       s = pool->solvables + rp;
-                       if (pool->installed && s->repo == pool->installed)
-                               snprintf(str, STRING_SIZE - 1, _("keep %s despite the inferior architecture"),
-                                       pool_solvable2str(pool, s));
-                       else
-                               snprintf(str, STRING_SIZE - 1, _("install %s despite the inferior architecture"),
-                                       pool_solvable2str(pool, s));
-
-               } else if (p == SOLVER_SOLUTION_DISTUPGRADE) {
-                       s = pool->solvables + rp;
-                       if (pool->installed && s->repo == pool->installed)
-                               snprintf(str, STRING_SIZE - 1, _("keep obsolete %s"),
-                                       pool_solvable2str(pool, s));
-                       else
-                               snprintf(str, STRING_SIZE - 1, _("install %s from excluded repository"),
-                                       pool_solvable2str(pool, s));
-
-               } else {
-                       s = pool->solvables + p;
-                       sd = rp ? pool->solvables + rp : 0;
-
-                       if (sd) {
-                               int illegal = policy_is_illegal(solver, s, sd, 0);
+       return (PyObject *)self;
+}
 
-                               // XXX multiple if clauses can match here
-                               if ((illegal & POLICY_ILLEGAL_DOWNGRADE) != 0)
-                                       snprintf(str, STRING_SIZE - 1, _("allow downgrade of %s to %s"),
-                                               pool_solvable2str(pool, s), pool_solvable2str(pool, sd));
+static void Solution_dealloc(SolutionObject* self) {
+       if (self->solution)
+               pakfire_solution_free(self->solution);
 
-                               if ((illegal & POLICY_ILLEGAL_ARCHCHANGE) != 0)
-                                       snprintf(str, STRING_SIZE - 1, _("allow architecture change of %s to %s"),
-                                               pool_solvable2str(pool, s), pool_solvable2str(pool, sd));
+       Py_TYPE(self)->tp_free((PyObject *)self);
+}
 
-                               if ((illegal & POLICY_ILLEGAL_VENDORCHANGE) != 0) {
-                                       if (sd->vendor)
-                                               snprintf(str, STRING_SIZE - 1, _("allow vendor change from '%s' (%s) to '%s' (%s)"),
-                                                       pool_id2str(pool, s->vendor), pool_solvable2str(pool, s),
-                                                       pool_id2str(pool, sd->vendor), pool_solvable2str(pool, sd));
-                                       else
-                                               snprintf(str, STRING_SIZE - 1, _("allow vendor change from '%s' (%s) to no vendor (%s)"),
-                                                       pool_id2str(pool, s->vendor), pool_solvable2str(pool, s),
-                                                       pool_solvable2str(pool, sd));
-                               }
+static int Solution_init(SolutionObject* self, PyObject* args, PyObject* kwds) {
+       return 0;
+}
 
-                               if (!illegal)
-                                       snprintf(str, STRING_SIZE - 1, _("allow replacement of %s with %s"),
-                                               pool_solvable2str(pool, s), pool_solvable2str(pool, sd));
-                       }
-               }
-       }
+static PyObject* Solution_string(SolutionObject* self) {
+       const char* string = pakfire_solution_to_string(self->solution);
 
-       return Py_BuildValue("s", &str);
+       return PyUnicode_FromString(string);
 }
+
+PyTypeObject SolutionType = {
+       PyVarObject_HEAD_INIT(NULL, 0)
+       tp_name:            "_pakfire.Solution",
+       tp_basicsize:       sizeof(SolutionObject),
+       tp_flags:           Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
+       tp_new:             Solution_new,
+       tp_dealloc:         (destructor)Solution_dealloc,
+       tp_init:            (initproc)Solution_init,
+       tp_doc:             "Solution object",
+       tp_str:             (reprfunc)Solution_string,
+};
index b49e1518d6685a0a3efe2e0d24de227f0b3f8f1f..44d6fcda92927dcfdadba9252e368e5ad6691105 100644 (file)
@@ -1,7 +1,7 @@
 /*#############################################################################
 #                                                                             #
 # Pakfire - The IPFire package management system                              #
-# Copyright (C) 2011 Pakfire development team                                 #
+# Copyright (C) 2017 Pakfire development team                                 #
 #                                                                             #
 # This program is free software: you can redistribute it and/or modify        #
 # it under the terms of the GNU General Public License as published by        #
 
 #include <Python.h>
 
-#include "solver.h"
+#include <pakfire/solution.h>
 
-// Sat Solution object
 typedef struct {
-    PyObject_HEAD
-    Solver *_solver;
-    Id problem_id;
-    Id id;
+       PyObject_HEAD
+       PakfireSolution solution;
 } SolutionObject;
 
-extern PyObject *Solution_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
-extern PyObject *Solution_dealloc(SolutionObject *self);
-
-extern PyObject *Solution_string(SolutionObject *self);
-
 extern PyTypeObject SolutionType;
 
+PyObject* new_solution(PakfireSolution solution);
+
 #endif /* PYTHON_PAKFIRE_SOLUTION_H */
index 07fb6785a7a1415ef77cbf0a8d449ce79e2457b2..dd78729a54acf90d300650f8b19a3c0075964066 100644 (file)
@@ -35,6 +35,8 @@ void pakfire_problem_append(PakfireProblem problem, PakfireProblem new_problem);
 
 const char* pakfire_problem_to_string(PakfireProblem problem);
 
+PakfireSolution pakfire_problem_get_solutions(PakfireProblem problem);
+
 #ifdef PAKFIRE_PRIVATE
 
 struct _PakfireProblem {
diff --git a/src/libpakfire/include/pakfire/solution.h b/src/libpakfire/include/pakfire/solution.h
new file mode 100644 (file)
index 0000000..ce5ee0e
--- /dev/null
@@ -0,0 +1,48 @@
+/*#############################################################################
+#                                                                             #
+# Pakfire - The IPFire package management system                              #
+# Copyright (C) 2017 Pakfire development team                                 #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#ifndef PAKFIRE_SOLUTION_H
+#define PAKFIRE_SOLUTION_H
+
+#include <pakfire/problem.h>
+
+PakfireSolution pakfire_solution_create(PakfireProblem problem, Id id);
+PakfireSolution pakfire_solution_ref(PakfireSolution solution);
+void pakfire_solution_free(PakfireSolution solution);
+
+PakfireSolution pakfire_solution_next(PakfireSolution solution);
+void pakfire_solution_append(PakfireSolution solution, PakfireSolution new_solution);
+
+char* pakfire_solution_to_string(PakfireSolution solution);
+
+#ifdef PAKFIRE_PRIVATE
+
+struct _PakfireSolution {
+       PakfireProblem problem;
+       Id id;
+       char** elements;
+
+       PakfireSolution next;
+       int nrefs;
+};
+
+#endif
+
+#endif /* PAKFIRE_SOLUTION_H */
index 51d764212b32efef32573f881d137883994804d3..26c01357f0653f8af044f13e56e11534b3d9f810 100644 (file)
@@ -36,6 +36,7 @@ typedef struct _PakfireRepo* PakfireRepo;
 typedef struct _PakfireRepoCache* PakfireRepoCache;
 typedef struct _PakfireRequest* PakfireRequest;
 typedef struct _PakfireSelector* PakfireSelector;
+typedef struct _PakfireSolution* PakfireSolution;
 typedef struct _PakfireStep* PakfireStep;
 typedef struct _PakfireTransaction* PakfireTransaction;
 
index 7e6e776c6934dc20135260e4577bc0a56053f74c..15f755cd4f814ba4984691d628606d2d1a8bb8bf 100644 (file)
@@ -163,6 +163,7 @@ global:
        pakfire_problem_append;
        pakfire_problem_create;
        pakfire_problem_free;
+       pakfire_problem_get_solutions;
        pakfire_problem_next;
        pakfire_problem_ref;
        pakfire_problem_to_string;
@@ -243,6 +244,13 @@ global:
        pakfire_selector_providers;
        pakfire_selector_set;
 
+       # solution
+       pakfire_solution_create;
+       pakfire_solution_free;
+       pakfire_solution_next;
+       pakfire_solution_ref;
+       pakfire_solution_to_string;
+
        # step
        pakfire_step_create;
        pakfire_step_free;
index 7f2b1a76a2dced5c84d93e1908fc9d4287fc9f23..be1d0557bd35043afed8787d7eb0f65085fac96a 100644 (file)
@@ -22,6 +22,7 @@
 #include <pakfire/i18n.h>
 #include <pakfire/problem.h>
 #include <pakfire/request.h>
+#include <pakfire/solution.h>
 #include <pakfire/util.h>
 
 static char* to_string(PakfireProblem problem) {
@@ -242,3 +243,19 @@ void pakfire_problem_append(PakfireProblem problem, PakfireProblem new_problem)
 const char* pakfire_problem_to_string(PakfireProblem problem) {
        return problem->string;
 }
+
+PakfireSolution pakfire_problem_get_solutions(PakfireProblem problem) {
+       PakfireSolution ret = NULL;
+
+       Id solution = 0;
+       while ((solution = solver_next_solution(problem->request->solver, problem->id, solution)) != 0) {
+               PakfireSolution s = pakfire_solution_create(problem, solution);
+
+               if (ret)
+                       pakfire_solution_append(ret, s);
+               else
+                       ret = s;
+       }
+
+       return ret;
+}
diff --git a/src/libpakfire/solution.c b/src/libpakfire/solution.c
new file mode 100644 (file)
index 0000000..fbbc414
--- /dev/null
@@ -0,0 +1,197 @@
+/*#############################################################################
+#                                                                             #
+# Pakfire - The IPFire package management system                              #
+# Copyright (C) 2017 Pakfire development team                                 #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#include <assert.h>
+
+#include <solv/policy.h>
+
+#include <pakfire/constants.h>
+#include <pakfire/i18n.h>
+#include <pakfire/problem.h>
+#include <pakfire/solution.h>
+#include <pakfire/util.h>
+
+static void import_elements(PakfireSolution solution) {
+       Solver* solver = solution->problem->request->solver;
+       Pool* pool = solver->pool;
+
+       // Reserve memory
+       unsigned int num = solver_solutionelement_count(solver, solution->problem->id, solution->id);
+       char** elements = solution->elements = pakfire_calloc(num + 1, sizeof(*elements));
+
+       Id p;
+       Id rp;
+       Id element = 0;
+       while ((element = solver_next_solutionelement(solver, solution->problem->id, solution->id, element, &p, &rp)) != 0) {
+               char line[STRING_SIZE];
+
+               if (p == SOLVER_SOLUTION_JOB || p == SOLVER_SOLUTION_POOLJOB) {
+                       if (p == SOLVER_SOLUTION_JOB)
+                               rp += solver->pooljobcnt;
+
+                       Id how  = solver->job.elements[rp-1];
+                       Id what = solver->job.elements[rp];
+
+                       // XXX pool_job2str must be localised
+                       snprintf(line, STRING_SIZE - 1, _("do not ask to %s"),
+                               pool_job2str(pool, how, what, 0));
+
+               } else if (p == SOLVER_SOLUTION_INFARCH) {
+                       Solvable* s = pool->solvables + rp;
+
+                       if (pool->installed && s->repo == pool->installed)
+                               snprintf(line, STRING_SIZE - 1, _("keep %s despite the inferior architecture"),
+                                       pool_solvable2str(pool, s));
+                       else
+                               snprintf(line, STRING_SIZE - 1, _("install %s despite the inferior architecture"),
+                                       pool_solvable2str(pool, s));
+
+               } else if (p == SOLVER_SOLUTION_DISTUPGRADE) {
+                       Solvable* s = pool->solvables + rp;
+
+                       if (pool->installed && s->repo == pool->installed)
+                               snprintf(line, STRING_SIZE - 1, _("keep obsolete %s"),
+                                       pool_solvable2str(pool, s));
+                       else
+                               snprintf(line, STRING_SIZE - 1, _("install %s"),
+                                       pool_solvable2str(pool, s));
+
+               } else if (p == SOLVER_SOLUTION_BEST) {
+                       Solvable* s = pool->solvables + rp;
+
+                       if (pool->installed && s->repo == pool->installed)
+                               snprintf(line, STRING_SIZE - 1, _("keep old %s"),
+                                       pool_solvable2str(pool, s));
+                       else
+                               snprintf(line, STRING_SIZE - 1, _("install %s despite the old version"),
+                                       pool_solvable2str(pool, s));
+
+               } else if (p > 0 && rp == 0)
+                       snprintf(line, STRING_SIZE - 1, _("allow deinstallation of %s"),
+                               pool_solvid2str(pool, p));
+
+               else if (p > 0 && rp > 0)
+                       snprintf(line, STRING_SIZE - 1, _("allow replacement of %s with %s"),
+                               pool_solvid2str(pool, p), pool_solvid2str(pool, rp));
+
+               else
+                       snprintf(line, STRING_SIZE - 1, _("bad solution element"));
+
+               // Save line in elements array
+               *elements++ = pakfire_strdup(line);
+       }
+
+       // Terminate array
+       *elements = NULL;
+}
+
+PakfireSolution pakfire_solution_create(PakfireProblem problem, Id id) {
+       PakfireSolution solution = pakfire_calloc(1, sizeof(*solution));
+
+       solution->problem = pakfire_problem_ref(problem);
+       solution->id = id;
+
+       // Initialise reference counter
+       solution->nrefs = 1;
+       solution->next = NULL;
+
+       // Extract information from solver
+       import_elements(solution);
+
+       return solution;
+}
+
+PakfireSolution pakfire_solution_ref(PakfireSolution solution) {
+       solution->nrefs++;
+
+       return solution;
+}
+
+void pakfire_solution_free(PakfireSolution solution) {
+       if (--solution->nrefs > 0)
+               return;
+
+       if (solution->next)
+               pakfire_solution_free(solution->next);
+
+       pakfire_problem_free(solution->problem);
+
+       if (solution->elements)
+               while (*solution->elements)
+                       pakfire_free(*solution->elements++);
+
+       pakfire_free(solution);
+}
+
+PakfireSolution pakfire_solution_next(PakfireSolution solution) {
+       return solution->next;
+}
+
+void pakfire_solution_append(PakfireSolution solution, PakfireSolution new_solution) {
+       PakfireSolution next;
+
+       // Go to last problem in list
+       while ((next = pakfire_solution_next(solution)) != NULL) {
+               solution = next;
+       }
+
+       solution->next = pakfire_solution_ref(new_solution);
+}
+
+static size_t count_elements_length(PakfireSolution solution) {
+       size_t length = 0;
+
+       char** elements = solution->elements;
+       while (*elements) {
+               length += strlen(*elements++) + 1;
+       }
+
+       return length;
+}
+
+char* pakfire_solution_to_string(PakfireSolution solution) {
+       // Determine length of output string
+       size_t length = count_elements_length(solution);
+
+       // Allocate string
+       char s[length];
+       char* p = s;
+
+       char** elements = solution->elements;
+       while (*elements) {
+               // Copy line
+               char* e = *elements++;
+               while (*e)
+                       *p++ = *e++;
+
+               // Add newline
+               *p++ = '\n';
+       }
+
+       // Terminate string
+       if (p > s)
+               *(p-1) = '\0';
+
+       // Make sure that we wrote the string exactly to
+       // the last character
+       assert((s + length) == p);
+
+       return pakfire_strdup(s);
+}