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