1 /*
2 * Copyright 2015 Red Hat, Inc.
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Matthias Clasen <mclasen@redhat.com>
20 */
21
22 #include "config.h"
23
24 #include <gio/gio.h>
25 #include <gi18n.h>
26
27 #ifdef G_OS_UNIX
28 #include <gio/gunixmounts.h>
29 #endif
30
31 #include "gio-tool.h"
32
33 static gboolean writable = FALSE;
34 static gboolean filesystem = FALSE;
35 static char *global_attributes = NULL;
36 static gboolean nofollow_symlinks = FALSE;
37
38 static const GOptionEntry entries[] = {
39 { "query-writable", 'w', 0, G_OPTION_ARG_NONE, &writable, N_("List writable attributes"), NULL },
40 { "filesystem", 'f', 0, G_OPTION_ARG_NONE, &filesystem, N_("Get file system info"), NULL },
41 { "attributes", 'a', 0, G_OPTION_ARG_STRING, &global_attributes, N_("The attributes to get"), N_("ATTRIBUTES") },
42 { "nofollow-symlinks", 'n', 0, G_OPTION_ARG_NONE, &nofollow_symlinks, N_("Don’t follow symbolic links"), NULL },
43 G_OPTION_ENTRY_NULL
44 };
45
46 static char *
47 escape_string (const char *in)
48 {
49 GString *str;
50 static char *hex_digits = "0123456789abcdef";
51 unsigned char c;
52
53
54 str = g_string_new ("");
55
56 while ((c = *in++) != 0)
57 {
58 if (c >= 32 && c <= 126 && c != '\\')
59 g_string_append_c (str, c);
60 else
61 {
62 g_string_append (str, "\\x");
63 g_string_append_c (str, hex_digits[(c >> 4) & 0xf]);
64 g_string_append_c (str, hex_digits[c & 0xf]);
65 }
66 }
67
68 return g_string_free (str, FALSE);
69 }
70
71 static char *
72 flatten_string (const char *in)
73 {
74 GString *str;
75 unsigned char c;
76
77 str = g_string_new ("");
78
79 while ((c = *in++) != 0)
80 {
81 switch (c)
82 {
83 case '\n':
84 g_string_append (str, " ↵ ");
85 break;
86
87 default:
88 g_string_append_c (str, c);
89 break;
90 }
91 }
92
93 return g_string_free (str, FALSE);
94 }
95
96 static void
97 show_attributes (GFileInfo *info)
98 {
99 char **attributes;
100 char *s, *flatten;
101 int i;
102
103 attributes = g_file_info_list_attributes (info, NULL);
104
105 g_print (_("attributes:\n"));
106 for (i = 0; attributes[i] != NULL; i++)
107 {
108 /* list the icons in order rather than displaying "GThemedIcon:0x8df7200" */
109 if (strcmp (attributes[i], "standard::icon") == 0 ||
110 strcmp (attributes[i], "standard::symbolic-icon") == 0)
111 {
112 GIcon *icon;
113 int j;
114 const char * const *names = NULL;
115
116 if (strcmp (attributes[i], "standard::symbolic-icon") == 0)
117 icon = g_file_info_get_symbolic_icon (info);
118 else
119 icon = g_file_info_get_icon (info);
120
121 /* only look up names if GThemedIcon */
122 if (G_IS_THEMED_ICON(icon))
123 {
124 names = g_themed_icon_get_names (G_THEMED_ICON (icon));
125 g_print (" %s: ", attributes[i]);
126 for (j = 0; names[j] != NULL; j++)
127 g_print ("%s%s", names[j], (names[j+1] == NULL)?"":", ");
128 g_print ("\n");
129 }
130 else
131 {
132 s = g_file_info_get_attribute_as_string (info, attributes[i]);
133 g_print (" %s: %s\n", attributes[i], s);
134 g_free (s);
135 }
136 }
137 else
138 {
139 s = g_file_info_get_attribute_as_string (info, attributes[i]);
140 flatten = flatten_string (s);
141 g_print (" %s: %s\n", attributes[i], flatten);
142 g_free (flatten);
143 g_free (s);
144 }
145 }
146 g_strfreev (attributes);
147 }
148
149 static void
150 show_info (GFile *file, GFileInfo *info)
151 {
152 const char *name, *type;
153 char *escaped, *uri, *flatten;
154 goffset size;
155 const char *path;
156 #ifdef G_OS_UNIX
157 GUnixMountEntry *entry;
158 #endif
159
160 name = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME) ?
161 g_file_info_get_display_name (info) : NULL;
162 if (name)
163 {
164 flatten = flatten_string (name);
165 /* Translators: This is a noun and represents and attribute of a file */
166 g_print (_("display name: %s\n"), flatten);
167 g_free (flatten);
168 }
169
170 name = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_EDIT_NAME) ?
171 g_file_info_get_edit_name (info) : NULL;
172 if (name)
173 {
174 flatten = flatten_string (name);
175 /* Translators: This is a noun and represents and attribute of a file */
176 g_print (_("edit name: %s\n"), flatten);
177 g_free (flatten);
178 }
179
180 name = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_NAME) ?
181 g_file_info_get_name (info) : NULL;
182 if (name)
183 {
184 escaped = escape_string (name);
185 g_print (_("name: %s\n"), escaped);
186 g_free (escaped);
187 }
188
189 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_TYPE))
190 {
191 type = file_type_to_string (g_file_info_get_file_type (info));
192 g_print (_("type: %s\n"), type);
193 }
194
195 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
196 {
197 size = g_file_info_get_size (info);
198 g_print (_("size: "));
199 g_print (" %"G_GUINT64_FORMAT"\n", (guint64)size);
200 }
201
202 if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) &&
203 g_file_info_get_is_hidden (info))
204 g_print (_("hidden\n"));
205
206 uri = g_file_get_uri (file);
207 g_print (_("uri: %s\n"), uri);
208 g_free (uri);
209
210 path = g_file_peek_path (file);
211 if (path)
212 {
213 flatten = flatten_string (path);
214 g_print (_("local path: %s\n"), flatten);
215 free (flatten);
216
217 #ifdef G_OS_UNIX
218 entry = g_unix_mount_at (path, NULL);
219 if (entry == NULL)
220 entry = g_unix_mount_for (path, NULL);
221 if (entry != NULL)
222 {
223 gchar *device;
224 const gchar *root;
225 gchar *root_string = NULL;
226 gchar *mount;
227 gchar *fs;
228 const gchar *options;
229 gchar *options_string = NULL;
230
231 device = g_strescape (g_unix_mount_get_device_path (entry), NULL);
232 root = g_unix_mount_get_root_path (entry);
233 if (root != NULL && g_strcmp0 (root, "/") != 0)
234 {
235 escaped = g_strescape (root, NULL);
236 root_string = g_strconcat ("[", escaped, "]", NULL);
237 g_free (escaped);
238 }
239 mount = g_strescape (g_unix_mount_get_mount_path (entry), NULL);
240 fs = g_strescape (g_unix_mount_get_fs_type (entry), NULL);
241
242 options = g_unix_mount_get_options (entry);
243 if (options != NULL)
244 {
245 options_string = g_strescape (options, NULL);
246 }
247
248 g_print (_("unix mount: %s%s %s %s %s\n"), device,
249 root_string ? root_string : "", mount, fs,
250 options_string ? options_string : "");
251
252 g_free (device);
253 g_free (root_string);
254 g_free (mount);
255 g_free (fs);
256 g_free (options_string);
257
258 g_unix_mount_free (entry);
259 }
260 #endif
261 }
262
263 show_attributes (info);
264 }
265
266 static gboolean
267 query_info (GFile *file)
268 {
269 GFileQueryInfoFlags flags;
270 GFileInfo *info;
271 GError *error;
272
273 if (file == NULL)
274 return FALSE;
275
276 if (global_attributes == NULL)
277 global_attributes = "*";
278
279 flags = 0;
280 if (nofollow_symlinks)
281 flags |= G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS;
282
283 error = NULL;
284 if (filesystem)
285 info = g_file_query_filesystem_info (file, global_attributes, NULL, &error);
286 else
287 info = g_file_query_info (file, global_attributes, flags, NULL, &error);
288
289 if (info == NULL)
290 {
291 print_file_error (file, error->message);
292 g_error_free (error);
293 return FALSE;
294 }
295
296 if (filesystem)
297 show_attributes (info);
298 else
299 show_info (file, info);
300
301 g_object_unref (info);
302
303 return TRUE;
304 }
305
306 static gboolean
307 get_writable_info (GFile *file)
308 {
309 GFileAttributeInfoList *list;
310 GError *error;
311 int i;
312 char *flags;
313
314 if (file == NULL)
315 return FALSE;
316
317 error = NULL;
318
319 list = g_file_query_settable_attributes (file, NULL, &error);
320 if (list == NULL)
321 {
322 print_file_error (file, error->message);
323 g_error_free (error);
324 return FALSE;
325 }
326
327 if (list->n_infos > 0)
328 {
329 g_print (_("Settable attributes:\n"));
330 for (i = 0; i < list->n_infos; i++)
331 {
332 flags = attribute_flags_to_string (list->infos[i].flags);
333 g_print (" %s (%s%s%s)\n",
334 list->infos[i].name,
335 attribute_type_to_string (list->infos[i].type),
336 (*flags != 0)?", ":"", flags);
337 g_free (flags);
338 }
339 }
340
341 g_file_attribute_info_list_unref (list);
342
343 list = g_file_query_writable_namespaces (file, NULL, &error);
344 if (list == NULL)
345 {
346 print_file_error (file, error->message);
347 g_error_free (error);
348 return FALSE;
349 }
350
351 if (list->n_infos > 0)
352 {
353 g_print (_("Writable attribute namespaces:\n"));
354 for (i = 0; i < list->n_infos; i++)
355 {
356 flags = attribute_flags_to_string (list->infos[i].flags);
357 g_print (" %s (%s%s%s)\n",
358 list->infos[i].name,
359 attribute_type_to_string (list->infos[i].type),
360 (*flags != 0)?", ":"", flags);
361 g_free (flags);
362 }
363 }
364
365 g_file_attribute_info_list_unref (list);
366
367 return TRUE;
368 }
369
370 int
371 handle_info (int argc, char *argv[], gboolean do_help)
372 {
373 GOptionContext *context;
374 gchar *param;
375 GError *error = NULL;
376 gboolean res;
377 gint i;
378 GFile *file;
379
380 g_set_prgname ("gio info");
381
382 /* Translators: commandline placeholder */
383 param = g_strdup_printf ("%s…", _("LOCATION"));
384 context = g_option_context_new (param);
385 g_free (param);
386 g_option_context_set_help_enabled (context, FALSE);
387 g_option_context_set_summary (context,
388 _("Show information about locations."));
389 g_option_context_set_description (context,
390 _("gio info is similar to the traditional ls utility, but using GIO\n"
391 "locations instead of local files: for example, you can use something\n"
392 "like smb://server/resource/file.txt as location. File attributes can\n"
393 "be specified with their GIO name, e.g. standard::icon, or just by\n"
394 "namespace, e.g. unix, or by “*”, which matches all attributes"));
395 g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
396
397 if (do_help)
398 {
399 show_help (context, NULL);
400 g_option_context_free (context);
401 return 0;
402 }
403
404 if (!g_option_context_parse (context, &argc, &argv, &error))
405 {
406 show_help (context, error->message);
407 g_error_free (error);
408 g_option_context_free (context);
409 return 1;
410 }
411
412 if (argc < 2)
413 {
414 show_help (context, _("No locations given"));
415 g_option_context_free (context);
416 return 1;
417 }
418
419 g_option_context_free (context);
420
421 res = TRUE;
422 for (i = 1; i < argc; i++)
423 {
424 file = g_file_new_for_commandline_arg (argv[i]);
425 if (writable)
426 res &= get_writable_info (file);
427 else
428 res &= query_info (file);
429 g_object_unref (file);
430 }
431
432 return res ? 0 : 2;
433 }