1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18 * Boston, MA 02111-1307, USA.
20 * Author: Alexander Larsson <alexl@redhat.com>
25 #include <sys/types.h>
30 #include "gcontenttypeprivate.h"
33 /* A content type is a platform specific string that defines the type
34 of a file. On unix it is a mime type, on win32 it is an extension string
35 like ".doc", ".txt" or a percieved string like "audio". Such strings
36 can be looked up in the registry at HKEY_CLASSES_ROOT.
44 get_registry_classes_key (const char *subdir,
45 const wchar_t *key_name)
56 wc_key = g_utf8_to_utf16 (subdir, -1, NULL, NULL, NULL);
57 if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
58 KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS &&
59 RegQueryValueExW (reg_key, key_name, 0,
60 &key_type, NULL, &nbytes) == ERROR_SUCCESS &&
63 wchar_t *wc_temp = g_new (wchar_t, (nbytes+1)/2 + 1);
64 RegQueryValueExW (reg_key, key_name, 0,
65 &key_type, (LPBYTE) wc_temp, &nbytes);
66 wc_temp[nbytes/2] = '\0';
67 value_utf8 = g_utf16_to_utf8 (wc_temp, -1, NULL, NULL, NULL);
73 RegCloseKey (reg_key);
79 * g_content_type_equals:
80 * @type1: a content type string.
81 * @type2: a content type string.
83 * Compares two content types for equality.
85 * Returns: %TRUE if the two strings are identical or equivalent,
89 g_content_type_equals (const char *type1,
92 char *progid1, *progid2;
95 g_return_val_if_fail (type1 != NULL, FALSE);
96 g_return_val_if_fail (type2 != NULL, FALSE);
98 if (g_ascii_strcasecmp (type1, type2) == 0)
102 progid1 = get_registry_classes_key (type1, NULL);
103 progid2 = get_registry_classes_key (type2, NULL);
104 if (progid1 != NULL && progid2 != NULL &&
105 strcmp (progid1, progid2) == 0)
114 * g_content_type_is_a:
115 * @type: a content type string. a content type string.
116 * @supertype: a string.
118 * Determines if @type is a subset of @supertype.
120 * Returns: %TRUE if @type is a kind of @supertype,
124 g_content_type_is_a (const char *type,
125 const char *supertype)
130 g_return_val_if_fail (type != NULL, FALSE);
131 g_return_val_if_fail (supertype != NULL, FALSE);
133 if (g_content_type_equals (type, supertype))
137 value_utf8 = get_registry_classes_key (type, L"PerceivedType");
138 if (value_utf8 && strcmp (value_utf8, supertype) == 0)
146 * g_content_type_is_unknown:
147 * @type: a content type string. a content type string.
152 g_content_type_is_unknown (const char *type)
154 g_return_val_if_fail (type != NULL, FALSE);
156 return strcmp ("*", type) == 0;
160 * g_content_type_get_description:
161 * @type: a content type string. a content type string.
163 * Returns: a short description of the content type @type.
166 g_content_type_get_description (const char *type)
171 g_return_val_if_fail (type != NULL, NULL);
173 progid = get_registry_classes_key (type, NULL);
176 description = get_registry_classes_key (progid, NULL);
183 if (g_content_type_is_unknown (type))
184 return g_strdup (_("Unknown type"));
185 return g_strdup_printf (_("%s filetype"), type);
189 * g_content_type_get_mime_type:
190 * @type: a content type string. a content type string.
192 * Returns: the registered mime-type for the given @type.
195 g_content_type_get_mime_type (const char *type)
199 g_return_val_if_fail (type != NULL, NULL);
201 mime = get_registry_classes_key (type, L"Content Type");
204 else if (g_content_type_is_unknown (type))
205 return g_strdup ("application/octet-stream");
206 else if (*type == '.')
207 return g_strdup_printf ("application/x-ext-%s", type+1);
208 /* TODO: Map "image" to "image/ *", etc? */
210 return g_strdup ("application/octet-stream");
214 * g_content_type_get_icon:
215 * @type: a content type string. a content type string.
217 * Returns: #GIcon corresponding to the content type.
220 g_content_type_get_icon (const char *type)
222 g_return_val_if_fail (type != NULL, NULL);
224 /* TODO: How do we represent icons???
225 In the registry they are the default value of
226 HKEY_CLASSES_ROOT\<progid>\DefaultIcon with typical values like:
228 REG_EXPAND_SZ: %SystemRoot%\System32\Wscript.exe,3
229 REG_SZ: shimgvw.dll,3
235 * g_content_type_can_be_executable:
236 * @type: a content type string.
238 * Returns: %TRUE if the file type corresponds to something that
239 * can be executable, %FALSE otherwise. Note that for instance
240 * things like textfiles can be executables (i.e. scripts)
243 g_content_type_can_be_executable (const char *type)
245 g_return_val_if_fail (type != NULL, FALSE);
247 if (strcmp (type, ".exe") == 0 ||
248 strcmp (type, ".com") == 0 ||
249 strcmp (type, ".bat") == 0)
255 looks_like_text (const guchar *data, gsize data_size)
259 for (i = 0; i < data_size; i++)
262 if (g_ascii_iscntrl (c) && !g_ascii_isspace (c))
269 * g_content_type_guess:
270 * @filename: a string.
271 * @data: a stream of data.
272 * @data_size: the size of @data.
273 * @result_uncertain: a flag indicating the certainty of the
276 * Returns: a string indicating a guessed content type for the
277 * given data. If the function is uncertain, @result_uncertain
278 * will be set to %TRUE.
281 g_content_type_guess (const char *filename,
284 gboolean *result_uncertain)
294 basename = g_path_get_basename (filename);
295 dot = strrchr (basename, '.');
297 type = g_strdup (dot);
304 if (data && looks_like_text (data, data_size))
305 return g_strdup (".txt");
307 return g_strdup ("*");
311 * g_content_types_get_registered:
313 * Returns: #GList of the registered content types.
316 g_content_types_get_registered (void)
319 wchar_t keyname[256];
327 while (RegEnumKeyExW(HKEY_CLASSES_ROOT,
334 NULL) == ERROR_SUCCESS)
336 key_utf8 = g_utf16_to_utf8 (keyname, -1, NULL, NULL, NULL);
339 if (*key_utf8 == '.')
340 types = g_list_prepend (types, key_utf8);
348 return g_list_reverse (types);
351 #else /* !G_OS_WIN32 - Unix specific version */
353 #define XDG_PREFIX _gio_xdg
354 #include "xdgmime/xdgmime.h"
356 /* We lock this mutex whenever we modify global state in this module. */
357 G_LOCK_DEFINE_STATIC (gio_xdgmime);
360 _g_unix_content_type_get_sniff_len (void)
364 G_LOCK (gio_xdgmime);
365 size = xdg_mime_get_max_buffer_extents ();
366 G_UNLOCK (gio_xdgmime);
372 _g_unix_content_type_unalias (const char *type)
376 G_LOCK (gio_xdgmime);
377 res = g_strdup (xdg_mime_unalias_mime_type (type));
378 G_UNLOCK (gio_xdgmime);
384 _g_unix_content_type_get_parents (const char *type)
387 const char **parents;
391 array = g_ptr_array_new ();
393 G_LOCK (gio_xdgmime);
395 umime = xdg_mime_unalias_mime_type (type);
396 g_ptr_array_add (array, g_strdup (umime));
398 parents = xdg_mime_get_mime_parents (umime);
399 for (i = 0; parents && parents[i] != NULL; i++)
400 g_ptr_array_add (array, g_strdup (parents[i]));
402 G_UNLOCK (gio_xdgmime);
404 g_ptr_array_add (array, NULL);
406 return (char **)g_ptr_array_free (array, FALSE);
410 g_content_type_equals (const char *type1,
415 g_return_val_if_fail (type1 != NULL, FALSE);
416 g_return_val_if_fail (type2 != NULL, FALSE);
418 G_LOCK (gio_xdgmime);
419 res = xdg_mime_mime_type_equal (type1, type2);
420 G_UNLOCK (gio_xdgmime);
426 g_content_type_is_a (const char *type,
427 const char *supertype)
431 g_return_val_if_fail (type != NULL, FALSE);
432 g_return_val_if_fail (supertype != NULL, FALSE);
434 G_LOCK (gio_xdgmime);
435 res = xdg_mime_mime_type_subclass (type, supertype);
436 G_UNLOCK (gio_xdgmime);
442 g_content_type_is_unknown (const char *type)
444 g_return_val_if_fail (type != NULL, FALSE);
446 return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0;
452 MIME_TAG_TYPE_COMMENT
457 int current_lang_level;
458 int comment_lang_level;
464 language_level (const char *lang)
466 const char * const *lang_list;
469 /* The returned list is sorted from most desirable to least
470 desirable and always contains the default locale "C". */
471 lang_list = g_get_language_names ();
473 for (i = 0; lang_list[i]; i++)
474 if (strcmp (lang_list[i], lang) == 0)
481 mime_info_start_element (GMarkupParseContext *context,
482 const gchar *element_name,
483 const gchar **attribute_names,
484 const gchar **attribute_values,
490 MimeParser *parser = user_data;
492 if (strcmp (element_name, "comment") == 0)
495 for (i = 0; attribute_names[i]; i++)
496 if (strcmp (attribute_names[i], "xml:lang") == 0)
498 lang = attribute_values[i];
502 parser->current_lang_level = language_level (lang);
503 parser->current_type = MIME_TAG_TYPE_COMMENT;
506 parser->current_type = MIME_TAG_TYPE_OTHER;
511 mime_info_end_element (GMarkupParseContext *context,
512 const gchar *element_name,
516 MimeParser *parser = user_data;
518 parser->current_type = MIME_TAG_TYPE_OTHER;
522 mime_info_text (GMarkupParseContext *context,
528 MimeParser *parser = user_data;
530 if (parser->current_type == MIME_TAG_TYPE_COMMENT &&
531 parser->current_lang_level > parser->comment_lang_level)
533 g_free (parser->comment);
534 parser->comment = g_strndup (text, text_len);
535 parser->comment_lang_level = parser->current_lang_level;
540 load_comment_for_mime_helper (const char *dir, const char *basename)
542 GMarkupParseContext *context;
543 char *filename, *data;
546 MimeParser parse_data = {0};
547 GMarkupParser parser = {
548 mime_info_start_element,
549 mime_info_end_element,
553 filename = g_build_filename (dir, "mime", basename, NULL);
555 res = g_file_get_contents (filename, &data, &len, NULL);
560 context = g_markup_parse_context_new (&parser, 0, &parse_data, NULL);
561 res = g_markup_parse_context_parse (context, data, len, NULL);
563 g_markup_parse_context_free (context);
568 return parse_data.comment;
573 load_comment_for_mime (const char *mimetype)
575 const char * const* dirs;
580 basename = g_strdup_printf ("%s.xml", mimetype);
582 comment = load_comment_for_mime_helper (g_get_user_data_dir (), basename);
589 dirs = g_get_system_data_dirs ();
591 for (i = 0; dirs[i] != NULL; i++)
593 comment = load_comment_for_mime_helper (dirs[i], basename);
602 return g_strdup_printf (_("%s type"), mimetype);
606 g_content_type_get_description (const char *type)
608 static GHashTable *type_comment_cache = NULL;
611 g_return_val_if_fail (type != NULL, NULL);
613 G_LOCK (gio_xdgmime);
614 if (type_comment_cache == NULL)
615 type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
617 comment = g_hash_table_lookup (type_comment_cache, type);
618 comment = g_strdup (comment);
619 G_UNLOCK (gio_xdgmime);
624 comment = load_comment_for_mime (type);
626 G_LOCK (gio_xdgmime);
627 g_hash_table_insert (type_comment_cache,
630 G_UNLOCK (gio_xdgmime);
636 g_content_type_get_mime_type (const char *type)
638 g_return_val_if_fail (type != NULL, NULL);
640 return g_strdup (type);
644 g_content_type_get_icon (const char *type)
646 g_return_val_if_fail (type != NULL, NULL);
648 /* TODO: Implement */
653 * g_content_type_can_be_executable:
654 * @type: a content type string.
656 * Returns: %TRUE if the file type corresponds to something that
657 * can be executable, %FALSE otherwise. Note that for instance
658 * things like textfiles can be executables (i.e. scripts)
661 g_content_type_can_be_executable (const char *type)
663 g_return_val_if_fail (type != NULL, FALSE);
665 if (g_content_type_is_a (type, "application/x-executable") ||
666 g_content_type_is_a (type, "text/plain"))
673 looks_like_text (const guchar *data, gsize data_size)
676 for (i = 0; i < data_size; i++)
678 if g_ascii_iscntrl (data[i])
685 g_content_type_guess (const char *filename,
688 gboolean *result_uncertain)
691 const char *name_mimetypes[10], *sniffed_mimetype;
694 int n_name_mimetypes;
698 n_name_mimetypes = 0;
699 sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN;
701 if (result_uncertain)
702 *result_uncertain = FALSE;
704 G_LOCK (gio_xdgmime);
708 basename = g_path_get_basename (filename);
709 n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10);
713 /* Got an extension match, and no conflicts. This is it. */
714 if (n_name_mimetypes == 1)
716 G_UNLOCK (gio_xdgmime);
717 return g_strdup (name_mimetypes[0]);
722 sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio);
723 if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
725 looks_like_text (data, data_size))
726 sniffed_mimetype = "text/plain";
729 if (n_name_mimetypes == 0)
731 if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
733 *result_uncertain = TRUE;
735 mimetype = g_strdup (sniffed_mimetype);
740 if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN)
742 if (sniffed_prio >= 80) /* High priority sniffing match, use that */
743 mimetype = g_strdup (sniffed_mimetype);
746 /* There are conflicts between the name matches and we have a sniffed
747 type, use that as a tie breaker. */
749 for (i = 0; i < n_name_mimetypes; i++)
751 if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype))
753 /* This nametype match is derived from (or the same as) the sniffed type).
754 This is probably it. */
755 mimetype = g_strdup (name_mimetypes[i]);
762 if (mimetype == NULL)
764 /* Conflicts, and sniffed type was no help or not there. guess on the first one */
765 mimetype = g_strdup (name_mimetypes[0]);
766 if (result_uncertain)
767 *result_uncertain = TRUE;
771 G_UNLOCK (gio_xdgmime);
777 foreach_mimetype (gpointer key,
781 GList **l = user_data;
783 *l = g_list_prepend (*l, (char *)key);
788 enumerate_mimetypes_subdir (const char *dir, const char *prefix, GHashTable *mimetypes)
797 while ((ent = readdir (d)) != NULL)
799 if (g_str_has_suffix (ent->d_name, ".xml"))
801 mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name);
802 g_hash_table_insert (mimetypes, mimetype, NULL);
810 enumerate_mimetypes_dir (const char *dir, GHashTable *mimetypes)
817 mimedir = g_build_filename (dir, "mime", NULL);
819 d = opendir (mimedir);
822 while ((ent = readdir (d)) != NULL)
824 if (strcmp (ent->d_name, "packages") != 0)
826 name = g_build_filename (mimedir, ent->d_name, NULL);
827 if (g_file_test (name, G_FILE_TEST_IS_DIR))
828 enumerate_mimetypes_subdir (name, ent->d_name, mimetypes);
839 g_content_types_get_registered (void)
841 const char * const* dirs;
842 GHashTable *mimetypes;
846 mimetypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
848 enumerate_mimetypes_dir (g_get_user_data_dir (), mimetypes);
849 dirs = g_get_system_data_dirs ();
851 for (i = 0; dirs[i] != NULL; i++)
852 enumerate_mimetypes_dir (dirs[i], mimetypes);
855 g_hash_table_foreach_steal (mimetypes, foreach_mimetype, &l);
856 g_hash_table_destroy (mimetypes);
861 #endif /* Unix version */