diff --git a/Makefile.am b/Makefile.am index 835f2c81..b64e205d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,7 @@ xdg_app_SOURCES = \ xdg-app-builtins-repo-contents.c \ xdg-app-builtins-install.c \ xdg-app-builtins-update.c \ + xdg-app-builtins-uninstall.c \ xdg-app-builtins-list.c \ xdg-app-builtins-run.c \ xdg-app-builtins-build-init.c \ diff --git a/xdg-app-builtins-uninstall.c b/xdg-app-builtins-uninstall.c new file mode 100644 index 00000000..d1a5fe86 --- /dev/null +++ b/xdg-app-builtins-uninstall.c @@ -0,0 +1,186 @@ +#include "config.h" + +#include +#include +#include +#include + +#include "libgsystem.h" + +#include "xdg-app-builtins.h" +#include "xdg-app-utils.h" + +static GOptionEntry options[] = { + { NULL } +}; + +gboolean +xdg_app_builtin_uninstall_runtime (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + gboolean ret = FALSE; + GOptionContext *context; + gs_unref_object XdgAppDir *dir = NULL; + gs_unref_object GFile *deploy_base = NULL; + const char *runtime; + const char *arch = NULL; + const char *branch = NULL; + gs_free char *ref = NULL; + + context = g_option_context_new ("RUNTIME [ARCH [BRANCH]] - Uninstall a runtime"); + + if (!xdg_app_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error)) + goto out; + + if (argc < 2) + { + usage_error (context, "RUNTIME must be specified", error); + goto out; + } + + runtime = argv[1]; + if (argc >= 3) + arch = argv[2]; + if (argc >= 4) + branch = argv[3]; + + /* TODO: look for apps, require --force */ + + ref = g_build_filename ("runtime", runtime, arch, branch, NULL); + + deploy_base = xdg_app_dir_get_deploy_dir (dir, ref); + if (!g_file_query_exists (deploy_base, cancellable)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Nothing to uninstall"); + goto out; + } + + if (!gs_shutil_rm_rf (deploy_base, cancellable, error)) + goto out; + + if (branch) + { + gs_unref_object GFile *db; + + db = deploy_base; + deploy_base = g_file_get_parent (deploy_base); + + if (!g_file_delete (deploy_base, cancellable, NULL)) + goto done; + } + + if (arch) + { + gs_unref_object GFile *db; + + db = deploy_base; + deploy_base = g_file_get_parent (deploy_base); + + if (!g_file_delete (deploy_base, cancellable, NULL)) + goto done; + } + + done: + ret = TRUE; + + out: + if (context) + g_option_context_free (context); + return ret; +} + +gboolean +xdg_app_builtin_uninstall_app (int argc, char **argv, GCancellable *cancellable, GError **error) +{ + gboolean ret = FALSE; + GOptionContext *context; + gs_unref_object XdgAppDir *dir = NULL; + gs_unref_object GFile *deploy_base = NULL; + const char *app; + const char *arch = NULL; + const char *branch = NULL; + gs_free char *ref = NULL; + GError *temp_error = NULL; + + context = g_option_context_new ("APP [ARCH [BRANCH]] - Uninstall an application"); + + if (!xdg_app_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error)) + goto out; + + if (argc < 2) + { + usage_error (context, "APP must be specified", error); + goto out; + } + + app = argv[1]; + if (argc >= 3) + arch = argv[2]; + if (argc >= 4) + branch = argv[3]; + + ref = g_build_filename ("app", app, arch, branch, NULL); + + deploy_base = xdg_app_dir_get_deploy_dir (dir, ref); + if (!g_file_query_exists (deploy_base, cancellable)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Nothing to uninstall"); + goto out; + } + + if (!gs_shutil_rm_rf (deploy_base, cancellable, error)) + goto out; + + if (branch) + { + gs_unref_object GFile *db; + + db = deploy_base; + deploy_base = g_file_get_parent (deploy_base); + + if (!g_file_delete (deploy_base, cancellable, NULL)) + goto done; + } + + if (arch) + { + gs_unref_object GFile *db; + gs_unref_object GFileEnumerator *dir_enum; + gs_unref_object GFileInfo *child_info; + + db = deploy_base; + deploy_base = g_file_get_parent (deploy_base); + + dir_enum = g_file_enumerate_children (deploy_base, G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + cancellable, error); + if (!dir_enum) + goto out; + + while ((child_info = g_file_enumerator_next_file (dir_enum, cancellable, &temp_error))) + { + const char *arch; + + arch = g_file_info_get_name (child_info); + if (strcmp (arch, "data") == 0) + continue; + + goto done; + } + if (temp_error != NULL) + goto out; + + if (!gs_shutil_rm_rf (deploy_base, cancellable, error)) + goto out; + } + + done: + ret = TRUE; + + out: + if (temp_error != NULL) + g_propagate_error (error, temp_error); + + if (context) + g_option_context_free (context); + return ret; +} diff --git a/xdg-app-builtins.h b/xdg-app-builtins.h index 81d9d427..a2dab943 100644 --- a/xdg-app-builtins.h +++ b/xdg-app-builtins.h @@ -34,9 +34,11 @@ BUILTINPROTO(list_repos); BUILTINPROTO(repo_contents); BUILTINPROTO(install_runtime); BUILTINPROTO(update_runtime); +BUILTINPROTO(uninstall_runtime); BUILTINPROTO(list_runtimes); BUILTINPROTO(install_app); BUILTINPROTO(update_app); +BUILTINPROTO(uninstall_app); BUILTINPROTO(list_apps); BUILTINPROTO(run); BUILTINPROTO(build_init); diff --git a/xdg-app-main.c b/xdg-app-main.c index 22d48bdb..5f461267 100644 --- a/xdg-app-main.c +++ b/xdg-app-main.c @@ -25,9 +25,11 @@ static XdgAppCommand commands[] = { { "repo-contents", xdg_app_builtin_repo_contents }, { "install-runtime", xdg_app_builtin_install_runtime }, { "update-runtime", xdg_app_builtin_update_runtime }, + { "uninstall-runtime", xdg_app_builtin_uninstall_runtime }, { "list-runtimes", xdg_app_builtin_list_runtimes }, { "install-app", xdg_app_builtin_install_app }, { "update-app", xdg_app_builtin_update_app }, + { "uninstall-app", xdg_app_builtin_uninstall_app }, { "list-apps", xdg_app_builtin_list_apps }, { "run", xdg_app_builtin_run }, { "build-init", xdg_app_builtin_build_init },