diff options
author | Xavier Claessens <xavier.claessens@collabora.com> | 2018-05-23 09:16:27 -0400 |
---|---|---|
committer | Xavier Claessens <xclaesse@gmail.com> | 2018-05-25 03:06:07 +0000 |
commit | ead46cdc7e70db6bd5c1535a0d2574e4b14803b8 (patch) | |
tree | bd1bd3d7f713b7a7942bc5352d478759bc1c58d7 /gio/gosxcontenttype.m | |
parent | 534e939500706af0ea02b56cffa22202014f578e (diff) | |
download | glib-ead46cdc7e70db6bd5c1535a0d2574e4b14803b8.tar.gz |
Revert "Revert "Rename objective-c files from .c to .m""
This reverts commit 2e9f3a9afe4c5cb1ebdda5e33e6fd3552bf56d38.
Diffstat (limited to 'gio/gosxcontenttype.m')
-rw-r--r-- | gio/gosxcontenttype.m | 582 |
1 files changed, 582 insertions, 0 deletions
diff --git a/gio/gosxcontenttype.m b/gio/gosxcontenttype.m new file mode 100644 index 000000000..52ba5763a --- /dev/null +++ b/gio/gosxcontenttype.m @@ -0,0 +1,582 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2014 Patrick Griffis + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#include "config.h" + +#include "gcontenttype.h" +#include "gicon.h" +#include "gthemedicon.h" + +#include <CoreServices/CoreServices.h> + +#define XDG_PREFIX _gio_xdg +#include "xdgmime/xdgmime.h" + +/* We lock this mutex whenever we modify global state in this module. */ +G_LOCK_DEFINE_STATIC (gio_xdgmime); + + +/*< internal > + * create_cfstring_from_cstr: + * @cstr: a #gchar + * + * Converts a cstr to a utf8 cfstring + * It must be CFReleased()'d. + * + */ +static CFStringRef +create_cfstring_from_cstr (const gchar *cstr) +{ + return CFStringCreateWithCString (NULL, cstr, kCFStringEncodingUTF8); +} + +/*< internal > + * create_cstr_from_cfstring: + * @str: a #CFStringRef + * + * Converts a cfstring to a utf8 cstring. + * The incoming cfstring is released for you. + * The returned string must be g_free()'d. + * + */ +static gchar * +create_cstr_from_cfstring (CFStringRef str) +{ + g_return_val_if_fail (str != NULL, NULL); + + CFIndex length = CFStringGetLength (str); + CFIndex maxlen = CFStringGetMaximumSizeForEncoding (length, kCFStringEncodingUTF8); + gchar *buffer = g_malloc (maxlen + 1); + Boolean success = CFStringGetCString (str, (char *) buffer, maxlen, + kCFStringEncodingUTF8); + CFRelease (str); + if (success) + return buffer; + else + { + g_free (buffer); + return NULL; + } +} + +/*< internal > + * create_cstr_from_cfstring_with_fallback: + * @str: a #CFStringRef + * @fallback: a #gchar + * + * Tries to convert a cfstring to a utf8 cstring. + * If @str is NULL or conversion fails @fallback is returned. + * The incoming cfstring is released for you. + * The returned string must be g_free()'d. + * + */ +static gchar * +create_cstr_from_cfstring_with_fallback (CFStringRef str, + const gchar *fallback) +{ + gchar *cstr = NULL; + + if (str) + cstr = create_cstr_from_cfstring (str); + if (!cstr) + return g_strdup (fallback); + + return cstr; +} + +gboolean +g_content_type_equals (const gchar *type1, + const gchar *type2) +{ + CFStringRef str1, str2; + gboolean ret; + + g_return_val_if_fail (type1 != NULL, FALSE); + g_return_val_if_fail (type2 != NULL, FALSE); + + if (g_ascii_strcasecmp (type1, type2) == 0) + return TRUE; + + str1 = create_cfstring_from_cstr (type1); + str2 = create_cfstring_from_cstr (type2); + + ret = UTTypeEqual (str1, str2); + + CFRelease (str1); + CFRelease (str2); + + return ret; +} + +gboolean +g_content_type_is_a (const gchar *ctype, + const gchar *csupertype) +{ + CFStringRef type, supertype; + gboolean ret; + + g_return_val_if_fail (ctype != NULL, FALSE); + g_return_val_if_fail (csupertype != NULL, FALSE); + + type = create_cfstring_from_cstr (ctype); + supertype = create_cfstring_from_cstr (csupertype); + + ret = UTTypeConformsTo (type, supertype); + + CFRelease (type); + CFRelease (supertype); + + return ret; +} + +gboolean +g_content_type_is_mime_type (const gchar *type, + const gchar *mime_type) +{ + gchar *content_type; + gboolean ret; + + g_return_val_if_fail (type != NULL, FALSE); + g_return_val_if_fail (mime_type != NULL, FALSE); + + content_type = g_content_type_from_mime_type (mime_type); + ret = g_content_type_is_a (type, content_type); + g_free (content_type); + + return ret; +} + +gboolean +g_content_type_is_unknown (const gchar *type) +{ + g_return_val_if_fail (type != NULL, FALSE); + + /* Should dynamic types be considered "unknown"? */ + if (g_str_has_prefix (type, "dyn.")) + return TRUE; + /* application/octet-stream */ + else if (g_strcmp0 (type, "public.data") == 0) + return TRUE; + + return FALSE; +} + +gchar * +g_content_type_get_description (const gchar *type) +{ + CFStringRef str; + CFStringRef desc_str; + + g_return_val_if_fail (type != NULL, NULL); + + str = create_cfstring_from_cstr (type); + desc_str = UTTypeCopyDescription (str); + + CFRelease (str); + return create_cstr_from_cfstring_with_fallback (desc_str, "unknown"); +} + +/* <internal> + * _get_generic_icon_name_from_mime_type + * + * This function produces a generic icon name from a @mime_type. + * If no generic icon name is found in the xdg mime database, the + * generic icon name is constructed. + * + * Background: + * generic-icon elements specify the icon to use as a generic icon for this + * particular mime-type, given by the name attribute. This is used if there + * is no specific icon (see icon for how these are found). These are used + * for categories of similar types (like spreadsheets or archives) that can + * use a common icon. The Icon Naming Specification lists a set of such + * icon names. If this element is not specified then the mimetype is used + * to generate the generic icon by using the top-level media type + * (e.g. "video" in "video/ogg") and appending "-x-generic" + * (i.e. "video-x-generic" in the previous example). + * + * From: https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-0.18.html + */ + +static gchar * +_get_generic_icon_name_from_mime_type (const gchar *mime_type) +{ + const gchar *xdg_icon_name; + gchar *icon_name; + + G_LOCK (gio_xdgmime); + xdg_icon_name = xdg_mime_get_generic_icon (mime_type); + G_UNLOCK (gio_xdgmime); + + if (xdg_icon_name == NULL) + { + const char *p; + const char *suffix = "-x-generic"; + gsize prefix_len; + + p = strchr (mime_type, '/'); + if (p == NULL) + prefix_len = strlen (mime_type); + else + prefix_len = p - mime_type; + + icon_name = g_malloc (prefix_len + strlen (suffix) + 1); + memcpy (icon_name, mime_type, prefix_len); + memcpy (icon_name + prefix_len, suffix, strlen (suffix)); + icon_name[prefix_len + strlen (suffix)] = 0; + } + else + { + icon_name = g_strdup (xdg_icon_name); + } + + return icon_name; +} + + +static GIcon * +g_content_type_get_icon_internal (const gchar *uti, + gboolean symbolic) +{ + char *mimetype_icon; + char *mime_type; + 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 (uti != NULL, NULL); + + mime_type = g_content_type_get_mime_type (uti); + + G_LOCK (gio_xdgmime); + xdg_icon = xdg_mime_get_icon (mime_type); + G_UNLOCK (gio_xdgmime); + + if (xdg_icon) + icon_names[n++] = g_strdup (xdg_icon); + + mimetype_icon = g_strdup (mime_type); + while ((q = strchr (mimetype_icon, '/')) != NULL) + *q = '-'; + + icon_names[n++] = mimetype_icon; + + generic_mimetype_icon = _get_generic_icon_name_from_mime_type (mime_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]); + + g_free(mime_type); + + return themed_icon; +} + +GIcon * +g_content_type_get_icon (const gchar *type) +{ + return g_content_type_get_icon_internal (type, FALSE); +} + +GIcon * +g_content_type_get_symbolic_icon (const gchar *type) +{ + return g_content_type_get_icon_internal (type, TRUE); +} + +gchar * +g_content_type_get_generic_icon_name (const gchar *type) +{ + return NULL; +} + +gboolean +g_content_type_can_be_executable (const gchar *type) +{ + CFStringRef uti; + gboolean ret = FALSE; + + g_return_val_if_fail (type != NULL, FALSE); + + uti = create_cfstring_from_cstr (type); + + if (UTTypeConformsTo (uti, kUTTypeApplication)) + ret = TRUE; + else if (UTTypeConformsTo (uti, CFSTR("public.executable"))) + ret = TRUE; + else if (UTTypeConformsTo (uti, CFSTR("public.script"))) + ret = TRUE; + /* Our tests assert that all text can be executable... */ + else if (UTTypeConformsTo (uti, CFSTR("public.text"))) + ret = TRUE; + + CFRelease (uti); + return ret; +} + +gchar * +g_content_type_from_mime_type (const gchar *mime_type) +{ + CFStringRef mime_str; + CFStringRef uti_str; + + g_return_val_if_fail (mime_type != NULL, NULL); + + /* Their api does not handle globs but they are common. */ + if (g_str_has_suffix (mime_type, "*")) + { + if (g_str_has_prefix (mime_type, "audio")) + return g_strdup ("public.audio"); + if (g_str_has_prefix (mime_type, "image")) + return g_strdup ("public.image"); + if (g_str_has_prefix (mime_type, "text")) + return g_strdup ("public.text"); + if (g_str_has_prefix (mime_type, "video")) + return g_strdup ("public.movie"); + } + + /* Some exceptions are needed for gdk-pixbuf. + * This list is not exhaustive. + */ + if (g_str_has_prefix (mime_type, "image")) + { + if (g_str_has_suffix (mime_type, "x-icns")) + return g_strdup ("com.apple.icns"); + if (g_str_has_suffix (mime_type, "x-tga")) + return g_strdup ("com.truevision.tga-image"); + if (g_str_has_suffix (mime_type, "x-ico")) + return g_strdup ("com.microsoft.ico "); + } + + /* These are also not supported... + * Used in glocalfileinfo.c + */ + if (g_str_has_prefix (mime_type, "inode")) + { + if (g_str_has_suffix (mime_type, "directory")) + return g_strdup ("public.folder"); + if (g_str_has_suffix (mime_type, "symlink")) + return g_strdup ("public.symlink"); + } + + /* This is correct according to the Apple docs: + https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html + */ + if (strcmp (mime_type, "text/plain") == 0) + return g_strdup ("public.text"); + + /* Non standard type */ + if (strcmp (mime_type, "application/x-executable") == 0) + return g_strdup ("public.executable"); + + mime_str = create_cfstring_from_cstr (mime_type); + uti_str = UTTypeCreatePreferredIdentifierForTag (kUTTagClassMIMEType, mime_str, NULL); + + CFRelease (mime_str); + return create_cstr_from_cfstring_with_fallback (uti_str, "public.data"); +} + +gchar * +g_content_type_get_mime_type (const gchar *type) +{ + CFStringRef uti_str; + CFStringRef mime_str; + + g_return_val_if_fail (type != NULL, NULL); + + /* We must match the additions above + * so conversions back and forth work. + */ + if (g_str_has_prefix (type, "public")) + { + if (g_str_has_suffix (type, ".image")) + return g_strdup ("image/*"); + if (g_str_has_suffix (type, ".movie")) + return g_strdup ("video/*"); + if (g_str_has_suffix (type, ".text")) + return g_strdup ("text/*"); + if (g_str_has_suffix (type, ".audio")) + return g_strdup ("audio/*"); + if (g_str_has_suffix (type, ".folder")) + return g_strdup ("inode/directory"); + if (g_str_has_suffix (type, ".symlink")) + return g_strdup ("inode/symlink"); + if (g_str_has_suffix (type, ".executable")) + return g_strdup ("application/x-executable"); + } + + uti_str = create_cfstring_from_cstr (type); + mime_str = UTTypeCopyPreferredTagWithClass(uti_str, kUTTagClassMIMEType); + + CFRelease (uti_str); + return create_cstr_from_cfstring_with_fallback (mime_str, "application/octet-stream"); +} + +static gboolean +looks_like_text (const guchar *data, + gsize data_size) +{ + gsize i; + guchar 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_guess (const gchar *filename, + const guchar *data, + gsize data_size, + gboolean *result_uncertain) +{ + CFStringRef uti = NULL; + gchar *cextension; + CFStringRef extension; + int uncertain = -1; + + g_return_val_if_fail (data_size != (gsize) -1, NULL); + + if (filename && *filename) + { + gchar *basename = g_path_get_basename (filename); + gchar *dirname = g_path_get_dirname (filename); + gsize i = strlen (filename); + + if (filename[i - 1] == '/') + { + if (g_strcmp0 (dirname, "/Volumes") == 0) + { + uti = CFStringCreateCopy (NULL, kUTTypeVolume); + } + else if ((cextension = strrchr (basename, '.')) != NULL) + { + cextension++; + extension = create_cfstring_from_cstr (cextension); + uti = UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension, + extension, NULL); + CFRelease (extension); + + if (CFStringHasPrefix (uti, CFSTR ("dyn."))) + { + CFRelease (uti); + uti = CFStringCreateCopy (NULL, kUTTypeFolder); + uncertain = TRUE; + } + } + else + { + uti = CFStringCreateCopy (NULL, kUTTypeFolder); + uncertain = TRUE; /* Matches Unix backend */ + } + } + else + { + /* GTK needs this... */ + if (g_str_has_suffix (basename, ".ui")) + { + uti = CFStringCreateCopy (NULL, kUTTypeXML); + } + else if (g_str_has_suffix (basename, ".txt")) + { + uti = CFStringCreateCopy (NULL, CFSTR ("public.text")); + } + else if ((cextension = strrchr (basename, '.')) != NULL) + { + cextension++; + extension = create_cfstring_from_cstr (cextension); + uti = UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension, + extension, NULL); + CFRelease (extension); + } + g_free (basename); + g_free (dirname); + } + } + if (data && (!filename || !uti || + CFStringCompare (uti, CFSTR ("public.data"), 0) == kCFCompareEqualTo)) + { + const char *sniffed_mimetype; + G_LOCK (gio_xdgmime); + sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, NULL); + G_UNLOCK (gio_xdgmime); + if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN) + { + gchar *uti_str = g_content_type_from_mime_type (sniffed_mimetype); + uti = create_cfstring_from_cstr (uti_str); + g_free (uti_str); + } + if (!uti && looks_like_text (data, data_size)) + { + if (g_str_has_prefix ((const gchar*)data, "#!/")) + uti = CFStringCreateCopy (NULL, CFSTR ("public.script")); + else + uti = CFStringCreateCopy (NULL, CFSTR ("public.text")); + } + } + + if (!uti) + { + /* Generic data type */ + uti = CFStringCreateCopy (NULL, CFSTR ("public.data")); + if (result_uncertain) + *result_uncertain = TRUE; + } + else if (result_uncertain) + { + *result_uncertain = uncertain == -1 ? FALSE : uncertain; + } + + return create_cstr_from_cfstring (uti); +} + +GList * +g_content_types_get_registered (void) +{ + /* TODO: UTTypeCreateAllIdentifiersForTag? */ + return NULL; +} + +gchar ** +g_content_type_guess_for_tree (GFile *root) +{ + return NULL; +} |