meson: Simplify the use of built tools
[glib.git] / gio / gosxcontenttype.c
blob485f5bfb61da6d17e189f7a4c09fcc8f2c092516
1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2014 Patrick Griffis
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 #include "config.h"
22 #include "gcontenttype.h"
23 #include "gicon.h"
24 #include "gthemedicon.h"
26 #include <CoreServices/CoreServices.h>
29 /*< internal >
30 * create_cfstring_from_cstr:
31 * @cstr: a #gchar
33 * Converts a cstr to a utf8 cfstring
34 * It must be CFReleased()'d.
37 static CFStringRef
38 create_cfstring_from_cstr (const gchar *cstr)
40 return CFStringCreateWithCString (NULL, cstr, kCFStringEncodingUTF8);
43 /*< internal >
44 * create_cstr_from_cfstring:
45 * @str: a #CFStringRef
47 * Converts a cfstring to a utf8 cstring.
48 * The incoming cfstring is released for you.
49 * The returned string must be g_free()'d.
52 static gchar *
53 create_cstr_from_cfstring (CFStringRef str)
55 const gchar *cstr;
57 if (str == NULL)
58 return NULL;
60 cstr = CFStringGetCStringPtr (str, kCFStringEncodingUTF8);
61 CFRelease (str);
63 return g_strdup (cstr);
66 /*< internal >
67 * create_cstr_from_cfstring_with_fallback:
68 * @str: a #CFStringRef
69 * @fallback: a #gchar
71 * Tries to convert a cfstring to a utf8 cstring.
72 * If @str is NULL or conversion fails @fallback is returned.
73 * The incoming cfstring is released for you.
74 * The returned string must be g_free()'d.
77 static gchar *
78 create_cstr_from_cfstring_with_fallback (CFStringRef str,
79 const gchar *fallback)
81 gchar *cstr;
83 cstr = create_cstr_from_cfstring (str);
84 if (!cstr)
85 return g_strdup (fallback);
87 return cstr;
90 gboolean
91 g_content_type_equals (const gchar *type1,
92 const gchar *type2)
94 CFStringRef str1, str2;
95 gboolean ret;
97 g_return_val_if_fail (type1 != NULL, FALSE);
98 g_return_val_if_fail (type2 != NULL, FALSE);
100 if (g_ascii_strcasecmp (type1, type2) == 0)
101 return TRUE;
103 str1 = create_cfstring_from_cstr (type1);
104 str2 = create_cfstring_from_cstr (type2);
106 ret = UTTypeEqual (str1, str2);
108 CFRelease (str1);
109 CFRelease (str2);
111 return ret;
114 gboolean
115 g_content_type_is_a (const gchar *ctype,
116 const gchar *csupertype)
118 CFStringRef type, supertype;
119 gboolean ret;
121 g_return_val_if_fail (ctype != NULL, FALSE);
122 g_return_val_if_fail (csupertype != NULL, FALSE);
124 type = create_cfstring_from_cstr (ctype);
125 supertype = create_cfstring_from_cstr (csupertype);
127 ret = UTTypeConformsTo (type, supertype);
129 CFRelease (type);
130 CFRelease (supertype);
132 return ret;
135 gboolean
136 g_content_type_is_mime_type (const gchar *type,
137 const gchar *mime_type)
139 gchar *content_type;
140 gboolean ret;
142 g_return_val_if_fail (type != NULL, FALSE);
143 g_return_val_if_fail (mime_type != NULL, FALSE);
145 content_type = g_content_type_from_mime_type (mime_type);
146 ret = g_content_type_is_a (type, content_type);
147 g_free (content_type);
149 return ret;
152 gboolean
153 g_content_type_is_unknown (const gchar *type)
155 g_return_val_if_fail (type != NULL, FALSE);
157 /* Should dynamic types be considered "unknown"? */
158 if (g_str_has_prefix (type, "dyn."))
159 return TRUE;
160 /* application/octet-stream */
161 else if (g_strcmp0 (type, "public.data") == 0)
162 return TRUE;
164 return FALSE;
167 gchar *
168 g_content_type_get_description (const gchar *type)
170 CFStringRef str;
171 CFStringRef desc_str;
173 g_return_val_if_fail (type != NULL, NULL);
175 str = create_cfstring_from_cstr (type);
176 desc_str = UTTypeCopyDescription (str);
178 CFRelease (str);
179 return create_cstr_from_cfstring_with_fallback (desc_str, "unknown");
182 static GIcon *
183 g_content_type_get_icon_internal (const gchar *type,
184 gboolean symbolic)
186 GIcon *icon = NULL;
187 gchar *name;
189 g_return_val_if_fail (type != NULL, NULL);
191 /* TODO: Show mimetype icons. */
192 if (g_content_type_can_be_executable (type))
193 name = "gtk-execute";
194 else if (g_content_type_is_a (type, "public.directory"))
195 name = symbolic ? "inode-directory-symbolic" : "inode-directory";
196 else
197 name = "gtk-file";
199 icon = g_themed_icon_new_with_default_fallbacks (name);
201 return icon;
204 GIcon *
205 g_content_type_get_icon (const gchar *type)
207 return g_content_type_get_icon_internal (type, FALSE);
210 GIcon *
211 g_content_type_get_symbolic_icon (const gchar *type)
213 return g_content_type_get_icon_internal (type, TRUE);
216 gchar *
217 g_content_type_get_generic_icon_name (const gchar *type)
219 return NULL;
222 gboolean
223 g_content_type_can_be_executable (const gchar *type)
225 CFStringRef uti;
226 gboolean ret = FALSE;
228 g_return_val_if_fail (type != NULL, FALSE);
230 uti = create_cfstring_from_cstr (type);
232 if (UTTypeConformsTo (uti, kUTTypeApplication))
233 ret = TRUE;
234 else if (UTTypeConformsTo (uti, CFSTR("public.executable")))
235 ret = TRUE;
236 else if (UTTypeConformsTo (uti, CFSTR("public.script")))
237 ret = TRUE;
238 /* Our tests assert that all text can be executable... */
239 else if (UTTypeConformsTo (uti, CFSTR("public.text")))
240 ret = TRUE;
242 CFRelease (uti);
243 return ret;
246 gchar *
247 g_content_type_from_mime_type (const gchar *mime_type)
249 CFStringRef mime_str;
250 CFStringRef uti_str;
252 g_return_val_if_fail (mime_type != NULL, NULL);
254 /* Their api does not handle globs but they are common. */
255 if (g_str_has_suffix (mime_type, "*"))
257 if (g_str_has_prefix (mime_type, "audio"))
258 return g_strdup ("public.audio");
259 if (g_str_has_prefix (mime_type, "image"))
260 return g_strdup ("public.image");
261 if (g_str_has_prefix (mime_type, "text"))
262 return g_strdup ("public.text");
263 if (g_str_has_prefix (mime_type, "video"))
264 return g_strdup ("public.movie");
267 /* Some exceptions are needed for gdk-pixbuf.
268 * This list is not exhaustive.
270 if (g_str_has_prefix (mime_type, "image"))
272 if (g_str_has_suffix (mime_type, "x-icns"))
273 return g_strdup ("com.apple.icns");
274 if (g_str_has_suffix (mime_type, "x-tga"))
275 return g_strdup ("com.truevision.tga-image");
276 if (g_str_has_suffix (mime_type, "x-ico"))
277 return g_strdup ("com.microsoft.ico ");
280 /* These are also not supported...
281 * Used in glocalfileinfo.c
283 if (g_str_has_prefix (mime_type, "inode"))
285 if (g_str_has_suffix (mime_type, "directory"))
286 return g_strdup ("public.folder");
287 if (g_str_has_suffix (mime_type, "symlink"))
288 return g_strdup ("public.symlink");
291 /* This is correct according to the Apple docs:
292 https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html
294 if (strcmp (mime_type, "text/plain") == 0)
295 return g_strdup ("public.text");
297 /* Non standard type */
298 if (strcmp (mime_type, "application/x-executable") == 0)
299 return g_strdup ("public.executable");
301 mime_str = create_cfstring_from_cstr (mime_type);
302 uti_str = UTTypeCreatePreferredIdentifierForTag (kUTTagClassMIMEType, mime_str, NULL);
304 CFRelease (mime_str);
305 return create_cstr_from_cfstring_with_fallback (uti_str, "public.data");
308 gchar *
309 g_content_type_get_mime_type (const gchar *type)
311 CFStringRef uti_str;
312 CFStringRef mime_str;
314 g_return_val_if_fail (type != NULL, NULL);
316 /* We must match the additions above
317 * so conversions back and forth work.
319 if (g_str_has_prefix (type, "public"))
321 if (g_str_has_suffix (type, ".image"))
322 return g_strdup ("image/*");
323 if (g_str_has_suffix (type, ".movie"))
324 return g_strdup ("video/*");
325 if (g_str_has_suffix (type, ".text"))
326 return g_strdup ("text/*");
327 if (g_str_has_suffix (type, ".audio"))
328 return g_strdup ("audio/*");
329 if (g_str_has_suffix (type, ".folder"))
330 return g_strdup ("inode/directory");
331 if (g_str_has_suffix (type, ".symlink"))
332 return g_strdup ("inode/symlink");
333 if (g_str_has_suffix (type, ".executable"))
334 return g_strdup ("application/x-executable");
337 uti_str = create_cfstring_from_cstr (type);
338 mime_str = UTTypeCopyPreferredTagWithClass(uti_str, kUTTagClassMIMEType);
340 CFRelease (uti_str);
341 return create_cstr_from_cfstring_with_fallback (mime_str, "application/octet-stream");
344 static gboolean
345 looks_like_text (const guchar *data,
346 gsize data_size)
348 gsize i;
349 guchar c;
351 for (i = 0; i < data_size; i++)
353 c = data[i];
354 if (g_ascii_iscntrl (c) && !g_ascii_isspace (c) && c != '\b')
355 return FALSE;
357 return TRUE;
360 gchar *
361 g_content_type_guess (const gchar *filename,
362 const guchar *data,
363 gsize data_size,
364 gboolean *result_uncertain)
366 CFStringRef uti = NULL;
367 gchar *cextension;
368 CFStringRef extension;
369 int uncertain = -1;
371 g_return_val_if_fail (data_size != (gsize) -1, NULL);
373 if (filename && *filename)
375 gchar *basename = g_path_get_basename (filename);
376 gchar *dirname = g_path_get_dirname (filename);
377 gsize i = strlen (filename);
379 if (filename[i - 1] == '/')
381 if (g_strcmp0 (dirname, "/Volumes") == 0)
383 uti = CFStringCreateCopy (NULL, kUTTypeVolume);
385 else if ((cextension = strrchr (basename, '.')) != NULL)
387 cextension++;
388 extension = create_cfstring_from_cstr (cextension);
389 uti = UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension,
390 extension, NULL);
391 CFRelease (extension);
393 if (CFStringHasPrefix (uti, CFSTR ("dyn.")))
395 CFRelease (uti);
396 uti = CFStringCreateCopy (NULL, kUTTypeFolder);
397 uncertain = TRUE;
400 else
402 uti = CFStringCreateCopy (NULL, kUTTypeFolder);
403 uncertain = TRUE; /* Matches Unix backend */
406 else
408 /* GTK needs this... */
409 if (g_str_has_suffix (basename, ".ui"))
411 uti = CFStringCreateCopy (NULL, kUTTypeXML);
413 else if (g_str_has_suffix (basename, ".txt"))
415 uti = CFStringCreateCopy (NULL, CFSTR ("public.text"));
417 else if ((cextension = strrchr (basename, '.')) != NULL)
419 cextension++;
420 extension = create_cfstring_from_cstr (cextension);
421 uti = UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension,
422 extension, NULL);
423 CFRelease (extension);
425 g_free (basename);
426 g_free (dirname);
429 if (data && (!filename || !uti ||
430 CFStringCompare (uti, CFSTR ("public.data"), 0) == kCFCompareEqualTo))
432 if (looks_like_text (data, data_size))
434 if (g_str_has_prefix ((const gchar*)data, "#!/"))
435 uti = CFStringCreateCopy (NULL, CFSTR ("public.script"));
436 else
437 uti = CFStringCreateCopy (NULL, CFSTR ("public.text"));
441 if (!uti)
443 /* Generic data type */
444 uti = CFStringCreateCopy (NULL, CFSTR ("public.data"));
445 if (result_uncertain)
446 *result_uncertain = TRUE;
448 else if (result_uncertain)
450 *result_uncertain = uncertain == -1 ? FALSE : uncertain;
453 return create_cstr_from_cfstring (uti);
456 GList *
457 g_content_types_get_registered (void)
459 /* TODO: UTTypeCreateAllIdentifiersForTag? */
460 return NULL;
463 gchar **
464 g_content_type_guess_for_tree (GFile *root)
466 return NULL;