static gboolean
print_documents (const char *app_id,
                 Column *columns,
                 GCancellable *cancellable,
                 GError **error)
{
  g_autoptr(GDBusConnection) session_bus = NULL;
  XdpDbusDocuments *documents;
  g_autoptr(GVariant) apps = NULL;
  g_autoptr(GVariantIter) iter = NULL;
  const char *id;
  const char *origin;
  FlatpakTablePrinter *printer;
  g_autofree char *mountpoint = NULL;
  gboolean need_perms = FALSE;
  int i;

  if (columns[0].name == NULL)
    return TRUE;

  session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, error);
  if (session_bus == NULL)
    return FALSE;

  documents = xdp_dbus_documents_proxy_new_sync (session_bus, 0,
                                                 "org.freedesktop.portal.Documents",
                                                 "/org/freedesktop/portal/documents",
                                                 NULL, error);

  if (documents == NULL)
    return FALSE;

  if (!xdp_dbus_documents_call_list_sync (documents, app_id ? app_id : "", &apps, NULL, error))
    return FALSE;

  if (!xdp_dbus_documents_call_get_mount_point_sync (documents, &mountpoint, NULL, error))
    return FALSE;

  printer = flatpak_table_printer_new ();
  flatpak_table_printer_set_columns (printer, columns);
  for (i = 0; columns[i].name; i++)
    {
      if (strcmp (columns[i].name, "permissions") == 0 ||
          strcmp (columns[i].name, "application") == 0)
        {
          need_perms = TRUE;
          break;
        }
    }

  iter = g_variant_iter_new (apps);
  while (g_variant_iter_next (iter, "{&s^&ay}", &id, &origin))
    {
      g_autoptr(GVariant) apps2 = NULL;
      g_autoptr(GVariantIter) iter2 = NULL;
      const char *app_id2 = NULL;
      const char **perms = NULL;
      gboolean just_perms = FALSE;

      if (need_perms)
        {
          g_autofree char *origin2 = NULL;
          if (!xdp_dbus_documents_call_info_sync (documents, id, &origin2, &apps2, NULL, error))
            return FALSE;
          iter2 = g_variant_iter_new (apps2);
        }

      while ((iter2 && g_variant_iter_next (iter2, "{&s^a&s}", &app_id2, &perms)) || !just_perms)
        {
          for (i = 0; columns[i].name; i++)
            {
              if (strcmp (columns[i].name, "application") == 0)
                flatpak_table_printer_add_column (printer, app_id2);
              else if (strcmp (columns[i].name, "permissions") == 0)
                {
                  g_autofree char *value = NULL;
                  if (perms)
                    value = g_strjoinv (" ", (char **)perms);
                  flatpak_table_printer_add_column (printer, value);
                }
              else if (just_perms)
                flatpak_table_printer_add_column (printer, "");
              else if (strcmp (columns[i].name, "id") == 0)
                flatpak_table_printer_add_column (printer, id);
              else if (strcmp (columns[i].name, "origin") == 0)
                flatpak_table_printer_add_column (printer, origin);
              else if (strcmp (columns[i].name, "path") == 0)
                {
                  g_autofree char *basename = g_path_get_basename (origin);
                  g_autofree char *path = g_build_filename (mountpoint, id, basename, NULL);
                  flatpak_table_printer_add_column (printer, path);
                }
            }

          flatpak_table_printer_finish_row (printer);

          just_perms = TRUE;
        }
    }

  flatpak_table_printer_print (printer);
  flatpak_table_printer_free (printer);

  return TRUE;
}
gboolean
flatpak_builtin_ls_remote (int argc, char **argv, GCancellable *cancellable, GError **error)
{
  g_autoptr(GOptionContext) context = NULL;
  g_autoptr(FlatpakDir) dir = NULL;
  g_autoptr(GHashTable) refs = NULL;
  GHashTableIter iter;
  gpointer key;
  gpointer value;
  g_autoptr(GHashTable) names = NULL;
  guint n_keys;
  g_autofree const char **keys = NULL;
  int i;
  const char *repository;
  const char **arches = flatpak_get_arches ();
  const char *opt_arches[] = {NULL, NULL};

  context = g_option_context_new (_(" REMOTE - Show available runtimes and applications"));
  g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);

  if (!flatpak_option_context_parse (context, options, &argc, &argv, 0, &dir, cancellable, error))
    return FALSE;

  if (!opt_app && !opt_runtime)
    opt_app = opt_runtime = TRUE;

  if (argc < 2)
    return usage_error (context, _("REMOTE must be specified"), error);

  if (argc > 2)
    return usage_error (context, _("Too many arguments"), error);

  repository = argv[1];


  if (!flatpak_dir_list_remote_refs (dir,
                                     repository,
                                     &refs,
                                     cancellable, error))
    return FALSE;

  names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);

  if (opt_arch != NULL)
    {
      if (strcmp (opt_arch, "*") == 0)
        arches = NULL;
      else
        {
          opt_arches[0] = opt_arch;
          arches = opt_arches;
        }
    }

  g_hash_table_iter_init (&iter, refs);
  while (g_hash_table_iter_next (&iter, &key, &value))
    {
      const char *ref = key;
      const char *checksum = value;
      const char *name = NULL;
      g_auto(GStrv) parts = NULL;

      parts = flatpak_decompose_ref (ref, NULL);
      if (parts == NULL)
        {
          g_debug ("Invalid remote ref %s\n", ref);
          continue;
        }

      if (opt_only_updates)
        {
          g_autofree char *deployed = NULL;

          deployed = flatpak_dir_read_active (dir, ref, cancellable);
          if (deployed == NULL)
            continue;

          if (g_strcmp0 (deployed, checksum) == 0)
            continue;
        }

      if (arches != NULL && !g_strv_contains (arches, parts[2]))
        continue;

      if (strcmp (parts[0], "runtime") == 0 && !opt_runtime)
        continue;

      if (strcmp (parts[0], "app") == 0 && !opt_app)
        continue;

      if (!opt_show_details)
        name = parts[1];
      else
        name = ref;

      if (g_hash_table_lookup (names, name) == NULL)
        g_hash_table_insert (names, g_strdup (name), g_strdup (checksum));
    }

  keys = (const char **) g_hash_table_get_keys_as_array (names, &n_keys);
  g_qsort_with_data (keys, n_keys, sizeof (char *), (GCompareDataFunc) flatpak_strcmp0_ptr, NULL);

  FlatpakTablePrinter *printer = flatpak_table_printer_new ();

  for (i = 0; i < n_keys; i++)
    {
      flatpak_table_printer_add_column (printer, keys[i]);
      if (opt_show_details)
        {
          g_autofree char *value = NULL;

          value = g_strdup ((char *) g_hash_table_lookup (names, keys[i]));
          value[MIN (strlen (value), 12)] = 0;
          flatpak_table_printer_add_column (printer, value);
        }
      flatpak_table_printer_finish_row (printer);
    }

  flatpak_table_printer_print (printer);
  flatpak_table_printer_free (printer);

  return TRUE;
}
static gboolean
enumerate_instances (Column *columns, GError **error)
{
  g_autoptr(GPtrArray) instances = NULL;
  FlatpakTablePrinter *printer;
  int i, j;

  if (columns[0].name == NULL)
    return TRUE;

  printer = flatpak_table_printer_new ();
  flatpak_table_printer_set_columns (printer, columns);

  instances = flatpak_instance_get_all ();
  if (instances->len == 0)
    {
      /* nothing to show */
      return TRUE;
    }

  for (j = 0; j < instances->len; j++)
    {
      FlatpakInstance *instance = (FlatpakInstance *) g_ptr_array_index (instances, j);

      flatpak_table_printer_add_column (printer, flatpak_instance_get_id (instance));

      for (i = 0; columns[i].name; i++)
        {
          if (strcmp (columns[i].name, "pid") == 0)
            {
              g_autofree char *pid = g_strdup_printf ("%d", flatpak_instance_get_pid (instance));
              flatpak_table_printer_add_column (printer, pid);
            }
          else if (strcmp (columns[i].name, "child-pid") == 0)
            {
              g_autofree char *pid = g_strdup_printf ("%d", flatpak_instance_get_child_pid (instance));
              flatpak_table_printer_add_column (printer, pid);
            }
          else if (strcmp (columns[i].name, "application") == 0)
            flatpak_table_printer_add_column (printer, flatpak_instance_get_app (instance));
          else if (strcmp (columns[i].name, "arch") == 0)
            flatpak_table_printer_add_column (printer, flatpak_instance_get_arch (instance));
          else if (strcmp (columns[i].name, "branch") == 0)
            flatpak_table_printer_add_column (printer, flatpak_instance_get_branch (instance));
          else if (strcmp (columns[i].name, "commit") == 0)
            flatpak_table_printer_add_column_len (printer,
                                                  flatpak_instance_get_commit (instance),
                                                  12);
          else if (strcmp (columns[i].name, "runtime") == 0)
            {
              const char *full_ref = flatpak_instance_get_runtime (instance);
              if (full_ref != NULL)
                {
                  g_auto(GStrv) ref = flatpak_decompose_ref (full_ref, NULL);
                  flatpak_table_printer_add_column (printer, ref[1]);
                }
            }
          else if (strcmp (columns[i].name, "runtime-branch") == 0)
            {
              const char *full_ref = flatpak_instance_get_runtime (instance);
              if (full_ref != NULL)
                {
                  g_auto(GStrv) ref = flatpak_decompose_ref (full_ref, NULL);
                  flatpak_table_printer_add_column (printer, ref[3]);
                }
            }
          else if (strcmp (columns[i].name, "runtime-commit") == 0)
            flatpak_table_printer_add_column_len (printer,
                                                  flatpak_instance_get_runtime_commit (instance),
                                                  12);
        }

      flatpak_table_printer_finish_row (printer);
    }

  flatpak_table_printer_print (printer);
  flatpak_table_printer_free (printer);

  return TRUE;
}