]> git.ipfire.org Git - pakfire.git/commitdiff
cli: Implement interactive selection of solutions
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 27 Sep 2023 15:13:51 +0000 (15:13 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 27 Sep 2023 15:13:51 +0000 (15:13 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/cli/lib/terminal.c
src/cli/lib/terminal.h
src/cli/pakfire/main.c
src/libpakfire/include/pakfire/pakfire.h
src/libpakfire/include/pakfire/transaction.h
src/libpakfire/libpakfire.sym
src/libpakfire/pakfire.c
src/libpakfire/transaction.c

index 049a5744db14a0296d3e3d9532cd5b336b7ba1ca..72702004b324e23991f38892b5ee0894f5c3875a 100644 (file)
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <pakfire/i18n.h>
 #include <pakfire/pakfire.h>
+#include <pakfire/problem.h>
+#include <pakfire/solution.h>
 
 #include "terminal.h"
 
@@ -84,3 +87,176 @@ END:
 
        return r;
 }
+
+static int cli_term_enter_number(const char* question, unsigned int* choice,
+               unsigned int min, unsigned int max) {
+       char* line = NULL;
+       size_t length = 0;
+       char* remainder = NULL;
+       int r = 1;
+
+       while (1) {
+               // Print question
+               printf("%s ", question);
+
+               // Do not wait for any input if the terminal isn't interactive
+               if (!cli_term_is_interactive())
+                       break;
+
+               // Wait for the user to enter something
+               ssize_t bytes_read = getline(&line, &length, stdin);
+               if (bytes_read < 0)
+                       goto ERROR;
+
+               // Convert input into an integer
+               unsigned long int value = strtoul(line, &remainder, 10);
+
+               // The remainder must point to newline
+               if (!remainder || *remainder != '\n')
+                       goto AGAIN;
+
+               // The value must be within bounds
+               if (value < min || value > max)
+                       goto AGAIN;
+
+               // Store choice
+               *choice = value;
+               r = 0;
+               break;
+
+AGAIN:
+               printf("%s\n", _("Invalid value"));
+       }
+
+ERROR:
+       if (line)
+               free(line);
+
+       return r;
+}
+
+static int cli_term_print_problem(struct pakfire_problem* problem, unsigned int* num_solutions) {
+       struct pakfire_solution** solutions = NULL;
+
+       // Show what the problem is
+       printf("  * %s\n", pakfire_problem_to_string(problem));
+
+       // Fetch the solutions
+       solutions = pakfire_problem_get_solutions(problem);
+
+       if (solutions) {
+               // Show a little headline
+               printf("    %s\n", _("Possible solutions:"));
+
+               // Show all possible solutions
+               for (struct pakfire_solution** s = solutions; *s; s++) {
+                       // Increment the solution counter
+                       (*num_solutions)++;
+
+                       // Show the solution
+                       printf("      [%d] %s\n", *num_solutions, pakfire_solution_to_string(*s));
+               }
+       }
+
+       // Cleanup
+       if (solutions) {
+               for (struct pakfire_solution** s = solutions; *s; s++)
+                       pakfire_solution_unref(*s);
+
+               free(solutions);
+       }
+
+       return 0;
+}
+
+static struct pakfire_solution* cli_term_find_solution(
+               struct pakfire_problem** problems, const unsigned int choice) {
+       struct pakfire_solution* selected_solution = NULL;
+       struct pakfire_solution** solutions = NULL;
+       int r;
+
+       // Count solutions
+       unsigned int i = 1;
+
+       for (struct pakfire_problem** problem = problems; *problem; problem++) {
+               // Fetch solutions
+               solutions = pakfire_problem_get_solutions(*problem);
+               if (!solutions)
+                       continue;
+
+               for (struct pakfire_solution** solution = solutions; *solution; solution++) {
+                       if (choice == i++)
+                               selected_solution = pakfire_solution_ref(*solution);
+
+                       pakfire_solution_unref(*solution);
+               }
+               free(solutions);
+
+               // If we found a solution, we can skip the rest
+               if (selected_solution)
+                       break;
+       }
+
+       return selected_solution;
+}
+
+int cli_term_pick_solution(
+               struct pakfire* pakfire, struct pakfire_transaction* transaction, void* data) {
+       struct pakfire_problem** problems = NULL;
+       struct pakfire_solution* solution = NULL;
+       unsigned int num_solutions = 0;
+       unsigned int choice = 0;
+       int r = 0;
+
+       // Fetch all problems
+       problems = pakfire_transaction_get_problems(transaction);
+       if (!problems)
+               goto ERROR;
+
+       for (;;) {
+               // Reset solutions
+               num_solutions = 0;
+
+               // Print a headline
+               printf("%s\n", _("One or more problems have occurred solving your request:"));
+
+               // Show all problems
+               for (struct pakfire_problem** p = problems; *p; p++) {
+                       r = cli_term_print_problem(*p, &num_solutions);
+                       if (r)
+                               goto ERROR;
+               }
+
+               // Let the user choose which solution they want
+               r = cli_term_enter_number(
+                       _("Please select a solution:"), &choice, 1, num_solutions);
+               if (r)
+                       goto ERROR;
+
+               // Find the selected solution
+               solution = cli_term_find_solution(problems, choice);
+               if (!solution)
+                       goto ERROR;
+
+               // Take the selected solution
+               r = pakfire_transaction_take_solution(transaction, solution);
+               if (r)
+                       goto ERROR;
+
+               // Success!
+               r = 0;
+               break;
+       }
+
+ERROR:
+       if (problems) {
+               for (struct pakfire_problem** p = problems; *p; p++)
+                       pakfire_problem_unref(*p);
+
+               free(problems);
+       }
+       if (solution)
+               pakfire_solution_unref(solution);
+
+       return r;
+}
index b805b4105fdf4222edd7d34a8791264d6ecd44be..7ff0f7134939bfe35b6ed39d950e2ad9e40b773d 100644 (file)
 #define PAKFIRE_CLI_TERMINAL_H
 
 #include <pakfire/pakfire.h>
+#include <pakfire/transaction.h>
 
 int cli_term_confirm(struct pakfire* pakfire,
        void* data, const char* message, const char* question);
 
+int cli_term_pick_solution(
+       struct pakfire* pakfire, struct pakfire_transaction* transaction, void* data);
+
 #endif /* PAKFIRE_CLI_TERMINAL_H */
index 8ecf5303603c9cc18e817417ac6ba3065a6c5d6c..80e0db344c21be0279c303cab7d0dcf4353fcce5 100644 (file)
@@ -254,6 +254,9 @@ int main(int argc, char* argv[]) {
        // Configure callbacks
        pakfire_set_confirm_callback(pakfire, cli_term_confirm, NULL);
 
+       // Configure pick solution callback
+       pakfire_set_pick_solution_callback(pakfire, cli_term_pick_solution, NULL);
+
        // Enable repositories
        for (unsigned int i = 0; i < config.num_enable_repos; i++)
                cli_set_repo_enabled(pakfire, config.enable_repos[i], 1);
index 8cf822b1c85dd62d03fbe7e5d9a5a95f13a3dd7b..975d9eba9efc8df3491ad75a4875f3a766f21c9a 100644 (file)
@@ -35,6 +35,7 @@ struct pakfire;
 #include <pakfire/parser.h>
 #include <pakfire/repo.h>
 #include <pakfire/repolist.h>
+#include <pakfire/transaction.h>
 
 enum pakfire_flags {
        PAKFIRE_FLAGS_OFFLINE                   = (1 << 0),
@@ -58,6 +59,12 @@ int pakfire_create(struct pakfire** pakfire, const char* path, const char* arch,
 struct pakfire* pakfire_ref(struct pakfire* pakfire);
 struct pakfire* pakfire_unref(struct pakfire* pakfire);
 
+// Callbacks
+typedef int (*pakfire_pick_solution_callback)
+       (struct pakfire* pakfire, struct pakfire_transaction* transaction, void* data);
+void pakfire_set_pick_solution_callback(
+       struct pakfire* pakfire, pakfire_pick_solution_callback callback, void* data);
+
 const char* pakfire_get_path(struct pakfire* pakfire);
 
 int pakfire_clean(struct pakfire* pakfire, int flags);
@@ -129,6 +136,7 @@ struct pakfire_config* pakfire_get_config(struct pakfire* pakfire);
 int pakfire_is_mountpoint(struct pakfire* pakfire, const char* path);
 
 int pakfire_confirm(struct pakfire* pakfire, const char* message, const char* question);
+int pakfire_pick_solution(struct pakfire* pakfire, struct pakfire_transaction* transaction);
 
 magic_t pakfire_get_magic(struct pakfire* pakfire);
 
index fd3f784d7272be4309acfeb54df22ebd7deb80d8..b35ea15844920ac7faeaf9089098b676943a63ce 100644 (file)
@@ -25,6 +25,7 @@ struct pakfire_transaction;
 
 #include <pakfire/pakfire.h>
 #include <pakfire/problem.h>
+#include <pakfire/solution.h>
 
 enum pakfire_transaction_flags {
        PAKFIRE_TRANSACTION_WITHOUT_RECOMMENDED = 1 << 1,
@@ -53,21 +54,12 @@ enum pakfire_job_flags {
        PAKFIRE_JOB_BEST                = 1 << 3,
 };
 
-typedef int (*pakfire_transaction_pick_solution_callback)
-       (struct pakfire_transaction* transaction, void* data);
-
 int pakfire_transaction_create(struct pakfire_transaction** transaction,
        struct pakfire* pakfire, int flags);
 
 struct pakfire_transaction* pakfire_transaction_ref(struct pakfire_transaction* transaction);
 struct pakfire_transaction* pakfire_transaction_unref(struct pakfire_transaction* transaction);
 
-// Callbacks
-void pakfire_transaction_set_status_callback(
-       struct pakfire_transaction* transaction, pakfire_status_callback callback, void* data);
-void pakfire_transaction_set_pick_solution_callback(
-       struct pakfire_transaction* transaction, pakfire_transaction_pick_solution_callback callback, void* data);
-
 int pakfire_transaction_solve(struct pakfire_transaction* transaction, int flags, char** problems);
 
 int pakfire_transaction_request(struct pakfire_transaction* transaction,
@@ -76,12 +68,16 @@ int pakfire_transaction_request_package(struct pakfire_transaction* transaction,
        const enum pakfire_job_action action, struct pakfire_package* package, int flags);
 
 struct pakfire_problem** pakfire_transaction_get_problems(struct pakfire_transaction* transaction);
+int pakfire_transaction_take_solution(
+       struct pakfire_transaction* transaction, struct pakfire_solution* solution);
 
 size_t pakfire_transaction_count(struct pakfire_transaction* transaction);
 char* pakfire_transaction_dump(struct pakfire_transaction* transaction, size_t width);
 
+#if 0
 void pakfire_transaction_set_status_callback(
        struct pakfire_transaction* transaction, pakfire_status_callback callback, void* data);
+#endif
 
 int pakfire_transaction_run(struct pakfire_transaction* transaction);
 
@@ -92,7 +88,6 @@ int pakfire_transaction_download(struct pakfire_transaction* transaction);
 #include <solv/solver.h>
 
 #include <pakfire/key.h>
-#include <pakfire/solution.h>
 
 // XXX needs removal
 enum pakfire_request_solve_flags {
@@ -101,9 +96,6 @@ enum pakfire_request_solve_flags {
 
 Solver* pakfire_transaction_get_solver(struct pakfire_transaction* transaction);
 
-int pakfire_transaction_take_solution(struct pakfire_transaction* transaction,
-       struct pakfire_solution* solution);
-
 int pakfire_transaction_compose_repo(struct pakfire_transaction* transaction,
        struct pakfire_key* key, const char* path);
 
index 5c50074f9797ab2ad32541453664a73cbaf728d2..f3573f5ce4dc416d7c34ec14e216950accf4af63 100644 (file)
@@ -33,6 +33,7 @@ global:
        pakfire_search;
        pakfire_set_confirm_callback;
        pakfire_set_log_callback;
+       pakfire_set_pick_solution_callback;
        pakfire_unref;
        pakfire_version_compare;
        pakfire_whatprovides;
@@ -281,6 +282,7 @@ global:
        pakfire_transaction_set_pick_solution_callback;
        pakfire_transaction_set_status_callback;
        pakfire_transaction_solve;
+       pakfire_transaction_take_solution;
        pakfire_transaction_unref;
 
 local:
index 5b98a69a21aed14be81753322ee2dfa69d5246e6..d8e5fa61f39a69534ef68c9bad1533edc0bde3cb 100644 (file)
@@ -107,6 +107,10 @@ struct pakfire {
                // Confirm
                pakfire_confirm_callback confirm;
                void* confirm_data;
+
+               // Pick Solution
+               pakfire_pick_solution_callback pick_solution;
+               void* pick_solution_data;
        } callbacks;
 
        // Logging
@@ -1074,6 +1078,25 @@ PAKFIRE_EXPORT struct pakfire* pakfire_unref(struct pakfire* pakfire) {
        return NULL;
 }
 
+// Callbacks
+
+PAKFIRE_EXPORT void pakfire_set_pick_solution_callback(struct pakfire* pakfire,
+               pakfire_pick_solution_callback callback, void* data) {
+       pakfire->callbacks.pick_solution = callback;
+       pakfire->callbacks.pick_solution_data = data;
+}
+
+int pakfire_pick_solution(struct pakfire* pakfire, struct pakfire_transaction* transaction) {
+       int r = 1;
+
+       // Call the callback
+       if (pakfire->callbacks.pick_solution)
+               r = pakfire->callbacks.pick_solution(
+                       pakfire, transaction, pakfire->callbacks.pick_solution_data);
+
+       return r;
+}
+
 int pakfire_has_flag(struct pakfire* pakfire, int flag) {
        return pakfire->flags & flag;
 }
index c114ea70a32dde2910d27c8d42640703e7e37d71..3f1f24ec23b6c0ba5b41fbb69bd17afa58d5814f 100644 (file)
@@ -71,10 +71,6 @@ struct pakfire_transaction {
                // Status
                pakfire_status_callback status;
                void* status_data;
-
-               // Pick Solution
-               pakfire_transaction_pick_solution_callback pick_solution;
-               void* pick_solution_data;
        } callbacks;
 };
 
@@ -377,12 +373,6 @@ PAKFIRE_EXPORT void pakfire_transaction_set_status_callback(
        transaction->callbacks.status_data = data;
 }
 
-PAKFIRE_EXPORT void pakfire_transaction_set_pick_solution_callback(
-               struct pakfire_transaction* transaction, pakfire_transaction_pick_solution_callback callback, void* data) {
-       transaction->callbacks.pick_solution = callback;
-       transaction->callbacks.pick_solution_data = data;
-}
-
 static int pakfire_transaction_get_progress(struct pakfire_transaction* transaction) {
        return transaction->progress * 100 / transaction->num;
 }
@@ -563,17 +553,6 @@ Solver* pakfire_transaction_get_solver(struct pakfire_transaction* transaction)
        return transaction->solver;
 }
 
-static int pakfire_transaction_pick_solution(struct pakfire_transaction* transaction) {
-       int r = 1;
-
-       // Call the callback
-       if (transaction->callbacks.pick_solution)
-               r = transaction->callbacks.pick_solution(
-                       transaction, transaction->callbacks.pick_solution_data);
-
-       return r;
-}
-
 PAKFIRE_EXPORT int pakfire_transaction_solve(struct pakfire_transaction* transaction,
                int flags, char** problems) {
        char* p = NULL;
@@ -642,7 +621,7 @@ RETRY:
                        }
 
                        // Ask the user to pick a solution
-                       r = pakfire_transaction_pick_solution(transaction);
+                       r = pakfire_pick_solution(transaction->pakfire, transaction);
                        switch (r) {
                                case 0:
                                        goto RETRY;
@@ -860,7 +839,7 @@ PAKFIRE_EXPORT int pakfire_transaction_request_package(struct pakfire_transactio
        return __pakfire_transaction_add(transaction, action, SOLVER_SOLVABLE, id, flags);
 }
 
-int pakfire_transaction_take_solution(struct pakfire_transaction* transaction,
+PAKFIRE_EXPORT int pakfire_transaction_take_solution(struct pakfire_transaction* transaction,
                struct pakfire_solution* solution) {
        struct pakfire_problem* problem = pakfire_solution_get_problem(solution);