Bug 561807 – inotify_sub.c :: dup_dirname() fails to remove trailing '/'
[glib.git] / gio / gdesktopappinfo.c
blobdbf3867455ad03a9dc330c0b9ef19c136919d6f1
1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright © 2007 Ryan Lortie
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307, USA.
21 * Author: Alexander Larsson <alexl@redhat.com>
24 #include "config.h"
26 #include <errno.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/wait.h>
31 #ifdef HAVE_CRT_EXTERNS_H
32 #include <crt_externs.h>
33 #endif
35 #include "gcontenttypeprivate.h"
36 #include "gdesktopappinfo.h"
37 #include "gfile.h"
38 #include "gioerror.h"
39 #include "gthemedicon.h"
40 #include "gfileicon.h"
41 #include <glib/gstdio.h>
42 #include "glibintl.h"
43 #include "giomodule-priv.h"
44 #include "gappinfo.h"
46 #include "gioalias.h"
48 /**
49 * SECTION:gdesktopappinfo
50 * @short_description: Application information from desktop files
51 * @include: gio/gdesktopappinfo.h
53 * #GDesktopAppInfo is an implementation of #GAppInfo based on
54 * desktop files.
56 **/
58 #define DEFAULT_APPLICATIONS_GROUP "Default Applications"
59 #define ADDED_ASSOCIATIONS_GROUP "Added Associations"
60 #define REMOVED_ASSOCIATIONS_GROUP "Removed Associations"
61 #define MIME_CACHE_GROUP "MIME Cache"
63 static void g_desktop_app_info_iface_init (GAppInfoIface *iface);
64 static GList * get_all_desktop_entries_for_mime_type (const char *base_mime_type,
65 const char **except);
66 static void mime_info_cache_reload (const char *dir);
67 static gboolean g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
68 GError **error);
70 /**
71 * GDesktopAppInfo:
73 * Information about an installed application from a desktop file.
75 struct _GDesktopAppInfo
77 GObject parent_instance;
79 char *desktop_id;
80 char *filename;
82 char *name;
83 /* FIXME: what about GenericName ? */
84 char *comment;
85 char *icon_name;
86 GIcon *icon;
87 char **only_show_in;
88 char **not_show_in;
89 char *try_exec;
90 char *exec;
91 char *binary;
92 char *path;
94 guint nodisplay : 1;
95 guint hidden : 1;
96 guint terminal : 1;
97 guint startup_notify : 1;
98 /* FIXME: what about StartupWMClass ? */
101 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT,
102 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
103 g_desktop_app_info_iface_init))
105 static gpointer
106 search_path_init (gpointer data)
108 char **args = NULL;
109 const char * const *data_dirs;
110 const char *user_data_dir;
111 int i, length, j;
113 data_dirs = g_get_system_data_dirs ();
114 length = g_strv_length ((char **) data_dirs);
116 args = g_new (char *, length + 2);
118 j = 0;
119 user_data_dir = g_get_user_data_dir ();
120 args[j++] = g_build_filename (user_data_dir, "applications", NULL);
121 for (i = 0; i < length; i++)
122 args[j++] = g_build_filename (data_dirs[i],
123 "applications", NULL);
124 args[j++] = NULL;
126 return args;
129 static const char * const *
130 get_applications_search_path (void)
132 static GOnce once_init = G_ONCE_INIT;
133 return g_once (&once_init, search_path_init, NULL);
136 static void
137 g_desktop_app_info_finalize (GObject *object)
139 GDesktopAppInfo *info;
141 info = G_DESKTOP_APP_INFO (object);
143 g_free (info->desktop_id);
144 g_free (info->filename);
145 g_free (info->name);
146 g_free (info->comment);
147 g_free (info->icon_name);
148 if (info->icon)
149 g_object_unref (info->icon);
150 g_strfreev (info->only_show_in);
151 g_strfreev (info->not_show_in);
152 g_free (info->try_exec);
153 g_free (info->exec);
154 g_free (info->binary);
155 g_free (info->path);
157 G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
160 static void
161 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
163 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
165 gobject_class->finalize = g_desktop_app_info_finalize;
168 static void
169 g_desktop_app_info_init (GDesktopAppInfo *local)
173 static char *
174 binary_from_exec (const char *exec)
176 const char *p, *start;
178 p = exec;
179 while (*p == ' ')
180 p++;
181 start = p;
182 while (*p != ' ' && *p != 0)
183 p++;
185 return g_strndup (start, p - start);
190 * g_desktop_app_info_new_from_keyfile:
191 * @key_file: an opened #GKeyFile
193 * Creates a new #GDesktopAppInfo.
195 * Returns: a new #GDesktopAppInfo or %NULL on error.
197 * Since: 2.18
199 GDesktopAppInfo *
200 g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
202 GDesktopAppInfo *info;
203 char *start_group;
204 char *type;
205 char *try_exec;
207 start_group = g_key_file_get_start_group (key_file);
208 if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
210 g_free (start_group);
211 return NULL;
213 g_free (start_group);
215 type = g_key_file_get_string (key_file,
216 G_KEY_FILE_DESKTOP_GROUP,
217 G_KEY_FILE_DESKTOP_KEY_TYPE,
218 NULL);
219 if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
221 g_free (type);
222 return NULL;
224 g_free (type);
226 try_exec = g_key_file_get_string (key_file,
227 G_KEY_FILE_DESKTOP_GROUP,
228 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
229 NULL);
230 if (try_exec && try_exec[0] != '\0')
232 char *t;
233 t = g_find_program_in_path (try_exec);
234 if (t == NULL)
236 g_free (try_exec);
237 return NULL;
239 g_free (t);
242 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
243 info->filename = NULL;
245 info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
246 info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
247 info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
248 info->icon_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
249 info->only_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL, NULL);
250 info->not_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL, NULL);
251 info->try_exec = try_exec;
252 info->exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
253 info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
254 info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
255 info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
256 info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
258 info->icon = NULL;
259 if (info->icon_name)
261 if (g_path_is_absolute (info->icon_name))
263 GFile *file;
265 file = g_file_new_for_path (info->icon_name);
266 info->icon = g_file_icon_new (file);
267 g_object_unref (file);
269 else
270 info->icon = g_themed_icon_new (info->icon_name);
273 if (info->exec)
274 info->binary = binary_from_exec (info->exec);
276 if (info->path && info->path[0] == '\0')
278 g_free (info->path);
279 info->path = NULL;
282 return info;
286 * g_desktop_app_info_new_from_filename:
287 * @filename: the path of a desktop file, in the GLib filename encoding
289 * Creates a new #GDesktopAppInfo.
291 * Returns: a new #GDesktopAppInfo or %NULL on error.
293 GDesktopAppInfo *
294 g_desktop_app_info_new_from_filename (const char *filename)
296 GKeyFile *key_file;
297 GDesktopAppInfo *info = NULL;
299 key_file = g_key_file_new ();
301 if (g_key_file_load_from_file (key_file,
302 filename,
303 G_KEY_FILE_NONE,
304 NULL))
306 info = g_desktop_app_info_new_from_keyfile (key_file);
307 if (info)
308 info->filename = g_strdup (filename);
311 g_key_file_free (key_file);
313 return info;
317 * g_desktop_app_info_new:
318 * @desktop_id: the desktop file id
320 * Creates a new #GDesktopAppInfo.
322 * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
324 GDesktopAppInfo *
325 g_desktop_app_info_new (const char *desktop_id)
327 GDesktopAppInfo *appinfo;
328 const char * const *dirs;
329 char *basename;
330 int i;
332 dirs = get_applications_search_path ();
334 basename = g_strdup (desktop_id);
336 for (i = 0; dirs[i] != NULL; i++)
338 char *filename;
339 char *p;
341 filename = g_build_filename (dirs[i], desktop_id, NULL);
342 appinfo = g_desktop_app_info_new_from_filename (filename);
343 g_free (filename);
344 if (appinfo != NULL)
345 goto found;
347 p = basename;
348 while ((p = strchr (p, '-')) != NULL)
350 *p = '/';
352 filename = g_build_filename (dirs[i], basename, NULL);
353 appinfo = g_desktop_app_info_new_from_filename (filename);
354 g_free (filename);
355 if (appinfo != NULL)
356 goto found;
357 *p = '-';
358 p++;
362 g_free (basename);
363 return NULL;
365 found:
366 g_free (basename);
368 appinfo->desktop_id = g_strdup (desktop_id);
370 if (g_desktop_app_info_get_is_hidden (appinfo))
372 g_object_unref (appinfo);
373 appinfo = NULL;
376 return appinfo;
379 static GAppInfo *
380 g_desktop_app_info_dup (GAppInfo *appinfo)
382 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
383 GDesktopAppInfo *new_info;
385 new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
387 new_info->filename = g_strdup (info->filename);
388 new_info->desktop_id = g_strdup (info->desktop_id);
390 new_info->name = g_strdup (info->name);
391 new_info->comment = g_strdup (info->comment);
392 new_info->nodisplay = info->nodisplay;
393 new_info->icon_name = g_strdup (info->icon_name);
394 new_info->icon = g_object_ref (info->icon);
395 new_info->only_show_in = g_strdupv (info->only_show_in);
396 new_info->not_show_in = g_strdupv (info->not_show_in);
397 new_info->try_exec = g_strdup (info->try_exec);
398 new_info->exec = g_strdup (info->exec);
399 new_info->binary = g_strdup (info->binary);
400 new_info->path = g_strdup (info->path);
401 new_info->hidden = info->hidden;
402 new_info->terminal = info->terminal;
403 new_info->startup_notify = info->startup_notify;
405 return G_APP_INFO (new_info);
408 static gboolean
409 g_desktop_app_info_equal (GAppInfo *appinfo1,
410 GAppInfo *appinfo2)
412 GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
413 GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
415 if (info1->desktop_id == NULL ||
416 info2->desktop_id == NULL)
417 return info1 == info2;
419 return strcmp (info1->desktop_id, info2->desktop_id) == 0;
422 static const char *
423 g_desktop_app_info_get_id (GAppInfo *appinfo)
425 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
427 return info->desktop_id;
430 static const char *
431 g_desktop_app_info_get_name (GAppInfo *appinfo)
433 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
435 if (info->name == NULL)
436 return _("Unnamed");
437 return info->name;
441 * g_desktop_app_info_get_is_hidden:
442 * @info: a #GDesktopAppInfo.
444 * A desktop file is hidden if the Hidden key in it is
445 * set to True.
447 * Returns: %TRUE if hidden, %FALSE otherwise.
449 gboolean
450 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
452 return info->hidden;
455 static const char *
456 g_desktop_app_info_get_description (GAppInfo *appinfo)
458 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
460 return info->comment;
463 static const char *
464 g_desktop_app_info_get_executable (GAppInfo *appinfo)
466 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
468 return info->binary;
471 static GIcon *
472 g_desktop_app_info_get_icon (GAppInfo *appinfo)
474 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
476 return info->icon;
479 static char *
480 expand_macro_single (char macro, char *uri)
482 GFile *file;
483 char *result = NULL;
484 char *path, *name;
486 file = g_file_new_for_uri (uri);
487 path = g_file_get_path (file);
488 g_object_unref (file);
490 switch (macro)
492 case 'u':
493 case 'U':
494 result = g_shell_quote (uri);
495 break;
496 case 'f':
497 case 'F':
498 if (path)
499 result = g_shell_quote (path);
500 break;
501 case 'd':
502 case 'D':
503 if (path)
505 name = g_path_get_dirname (path);
506 result = g_shell_quote (name);
507 g_free (name);
509 break;
510 case 'n':
511 case 'N':
512 if (path)
514 name = g_path_get_basename (path);
515 result = g_shell_quote (name);
516 g_free (name);
518 break;
521 g_free (path);
523 return result;
526 static void
527 expand_macro (char macro,
528 GString *exec,
529 GDesktopAppInfo *info,
530 GList **uri_list)
532 GList *uris = *uri_list;
533 char *expanded;
534 gboolean force_file_uri;
535 char force_file_uri_macro;
537 g_return_if_fail (exec != NULL);
539 /* On %u and %U, pass POSIX file path pointing to the URI via
540 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
541 * running or the URI doesn't have a POSIX file path via FUSE
542 * we'll just pass the URI.
544 switch (macro)
546 case 'u':
547 force_file_uri_macro = 'f';
548 force_file_uri = TRUE;
549 break;
550 case 'U':
551 force_file_uri_macro = 'F';
552 force_file_uri = TRUE;
553 break;
554 default:
555 force_file_uri_macro = macro;
556 force_file_uri = FALSE;
557 break;
560 switch (macro)
562 case 'u':
563 case 'f':
564 case 'd':
565 case 'n':
566 if (uris)
568 if (!force_file_uri)
570 expanded = expand_macro_single (macro, uris->data);
572 else
574 expanded = expand_macro_single (force_file_uri_macro, uris->data);
575 if (expanded == NULL)
576 expanded = expand_macro_single (macro, uris->data);
579 if (expanded)
581 g_string_append (exec, expanded);
582 g_free (expanded);
584 uris = uris->next;
587 break;
589 case 'U':
590 case 'F':
591 case 'D':
592 case 'N':
593 while (uris)
595 if (!force_file_uri)
597 expanded = expand_macro_single (macro, uris->data);
599 else
601 expanded = expand_macro_single (force_file_uri_macro, uris->data);
602 if (expanded == NULL)
603 expanded = expand_macro_single (macro, uris->data);
606 if (expanded)
608 g_string_append (exec, expanded);
609 g_free (expanded);
612 uris = uris->next;
614 if (uris != NULL && expanded)
615 g_string_append_c (exec, ' ');
618 break;
620 case 'i':
621 if (info->icon_name)
623 g_string_append (exec, "--icon ");
624 g_string_append (exec, info->icon_name);
626 break;
628 case 'c':
629 if (info->name)
630 g_string_append (exec, info->name);
631 break;
633 case 'k':
634 if (info->filename)
635 g_string_append (exec, info->filename);
636 break;
638 case 'm': /* deprecated */
639 break;
641 case '%':
642 g_string_append_c (exec, '%');
643 break;
646 *uri_list = uris;
649 static gboolean
650 expand_application_parameters (GDesktopAppInfo *info,
651 GList **uris,
652 int *argc,
653 char ***argv,
654 GError **error)
656 GList *uri_list = *uris;
657 const char *p = info->exec;
658 GString *expanded_exec = g_string_new (NULL);
659 gboolean res;
661 if (info->exec == NULL)
663 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
664 _("Desktop file didn't specify Exec field"));
665 return FALSE;
668 while (*p)
670 if (p[0] == '%' && p[1] != '\0')
672 expand_macro (p[1], expanded_exec, info, uris);
673 p++;
675 else
676 g_string_append_c (expanded_exec, *p);
678 p++;
681 /* No file substitutions */
682 if (uri_list == *uris && uri_list != NULL)
684 /* If there is no macro default to %f. This is also what KDE does */
685 g_string_append_c (expanded_exec, ' ');
686 expand_macro ('f', expanded_exec, info, uris);
689 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
690 g_string_free (expanded_exec, TRUE);
691 return res;
694 static gboolean
695 prepend_terminal_to_vector (int *argc,
696 char ***argv)
698 #ifndef G_OS_WIN32
699 char **real_argv;
700 int real_argc;
701 int i, j;
702 char **term_argv = NULL;
703 int term_argc = 0;
704 char *check;
705 char **the_argv;
707 g_return_val_if_fail (argc != NULL, FALSE);
708 g_return_val_if_fail (argv != NULL, FALSE);
710 /* sanity */
711 if(*argv == NULL)
712 *argc = 0;
714 the_argv = *argv;
716 /* compute size if not given */
717 if (*argc < 0)
719 for (i = 0; the_argv[i] != NULL; i++)
721 *argc = i;
724 term_argc = 2;
725 term_argv = g_new0 (char *, 3);
727 check = g_find_program_in_path ("gnome-terminal");
728 if (check != NULL)
730 term_argv[0] = check;
731 /* Note that gnome-terminal takes -x and
732 * as -e in gnome-terminal is broken we use that. */
733 term_argv[1] = g_strdup ("-x");
735 else
737 if (check == NULL)
738 check = g_find_program_in_path ("nxterm");
739 if (check == NULL)
740 check = g_find_program_in_path ("color-xterm");
741 if (check == NULL)
742 check = g_find_program_in_path ("rxvt");
743 if (check == NULL)
744 check = g_find_program_in_path ("xterm");
745 if (check == NULL)
746 check = g_find_program_in_path ("dtterm");
747 if (check == NULL)
749 check = g_strdup ("xterm");
750 g_warning ("couldn't find a terminal, falling back to xterm");
752 term_argv[0] = check;
753 term_argv[1] = g_strdup ("-e");
756 real_argc = term_argc + *argc;
757 real_argv = g_new (char *, real_argc + 1);
759 for (i = 0; i < term_argc; i++)
760 real_argv[i] = term_argv[i];
762 for (j = 0; j < *argc; j++, i++)
763 real_argv[i] = (char *)the_argv[j];
765 real_argv[i] = NULL;
767 g_free (*argv);
768 *argv = real_argv;
769 *argc = real_argc;
771 /* we use g_free here as we sucked all the inner strings
772 * out from it into real_argv */
773 g_free (term_argv);
774 return TRUE;
775 #else
776 return FALSE;
777 #endif /* G_OS_WIN32 */
780 /* '=' is the new '\0'.
781 * DO NOT CALL unless at least one string ends with '='
783 static gboolean
784 is_env (const char *a,
785 const char *b)
787 while (*a == *b)
789 if (*a == 0 || *b == 0)
790 return FALSE;
792 if (*a == '=')
793 return TRUE;
795 a++;
796 b++;
799 return FALSE;
802 /* free with g_strfreev */
803 static char **
804 replace_env_var (char **old_environ,
805 const char *env_var,
806 const char *new_value)
808 int length, new_length;
809 int index, new_index;
810 char **new_environ;
811 int i, new_i;
813 /* do two things at once:
814 * - discover the length of the environment ('length')
815 * - find the location (if any) of the env var ('index')
817 index = -1;
818 for (length = 0; old_environ[length]; length++)
820 /* if we already have it in our environment, replace */
821 if (is_env (old_environ[length], env_var))
822 index = length;
826 /* no current env var, no desired env value.
827 * this is easy :)
829 if (new_value == NULL && index == -1)
830 return old_environ;
832 /* in all cases now, we will be using a modified environment.
833 * determine its length and allocated it.
835 * after this block:
836 * new_index = location to insert, if any
837 * new_length = length of the new array
838 * new_environ = the pointer array for the new environment
841 if (new_value == NULL && index >= 0)
843 /* in this case, we will be removing an entry */
844 new_length = length - 1;
845 new_index = -1;
847 else if (new_value != NULL && index < 0)
849 /* in this case, we will be adding an entry to the end */
850 new_length = length + 1;
851 new_index = length;
853 else
854 /* in this case, we will be replacing the existing entry */
856 new_length = length;
857 new_index = index;
860 new_environ = g_malloc (sizeof (char *) * (new_length + 1));
861 new_environ[new_length] = NULL;
863 /* now we do the copying.
864 * for each entry in the new environment, we decide what to do
867 i = 0;
868 for (new_i = 0; new_i < new_length; new_i++)
870 if (new_i == new_index)
872 /* insert our new item */
873 new_environ[new_i] = g_strconcat (env_var,
874 "=",
875 new_value,
876 NULL);
878 /* if we had an old entry, skip it now */
879 if (index >= 0)
880 i++;
882 else
884 /* if this is the old DESKTOP_STARTUP_ID, skip it */
885 if (i == index)
886 i++;
888 /* copy an old item */
889 new_environ[new_i] = g_strdup (old_environ[i]);
890 i++;
894 g_strfreev (old_environ);
896 return new_environ;
899 static GList *
900 uri_list_segment_to_files (GList *start,
901 GList *end)
903 GList *res;
904 GFile *file;
906 res = NULL;
907 while (start != NULL && start != end)
909 file = g_file_new_for_uri ((char *)start->data);
910 res = g_list_prepend (res, file);
911 start = start->next;
914 return g_list_reverse (res);
917 #ifdef HAVE__NSGETENVIRON
918 #define environ (*_NSGetEnviron())
919 #elif !defined(G_OS_WIN32)
921 /* According to the Single Unix Specification, environ is not in
922 * * any system header, although unistd.h often declares it.
923 * */
924 extern char **environ;
925 #endif
927 static gboolean
928 g_desktop_app_info_launch_uris (GAppInfo *appinfo,
929 GList *uris,
930 GAppLaunchContext *launch_context,
931 GError **error)
933 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
934 gboolean completed = FALSE;
935 GList *old_uris;
936 GList *launched_files;
937 char **envp;
938 char **argv;
939 int argc;
940 char *display;
941 char *sn_id;
943 g_return_val_if_fail (appinfo != NULL, FALSE);
945 argv = NULL;
946 envp = NULL;
950 old_uris = uris;
951 if (!expand_application_parameters (info, &uris,
952 &argc, &argv, error))
953 goto out;
955 if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
957 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
958 _("Unable to find terminal required for application"));
959 goto out;
962 sn_id = NULL;
963 if (launch_context)
965 launched_files = uri_list_segment_to_files (old_uris, uris);
967 display = g_app_launch_context_get_display (launch_context,
968 appinfo,
969 launched_files);
971 sn_id = NULL;
972 if (info->startup_notify)
973 sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
974 appinfo,
975 launched_files);
977 if (display || sn_id)
979 #ifdef G_OS_WIN32
980 /* FIXME */
981 envp = g_new0 (char *, 1);
982 #else
983 envp = g_strdupv (environ);
984 #endif
986 if (display)
987 envp = replace_env_var (envp,
988 "DISPLAY",
989 display);
991 if (sn_id)
992 envp = replace_env_var (envp,
993 "DESKTOP_STARTUP_ID",
994 sn_id);
997 g_free (display);
999 g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
1000 g_list_free (launched_files);
1003 if (!g_spawn_async (info->path, /* working directory */
1004 argv,
1005 envp,
1006 G_SPAWN_SEARCH_PATH /* flags */,
1007 NULL /* child_setup */,
1008 NULL /* data */,
1009 NULL /* child_pid */,
1010 error))
1012 if (sn_id)
1014 g_app_launch_context_launch_failed (launch_context, sn_id);
1015 g_free (sn_id);
1017 goto out;
1021 g_free (sn_id);
1023 g_strfreev (envp);
1024 g_strfreev (argv);
1025 envp = NULL;
1026 argv = NULL;
1028 while (uris != NULL);
1030 completed = TRUE;
1032 out:
1033 g_strfreev (argv);
1034 g_strfreev (envp);
1036 return completed;
1039 static gboolean
1040 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
1042 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1044 return info->exec &&
1045 ((strstr (info->exec, "%u") != NULL) ||
1046 (strstr (info->exec, "%U") != NULL));
1049 static gboolean
1050 g_desktop_app_info_supports_files (GAppInfo *appinfo)
1052 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1054 return info->exec &&
1055 ((strstr (info->exec, "%f") != NULL) ||
1056 (strstr (info->exec, "%F") != NULL));
1059 static gboolean
1060 g_desktop_app_info_launch (GAppInfo *appinfo,
1061 GList *files,
1062 GAppLaunchContext *launch_context,
1063 GError **error)
1065 GList *uris;
1066 char *uri;
1067 gboolean res;
1069 uris = NULL;
1070 while (files)
1072 uri = g_file_get_uri (files->data);
1073 uris = g_list_prepend (uris, uri);
1074 files = files->next;
1077 uris = g_list_reverse (uris);
1079 res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
1081 g_list_foreach (uris, (GFunc)g_free, NULL);
1082 g_list_free (uris);
1084 return res;
1087 G_LOCK_DEFINE_STATIC (g_desktop_env);
1088 static gchar *g_desktop_env = NULL;
1091 * g_desktop_app_info_set_desktop_env:
1092 * @desktop_env: a string specifying what desktop this is
1094 * Sets the name of the desktop that the application is running in.
1095 * This is used by g_app_info_should_show() to evaluate the
1096 * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
1097 * desktop entry fields.
1099 * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop
1100 * Menu specification</ulink> recognizes the following:
1101 * <simplelist>
1102 * <member>GNOME</member>
1103 * <member>KDE</member>
1104 * <member>ROX</member>
1105 * <member>XFCE</member>
1106 * <member>Old</member>
1107 * </simplelist>
1109 * Should be called only once; subsequent calls are ignored.
1111 void
1112 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
1114 G_LOCK (g_desktop_env);
1115 if (!g_desktop_env)
1116 g_desktop_env = g_strdup (desktop_env);
1117 G_UNLOCK (g_desktop_env);
1120 static gboolean
1121 g_desktop_app_info_should_show (GAppInfo *appinfo)
1123 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1124 gboolean found;
1125 const gchar *desktop_env;
1126 int i;
1128 if (info->nodisplay)
1129 return FALSE;
1131 G_LOCK (g_desktop_env);
1132 desktop_env = g_desktop_env;
1133 G_UNLOCK (g_desktop_env);
1135 if (info->only_show_in)
1137 if (desktop_env == NULL)
1138 return FALSE;
1140 found = FALSE;
1141 for (i = 0; info->only_show_in[i] != NULL; i++)
1143 if (strcmp (info->only_show_in[i], desktop_env) == 0)
1145 found = TRUE;
1146 break;
1149 if (!found)
1150 return FALSE;
1153 if (info->not_show_in && desktop_env)
1155 for (i = 0; info->not_show_in[i] != NULL; i++)
1157 if (strcmp (info->not_show_in[i], desktop_env) == 0)
1158 return FALSE;
1162 return TRUE;
1165 typedef enum {
1166 APP_DIR,
1167 MIMETYPE_DIR
1168 } DirType;
1170 static char *
1171 ensure_dir (DirType type,
1172 GError **error)
1174 char *path, *display_name;
1175 int errsv;
1177 if (type == APP_DIR)
1178 path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1179 else
1180 path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
1182 errno = 0;
1183 if (g_mkdir_with_parents (path, 0700) == 0)
1184 return path;
1186 errsv = errno;
1187 display_name = g_filename_display_name (path);
1188 if (type == APP_DIR)
1189 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1190 _("Can't create user application configuration folder %s: %s"),
1191 display_name, g_strerror (errsv));
1192 else
1193 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1194 _("Can't create user MIME configuration folder %s: %s"),
1195 display_name, g_strerror (errsv));
1197 g_free (display_name);
1198 g_free (path);
1200 return NULL;
1203 static gboolean
1204 update_mimeapps_list (const char *desktop_id,
1205 const char *content_type,
1206 gboolean add_as_default,
1207 gboolean add_non_default,
1208 gboolean remove,
1209 GError **error)
1211 char *dirname, *filename;
1212 GKeyFile *key_file;
1213 gboolean load_succeeded, res;
1214 char **old_list, **list;
1215 GList *system_list, *l;
1216 gsize length, data_size;
1217 char *data;
1218 int i, j, k;
1219 char **content_types;
1221 /* Don't add both at start and end */
1222 g_assert (!(add_as_default && add_non_default));
1224 dirname = ensure_dir (APP_DIR, error);
1225 if (!dirname)
1226 return FALSE;
1228 filename = g_build_filename (dirname, "mimeapps.list", NULL);
1229 g_free (dirname);
1231 key_file = g_key_file_new ();
1232 load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
1233 if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP))
1235 g_key_file_free (key_file);
1236 key_file = g_key_file_new ();
1239 if (content_type)
1241 content_types = g_new (char *, 2);
1242 content_types[0] = g_strdup (content_type);
1243 content_types[1] = NULL;
1245 else
1247 content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
1250 for (k = 0; content_types && content_types[k]; k++)
1252 /* Add to the right place in the list */
1254 length = 0;
1255 old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
1256 content_types[k], &length, NULL);
1258 list = g_new (char *, 1 + length + 1);
1260 i = 0;
1261 if (add_as_default)
1262 list[i++] = g_strdup (desktop_id);
1263 if (old_list)
1265 for (j = 0; old_list[j] != NULL; j++)
1267 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1268 list[i++] = g_strdup (old_list[j]);
1269 else if (add_non_default)
1271 /* If adding as non-default, and its already in,
1272 don't change order of desktop ids */
1273 add_non_default = FALSE;
1274 list[i++] = g_strdup (old_list[j]);
1279 if (add_non_default)
1281 /* We're adding as non-default, and it wasn't already in the list,
1282 so we add at the end. But to avoid listing the app before the
1283 current system default (thus changing the default) we have to
1284 add the current list of (not yet listed) apps before it. */
1286 list[i] = NULL; /* Terminate current list so we can use it */
1287 system_list = get_all_desktop_entries_for_mime_type (content_type, list);
1289 list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1);
1291 for (l = system_list; l != NULL; l = l->next)
1293 list[i++] = l->data; /* no strdup, taking ownership */
1294 if (g_strcmp0 (l->data, desktop_id) == 0)
1295 add_non_default = FALSE;
1297 g_list_free (system_list);
1299 if (add_non_default)
1300 list[i++] = g_strdup (desktop_id);
1303 list[i] = NULL;
1305 g_strfreev (old_list);
1307 if (list[0] == NULL || desktop_id == NULL)
1308 g_key_file_remove_key (key_file,
1309 ADDED_ASSOCIATIONS_GROUP,
1310 content_types[k],
1311 NULL);
1312 else
1313 g_key_file_set_string_list (key_file,
1314 ADDED_ASSOCIATIONS_GROUP,
1315 content_types[k],
1316 (const char * const *)list, i);
1318 g_strfreev (list);
1321 if (content_type)
1323 /* reuse the list from above */
1325 else
1327 g_strfreev (content_types);
1328 content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
1331 for (k = 0; content_types && content_types[k]; k++)
1333 /* Remove from removed associations group (unless remove) */
1335 length = 0;
1336 old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
1337 content_types[k], &length, NULL);
1339 list = g_new (char *, 1 + length + 1);
1341 i = 0;
1342 if (remove)
1343 list[i++] = g_strdup (desktop_id);
1344 if (old_list)
1346 for (j = 0; old_list[j] != NULL; j++)
1348 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1349 list[i++] = g_strdup (old_list[j]);
1352 list[i] = NULL;
1354 g_strfreev (old_list);
1356 if (list[0] == NULL || desktop_id == NULL)
1357 g_key_file_remove_key (key_file,
1358 REMOVED_ASSOCIATIONS_GROUP,
1359 content_types[k],
1360 NULL);
1361 else
1362 g_key_file_set_string_list (key_file,
1363 REMOVED_ASSOCIATIONS_GROUP,
1364 content_types[k],
1365 (const char * const *)list, i);
1367 g_strfreev (list);
1370 g_strfreev (content_types);
1372 data = g_key_file_to_data (key_file, &data_size, error);
1373 g_key_file_free (key_file);
1375 res = g_file_set_contents (filename, data, data_size, error);
1377 mime_info_cache_reload (NULL);
1379 g_free (filename);
1380 g_free (data);
1382 return res;
1385 static gboolean
1386 g_desktop_app_info_set_as_default_for_type (GAppInfo *appinfo,
1387 const char *content_type,
1388 GError **error)
1390 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1392 if (!g_desktop_app_info_ensure_saved (info, error))
1393 return FALSE;
1395 return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error);
1398 static void
1399 update_program_done (GPid pid,
1400 gint status,
1401 gpointer data)
1403 /* Did the application exit correctly */
1404 if (WIFEXITED (status) &&
1405 WEXITSTATUS (status) == 0)
1407 /* Here we could clean out any caches in use */
1411 static void
1412 run_update_command (char *command,
1413 char *subdir)
1415 char *argv[3] = {
1416 NULL,
1417 NULL,
1418 NULL,
1420 GPid pid = 0;
1421 GError *error = NULL;
1423 argv[0] = command;
1424 argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
1426 if (g_spawn_async ("/", argv,
1427 NULL, /* envp */
1428 G_SPAWN_SEARCH_PATH |
1429 G_SPAWN_STDOUT_TO_DEV_NULL |
1430 G_SPAWN_STDERR_TO_DEV_NULL |
1431 G_SPAWN_DO_NOT_REAP_CHILD,
1432 NULL, NULL, /* No setup function */
1433 &pid,
1434 &error))
1435 g_child_watch_add (pid, update_program_done, NULL);
1436 else
1438 /* If we get an error at this point, it's quite likely the user doesn't
1439 * have an installed copy of either 'update-mime-database' or
1440 * 'update-desktop-database'. I don't think we want to popup an error
1441 * dialog at this point, so we just do a g_warning to give the user a
1442 * chance of debugging it.
1444 g_warning ("%s", error->message);
1447 g_free (argv[1]);
1450 static gboolean
1451 g_desktop_app_info_set_as_default_for_extension (GAppInfo *appinfo,
1452 const char *extension,
1453 GError **error)
1455 char *filename, *basename, *mimetype;
1456 char *dirname;
1457 gboolean res;
1459 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
1460 return FALSE;
1462 dirname = ensure_dir (MIMETYPE_DIR, error);
1463 if (!dirname)
1464 return FALSE;
1466 basename = g_strdup_printf ("user-extension-%s.xml", extension);
1467 filename = g_build_filename (dirname, basename, NULL);
1468 g_free (basename);
1469 g_free (dirname);
1471 mimetype = g_strdup_printf ("application/x-extension-%s", extension);
1473 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1475 char *contents;
1477 contents =
1478 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1479 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
1480 " <mime-type type=\"%s\">\n"
1481 " <comment>%s document</comment>\n"
1482 " <glob pattern=\"*.%s\"/>\n"
1483 " </mime-type>\n"
1484 "</mime-info>\n", mimetype, extension, extension);
1486 g_file_set_contents (filename, contents, -1, NULL);
1487 g_free (contents);
1489 run_update_command ("update-mime-database", "mime");
1491 g_free (filename);
1493 res = g_desktop_app_info_set_as_default_for_type (appinfo,
1494 mimetype,
1495 error);
1497 g_free (mimetype);
1499 return res;
1502 static gboolean
1503 g_desktop_app_info_add_supports_type (GAppInfo *appinfo,
1504 const char *content_type,
1505 GError **error)
1507 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1509 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1510 return FALSE;
1512 return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error);
1515 static gboolean
1516 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
1518 return TRUE;
1521 static gboolean
1522 g_desktop_app_info_remove_supports_type (GAppInfo *appinfo,
1523 const char *content_type,
1524 GError **error)
1526 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1528 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1529 return FALSE;
1531 return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error);
1534 static gboolean
1535 g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
1536 GError **error)
1538 GKeyFile *key_file;
1539 char *dirname;
1540 char *filename;
1541 char *data, *desktop_id;
1542 gsize data_size;
1543 int fd;
1544 gboolean res;
1546 if (info->filename != NULL)
1547 return TRUE;
1549 /* This is only used for object created with
1550 * g_app_info_create_from_commandline. All other
1551 * object should have a filename
1554 dirname = ensure_dir (APP_DIR, error);
1555 if (!dirname)
1556 return FALSE;
1558 key_file = g_key_file_new ();
1560 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1561 "Encoding", "UTF-8");
1562 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1563 G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
1564 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1565 G_KEY_FILE_DESKTOP_KEY_TYPE,
1566 G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
1567 if (info->terminal)
1568 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1569 G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
1571 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1572 G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
1574 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1575 G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
1577 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1578 G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
1580 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1581 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
1583 data = g_key_file_to_data (key_file, &data_size, NULL);
1584 g_key_file_free (key_file);
1586 desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
1587 filename = g_build_filename (dirname, desktop_id, NULL);
1588 g_free (desktop_id);
1589 g_free (dirname);
1591 fd = g_mkstemp (filename);
1592 if (fd == -1)
1594 char *display_name;
1596 display_name = g_filename_display_name (filename);
1597 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1598 _("Can't create user desktop file %s"), display_name);
1599 g_free (display_name);
1600 g_free (filename);
1601 g_free (data);
1602 return FALSE;
1605 desktop_id = g_path_get_basename (filename);
1607 close (fd);
1609 res = g_file_set_contents (filename, data, data_size, error);
1610 if (!res)
1612 g_free (desktop_id);
1613 g_free (filename);
1614 return FALSE;
1617 info->filename = filename;
1618 info->desktop_id = desktop_id;
1620 run_update_command ("update-desktop-database", "applications");
1622 return TRUE;
1625 static gboolean
1626 g_desktop_app_info_can_delete (GAppInfo *appinfo)
1628 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1630 if (info->filename)
1631 return g_access (info->filename, W_OK) == 0;
1633 return FALSE;
1636 static gboolean
1637 g_desktop_app_info_delete (GAppInfo *appinfo)
1639 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1641 if (info->filename)
1643 if (g_remove (info->filename) == 0)
1645 update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
1647 g_free (info->filename);
1648 info->filename = NULL;
1649 g_free (info->desktop_id);
1650 info->desktop_id = NULL;
1652 return TRUE;
1656 return FALSE;
1660 * g_app_info_create_from_commandline:
1661 * @commandline: the commandline to use
1662 * @application_name: the application name, or %NULL to use @commandline
1663 * @flags: flags that can specify details of the created #GAppInfo
1664 * @error: a #GError location to store the error occuring, %NULL to ignore.
1666 * Creates a new #GAppInfo from the given information.
1668 * Returns: new #GAppInfo for given command.
1670 GAppInfo *
1671 g_app_info_create_from_commandline (const char *commandline,
1672 const char *application_name,
1673 GAppInfoCreateFlags flags,
1674 GError **error)
1676 char **split;
1677 char *basename;
1678 GDesktopAppInfo *info;
1680 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1682 info->filename = NULL;
1683 info->desktop_id = NULL;
1685 info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
1686 info->startup_notify = FALSE;
1687 info->hidden = FALSE;
1688 if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
1689 info->exec = g_strconcat (commandline, " %u", NULL);
1690 else
1691 info->exec = g_strconcat (commandline, " %f", NULL);
1692 info->nodisplay = TRUE;
1693 info->binary = binary_from_exec (info->exec);
1695 if (application_name)
1696 info->name = g_strdup (application_name);
1697 else
1699 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1700 split = g_strsplit (commandline, " ", 2);
1701 basename = g_path_get_basename (split[0]);
1702 g_strfreev (split);
1703 info->name = basename;
1704 if (info->name == NULL)
1705 info->name = g_strdup ("custom");
1707 info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
1709 return G_APP_INFO (info);
1712 static void
1713 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1715 iface->dup = g_desktop_app_info_dup;
1716 iface->equal = g_desktop_app_info_equal;
1717 iface->get_id = g_desktop_app_info_get_id;
1718 iface->get_name = g_desktop_app_info_get_name;
1719 iface->get_description = g_desktop_app_info_get_description;
1720 iface->get_executable = g_desktop_app_info_get_executable;
1721 iface->get_icon = g_desktop_app_info_get_icon;
1722 iface->launch = g_desktop_app_info_launch;
1723 iface->supports_uris = g_desktop_app_info_supports_uris;
1724 iface->supports_files = g_desktop_app_info_supports_files;
1725 iface->launch_uris = g_desktop_app_info_launch_uris;
1726 iface->should_show = g_desktop_app_info_should_show;
1727 iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1728 iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1729 iface->add_supports_type = g_desktop_app_info_add_supports_type;
1730 iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1731 iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1732 iface->can_delete = g_desktop_app_info_can_delete;
1733 iface->do_delete = g_desktop_app_info_delete;
1736 static gboolean
1737 app_info_in_list (GAppInfo *info,
1738 GList *list)
1740 while (list != NULL)
1742 if (g_app_info_equal (info, list->data))
1743 return TRUE;
1744 list = list->next;
1746 return FALSE;
1751 * g_app_info_get_all_for_type:
1752 * @content_type: the content type to find a #GAppInfo for
1754 * Gets a list of all #GAppInfo s for a given content type.
1756 * Returns: #GList of #GAppInfo s for given @content_type
1757 * or %NULL on error.
1759 GList *
1760 g_app_info_get_all_for_type (const char *content_type)
1762 GList *desktop_entries, *l;
1763 GList *infos;
1764 GDesktopAppInfo *info;
1766 g_return_val_if_fail (content_type != NULL, NULL);
1768 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1770 infos = NULL;
1771 for (l = desktop_entries; l != NULL; l = l->next)
1773 char *desktop_entry = l->data;
1775 info = g_desktop_app_info_new (desktop_entry);
1776 if (info)
1778 if (app_info_in_list (G_APP_INFO (info), infos))
1779 g_object_unref (info);
1780 else
1781 infos = g_list_prepend (infos, info);
1783 g_free (desktop_entry);
1786 g_list_free (desktop_entries);
1788 return g_list_reverse (infos);
1792 * g_app_info_reset_type_associations:
1793 * @content_type: a content type
1795 * Removes all changes to the type associations done by
1796 * g_app_info_set_as_default_for_type(),
1797 * g_app_info_set_as_default_for_extension(),
1798 * g_app_info_add_supports_type() of g_app_info_remove_supports_type().
1800 * Since: 2.20
1802 void
1803 g_app_info_reset_type_associations (const char *content_type)
1805 update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
1809 * g_app_info_get_default_for_type:
1810 * @content_type: the content type to find a #GAppInfo for
1811 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1812 * support URIs
1814 * Gets the #GAppInfo that correspond to a given content type.
1816 * Returns: #GAppInfo for given @content_type or %NULL on error.
1818 GAppInfo *
1819 g_app_info_get_default_for_type (const char *content_type,
1820 gboolean must_support_uris)
1822 GList *desktop_entries, *l;
1823 GAppInfo *info;
1825 g_return_val_if_fail (content_type != NULL, NULL);
1827 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1829 info = NULL;
1830 for (l = desktop_entries; l != NULL; l = l->next)
1832 char *desktop_entry = l->data;
1834 info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1835 if (info)
1837 if (must_support_uris && !g_app_info_supports_uris (info))
1839 g_object_unref (info);
1840 info = NULL;
1842 else
1843 break;
1847 g_list_foreach (desktop_entries, (GFunc)g_free, NULL);
1848 g_list_free (desktop_entries);
1850 return info;
1854 * g_app_info_get_default_for_uri_scheme:
1855 * @uri_scheme: a string containing a URI scheme.
1857 * Gets the default application for launching applications
1858 * using this URI scheme. A URI scheme is the initial part
1859 * of the URI, up to but not including the ':', e.g. "http",
1860 * "ftp" or "sip".
1862 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
1864 GAppInfo *
1865 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1867 static gsize lookup = 0;
1869 if (g_once_init_enter (&lookup))
1871 gsize setup_value = 1;
1872 GDesktopAppInfoLookup *lookup_instance;
1873 const char *use_this;
1874 GIOExtensionPoint *ep;
1875 GIOExtension *extension;
1876 GList *l;
1878 use_this = g_getenv ("GIO_USE_URI_ASSOCIATION");
1880 /* Ensure vfs in modules loaded */
1881 _g_io_modules_ensure_loaded ();
1883 ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
1885 lookup_instance = NULL;
1886 if (use_this)
1888 extension = g_io_extension_point_get_extension_by_name (ep, use_this);
1889 if (extension)
1890 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1893 if (lookup_instance == NULL)
1895 for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
1897 extension = l->data;
1898 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1899 if (lookup_instance != NULL)
1900 break;
1904 if (lookup_instance != NULL)
1905 setup_value = (gsize)lookup_instance;
1907 g_once_init_leave (&lookup, setup_value);
1910 if (lookup == 1)
1911 return NULL;
1913 return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup),
1914 uri_scheme);
1918 static void
1919 get_apps_from_dir (GHashTable *apps,
1920 const char *dirname,
1921 const char *prefix)
1923 GDir *dir;
1924 const char *basename;
1925 char *filename, *subprefix, *desktop_id;
1926 gboolean hidden;
1927 GDesktopAppInfo *appinfo;
1929 dir = g_dir_open (dirname, 0, NULL);
1930 if (dir)
1932 while ((basename = g_dir_read_name (dir)) != NULL)
1934 filename = g_build_filename (dirname, basename, NULL);
1935 if (g_str_has_suffix (basename, ".desktop"))
1937 desktop_id = g_strconcat (prefix, basename, NULL);
1939 /* Use _extended so we catch NULLs too (hidden) */
1940 if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
1942 appinfo = g_desktop_app_info_new_from_filename (filename);
1944 if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1946 g_object_unref (appinfo);
1947 appinfo = NULL;
1948 hidden = TRUE;
1951 if (appinfo || hidden)
1953 g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
1955 if (appinfo)
1957 /* Reuse instead of strdup here */
1958 appinfo->desktop_id = desktop_id;
1959 desktop_id = NULL;
1963 g_free (desktop_id);
1965 else
1967 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1969 subprefix = g_strconcat (prefix, basename, "-", NULL);
1970 get_apps_from_dir (apps, filename, subprefix);
1971 g_free (subprefix);
1974 g_free (filename);
1976 g_dir_close (dir);
1982 * g_app_info_get_all:
1984 * Gets a list of all of the applications currently registered
1985 * on this system.
1987 * For desktop files, this includes applications that have
1988 * <literal>NoDisplay=true</literal> set or are excluded from
1989 * display by means of <literal>OnlyShowIn</literal> or
1990 * <literal>NotShowIn</literal>. See g_app_info_should_show().
1991 * The returned list does not include applications which have
1992 * the <literal>Hidden</literal> key set.
1994 * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
1996 GList *
1997 g_app_info_get_all (void)
1999 const char * const *dirs;
2000 GHashTable *apps;
2001 GHashTableIter iter;
2002 gpointer value;
2003 int i;
2004 GList *infos;
2006 dirs = get_applications_search_path ();
2008 apps = g_hash_table_new_full (g_str_hash, g_str_equal,
2009 g_free, NULL);
2012 for (i = 0; dirs[i] != NULL; i++)
2013 get_apps_from_dir (apps, dirs[i], "");
2016 infos = NULL;
2017 g_hash_table_iter_init (&iter, apps);
2018 while (g_hash_table_iter_next (&iter, NULL, &value))
2020 if (value)
2021 infos = g_list_prepend (infos, value);
2024 g_hash_table_destroy (apps);
2026 return g_list_reverse (infos);
2029 /* Cacheing of mimeinfo.cache and defaults.list files */
2031 typedef struct {
2032 char *path;
2033 GHashTable *mime_info_cache_map;
2034 GHashTable *defaults_list_map;
2035 GHashTable *mimeapps_list_added_map;
2036 GHashTable *mimeapps_list_removed_map;
2037 time_t mime_info_cache_timestamp;
2038 time_t defaults_list_timestamp;
2039 time_t mimeapps_list_timestamp;
2040 } MimeInfoCacheDir;
2042 typedef struct {
2043 GList *dirs; /* mimeinfo.cache and defaults.list */
2044 GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
2045 time_t last_stat_time;
2046 guint should_ping_mime_monitor : 1;
2047 } MimeInfoCache;
2049 static MimeInfoCache *mime_info_cache = NULL;
2050 G_LOCK_DEFINE_STATIC (mime_info_cache);
2052 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2053 const char *mime_type,
2054 char **new_desktop_file_ids);
2056 static MimeInfoCache * mime_info_cache_new (void);
2058 static void
2059 destroy_info_cache_value (gpointer key,
2060 GList *value,
2061 gpointer data)
2063 g_list_foreach (value, (GFunc)g_free, NULL);
2064 g_list_free (value);
2067 static void
2068 destroy_info_cache_map (GHashTable *info_cache_map)
2070 g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
2071 g_hash_table_destroy (info_cache_map);
2074 static gboolean
2075 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
2076 const char *cache_file,
2077 time_t *timestamp)
2079 struct stat buf;
2080 char *filename;
2082 filename = g_build_filename (dir->path, cache_file, NULL);
2084 if (g_stat (filename, &buf) < 0)
2086 g_free (filename);
2087 return TRUE;
2089 g_free (filename);
2091 if (buf.st_mtime != *timestamp)
2092 return TRUE;
2094 return FALSE;
2097 /* Call with lock held */
2098 static gboolean
2099 remove_all (gpointer key,
2100 gpointer value,
2101 gpointer user_data)
2103 return TRUE;
2107 static void
2108 mime_info_cache_blow_global_cache (void)
2110 g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
2111 remove_all, NULL);
2114 static void
2115 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
2117 GError *load_error;
2118 GKeyFile *key_file;
2119 gchar *filename, **mime_types;
2120 int i;
2121 struct stat buf;
2123 load_error = NULL;
2124 mime_types = NULL;
2126 if (dir->mime_info_cache_map != NULL &&
2127 !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
2128 &dir->mime_info_cache_timestamp))
2129 return;
2131 if (dir->mime_info_cache_map != NULL)
2132 destroy_info_cache_map (dir->mime_info_cache_map);
2134 dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2135 (GDestroyNotify) g_free,
2136 NULL);
2138 key_file = g_key_file_new ();
2140 filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
2142 if (g_stat (filename, &buf) < 0)
2143 goto error;
2145 if (dir->mime_info_cache_timestamp > 0)
2146 mime_info_cache->should_ping_mime_monitor = TRUE;
2148 dir->mime_info_cache_timestamp = buf.st_mtime;
2150 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2152 g_free (filename);
2153 filename = NULL;
2155 if (load_error != NULL)
2156 goto error;
2158 mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
2159 NULL, &load_error);
2161 if (load_error != NULL)
2162 goto error;
2164 for (i = 0; mime_types[i] != NULL; i++)
2166 gchar **desktop_file_ids;
2167 char *unaliased_type;
2168 desktop_file_ids = g_key_file_get_string_list (key_file,
2169 MIME_CACHE_GROUP,
2170 mime_types[i],
2171 NULL,
2172 NULL);
2174 if (desktop_file_ids == NULL)
2175 continue;
2177 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2178 mime_info_cache_dir_add_desktop_entries (dir,
2179 unaliased_type,
2180 desktop_file_ids);
2181 g_free (unaliased_type);
2183 g_strfreev (desktop_file_ids);
2186 g_strfreev (mime_types);
2187 g_key_file_free (key_file);
2189 return;
2190 error:
2191 g_free (filename);
2192 g_key_file_free (key_file);
2194 if (mime_types != NULL)
2195 g_strfreev (mime_types);
2197 if (load_error)
2198 g_error_free (load_error);
2201 static void
2202 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
2204 GKeyFile *key_file;
2205 GError *load_error;
2206 gchar *filename, **mime_types;
2207 char *unaliased_type;
2208 char **desktop_file_ids;
2209 int i;
2210 struct stat buf;
2212 load_error = NULL;
2213 mime_types = NULL;
2215 if (dir->defaults_list_map != NULL &&
2216 !mime_info_cache_dir_out_of_date (dir, "defaults.list",
2217 &dir->defaults_list_timestamp))
2218 return;
2220 if (dir->defaults_list_map != NULL)
2221 g_hash_table_destroy (dir->defaults_list_map);
2222 dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2223 g_free, (GDestroyNotify)g_strfreev);
2226 key_file = g_key_file_new ();
2228 filename = g_build_filename (dir->path, "defaults.list", NULL);
2229 if (g_stat (filename, &buf) < 0)
2230 goto error;
2232 if (dir->defaults_list_timestamp > 0)
2233 mime_info_cache->should_ping_mime_monitor = TRUE;
2235 dir->defaults_list_timestamp = buf.st_mtime;
2237 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2238 g_free (filename);
2239 filename = NULL;
2241 if (load_error != NULL)
2242 goto error;
2244 mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2245 NULL, NULL);
2246 if (mime_types != NULL)
2248 for (i = 0; mime_types[i] != NULL; i++)
2250 desktop_file_ids = g_key_file_get_string_list (key_file,
2251 DEFAULT_APPLICATIONS_GROUP,
2252 mime_types[i],
2253 NULL,
2254 NULL);
2255 if (desktop_file_ids == NULL)
2256 continue;
2258 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2259 g_hash_table_replace (dir->defaults_list_map,
2260 unaliased_type,
2261 desktop_file_ids);
2264 g_strfreev (mime_types);
2267 g_key_file_free (key_file);
2268 return;
2270 error:
2271 g_free (filename);
2272 g_key_file_free (key_file);
2274 if (mime_types != NULL)
2275 g_strfreev (mime_types);
2277 if (load_error)
2278 g_error_free (load_error);
2281 static void
2282 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
2284 GKeyFile *key_file;
2285 GError *load_error;
2286 gchar *filename, **mime_types;
2287 char *unaliased_type;
2288 char **desktop_file_ids;
2289 int i;
2290 struct stat buf;
2292 load_error = NULL;
2293 mime_types = NULL;
2295 if (dir->mimeapps_list_added_map != NULL &&
2296 !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
2297 &dir->mimeapps_list_timestamp))
2298 return;
2300 if (dir->mimeapps_list_added_map != NULL)
2301 g_hash_table_destroy (dir->mimeapps_list_added_map);
2302 dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2303 g_free, (GDestroyNotify)g_strfreev);
2305 if (dir->mimeapps_list_removed_map != NULL)
2306 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2307 dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2308 g_free, (GDestroyNotify)g_strfreev);
2310 key_file = g_key_file_new ();
2312 filename = g_build_filename (dir->path, "mimeapps.list", NULL);
2313 if (g_stat (filename, &buf) < 0)
2314 goto error;
2316 if (dir->mimeapps_list_timestamp > 0)
2317 mime_info_cache->should_ping_mime_monitor = TRUE;
2319 dir->mimeapps_list_timestamp = buf.st_mtime;
2321 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2322 g_free (filename);
2323 filename = NULL;
2325 if (load_error != NULL)
2326 goto error;
2328 mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
2329 NULL, NULL);
2330 if (mime_types != NULL)
2332 for (i = 0; mime_types[i] != NULL; i++)
2334 desktop_file_ids = g_key_file_get_string_list (key_file,
2335 ADDED_ASSOCIATIONS_GROUP,
2336 mime_types[i],
2337 NULL,
2338 NULL);
2339 if (desktop_file_ids == NULL)
2340 continue;
2342 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2343 g_hash_table_replace (dir->mimeapps_list_added_map,
2344 unaliased_type,
2345 desktop_file_ids);
2348 g_strfreev (mime_types);
2351 mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
2352 NULL, NULL);
2353 if (mime_types != NULL)
2355 for (i = 0; mime_types[i] != NULL; i++)
2357 desktop_file_ids = g_key_file_get_string_list (key_file,
2358 REMOVED_ASSOCIATIONS_GROUP,
2359 mime_types[i],
2360 NULL,
2361 NULL);
2362 if (desktop_file_ids == NULL)
2363 continue;
2365 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2366 g_hash_table_replace (dir->mimeapps_list_removed_map,
2367 unaliased_type,
2368 desktop_file_ids);
2371 g_strfreev (mime_types);
2374 g_key_file_free (key_file);
2375 return;
2377 error:
2378 g_free (filename);
2379 g_key_file_free (key_file);
2381 if (mime_types != NULL)
2382 g_strfreev (mime_types);
2384 if (load_error)
2385 g_error_free (load_error);
2388 static MimeInfoCacheDir *
2389 mime_info_cache_dir_new (const char *path)
2391 MimeInfoCacheDir *dir;
2393 dir = g_new0 (MimeInfoCacheDir, 1);
2394 dir->path = g_strdup (path);
2396 return dir;
2399 static void
2400 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2402 if (dir == NULL)
2403 return;
2405 if (dir->mime_info_cache_map != NULL)
2407 destroy_info_cache_map (dir->mime_info_cache_map);
2408 dir->mime_info_cache_map = NULL;
2412 if (dir->defaults_list_map != NULL)
2414 g_hash_table_destroy (dir->defaults_list_map);
2415 dir->defaults_list_map = NULL;
2418 if (dir->mimeapps_list_added_map != NULL)
2420 g_hash_table_destroy (dir->mimeapps_list_added_map);
2421 dir->mimeapps_list_added_map = NULL;
2424 if (dir->mimeapps_list_removed_map != NULL)
2426 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2427 dir->mimeapps_list_removed_map = NULL;
2430 g_free (dir);
2433 static void
2434 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2435 const char *mime_type,
2436 char **new_desktop_file_ids)
2438 GList *desktop_file_ids;
2439 int i;
2441 desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2442 mime_type);
2444 for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2446 if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i]))
2447 desktop_file_ids = g_list_append (desktop_file_ids,
2448 g_strdup (new_desktop_file_ids[i]));
2451 g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2454 static void
2455 mime_info_cache_init_dir_lists (void)
2457 const char * const *dirs;
2458 int i;
2460 mime_info_cache = mime_info_cache_new ();
2462 dirs = get_applications_search_path ();
2464 for (i = 0; dirs[i] != NULL; i++)
2466 MimeInfoCacheDir *dir;
2468 dir = mime_info_cache_dir_new (dirs[i]);
2470 if (dir != NULL)
2472 mime_info_cache_dir_init (dir);
2473 mime_info_cache_dir_init_defaults_list (dir);
2474 mime_info_cache_dir_init_mimeapps_list (dir);
2476 mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2481 static void
2482 mime_info_cache_update_dir_lists (void)
2484 GList *tmp;
2486 tmp = mime_info_cache->dirs;
2488 while (tmp != NULL)
2490 MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2492 /* No need to do this if we had file monitors... */
2493 mime_info_cache_blow_global_cache ();
2494 mime_info_cache_dir_init (dir);
2495 mime_info_cache_dir_init_defaults_list (dir);
2496 mime_info_cache_dir_init_mimeapps_list (dir);
2498 tmp = tmp->next;
2502 static void
2503 mime_info_cache_init (void)
2505 G_LOCK (mime_info_cache);
2506 if (mime_info_cache == NULL)
2507 mime_info_cache_init_dir_lists ();
2508 else
2510 time_t now;
2512 time (&now);
2513 if (now >= mime_info_cache->last_stat_time + 10)
2515 mime_info_cache_update_dir_lists ();
2516 mime_info_cache->last_stat_time = now;
2520 if (mime_info_cache->should_ping_mime_monitor)
2522 /* g_idle_add (emit_mime_changed, NULL); */
2523 mime_info_cache->should_ping_mime_monitor = FALSE;
2526 G_UNLOCK (mime_info_cache);
2529 static MimeInfoCache *
2530 mime_info_cache_new (void)
2532 MimeInfoCache *cache;
2534 cache = g_new0 (MimeInfoCache, 1);
2536 cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2537 (GDestroyNotify) g_free,
2538 (GDestroyNotify) g_free);
2539 return cache;
2542 static void
2543 mime_info_cache_free (MimeInfoCache *cache)
2545 if (cache == NULL)
2546 return;
2548 g_list_foreach (cache->dirs,
2549 (GFunc) mime_info_cache_dir_free,
2550 NULL);
2551 g_list_free (cache->dirs);
2552 g_hash_table_destroy (cache->global_defaults_cache);
2553 g_free (cache);
2557 * mime_info_cache_reload:
2558 * @dir: directory path which needs reloading.
2560 * Reload the mime information for the @dir.
2562 static void
2563 mime_info_cache_reload (const char *dir)
2565 /* FIXME: just reload the dir that needs reloading,
2566 * don't blow the whole cache
2568 if (mime_info_cache != NULL)
2570 G_LOCK (mime_info_cache);
2571 mime_info_cache_free (mime_info_cache);
2572 mime_info_cache = NULL;
2573 G_UNLOCK (mime_info_cache);
2577 static GList *
2578 append_desktop_entry (GList *list,
2579 const char *desktop_entry,
2580 GList *removed_entries)
2582 /* Add if not already in list, and valid */
2583 if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
2584 !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
2585 list = g_list_prepend (list, g_strdup (desktop_entry));
2587 return list;
2591 * get_all_desktop_entries_for_mime_type:
2592 * @mime_type: a mime type.
2593 * @except: NULL or a strv list
2595 * Returns all the desktop ids for @mime_type. The desktop files
2596 * are listed in an order so that default applications are listed before
2597 * non-default ones, and handlers for inherited mimetypes are listed
2598 * after the base ones.
2600 * Optionally doesn't list the desktop ids given in the @except
2602 * Return value: a #GList containing the desktop ids which claim
2603 * to handle @mime_type.
2605 static GList *
2606 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
2607 const char **except)
2609 GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
2610 MimeInfoCacheDir *dir;
2611 char *mime_type;
2612 char **mime_types;
2613 char **default_entries;
2614 char **removed_associations;
2615 int i, j, k;
2616 GPtrArray *array;
2617 char **anc;
2619 mime_info_cache_init ();
2621 /* collect all ancestors */
2622 mime_types = _g_unix_content_type_get_parents (base_mime_type);
2623 array = g_ptr_array_new ();
2624 for (i = 0; mime_types[i]; i++)
2625 g_ptr_array_add (array, mime_types[i]);
2626 g_free (mime_types);
2627 for (i = 0; i < array->len; i++)
2629 anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
2630 for (j = 0; anc[j]; j++)
2632 for (k = 0; k < array->len; k++)
2634 if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
2635 break;
2637 if (k == array->len) /* not found */
2638 g_ptr_array_add (array, g_strdup (anc[j]));
2640 g_strfreev (anc);
2642 g_ptr_array_add (array, NULL);
2643 mime_types = (char **)g_ptr_array_free (array, FALSE);
2645 G_LOCK (mime_info_cache);
2647 removed_entries = NULL;
2648 desktop_entries = NULL;
2650 for (i = 0; except != NULL && except[i] != NULL; i++)
2651 removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
2653 for (i = 0; mime_types[i] != NULL; i++)
2655 mime_type = mime_types[i];
2657 /* Go through all apps listed as defaults */
2658 for (dir_list = mime_info_cache->dirs;
2659 dir_list != NULL;
2660 dir_list = dir_list->next)
2662 dir = dir_list->data;
2664 /* First added associations from mimeapps.list */
2665 default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
2666 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2667 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2669 /* Then removed associations from mimeapps.list */
2670 removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
2671 for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
2672 removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
2674 /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
2675 default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2676 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2677 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2680 /* Go through all entries that support the mimetype */
2681 for (dir_list = mime_info_cache->dirs;
2682 dir_list != NULL;
2683 dir_list = dir_list->next)
2685 dir = dir_list->data;
2687 list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2688 for (tmp = list; tmp != NULL; tmp = tmp->next)
2689 desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
2693 G_UNLOCK (mime_info_cache);
2695 g_strfreev (mime_types);
2697 g_list_foreach (removed_entries, (GFunc)g_free, NULL);
2698 g_list_free (removed_entries);
2700 desktop_entries = g_list_reverse (desktop_entries);
2702 return desktop_entries;
2705 /* GDesktopAppInfoLookup interface: */
2707 static void g_desktop_app_info_lookup_base_init (gpointer g_class);
2708 static void g_desktop_app_info_lookup_class_init (gpointer g_class,
2709 gpointer class_data);
2711 GType
2712 g_desktop_app_info_lookup_get_type (void)
2714 static volatile gsize g_define_type_id__volatile = 0;
2716 if (g_once_init_enter (&g_define_type_id__volatile))
2718 const GTypeInfo desktop_app_info_lookup_info =
2720 sizeof (GDesktopAppInfoLookupIface), /* class_size */
2721 g_desktop_app_info_lookup_base_init, /* base_init */
2722 NULL, /* base_finalize */
2723 g_desktop_app_info_lookup_class_init,
2724 NULL, /* class_finalize */
2725 NULL, /* class_data */
2727 0, /* n_preallocs */
2728 NULL
2730 GType g_define_type_id =
2731 g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLookup"),
2732 &desktop_app_info_lookup_info, 0);
2734 g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
2736 g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
2739 return g_define_type_id__volatile;
2742 static void
2743 g_desktop_app_info_lookup_class_init (gpointer g_class,
2744 gpointer class_data)
2748 static void
2749 g_desktop_app_info_lookup_base_init (gpointer g_class)
2754 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
2755 * @lookup: a #GDesktopAppInfoLookup
2756 * @uri_scheme: a string containing a URI scheme.
2758 * Gets the default application for launching applications
2759 * using this URI scheme for a particular GDesktopAppInfoLookup
2760 * implementation.
2762 * The GDesktopAppInfoLookup interface and this function is used
2763 * to implement g_app_info_get_default_for_uri_scheme() backends
2764 * in a GIO module. There is no reason for applications to use it
2765 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
2767 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
2769 GAppInfo *
2770 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
2771 const char *uri_scheme)
2773 GDesktopAppInfoLookupIface *iface;
2775 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
2777 iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
2779 return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
2782 #define __G_DESKTOP_APP_INFO_C__
2783 #include "gioaliasdef.c"