From ee0ffbcb352913efa21dfad6062b8d9d7edf4001 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 17 Oct 2016 17:27:16 +0200 Subject: [PATCH] uninstall: Support multiple REFs --- app/flatpak-builtins-uninstall.c | 162 ++++++++++++++++--------------- doc/flatpak-uninstall.xml | 20 ++-- 2 files changed, 98 insertions(+), 84 deletions(-) diff --git a/app/flatpak-builtins-uninstall.c b/app/flatpak-builtins-uninstall.c index cf01305d..2531411c 100644 --- a/app/flatpak-builtins-uninstall.c +++ b/app/flatpak-builtins-uninstall.c @@ -49,79 +49,105 @@ static GOptionEntry options[] = { { NULL } }; +static gboolean +looks_like_branch (const char *branch) +{ + /* In particular, / is not a valid branch char, so + this lets us distinguish full or partial refs as + non-branches. */ + if (!flatpak_is_valid_branch (branch, NULL)) + return FALSE; + + /* Dots are allowed in branches, but not really used much, while + they are required for app ids, so thats a good check to + distinguish the two */ + if (strchr (branch, '.') != NULL) + return FALSE; + + return TRUE; +} + gboolean flatpak_builtin_uninstall (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(FlatpakDir) dir = NULL; - const char *pref = NULL; + char **prefs = NULL; + int i, j, n_prefs; const char *default_branch = NULL; g_autofree char *ref = NULL; FlatpakHelperUninstallFlags flags = 0; g_autoptr(GPtrArray) related = NULL; FlatpakKinds kinds; FlatpakKinds kind; - g_autofree char *id = NULL; - g_autofree char *arch = NULL; - g_autofree char *branch = NULL; - int i; + g_autoptr(GHashTable) uninstall_refs_hash = NULL; + g_autoptr(GPtrArray) uninstall_refs = NULL; - context = g_option_context_new (_("NAME [BRANCH] - Uninstall an application")); + context = g_option_context_new (_("REF... - Uninstall an application")); g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); if (!flatpak_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error)) return FALSE; if (argc < 2) - return usage_error (context, _("NAME must be specified"), error); + return usage_error (context, _("Must specify at least one REF"), error); - if (argc > 3) - return usage_error (context, _("Too many arguments"), error); + prefs = &argv[1]; + n_prefs = argc - 1; - pref = argv[1]; - if (argc > 2) - default_branch = argv[2]; + /* Backwards compat for old "REPOSITORY NAME [BRANCH]" argument version */ + if (argc == 3 && looks_like_branch (argv[2])) + { + default_branch = argv[2]; + n_prefs = 1; + } kinds = flatpak_kinds_from_bools (opt_app, opt_runtime); + uninstall_refs = g_ptr_array_new_with_free_func (g_free); + uninstall_refs_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - if (!flatpak_split_partial_ref_arg (pref, kinds, opt_arch, default_branch, - &kinds, &id, &arch, &branch, error)) - return FALSE; - - ref = flatpak_dir_find_installed_ref (dir, id, branch, arch, - kinds, &kind, error); - if (ref == NULL) - return FALSE; - - /* TODO: when removing runtimes, look for apps that use it, require --force */ - - if (opt_keep_ref) - flags |= FLATPAK_HELPER_UNINSTALL_FLAGS_KEEP_REF; - if (opt_force_remove) - flags |= FLATPAK_HELPER_UNINSTALL_FLAGS_FORCE_REMOVE; - - if (!opt_no_related) + for (j = 0; j < n_prefs; j++) { + const char *pref = NULL; + FlatpakKinds matched_kinds; + g_autofree char *id = NULL; + g_autofree char *arch = NULL; + g_autofree char *branch = NULL; g_autoptr(GError) local_error = NULL; g_autofree char *origin = NULL; + pref = prefs[j]; + + if (!flatpak_split_partial_ref_arg (pref, kinds, opt_arch, default_branch, + &matched_kinds, &id, &arch, &branch, error)) + return FALSE; + + ref = flatpak_dir_find_installed_ref (dir, id, branch, arch, + kinds, &kind, error); + if (ref == NULL) + return FALSE; + + if (g_hash_table_insert (uninstall_refs_hash, g_strdup (ref), NULL)) + g_ptr_array_add (uninstall_refs, g_strdup (ref)); + + /* TODO: when removing runtimes, look for apps that use it, require --force */ + + if (opt_no_related) + continue; + origin = flatpak_dir_get_origin (dir, ref, NULL, NULL); - if (origin) + if (origin == NULL) + continue; + + related = flatpak_dir_find_local_related (dir, ref, origin, + NULL, &local_error); + if (related == NULL) { - related = flatpak_dir_find_local_related (dir, ref, origin, - NULL, &local_error); - if (related == NULL) - g_printerr (_("Warning: Problem looking for related refs: %s\n"), - local_error->message); + g_printerr (_("Warning: Problem looking for related refs: %s\n"), + local_error->message); + continue; } - } - if (!flatpak_dir_uninstall (dir, ref, flags, - cancellable, error)) - return FALSE; - - if (related != NULL) - { for (i = 0; i < related->len; i++) { FlatpakRelated *rel = g_ptr_array_index (related, i); @@ -132,15 +158,26 @@ flatpak_builtin_uninstall (int argc, char **argv, GCancellable *cancellable, GEr continue; parts = g_strsplit (rel->ref, "/", 0); - g_print (_("Uninstalling related: %s\n"), parts[1]); - if (!flatpak_dir_uninstall (dir, rel->ref, flags, - cancellable, &local_error)) - g_printerr (_("Warning: Failed to uninstall related ref: %s\n"), - rel->ref); + if (g_hash_table_insert (uninstall_refs_hash, g_strdup (rel->ref), NULL)) + g_ptr_array_add (uninstall_refs, g_strdup (rel->ref)); } } + if (opt_keep_ref) + flags |= FLATPAK_HELPER_UNINSTALL_FLAGS_KEEP_REF; + if (opt_force_remove) + flags |= FLATPAK_HELPER_UNINSTALL_FLAGS_FORCE_REMOVE; + + for (i = 0; i < uninstall_refs->len; i++) + { + const char *ref = (char *)g_ptr_array_index (uninstall_refs, i); + g_print ("uninstalling %s\n", ref); + if (!flatpak_dir_uninstall (dir, ref, flags, + cancellable, error)) + return FALSE; + } + return TRUE; } @@ -152,7 +189,6 @@ flatpak_complete_uninstall (FlatpakCompletion *completion) g_autoptr(GError) error = NULL; g_auto(GStrv) refs = NULL; FlatpakKinds kinds; - int i; context = g_option_context_new (""); if (!flatpak_option_context_parse (context, options, &completion->argc, &completion->argv, 0, &dir, NULL, NULL)) @@ -163,37 +199,11 @@ flatpak_complete_uninstall (FlatpakCompletion *completion) switch (completion->argc) { case 0: - case 1: /* NAME */ + default: /* REF */ flatpak_complete_options (completion, global_entries); flatpak_complete_options (completion, options); flatpak_complete_options (completion, user_entries); - - refs = flatpak_dir_find_installed_refs (dir, NULL, NULL, opt_arch, - kinds, &error); - if (refs == NULL) - flatpak_completion_debug ("find installed refs error: %s", error->message); - for (i = 0; refs != NULL && refs[i] != NULL; i++) - { - g_auto(GStrv) parts = flatpak_decompose_ref (refs[i], NULL); - if (parts) - flatpak_complete_word (completion, "%s ", parts[1]); - } - break; - - case 2: /* Branch */ - refs = flatpak_dir_find_installed_refs (dir, completion->argv[1], NULL, opt_arch, - kinds, &error); - if (refs == NULL) - flatpak_completion_debug ("find installed refs error: %s", error->message); - for (i = 0; refs != NULL && refs[i] != NULL; i++) - { - g_auto(GStrv) parts = flatpak_decompose_ref (refs[i], NULL); - if (parts) - flatpak_complete_word (completion, "%s ", parts[3]); - } - break; - - default: + flatpak_complete_partial_ref (completion, kinds, opt_arch, dir, NULL); break; } diff --git a/doc/flatpak-uninstall.xml b/doc/flatpak-uninstall.xml index 6d715549..e3f4c17e 100644 --- a/doc/flatpak-uninstall.xml +++ b/doc/flatpak-uninstall.xml @@ -32,8 +32,7 @@ flatpak uninstall OPTION - NAME - BRANCH + REF @@ -41,12 +40,17 @@ Description - Uninstalls an application or runtime. The NAME, - ARCH and BRANCH - arguments must identify an installed application. - If BRANCH is not specified, it defaults - to whatever is installed, unless there are multiple versions, then you - have to specify a branch. + Uninstalls an application or runtime. REF is a reference to the + application or runtime to install. If no REF is given, everything + is updated. + + + Each REF arguments is a full or partial indentifier in the + flatpak ref format, which looks like "(app|runtime)/ID/ARCH/BRANCH". All elements + except ID are optional and can be left out, including the slashes, + so most of the time you need only specify ID. Any part left out will be matched + against what is installed, and if there are multiple matches an error message + will list the alternatives. By default this looks for both installed apps and runtime