diff --git a/gio/gappinfo.c b/gio/gappinfo.c index dd2f75695..c54fc677b 100644 --- a/gio/gappinfo.c +++ b/gio/gappinfo.c @@ -116,6 +116,38 @@ g_app_info_default_init (GAppInfoInterface *iface) { } +/** + * g_app_info_create_from_commandline: + * @commandline: (type filename): the command line to use + * @application_name: (nullable): the application name, or `NULL` to use @commandline + * @flags: flags that can specify details of the created [iface@Gio.AppInfo] + * @error: a [type@GLib.Error] location to store the error occurring, + * `NULL` to ignore. + * + * Creates a new [iface@Gio.AppInfo] from the given information. + * + * Note that for @commandline, the quoting rules of the `Exec` key of the + * [freedesktop.org Desktop Entry Specification](http://freedesktop.org/Standards/desktop-entry-spec) + * are applied. For example, if the @commandline contains + * percent-encoded URIs, the percent-character must be doubled in order to prevent it from + * being swallowed by `Exec` key unquoting. See + * [the specification](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s07.html) + * for exact quoting rules. + * + * Returns: (transfer full): new [iface@Gio.AppInfo] for given command. + **/ +GAppInfo * +g_app_info_create_from_commandline (const char *commandline, + const char *application_name, + GAppInfoCreateFlags flags, + GError **error) +{ + g_return_val_if_fail (commandline, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + return g_app_info_create_from_commandline_impl (commandline, application_name, + flags, error); +} /** * g_app_info_dup: @@ -374,6 +406,156 @@ g_app_info_set_as_last_used_for_type (GAppInfo *appinfo, return FALSE; } +/** + * g_app_info_get_all: + * + * Gets a list of all of the applications currently registered + * on this system. + * + * For desktop files, this includes applications that have + * [`NoDisplay=true`](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-nodisplay) + * set or are excluded from display by means of + * [`OnlyShowIn`](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-onlyshowin) + * or [`NotShowIn`](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-notshowin). + * See [method@Gio.AppInfo.should_show]. + * + * The returned list does not include applications which have the + * [`Hidden` key](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-hidden) + * set. + * + * Returns: (element-type GAppInfo) (transfer full): a newly allocated + * list of references to [iface@Gio.AppInfo]s. + **/ +GList * +g_app_info_get_all (void) +{ + return g_app_info_get_all_impl (); +} + +/** + * g_app_info_get_recommended_for_type: + * @content_type: the content type to find a [iface@Gio.AppInfo] for + * + * Gets a list of recommended [iface@Gio.AppInfo]s for a given content type, + * i.e. those applications which claim to support the given content type + * exactly, and not by MIME type subclassing. + * + * Note that the first application of the list is the last used one, i.e. + * the last one for which [method@Gio.AppInfo.set_as_last_used_for_type] has + * been called. + * + * Returns: (element-type GAppInfo) (transfer full): list of + * [iface@Gio.AppInfo]s for given @content_type or `NULL` on error. + * + * Since: 2.28 + **/ +GList * +g_app_info_get_recommended_for_type (const gchar *content_type) +{ + g_return_val_if_fail (content_type != NULL, NULL); + + return g_app_info_get_recommended_for_type_impl (content_type); +} + +/** + * g_app_info_get_fallback_for_type: + * @content_type: the content type to find a [iface@Gio.AppInfo] for + * + * Gets a list of fallback [iface@Gio.AppInfo]s for a given content type, i.e. + * those applications which claim to support the given content type by MIME + * type subclassing and not directly. + * + * Returns: (element-type GAppInfo) (transfer full): list of [iface@Gio.AppInfo]s + * for given @content_type or `NULL` on error. + * + * Since: 2.28 + **/ +GList * +g_app_info_get_fallback_for_type (const gchar *content_type) +{ + g_return_val_if_fail (content_type != NULL, NULL); + + return g_app_info_get_fallback_for_type_impl (content_type); +} + +/** + * g_app_info_get_all_for_type: + * @content_type: the content type to find a [iface@Gio.AppInfo] for + * + * Gets a list of all [iface@Gio.AppInfo]s for a given content type, + * including the recommended and fallback [iface@Gio.AppInfo]s. See + * [func@Gio.AppInfo.get_recommended_for_type] and + * [func@Gio.AppInfo.get_fallback_for_type]. + * + * Returns: (element-type GAppInfo) (transfer full): list of + * [iface@Gio.AppInfo]s for given @content_type. + **/ +GList * +g_app_info_get_all_for_type (const char *content_type) +{ + g_return_val_if_fail (content_type != NULL, NULL); + + return g_app_info_get_all_for_type_impl (content_type); +} + +/** + * g_app_info_reset_type_associations: + * @content_type: a content type + * + * Removes all changes to the type associations done by + * [method@Gio.AppInfo.set_as_default_for_type], + * [method@Gio.AppInfo.set_as_default_for_extension], + * [method@Gio.AppInfo.add_supports_type] or + * [method@Gio.AppInfo.remove_supports_type]. + * + * Since: 2.20 + */ +void +g_app_info_reset_type_associations (const char *content_type) +{ + g_app_info_reset_type_associations_impl (content_type); +} + +/** + * g_app_info_get_default_for_type: + * @content_type: the content type to find a [iface@Gio.AppInfo] for + * @must_support_uris: if `TRUE`, the [iface@Gio.AppInfo] is expected to + * support URIs + * + * Gets the default [iface@Gio.AppInfo] for a given content type. + * + * Returns: (transfer full) (nullable): [iface@Gio.AppInfo] for given + * @content_type or `NULL` on error. + */ +GAppInfo * +g_app_info_get_default_for_type (const char *content_type, + gboolean must_support_uris) +{ + g_return_val_if_fail (content_type != NULL, NULL); + + return g_app_info_get_default_for_type_impl (content_type, must_support_uris); +} + +/** + * g_app_info_get_default_for_uri_scheme: + * @uri_scheme: a string containing a URI scheme. + * + * Gets the default application for handling URIs with the given URI scheme. + * + * A URI scheme is the initial part of the URI, up to but not including the `:`. + * For example, `http`, `ftp` or `sip`. + * + * Returns: (transfer full) (nullable): [iface@Gio.AppInfo] for given + * @uri_scheme or `NULL` on error. + */ +GAppInfo * +g_app_info_get_default_for_uri_scheme (const char *uri_scheme) +{ + g_return_val_if_fail (uri_scheme != NULL && *uri_scheme != '\0', NULL); + + return g_app_info_get_default_for_uri_scheme_impl (uri_scheme); +} + /** * g_app_info_set_as_default_for_extension: * @appinfo: the app info diff --git a/gio/gappinfoprivate.h b/gio/gappinfoprivate.h index dbf46c22a..df6a2d3bf 100644 --- a/gio/gappinfoprivate.h +++ b/gio/gappinfoprivate.h @@ -25,4 +25,17 @@ void g_app_info_monitor_fire (void); +GAppInfo *g_app_info_create_from_commandline_impl (const char *commandline, + const char *application_name, + GAppInfoCreateFlags flags, + GError **error); +GList *g_app_info_get_recommended_for_type_impl (const gchar *content_type); +GList *g_app_info_get_fallback_for_type_impl (const gchar *content_type); +GList *g_app_info_get_all_for_type_impl (const char *content_type); +void g_app_info_reset_type_associations_impl (const char *content_type); +GAppInfo *g_app_info_get_default_for_type_impl (const char *content_type, + gboolean must_support_uris); +GAppInfo *g_app_info_get_default_for_uri_scheme_impl (const char *uri_scheme); +GList *g_app_info_get_all_impl (void); + #endif /* __G_APP_INFO_PRIVATE_H__ */ diff --git a/gio/gcontenttype-fdo.c b/gio/gcontenttype-fdo.c new file mode 100644 index 000000000..230cea182 --- /dev/null +++ b/gio/gcontenttype-fdo.c @@ -0,0 +1,1356 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + * + * Author: Alexander Larsson + */ + +#include "config.h" +#include +#include +#include +#include +#include "gcontenttypeprivate.h" +#include "gthemedicon.h" +#include "gicon.h" +#include "gfile.h" +#include "gfileenumerator.h" +#include "gfileinfo.h" +#include "glibintl.h" +#include "glib-private.h" + +#include + +#define XDG_PREFIX _gio_xdg +#include "xdgmime/xdgmime.h" + +static void tree_magic_schedule_reload (void); + +/* We lock this mutex whenever we modify global state in this module. + * Taking and releasing this lock should always be associated with a pair of + * g_begin_ignore_leaks()/g_end_ignore_leaks() calls, as any call into xdgmime + * could trigger xdg_mime_init(), which makes a number of one-time allocations + * which GLib can never free as it doesn’t know when is suitable to call + * xdg_mime_shutdown(). */ +G_LOCK_DEFINE_STATIC (gio_xdgmime); + +gsize +_g_unix_content_type_get_sniff_len (void) +{ + gsize size; + + G_LOCK (gio_xdgmime); + g_begin_ignore_leaks (); + size = xdg_mime_get_max_buffer_extents (); + g_end_ignore_leaks (); + G_UNLOCK (gio_xdgmime); + + return size; +} + +gchar * +_g_unix_content_type_unalias (const gchar *type) +{ + gchar *res; + + G_LOCK (gio_xdgmime); + g_begin_ignore_leaks (); + res = g_strdup (xdg_mime_unalias_mime_type (type)); + g_end_ignore_leaks (); + G_UNLOCK (gio_xdgmime); + + return res; +} + +gchar ** +_g_unix_content_type_get_parents (const gchar *type) +{ + const gchar *umime; + gchar **parents; + GPtrArray *array; + int i; + + array = g_ptr_array_new (); + + G_LOCK (gio_xdgmime); + g_begin_ignore_leaks (); + + umime = xdg_mime_unalias_mime_type (type); + + g_ptr_array_add (array, g_strdup (umime)); + + parents = xdg_mime_list_mime_parents (umime); + for (i = 0; parents && parents[i] != NULL; i++) + g_ptr_array_add (array, g_strdup (parents[i])); + + free (parents); + + g_end_ignore_leaks (); + G_UNLOCK (gio_xdgmime); + + g_ptr_array_add (array, NULL); + + return (gchar **)g_ptr_array_free (array, FALSE); +} + +G_LOCK_DEFINE_STATIC (global_mime_dirs); +static gchar **global_mime_dirs = NULL; + +static void +_g_content_type_set_mime_dirs_locked (const char * const *dirs) +{ + g_clear_pointer (&global_mime_dirs, g_strfreev); + + if (dirs != NULL) + { + global_mime_dirs = g_strdupv ((gchar **) dirs); + } + else + { + GPtrArray *mime_dirs = g_ptr_array_new_with_free_func (g_free); + const gchar * const *system_dirs = g_get_system_data_dirs (); + + g_ptr_array_add (mime_dirs, g_build_filename (g_get_user_data_dir (), "mime", NULL)); + for (; *system_dirs != NULL; system_dirs++) + g_ptr_array_add (mime_dirs, g_build_filename (*system_dirs, "mime", NULL)); + g_ptr_array_add (mime_dirs, NULL); /* NULL terminator */ + + global_mime_dirs = (gchar **) g_ptr_array_free (mime_dirs, FALSE); + } + + xdg_mime_set_dirs ((const gchar * const *) global_mime_dirs); + tree_magic_schedule_reload (); +} + +/*< private >*/ +void +g_content_type_set_mime_dirs_impl (const gchar * const *dirs) +{ + G_LOCK (global_mime_dirs); + _g_content_type_set_mime_dirs_locked (dirs); + G_UNLOCK (global_mime_dirs); +} + +/*< private >*/ +const gchar * const * +g_content_type_get_mime_dirs_impl (void) +{ + const gchar * const *mime_dirs; + + G_LOCK (global_mime_dirs); + + if (global_mime_dirs == NULL) + _g_content_type_set_mime_dirs_locked (NULL); + + mime_dirs = (const gchar * const *) global_mime_dirs; + + G_UNLOCK (global_mime_dirs); + + g_assert (mime_dirs != NULL); + return mime_dirs; +} + +gboolean +g_content_type_equals_impl (const gchar *type1, + const gchar *type2) +{ + gboolean res; + + g_return_val_if_fail (type1 != NULL, FALSE); + g_return_val_if_fail (type2 != NULL, FALSE); + + G_LOCK (gio_xdgmime); + g_begin_ignore_leaks (); + res = xdg_mime_mime_type_equal (type1, type2); + g_end_ignore_leaks (); + G_UNLOCK (gio_xdgmime); + + return res; +} + +gboolean +g_content_type_is_a_impl (const gchar *type, + const gchar *supertype) +{ + gboolean res; + + g_return_val_if_fail (type != NULL, FALSE); + g_return_val_if_fail (supertype != NULL, FALSE); + + G_LOCK (gio_xdgmime); + g_begin_ignore_leaks (); + res = xdg_mime_mime_type_subclass (type, supertype); + g_end_ignore_leaks (); + G_UNLOCK (gio_xdgmime); + + return res; +} + +gboolean +g_content_type_is_mime_type_impl (const gchar *type, + const gchar *mime_type) +{ + return g_content_type_is_a (type, mime_type); +} + +gboolean +g_content_type_is_unknown_impl (const gchar *type) +{ + g_return_val_if_fail (type != NULL, FALSE); + + return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0; +} + + +typedef enum { + MIME_TAG_TYPE_OTHER, + MIME_TAG_TYPE_COMMENT +} MimeTagType; + +typedef struct { + int current_type; + int current_lang_level; + int comment_lang_level; + char *comment; +} MimeParser; + + +static int +language_level (const char *lang) +{ + const char * const *lang_list; + int i; + + /* The returned list is sorted from most desirable to least + desirable and always contains the default locale "C". */ + lang_list = g_get_language_names (); + + for (i = 0; lang_list[i]; i++) + if (strcmp (lang_list[i], lang) == 0) + return 1000-i; + + return 0; +} + +static void +mime_info_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + int i; + const char *lang; + MimeParser *parser = user_data; + + if (strcmp (element_name, "comment") == 0) + { + lang = "C"; + for (i = 0; attribute_names[i]; i++) + if (strcmp (attribute_names[i], "xml:lang") == 0) + { + lang = attribute_values[i]; + break; + } + + parser->current_lang_level = language_level (lang); + parser->current_type = MIME_TAG_TYPE_COMMENT; + } + else + parser->current_type = MIME_TAG_TYPE_OTHER; +} + +static void +mime_info_end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + MimeParser *parser = user_data; + + parser->current_type = MIME_TAG_TYPE_OTHER; +} + +static void +mime_info_text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + MimeParser *parser = user_data; + + if (parser->current_type == MIME_TAG_TYPE_COMMENT && + parser->current_lang_level > parser->comment_lang_level) + { + g_free (parser->comment); + parser->comment = g_strndup (text, text_len); + parser->comment_lang_level = parser->current_lang_level; + } +} + +static char * +load_comment_for_mime_helper (const char *dir, + const char *basename) +{ + GMarkupParseContext *context; + char *filename, *data; + gsize len; + gboolean res; + MimeParser parse_data = {0}; + GMarkupParser parser = { + mime_info_start_element, + mime_info_end_element, + mime_info_text, + NULL, + NULL + }; + + filename = g_build_filename (dir, basename, NULL); + + res = g_file_get_contents (filename, &data, &len, NULL); + g_free (filename); + if (!res) + return NULL; + + context = g_markup_parse_context_new (&parser, G_MARKUP_DEFAULT_FLAGS, &parse_data, NULL); + res = g_markup_parse_context_parse (context, data, len, NULL); + g_free (data); + g_markup_parse_context_free (context); + + if (!res) + return NULL; + + return parse_data.comment; +} + + +static char * +load_comment_for_mime (const char *mimetype) +{ + const char * const *dirs; + char *basename; + char *comment; + gsize i; + + basename = g_strdup_printf ("%s.xml", mimetype); + + dirs = g_content_type_get_mime_dirs (); + for (i = 0; dirs[i] != NULL; i++) + { + comment = load_comment_for_mime_helper (dirs[i], basename); + if (comment) + { + g_free (basename); + return comment; + } + } + g_free (basename); + + return g_strdup_printf (_("%s type"), mimetype); +} + +gchar * +g_content_type_get_description_impl (const gchar *type) +{ + static GHashTable *type_comment_cache = NULL; + gchar *type_copy = NULL; + gchar *comment; + + g_return_val_if_fail (type != NULL, NULL); + + G_LOCK (gio_xdgmime); + g_begin_ignore_leaks (); + type = xdg_mime_unalias_mime_type (type); + g_end_ignore_leaks (); + + if (type_comment_cache == NULL) + type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + comment = g_hash_table_lookup (type_comment_cache, type); + comment = g_strdup (comment); + + if (comment != NULL) + { + G_UNLOCK (gio_xdgmime); + return g_steal_pointer (&comment); + } + + type_copy = g_strdup (type); + + G_UNLOCK (gio_xdgmime); + comment = load_comment_for_mime (type_copy); + G_LOCK (gio_xdgmime); + + g_hash_table_insert (type_comment_cache, + g_steal_pointer (&type_copy), + g_strdup (comment)); + G_UNLOCK (gio_xdgmime); + + return g_steal_pointer (&comment); +} + +char * +g_content_type_get_mime_type_impl (const char *type) +{ + g_return_val_if_fail (type != NULL, NULL); + + return g_strdup (type); +} + +static GIcon * +g_content_type_get_icon_internal (const gchar *type, + gboolean symbolic) +{ + char *mimetype_icon; + char *generic_mimetype_icon = NULL; + char *q; + char *icon_names[6]; + int n = 0; + GIcon *themed_icon; + const char *xdg_icon; + int i; + + g_return_val_if_fail (type != NULL, NULL); + + G_LOCK (gio_xdgmime); + g_begin_ignore_leaks (); + xdg_icon = xdg_mime_get_icon (type); + g_end_ignore_leaks (); + G_UNLOCK (gio_xdgmime); + + if (xdg_icon) + icon_names[n++] = g_strdup (xdg_icon); + + mimetype_icon = g_strdup (type); + while ((q = strchr (mimetype_icon, '/')) != NULL) + *q = '-'; + + icon_names[n++] = mimetype_icon; + + generic_mimetype_icon = g_content_type_get_generic_icon_name (type); + if (generic_mimetype_icon) + icon_names[n++] = generic_mimetype_icon; + + if (symbolic) + { + for (i = 0; i < n; i++) + { + icon_names[n + i] = icon_names[i]; + icon_names[i] = g_strconcat (icon_names[i], "-symbolic", NULL); + } + + n += n; + } + + themed_icon = g_themed_icon_new_from_names (icon_names, n); + + for (i = 0; i < n; i++) + g_free (icon_names[i]); + + return themed_icon; +} + +GIcon * +g_content_type_get_icon_impl (const gchar *type) +{ + return g_content_type_get_icon_internal (type, FALSE); +} + +GIcon * +g_content_type_get_symbolic_icon_impl (const gchar *type) +{ + return g_content_type_get_icon_internal (type, TRUE); +} + +gchar * +g_content_type_get_generic_icon_name_impl (const gchar *type) +{ + const gchar *xdg_icon_name; + gchar *icon_name; + + g_return_val_if_fail (type != NULL, NULL); + + G_LOCK (gio_xdgmime); + g_begin_ignore_leaks (); + xdg_icon_name = xdg_mime_get_generic_icon (type); + g_end_ignore_leaks (); + G_UNLOCK (gio_xdgmime); + + if (!xdg_icon_name) + { + const char *p; + const char *suffix = "-x-generic"; + + p = strchr (type, '/'); + if (p == NULL) + p = type + strlen (type); + + icon_name = g_malloc (p - type + strlen (suffix) + 1); + memcpy (icon_name, type, p - type); + memcpy (icon_name + (p - type), suffix, strlen (suffix)); + icon_name[(p - type) + strlen (suffix)] = 0; + } + else + { + icon_name = g_strdup (xdg_icon_name); + } + + return icon_name; +} + +gboolean +g_content_type_can_be_executable_impl (const gchar *type) +{ + g_return_val_if_fail (type != NULL, FALSE); + + if (g_content_type_is_a (type, "application/x-executable") || + g_content_type_is_a (type, "text/plain")) + return TRUE; + + return FALSE; +} + +static gboolean +looks_like_text (const guchar *data, gsize data_size) +{ + gsize i; + char c; + + for (i = 0; i < data_size; i++) + { + c = data[i]; + + if (g_ascii_iscntrl (c) && + !g_ascii_isspace (c) && + c != '\b') + return FALSE; + } + return TRUE; +} + +gchar * +g_content_type_from_mime_type_impl (const gchar *mime_type) +{ + char *umime; + + g_return_val_if_fail (mime_type != NULL, NULL); + + G_LOCK (gio_xdgmime); + g_begin_ignore_leaks (); + /* mime type and content type are same on unixes */ + umime = g_strdup (xdg_mime_unalias_mime_type (mime_type)); + g_end_ignore_leaks (); + G_UNLOCK (gio_xdgmime); + + return umime; +} + +gchar * +g_content_type_guess_impl (const gchar *filename, + const guchar *data, + gsize data_size, + gboolean *result_uncertain) +{ + char *basename; + const char *name_mimetypes[10], *sniffed_mimetype; + char *mimetype; + int i; + int n_name_mimetypes; + int sniffed_prio; + + sniffed_prio = 0; + n_name_mimetypes = 0; + sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN; + + if (result_uncertain) + *result_uncertain = FALSE; + + /* our test suite and potentially other code used -1 in the past, which is + * not documented and not allowed; guard against that */ + g_return_val_if_fail (data_size != (gsize) -1, g_strdup (XDG_MIME_TYPE_UNKNOWN)); + + G_LOCK (gio_xdgmime); + g_begin_ignore_leaks (); + + if (filename) + { + i = strlen (filename); + if (i > 0 && filename[i - 1] == '/') + { + name_mimetypes[0] = "inode/directory"; + name_mimetypes[1] = NULL; + n_name_mimetypes = 1; + if (result_uncertain) + *result_uncertain = TRUE; + } + else + { + basename = g_path_get_basename (filename); + n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10); + g_free (basename); + } + } + + /* Got an extension match, and no conflicts. This is it. */ + if (n_name_mimetypes == 1) + { + gchar *s = g_strdup (name_mimetypes[0]); + g_end_ignore_leaks (); + G_UNLOCK (gio_xdgmime); + return s; + } + + if (data) + { + sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio); + if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN && + data && + looks_like_text (data, data_size)) + sniffed_mimetype = "text/plain"; + + /* For security reasons we don't ever want to sniff desktop files + * where we know the filename and it doesn't have a .desktop extension. + * This is because desktop files allow executing any application and + * we don't want to make it possible to hide them looking like something + * else. + */ + if (filename != NULL && + strcmp (sniffed_mimetype, "application/x-desktop") == 0) + sniffed_mimetype = "text/plain"; + } + + if (n_name_mimetypes == 0) + { + if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN && + result_uncertain) + *result_uncertain = TRUE; + + mimetype = g_strdup (sniffed_mimetype); + } + else + { + mimetype = NULL; + if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN) + { + if (sniffed_prio >= 80) /* High priority sniffing match, use that */ + mimetype = g_strdup (sniffed_mimetype); + else + { + /* There are conflicts between the name matches and we + * have a sniffed type, use that as a tie breaker. + */ + for (i = 0; i < n_name_mimetypes; i++) + { + if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype)) + { + /* This nametype match is derived from (or the same as) + * the sniffed type). This is probably it. + */ + mimetype = g_strdup (name_mimetypes[i]); + break; + } + } + } + } + + if (mimetype == NULL) + { + /* Conflicts, and sniffed type was no help or not there. + * Guess on the first one + */ + mimetype = g_strdup (name_mimetypes[0]); + if (result_uncertain) + *result_uncertain = TRUE; + } + } + + g_end_ignore_leaks (); + G_UNLOCK (gio_xdgmime); + + return mimetype; +} + +static void +enumerate_mimetypes_subdir (const char *dir, + const char *prefix, + GHashTable *mimetypes) +{ + DIR *d; + struct dirent *ent; + char *mimetype; + + d = opendir (dir); + if (d) + { + while ((ent = readdir (d)) != NULL) + { + if (g_str_has_suffix (ent->d_name, ".xml")) + { + mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name); + g_hash_table_replace (mimetypes, mimetype, NULL); + } + } + closedir (d); + } +} + +static void +enumerate_mimetypes_dir (const char *dir, + GHashTable *mimetypes) +{ + DIR *d; + struct dirent *ent; + const char *mimedir; + char *name; + + mimedir = dir; + + d = opendir (mimedir); + if (d) + { + while ((ent = readdir (d)) != NULL) + { + if (strcmp (ent->d_name, "packages") != 0) + { + name = g_build_filename (mimedir, ent->d_name, NULL); + if (g_file_test (name, G_FILE_TEST_IS_DIR)) + enumerate_mimetypes_subdir (name, ent->d_name, mimetypes); + g_free (name); + } + } + closedir (d); + } +} + +GList * +g_content_types_get_registered_impl (void) +{ + const char * const *dirs; + GHashTable *mimetypes; + GHashTableIter iter; + gpointer key; + gsize i; + GList *l; + + mimetypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + dirs = g_content_type_get_mime_dirs (); + for (i = 0; dirs[i] != NULL; i++) + enumerate_mimetypes_dir (dirs[i], mimetypes); + + l = NULL; + g_hash_table_iter_init (&iter, mimetypes); + while (g_hash_table_iter_next (&iter, &key, NULL)) + { + l = g_list_prepend (l, key); + g_hash_table_iter_steal (&iter); + } + + g_hash_table_destroy (mimetypes); + + return l; +} + + +/* tree magic data */ +static GList *tree_matches = NULL; +static gboolean need_reload = FALSE; + +G_LOCK_DEFINE_STATIC (gio_treemagic); + +typedef struct +{ + gchar *path; + GFileType type; + guint match_case : 1; + guint executable : 1; + guint non_empty : 1; + guint on_disc : 1; + gchar *mimetype; + GList *matches; +} TreeMatchlet; + +typedef struct +{ + gchar *contenttype; + gint priority; + GList *matches; +} TreeMatch; + + +static void +tree_matchlet_free (TreeMatchlet *matchlet) +{ + g_list_free_full (matchlet->matches, (GDestroyNotify) tree_matchlet_free); + g_free (matchlet->path); + g_free (matchlet->mimetype); + g_slice_free (TreeMatchlet, matchlet); +} + +static void +tree_match_free (TreeMatch *match) +{ + g_list_free_full (match->matches, (GDestroyNotify) tree_matchlet_free); + g_free (match->contenttype); + g_slice_free (TreeMatch, match); +} + +static TreeMatch * +parse_header (gchar *line) +{ + gint len; + gchar *s; + TreeMatch *match; + + len = strlen (line); + + if (line[0] != '[' || line[len - 1] != ']') + return NULL; + + line[len - 1] = 0; + s = strchr (line, ':'); + if (s == NULL) + return NULL; + + match = g_slice_new0 (TreeMatch); + match->priority = atoi (line + 1); + match->contenttype = g_strdup (s + 1); + + return match; +} + +static TreeMatchlet * +parse_match_line (gchar *line, + gint *depth) +{ + gchar *s, *p; + TreeMatchlet *matchlet; + gchar **parts; + gint i; + + matchlet = g_slice_new0 (TreeMatchlet); + + if (line[0] == '>') + { + *depth = 0; + s = line; + } + else + { + *depth = atoi (line); + s = strchr (line, '>'); + if (s == NULL) + goto handle_error; + } + s += 2; + p = strchr (s, '"'); + if (p == NULL) + goto handle_error; + *p = 0; + + matchlet->path = g_strdup (s); + s = p + 1; + parts = g_strsplit (s, ",", 0); + if (strcmp (parts[0], "=file") == 0) + matchlet->type = G_FILE_TYPE_REGULAR; + else if (strcmp (parts[0], "=directory") == 0) + matchlet->type = G_FILE_TYPE_DIRECTORY; + else if (strcmp (parts[0], "=link") == 0) + matchlet->type = G_FILE_TYPE_SYMBOLIC_LINK; + else + matchlet->type = G_FILE_TYPE_UNKNOWN; + for (i = 1; parts[i]; i++) + { + if (strcmp (parts[i], "executable") == 0) + matchlet->executable = 1; + else if (strcmp (parts[i], "match-case") == 0) + matchlet->match_case = 1; + else if (strcmp (parts[i], "non-empty") == 0) + matchlet->non_empty = 1; + else if (strcmp (parts[i], "on-disc") == 0) + matchlet->on_disc = 1; + else + matchlet->mimetype = g_strdup (parts[i]); + } + + g_strfreev (parts); + + return matchlet; + +handle_error: + g_slice_free (TreeMatchlet, matchlet); + return NULL; +} + +static gint +cmp_match (gconstpointer a, gconstpointer b) +{ + const TreeMatch *aa = (const TreeMatch *)a; + const TreeMatch *bb = (const TreeMatch *)b; + + return bb->priority - aa->priority; +} + +static void +insert_match (TreeMatch *match) +{ + tree_matches = g_list_insert_sorted (tree_matches, match, cmp_match); +} + +static void +insert_matchlet (TreeMatch *match, + TreeMatchlet *matchlet, + gint depth) +{ + if (depth == 0) + match->matches = g_list_append (match->matches, matchlet); + else + { + GList *last; + TreeMatchlet *m; + + last = g_list_last (match->matches); + if (!last) + { + tree_matchlet_free (matchlet); + g_warning ("can't insert tree matchlet at depth %d", depth); + return; + } + + m = (TreeMatchlet *) last->data; + while (--depth > 0) + { + last = g_list_last (m->matches); + if (!last) + { + tree_matchlet_free (matchlet); + g_warning ("can't insert tree matchlet at depth %d", depth); + return; + } + + m = (TreeMatchlet *) last->data; + } + m->matches = g_list_append (m->matches, matchlet); + } +} + +static void +read_tree_magic_from_directory (const gchar *prefix) +{ + gchar *filename; + gchar *text; + gsize len; + gchar **lines; + gsize i; + TreeMatch *match; + TreeMatchlet *matchlet; + gint depth; + + filename = g_build_filename (prefix, "treemagic", NULL); + + if (g_file_get_contents (filename, &text, &len, NULL)) + { + if (strcmp (text, "MIME-TreeMagic") == 0) + { + lines = g_strsplit (text + strlen ("MIME-TreeMagic") + 2, "\n", 0); + match = NULL; + for (i = 0; lines[i] && lines[i][0]; i++) + { + if (lines[i][0] == '[' && (match = parse_header (lines[i])) != NULL) + { + insert_match (match); + } + else if (match != NULL) + { + matchlet = parse_match_line (lines[i], &depth); + if (matchlet == NULL) + { + g_warning ("%s: body corrupt; skipping", filename); + break; + } + insert_matchlet (match, matchlet, depth); + } + else + { + g_warning ("%s: header corrupt; skipping", filename); + break; + } + } + + g_strfreev (lines); + } + else + g_warning ("%s: header not found, skipping", filename); + + g_free (text); + } + + g_free (filename); +} + +static void +tree_magic_schedule_reload (void) +{ + need_reload = TRUE; +} + +static void +xdg_mime_reload (void *user_data) +{ + tree_magic_schedule_reload (); +} + +static void +tree_magic_shutdown (void) +{ + g_list_free_full (tree_matches, (GDestroyNotify) tree_match_free); + tree_matches = NULL; +} + +static void +tree_magic_init (void) +{ + static gboolean initialized = FALSE; + gsize i; + + if (!initialized) + { + initialized = TRUE; + + xdg_mime_register_reload_callback (xdg_mime_reload, NULL, NULL); + need_reload = TRUE; + } + + if (need_reload) + { + const char * const *dirs; + + need_reload = FALSE; + + tree_magic_shutdown (); + + dirs = g_content_type_get_mime_dirs (); + for (i = 0; dirs[i] != NULL; i++) + read_tree_magic_from_directory (dirs[i]); + } +} + +/* a filtering enumerator */ + +typedef struct +{ + gchar *path; + gint depth; + gboolean ignore_case; + gchar **components; + gchar **case_components; + GFileEnumerator **enumerators; + GFile **children; +} Enumerator; + +static gboolean +component_match (Enumerator *e, + gint depth, + const gchar *name) +{ + gchar *case_folded, *key, *utf8_name; + gboolean found; + + if (strcmp (name, e->components[depth]) == 0) + return TRUE; + + if (!e->ignore_case) + return FALSE; + + utf8_name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL); + if (utf8_name == NULL) + utf8_name = g_utf8_make_valid (name, -1); + + case_folded = g_utf8_casefold (utf8_name, -1); + key = g_utf8_collate_key (case_folded, -1); + + found = strcmp (key, e->case_components[depth]) == 0; + + g_free (utf8_name); + g_free (case_folded); + g_free (key); + + return found; +} + +static GFile * +next_match_recurse (Enumerator *e, + gint depth) +{ + GFile *file; + GFileInfo *info; + const gchar *name; + + while (TRUE) + { + if (e->enumerators[depth] == NULL) + { + if (depth > 0) + { + file = next_match_recurse (e, depth - 1); + if (file) + { + e->children[depth] = file; + e->enumerators[depth] = g_file_enumerate_children (file, + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + } + } + if (e->enumerators[depth] == NULL) + return NULL; + } + + while ((info = g_file_enumerator_next_file (e->enumerators[depth], NULL, NULL))) + { + name = g_file_info_get_name (info); + if (component_match (e, depth, name)) + { + file = g_file_get_child (e->children[depth], name); + g_object_unref (info); + return file; + } + g_object_unref (info); + } + + g_object_unref (e->enumerators[depth]); + e->enumerators[depth] = NULL; + g_object_unref (e->children[depth]); + e->children[depth] = NULL; + } +} + +static GFile * +enumerator_next (Enumerator *e) +{ + return next_match_recurse (e, e->depth - 1); +} + +static Enumerator * +enumerator_new (GFile *root, + const char *path, + gboolean ignore_case) +{ + Enumerator *e; + gint i; + gchar *case_folded; + + e = g_new0 (Enumerator, 1); + e->path = g_strdup (path); + e->ignore_case = ignore_case; + + e->components = g_strsplit (e->path, G_DIR_SEPARATOR_S, -1); + e->depth = g_strv_length (e->components); + if (e->ignore_case) + { + e->case_components = g_new0 (char *, e->depth + 1); + for (i = 0; e->components[i]; i++) + { + case_folded = g_utf8_casefold (e->components[i], -1); + e->case_components[i] = g_utf8_collate_key (case_folded, -1); + g_free (case_folded); + } + } + + e->children = g_new0 (GFile *, e->depth); + e->children[0] = g_object_ref (root); + e->enumerators = g_new0 (GFileEnumerator *, e->depth); + e->enumerators[0] = g_file_enumerate_children (root, + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + + return e; +} + +static void +enumerator_free (Enumerator *e) +{ + gint i; + + for (i = 0; i < e->depth; i++) + { + if (e->enumerators[i]) + g_object_unref (e->enumerators[i]); + if (e->children[i]) + g_object_unref (e->children[i]); + } + + g_free (e->enumerators); + g_free (e->children); + g_strfreev (e->components); + if (e->case_components) + g_strfreev (e->case_components); + g_free (e->path); + g_free (e); +} + +static gboolean +matchlet_match (TreeMatchlet *matchlet, + GFile *root) +{ + GFile *file; + GFileInfo *info; + gboolean result; + const gchar *attrs; + Enumerator *e; + GList *l; + + e = enumerator_new (root, matchlet->path, !matchlet->match_case); + + do + { + file = enumerator_next (e); + if (!file) + { + enumerator_free (e); + return FALSE; + } + + if (matchlet->mimetype) + attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE "," + G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE "," + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE; + else + attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE "," + G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE; + info = g_file_query_info (file, + attrs, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + if (info) + { + result = TRUE; + + if (matchlet->type != G_FILE_TYPE_UNKNOWN && + g_file_info_get_file_type (info) != matchlet->type) + result = FALSE; + + if (matchlet->executable && + !g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) + result = FALSE; + } + else + result = FALSE; + + if (result && matchlet->non_empty) + { + GFileEnumerator *child_enum; + GFileInfo *child_info; + + child_enum = g_file_enumerate_children (file, + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + + if (child_enum) + { + child_info = g_file_enumerator_next_file (child_enum, NULL, NULL); + if (child_info) + g_object_unref (child_info); + else + result = FALSE; + g_object_unref (child_enum); + } + else + result = FALSE; + } + + if (result && matchlet->mimetype) + { + if (strcmp (matchlet->mimetype, g_file_info_get_content_type (info)) != 0) + result = FALSE; + } + + if (info) + g_object_unref (info); + g_object_unref (file); + } + while (!result); + + enumerator_free (e); + + if (!matchlet->matches) + return TRUE; + + for (l = matchlet->matches; l; l = l->next) + { + TreeMatchlet *submatchlet; + + submatchlet = l->data; + if (matchlet_match (submatchlet, root)) + return TRUE; + } + + return FALSE; +} + +static void +match_match (TreeMatch *match, + GFile *root, + GPtrArray *types) +{ + GList *l; + + for (l = match->matches; l; l = l->next) + { + TreeMatchlet *matchlet = l->data; + if (matchlet_match (matchlet, root)) + { + g_ptr_array_add (types, g_strdup (match->contenttype)); + break; + } + } +} + +gchar ** +g_content_type_guess_for_tree_impl (GFile *root) +{ + GPtrArray *types; + GList *l; + + types = g_ptr_array_new (); + + G_LOCK (gio_treemagic); + + tree_magic_init (); + for (l = tree_matches; l; l = l->next) + { + TreeMatch *match = l->data; + match_match (match, root, types); + } + + G_UNLOCK (gio_treemagic); + + g_ptr_array_add (types, NULL); + + return (gchar **)g_ptr_array_free (types, FALSE); +} diff --git a/gio/gosxcontenttype.m b/gio/gcontenttype-osx.m similarity index 92% rename from gio/gosxcontenttype.m rename to gio/gcontenttype-osx.m index 4d7a650c7..b498380cd 100644 --- a/gio/gosxcontenttype.m +++ b/gio/gcontenttype-osx.m @@ -20,6 +20,7 @@ #include "config.h" #include "gcontenttype.h" +#include "gcontenttypeprivate.h" #include "gicon.h" #include "gthemedicon.h" @@ -107,22 +108,22 @@ create_cstr_from_cfstring_with_fallback (CFStringRef str, /*< private >*/ void -g_content_type_set_mime_dirs (const gchar * const *dirs) +g_content_type_set_mime_dirs_impl (const gchar * const *dirs) { /* noop on macOS */ } /*< private >*/ const gchar * const * -g_content_type_get_mime_dirs (void) +g_content_type_get_mime_dirs_impl (void) { const gchar * const *mime_dirs = { NULL }; return mime_dirs; } gboolean -g_content_type_equals (const gchar *type1, - const gchar *type2) +g_content_type_equals_impl (const gchar *type1, + const gchar *type2) { CFStringRef str1, str2; gboolean ret; @@ -145,8 +146,8 @@ g_content_type_equals (const gchar *type1, } gboolean -g_content_type_is_a (const gchar *ctype, - const gchar *csupertype) +g_content_type_is_a_impl (const gchar *ctype, + const gchar *csupertype) { CFStringRef type, supertype; gboolean ret; @@ -166,8 +167,8 @@ g_content_type_is_a (const gchar *ctype, } gboolean -g_content_type_is_mime_type (const gchar *type, - const gchar *mime_type) +g_content_type_is_mime_type_impl (const gchar *type, + const gchar *mime_type) { gchar *content_type; gboolean ret; @@ -183,7 +184,7 @@ g_content_type_is_mime_type (const gchar *type, } gboolean -g_content_type_is_unknown (const gchar *type) +g_content_type_is_unknown_impl (const gchar *type) { g_return_val_if_fail (type != NULL, FALSE); @@ -198,7 +199,7 @@ g_content_type_is_unknown (const gchar *type) } gchar * -g_content_type_get_description (const gchar *type) +g_content_type_get_description_impl (const gchar *type) { CFStringRef str; CFStringRef desc_str; @@ -327,25 +328,25 @@ g_content_type_get_icon_internal (const gchar *uti, } GIcon * -g_content_type_get_icon (const gchar *type) +g_content_type_get_icon_impl (const gchar *type) { return g_content_type_get_icon_internal (type, FALSE); } GIcon * -g_content_type_get_symbolic_icon (const gchar *type) +g_content_type_get_symbolic_icon_impl (const gchar *type) { return g_content_type_get_icon_internal (type, TRUE); } gchar * -g_content_type_get_generic_icon_name (const gchar *type) +g_content_type_get_generic_icon_name_impl (const gchar *type) { return NULL; } gboolean -g_content_type_can_be_executable (const gchar *type) +g_content_type_can_be_executable_impl (const gchar *type) { CFStringRef uti; gboolean ret = FALSE; @@ -369,7 +370,7 @@ g_content_type_can_be_executable (const gchar *type) } gchar * -g_content_type_from_mime_type (const gchar *mime_type) +g_content_type_from_mime_type_impl (const gchar *mime_type) { CFStringRef mime_str; CFStringRef uti_str; @@ -437,7 +438,7 @@ g_content_type_from_mime_type (const gchar *mime_type) } gchar * -g_content_type_get_mime_type (const gchar *type) +g_content_type_get_mime_type_impl (const gchar *type) { CFStringRef uti_str; CFStringRef mime_str; @@ -489,10 +490,10 @@ looks_like_text (const guchar *data, } gchar * -g_content_type_guess (const gchar *filename, - const guchar *data, - gsize data_size, - gboolean *result_uncertain) +g_content_type_guess_impl (const gchar *filename, + const guchar *data, + gsize data_size, + gboolean *result_uncertain) { CFStringRef uti = NULL; gchar *cextension; @@ -595,14 +596,14 @@ g_content_type_guess (const gchar *filename, } GList * -g_content_types_get_registered (void) +g_content_types_get_registered_impl (void) { /* TODO: UTTypeCreateAllIdentifiersForTag? */ return NULL; } gchar ** -g_content_type_guess_for_tree (GFile *root) +g_content_type_guess_for_tree_impl (GFile *root) { return NULL; } diff --git a/gio/gcontenttype-win32.c b/gio/gcontenttype-win32.c index d227d599e..a81a86976 100644 --- a/gio/gcontenttype-win32.c +++ b/gio/gcontenttype-win32.c @@ -28,6 +28,7 @@ #include #include #include "gcontenttype.h" +#include "gcontenttypeprivate.h" #include "gthemedicon.h" #include "gicon.h" #include "glibintl.h" @@ -87,22 +88,22 @@ get_registry_classes_key (const char *subdir, /*< private >*/ void -g_content_type_set_mime_dirs (const gchar * const *dirs) +g_content_type_set_mime_dirs_impl (const gchar * const *dirs) { /* noop on Windows */ } /*< private >*/ const gchar * const * -g_content_type_get_mime_dirs (void) +g_content_type_get_mime_dirs_impl (void) { const gchar * const *mime_dirs = { NULL }; return mime_dirs; } gboolean -g_content_type_equals (const gchar *type1, - const gchar *type2) +g_content_type_equals_impl (const gchar *type1, + const gchar *type2) { char *progid1, *progid2; gboolean res; @@ -126,8 +127,8 @@ g_content_type_equals (const gchar *type1, } gboolean -g_content_type_is_a (const gchar *type, - const gchar *supertype) +g_content_type_is_a_impl (const gchar *type, + const gchar *supertype) { gboolean res; char *perceived_type; @@ -152,8 +153,8 @@ g_content_type_is_a (const gchar *type, } gboolean -g_content_type_is_mime_type (const gchar *type, - const gchar *mime_type) +g_content_type_is_mime_type_impl (const gchar *type, + const gchar *mime_type) { gchar *content_type; gboolean ret; @@ -169,7 +170,7 @@ g_content_type_is_mime_type (const gchar *type, } gboolean -g_content_type_is_unknown (const gchar *type) +g_content_type_is_unknown_impl (const gchar *type) { g_return_val_if_fail (type != NULL, FALSE); @@ -177,7 +178,7 @@ g_content_type_is_unknown (const gchar *type) } gchar * -g_content_type_get_description (const gchar *type) +g_content_type_get_description_impl (const gchar *type) { char *progid; char *description; @@ -201,7 +202,7 @@ g_content_type_get_description (const gchar *type) } gchar * -g_content_type_get_mime_type (const gchar *type) +g_content_type_get_mime_type_impl (const gchar *type) { char *mime; @@ -224,17 +225,8 @@ g_content_type_get_mime_type (const gchar *type) G_LOCK_DEFINE_STATIC (_type_icons); static GHashTable *_type_icons = NULL; -/** - * g_content_type_get_icon: - * @type: a content type string - * - * Gets the icon for a content type. - * - * Returns: (transfer full): #GIcon corresponding to the content type. Free the returned - * object with g_object_unref() - */ GIcon * -g_content_type_get_icon (const gchar *type) +g_content_type_get_icon_impl (const gchar *type) { GIcon *themed_icon; char *name = NULL; @@ -292,19 +284,19 @@ g_content_type_get_icon (const gchar *type) } GIcon * -g_content_type_get_symbolic_icon (const gchar *type) +g_content_type_get_symbolic_icon_impl (const gchar *type) { return g_content_type_get_icon (type); } gchar * -g_content_type_get_generic_icon_name (const gchar *type) +g_content_type_get_generic_icon_name_impl (const gchar *type) { return NULL; } gboolean -g_content_type_can_be_executable (const gchar *type) +g_content_type_can_be_executable_impl (const gchar *type) { g_return_val_if_fail (type != NULL, FALSE); @@ -343,7 +335,7 @@ looks_like_text (const guchar *data, } gchar * -g_content_type_from_mime_type (const gchar *mime_type) +g_content_type_from_mime_type_impl (const gchar *mime_type) { char *key, *content_type; @@ -362,10 +354,10 @@ g_content_type_from_mime_type (const gchar *mime_type) } gchar * -g_content_type_guess (const gchar *filename, - const guchar *data, - gsize data_size, - gboolean *result_uncertain) +g_content_type_guess_impl (const gchar *filename, + const guchar *data, + gsize data_size, + gboolean *result_uncertain) { char *basename; char *type; @@ -410,7 +402,7 @@ g_content_type_guess (const gchar *filename, } GList * -g_content_types_get_registered (void) +g_content_types_get_registered_impl (void) { DWORD index; wchar_t keyname[256]; @@ -446,7 +438,7 @@ g_content_types_get_registered (void) } gchar ** -g_content_type_guess_for_tree (GFile *root) +g_content_type_guess_for_tree_impl (GFile *root) { /* FIXME: implement */ return NULL; diff --git a/gio/gcontenttype.c b/gio/gcontenttype.c index de9e5a739..aa8f4040a 100644 --- a/gio/gcontenttype.c +++ b/gio/gcontenttype.c @@ -23,18 +23,8 @@ */ #include "config.h" -#include -#include -#include -#include #include "gcontenttypeprivate.h" -#include "gthemedicon.h" -#include "gicon.h" #include "gfile.h" -#include "gfileenumerator.h" -#include "gfileinfo.h" -#include "glibintl.h" -#include "glib-private.h" /** @@ -51,109 +41,6 @@ * such as `com.apple.application`. **/ -#include - -#define XDG_PREFIX _gio_xdg -#include "xdgmime/xdgmime.h" - -static void tree_magic_schedule_reload (void); - -/* We lock this mutex whenever we modify global state in this module. - * Taking and releasing this lock should always be associated with a pair of - * g_begin_ignore_leaks()/g_end_ignore_leaks() calls, as any call into xdgmime - * could trigger xdg_mime_init(), which makes a number of one-time allocations - * which GLib can never free as it doesn’t know when is suitable to call - * xdg_mime_shutdown(). */ -G_LOCK_DEFINE_STATIC (gio_xdgmime); - -gsize -_g_unix_content_type_get_sniff_len (void) -{ - gsize size; - - G_LOCK (gio_xdgmime); - g_begin_ignore_leaks (); - size = xdg_mime_get_max_buffer_extents (); - g_end_ignore_leaks (); - G_UNLOCK (gio_xdgmime); - - return size; -} - -gchar * -_g_unix_content_type_unalias (const gchar *type) -{ - gchar *res; - - G_LOCK (gio_xdgmime); - g_begin_ignore_leaks (); - res = g_strdup (xdg_mime_unalias_mime_type (type)); - g_end_ignore_leaks (); - G_UNLOCK (gio_xdgmime); - - return res; -} - -gchar ** -_g_unix_content_type_get_parents (const gchar *type) -{ - const gchar *umime; - gchar **parents; - GPtrArray *array; - int i; - - array = g_ptr_array_new (); - - G_LOCK (gio_xdgmime); - g_begin_ignore_leaks (); - - umime = xdg_mime_unalias_mime_type (type); - - g_ptr_array_add (array, g_strdup (umime)); - - parents = xdg_mime_list_mime_parents (umime); - for (i = 0; parents && parents[i] != NULL; i++) - g_ptr_array_add (array, g_strdup (parents[i])); - - free (parents); - - g_end_ignore_leaks (); - G_UNLOCK (gio_xdgmime); - - g_ptr_array_add (array, NULL); - - return (gchar **)g_ptr_array_free (array, FALSE); -} - -G_LOCK_DEFINE_STATIC (global_mime_dirs); -static gchar **global_mime_dirs = NULL; - -static void -_g_content_type_set_mime_dirs_locked (const char * const *dirs) -{ - g_clear_pointer (&global_mime_dirs, g_strfreev); - - if (dirs != NULL) - { - global_mime_dirs = g_strdupv ((gchar **) dirs); - } - else - { - GPtrArray *mime_dirs = g_ptr_array_new_with_free_func (g_free); - const gchar * const *system_dirs = g_get_system_data_dirs (); - - g_ptr_array_add (mime_dirs, g_build_filename (g_get_user_data_dir (), "mime", NULL)); - for (; *system_dirs != NULL; system_dirs++) - g_ptr_array_add (mime_dirs, g_build_filename (*system_dirs, "mime", NULL)); - g_ptr_array_add (mime_dirs, NULL); /* NULL terminator */ - - global_mime_dirs = (gchar **) g_ptr_array_free (mime_dirs, FALSE); - } - - xdg_mime_set_dirs ((const gchar * const *) global_mime_dirs); - tree_magic_schedule_reload (); -} - /** * g_content_type_set_mime_dirs: * @dirs: (array zero-terminated=1) (nullable): %NULL-terminated list of @@ -186,13 +73,10 @@ _g_content_type_set_mime_dirs_locked (const char * const *dirs) * * Since: 2.60 */ -/*< private >*/ void g_content_type_set_mime_dirs (const gchar * const *dirs) { - G_LOCK (global_mime_dirs); - _g_content_type_set_mime_dirs_locked (dirs); - G_UNLOCK (global_mime_dirs); + g_content_type_set_mime_dirs_impl (dirs); } /** @@ -206,23 +90,10 @@ g_content_type_set_mime_dirs (const gchar * const *dirs) * and with the first directory to try listed first * Since: 2.60 */ -/*< private >*/ const gchar * const * g_content_type_get_mime_dirs (void) { - const gchar * const *mime_dirs; - - G_LOCK (global_mime_dirs); - - if (global_mime_dirs == NULL) - _g_content_type_set_mime_dirs_locked (NULL); - - mime_dirs = (const gchar * const *) global_mime_dirs; - - G_UNLOCK (global_mime_dirs); - - g_assert (mime_dirs != NULL); - return mime_dirs; + return g_content_type_get_mime_dirs_impl (); } /** @@ -239,18 +110,10 @@ gboolean g_content_type_equals (const gchar *type1, const gchar *type2) { - gboolean res; - g_return_val_if_fail (type1 != NULL, FALSE); g_return_val_if_fail (type2 != NULL, FALSE); - G_LOCK (gio_xdgmime); - g_begin_ignore_leaks (); - res = xdg_mime_mime_type_equal (type1, type2); - g_end_ignore_leaks (); - G_UNLOCK (gio_xdgmime); - - return res; + return g_content_type_equals_impl (type1, type2); } /** @@ -267,18 +130,10 @@ gboolean g_content_type_is_a (const gchar *type, const gchar *supertype) { - gboolean res; - g_return_val_if_fail (type != NULL, FALSE); g_return_val_if_fail (supertype != NULL, FALSE); - G_LOCK (gio_xdgmime); - g_begin_ignore_leaks (); - res = xdg_mime_mime_type_subclass (type, supertype); - g_end_ignore_leaks (); - G_UNLOCK (gio_xdgmime); - - return res; + return g_content_type_is_a_impl (type, supertype); } /** @@ -298,7 +153,10 @@ gboolean g_content_type_is_mime_type (const gchar *type, const gchar *mime_type) { - return g_content_type_is_a (type, mime_type); + g_return_val_if_fail (type != NULL, FALSE); + g_return_val_if_fail (mime_type != NULL, FALSE); + + return g_content_type_is_mime_type_impl (type, mime_type); } /** @@ -317,157 +175,7 @@ g_content_type_is_unknown (const gchar *type) { g_return_val_if_fail (type != NULL, FALSE); - return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0; -} - - -typedef enum { - MIME_TAG_TYPE_OTHER, - MIME_TAG_TYPE_COMMENT -} MimeTagType; - -typedef struct { - int current_type; - int current_lang_level; - int comment_lang_level; - char *comment; -} MimeParser; - - -static int -language_level (const char *lang) -{ - const char * const *lang_list; - int i; - - /* The returned list is sorted from most desirable to least - desirable and always contains the default locale "C". */ - lang_list = g_get_language_names (); - - for (i = 0; lang_list[i]; i++) - if (strcmp (lang_list[i], lang) == 0) - return 1000-i; - - return 0; -} - -static void -mime_info_start_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, - GError **error) -{ - int i; - const char *lang; - MimeParser *parser = user_data; - - if (strcmp (element_name, "comment") == 0) - { - lang = "C"; - for (i = 0; attribute_names[i]; i++) - if (strcmp (attribute_names[i], "xml:lang") == 0) - { - lang = attribute_values[i]; - break; - } - - parser->current_lang_level = language_level (lang); - parser->current_type = MIME_TAG_TYPE_COMMENT; - } - else - parser->current_type = MIME_TAG_TYPE_OTHER; -} - -static void -mime_info_end_element (GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **error) -{ - MimeParser *parser = user_data; - - parser->current_type = MIME_TAG_TYPE_OTHER; -} - -static void -mime_info_text (GMarkupParseContext *context, - const gchar *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - MimeParser *parser = user_data; - - if (parser->current_type == MIME_TAG_TYPE_COMMENT && - parser->current_lang_level > parser->comment_lang_level) - { - g_free (parser->comment); - parser->comment = g_strndup (text, text_len); - parser->comment_lang_level = parser->current_lang_level; - } -} - -static char * -load_comment_for_mime_helper (const char *dir, - const char *basename) -{ - GMarkupParseContext *context; - char *filename, *data; - gsize len; - gboolean res; - MimeParser parse_data = {0}; - GMarkupParser parser = { - mime_info_start_element, - mime_info_end_element, - mime_info_text, - NULL, - NULL - }; - - filename = g_build_filename (dir, basename, NULL); - - res = g_file_get_contents (filename, &data, &len, NULL); - g_free (filename); - if (!res) - return NULL; - - context = g_markup_parse_context_new (&parser, G_MARKUP_DEFAULT_FLAGS, &parse_data, NULL); - res = g_markup_parse_context_parse (context, data, len, NULL); - g_free (data); - g_markup_parse_context_free (context); - - if (!res) - return NULL; - - return parse_data.comment; -} - - -static char * -load_comment_for_mime (const char *mimetype) -{ - const char * const *dirs; - char *basename; - char *comment; - gsize i; - - basename = g_strdup_printf ("%s.xml", mimetype); - - dirs = g_content_type_get_mime_dirs (); - for (i = 0; dirs[i] != NULL; i++) - { - comment = load_comment_for_mime_helper (dirs[i], basename); - if (comment) - { - g_free (basename); - return comment; - } - } - g_free (basename); - - return g_strdup_printf (_("%s type"), mimetype); + return g_content_type_is_unknown_impl (type); } /** @@ -482,41 +190,9 @@ load_comment_for_mime (const char *mimetype) gchar * g_content_type_get_description (const gchar *type) { - static GHashTable *type_comment_cache = NULL; - gchar *type_copy = NULL; - gchar *comment; - g_return_val_if_fail (type != NULL, NULL); - G_LOCK (gio_xdgmime); - g_begin_ignore_leaks (); - type = xdg_mime_unalias_mime_type (type); - g_end_ignore_leaks (); - - if (type_comment_cache == NULL) - type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - comment = g_hash_table_lookup (type_comment_cache, type); - comment = g_strdup (comment); - - if (comment != NULL) - { - G_UNLOCK (gio_xdgmime); - return g_steal_pointer (&comment); - } - - type_copy = g_strdup (type); - - G_UNLOCK (gio_xdgmime); - comment = load_comment_for_mime (type_copy); - G_LOCK (gio_xdgmime); - - g_hash_table_insert (type_comment_cache, - g_steal_pointer (&type_copy), - g_strdup (comment)); - G_UNLOCK (gio_xdgmime); - - return g_steal_pointer (&comment); + return g_content_type_get_description_impl (type); } /** @@ -533,60 +209,7 @@ g_content_type_get_mime_type (const char *type) { g_return_val_if_fail (type != NULL, NULL); - return g_strdup (type); -} - -static GIcon * -g_content_type_get_icon_internal (const gchar *type, - gboolean symbolic) -{ - char *mimetype_icon; - char *generic_mimetype_icon = NULL; - char *q; - char *icon_names[6]; - int n = 0; - GIcon *themed_icon; - const char *xdg_icon; - int i; - - g_return_val_if_fail (type != NULL, NULL); - - G_LOCK (gio_xdgmime); - g_begin_ignore_leaks (); - xdg_icon = xdg_mime_get_icon (type); - g_end_ignore_leaks (); - G_UNLOCK (gio_xdgmime); - - if (xdg_icon) - icon_names[n++] = g_strdup (xdg_icon); - - mimetype_icon = g_strdup (type); - while ((q = strchr (mimetype_icon, '/')) != NULL) - *q = '-'; - - icon_names[n++] = mimetype_icon; - - generic_mimetype_icon = g_content_type_get_generic_icon_name (type); - if (generic_mimetype_icon) - icon_names[n++] = generic_mimetype_icon; - - if (symbolic) - { - for (i = 0; i < n; i++) - { - icon_names[n + i] = icon_names[i]; - icon_names[i] = g_strconcat (icon_names[i], "-symbolic", NULL); - } - - n += n; - } - - themed_icon = g_themed_icon_new_from_names (icon_names, n); - - for (i = 0; i < n; i++) - g_free (icon_names[i]); - - return themed_icon; + return g_content_type_get_mime_type_impl (type); } /** @@ -601,7 +224,9 @@ g_content_type_get_icon_internal (const gchar *type, GIcon * g_content_type_get_icon (const gchar *type) { - return g_content_type_get_icon_internal (type, FALSE); + g_return_val_if_fail (type != NULL, NULL); + + return g_content_type_get_icon_impl (type); } /** @@ -618,7 +243,9 @@ g_content_type_get_icon (const gchar *type) GIcon * g_content_type_get_symbolic_icon (const gchar *type) { - return g_content_type_get_icon_internal (type, TRUE); + g_return_val_if_fail (type != NULL, NULL); + + return g_content_type_get_symbolic_icon_impl (type); } /** @@ -639,37 +266,9 @@ g_content_type_get_symbolic_icon (const gchar *type) gchar * g_content_type_get_generic_icon_name (const gchar *type) { - const gchar *xdg_icon_name; - gchar *icon_name; - g_return_val_if_fail (type != NULL, NULL); - G_LOCK (gio_xdgmime); - g_begin_ignore_leaks (); - xdg_icon_name = xdg_mime_get_generic_icon (type); - g_end_ignore_leaks (); - G_UNLOCK (gio_xdgmime); - - if (!xdg_icon_name) - { - const char *p; - const char *suffix = "-x-generic"; - - p = strchr (type, '/'); - if (p == NULL) - p = type + strlen (type); - - icon_name = g_malloc (p - type + strlen (suffix) + 1); - memcpy (icon_name, type, p - type); - memcpy (icon_name + (p - type), suffix, strlen (suffix)); - icon_name[(p - type) + strlen (suffix)] = 0; - } - else - { - icon_name = g_strdup (xdg_icon_name); - } - - return icon_name; + return g_content_type_get_generic_icon_name_impl (type); } /** @@ -687,29 +286,7 @@ g_content_type_can_be_executable (const gchar *type) { g_return_val_if_fail (type != NULL, FALSE); - if (g_content_type_is_a (type, "application/x-executable") || - g_content_type_is_a (type, "text/plain")) - return TRUE; - - return FALSE; -} - -static gboolean -looks_like_text (const guchar *data, gsize data_size) -{ - gsize i; - char c; - - for (i = 0; i < data_size; i++) - { - c = data[i]; - - if (g_ascii_iscntrl (c) && - !g_ascii_isspace (c) && - c != '\b') - return FALSE; - } - return TRUE; + return g_content_type_can_be_executable_impl (type); } /** @@ -726,18 +303,9 @@ looks_like_text (const guchar *data, gsize data_size) gchar * g_content_type_from_mime_type (const gchar *mime_type) { - char *umime; - g_return_val_if_fail (mime_type != NULL, NULL); - G_LOCK (gio_xdgmime); - g_begin_ignore_leaks (); - /* mime type and content type are same on unixes */ - umime = g_strdup (xdg_mime_unalias_mime_type (mime_type)); - g_end_ignore_leaks (); - G_UNLOCK (gio_xdgmime); - - return umime; + return g_content_type_from_mime_type_impl (mime_type); } /** @@ -762,175 +330,7 @@ g_content_type_guess (const gchar *filename, gsize data_size, gboolean *result_uncertain) { - char *basename; - const char *name_mimetypes[10], *sniffed_mimetype; - char *mimetype; - int i; - int n_name_mimetypes; - int sniffed_prio; - - sniffed_prio = 0; - n_name_mimetypes = 0; - sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN; - - if (result_uncertain) - *result_uncertain = FALSE; - - /* our test suite and potentially other code used -1 in the past, which is - * not documented and not allowed; guard against that */ - g_return_val_if_fail (data_size != (gsize) -1, g_strdup (XDG_MIME_TYPE_UNKNOWN)); - - G_LOCK (gio_xdgmime); - g_begin_ignore_leaks (); - - if (filename) - { - i = strlen (filename); - if (i > 0 && filename[i - 1] == '/') - { - name_mimetypes[0] = "inode/directory"; - name_mimetypes[1] = NULL; - n_name_mimetypes = 1; - if (result_uncertain) - *result_uncertain = TRUE; - } - else - { - basename = g_path_get_basename (filename); - n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10); - g_free (basename); - } - } - - /* Got an extension match, and no conflicts. This is it. */ - if (n_name_mimetypes == 1) - { - gchar *s = g_strdup (name_mimetypes[0]); - g_end_ignore_leaks (); - G_UNLOCK (gio_xdgmime); - return s; - } - - if (data) - { - sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio); - if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN && - data && - looks_like_text (data, data_size)) - sniffed_mimetype = "text/plain"; - - /* For security reasons we don't ever want to sniff desktop files - * where we know the filename and it doesn't have a .desktop extension. - * This is because desktop files allow executing any application and - * we don't want to make it possible to hide them looking like something - * else. - */ - if (filename != NULL && - strcmp (sniffed_mimetype, "application/x-desktop") == 0) - sniffed_mimetype = "text/plain"; - } - - if (n_name_mimetypes == 0) - { - if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN && - result_uncertain) - *result_uncertain = TRUE; - - mimetype = g_strdup (sniffed_mimetype); - } - else - { - mimetype = NULL; - if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN) - { - if (sniffed_prio >= 80) /* High priority sniffing match, use that */ - mimetype = g_strdup (sniffed_mimetype); - else - { - /* There are conflicts between the name matches and we - * have a sniffed type, use that as a tie breaker. - */ - for (i = 0; i < n_name_mimetypes; i++) - { - if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype)) - { - /* This nametype match is derived from (or the same as) - * the sniffed type). This is probably it. - */ - mimetype = g_strdup (name_mimetypes[i]); - break; - } - } - } - } - - if (mimetype == NULL) - { - /* Conflicts, and sniffed type was no help or not there. - * Guess on the first one - */ - mimetype = g_strdup (name_mimetypes[0]); - if (result_uncertain) - *result_uncertain = TRUE; - } - } - - g_end_ignore_leaks (); - G_UNLOCK (gio_xdgmime); - - return mimetype; -} - -static void -enumerate_mimetypes_subdir (const char *dir, - const char *prefix, - GHashTable *mimetypes) -{ - DIR *d; - struct dirent *ent; - char *mimetype; - - d = opendir (dir); - if (d) - { - while ((ent = readdir (d)) != NULL) - { - if (g_str_has_suffix (ent->d_name, ".xml")) - { - mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name); - g_hash_table_replace (mimetypes, mimetype, NULL); - } - } - closedir (d); - } -} - -static void -enumerate_mimetypes_dir (const char *dir, - GHashTable *mimetypes) -{ - DIR *d; - struct dirent *ent; - const char *mimedir; - char *name; - - mimedir = dir; - - d = opendir (mimedir); - if (d) - { - while ((ent = readdir (d)) != NULL) - { - if (strcmp (ent->d_name, "packages") != 0) - { - name = g_build_filename (mimedir, ent->d_name, NULL); - if (g_file_test (name, G_FILE_TEST_IS_DIR)) - enumerate_mimetypes_subdir (name, ent->d_name, mimetypes); - g_free (name); - } - } - closedir (d); - } + return g_content_type_guess_impl (filename, data, data_size, result_uncertain); } /** @@ -946,591 +346,7 @@ enumerate_mimetypes_dir (const char *dir, GList * g_content_types_get_registered (void) { - const char * const *dirs; - GHashTable *mimetypes; - GHashTableIter iter; - gpointer key; - gsize i; - GList *l; - - mimetypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - dirs = g_content_type_get_mime_dirs (); - for (i = 0; dirs[i] != NULL; i++) - enumerate_mimetypes_dir (dirs[i], mimetypes); - - l = NULL; - g_hash_table_iter_init (&iter, mimetypes); - while (g_hash_table_iter_next (&iter, &key, NULL)) - { - l = g_list_prepend (l, key); - g_hash_table_iter_steal (&iter); - } - - g_hash_table_destroy (mimetypes); - - return l; -} - - -/* tree magic data */ -static GList *tree_matches = NULL; -static gboolean need_reload = FALSE; - -G_LOCK_DEFINE_STATIC (gio_treemagic); - -typedef struct -{ - gchar *path; - GFileType type; - guint match_case : 1; - guint executable : 1; - guint non_empty : 1; - guint on_disc : 1; - gchar *mimetype; - GList *matches; -} TreeMatchlet; - -typedef struct -{ - gchar *contenttype; - gint priority; - GList *matches; -} TreeMatch; - - -static void -tree_matchlet_free (TreeMatchlet *matchlet) -{ - g_list_free_full (matchlet->matches, (GDestroyNotify) tree_matchlet_free); - g_free (matchlet->path); - g_free (matchlet->mimetype); - g_slice_free (TreeMatchlet, matchlet); -} - -static void -tree_match_free (TreeMatch *match) -{ - g_list_free_full (match->matches, (GDestroyNotify) tree_matchlet_free); - g_free (match->contenttype); - g_slice_free (TreeMatch, match); -} - -static TreeMatch * -parse_header (gchar *line) -{ - gint len; - gchar *s; - TreeMatch *match; - - len = strlen (line); - - if (line[0] != '[' || line[len - 1] != ']') - return NULL; - - line[len - 1] = 0; - s = strchr (line, ':'); - if (s == NULL) - return NULL; - - match = g_slice_new0 (TreeMatch); - match->priority = atoi (line + 1); - match->contenttype = g_strdup (s + 1); - - return match; -} - -static TreeMatchlet * -parse_match_line (gchar *line, - gint *depth) -{ - gchar *s, *p; - TreeMatchlet *matchlet; - gchar **parts; - gint i; - - matchlet = g_slice_new0 (TreeMatchlet); - - if (line[0] == '>') - { - *depth = 0; - s = line; - } - else - { - *depth = atoi (line); - s = strchr (line, '>'); - if (s == NULL) - goto handle_error; - } - s += 2; - p = strchr (s, '"'); - if (p == NULL) - goto handle_error; - *p = 0; - - matchlet->path = g_strdup (s); - s = p + 1; - parts = g_strsplit (s, ",", 0); - if (strcmp (parts[0], "=file") == 0) - matchlet->type = G_FILE_TYPE_REGULAR; - else if (strcmp (parts[0], "=directory") == 0) - matchlet->type = G_FILE_TYPE_DIRECTORY; - else if (strcmp (parts[0], "=link") == 0) - matchlet->type = G_FILE_TYPE_SYMBOLIC_LINK; - else - matchlet->type = G_FILE_TYPE_UNKNOWN; - for (i = 1; parts[i]; i++) - { - if (strcmp (parts[i], "executable") == 0) - matchlet->executable = 1; - else if (strcmp (parts[i], "match-case") == 0) - matchlet->match_case = 1; - else if (strcmp (parts[i], "non-empty") == 0) - matchlet->non_empty = 1; - else if (strcmp (parts[i], "on-disc") == 0) - matchlet->on_disc = 1; - else - matchlet->mimetype = g_strdup (parts[i]); - } - - g_strfreev (parts); - - return matchlet; - -handle_error: - g_slice_free (TreeMatchlet, matchlet); - return NULL; -} - -static gint -cmp_match (gconstpointer a, gconstpointer b) -{ - const TreeMatch *aa = (const TreeMatch *)a; - const TreeMatch *bb = (const TreeMatch *)b; - - return bb->priority - aa->priority; -} - -static void -insert_match (TreeMatch *match) -{ - tree_matches = g_list_insert_sorted (tree_matches, match, cmp_match); -} - -static void -insert_matchlet (TreeMatch *match, - TreeMatchlet *matchlet, - gint depth) -{ - if (depth == 0) - match->matches = g_list_append (match->matches, matchlet); - else - { - GList *last; - TreeMatchlet *m; - - last = g_list_last (match->matches); - if (!last) - { - tree_matchlet_free (matchlet); - g_warning ("can't insert tree matchlet at depth %d", depth); - return; - } - - m = (TreeMatchlet *) last->data; - while (--depth > 0) - { - last = g_list_last (m->matches); - if (!last) - { - tree_matchlet_free (matchlet); - g_warning ("can't insert tree matchlet at depth %d", depth); - return; - } - - m = (TreeMatchlet *) last->data; - } - m->matches = g_list_append (m->matches, matchlet); - } -} - -static void -read_tree_magic_from_directory (const gchar *prefix) -{ - gchar *filename; - gchar *text; - gsize len; - gchar **lines; - gsize i; - TreeMatch *match; - TreeMatchlet *matchlet; - gint depth; - - filename = g_build_filename (prefix, "treemagic", NULL); - - if (g_file_get_contents (filename, &text, &len, NULL)) - { - if (strcmp (text, "MIME-TreeMagic") == 0) - { - lines = g_strsplit (text + strlen ("MIME-TreeMagic") + 2, "\n", 0); - match = NULL; - for (i = 0; lines[i] && lines[i][0]; i++) - { - if (lines[i][0] == '[' && (match = parse_header (lines[i])) != NULL) - { - insert_match (match); - } - else if (match != NULL) - { - matchlet = parse_match_line (lines[i], &depth); - if (matchlet == NULL) - { - g_warning ("%s: body corrupt; skipping", filename); - break; - } - insert_matchlet (match, matchlet, depth); - } - else - { - g_warning ("%s: header corrupt; skipping", filename); - break; - } - } - - g_strfreev (lines); - } - else - g_warning ("%s: header not found, skipping", filename); - - g_free (text); - } - - g_free (filename); -} - -static void -tree_magic_schedule_reload (void) -{ - need_reload = TRUE; -} - -static void -xdg_mime_reload (void *user_data) -{ - tree_magic_schedule_reload (); -} - -static void -tree_magic_shutdown (void) -{ - g_list_free_full (tree_matches, (GDestroyNotify) tree_match_free); - tree_matches = NULL; -} - -static void -tree_magic_init (void) -{ - static gboolean initialized = FALSE; - gsize i; - - if (!initialized) - { - initialized = TRUE; - - xdg_mime_register_reload_callback (xdg_mime_reload, NULL, NULL); - need_reload = TRUE; - } - - if (need_reload) - { - const char * const *dirs; - - need_reload = FALSE; - - tree_magic_shutdown (); - - dirs = g_content_type_get_mime_dirs (); - for (i = 0; dirs[i] != NULL; i++) - read_tree_magic_from_directory (dirs[i]); - } -} - -/* a filtering enumerator */ - -typedef struct -{ - gchar *path; - gint depth; - gboolean ignore_case; - gchar **components; - gchar **case_components; - GFileEnumerator **enumerators; - GFile **children; -} Enumerator; - -static gboolean -component_match (Enumerator *e, - gint depth, - const gchar *name) -{ - gchar *case_folded, *key, *utf8_name; - gboolean found; - - if (strcmp (name, e->components[depth]) == 0) - return TRUE; - - if (!e->ignore_case) - return FALSE; - - utf8_name = g_filename_to_utf8 (name, -1, NULL, NULL, NULL); - if (utf8_name == NULL) - utf8_name = g_utf8_make_valid (name, -1); - - case_folded = g_utf8_casefold (utf8_name, -1); - key = g_utf8_collate_key (case_folded, -1); - - found = strcmp (key, e->case_components[depth]) == 0; - - g_free (utf8_name); - g_free (case_folded); - g_free (key); - - return found; -} - -static GFile * -next_match_recurse (Enumerator *e, - gint depth) -{ - GFile *file; - GFileInfo *info; - const gchar *name; - - while (TRUE) - { - if (e->enumerators[depth] == NULL) - { - if (depth > 0) - { - file = next_match_recurse (e, depth - 1); - if (file) - { - e->children[depth] = file; - e->enumerators[depth] = g_file_enumerate_children (file, - G_FILE_ATTRIBUTE_STANDARD_NAME, - G_FILE_QUERY_INFO_NONE, - NULL, - NULL); - } - } - if (e->enumerators[depth] == NULL) - return NULL; - } - - while ((info = g_file_enumerator_next_file (e->enumerators[depth], NULL, NULL))) - { - name = g_file_info_get_name (info); - if (component_match (e, depth, name)) - { - file = g_file_get_child (e->children[depth], name); - g_object_unref (info); - return file; - } - g_object_unref (info); - } - - g_object_unref (e->enumerators[depth]); - e->enumerators[depth] = NULL; - g_object_unref (e->children[depth]); - e->children[depth] = NULL; - } -} - -static GFile * -enumerator_next (Enumerator *e) -{ - return next_match_recurse (e, e->depth - 1); -} - -static Enumerator * -enumerator_new (GFile *root, - const char *path, - gboolean ignore_case) -{ - Enumerator *e; - gint i; - gchar *case_folded; - - e = g_new0 (Enumerator, 1); - e->path = g_strdup (path); - e->ignore_case = ignore_case; - - e->components = g_strsplit (e->path, G_DIR_SEPARATOR_S, -1); - e->depth = g_strv_length (e->components); - if (e->ignore_case) - { - e->case_components = g_new0 (char *, e->depth + 1); - for (i = 0; e->components[i]; i++) - { - case_folded = g_utf8_casefold (e->components[i], -1); - e->case_components[i] = g_utf8_collate_key (case_folded, -1); - g_free (case_folded); - } - } - - e->children = g_new0 (GFile *, e->depth); - e->children[0] = g_object_ref (root); - e->enumerators = g_new0 (GFileEnumerator *, e->depth); - e->enumerators[0] = g_file_enumerate_children (root, - G_FILE_ATTRIBUTE_STANDARD_NAME, - G_FILE_QUERY_INFO_NONE, - NULL, - NULL); - - return e; -} - -static void -enumerator_free (Enumerator *e) -{ - gint i; - - for (i = 0; i < e->depth; i++) - { - if (e->enumerators[i]) - g_object_unref (e->enumerators[i]); - if (e->children[i]) - g_object_unref (e->children[i]); - } - - g_free (e->enumerators); - g_free (e->children); - g_strfreev (e->components); - if (e->case_components) - g_strfreev (e->case_components); - g_free (e->path); - g_free (e); -} - -static gboolean -matchlet_match (TreeMatchlet *matchlet, - GFile *root) -{ - GFile *file; - GFileInfo *info; - gboolean result; - const gchar *attrs; - Enumerator *e; - GList *l; - - e = enumerator_new (root, matchlet->path, !matchlet->match_case); - - do - { - file = enumerator_next (e); - if (!file) - { - enumerator_free (e); - return FALSE; - } - - if (matchlet->mimetype) - attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE "," - G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE "," - G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE; - else - attrs = G_FILE_ATTRIBUTE_STANDARD_TYPE "," - G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE; - info = g_file_query_info (file, - attrs, - G_FILE_QUERY_INFO_NONE, - NULL, - NULL); - if (info) - { - result = TRUE; - - if (matchlet->type != G_FILE_TYPE_UNKNOWN && - g_file_info_get_file_type (info) != matchlet->type) - result = FALSE; - - if (matchlet->executable && - !g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) - result = FALSE; - } - else - result = FALSE; - - if (result && matchlet->non_empty) - { - GFileEnumerator *child_enum; - GFileInfo *child_info; - - child_enum = g_file_enumerate_children (file, - G_FILE_ATTRIBUTE_STANDARD_NAME, - G_FILE_QUERY_INFO_NONE, - NULL, - NULL); - - if (child_enum) - { - child_info = g_file_enumerator_next_file (child_enum, NULL, NULL); - if (child_info) - g_object_unref (child_info); - else - result = FALSE; - g_object_unref (child_enum); - } - else - result = FALSE; - } - - if (result && matchlet->mimetype) - { - if (strcmp (matchlet->mimetype, g_file_info_get_content_type (info)) != 0) - result = FALSE; - } - - if (info) - g_object_unref (info); - g_object_unref (file); - } - while (!result); - - enumerator_free (e); - - if (!matchlet->matches) - return TRUE; - - for (l = matchlet->matches; l; l = l->next) - { - TreeMatchlet *submatchlet; - - submatchlet = l->data; - if (matchlet_match (submatchlet, root)) - return TRUE; - } - - return FALSE; -} - -static void -match_match (TreeMatch *match, - GFile *root, - GPtrArray *types) -{ - GList *l; - - for (l = match->matches; l; l = l->next) - { - TreeMatchlet *matchlet = l->data; - if (matchlet_match (matchlet, root)) - { - g_ptr_array_add (types, g_strdup (match->contenttype)); - break; - } - } + return g_content_types_get_registered_impl (); } /** @@ -1558,23 +374,7 @@ match_match (TreeMatch *match, gchar ** g_content_type_guess_for_tree (GFile *root) { - GPtrArray *types; - GList *l; + g_return_val_if_fail (G_IS_FILE (root), NULL); - types = g_ptr_array_new (); - - G_LOCK (gio_treemagic); - - tree_magic_init (); - for (l = tree_matches; l; l = l->next) - { - TreeMatch *match = l->data; - match_match (match, root, types); - } - - G_UNLOCK (gio_treemagic); - - g_ptr_array_add (types, NULL); - - return (gchar **)g_ptr_array_free (types, FALSE); + return g_content_type_guess_for_tree_impl (root); } diff --git a/gio/gcontenttypeprivate.h b/gio/gcontenttypeprivate.h index d3d671b91..a642b63b8 100644 --- a/gio/gcontenttypeprivate.h +++ b/gio/gcontenttypeprivate.h @@ -31,6 +31,29 @@ gsize _g_unix_content_type_get_sniff_len (void); char * _g_unix_content_type_unalias (const char *type); char **_g_unix_content_type_get_parents (const char *type); +void g_content_type_set_mime_dirs_impl (const gchar * const *dirs); +const gchar * const *g_content_type_get_mime_dirs_impl (void); +gboolean g_content_type_equals_impl (const gchar *type1, + const gchar *type2); +gboolean g_content_type_is_a_impl (const gchar *type, + const gchar *supertype); +gboolean g_content_type_is_mime_type_impl (const gchar *type, + const gchar *mime_type); +gboolean g_content_type_is_unknown_impl (const gchar *type); +gchar *g_content_type_get_description_impl (const gchar *type); +char *g_content_type_get_mime_type_impl (const char *type); +GIcon *g_content_type_get_icon_impl (const gchar *type); +GIcon *g_content_type_get_symbolic_icon_impl (const gchar *type); +gchar *g_content_type_get_generic_icon_name_impl (const gchar *type); +gboolean g_content_type_can_be_executable_impl (const gchar *type); +gchar *g_content_type_from_mime_type_impl (const gchar *mime_type); +gchar *g_content_type_guess_impl (const gchar *filename, + const guchar *data, + gsize data_size, + gboolean *result_uncertain); +GList *g_content_types_get_registered_impl (void); +gchar **g_content_type_guess_for_tree_impl (GFile *root); + G_END_DECLS #endif /* __G_CONTENT_TYPE_PRIVATE_H__ */ diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index 640057a06..343f30949 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -1777,7 +1777,7 @@ g_desktop_app_info_class_init (GDesktopAppInfoClass *klass) /** * GDesktopAppInfo:filename: * - * The origin filename of this #GDesktopAppInfo + * The origin filename of this [class@Gio.DesktopAppInfo] */ g_object_class_install_property (gobject_class, PROP_FILENAME, @@ -2064,11 +2064,11 @@ g_desktop_app_info_load_file (GDesktopAppInfo *self) /** * g_desktop_app_info_new_from_keyfile: - * @key_file: an opened #GKeyFile + * @key_file: an opened [type@GLib.KeyFile] * - * Creates a new #GDesktopAppInfo. + * Creates a new [class@Gio.DesktopAppInfo]. * - * Returns: (nullable): a new #GDesktopAppInfo or %NULL on error. + * Returns: (nullable): a new [class@Gio.DesktopAppInfo] or `NULL` on error. * * Since: 2.18 **/ @@ -2095,9 +2095,9 @@ g_desktop_app_info_new_from_keyfile (GKeyFile *key_file) * @filename: (type filename): the path of a desktop file, in the GLib * filename encoding * - * Creates a new #GDesktopAppInfo. + * Creates a new [class@Gio.DesktopAppInfo]. * - * Returns: (nullable): a new #GDesktopAppInfo or %NULL on error. + * Returns: (nullable): a new [class@Gio.DesktopAppInfo] or `NULL` on error. **/ GDesktopAppInfo * g_desktop_app_info_new_from_filename (const char *filename) @@ -2115,22 +2115,22 @@ g_desktop_app_info_new_from_filename (const char *filename) /** * g_desktop_app_info_new: - * @desktop_id: the desktop file id + * @desktop_id: the desktop file ID * - * Creates a new #GDesktopAppInfo based on a desktop file id. + * Creates a new [class@Gio.DesktopAppInfo] based on a desktop file ID. * - * A desktop file id is the basename of the desktop file, including the - * .desktop extension. GIO is looking for a desktop file with this name + * A desktop file ID is the basename of the desktop file, including the + * `.desktop` extension. GIO is looking for a desktop file with this name * in the `applications` subdirectories of the XDG * data directories (i.e. the directories specified in the `XDG_DATA_HOME` * and `XDG_DATA_DIRS` environment variables). GIO also supports the * prefix-to-subdirectory mapping that is described in the * [Menu Spec](http://standards.freedesktop.org/menu-spec/latest/) - * (i.e. a desktop id of kde-foo.desktop will match + * (i.e. a desktop ID of `kde-foo.desktop` will match * `/usr/share/applications/kde/foo.desktop`). * - * Returns: (nullable): a new #GDesktopAppInfo, or %NULL if no desktop - * file with that id exists. + * Returns: (nullable): a new [class@Gio.DesktopAppInfo], or `NULL` if no + * desktop file with that ID exists. */ GDesktopAppInfo * g_desktop_app_info_new (const char *desktop_id) @@ -2248,12 +2248,13 @@ g_desktop_app_info_get_display_name (GAppInfo *appinfo) /** * g_desktop_app_info_get_is_hidden: - * @info: a #GDesktopAppInfo. + * @info: a [class@Gio.DesktopAppInfo]. * - * A desktop file is hidden if the Hidden key in it is - * set to True. + * A desktop file is hidden if the + * [`Hidden` key](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-hidden) + * in it is set to `True`. * - * Returns: %TRUE if hidden, %FALSE otherwise. + * Returns: `TRUE` if hidden, `FALSE` otherwise. **/ gboolean g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info) @@ -2263,14 +2264,14 @@ g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info) /** * g_desktop_app_info_get_filename: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * * When @info was created from a known filename, return it. In some - * situations such as the #GDesktopAppInfo returned from - * g_desktop_app_info_new_from_keyfile(), this function will return %NULL. + * situations such as a [class@Gio.DesktopAppInfo] returned from + * [ctor@Gio.DesktopAppInfo.new_from_keyfile], this function will return `NULL`. * * Returns: (nullable) (type filename): The full path to the file for @info, - * or %NULL if not known. + * or `NULL` if not known. * Since: 2.24 */ const char * @@ -2313,12 +2314,14 @@ g_desktop_app_info_get_icon (GAppInfo *appinfo) /** * g_desktop_app_info_get_categories: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * * Gets the categories from the desktop file. * - * Returns: (nullable): The unparsed Categories key from the desktop file; - * i.e. no attempt is made to split it by ';' or validate it. + * Returns: (nullable): The unparsed + * [`Categories` key](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-categories) + * from the desktop file; + * i.e. no attempt is made to split it by `;` or validate it. */ const char * g_desktop_app_info_get_categories (GDesktopAppInfo *info) @@ -2328,11 +2331,12 @@ g_desktop_app_info_get_categories (GDesktopAppInfo *info) /** * g_desktop_app_info_get_keywords: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * * Gets the keywords from the desktop file. * - * Returns: (transfer none): The value of the Keywords key + * Returns: (transfer none): The value of the + * [`Keywords` key](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-keywords) * * Since: 2.32 */ @@ -2344,11 +2348,12 @@ g_desktop_app_info_get_keywords (GDesktopAppInfo *info) /** * g_desktop_app_info_get_generic_name: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * * Gets the generic name from the desktop file. * - * Returns: (nullable): The value of the GenericName key + * Returns: (nullable): The value of the + * [`GenericName` key](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-genericname) */ const char * g_desktop_app_info_get_generic_name (GDesktopAppInfo *info) @@ -2358,13 +2363,14 @@ g_desktop_app_info_get_generic_name (GDesktopAppInfo *info) /** * g_desktop_app_info_get_nodisplay: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * - * Gets the value of the NoDisplay key, which helps determine if the - * application info should be shown in menus. See - * %G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY and g_app_info_should_show(). + * Gets the value of the + * [`NoDisplay` key](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-nodisplay) + * which helps determine if the application info should be shown in menus. See + * `G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY` and [method@Gio.AppInfo.should_show]. * - * Returns: The value of the NoDisplay key + * Returns: The value of the `NoDisplay` key * * Since: 2.30 */ @@ -2376,24 +2382,25 @@ g_desktop_app_info_get_nodisplay (GDesktopAppInfo *info) /** * g_desktop_app_info_get_show_in: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * @desktop_env: (nullable): a string specifying a desktop name * * Checks if the application info should be shown in menus that list available * applications for a specific name of the desktop, based on the - * `OnlyShowIn` and `NotShowIn` keys. + * [`OnlyShowIn`](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-onlyshowin) + * and [`NotShowIn`](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-notshowin) + * keys. * - * @desktop_env should typically be given as %NULL, in which case the + * @desktop_env should typically be given as `NULL`, in which case the * `XDG_CURRENT_DESKTOP` environment variable is consulted. If you want * to override the default mechanism then you may specify @desktop_env, * but this is not recommended. * - * Note that g_app_info_should_show() for @info will include this check (with - * %NULL for @desktop_env) as well as additional checks. + * Note that [method@Gio.AppInfo.should_show] for @info will include this check + * (with `NULL` for @desktop_env) as well as additional checks. * - * Returns: %TRUE if the @info should be shown in @desktop_env according to the - * `OnlyShowIn` and `NotShowIn` keys, %FALSE - * otherwise. + * Returns: `TRUE` if the @info should be shown in @desktop_env according to the + * `OnlyShowIn` and `NotShowIn` keys, `FALSE` otherwise. * * Since: 2.30 */ @@ -3544,28 +3551,28 @@ g_desktop_app_info_launch (GAppInfo *appinfo, /** * g_desktop_app_info_launch_uris_as_manager_with_fds: - * @appinfo: a #GDesktopAppInfo + * @appinfo: a [class@Gio.DesktopAppInfo] * @uris: (element-type utf8): List of URIs - * @launch_context: (nullable): a #GAppLaunchContext - * @spawn_flags: #GSpawnFlags, used for each process - * @user_setup: (scope async) (nullable) (closure user_setup_data): a #GSpawnChildSetupFunc, used once - * for each process. + * @launch_context: (nullable): a [class@Gio.AppLaunchContext] + * @spawn_flags: [flags@GLib.SpawnFlags], used for each process + * @user_setup: (scope async) (nullable) (closure user_setup_data): a + * [callback@GLib.SpawnChildSetupFunc], used once for each process. * @user_setup_data: User data for @user_setup * @pid_callback: (scope call) (nullable) (closure pid_callback_data): Callback for child processes * @pid_callback_data: User data for @callback - * @stdin_fd: file descriptor to use for child's stdin, or -1 - * @stdout_fd: file descriptor to use for child's stdout, or -1 - * @stderr_fd: file descriptor to use for child's stderr, or -1 - * @error: return location for a #GError, or %NULL + * @stdin_fd: file descriptor to use for child’s stdin, or `-1` + * @stdout_fd: file descriptor to use for child’s stdout, or `-1` + * @stderr_fd: file descriptor to use for child’s stderr, or `-1` + * @error: return location for a #GError, or `NULL` * - * Equivalent to g_desktop_app_info_launch_uris_as_manager() but allows + * Equivalent to [method@Gio.DesktopAppInfo.launch_uris_as_manager] but allows * you to pass in file descriptors for the stdin, stdout and stderr streams * of the launched process. * * If application launching occurs via some non-spawn mechanism (e.g. D-Bus * activation) then @stdin_fd, @stdout_fd and @stderr_fd are ignored. * - * Returns: %TRUE on successful launch, %FALSE otherwise. + * Returns: `TRUE` on successful launch, `FALSE` otherwise. * * Since: 2.58 */ @@ -3599,34 +3606,35 @@ g_desktop_app_info_launch_uris_as_manager_with_fds (GDesktopAppInfo * /** * g_desktop_app_info_launch_uris_as_manager: - * @appinfo: a #GDesktopAppInfo + * @appinfo: a [class@Gio.DesktopAppInfo] * @uris: (element-type utf8): List of URIs - * @launch_context: (nullable): a #GAppLaunchContext - * @spawn_flags: #GSpawnFlags, used for each process - * @user_setup: (scope async) (nullable): a #GSpawnChildSetupFunc, used once - * for each process. + * @launch_context: (nullable): a [class@Gio.AppLaunchContext] + * @spawn_flags: [flags@GLib.SpawnFlags], used for each process + * @user_setup: (scope async) (nullable): a [callback@GLib.SpawnChildSetupFunc], + * used once for each process. * @user_setup_data: (closure user_setup) (nullable): User data for @user_setup * @pid_callback: (scope call) (nullable): Callback for child processes * @pid_callback_data: (closure pid_callback) (nullable): User data for @callback - * @error: return location for a #GError, or %NULL + * @error: return location for a #GError, or `NULL` * - * This function performs the equivalent of g_app_info_launch_uris(), + * This function performs the equivalent of [method@Gio.AppInfo.launch_uris], * but is intended primarily for operating system components that * launch applications. Ordinary applications should use - * g_app_info_launch_uris(). + * [method@Gio.AppInfo.launch_uris]. * * If the application is launched via GSpawn, then @spawn_flags, @user_setup - * and @user_setup_data are used for the call to g_spawn_async(). + * and @user_setup_data are used for the call to [func@GLib.spawn_async]. * Additionally, @pid_callback (with @pid_callback_data) will be called to - * inform about the PID of the created process. See g_spawn_async_with_pipes() - * for information on certain parameter conditions that can enable an - * optimized posix_spawn() codepath to be used. + * inform about the PID of the created process. See + * [func@GLib.spawn_async_with_pipes] for information on certain parameter + * conditions that can enable an optimized [`posix_spawn()`](man:posix_spawn(3)) + * code path to be used. * - * If application launching occurs via some other mechanism (eg: D-Bus + * If application launching occurs via some other mechanism (for example, D-Bus * activation) then @spawn_flags, @user_setup, @user_setup_data, * @pid_callback and @pid_callback_data are ignored. * - * Returns: %TRUE on successful launch, %FALSE otherwise. + * Returns: `TRUE` on successful launch, `FALSE` otherwise. */ gboolean g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo *appinfo, @@ -3658,15 +3666,17 @@ g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo *appinfo, * @desktop_env: a string specifying what desktop this is * * Sets the name of the desktop that the application is running in. - * This is used by g_app_info_should_show() and - * g_desktop_app_info_get_show_in() to evaluate the - * `OnlyShowIn` and `NotShowIn` - * desktop entry fields. + * + * This is used by [method@Gio.AppInfo.should_show] and + * [method@Gio.DesktopAppInfo.get_show_in] to evaluate the + * [`OnlyShowIn`](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-onlyshowin) + * and [`NotShowIn`](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s06.html#key-notshowin) + * keys. * * Should be called only once; subsequent calls are ignored. * * Deprecated:2.42:do not use this API. Since 2.42 the value of the - * `XDG_CURRENT_DESKTOP` environment variable will be used. + * `XDG_CURRENT_DESKTOP` environment variable will be used. */ void g_desktop_app_info_set_desktop_env (const gchar *desktop_env) @@ -4314,28 +4324,12 @@ g_desktop_app_info_delete (GAppInfo *appinfo) } /* Create for commandline {{{2 */ -/** - * g_app_info_create_from_commandline: - * @commandline: (type filename): the commandline to use - * @application_name: (nullable): the application name, or %NULL to use @commandline - * @flags: flags that can specify details of the created #GAppInfo - * @error: a #GError location to store the error occurring, %NULL to ignore. - * - * Creates a new #GAppInfo from the given information. - * - * Note that for @commandline, the quoting rules of the Exec key of the - * [freedesktop.org Desktop Entry Specification](http://freedesktop.org/Standards/desktop-entry-spec) - * are applied. For example, if the @commandline contains - * percent-encoded URIs, the percent-character must be doubled in order to prevent it from - * being swallowed by Exec key unquoting. See the specification for exact quoting rules. - * - * Returns: (transfer full): new #GAppInfo for given command. - **/ + GAppInfo * -g_app_info_create_from_commandline (const char *commandline, - const char *application_name, - GAppInfoCreateFlags flags, - GError **error) +g_app_info_create_from_commandline_impl (const char *commandline, + const char *application_name, + GAppInfoCreateFlags flags, + GError **error) { char **split; char *basename; @@ -4484,25 +4478,8 @@ g_desktop_app_info_get_desktop_ids_for_content_type (const gchar *content_type, return (gchar **) g_ptr_array_free (hits, FALSE); } -/** - * g_app_info_get_recommended_for_type: - * @content_type: the content type to find a [iface@Gio.AppInfo] for - * - * Gets a list of recommended [iface@Gio.AppInfo]s for a given content type, - * i.e. those applications which claim to support the given content type - * exactly, and not by MIME type subclassing. - * - * Note that the first application of the list is the last used one, i.e. - * the last one for which [method@Gio.AppInfo.set_as_last_used_for_type] has - * been called. - * - * Returns: (element-type GAppInfo) (transfer full): list of - * [iface@Gio.AppInfo]s for given @content_type or `NULL` on error. - * - * Since: 2.28 - **/ GList * -g_app_info_get_recommended_for_type (const gchar *content_type) +g_app_info_get_recommended_for_type_impl (const gchar *content_type) { gchar **desktop_ids; GList *infos; @@ -4527,21 +4504,8 @@ g_app_info_get_recommended_for_type (const gchar *content_type) return g_list_reverse (infos); } -/** - * g_app_info_get_fallback_for_type: - * @content_type: the content type to find a [iface@Gio.AppInfo] for - * - * Gets a list of fallback [iface@Gio.AppInfo]s for a given content type, i.e. - * those applications which claim to support the given content type by MIME - * type subclassing and not directly. - * - * Returns: (element-type GAppInfo) (transfer full): list of [iface@Gio.AppInfo]s - * for given @content_type or `NULL` on error. - * - * Since: 2.28 - **/ GList * -g_app_info_get_fallback_for_type (const gchar *content_type) +g_app_info_get_fallback_for_type_impl (const gchar *content_type) { gchar **recommended_ids; gchar **all_ids; @@ -4579,20 +4543,8 @@ g_app_info_get_fallback_for_type (const gchar *content_type) return g_list_reverse (infos); } -/** - * g_app_info_get_all_for_type: - * @content_type: the content type to find a [iface@Gio.AppInfo] for - * - * Gets a list of all [iface@Gio.AppInfo]s for a given content type, - * including the recommended and fallback [iface@Gio.AppInfo]s. See - * [func@Gio.AppInfo.get_recommended_for_type] and - * [func@Gio.AppInfo.get_fallback_for_type]. - * - * Returns: (element-type GAppInfo) (transfer full): list of - * [iface@Gio.AppInfo]s for given @content_type. - **/ GList * -g_app_info_get_all_for_type (const char *content_type) +g_app_info_get_all_for_type_impl (const char *content_type) { gchar **desktop_ids; GList *infos; @@ -4617,40 +4569,17 @@ g_app_info_get_all_for_type (const char *content_type) return g_list_reverse (infos); } -/** - * g_app_info_reset_type_associations: - * @content_type: a content type - * - * Removes all changes to the type associations done by - * [method@Gio.AppInfo.set_as_default_for_type], - * [method@Gio.AppInfo.set_as_default_for_extension], - * [method@Gio.AppInfo.add_supports_type] or - * [method@Gio.AppInfo.remove_supports_type]. - * - * Since: 2.20 - */ void -g_app_info_reset_type_associations (const char *content_type) +g_app_info_reset_type_associations_impl (const char *content_type) { update_mimeapps_list (NULL, content_type, UPDATE_MIME_NONE, NULL); } -/** - * g_app_info_get_default_for_type: - * @content_type: the content type to find a [iface@Gio.AppInfo] for - * @must_support_uris: if `TRUE`, the [iface@Gio.AppInfo] is expected to - * support URIs - * - * Gets the default [iface@Gio.AppInfo] for a given content type. - * - * Returns: (transfer full) (nullable): [iface@Gio.AppInfo] for given - * @content_type or `NULL` on error. - */ GAppInfo * -g_app_info_get_default_for_type (const char *content_type, - gboolean must_support_uris) +g_app_info_get_default_for_type_impl (const char *content_type, + gboolean must_support_uris) { GPtrArray *blocklist; GPtrArray *results; @@ -4713,20 +4642,8 @@ out: return info; } -/** - * g_app_info_get_default_for_uri_scheme: - * @uri_scheme: a string containing a URI scheme. - * - * Gets the default application for handling URIs with - * the given URI scheme. A URI scheme is the initial part - * of the URI, up to but not including the ':', e.g. "http", - * "ftp" or "sip". - * - * Returns: (transfer full) (nullable): #GAppInfo for given @uri_scheme or - * %NULL on error. - */ GAppInfo * -g_app_info_get_default_for_uri_scheme (const char *uri_scheme) +g_app_info_get_default_for_uri_scheme_impl (const char *uri_scheme) { GAppInfo *app_info; char *content_type, *scheme_down; @@ -4751,10 +4668,10 @@ g_app_info_get_default_for_uri_scheme (const char *uri_scheme) * Gets all applications that implement @interface. * * An application implements an interface if that interface is listed in - * the Implements= line of the desktop file of the application. + * the `Implements` line of the desktop file of the application. * - * Returns: (element-type GDesktopAppInfo) (transfer full): a list of #GDesktopAppInfo - * objects. + * Returns: (element-type GDesktopAppInfo) (transfer full): a list of + * [class@Gio.DesktopAppInfo] objects. * * Since: 2.42 **/ @@ -4807,15 +4724,15 @@ g_desktop_app_info_get_implementations (const gchar *interface) * any time. * * None of the search results are subjected to the normal validation - * checks performed by g_desktop_app_info_new() (for example, checking that + * checks performed by [ctor@Gio.DesktopAppInfo.new] (for example, checking that * the executable referenced by a result exists), and so it is possible for - * g_desktop_app_info_new() to return %NULL when passed an app ID returned by - * this function. It is expected that calling code will do this when - * subsequently creating a #GDesktopAppInfo for each result. + * [ctor@Gio.DesktopAppInfo.new] to return `NULL` when passed an app ID returned + * by this function. It is expected that calling code will do this when + * subsequently creating a [class@Gio.DesktopAppInfo] for each result. * * Returns: (array zero-terminated=1) (element-type GStrv) (transfer full): a - * list of strvs. Free each item with g_strfreev() and free the outer - * list with g_free(). + * list of strvs. Free each item with [func@GLib.strfreev] and free the outer + * list with [func@GLib.free]. */ gchar *** g_desktop_app_info_search (const gchar *search_string) @@ -4892,23 +4809,8 @@ g_desktop_app_info_search (const gchar *search_string) return results; } -/** - * g_app_info_get_all: - * - * Gets a list of all of the applications currently registered - * on this system. - * - * For desktop files, this includes applications that have - * `NoDisplay=true` set or are excluded from display by means - * of `OnlyShowIn` or `NotShowIn`. See [method@Gio.AppInfo.should_show]. - * The returned list does not include applications which have - * the `Hidden` key set. - * - * Returns: (element-type GAppInfo) (transfer full): a newly allocated - * list of references to [iface@Gio.AppInfo]s. - **/ GList * -g_app_info_get_all (void) +g_app_info_get_all_impl (void) { GHashTable *apps; GHashTableIter iter; @@ -4946,8 +4848,8 @@ g_app_info_get_all (void) * #GDesktopAppInfoLookup is an opaque data structure and can only be accessed * using the following functions. * - * Deprecated: 2.28: The #GDesktopAppInfoLookup interface is deprecated and - * unused by GIO. + * Deprecated: 2.28: The [iface@Gio.DesktopAppInfoLookup] interface is + * deprecated and unused by GIO. **/ G_GNUC_BEGIN_IGNORE_DEPRECATIONS @@ -4964,23 +4866,24 @@ g_desktop_app_info_lookup_default_init (GDesktopAppInfoLookupInterface *iface) /** * g_desktop_app_info_lookup_get_default_for_uri_scheme: - * @lookup: a #GDesktopAppInfoLookup + * @lookup: a [iface@Gio.DesktopAppInfoLookup] * @uri_scheme: a string containing a URI scheme. * * Gets the default application for launching applications - * using this URI scheme for a particular #GDesktopAppInfoLookup + * using this URI scheme for a particular [iface@Gio.DesktopAppInfoLookup] * implementation. * - * The #GDesktopAppInfoLookup interface and this function is used - * to implement g_app_info_get_default_for_uri_scheme() backends + * The [iface@Gio.DesktopAppInfoLookup] interface and this function is used + * to implement [func@Gio.AppInfo.get_default_for_uri_scheme] backends * in a GIO module. There is no reason for applications to use it - * directly. Applications should use g_app_info_get_default_for_uri_scheme(). + * directly. Applications should use + * [func@Gio.AppInfo.get_default_for_uri_scheme]. * - * Returns: (transfer full) (nullable): #GAppInfo for given @uri_scheme or - * %NULL on error. + * Returns: (transfer full) (nullable): [iface@Gio.AppInfo] for given + * @uri_scheme or `NULL` on error. * - * Deprecated: 2.28: The #GDesktopAppInfoLookup interface is deprecated and - * unused by GIO. + * Deprecated: 2.28: The [iface@Gio.DesktopAppInfoLookup] interface is + * deprecated and unused by GIO. */ GAppInfo * g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup, @@ -5001,14 +4904,14 @@ G_GNUC_END_IGNORE_DEPRECATIONS /** * g_desktop_app_info_get_startup_wm_class: - * @info: a #GDesktopAppInfo that supports startup notify + * @info: a [class@Gio.DesktopAppInfo] that supports startup notify * - * Retrieves the StartupWMClass field from @info. This represents the - * WM_CLASS property of the main window of the application, if launched + * Retrieves the `StartupWMClass` field from @info. This represents the + * `WM_CLASS` property of the main window of the application, if launched * through @info. * - * Returns: (nullable) (transfer none): the startup WM class, or %NULL if none is set - * in the desktop file. + * Returns: (nullable) (transfer none): the startup WM class, or `NULL` if none + * is set in the desktop file. * * Since: 2.34 */ @@ -5022,15 +4925,15 @@ g_desktop_app_info_get_startup_wm_class (GDesktopAppInfo *info) /** * g_desktop_app_info_get_string: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * @key: the key to look up * * Looks up a string value in the keyfile backing @info. * - * The @key is looked up in the "Desktop Entry" group. + * The @key is looked up in the `Desktop Entry` group. * - * Returns: (nullable): a newly allocated string, or %NULL if the key - * is not found + * Returns: (nullable): a newly allocated string, or `NULL` if the key is not + * found * * Since: 2.36 */ @@ -5046,16 +4949,16 @@ g_desktop_app_info_get_string (GDesktopAppInfo *info, /** * g_desktop_app_info_get_locale_string: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * @key: the key to look up * * Looks up a localized string value in the keyfile backing @info * translated to the current locale. * - * The @key is looked up in the "Desktop Entry" group. + * The @key is looked up in the `Desktop Entry` group. * - * Returns: (nullable): a newly allocated string, or %NULL if the key - * is not found + * Returns: (nullable): a newly allocated string, or `NULL` if the key is not + * found * * Since: 2.56 */ @@ -5073,15 +4976,14 @@ g_desktop_app_info_get_locale_string (GDesktopAppInfo *info, /** * g_desktop_app_info_get_boolean: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * @key: the key to look up * * Looks up a boolean value in the keyfile backing @info. * - * The @key is looked up in the "Desktop Entry" group. + * The @key is looked up in the `Desktop Entry` group. * - * Returns: the boolean value, or %FALSE if the key - * is not found + * Returns: the boolean value, or `FALSE` if the key is not found * * Since: 2.36 */ @@ -5097,17 +4999,18 @@ g_desktop_app_info_get_boolean (GDesktopAppInfo *info, /** * g_desktop_app_info_get_string_list: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * @key: the key to look up - * @length: (out) (optional): return location for the number of returned strings, or %NULL + * @length: (out) (optional): return location for the number of returned + * strings, or `NULL` * * Looks up a string list value in the keyfile backing @info. * - * The @key is looked up in the "Desktop Entry" group. + * The @key is looked up in the `Desktop Entry` group. * * Returns: (array zero-terminated=1 length=length) (element-type utf8) (transfer full): - * a %NULL-terminated string array or %NULL if the specified - * key cannot be found. The array should be freed with g_strfreev(). + * a `NULL`-terminated string array or `NULL` if the specified + * key cannot be found. The array should be freed with [func@GLib.strfreev]. * * Since: 2.60 */ @@ -5124,13 +5027,13 @@ g_desktop_app_info_get_string_list (GDesktopAppInfo *info, /** * g_desktop_app_info_has_key: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * @key: the key to look up * - * Returns whether @key exists in the "Desktop Entry" group + * Returns whether @key exists in the `Desktop Entry` group * of the keyfile backing @info. * - * Returns: %TRUE if the @key exists + * Returns: `TRUE` if the @key exists * * Since: 2.36 */ @@ -5148,15 +5051,17 @@ g_desktop_app_info_has_key (GDesktopAppInfo *info, /** * g_desktop_app_info_list_actions: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * - * Returns the list of "additional application actions" supported on the - * desktop file, as per the desktop file specification. + * Returns the list of + * [‘additional application actions’](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s11.html) + * supported on the desktop file, as per the desktop file specification. * * As per the specification, this is the list of actions that are - * explicitly listed in the "Actions" key of the [Desktop Entry] group. + * explicitly listed in the `Actions` key of the `Desktop Entry` group. * - * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): a list of strings, always non-%NULL + * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): a + * list of strings, always non-`NULL` * * Since: 2.38 **/ @@ -5183,14 +5088,15 @@ app_info_has_action (GDesktopAppInfo *info, /** * g_desktop_app_info_get_action_name: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * @action_name: the name of the action as from - * g_desktop_app_info_list_actions() + * [method@Gio.DesktopAppInfo.list_actions] * - * Gets the user-visible display name of the "additional application - * action" specified by @action_name. + * Gets the user-visible display name of the + * [‘additional application actions’](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s11.html) + * specified by @action_name. * - * This corresponds to the "Name" key within the keyfile group for the + * This corresponds to the `Name` key within the keyfile group for the * action. * * Returns: (transfer full): the locale-specific action name @@ -5225,25 +5131,26 @@ g_desktop_app_info_get_action_name (GDesktopAppInfo *info, /** * g_desktop_app_info_launch_action: - * @info: a #GDesktopAppInfo + * @info: a [class@Gio.DesktopAppInfo] * @action_name: the name of the action as from - * g_desktop_app_info_list_actions() - * @launch_context: (nullable): a #GAppLaunchContext + * [method@Gio.DesktopAppInfo.list_actions] + * @launch_context: (nullable): a [class@Gio.AppLaunchContext] * * Activates the named application action. * * You may only call this function on action names that were - * returned from g_desktop_app_info_list_actions(). + * returned from [method@Gio.DesktopAppInfo.list_actions]. * * Note that if the main entry of the desktop file indicates that the * application supports startup notification, and @launch_context is - * non-%NULL, then startup notification will be used when activating the + * non-`NULL`, then startup notification will be used when activating the * action (and as such, invocation of the action on the receiving side * must signal the end of startup notification when it is completed). * This is the expected behaviour of applications declaring additional - * actions, as per the desktop file specification. + * actions, as per the + * [desktop file specification](https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s11.html). * - * As with g_app_info_launch() there is no way to detect failures that + * As with [method@Gio.AppInfo.launch] there is no way to detect failures that * occur while using this function. * * Since: 2.38 diff --git a/gio/gosxappinfo.m b/gio/gosxappinfo.m index 50b08e0be..6de647d6b 100644 --- a/gio/gosxappinfo.m +++ b/gio/gosxappinfo.m @@ -22,6 +22,7 @@ #include "config.h" #include "gappinfo.h" +#include "gappinfoprivate.h" #include "gosxappinfo.h" #include "gcontenttype.h" #include "gfile.h" @@ -577,10 +578,10 @@ g_osx_app_info_iface_init (GAppInfoIface *iface) } GAppInfo * -g_app_info_create_from_commandline (const char *commandline, - const char *application_name, - GAppInfoCreateFlags flags, - GError **error) +g_app_info_create_from_commandline_impl (const char *commandline, + const char *application_name, + GAppInfoCreateFlags flags, + GError **error) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Creating an app info from a command line not currently supported"); @@ -622,7 +623,7 @@ g_osx_app_info_get_all_for_scheme (const char *cscheme) } GList * -g_app_info_get_all_for_type (const char *content_type) +g_app_info_get_all_for_type_impl (const char *content_type) { gchar *mime_type; CFArrayRef bundle_list; @@ -667,20 +668,20 @@ g_app_info_get_all_for_type (const char *content_type) } GList * -g_app_info_get_recommended_for_type (const char *content_type) +g_app_info_get_recommended_for_type_impl (const char *content_type) { return g_app_info_get_all_for_type (content_type); } GList * -g_app_info_get_fallback_for_type (const char *content_type) +g_app_info_get_fallback_for_type_impl (const char *content_type) { return g_app_info_get_all_for_type (content_type); } GAppInfo * -g_app_info_get_default_for_type (const char *content_type, - gboolean must_support_uris) +g_app_info_get_default_for_type_impl (const char *content_type, + gboolean must_support_uris) { gchar *mime_type; CFStringRef type; @@ -731,7 +732,7 @@ g_app_info_get_default_for_type (const char *content_type, } GAppInfo * -g_app_info_get_default_for_uri_scheme (const char *uri_scheme) +g_app_info_get_default_for_uri_scheme_impl (const char *uri_scheme) { CFStringRef scheme, bundle_id; NSBundle *bundle; @@ -756,7 +757,7 @@ g_app_info_get_default_for_uri_scheme (const char *uri_scheme) } GList * -g_app_info_get_all (void) +g_app_info_get_all_impl (void) { /* There is no API for this afaict * could manually do it... @@ -765,6 +766,6 @@ g_app_info_get_all (void) } void -g_app_info_reset_type_associations (const char *content_type) +g_app_info_reset_type_associations_impl (const char *content_type) { } diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c index 0b179d877..f2b9cf346 100644 --- a/gio/gwin32appinfo.c +++ b/gio/gwin32appinfo.c @@ -29,6 +29,7 @@ #include #include "gcontenttype.h" +#include "gappinfoprivate.h" #include "gwin32appinfo.h" #include "gappinfo.h" #include "gioerror.h" @@ -5671,10 +5672,10 @@ g_win32_app_info_get_supported_types (GAppInfo *appinfo) } GAppInfo * -g_app_info_create_from_commandline (const char *commandline, - const char *application_name, - GAppInfoCreateFlags flags, - GError **error) +g_app_info_create_from_commandline_impl (const char *commandline, + const char *application_name, + GAppInfoCreateFlags flags, + GError **error) { GWin32AppInfo *info; GWin32AppInfoApplication *app; @@ -5754,7 +5755,7 @@ g_win32_app_info_iface_init (GAppInfoIface *iface) } GAppInfo * -g_app_info_get_default_for_uri_scheme (const char *uri_scheme) +g_app_info_get_default_for_uri_scheme_impl (const char *uri_scheme) { GWin32AppInfoURLSchema *scheme = NULL; char *scheme_down; @@ -5796,8 +5797,8 @@ g_app_info_get_default_for_uri_scheme (const char *uri_scheme) } GAppInfo * -g_app_info_get_default_for_type (const char *content_type, - gboolean must_support_uris) +g_app_info_get_default_for_type_impl (const char *content_type, + gboolean must_support_uris) { GWin32AppInfoFileExtension *ext = NULL; char *ext_down; @@ -5858,7 +5859,7 @@ g_app_info_get_default_for_type (const char *content_type, } GList * -g_app_info_get_all (void) +g_app_info_get_all_impl (void) { GHashTableIter iter; gpointer value; @@ -5887,7 +5888,7 @@ g_app_info_get_all (void) } GList * -g_app_info_get_all_for_type (const char *content_type) +g_app_info_get_all_for_type_impl (const char *content_type) { GWin32AppInfoFileExtension *ext = NULL; char *ext_down; @@ -5957,21 +5958,21 @@ g_app_info_get_all_for_type (const char *content_type) } GList * -g_app_info_get_fallback_for_type (const gchar *content_type) +g_app_info_get_fallback_for_type_impl (const gchar *content_type) { /* TODO: fix this once gcontenttype support is improved */ return g_app_info_get_all_for_type (content_type); } GList * -g_app_info_get_recommended_for_type (const gchar *content_type) +g_app_info_get_recommended_for_type_impl (const gchar *content_type) { /* TODO: fix this once gcontenttype support is improved */ return g_app_info_get_all_for_type (content_type); } void -g_app_info_reset_type_associations (const char *content_type) +g_app_info_reset_type_associations_impl (const char *content_type) { /* nothing to do */ } diff --git a/gio/meson.build b/gio/meson.build index a1c0ae36b..3da7a6e47 100644 --- a/gio/meson.build +++ b/gio/meson.build @@ -392,7 +392,7 @@ if host_system != 'windows' if glib_have_cocoa settings_sources += files('gnextstepsettingsbackend.m') - contenttype_sources += files('gosxcontenttype.m') + contenttype_sources += files('gcontenttype-osx.m') unix_sources += files('gosxappinfo.m') framework_dep = dependency('appleframeworks', modules : ['Foundation', 'CoreFoundation', 'AppKit']) platform_deps += [framework_dep] @@ -405,7 +405,7 @@ if host_system != 'windows' ) application_headers += files('gosxappinfo.h') else - contenttype_sources += files('gcontenttype.c') + contenttype_sources += files('gcontenttype-fdo.c') unix_sources += files('gdesktopappinfo.c') gio_unix_include_headers += files('gdesktopappinfo.h') launch_desktop_sources = files('gio-launch-desktop.c') @@ -489,6 +489,7 @@ gio_base_sources = files( 'gbytesicon.c', 'gcancellable.c', 'gcharsetconverter.c', + 'gcontenttype.c', 'gcontextspecificgroup.c', 'gconverter.c', 'gconverterinputstream.c', diff --git a/po/POTFILES.in b/po/POTFILES.in index 8be46bf29..bfe244769 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -12,7 +12,7 @@ gio/gbufferedoutputstream.c gio/gbytesicon.c gio/gcancellable.c gio/gcharsetconverter.c -gio/gcontenttype.c +gio/gcontenttype-fdo.c gio/gcontenttype-win32.c gio/gconverter.c gio/gconverterinputstream.c diff --git a/tools/glib.supp b/tools/glib.supp index bddfe603e..5e4fbd23e 100644 --- a/tools/glib.supp +++ b/tools/glib.supp @@ -896,7 +896,7 @@ fun:g_get_home_dir } -# gcontenttype.c caches a one-time allocation global array of @global_mime_dirs. +# gcontenttype-fdo.c caches a one-time allocation global array of @global_mime_dirs. { content_type_mime_dirs_realloc Memcheck:Leak