| /* |
| * 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; |
| } |