1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3 /* GIO - GLib Input, Output and Streaming Library
5 * Copyright (C) 2006-2007 Red Hat, Inc.
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.
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.
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20 * Boston, MA 02111-1307, USA.
22 * Author: Alexander Larsson <alexl@redhat.com>
26 #include <sys/types.h>
29 #include "gcontenttypeprivate.h"
30 #include "gthemedicon.h"
37 * SECTION:gcontenttype
38 * @short_description: Platform-specific content typing
41 * A content type is a platform specific string that defines the type
42 * of a file. On unix it is a mime type, on win32 it is an extension string
43 * like ".doc", ".txt" or a percieved string like "audio". Such strings
44 * can be looked up in the registry at HKEY_CLASSES_ROOT.
52 get_registry_classes_key (const char *subdir,
53 const wchar_t *key_name)
64 wc_key = g_utf8_to_utf16 (subdir, -1, NULL, NULL, NULL);
65 if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
66 KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS &&
67 RegQueryValueExW (reg_key, key_name, 0,
68 &key_type, NULL, &nbytes) == ERROR_SUCCESS &&
69 (key_type == REG_SZ || key_type == REG_EXPAND_SZ))
71 wchar_t *wc_temp = g_new (wchar_t, (nbytes+1)/2 + 1);
72 RegQueryValueExW (reg_key, key_name, 0,
73 &key_type, (LPBYTE) wc_temp, &nbytes);
74 wc_temp[nbytes/2] = '\0';
75 if (key_type == REG_EXPAND_SZ)
78 int len = ExpandEnvironmentStringsW (wc_temp, dummy, 1);
81 wchar_t *wc_temp_expanded = g_new (wchar_t, len);
82 if (ExpandEnvironmentStringsW (wc_temp, wc_temp_expanded, len) == len)
83 value_utf8 = g_utf16_to_utf8 (wc_temp_expanded, -1, NULL, NULL, NULL);
84 g_free (wc_temp_expanded);
89 value_utf8 = g_utf16_to_utf8 (wc_temp, -1, NULL, NULL, NULL);
97 RegCloseKey (reg_key);
103 g_content_type_equals (const char *type1,
106 char *progid1, *progid2;
109 g_return_val_if_fail (type1 != NULL, FALSE);
110 g_return_val_if_fail (type2 != NULL, FALSE);
112 if (g_ascii_strcasecmp (type1, type2) == 0)
116 progid1 = get_registry_classes_key (type1, NULL);
117 progid2 = get_registry_classes_key (type2, NULL);
118 if (progid1 != NULL && progid2 != NULL &&
119 strcmp (progid1, progid2) == 0)
128 g_content_type_is_a (const char *type,
129 const char *supertype)
134 g_return_val_if_fail (type != NULL, FALSE);
135 g_return_val_if_fail (supertype != NULL, FALSE);
137 if (g_content_type_equals (type, supertype))
141 value_utf8 = get_registry_classes_key (type, L"PerceivedType");
142 if (value_utf8 && strcmp (value_utf8, supertype) == 0)
150 g_content_type_is_unknown (const char *type)
152 g_return_val_if_fail (type != NULL, FALSE);
154 return strcmp ("*", type) == 0;
158 g_content_type_get_description (const char *type)
163 g_return_val_if_fail (type != NULL, NULL);
165 progid = get_registry_classes_key (type, NULL);
168 description = get_registry_classes_key (progid, NULL);
175 if (g_content_type_is_unknown (type))
176 return g_strdup (_("Unknown type"));
177 return g_strdup_printf (_("%s filetype"), type);
181 g_content_type_get_mime_type (const char *type)
185 g_return_val_if_fail (type != NULL, NULL);
187 mime = get_registry_classes_key (type, L"Content Type");
190 else if (g_content_type_is_unknown (type))
191 return g_strdup ("application/octet-stream");
192 else if (*type == '.')
193 return g_strdup_printf ("application/x-ext-%s", type+1);
194 /* TODO: Map "image" to "image/ *", etc? */
196 return g_strdup ("application/octet-stream");
199 G_LOCK_DEFINE_STATIC (_type_icons);
200 static GHashTable *_type_icons = NULL;
203 g_content_type_get_icon (const char *type)
208 g_return_val_if_fail (type != NULL, NULL);
210 /* In the Registry icons are the default value of
211 HKEY_CLASSES_ROOT\<progid>\DefaultIcon with typical values like:
213 REG_EXPAND_SZ: %SystemRoot%\System32\Wscript.exe,3
214 REG_SZ: shimgvw.dll,3
216 G_LOCK (_type_icons);
218 _type_icons = g_hash_table_new (g_str_hash, g_str_equal);
219 name = g_hash_table_lookup (_type_icons, type);
220 if (!name && type[0] == '.')
222 /* double lookup by extension */
223 gchar *key = get_registry_classes_key (type, NULL);
225 key = g_strconcat (type+1, "file\\DefaultIcon", NULL);
228 gchar *key2 = g_strconcat (key, "\\DefaultIcon", NULL);
232 name = get_registry_classes_key (key, NULL);
233 if (name && strcmp (name, "%1") == 0)
239 g_hash_table_insert (_type_icons, g_strdup (type), g_strdup (name));
243 /* icon-name similar to how it was with gtk-2-12 */
246 themed_icon = g_themed_icon_new (name);
250 /* if not found an icon fall back to gtk-builtins */
251 name = strcmp (type, "inode/directory") == 0 ? "gtk-directory" :
252 g_content_type_can_be_executable (type) ? "gtk-execute" : "gtk-file";
253 g_hash_table_insert (_type_icons, g_strdup (type), g_strdup (name));
254 themed_icon = g_themed_icon_new_with_default_fallbacks (name);
256 G_UNLOCK (_type_icons);
258 return G_ICON (themed_icon);
262 g_content_type_can_be_executable (const char *type)
264 g_return_val_if_fail (type != NULL, FALSE);
266 if (strcmp (type, ".exe") == 0 ||
267 strcmp (type, ".com") == 0 ||
268 strcmp (type, ".bat") == 0)
271 /* TODO: Also look at PATHEXT, which lists the extensions for
272 * "scripts" in addition to those for true binary executables.
274 * (PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH for me
275 * right now, for instance). And in a sense, all associated file
276 * types are "executable" on Windows... You can just type foo.jpg as
277 * a command name in cmd.exe, and it will run the application
278 * associated with .jpg. Hard to say what this API actually means
286 looks_like_text (const guchar *data,
291 for (i = 0; i < data_size; i++)
294 if (g_ascii_iscntrl (c) && !g_ascii_isspace (c))
301 g_content_type_from_mime_type (const char *mime_type)
303 char *key, *content_type;
305 g_return_val_if_fail (mime_type != NULL, NULL);
307 key = g_strconcat ("MIME\\DataBase\\Content Type\\", mime_type, NULL);
308 content_type = get_registry_classes_key (key, L"Extension");
315 g_content_type_guess (const char *filename,
318 gboolean *result_uncertain)
328 basename = g_path_get_basename (filename);
329 dot = strrchr (basename, '.');
331 type = g_strdup (dot);
338 if (data && looks_like_text (data, data_size))
339 return g_strdup (".txt");
341 return g_strdup ("*");
345 g_content_types_get_registered (void)
348 wchar_t keyname[256];
356 while (RegEnumKeyExW(HKEY_CLASSES_ROOT,
363 NULL) == ERROR_SUCCESS)
365 key_utf8 = g_utf16_to_utf8 (keyname, -1, NULL, NULL, NULL);
368 if (*key_utf8 == '.')
369 types = g_list_prepend (types, key_utf8);
377 return g_list_reverse (types);
380 #else /* !G_OS_WIN32 - Unix specific version */
384 #define XDG_PREFIX _gio_xdg
385 #include "xdgmime/xdgmime.h"
387 /* We lock this mutex whenever we modify global state in this module. */
388 G_LOCK_DEFINE_STATIC (gio_xdgmime);
391 _g_unix_content_type_get_sniff_len (void)
395 G_LOCK (gio_xdgmime);
396 size = xdg_mime_get_max_buffer_extents ();
397 G_UNLOCK (gio_xdgmime);
403 _g_unix_content_type_unalias (const char *type)
407 G_LOCK (gio_xdgmime);
408 res = g_strdup (xdg_mime_unalias_mime_type (type));
409 G_UNLOCK (gio_xdgmime);
415 _g_unix_content_type_get_parents (const char *type)
422 array = g_ptr_array_new ();
424 G_LOCK (gio_xdgmime);
426 umime = xdg_mime_unalias_mime_type (type);
428 g_ptr_array_add (array, g_strdup (umime));
430 parents = xdg_mime_list_mime_parents (umime);
431 for (i = 0; parents && parents[i] != NULL; i++)
432 g_ptr_array_add (array, g_strdup (parents[i]));
436 G_UNLOCK (gio_xdgmime);
438 g_ptr_array_add (array, NULL);
440 return (char **)g_ptr_array_free (array, FALSE);
444 * g_content_type_equals:
445 * @type1: a content type string.
446 * @type2: a content type string.
448 * Compares two content types for equality.
450 * Returns: %TRUE if the two strings are identical or equivalent,
454 g_content_type_equals (const char *type1,
459 g_return_val_if_fail (type1 != NULL, FALSE);
460 g_return_val_if_fail (type2 != NULL, FALSE);
462 G_LOCK (gio_xdgmime);
463 res = xdg_mime_mime_type_equal (type1, type2);
464 G_UNLOCK (gio_xdgmime);
470 * g_content_type_is_a:
471 * @type: a content type string.
472 * @supertype: a string.
474 * Determines if @type is a subset of @supertype.
476 * Returns: %TRUE if @type is a kind of @supertype,
480 g_content_type_is_a (const char *type,
481 const char *supertype)
485 g_return_val_if_fail (type != NULL, FALSE);
486 g_return_val_if_fail (supertype != NULL, FALSE);
488 G_LOCK (gio_xdgmime);
489 res = xdg_mime_mime_type_subclass (type, supertype);
490 G_UNLOCK (gio_xdgmime);
496 * g_content_type_is_unknown:
497 * @type: a content type string.
499 * Checks if the content type is the generic "unknown" type.
500 * On unix this is the "application/octet-stream" mimetype,
501 * while on win32 it is "*".
503 * Returns: %TRUE if the type is the unknown type.
506 g_content_type_is_unknown (const char *type)
508 g_return_val_if_fail (type != NULL, FALSE);
510 return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0;
516 MIME_TAG_TYPE_COMMENT
521 int current_lang_level;
522 int comment_lang_level;
528 language_level (const char *lang)
530 const char * const *lang_list;
533 /* The returned list is sorted from most desirable to least
534 desirable and always contains the default locale "C". */
535 lang_list = g_get_language_names ();
537 for (i = 0; lang_list[i]; i++)
538 if (strcmp (lang_list[i], lang) == 0)
545 mime_info_start_element (GMarkupParseContext *context,
546 const gchar *element_name,
547 const gchar **attribute_names,
548 const gchar **attribute_values,
554 MimeParser *parser = user_data;
556 if (strcmp (element_name, "comment") == 0)
559 for (i = 0; attribute_names[i]; i++)
560 if (strcmp (attribute_names[i], "xml:lang") == 0)
562 lang = attribute_values[i];
566 parser->current_lang_level = language_level (lang);
567 parser->current_type = MIME_TAG_TYPE_COMMENT;
570 parser->current_type = MIME_TAG_TYPE_OTHER;
575 mime_info_end_element (GMarkupParseContext *context,
576 const gchar *element_name,
580 MimeParser *parser = user_data;
582 parser->current_type = MIME_TAG_TYPE_OTHER;
586 mime_info_text (GMarkupParseContext *context,
592 MimeParser *parser = user_data;
594 if (parser->current_type == MIME_TAG_TYPE_COMMENT &&
595 parser->current_lang_level > parser->comment_lang_level)
597 g_free (parser->comment);
598 parser->comment = g_strndup (text, text_len);
599 parser->comment_lang_level = parser->current_lang_level;
604 load_comment_for_mime_helper (const char *dir,
605 const char *basename)
607 GMarkupParseContext *context;
608 char *filename, *data;
611 MimeParser parse_data = {0};
612 GMarkupParser parser = {
613 mime_info_start_element,
614 mime_info_end_element,
618 filename = g_build_filename (dir, "mime", basename, NULL);
620 res = g_file_get_contents (filename, &data, &len, NULL);
625 context = g_markup_parse_context_new (&parser, 0, &parse_data, NULL);
626 res = g_markup_parse_context_parse (context, data, len, NULL);
628 g_markup_parse_context_free (context);
633 return parse_data.comment;
638 load_comment_for_mime (const char *mimetype)
640 const char * const* dirs;
645 basename = g_strdup_printf ("%s.xml", mimetype);
647 comment = load_comment_for_mime_helper (g_get_user_data_dir (), basename);
654 dirs = g_get_system_data_dirs ();
656 for (i = 0; dirs[i] != NULL; i++)
658 comment = load_comment_for_mime_helper (dirs[i], basename);
667 return g_strdup_printf (_("%s type"), mimetype);
671 * g_content_type_get_description:
672 * @type: a content type string.
674 * Gets the human readable description of the content type.
676 * Returns: a short description of the content type @type.
679 g_content_type_get_description (const char *type)
681 static GHashTable *type_comment_cache = NULL;
684 g_return_val_if_fail (type != NULL, NULL);
686 G_LOCK (gio_xdgmime);
687 type = xdg_mime_unalias_mime_type (type);
689 if (type_comment_cache == NULL)
690 type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
692 comment = g_hash_table_lookup (type_comment_cache, type);
693 comment = g_strdup (comment);
694 G_UNLOCK (gio_xdgmime);
699 comment = load_comment_for_mime (type);
701 G_LOCK (gio_xdgmime);
702 g_hash_table_insert (type_comment_cache,
705 G_UNLOCK (gio_xdgmime);
711 * g_content_type_get_mime_type:
712 * @type: a content type string.
714 * Gets the mime-type for the content type. If one is registered
716 * Returns: the registered mime-type for the given @type, or NULL if unknown.
719 g_content_type_get_mime_type (const char *type)
721 g_return_val_if_fail (type != NULL, NULL);
723 return g_strdup (type);
727 * g_content_type_get_icon:
728 * @type: a content type string.
730 * Gets the icon for a content type.
732 * Returns: #GIcon corresponding to the content type.
735 g_content_type_get_icon (const char *type)
737 char *mimetype_icon, *generic_mimetype_icon, *q;
738 char *xdg_mimetype_icon, *legacy_mimetype_icon;
744 g_return_val_if_fail (type != NULL, NULL);
746 G_LOCK (gio_xdgmime);
747 xdg_mimetype_icon = g_strdup (xdg_mime_get_icon (type));
748 G_UNLOCK (gio_xdgmime);
750 mimetype_icon = g_strdup (type);
752 while ((q = strchr (mimetype_icon, '/')) != NULL)
755 p = strchr (type, '/');
757 p = type + strlen (type);
759 /* Not all icons have migrated to the new icon theme spec, look for old names too */
760 legacy_mimetype_icon = g_strconcat ("gnome-mime-", mimetype_icon, NULL);
762 generic_mimetype_icon = g_malloc (p - type + strlen ("-x-generic") + 1);
763 memcpy (generic_mimetype_icon, type, p - type);
764 memcpy (generic_mimetype_icon + (p - type), "-x-generic", strlen ("-x-generic"));
765 generic_mimetype_icon[(p - type) + strlen ("-x-generic")] = 0;
767 if (xdg_mimetype_icon)
768 icon_names[n++] = xdg_mimetype_icon;
770 icon_names[n++] = mimetype_icon;
771 icon_names[n++] = legacy_mimetype_icon;
772 icon_names[n++] = generic_mimetype_icon;
774 themed_icon = g_themed_icon_new_from_names (icon_names, n);
776 g_free (xdg_mimetype_icon);
777 g_free (mimetype_icon);
778 g_free (legacy_mimetype_icon);
779 g_free (generic_mimetype_icon);
785 * g_content_type_can_be_executable:
786 * @type: a content type string.
788 * Checks if a content type can be executable. Note that for instance
789 * things like text files can be executables (i.e. scripts and batch files).
791 * Returns: %TRUE if the file type corresponds to a type that
792 * can be executable, %FALSE otherwise.
795 g_content_type_can_be_executable (const char *type)
797 g_return_val_if_fail (type != NULL, FALSE);
799 if (g_content_type_is_a (type, "application/x-executable") ||
800 g_content_type_is_a (type, "text/plain"))
807 looks_like_text (const guchar *data, gsize data_size)
812 for (i = 0; i < data_size; i++)
816 if (g_ascii_iscntrl (c) &&
817 !g_ascii_isspace (c))
824 * g_content_type_from_mime_type:
825 * @mime_type: a mime type string.
827 * Tries to find a content type based on the mime type name.
829 * Returns: Newly allocated string with content type or NULL when does not know.
834 g_content_type_from_mime_type (const char *mime_type)
838 g_return_val_if_fail (mime_type != NULL, NULL);
840 G_LOCK (gio_xdgmime);
841 /* mime type and content type are same on unixes */
842 umime = g_strdup (xdg_mime_unalias_mime_type (mime_type));
843 G_LOCK (gio_xdgmime);
849 * g_content_type_guess:
850 * @filename: a string.
851 * @data: a stream of data.
852 * @data_size: the size of @data.
853 * @result_uncertain: a flag indicating the certainty of the
856 * Guesses the content type based on example data. If the function is
857 * uncertain, @result_uncertain will be set to %TRUE.
859 * Returns: a string indicating a guessed content type for the
863 g_content_type_guess (const char *filename,
866 gboolean *result_uncertain)
869 const char *name_mimetypes[10], *sniffed_mimetype;
872 int n_name_mimetypes;
876 n_name_mimetypes = 0;
877 sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN;
879 if (result_uncertain)
880 *result_uncertain = FALSE;
882 G_LOCK (gio_xdgmime);
886 basename = g_path_get_basename (filename);
887 n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10);
891 /* Got an extension match, and no conflicts. This is it. */
892 if (n_name_mimetypes == 1)
894 G_UNLOCK (gio_xdgmime);
895 return g_strdup (name_mimetypes[0]);
900 sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio);
901 if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
903 looks_like_text (data, data_size))
904 sniffed_mimetype = "text/plain";
907 if (n_name_mimetypes == 0)
909 if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
911 *result_uncertain = TRUE;
913 mimetype = g_strdup (sniffed_mimetype);
918 if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN)
920 if (sniffed_prio >= 80) /* High priority sniffing match, use that */
921 mimetype = g_strdup (sniffed_mimetype);
924 /* There are conflicts between the name matches and we have a sniffed
925 type, use that as a tie breaker. */
927 for (i = 0; i < n_name_mimetypes; i++)
929 if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype))
931 /* This nametype match is derived from (or the same as) the sniffed type).
932 This is probably it. */
933 mimetype = g_strdup (name_mimetypes[i]);
940 if (mimetype == NULL)
942 /* Conflicts, and sniffed type was no help or not there. Guess on the first one */
943 mimetype = g_strdup (name_mimetypes[0]);
944 if (result_uncertain)
945 *result_uncertain = TRUE;
949 G_UNLOCK (gio_xdgmime);
955 enumerate_mimetypes_subdir (const char *dir,
957 GHashTable *mimetypes)
966 while ((ent = readdir (d)) != NULL)
968 if (g_str_has_suffix (ent->d_name, ".xml"))
970 mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name);
971 g_hash_table_insert (mimetypes, mimetype, NULL);
979 enumerate_mimetypes_dir (const char *dir,
980 GHashTable *mimetypes)
987 mimedir = g_build_filename (dir, "mime", NULL);
989 d = opendir (mimedir);
992 while ((ent = readdir (d)) != NULL)
994 if (strcmp (ent->d_name, "packages") != 0)
996 name = g_build_filename (mimedir, ent->d_name, NULL);
997 if (g_file_test (name, G_FILE_TEST_IS_DIR))
998 enumerate_mimetypes_subdir (name, ent->d_name, mimetypes);
1009 * g_content_types_get_registered:
1011 * Gets a list of strings containing all the registered content types
1012 * known to the system. The list and its data should be freed using
1013 * @g_list_foreach(list, g_free, NULL) and @g_list_free(list)
1014 * Returns: #GList of the registered content types.
1017 g_content_types_get_registered (void)
1019 const char * const* dirs;
1020 GHashTable *mimetypes;
1021 GHashTableIter iter;
1026 mimetypes = g_hash_table_new (g_str_hash, g_str_equal);
1028 enumerate_mimetypes_dir (g_get_user_data_dir (), mimetypes);
1029 dirs = g_get_system_data_dirs ();
1031 for (i = 0; dirs[i] != NULL; i++)
1032 enumerate_mimetypes_dir (dirs[i], mimetypes);
1035 g_hash_table_iter_init (&iter, mimetypes);
1036 while (g_hash_table_iter_next (&iter, &key, NULL))
1037 l = g_list_prepend (l, key);
1039 g_hash_table_destroy (mimetypes);
1044 #endif /* Unix version */
1046 #define __G_CONTENT_TYPE_C__
1047 #include "gioaliasdef.c"