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>
27 #include "gcontenttypeprivate.h"
33 * SECTION:gcontenttype
34 * @short_description: Platform-specific content typing
37 * A content type is a platform specific string that defines the type
38 * of a file. On unix it is a mime type, on win32 it is an extension string
39 * like ".doc", ".txt" or a percieved string like "audio". Such strings
40 * can be looked up in the registry at HKEY_CLASSES_ROOT.
48 get_registry_classes_key (const char *subdir,
49 const wchar_t *key_name)
60 wc_key = g_utf8_to_utf16 (subdir, -1, NULL, NULL, NULL);
61 if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
62 KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS &&
63 RegQueryValueExW (reg_key, key_name, 0,
64 &key_type, NULL, &nbytes) == ERROR_SUCCESS &&
67 wchar_t *wc_temp = g_new (wchar_t, (nbytes+1)/2 + 1);
68 RegQueryValueExW (reg_key, key_name, 0,
69 &key_type, (LPBYTE) wc_temp, &nbytes);
70 wc_temp[nbytes/2] = '\0';
71 value_utf8 = g_utf16_to_utf8 (wc_temp, -1, NULL, NULL, NULL);
77 RegCloseKey (reg_key);
83 g_content_type_equals (const char *type1,
86 char *progid1, *progid2;
89 g_return_val_if_fail (type1 != NULL, FALSE);
90 g_return_val_if_fail (type2 != NULL, FALSE);
92 if (g_ascii_strcasecmp (type1, type2) == 0)
96 progid1 = get_registry_classes_key (type1, NULL);
97 progid2 = get_registry_classes_key (type2, NULL);
98 if (progid1 != NULL && progid2 != NULL &&
99 strcmp (progid1, progid2) == 0)
108 g_content_type_is_a (const char *type,
109 const char *supertype)
114 g_return_val_if_fail (type != NULL, FALSE);
115 g_return_val_if_fail (supertype != NULL, FALSE);
117 if (g_content_type_equals (type, supertype))
121 value_utf8 = get_registry_classes_key (type, L"PerceivedType");
122 if (value_utf8 && strcmp (value_utf8, supertype) == 0)
130 g_content_type_is_unknown (const char *type)
132 g_return_val_if_fail (type != NULL, FALSE);
134 return strcmp ("*", type) == 0;
138 g_content_type_get_description (const char *type)
143 g_return_val_if_fail (type != NULL, NULL);
145 progid = get_registry_classes_key (type, NULL);
148 description = get_registry_classes_key (progid, NULL);
155 if (g_content_type_is_unknown (type))
156 return g_strdup (_("Unknown type"));
157 return g_strdup_printf (_("%s filetype"), type);
161 g_content_type_get_mime_type (const char *type)
165 g_return_val_if_fail (type != NULL, NULL);
167 mime = get_registry_classes_key (type, L"Content Type");
170 else if (g_content_type_is_unknown (type))
171 return g_strdup ("application/octet-stream");
172 else if (*type == '.')
173 return g_strdup_printf ("application/x-ext-%s", type+1);
174 /* TODO: Map "image" to "image/ *", etc? */
176 return g_strdup ("application/octet-stream");
180 g_content_type_get_icon (const char *type)
182 g_return_val_if_fail (type != NULL, NULL);
184 /* TODO: How do we represent icons???
185 In the registry they are the default value of
186 HKEY_CLASSES_ROOT\<progid>\DefaultIcon with typical values like:
188 REG_EXPAND_SZ: %SystemRoot%\System32\Wscript.exe,3
189 REG_SZ: shimgvw.dll,3
195 g_content_type_can_be_executable (const char *type)
197 g_return_val_if_fail (type != NULL, FALSE);
199 if (strcmp (type, ".exe") == 0 ||
200 strcmp (type, ".com") == 0 ||
201 strcmp (type, ".bat") == 0)
207 looks_like_text (const guchar *data,
212 for (i = 0; i < data_size; i++)
215 if (g_ascii_iscntrl (c) && !g_ascii_isspace (c))
222 g_content_type_guess (const char *filename,
225 gboolean *result_uncertain)
235 basename = g_path_get_basename (filename);
236 dot = strrchr (basename, '.');
238 type = g_strdup (dot);
245 if (data && looks_like_text (data, data_size))
246 return g_strdup (".txt");
248 return g_strdup ("*");
252 g_content_types_get_registered (void)
255 wchar_t keyname[256];
263 while (RegEnumKeyExW(HKEY_CLASSES_ROOT,
270 NULL) == ERROR_SUCCESS)
272 key_utf8 = g_utf16_to_utf8 (keyname, -1, NULL, NULL, NULL);
275 if (*key_utf8 == '.')
276 types = g_list_prepend (types, key_utf8);
284 return g_list_reverse (types);
287 #else /* !G_OS_WIN32 - Unix specific version */
291 #define XDG_PREFIX _gio_xdg
292 #include "xdgmime/xdgmime.h"
294 /* We lock this mutex whenever we modify global state in this module. */
295 G_LOCK_DEFINE_STATIC (gio_xdgmime);
298 _g_unix_content_type_get_sniff_len (void)
302 G_LOCK (gio_xdgmime);
303 size = xdg_mime_get_max_buffer_extents ();
304 G_UNLOCK (gio_xdgmime);
310 _g_unix_content_type_unalias (const char *type)
314 G_LOCK (gio_xdgmime);
315 res = g_strdup (xdg_mime_unalias_mime_type (type));
316 G_UNLOCK (gio_xdgmime);
322 _g_unix_content_type_get_parents (const char *type)
325 const char **parents;
329 array = g_ptr_array_new ();
331 G_LOCK (gio_xdgmime);
333 umime = xdg_mime_unalias_mime_type (type);
334 g_ptr_array_add (array, g_strdup (umime));
336 parents = xdg_mime_get_mime_parents (umime);
337 for (i = 0; parents && parents[i] != NULL; i++)
338 g_ptr_array_add (array, g_strdup (parents[i]));
340 G_UNLOCK (gio_xdgmime);
342 g_ptr_array_add (array, NULL);
344 return (char **)g_ptr_array_free (array, FALSE);
348 * g_content_type_equals:
349 * @type1: a content type string.
350 * @type2: a content type string.
352 * Compares two content types for equality.
354 * Returns: %TRUE if the two strings are identical or equivalent,
358 g_content_type_equals (const char *type1,
363 g_return_val_if_fail (type1 != NULL, FALSE);
364 g_return_val_if_fail (type2 != NULL, FALSE);
366 G_LOCK (gio_xdgmime);
367 res = xdg_mime_mime_type_equal (type1, type2);
368 G_UNLOCK (gio_xdgmime);
374 * g_content_type_is_a:
375 * @type: a content type string.
376 * @supertype: a string.
378 * Determines if @type is a subset of @supertype.
380 * Returns: %TRUE if @type is a kind of @supertype,
384 g_content_type_is_a (const char *type,
385 const char *supertype)
389 g_return_val_if_fail (type != NULL, FALSE);
390 g_return_val_if_fail (supertype != NULL, FALSE);
392 G_LOCK (gio_xdgmime);
393 res = xdg_mime_mime_type_subclass (type, supertype);
394 G_UNLOCK (gio_xdgmime);
400 * g_content_type_is_unknown:
401 * @type: a content type string.
403 * Checks if the content type is the generic "unknown" type.
404 * On unix this is the "application/octet-stream" mimetype,
405 * while on win32 it is "*".
407 * Returns: %TRUE if the type is the unknown type.
410 g_content_type_is_unknown (const char *type)
412 g_return_val_if_fail (type != NULL, FALSE);
414 return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0;
420 MIME_TAG_TYPE_COMMENT
425 int current_lang_level;
426 int comment_lang_level;
432 language_level (const char *lang)
434 const char * const *lang_list;
437 /* The returned list is sorted from most desirable to least
438 desirable and always contains the default locale "C". */
439 lang_list = g_get_language_names ();
441 for (i = 0; lang_list[i]; i++)
442 if (strcmp (lang_list[i], lang) == 0)
449 mime_info_start_element (GMarkupParseContext *context,
450 const gchar *element_name,
451 const gchar **attribute_names,
452 const gchar **attribute_values,
458 MimeParser *parser = user_data;
460 if (strcmp (element_name, "comment") == 0)
463 for (i = 0; attribute_names[i]; i++)
464 if (strcmp (attribute_names[i], "xml:lang") == 0)
466 lang = attribute_values[i];
470 parser->current_lang_level = language_level (lang);
471 parser->current_type = MIME_TAG_TYPE_COMMENT;
474 parser->current_type = MIME_TAG_TYPE_OTHER;
479 mime_info_end_element (GMarkupParseContext *context,
480 const gchar *element_name,
484 MimeParser *parser = user_data;
486 parser->current_type = MIME_TAG_TYPE_OTHER;
490 mime_info_text (GMarkupParseContext *context,
496 MimeParser *parser = user_data;
498 if (parser->current_type == MIME_TAG_TYPE_COMMENT &&
499 parser->current_lang_level > parser->comment_lang_level)
501 g_free (parser->comment);
502 parser->comment = g_strndup (text, text_len);
503 parser->comment_lang_level = parser->current_lang_level;
508 load_comment_for_mime_helper (const char *dir,
509 const char *basename)
511 GMarkupParseContext *context;
512 char *filename, *data;
515 MimeParser parse_data = {0};
516 GMarkupParser parser = {
517 mime_info_start_element,
518 mime_info_end_element,
522 filename = g_build_filename (dir, "mime", basename, NULL);
524 res = g_file_get_contents (filename, &data, &len, NULL);
529 context = g_markup_parse_context_new (&parser, 0, &parse_data, NULL);
530 res = g_markup_parse_context_parse (context, data, len, NULL);
532 g_markup_parse_context_free (context);
537 return parse_data.comment;
542 load_comment_for_mime (const char *mimetype)
544 const char * const* dirs;
549 basename = g_strdup_printf ("%s.xml", mimetype);
551 comment = load_comment_for_mime_helper (g_get_user_data_dir (), basename);
558 dirs = g_get_system_data_dirs ();
560 for (i = 0; dirs[i] != NULL; i++)
562 comment = load_comment_for_mime_helper (dirs[i], basename);
571 return g_strdup_printf (_("%s type"), mimetype);
575 * g_content_type_get_description:
576 * @type: a content type string.
578 * Gets the human readable description of the content type.
580 * Returns: a short description of the content type @type.
583 g_content_type_get_description (const char *type)
585 static GHashTable *type_comment_cache = NULL;
588 g_return_val_if_fail (type != NULL, NULL);
590 G_LOCK (gio_xdgmime);
591 if (type_comment_cache == NULL)
592 type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
594 comment = g_hash_table_lookup (type_comment_cache, type);
595 comment = g_strdup (comment);
596 G_UNLOCK (gio_xdgmime);
601 comment = load_comment_for_mime (type);
603 G_LOCK (gio_xdgmime);
604 g_hash_table_insert (type_comment_cache,
607 G_UNLOCK (gio_xdgmime);
613 * g_content_type_get_mime_type:
614 * @type: a content type string.
616 * Gets the mime-type for the content type. If one is registered
618 * Returns: the registered mime-type for the given @type, or NULL if unknown.
621 g_content_type_get_mime_type (const char *type)
623 g_return_val_if_fail (type != NULL, NULL);
625 return g_strdup (type);
629 * g_content_type_get_icon:
630 * @type: a content type string.
632 * Gets the icon for a content type.
634 * Returns: #GIcon corresponding to the content type.
637 g_content_type_get_icon (const char *type)
639 g_return_val_if_fail (type != NULL, NULL);
641 /* TODO: Implement */
646 * g_content_type_can_be_executable:
647 * @type: a content type string.
649 * Checks if a content type can be executable. Note that for instance
650 * things like text files can be executables (i.e. scripts and batch files).
652 * Returns: %TRUE if the file type corresponds to a type that
653 * can be executable, %FALSE otherwise.
656 g_content_type_can_be_executable (const char *type)
658 g_return_val_if_fail (type != NULL, FALSE);
660 if (g_content_type_is_a (type, "application/x-executable") ||
661 g_content_type_is_a (type, "text/plain"))
668 looks_like_text (const guchar *data, gsize data_size)
671 for (i = 0; i < data_size; i++)
673 if g_ascii_iscntrl (data[i])
680 * g_content_type_guess:
681 * @filename: a string.
682 * @data: a stream of data.
683 * @data_size: the size of @data.
684 * @result_uncertain: a flag indicating the certainty of the
687 * Guesses the content type based on example data. If the function is
688 * uncertain, @result_uncertain will be set to %TRUE.
690 * Returns: a string indicating a guessed content type for the
694 g_content_type_guess (const char *filename,
697 gboolean *result_uncertain)
700 const char *name_mimetypes[10], *sniffed_mimetype;
703 int n_name_mimetypes;
707 n_name_mimetypes = 0;
708 sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN;
710 if (result_uncertain)
711 *result_uncertain = FALSE;
713 G_LOCK (gio_xdgmime);
717 basename = g_path_get_basename (filename);
718 n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10);
722 /* Got an extension match, and no conflicts. This is it. */
723 if (n_name_mimetypes == 1)
725 G_UNLOCK (gio_xdgmime);
726 return g_strdup (name_mimetypes[0]);
731 sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio);
732 if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
734 looks_like_text (data, data_size))
735 sniffed_mimetype = "text/plain";
738 if (n_name_mimetypes == 0)
740 if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
742 *result_uncertain = TRUE;
744 mimetype = g_strdup (sniffed_mimetype);
749 if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN)
751 if (sniffed_prio >= 80) /* High priority sniffing match, use that */
752 mimetype = g_strdup (sniffed_mimetype);
755 /* There are conflicts between the name matches and we have a sniffed
756 type, use that as a tie breaker. */
758 for (i = 0; i < n_name_mimetypes; i++)
760 if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype))
762 /* This nametype match is derived from (or the same as) the sniffed type).
763 This is probably it. */
764 mimetype = g_strdup (name_mimetypes[i]);
771 if (mimetype == NULL)
773 /* Conflicts, and sniffed type was no help or not there. guess on the first one */
774 mimetype = g_strdup (name_mimetypes[0]);
775 if (result_uncertain)
776 *result_uncertain = TRUE;
780 G_UNLOCK (gio_xdgmime);
786 enumerate_mimetypes_subdir (const char *dir,
788 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,
811 GHashTable *mimetypes)
818 mimedir = g_build_filename (dir, "mime", NULL);
820 d = opendir (mimedir);
823 while ((ent = readdir (d)) != NULL)
825 if (strcmp (ent->d_name, "packages") != 0)
827 name = g_build_filename (mimedir, ent->d_name, NULL);
828 if (g_file_test (name, G_FILE_TEST_IS_DIR))
829 enumerate_mimetypes_subdir (name, ent->d_name, mimetypes);
840 * g_content_types_get_registered:
842 * Gets a list of strings containing all the registered content types
843 * known to the system. The list and its data should be freed using
844 * @g_list_foreach(list, g_free, NULL) and @g_list_free(list)
845 * Returns: #GList of the registered content types.
848 g_content_types_get_registered (void)
850 const char * const* dirs;
851 GHashTable *mimetypes;
857 mimetypes = g_hash_table_new (g_str_hash, g_str_equal);
859 enumerate_mimetypes_dir (g_get_user_data_dir (), mimetypes);
860 dirs = g_get_system_data_dirs ();
862 for (i = 0; dirs[i] != NULL; i++)
863 enumerate_mimetypes_dir (dirs[i], mimetypes);
866 g_hash_table_iter_init (&iter, mimetypes);
867 while (g_hash_table_iter_next (&iter, &key, NULL))
868 l = g_list_prepend (l, key);
870 g_hash_table_destroy (mimetypes);
875 #endif /* Unix version */
877 #define __G_CONTENT_TYPE_C__
878 #include "gioaliasdef.c"