| /* | |
| * Copyright 2015 Red Hat, Inc. | |
| * | |
| * SPDX-License-Identifier: LGPL-2.1-or-later | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library 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 | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
| * | |
| * Author: Matthias Clasen <mclasen@redhat.com> | |
| */ | |
| #include "config.h" | |
| #include <gio/gio.h> | |
| #include <gi18n.h> | |
| #ifdef G_OS_UNIX | |
| #include <gio/gunixmounts.h> | |
| #endif | |
| #include "gio-tool.h" | |
| static gboolean writable = FALSE; | |
| static gboolean filesystem = FALSE; | |
| static char *global_attributes = NULL; | |
| static gboolean nofollow_symlinks = FALSE; | |
| static const GOptionEntry entries[] = { | |
| { "query-writable", 'w', 0, G_OPTION_ARG_NONE, &writable, N_("List writable attributes"), NULL }, | |
| { "filesystem", 'f', 0, G_OPTION_ARG_NONE, &filesystem, N_("Get file system info"), NULL }, | |
| { "attributes", 'a', 0, G_OPTION_ARG_STRING, &global_attributes, N_("The attributes to get"), N_("ATTRIBUTES") }, | |
| { "nofollow-symlinks", 'n', 0, G_OPTION_ARG_NONE, &nofollow_symlinks, N_("Don’t follow symbolic links"), NULL }, | |
| G_OPTION_ENTRY_NULL | |
| }; | |
| static char * | |
| escape_string (const char *in) | |
| { | |
| GString *str; | |
| static char *hex_digits = "0123456789abcdef"; | |
| unsigned char c; | |
| str = g_string_new (""); | |
| while ((c = *in++) != 0) | |
| { | |
| if (c >= 32 && c <= 126 && c != '\\') | |
| g_string_append_c (str, c); | |
| else | |
| { | |
| g_string_append (str, "\\x"); | |
| g_string_append_c (str, hex_digits[(c >> 4) & 0xf]); | |
| g_string_append_c (str, hex_digits[c & 0xf]); | |
| } | |
| } | |
| return g_string_free (str, FALSE); | |
| } | |
| static char * | |
| flatten_string (const char *in) | |
| { | |
| GString *str; | |
| unsigned char c; | |
| str = g_string_new (""); | |
| while ((c = *in++) != 0) | |
| { | |
| switch (c) | |
| { | |
| case '\n': | |
| g_string_append (str, " ↵ "); | |
| break; | |
| default: | |
| g_string_append_c (str, c); | |
| break; | |
| } | |
| } | |
| return g_string_free (str, FALSE); | |
| } | |
| static void | |
| show_attributes (GFileInfo *info) | |
| { | |
| char **attributes; | |
| char *s, *flatten; | |
| int i; | |
| attributes = g_file_info_list_attributes (info, NULL); | |
| g_print (_("attributes:\n")); | |
| for (i = 0; attributes[i] != NULL; i++) | |
| { | |
| /* list the icons in order rather than displaying "GThemedIcon:0x8df7200" */ | |
| if (strcmp (attributes[i], "standard::icon") == 0 || | |
| strcmp (attributes[i], "standard::symbolic-icon") == 0) | |
| { | |
| GIcon *icon; | |
| int j; | |
| const char * const *names = NULL; | |
| if (strcmp (attributes[i], "standard::symbolic-icon") == 0) | |
| icon = g_file_info_get_symbolic_icon (info); | |
| else | |
| icon = g_file_info_get_icon (info); | |
| /* only look up names if GThemedIcon */ | |
| if (G_IS_THEMED_ICON(icon)) | |
| { | |
| names = g_themed_icon_get_names (G_THEMED_ICON (icon)); | |
| g_print (" %s: ", attributes[i]); | |
| for (j = 0; names[j] != NULL; j++) | |
| g_print ("%s%s", names[j], (names[j+1] == NULL)?"":", "); | |
| g_print ("\n"); | |
| } | |
| else | |
| { | |
| s = g_file_info_get_attribute_as_string (info, attributes[i]); | |
| g_print (" %s: %s\n", attributes[i], s); | |
| g_free (s); | |
| } | |
| } | |
| else | |
| { | |
| s = g_file_info_get_attribute_as_string (info, attributes[i]); | |
| flatten = flatten_string (s); | |
| g_print (" %s: %s\n", attributes[i], flatten); | |
| g_free (flatten); | |
| g_free (s); | |
| } | |
| } | |
| g_strfreev (attributes); | |
| } | |
| static void | |
| show_info (GFile *file, GFileInfo *info) | |
| { | |
| const char *name, *type; | |
| char *escaped, *uri, *flatten; | |
| goffset size; | |
| const char *path; | |
| #ifdef G_OS_UNIX | |
| GUnixMountEntry *entry; | |
| #endif | |
| name = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME) ? | |
| g_file_info_get_display_name (info) : NULL; | |
| if (name) | |
| { | |
| flatten = flatten_string (name); | |
| /* Translators: This is a noun and represents and attribute of a file */ | |
| g_print (_("display name: %s\n"), flatten); | |
| g_free (flatten); | |
| } | |
| name = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME) ? | |
| g_file_info_get_edit_name (info) : NULL; | |
| if (name) | |
| { | |
| flatten = flatten_string (name); | |
| /* Translators: This is a noun and represents and attribute of a file */ | |
| g_print (_("edit name: %s\n"), flatten); | |
| g_free (flatten); | |
| } | |
| name = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_NAME) ? | |
| g_file_info_get_name (info) : NULL; | |
| if (name) | |
| { | |
| escaped = escape_string (name); | |
| g_print (_("name: %s\n"), escaped); | |
| g_free (escaped); | |
| } | |
| if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_TYPE)) | |
| { | |
| type = file_type_to_string (g_file_info_get_file_type (info)); | |
| g_print (_("type: %s\n"), type); | |
| } | |
| if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) | |
| { | |
| size = g_file_info_get_size (info); | |
| g_print (_("size: ")); | |
| g_print (" %"G_GUINT64_FORMAT"\n", (guint64)size); | |
| } | |
| if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) && | |
| g_file_info_get_is_hidden (info)) | |
| g_print (_("hidden\n")); | |
| uri = g_file_get_uri (file); | |
| g_print (_("uri: %s\n"), uri); | |
| g_free (uri); | |
| path = g_file_peek_path (file); | |
| if (path) | |
| { | |
| flatten = flatten_string (path); | |
| g_print (_("local path: %s\n"), flatten); | |
| free (flatten); | |
| #ifdef G_OS_UNIX | |
| entry = g_unix_mount_at (path, NULL); | |
| if (entry == NULL) | |
| entry = g_unix_mount_for (path, NULL); | |
| if (entry != NULL) | |
| { | |
| gchar *device; | |
| const gchar *root; | |
| gchar *root_string = NULL; | |
| gchar *mount; | |
| gchar *fs; | |
| const gchar *options; | |
| gchar *options_string = NULL; | |
| device = g_strescape (g_unix_mount_get_device_path (entry), NULL); | |
| root = g_unix_mount_get_root_path (entry); | |
| if (root != NULL && g_strcmp0 (root, "/") != 0) | |
| { | |
| escaped = g_strescape (root, NULL); | |
| root_string = g_strconcat ("[", escaped, "]", NULL); | |
| g_free (escaped); | |
| } | |
| mount = g_strescape (g_unix_mount_get_mount_path (entry), NULL); | |
| fs = g_strescape (g_unix_mount_get_fs_type (entry), NULL); | |
| options = g_unix_mount_get_options (entry); | |
| if (options != NULL) | |
| { | |
| options_string = g_strescape (options, NULL); | |
| } | |
| g_print (_("unix mount: %s%s %s %s %s\n"), device, | |
| root_string ? root_string : "", mount, fs, | |
| options_string ? options_string : ""); | |
| g_free (device); | |
| g_free (root_string); | |
| g_free (mount); | |
| g_free (fs); | |
| g_free (options_string); | |
| g_unix_mount_free (entry); | |
| } | |
| #endif | |
| } | |
| show_attributes (info); | |
| } | |
| static gboolean | |
| query_info (GFile *file) | |
| { | |
| GFileQueryInfoFlags flags; | |
| GFileInfo *info; | |
| GError *error; | |
| if (file == NULL) | |
| return FALSE; | |
| if (global_attributes == NULL) | |
| global_attributes = "*"; | |
| flags = 0; | |
| if (nofollow_symlinks) | |
| flags |= G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS; | |
| error = NULL; | |
| if (filesystem) | |
| info = g_file_query_filesystem_info (file, global_attributes, NULL, &error); | |
| else | |
| info = g_file_query_info (file, global_attributes, flags, NULL, &error); | |
| if (info == NULL) | |
| { | |
| print_file_error (file, error->message); | |
| g_error_free (error); | |
| return FALSE; | |
| } | |
| if (filesystem) | |
| show_attributes (info); | |
| else | |
| show_info (file, info); | |
| g_object_unref (info); | |
| return TRUE; | |
| } | |
| static gboolean | |
| get_writable_info (GFile *file) | |
| { | |
| GFileAttributeInfoList *list; | |
| GError *error; | |
| int i; | |
| char *flags; | |
| if (file == NULL) | |
| return FALSE; | |
| error = NULL; | |
| list = g_file_query_settable_attributes (file, NULL, &error); | |
| if (list == NULL) | |
| { | |
| print_file_error (file, error->message); | |
| g_error_free (error); | |
| return FALSE; | |
| } | |
| if (list->n_infos > 0) | |
| { | |
| g_print (_("Settable attributes:\n")); | |
| for (i = 0; i < list->n_infos; i++) | |
| { | |
| flags = attribute_flags_to_string (list->infos[i].flags); | |
| g_print (" %s (%s%s%s)\n", | |
| list->infos[i].name, | |
| attribute_type_to_string (list->infos[i].type), | |
| (*flags != 0)?", ":"", flags); | |
| g_free (flags); | |
| } | |
| } | |
| g_file_attribute_info_list_unref (list); | |
| list = g_file_query_writable_namespaces (file, NULL, &error); | |
| if (list == NULL) | |
| { | |
| print_file_error (file, error->message); | |
| g_error_free (error); | |
| return FALSE; | |
| } | |
| if (list->n_infos > 0) | |
| { | |
| g_print (_("Writable attribute namespaces:\n")); | |
| for (i = 0; i < list->n_infos; i++) | |
| { | |
| flags = attribute_flags_to_string (list->infos[i].flags); | |
| g_print (" %s (%s%s%s)\n", | |
| list->infos[i].name, | |
| attribute_type_to_string (list->infos[i].type), | |
| (*flags != 0)?", ":"", flags); | |
| g_free (flags); | |
| } | |
| } | |
| g_file_attribute_info_list_unref (list); | |
| return TRUE; | |
| } | |
| int | |
| handle_info (int argc, char *argv[], gboolean do_help) | |
| { | |
| GOptionContext *context; | |
| gchar *param; | |
| GError *error = NULL; | |
| gboolean res; | |
| gint i; | |
| GFile *file; | |
| g_set_prgname ("gio info"); | |
| /* Translators: commandline placeholder */ | |
| param = g_strdup_printf ("%s…", _("LOCATION")); | |
| context = g_option_context_new (param); | |
| g_free (param); | |
| g_option_context_set_help_enabled (context, FALSE); | |
| g_option_context_set_summary (context, | |
| _("Show information about locations.")); | |
| g_option_context_set_description (context, | |
| _("gio info is similar to the traditional ls utility, but using GIO\n" | |
| "locations instead of local files: for example, you can use something\n" | |
| "like smb://server/resource/file.txt as location. File attributes can\n" | |
| "be specified with their GIO name, e.g. standard::icon, or just by\n" | |
| "namespace, e.g. unix, or by “*”, which matches all attributes")); | |
| g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); | |
| if (do_help) | |
| { | |
| show_help (context, NULL); | |
| g_option_context_free (context); | |
| return 0; | |
| } | |
| if (!g_option_context_parse (context, &argc, &argv, &error)) | |
| { | |
| show_help (context, error->message); | |
| g_error_free (error); | |
| g_option_context_free (context); | |
| return 1; | |
| } | |
| if (argc < 2) | |
| { | |
| show_help (context, _("No locations given")); | |
| g_option_context_free (context); | |
| return 1; | |
| } | |
| g_option_context_free (context); | |
| res = TRUE; | |
| for (i = 1; i < argc; i++) | |
| { | |
| file = g_file_new_for_commandline_arg (argv[i]); | |
| if (writable) | |
| res &= get_writable_info (file); | |
| else | |
| res &= query_info (file); | |
| g_object_unref (file); | |
| } | |
| return res ? 0 : 2; | |
| } |