Home | History | Annotate | Download | only in gio
      1 /* GIO - GLib Input, Output and Streaming Library
      2  *
      3  * Copyright (C) 2006-2007 Red Hat, Inc.
      4  * Copyright  2007 Ryan Lortie
      5  *
      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.
     10  *
     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.
     15  *
     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.
     20  *
     21  * Author: Alexander Larsson <alexl (at) redhat.com>
     22  */
     23 
     24 #include "config.h"
     25 
     26 #include <errno.h>
     27 #include <string.h>
     28 #include <unistd.h>
     29 #include <sys/wait.h>
     30 
     31 #ifdef HAVE_CRT_EXTERNS_H
     32 #include <crt_externs.h>
     33 #endif
     34 
     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"
     45 
     46 #include "gioalias.h"
     47 
     48 /**
     49  * SECTION:gdesktopappinfo
     50  * @short_description: Application information from desktop files
     51  * @include: gio/gdesktopappinfo.h
     52  *
     53  * #GDesktopAppInfo is an implementation of #GAppInfo based on
     54  * desktop files.
     55  *
     56  * Note that <filename>&lt;gio/gdesktopappinfo.h&gt;</filename> belongs to
     57  * the UNIX-specific GIO interfaces, thus you have to use the
     58  * <filename>gio-unix-2.0.pc</filename> pkg-config file when using it.
     59  */
     60 
     61 #define DEFAULT_APPLICATIONS_GROUP  "Default Applications"
     62 #define ADDED_ASSOCIATIONS_GROUP    "Added Associations"
     63 #define REMOVED_ASSOCIATIONS_GROUP  "Removed Associations"
     64 #define MIME_CACHE_GROUP            "MIME Cache"
     65 
     66 static void     g_desktop_app_info_iface_init         (GAppInfoIface    *iface);
     67 static GList *  get_all_desktop_entries_for_mime_type (const char       *base_mime_type,
     68 						       const char      **except);
     69 static void     mime_info_cache_reload                (const char       *dir);
     70 static gboolean g_desktop_app_info_ensure_saved       (GDesktopAppInfo  *info,
     71 						       GError          **error);
     72 
     73 /**
     74  * GDesktopAppInfo:
     75  *
     76  * Information about an installed application from a desktop file.
     77  */
     78 struct _GDesktopAppInfo
     79 {
     80   GObject parent_instance;
     81 
     82   char *desktop_id;
     83   char *filename;
     84 
     85   char *name;
     86   /* FIXME: what about GenericName ? */
     87   char *comment;
     88   char *icon_name;
     89   GIcon *icon;
     90   char **only_show_in;
     91   char **not_show_in;
     92   char *try_exec;
     93   char *exec;
     94   char *binary;
     95   char *path;
     96 
     97   guint nodisplay       : 1;
     98   guint hidden          : 1;
     99   guint terminal        : 1;
    100   guint startup_notify  : 1;
    101   guint no_fuse         : 1;
    102   /* FIXME: what about StartupWMClass ? */
    103 };
    104 
    105 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT,
    106 			 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
    107 						g_desktop_app_info_iface_init))
    108 
    109 static gpointer
    110 search_path_init (gpointer data)
    111 {
    112   char **args = NULL;
    113   const char * const *data_dirs;
    114   const char *user_data_dir;
    115   int i, length, j;
    116 
    117   data_dirs = g_get_system_data_dirs ();
    118   length = g_strv_length ((char **) data_dirs);
    119 
    120   args = g_new (char *, length + 2);
    121 
    122   j = 0;
    123   user_data_dir = g_get_user_data_dir ();
    124   args[j++] = g_build_filename (user_data_dir, "applications", NULL);
    125   for (i = 0; i < length; i++)
    126     args[j++] = g_build_filename (data_dirs[i],
    127 				  "applications", NULL);
    128   args[j++] = NULL;
    129 
    130   return args;
    131 }
    132 
    133 static const char * const *
    134 get_applications_search_path (void)
    135 {
    136   static GOnce once_init = G_ONCE_INIT;
    137   return g_once (&once_init, search_path_init, NULL);
    138 }
    139 
    140 static void
    141 g_desktop_app_info_finalize (GObject *object)
    142 {
    143   GDesktopAppInfo *info;
    144 
    145   info = G_DESKTOP_APP_INFO (object);
    146 
    147   g_free (info->desktop_id);
    148   g_free (info->filename);
    149   g_free (info->name);
    150   g_free (info->comment);
    151   g_free (info->icon_name);
    152   if (info->icon)
    153     g_object_unref (info->icon);
    154   g_strfreev (info->only_show_in);
    155   g_strfreev (info->not_show_in);
    156   g_free (info->try_exec);
    157   g_free (info->exec);
    158   g_free (info->binary);
    159   g_free (info->path);
    160 
    161   G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
    162 }
    163 
    164 static void
    165 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
    166 {
    167   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
    168 
    169   gobject_class->finalize = g_desktop_app_info_finalize;
    170 }
    171 
    172 static void
    173 g_desktop_app_info_init (GDesktopAppInfo *local)
    174 {
    175 }
    176 
    177 static char *
    178 binary_from_exec (const char *exec)
    179 {
    180   const char *p, *start;
    181 
    182   p = exec;
    183   while (*p == ' ')
    184     p++;
    185   start = p;
    186   while (*p != ' ' && *p != 0)
    187     p++;
    188 
    189   return g_strndup (start, p - start);
    190 
    191 }
    192 
    193 /**
    194  * g_desktop_app_info_new_from_keyfile:
    195  * @key_file: an opened #GKeyFile
    196  *
    197  * Creates a new #GDesktopAppInfo.
    198  *
    199  * Returns: a new #GDesktopAppInfo or %NULL on error.
    200  *
    201  * Since: 2.18
    202  **/
    203 GDesktopAppInfo *
    204 g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
    205 {
    206   GDesktopAppInfo *info;
    207   char *start_group;
    208   char *type;
    209   char *try_exec;
    210 
    211   start_group = g_key_file_get_start_group (key_file);
    212   if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
    213     {
    214       g_free (start_group);
    215       return NULL;
    216     }
    217   g_free (start_group);
    218 
    219   type = g_key_file_get_string (key_file,
    220                                 G_KEY_FILE_DESKTOP_GROUP,
    221                                 G_KEY_FILE_DESKTOP_KEY_TYPE,
    222                                 NULL);
    223   if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
    224     {
    225       g_free (type);
    226       return NULL;
    227     }
    228   g_free (type);
    229 
    230   try_exec = g_key_file_get_string (key_file,
    231                                     G_KEY_FILE_DESKTOP_GROUP,
    232                                     G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
    233                                     NULL);
    234   if (try_exec && try_exec[0] != '\0')
    235     {
    236       char *t;
    237       t = g_find_program_in_path (try_exec);
    238       if (t == NULL)
    239 	{
    240 	  g_free (try_exec);
    241 	  return NULL;
    242 	}
    243       g_free (t);
    244     }
    245 
    246   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
    247   info->filename = NULL;
    248 
    249   info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
    250   info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
    251   info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
    252   info->icon_name =  g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
    253   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);
    254   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);
    255   info->try_exec = try_exec;
    256   info->exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
    257   info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
    258   info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
    259   info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
    260   info->no_fuse = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GIO-NoFuse", NULL) != FALSE;
    261   info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
    262 
    263   info->icon = NULL;
    264   if (info->icon_name)
    265     {
    266       if (g_path_is_absolute (info->icon_name))
    267 	{
    268 	  GFile *file;
    269 
    270 	  file = g_file_new_for_path (info->icon_name);
    271 	  info->icon = g_file_icon_new (file);
    272 	  g_object_unref (file);
    273 	}
    274       else
    275         {
    276           char *p;
    277 
    278           /* Work around a common mistake in desktop files */
    279           if ((p = strrchr (info->icon_name, '.')) != NULL &&
    280               (strcmp (p, ".png") == 0 ||
    281                strcmp (p, ".xpm") == 0 ||
    282                strcmp (p, ".svg") == 0))
    283             *p = 0;
    284 
    285 	  info->icon = g_themed_icon_new (info->icon_name);
    286         }
    287     }
    288 
    289   if (info->exec)
    290     info->binary = binary_from_exec (info->exec);
    291 
    292   if (info->path && info->path[0] == '\0')
    293     {
    294       g_free (info->path);
    295       info->path = NULL;
    296     }
    297 
    298   return info;
    299 }
    300 
    301 /**
    302  * g_desktop_app_info_new_from_filename:
    303  * @filename: the path of a desktop file, in the GLib filename encoding
    304  *
    305  * Creates a new #GDesktopAppInfo.
    306  *
    307  * Returns: a new #GDesktopAppInfo or %NULL on error.
    308  **/
    309 GDesktopAppInfo *
    310 g_desktop_app_info_new_from_filename (const char *filename)
    311 {
    312   GKeyFile *key_file;
    313   GDesktopAppInfo *info = NULL;
    314 
    315   key_file = g_key_file_new ();
    316 
    317   if (g_key_file_load_from_file (key_file,
    318 				 filename,
    319 				 G_KEY_FILE_NONE,
    320 				 NULL))
    321     {
    322       info = g_desktop_app_info_new_from_keyfile (key_file);
    323       if (info)
    324         info->filename = g_strdup (filename);
    325     }
    326 
    327   g_key_file_free (key_file);
    328 
    329   return info;
    330 }
    331 
    332 /**
    333  * g_desktop_app_info_new:
    334  * @desktop_id: the desktop file id
    335  *
    336  * Creates a new #GDesktopAppInfo based on a desktop file id.
    337  *
    338  * A desktop file id is the basename of the desktop file, including the
    339  * .desktop extension. GIO is looking for a desktop file with this name
    340  * in the <filename>applications</filename> subdirectories of the XDG data
    341  * directories (i.e. the directories specified in the
    342  * <envar>XDG_DATA_HOME</envar> and <envar>XDG_DATA_DIRS</envar> environment
    343  * variables). GIO also supports the prefix-to-subdirectory mapping that is
    344  * described in the <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Menu Spec</ulink>
    345  * (i.e. a desktop id of kde-foo.desktop will match
    346  * <filename>/usr/share/applications/kde/foo.desktop</filename>).
    347  *
    348  * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
    349  */
    350 GDesktopAppInfo *
    351 g_desktop_app_info_new (const char *desktop_id)
    352 {
    353   GDesktopAppInfo *appinfo;
    354   const char * const *dirs;
    355   char *basename;
    356   int i;
    357 
    358   dirs = get_applications_search_path ();
    359 
    360   basename = g_strdup (desktop_id);
    361 
    362   for (i = 0; dirs[i] != NULL; i++)
    363     {
    364       char *filename;
    365       char *p;
    366 
    367       filename = g_build_filename (dirs[i], desktop_id, NULL);
    368       appinfo = g_desktop_app_info_new_from_filename (filename);
    369       g_free (filename);
    370       if (appinfo != NULL)
    371 	goto found;
    372 
    373       p = basename;
    374       while ((p = strchr (p, '-')) != NULL)
    375 	{
    376 	  *p = '/';
    377 
    378 	  filename = g_build_filename (dirs[i], basename, NULL);
    379 	  appinfo = g_desktop_app_info_new_from_filename (filename);
    380 	  g_free (filename);
    381 	  if (appinfo != NULL)
    382 	    goto found;
    383 	  *p = '-';
    384 	  p++;
    385 	}
    386     }
    387 
    388   g_free (basename);
    389   return NULL;
    390 
    391  found:
    392   g_free (basename);
    393 
    394   appinfo->desktop_id = g_strdup (desktop_id);
    395 
    396   if (g_desktop_app_info_get_is_hidden (appinfo))
    397     {
    398       g_object_unref (appinfo);
    399       appinfo = NULL;
    400     }
    401 
    402   return appinfo;
    403 }
    404 
    405 static GAppInfo *
    406 g_desktop_app_info_dup (GAppInfo *appinfo)
    407 {
    408   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
    409   GDesktopAppInfo *new_info;
    410 
    411   new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
    412 
    413   new_info->filename = g_strdup (info->filename);
    414   new_info->desktop_id = g_strdup (info->desktop_id);
    415 
    416   new_info->name = g_strdup (info->name);
    417   new_info->comment = g_strdup (info->comment);
    418   new_info->nodisplay = info->nodisplay;
    419   new_info->icon_name = g_strdup (info->icon_name);
    420   if (info->icon)
    421     new_info->icon = g_object_ref (info->icon);
    422   new_info->only_show_in = g_strdupv (info->only_show_in);
    423   new_info->not_show_in = g_strdupv (info->not_show_in);
    424   new_info->try_exec = g_strdup (info->try_exec);
    425   new_info->exec = g_strdup (info->exec);
    426   new_info->binary = g_strdup (info->binary);
    427   new_info->path = g_strdup (info->path);
    428   new_info->hidden = info->hidden;
    429   new_info->terminal = info->terminal;
    430   new_info->startup_notify = info->startup_notify;
    431 
    432   return G_APP_INFO (new_info);
    433 }
    434 
    435 static gboolean
    436 g_desktop_app_info_equal (GAppInfo *appinfo1,
    437 			  GAppInfo *appinfo2)
    438 {
    439   GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
    440   GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
    441 
    442   if (info1->desktop_id == NULL ||
    443       info2->desktop_id == NULL)
    444     return info1 == info2;
    445 
    446   return strcmp (info1->desktop_id, info2->desktop_id) == 0;
    447 }
    448 
    449 static const char *
    450 g_desktop_app_info_get_id (GAppInfo *appinfo)
    451 {
    452   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
    453 
    454   return info->desktop_id;
    455 }
    456 
    457 static const char *
    458 g_desktop_app_info_get_name (GAppInfo *appinfo)
    459 {
    460   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
    461 
    462   if (info->name == NULL)
    463     return _("Unnamed");
    464   return info->name;
    465 }
    466 
    467 /**
    468  * g_desktop_app_info_get_is_hidden:
    469  * @info: a #GDesktopAppInfo.
    470  *
    471  * A desktop file is hidden if the Hidden key in it is
    472  * set to True.
    473  *
    474  * Returns: %TRUE if hidden, %FALSE otherwise.
    475  **/
    476 gboolean
    477 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
    478 {
    479   return info->hidden;
    480 }
    481 
    482 static const char *
    483 g_desktop_app_info_get_description (GAppInfo *appinfo)
    484 {
    485   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
    486 
    487   return info->comment;
    488 }
    489 
    490 static const char *
    491 g_desktop_app_info_get_executable (GAppInfo *appinfo)
    492 {
    493   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
    494 
    495   return info->binary;
    496 }
    497 
    498 static const char *
    499 g_desktop_app_info_get_commandline (GAppInfo *appinfo)
    500 {
    501   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
    502 
    503   return info->exec;
    504 }
    505 
    506 static GIcon *
    507 g_desktop_app_info_get_icon (GAppInfo *appinfo)
    508 {
    509   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
    510 
    511   return info->icon;
    512 }
    513 
    514 static char *
    515 expand_macro_single (char macro, char *uri)
    516 {
    517   GFile *file;
    518   char *result = NULL;
    519   char *path, *name;
    520 
    521   file = g_file_new_for_uri (uri);
    522   path = g_file_get_path (file);
    523   g_object_unref (file);
    524 
    525   switch (macro)
    526     {
    527     case 'u':
    528     case 'U':
    529       result = g_shell_quote (uri);
    530       break;
    531     case 'f':
    532     case 'F':
    533       if (path)
    534 	result = g_shell_quote (path);
    535       break;
    536     case 'd':
    537     case 'D':
    538       if (path)
    539         {
    540           name = g_path_get_dirname (path);
    541 	  result = g_shell_quote (name);
    542           g_free (name);
    543         }
    544       break;
    545     case 'n':
    546     case 'N':
    547       if (path)
    548         {
    549           name = g_path_get_basename (path);
    550 	  result = g_shell_quote (name);
    551           g_free (name);
    552         }
    553       break;
    554     }
    555 
    556   g_free (path);
    557 
    558   return result;
    559 }
    560 
    561 static void
    562 expand_macro (char              macro,
    563               GString          *exec,
    564               GDesktopAppInfo  *info,
    565               GList           **uri_list)
    566 {
    567   GList *uris = *uri_list;
    568   char *expanded;
    569   gboolean force_file_uri;
    570   char force_file_uri_macro;
    571   char *uri;
    572 
    573   g_return_if_fail (exec != NULL);
    574 
    575   /* On %u and %U, pass POSIX file path pointing to the URI via
    576    * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
    577    * running or the URI doesn't have a POSIX file path via FUSE
    578    * we'll just pass the URI.
    579    */
    580   force_file_uri_macro = macro;
    581   force_file_uri = FALSE;
    582   if (!info->no_fuse)
    583     {
    584       switch (macro)
    585 	{
    586 	case 'u':
    587 	  force_file_uri_macro = 'f';
    588 	  force_file_uri = TRUE;
    589 	  break;
    590 	case 'U':
    591 	  force_file_uri_macro = 'F';
    592 	  force_file_uri = TRUE;
    593 	  break;
    594 	default:
    595 	  break;
    596 	}
    597     }
    598 
    599   switch (macro)
    600     {
    601     case 'u':
    602     case 'f':
    603     case 'd':
    604     case 'n':
    605       if (uris)
    606 	{
    607 	  uri = uris->data;
    608           if (!force_file_uri ||
    609 	      /* Pass URI if it contains an anchor */
    610 	      strchr (uri, '#') != NULL)
    611             {
    612               expanded = expand_macro_single (macro, uri);
    613             }
    614           else
    615             {
    616               expanded = expand_macro_single (force_file_uri_macro, uri);
    617               if (expanded == NULL)
    618                 expanded = expand_macro_single (macro, uri);
    619             }
    620 
    621 	  if (expanded)
    622 	    {
    623 	      g_string_append (exec, expanded);
    624 	      g_free (expanded);
    625 	    }
    626 	  uris = uris->next;
    627 	}
    628 
    629       break;
    630 
    631     case 'U':
    632     case 'F':
    633     case 'D':
    634     case 'N':
    635       while (uris)
    636 	{
    637 	  uri = uris->data;
    638 
    639           if (!force_file_uri ||
    640 	      /* Pass URI if it contains an anchor */
    641 	      strchr (uri, '#') != NULL)
    642             {
    643               expanded = expand_macro_single (macro, uri);
    644             }
    645           else
    646             {
    647               expanded = expand_macro_single (force_file_uri_macro, uri);
    648               if (expanded == NULL)
    649                 expanded = expand_macro_single (macro, uri);
    650             }
    651 
    652 	  if (expanded)
    653 	    {
    654 	      g_string_append (exec, expanded);
    655 	      g_free (expanded);
    656 	    }
    657 
    658 	  uris = uris->next;
    659 
    660 	  if (uris != NULL && expanded)
    661 	    g_string_append_c (exec, ' ');
    662 	}
    663 
    664       break;
    665 
    666     case 'i':
    667       if (info->icon_name)
    668 	{
    669 	  g_string_append (exec, "--icon ");
    670 	  g_string_append (exec, info->icon_name);
    671 	}
    672       break;
    673 
    674     case 'c':
    675       if (info->name)
    676 	g_string_append (exec, info->name);
    677       break;
    678 
    679     case 'k':
    680       if (info->filename)
    681 	g_string_append (exec, info->filename);
    682       break;
    683 
    684     case 'm': /* deprecated */
    685       break;
    686 
    687     case '%':
    688       g_string_append_c (exec, '%');
    689       break;
    690     }
    691 
    692   *uri_list = uris;
    693 }
    694 
    695 static gboolean
    696 expand_application_parameters (GDesktopAppInfo   *info,
    697 			       GList            **uris,
    698 			       int               *argc,
    699 			       char            ***argv,
    700 			       GError           **error)
    701 {
    702   GList *uri_list = *uris;
    703   const char *p = info->exec;
    704   GString *expanded_exec = g_string_new (NULL);
    705   gboolean res;
    706 
    707   if (info->exec == NULL)
    708     {
    709       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
    710                            _("Desktop file didn't specify Exec field"));
    711       return FALSE;
    712     }
    713 
    714   while (*p)
    715     {
    716       if (p[0] == '%' && p[1] != '\0')
    717 	{
    718 	  expand_macro (p[1], expanded_exec, info, uris);
    719 	  p++;
    720 	}
    721       else
    722 	g_string_append_c (expanded_exec, *p);
    723 
    724       p++;
    725     }
    726 
    727   /* No file substitutions */
    728   if (uri_list == *uris && uri_list != NULL)
    729     {
    730       /* If there is no macro default to %f. This is also what KDE does */
    731       g_string_append_c (expanded_exec, ' ');
    732       expand_macro ('f', expanded_exec, info, uris);
    733     }
    734 
    735   res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
    736   g_string_free (expanded_exec, TRUE);
    737   return res;
    738 }
    739 
    740 static gboolean
    741 prepend_terminal_to_vector (int    *argc,
    742 			    char ***argv)
    743 {
    744 #ifndef G_OS_WIN32
    745   char **real_argv;
    746   int real_argc;
    747   int i, j;
    748   char **term_argv = NULL;
    749   int term_argc = 0;
    750   char *check;
    751   char **the_argv;
    752 
    753   g_return_val_if_fail (argc != NULL, FALSE);
    754   g_return_val_if_fail (argv != NULL, FALSE);
    755 
    756   /* sanity */
    757   if(*argv == NULL)
    758     *argc = 0;
    759 
    760   the_argv = *argv;
    761 
    762   /* compute size if not given */
    763   if (*argc < 0)
    764     {
    765       for (i = 0; the_argv[i] != NULL; i++)
    766 	;
    767       *argc = i;
    768     }
    769 
    770   term_argc = 2;
    771   term_argv = g_new0 (char *, 3);
    772 
    773   check = g_find_program_in_path ("gnome-terminal");
    774   if (check != NULL)
    775     {
    776       term_argv[0] = check;
    777       /* Note that gnome-terminal takes -x and
    778        * as -e in gnome-terminal is broken we use that. */
    779       term_argv[1] = g_strdup ("-x");
    780     }
    781   else
    782     {
    783       if (check == NULL)
    784 	check = g_find_program_in_path ("nxterm");
    785       if (check == NULL)
    786 	check = g_find_program_in_path ("color-xterm");
    787       if (check == NULL)
    788 	check = g_find_program_in_path ("rxvt");
    789       if (check == NULL)
    790 	check = g_find_program_in_path ("xterm");
    791       if (check == NULL)
    792 	check = g_find_program_in_path ("dtterm");
    793       if (check == NULL)
    794 	{
    795 	  check = g_strdup ("xterm");
    796 	  g_warning ("couldn't find a terminal, falling back to xterm");
    797 	}
    798       term_argv[0] = check;
    799       term_argv[1] = g_strdup ("-e");
    800     }
    801 
    802   real_argc = term_argc + *argc;
    803   real_argv = g_new (char *, real_argc + 1);
    804 
    805   for (i = 0; i < term_argc; i++)
    806     real_argv[i] = term_argv[i];
    807 
    808   for (j = 0; j < *argc; j++, i++)
    809     real_argv[i] = (char *)the_argv[j];
    810 
    811   real_argv[i] = NULL;
    812 
    813   g_free (*argv);
    814   *argv = real_argv;
    815   *argc = real_argc;
    816 
    817   /* we use g_free here as we sucked all the inner strings
    818    * out from it into real_argv */
    819   g_free (term_argv);
    820   return TRUE;
    821 #else
    822   return FALSE;
    823 #endif /* G_OS_WIN32 */
    824 }
    825 
    826 /* '=' is the new '\0'.
    827  * DO NOT CALL unless at least one string ends with '='
    828  */
    829 static gboolean
    830 is_env (const char *a,
    831 	const char *b)
    832 {
    833   while (*a == *b)
    834   {
    835     if (*a == 0 || *b == 0)
    836       return FALSE;
    837 
    838     if (*a == '=')
    839       return TRUE;
    840 
    841     a++;
    842     b++;
    843   }
    844 
    845   return FALSE;
    846 }
    847 
    848 /* free with g_strfreev */
    849 static char **
    850 replace_env_var (char       **old_environ,
    851 		 const char  *env_var,
    852 		 const char  *new_value)
    853 {
    854   int length, new_length;
    855   int index, new_index;
    856   char **new_environ;
    857   int i, new_i;
    858 
    859   /* do two things at once:
    860    *  - discover the length of the environment ('length')
    861    *  - find the location (if any) of the env var ('index')
    862    */
    863   index = -1;
    864   for (length = 0; old_environ[length]; length++)
    865     {
    866       /* if we already have it in our environment, replace */
    867       if (is_env (old_environ[length], env_var))
    868 	index = length;
    869     }
    870 
    871 
    872   /* no current env var, no desired env value.
    873    * this is easy :)
    874    */
    875   if (new_value == NULL && index == -1)
    876     return old_environ;
    877 
    878   /* in all cases now, we will be using a modified environment.
    879    * determine its length and allocated it.
    880    *
    881    * after this block:
    882    *   new_index   = location to insert, if any
    883    *   new_length  = length of the new array
    884    *   new_environ = the pointer array for the new environment
    885    */
    886 
    887   if (new_value == NULL && index >= 0)
    888     {
    889       /* in this case, we will be removing an entry */
    890       new_length = length - 1;
    891       new_index = -1;
    892     }
    893   else if (new_value != NULL && index < 0)
    894     {
    895       /* in this case, we will be adding an entry to the end */
    896       new_length = length + 1;
    897       new_index = length;
    898     }
    899   else
    900     /* in this case, we will be replacing the existing entry */
    901     {
    902       new_length = length;
    903       new_index = index;
    904     }
    905 
    906   new_environ = g_malloc (sizeof (char *) * (new_length + 1));
    907   new_environ[new_length] = NULL;
    908 
    909   /* now we do the copying.
    910    * for each entry in the new environment, we decide what to do
    911    */
    912 
    913   i = 0;
    914   for (new_i = 0; new_i < new_length; new_i++)
    915     {
    916       if (new_i == new_index)
    917 	{
    918 	  /* insert our new item */
    919 	  new_environ[new_i] = g_strconcat (env_var,
    920 					    "=",
    921 					    new_value,
    922 					    NULL);
    923 
    924 	  /* if we had an old entry, skip it now */
    925 	  if (index >= 0)
    926 	    i++;
    927 	}
    928       else
    929 	{
    930 	  /* if this is the old DESKTOP_STARTUP_ID, skip it */
    931 	  if (i == index)
    932 	    i++;
    933 
    934 	  /* copy an old item */
    935 	  new_environ[new_i] = g_strdup (old_environ[i]);
    936 	  i++;
    937 	}
    938     }
    939 
    940   g_strfreev (old_environ);
    941 
    942   return new_environ;
    943 }
    944 
    945 static GList *
    946 uri_list_segment_to_files (GList *start,
    947 			   GList *end)
    948 {
    949   GList *res;
    950   GFile *file;
    951 
    952   res = NULL;
    953   while (start != NULL && start != end)
    954     {
    955       file = g_file_new_for_uri ((char *)start->data);
    956       res = g_list_prepend (res, file);
    957       start = start->next;
    958     }
    959 
    960   return g_list_reverse (res);
    961 }
    962 
    963 #ifdef HAVE__NSGETENVIRON
    964 #define environ (*_NSGetEnviron())
    965 #elif !defined(G_OS_WIN32)
    966 
    967 /* According to the Single Unix Specification, environ is not in
    968  *  * any system header, although unistd.h often declares it.
    969  *   */
    970 extern char **environ;
    971 #endif
    972 
    973 static gboolean
    974 g_desktop_app_info_launch_uris (GAppInfo           *appinfo,
    975 				GList              *uris,
    976 				GAppLaunchContext  *launch_context,
    977 				GError            **error)
    978 {
    979   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
    980   gboolean completed = FALSE;
    981   GList *old_uris;
    982   GList *launched_files;
    983   char **envp;
    984   char **argv;
    985   int argc;
    986   char *display;
    987   char *sn_id;
    988 
    989   g_return_val_if_fail (appinfo != NULL, FALSE);
    990 
    991   argv = NULL;
    992   envp = NULL;
    993 
    994   do
    995     {
    996       old_uris = uris;
    997       if (!expand_application_parameters (info, &uris,
    998 					  &argc, &argv, error))
    999 	goto out;
   1000 
   1001       if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
   1002 	{
   1003 	  g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
   1004                                _("Unable to find terminal required for application"));
   1005 	  goto out;
   1006 	}
   1007 
   1008       sn_id = NULL;
   1009       if (launch_context)
   1010 	{
   1011 	  launched_files = uri_list_segment_to_files (old_uris, uris);
   1012 
   1013 	  display = g_app_launch_context_get_display (launch_context,
   1014 						      appinfo,
   1015 						      launched_files);
   1016 
   1017 	  sn_id = NULL;
   1018 	  if (info->startup_notify)
   1019 	    sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
   1020 								appinfo,
   1021 								launched_files);
   1022 
   1023 	  if (display || sn_id)
   1024 	    {
   1025 #ifdef G_OS_WIN32
   1026 	      /* FIXME */
   1027 	      envp = g_new0 (char *, 1);
   1028 #else
   1029 	      envp = g_strdupv (environ);
   1030 #endif
   1031 
   1032 	      if (display)
   1033 		envp = replace_env_var (envp,
   1034 					"DISPLAY",
   1035 					display);
   1036 
   1037 	      if (sn_id)
   1038 		envp = replace_env_var (envp,
   1039 					"DESKTOP_STARTUP_ID",
   1040 					sn_id);
   1041 	    }
   1042 
   1043 	  g_free (display);
   1044 
   1045 	  g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
   1046 	  g_list_free (launched_files);
   1047 	}
   1048 
   1049       if (!g_spawn_async (info->path,  /* working directory */
   1050 			  argv,
   1051 			  envp,
   1052 			  G_SPAWN_SEARCH_PATH /* flags */,
   1053 			  NULL /* child_setup */,
   1054 			  NULL /* data */,
   1055 			  NULL /* child_pid */,
   1056 			  error))
   1057 	{
   1058 	  if (sn_id)
   1059 	    {
   1060 	      g_app_launch_context_launch_failed (launch_context, sn_id);
   1061 	      g_free (sn_id);
   1062 	    }
   1063 	  goto out;
   1064 	}
   1065 
   1066 
   1067       g_free (sn_id);
   1068 
   1069       g_strfreev (envp);
   1070       g_strfreev (argv);
   1071       envp = NULL;
   1072       argv = NULL;
   1073     }
   1074   while (uris != NULL);
   1075 
   1076   completed = TRUE;
   1077 
   1078  out:
   1079   g_strfreev (argv);
   1080   g_strfreev (envp);
   1081 
   1082   return completed;
   1083 }
   1084 
   1085 static gboolean
   1086 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
   1087 {
   1088   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
   1089 
   1090   return info->exec &&
   1091     ((strstr (info->exec, "%u") != NULL) ||
   1092      (strstr (info->exec, "%U") != NULL));
   1093 }
   1094 
   1095 static gboolean
   1096 g_desktop_app_info_supports_files (GAppInfo *appinfo)
   1097 {
   1098   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
   1099 
   1100   return info->exec &&
   1101     ((strstr (info->exec, "%f") != NULL) ||
   1102      (strstr (info->exec, "%F") != NULL));
   1103 }
   1104 
   1105 static gboolean
   1106 g_desktop_app_info_launch (GAppInfo           *appinfo,
   1107 			   GList              *files,
   1108 			   GAppLaunchContext  *launch_context,
   1109 			   GError            **error)
   1110 {
   1111   GList *uris;
   1112   char *uri;
   1113   gboolean res;
   1114 
   1115   uris = NULL;
   1116   while (files)
   1117     {
   1118       uri = g_file_get_uri (files->data);
   1119       uris = g_list_prepend (uris, uri);
   1120       files = files->next;
   1121     }
   1122 
   1123   uris = g_list_reverse (uris);
   1124 
   1125   res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
   1126 
   1127   g_list_foreach  (uris, (GFunc)g_free, NULL);
   1128   g_list_free (uris);
   1129 
   1130   return res;
   1131 }
   1132 
   1133 G_LOCK_DEFINE_STATIC (g_desktop_env);
   1134 static gchar *g_desktop_env = NULL;
   1135 
   1136 /**
   1137  * g_desktop_app_info_set_desktop_env:
   1138  * @desktop_env: a string specifying what desktop this is
   1139  *
   1140  * Sets the name of the desktop that the application is running in.
   1141  * This is used by g_app_info_should_show() to evaluate the
   1142  * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
   1143  * desktop entry fields.
   1144  *
   1145  * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop
   1146  * Menu specification</ulink> recognizes the following:
   1147  * <simplelist>
   1148  *   <member>GNOME</member>
   1149  *   <member>KDE</member>
   1150  *   <member>ROX</member>
   1151  *   <member>XFCE</member>
   1152  *   <member>Old</member>
   1153  * </simplelist>
   1154  *
   1155  * Should be called only once; subsequent calls are ignored.
   1156  */
   1157 void
   1158 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
   1159 {
   1160   G_LOCK (g_desktop_env);
   1161   if (!g_desktop_env)
   1162     g_desktop_env = g_strdup (desktop_env);
   1163   G_UNLOCK (g_desktop_env);
   1164 }
   1165 
   1166 static gboolean
   1167 g_desktop_app_info_should_show (GAppInfo *appinfo)
   1168 {
   1169   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
   1170   gboolean found;
   1171   const gchar *desktop_env;
   1172   int i;
   1173 
   1174   if (info->nodisplay)
   1175     return FALSE;
   1176 
   1177   G_LOCK (g_desktop_env);
   1178   desktop_env = g_desktop_env;
   1179   G_UNLOCK (g_desktop_env);
   1180 
   1181   if (info->only_show_in)
   1182     {
   1183       if (desktop_env == NULL)
   1184 	return FALSE;
   1185 
   1186       found = FALSE;
   1187       for (i = 0; info->only_show_in[i] != NULL; i++)
   1188 	{
   1189 	  if (strcmp (info->only_show_in[i], desktop_env) == 0)
   1190 	    {
   1191 	      found = TRUE;
   1192 	      break;
   1193 	    }
   1194 	}
   1195       if (!found)
   1196 	return FALSE;
   1197     }
   1198 
   1199   if (info->not_show_in && desktop_env)
   1200     {
   1201       for (i = 0; info->not_show_in[i] != NULL; i++)
   1202 	{
   1203 	  if (strcmp (info->not_show_in[i], desktop_env) == 0)
   1204 	    return FALSE;
   1205 	}
   1206     }
   1207 
   1208   return TRUE;
   1209 }
   1210 
   1211 typedef enum {
   1212   APP_DIR,
   1213   MIMETYPE_DIR
   1214 } DirType;
   1215 
   1216 static char *
   1217 ensure_dir (DirType   type,
   1218             GError  **error)
   1219 {
   1220   char *path, *display_name;
   1221   int errsv;
   1222 
   1223   if (type == APP_DIR)
   1224     path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
   1225   else
   1226     path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
   1227 
   1228   errno = 0;
   1229   if (g_mkdir_with_parents (path, 0700) == 0)
   1230     return path;
   1231 
   1232   errsv = errno;
   1233   display_name = g_filename_display_name (path);
   1234   if (type == APP_DIR)
   1235     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
   1236                  _("Can't create user application configuration folder %s: %s"),
   1237                  display_name, g_strerror (errsv));
   1238   else
   1239     g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
   1240                  _("Can't create user MIME configuration folder %s: %s"),
   1241                  display_name, g_strerror (errsv));
   1242 
   1243   g_free (display_name);
   1244   g_free (path);
   1245 
   1246   return NULL;
   1247 }
   1248 
   1249 static gboolean
   1250 update_mimeapps_list (const char  *desktop_id,
   1251 		      const char  *content_type,
   1252 		      gboolean     add_as_default,
   1253 		      gboolean     add_non_default,
   1254 		      gboolean     remove,
   1255 		      GError     **error)
   1256 {
   1257   char *dirname, *filename;
   1258   GKeyFile *key_file;
   1259   gboolean load_succeeded, res;
   1260   char **old_list, **list;
   1261   GList *system_list, *l;
   1262   gsize length, data_size;
   1263   char *data;
   1264   int i, j, k;
   1265   char **content_types;
   1266 
   1267   /* Don't add both at start and end */
   1268   g_assert (!(add_as_default && add_non_default));
   1269 
   1270   dirname = ensure_dir (APP_DIR, error);
   1271   if (!dirname)
   1272     return FALSE;
   1273 
   1274   filename = g_build_filename (dirname, "mimeapps.list", NULL);
   1275   g_free (dirname);
   1276 
   1277   key_file = g_key_file_new ();
   1278   load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
   1279   if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP))
   1280     {
   1281       g_key_file_free (key_file);
   1282       key_file = g_key_file_new ();
   1283     }
   1284 
   1285   if (content_type)
   1286     {
   1287       content_types = g_new (char *, 2);
   1288       content_types[0] = g_strdup (content_type);
   1289       content_types[1] = NULL;
   1290     }
   1291   else
   1292     {
   1293       content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
   1294     }
   1295 
   1296   for (k = 0; content_types && content_types[k]; k++)
   1297     {
   1298       /* Add to the right place in the list */
   1299 
   1300       length = 0;
   1301       old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
   1302 					     content_types[k], &length, NULL);
   1303 
   1304       list = g_new (char *, 1 + length + 1);
   1305 
   1306       i = 0;
   1307       if (add_as_default)
   1308         list[i++] = g_strdup (desktop_id);
   1309       if (old_list)
   1310         {
   1311           for (j = 0; old_list[j] != NULL; j++)
   1312 	    {
   1313 	      if (g_strcmp0 (old_list[j], desktop_id) != 0)
   1314 	        list[i++] = g_strdup (old_list[j]);
   1315 	      else if (add_non_default)
   1316 		{
   1317 		  /* If adding as non-default, and it's already in,
   1318 		     don't change order of desktop ids */
   1319 		  add_non_default = FALSE;
   1320 		  list[i++] = g_strdup (old_list[j]);
   1321 		}
   1322 	    }
   1323         }
   1324 
   1325       if (add_non_default)
   1326 	{
   1327 	  /* We're adding as non-default, and it wasn't already in the list,
   1328 	     so we add at the end. But to avoid listing the app before the
   1329 	     current system default (thus changing the default) we have to
   1330 	     add the current list of (not yet listed) apps before it. */
   1331 
   1332 	  list[i] = NULL; /* Terminate current list so we can use it */
   1333 	  system_list =  get_all_desktop_entries_for_mime_type (content_type, (const char **)list);
   1334 
   1335 	  list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1);
   1336 
   1337 	  for (l = system_list; l != NULL; l = l->next)
   1338 	    {
   1339 	      list[i++] = l->data; /* no strdup, taking ownership */
   1340 	      if (g_strcmp0 (l->data, desktop_id) == 0)
   1341 		add_non_default = FALSE;
   1342 	    }
   1343 	  g_list_free (system_list);
   1344 
   1345 	  if (add_non_default)
   1346 	    list[i++] = g_strdup (desktop_id);
   1347 	}
   1348 
   1349       list[i] = NULL;
   1350 
   1351       g_strfreev (old_list);
   1352 
   1353       if (list[0] == NULL || desktop_id == NULL)
   1354         g_key_file_remove_key (key_file,
   1355 			       ADDED_ASSOCIATIONS_GROUP,
   1356 			       content_types[k],
   1357 			       NULL);
   1358       else
   1359         g_key_file_set_string_list (key_file,
   1360 			            ADDED_ASSOCIATIONS_GROUP,
   1361 			            content_types[k],
   1362 			            (const char * const *)list, i);
   1363 
   1364       g_strfreev (list);
   1365     }
   1366 
   1367   if (content_type)
   1368     {
   1369       /* reuse the list from above */
   1370     }
   1371   else
   1372     {
   1373       g_strfreev (content_types);
   1374       content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
   1375     }
   1376 
   1377   for (k = 0; content_types && content_types[k]; k++)
   1378     {
   1379       /* Remove from removed associations group (unless remove) */
   1380 
   1381       length = 0;
   1382       old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
   1383 					     content_types[k], &length, NULL);
   1384 
   1385       list = g_new (char *, 1 + length + 1);
   1386 
   1387       i = 0;
   1388       if (remove)
   1389         list[i++] = g_strdup (desktop_id);
   1390       if (old_list)
   1391         {
   1392           for (j = 0; old_list[j] != NULL; j++)
   1393 	    {
   1394 	      if (g_strcmp0 (old_list[j], desktop_id) != 0)
   1395 	        list[i++] = g_strdup (old_list[j]);
   1396 	    }
   1397         }
   1398       list[i] = NULL;
   1399 
   1400       g_strfreev (old_list);
   1401 
   1402       if (list[0] == NULL || desktop_id == NULL)
   1403         g_key_file_remove_key (key_file,
   1404 			       REMOVED_ASSOCIATIONS_GROUP,
   1405 			       content_types[k],
   1406 			       NULL);
   1407       else
   1408         g_key_file_set_string_list (key_file,
   1409 				    REMOVED_ASSOCIATIONS_GROUP,
   1410 				    content_types[k],
   1411 				    (const char * const *)list, i);
   1412 
   1413       g_strfreev (list);
   1414     }
   1415 
   1416   g_strfreev (content_types);
   1417 
   1418   data = g_key_file_to_data (key_file, &data_size, error);
   1419   g_key_file_free (key_file);
   1420 
   1421   res = g_file_set_contents (filename, data, data_size, error);
   1422 
   1423   mime_info_cache_reload (NULL);
   1424 
   1425   g_free (filename);
   1426   g_free (data);
   1427 
   1428   return res;
   1429 }
   1430 
   1431 static gboolean
   1432 g_desktop_app_info_set_as_default_for_type (GAppInfo    *appinfo,
   1433 					    const char  *content_type,
   1434 					    GError     **error)
   1435 {
   1436   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
   1437 
   1438   if (!g_desktop_app_info_ensure_saved (info, error))
   1439     return FALSE;
   1440 
   1441   return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error);
   1442 }
   1443 
   1444 static void
   1445 update_program_done (GPid     pid,
   1446 		     gint     status,
   1447 		     gpointer data)
   1448 {
   1449   /* Did the application exit correctly */
   1450   if (WIFEXITED (status) &&
   1451       WEXITSTATUS (status) == 0)
   1452     {
   1453       /* Here we could clean out any caches in use */
   1454     }
   1455 }
   1456 
   1457 static void
   1458 run_update_command (char *command,
   1459 		    char *subdir)
   1460 {
   1461 	char *argv[3] = {
   1462 		NULL,
   1463 		NULL,
   1464 		NULL,
   1465 	};
   1466 	GPid pid = 0;
   1467 	GError *error = NULL;
   1468 
   1469 	argv[0] = command;
   1470 	argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
   1471 
   1472 	if (g_spawn_async ("/", argv,
   1473 			   NULL,       /* envp */
   1474 			   G_SPAWN_SEARCH_PATH |
   1475 			   G_SPAWN_STDOUT_TO_DEV_NULL |
   1476 			   G_SPAWN_STDERR_TO_DEV_NULL |
   1477 			   G_SPAWN_DO_NOT_REAP_CHILD,
   1478 			   NULL, NULL, /* No setup function */
   1479 			   &pid,
   1480 			   &error))
   1481 	  g_child_watch_add (pid, update_program_done, NULL);
   1482 	else
   1483 	  {
   1484 	    /* If we get an error at this point, it's quite likely the user doesn't
   1485 	     * have an installed copy of either 'update-mime-database' or
   1486 	     * 'update-desktop-database'.  I don't think we want to popup an error
   1487 	     * dialog at this point, so we just do a g_warning to give the user a
   1488 	     * chance of debugging it.
   1489 	     */
   1490 	    g_warning ("%s", error->message);
   1491 	  }
   1492 
   1493 	g_free (argv[1]);
   1494 }
   1495 
   1496 static gboolean
   1497 g_desktop_app_info_set_as_default_for_extension (GAppInfo    *appinfo,
   1498 						 const char  *extension,
   1499 						 GError     **error)
   1500 {
   1501   char *filename, *basename, *mimetype;
   1502   char *dirname;
   1503   gboolean res;
   1504 
   1505   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
   1506     return FALSE;
   1507 
   1508   dirname = ensure_dir (MIMETYPE_DIR, error);
   1509   if (!dirname)
   1510     return FALSE;
   1511 
   1512   basename = g_strdup_printf ("user-extension-%s.xml", extension);
   1513   filename = g_build_filename (dirname, basename, NULL);
   1514   g_free (basename);
   1515   g_free (dirname);
   1516 
   1517   mimetype = g_strdup_printf ("application/x-extension-%s", extension);
   1518 
   1519   if (!g_file_test (filename, G_FILE_TEST_EXISTS))
   1520     {
   1521       char *contents;
   1522 
   1523       contents =
   1524         g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
   1525                          "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
   1526                          " <mime-type type=\"%s\">\n"
   1527                          "  <comment>%s document</comment>\n"
   1528                          "  <glob pattern=\"*.%s\"/>\n"
   1529                          " </mime-type>\n"
   1530                          "</mime-info>\n", mimetype, extension, extension);
   1531 
   1532       g_file_set_contents (filename, contents, -1, NULL);
   1533       g_free (contents);
   1534 
   1535       run_update_command ("update-mime-database", "mime");
   1536     }
   1537   g_free (filename);
   1538 
   1539   res = g_desktop_app_info_set_as_default_for_type (appinfo,
   1540 						    mimetype,
   1541 						    error);
   1542 
   1543   g_free (mimetype);
   1544 
   1545   return res;
   1546 }
   1547 
   1548 static gboolean
   1549 g_desktop_app_info_add_supports_type (GAppInfo    *appinfo,
   1550 				      const char  *content_type,
   1551 				      GError     **error)
   1552 {
   1553   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
   1554 
   1555   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
   1556     return FALSE;
   1557 
   1558   return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error);
   1559 }
   1560 
   1561 static gboolean
   1562 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
   1563 {
   1564   return TRUE;
   1565 }
   1566 
   1567 static gboolean
   1568 g_desktop_app_info_remove_supports_type (GAppInfo    *appinfo,
   1569 					 const char  *content_type,
   1570 					 GError     **error)
   1571 {
   1572   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
   1573 
   1574   if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
   1575     return FALSE;
   1576 
   1577   return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error);
   1578 }
   1579 
   1580 static gboolean
   1581 g_desktop_app_info_ensure_saved (GDesktopAppInfo  *info,
   1582 				 GError          **error)
   1583 {
   1584   GKeyFile *key_file;
   1585   char *dirname;
   1586   char *filename;
   1587   char *data, *desktop_id;
   1588   gsize data_size;
   1589   int fd;
   1590   gboolean res;
   1591 
   1592   if (info->filename != NULL)
   1593     return TRUE;
   1594 
   1595   /* This is only used for object created with
   1596    * g_app_info_create_from_commandline. All other
   1597    * object should have a filename
   1598    */
   1599 
   1600   dirname = ensure_dir (APP_DIR, error);
   1601   if (!dirname)
   1602     return FALSE;
   1603 
   1604   key_file = g_key_file_new ();
   1605 
   1606   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
   1607 			 "Encoding", "UTF-8");
   1608   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
   1609 			 G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
   1610   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
   1611 			 G_KEY_FILE_DESKTOP_KEY_TYPE,
   1612                          G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
   1613   if (info->terminal)
   1614     g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
   1615 			    G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
   1616 
   1617   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
   1618 			 G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
   1619 
   1620   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
   1621 			 G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
   1622 
   1623   g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
   1624 			 G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
   1625 
   1626   g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
   1627 			  G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
   1628 
   1629   data = g_key_file_to_data (key_file, &data_size, NULL);
   1630   g_key_file_free (key_file);
   1631 
   1632   desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
   1633   filename = g_build_filename (dirname, desktop_id, NULL);
   1634   g_free (desktop_id);
   1635   g_free (dirname);
   1636 
   1637   fd = g_mkstemp (filename);
   1638   if (fd == -1)
   1639     {
   1640       char *display_name;
   1641 
   1642       display_name = g_filename_display_name (filename);
   1643       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
   1644 		   _("Can't create user desktop file %s"), display_name);
   1645       g_free (display_name);
   1646       g_free (filename);
   1647       g_free (data);
   1648       return FALSE;
   1649     }
   1650 
   1651   desktop_id = g_path_get_basename (filename);
   1652 
   1653   close (fd);
   1654 
   1655   res = g_file_set_contents (filename, data, data_size, error);
   1656   if (!res)
   1657     {
   1658       g_free (desktop_id);
   1659       g_free (filename);
   1660       return FALSE;
   1661     }
   1662 
   1663   info->filename = filename;
   1664   info->desktop_id = desktop_id;
   1665 
   1666   run_update_command ("update-desktop-database", "applications");
   1667 
   1668   return TRUE;
   1669 }
   1670 
   1671 static gboolean
   1672 g_desktop_app_info_can_delete (GAppInfo *appinfo)
   1673 {
   1674   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
   1675 
   1676   if (info->filename)
   1677     {
   1678       if (strstr (info->filename, "/userapp-"))
   1679         return g_access (info->filename, W_OK) == 0;
   1680     }
   1681 
   1682   return FALSE;
   1683 }
   1684 
   1685 static gboolean
   1686 g_desktop_app_info_delete (GAppInfo *appinfo)
   1687 {
   1688   GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
   1689 
   1690   if (info->filename)
   1691     {
   1692       if (g_remove (info->filename) == 0)
   1693         {
   1694           update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
   1695 
   1696           g_free (info->filename);
   1697           info->filename = NULL;
   1698           g_free (info->desktop_id);
   1699           info->desktop_id = NULL;
   1700 
   1701           return TRUE;
   1702         }
   1703     }
   1704 
   1705   return FALSE;
   1706 }
   1707 
   1708 /**
   1709  * g_app_info_create_from_commandline:
   1710  * @commandline: the commandline to use
   1711  * @application_name: the application name, or %NULL to use @commandline
   1712  * @flags: flags that can specify details of the created #GAppInfo
   1713  * @error: a #GError location to store the error occuring, %NULL to ignore.
   1714  *
   1715  * Creates a new #GAppInfo from the given information.
   1716  *
   1717  * Returns: new #GAppInfo for given command.
   1718  **/
   1719 GAppInfo *
   1720 g_app_info_create_from_commandline (const char           *commandline,
   1721 				    const char           *application_name,
   1722 				    GAppInfoCreateFlags   flags,
   1723 				    GError              **error)
   1724 {
   1725   char **split;
   1726   char *basename;
   1727   GDesktopAppInfo *info;
   1728 
   1729   info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
   1730 
   1731   info->filename = NULL;
   1732   info->desktop_id = NULL;
   1733 
   1734   info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
   1735   info->startup_notify = FALSE;
   1736   info->hidden = FALSE;
   1737   if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
   1738     info->exec = g_strconcat (commandline, " %u", NULL);
   1739   else
   1740     info->exec = g_strconcat (commandline, " %f", NULL);
   1741   info->nodisplay = TRUE;
   1742   info->binary = binary_from_exec (info->exec);
   1743 
   1744   if (application_name)
   1745     info->name = g_strdup (application_name);
   1746   else
   1747     {
   1748       /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
   1749       split = g_strsplit (commandline, " ", 2);
   1750       basename = g_path_get_basename (split[0]);
   1751       g_strfreev (split);
   1752       info->name = basename;
   1753       if (info->name == NULL)
   1754 	info->name = g_strdup ("custom");
   1755     }
   1756   info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
   1757 
   1758   return G_APP_INFO (info);
   1759 }
   1760 
   1761 static void
   1762 g_desktop_app_info_iface_init (GAppInfoIface *iface)
   1763 {
   1764   iface->dup = g_desktop_app_info_dup;
   1765   iface->equal = g_desktop_app_info_equal;
   1766   iface->get_id = g_desktop_app_info_get_id;
   1767   iface->get_name = g_desktop_app_info_get_name;
   1768   iface->get_description = g_desktop_app_info_get_description;
   1769   iface->get_executable = g_desktop_app_info_get_executable;
   1770   iface->get_icon = g_desktop_app_info_get_icon;
   1771   iface->launch = g_desktop_app_info_launch;
   1772   iface->supports_uris = g_desktop_app_info_supports_uris;
   1773   iface->supports_files = g_desktop_app_info_supports_files;
   1774   iface->launch_uris = g_desktop_app_info_launch_uris;
   1775   iface->should_show = g_desktop_app_info_should_show;
   1776   iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
   1777   iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
   1778   iface->add_supports_type = g_desktop_app_info_add_supports_type;
   1779   iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
   1780   iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
   1781   iface->can_delete = g_desktop_app_info_can_delete;
   1782   iface->do_delete = g_desktop_app_info_delete;
   1783   iface->get_commandline = g_desktop_app_info_get_commandline;
   1784 }
   1785 
   1786 static gboolean
   1787 app_info_in_list (GAppInfo *info,
   1788                   GList    *list)
   1789 {
   1790   while (list != NULL)
   1791     {
   1792       if (g_app_info_equal (info, list->data))
   1793 	return TRUE;
   1794       list = list->next;
   1795     }
   1796   return FALSE;
   1797 }
   1798 
   1799 
   1800 /**
   1801  * g_app_info_get_all_for_type:
   1802  * @content_type: the content type to find a #GAppInfo for
   1803  *
   1804  * Gets a list of all #GAppInfo s for a given content type.
   1805  *
   1806  * Returns: #GList of #GAppInfo s for given @content_type
   1807  *    or %NULL on error.
   1808  **/
   1809 GList *
   1810 g_app_info_get_all_for_type (const char *content_type)
   1811 {
   1812   GList *desktop_entries, *l;
   1813   GList *infos;
   1814   GDesktopAppInfo *info;
   1815 
   1816   g_return_val_if_fail (content_type != NULL, NULL);
   1817 
   1818   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
   1819 
   1820   infos = NULL;
   1821   for (l = desktop_entries; l != NULL; l = l->next)
   1822     {
   1823       char *desktop_entry = l->data;
   1824 
   1825       info = g_desktop_app_info_new (desktop_entry);
   1826       if (info)
   1827 	{
   1828 	  if (app_info_in_list (G_APP_INFO (info), infos))
   1829 	    g_object_unref (info);
   1830 	  else
   1831 	    infos = g_list_prepend (infos, info);
   1832 	}
   1833       g_free (desktop_entry);
   1834     }
   1835 
   1836   g_list_free (desktop_entries);
   1837 
   1838   return g_list_reverse (infos);
   1839 }
   1840 
   1841 /**
   1842  * g_app_info_reset_type_associations:
   1843  * @content_type: a content type
   1844  *
   1845  * Removes all changes to the type associations done by
   1846  * g_app_info_set_as_default_for_type(),
   1847  * g_app_info_set_as_default_for_extension(),
   1848  * g_app_info_add_supports_type() of g_app_info_remove_supports_type().
   1849  *
   1850  * Since: 2.20
   1851  */
   1852 void
   1853 g_app_info_reset_type_associations (const char *content_type)
   1854 {
   1855   update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
   1856 }
   1857 
   1858 /**
   1859  * g_app_info_get_default_for_type:
   1860  * @content_type: the content type to find a #GAppInfo for
   1861  * @must_support_uris: if %TRUE, the #GAppInfo is expected to
   1862  *     support URIs
   1863  *
   1864  * Gets the #GAppInfo that correspond to a given content type.
   1865  *
   1866  * Returns: #GAppInfo for given @content_type or %NULL on error.
   1867  **/
   1868 GAppInfo *
   1869 g_app_info_get_default_for_type (const char *content_type,
   1870 				 gboolean    must_support_uris)
   1871 {
   1872   GList *desktop_entries, *l;
   1873   GAppInfo *info;
   1874 
   1875   g_return_val_if_fail (content_type != NULL, NULL);
   1876 
   1877   desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
   1878 
   1879   info = NULL;
   1880   for (l = desktop_entries; l != NULL; l = l->next)
   1881     {
   1882       char *desktop_entry = l->data;
   1883 
   1884       info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
   1885       if (info)
   1886 	{
   1887 	  if (must_support_uris && !g_app_info_supports_uris (info))
   1888 	    {
   1889 	      g_object_unref (info);
   1890 	      info = NULL;
   1891 	    }
   1892 	  else
   1893 	    break;
   1894 	}
   1895     }
   1896 
   1897   g_list_foreach  (desktop_entries, (GFunc)g_free, NULL);
   1898   g_list_free (desktop_entries);
   1899 
   1900   return info;
   1901 }
   1902 
   1903 /**
   1904  * g_app_info_get_default_for_uri_scheme:
   1905  * @uri_scheme: a string containing a URI scheme.
   1906  *
   1907  * Gets the default application for launching applications
   1908  * using this URI scheme. A URI scheme is the initial part
   1909  * of the URI, up to but not including the ':', e.g. "http",
   1910  * "ftp" or "sip".
   1911  *
   1912  * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
   1913  **/
   1914 GAppInfo *
   1915 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
   1916 {
   1917   static gsize lookup = 0;
   1918 
   1919   if (g_once_init_enter (&lookup))
   1920     {
   1921       gsize setup_value = 1;
   1922       GDesktopAppInfoLookup *lookup_instance;
   1923       const char *use_this;
   1924       GIOExtensionPoint *ep;
   1925       GIOExtension *extension;
   1926       GList *l;
   1927 
   1928       use_this = g_getenv ("GIO_USE_URI_ASSOCIATION");
   1929 
   1930       /* Ensure vfs in modules loaded */
   1931       _g_io_modules_ensure_loaded ();
   1932 
   1933       ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
   1934 
   1935       lookup_instance = NULL;
   1936       if (use_this)
   1937 	{
   1938 	  extension = g_io_extension_point_get_extension_by_name (ep, use_this);
   1939 	  if (extension)
   1940 	    lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
   1941 	}
   1942 
   1943       if (lookup_instance == NULL)
   1944 	{
   1945 	  for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
   1946 	    {
   1947 	      extension = l->data;
   1948 	      lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
   1949 	      if (lookup_instance != NULL)
   1950 		break;
   1951 	    }
   1952 	}
   1953 
   1954       if (lookup_instance != NULL)
   1955 	setup_value = (gsize)lookup_instance;
   1956 
   1957       g_once_init_leave (&lookup, setup_value);
   1958     }
   1959 
   1960   if (lookup == 1)
   1961     return NULL;
   1962 
   1963   return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup),
   1964 							       uri_scheme);
   1965 }
   1966 
   1967 
   1968 static void
   1969 get_apps_from_dir (GHashTable *apps,
   1970                    const char *dirname,
   1971                    const char *prefix)
   1972 {
   1973   GDir *dir;
   1974   const char *basename;
   1975   char *filename, *subprefix, *desktop_id;
   1976   gboolean hidden;
   1977   GDesktopAppInfo *appinfo;
   1978 
   1979   dir = g_dir_open (dirname, 0, NULL);
   1980   if (dir)
   1981     {
   1982       while ((basename = g_dir_read_name (dir)) != NULL)
   1983 	{
   1984 	  filename = g_build_filename (dirname, basename, NULL);
   1985 	  if (g_str_has_suffix (basename, ".desktop"))
   1986 	    {
   1987 	      desktop_id = g_strconcat (prefix, basename, NULL);
   1988 
   1989 	      /* Use _extended so we catch NULLs too (hidden) */
   1990 	      if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
   1991 		{
   1992 		  appinfo = g_desktop_app_info_new_from_filename (filename);
   1993 
   1994 		  if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
   1995 		    {
   1996 		      g_object_unref (appinfo);
   1997 		      appinfo = NULL;
   1998 		      hidden = TRUE;
   1999 		    }
   2000 
   2001 		  if (appinfo || hidden)
   2002 		    {
   2003 		      g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
   2004 
   2005 		      if (appinfo)
   2006 			{
   2007 			  /* Reuse instead of strdup here */
   2008 			  appinfo->desktop_id = desktop_id;
   2009 			  desktop_id = NULL;
   2010 			}
   2011 		    }
   2012 		}
   2013 	      g_free (desktop_id);
   2014 	    }
   2015 	  else
   2016 	    {
   2017 	      if (g_file_test (filename, G_FILE_TEST_IS_DIR))
   2018 		{
   2019 		  subprefix = g_strconcat (prefix, basename, "-", NULL);
   2020 		  get_apps_from_dir (apps, filename, subprefix);
   2021 		  g_free (subprefix);
   2022 		}
   2023 	    }
   2024 	  g_free (filename);
   2025 	}
   2026       g_dir_close (dir);
   2027     }
   2028 }
   2029 
   2030 
   2031 /**
   2032  * g_app_info_get_all:
   2033  *
   2034  * Gets a list of all of the applications currently registered
   2035  * on this system.
   2036  *
   2037  * For desktop files, this includes applications that have
   2038  * <literal>NoDisplay=true</literal> set or are excluded from
   2039  * display by means of <literal>OnlyShowIn</literal> or
   2040  * <literal>NotShowIn</literal>. See g_app_info_should_show().
   2041  * The returned list does not include applications which have
   2042  * the <literal>Hidden</literal> key set.
   2043  *
   2044  * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
   2045  **/
   2046 GList *
   2047 g_app_info_get_all (void)
   2048 {
   2049   const char * const *dirs;
   2050   GHashTable *apps;
   2051   GHashTableIter iter;
   2052   gpointer value;
   2053   int i;
   2054   GList *infos;
   2055 
   2056   dirs = get_applications_search_path ();
   2057 
   2058   apps = g_hash_table_new_full (g_str_hash, g_str_equal,
   2059 				g_free, NULL);
   2060 
   2061 
   2062   for (i = 0; dirs[i] != NULL; i++)
   2063     get_apps_from_dir (apps, dirs[i], "");
   2064 
   2065 
   2066   infos = NULL;
   2067   g_hash_table_iter_init (&iter, apps);
   2068   while (g_hash_table_iter_next (&iter, NULL, &value))
   2069     {
   2070       if (value)
   2071         infos = g_list_prepend (infos, value);
   2072     }
   2073 
   2074   g_hash_table_destroy (apps);
   2075 
   2076   return g_list_reverse (infos);
   2077 }
   2078 
   2079 /* Cacheing of mimeinfo.cache and defaults.list files */
   2080 
   2081 typedef struct {
   2082   char *path;
   2083   GHashTable *mime_info_cache_map;
   2084   GHashTable *defaults_list_map;
   2085   GHashTable *mimeapps_list_added_map;
   2086   GHashTable *mimeapps_list_removed_map;
   2087   time_t mime_info_cache_timestamp;
   2088   time_t defaults_list_timestamp;
   2089   time_t mimeapps_list_timestamp;
   2090 } MimeInfoCacheDir;
   2091 
   2092 typedef struct {
   2093   GList *dirs;                       /* mimeinfo.cache and defaults.list */
   2094   GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
   2095   time_t last_stat_time;
   2096   guint should_ping_mime_monitor : 1;
   2097 } MimeInfoCache;
   2098 
   2099 static MimeInfoCache *mime_info_cache = NULL;
   2100 G_LOCK_DEFINE_STATIC (mime_info_cache);
   2101 
   2102 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
   2103 						     const char        *mime_type,
   2104 						     char             **new_desktop_file_ids);
   2105 
   2106 static MimeInfoCache * mime_info_cache_new (void);
   2107 
   2108 static void
   2109 destroy_info_cache_value (gpointer  key,
   2110                           GList    *value,
   2111                           gpointer  data)
   2112 {
   2113   g_list_foreach (value, (GFunc)g_free, NULL);
   2114   g_list_free (value);
   2115 }
   2116 
   2117 static void
   2118 destroy_info_cache_map (GHashTable *info_cache_map)
   2119 {
   2120   g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
   2121   g_hash_table_destroy (info_cache_map);
   2122 }
   2123 
   2124 static gboolean
   2125 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
   2126 				 const char       *cache_file,
   2127 				 time_t           *timestamp)
   2128 {
   2129   struct stat buf;
   2130   char *filename;
   2131 
   2132   filename = g_build_filename (dir->path, cache_file, NULL);
   2133 
   2134   if (g_stat (filename, &buf) < 0)
   2135     {
   2136       g_free (filename);
   2137       return TRUE;
   2138     }
   2139   g_free (filename);
   2140 
   2141   if (buf.st_mtime != *timestamp)
   2142     return TRUE;
   2143 
   2144   return FALSE;
   2145 }
   2146 
   2147 /* Call with lock held */
   2148 static gboolean
   2149 remove_all (gpointer  key,
   2150 	    gpointer  value,
   2151 	    gpointer  user_data)
   2152 {
   2153   return TRUE;
   2154 }
   2155 
   2156 
   2157 static void
   2158 mime_info_cache_blow_global_cache (void)
   2159 {
   2160   g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
   2161 			       remove_all, NULL);
   2162 }
   2163 
   2164 static void
   2165 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
   2166 {
   2167   GError *load_error;
   2168   GKeyFile *key_file;
   2169   gchar *filename, **mime_types;
   2170   int i;
   2171   struct stat buf;
   2172 
   2173   load_error = NULL;
   2174   mime_types = NULL;
   2175 
   2176   if (dir->mime_info_cache_map != NULL &&
   2177       !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
   2178 					&dir->mime_info_cache_timestamp))
   2179     return;
   2180 
   2181   if (dir->mime_info_cache_map != NULL)
   2182     destroy_info_cache_map (dir->mime_info_cache_map);
   2183 
   2184   dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
   2185 						    (GDestroyNotify) g_free,
   2186 						    NULL);
   2187 
   2188   key_file = g_key_file_new ();
   2189 
   2190   filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
   2191 
   2192   if (g_stat (filename, &buf) < 0)
   2193     goto error;
   2194 
   2195   if (dir->mime_info_cache_timestamp > 0)
   2196     mime_info_cache->should_ping_mime_monitor = TRUE;
   2197 
   2198   dir->mime_info_cache_timestamp = buf.st_mtime;
   2199 
   2200   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
   2201 
   2202   g_free (filename);
   2203   filename = NULL;
   2204 
   2205   if (load_error != NULL)
   2206     goto error;
   2207 
   2208   mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
   2209 				    NULL, &load_error);
   2210 
   2211   if (load_error != NULL)
   2212     goto error;
   2213 
   2214   for (i = 0; mime_types[i] != NULL; i++)
   2215     {
   2216       gchar **desktop_file_ids;
   2217       char *unaliased_type;
   2218       desktop_file_ids = g_key_file_get_string_list (key_file,
   2219 						     MIME_CACHE_GROUP,
   2220 						     mime_types[i],
   2221 						     NULL,
   2222 						     NULL);
   2223 
   2224       if (desktop_file_ids == NULL)
   2225 	continue;
   2226 
   2227       unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
   2228       mime_info_cache_dir_add_desktop_entries (dir,
   2229 					       unaliased_type,
   2230 					       desktop_file_ids);
   2231       g_free (unaliased_type);
   2232 
   2233       g_strfreev (desktop_file_ids);
   2234     }
   2235 
   2236   g_strfreev (mime_types);
   2237   g_key_file_free (key_file);
   2238 
   2239   return;
   2240  error:
   2241   g_free (filename);
   2242   g_key_file_free (key_file);
   2243 
   2244   if (mime_types != NULL)
   2245     g_strfreev (mime_types);
   2246 
   2247   if (load_error)
   2248     g_error_free (load_error);
   2249 }
   2250 
   2251 static void
   2252 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
   2253 {
   2254   GKeyFile *key_file;
   2255   GError *load_error;
   2256   gchar *filename, **mime_types;
   2257   char *unaliased_type;
   2258   char **desktop_file_ids;
   2259   int i;
   2260   struct stat buf;
   2261 
   2262   load_error = NULL;
   2263   mime_types = NULL;
   2264 
   2265   if (dir->defaults_list_map != NULL &&
   2266       !mime_info_cache_dir_out_of_date (dir, "defaults.list",
   2267 					&dir->defaults_list_timestamp))
   2268     return;
   2269 
   2270   if (dir->defaults_list_map != NULL)
   2271     g_hash_table_destroy (dir->defaults_list_map);
   2272   dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
   2273 						  g_free, (GDestroyNotify)g_strfreev);
   2274 
   2275 
   2276   key_file = g_key_file_new ();
   2277 
   2278   filename = g_build_filename (dir->path, "defaults.list", NULL);
   2279   if (g_stat (filename, &buf) < 0)
   2280     goto error;
   2281 
   2282   if (dir->defaults_list_timestamp > 0)
   2283     mime_info_cache->should_ping_mime_monitor = TRUE;
   2284 
   2285   dir->defaults_list_timestamp = buf.st_mtime;
   2286 
   2287   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
   2288   g_free (filename);
   2289   filename = NULL;
   2290 
   2291   if (load_error != NULL)
   2292     goto error;
   2293 
   2294   mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
   2295 				    NULL, NULL);
   2296   if (mime_types != NULL)
   2297     {
   2298       for (i = 0; mime_types[i] != NULL; i++)
   2299 	{
   2300 	  desktop_file_ids = g_key_file_get_string_list (key_file,
   2301 							 DEFAULT_APPLICATIONS_GROUP,
   2302 							 mime_types[i],
   2303 							 NULL,
   2304 							 NULL);
   2305 	  if (desktop_file_ids == NULL)
   2306 	    continue;
   2307 
   2308 	  unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
   2309 	  g_hash_table_replace (dir->defaults_list_map,
   2310 				unaliased_type,
   2311 				desktop_file_ids);
   2312 	}
   2313 
   2314       g_strfreev (mime_types);
   2315     }
   2316 
   2317   g_key_file_free (key_file);
   2318   return;
   2319 
   2320  error:
   2321   g_free (filename);
   2322   g_key_file_free (key_file);
   2323 
   2324   if (mime_types != NULL)
   2325     g_strfreev (mime_types);
   2326 
   2327   if (load_error)
   2328     g_error_free (load_error);
   2329 }
   2330 
   2331 static void
   2332 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
   2333 {
   2334   GKeyFile *key_file;
   2335   GError *load_error;
   2336   gchar *filename, **mime_types;
   2337   char *unaliased_type;
   2338   char **desktop_file_ids;
   2339   int i;
   2340   struct stat buf;
   2341 
   2342   load_error = NULL;
   2343   mime_types = NULL;
   2344 
   2345   if (dir->mimeapps_list_added_map != NULL &&
   2346       !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
   2347 					&dir->mimeapps_list_timestamp))
   2348     return;
   2349 
   2350   if (dir->mimeapps_list_added_map != NULL)
   2351     g_hash_table_destroy (dir->mimeapps_list_added_map);
   2352   dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
   2353 							g_free, (GDestroyNotify)g_strfreev);
   2354 
   2355   if (dir->mimeapps_list_removed_map != NULL)
   2356     g_hash_table_destroy (dir->mimeapps_list_removed_map);
   2357   dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
   2358 							  g_free, (GDestroyNotify)g_strfreev);
   2359 
   2360   key_file = g_key_file_new ();
   2361 
   2362   filename = g_build_filename (dir->path, "mimeapps.list", NULL);
   2363   if (g_stat (filename, &buf) < 0)
   2364     goto error;
   2365 
   2366   if (dir->mimeapps_list_timestamp > 0)
   2367     mime_info_cache->should_ping_mime_monitor = TRUE;
   2368 
   2369   dir->mimeapps_list_timestamp = buf.st_mtime;
   2370 
   2371   g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
   2372   g_free (filename);
   2373   filename = NULL;
   2374 
   2375   if (load_error != NULL)
   2376     goto error;
   2377 
   2378   mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
   2379 				    NULL, NULL);
   2380   if (mime_types != NULL)
   2381     {
   2382       for (i = 0; mime_types[i] != NULL; i++)
   2383 	{
   2384 	  desktop_file_ids = g_key_file_get_string_list (key_file,
   2385 							 ADDED_ASSOCIATIONS_GROUP,
   2386 							 mime_types[i],
   2387 							 NULL,
   2388 							 NULL);
   2389 	  if (desktop_file_ids == NULL)
   2390 	    continue;
   2391 
   2392 	  unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
   2393 	  g_hash_table_replace (dir->mimeapps_list_added_map,
   2394 				unaliased_type,
   2395 				desktop_file_ids);
   2396 	}
   2397 
   2398       g_strfreev (mime_types);
   2399     }
   2400 
   2401   mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
   2402 				    NULL, NULL);
   2403   if (mime_types != NULL)
   2404     {
   2405       for (i = 0; mime_types[i] != NULL; i++)
   2406 	{
   2407 	  desktop_file_ids = g_key_file_get_string_list (key_file,
   2408 							 REMOVED_ASSOCIATIONS_GROUP,
   2409 							 mime_types[i],
   2410 							 NULL,
   2411 							 NULL);
   2412 	  if (desktop_file_ids == NULL)
   2413 	    continue;
   2414 
   2415 	  unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
   2416 	  g_hash_table_replace (dir->mimeapps_list_removed_map,
   2417 				unaliased_type,
   2418 				desktop_file_ids);
   2419 	}
   2420 
   2421       g_strfreev (mime_types);
   2422     }
   2423 
   2424   g_key_file_free (key_file);
   2425   return;
   2426 
   2427  error:
   2428   g_free (filename);
   2429   g_key_file_free (key_file);
   2430 
   2431   if (mime_types != NULL)
   2432     g_strfreev (mime_types);
   2433 
   2434   if (load_error)
   2435     g_error_free (load_error);
   2436 }
   2437 
   2438 static MimeInfoCacheDir *
   2439 mime_info_cache_dir_new (const char *path)
   2440 {
   2441   MimeInfoCacheDir *dir;
   2442 
   2443   dir = g_new0 (MimeInfoCacheDir, 1);
   2444   dir->path = g_strdup (path);
   2445 
   2446   return dir;
   2447 }
   2448 
   2449 static void
   2450 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
   2451 {
   2452   if (dir == NULL)
   2453     return;
   2454 
   2455   if (dir->mime_info_cache_map != NULL)
   2456     {
   2457       destroy_info_cache_map (dir->mime_info_cache_map);
   2458       dir->mime_info_cache_map = NULL;
   2459 
   2460   }
   2461 
   2462   if (dir->defaults_list_map != NULL)
   2463     {
   2464       g_hash_table_destroy (dir->defaults_list_map);
   2465       dir->defaults_list_map = NULL;
   2466     }
   2467 
   2468   if (dir->mimeapps_list_added_map != NULL)
   2469     {
   2470       g_hash_table_destroy (dir->mimeapps_list_added_map);
   2471       dir->mimeapps_list_added_map = NULL;
   2472     }
   2473 
   2474   if (dir->mimeapps_list_removed_map != NULL)
   2475     {
   2476       g_hash_table_destroy (dir->mimeapps_list_removed_map);
   2477       dir->mimeapps_list_removed_map = NULL;
   2478     }
   2479 
   2480   g_free (dir);
   2481 }
   2482 
   2483 static void
   2484 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir  *dir,
   2485 					 const char        *mime_type,
   2486 					 char             **new_desktop_file_ids)
   2487 {
   2488   GList *desktop_file_ids;
   2489   int i;
   2490 
   2491   desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
   2492 					  mime_type);
   2493 
   2494   for (i = 0; new_desktop_file_ids[i] != NULL; i++)
   2495     {
   2496       if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i]))
   2497 	desktop_file_ids = g_list_append (desktop_file_ids,
   2498 					  g_strdup (new_desktop_file_ids[i]));
   2499     }
   2500 
   2501   g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
   2502 }
   2503 
   2504 static void
   2505 mime_info_cache_init_dir_lists (void)
   2506 {
   2507   const char * const *dirs;
   2508   int i;
   2509 
   2510   mime_info_cache = mime_info_cache_new ();
   2511 
   2512   dirs = get_applications_search_path ();
   2513 
   2514   for (i = 0; dirs[i] != NULL; i++)
   2515     {
   2516       MimeInfoCacheDir *dir;
   2517 
   2518       dir = mime_info_cache_dir_new (dirs[i]);
   2519 
   2520       if (dir != NULL)
   2521 	{
   2522 	  mime_info_cache_dir_init (dir);
   2523 	  mime_info_cache_dir_init_defaults_list (dir);
   2524 	  mime_info_cache_dir_init_mimeapps_list (dir);
   2525 
   2526 	  mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
   2527 	}
   2528     }
   2529 }
   2530 
   2531 static void
   2532 mime_info_cache_update_dir_lists (void)
   2533 {
   2534   GList *tmp;
   2535 
   2536   tmp = mime_info_cache->dirs;
   2537 
   2538   while (tmp != NULL)
   2539     {
   2540       MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
   2541 
   2542       /* No need to do this if we had file monitors... */
   2543       mime_info_cache_blow_global_cache ();
   2544       mime_info_cache_dir_init (dir);
   2545       mime_info_cache_dir_init_defaults_list (dir);
   2546       mime_info_cache_dir_init_mimeapps_list (dir);
   2547 
   2548       tmp = tmp->next;
   2549     }
   2550 }
   2551 
   2552 static void
   2553 mime_info_cache_init (void)
   2554 {
   2555   G_LOCK (mime_info_cache);
   2556   if (mime_info_cache == NULL)
   2557     mime_info_cache_init_dir_lists ();
   2558   else
   2559     {
   2560       time_t now;
   2561 
   2562       time (&now);
   2563       if (now >= mime_info_cache->last_stat_time + 10)
   2564 	{
   2565 	  mime_info_cache_update_dir_lists ();
   2566 	  mime_info_cache->last_stat_time = now;
   2567 	}
   2568     }
   2569 
   2570   if (mime_info_cache->should_ping_mime_monitor)
   2571     {
   2572       /* g_idle_add (emit_mime_changed, NULL); */
   2573       mime_info_cache->should_ping_mime_monitor = FALSE;
   2574     }
   2575 
   2576   G_UNLOCK (mime_info_cache);
   2577 }
   2578 
   2579 static MimeInfoCache *
   2580 mime_info_cache_new (void)
   2581 {
   2582   MimeInfoCache *cache;
   2583 
   2584   cache = g_new0 (MimeInfoCache, 1);
   2585 
   2586   cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
   2587 							(GDestroyNotify) g_free,
   2588 							(GDestroyNotify) g_free);
   2589   return cache;
   2590 }
   2591 
   2592 static void
   2593 mime_info_cache_free (MimeInfoCache *cache)
   2594 {
   2595   if (cache == NULL)
   2596     return;
   2597 
   2598   g_list_foreach (cache->dirs,
   2599 		  (GFunc) mime_info_cache_dir_free,
   2600 		  NULL);
   2601   g_list_free (cache->dirs);
   2602   g_hash_table_destroy (cache->global_defaults_cache);
   2603   g_free (cache);
   2604 }
   2605 
   2606 /**
   2607  * mime_info_cache_reload:
   2608  * @dir: directory path which needs reloading.
   2609  *
   2610  * Reload the mime information for the @dir.
   2611  */
   2612 static void
   2613 mime_info_cache_reload (const char *dir)
   2614 {
   2615   /* FIXME: just reload the dir that needs reloading,
   2616    * don't blow the whole cache
   2617    */
   2618   if (mime_info_cache != NULL)
   2619     {
   2620       G_LOCK (mime_info_cache);
   2621       mime_info_cache_free (mime_info_cache);
   2622       mime_info_cache = NULL;
   2623       G_UNLOCK (mime_info_cache);
   2624     }
   2625 }
   2626 
   2627 static GList *
   2628 append_desktop_entry (GList      *list,
   2629                       const char *desktop_entry,
   2630 		      GList      *removed_entries)
   2631 {
   2632   /* Add if not already in list, and valid */
   2633   if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
   2634       !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
   2635     list = g_list_prepend (list, g_strdup (desktop_entry));
   2636 
   2637   return list;
   2638 }
   2639 
   2640 /**
   2641  * get_all_desktop_entries_for_mime_type:
   2642  * @mime_type: a mime type.
   2643  * @except: NULL or a strv list
   2644  *
   2645  * Returns all the desktop ids for @mime_type. The desktop files
   2646  * are listed in an order so that default applications are listed before
   2647  * non-default ones, and handlers for inherited mimetypes are listed
   2648  * after the base ones.
   2649  *
   2650  * Optionally doesn't list the desktop ids given in the @except
   2651  *
   2652  * Return value: a #GList containing the desktop ids which claim
   2653  *    to handle @mime_type.
   2654  */
   2655 static GList *
   2656 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
   2657 				       const char **except)
   2658 {
   2659   GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
   2660   MimeInfoCacheDir *dir;
   2661   char *mime_type;
   2662   char **mime_types;
   2663   char **default_entries;
   2664   char **removed_associations;
   2665   int i, j, k;
   2666   GPtrArray *array;
   2667   char **anc;
   2668 
   2669   mime_info_cache_init ();
   2670 
   2671   /* collect all ancestors */
   2672   mime_types = _g_unix_content_type_get_parents (base_mime_type);
   2673   array = g_ptr_array_new ();
   2674   for (i = 0; mime_types[i]; i++)
   2675     g_ptr_array_add (array, mime_types[i]);
   2676   g_free (mime_types);
   2677   for (i = 0; i < array->len; i++)
   2678     {
   2679       anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
   2680       for (j = 0; anc[j]; j++)
   2681         {
   2682           for (k = 0; k < array->len; k++)
   2683             {
   2684               if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
   2685                 break;
   2686             }
   2687           if (k == array->len) /* not found */
   2688             g_ptr_array_add (array, g_strdup (anc[j]));
   2689         }
   2690       g_strfreev (anc);
   2691     }
   2692   g_ptr_array_add (array, NULL);
   2693   mime_types = (char **)g_ptr_array_free (array, FALSE);
   2694 
   2695   G_LOCK (mime_info_cache);
   2696 
   2697   removed_entries = NULL;
   2698   desktop_entries = NULL;
   2699 
   2700   for (i = 0; except != NULL && except[i] != NULL; i++)
   2701     removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
   2702 
   2703   for (i = 0; mime_types[i] != NULL; i++)
   2704     {
   2705       mime_type = mime_types[i];
   2706 
   2707       /* Go through all apps listed as defaults */
   2708       for (dir_list = mime_info_cache->dirs;
   2709 	   dir_list != NULL;
   2710 	   dir_list = dir_list->next)
   2711 	{
   2712 	  dir = dir_list->data;
   2713 
   2714 	  /* First added associations from mimeapps.list */
   2715 	  default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
   2716 	  for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
   2717 	    desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
   2718 
   2719 	  /* Then removed associations from mimeapps.list */
   2720 	  removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
   2721 	  for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
   2722 	    removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
   2723 
   2724 	  /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
   2725 	  default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
   2726 	  for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
   2727 	    desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
   2728 	}
   2729 
   2730       /* Go through all entries that support the mimetype */
   2731       for (dir_list = mime_info_cache->dirs;
   2732 	   dir_list != NULL;
   2733 	   dir_list = dir_list->next)
   2734         {
   2735 	  dir = dir_list->data;
   2736 
   2737 	  list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
   2738 	  for (tmp = list; tmp != NULL; tmp = tmp->next)
   2739 	    desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
   2740         }
   2741     }
   2742 
   2743   G_UNLOCK (mime_info_cache);
   2744 
   2745   g_strfreev (mime_types);
   2746 
   2747   g_list_foreach (removed_entries, (GFunc)g_free, NULL);
   2748   g_list_free (removed_entries);
   2749 
   2750   desktop_entries = g_list_reverse (desktop_entries);
   2751 
   2752   return desktop_entries;
   2753 }
   2754 
   2755 /* GDesktopAppInfoLookup interface: */
   2756 
   2757 static void g_desktop_app_info_lookup_base_init (gpointer g_class);
   2758 static void g_desktop_app_info_lookup_class_init (gpointer g_class,
   2759 						  gpointer class_data);
   2760 
   2761 GType
   2762 g_desktop_app_info_lookup_get_type (void)
   2763 {
   2764   static volatile gsize g_define_type_id__volatile = 0;
   2765 
   2766   if (g_once_init_enter (&g_define_type_id__volatile))
   2767     {
   2768       const GTypeInfo desktop_app_info_lookup_info =
   2769       {
   2770         sizeof (GDesktopAppInfoLookupIface), /* class_size */
   2771 	g_desktop_app_info_lookup_base_init,   /* base_init */
   2772 	NULL,		/* base_finalize */
   2773 	g_desktop_app_info_lookup_class_init,
   2774 	NULL,		/* class_finalize */
   2775 	NULL,		/* class_data */
   2776 	0,
   2777 	0,              /* n_preallocs */
   2778 	NULL
   2779       };
   2780       GType g_define_type_id =
   2781 	g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLookup"),
   2782 				&desktop_app_info_lookup_info, 0);
   2783 
   2784       g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
   2785 
   2786       g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
   2787     }
   2788 
   2789   return g_define_type_id__volatile;
   2790 }
   2791 
   2792 static void
   2793 g_desktop_app_info_lookup_class_init (gpointer g_class,
   2794 				      gpointer class_data)
   2795 {
   2796 }
   2797 
   2798 static void
   2799 g_desktop_app_info_lookup_base_init (gpointer g_class)
   2800 {
   2801 }
   2802 
   2803 /**
   2804  * g_desktop_app_info_lookup_get_default_for_uri_scheme:
   2805  * @lookup: a #GDesktopAppInfoLookup
   2806  * @uri_scheme: a string containing a URI scheme.
   2807  *
   2808  * Gets the default application for launching applications
   2809  * using this URI scheme for a particular GDesktopAppInfoLookup
   2810  * implementation.
   2811  *
   2812  * The GDesktopAppInfoLookup interface and this function is used
   2813  * to implement g_app_info_get_default_for_uri_scheme() backends
   2814  * in a GIO module. There is no reason for applications to use it
   2815  * directly. Applications should use g_app_info_get_default_for_uri_scheme().
   2816  *
   2817  * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
   2818  */
   2819 GAppInfo *
   2820 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
   2821 						      const char            *uri_scheme)
   2822 {
   2823   GDesktopAppInfoLookupIface *iface;
   2824 
   2825   g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
   2826 
   2827   iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
   2828 
   2829   return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
   2830 }
   2831 
   2832 #define __G_DESKTOP_APP_INFO_C__
   2833 #include "gioaliasdef.c"
   2834