1 
2 /* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
3 /*
4  * Copyright (C) 2011 Tiger Soldier <tigersoldier@gmail.com>
5  *
6  * This file is part of OSD Lyrics.
7  *
8  * OSD Lyrics is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * OSD Lyrics is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with OSD Lyrics.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 #include <string.h>
22 #include "ol_app_info.h"
23 #include "ol_intl.h"
24 #include "ol_debug.h"
25 
26 struct _OlAppInfo
27 {
28   GObject parent;
29   gchar *cmdline;
30   gchar *name;
31   GIcon *icon;
32   gchar *binfile;
33   gboolean should_show;
34 };
35 
36 static gchar *_str_join_argv (gchar **argv);
37 static gboolean _shell_arg_need_quote (const gchar *arg);
38 static gchar *_find_binfile (const gchar *binfile, gboolean match_prefix);
39 static gchar *_find_file_in_path_list (GList *path_list,
40                                        const gchar *prefix,
41                                        const gchar *suffix,
42                                        gboolean match_prefix,
43                                        gboolean (*file_test_func) (const gchar *));
44 static gboolean _file_is_executable (const gchar *filename);
45 static gboolean _file_exists (const gchar *filename);
46 static GIcon *_icon_new_from_name (const gchar *icon_name);
47 static void _app_info_set_from_desktop_file (OlAppInfo *info,
48                                              enum OlAppInfoFlags flags);
49 static void ol_app_info_finalize (GObject *object);
50 static void ol_app_info_iface_init (GAppInfoIface *iface);
51 static void ol_app_info_class_init (OlAppInfoClass *klass);
52 static void ol_app_info_init (OlAppInfo *info);
53 static void _strv_replace (gchar **argv,
54                            guint index,
55                            const gchar *new_value);
56 static GList *_get_desktop_file_path_list ();
57 static gpointer _get_desktop_file_path_list_once (gpointer data);
58 static GList *_prepend_subdirs (GList *list);
59 /* -----------GAppInfo interfaces--------------- */
60 static GAppInfo *_app_info_dup (GAppInfo *appinfo);
61 static gboolean _app_info_equal (GAppInfo *appinfo1,
62                                  GAppInfo *appinfo2);
63 static const char *_app_info_get_id (GAppInfo *appinfo);
64 static const char *_app_info_get_name (GAppInfo *appinfo);
65 static const char *_app_info_get_display_name (GAppInfo *appinfo);
66 static const char *_app_info_get_description (GAppInfo *appinfo);
67 static const char *_app_info_get_executable (GAppInfo *appinfo);
68 static const char *_app_info_get_commandline (GAppInfo *appinfo);
69 static GIcon *_app_info_get_icon (GAppInfo *appinfo);
70 static gboolean _app_info_launch (GAppInfo *appinfo,
71                                   GList *files,
72                                   GAppLaunchContext *launch_context,
73                                   GError **error);
74 static gboolean _app_info_supports_uris (GAppInfo *appinfo);
75 static gboolean _app_info_supports_files (GAppInfo *appinfo);
76 static gboolean _app_info_launch_uris (GAppInfo *appinfo,
77                                        GList *uris,
78                                        GAppLaunchContext *launch_context,
79                                        GError **error);
80 static gboolean _app_info_should_show (GAppInfo *appinfo);
81 static gboolean _app_info_set_as_default_for_type (GAppInfo *appinfo,
82                                                    const char *content_type,
83                                                    GError **error);
84 static gboolean _app_info_set_as_default_for_extension (GAppInfo *appinfo,
85                                                         const char *extension,
86                                                         GError **error);
87 static gboolean _app_info_add_supports_type (GAppInfo *appinfo,
88                                              const char *content_type,
89                                              GError **error);
90 static gboolean _app_info_can_remove_supports_type (GAppInfo *appinfo);
91 static gboolean _app_info_remove_supports_type (GAppInfo *appinfo,
92                                                 const char *content_type,
93                                                 GError **error);
94 static gboolean _app_info_can_delete (GAppInfo *appinfo);
95 static gboolean _app_info_do_delete (GAppInfo *appinfo);
96 
97 G_DEFINE_TYPE_WITH_CODE (OlAppInfo, ol_app_info, G_TYPE_OBJECT,
98                          G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
99                                                 ol_app_info_iface_init));
100 
101 static void
ol_app_info_class_init(OlAppInfoClass * klass)102 ol_app_info_class_init (OlAppInfoClass *klass)
103 {
104   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
105   gobject_class->finalize = ol_app_info_finalize;
106 }
107 
108 static void
ol_app_info_init(OlAppInfo * info)109 ol_app_info_init (OlAppInfo *info)
110 {
111 }
112 
113 static void
ol_app_info_iface_init(GAppInfoIface * iface)114 ol_app_info_iface_init (GAppInfoIface *iface)
115 {
116   iface->dup = _app_info_dup;
117   iface->equal = _app_info_equal;
118   iface->get_id = _app_info_get_id;
119   iface->get_name = _app_info_get_name;
120   iface->get_description = _app_info_get_description;
121   iface->get_executable = _app_info_get_executable;
122   iface->get_icon = _app_info_get_icon;
123   iface->launch = _app_info_launch;
124   iface->supports_uris = _app_info_supports_uris;
125   iface->supports_files = _app_info_supports_files;
126   iface->launch_uris = _app_info_launch_uris;
127   iface->should_show = _app_info_should_show;
128   iface->set_as_default_for_type = _app_info_set_as_default_for_type;
129   iface->set_as_default_for_extension = _app_info_set_as_default_for_extension;
130   iface->add_supports_type = _app_info_add_supports_type;
131   iface->can_remove_supports_type = _app_info_can_remove_supports_type;
132   iface->remove_supports_type = _app_info_remove_supports_type;
133   iface->can_delete = _app_info_can_delete;
134   iface->do_delete = _app_info_do_delete;
135   iface->get_commandline = _app_info_get_commandline;
136   iface->get_display_name = _app_info_get_display_name;
137 }
138 
139 static GAppInfo *
_app_info_dup(GAppInfo * appinfo)140 _app_info_dup (GAppInfo *appinfo)
141 {
142   OlAppInfo *info = OL_APP_INFO (appinfo);
143   OlAppInfo *new_info;
144 
145   new_info = g_object_new (OL_TYPE_APP_INFO, NULL);
146   new_info->cmdline = g_strdup (info->cmdline);
147   new_info->name = g_strdup (info->name);
148   new_info->binfile = g_strdup (info->binfile);
149   if (info->icon)
150     new_info->icon = g_object_ref (info->icon);
151   new_info->should_show = info->should_show;
152   return G_APP_INFO (new_info);
153 }
154 
155 static gboolean
_app_info_equal(GAppInfo * appinfo1,GAppInfo * appinfo2)156 _app_info_equal (GAppInfo *appinfo1,
157                  GAppInfo *appinfo2)
158 {
159   OlAppInfo *info1 = OL_APP_INFO (appinfo1);
160   OlAppInfo *info2 = OL_APP_INFO (appinfo2);
161 
162   return strcmp (info1->cmdline, info2->cmdline) == 0;
163 }
164 
165 static const char *
_app_info_get_id(GAppInfo * appinfo)166 _app_info_get_id (GAppInfo *appinfo)
167 {
168   OlAppInfo *info = OL_APP_INFO (appinfo);
169 
170   return info->cmdline;
171 }
172 
173 static const char *
_app_info_get_name(GAppInfo * appinfo)174 _app_info_get_name (GAppInfo *appinfo)
175 {
176   OlAppInfo *info = OL_APP_INFO (appinfo);
177 
178   if (info->name == NULL)
179     return _("Unnamed");
180   return info->name;
181 }
182 
183 static const char *
_app_info_get_display_name(GAppInfo * appinfo)184 _app_info_get_display_name (GAppInfo *appinfo)
185 {
186   OlAppInfo *info = OL_APP_INFO (appinfo);
187 
188   if (info->name == NULL)
189     return _("Unnamed");
190   return info->name;
191 }
192 
193 static const char *
_app_info_get_description(GAppInfo * appinfo)194 _app_info_get_description (GAppInfo *appinfo)
195 {
196   return "";
197 }
198 
199 static const char *
_app_info_get_executable(GAppInfo * appinfo)200 _app_info_get_executable (GAppInfo *appinfo)
201 {
202   OlAppInfo *info = OL_APP_INFO (appinfo);
203 
204   return info->binfile;
205 }
206 
207 static const char *
_app_info_get_commandline(GAppInfo * appinfo)208 _app_info_get_commandline (GAppInfo *appinfo)
209 {
210   OlAppInfo *info = OL_APP_INFO (appinfo);
211 
212   return info->cmdline;
213 }
214 
215 static GIcon *
_app_info_get_icon(GAppInfo * appinfo)216 _app_info_get_icon (GAppInfo *appinfo)
217 {
218   OlAppInfo *info = OL_APP_INFO (appinfo);
219 
220   return info->icon;
221 }
222 
223 static gboolean
_app_info_launch(GAppInfo * appinfo,GList * files,GAppLaunchContext * launch_context,GError ** error)224 _app_info_launch (GAppInfo *appinfo,
225                   GList *files,
226                   GAppLaunchContext *launch_context,
227                   GError **error)
228 {
229   OlAppInfo *info = OL_APP_INFO (appinfo);
230   /* TODO: implement launching. */
231   GAppInfo* app = g_app_info_create_from_commandline (info->cmdline, "", 0, NULL);
232   gboolean ret = g_app_info_launch (app, files, launch_context, error);
233   g_object_unref (G_OBJECT (app));
234   return ret;
235 }
236 
237 static gboolean
_app_info_supports_uris(GAppInfo * appinfo)238 _app_info_supports_uris (GAppInfo *appinfo)
239 {
240   return FALSE;
241 }
242 
243 static gboolean
_app_info_supports_files(GAppInfo * appinfo)244 _app_info_supports_files (GAppInfo *appinfo)
245 {
246   return FALSE;
247 }
248 
249 static gboolean
_app_info_launch_uris(GAppInfo * appinfo,GList * uris,GAppLaunchContext * launch_context,GError ** error)250 _app_info_launch_uris (GAppInfo *appinfo,
251                        GList *uris,
252                        GAppLaunchContext *launch_context,
253                        GError **error)
254 {
255   OlAppInfo *info = OL_APP_INFO (appinfo);
256   /* TODO: implement launching. */
257   GAppInfo* app = g_app_info_create_from_commandline (info->cmdline, "", 0, NULL);
258   gboolean ret = g_app_info_launch_uris (app, uris, launch_context, error);
259   g_object_unref (G_OBJECT (app));
260   return ret;
261 }
262 
263 static gboolean
_app_info_should_show(GAppInfo * appinfo)264 _app_info_should_show (GAppInfo *appinfo)
265 {
266   OlAppInfo *info = OL_APP_INFO (appinfo);
267   return info->should_show;
268 }
269 
270 static gboolean
_app_info_set_as_default_for_type(GAppInfo * appinfo,const char * content_type,GError ** error)271 _app_info_set_as_default_for_type (GAppInfo *appinfo,
272                                    const char *content_type,
273                                    GError **error)
274 {
275   if (error)
276     *error = g_error_new (g_quark_from_string ("OSD Lyrics"),
277                           0,    /* error code */
278                           "OlAppInfo does not support set_as_default_for_type");
279   return FALSE;
280 }
281 
282 static gboolean
_app_info_set_as_default_for_extension(GAppInfo * appinfo,const char * extension,GError ** error)283 _app_info_set_as_default_for_extension (GAppInfo *appinfo,
284                                         const char *extension,
285                                         GError **error)
286 {
287   if (error)
288     *error = g_error_new (g_quark_from_string ("OSD Lyrics"),
289                           0,    /* error code */
290                           "OlAppInfo does not support set_as_default_for_extension");
291   return FALSE;
292 }
293 
294 static gboolean
_app_info_add_supports_type(GAppInfo * appinfo,const char * content_type,GError ** error)295 _app_info_add_supports_type (GAppInfo *appinfo,
296                              const char *content_type,
297                              GError **error)
298 {
299   if (error)
300     *error = g_error_new (g_quark_from_string ("OSD Lyrics"),
301                           0,    /* error code */
302                           "OlAppInfo does not support add_supports_type");
303   return FALSE;
304 }
305 
306 static gboolean
_app_info_can_remove_supports_type(GAppInfo * appinfo)307 _app_info_can_remove_supports_type (GAppInfo *appinfo)
308 {
309   return FALSE;
310 }
311 
312 static gboolean
_app_info_remove_supports_type(GAppInfo * appinfo,const char * content_type,GError ** error)313 _app_info_remove_supports_type (GAppInfo *appinfo,
314                                 const char *content_type,
315                                 GError **error)
316 {
317   if (error)
318     *error = g_error_new (g_quark_from_string ("OSD Lyrics"),
319                           0,    /* error code */
320                           "OlAppInfo does not support remove_supports_type");
321   return FALSE;
322 }
323 
324 static gboolean
_app_info_can_delete(GAppInfo * appinfo)325 _app_info_can_delete (GAppInfo *appinfo)
326 {
327   return FALSE;
328 }
329 
330 static gboolean
_app_info_do_delete(GAppInfo * appinfo)331 _app_info_do_delete (GAppInfo *appinfo)
332 {
333   return FALSE;
334 }
335 
336 OlAppInfo *
ol_app_info_new(const char * cmdline,const char * name,const char * icon_name,enum OlAppInfoFlags flags,GError ** error)337 ol_app_info_new (const char *cmdline,
338                  const char *name,
339                  const char *icon_name,
340                  enum OlAppInfoFlags flags,
341                  GError **error)
342 {
343   if (cmdline == NULL)
344   {
345     if (error)
346       *error = g_error_new (g_quark_from_string ("OSD Lyrics"),
347                             0,
348                             "cmdline cannot be NULL");
349     return NULL;
350   }
351   int cmd_index = 0;
352   if (flags & OL_APP_INFO_SECOND_IS_EXEC)
353     cmd_index = 1;
354   gchar **argv = NULL;
355   gint argc = 0;
356   GError *parse_error = NULL;
357   if (!g_shell_parse_argv (cmdline, &argc, &argv, &parse_error))
358   {
359     if (error)
360       *error = g_error_new (g_quark_from_string ("OSD Lyrics"),
361                             0,
362                             "Cannot parse cmdline: %s", parse_error->message);
363     g_error_free (parse_error);
364     return NULL;
365   }
366   if (argc <= cmd_index)
367   {
368     if (error)
369       *error = g_error_new (g_quark_from_string ("OSD Lyrics"),
370                             0,
371                             "Command name is required in cmdline");
372     g_strfreev (argv);
373     return NULL;
374   }
375   OlAppInfo *info = g_object_new (OL_TYPE_APP_INFO, NULL);
376   info->binfile = _find_binfile (argv[cmd_index],
377                                  (flags & OL_APP_INFO_WITH_PREFIX) != 0);
378   if (info->binfile == NULL)
379   {
380     info->binfile = g_strdup (argv[cmd_index]);
381     info->cmdline = g_strdup (cmdline);
382     info->should_show = FALSE;
383   }
384   else
385   {
386     info->should_show = TRUE;
387     _strv_replace (argv, cmd_index, info->binfile);
388     info->cmdline = _str_join_argv (argv);
389   }
390   if (!name)
391     name = info->binfile;
392   if (!icon_name)
393     icon_name = info->binfile;
394   info->icon = _icon_new_from_name (icon_name);
395   info->name = g_strdup (name);
396   if (flags & OL_APP_INFO_PREFER_DESKTOP_FILE)
397     _app_info_set_from_desktop_file (info, flags);
398   return info;
399 }
400 
401 static gchar *
_str_join_argv(gchar ** argv)402 _str_join_argv (gchar **argv)
403 {
404   GString *str_builder = g_string_new ("");
405   gboolean first = TRUE;
406   while (argv && *argv)
407   {
408     if (!first)
409       g_string_append_c (str_builder, ' ');
410     else
411       first = FALSE;
412     if (_shell_arg_need_quote (*argv))
413     {
414       gchar *quoted = g_shell_quote (*argv);
415       g_string_append (str_builder, quoted);
416       g_free (quoted);
417     }
418     else
419     {
420       g_string_append (str_builder, *argv);
421     }
422     argv++;
423   }
424   return g_string_free (str_builder, FALSE);
425 }
426 
427 static gboolean
_shell_arg_need_quote(const gchar * arg)428 _shell_arg_need_quote (const gchar *arg)
429 {
430   if (arg == NULL)
431     return FALSE;
432   if (!strchr (arg, ' ') && !strchr (arg, '\t') && !strchr (arg, '\n') &&
433       !strchr (arg, '\'') && !strchr (arg, '\"') && !strchr (arg, '\\'))
434     return FALSE;
435   else
436     return TRUE;
437 }
438 
439 static gchar *
_find_binfile(const gchar * binfile,gboolean match_prefix)440 _find_binfile (const gchar *binfile, gboolean match_prefix)
441 {
442   GList *path_list = NULL;
443   gchar *ret = NULL;
444   if (g_path_is_absolute (binfile))
445   {
446     gchar *dirname = g_path_get_dirname (binfile);
447     path_list = g_list_append (path_list, dirname);
448     binfile += strlen (dirname);
449     if (*binfile == G_DIR_SEPARATOR)
450       binfile++;
451   }
452   else
453   {
454     const char *env_path = g_getenv ("PATH");
455     if (!env_path)
456     {
457       env_path = "/bin:/usr/bin";
458     }
459     gchar **pathv = g_strsplit (env_path, G_SEARCHPATH_SEPARATOR_S, -1);
460     gchar **pathiter;
461     for (pathiter = pathv; *pathiter != NULL; pathiter++)
462     {
463       if (*pathiter != '\0')
464         path_list = g_list_prepend (path_list, g_strdup (*pathiter));
465     }
466     path_list = g_list_reverse (path_list);
467     g_strfreev (pathv);
468   }
469   gchar *filepath = _find_file_in_path_list (path_list,
470                                              binfile,
471                                              "",
472                                              match_prefix,
473                                              _file_is_executable);
474   if (filepath)
475   {
476     ret = g_path_get_basename (filepath);
477     g_free (filepath);
478   }
479   for (; path_list != NULL; path_list = g_list_delete_link (path_list, path_list))
480     g_free (path_list->data);
481   return ret;
482 }
483 
484 /**
485  *
486  *
487  * @param path_list A GList of gchar*.
488  * @param prefix The prefix of the filename, cannot be #NULL.
489  * @param suffix The suffix of filename. If #match_prefix is #TRUE, #NULL or
490  *               empty string means any suffix is acceptable.
491  * @param match_prefix
492  *
493  * @return
494  */
495 static gchar *
_find_file_in_path_list(GList * path_list,const gchar * prefix,const gchar * suffix,gboolean match_prefix,gboolean (* file_test_func)(const gchar *))496 _find_file_in_path_list (GList *path_list,
497                          const gchar *prefix,
498                          const gchar *suffix,
499                          gboolean match_prefix,
500                          gboolean (*file_test_func) (const gchar *))
501 {
502   ol_assert_ret (prefix != NULL, NULL);
503   if (suffix == NULL)
504     suffix = "";
505   if (!file_test_func)
506     file_test_func = _file_exists;
507   GList *pathiter;
508   gchar *ret = NULL;
509   for (pathiter = path_list; pathiter != NULL; pathiter = g_list_next (pathiter))
510   {
511     gchar *path = pathiter->data;
512     if (!match_prefix)
513     {
514       gchar *filename = NULL;
515       gchar *fullname = g_strdup_printf ("%s%s", prefix, suffix);
516       filename = g_build_filename (path, fullname, NULL);
517       g_free (fullname);
518       if (file_test_func (filename))
519         ret = filename;
520       else
521         g_free (filename);
522     } /* if !match_prefix */
523     else
524     {
525       GError *error = NULL;
526       GDir *dir = g_dir_open (path, 0, &error);
527       if (!dir)
528       {
529         ol_errorf ("Cannot open path %s: %s\n", path, error->message);
530         g_error_free (error);
531         continue;
532       }
533       const gchar *name;
534       while ((name = g_dir_read_name (dir)) != NULL)
535       {
536         if (g_str_has_prefix (name, prefix) && g_str_has_suffix (name, suffix))
537         {
538           gchar *filename = g_build_filename (path, name, NULL);
539           if (file_test_func (filename) &&
540               (!ret || strcmp (filename, ret) < 0))
541           {
542             g_free (ret);
543             ret = filename;
544           }
545           else
546           {
547             g_free (filename);
548           }
549         }
550       }
551       g_dir_close (dir);
552     }
553     if (ret != NULL) break;
554   }
555   return ret;
556 }
557 
558 static gboolean
_file_is_executable(const gchar * filename)559 _file_is_executable (const gchar *filename)
560 {
561   return (g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE) &&
562           !g_file_test (filename, G_FILE_TEST_IS_DIR));
563 }
564 
565 static gboolean
_file_exists(const gchar * filename)566 _file_exists (const gchar *filename)
567 {
568   return (g_file_test (filename, G_FILE_TEST_EXISTS) &&
569           !g_file_test (filename, G_FILE_TEST_IS_DIR));
570 }
571 
572 static GIcon *
_icon_new_from_name(const gchar * icon_name)573 _icon_new_from_name (const gchar *icon_name)
574 {
575   ol_assert_ret (icon_name != NULL, NULL);
576   /* This is taken from gdesktopappinfo.c of GIO */
577   GIcon *icon = NULL;
578   if (g_path_is_absolute (icon_name))
579   {
580     GFile *file;
581     file = g_file_new_for_path (icon_name);
582     icon = g_file_icon_new (file);
583     g_object_unref (file);
584   }
585   else
586   {
587     char *p;
588     /* Work around a common mistake in desktop files */
589     if ((p = strrchr (icon_name, '.')) != NULL &&
590         (strcmp (p, ".png") == 0 ||
591          strcmp (p, ".xpm") == 0 ||
592          strcmp (p, ".svg") == 0))
593     {
594       gchar *real_name = g_strndup (icon_name, p - icon_name);
595       icon = g_themed_icon_new (real_name);
596       g_free (real_name);
597     }
598     else
599     {
600       icon = g_themed_icon_new (icon_name);
601     }
602   }
603   return icon;
604 }
605 
606 static void
_app_info_set_from_desktop_file(OlAppInfo * info,enum OlAppInfoFlags flags)607 _app_info_set_from_desktop_file (OlAppInfo *info,
608                                  enum OlAppInfoFlags flags)
609 {
610   GList *path_list = _get_desktop_file_path_list ();
611   gchar *filename = _find_file_in_path_list (path_list,
612                                              info->binfile,
613                                              ".desktop",
614                                              (flags & OL_APP_INFO_WITH_PREFIX) != 0,
615                                              NULL);
616   if (!filename)
617   {
618     ol_debugf ("Cannot find desktop file for %s\n", info->binfile);
619     return;
620   }
621   GKeyFile *keyfile = g_key_file_new ();
622   GError *error = NULL;
623   if (!g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
624   {
625     ol_errorf ("Cannot open desktop file %s: %s\n", filename, error->message);
626     g_error_free (error);
627   }
628   else
629   {
630     if (flags & OL_APP_INFO_USE_DESKTOP_NAME)
631     {
632       gchar *name = g_key_file_get_locale_string (keyfile,
633                                                   G_KEY_FILE_DESKTOP_GROUP,
634                                                   G_KEY_FILE_DESKTOP_KEY_NAME,
635                                                   NULL,
636                                                   NULL);
637       if (name != NULL)
638       {
639         if (info->name != NULL)
640           g_free (info->name);
641         info->name = name;
642       }
643     }
644     if (flags & OL_APP_INFO_USE_DESKTOP_CMDLINE)
645     {
646       gchar *cmdline = g_key_file_get_locale_string (keyfile,
647                                                      G_KEY_FILE_DESKTOP_GROUP,
648                                                      G_KEY_FILE_DESKTOP_KEY_EXEC,
649                                                      NULL,
650                                                      NULL);
651       if (cmdline != NULL)
652       {
653         if (info->cmdline != NULL)
654           g_free (info->cmdline);
655         info->cmdline = cmdline;
656       }
657     }
658     if (flags & OL_APP_INFO_USE_DESKTOP_ICON)
659     {
660       gchar *icon_name = g_key_file_get_locale_string (keyfile,
661                                                        G_KEY_FILE_DESKTOP_GROUP,
662                                                        G_KEY_FILE_DESKTOP_KEY_ICON,
663                                                        NULL,
664                                                        NULL);
665       GIcon *icon = _icon_new_from_name (icon_name);
666       if (icon != NULL)
667       {
668         if (info->icon != NULL)
669           g_object_unref (info->icon);
670         info->icon = icon;
671       }
672     }
673     info->should_show = !g_key_file_get_boolean (keyfile,
674                                                  G_KEY_FILE_DESKTOP_GROUP,
675                                                  G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
676                                                  NULL);
677   }
678   g_free (filename);
679   g_key_file_free (keyfile);
680 }
681 
682 static void
ol_app_info_finalize(GObject * object)683 ol_app_info_finalize (GObject *object)
684 {
685   OlAppInfo *info = OL_APP_INFO (object);
686   if (info->name)
687   {
688     g_free (info->name);
689     info->name = NULL;
690   }
691   if (info->cmdline)
692   {
693     g_free (info->cmdline);
694     info->cmdline = NULL;
695   }
696   if (info->binfile)
697   {
698     g_free (info->binfile);
699     info->binfile = NULL;
700   }
701   if (info->icon)
702   {
703     g_object_unref (info->icon);
704     info->icon = NULL;
705   }
706   G_OBJECT_CLASS (ol_app_info_parent_class)->finalize (object);
707 }
708 
709 static void
_strv_replace(gchar ** argv,guint index,const gchar * new_value)710 _strv_replace (gchar **argv, guint index, const gchar *new_value)
711 {
712   g_free (argv[index]);
713   argv[index] = g_strdup (new_value);
714 }
715 
716 /**
717  * Returns the list of paths to find desktop files.
718  *
719  *
720  * @return A GList of gchar*. Should NOT be freed.
721  */
722 static GList *
_get_desktop_file_path_list()723 _get_desktop_file_path_list ()
724 {
725   GOnce once = G_ONCE_INIT;
726   g_once (&once, _get_desktop_file_path_list_once, NULL);
727   return (GList*) once.retval;
728 }
729 
730 static gpointer
_get_desktop_file_path_list_once(gpointer data)731 _get_desktop_file_path_list_once (gpointer data)
732 {
733   GList *list = NULL;
734   list = g_list_prepend (list,
735                          g_build_filename (g_get_user_data_dir(),
736                                            "applications",
737                                            NULL));
738   list = _prepend_subdirs (list);
739   const gchar * const * data_dirs = g_get_system_data_dirs ();
740   while (*data_dirs != NULL)
741   {
742     list = g_list_prepend (list,
743                            g_build_filename (*data_dirs, "applications", NULL));
744     list = _prepend_subdirs (list);
745     data_dirs++;
746   }
747   return g_list_reverse (list);
748 }
749 
750 static GList *
_prepend_subdirs(GList * list)751 _prepend_subdirs (GList *list)
752 {
753   gchar *path = list->data;
754   GError *error = NULL;
755   GDir *dir = g_dir_open (path, 0, &error);
756   if (!dir)
757   {
758     ol_errorf ("Cannot open dir %s: %s\n", path, error->message);
759     g_error_free (error);
760     return list;
761   }
762   const gchar *name;
763   while ((name = g_dir_read_name (dir)) != NULL)
764   {
765     gchar *filepath = g_build_filename (path, name, NULL);
766     if (g_file_test (filepath, G_FILE_TEST_IS_DIR))
767     {
768       list = g_list_prepend (list, filepath);
769       list = _prepend_subdirs (list);
770     }
771     else
772     {
773       g_free (filepath);
774     }
775   }
776   g_dir_close (dir);
777   return list;
778 }
779