/* utils.c * * Copyright 2018 Matthias Clasen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 600 #endif #include #include #ifdef HAVE_SNAP #include #endif #include #include #include #include "utils.h" static gint ftw_remove_cb (const gchar *path, const struct stat *sb, gint typeflags, struct FTW *ftwbuf) { remove (path); return 0; } static void file_remove_thread_func (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { GFile *file = source_object; g_autofree gchar *path = g_file_get_path (file); nftw (path, ftw_remove_cb, 20, FTW_PHYS | FTW_DEPTH); if (g_task_set_return_on_cancel (task, FALSE)) g_task_return_boolean (task, TRUE); } void file_remove_async (GFile *file, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) { g_autoptr(GTask) task = g_task_new (file, cancellable, callback, data); g_task_set_return_on_cancel (task, TRUE); g_task_run_in_thread (task, file_remove_thread_func); } gboolean file_remove_finish (GFile *file, GAsyncResult *result, GError **error) { g_return_val_if_fail (g_task_is_valid (result, file), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } static GPrivate size_key = G_PRIVATE_INIT (g_free); static gint ftw_size_cb (const gchar *path, const struct stat *sb, gint typeflags, struct FTW *ftwbuf) { guint64 *size = (guint64*)g_private_get (&size_key); if (typeflags == FTW_F) *size += sb->st_size; return 0; } static void file_size_thread_func (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { GFile *file = source_object; g_autofree gchar *path = g_file_get_path (file); guint64 *total; g_private_replace (&size_key, g_new0 (guint64, 1)); nftw (path, ftw_size_cb, 20, FTW_DEPTH); total = g_new0 (guint64, 1); *total = *(guint64*)g_private_get (&size_key); if (g_task_set_return_on_cancel (task, FALSE)) g_task_return_pointer (task, total, g_free); } void file_size_async (GFile *file, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) { g_autoptr(GTask) task = g_task_new (file, cancellable, callback, data); g_task_set_return_on_cancel (task, TRUE); g_task_run_in_thread (task, file_size_thread_func); } gboolean file_size_finish (GFile *file, GAsyncResult *result, guint64 *size, GError **error) { g_autofree guint64 *data = NULL; g_return_val_if_fail (g_task_is_valid (result, file), FALSE); data = g_task_propagate_pointer (G_TASK (result), error); if (data == NULL) return FALSE; if (size != NULL) *size = *data; return TRUE; } void listbox_remove_all (GtkListBox *listbox) { GtkWidget *child; while ((child = gtk_widget_get_first_child (GTK_WIDGET (listbox)))) gtk_list_box_remove (listbox, child); } static gchar * get_output_of (const gchar **argv) { g_autofree gchar *output = NULL; int status; if (!g_spawn_sync (NULL, (gchar**) argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, &output, NULL, &status, NULL)) return NULL; if (!g_spawn_check_wait_status (status, NULL)) return NULL; return g_steal_pointer (&output); } GKeyFile * get_flatpak_metadata (const gchar *app_id) { const gchar *argv[5] = { "flatpak", "info", "-m", "app", NULL }; g_autofree gchar *data = NULL; g_autoptr(GError) error = NULL; g_autoptr(GKeyFile) keyfile = NULL; argv[3] = app_id; data = get_output_of (argv); if (data == NULL) return NULL; keyfile = g_key_file_new (); if (!g_key_file_load_from_data (keyfile, data, -1, 0, &error)) { g_warning ("%s", error->message); return NULL; } return g_steal_pointer (&keyfile); } guint64 get_flatpak_app_size (const gchar *app_id) { const gchar *argv[5] = { "flatpak", "info", "-s", "app", NULL }; g_autofree gchar *data = NULL; guint64 factor; double val; argv[3] = app_id; data = get_output_of (argv); if (data == NULL) return 0; data = g_strstrip (data); if (g_str_has_suffix (data, "kB") || g_str_has_suffix (data, "kb")) factor = 1000; else if (g_str_has_suffix (data, "MB") || g_str_has_suffix (data, "Mb")) factor = 1000 * 1000; else if (g_str_has_suffix (data, "GB") || g_str_has_suffix (data, "Gb")) factor = 1000 * 1000 * 1000; else if (g_str_has_suffix (data, "KiB") || g_str_has_suffix (data, "Kib")) factor = 1024; else if (g_str_has_suffix (data, "MiB") || g_str_has_suffix (data, "Mib")) factor = 1024 * 1024; else if (g_str_has_suffix (data, "GiB") || g_str_has_suffix (data, "Gib")) factor = 1024 * 1024 * 1024; else factor = 1; val = g_ascii_strtod (data, NULL); return (guint64)(val * factor); } guint64 get_snap_app_size (const gchar *snap_name) { #ifdef HAVE_SNAP g_autoptr(SnapdClient) client = NULL; g_autoptr(SnapdSnap) snap = NULL; g_autoptr(GError) error = NULL; client = snapd_client_new (); snap = snapd_client_get_snap_sync (client, snap_name, NULL, &error); if (snap == NULL) { g_warning ("Failed to get snap size: %s", error->message); return 0; } return snapd_snap_get_installed_size (snap); #else return 0; #endif } char * get_app_id (GAppInfo *info) { gchar *app_id = g_strdup (g_app_info_get_id (info)); if (g_str_has_suffix (app_id, ".desktop")) app_id[strlen (app_id) - strlen (".desktop")] = '\0'; return app_id; }