Bug Summary

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