nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ |
2 | |||
3 | /* GIO - GLib Input, Output and Streaming Library |
||
4 | * |
||
5 | * Copyright (C) 2006-2007 Red Hat, Inc. |
||
6 | * |
||
7 | * This library is free software; you can redistribute it and/or |
||
8 | * modify it under the terms of the GNU Lesser General Public |
||
9 | * License as published by the Free Software Foundation; either |
||
10 | * version 2 of the License, or (at your option) any later version. |
||
11 | * |
||
12 | * This library is distributed in the hope that it will be useful, |
||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
15 | * Lesser General Public License for more details. |
||
16 | * |
||
17 | * You should have received a copy of the GNU Lesser General |
||
18 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
19 | * |
||
20 | * Author: Alexander Larsson <alexl@redhat.com> |
||
21 | */ |
||
22 | |||
23 | #include "config.h" |
||
24 | #include <sys/types.h> |
||
25 | #include <stdlib.h> |
||
26 | #include <string.h> |
||
27 | #include <stdio.h> |
||
28 | #include "gcontenttypeprivate.h" |
||
29 | #include "gthemedicon.h" |
||
30 | #include "gicon.h" |
||
31 | #include "gfile.h" |
||
32 | #include "gfileenumerator.h" |
||
33 | #include "gfileinfo.h" |
||
34 | #include "glibintl.h" |
||
35 | |||
36 | |||
37 | /** |
||
38 | * SECTION:gcontenttype |
||
39 | * @short_description: Platform-specific content typing |
||
40 | * @include: gio/gio.h |
||
41 | * |
||
42 | * A content type is a platform specific string that defines the type |
||
43 | * of a file. On UNIX it is a |
||
44 | * [mime type](http://www.wikipedia.org/wiki/Internet_media_type) |
||
45 | * like "text/plain" or "image/png". |
||
46 | * On Win32 it is an extension string like ".doc", ".txt" or a perceived |
||
47 | * string like "audio". Such strings can be looked up in the registry at |
||
48 | * HKEY_CLASSES_ROOT. |
||
49 | **/ |
||
50 | |||
51 | #include <dirent.h> |
||
52 | |||
53 | #define XDG_PREFIX _gio_xdg |
||
54 | #include "xdgmime/xdgmime.h" |
||
55 | |||
56 | /* We lock this mutex whenever we modify global state in this module. */ |
||
57 | G_LOCK_DEFINE_STATIC (gio_xdgmime); |
||
58 | |||
59 | gsize |
||
60 | _g_unix_content_type_get_sniff_len (void) |
||
61 | { |
||
62 | gsize size; |
||
63 | |||
64 | G_LOCK (gio_xdgmime); |
||
65 | size = xdg_mime_get_max_buffer_extents (); |
||
66 | G_UNLOCK (gio_xdgmime); |
||
67 | |||
68 | return size; |
||
69 | } |
||
70 | |||
71 | gchar * |
||
72 | _g_unix_content_type_unalias (const gchar *type) |
||
73 | { |
||
74 | gchar *res; |
||
75 | |||
76 | G_LOCK (gio_xdgmime); |
||
77 | res = g_strdup (xdg_mime_unalias_mime_type (type)); |
||
78 | G_UNLOCK (gio_xdgmime); |
||
79 | |||
80 | return res; |
||
81 | } |
||
82 | |||
83 | gchar ** |
||
84 | _g_unix_content_type_get_parents (const gchar *type) |
||
85 | { |
||
86 | const gchar *umime; |
||
87 | gchar **parents; |
||
88 | GPtrArray *array; |
||
89 | int i; |
||
90 | |||
91 | array = g_ptr_array_new (); |
||
92 | |||
93 | G_LOCK (gio_xdgmime); |
||
94 | |||
95 | umime = xdg_mime_unalias_mime_type (type); |
||
96 | |||
97 | g_ptr_array_add (array, g_strdup (umime)); |
||
98 | |||
99 | parents = xdg_mime_list_mime_parents (umime); |
||
100 | for (i = 0; parents && parents[i] != NULL; i++) |
||
101 | g_ptr_array_add (array, g_strdup (parents[i])); |
||
102 | |||
103 | free (parents); |
||
104 | |||
105 | G_UNLOCK (gio_xdgmime); |
||
106 | |||
107 | g_ptr_array_add (array, NULL); |
||
108 | |||
109 | return (gchar **)g_ptr_array_free (array, FALSE); |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * g_content_type_equals: |
||
114 | * @type1: a content type string |
||
115 | * @type2: a content type string |
||
116 | * |
||
117 | * Compares two content types for equality. |
||
118 | * |
||
119 | * Returns: %TRUE if the two strings are identical or equivalent, |
||
120 | * %FALSE otherwise. |
||
121 | */ |
||
122 | gboolean |
||
123 | g_content_type_equals (const gchar *type1, |
||
124 | const gchar *type2) |
||
125 | { |
||
126 | gboolean res; |
||
127 | |||
128 | g_return_val_if_fail (type1 != NULL, FALSE); |
||
129 | g_return_val_if_fail (type2 != NULL, FALSE); |
||
130 | |||
131 | G_LOCK (gio_xdgmime); |
||
132 | res = xdg_mime_mime_type_equal (type1, type2); |
||
133 | G_UNLOCK (gio_xdgmime); |
||
134 | |||
135 | return res; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * g_content_type_is_a: |
||
140 | * @type: a content type string |
||
141 | * @supertype: a content type string |
||
142 | * |
||
143 | * Determines if @type is a subset of @supertype. |
||
144 | * |
||
145 | * Returns: %TRUE if @type is a kind of @supertype, |
||
146 | * %FALSE otherwise. |
||
147 | */ |
||
148 | gboolean |
||
149 | g_content_type_is_a (const gchar *type, |
||
150 | const gchar *supertype) |
||
151 | { |
||
152 | gboolean res; |
||
153 | |||
154 | g_return_val_if_fail (type != NULL, FALSE); |
||
155 | g_return_val_if_fail (supertype != NULL, FALSE); |
||
156 | |||
157 | G_LOCK (gio_xdgmime); |
||
158 | res = xdg_mime_mime_type_subclass (type, supertype); |
||
159 | G_UNLOCK (gio_xdgmime); |
||
160 | |||
161 | return res; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * g_content_type_is_unknown: |
||
166 | * @type: a content type string |
||
167 | * |
||
168 | * Checks if the content type is the generic "unknown" type. |
||
169 | * On UNIX this is the "application/octet-stream" mimetype, |
||
170 | * while on win32 it is "*". |
||
171 | * |
||
172 | * Returns: %TRUE if the type is the unknown type. |
||
173 | */ |
||
174 | gboolean |
||
175 | g_content_type_is_unknown (const gchar *type) |
||
176 | { |
||
177 | g_return_val_if_fail (type != NULL, FALSE); |
||
178 | |||
179 | return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0; |
||
180 | } |
||
181 | |||
182 | |||
183 | typedef enum { |
||
184 | MIME_TAG_TYPE_OTHER, |
||
185 | MIME_TAG_TYPE_COMMENT |
||
186 | } MimeTagType; |
||
187 | |||
188 | typedef struct { |
||
189 | int current_type; |
||
190 | int current_lang_level; |
||
191 | int comment_lang_level; |
||
192 | char *comment; |
||
193 | } MimeParser; |
||
194 | |||
195 | |||
196 | static int |
||
197 | language_level (const char *lang) |
||
198 | { |
||
199 | const char * const *lang_list; |
||
200 | int i; |
||
201 | |||
202 | /* The returned list is sorted from most desirable to least |
||
203 | desirable and always contains the default locale "C". */ |
||
204 | lang_list = g_get_language_names (); |
||
205 | |||
206 | for (i = 0; lang_list[i]; i++) |
||
207 | if (strcmp (lang_list[i], lang) == 0) |
||
208 | return 1000-i; |
||
209 | |||
210 | return 0; |
||
211 | } |
||
212 | |||
213 | static void |
||
214 | mime_info_start_element (GMarkupParseContext *context, |
||
215 | const gchar *element_name, |
||
216 | const gchar **attribute_names, |
||
217 | const gchar **attribute_values, |
||
218 | gpointer user_data, |
||
219 | GError **error) |
||
220 | { |
||
221 | int i; |
||
222 | const char *lang; |
||
223 | MimeParser *parser = user_data; |
||
224 | |||
225 | if (strcmp (element_name, "comment") == 0) |
||
226 | { |
||
227 | lang = "C"; |
||
228 | for (i = 0; attribute_names[i]; i++) |
||
229 | if (strcmp (attribute_names[i], "xml:lang") == 0) |
||
230 | { |
||
231 | lang = attribute_values[i]; |
||
232 | break; |
||
233 | } |
||
234 | |||
235 | parser->current_lang_level = language_level (lang); |
||
236 | parser->current_type = MIME_TAG_TYPE_COMMENT; |
||
237 | } |
||
238 | else |
||
239 | parser->current_type = MIME_TAG_TYPE_OTHER; |
||
240 | } |
||
241 | |||
242 | static void |
||
243 | mime_info_end_element (GMarkupParseContext *context, |
||
244 | const gchar *element_name, |
||
245 | gpointer user_data, |
||
246 | GError **error) |
||
247 | { |
||
248 | MimeParser *parser = user_data; |
||
249 | |||
250 | parser->current_type = MIME_TAG_TYPE_OTHER; |
||
251 | } |
||
252 | |||
253 | static void |
||
254 | mime_info_text (GMarkupParseContext *context, |
||
255 | const gchar *text, |
||
256 | gsize text_len, |
||
257 | gpointer user_data, |
||
258 | GError **error) |
||
259 | { |
||
260 | MimeParser *parser = user_data; |
||
261 | |||
262 | if (parser->current_type == MIME_TAG_TYPE_COMMENT && |
||
263 | parser->current_lang_level > parser->comment_lang_level) |
||
264 | { |
||
265 | g_free (parser->comment); |
||
266 | parser->comment = g_strndup (text, text_len); |
||
267 | parser->comment_lang_level = parser->current_lang_level; |
||
268 | } |
||
269 | } |
||
270 | |||
271 | static char * |
||
272 | load_comment_for_mime_helper (const char *dir, |
||
273 | const char *basename) |
||
274 | { |
||
275 | GMarkupParseContext *context; |
||
276 | char *filename, *data; |
||
277 | gsize len; |
||
278 | gboolean res; |
||
279 | MimeParser parse_data = {0}; |
||
280 | GMarkupParser parser = { |
||
281 | mime_info_start_element, |
||
282 | mime_info_end_element, |
||
283 | mime_info_text |
||
284 | }; |
||
285 | |||
286 | filename = g_build_filename (dir, "mime", basename, NULL); |
||
287 | |||
288 | res = g_file_get_contents (filename, &data, &len, NULL); |
||
289 | g_free (filename); |
||
290 | if (!res) |
||
291 | return NULL; |
||
292 | |||
293 | context = g_markup_parse_context_new (&parser, 0, &parse_data, NULL); |
||
294 | res = g_markup_parse_context_parse (context, data, len, NULL); |
||
295 | g_free (data); |
||
296 | g_markup_parse_context_free (context); |
||
297 | |||
298 | if (!res) |
||
299 | return NULL; |
||
300 | |||
301 | return parse_data.comment; |
||
302 | } |
||
303 | |||
304 | |||
305 | static char * |
||
306 | load_comment_for_mime (const char *mimetype) |
||
307 | { |
||
308 | const char * const* dirs; |
||
309 | char *basename; |
||
310 | char *comment; |
||
311 | int i; |
||
312 | |||
313 | basename = g_strdup_printf ("%s.xml", mimetype); |
||
314 | |||
315 | comment = load_comment_for_mime_helper (g_get_user_data_dir (), basename); |
||
316 | if (comment) |
||
317 | { |
||
318 | g_free (basename); |
||
319 | return comment; |
||
320 | } |
||
321 | |||
322 | dirs = g_get_system_data_dirs (); |
||
323 | |||
324 | for (i = 0; dirs[i] != NULL; i++) |
||
325 | { |
||
326 | comment = load_comment_for_mime_helper (dirs[i], basename); |
||
327 | if (comment) |
||
328 | { |
||
329 | g_free (basename); |
||
330 | return comment; |
||
331 | } |
||
332 | } |
||
333 | g_free (basename); |
||
334 | |||
335 | return g_strdup_printf (_("%s type"), mimetype); |
||
336 | } |
||
337 | |||
338 | /** |
||
339 | * g_content_type_get_description: |
||
340 | * @type: a content type string |
||
341 | * |
||
342 | * Gets the human readable description of the content type. |
||
343 | * |
||
344 | * Returns: a short description of the content type @type. Free the |
||
345 | * returned string with g_free() |
||
346 | */ |
||
347 | gchar * |
||
348 | g_content_type_get_description (const gchar *type) |
||
349 | { |
||
350 | static GHashTable *type_comment_cache = NULL; |
||
351 | gchar *comment; |
||
352 | |||
353 | g_return_val_if_fail (type != NULL, NULL); |
||
354 | |||
355 | G_LOCK (gio_xdgmime); |
||
356 | type = xdg_mime_unalias_mime_type (type); |
||
357 | |||
358 | if (type_comment_cache == NULL) |
||
359 | type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); |
||
360 | |||
361 | comment = g_hash_table_lookup (type_comment_cache, type); |
||
362 | comment = g_strdup (comment); |
||
363 | G_UNLOCK (gio_xdgmime); |
||
364 | |||
365 | if (comment != NULL) |
||
366 | return comment; |
||
367 | |||
368 | comment = load_comment_for_mime (type); |
||
369 | |||
370 | G_LOCK (gio_xdgmime); |
||
371 | g_hash_table_insert (type_comment_cache, |
||
372 | g_strdup (type), |
||
373 | g_strdup (comment)); |
||
374 | G_UNLOCK (gio_xdgmime); |
||
375 | |||
376 | return comment; |
||
377 | } |
||
378 | |||
379 | /** |
||
380 | * g_content_type_get_mime_type: |
||
381 | * @type: a content type string |
||
382 | * |
||
383 | * Gets the mime type for the content type, if one is registered. |
||
384 | * |
||
385 | * Returns: (nullable): the registered mime type for the given @type, |
||
386 | * or %NULL if unknown. |
||
387 | */ |
||
388 | char * |
||
389 | g_content_type_get_mime_type (const char *type) |
||
390 | { |
||
391 | g_return_val_if_fail (type != NULL, NULL); |
||
392 | |||
393 | return g_strdup (type); |
||
394 | } |
||
395 | |||
396 | static GIcon * |
||
397 | g_content_type_get_icon_internal (const gchar *type, |
||
398 | gboolean symbolic) |
||
399 | { |
||
400 | char *mimetype_icon; |
||
401 | char *generic_mimetype_icon = NULL; |
||
402 | char *q; |
||
403 | char *icon_names[6]; |
||
404 | int n = 0; |
||
405 | GIcon *themed_icon; |
||
406 | const char *xdg_icon; |
||
407 | int i; |
||
408 | |||
409 | g_return_val_if_fail (type != NULL, NULL); |
||
410 | |||
411 | G_LOCK (gio_xdgmime); |
||
412 | xdg_icon = xdg_mime_get_icon (type); |
||
413 | G_UNLOCK (gio_xdgmime); |
||
414 | |||
415 | if (xdg_icon) |
||
416 | icon_names[n++] = g_strdup (xdg_icon); |
||
417 | |||
418 | mimetype_icon = g_strdup (type); |
||
419 | while ((q = strchr (mimetype_icon, '/')) != NULL) |
||
420 | *q = '-'; |
||
421 | |||
422 | icon_names[n++] = mimetype_icon; |
||
423 | |||
424 | generic_mimetype_icon = g_content_type_get_generic_icon_name (type); |
||
425 | if (generic_mimetype_icon) |
||
426 | icon_names[n++] = generic_mimetype_icon; |
||
427 | |||
428 | if (symbolic) |
||
429 | { |
||
430 | for (i = 0; i < n; i++) |
||
431 | { |
||
432 | icon_names[n + i] = icon_names[i]; |
||
433 | icon_names[i] = g_strconcat (icon_names[i], "-symbolic", NULL); |
||
434 | } |
||
435 | |||
436 | n += n; |
||
437 | } |
||
438 | |||
439 | themed_icon = g_themed_icon_new_from_names (icon_names, n); |
||
440 | |||
441 | for (i = 0; i < n; i++) |
||
442 | g_free (icon_names[i]); |
||
443 | |||
444 | return themed_icon; |
||
445 | } |
||
446 | |||
447 | /** |
||
448 | * g_content_type_get_icon: |
||
449 | * @type: a content type string |
||
450 | * |
||
451 | * Gets the icon for a content type. |
||
452 | * |
||
453 | * Returns: (transfer full): #GIcon corresponding to the content type. Free the returned |
||
454 | * object with g_object_unref() |
||
455 | */ |
||
456 | GIcon * |
||
457 | g_content_type_get_icon (const gchar *type) |
||
458 | { |
||
459 | return g_content_type_get_icon_internal (type, FALSE); |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * g_content_type_get_symbolic_icon: |
||
464 | * @type: a content type string |
||
465 | * |
||
466 | * Gets the symbolic icon for a content type. |
||
467 | * |
||
468 | * Returns: (transfer full): symbolic #GIcon corresponding to the content type. |
||
469 | * Free the returned object with g_object_unref() |
||
470 | * |
||
471 | * Since: 2.34 |
||
472 | */ |
||
473 | GIcon * |
||
474 | g_content_type_get_symbolic_icon (const gchar *type) |
||
475 | { |
||
476 | return g_content_type_get_icon_internal (type, TRUE); |
||
477 | } |
||
478 | |||
479 | /** |
||
480 | * g_content_type_get_generic_icon_name: |
||
481 | * @type: a content type string |
||
482 | * |
||
483 | * Gets the generic icon name for a content type. |
||
484 | * |
||
485 | * See the |
||
486 | * [shared-mime-info](http://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec) |
||
487 | * specification for more on the generic icon name. |
||
488 | * |
||
489 | * Returns: (allow-none): the registered generic icon name for the given @type, |
||
490 | * or %NULL if unknown. Free with g_free() |
||
491 | * |
||
492 | * Since: 2.34 |
||
493 | */ |
||
494 | gchar * |
||
495 | g_content_type_get_generic_icon_name (const gchar *type) |
||
496 | { |
||
497 | const gchar *xdg_icon_name; |
||
498 | gchar *icon_name; |
||
499 | |||
500 | G_LOCK (gio_xdgmime); |
||
501 | xdg_icon_name = xdg_mime_get_generic_icon (type); |
||
502 | G_UNLOCK (gio_xdgmime); |
||
503 | |||
504 | if (!xdg_icon_name) |
||
505 | { |
||
506 | const char *p; |
||
507 | const char *suffix = "-x-generic"; |
||
508 | |||
509 | p = strchr (type, '/'); |
||
510 | if (p == NULL) |
||
511 | p = type + strlen (type); |
||
512 | |||
513 | icon_name = g_malloc (p - type + strlen (suffix) + 1); |
||
514 | memcpy (icon_name, type, p - type); |
||
515 | memcpy (icon_name + (p - type), suffix, strlen (suffix)); |
||
516 | icon_name[(p - type) + strlen (suffix)] = 0; |
||
517 | } |
||
518 | else |
||
519 | { |
||
520 | icon_name = g_strdup (xdg_icon_name); |
||
521 | } |
||
522 | |||
523 | return icon_name; |
||
524 | } |
||
525 | |||
526 | /** |
||
527 | * g_content_type_can_be_executable: |
||
528 | * @type: a content type string |
||
529 | * |
||
530 | * Checks if a content type can be executable. Note that for instance |
||
531 | * things like text files can be executables (i.e. scripts and batch files). |
||
532 | * |
||
533 | * Returns: %TRUE if the file type corresponds to a type that |
||
534 | * can be executable, %FALSE otherwise. |
||
535 | */ |
||
536 | gboolean |
||
537 | g_content_type_can_be_executable (const gchar *type) |
||
538 | { |
||
539 | g_return_val_if_fail (type != NULL, FALSE); |
||
540 | |||
541 | if (g_content_type_is_a (type, "application/x-executable") || |
||
542 | g_content_type_is_a (type, "text/plain")) |
||
543 | return TRUE; |
||
544 | |||
545 | return FALSE; |
||
546 | } |
||
547 | |||
548 | static gboolean |
||
549 | looks_like_text (const guchar *data, gsize data_size) |
||
550 | { |
||
551 | gsize i; |
||
552 | char c; |
||
553 | |||
554 | for (i = 0; i < data_size; i++) |
||
555 | { |
||
556 | c = data[i]; |
||
557 | |||
558 | if (g_ascii_iscntrl (c) && |
||
559 | !g_ascii_isspace (c) && |
||
560 | c != '\b') |
||
561 | return FALSE; |
||
562 | } |
||
563 | return TRUE; |
||
564 | } |
||
565 | |||
566 | /** |
||
567 | * g_content_type_from_mime_type: |
||
568 | * @mime_type: a mime type string |
||
569 | * |
||
570 | * Tries to find a content type based on the mime type name. |
||
571 | * |
||
572 | * Returns: (nullable): Newly allocated string with content type or |
||
573 | * %NULL. Free with g_free() |
||
574 | * |
||
575 | * Since: 2.18 |
||
576 | **/ |
||
577 | gchar * |
||
578 | g_content_type_from_mime_type (const gchar *mime_type) |
||
579 | { |
||
580 | char *umime; |
||
581 | |||
582 | g_return_val_if_fail (mime_type != NULL, NULL); |
||
583 | |||
584 | G_LOCK (gio_xdgmime); |
||
585 | /* mime type and content type are same on unixes */ |
||
586 | umime = g_strdup (xdg_mime_unalias_mime_type (mime_type)); |
||
587 | G_UNLOCK (gio_xdgmime); |
||
588 | |||
589 | return umime; |
||
590 | } |
||
591 | |||
592 | /** |
||
593 | * g_content_type_guess: |
||
594 | * @filename: (allow-none): a string, or %NULL |
||
595 | * @data: (allow-none) (array length=data_size): a stream of data, or %NULL |
||
596 | * @data_size: the size of @data |
||
597 | * @result_uncertain: (allow-none) (out): return location for the certainty |
||
598 | * of the result, or %NULL |
||
599 | * |
||
600 | * Guesses the content type based on example data. If the function is |
||
601 | * uncertain, @result_uncertain will be set to %TRUE. Either @filename |
||
602 | * or @data may be %NULL, in which case the guess will be based solely |
||
603 | * on the other argument. |
||
604 | * |
||
605 | * Returns: a string indicating a guessed content type for the |
||
606 | * given data. Free with g_free() |
||
607 | */ |
||
608 | gchar * |
||
609 | g_content_type_guess (const gchar *filename, |
||
610 | const guchar *data, |
||
611 | gsize data_size, |
||
612 | gboolean *result_uncertain) |
||
613 | { |
||
614 | char *basename; |
||
615 | const char *name_mimetypes[10], *sniffed_mimetype; |
||
616 | char *mimetype; |
||
617 | int i; |
||
618 | int n_name_mimetypes; |
||
619 | int sniffed_prio; |
||
620 | |||
621 | sniffed_prio = 0; |
||
622 | n_name_mimetypes = 0; |
||
623 | sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN; |
||
624 | |||
625 | if (result_uncertain) |
||
626 | *result_uncertain = FALSE; |
||
627 | |||
628 | /* our test suite and potentially other code used -1 in the past, which is |
||
629 | * not documented and not allowed; guard against that */ |
||
630 | g_return_val_if_fail (data_size != (gsize) -1, g_strdup (XDG_MIME_TYPE_UNKNOWN)); |
||
631 | |||
632 | G_LOCK (gio_xdgmime); |
||
633 | |||
634 | if (filename) |
||
635 | { |
||
636 | i = strlen (filename); |
||
637 | if (filename[i - 1] == '/') |
||
638 | { |
||
639 | name_mimetypes[0] = "inode/directory"; |
||
640 | name_mimetypes[1] = NULL; |
||
641 | n_name_mimetypes = 1; |
||
642 | if (result_uncertain) |
||
643 | *result_uncertain = TRUE; |
||
644 | } |
||
645 | else |
||
646 | { |
||
647 | basename = g_path_get_basename (filename); |
||
648 | n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10); |
||
649 | g_free (basename); |
||
650 | } |
||
651 | } |
||
652 | |||
653 | /* Got an extension match, and no conflicts. This is it. */ |
||
654 | if (n_name_mimetypes == 1) |
||
655 | { |
||
656 | gchar *s = g_strdup (name_mimetypes[0]); |
||
657 | G_UNLOCK (gio_xdgmime); |
||
658 | return s; |
||
659 | } |
||
660 | |||
661 | if (data) |
||
662 | { |
||
663 | sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio); |
||
664 | if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN && |
||
665 | data && |
||
666 | looks_like_text (data, data_size)) |
||
667 | sniffed_mimetype = "text/plain"; |
||
668 | |||
669 | /* For security reasons we don't ever want to sniff desktop files |
||
670 | * where we know the filename and it doesn't have a .desktop extension. |
||
671 | * This is because desktop files allow executing any application and |
||
672 | * we don't want to make it possible to hide them looking like something |
||
673 | * else. |
||
674 | */ |
||
675 | if (filename != NULL && |
||
676 | strcmp (sniffed_mimetype, "application/x-desktop") == 0) |
||
677 | sniffed_mimetype = "text/plain"; |
||
678 | } |
||
679 | |||
680 | if (n_name_mimetypes == 0) |
||
681 | { |
||
682 | if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN && |
||
683 | result_uncertain) |
||
684 | *result_uncertain = TRUE; |
||
685 | |||
686 | mimetype = g_strdup (sniffed_mimetype); |
||
687 | } |
||
688 | else |
||
689 | { |
||
690 | mimetype = NULL; |
||
691 | if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN) |
||
692 | { |
||
693 | if (sniffed_prio >= 80) /* High priority sniffing match, use that */ |
||
694 | mimetype = g_strdup (sniffed_mimetype); |
||
695 | else |
||
696 | { |
||
697 | /* There are conflicts between the name matches and we |
||
698 | * have a sniffed type, use that as a tie breaker. |
||
699 | */ |
||
700 | for (i = 0; i < n_name_mimetypes; i++) |
||
701 | { |
||
702 | if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype)) |
||
703 | { |
||
704 | /* This nametype match is derived from (or the same as) |
||
705 | * the sniffed type). This is probably it. |
||
706 | */ |
||
707 | mimetype = g_strdup (name_mimetypes[i]); |
||
708 | break; |
||
709 | } |
||
710 | } |
||
711 | } |
||
712 | } |
||
713 | |||
714 | if (mimetype == NULL) |
||
715 | { |
||
716 | /* Conflicts, and sniffed type was no help or not there. |
||
717 | * Guess on the first one |
||
718 | */ |
||
719 | mimetype = g_strdup (name_mimetypes[0]); |
||
720 | if (result_uncertain) |
||
721 | *result_uncertain = TRUE; |
||
722 | } |
||
723 | } |
||
724 | |||
725 | G_UNLOCK (gio_xdgmime); |
||
726 | |||
727 | return mimetype; |
||
728 | } |
||
729 | |||
730 | static void |
||
731 | enumerate_mimetypes_subdir (const char *dir, |
||
732 | const char *prefix, |
||
733 | GHashTable *mimetypes) |
||
734 | { |
||
735 | DIR *d; |
||
736 | struct dirent *ent; |
||
737 | char *mimetype; |
||
738 | |||
739 | d = opendir (dir); |
||
740 | if (d) |
||
741 | { |
||
742 | while ((ent = readdir (d)) != NULL) |
||
743 | { |
||
744 | if (g_str_has_suffix (ent->d_name, ".xml")) |
||
745 | { |
||
746 | mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name); |
||
747 | g_hash_table_replace (mimetypes, mimetype, NULL); |
||
748 | } |
||
749 | } |
||
750 | closedir (d); |
||
751 | } |
||
752 | } |
||
753 | |||
754 | static void |
||
755 | enumerate_mimetypes_dir (const char *dir, |
||
756 | GHashTable *mimetypes) |
||
757 | { |
||
758 | DIR *d; |
||
759 | struct dirent *ent; |
||
760 | char *mimedir; |
||
761 | char *name; |
||
762 | |||
763 | mimedir = g_build_filename (dir, "mime", NULL); |
||
764 | |||
765 | d = opendir (mimedir); |
||
766 | if (d) |
||
767 | { |
||
768 | while ((ent = readdir (d)) != NULL) |
||
769 | { |
||
770 | if (strcmp (ent->d_name, "packages") != 0) |
||
771 | { |
||
772 | name = g_build_filename (mimedir, ent->d_name, NULL); |
||
773 | if (g_file_test (name, G_FILE_TEST_IS_DIR)) |
||
774 | enumerate_mimetypes_subdir (name, ent->d_name, mimetypes); |
||
775 | g_free (name); |
||
776 | } |
||
777 | } |
||
778 | closedir (d); |
||
779 | } |
||
780 | |||
781 | g_free (mimedir); |
||
782 | } |
||
783 | |||
784 | /** |
||
785 | * g_content_types_get_registered: |
||
786 | * |
||
787 | * Gets a list of strings containing all the registered content types |
||
788 | * known to the system. The list and its data should be freed using |
||
789 | * g_list_free_full (list, g_free). |
||
790 | * |
||
791 | * Returns: (element-type utf8) (transfer full): list of the registered |
||
792 | * content types |
||
793 | */ |
||
794 | GList * |
||
795 | g_content_types_get_registered (void) |
||
796 | { |
||
797 | const char * const* dirs; |
||
798 | GHashTable *mimetypes; |
||
799 | GHashTableIter iter; |
||
800 | gpointer key; |
||
801 | int i; |
||
802 | GList *l; |
||
803 | |||
804 | mimetypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); |
||
805 | |||
806 | enumerate_mimetypes_dir (g_get_user_data_dir (), mimetypes); |
||
807 | dirs = g_get_system_data_dirs (); |
||
808 | |||
809 | for (i = 0; dirs[i] != NULL; i++) |
||
810 | enumerate_mimetypes_dir (dirs[i], mimetypes); |
||
811 | |||
812 | l = NULL; |
||
813 | g_hash_table_iter_init (&iter, mimetypes); |
||
814 | while (g_hash_table_iter_next (&iter, &key, NULL)) |
||
815 | { |
||
816 | l = g_list_prepend (l, key); |
||
817 | g_hash_table_iter_steal (&iter); |
||
818 | } |
||
819 | |||
820 | g_hash_table_destroy (mimetypes); |
||
821 | |||
822 | return l; |
||
823 | } |
||
824 | |||
825 | |||
826 | /* tree magic data */ |
||
827 | static GList *tree_matches = NULL; |
||
828 | static gboolean need_reload = FALSE; |
||
829 | |||
830 | G_LOCK_DEFINE_STATIC (gio_treemagic); |
||
831 | |||
832 | typedef struct |
||
833 | { |
||
834 | gchar *path; |
||
835 | GFileType type; |
||
836 | guint match_case : 1; |
||
837 | guint executable : 1; |
||
838 | guint non_empty : 1; |
||
839 | guint on_disc : 1; |
||
840 | gchar *mimetype; |
||
841 | GList *matches; |
||
842 | } TreeMatchlet; |
||
843 | |||
844 | typedef struct |
||
845 | { |
||
846 | gchar *contenttype; |
||
847 | gint priority; |
||
848 | GList *matches; |
||
849 | } TreeMatch; |
||
850 | |||
851 | |||
852 | static void |
||
853 | tree_matchlet_free (TreeMatchlet *matchlet) |
||
854 | { |
||
855 | g_list_free_full (matchlet->matches, (GDestroyNotify) tree_matchlet_free); |
||
856 | g_free (matchlet->path); |
||
857 | g_free (matchlet->mimetype); |
||
858 | g_slice_free (TreeMatchlet, matchlet); |
||
859 | } |
||
860 | |||
861 | static void |
||
862 | tree_match_free (TreeMatch *match) |
||
863 | { |
||
864 | g_list_free_full (match->matches, (GDestroyNotify) tree_matchlet_free); |
||
865 | g_free (match->contenttype); |
||
866 | g_slice_free (TreeMatch, match); |
||
867 | } |
||
868 | |||
869 | static TreeMatch * |
||
870 | parse_header (gchar *line) |
||
871 | { |
||
872 | gint len; |
||
873 | gchar *s; |
||
874 | TreeMatch *match; |
||
875 | |||
876 | len = strlen (line); |
||
877 | |||
878 | if (line[0] != '[' || line[len - 1] != ']') |
||
879 | return NULL; |
||
880 | |||
881 | line[len - 1] = 0; |
||
882 | s = strchr (line, ':'); |
||
883 | |||
884 | match = g_slice_new0 (TreeMatch); |
||
885 | match->priority = atoi (line + 1); |
||
886 | match->contenttype = g_strdup (s + 1); |
||
887 | |||
888 | return match; |
||
889 | } |
||
890 | |||
891 | static TreeMatchlet * |
||
892 | parse_match_line (gchar *line, |
||
893 | gint *depth) |
||
894 | { |
||
895 | gchar *s, *p; |
||
896 | TreeMatchlet *matchlet; |
||
897 | gchar **parts; |
||
898 | gint i; |
||
899 | |||
900 | matchlet = g_slice_new0 (TreeMatchlet); |
||
901 | |||
902 | if (line[0] == '>') |
||
903 | { |
||
904 | *depth = 0; |
||
905 | s = line; |
||
906 | } |
||
907 | else |
||
908 | { |
||
909 | *depth = atoi (line); |
||
910 | s = strchr (line, '>'); |
||
911 | } |
||
912 | s += 2; |
||
913 | p = strchr (s, '"'); |
||
914 | *p = 0; |
||
915 | |||
916 | matchlet->path = g_strdup (s); |
||
917 | s = p + 1; |
||
918 | parts = g_strsplit (s, ",", 0); |
||
919 | if (strcmp (parts[0], "=file") == 0) |
||
920 | matchlet->type = G_FILE_TYPE_REGULAR; |
||
921 | else if (strcmp (parts[0], "=directory") == 0) |
||
922 | matchlet->type = G_FILE_TYPE_DIRECTORY; |
||
923 | else if (strcmp (parts[0], "=link") == 0) |
||
924 | matchlet->type = G_FILE_TYPE_SYMBOLIC_LINK; |
||
925 | else |
||
926 | matchlet->type = G_FILE_TYPE_UNKNOWN; |
||
927 | for (i = 1; parts[i]; i++) |
||
928 | { |
||
929 | if (strcmp (parts[i], "executable") == 0) |
||
930 | matchlet->executable = 1; |
||
931 | else if (strcmp (parts[i], "match-case") == 0) |
||
932 | matchlet->match_case = 1; |
||
933 | else if (strcmp (parts[i], "non-empty") == 0) |
||
934 | matchlet->non_empty = 1; |
||
935 | else if (strcmp (parts[i], "on-disc") == 0) |
||
936 | matchlet->on_disc = 1; |
||
937 | else |
||
938 | matchlet->mimetype = g_strdup (parts[i]); |
||
939 | } |
||
940 | |||
941 | g_strfreev (parts); |
||
942 | |||
943 | return matchlet; |
||
944 | } |
||
945 | |||
946 | static gint |
||
947 | cmp_match (gconstpointer a, gconstpointer b) |
||
948 | { |
||
949 | const TreeMatch *aa = (const TreeMatch *)a; |
||
950 | const TreeMatch *bb = (const TreeMatch *)b; |
||
951 | |||
952 | return bb->priority - aa->priority; |
||
953 | } |
||
954 | |||
955 | static void |
||
956 | insert_match (TreeMatch *match) |
||
957 | { |
||
958 | tree_matches = g_list_insert_sorted (tree_matches, match, cmp_match); |
||
959 | } |
||
960 | |||
961 | static void |
||
962 | insert_matchlet (TreeMatch *match, |
||
963 | TreeMatchlet *matchlet, |
||
964 | gint depth) |
||
965 | { |
||
966 | if (depth == 0) |
||
967 | match->matches = g_list_append (match->matches, matchlet); |
||
968 | else |
||
969 | { |
||
970 | GList *last; |
||
971 | TreeMatchlet *m; |
||
972 | |||
973 | last = g_list_last (match->matches); |
||
974 | if (!last) |
||
975 | { |
||
976 | tree_matchlet_free (matchlet); |
||
977 | g_warning ("can't insert tree matchlet at depth %d", depth); |
||
978 | return; |
||
979 | } |
||
980 | |||
981 | m = (TreeMatchlet *) last->data; |
||
982 | while (--depth > 0) |
||
983 | { |
||
984 | last = g_list_last (m->matches); |
||
985 | if (!last) |
||
986 | { |
||
987 | tree_matchlet_free (matchlet); |
||
988 | g_warning ("can't insert tree matchlet at depth %d", depth); |
||
989 | return; |
||
990 | } |
||
991 | |||
992 | m = (TreeMatchlet *) last->data; |
||
993 | } |
||
994 | m->matches = g_list_append (m->matches, matchlet); |
||
995 | } |
||
996 | } |
||
997 | |||
998 | static void |
||
999 | read_tree_magic_from_directory (const gchar *prefix) |
||
1000 | { |
||
1001 | gchar *filename; |
||
1002 | gchar *text; |
||
1003 | gsize len; |
||
1004 | gchar **lines; |
||
1005 | gint i; |
||
1006 | TreeMatch *match; |
||
1007 | TreeMatchlet *matchlet; |
||
1008 | gint depth; |
||
1009 | |||
1010 | filename = g_build_filename (prefix, "mime", "treemagic", NULL); |
||
1011 | |||
1012 | if (g_file_get_contents (filename, &text, &len, NULL)) |
||
1013 | { |
||
1014 | if (strcmp (text, "MIME-TreeMagic") == 0) |
||
1015 | { |
||
1016 | lines = g_strsplit (text + strlen ("MIME-TreeMagic") + 2, "\n", 0); |
||
1017 | match = NULL; |
||
1018 | for (i = 0; lines[i] && lines[i][0]; i++) |
||
1019 | { |
||
1020 | if (lines[i][0] == '[') |
||
1021 | { |
||
1022 | match = parse_header (lines[i]); |
||
1023 | insert_match (match); |
||
1024 | } |
||
1025 | else if (match != NULL) |
||
1026 | { |
||
1027 | matchlet = parse_match_line (lines[i], &depth); |
||
1028 | insert_matchlet (match, matchlet, depth); |
||
1029 | } |
||
1030 | else |
||
1031 | { |
||
1032 | g_warning ("%s: header corrupt; skipping\n", filename); |
||
1033 | break; |
||
1034 | } |
||
1035 | } |
||
1036 | |||
1037 | g_strfreev (lines); |
||
1038 | } |
||
1039 | else |
||
1040 | g_warning ("%s: header not found, skipping\n", filename); |
||
1041 | |||
1042 | g_free (text); |
||
1043 | } |
||
1044 | |||
1045 | g_free (filename); |
||
1046 | } |
||
1047 | |||
1048 | |||
1049 | static void |
||
1050 | xdg_mime_reload (void *user_data) |
||
1051 | { |
||
1052 | need_reload = TRUE; |
||
1053 | } |
||
1054 | |||
1055 | static void |
||
1056 | tree_magic_shutdown (void) |
||
1057 | { |
||
1058 | g_list_free_full (tree_matches, (GDestroyNotify) tree_match_free); |
||
1059 | tree_matches = NULL; |
||
1060 | } |
||
1061 | |||
1062 | static void |
||
1063 | tree_magic_init (void) |
||
1064 | { |
||
1065 | static gboolean initialized = FALSE; |
||
1066 | const gchar *dir; |
||
1067 | const gchar * const * dirs; |
||
1068 | int i; |
||
1069 | |||
1070 | if (!initialized) |
||
1071 | { |
||
1072 | initialized = TRUE; |
||
1073 | |||
1074 | xdg_mime_register_reload_callback (xdg_mime_reload, NULL, NULL); |
||
1075 | need_reload = TRUE; |
||
1076 | } |
||
1077 | |||
1078 | if (need_reload) |
||
1079 | { |
||
1080 | need_reload = FALSE; |
||
1081 | |||
1082 | tree_magic_shutdown (); |
||
1083 | |||
1084 | dir = g_get_user_data_dir (); |
||
1085 | read_tree_magic_from_directory (dir); |
||
1086 | dirs = g_get_system_data_dirs (); |
||
1087 | for (i = 0; dirs[i]; i++) |
||
1088 | read_tree_magic_from_directory (dirs[i]); |
||
1089 | } |
||
1090 | } |
||
1091 | |||
1092 | /* a filtering enumerator */ |
||
1093 | |||
1094 | typedef struct |
||
1095 | { |
||
1096 | gchar *path; |
||
1097 | gint depth; |
||
1098 | gboolean ignore_case; |
||
1099 | gchar **components; |
||
1100 | gchar **case_components; |
||
1101 | GFileEnumerator **enumerators; |
||
1102 | GFile **children; |
||
1103 | } Enumerator; |
||
1104 | |||
1105 | static gboolean |
||
1106 | component_match (Enumerator *e, |
||
1107 | gint depth, |
||
1108 | const gchar *name) |
||
1109 | { |
||
1110 | gchar *case_folded, *key; |
||
1111 | gboolean found; |
||
1112 | |||
1113 | if (strcmp (name, e->components[depth]) == 0) |
||
1114 | return TRUE; |
||
1115 | |||
1116 | if (!e->ignore_case) |
||
1117 | return FALSE; |
||
1118 | |||
1119 | case_folded = g_utf8_casefold (name, -1); |
||
1120 | key = g_utf8_collate_key (case_folded, -1); |
||
1121 | |||
1122 | found = strcmp (key, e->case_components[depth]) == 0; |
||
1123 | |||
1124 | g_free (case_folded); |
||
1125 | g_free (key); |
||
1126 | |||
1127 | return found; |
||
1128 | } |
||
1129 | |||
1130 | static GFile * |
||
1131 | next_match_recurse (Enumerator *e, |
||
1132 | gint depth) |
||
1133 | { |
||
1134 | GFile *file; |
||
1135 | GFileInfo *info; |
||
1136 | const gchar *name; |
||
1137 | |||
1138 | while (TRUE) |
||
1139 | { |
||
1140 | if (e->enumerators[depth] == NULL) |
||
1141 | { |
||
1142 | if (depth > 0) |
||
1143 | { |
||
1144 | file = next_match_recurse (e, depth - 1); |
||
1145 | if (file) |
||
1146 | { |
||
1147 | e->children[depth] = file; |
||
1148 | e->enumerators[depth] = g_file_enumerate_children (file, |
||
1149 | G_FILE_ATTRIBUTE_STANDARD_NAME, |
||
1150 | G_FILE_QUERY_INFO_NONE, |
||
1151 | NULL, |
||
1152 | NULL); |
||
1153 | } |
||
1154 | } |
||
1155 | if (e->enumerators[depth] == NULL) |
||
1156 | return NULL; |
||
1157 | } |
||
1158 | |||
1159 | while ((info = g_file_enumerator_next_file (e->enumerators[depth], NULL, NULL))) |
||
1160 | { |
||
1161 | name = g_file_info_get_name (info); |
||
1162 | if (component_match (e, depth, name)) |
||
1163 | { |
||
1164 | file = g_file_get_child (e->children[depth], name); |
||
1165 | g_object_unref (info); |
||
1166 | return file; |
||
1167 | } |
||
1168 | g_object_unref (info); |
||
1169 | } |
||
1170 | |||
1171 | g_object_unref (e->enumerators[depth]); |
||
1172 | e->enumerators[depth] = NULL; |
||
1173 | g_object_unref (e->children[depth]); |
||
1174 | e->children[depth] = NULL; |
||
1175 | } |
||
1176 | } |
||
1177 | |||
1178 | static GFile * |
||
1179 | enumerator_next (Enumerator *e) |
||
1180 | { |
||
1181 | return next_match_recurse (e, e->depth - 1); |
||
1182 | } |
||
1183 | |||
1184 | static Enumerator * |
||
1185 | enumerator_new (GFile *root, |
||
1186 | const char *path, |
||
1187 | gboolean ignore_case) |
||
1188 | { |
||
1189 | Enumerator *e; |
||
1190 | gint i; |
||
1191 | gchar *case_folded; |
||
1192 | |||
1193 | e = g_new0 (Enumerator, 1); |
||
1194 | e->path = g_strdup (path); |
||
1195 | e->ignore_case = ignore_case; |
||
1196 | |||
1197 | e->components = g_strsplit (e->path, G_DIR_SEPARATOR_S, -1); |
||
1198 | e->depth = g_strv_length (e->components); |
||
1199 | if (e->ignore_case) |
||
1200 | { |
||
1201 | e->case_components = g_new0 (char *, e->depth + 1); |
||
1202 | for (i = 0; e->components[i]; i++) |
||
1203 | { |
||
1204 | case_folded = g_utf8_casefold (e->components[i], -1); |
||
1205 | e->case_components[i] = g_utf8_collate_key (case_folded, -1); |
||
1206 | g_free (case_folded); |
||
1207 | } |
||
1208 | } |
||
1209 | |||
1210 | e->children = g_new0 (GFile *, e->depth); |
||
1211 | e->children[0] = g_object_ref (root); |
||
1212 | e->enumerators = g_new0 (GFileEnumerator *, e->depth); |
||
1213 | e->enumerators[0] = g_file_enumerate_children (root, |
||
1214 | G_FILE_ATTRIBUTE_STANDARD_NAME, |
||
1215 | G_FILE_QUERY_INFO_NONE, |
||
1216 | NULL, |
||
1217 | NULL); |
||
1218 | |||
1219 | return e; |
||
1220 | } |
||
1221 | |||
1222 | static void |
||
1223 | enumerator_free (Enumerator *e) |
||
1224 | { |
||
1225 | gint i; |
||
1226 | |||
1227 | for (i = 0; i < e->depth; i++) |
||
1228 | { |
||
1229 | if (e->enumerators[i]) |
||
1230 | g_object_unref (e->enumerators[i]); |
||
1231 | if (e->children[i]) |
||
1232 | g_object_unref (e->children[i]); |
||
1233 | } |
||
1234 | |||
1235 | g_free (e->enumerators); |
||
1236 | g_free (e->children); |
||
1237 | g_strfreev (e->components); |
||
1238 | if (e->case_components) |
||
1239 | g_strfreev (e->case_components); |
||
1240 | g_free (e->path); |
||
1241 | g_free (e); |
||
1242 | } |
||
1243 | |||
1244 | static gboolean |
||
1245 | matchlet_match (TreeMatchlet *matchlet, |
||
1246 | GFile *root) |
||
1247 | { |
||
1248 | GFile *file; |
||
1249 | GFileInfo *info; |
||
1250 | gboolean result; |
||
1251 | const gchar *attrs; |
||
1252 | Enumerator *e; |
||
1253 | GList *l; |
||
1254 | |||
1255 | e = enumerator_new (root, matchlet->path, !matchlet->match_case); |
||
1256 | |||
1257 | do |
||
1258 | { |
||
1259 | file = enumerator_next (e); |
||
1260 | if (!file) |
||
1261 | { |
||
1262 | enumerator_free (e); |
||
1263 | return FALSE; |
||
1264 | } |
||
1265 | |||
1266 | if (matchlet->mimetype) |
||
1267 | attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE "," |
||
1268 | G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE "," |
||
1269 | G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE; |
||
1270 | else |
||
1271 | attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE "," |
||
1272 | G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE; |
||
1273 | info = g_file_query_info (file, |
||
1274 | attrs, |
||
1275 | G_FILE_QUERY_INFO_NONE, |
||
1276 | NULL, |
||
1277 | NULL); |
||
1278 | if (info) |
||
1279 | { |
||
1280 | result = TRUE; |
||
1281 | |||
1282 | if (matchlet->type != G_FILE_TYPE_UNKNOWN && |
||
1283 | g_file_info_get_file_type (info) != matchlet->type) |
||
1284 | result = FALSE; |
||
1285 | |||
1286 | if (matchlet->executable && |
||
1287 | !g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) |
||
1288 | result = FALSE; |
||
1289 | } |
||
1290 | else |
||
1291 | result = FALSE; |
||
1292 | |||
1293 | if (result && matchlet->non_empty) |
||
1294 | { |
||
1295 | GFileEnumerator *child_enum; |
||
1296 | GFileInfo *child_info; |
||
1297 | |||
1298 | child_enum = g_file_enumerate_children (file, |
||
1299 | G_FILE_ATTRIBUTE_STANDARD_NAME, |
||
1300 | G_FILE_QUERY_INFO_NONE, |
||
1301 | NULL, |
||
1302 | NULL); |
||
1303 | |||
1304 | if (child_enum) |
||
1305 | { |
||
1306 | child_info = g_file_enumerator_next_file (child_enum, NULL, NULL); |
||
1307 | if (child_info) |
||
1308 | g_object_unref (child_info); |
||
1309 | else |
||
1310 | result = FALSE; |
||
1311 | g_object_unref (child_enum); |
||
1312 | } |
||
1313 | else |
||
1314 | result = FALSE; |
||
1315 | } |
||
1316 | |||
1317 | if (result && matchlet->mimetype) |
||
1318 | { |
||
1319 | if (strcmp (matchlet->mimetype, g_file_info_get_content_type (info)) != 0) |
||
1320 | result = FALSE; |
||
1321 | } |
||
1322 | |||
1323 | if (info) |
||
1324 | g_object_unref (info); |
||
1325 | g_object_unref (file); |
||
1326 | } |
||
1327 | while (!result); |
||
1328 | |||
1329 | enumerator_free (e); |
||
1330 | |||
1331 | if (!matchlet->matches) |
||
1332 | return TRUE; |
||
1333 | |||
1334 | for (l = matchlet->matches; l; l = l->next) |
||
1335 | { |
||
1336 | TreeMatchlet *submatchlet; |
||
1337 | |||
1338 | submatchlet = l->data; |
||
1339 | if (matchlet_match (submatchlet, root)) |
||
1340 | return TRUE; |
||
1341 | } |
||
1342 | |||
1343 | return FALSE; |
||
1344 | } |
||
1345 | |||
1346 | static void |
||
1347 | match_match (TreeMatch *match, |
||
1348 | GFile *root, |
||
1349 | GPtrArray *types) |
||
1350 | { |
||
1351 | GList *l; |
||
1352 | |||
1353 | for (l = match->matches; l; l = l->next) |
||
1354 | { |
||
1355 | TreeMatchlet *matchlet = l->data; |
||
1356 | if (matchlet_match (matchlet, root)) |
||
1357 | { |
||
1358 | g_ptr_array_add (types, g_strdup (match->contenttype)); |
||
1359 | break; |
||
1360 | } |
||
1361 | } |
||
1362 | } |
||
1363 | |||
1364 | /** |
||
1365 | * g_content_type_guess_for_tree: |
||
1366 | * @root: the root of the tree to guess a type for |
||
1367 | * |
||
1368 | * Tries to guess the type of the tree with root @root, by |
||
1369 | * looking at the files it contains. The result is an array |
||
1370 | * of content types, with the best guess coming first. |
||
1371 | * |
||
1372 | * The types returned all have the form x-content/foo, e.g. |
||
1373 | * x-content/audio-cdda (for audio CDs) or x-content/image-dcf |
||
1374 | * (for a camera memory card). See the |
||
1375 | * [shared-mime-info](http://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec) |
||
1376 | * specification for more on x-content types. |
||
1377 | * |
||
1378 | * This function is useful in the implementation of |
||
1379 | * g_mount_guess_content_type(). |
||
1380 | * |
||
1381 | * Returns: (transfer full) (array zero-terminated=1): an %NULL-terminated |
||
1382 | * array of zero or more content types. Free with g_strfreev() |
||
1383 | * |
||
1384 | * Since: 2.18 |
||
1385 | */ |
||
1386 | gchar ** |
||
1387 | g_content_type_guess_for_tree (GFile *root) |
||
1388 | { |
||
1389 | GPtrArray *types; |
||
1390 | GList *l; |
||
1391 | |||
1392 | types = g_ptr_array_new (); |
||
1393 | |||
1394 | G_LOCK (gio_treemagic); |
||
1395 | |||
1396 | tree_magic_init (); |
||
1397 | for (l = tree_matches; l; l = l->next) |
||
1398 | { |
||
1399 | TreeMatch *match = l->data; |
||
1400 | match_match (match, root, types); |
||
1401 | } |
||
1402 | |||
1403 | G_UNLOCK (gio_treemagic); |
||
1404 | |||
1405 | g_ptr_array_add (types, NULL); |
||
1406 | |||
1407 | return (gchar **)g_ptr_array_free (types, FALSE); |
||
1408 | } |