Replace bash completion shell script with C-based version

This doesn't do everything yet, but its got the basics covered.
This commit is contained in:
Alexander Larsson
2016-05-27 16:30:13 +02:00
parent c7c9505e90
commit eeaa832095
15 changed files with 930 additions and 525 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -24,6 +24,7 @@
#include <ostree.h>
#include <gio/gio.h>
#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

View File

@@ -24,6 +24,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <gio/gio.h>
#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);

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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)
{

View File

@@ -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);

View File

@@ -24,6 +24,7 @@
#include "lib/flatpak-error.h"
#include "flatpak-dir.h"
#include "flatpak-portal-error.h"
#include "flatpak-run.h"
#include <string.h>
#include <stdlib.h>
@@ -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);
}

View File

@@ -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__ */

View File

@@ -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