diff --git a/app/flatpak-builtins-info.c b/app/flatpak-builtins-info.c index 3589a6c7..525227c7 100644 --- a/app/flatpak-builtins-info.c +++ b/app/flatpak-builtins-info.c @@ -165,3 +165,102 @@ flatpak_builtin_info (int argc, char **argv, GCancellable *cancellable, GError * return TRUE; } + +gboolean +flatpak_complete_info (FlatpakCompletion *completion) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(FlatpakDir) user_dir = NULL; + g_autoptr(FlatpakDir) system_dir = NULL; + g_autoptr(GError) error = NULL; + g_auto(GStrv) refs = NULL; + int i; + + context = g_option_context_new (""); + if (!flatpak_option_context_parse (context, options, &completion->argc, &completion->argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, NULL, NULL)) + return FALSE; + + if (!opt_app && !opt_runtime) + opt_app = opt_runtime = TRUE; + + if (!opt_user && !opt_system) + opt_user = opt_system = TRUE; + + if (opt_user) + user_dir = flatpak_dir_get_user (); + + if (opt_system) + system_dir = flatpak_dir_get_system (); + + switch (completion->argc) + { + case 0: + case 1: /* NAME */ + flatpak_complete_options (completion, global_entries); + flatpak_complete_options (completion, options); + + if (user_dir) + { + g_auto(GStrv) refs = flatpak_dir_find_installed_refs (user_dir, NULL, NULL, opt_arch, + opt_app, opt_runtime, &error); + if (refs == NULL) + flatpak_completion_debug ("find local 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]); + } + } + + if (system_dir) + { + g_auto(GStrv) refs = flatpak_dir_find_installed_refs (system_dir, NULL, NULL, opt_arch, + opt_app, opt_runtime, &error); + if (refs == NULL) + flatpak_completion_debug ("find local 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 */ + if (user_dir) + { + g_auto(GStrv) refs = flatpak_dir_find_installed_refs (user_dir, completion->argv[1], NULL, opt_arch, + opt_app, opt_runtime, &error); + if (refs == NULL) + flatpak_completion_debug ("find remote 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]); + } + } + if (user_dir) + { + g_auto(GStrv) refs = flatpak_dir_find_installed_refs (user_dir, completion->argv[1], NULL, opt_arch, + opt_app, opt_runtime, &error); + if (refs == NULL) + flatpak_completion_debug ("find remote 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: + break; + } + + return TRUE; +} diff --git a/app/flatpak-builtins-install.c b/app/flatpak-builtins-install.c index 0b50bf00..5b856985 100644 --- a/app/flatpak-builtins-install.c +++ b/app/flatpak-builtins-install.c @@ -52,7 +52,7 @@ static GOptionEntry options[] = { { "app", 0, 0, G_OPTION_ARG_NONE, &opt_app, "Look for app with the specified name", }, { "bundle", 0, 0, G_OPTION_ARG_NONE, &opt_bundle, "Install from local bundle file", }, { "gpg-file", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_gpg_file, "Check bundle signatures with GPG key from FILE (- for stdin)", "FILE" }, - { "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths, "Only install this subpath", "path" }, + { "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths, "Only install this subpath", "PATH" }, { NULL } }; @@ -252,3 +252,76 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro return TRUE; } + +gboolean +flatpak_complete_install (FlatpakCompletion *completion) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(FlatpakDir) dir = NULL; + g_autoptr(GError) error = NULL; + g_auto(GStrv) refs = NULL; + int i; + + context = g_option_context_new (""); + if (!flatpak_option_context_parse (context, options, &completion->argc, &completion->argv, 0, &dir, NULL, NULL)) + return FALSE; + + if (!opt_app && !opt_runtime) + opt_app = opt_runtime = TRUE; + + flatpak_completion_debug ("install argc %d", completion->argc); + + switch (completion->argc) + { + case 0: + case 1: /* REMOTE */ + flatpak_complete_options (completion, global_entries); + flatpak_complete_options (completion, options); + flatpak_complete_options (completion, user_entries); + + { + g_auto(GStrv) remotes = flatpak_dir_list_remotes (dir, NULL, NULL); + if (remotes == NULL) + return FALSE; + for (i = 0; remotes[i] != NULL; i++) + flatpak_complete_word (completion, "%s ", remotes[i]); + } + + break; + + case 2: /* Name */ + refs = flatpak_dir_find_remote_refs (dir, completion->argv[1], NULL, NULL, + opt_arch, opt_app, opt_runtime, + NULL, &error); + if (refs == NULL) + flatpak_completion_debug ("find remote 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 3: /* Branch */ + refs = flatpak_dir_find_remote_refs (dir, completion->argv[1], completion->argv[2], NULL, + opt_arch, opt_app, opt_runtime, + NULL, &error); + if (refs == NULL) + flatpak_completion_debug ("find remote 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: + break; + } + + return TRUE; +} diff --git a/app/flatpak-builtins-list.c b/app/flatpak-builtins-list.c index 34e7e570..2922f46d 100644 --- a/app/flatpak-builtins-list.c +++ b/app/flatpak-builtins-list.c @@ -262,3 +262,11 @@ flatpak_builtin_list (int argc, char **argv, GCancellable *cancellable, GError * return TRUE; } + +gboolean +flatpak_complete_list (FlatpakCompletion *completion) +{ + flatpak_complete_options (completion, global_entries); + flatpak_complete_options (completion, options); + return TRUE; +} diff --git a/app/flatpak-builtins-run.c b/app/flatpak-builtins-run.c index c00b5933..de572e2b 100644 --- a/app/flatpak-builtins-run.c +++ b/app/flatpak-builtins-run.c @@ -135,3 +135,65 @@ flatpak_builtin_run (int argc, char **argv, GCancellable *cancellable, GError ** /* Not actually reached... */ return TRUE; } + +gboolean +flatpak_complete_run (FlatpakCompletion *completion) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(FlatpakDir) user_dir = NULL; + g_autoptr(FlatpakDir) system_dir = NULL; + g_autoptr(GError) error = NULL; + int i; + g_autoptr(FlatpakContext) arg_context = NULL; + + context = g_option_context_new (""); + + arg_context = flatpak_context_new (); + g_option_context_add_group (context, flatpak_context_get_options (arg_context)); + + if (!flatpak_option_context_parse (context, options, &completion->argc, &completion->argv, + FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, NULL, NULL)) + return FALSE; + + switch (completion->argc) + { + case 0: + case 1: /* NAME */ + flatpak_complete_options (completion, global_entries); + flatpak_complete_options (completion, options); + flatpak_context_complete (arg_context, completion); + + user_dir = flatpak_dir_get_user (); + { + g_auto(GStrv) refs = flatpak_dir_find_installed_refs (user_dir, NULL, NULL, opt_arch, + TRUE, FALSE, &error); + if (refs == NULL) + flatpak_completion_debug ("find local 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]); + } + } + + system_dir = flatpak_dir_get_system (); + { + g_auto(GStrv) refs = flatpak_dir_find_installed_refs (system_dir, NULL, NULL, opt_arch, + TRUE, FALSE, &error); + if (refs == NULL) + flatpak_completion_debug ("find local 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; + + } + + return TRUE; +} diff --git a/app/flatpak-builtins-uninstall.c b/app/flatpak-builtins-uninstall.c index 1091e201..071a65d8 100644 --- a/app/flatpak-builtins-uninstall.c +++ b/app/flatpak-builtins-uninstall.c @@ -57,7 +57,7 @@ flatpak_builtin_uninstall (int argc, char **argv, GCancellable *cancellable, GEr gboolean is_app; FlatpakHelperUninstallFlags flags = 0; - context = g_option_context_new ("APP [BRANCH] - Uninstall an application"); + context = g_option_context_new ("NAME [BRANCH] - Uninstall an application"); if (!flatpak_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error)) return FALSE; @@ -94,3 +94,59 @@ flatpak_builtin_uninstall (int argc, char **argv, GCancellable *cancellable, GEr return TRUE; } + +gboolean +flatpak_complete_uninstall (FlatpakCompletion *completion) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(FlatpakDir) dir = NULL; + g_autoptr(GError) error = NULL; + g_auto(GStrv) refs = NULL; + int i; + + context = g_option_context_new (""); + if (!flatpak_option_context_parse (context, options, &completion->argc, &completion->argv, 0, &dir, NULL, NULL)) + return FALSE; + + if (!opt_app && !opt_runtime) + opt_app = opt_runtime = TRUE; + + switch (completion->argc) + { + case 0: + case 1: /* NAME */ + 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, + opt_app, opt_runtime, &error); + if (refs == NULL) + flatpak_completion_debug ("find local 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, + opt_app, opt_runtime, &error); + if (refs == NULL) + flatpak_completion_debug ("find remote 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: + break; + } + + return TRUE; +} diff --git a/app/flatpak-builtins-update.c b/app/flatpak-builtins-update.c index 69a95f13..33e6c4c0 100644 --- a/app/flatpak-builtins-update.c +++ b/app/flatpak-builtins-update.c @@ -50,7 +50,7 @@ static GOptionEntry options[] = { { "runtime", 0, 0, G_OPTION_ARG_NONE, &opt_runtime, "Look for runtime with the specified name", }, { "app", 0, 0, G_OPTION_ARG_NONE, &opt_app, "Look for app with the specified name", }, { "appstream", 0, 0, G_OPTION_ARG_NONE, &opt_appstream, "Update appstream for remote", }, - { "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths, "Only update this subpath", "path" }, + { "subpath", 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &opt_subpaths, "Only update this subpath", "PATH" }, { NULL } }; @@ -107,7 +107,6 @@ flatpak_builtin_update (int argc, g_autoptr(FlatpakDir) dir = NULL; const char *name = NULL; const char *branch = NULL; - const char *arch = NULL; gboolean failed = FALSE; int i; @@ -125,9 +124,7 @@ flatpak_builtin_update (int argc, branch = argv[2]; if (opt_arch == NULL) - arch = flatpak_get_arch (); - else - arch = opt_arch; + opt_arch = (char *)flatpak_get_arch (); if (!opt_app && !opt_runtime) opt_app = opt_runtime = TRUE; @@ -155,7 +152,7 @@ flatpak_builtin_update (int argc, if (name != NULL && strcmp (parts[1], name) != 0) continue; - if (strcmp (parts[2], arch) != 0) + if (strcmp (parts[2], opt_arch) != 0) continue; g_print ("Updating application %s %s\n", parts[1], parts[3]); @@ -186,7 +183,7 @@ flatpak_builtin_update (int argc, if (name != NULL && strcmp (parts[1], name) != 0) continue; - if (strcmp (parts[2], arch) != 0) + if (strcmp (parts[2], opt_arch) != 0) continue; g_print ("Updating runtime %s %s\n", parts[1], parts[3]); @@ -207,7 +204,7 @@ flatpak_builtin_update (int argc, ref = flatpak_dir_find_installed_ref (dir, name, branch, - arch, + opt_arch, opt_app, opt_runtime, &is_app, error); if (ref == NULL) @@ -226,3 +223,59 @@ flatpak_builtin_update (int argc, return TRUE; } + +gboolean +flatpak_complete_update (FlatpakCompletion *completion) +{ + g_autoptr(GOptionContext) context = NULL; + g_autoptr(FlatpakDir) dir = NULL; + g_autoptr(GError) error = NULL; + g_auto(GStrv) refs = NULL; + int i; + + context = g_option_context_new (""); + if (!flatpak_option_context_parse (context, options, &completion->argc, &completion->argv, 0, &dir, NULL, NULL)) + return FALSE; + + if (!opt_app && !opt_runtime) + opt_app = opt_runtime = TRUE; + + switch (completion->argc) + { + case 0: + case 1: /* NAME */ + 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, + opt_app, opt_runtime, &error); + if (refs == NULL) + flatpak_completion_debug ("find local 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 \n", parts[1]); + } + break; + + case 2: /* Branch */ + refs = flatpak_dir_find_installed_refs (dir, completion->argv[1], NULL, opt_arch, + opt_app, opt_runtime, &error); + if (refs == NULL) + flatpak_completion_debug ("find remote 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: + break; + } + + return TRUE; +} diff --git a/app/flatpak-builtins.h b/app/flatpak-builtins.h index 1f6cda5c..133a57c7 100644 --- a/app/flatpak-builtins.h +++ b/app/flatpak-builtins.h @@ -24,6 +24,7 @@ #include #include +#include "flatpak-utils.h" #include "flatpak-dir.h" G_BEGIN_DECLS @@ -42,36 +43,42 @@ gboolean flatpak_option_context_parse (GOptionContext *context, GCancellable *cancellable, GError **error); +extern GOptionEntry user_entries[]; +extern GOptionEntry global_entries[]; + gboolean usage_error (GOptionContext *context, const char *message, GError **error); -#define BUILTINPROTO(name) gboolean flatpak_builtin_ ## name (int argc, char **argv, GCancellable * cancellable, GError * *error) +#define BUILTINPROTO(name) \ + gboolean flatpak_builtin_ ## name (int argc, char **argv, GCancellable * cancellable, GError * *error); \ + gboolean flatpak_complete_ ## name (FlatpakCompletion *completion); -BUILTINPROTO (add_remote); -BUILTINPROTO (modify_remote); -BUILTINPROTO (delete_remote); -BUILTINPROTO (ls_remote); -BUILTINPROTO (list_remotes); -BUILTINPROTO (install); -BUILTINPROTO (update); -BUILTINPROTO (make_current_app); -BUILTINPROTO (uninstall); -BUILTINPROTO (install_bundle); -BUILTINPROTO (list); -BUILTINPROTO (info); -BUILTINPROTO (run); -BUILTINPROTO (enter); -BUILTINPROTO (build_init); -BUILTINPROTO (build); -BUILTINPROTO (build_finish); -BUILTINPROTO (build_sign); -BUILTINPROTO (build_export); -BUILTINPROTO (build_bundle); -BUILTINPROTO (build_import); -BUILTINPROTO (build_update_repo); -BUILTINPROTO (export_file); -BUILTINPROTO (override); + +BUILTINPROTO (add_remote) +BUILTINPROTO (modify_remote) +BUILTINPROTO (delete_remote) +BUILTINPROTO (ls_remote) +BUILTINPROTO (list_remotes) +BUILTINPROTO (install) +BUILTINPROTO (update) +BUILTINPROTO (make_current_app) +BUILTINPROTO (uninstall) +BUILTINPROTO (install_bundle) +BUILTINPROTO (list) +BUILTINPROTO (info) +BUILTINPROTO (run) +BUILTINPROTO (enter) +BUILTINPROTO (build_init) +BUILTINPROTO (build) +BUILTINPROTO (build_finish) +BUILTINPROTO (build_sign) +BUILTINPROTO (build_export) +BUILTINPROTO (build_bundle) +BUILTINPROTO (build_import) +BUILTINPROTO (build_update_repo) +BUILTINPROTO (export_file) +BUILTINPROTO (override) #undef BUILTINPROTO diff --git a/app/flatpak-main.c b/app/flatpak-main.c index c37f8e3e..9591757b 100644 --- a/app/flatpak-main.c +++ b/app/flatpak-main.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "libglnx/libglnx.h" @@ -40,57 +41,62 @@ static gboolean opt_user; typedef struct { const char *name; + const char *description; gboolean (*fn)(int argc, char **argv, GCancellable *cancellable, GError **error); - const char *description; + gboolean (*complete)(FlatpakCompletion *completion); gboolean deprecated; } FlatpakCommand; static FlatpakCommand commands[] = { { " Manage installed apps and runtimes" }, - { "install", flatpak_builtin_install, "Install an application or runtime from a remote"}, - { "update", flatpak_builtin_update, "Update an installed application or runtime"}, - { "uninstall", flatpak_builtin_uninstall, "Uninstall an installed application or runtime" }, - { "list", flatpak_builtin_list, "List installed apps and/or runtimes" }, - { "info", flatpak_builtin_info, "Show info for installed app or runtime" }, + { "install", "Install an application or runtime from a remote", flatpak_builtin_install, flatpak_complete_install }, + { "update", "Update an installed application or runtime", flatpak_builtin_update, flatpak_complete_update }, + { "uninstall", "Uninstall an installed application or runtime", flatpak_builtin_uninstall, flatpak_complete_uninstall }, + { "list", "List installed apps and/or runtimes", flatpak_builtin_list, flatpak_complete_list }, + { "info", "Show info for installed app or runtime", flatpak_builtin_info, flatpak_complete_info }, { "\n Running applications" }, - { "run", flatpak_builtin_run, "Run an application" }, - { "override", flatpak_builtin_override, "Override permissions for an application" }, - { "export-file", flatpak_builtin_export_file, "Grant an application access to a specific file" }, - { "make-current", flatpak_builtin_make_current_app, "Specify default version to run" }, - { "enter", flatpak_builtin_enter, "Enter the namespace of a running application" }, + { "run", "Run an application", flatpak_builtin_run, flatpak_complete_run }, + { "override", "Override permissions for an application", flatpak_builtin_override }, + { "export-file", "Grant an application access to a specific file", flatpak_builtin_export_file}, + { "make-current", "Specify default version to run", flatpak_builtin_make_current_app}, + { "enter", "Enter the namespace of a running application", flatpak_builtin_enter }, { "\n Manage remote repositories" }, - { "remote-add", flatpak_builtin_add_remote, "Add a new remote repository (by URL)" }, - { "remote-modify", flatpak_builtin_modify_remote, "Modify properties of a configured remote" }, - { "remote-delete", flatpak_builtin_delete_remote, "Delete a configured remote" }, - { "remote-list", flatpak_builtin_list_remotes, "List all configured remotes" }, - { "remote-ls", flatpak_builtin_ls_remote, "List contents of a configured remote" }, + { "remote-add", "Add a new remote repository (by URL)", flatpak_builtin_add_remote }, + { "remote-modify", "Modify properties of a configured remote", flatpak_builtin_modify_remote}, + { "remote-delete", "Delete a configured remote", flatpak_builtin_delete_remote}, + { "remote-list", "List all configured remotes", flatpak_builtin_list_remotes}, + { "remote-ls", "List contents of a configured remote", flatpak_builtin_ls_remote }, { "\n Build applications" }, - { "build-init", flatpak_builtin_build_init, "Initialize a directory for building" }, - { "build", flatpak_builtin_build, "Run a build command inside the build dir" }, - { "build-finish", flatpak_builtin_build_finish, "Finish a build dir for export" }, - { "build-export", flatpak_builtin_build_export, "Export a build dir to a repository" }, - { "build-bundle", flatpak_builtin_build_bundle, "Create a bundle file from a build directory" }, - { "build-import-bundle", flatpak_builtin_build_import, "Import a bundle file" }, - { "build-sign", flatpak_builtin_build_sign, "Sign an application or runtime" }, - { "build-update-repo", flatpak_builtin_build_update_repo, "Update the summary file in a repository" }, + { "build-init", "Initialize a directory for building", flatpak_builtin_build_init }, + { "build", "Run a build command inside the build dir", flatpak_builtin_build }, + { "build-finish", "Finish a build dir for export", flatpak_builtin_build_finish }, + { "build-export", "Export a build dir to a repository", flatpak_builtin_build_export }, + { "build-bundle", "Create a bundle file from a build directory", flatpak_builtin_build_bundle }, + { "build-import-bundle", "Import a bundle file", flatpak_builtin_build_import }, + { "build-sign", "Sign an application or runtime", flatpak_builtin_build_sign }, + { "build-update-repo", "Update the summary file in a repository", flatpak_builtin_build_update_repo }, { NULL } }; -static GOptionEntry global_entries[] = { +GOptionEntry global_entries[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print debug information during command processing", NULL }, + { NULL } +}; + +static GOptionEntry empty_entries[] = { { "version", 0, 0, G_OPTION_ARG_NONE, &opt_version, "Print version information and exit", NULL }, { "default-arch", 0, 0, G_OPTION_ARG_NONE, &opt_default_arch, "Print default arch and exit", NULL }, { NULL } }; -static GOptionEntry user_entries[] = { +GOptionEntry user_entries[] = { { "user", 0, 0, G_OPTION_ARG_NONE, &opt_user, "Work on user installations", NULL }, { "system", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &opt_user, "Work on system-wide installations (default)", NULL }, { NULL } @@ -233,17 +239,12 @@ usage_error (GOptionContext *context, const char *message, GError **error) return FALSE; } -int -flatpak_run (int argc, - char **argv, - GError **res_error) +FlatpakCommand * +extract_command (int *argc, + char **argv) { FlatpakCommand *command; - GError *error = NULL; - GCancellable *cancellable = NULL; const char *command_name = NULL; - g_autofree char *prgname = NULL; - gboolean success = FALSE; int in, out; /* @@ -251,7 +252,7 @@ flatpak_run (int argc, * necessary, in order to pass relevant options through * to the commands, but also have them take effect globally. */ - for (in = 1, out = 1; in < argc; in++, out++) + for (in = 1, out = 1; in < *argc; in++, out++) { /* The non-option is the command, take it out of the arguments */ if (argv[in][0] != '-') @@ -267,7 +268,8 @@ flatpak_run (int argc, argv[out] = argv[in]; } - argc = out; + *argc = out; + argv[out] = NULL; command = commands; while (command->name) @@ -278,31 +280,47 @@ flatpak_run (int argc, command++; } + return command; +} + + +int +flatpak_run (int argc, + char **argv, + GError **res_error) +{ + FlatpakCommand *command; + GError *error = NULL; + GCancellable *cancellable = NULL; + const char *command_name = NULL; + g_autofree char *prgname = NULL; + gboolean success = FALSE; + + command = extract_command (&argc, argv); + if (!command->fn) { - g_autoptr(GOptionContext) context = NULL; + GOptionContext *context; g_autofree char *help; context = flatpak_option_context_new_with_commands (commands); - if (command_name != NULL) + /* This will not return for some options (e.g. --version). */ + if (flatpak_option_context_parse (context, empty_entries, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, &error)) { - g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Unknown command '%s'", command_name); - } - else - { - /* This will not return for some options (e.g. --version). */ - if (flatpak_option_context_parse (context, NULL, &argc, &argv, FLATPAK_BUILTIN_FLAG_NO_DIR, NULL, cancellable, &error)) - { - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, - "No command specified"); - } + if (command_name == NULL) + g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No command specified"); + else + g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Unknown command '%s'", command_name); } help = g_option_context_get_help (context, FALSE, NULL); g_printerr ("%s", help); + g_option_context_free (context); + goto out; } @@ -324,6 +342,48 @@ out: return 0; } +static int +complete (int argc, + char **argv) +{ + FlatpakCommand *command; + g_autofree char *initial_completion_line = NULL; + FlatpakCompletion *completion; + + completion = flatpak_completion_new (argv[2], argv[3], argv[4]); + if (completion == NULL) + return 1; + + command = extract_command (&completion->argc, completion->argv); + flatpak_completion_debug ("command=%p '%s'", command->fn, command->name); + + if (!command->fn) + { + FlatpakCommand *c = commands; + while (c->name) + { + if (c->fn != NULL) + flatpak_complete_word (completion, "%s ", c->name); + c++; + } + + flatpak_complete_options (completion, global_entries); + flatpak_complete_options (completion, empty_entries); + flatpak_complete_options (completion, user_entries); + } + else if (command->complete) + { + if (!command->complete (completion)) + return 1; + } + else + { + flatpak_complete_options (completion, global_entries); + } + + return 0; +} + int main (int argc, char **argv) @@ -347,6 +407,9 @@ main (int argc, else g_unsetenv ("GIO_USE_VFS"); + if (argc >= 4 && strcmp (argv[1], "complete") == 0) + return complete (argc, argv); + flatpak_migrate_from_xdg_app (); ret = flatpak_run (argc, argv, &error); diff --git a/common/flatpak-dir.c b/common/flatpak-dir.c index d7416c44..99a85f6e 100644 --- a/common/flatpak-dir.c +++ b/common/flatpak-dir.c @@ -3859,23 +3859,23 @@ flatpak_dir_remote_list_refs (FlatpakDir *self, return TRUE; } -char * -find_matching_ref (GHashTable *refs, - const char *name, - const char *opt_branch, - const char *opt_arch, - gboolean app, - gboolean runtime, - GError **error) +GPtrArray * +find_matching_refs (GHashTable *refs, + const char *opt_name, + const char *opt_branch, + const char *opt_arch, + gboolean app, + gboolean runtime, + GError **error) { g_autoptr(GPtrArray) matched_refs = NULL; const char *arch = NULL; GHashTableIter hash_iter; gpointer key; - if (!flatpak_is_valid_name (name)) + if (opt_name && !flatpak_is_valid_name (opt_name)) { - flatpak_fail (error, "'%s' is not a valid name", name); + flatpak_fail (error, "'%s' is not a valid name", opt_name); return NULL; } @@ -3915,8 +3915,10 @@ find_matching_ref (GHashTable *refs, if (parts == NULL) continue; - if (strcmp (parts[1], name) != 0 || - strcmp (parts[2], arch) != 0) + if (opt_name != NULL && strcmp (opt_name, parts[1]) != 0) + continue; + + if (strcmp (parts[2], arch) != 0) continue; if (opt_branch != NULL && strcmp (opt_branch, parts[3]) != 0) @@ -3925,6 +3927,26 @@ find_matching_ref (GHashTable *refs, g_ptr_array_add (matched_refs, g_steal_pointer (&ref)); } + return g_steal_pointer (&matched_refs); +} + + +char * +find_matching_ref (GHashTable *refs, + const char *name, + const char *opt_branch, + const char *opt_arch, + gboolean app, + gboolean runtime, + GError **error) +{ + g_autoptr(GPtrArray) matched_refs = NULL; + + matched_refs = find_matching_refs (refs, name, opt_branch, opt_arch, + app, runtime, error); + if (matched_refs == NULL) + return NULL; + if (matched_refs->len == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, @@ -3953,6 +3975,41 @@ find_matching_ref (GHashTable *refs, return g_strdup (g_ptr_array_index (matched_refs, 0)); } +char ** +flatpak_dir_find_remote_refs (FlatpakDir *self, + const char *remote, + const char *name, + const char *opt_branch, + const char *opt_arch, + gboolean app, + gboolean runtime, + GCancellable *cancellable, + GError **error) +{ + g_autofree char *refspec_prefix = NULL; + g_autofree char *remote_ref = NULL; + g_autoptr(GHashTable) remote_refs = NULL; + g_autoptr(GError) my_error = NULL; + GPtrArray *matched_refs; + + if (!flatpak_dir_ensure_repo (self, NULL, error)) + return NULL; + + refspec_prefix = g_strconcat (remote, ":.", NULL); + + if (!flatpak_dir_remote_list_refs (self, remote, + &remote_refs, cancellable, error)) + return NULL; + + matched_refs = find_matching_refs (remote_refs, name, opt_branch, + opt_arch, app, runtime, error); + if (matched_refs == NULL) + return NULL; + + g_ptr_array_add (matched_refs, NULL); + return (char **)g_ptr_array_free (matched_refs, FALSE); +} + char * flatpak_dir_find_remote_ref (FlatpakDir *self, const char *remote, @@ -3965,16 +4022,11 @@ flatpak_dir_find_remote_ref (FlatpakDir *self, GCancellable *cancellable, GError **error) { - const char *arch = NULL; g_autofree char *refspec_prefix = NULL; g_autofree char *remote_ref = NULL; g_autoptr(GHashTable) remote_refs = NULL; g_autoptr(GError) my_error = NULL; - arch = opt_arch; - if (arch == NULL) - arch = flatpak_get_arch (); - if (!flatpak_dir_ensure_repo (self, NULL, error)) return NULL; @@ -4010,25 +4062,15 @@ flatpak_dir_find_remote_ref (FlatpakDir *self, return NULL; } -char * -flatpak_dir_find_installed_ref (FlatpakDir *self, - const char *name, - const char *opt_branch, - const char *opt_arch, - gboolean app, - gboolean runtime, - gboolean *is_app, - GError **error) -{ - const char *arch = NULL; - g_autofree char *local_ref = NULL; - g_autoptr(GHashTable) local_refs = NULL; - g_autoptr(GError) my_error = NULL; - int i; - arch = opt_arch; - if (arch == NULL) - arch = flatpak_get_arch (); +static GHashTable * +flatpak_dir_get_all_installed_refs (FlatpakDir *self, + gboolean app, + gboolean runtime, + GError **error) +{ + g_autoptr(GHashTable) local_refs = NULL; + int i; if (!flatpak_dir_ensure_repo (self, NULL, error)) return NULL; @@ -4057,7 +4099,56 @@ flatpak_dir_find_installed_ref (FlatpakDir *self, GINT_TO_POINTER (1)); } - local_ref = find_matching_ref (local_refs, name, opt_branch, + return g_steal_pointer (&local_refs); +} + +char ** +flatpak_dir_find_installed_refs (FlatpakDir *self, + const char *opt_name, + const char *opt_branch, + const char *opt_arch, + gboolean app, + gboolean runtime, + GError **error) +{ + g_autofree char *local_ref = NULL; + g_autoptr(GHashTable) local_refs = NULL; + g_autoptr(GError) my_error = NULL; + GPtrArray *matched_refs; + + local_refs = flatpak_dir_get_all_installed_refs (self, app, runtime, error); + if (local_refs == NULL) + return NULL; + + matched_refs = find_matching_refs (local_refs, opt_name, opt_branch, + opt_arch, app, runtime, error); + if (matched_refs == NULL) + return NULL; + + g_ptr_array_add (matched_refs, NULL); + return (char **)g_ptr_array_free (matched_refs, FALSE); + +} + +char * +flatpak_dir_find_installed_ref (FlatpakDir *self, + const char *opt_name, + const char *opt_branch, + const char *opt_arch, + gboolean app, + gboolean runtime, + gboolean *is_app, + GError **error) +{ + g_autofree char *local_ref = NULL; + g_autoptr(GHashTable) local_refs = NULL; + g_autoptr(GError) my_error = NULL; + + local_refs = flatpak_dir_get_all_installed_refs (self, app, runtime, error); + if (local_refs == NULL) + return NULL; + + local_ref = find_matching_ref (local_refs, opt_name, opt_branch, opt_arch, app, runtime, &my_error); if (local_ref == NULL) { @@ -4078,7 +4169,7 @@ flatpak_dir_find_installed_ref (FlatpakDir *self, } g_set_error (error, FLATPAK_ERROR, FLATPAK_ERROR_NOT_INSTALLED, - "%s %s not installed", name, opt_branch ? opt_branch : "master"); + "%s %s not installed", opt_name ? opt_name : "*unspecified*", opt_branch ? opt_branch : "master"); return NULL; } diff --git a/common/flatpak-dir.h b/common/flatpak-dir.h index 6ea64a1a..541819b3 100644 --- a/common/flatpak-dir.h +++ b/common/flatpak-dir.h @@ -142,14 +142,30 @@ char * flatpak_dir_find_remote_ref (FlatpakDir *self, gboolean *is_app, GCancellable *cancellable, GError **error); +char ** flatpak_dir_find_remote_refs (FlatpakDir *self, + const char *remote, + const char *name, + const char *opt_branch, + const char *opt_arch, + gboolean app, + gboolean runtime, + GCancellable *cancellable, + GError **error); char * flatpak_dir_find_installed_ref (FlatpakDir *self, - const char *name, + const char *opt_name, const char *opt_branch, const char *opt_arch, gboolean app, gboolean runtime, gboolean *is_app, GError **error); +char ** flatpak_dir_find_installed_refs (FlatpakDir *self, + const char *opt_name, + const char *opt_branch, + const char *opt_arch, + gboolean app, + gboolean runtime, + GError **error); FlatpakDeploy *flatpak_dir_load_deployed (FlatpakDir *self, const char *ref, const char *checksum, diff --git a/common/flatpak-run.c b/common/flatpak-run.c index 19a50a74..80348979 100644 --- a/common/flatpak-run.c +++ b/common/flatpak-run.c @@ -60,7 +60,7 @@ typedef enum { /* Same order as enum */ -static const char *flatpak_context_shares[] = { +const char *flatpak_context_shares[] = { "network", "ipc", NULL @@ -75,7 +75,7 @@ typedef enum { } FlatpakContextSockets; /* Same order as enum */ -static const char *flatpak_context_sockets[] = { +const char *flatpak_context_sockets[] = { "x11", "wayland", "pulseaudio", @@ -93,7 +93,7 @@ typedef enum { FLATPAK_CONTEXT_DEVICE_DRI = 1 << 0, } FlatpakContextDevices; -static const char *flatpak_context_devices[] = { +const char *flatpak_context_devices[] = { "dri", NULL }; @@ -861,6 +861,12 @@ static GOptionEntry context_options[] = { { NULL } }; +void +flatpak_context_complete (FlatpakContext *context, FlatpakCompletion *completion) +{ + flatpak_complete_options (completion, context_options); +} + GOptionGroup * flatpak_context_get_options (FlatpakContext *context) { diff --git a/common/flatpak-run.h b/common/flatpak-run.h index c6077c99..00530319 100644 --- a/common/flatpak-run.h +++ b/common/flatpak-run.h @@ -24,6 +24,7 @@ #include "libglnx/libglnx.h" #include "dbus-proxy/flatpak-proxy.h" #include "flatpak-common-types.h" +#include "flatpak-utils.h" gboolean flatpak_run_in_transient_unit (const char *app_id, GError **error); @@ -38,11 +39,17 @@ gboolean flatpak_run_in_transient_unit (const char *app_id, #define FLATPAK_METADATA_KEY_PERSISTENT "persistent" #define FLATPAK_METADATA_KEY_DEVICES "devices" +extern const char *flatpak_context_sockets[]; +extern const char *flatpak_context_devices[]; +extern const char *flatpak_context_shares[]; + FlatpakContext *flatpak_context_new (void); void flatpak_context_free (FlatpakContext *context); void flatpak_context_merge (FlatpakContext *context, FlatpakContext *other); GOptionGroup *flatpak_context_get_options (FlatpakContext *context); +void flatpak_context_complete (FlatpakContext *context, + FlatpakCompletion *completion); gboolean flatpak_context_load_metadata (FlatpakContext *context, GKeyFile *metakey, GError **error); diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c index 2fc1c1c5..6d7c7be0 100644 --- a/common/flatpak-utils.c +++ b/common/flatpak-utils.c @@ -24,6 +24,7 @@ #include "lib/flatpak-error.h" #include "flatpak-dir.h" #include "flatpak-portal-error.h" +#include "flatpak-run.h" #include #include @@ -3046,3 +3047,235 @@ flatpak_allocate_tmpdir (int tmpdir_dfd, return TRUE; } + +/* Uncomment to get debug traces in /tmp/flatpak-completion-debug.txt (nice + * to not have it interfere with stdout/stderr) + */ +#if 0 +void +flatpak_completion_debug (const gchar *format, ...) +{ + va_list var_args; + gchar *s; + static FILE *f = NULL; + + va_start (var_args, format); + s = g_strdup_vprintf (format, var_args); + if (f == NULL) + f = fopen ("/tmp/flatpak-completion-debug.txt", "a+"); + fprintf (f, "%s\n", s); + fflush (f); + g_free (s); +} +#else +void +flatpak_completion_debug (const gchar *format, ...) +{ +} +#endif + +static gboolean +is_word_separator (char c) +{ + return g_ascii_isspace (c); +} + +void +flatpak_complete_word (FlatpakCompletion *completion, + char *format, ...) +{ + va_list args; + const char *rest; + g_autofree char *string = NULL; + + g_return_if_fail (format != NULL); + + va_start (args, format); + string = g_strdup_vprintf (format, args); + va_end (args); + + if (!g_str_has_prefix (string, completion->cur)) + return; + + /* I'm not sure exactly what bash is doing here, but this seems to work... */ + if (strcmp (completion->shell_cur, "=") == 0) + rest = string + strlen (completion->cur) - strlen (completion->shell_cur) + 1; + else + rest = string + strlen (completion->cur) - strlen (completion->shell_cur); + + flatpak_completion_debug ("completing word: '%s' (%s)", string, rest); + + g_print ("%s\n", rest); +} + +void +flatpak_complete_options (FlatpakCompletion *completion, + GOptionEntry *entries) +{ + GOptionEntry *e = entries; + int i; + + while (e->long_name != NULL) + { + if (e->arg_description) + { + g_autofree char *prefix = g_strdup_printf ("--%s=", e->long_name); + + if (g_str_has_prefix (completion->cur, prefix)) + { + if (strcmp (e->arg_description, "ARCH") == 0) + { + const char *arches[] = {"i386", "x86_64", "aarch64", "arm"}; + for (i = 0; i < G_N_ELEMENTS (arches); i++) + flatpak_complete_word (completion, "%s%s ", prefix, arches[i]); + } + else if (strcmp (e->arg_description, "SHARE") == 0) + { + for (i = 0; flatpak_context_shares[i] != NULL; i++) + flatpak_complete_word (completion, "%s%s ", prefix, flatpak_context_shares[i]); + } + else if (strcmp (e->arg_description, "DEVICE") == 0) + { + for (i = 0; flatpak_context_devices[i] != NULL; i++) + flatpak_complete_word (completion, "%s%s ", prefix, flatpak_context_devices[i]); + } + else if (strcmp (e->arg_description, "SOCKET") == 0) + { + for (i = 0; flatpak_context_sockets[i] != NULL; i++) + flatpak_complete_word (completion, "%s%s ", prefix, flatpak_context_sockets[i]); + } + else + flatpak_complete_word (completion, "%s", prefix); + } + else + flatpak_complete_word (completion, "%s", prefix); + } + else + flatpak_complete_word (completion, "--%s ", e->long_name); + if (e->short_name != 0) + flatpak_complete_word (completion, "-%c ", e->short_name); + e++; + } +} + +static gchar * +pick_word_at (const char *s, + int cursor, + int *out_word_begins_at) +{ + int begin, end; + + if (s[0] == '\0') + { + if (out_word_begins_at != NULL) + *out_word_begins_at = -1; + return NULL; + } + + if (is_word_separator (s[cursor]) && ((cursor > 0 && is_word_separator(s[cursor-1])) || cursor == 0)) + { + if (out_word_begins_at != NULL) + *out_word_begins_at = cursor; + return g_strdup (""); + } + + while (!is_word_separator (s[cursor - 1]) && cursor > 0) + cursor--; + begin = cursor; + + end = begin; + while (!is_word_separator (s[end]) && s[end] != '\0') + end++; + + if (out_word_begins_at != NULL) + *out_word_begins_at = begin; + + return g_strndup (s + begin, end - begin); +} + +FlatpakCompletion * +flatpak_completion_new (const char *arg_line, + const char *arg_point, + const char *arg_cur) +{ + FlatpakCompletion *completion; + g_autofree char *initial_completion_line = NULL; + int _point; + char *endp; + int cur_begin; + int i; + + _point = strtol (arg_point, &endp, 10); + if (endp == arg_point || *endp != '\0') + return NULL; + + completion = g_new0 (FlatpakCompletion, 1); + completion->line = g_strdup (arg_line); + completion->shell_cur = g_strdup (arg_cur); + completion->point = _point; + + flatpak_completion_debug ("========================================"); + flatpak_completion_debug ("completion_point=%d", completion->point); + flatpak_completion_debug ("completion_shell_cur='%s'", completion->shell_cur); + flatpak_completion_debug ("----"); + flatpak_completion_debug (" 0123456789012345678901234567890123456789012345678901234567890123456789"); + flatpak_completion_debug ("'%s'", completion->line); + flatpak_completion_debug (" %*s^", completion->point, ""); + + /* compute cur and prev */ + completion->prev = NULL; + completion->cur = pick_word_at (completion->line, completion->point, &cur_begin); + if (cur_begin > 0) + { + gint prev_end; + for (prev_end = cur_begin - 1; prev_end >= 0; prev_end--) + { + if (!is_word_separator (completion->line[prev_end])) + { + completion->prev = pick_word_at (completion->line, prev_end, NULL); + break; + } + } + + initial_completion_line = g_strndup (completion->line, cur_begin); + } + else + initial_completion_line = g_strdup (""); + + flatpak_completion_debug ("'%s'", initial_completion_line); + flatpak_completion_debug ("----"); + + flatpak_completion_debug (" cur='%s'", completion->cur); + flatpak_completion_debug ("prev='%s'", completion->prev); + + if (!g_shell_parse_argv (initial_completion_line, + &completion->argc, + &completion->argv, + NULL)) + { + /* it's very possible the command line can't be parsed (for + * example, missing quotes etc) - in that case, we just + * don't autocomplete at all + */ + flatpak_completion_free (completion); + return NULL; + } + + flatpak_completion_debug ("completion_argv:"); + for (i = 0; i < completion->argc; i++) + flatpak_completion_debug (completion->argv[i]); + + flatpak_completion_debug ("----"); + + return completion; +} + +void +flatpak_completion_free (FlatpakCompletion *completion) +{ + g_free (completion->cur); + g_free (completion->prev); + g_free (completion->line); + g_strfreev (completion->argv); + g_free (completion); +} diff --git a/common/flatpak-utils.h b/common/flatpak-utils.h index 5621b653..bf40fd92 100644 --- a/common/flatpak-utils.h +++ b/common/flatpak-utils.h @@ -356,4 +356,26 @@ gboolean flatpak_allocate_tmpdir (int tmpdir_dfd, GError **error); +typedef struct { + char *shell_cur; + char *cur; + char *prev; + char *line; + int point; + char **argv; + int argc; +} FlatpakCompletion; + +void flatpak_completion_debug (const gchar *format, ...); + +FlatpakCompletion *flatpak_completion_new (const char *arg_line, + const char *arg_point, + const char *arg_cur); +void flatpak_complete_word (FlatpakCompletion *completion, + char *format, + ...); +void flatpak_complete_options (FlatpakCompletion *completion, + GOptionEntry *entries); +void flatpak_completion_free (FlatpakCompletion *completion); + #endif /* __FLATPAK_UTILS_H__ */ diff --git a/completion/flatpak b/completion/flatpak index 42fbaaf5..10cbdd89 100755 --- a/completion/flatpak +++ b/completion/flatpak @@ -1,405 +1,14 @@ +# Check for bash [ -z "$BASH_VERSION" ] && return -__contains_word () { - local w word=$1; shift - for w in "$@"; do - [[ $w = "$word" ]] && return - done +#################################################################################################### + +__flatpak() { + local IFS=$'\n' + local cur=`_get_cword :` + COMPREPLY=($(flatpak complete "${COMP_LINE}" "${COMP_POINT}" "${cur}")) } -_flatpak() { - local cur prev - local cur=${COMP_WORDS[COMP_CWORD]} - local prev=${COMP_WORDS[COMP_CWORD-1]} +#################################################################################################### - if [[ $cur == "=" ]] && [[ "$prev" = -* ]]; then - cur="" - elif [[ $prev == "=" ]] && [[ "${COMP_WORDS[COMP_CWORD-2]}" = -* ]]; then - prev=${COMP_WORDS[COMP_CWORD-2]} - fi - - local i verb comps mode kind bundle - local remote name - local file dir cmd sdk loc - - local -A VERBS=( - [ALL]='remote-add remote-modify remote-delete remote-ls remote-list install update uninstall list run override enter export-file build-init build build-finish build-export build-bundle build-update-repo make-current' - [MODE]='remote-add remote-modify remote-delete remote-ls remote-list install update uninstall list list make-current' - [KIND]='install update uninstall list remote-ls' - [PERMS]='run override build build-finish' - [UNINSTALL]='uninstall' - [SHOW_DETAILS]='list remote-list remote-ls' - [ARCH]='build-init install build-bundle run uninstall update make-current' - [USER_AND_SYSTEM]='run remote-list list' - [APP_AND_RUNTIME]='install update uninstall' - ) - - local -A OPTS=( - [GENERAL]='--help --verbose --version' - [MODE]='--user --system' - [KIND]='--app --runtime' - [ARCH]='--arch=' - [SHOW_DETAILS]='-d --show-details' - [PERMS]='--share= --unshare= --socket= --nosocket= --device= --nodevice= --filesystem= --env= --own-name= --talk-name= --persist=' - [ADD_REMOTE]='--no-gpg-verify --if-not-exists --title= --gpg-import= --gpg-key=' - [MODIFY_REMOTE]='--no-gpg-verify --gpg-verify --title= --gpg-import= --gpg-key=' - [LIST_REMOTES]='--show-details' - [LS_REMOTE]='--show-details --updates' - [UNINSTALL]='--keep-ref --force-remove' - [INSTALL]='--bundle --gpg-file=' - [BUILD_BUNDLE]='--gpg-keys= --runtime --arch= --repo-url=' - [RUN]='--command= --branch= --devel --runtime=' - [BUILD_INIT]='--arch= --var=' - [BUILD]='--runtime' - [BUILD_FINISH]='--command= --allow' - [BUILD_EXPORT]='--subject= --body=' - [EXPORT_FILE]='--app= --allow-write --allow-delete --allow-grant-permission --unique --transient' - - [ARG]='--arch --command --branch --var --share --unshare --socket --nosocket --device --nodevice --subject --body --title --runtime --filesystem' - ) - - for ((i=0; i < COMP_CWORD; i++)); do - if [[ "${COMP_WORDS[i]}" = -* ]]; then - continue - fi - if __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then - continue - fi - if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && - test -z $first_verb; then - first_verb=${COMP_WORDS[i]} - fi - done - - if __contains_word "--bundle" ${COMP_WORDS[*]}; then - bundle="true" - else - bundle="false" - fi - - if __contains_word "--user" ${COMP_WORDS[*]}; then - if __contains_word "--system" ${COMP_WORDS[*]}; then - mode="--user --system" - else - mode="--user" - fi - else - if __contains_word "--system" ${COMP_WORDS[*]}; then - mode=--system - else - if __contains_word "$first_verb" ${VERBS[USER_AND_SYSTEM]}; then - mode="--user --system" - else - mode="--system" - fi - fi - fi - - if __contains_word "--app" ${COMP_WORDS[*]}; then - if __contains_word "--runtime" ${COMP_WORDS[*]}; then - kind="--app --runtime" - else - kind="--app" - fi - else - if __contains_word "--runtime" ${COMP_WORDS[*]}; then - kind="--runtime" - else - if __contains_word "$first_verb" ${VERBS[APP_AND_RUNTIME]}; then - kind="--app --runtime" - else - kind="--app" - fi - fi - fi - - if __contains_word "$prev" ${OPTS[ARG]}; then - case $prev in - --arch) - comps='x86_64 i686' - ;; - --command) - comps=$(compgen -A command) - ;; - --var) - comps=$(flatpak $mode list --runtime) - ;; - --runtime) - if __contains_word "$verb" ${VERBS[KIND]}; then - comps=XXX - else - comps=$(flatpak $mode list --runtime) - fi - ;; - --share|--noshare) - comps='network ipc' - ;; - --device|--nodevice) - comps='dri' - ;; - --socket|--nosocket) - comps='x11 wayland pulseaudio system-bus session-bus' - ;; - --branch|--subject|--body|--title) - comps='' - ;; - --filesystem) - comps='host home xdg-desktop xdg-documents xdg-download xdg-music xdg-pictures xdg-public-share xdg-templates xdg-videos' - ;; - esac - if [[ $comps != "XXXX" ]] ; then - COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) - return 0 - fi - fi - - for ((i=0; i < COMP_CWORD; i++)); do - if [[ "${COMP_WORDS[i]}" = -* ]]; then - continue - fi - if __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then - continue - fi - if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && - test -z $verb; then - verb=${COMP_WORDS[i]} - elif [[ $verb = install && $bundle = "true" ]]; then - if [[ -z $file ]]; then - file=${COMP_WORDS[i]} - fi - elif [[ $verb = install ]]; then - if [[ -z $remote ]]; then - remote=${COMP_WORDS[i]} - elif [[ -z $name ]]; then - name=${COMP_WORDS[i]} - fi - elif [[ $verb =~ (update|uninstall|run) ]]; then - if [[ -z $name ]]; then - name=${COMP_WORDS[i]} - fi - elif [[ $verb = build-init ]]; then - if [[ -z $dir ]]; then - dir=${COMP_WORDS[i]} - elif [[ -z $sdk ]]; then - sdk=${COMP_WORDS[i]} - elif [[ -z $name ]]; then - name=${COMP_WORDS[i]} - fi - elif [[ $verb = build ]]; then - if [[ -z $dir ]]; then - dir=${COMP_WORDS[i]} - elif [[ -z $cmd ]]; then - cmd=${COMP_WORDS[i]} - fi - elif [[ $verb = build-finish ]]; then - if [[ -z $dir ]]; then - dir=${COMP_WORDS[i]} - fi - elif [[ $verb = build-export ]]; then - if [[ -z $loc ]]; then - loc=${COMP_WORDS[i]} - elif [[ -z $dir ]]; then - dir=${COMP_WORDS[i]} - elif [[ -z $name ]]; then - name=${COMP_WORDS[i]} - fi - elif [[ $verb = build-bundle ]]; then - if [[ -z $loc ]]; then - loc=${COMP_WORDS[i]} - elif [[ -z $file ]]; then - file=${COMP_WORDS[i]} - elif [[ -z $name ]]; then - name=${COMP_WORDS[i]} - fi - elif [[ $verb = build-update-repo ]]; then - if [[ -z $loc ]]; then - loc=${COMP_WORDS[i]} - fi - elif [[ $verb = export-file ]]; then - if [[ -z $file ]]; then - file=${COMP_WORDS[i]} - fi - fi - done - - if [[ -z $verb ]]; then - comps="${VERBS[*]}" - - elif [[ "$cur" = -* ]]; then - comps="${OPTS[GENERAL]}" - if __contains_word "$verb" ${VERBS[MODE]}; then - comps="$comps ${OPTS[MODE]}" - fi - if __contains_word "$verb" ${VERBS[KIND]}; then - comps="$comps ${OPTS[KIND]}" - fi - if __contains_word "$verb" ${VERBS[PERMS]}; then - comps="$comps ${OPTS[PERMS]}" - fi - if [ "$verb" = "remote-list" ]; then - comps="$comps ${OPTS[LIST_REMOTES]}" - fi - if __contains_word "$verb" ${VERBS[ARCH]}; then - comps="$comps ${OPTS[ARCH]}" - fi - if __contains_word "$verb" ${VERBS[SHOW_DETAILS]}; then - comps="$comps ${OPTS[SHOW_DETAILS]}" - fi - if __contains_word "$verb" ${VERBS[UNINSTALL]}; then - comps="$comps ${OPTS[UNINSTALL]}" - fi - if [ "$verb" = "run" ]; then - comps="$comps ${OPTS[RUN]}" - fi - if [ "$verb" = "export-file" ]; then - comps="$comps ${OPTS[EXPORT_FILE]}" - fi - if [ "$verb" = "remote-ls" ]; then - comps="$comps ${OPTS[LS_REMOTE]}" - fi - if [ "$verb" = "build-init" ]; then - comps="$comps ${OPTS[BUILD_INIT]}" - fi - if [ "$verb" = "build" ]; then - comps="$comps ${OPTS[BUILD]}" - fi - if [ "$verb" = "build-finish" ]; then - comps="$comps ${OPTS[BUILD_FINISH]}" - fi - if [ "$verb" = "build-bundle" ]; then - comps="$comps ${OPTS[BUILD_BUNDLE]}" - fi - if [ "$verb" = "install" ]; then - comps="$comps ${OPTS[INSTALL]}" - fi - if [ "$verb" = "build-export" ]; then - comps="$comps ${OPTS[BUILD_EXPORT]}" - fi - if [ "$verb" = "repo-update" ]; then - comps="$comps ${OPTS[REPO_UPDATE]}" - fi - if [ "$verb" = "remote-add" ]; then - comps="$comps ${OPTS[ADD_REMOTE]}" - fi - if [ "$verb" = "remote-modify" ]; then - comps="$comps ${OPTS[MODIFY_REMOTE]}" - fi - else - case "$verb" in - remote-add|remote-modify|remote-delete|remote-ls) - comps=$(flatpak $mode remote-list) - ;; - - install) - if [[ $bundle == "true" ]]; then - comps='' - compopt -o filenames - else - if [[ -z $remote ]]; then - comps=$(flatpak $mode remote-list) - if [[ $mode == "--system" ]]; then - comps="$comps --user" - fi - elif [[ -z $name ]]; then - comps=$(flatpak remote-ls $mode $kind $remote) - else - comps='' # FIXME: branches - fi - fi - ;; - - remote-list|list) - comps='' - ;; - - update|uninstall) - if [[ -z $name ]]; then - comps=$(flatpak $mode $kind list) - else - comps='' # FIXME: branches - fi - ;; - - run|override) - # run doesn't take --user or --system, so don't use mode here - if [[ -z $name ]]; then - comps=$(flatpak list --app) - fi - ;; - - - build-init) - if [[ -z $dir ]]; then - comps='' - compopt -o dirnames - elif [[ -z $sdk ]]; then - comps="$(flatpak list --runtime) $(flatpak --user list --runtime)" - elif [[ -z $name ]]; then - comps="$(flatpak list --runtime) $(flatpak --user list --runtime)" - else - comps='' # FIXME: branches - fi - ;; - - build) - if [[ -z $dir ]]; then - comps='' - compopt -o dirnames - elif [[ -z $cmd ]]; then - comps='' - compopt -o bashdefault - fi - ;; - - build-finish) - if [[ -z $dir ]]; then - comps='' - compopt -o dirnames - fi - ;; - - build-export) - if [[ -z $loc ]]; then - comps='' - compopt -o dirnames - elif [[ -z $dir ]]; then - comps='' - compopt -o dirnames - fi - ;; - - build-bundle) - if [[ -z $loc ]]; then - comps='' - compopt -o dirnames - elif [[ -z $file ]]; then - comps='' - compopt -o filenames - else - comps='' # FIXME: list apps/runtimes - fi - ;; - - build-update-repo) - if [[ -z $loc ]]; then - comps='' - compopt -o dirnames - fi - ;; - - export-file) - if [[ -z $file ]]; then - comps='' - compopt -o dirnames - fi - ;; - - esac - fi - - COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) - [[ $COMPREPLY == *= ]] && compopt -o nospace - return 0 -} - -complete -F _flatpak flatpak +complete -o nospace -F __flatpak flatpak