| File: | entry-directories.c |
| Warning: | line 397, column 22 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 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 | ||||
| 33 | typedef struct CachedDir CachedDir; | |||
| 34 | typedef struct CachedDirMonitor CachedDirMonitor; | |||
| 35 | ||||
| 36 | struct 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 | ||||
| 45 | struct EntryDirectoryList { | |||
| 46 | int refcount; | |||
| 47 | int length; | |||
| 48 | GList* dirs; | |||
| 49 | }; | |||
| 50 | ||||
| 51 | struct 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 | ||||
| 70 | struct CachedDirMonitor { | |||
| 71 | EntryDirectory* ed; | |||
| 72 | EntryDirectoryChangedFunc callback; | |||
| 73 | gpointer user_data; | |||
| 74 | }; | |||
| 75 | ||||
| 76 | static void cached_dir_add_reference (CachedDir *dir); | |||
| 77 | static void cached_dir_remove_reference (CachedDir *dir); | |||
| 78 | static void cached_dir_free (CachedDir *dir); | |||
| 79 | static gboolean cached_dir_load_entries_recursive (CachedDir *dir, | |||
| 80 | const char *dirname); | |||
| 81 | static void cached_dir_unref (CachedDir *dir); | |||
| 82 | static void cached_dir_unref_noparent (CachedDir *dir); | |||
| 83 | static CachedDir * cached_dir_add_subdir (CachedDir *dir, | |||
| 84 | const char *basename, | |||
| 85 | const char *path); | |||
| 86 | static gboolean cached_dir_remove_subdir (CachedDir *dir, | |||
| 87 | const char *basename); | |||
| 88 | ||||
| 89 | static 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 | ||||
| 98 | static CachedDir* dir_cache = NULL((void*)0); | |||
| 99 | ||||
| 100 | static void | |||
| 101 | clear_cache (CachedDir *dir G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
| 102 | gpointer *cache) | |||
| 103 | { | |||
| 104 | *cache = NULL((void*)0); | |||
| 105 | } | |||
| 106 | ||||
| 107 | static CachedDir * | |||
| 108 | cached_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 | ||||
| 118 | static CachedDir * | |||
| 119 | cached_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 | ||||
| 133 | static void | |||
| 134 | cached_dir_free (CachedDir *dir) | |||
| 135 | { | |||
| 136 | if (dir->dir_monitor) | |||
| 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); | |||
| 156 | } | |||
| 157 | ||||
| 158 | static CachedDir * | |||
| 159 | cached_dir_ref (CachedDir *dir) | |||
| 160 | { | |||
| 161 | dir->references++; | |||
| 162 | return dir; | |||
| 163 | } | |||
| 164 | ||||
| 165 | static void | |||
| 166 | cached_dir_unref (CachedDir *dir) | |||
| 167 | { | |||
| 168 | if (--dir->references == 0) | |||
| 169 | { | |||
| 170 | CachedDir *parent; | |||
| 171 | ||||
| 172 | parent = dir->parent; | |||
| 173 | ||||
| 174 | if (parent != NULL((void*)0)) | |||
| 175 | cached_dir_remove_subdir (parent, dir->name); | |||
| 176 | ||||
| 177 | if (dir->notify) | |||
| 178 | dir->notify (dir, dir->notify_data); | |||
| 179 | ||||
| 180 | cached_dir_free (dir); | |||
| 181 | } | |||
| 182 | } | |||
| 183 | ||||
| 184 | static void | |||
| 185 | cached_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 | ||||
| 196 | static 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 | ||||
| 214 | static 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 | ||||
| 230 | static 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 | ||||
| 260 | static 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 | ||||
| 297 | static 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 | ||||
| 312 | static 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 | ||||
| 335 | static 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 | ||||
| 355 | static CachedDir * | |||
| 356 | cached_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 | ||||
| 386 | static 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)) | |||
| 393 | { | |||
| 394 | subdir->deleted = TRUE(!(0)); | |||
| 395 | ||||
| 396 | cached_dir_unref (subdir); | |||
| 397 | dir->subdirs = g_slist_remove (dir->subdirs, subdir); | |||
| ||||
| 398 | ||||
| 399 | return TRUE(!(0)); | |||
| 400 | } | |||
| 401 | ||||
| 402 | return FALSE(0); | |||
| 403 | } | |||
| 404 | ||||
| 405 | static guint monitors_idle_handler = 0; | |||
| 406 | static GSList *pending_monitors_dirs = NULL((void*)0); | |||
| 407 | ||||
| 408 | static void | |||
| 409 | cached_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 | ||||
| 428 | static gboolean | |||
| 429 | emit_monitors_in_idle (gpointer user_data) | |||
| 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 | ||||
| 455 | static void | |||
| 456 | cached_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 | ||||
| 491 | static 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" ) ) || | |||
| ||||
| 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" ) )) | |||
| 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 */ | |||
| 526 | { | |||
| 527 | switch (event) | |||
| 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); | |||
| 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 | ||||
| 569 | static 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 | ||||
| 580 | static 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 | ||||
| 644 | static 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 | ||||
| 673 | static 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 | ||||
| 695 | static 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 | ||||
| 705 | static 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 | ||||
| 723 | static 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 | ||||
| 758 | EntryDirectory* 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 | ||||
| 763 | EntryDirectory* 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 | ||||
| 768 | EntryDirectory* 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 | ||||
| 778 | void 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 | ||||
| 798 | static 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 | ||||
| 803 | static 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 | ||||
| 808 | static 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 | ||||
| 822 | static 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 | ||||
| 859 | typedef gboolean (*EntryDirectoryForeachFunc) (EntryDirectory* ed, DesktopEntry* entry, const char* file_id, DesktopEntrySet* set, gpointer user_data); | |||
| 860 | ||||
| 861 | static 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 | ||||
| 925 | static 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 | ||||
| 941 | void 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 | ||||
| 1007 | EntryDirectoryList* 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 | ||||
| 1020 | EntryDirectoryList* 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 | ||||
| 1030 | void 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 | ||||
| 1045 | void 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 | ||||
| 1052 | int entry_directory_list_get_length(EntryDirectoryList* list) | |||
| 1053 | { | |||
| 1054 | return list->length; | |||
| 1055 | } | |||
| 1056 | ||||
| 1057 | void 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 | ||||
| 1079 | DesktopEntry* 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 | ||||
| 1096 | gboolean _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 | ||||
| 1119 | static 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 | ||||
| 1141 | static DesktopEntrySet* entry_directory_last_set = NULL((void*)0); | |||
| 1142 | static EntryDirectoryList* entry_directory_last_list = NULL((void*)0); | |||
| 1143 | ||||
| 1144 | void _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 | ||||
| 1155 | DesktopEntrySet* _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 | ||||
| 1204 | void 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 | ||||
| 1216 | void 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 | } |