From: Michael Tremer Date: Wed, 27 Sep 2023 15:13:51 +0000 (+0000) Subject: cli: Implement interactive selection of solutions X-Git-Tag: 0.9.30~1627 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fe88e9299607b33c8b96bd586133327221d29762;p=pakfire.git cli: Implement interactive selection of solutions Signed-off-by: Michael Tremer --- diff --git a/src/cli/lib/terminal.c b/src/cli/lib/terminal.c index 049a5744d..72702004b 100644 --- a/src/cli/lib/terminal.c +++ b/src/cli/lib/terminal.c @@ -22,7 +22,10 @@ #include #include +#include #include +#include +#include #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; +} diff --git a/src/cli/lib/terminal.h b/src/cli/lib/terminal.h index b805b4105..7ff0f7134 100644 --- a/src/cli/lib/terminal.h +++ b/src/cli/lib/terminal.h @@ -22,8 +22,12 @@ #define PAKFIRE_CLI_TERMINAL_H #include +#include 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 */ diff --git a/src/cli/pakfire/main.c b/src/cli/pakfire/main.c index 8ecf53036..80e0db344 100644 --- a/src/cli/pakfire/main.c +++ b/src/cli/pakfire/main.c @@ -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); diff --git a/src/libpakfire/include/pakfire/pakfire.h b/src/libpakfire/include/pakfire/pakfire.h index 8cf822b1c..975d9eba9 100644 --- a/src/libpakfire/include/pakfire/pakfire.h +++ b/src/libpakfire/include/pakfire/pakfire.h @@ -35,6 +35,7 @@ struct pakfire; #include #include #include +#include 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); diff --git a/src/libpakfire/include/pakfire/transaction.h b/src/libpakfire/include/pakfire/transaction.h index fd3f784d7..b35ea1584 100644 --- a/src/libpakfire/include/pakfire/transaction.h +++ b/src/libpakfire/include/pakfire/transaction.h @@ -25,6 +25,7 @@ struct pakfire_transaction; #include #include +#include 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 #include -#include // 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); diff --git a/src/libpakfire/libpakfire.sym b/src/libpakfire/libpakfire.sym index 5c50074f9..f3573f5ce 100644 --- a/src/libpakfire/libpakfire.sym +++ b/src/libpakfire/libpakfire.sym @@ -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: diff --git a/src/libpakfire/pakfire.c b/src/libpakfire/pakfire.c index 5b98a69a2..d8e5fa61f 100644 --- a/src/libpakfire/pakfire.c +++ b/src/libpakfire/pakfire.c @@ -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; } diff --git a/src/libpakfire/transaction.c b/src/libpakfire/transaction.c index c114ea70a..3f1f24ec2 100644 --- a/src/libpakfire/transaction.c +++ b/src/libpakfire/transaction.c @@ -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);