Bug Summary

File:entry-directories.c
Warning:line 404, column 22
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name entry-directories.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/rootdir/libmenu -fcoverage-compilation-dir=/rootdir/libmenu -resource-dir /usr/lib/llvm-19/lib/clang/19 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/libmount -I /usr/include/blkid -D CAFEMENU_I_KNOW_THIS_IS_UNSTABLE -D PIC -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2025-02-13-080227-11729-1 -x c entry-directories.c
1/*
2 * Copyright (C) 2002 - 2004 Red Hat, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include <config.h>
21
22#include "entry-directories.h"
23
24#include <string.h>
25#include <errno(*__errno_location ()).h>
26#include <sys/types.h>
27#include <dirent.h>
28#include <stdlib.h>
29
30#include "menu-util.h"
31#include "menu-monitor.h"
32
33typedef struct CachedDir CachedDir;
34typedef struct CachedDirMonitor CachedDirMonitor;
35
36struct EntryDirectory {
37 CachedDir* dir;
38 char* legacy_prefix;
39
40 guint entry_type: 2;
41 guint is_legacy: 1;
42 guint refcount: 24;
43};
44
45struct EntryDirectoryList {
46 int refcount;
47 int length;
48 GList* dirs;
49};
50
51struct CachedDir {
52 CachedDir* parent;
53 char* name;
54
55 GSList* entries;
56 GSList* subdirs;
57
58 MenuMonitor* dir_monitor;
59 GSList* monitors;
60
61 guint have_read_entries: 1;
62 guint deleted: 1;
63
64 guint references;
65
66 GFunc notify;
67 gpointer notify_data;
68};
69
70struct CachedDirMonitor {
71 EntryDirectory* ed;
72 EntryDirectoryChangedFunc callback;
73 gpointer user_data;
74};
75
76static void cached_dir_add_reference (CachedDir *dir);
77static void cached_dir_remove_reference (CachedDir *dir);
78static void cached_dir_free (CachedDir *dir);
79static gboolean cached_dir_load_entries_recursive (CachedDir *dir,
80 const char *dirname);
81static void cached_dir_unref (CachedDir *dir);
82static void cached_dir_unref_noparent (CachedDir *dir);
83static CachedDir * cached_dir_add_subdir (CachedDir *dir,
84 const char *basename,
85 const char *path);
86static gboolean cached_dir_remove_subdir (CachedDir *dir,
87 const char *basename);
88
89static void handle_cached_dir_changed (MenuMonitor *monitor,
90 MenuMonitorEvent event,
91 const char *path,
92 CachedDir *dir);
93
94/*
95 * Entry directory cache
96 */
97
98static CachedDir* dir_cache = NULL((void*)0);
99
100static void
101clear_cache (CachedDir *dir G_GNUC_UNUSED__attribute__ ((__unused__)),
102 gpointer *cache)
103{
104 *cache = NULL((void*)0);
105}
106
107static CachedDir *
108cached_dir_new (const char *name)
109{
110 CachedDir* dir;
111
112 dir = g_new0 (CachedDir, 1)((CachedDir *) g_malloc0_n ((1), sizeof (CachedDir)));
113 dir->name = g_strdup (name)g_strdup_inline (name);
114
115 return dir;
116}
117
118static CachedDir *
119cached_dir_new_full (const char *name,
120 GFunc notify,
121 gpointer notify_data)
122{
123 CachedDir *dir;
124
125 dir = cached_dir_new (name);
126
127 dir->notify = notify;
128 dir->notify_data = notify_data;
129
130 return dir;
131}
132
133static void
134cached_dir_free (CachedDir *dir)
135{
136 if (dir->dir_monitor)
21
Assuming field 'dir_monitor' is null
22
Taking false branch
137 {
138 menu_monitor_remove_notify (dir->dir_monitor,
139 (MenuMonitorNotifyFunc) handle_cached_dir_changed,
140 dir);
141 menu_monitor_unref (dir->dir_monitor);
142 dir->dir_monitor = NULL((void*)0);
143 }
144
145 g_slist_foreach (dir->monitors, (GFunc) g_free, NULL((void*)0));
146 g_slist_free (dir->monitors);
147 dir->monitors = NULL((void*)0);
148
149 g_slist_foreach (dir->entries,
150 (GFunc) desktop_entry_unref,
151 NULL((void*)0));
152 g_slist_free (dir->entries);
153 dir->entries = NULL((void*)0);
154
155 g_slist_foreach (dir->subdirs,
156 (GFunc) cached_dir_unref_noparent,
157 NULL((void*)0));
158 g_slist_free (dir->subdirs);
159 dir->subdirs = NULL((void*)0);
160
161 g_free (dir->name);
162 g_free (dir);
23
Memory is released
163}
164
165static CachedDir *
166cached_dir_ref (CachedDir *dir)
167{
168 dir->references++;
169 return dir;
170}
171
172static void
173cached_dir_unref (CachedDir *dir)
174{
175 if (--dir->references == 0)
14
Assuming the condition is true
15
Taking true branch
176 {
177 CachedDir *parent;
178
179 parent = dir->parent;
180
181 if (parent != NULL((void*)0))
16
Assuming 'parent' is equal to NULL
17
Taking false branch
182 cached_dir_remove_subdir (parent, dir->name);
183
184 if (dir->notify)
18
Assuming field 'notify' is null
19
Taking false branch
185 dir->notify (dir, dir->notify_data);
186
187 cached_dir_free (dir);
20
Calling 'cached_dir_free'
24
Returning; memory was released via 1st parameter
188 }
189}
190
191static void
192cached_dir_unref_noparent (CachedDir *dir)
193{
194 if (--dir->references == 0)
195 {
196 if (dir->notify)
197 dir->notify (dir, dir->notify_data);
198
199 cached_dir_free (dir);
200 }
201}
202
203static inline CachedDir* find_subdir(CachedDir* dir, const char* subdir)
204{
205 GSList *tmp;
206
207 tmp = dir->subdirs;
208 while (tmp != NULL((void*)0))
209 {
210 CachedDir *sub = tmp->data;
211
212 if (strcmp (sub->name, subdir) == 0)
213 return sub;
214
215 tmp = tmp->next;
216 }
217
218 return NULL((void*)0);
219}
220
221static DesktopEntry* find_entry(CachedDir* dir, const char* basename)
222{
223 GSList *tmp;
224
225 tmp = dir->entries;
226 while (tmp != NULL((void*)0))
227 {
228 if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0)
229 return tmp->data;
230
231 tmp = tmp->next;
232 }
233
234 return NULL((void*)0);
235}
236
237static DesktopEntry* cached_dir_find_relative_path(CachedDir* dir, const char* relative_path)
238{
239 DesktopEntry *retval = NULL((void*)0);
240 char **split;
241 int i;
242
243 split = g_strsplit (relative_path, "/", -1);
244
245 i = 0;
246 while (split[i] != NULL((void*)0))
247 {
248 if (split[i + 1] != NULL((void*)0))
249 {
250 if ((dir = find_subdir (dir, split[i])) == NULL((void*)0))
251 break;
252 }
253 else
254 {
255 retval = find_entry (dir, split[i]);
256 break;
257 }
258
259 ++i;
260 }
261
262 g_strfreev (split);
263
264 return retval;
265}
266
267static CachedDir* cached_dir_lookup(const char* canonical)
268{
269 CachedDir *dir;
270 char **split;
271 int i;
272
273 if (dir_cache == NULL((void*)0))
274 dir_cache = cached_dir_new_full ("/",
275 (GFunc) clear_cache,
276 &dir_cache);
277 dir = dir_cache;
278
279 g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR)do { if (canonical != ((void*)0) && canonical[0] == '/'
) ; else g_assertion_message_expr (((gchar*) 0), "entry-directories.c"
, 279, ((const char*) (__func__)), "canonical != NULL && canonical[0] == G_DIR_SEPARATOR"
); } while (0)
;
280
281 menu_verbose ("Looking up cached dir \"%s\"\n", canonical);
282
283 split = g_strsplit (canonical + 1, "/", -1);
284
285 i = 0;
286 while (split[i] != NULL((void*)0))
287 {
288 CachedDir *subdir;
289
290 subdir = cached_dir_add_subdir (dir, split[i], NULL((void*)0));
291
292 dir = subdir;
293
294 ++i;
295 }
296
297 g_strfreev (split);
298
299 g_assert (dir != NULL)do { if (dir != ((void*)0)) ; else g_assertion_message_expr (
((gchar*) 0), "entry-directories.c", 299, ((const char*) (__func__
)), "dir != NULL"); } while (0)
;
300
301 return dir;
302}
303
304static gboolean cached_dir_add_entry (CachedDir *dir,
305 const char *basename G_GNUC_UNUSED__attribute__ ((__unused__)),
306 const char *path)
307{
308 DesktopEntry *entry;
309
310 entry = desktop_entry_new (path);
311 if (entry == NULL((void*)0))
312 return FALSE(0);
313
314 dir->entries = g_slist_prepend (dir->entries, entry);
315
316 return TRUE(!(0));
317}
318
319static gboolean cached_dir_update_entry(CachedDir* dir, const char* basename, const char* path)
320{
321 GSList *tmp;
322
323 tmp = dir->entries;
324 while (tmp != NULL((void*)0))
325 {
326 if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0)
327 {
328 if (!desktop_entry_reload (tmp->data))
329 {
330 dir->entries = g_slist_delete_link (dir->entries, tmp);
331 }
332
333 return TRUE(!(0));
334 }
335
336 tmp = tmp->next;
337 }
338
339 return cached_dir_add_entry (dir, basename, path);
340}
341
342static gboolean cached_dir_remove_entry(CachedDir* dir, const char* basename)
343{
344 GSList *tmp;
345
346 tmp = dir->entries;
347 while (tmp != NULL((void*)0))
348 {
349 if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0)
350 {
351 desktop_entry_unref (tmp->data);
352 dir->entries = g_slist_delete_link (dir->entries, tmp);
353 return TRUE(!(0));
354 }
355
356 tmp = tmp->next;
357 }
358
359 return FALSE(0);
360}
361
362static CachedDir *
363cached_dir_add_subdir (CachedDir *dir,
364 const char *basename,
365 const char *path)
366{
367 CachedDir *subdir;
368
369 subdir = find_subdir (dir, basename);
370
371 if (subdir != NULL((void*)0))
372 {
373 subdir->deleted = FALSE(0);
374 return subdir;
375 }
376
377 subdir = cached_dir_new (basename);
378
379 if (path != NULL((void*)0) && !cached_dir_load_entries_recursive (subdir, path))
380 {
381 cached_dir_free (subdir);
382 return NULL((void*)0);
383 }
384
385 menu_verbose ("Caching dir \"%s\"\n", basename);
386
387 subdir->parent = dir;
388 dir->subdirs = g_slist_prepend (dir->subdirs, cached_dir_ref (subdir));
389
390 return subdir;
391}
392
393static gboolean cached_dir_remove_subdir(CachedDir* dir, const char* basename)
394{
395 CachedDir *subdir;
396
397 subdir = find_subdir (dir, basename);
398
399 if (subdir != NULL((void*)0))
11
Assuming 'subdir' is not equal to NULL
12
Taking true branch
400 {
401 subdir->deleted = TRUE(!(0));
402
403 cached_dir_unref (subdir);
13
Calling 'cached_dir_unref'
25
Returning; memory was released via 1st parameter
404 dir->subdirs = g_slist_remove (dir->subdirs, subdir);
26
Use of memory after it is freed
405
406 return TRUE(!(0));
407 }
408
409 return FALSE(0);
410}
411
412static guint monitors_idle_handler = 0;
413static GSList *pending_monitors_dirs = NULL((void*)0);
414
415static void
416cached_dir_invoke_monitors (CachedDir *dir)
417{
418 GSList *tmp;
419
420 tmp = dir->monitors;
421 while (tmp != NULL((void*)0))
422 {
423 CachedDirMonitor *monitor = tmp->data;
424 GSList *next = tmp->next;
425
426 monitor->callback (monitor->ed, monitor->user_data);
427
428 tmp = next;
429 }
430
431 /* we explicitly don't invoke monitors of the parent since an
432 * event has been queued for it too */
433}
434
435static gboolean
436emit_monitors_in_idle (void)
437{
438 GSList *monitors_to_emit;
439 GSList *tmp;
440
441 monitors_to_emit = pending_monitors_dirs;
442
443 pending_monitors_dirs = NULL((void*)0);
444 monitors_idle_handler = 0;
445
446 tmp = monitors_to_emit;
447 while (tmp != NULL((void*)0))
448 {
449 CachedDir *dir = tmp->data;
450
451 cached_dir_invoke_monitors (dir);
452 cached_dir_remove_reference (dir);
453
454 tmp = tmp->next;
455 }
456
457 g_slist_free (monitors_to_emit);
458
459 return FALSE(0);
460}
461
462static void
463cached_dir_queue_monitor_event (CachedDir *dir)
464{
465 GSList *tmp;
466
467 tmp = pending_monitors_dirs;
468 while (tmp != NULL((void*)0))
469 {
470 CachedDir *d = tmp->data;
471 GSList *next = tmp->next;
472
473 if (dir->parent == d->parent &&
474 g_strcmp0 (dir->name, d->name) == 0)
475 break;
476
477 tmp = next;
478 }
479
480 /* not found, so let's queue it */
481 if (tmp == NULL((void*)0))
482 {
483 cached_dir_add_reference (dir);
484 pending_monitors_dirs = g_slist_append (pending_monitors_dirs, dir);
485 }
486
487 if (dir->parent)
488 {
489 cached_dir_queue_monitor_event (dir->parent);
490 }
491
492 if (monitors_idle_handler == 0)
493 {
494 monitors_idle_handler = g_idle_add ((GSourceFunc) emit_monitors_in_idle, NULL((void*)0));
495 }
496}
497
498static void handle_cached_dir_changed (MenuMonitor *monitor G_GNUC_UNUSED__attribute__ ((__unused__)),
499 MenuMonitorEvent event,
500 const char *path,
501 CachedDir *dir)
502{
503 gboolean handled = FALSE(0);
504 char *basename;
505 char *dirname;
506
507 dirname = g_path_get_dirname (path);
508 basename = g_path_get_basename (path);
509
510 dir = cached_dir_lookup (dirname);
511 cached_dir_add_reference (dir);
512
513 if (g_str_has_suffix (basename, ".desktop")(__builtin_constant_p (".desktop")? __extension__ ({ const char
* const __str = (basename); const char * const __suffix = (".desktop"
); gboolean __result = (0); if (__str == ((void*)0) || __suffix
== ((void*)0)) __result = (g_str_has_suffix) (__str, __suffix
); else { const size_t __str_len = strlen (((__str) + !(__str
))); const size_t __suffix_len = strlen (((__suffix) + !(__suffix
))); if (__str_len >= __suffix_len) __result = memcmp (__str
+ __str_len - __suffix_len, ((__suffix) + !(__suffix)), __suffix_len
) == 0; } __result; }) : (g_str_has_suffix) (basename, ".desktop"
) )
||
1
'?' condition is true
2
Assuming '__str' is equal to null
3
Assuming the condition is false
6
Taking false branch
514 g_str_has_suffix (basename, ".directory")(__builtin_constant_p (".directory")? __extension__ ({ const char
* const __str = (basename); const char * const __suffix = (".directory"
); gboolean __result = (0); if (__str == ((void*)0) || __suffix
== ((void*)0)) __result = (g_str_has_suffix) (__str, __suffix
); else { const size_t __str_len = strlen (((__str) + !(__str
))); const size_t __suffix_len = strlen (((__suffix) + !(__suffix
))); if (__str_len >= __suffix_len) __result = memcmp (__str
+ __str_len - __suffix_len, ((__suffix) + !(__suffix)), __suffix_len
) == 0; } __result; }) : (g_str_has_suffix) (basename, ".directory"
) )
)
4
'?' condition is true
5
Assuming the condition is false
515 {
516 switch (event)
517 {
518 case MENU_MONITOR_EVENT_CREATED:
519 case MENU_MONITOR_EVENT_CHANGED:
520 handled = cached_dir_update_entry (dir, basename, path);
521 break;
522
523 case MENU_MONITOR_EVENT_DELETED:
524 handled = cached_dir_remove_entry (dir, basename);
525 break;
526
527 default:
528 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "entry-directories.c"
, 528, ((const char*) (__func__)), ((void*)0)); } while (0)
;
529 break;
530 }
531 }
532 else if (g_file_test (path, G_FILE_TEST_IS_DIR)) /* Try recursing */
7
Assuming the condition is true
8
Taking true branch
533 {
534 switch (event)
9
Control jumps to 'case MENU_MONITOR_EVENT_DELETED:' at line 543
535 {
536 case MENU_MONITOR_EVENT_CREATED:
537 handled = cached_dir_add_subdir (dir, basename, path) != NULL((void*)0);
538 break;
539
540 case MENU_MONITOR_EVENT_CHANGED:
541 break;
542
543 case MENU_MONITOR_EVENT_DELETED:
544 handled = cached_dir_remove_subdir (dir, basename);
10
Calling 'cached_dir_remove_subdir'
545 break;
546
547 default:
548 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "entry-directories.c"
, 548, ((const char*) (__func__)), ((void*)0)); } while (0)
;
549 break;
550 }
551 }
552
553 g_free (basename);
554 g_free (dirname);
555
556 if (handled)
557 {
558 menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n",
559 dir->name,
560 path,
561 event == MENU_MONITOR_EVENT_CREATED ? ("created") :
562 event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed"));
563
564 /* CHANGED events don't change the set of desktop entries */
565 if (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED)
566 {
567 _entry_directory_list_empty_desktop_cache ();
568 }
569
570 cached_dir_queue_monitor_event (dir);
571 }
572
573 cached_dir_remove_reference (dir);
574}
575
576static void cached_dir_ensure_monitor(CachedDir* dir, const char* dirname)
577{
578 if (dir->dir_monitor == NULL((void*)0))
579 {
580 dir->dir_monitor = menu_get_directory_monitor (dirname);
581 menu_monitor_add_notify (dir->dir_monitor,
582 (MenuMonitorNotifyFunc) handle_cached_dir_changed,
583 dir);
584 }
585}
586
587static gboolean cached_dir_load_entries_recursive(CachedDir* dir, const char* dirname)
588{
589 DIR *dp;
590 struct dirent *dent;
591 GString *fullpath;
592 gsize fullpath_len;
593
594 g_assert (dir != NULL)do { if (dir != ((void*)0)) ; else g_assertion_message_expr (
((gchar*) 0), "entry-directories.c", 594, ((const char*) (__func__
)), "dir != NULL"); } while (0)
;
595
596 if (dir->have_read_entries)
597 return TRUE(!(0));
598
599 menu_verbose ("Attempting to read entries from %s (full path %s)\n",
600 dir->name, dirname);
601
602 dp = opendir (dirname);
603 if (dp == NULL((void*)0))
604 {
605 menu_verbose ("Unable to list directory \"%s\"\n",
606 dirname);
607 return FALSE(0);
608 }
609
610 cached_dir_ensure_monitor (dir, dirname);
611
612 fullpath = g_string_new (dirname);
613 if (fullpath->str[fullpath->len - 1] != G_DIR_SEPARATOR'/')
614 g_string_append_c (fullpath, G_DIR_SEPARATOR)g_string_append_c_inline (fullpath, '/');
615
616 fullpath_len = fullpath->len;
617
618 while ((dent = readdir (dp)) != NULL((void*)0))
619 {
620 /* ignore . and .. */
621 if (dent->d_name[0] == '.' &&
622 (dent->d_name[1] == '\0' ||
623 (dent->d_name[1] == '.' &&
624 dent->d_name[2] == '\0')))
625 continue;
626
627 g_string_append (fullpath, dent->d_name)(__builtin_constant_p (dent->d_name) ? __extension__ ({ const
char * const __val = (dent->d_name); g_string_append_len_inline
(fullpath, __val, (__val != ((void*)0)) ? (gssize) strlen ((
(__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(fullpath, dent->d_name, (gssize) -1))
;
628
629 if (g_str_has_suffix (dent->d_name, ".desktop")(__builtin_constant_p (".desktop")? __extension__ ({ const char
* const __str = (dent->d_name); const char * const __suffix
= (".desktop"); gboolean __result = (0); if (__str == ((void
*)0) || __suffix == ((void*)0)) __result = (g_str_has_suffix)
(__str, __suffix); else { const size_t __str_len = strlen ((
(__str) + !(__str))); const size_t __suffix_len = strlen (((__suffix
) + !(__suffix))); if (__str_len >= __suffix_len) __result
= memcmp (__str + __str_len - __suffix_len, ((__suffix) + !(
__suffix)), __suffix_len) == 0; } __result; }) : (g_str_has_suffix
) (dent->d_name, ".desktop") )
||
630 g_str_has_suffix (dent->d_name, ".directory")(__builtin_constant_p (".directory")? __extension__ ({ const char
* const __str = (dent->d_name); const char * const __suffix
= (".directory"); gboolean __result = (0); if (__str == ((void
*)0) || __suffix == ((void*)0)) __result = (g_str_has_suffix)
(__str, __suffix); else { const size_t __str_len = strlen ((
(__str) + !(__str))); const size_t __suffix_len = strlen (((__suffix
) + !(__suffix))); if (__str_len >= __suffix_len) __result
= memcmp (__str + __str_len - __suffix_len, ((__suffix) + !(
__suffix)), __suffix_len) == 0; } __result; }) : (g_str_has_suffix
) (dent->d_name, ".directory") )
)
631 {
632 cached_dir_add_entry (dir, dent->d_name, fullpath->str);
633 }
634 else /* Try recursing */
635 {
636 cached_dir_add_subdir (dir, dent->d_name, fullpath->str);
637 }
638
639 g_string_truncate (fullpath, fullpath_len)g_string_truncate_inline (fullpath, fullpath_len);
640 }
641
642 closedir (dp);
643
644 g_string_free (fullpath, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) (
(fullpath), ((!(0)))) : g_string_free_and_steal (fullpath)) :
(g_string_free) ((fullpath), ((!(0)))))
;
645
646 dir->have_read_entries = TRUE(!(0));
647
648 return TRUE(!(0));
649}
650
651static void cached_dir_add_monitor(CachedDir* dir, EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data)
652{
653 CachedDirMonitor *monitor;
654 GSList *tmp;
655
656 tmp = dir->monitors;
657 while (tmp != NULL((void*)0))
658 {
659 monitor = tmp->data;
660
661 if (monitor->ed == ed &&
662 monitor->callback == callback &&
663 monitor->user_data == user_data)
664 break;
665
666 tmp = tmp->next;
667 }
668
669 if (tmp == NULL((void*)0))
670 {
671 monitor = g_new0 (CachedDirMonitor, 1)((CachedDirMonitor *) g_malloc0_n ((1), sizeof (CachedDirMonitor
)))
;
672 monitor->ed = ed;
673 monitor->callback = callback;
674 monitor->user_data = user_data;
675
676 dir->monitors = g_slist_append (dir->monitors, monitor);
677 }
678}
679
680static void cached_dir_remove_monitor(CachedDir* dir, EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data)
681{
682 GSList *tmp;
683
684 tmp = dir->monitors;
685 while (tmp != NULL((void*)0))
686 {
687 CachedDirMonitor *monitor = tmp->data;
688 GSList *next = tmp->next;
689
690 if (monitor->ed == ed &&
691 monitor->callback == callback &&
692 monitor->user_data == user_data)
693 {
694 dir->monitors = g_slist_delete_link (dir->monitors, tmp);
695 g_free (monitor);
696 }
697
698 tmp = next;
699 }
700}
701
702static void cached_dir_add_reference(CachedDir* dir)
703{
704 cached_dir_ref (dir);
705
706 if (dir->parent != NULL((void*)0))
707 {
708 cached_dir_add_reference (dir->parent);
709 }
710}
711
712static void cached_dir_remove_reference(CachedDir* dir)
713{
714 CachedDir *parent;
715
716 parent = dir->parent;
717
718 cached_dir_unref (dir);
719
720 if (parent != NULL((void*)0))
721 {
722 cached_dir_remove_reference (parent);
723 }
724}
725
726/*
727 * Entry directories
728 */
729
730static EntryDirectory* entry_directory_new_full(DesktopEntryType entry_type, const char* path, gboolean is_legacy, const char* legacy_prefix)
731{
732 EntryDirectory *ed;
733 char *canonical;
734
735 menu_verbose ("Loading entry directory \"%s\" (legacy %s)\n",
736 path,
737 is_legacy ? "<yes>" : "<no>");
738
739 canonical = realpath (path, NULL((void*)0));
740 if (canonical == NULL((void*)0))
741 {
742 menu_verbose ("Failed to canonicalize \"%s\": %s\n",
743 path, g_strerror (errno));
744 return NULL((void*)0);
745 }
746
747 ed = g_new0 (EntryDirectory, 1)((EntryDirectory *) g_malloc0_n ((1), sizeof (EntryDirectory)
))
;
748
749 ed->dir = cached_dir_lookup (canonical);
750 g_assert (ed->dir != NULL)do { if (ed->dir != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "entry-directories.c", 750, ((const char*) (__func__
)), "ed->dir != NULL"); } while (0)
;
751
752 cached_dir_add_reference (ed->dir);
753 cached_dir_load_entries_recursive (ed->dir, canonical);
754
755 ed->legacy_prefix = g_strdup (legacy_prefix)g_strdup_inline (legacy_prefix);
756 ed->entry_type = entry_type;
757 ed->is_legacy = is_legacy != FALSE(0);
758 ed->refcount = 1;
759
760 g_free (canonical);
761
762 return ed;
763}
764
765EntryDirectory* entry_directory_new(DesktopEntryType entry_type, const char* path)
766{
767 return entry_directory_new_full (entry_type, path, FALSE(0), NULL((void*)0));
768}
769
770EntryDirectory* entry_directory_new_legacy(DesktopEntryType entry_type, const char* path, const char* legacy_prefix)
771{
772 return entry_directory_new_full(entry_type, path, TRUE(!(0)), legacy_prefix);
773}
774
775EntryDirectory* entry_directory_ref(EntryDirectory* ed)
776{
777 g_return_val_if_fail(ed != NULL, NULL)do { if ((ed != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "ed != NULL"); return
(((void*)0)); } } while (0)
;
778 g_return_val_if_fail(ed->refcount > 0, NULL)do { if ((ed->refcount > 0)) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "ed->refcount > 0"
); return (((void*)0)); } } while (0)
;
779
780 ed->refcount++;
781
782 return ed;
783}
784
785void entry_directory_unref(EntryDirectory* ed)
786{
787 g_return_if_fail (ed != NULL)do { if ((ed != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "ed != NULL"); return
; } } while (0)
;
788 g_return_if_fail (ed->refcount > 0)do { if ((ed->refcount > 0)) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "ed->refcount > 0"
); return; } } while (0)
;
789
790 if (--ed->refcount == 0)
791 {
792 cached_dir_remove_reference (ed->dir);
793
794 ed->dir = NULL((void*)0);
795 ed->entry_type = DESKTOP_ENTRY_INVALID;
796 ed->is_legacy = FALSE(0);
797
798 g_free (ed->legacy_prefix);
799 ed->legacy_prefix = NULL((void*)0);
800
801 g_free (ed);
802 }
803}
804
805static void entry_directory_add_monitor(EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data)
806{
807 cached_dir_add_monitor (ed->dir, ed, callback, user_data);
808}
809
810static void entry_directory_remove_monitor(EntryDirectory* ed, EntryDirectoryChangedFunc callback, gpointer user_data)
811{
812 cached_dir_remove_monitor (ed->dir, ed, callback, user_data);
813}
814
815static DesktopEntry* entry_directory_get_directory(EntryDirectory* ed, const char* relative_path)
816{
817 DesktopEntry *entry;
818
819 if (ed->entry_type != DESKTOP_ENTRY_DIRECTORY)
820 return NULL((void*)0);
821
822 entry = cached_dir_find_relative_path (ed->dir, relative_path);
823 if (entry == NULL((void*)0) || desktop_entry_get_type (entry) != DESKTOP_ENTRY_DIRECTORY)
824 return NULL((void*)0);
825
826 return desktop_entry_ref (entry);
827}
828
829static char* get_desktop_file_id_from_path(EntryDirectory* ed, DesktopEntryType entry_type, const char* relative_path)
830{
831 char *retval;
832
833 retval = NULL((void*)0);
834
835 if (entry_type == DESKTOP_ENTRY_DESKTOP)
836 {
837 if (!ed->is_legacy)
838 {
839 retval = g_strdelimit (g_strdup (relative_path)g_strdup_inline (relative_path), "/", '-');
840 }
841 else
842 {
843 char *basename;
844
845 basename = g_path_get_basename (relative_path);
846
847 if (ed->legacy_prefix)
848 {
849 retval = g_strjoin ("-", ed->legacy_prefix, basename, NULL((void*)0));
850 g_free (basename);
851 }
852 else
853 {
854 retval = basename;
855 }
856 }
857 }
858 else
859 {
860 retval = g_strdup (relative_path)g_strdup_inline (relative_path);
861 }
862
863 return retval;
864}
865
866typedef gboolean (*EntryDirectoryForeachFunc) (EntryDirectory* ed, DesktopEntry* entry, const char* file_id, DesktopEntrySet* set, gpointer user_data);
867
868static gboolean entry_directory_foreach_recursive(EntryDirectory* ed, CachedDir* cd, GString* relative_path, EntryDirectoryForeachFunc func, DesktopEntrySet* set, gpointer user_data)
869{
870 GSList *tmp;
871 int relative_path_len;
872
873 if (cd->deleted)
874 return TRUE(!(0));
875
876 relative_path_len = relative_path->len;
877
878 tmp = cd->entries;
879 while (tmp != NULL((void*)0))
880 {
881 DesktopEntry *entry = tmp->data;
882
883 if (desktop_entry_get_type (entry) == ed->entry_type)
884 {
885 gboolean ret;
886 char *file_id;
887
888 g_string_append (relative_path,(__builtin_constant_p (desktop_entry_get_basename (entry)) ? __extension__
({ const char * const __val = (desktop_entry_get_basename (entry
)); g_string_append_len_inline (relative_path, __val, (__val !=
((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (relative_path, desktop_entry_get_basename
(entry), (gssize) -1))
889 desktop_entry_get_basename (entry))(__builtin_constant_p (desktop_entry_get_basename (entry)) ? __extension__
({ const char * const __val = (desktop_entry_get_basename (entry
)); g_string_append_len_inline (relative_path, __val, (__val !=
((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (relative_path, desktop_entry_get_basename
(entry), (gssize) -1))
;
890
891 file_id = get_desktop_file_id_from_path (ed,
892 ed->entry_type,
893 relative_path->str);
894
895 ret = func (ed, entry, file_id, set, user_data);
896
897 g_free (file_id);
898
899 g_string_truncate (relative_path, relative_path_len)g_string_truncate_inline (relative_path, relative_path_len);
900
901 if (!ret)
902 return FALSE(0);
903 }
904
905 tmp = tmp->next;
906 }
907
908 tmp = cd->subdirs;
909 while (tmp != NULL((void*)0))
910 {
911 CachedDir *subdir = tmp->data;
912
913 g_string_append (relative_path, subdir->name)(__builtin_constant_p (subdir->name) ? __extension__ ({ const
char * const __val = (subdir->name); g_string_append_len_inline
(relative_path, __val, (__val != ((void*)0)) ? (gssize) strlen
(((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(relative_path, subdir->name, (gssize) -1))
;
914 g_string_append_c (relative_path, G_DIR_SEPARATOR)g_string_append_c_inline (relative_path, '/');
915
916 if (!entry_directory_foreach_recursive (ed,
917 subdir,
918 relative_path,
919 func,
920 set,
921 user_data))
922 return FALSE(0);
923
924 g_string_truncate (relative_path, relative_path_len)g_string_truncate_inline (relative_path, relative_path_len);
925
926 tmp = tmp->next;
927 }
928
929 return TRUE(!(0));
930}
931
932static void entry_directory_foreach(EntryDirectory* ed, EntryDirectoryForeachFunc func, DesktopEntrySet* set, gpointer user_data)
933{
934 GString *path;
935
936 path = g_string_new (NULL((void*)0));
937
938 entry_directory_foreach_recursive (ed,
939 ed->dir,
940 path,
941 func,
942 set,
943 user_data);
944
945 g_string_free (path, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) (
(path), ((!(0)))) : g_string_free_and_steal (path)) : (g_string_free
) ((path), ((!(0)))))
;
946}
947
948void entry_directory_get_flat_contents(EntryDirectory* ed, DesktopEntrySet* desktop_entries, DesktopEntrySet* directory_entries, GSList** subdirs)
949{
950 GSList *tmp;
951
952 if (subdirs)
953 *subdirs = NULL((void*)0);
954
955 tmp = ed->dir->entries;
956 while (tmp != NULL((void*)0))
957 {
958 DesktopEntry *entry = tmp->data;
959 const char *basename;
960
961 basename = desktop_entry_get_basename (entry);
962
963 if (desktop_entries &&
964 desktop_entry_get_type (entry) == DESKTOP_ENTRY_DESKTOP)
965 {
966 char *file_id;
967
968 file_id = get_desktop_file_id_from_path (ed,
969 DESKTOP_ENTRY_DESKTOP,
970 basename);
971
972 desktop_entry_set_add_entry (desktop_entries,
973 entry,
974 file_id);
975
976 g_free (file_id);
977 }
978
979 if (directory_entries &&
980 desktop_entry_get_type (entry) == DESKTOP_ENTRY_DIRECTORY)
981 {
982 desktop_entry_set_add_entry (directory_entries,
983 entry,
984 basename);
985 }
986
987 tmp = tmp->next;
988 }
989
990 if (subdirs)
991 {
992 tmp = ed->dir->subdirs;
993 while (tmp != NULL((void*)0))
994 {
995 CachedDir *cd = tmp->data;
996
997 if (!cd->deleted)
998 {
999 *subdirs = g_slist_prepend (*subdirs, g_strdup (cd->name)g_strdup_inline (cd->name));
1000 }
1001
1002 tmp = tmp->next;
1003 }
1004 }
1005
1006 if (subdirs)
1007 *subdirs = g_slist_reverse (*subdirs);
1008}
1009
1010/*
1011 * Entry directory lists
1012 */
1013
1014EntryDirectoryList* entry_directory_list_new(void)
1015{
1016 EntryDirectoryList *list;
1017
1018 list = g_new0 (EntryDirectoryList, 1)((EntryDirectoryList *) g_malloc0_n ((1), sizeof (EntryDirectoryList
)))
;
1019
1020 list->refcount = 1;
1021 list->dirs = NULL((void*)0);
1022 list->length = 0;
1023
1024 return list;
1025}
1026
1027EntryDirectoryList* entry_directory_list_ref(EntryDirectoryList* list)
1028{
1029 g_return_val_if_fail (list != NULL, NULL)do { if ((list != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "list != NULL"); return
(((void*)0)); } } while (0)
;
1030 g_return_val_if_fail (list->refcount > 0, NULL)do { if ((list->refcount > 0)) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "list->refcount > 0"
); return (((void*)0)); } } while (0)
;
1031
1032 list->refcount += 1;
1033
1034 return list;
1035}
1036
1037void entry_directory_list_unref(EntryDirectoryList* list)
1038{
1039 g_return_if_fail (list != NULL)do { if ((list != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "list != NULL"); return
; } } while (0)
;
1040 g_return_if_fail (list->refcount > 0)do { if ((list->refcount > 0)) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "list->refcount > 0"
); return; } } while (0)
;
1041
1042 list->refcount -= 1;
1043 if (list->refcount == 0)
1044 {
1045 g_list_foreach (list->dirs, (GFunc) entry_directory_unref, NULL((void*)0));
1046 g_list_free (list->dirs);
1047 list->dirs = NULL((void*)0);
1048 list->length = 0;
1049 g_free (list);
1050 }
1051}
1052
1053void entry_directory_list_prepend(EntryDirectoryList* list, EntryDirectory* ed)
1054{
1055 list->length += 1;
1056 list->dirs = g_list_prepend (list->dirs,
1057 entry_directory_ref (ed));
1058}
1059
1060int entry_directory_list_get_length(EntryDirectoryList* list)
1061{
1062 return list->length;
1063}
1064
1065void entry_directory_list_append_list(EntryDirectoryList* list, EntryDirectoryList* to_append)
1066{
1067 GList *tmp;
1068 GList *new_dirs = NULL((void*)0);
1069
1070 if (to_append->length == 0)
1071 return;
1072
1073 tmp = to_append->dirs;
1074 while (tmp != NULL((void*)0))
1075 {
1076 list->length += 1;
1077 new_dirs = g_list_prepend (new_dirs,
1078 entry_directory_ref (tmp->data));
1079
1080 tmp = tmp->next;
1081 }
1082
1083 new_dirs = g_list_reverse (new_dirs);
1084 list->dirs = g_list_concat (list->dirs, new_dirs);
1085}
1086
1087DesktopEntry* entry_directory_list_get_directory(EntryDirectoryList *list, const char* relative_path)
1088{
1089 DesktopEntry *retval = NULL((void*)0);
1090 GList *tmp;
1091
1092 tmp = list->dirs;
1093 while (tmp != NULL((void*)0))
1094 {
1095 if ((retval = entry_directory_get_directory (tmp->data, relative_path)) != NULL((void*)0))
1096 break;
1097
1098 tmp = tmp->next;
1099 }
1100
1101 return retval;
1102}
1103
1104gboolean _entry_directory_list_compare(const EntryDirectoryList* a, const EntryDirectoryList* b)
1105{
1106 GList *al, *bl;
1107
1108 if (a == NULL((void*)0) && b == NULL((void*)0))
1109 return TRUE(!(0));
1110
1111 if ((a == NULL((void*)0) || b == NULL((void*)0)))
1112 return FALSE(0);
1113
1114 if (a->length != b->length)
1115 return FALSE(0);
1116
1117 al = a->dirs; bl = b->dirs;
1118 while (al && bl && al->data == bl->data)
1119 {
1120 al = al->next;
1121 bl = bl->next;
1122 }
1123
1124 return (al == NULL((void*)0) && bl == NULL((void*)0));
1125}
1126
1127static gboolean get_all_func (EntryDirectory *ed,
1128 DesktopEntry *entry,
1129 const char *file_id,
1130 DesktopEntrySet *set,
1131 gpointer user_data G_GNUC_UNUSED__attribute__ ((__unused__)))
1132{
1133 if (ed->is_legacy && !desktop_entry_has_categories (entry))
1134 {
1135 entry = desktop_entry_copy (entry);
1136 desktop_entry_add_legacy_category (entry);
1137 }
1138 else
1139 {
1140 entry = desktop_entry_ref (entry);
1141 }
1142
1143 desktop_entry_set_add_entry (set, entry, file_id);
1144 desktop_entry_unref (entry);
1145
1146 return TRUE(!(0));
1147}
1148
1149static DesktopEntrySet* entry_directory_last_set = NULL((void*)0);
1150static EntryDirectoryList* entry_directory_last_list = NULL((void*)0);
1151
1152void _entry_directory_list_empty_desktop_cache(void)
1153{
1154 if (entry_directory_last_set != NULL((void*)0))
1155 desktop_entry_set_unref (entry_directory_last_set);
1156 entry_directory_last_set = NULL((void*)0);
1157
1158 if (entry_directory_last_list != NULL((void*)0))
1159 entry_directory_list_unref (entry_directory_last_list);
1160 entry_directory_last_list = NULL((void*)0);
1161}
1162
1163DesktopEntrySet* _entry_directory_list_get_all_desktops(EntryDirectoryList* list)
1164{
1165 GList *tmp;
1166 DesktopEntrySet *set;
1167
1168 /* The only tricky thing here is that desktop files later
1169 * in the search list with the same relative path
1170 * are "hidden" by desktop files earlier in the path,
1171 * so we have to do the earlier files first causing
1172 * the later files to replace the earlier files
1173 * in the DesktopEntrySet
1174 *
1175 * We go from the end of the list so we can just
1176 * g_hash_table_replace and not have to do two
1177 * hash lookups (check for existing entry, then insert new
1178 * entry)
1179 */
1180
1181 /* This method is -extremely- slow, so we have a simple
1182 one-entry cache here */
1183 if (_entry_directory_list_compare (list, entry_directory_last_list))
1184 {
1185 menu_verbose (" Hit desktop list (%p) cache\n", list);
1186 return desktop_entry_set_ref (entry_directory_last_set);
1187 }
1188
1189 if (entry_directory_last_set != NULL((void*)0))
1190 desktop_entry_set_unref (entry_directory_last_set);
1191 if (entry_directory_last_list != NULL((void*)0))
1192 entry_directory_list_unref (entry_directory_last_list);
1193
1194 set = desktop_entry_set_new ();
1195 menu_verbose (" Storing all of list %p in set %p\n",
1196 list, set);
1197
1198 tmp = g_list_last (list->dirs);
1199 while (tmp != NULL((void*)0))
1200 {
1201 entry_directory_foreach (tmp->data, get_all_func, set, NULL((void*)0));
1202
1203 tmp = tmp->prev;
1204 }
1205
1206 entry_directory_last_list = entry_directory_list_ref (list);
1207 entry_directory_last_set = desktop_entry_set_ref (set);
1208
1209 return set;
1210}
1211
1212void entry_directory_list_add_monitors(EntryDirectoryList* list, EntryDirectoryChangedFunc callback, gpointer user_data)
1213{
1214 GList *tmp;
1215
1216 tmp = list->dirs;
1217 while (tmp != NULL((void*)0))
1218 {
1219 entry_directory_add_monitor (tmp->data, callback, user_data);
1220 tmp = tmp->next;
1221 }
1222}
1223
1224void entry_directory_list_remove_monitors(EntryDirectoryList* list, EntryDirectoryChangedFunc callback, gpointer user_data)
1225{
1226 GList *tmp;
1227
1228 tmp = list->dirs;
1229 while (tmp != NULL((void*)0))
1230 {
1231 entry_directory_remove_monitor (tmp->data, callback, user_data);
1232 tmp = tmp->next;
1233 }
1234}