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>
24 #include <sys/types.h>
28 #include "gcontenttypeprivate.h"
31 /* This really sucks. GTK-Doc isn't smart enough to realize there are two
32 * different versions of the functions within this file, based on the platform.
33 * I can move the definitions out of this file, but that's ugly.
37 * SECTION:gcontenttype
38 * @short_description: platform specific content typing
40 * A content type is a platform specific string that defines the type
41 * of a file. On unix it is a mime type, on win32 it is an extension string
42 * like ".doc", ".txt" or a percieved string like "audio". Such strings
43 * can be looked up in the registry at HKEY_CLASSES_ROOT.
51 get_registry_classes_key (const char *subdir,
52 const wchar_t *key_name)
63 wc_key = g_utf8_to_utf16 (subdir, -1, NULL, NULL, NULL);
64 if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
65 KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS &&
66 RegQueryValueExW (reg_key, key_name, 0,
67 &key_type, NULL, &nbytes) == ERROR_SUCCESS &&
70 wchar_t *wc_temp = g_new (wchar_t, (nbytes+1)/2 + 1);
71 RegQueryValueExW (reg_key, key_name, 0,
72 &key_type, (LPBYTE) wc_temp, &nbytes);
73 wc_temp[nbytes/2] = '\0';
74 value_utf8 = g_utf16_to_utf8 (wc_temp, -1, NULL, NULL, NULL);
80 RegCloseKey (reg_key);
86 * g_content_type_equals:
87 * @type1: a content type string.
88 * @type2: a content type string.
90 * Compares two content types for equality.
92 * Returns: %TRUE if the two strings are identical or equivalent,
96 g_content_type_equals (const char *type1,
99 char *progid1, *progid2;
102 g_return_val_if_fail (type1 != NULL, FALSE);
103 g_return_val_if_fail (type2 != NULL, FALSE);
105 if (g_ascii_strcasecmp (type1, type2) == 0)
109 progid1 = get_registry_classes_key (type1, NULL);
110 progid2 = get_registry_classes_key (type2, NULL);
111 if (progid1 != NULL && progid2 != NULL &&
112 strcmp (progid1, progid2) == 0)
121 * g_content_type_is_a:
122 * @type: a content type string.
123 * @supertype: a string.
125 * Determines if @type is a subset of @supertype.
127 * Returns: %TRUE if @type is a kind of @supertype,
131 g_content_type_is_a (const char *type,
132 const char *supertype)
137 g_return_val_if_fail (type != NULL, FALSE);
138 g_return_val_if_fail (supertype != NULL, FALSE);
140 if (g_content_type_equals (type, supertype))
144 value_utf8 = get_registry_classes_key (type, L"PerceivedType");
145 if (value_utf8 && strcmp (value_utf8, supertype) == 0)
153 * g_content_type_is_unknown:
154 * @type: a content type string.
156 * Checks if the content type is known by GIO.
158 * Returns: %TRUE if the type is unknown.
161 g_content_type_is_unknown (const char *type)
163 g_return_val_if_fail (type != NULL, FALSE);
165 return strcmp ("*", type) == 0;
169 * g_content_type_get_description:
170 * @type: a content type string.
172 * Gets the human readable description of the content type.
174 * Returns: a short description of the content type @type.
177 g_content_type_get_description (const char *type)
182 g_return_val_if_fail (type != NULL, NULL);
184 progid = get_registry_classes_key (type, NULL);
187 description = get_registry_classes_key (progid, NULL);
194 if (g_content_type_is_unknown (type))
195 return g_strdup (_("Unknown type"));
196 return g_strdup_printf (_("%s filetype"), type);
200 * g_content_type_get_mime_type:
201 * @type: a content type string.
203 * Gets the mime-type for the content type.
205 * Returns: the registered mime-type for the given @type.
208 g_content_type_get_mime_type (const char *type)
212 g_return_val_if_fail (type != NULL, NULL);
214 mime = get_registry_classes_key (type, L"Content Type");
217 else if (g_content_type_is_unknown (type))
218 return g_strdup ("application/octet-stream");
219 else if (*type == '.')
220 return g_strdup_printf ("application/x-ext-%s", type+1);
221 /* TODO: Map "image" to "image/ *", etc? */
223 return g_strdup ("application/octet-stream");
227 * g_content_type_get_icon:
228 * @type: a content type string.
230 * Gets the icon for a content type.
232 * Returns: #GIcon corresponding to the content type.
235 g_content_type_get_icon (const char *type)
237 g_return_val_if_fail (type != NULL, NULL);
239 /* TODO: How do we represent icons???
240 In the registry they are the default value of
241 HKEY_CLASSES_ROOT\<progid>\DefaultIcon with typical values like:
243 REG_EXPAND_SZ: %SystemRoot%\System32\Wscript.exe,3
244 REG_SZ: shimgvw.dll,3
250 * g_content_type_can_be_executable:
251 * @type: a content type string.
253 * Checks if a content type can be executable. Note that for instance
254 * things like text files can be executables (i.e. scripts and batch files).
256 * Returns: %TRUE if the file type corresponds to a type that
257 * can be executable, %FALSE otherwise.
260 g_content_type_can_be_executable (const char *type)
262 g_return_val_if_fail (type != NULL, FALSE);
264 if (strcmp (type, ".exe") == 0 ||
265 strcmp (type, ".com") == 0 ||
266 strcmp (type, ".bat") == 0)
272 looks_like_text (const guchar *data, gsize data_size)
276 for (i = 0; i < data_size; i++)
279 if (g_ascii_iscntrl (c) && !g_ascii_isspace (c))
286 * g_content_type_guess:
287 * @filename: a string.
288 * @data: a stream of data.
289 * @data_size: the size of @data.
290 * @result_uncertain: a flag indicating the certainty of the
293 * Guesses the content type based on example data. If the function is
294 * uncertain, @result_uncertain will be set to %TRUE.
296 * Returns: a string indicating a guessed content type for the
300 g_content_type_guess (const char *filename,
303 gboolean *result_uncertain)
313 basename = g_path_get_basename (filename);
314 dot = strrchr (basename, '.');
316 type = g_strdup (dot);
323 if (data && looks_like_text (data, data_size))
324 return g_strdup (".txt");
326 return g_strdup ("*");
330 * g_content_types_get_registered:
332 * Gets a list of strings containing the registered content types on
335 * Returns: #GList of the registered content types.
338 g_content_types_get_registered (void)
341 wchar_t keyname[256];
349 while (RegEnumKeyExW(HKEY_CLASSES_ROOT,
356 NULL) == ERROR_SUCCESS)
358 key_utf8 = g_utf16_to_utf8 (keyname, -1, NULL, NULL, NULL);
361 if (*key_utf8 == '.')
362 types = g_list_prepend (types, key_utf8);
370 return g_list_reverse (types);
373 #else /* !G_OS_WIN32 - Unix specific version */
375 #define XDG_PREFIX _gio_xdg
376 #include "xdgmime/xdgmime.h"
378 /* We lock this mutex whenever we modify global state in this module. */
379 G_LOCK_DEFINE_STATIC (gio_xdgmime);
382 _g_unix_content_type_get_sniff_len (void)
386 G_LOCK (gio_xdgmime);
387 size = xdg_mime_get_max_buffer_extents ();
388 G_UNLOCK (gio_xdgmime);
394 _g_unix_content_type_unalias (const char *type)
398 G_LOCK (gio_xdgmime);
399 res = g_strdup (xdg_mime_unalias_mime_type (type));
400 G_UNLOCK (gio_xdgmime);
406 _g_unix_content_type_get_parents (const char *type)
409 const char **parents;
413 array = g_ptr_array_new ();
415 G_LOCK (gio_xdgmime);
417 umime = xdg_mime_unalias_mime_type (type);
418 g_ptr_array_add (array, g_strdup (umime));
420 parents = xdg_mime_get_mime_parents (umime);
421 for (i = 0; parents && parents[i] != NULL; i++)
422 g_ptr_array_add (array, g_strdup (parents[i]));
424 G_UNLOCK (gio_xdgmime);
426 g_ptr_array_add (array, NULL);
428 return (char **)g_ptr_array_free (array, FALSE);
432 g_content_type_equals (const char *type1,
437 g_return_val_if_fail (type1 != NULL, FALSE);
438 g_return_val_if_fail (type2 != NULL, FALSE);
440 G_LOCK (gio_xdgmime);
441 res = xdg_mime_mime_type_equal (type1, type2);
442 G_UNLOCK (gio_xdgmime);
448 g_content_type_is_a (const char *type,
449 const char *supertype)
453 g_return_val_if_fail (type != NULL, FALSE);
454 g_return_val_if_fail (supertype != NULL, FALSE);
456 G_LOCK (gio_xdgmime);
457 res = xdg_mime_mime_type_subclass (type, supertype);
458 G_UNLOCK (gio_xdgmime);
464 g_content_type_is_unknown (const char *type)
466 g_return_val_if_fail (type != NULL, FALSE);
468 return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0;
474 MIME_TAG_TYPE_COMMENT
479 int current_lang_level;
480 int comment_lang_level;
486 language_level (const char *lang)
488 const char * const *lang_list;
491 /* The returned list is sorted from most desirable to least
492 desirable and always contains the default locale "C". */
493 lang_list = g_get_language_names ();
495 for (i = 0; lang_list[i]; i++)
496 if (strcmp (lang_list[i], lang) == 0)
503 mime_info_start_element (GMarkupParseContext *context,
504 const gchar *element_name,
505 const gchar **attribute_names,
506 const gchar **attribute_values,
512 MimeParser *parser = user_data;
514 if (strcmp (element_name, "comment") == 0)
517 for (i = 0; attribute_names[i]; i++)
518 if (strcmp (attribute_names[i], "xml:lang") == 0)
520 lang = attribute_values[i];
524 parser->current_lang_level = language_level (lang);
525 parser->current_type = MIME_TAG_TYPE_COMMENT;
528 parser->current_type = MIME_TAG_TYPE_OTHER;
533 mime_info_end_element (GMarkupParseContext *context,
534 const gchar *element_name,
538 MimeParser *parser = user_data;
540 parser->current_type = MIME_TAG_TYPE_OTHER;
544 mime_info_text (GMarkupParseContext *context,
550 MimeParser *parser = user_data;
552 if (parser->current_type == MIME_TAG_TYPE_COMMENT &&
553 parser->current_lang_level > parser->comment_lang_level)
555 g_free (parser->comment);
556 parser->comment = g_strndup (text, text_len);
557 parser->comment_lang_level = parser->current_lang_level;
562 load_comment_for_mime_helper (const char *dir, const char *basename)
564 GMarkupParseContext *context;
565 char *filename, *data;
568 MimeParser parse_data = {0};
569 GMarkupParser parser = {
570 mime_info_start_element,
571 mime_info_end_element,
575 filename = g_build_filename (dir, "mime", basename, NULL);
577 res = g_file_get_contents (filename, &data, &len, NULL);
582 context = g_markup_parse_context_new (&parser, 0, &parse_data, NULL);
583 res = g_markup_parse_context_parse (context, data, len, NULL);
585 g_markup_parse_context_free (context);
590 return parse_data.comment;
595 load_comment_for_mime (const char *mimetype)
597 const char * const* dirs;
602 basename = g_strdup_printf ("%s.xml", mimetype);
604 comment = load_comment_for_mime_helper (g_get_user_data_dir (), basename);
611 dirs = g_get_system_data_dirs ();
613 for (i = 0; dirs[i] != NULL; i++)
615 comment = load_comment_for_mime_helper (dirs[i], basename);
624 return g_strdup_printf (_("%s type"), mimetype);
628 g_content_type_get_description (const char *type)
630 static GHashTable *type_comment_cache = NULL;
633 g_return_val_if_fail (type != NULL, NULL);
635 G_LOCK (gio_xdgmime);
636 if (type_comment_cache == NULL)
637 type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
639 comment = g_hash_table_lookup (type_comment_cache, type);
640 comment = g_strdup (comment);
641 G_UNLOCK (gio_xdgmime);
646 comment = load_comment_for_mime (type);
648 G_LOCK (gio_xdgmime);
649 g_hash_table_insert (type_comment_cache,
652 G_UNLOCK (gio_xdgmime);
658 g_content_type_get_mime_type (const char *type)
660 g_return_val_if_fail (type != NULL, NULL);
662 return g_strdup (type);
666 g_content_type_get_icon (const char *type)
668 g_return_val_if_fail (type != NULL, NULL);
670 /* TODO: Implement */
675 * g_content_type_can_be_executable:
676 * @type: a content type string.
678 * Returns: %TRUE if the file type corresponds to something that
679 * can be executable, %FALSE otherwise. Note that for instance
680 * things like textfiles can be executables (i.e. scripts)
683 g_content_type_can_be_executable (const char *type)
685 g_return_val_if_fail (type != NULL, FALSE);
687 if (g_content_type_is_a (type, "application/x-executable") ||
688 g_content_type_is_a (type, "text/plain"))
695 looks_like_text (const guchar *data, gsize data_size)
698 for (i = 0; i < data_size; i++)
700 if g_ascii_iscntrl (data[i])
707 g_content_type_guess (const char *filename,
710 gboolean *result_uncertain)
713 const char *name_mimetypes[10], *sniffed_mimetype;
716 int n_name_mimetypes;
720 n_name_mimetypes = 0;
721 sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN;
723 if (result_uncertain)
724 *result_uncertain = FALSE;
726 G_LOCK (gio_xdgmime);
730 basename = g_path_get_basename (filename);
731 n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10);
735 /* Got an extension match, and no conflicts. This is it. */
736 if (n_name_mimetypes == 1)
738 G_UNLOCK (gio_xdgmime);
739 return g_strdup (name_mimetypes[0]);
744 sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio);
745 if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
747 looks_like_text (data, data_size))
748 sniffed_mimetype = "text/plain";
751 if (n_name_mimetypes == 0)
753 if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
755 *result_uncertain = TRUE;
757 mimetype = g_strdup (sniffed_mimetype);
762 if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN)
764 if (sniffed_prio >= 80) /* High priority sniffing match, use that */
765 mimetype = g_strdup (sniffed_mimetype);
768 /* There are conflicts between the name matches and we have a sniffed
769 type, use that as a tie breaker. */
771 for (i = 0; i < n_name_mimetypes; i++)
773 if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype))
775 /* This nametype match is derived from (or the same as) the sniffed type).
776 This is probably it. */
777 mimetype = g_strdup (name_mimetypes[i]);
784 if (mimetype == NULL)
786 /* Conflicts, and sniffed type was no help or not there. guess on the first one */
787 mimetype = g_strdup (name_mimetypes[0]);
788 if (result_uncertain)
789 *result_uncertain = TRUE;
793 G_UNLOCK (gio_xdgmime);
799 foreach_mimetype (gpointer key,
803 GList **l = user_data;
805 *l = g_list_prepend (*l, (char *)key);
810 enumerate_mimetypes_subdir (const char *dir, const char *prefix, GHashTable *mimetypes)
819 while ((ent = readdir (d)) != NULL)
821 if (g_str_has_suffix (ent->d_name, ".xml"))
823 mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name);
824 g_hash_table_insert (mimetypes, mimetype, NULL);
832 enumerate_mimetypes_dir (const char *dir, GHashTable *mimetypes)
839 mimedir = g_build_filename (dir, "mime", NULL);
841 d = opendir (mimedir);
844 while ((ent = readdir (d)) != NULL)
846 if (strcmp (ent->d_name, "packages") != 0)
848 name = g_build_filename (mimedir, ent->d_name, NULL);
849 if (g_file_test (name, G_FILE_TEST_IS_DIR))
850 enumerate_mimetypes_subdir (name, ent->d_name, mimetypes);
861 g_content_types_get_registered (void)
863 const char * const* dirs;
864 GHashTable *mimetypes;
868 mimetypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
870 enumerate_mimetypes_dir (g_get_user_data_dir (), mimetypes);
871 dirs = g_get_system_data_dirs ();
873 for (i = 0; dirs[i] != NULL; i++)
874 enumerate_mimetypes_dir (dirs[i], mimetypes);
877 g_hash_table_foreach_steal (mimetypes, foreach_mimetype, &l);
878 g_hash_table_destroy (mimetypes);
883 #endif /* Unix version */