diff --git a/app/flatpak-builtins-install.c b/app/flatpak-builtins-install.c
index 5305a58f..6a233f06 100644
--- a/app/flatpak-builtins-install.c
+++ b/app/flatpak-builtins-install.c
@@ -151,8 +151,12 @@ do_install (FlatpakDir *dir,
GError **error)
{
g_autoptr(GPtrArray) related = NULL;
+ const char *slash;
int i;
+ slash = strchr (ref, '/');
+ g_print (_("Installing: %s\n"), slash + 1);
+
if (!flatpak_dir_install (dir,
opt_no_pull,
opt_no_deploy,
@@ -223,6 +227,7 @@ install_from (FlatpakDir *dir,
g_autofree char *remote = NULL;
g_autofree char *ref = NULL;
g_auto(GStrv) parts = NULL;
+ const char *slash;
FlatpakDir *clone;
if (argc < 2)
@@ -248,8 +253,8 @@ install_from (FlatpakDir *dir,
if (!flatpak_dir_ensure_repo (clone, cancellable, error))
return FALSE;
- parts = g_strsplit (ref, "/", 0);
- g_print (_("Installing: %s\n"), parts[1]);
+ slash = strchr (ref, '/');
+ g_print (_("Installing: %s\n"), slash + 1);
if (!do_install (clone,
opt_no_pull,
@@ -262,22 +267,37 @@ install_from (FlatpakDir *dir,
return TRUE;
}
+static gboolean
+looks_like_branch (const char *branch)
+{
+ /* In particular, / is not a valid branch char, so
+ this lets us distinguish full or partial refs as
+ non-branches. */
+ if (!flatpak_is_valid_branch (branch, NULL))
+ return FALSE;
+
+ /* Dots are allowed in branches, but not really used much, while
+ they are required for app ids, so thats a good check to
+ distinguish the two */
+ if (strchr (branch, '.') != NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
gboolean
flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GError **error)
{
g_autoptr(GOptionContext) context = NULL;
g_autoptr(FlatpakDir) dir = NULL;
const char *repository;
- const char *pref = NULL;
+ char **prefs = NULL;
+ int i, n_prefs;
const char *default_branch = NULL;
- g_autofree char *ref = NULL;
FlatpakKinds kinds;
- FlatpakKinds kind;
- g_autofree char *id = NULL;
- g_autofree char *arch = NULL;
- g_autofree char *branch = NULL;
+ g_autoptr(GPtrArray) refs = NULL;
- context = g_option_context_new (_("REPOSITORY NAME [BRANCH] - Install an application or runtime"));
+ context = g_option_context_new (_("REPOSITORY REF... - Install applications or runtimes"));
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
if (!flatpak_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error))
@@ -290,34 +310,58 @@ flatpak_builtin_install (int argc, char **argv, GCancellable *cancellable, GErro
return install_from (dir, context, argc, argv, cancellable, error);
if (argc < 3)
- return usage_error (context, _("REPOSITORY and NAME must be specified"), error);
+ return usage_error (context, _("REPOSITORY and REF must be specified"), error);
if (argc > 4)
return usage_error (context, _("Too many arguments"), error);
repository = argv[1];
- pref = argv[2];
- if (argc >= 4)
- default_branch = argv[3];
+ prefs = &argv[2];
+ n_prefs = argc - 2;
+
+ /* Backwards compat for old "REPOSITORY NAME [BRANCH]" argument version */
+ if (argc == 4 && looks_like_branch (argv[3]))
+ {
+ default_branch = argv[3];
+ n_prefs = 1;
+ }
kinds = flatpak_kinds_from_bools (opt_app, opt_runtime);
- if (!flatpak_split_partial_ref_arg (pref, kinds, opt_arch, default_branch,
- &kinds, &id, &arch, &branch, error))
- return FALSE;
+ refs = g_ptr_array_new_with_free_func (g_free);
+ for (i = 0; i < n_prefs; i++)
+ {
+ const char *pref = prefs[i];
+ FlatpakKinds matched_kinds;
+ g_autofree char *id = NULL;
+ g_autofree char *arch = NULL;
+ g_autofree char *branch = NULL;
+ FlatpakKinds kind;
+ g_autofree char *ref = NULL;
- ref = flatpak_dir_find_remote_ref (dir, repository, id, branch, arch,
- kinds, &kind, cancellable, error);
- if (ref == NULL)
- return FALSE;
+ if (!flatpak_split_partial_ref_arg (pref, kinds, opt_arch, default_branch,
+ &matched_kinds, &id, &arch, &branch, error))
+ return FALSE;
- if (!do_install (dir,
- opt_no_pull,
- opt_no_deploy,
- ref, repository,
- (const char **)opt_subpaths,
- cancellable, error))
- return FALSE;
+ ref = flatpak_dir_find_remote_ref (dir, repository, id, branch, arch,
+ matched_kinds, &kind, cancellable, error);
+ if (ref == NULL)
+ return FALSE;
+
+ g_ptr_array_add (refs, g_steal_pointer (&ref));
+ }
+
+ for (i = 0; i < refs->len; i++)
+ {
+ const char *ref = g_ptr_array_index (refs, i);
+ if (!do_install (dir,
+ opt_no_pull,
+ opt_no_deploy,
+ ref, repository,
+ (const char **)opt_subpaths,
+ cancellable, error))
+ return FALSE;
+ }
return TRUE;
}
@@ -327,8 +371,6 @@ 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;
FlatpakKinds kinds;
int i;
@@ -338,8 +380,6 @@ flatpak_complete_install (FlatpakCompletion *completion)
kinds = flatpak_kinds_from_bools (opt_app, opt_runtime);
- flatpak_completion_debug ("install argc %d", completion->argc);
-
switch (completion->argc)
{
case 0:
@@ -358,37 +398,8 @@ flatpak_complete_install (FlatpakCompletion *completion)
break;
- case 2: /* Name */
- refs = flatpak_dir_find_remote_refs (dir, completion->argv[1], NULL, NULL,
- opt_arch, kinds,
- 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, kinds,
- 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:
+ flatpak_complete_partial_remote_ref (completion, kinds, opt_arch, dir, completion->argv[1]);
break;
}
diff --git a/common/flatpak-utils.c b/common/flatpak-utils.c
index c4f52008..669765cf 100644
--- a/common/flatpak-utils.c
+++ b/common/flatpak-utils.c
@@ -684,15 +684,16 @@ flatpak_kinds_from_bools (gboolean app, gboolean runtime)
}
gboolean
-flatpak_split_partial_ref_arg (const char *partial_ref,
- FlatpakKinds default_kinds,
- const char *default_arch,
- const char *default_branch,
- FlatpakKinds *out_kinds,
- char **out_id,
- char **out_arch,
- char **out_branch,
- GError **error)
+_flatpak_split_partial_ref_arg (const char *partial_ref,
+ gboolean validate,
+ FlatpakKinds default_kinds,
+ const char *default_arch,
+ const char *default_branch,
+ FlatpakKinds *out_kinds,
+ char **out_id,
+ char **out_arch,
+ char **out_branch,
+ GError **error)
{
const char *id_start = NULL;
const char *id_end = NULL;
@@ -723,7 +724,7 @@ flatpak_split_partial_ref_arg (const char *partial_ref,
id_end = next_element (&partial_ref);
id = g_strndup (id_start, id_end - id_start);
- if (!flatpak_is_valid_name (id, &local_error))
+ if (validate && !flatpak_is_valid_name (id, &local_error))
return flatpak_fail (error, "Invalid id %s: %s", id, local_error->message);
arch_start = partial_ref;
@@ -740,7 +741,7 @@ flatpak_split_partial_ref_arg (const char *partial_ref,
else
branch = g_strdup (default_branch);
- if (branch != NULL && !flatpak_is_valid_branch (branch, &local_error))
+ if (validate && branch != NULL && !flatpak_is_valid_branch (branch, &local_error))
return flatpak_fail (error, "Invalid branch %s: %s", branch, local_error->message);
if (out_kinds)
@@ -755,6 +756,52 @@ flatpak_split_partial_ref_arg (const char *partial_ref,
return TRUE;
}
+gboolean
+flatpak_split_partial_ref_arg (const char *partial_ref,
+ FlatpakKinds default_kinds,
+ const char *default_arch,
+ const char *default_branch,
+ FlatpakKinds *out_kinds,
+ char **out_id,
+ char **out_arch,
+ char **out_branch,
+ GError **error)
+{
+ return _flatpak_split_partial_ref_arg (partial_ref,
+ TRUE,
+ default_kinds,
+ default_arch,
+ default_branch,
+ out_kinds,
+ out_id,
+ out_arch,
+ out_branch,
+ error);
+}
+
+gboolean
+flatpak_split_partial_ref_arg_novalidate (const char *partial_ref,
+ FlatpakKinds default_kinds,
+ const char *default_arch,
+ const char *default_branch,
+ FlatpakKinds *out_kinds,
+ char **out_id,
+ char **out_arch,
+ char **out_branch)
+{
+ return _flatpak_split_partial_ref_arg (partial_ref,
+ FALSE,
+ default_kinds,
+ default_arch,
+ default_branch,
+ out_kinds,
+ out_id,
+ out_arch,
+ out_branch,
+ NULL);
+}
+
+
char *
flatpak_compose_ref (gboolean app,
const char *name,
@@ -3851,6 +3898,91 @@ flatpak_complete_ref (FlatpakCompletion *completion,
}
}
+int
+find_current_element (const char *str)
+{
+ int count = 0;
+
+ if (g_str_has_prefix (str, "app/"))
+ str += strlen ("app/");
+ else if (g_str_has_prefix (str, "runtime/"))
+ str += strlen ("runtime/");
+
+ while (str != NULL && count <= 3)
+ {
+ str = strchr (str, '/');
+ count++;
+ if (str != NULL)
+ str = str + 1;
+ }
+
+ return count;
+}
+
+void
+flatpak_complete_partial_remote_ref (FlatpakCompletion *completion,
+ FlatpakKinds kinds,
+ const char *only_arch,
+ FlatpakDir *dir,
+ const char *remote)
+{
+ FlatpakKinds matched_kinds;
+ const char *pref;
+ g_autofree char *id = NULL;
+ g_autofree char *arch = NULL;
+ g_autofree char *branch = NULL;
+ g_auto(GStrv) refs = NULL;
+ int element;
+ const char *cur_parts[4] = { NULL };
+ g_autoptr(GError) error = NULL;
+ int i;
+
+ pref = completion->cur;
+ element = find_current_element (pref);
+
+ flatpak_split_partial_ref_arg_novalidate (pref, kinds,
+ NULL, NULL,
+ &matched_kinds, &id, &arch, &branch);
+
+ cur_parts[1] = id;
+ cur_parts[2] = arch ? arch : "";
+ cur_parts[3] = branch ? branch : "";
+
+ refs = flatpak_dir_find_remote_refs (dir, completion->argv[1],
+ (element > 1) ? id : NULL,
+ (element > 3) ? branch : NULL,
+ (element > 2 )? arch : only_arch,
+ matched_kinds, NULL, &error);
+ if (refs == NULL)
+ flatpak_completion_debug ("find remote refs error: %s", error->message);
+ for (i = 0; refs != NULL && refs[i] != NULL; i++)
+ {
+ int j;
+ g_autoptr(GString) comp = NULL;
+ g_auto(GStrv) parts = flatpak_decompose_ref (refs[i], NULL);
+ if (parts == NULL)
+ continue;
+
+ if (!g_str_has_prefix (parts[element], cur_parts[element]))
+ continue;
+
+ comp = g_string_new (pref);
+ g_string_append (comp, parts[element] + strlen (cur_parts[element]));
+
+ /* Only complete on the last part if the user explicitly adds a / */
+ if (element >= 2)
+ {
+ for (j = element + 1; j < 4; j++)
+ {
+ g_string_append (comp, "/");
+ g_string_append (comp, parts[j]);
+ }
+ }
+
+ flatpak_complete_word (completion, "%s", comp->str);
+ }
+}
+
static gboolean
switch_already_in_line (FlatpakCompletion *completion,
GOptionEntry *entry)
diff --git a/common/flatpak-utils.h b/common/flatpak-utils.h
index ef04e55d..df5a0326 100644
--- a/common/flatpak-utils.h
+++ b/common/flatpak-utils.h
@@ -100,6 +100,14 @@ gboolean flatpak_split_partial_ref_arg (const char *partial_ref,
char **out_arch,
char **out_branch,
GError **error);
+gboolean flatpak_split_partial_ref_arg_novalidate (const char *partial_ref,
+ FlatpakKinds default_kinds,
+ const char *default_arch,
+ const char *default_branch,
+ FlatpakKinds *out_kinds,
+ char **out_id,
+ char **out_arch,
+ char **out_branch);
char * flatpak_compose_ref (gboolean app,
const char *name,
@@ -478,6 +486,11 @@ void flatpak_complete_word (FlatpakCompletion *completion,
...);
void flatpak_complete_ref (FlatpakCompletion *completion,
OstreeRepo *repo);
+void flatpak_complete_partial_remote_ref (FlatpakCompletion *completion,
+ FlatpakKinds kinds,
+ const char *only_arch,
+ FlatpakDir *dir,
+ const char *remote);
void flatpak_complete_file (FlatpakCompletion *completion);
void flatpak_complete_dir (FlatpakCompletion *completion);
void flatpak_complete_options (FlatpakCompletion *completion,
diff --git a/doc/flatpak-install.xml b/doc/flatpak-install.xml
index 49919bc0..0482bc22 100644
--- a/doc/flatpak-install.xml
+++ b/doc/flatpak-install.xml
@@ -33,8 +33,7 @@
flatpak install
OPTION
REMOTE
- NAME
- BRANCH
+ REF
flatpak install
@@ -52,14 +51,21 @@
Installs an application or runtime. REMOTE must name
- an existing remote and NAME is the name of the
- application or runtime to install. Optionally, BRANCH can
- be specified to install a branch other than the default branch. This required
- if there are multiple matches in the selected remote.
+ an existing remote and REF is a reference to the
+ application or runtime to install.
- By default this looks for both apps and runtime with the given NAME in
- the specified REMOTE, but you can limit this by using the --app or --runtime option.
+ Each REF arguments is a full or partial indentifier in the
+ flatpak ref format, which looks like "(app|runtime)/ID/ARCH/BRANCH". All elements
+ except ID are optional and can be left out, including the slashes,
+ so most of the time you need only specify ID. Any part left out will be matched
+ against what is in the remote, and if there are multiple matches an error message
+ will list the alternatives.
+
+
+ By default this looks for both apps and runtime with the given REF in
+ the specified REMOTE, but you can limit this by using the --app or
+ --runtime option, or by supplying the initial element in the REF.
Note that flatpak allows one to have multiple branches of an application and runtimes
@@ -134,7 +140,7 @@
- The architecture to install for.
+ The default architecture to install for, if not given explicitly in the REF.
@@ -175,15 +181,14 @@
- Only look for an app with the given name.
+ Assume that all REFs are apps if not explicitly specified.
-
- Only look for an runtime with the given name.
+ Assume that all REFs are runtimes if not explicitly specified.