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/>.
22 #include "gcontenttype.h"
24 #include "gthemedicon.h"
26 #include <CoreServices/CoreServices.h>
30 * create_cfstring_from_cstr:
33 * Converts a cstr to a utf8 cfstring
34 * It must be CFReleased()'d.
38 create_cfstring_from_cstr (const gchar
*cstr
)
40 return CFStringCreateWithCString (NULL
, cstr
, kCFStringEncodingUTF8
);
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.
53 create_cstr_from_cfstring (CFStringRef str
)
60 cstr
= CFStringGetCStringPtr (str
, kCFStringEncodingUTF8
);
63 return g_strdup (cstr
);
67 * create_cstr_from_cfstring_with_fallback:
68 * @str: a #CFStringRef
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.
78 create_cstr_from_cfstring_with_fallback (CFStringRef str
,
79 const gchar
*fallback
)
83 cstr
= create_cstr_from_cfstring (str
);
85 return g_strdup (fallback
);
91 g_content_type_equals (const gchar
*type1
,
94 CFStringRef str1
, str2
;
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)
103 str1
= create_cfstring_from_cstr (type1
);
104 str2
= create_cfstring_from_cstr (type2
);
106 ret
= UTTypeEqual (str1
, str2
);
115 g_content_type_is_a (const gchar
*ctype
,
116 const gchar
*csupertype
)
118 CFStringRef type
, supertype
;
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
);
130 CFRelease (supertype
);
136 g_content_type_is_mime_type (const gchar
*type
,
137 const gchar
*mime_type
)
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
);
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."))
160 /* application/octet-stream */
161 else if (g_strcmp0 (type
, "public.data") == 0)
168 g_content_type_get_description (const gchar
*type
)
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
);
179 return create_cstr_from_cfstring_with_fallback (desc_str
, "unknown");
183 g_content_type_get_icon_internal (const gchar
*type
,
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";
199 icon
= g_themed_icon_new_with_default_fallbacks (name
);
205 g_content_type_get_icon (const gchar
*type
)
207 return g_content_type_get_icon_internal (type
, FALSE
);
211 g_content_type_get_symbolic_icon (const gchar
*type
)
213 return g_content_type_get_icon_internal (type
, TRUE
);
217 g_content_type_get_generic_icon_name (const gchar
*type
)
223 g_content_type_can_be_executable (const gchar
*type
)
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
))
234 else if (UTTypeConformsTo (uti
, CFSTR("public.executable")))
236 else if (UTTypeConformsTo (uti
, CFSTR("public.script")))
238 /* Our tests assert that all text can be executable... */
239 else if (UTTypeConformsTo (uti
, CFSTR("public.text")))
247 g_content_type_from_mime_type (const gchar
*mime_type
)
249 CFStringRef mime_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");
309 g_content_type_get_mime_type (const gchar
*type
)
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
);
341 return create_cstr_from_cfstring_with_fallback (mime_str
, "application/octet-stream");
345 looks_like_text (const guchar
*data
,
351 for (i
= 0; i
< data_size
; i
++)
354 if (g_ascii_iscntrl (c
) && !g_ascii_isspace (c
) && c
!= '\b')
361 g_content_type_guess (const gchar
*filename
,
364 gboolean
*result_uncertain
)
366 CFStringRef uti
= NULL
;
368 CFStringRef extension
;
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
)
388 extension
= create_cfstring_from_cstr (cextension
);
389 uti
= UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension
,
391 CFRelease (extension
);
393 if (CFStringHasPrefix (uti
, CFSTR ("dyn.")))
396 uti
= CFStringCreateCopy (NULL
, kUTTypeFolder
);
402 uti
= CFStringCreateCopy (NULL
, kUTTypeFolder
);
403 uncertain
= TRUE
; /* Matches Unix backend */
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
)
420 extension
= create_cfstring_from_cstr (cextension
);
421 uti
= UTTypeCreatePreferredIdentifierForTag (kUTTagClassFilenameExtension
,
423 CFRelease (extension
);
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"));
437 uti
= CFStringCreateCopy (NULL
, CFSTR ("public.text"));
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
);
457 g_content_types_get_registered (void)
459 /* TODO: UTTypeCreateAllIdentifiersForTag? */
464 g_content_type_guess_for_tree (GFile
*root
)