Bug Summary

File:menu-layout.c
Warning:line 2084, column 15
Value stored to 'prev' is never read

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 menu-layout.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-090307-11729-1 -x c menu-layout.c
1/* Menu layout in-memory data structure (a custom "DOM tree") */
2
3/*
4 * Copyright (C) 2002 - 2004 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include <config.h>
23
24#include "menu-layout.h"
25
26#include <string.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <unistd.h>
30#include <errno(*__errno_location ()).h>
31
32#include "entry-directories.h"
33#include "menu-util.h"
34
35typedef struct MenuLayoutNodeMenu MenuLayoutNodeMenu;
36typedef struct MenuLayoutNodeRoot MenuLayoutNodeRoot;
37typedef struct MenuLayoutNodeLegacyDir MenuLayoutNodeLegacyDir;
38typedef struct MenuLayoutNodeMergeFile MenuLayoutNodeMergeFile;
39typedef struct MenuLayoutNodeDefaultLayout MenuLayoutNodeDefaultLayout;
40typedef struct MenuLayoutNodeMenuname MenuLayoutNodeMenuname;
41typedef struct MenuLayoutNodeMerge MenuLayoutNodeMerge;
42
43struct MenuLayoutNode
44{
45 /* Node lists are circular, for length-one lists
46 * prev/next point back to the node itself.
47 */
48 MenuLayoutNode *prev;
49 MenuLayoutNode *next;
50 MenuLayoutNode *parent;
51 MenuLayoutNode *children;
52
53 char *content;
54
55 guint refcount : 20;
56 guint type : 7;
57};
58
59struct MenuLayoutNodeRoot
60{
61 MenuLayoutNode node;
62
63 char *basedir;
64 char *name;
65
66 GMainContext *main_context;
67
68 GSList *monitors;
69 GSource *monitors_idle_handler;
70};
71
72struct MenuLayoutNodeMenu
73{
74 MenuLayoutNode node;
75
76 MenuLayoutNode *name_node; /* cache of the <Name> node */
77
78 EntryDirectoryList *app_dirs;
79 EntryDirectoryList *dir_dirs;
80};
81
82struct MenuLayoutNodeLegacyDir
83{
84 MenuLayoutNode node;
85
86 char *prefix;
87};
88
89struct MenuLayoutNodeMergeFile
90{
91 MenuLayoutNode node;
92
93 MenuMergeFileType type;
94};
95
96struct MenuLayoutNodeDefaultLayout
97{
98 MenuLayoutNode node;
99
100 MenuLayoutValues layout_values;
101};
102
103struct MenuLayoutNodeMenuname
104{
105 MenuLayoutNode node;
106
107 MenuLayoutValues layout_values;
108};
109
110struct MenuLayoutNodeMerge
111{
112 MenuLayoutNode node;
113
114 MenuLayoutMergeType merge_type;
115};
116
117typedef struct
118{
119 MenuLayoutNodeEntriesChangedFunc callback;
120 gpointer user_data;
121} MenuLayoutNodeEntriesMonitor;
122
123
124static inline MenuLayoutNode *
125node_next (MenuLayoutNode *node)
126{
127 /* root nodes (no parent) never have siblings */
128 if (node->parent == NULL((void*)0))
129 return NULL((void*)0);
130
131 /* circular list */
132 if (node->next == node->parent->children)
133 return NULL((void*)0);
134
135 return node->next;
136}
137
138static gboolean
139menu_layout_invoke_monitors (MenuLayoutNodeRoot *nr)
140{
141 GSList *tmp;
142
143 g_assert (nr->node.type == MENU_LAYOUT_NODE_ROOT)do { if (nr->node.type == MENU_LAYOUT_NODE_ROOT) ; else g_assertion_message_expr
(((gchar*) 0), "menu-layout.c", 143, ((const char*) (__func__
)), "nr->node.type == MENU_LAYOUT_NODE_ROOT"); } while (0)
;
144
145 nr->monitors_idle_handler = NULL((void*)0);
146
147 tmp = nr->monitors;
148 while (tmp != NULL((void*)0))
149 {
150 MenuLayoutNodeEntriesMonitor *monitor = tmp->data;
151 GSList *next = tmp->next;
152
153 monitor->callback ((MenuLayoutNode *) nr, monitor->user_data);
154
155 tmp = next;
156 }
157
158 return FALSE(0);
159}
160
161static void
162handle_entry_directory_changed (EntryDirectory *dir G_GNUC_UNUSED__attribute__ ((__unused__)),
163 MenuLayoutNode *node)
164{
165 MenuLayoutNodeRoot *nr;
166
167 g_assert (node->type == MENU_LAYOUT_NODE_MENU)do { if (node->type == MENU_LAYOUT_NODE_MENU) ; else g_assertion_message_expr
(((gchar*) 0), "menu-layout.c", 167, ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_MENU"); } while (0)
;
168
169 nr = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node);
170
171 if (nr->monitors_idle_handler == NULL((void*)0))
172 {
173 nr->monitors_idle_handler = g_idle_source_new ();
174 g_source_set_callback (nr->monitors_idle_handler,
175 (GSourceFunc) menu_layout_invoke_monitors, nr, NULL((void*)0));
176 g_source_attach (nr->monitors_idle_handler, nr->main_context);
177 g_source_unref (nr->monitors_idle_handler);
178 }
179}
180
181static void
182remove_entry_directory_list (MenuLayoutNodeMenu *nm,
183 EntryDirectoryList **dirs)
184{
185 if (*dirs)
186 {
187 entry_directory_list_remove_monitors (*dirs,
188 (EntryDirectoryChangedFunc) handle_entry_directory_changed,
189 nm);
190 entry_directory_list_unref (*dirs);
191 *dirs = NULL((void*)0);
192 }
193}
194
195MenuLayoutNode *
196menu_layout_node_ref (MenuLayoutNode *node)
197{
198 g_return_val_if_fail (node != NULL, NULL)do { if ((node != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "node != NULL"); return
(((void*)0)); } } while (0)
;
199
200 node->refcount += 1;
201
202 return node;
203}
204
205void
206menu_layout_node_unref (MenuLayoutNode *node)
207{
208 g_return_if_fail (node != NULL)do { if ((node != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "node != NULL"); return
; } } while (0)
;
209 g_return_if_fail (node->refcount > 0)do { if ((node->refcount > 0)) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "node->refcount > 0"
); return; } } while (0)
;
210
211 node->refcount -= 1;
212 if (node->refcount == 0)
213 {
214 MenuLayoutNode *iter;
215
216 iter = node->children;
217 while (iter != NULL((void*)0))
218 {
219 MenuLayoutNode *next = node_next (iter);
220
221 menu_layout_node_unref (iter);
222
223 iter = next;
224 }
225
226 if (node->type == MENU_LAYOUT_NODE_MENU)
227 {
228 MenuLayoutNodeMenu *nm = (MenuLayoutNodeMenu *) node;
229
230 if (nm->name_node)
231 menu_layout_node_unref (nm->name_node);
232
233 remove_entry_directory_list (nm, &nm->app_dirs);
234 remove_entry_directory_list (nm, &nm->dir_dirs);
235 }
236 else if (node->type == MENU_LAYOUT_NODE_LEGACY_DIR)
237 {
238 MenuLayoutNodeLegacyDir *legacy = (MenuLayoutNodeLegacyDir *) node;
239
240 g_free (legacy->prefix);
241 }
242 else if (node->type == MENU_LAYOUT_NODE_ROOT)
243 {
244 MenuLayoutNodeRoot *nr = (MenuLayoutNodeRoot*) node;
245
246 g_slist_foreach (nr->monitors, (GFunc) g_free, NULL((void*)0));
247 g_slist_free (nr->monitors);
248
249 if (nr->monitors_idle_handler != NULL((void*)0))
250 g_source_destroy (nr->monitors_idle_handler);
251 nr->monitors_idle_handler = NULL((void*)0);
252
253 if (nr->main_context != NULL((void*)0))
254 g_main_context_unref (nr->main_context);
255 nr->main_context = NULL((void*)0);
256
257 g_free (nr->basedir);
258 g_free (nr->name);
259 }
260
261 g_free (node->content);
262 g_free (node);
263 }
264}
265
266MenuLayoutNode *
267menu_layout_node_new (MenuLayoutNodeType type)
268{
269 MenuLayoutNode *node;
270
271 switch (type)
272 {
273 case MENU_LAYOUT_NODE_MENU:
274 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMenu, 1)((MenuLayoutNodeMenu *) g_malloc0_n ((1), sizeof (MenuLayoutNodeMenu
)))
;
275 break;
276
277 case MENU_LAYOUT_NODE_LEGACY_DIR:
278 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeLegacyDir, 1)((MenuLayoutNodeLegacyDir *) g_malloc0_n ((1), sizeof (MenuLayoutNodeLegacyDir
)))
;
279 break;
280
281 case MENU_LAYOUT_NODE_ROOT:
282 node = (MenuLayoutNode*) g_new0 (MenuLayoutNodeRoot, 1)((MenuLayoutNodeRoot *) g_malloc0_n ((1), sizeof (MenuLayoutNodeRoot
)))
;
283 break;
284
285 case MENU_LAYOUT_NODE_MERGE_FILE:
286 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMergeFile, 1)((MenuLayoutNodeMergeFile *) g_malloc0_n ((1), sizeof (MenuLayoutNodeMergeFile
)))
;
287 break;
288
289 case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
290 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeDefaultLayout, 1)((MenuLayoutNodeDefaultLayout *) g_malloc0_n ((1), sizeof (MenuLayoutNodeDefaultLayout
)))
;
291 break;
292
293 case MENU_LAYOUT_NODE_MENUNAME:
294 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMenuname, 1)((MenuLayoutNodeMenuname *) g_malloc0_n ((1), sizeof (MenuLayoutNodeMenuname
)))
;
295 break;
296
297 case MENU_LAYOUT_NODE_MERGE:
298 node = (MenuLayoutNode *) g_new0 (MenuLayoutNodeMerge, 1)((MenuLayoutNodeMerge *) g_malloc0_n ((1), sizeof (MenuLayoutNodeMerge
)))
;
299 break;
300
301 default:
302 node = g_new0 (MenuLayoutNode, 1)((MenuLayoutNode *) g_malloc0_n ((1), sizeof (MenuLayoutNode)
))
;
303 break;
304 }
305
306 node->type = type;
307
308 node->refcount = 1;
309
310 /* we're in a list of one node */
311 node->next = node;
312 node->prev = node;
313
314 return node;
315}
316
317MenuLayoutNode *
318menu_layout_node_get_next (MenuLayoutNode *node)
319{
320 return node_next (node);
321}
322
323MenuLayoutNode *
324menu_layout_node_get_parent (MenuLayoutNode *node)
325{
326 return node->parent;
327}
328
329MenuLayoutNode *
330menu_layout_node_get_children (MenuLayoutNode *node)
331{
332 return node->children;
333}
334
335MenuLayoutNode *
336menu_layout_node_get_root (MenuLayoutNode *node)
337{
338 MenuLayoutNode *parent;
339
340 parent = node;
341 while (parent->parent != NULL((void*)0))
342 parent = parent->parent;
343
344 g_assert (parent->type == MENU_LAYOUT_NODE_ROOT)do { if (parent->type == MENU_LAYOUT_NODE_ROOT) ; else g_assertion_message_expr
(((gchar*) 0), "menu-layout.c", 344, ((const char*) (__func__
)), "parent->type == MENU_LAYOUT_NODE_ROOT"); } while (0)
;
345
346 return parent;
347}
348
349char *
350menu_layout_node_get_content_as_path (MenuLayoutNode *node)
351{
352 if (node->content == NULL((void*)0))
353 {
354 menu_verbose (" (node has no content to get as a path)\n");
355 return NULL((void*)0);
356 }
357
358 if (g_path_is_absolute (node->content))
359 {
360 return g_strdup (node->content)g_strdup_inline (node->content);
361 }
362 else
363 {
364 MenuLayoutNodeRoot *root;
365
366 root = (MenuLayoutNodeRoot *) menu_layout_node_get_root (node);
367
368 if (root->basedir == NULL((void*)0))
369 {
370 menu_verbose ("No basedir available, using \"%s\" as-is\n",
371 node->content);
372 return g_strdup (node->content)g_strdup_inline (node->content);
373 }
374 else
375 {
376 menu_verbose ("Using basedir \"%s\" filename \"%s\"\n",
377 root->basedir, node->content);
378 return g_build_filename (root->basedir, node->content, NULL((void*)0));
379 }
380 }
381}
382
383#define RETURN_IF_NO_PARENT(node)do { if ((node)->parent == ((void*)0)) { g_warning ("To add siblings to a menu node, "
"it must not be the root node, " "and must be linked in below some root node\n"
"node parent = %p and type = %d", (node)->parent, (node)->
type); return; } } while (0)
G_STMT_STARTdo { \
384 if ((node)->parent == NULL((void*)0)) \
385 { \
386 g_warning ("To add siblings to a menu node, " \
387 "it must not be the root node, " \
388 "and must be linked in below some root node\n" \
389 "node parent = %p and type = %d", \
390 (node)->parent, (node)->type); \
391 return; \
392 } \
393 } G_STMT_ENDwhile (0)
394
395#define RETURN_IF_HAS_ENTRY_DIRS(node)do { if ((node)->type == MENU_LAYOUT_NODE_MENU && (
((MenuLayoutNodeMenu*)(node))->app_dirs != ((void*)0) || (
(MenuLayoutNodeMenu*)(node))->dir_dirs != ((void*)0))) { g_warning
("node acquired ->app_dirs or ->dir_dirs " "while not rooted in a tree\n"
); return; } } while (0)
G_STMT_STARTdo { \
396 if ((node)->type == MENU_LAYOUT_NODE_MENU && \
397 (((MenuLayoutNodeMenu*)(node))->app_dirs != NULL((void*)0) || \
398 ((MenuLayoutNodeMenu*)(node))->dir_dirs != NULL((void*)0))) \
399 { \
400 g_warning ("node acquired ->app_dirs or ->dir_dirs " \
401 "while not rooted in a tree\n"); \
402 return; \
403 } \
404 } G_STMT_ENDwhile (0) \
405
406void
407menu_layout_node_insert_before (MenuLayoutNode *node,
408 MenuLayoutNode *new_sibling)
409{
410 g_return_if_fail (new_sibling != NULL)do { if ((new_sibling != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "new_sibling != NULL"
); return; } } while (0)
;
411 g_return_if_fail (new_sibling->parent == NULL)do { if ((new_sibling->parent == ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "new_sibling->parent == NULL"
); return; } } while (0)
;
412
413 RETURN_IF_NO_PARENT (node)do { if ((node)->parent == ((void*)0)) { g_warning ("To add siblings to a menu node, "
"it must not be the root node, " "and must be linked in below some root node\n"
"node parent = %p and type = %d", (node)->parent, (node)->
type); return; } } while (0)
;
414 RETURN_IF_HAS_ENTRY_DIRS (new_sibling)do { if ((new_sibling)->type == MENU_LAYOUT_NODE_MENU &&
(((MenuLayoutNodeMenu*)(new_sibling))->app_dirs != ((void
*)0) || ((MenuLayoutNodeMenu*)(new_sibling))->dir_dirs != (
(void*)0))) { g_warning ("node acquired ->app_dirs or ->dir_dirs "
"while not rooted in a tree\n"); return; } } while (0)
;
415
416 new_sibling->next = node;
417 new_sibling->prev = node->prev;
418
419 node->prev = new_sibling;
420 new_sibling->prev->next = new_sibling;
421
422 new_sibling->parent = node->parent;
423
424 if (node == node->parent->children)
425 node->parent->children = new_sibling;
426
427 menu_layout_node_ref (new_sibling);
428}
429
430void
431menu_layout_node_insert_after (MenuLayoutNode *node,
432 MenuLayoutNode *new_sibling)
433{
434 g_return_if_fail (new_sibling != NULL)do { if ((new_sibling != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "new_sibling != NULL"
); return; } } while (0)
;
435 g_return_if_fail (new_sibling->parent == NULL)do { if ((new_sibling->parent == ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "new_sibling->parent == NULL"
); return; } } while (0)
;
436
437 RETURN_IF_NO_PARENT (node)do { if ((node)->parent == ((void*)0)) { g_warning ("To add siblings to a menu node, "
"it must not be the root node, " "and must be linked in below some root node\n"
"node parent = %p and type = %d", (node)->parent, (node)->
type); return; } } while (0)
;
438 RETURN_IF_HAS_ENTRY_DIRS (new_sibling)do { if ((new_sibling)->type == MENU_LAYOUT_NODE_MENU &&
(((MenuLayoutNodeMenu*)(new_sibling))->app_dirs != ((void
*)0) || ((MenuLayoutNodeMenu*)(new_sibling))->dir_dirs != (
(void*)0))) { g_warning ("node acquired ->app_dirs or ->dir_dirs "
"while not rooted in a tree\n"); return; } } while (0)
;
439
440 new_sibling->prev = node;
441 new_sibling->next = node->next;
442
443 node->next = new_sibling;
444 new_sibling->next->prev = new_sibling;
445
446 new_sibling->parent = node->parent;
447
448 menu_layout_node_ref (new_sibling);
449}
450
451void
452menu_layout_node_prepend_child (MenuLayoutNode *parent,
453 MenuLayoutNode *new_child)
454{
455 RETURN_IF_HAS_ENTRY_DIRS (new_child)do { if ((new_child)->type == MENU_LAYOUT_NODE_MENU &&
(((MenuLayoutNodeMenu*)(new_child))->app_dirs != ((void*)
0) || ((MenuLayoutNodeMenu*)(new_child))->dir_dirs != ((void
*)0))) { g_warning ("node acquired ->app_dirs or ->dir_dirs "
"while not rooted in a tree\n"); return; } } while (0)
;
456
457 if (parent->children)
458 {
459 menu_layout_node_insert_before (parent->children, new_child);
460 }
461 else
462 {
463 parent->children = menu_layout_node_ref (new_child);
464 new_child->parent = parent;
465 }
466}
467
468void
469menu_layout_node_append_child (MenuLayoutNode *parent,
470 MenuLayoutNode *new_child)
471{
472 RETURN_IF_HAS_ENTRY_DIRS (new_child)do { if ((new_child)->type == MENU_LAYOUT_NODE_MENU &&
(((MenuLayoutNodeMenu*)(new_child))->app_dirs != ((void*)
0) || ((MenuLayoutNodeMenu*)(new_child))->dir_dirs != ((void
*)0))) { g_warning ("node acquired ->app_dirs or ->dir_dirs "
"while not rooted in a tree\n"); return; } } while (0)
;
473
474 if (parent->children)
475 {
476 menu_layout_node_insert_after (parent->children->prev, new_child);
477 }
478 else
479 {
480 parent->children = menu_layout_node_ref (new_child);
481 new_child->parent = parent;
482 }
483}
484
485void
486menu_layout_node_unlink (MenuLayoutNode *node)
487{
488 g_return_if_fail (node != NULL)do { if ((node != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "node != NULL"); return
; } } while (0)
;
489 g_return_if_fail (node->parent != NULL)do { if ((node->parent != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "node->parent != NULL"
); return; } } while (0)
;
490
491 menu_layout_node_steal (node);
492 menu_layout_node_unref (node);
493}
494
495static void
496recursive_clean_entry_directory_lists (MenuLayoutNode *node,
497 gboolean apps)
498{
499 EntryDirectoryList **dirs;
500 MenuLayoutNodeMenu *nm;
501 MenuLayoutNode *iter;
502
503 if (node->type != MENU_LAYOUT_NODE_MENU)
504 return;
505
506 nm = (MenuLayoutNodeMenu *) node;
507
508 dirs = apps ? &nm->app_dirs : &nm->dir_dirs;
509
510 if (*dirs == NULL((void*)0) || entry_directory_list_get_length (*dirs) == 0)
511 return; /* child menus continue to have valid lists */
512
513 remove_entry_directory_list (nm, dirs);
514
515 iter = node->children;
516 while (iter != NULL((void*)0))
517 {
518 if (iter->type == MENU_LAYOUT_NODE_MENU)
519 recursive_clean_entry_directory_lists (iter, apps);
520
521 iter = node_next (iter);
522 }
523}
524
525void
526menu_layout_node_steal (MenuLayoutNode *node)
527{
528 g_return_if_fail (node != NULL)do { if ((node != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "node != NULL"); return
; } } while (0)
;
529 g_return_if_fail (node->parent != NULL)do { if ((node->parent != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "node->parent != NULL"
); return; } } while (0)
;
530
531 switch (node->type)
532 {
533 case MENU_LAYOUT_NODE_NAME:
534 {
535 MenuLayoutNodeMenu *nm = (MenuLayoutNodeMenu *) node->parent;
536
537 if (nm->name_node == node)
538 {
539 menu_layout_node_unref (nm->name_node);
540 nm->name_node = NULL((void*)0);
541 }
542 }
543 break;
544
545 case MENU_LAYOUT_NODE_APP_DIR:
546 recursive_clean_entry_directory_lists (node->parent, TRUE(!(0)));
547 break;
548
549 case MENU_LAYOUT_NODE_DIRECTORY_DIR:
550 recursive_clean_entry_directory_lists (node->parent, FALSE(0));
551 break;
552
553 default:
554 break;
555 }
556
557 if (node->parent && node->parent->children == node)
558 {
559 if (node->next != node)
560 node->parent->children = node->next;
561 else
562 node->parent->children = NULL((void*)0);
563 }
564
565 /* these are no-ops for length-one node lists */
566 node->prev->next = node->next;
567 node->next->prev = node->prev;
568
569 node->parent = NULL((void*)0);
570
571 /* point to ourselves, now we're length one */
572 node->next = node;
573 node->prev = node;
574}
575
576MenuLayoutNodeType
577menu_layout_node_get_type (MenuLayoutNode *node)
578{
579 return node->type;
580}
581
582const char *
583menu_layout_node_get_content (MenuLayoutNode *node)
584{
585 return node->content;
586}
587
588void
589menu_layout_node_set_content (MenuLayoutNode *node,
590 const char *content)
591{
592 if (node->content == content)
593 return;
594
595 g_free (node->content);
596 node->content = g_strdup (content)g_strdup_inline (content);
597}
598
599const char *
600menu_layout_node_root_get_name (MenuLayoutNode *node)
601{
602 MenuLayoutNodeRoot *nr;
603
604 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_ROOT, NULL)do { if ((node->type == MENU_LAYOUT_NODE_ROOT)) { } else {
g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_ROOT"); return (((void
*)0)); } } while (0)
;
605
606 nr = (MenuLayoutNodeRoot*) node;
607
608 return nr->name;
609}
610
611const char *
612menu_layout_node_root_get_basedir (MenuLayoutNode *node)
613{
614 MenuLayoutNodeRoot *nr;
615
616 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_ROOT, NULL)do { if ((node->type == MENU_LAYOUT_NODE_ROOT)) { } else {
g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_ROOT"); return (((void
*)0)); } } while (0)
;
617
618 nr = (MenuLayoutNodeRoot*) node;
619
620 return nr->basedir;
621}
622
623const char *
624menu_layout_node_menu_get_name (MenuLayoutNode *node)
625{
626 MenuLayoutNodeMenu *nm;
627
628 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL)do { if ((node->type == MENU_LAYOUT_NODE_MENU)) { } else {
g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_MENU"); return (((void
*)0)); } } while (0)
;
629
630 nm = (MenuLayoutNodeMenu*) node;
631
632 if (nm->name_node == NULL((void*)0))
633 {
634 MenuLayoutNode *iter;
635
636 iter = node->children;
637 while (iter != NULL((void*)0))
638 {
639 if (iter->type == MENU_LAYOUT_NODE_NAME)
640 {
641 nm->name_node = menu_layout_node_ref (iter);
642 break;
643 }
644
645 iter = node_next (iter);
646 }
647 }
648
649 if (nm->name_node == NULL((void*)0))
650 return NULL((void*)0);
651
652 return menu_layout_node_get_content (nm->name_node);
653}
654
655static void
656ensure_dir_lists (MenuLayoutNodeMenu *nm)
657{
658 MenuLayoutNode *node;
659 MenuLayoutNode *iter;
660 EntryDirectoryList *app_dirs;
661 EntryDirectoryList *dir_dirs;
662
663 node = (MenuLayoutNode *) nm;
664
665 if (nm->app_dirs && nm->dir_dirs)
666 return;
667
668 app_dirs = NULL((void*)0);
669 dir_dirs = NULL((void*)0);
670
671 if (nm->app_dirs == NULL((void*)0))
672 {
673 app_dirs = entry_directory_list_new ();
674
675 if (node->parent && node->parent->type == MENU_LAYOUT_NODE_MENU)
676 {
677 EntryDirectoryList *dirs;
678
679 if ((dirs = menu_layout_node_menu_get_app_dirs (node->parent)))
680 entry_directory_list_append_list (app_dirs, dirs);
681 }
682 }
683
684 if (nm->dir_dirs == NULL((void*)0))
685 {
686 dir_dirs = entry_directory_list_new ();
687
688 if (node->parent && node->parent->type == MENU_LAYOUT_NODE_MENU)
689 {
690 EntryDirectoryList *dirs;
691
692 if ((dirs = menu_layout_node_menu_get_directory_dirs (node->parent)))
693 entry_directory_list_append_list (dir_dirs, dirs);
694 }
695 }
696
697 iter = node->children;
698 while (iter != NULL((void*)0))
699 {
700 EntryDirectory *ed;
701
702 if (app_dirs != NULL((void*)0) && iter->type == MENU_LAYOUT_NODE_APP_DIR)
703 {
704 char *path;
705
706 path = menu_layout_node_get_content_as_path (iter);
707
708 ed = entry_directory_new (DESKTOP_ENTRY_DESKTOP, path);
709 if (ed != NULL((void*)0))
710 {
711 entry_directory_list_prepend (app_dirs, ed);
712 entry_directory_unref (ed);
713 }
714
715 g_free (path);
716 }
717
718 if (dir_dirs != NULL((void*)0) && iter->type == MENU_LAYOUT_NODE_DIRECTORY_DIR)
719 {
720 char *path;
721
722 path = menu_layout_node_get_content_as_path (iter);
723
724 ed = entry_directory_new (DESKTOP_ENTRY_DIRECTORY, path);
725 if (ed != NULL((void*)0))
726 {
727 entry_directory_list_prepend (dir_dirs, ed);
728 entry_directory_unref (ed);
729 }
730
731 g_free (path);
732 }
733
734 if (iter->type == MENU_LAYOUT_NODE_LEGACY_DIR)
735 {
736 MenuLayoutNodeLegacyDir *legacy = (MenuLayoutNodeLegacyDir *) iter;
737 char *path;
738
739 path = menu_layout_node_get_content_as_path (iter);
740
741 if (app_dirs != NULL((void*)0)) /* we're loading app dirs */
742 {
743 ed = entry_directory_new_legacy (DESKTOP_ENTRY_DESKTOP,
744 path,
745 legacy->prefix);
746 if (ed != NULL((void*)0))
747 {
748 entry_directory_list_prepend (app_dirs, ed);
749 entry_directory_unref (ed);
750 }
751 }
752
753 if (dir_dirs != NULL((void*)0)) /* we're loading dir dirs */
754 {
755 ed = entry_directory_new_legacy (DESKTOP_ENTRY_DIRECTORY,
756 path,
757 legacy->prefix);
758 if (ed != NULL((void*)0))
759 {
760 entry_directory_list_prepend (dir_dirs, ed);
761 entry_directory_unref (ed);
762 }
763 }
764
765 g_free (path);
766 }
767
768 iter = node_next (iter);
769 }
770
771 if (app_dirs)
772 {
773 g_assert (nm->app_dirs == NULL)do { if (nm->app_dirs == ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "menu-layout.c", 773, ((const char*) (__func__
)), "nm->app_dirs == NULL"); } while (0)
;
774
775 nm->app_dirs = app_dirs;
776 entry_directory_list_add_monitors (nm->app_dirs,
777 (EntryDirectoryChangedFunc) handle_entry_directory_changed,
778 nm);
779 }
780
781 if (dir_dirs)
782 {
783 g_assert (nm->dir_dirs == NULL)do { if (nm->dir_dirs == ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "menu-layout.c", 783, ((const char*) (__func__
)), "nm->dir_dirs == NULL"); } while (0)
;
784
785 nm->dir_dirs = dir_dirs;
786 entry_directory_list_add_monitors (nm->dir_dirs,
787 (EntryDirectoryChangedFunc) handle_entry_directory_changed,
788 nm);
789 }
790}
791
792EntryDirectoryList *
793menu_layout_node_menu_get_app_dirs (MenuLayoutNode *node)
794{
795 MenuLayoutNodeMenu *nm;
796
797 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL)do { if ((node->type == MENU_LAYOUT_NODE_MENU)) { } else {
g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_MENU"); return (((void
*)0)); } } while (0)
;
798
799 nm = (MenuLayoutNodeMenu *) node;
800
801 ensure_dir_lists (nm);
802
803 return nm->app_dirs;
804}
805
806EntryDirectoryList *
807menu_layout_node_menu_get_directory_dirs (MenuLayoutNode *node)
808{
809 MenuLayoutNodeMenu *nm;
810
811 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MENU, NULL)do { if ((node->type == MENU_LAYOUT_NODE_MENU)) { } else {
g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_MENU"); return (((void
*)0)); } } while (0)
;
812
813 nm = (MenuLayoutNodeMenu *) node;
814
815 ensure_dir_lists (nm);
816
817 return nm->dir_dirs;
818}
819
820const char *
821menu_layout_node_move_get_old (MenuLayoutNode *node)
822{
823 MenuLayoutNode *iter;
824
825 iter = node->children;
826 while (iter != NULL((void*)0))
827 {
828 if (iter->type == MENU_LAYOUT_NODE_OLD)
829 return iter->content;
830
831 iter = node_next (iter);
832 }
833
834 return NULL((void*)0);
835}
836
837const char *
838menu_layout_node_move_get_new (MenuLayoutNode *node)
839{
840 MenuLayoutNode *iter;
841
842 iter = node->children;
843 while (iter != NULL((void*)0))
844 {
845 if (iter->type == MENU_LAYOUT_NODE_NEW)
846 return iter->content;
847
848 iter = node_next (iter);
849 }
850
851 return NULL((void*)0);
852}
853
854const char *
855menu_layout_node_legacy_dir_get_prefix (MenuLayoutNode *node)
856{
857 MenuLayoutNodeLegacyDir *legacy;
858
859 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_LEGACY_DIR, NULL)do { if ((node->type == MENU_LAYOUT_NODE_LEGACY_DIR)) { } else
{ g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_LEGACY_DIR"); return (
((void*)0)); } } while (0)
;
860
861 legacy = (MenuLayoutNodeLegacyDir *) node;
862
863 return legacy->prefix;
864}
865
866void
867menu_layout_node_legacy_dir_set_prefix (MenuLayoutNode *node,
868 const char *prefix)
869{
870 MenuLayoutNodeLegacyDir *legacy;
871
872 g_return_if_fail (node->type == MENU_LAYOUT_NODE_LEGACY_DIR)do { if ((node->type == MENU_LAYOUT_NODE_LEGACY_DIR)) { } else
{ g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_LEGACY_DIR"); return; }
} while (0)
;
873
874 legacy = (MenuLayoutNodeLegacyDir *) node;
875
876 g_free (legacy->prefix);
877 legacy->prefix = g_strdup (prefix)g_strdup_inline (prefix);
878}
879
880MenuMergeFileType
881menu_layout_node_merge_file_get_type (MenuLayoutNode *node)
882{
883 MenuLayoutNodeMergeFile *merge_file;
884
885 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MERGE_FILE, FALSE)do { if ((node->type == MENU_LAYOUT_NODE_MERGE_FILE)) { } else
{ g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_MERGE_FILE"); return (
(0)); } } while (0)
;
886
887 merge_file = (MenuLayoutNodeMergeFile *) node;
888
889 return merge_file->type;
890}
891
892void
893menu_layout_node_merge_file_set_type (MenuLayoutNode *node,
894 MenuMergeFileType type)
895{
896 MenuLayoutNodeMergeFile *merge_file;
897
898 g_return_if_fail (node->type == MENU_LAYOUT_NODE_MERGE_FILE)do { if ((node->type == MENU_LAYOUT_NODE_MERGE_FILE)) { } else
{ g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_MERGE_FILE"); return; }
} while (0)
;
899
900 merge_file = (MenuLayoutNodeMergeFile *) node;
901
902 merge_file->type = type;
903}
904
905MenuLayoutMergeType
906menu_layout_node_merge_get_type (MenuLayoutNode *node)
907{
908 MenuLayoutNodeMerge *merge;
909
910 g_return_val_if_fail (node->type == MENU_LAYOUT_NODE_MERGE, 0)do { if ((node->type == MENU_LAYOUT_NODE_MERGE)) { } else {
g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_MERGE"); return (0); }
} while (0)
;
911
912 merge = (MenuLayoutNodeMerge *) node;
913
914 return merge->merge_type;
915}
916
917static void
918menu_layout_node_merge_set_type (MenuLayoutNode *node,
919 const char *merge_type)
920{
921 MenuLayoutNodeMerge *merge;
922
923 g_return_if_fail (node->type == MENU_LAYOUT_NODE_MERGE)do { if ((node->type == MENU_LAYOUT_NODE_MERGE)) { } else {
g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_MERGE"); return; } } while
(0)
;
924
925 merge = (MenuLayoutNodeMerge *) node;
926
927 merge->merge_type = MENU_LAYOUT_MERGE_NONE;
928
929 if (strcmp (merge_type, "menus") == 0)
930 {
931 merge->merge_type = MENU_LAYOUT_MERGE_MENUS;
932 }
933 else if (strcmp (merge_type, "files") == 0)
934 {
935 merge->merge_type = MENU_LAYOUT_MERGE_FILES;
936 }
937 else if (strcmp (merge_type, "all") == 0)
938 {
939 merge->merge_type = MENU_LAYOUT_MERGE_ALL;
940 }
941}
942
943void
944menu_layout_node_default_layout_get_values (MenuLayoutNode *node,
945 MenuLayoutValues *values)
946{
947 MenuLayoutNodeDefaultLayout *default_layout;
948
949 g_return_if_fail (node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT)do { if ((node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT)) {
} else { g_return_if_fail_warning (((gchar*) 0), ((const char
*) (__func__)), "node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT"
); return; } } while (0)
;
950 g_return_if_fail (values != NULL)do { if ((values != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "values != NULL")
; return; } } while (0)
;
951
952 default_layout = (MenuLayoutNodeDefaultLayout *) node;
953
954 *values = default_layout->layout_values;
955}
956
957void
958menu_layout_node_menuname_get_values (MenuLayoutNode *node,
959 MenuLayoutValues *values)
960{
961 MenuLayoutNodeMenuname *menuname;
962
963 g_return_if_fail (node->type == MENU_LAYOUT_NODE_MENUNAME)do { if ((node->type == MENU_LAYOUT_NODE_MENUNAME)) { } else
{ g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_MENUNAME"); return; } }
while (0)
;
964 g_return_if_fail (values != NULL)do { if ((values != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "values != NULL")
; return; } } while (0)
;
965
966 menuname = (MenuLayoutNodeMenuname *) node;
967
968 *values = menuname->layout_values;
969}
970
971static void
972menu_layout_values_set (MenuLayoutValues *values,
973 const char *show_empty,
974 const char *inline_menus,
975 const char *inline_limit,
976 const char *inline_header,
977 const char *inline_alias)
978{
979 values->mask = MENU_LAYOUT_VALUES_NONE;
980 values->show_empty = FALSE(0);
981 values->inline_menus = FALSE(0);
982 values->inline_limit = 4;
983 values->inline_header = FALSE(0);
984 values->inline_alias = FALSE(0);
985
986 if (show_empty != NULL((void*)0))
987 {
988 values->show_empty = strcmp (show_empty, "true") == 0;
989 values->mask |= MENU_LAYOUT_VALUES_SHOW_EMPTY;
990 }
991
992 if (inline_menus != NULL((void*)0))
993 {
994 values->inline_menus = strcmp (inline_menus, "true") == 0;
995 values->mask |= MENU_LAYOUT_VALUES_INLINE_MENUS;
996 }
997
998 if (inline_limit != NULL((void*)0))
999 {
1000 char *end;
1001 int limit;
1002
1003 limit = strtol (inline_limit, &end, 10);
1004 if (*end == '\0')
1005 {
1006 values->inline_limit = limit;
1007 values->mask |= MENU_LAYOUT_VALUES_INLINE_LIMIT;
1008 }
1009 }
1010
1011 if (inline_header != NULL((void*)0))
1012 {
1013 values->inline_header = strcmp (inline_header, "true") == 0;
1014 values->mask |= MENU_LAYOUT_VALUES_INLINE_HEADER;
1015 }
1016
1017 if (inline_alias != NULL((void*)0))
1018 {
1019 values->inline_alias = strcmp (inline_alias, "true") == 0;
1020 values->mask |= MENU_LAYOUT_VALUES_INLINE_ALIAS;
1021 }
1022}
1023
1024static void
1025menu_layout_node_default_layout_set_values (MenuLayoutNode *node,
1026 const char *show_empty,
1027 const char *inline_menus,
1028 const char *inline_limit,
1029 const char *inline_header,
1030 const char *inline_alias)
1031{
1032 MenuLayoutNodeDefaultLayout *default_layout;
1033
1034 g_return_if_fail (node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT)do { if ((node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT)) {
} else { g_return_if_fail_warning (((gchar*) 0), ((const char
*) (__func__)), "node->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT"
); return; } } while (0)
;
1035
1036 default_layout = (MenuLayoutNodeDefaultLayout *) node;
1037
1038 menu_layout_values_set (&default_layout->layout_values,
1039 show_empty,
1040 inline_menus,
1041 inline_limit,
1042 inline_header,
1043 inline_alias);
1044}
1045
1046static void
1047menu_layout_node_menuname_set_values (MenuLayoutNode *node,
1048 const char *show_empty,
1049 const char *inline_menus,
1050 const char *inline_limit,
1051 const char *inline_header,
1052 const char *inline_alias)
1053{
1054 MenuLayoutNodeMenuname *menuname;
1055
1056 g_return_if_fail (node->type == MENU_LAYOUT_NODE_MENUNAME)do { if ((node->type == MENU_LAYOUT_NODE_MENUNAME)) { } else
{ g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_MENUNAME"); return; } }
while (0)
;
1057
1058 menuname = (MenuLayoutNodeMenuname *) node;
1059
1060 menu_layout_values_set (&menuname->layout_values,
1061 show_empty,
1062 inline_menus,
1063 inline_limit,
1064 inline_header,
1065 inline_alias);
1066}
1067
1068void
1069menu_layout_node_root_add_entries_monitor (MenuLayoutNode *node,
1070 MenuLayoutNodeEntriesChangedFunc callback,
1071 gpointer user_data)
1072{
1073 MenuLayoutNodeEntriesMonitor *monitor;
1074 MenuLayoutNodeRoot *nr;
1075 GSList *tmp;
1076
1077 g_return_if_fail (node->type == MENU_LAYOUT_NODE_ROOT)do { if ((node->type == MENU_LAYOUT_NODE_ROOT)) { } else {
g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_ROOT"); return; } } while
(0)
;
1078
1079 nr = (MenuLayoutNodeRoot *) node;
1080
1081 tmp = nr->monitors;
1082 while (tmp != NULL((void*)0))
1083 {
1084 monitor = tmp->data;
1085
1086 if (monitor->callback == callback &&
1087 monitor->user_data == user_data)
1088 break;
1089
1090 tmp = tmp->next;
1091 }
1092
1093 if (tmp == NULL((void*)0))
1094 {
1095 monitor = g_new0 (MenuLayoutNodeEntriesMonitor, 1)((MenuLayoutNodeEntriesMonitor *) g_malloc0_n ((1), sizeof (MenuLayoutNodeEntriesMonitor
)))
;
1096 monitor->callback = callback;
1097 monitor->user_data = user_data;
1098
1099 nr->monitors = g_slist_append (nr->monitors, monitor);
1100 }
1101}
1102
1103void
1104menu_layout_node_root_remove_entries_monitor (MenuLayoutNode *node,
1105 MenuLayoutNodeEntriesChangedFunc callback,
1106 gpointer user_data)
1107{
1108 MenuLayoutNodeRoot *nr;
1109 GSList *tmp;
1110
1111 g_return_if_fail (node->type == MENU_LAYOUT_NODE_ROOT)do { if ((node->type == MENU_LAYOUT_NODE_ROOT)) { } else {
g_return_if_fail_warning (((gchar*) 0), ((const char*) (__func__
)), "node->type == MENU_LAYOUT_NODE_ROOT"); return; } } while
(0)
;
1112
1113 nr = (MenuLayoutNodeRoot *) node;
1114
1115 tmp = nr->monitors;
1116 while (tmp != NULL((void*)0))
1117 {
1118 MenuLayoutNodeEntriesMonitor *monitor = tmp->data;
1119 GSList *next = tmp->next;
1120
1121 if (monitor->callback == callback &&
1122 monitor->user_data == user_data)
1123 {
1124 nr->monitors = g_slist_delete_link (nr->monitors, tmp);
1125 g_free (monitor);
1126 }
1127
1128 tmp = next;
1129 }
1130}
1131
1132
1133/*
1134 * Menu file parsing
1135 */
1136
1137typedef struct
1138{
1139 MenuLayoutNode *root;
1140 MenuLayoutNode *stack_top;
1141} MenuParser;
1142
1143static void set_error (GError **err,
1144 GMarkupParseContext *context,
1145 int error_domain,
1146 int error_code,
1147 const char *format,
1148 ...) G_GNUC_PRINTF (5, 6)__attribute__((__format__ (__printf__, 5, 6)));
1149
1150static void add_context_to_error (GError **err,
1151 GMarkupParseContext *context);
1152
1153static void start_element_handler (GMarkupParseContext *context,
1154 const char *element_name,
1155 const char **attribute_names,
1156 const char **attribute_values,
1157 gpointer user_data,
1158 GError **error);
1159static void end_element_handler (GMarkupParseContext *context,
1160 const char *element_name,
1161 gpointer user_data,
1162 GError **error);
1163static void text_handler (GMarkupParseContext *context,
1164 const char *text,
1165 gsize text_len,
1166 gpointer user_data,
1167 GError **error);
1168static void passthrough_handler (GMarkupParseContext *context,
1169 const char *passthrough_text,
1170 gsize text_len,
1171 gpointer user_data,
1172 GError **error);
1173
1174
1175static GMarkupParser menu_funcs = {
1176 start_element_handler,
1177 end_element_handler,
1178 text_handler,
1179 passthrough_handler,
1180 NULL((void*)0)
1181};
1182
1183static void
1184set_error (GError **err,
1185 GMarkupParseContext *context,
1186 int error_domain,
1187 int error_code,
1188 const char *format,
1189 ...)
1190{
1191 int line, ch;
1192 va_list args;
1193 char *str;
1194
1195 g_markup_parse_context_get_position (context, &line, &ch);
1196
1197 va_start (args, format)__builtin_va_start(args, format);
1198 str = g_strdup_vprintf (format, args);
1199 va_end (args)__builtin_va_end(args);
1200
1201 g_set_error (err, error_domain, error_code,
1202 "Line %d character %d: %s",
1203 line, ch, str);
1204
1205 g_free (str);
1206}
1207
1208static void
1209add_context_to_error (GError **err,
1210 GMarkupParseContext *context)
1211{
1212 int line, ch;
1213 char *str;
1214
1215 if (err == NULL((void*)0) || *err == NULL((void*)0))
1216 return;
1217
1218 g_markup_parse_context_get_position (context, &line, &ch);
1219
1220 str = g_strdup_printf ("Line %d character %d: %s",
1221 line, ch, (*err)->message);
1222 g_free ((*err)->message);
1223 (*err)->message = str;
1224}
1225
1226#define ELEMENT_IS(name)(strcmp (element_name, (name)) == 0) (strcmp (element_name, (name)) == 0)
1227
1228typedef struct
1229{
1230 const char *name;
1231 const char **retloc;
1232} LocateAttr;
1233
1234static gboolean
1235locate_attributes (GMarkupParseContext *context,
1236 const char *element_name,
1237 const char **attribute_names,
1238 const char **attribute_values,
1239 GError **error,
1240 const char *first_attribute_name,
1241 const char **first_attribute_retloc,
1242 ...)
1243{
1244#define MAX_ATTRS 24
1245 LocateAttr attrs[MAX_ATTRS];
1246 int n_attrs;
1247 va_list args;
1248 const char *name;
1249 const char **retloc;
1250 gboolean retval;
1251 int i;
1252
1253 g_return_val_if_fail (first_attribute_name != NULL, FALSE)do { if ((first_attribute_name != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "first_attribute_name != NULL"
); return ((0)); } } while (0)
;
1254 g_return_val_if_fail (first_attribute_retloc != NULL, FALSE)do { if ((first_attribute_retloc != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "first_attribute_retloc != NULL"
); return ((0)); } } while (0)
;
1255
1256 retval = TRUE(!(0));
1257
1258 n_attrs = 1;
1259 attrs[0].name = first_attribute_name;
1260 attrs[0].retloc = first_attribute_retloc;
1261 *first_attribute_retloc = NULL((void*)0);
1262
1263 va_start (args, first_attribute_retloc)__builtin_va_start(args, first_attribute_retloc);
1264
1265 name = va_arg (args, const char *)__builtin_va_arg(args, const char *);
1266 retloc = va_arg (args, const char **)__builtin_va_arg(args, const char **);
1267
1268 while (name != NULL((void*)0))
1269 {
1270 g_return_val_if_fail (retloc != NULL, FALSE)do { if ((retloc != ((void*)0))) { } else { g_return_if_fail_warning
(((gchar*) 0), ((const char*) (__func__)), "retloc != NULL")
; return ((0)); } } while (0)
;
1271
1272 g_assert (n_attrs < MAX_ATTRS)do { if (n_attrs < MAX_ATTRS) ; else g_assertion_message_expr
(((gchar*) 0), "menu-layout.c", 1272, ((const char*) (__func__
)), "n_attrs < MAX_ATTRS"); } while (0)
;
1273
1274 attrs[n_attrs].name = name;
1275 attrs[n_attrs].retloc = retloc;
1276 n_attrs += 1;
1277 *retloc = NULL((void*)0);
1278
1279 name = va_arg (args, const char *)__builtin_va_arg(args, const char *);
1280 retloc = va_arg (args, const char **)__builtin_va_arg(args, const char **);
1281 }
1282
1283 va_end (args)__builtin_va_end(args);
1284
1285 i = 0;
1286 while (attribute_names[i])
1287 {
1288 int j;
1289
1290 j = 0;
1291 while (j < n_attrs)
1292 {
1293 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
1294 {
1295 retloc = attrs[j].retloc;
1296
1297 if (*retloc != NULL((void*)0))
1298 {
1299 set_error (error, context,
1300 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1301 "Attribute \"%s\" repeated twice on the same <%s> element",
1302 attrs[j].name, element_name);
1303 retval = FALSE(0);
1304 goto out;
1305 }
1306
1307 *retloc = attribute_values[i];
1308 break;
1309 }
1310
1311 ++j;
1312 }
1313
1314 if (j == n_attrs)
1315 {
1316 set_error (error, context,
1317 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1318 "Attribute \"%s\" is invalid on <%s> element in this context",
1319 attribute_names[i], element_name);
1320 retval = FALSE(0);
1321 goto out;
1322 }
1323
1324 ++i;
1325 }
1326
1327 out:
1328 return retval;
1329
1330#undef MAX_ATTRS
1331}
1332
1333static gboolean
1334check_no_attributes (GMarkupParseContext *context,
1335 const char *element_name,
1336 const char **attribute_names,
1337 const char **attribute_values G_GNUC_UNUSED__attribute__ ((__unused__)),
1338 GError **error)
1339{
1340 if (attribute_names[0] != NULL((void*)0))
1341 {
1342 set_error (error, context,
1343 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1344 "Attribute \"%s\" is invalid on <%s> element in this context",
1345 attribute_names[0], element_name);
1346 return FALSE(0);
1347 }
1348
1349 return TRUE(!(0));
1350}
1351
1352static int
1353has_child_of_type (MenuLayoutNode *node,
1354 MenuLayoutNodeType type)
1355{
1356 MenuLayoutNode *iter;
1357
1358 iter = node->children;
1359 while (iter)
1360 {
1361 if (iter->type == type)
1362 return TRUE(!(0));
1363
1364 iter = node_next (iter);
1365 }
1366
1367 return FALSE(0);
1368}
1369
1370static void
1371push_node (MenuParser *parser,
1372 MenuLayoutNodeType type)
1373{
1374 MenuLayoutNode *node;
1375
1376 node = menu_layout_node_new (type);
1377 menu_layout_node_append_child (parser->stack_top, node);
1378 menu_layout_node_unref (node);
1379
1380 parser->stack_top = node;
1381}
1382
1383static void
1384start_menu_element (MenuParser *parser,
1385 GMarkupParseContext *context,
1386 const char *element_name,
1387 const char **attribute_names,
1388 const char **attribute_values,
1389 GError **error)
1390{
1391 if (!check_no_attributes (context, element_name,
1392 attribute_names, attribute_values,
1393 error))
1394 return;
1395
1396 if (!(parser->stack_top->type == MENU_LAYOUT_NODE_ROOT ||
1397 parser->stack_top->type == MENU_LAYOUT_NODE_MENU))
1398 {
1399 set_error (error, context,
1400 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1401 "<Menu> element can only appear below other <Menu> elements or at toplevel\n");
1402 }
1403 else
1404 {
1405 push_node (parser, MENU_LAYOUT_NODE_MENU);
1406 }
1407}
1408
1409static void
1410start_menu_child_element (MenuParser *parser,
1411 GMarkupParseContext *context,
1412 const char *element_name,
1413 const char **attribute_names,
1414 const char **attribute_values,
1415 GError **error)
1416{
1417 if (ELEMENT_IS ("LegacyDir")(strcmp (element_name, ("LegacyDir")) == 0))
1418 {
1419 const char *prefix;
1420
1421 push_node (parser, MENU_LAYOUT_NODE_LEGACY_DIR);
1422
1423 if (!locate_attributes (context, element_name,
1424 attribute_names, attribute_values,
1425 error,
1426 "prefix", &prefix,
1427 NULL((void*)0)))
1428 return;
1429
1430 menu_layout_node_legacy_dir_set_prefix (parser->stack_top, prefix);
1431 }
1432 else if (ELEMENT_IS ("MergeFile")(strcmp (element_name, ("MergeFile")) == 0))
1433 {
1434 const char *type;
1435
1436 push_node (parser, MENU_LAYOUT_NODE_MERGE_FILE);
1437
1438 if (!locate_attributes (context, element_name,
1439 attribute_names, attribute_values,
1440 error,
1441 "type", &type,
1442 NULL((void*)0)))
1443 return;
1444
1445 if (type != NULL((void*)0) && strcmp (type, "parent") == 0)
1446 {
1447 menu_layout_node_merge_file_set_type (parser->stack_top,
1448 MENU_MERGE_FILE_TYPE_PARENT);
1449 }
1450 }
1451 else if (ELEMENT_IS ("DefaultLayout")(strcmp (element_name, ("DefaultLayout")) == 0))
1452 {
1453 const char *show_empty;
1454 const char *inline_menus;
1455 const char *inline_limit;
1456 const char *inline_header;
1457 const char *inline_alias;
1458
1459 push_node (parser, MENU_LAYOUT_NODE_DEFAULT_LAYOUT);
1460
1461 locate_attributes (context, element_name,
1462 attribute_names, attribute_values,
1463 error,
1464 "show_empty", &show_empty,
1465 "inline", &inline_menus,
1466 "inline_limit", &inline_limit,
1467 "inline_header", &inline_header,
1468 "inline_alias", &inline_alias,
1469 NULL((void*)0));
1470
1471 menu_layout_node_default_layout_set_values (parser->stack_top,
1472 show_empty,
1473 inline_menus,
1474 inline_limit,
1475 inline_header,
1476 inline_alias);
1477 }
1478 else
1479 {
1480 if (!check_no_attributes (context, element_name,
1481 attribute_names, attribute_values,
1482 error))
1483 return;
1484
1485 if (ELEMENT_IS ("AppDir")(strcmp (element_name, ("AppDir")) == 0))
1486 {
1487 push_node (parser, MENU_LAYOUT_NODE_APP_DIR);
1488 }
1489 else if (ELEMENT_IS ("DefaultAppDirs")(strcmp (element_name, ("DefaultAppDirs")) == 0))
1490 {
1491 push_node (parser, MENU_LAYOUT_NODE_DEFAULT_APP_DIRS);
1492 }
1493 else if (ELEMENT_IS ("DirectoryDir")(strcmp (element_name, ("DirectoryDir")) == 0))
1494 {
1495 push_node (parser, MENU_LAYOUT_NODE_DIRECTORY_DIR);
1496 }
1497 else if (ELEMENT_IS ("DefaultDirectoryDirs")(strcmp (element_name, ("DefaultDirectoryDirs")) == 0))
1498 {
1499 push_node (parser, MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS);
1500 }
1501 else if (ELEMENT_IS ("DefaultMergeDirs")(strcmp (element_name, ("DefaultMergeDirs")) == 0))
1502 {
1503 push_node (parser, MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS);
1504 }
1505 else if (ELEMENT_IS ("Name")(strcmp (element_name, ("Name")) == 0))
1506 {
1507 if (has_child_of_type (parser->stack_top, MENU_LAYOUT_NODE_NAME))
1508 {
1509 set_error (error, context,
1510 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1511 "Multiple <Name> elements in a <Menu> element is not allowed\n");
1512 return;
1513 }
1514
1515 push_node (parser, MENU_LAYOUT_NODE_NAME);
1516 }
1517 else if (ELEMENT_IS ("Directory")(strcmp (element_name, ("Directory")) == 0))
1518 {
1519 push_node (parser, MENU_LAYOUT_NODE_DIRECTORY);
1520 }
1521 else if (ELEMENT_IS ("OnlyUnallocated")(strcmp (element_name, ("OnlyUnallocated")) == 0))
1522 {
1523 push_node (parser, MENU_LAYOUT_NODE_ONLY_UNALLOCATED);
1524 }
1525 else if (ELEMENT_IS ("NotOnlyUnallocated")(strcmp (element_name, ("NotOnlyUnallocated")) == 0))
1526 {
1527 push_node (parser, MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED);
1528 }
1529 else if (ELEMENT_IS ("Include")(strcmp (element_name, ("Include")) == 0))
1530 {
1531 push_node (parser, MENU_LAYOUT_NODE_INCLUDE);
1532 }
1533 else if (ELEMENT_IS ("Exclude")(strcmp (element_name, ("Exclude")) == 0))
1534 {
1535 push_node (parser, MENU_LAYOUT_NODE_EXCLUDE);
1536 }
1537 else if (ELEMENT_IS ("MergeDir")(strcmp (element_name, ("MergeDir")) == 0))
1538 {
1539 push_node (parser, MENU_LAYOUT_NODE_MERGE_DIR);
1540 }
1541 else if (ELEMENT_IS ("KDELegacyDirs")(strcmp (element_name, ("KDELegacyDirs")) == 0))
1542 {
1543 push_node (parser, MENU_LAYOUT_NODE_KDE_LEGACY_DIRS);
1544 }
1545 else if (ELEMENT_IS ("Move")(strcmp (element_name, ("Move")) == 0))
1546 {
1547 push_node (parser, MENU_LAYOUT_NODE_MOVE);
1548 }
1549 else if (ELEMENT_IS ("Deleted")(strcmp (element_name, ("Deleted")) == 0))
1550 {
1551 push_node (parser, MENU_LAYOUT_NODE_DELETED);
1552
1553 }
1554 else if (ELEMENT_IS ("NotDeleted")(strcmp (element_name, ("NotDeleted")) == 0))
1555 {
1556 push_node (parser, MENU_LAYOUT_NODE_NOT_DELETED);
1557 }
1558 else if (ELEMENT_IS ("Layout")(strcmp (element_name, ("Layout")) == 0))
1559 {
1560 push_node (parser, MENU_LAYOUT_NODE_LAYOUT);
1561 }
1562 else
1563 {
1564 set_error (error, context,
1565 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1566 "Element <%s> may not appear below <%s>\n",
1567 element_name, "Menu");
1568 }
1569 }
1570}
1571
1572static void
1573start_matching_rule_element (MenuParser *parser,
1574 GMarkupParseContext *context,
1575 const char *element_name,
1576 const char **attribute_names,
1577 const char **attribute_values,
1578 GError **error)
1579{
1580 if (!check_no_attributes (context, element_name,
1581 attribute_names, attribute_values,
1582 error))
1583 return;
1584
1585
1586 if (ELEMENT_IS ("Filename")(strcmp (element_name, ("Filename")) == 0))
1587 {
1588 push_node (parser, MENU_LAYOUT_NODE_FILENAME);
1589 }
1590 else if (ELEMENT_IS ("Category")(strcmp (element_name, ("Category")) == 0))
1591 {
1592 push_node (parser, MENU_LAYOUT_NODE_CATEGORY);
1593 }
1594 else if (ELEMENT_IS ("All")(strcmp (element_name, ("All")) == 0))
1595 {
1596 push_node (parser, MENU_LAYOUT_NODE_ALL);
1597 }
1598 else if (ELEMENT_IS ("And")(strcmp (element_name, ("And")) == 0))
1599 {
1600 push_node (parser, MENU_LAYOUT_NODE_AND);
1601 }
1602 else if (ELEMENT_IS ("Or")(strcmp (element_name, ("Or")) == 0))
1603 {
1604 push_node (parser, MENU_LAYOUT_NODE_OR);
1605 }
1606 else if (ELEMENT_IS ("Not")(strcmp (element_name, ("Not")) == 0))
1607 {
1608 push_node (parser, MENU_LAYOUT_NODE_NOT);
1609 }
1610 else
1611 {
1612 set_error (error, context,
1613 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1614 "Element <%s> may not appear in this context\n",
1615 element_name);
1616 }
1617}
1618
1619static void
1620start_move_child_element (MenuParser *parser,
1621 GMarkupParseContext *context,
1622 const char *element_name,
1623 const char **attribute_names,
1624 const char **attribute_values,
1625 GError **error)
1626{
1627 if (!check_no_attributes (context, element_name,
1628 attribute_names, attribute_values,
1629 error))
1630 return;
1631
1632 if (ELEMENT_IS ("Old")(strcmp (element_name, ("Old")) == 0))
1633 {
1634 push_node (parser, MENU_LAYOUT_NODE_OLD);
1635 }
1636 else if (ELEMENT_IS ("New")(strcmp (element_name, ("New")) == 0))
1637 {
1638 push_node (parser, MENU_LAYOUT_NODE_NEW);
1639 }
1640 else
1641 {
1642 set_error (error, context,
1643 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1644 "Element <%s> may not appear below <%s>\n",
1645 element_name, "Move");
1646 }
1647}
1648
1649static void
1650start_layout_child_element (MenuParser *parser,
1651 GMarkupParseContext *context,
1652 const char *element_name,
1653 const char **attribute_names,
1654 const char **attribute_values,
1655 GError **error)
1656{
1657 if (ELEMENT_IS ("Menuname")(strcmp (element_name, ("Menuname")) == 0))
1658 {
1659 const char *show_empty;
1660 const char *inline_menus;
1661 const char *inline_limit;
1662 const char *inline_header;
1663 const char *inline_alias;
1664
1665 push_node (parser, MENU_LAYOUT_NODE_MENUNAME);
1666
1667 locate_attributes (context, element_name,
1668 attribute_names, attribute_values,
1669 error,
1670 "show_empty", &show_empty,
1671 "inline", &inline_menus,
1672 "inline_limit", &inline_limit,
1673 "inline_header", &inline_header,
1674 "inline_alias", &inline_alias,
1675 NULL((void*)0));
1676
1677 menu_layout_node_menuname_set_values (parser->stack_top,
1678 show_empty,
1679 inline_menus,
1680 inline_limit,
1681 inline_header,
1682 inline_alias);
1683 }
1684 else if (ELEMENT_IS ("Merge")(strcmp (element_name, ("Merge")) == 0))
1685 {
1686 const char *type;
1687
1688 push_node (parser, MENU_LAYOUT_NODE_MERGE);
1689
1690 locate_attributes (context, element_name,
1691 attribute_names, attribute_values,
1692 error,
1693 "type", &type,
1694 NULL((void*)0));
1695
1696 menu_layout_node_merge_set_type (parser->stack_top, type);
1697 }
1698 else
1699 {
1700 if (!check_no_attributes (context, element_name,
1701 attribute_names, attribute_values,
1702 error))
1703 return;
1704
1705 if (ELEMENT_IS ("Filename")(strcmp (element_name, ("Filename")) == 0))
1706 {
1707 push_node (parser, MENU_LAYOUT_NODE_FILENAME);
1708 }
1709 else if (ELEMENT_IS ("Separator")(strcmp (element_name, ("Separator")) == 0))
1710 {
1711 push_node (parser, MENU_LAYOUT_NODE_SEPARATOR);
1712 }
1713 else
1714 {
1715 set_error (error, context,
1716 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1717 "Element <%s> may not appear below <%s>\n",
1718 element_name, "Move");
1719 }
1720 }
1721}
1722
1723static void
1724start_element_handler (GMarkupParseContext *context,
1725 const char *element_name,
1726 const char **attribute_names,
1727 const char **attribute_values,
1728 gpointer user_data,
1729 GError **error)
1730{
1731 MenuParser *parser = user_data;
1732
1733 if (ELEMENT_IS ("Menu")(strcmp (element_name, ("Menu")) == 0))
1734 {
1735 if (parser->stack_top == parser->root &&
1736 has_child_of_type (parser->root, MENU_LAYOUT_NODE_MENU))
1737 {
1738 set_error (error, context,
1739 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1740 "Multiple root elements in menu file, only one toplevel <Menu> is allowed\n");
1741 return;
1742 }
1743
1744 start_menu_element (parser, context, element_name,
1745 attribute_names, attribute_values,
1746 error);
1747 }
1748 else if (parser->stack_top == parser->root)
1749 {
1750 set_error (error, context,
1751 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1752 "Root element in a menu file must be <Menu>, not <%s>\n",
1753 element_name);
1754 }
1755 else if (parser->stack_top->type == MENU_LAYOUT_NODE_MENU)
1756 {
1757 start_menu_child_element (parser, context, element_name,
1758 attribute_names, attribute_values,
1759 error);
1760 }
1761 else if (parser->stack_top->type == MENU_LAYOUT_NODE_INCLUDE ||
1762 parser->stack_top->type == MENU_LAYOUT_NODE_EXCLUDE ||
1763 parser->stack_top->type == MENU_LAYOUT_NODE_AND ||
1764 parser->stack_top->type == MENU_LAYOUT_NODE_OR ||
1765 parser->stack_top->type == MENU_LAYOUT_NODE_NOT)
1766 {
1767 start_matching_rule_element (parser, context, element_name,
1768 attribute_names, attribute_values,
1769 error);
1770 }
1771 else if (parser->stack_top->type == MENU_LAYOUT_NODE_MOVE)
1772 {
1773 start_move_child_element (parser, context, element_name,
1774 attribute_names, attribute_values,
1775 error);
1776 }
1777 else if (parser->stack_top->type == MENU_LAYOUT_NODE_LAYOUT ||
1778 parser->stack_top->type == MENU_LAYOUT_NODE_DEFAULT_LAYOUT)
1779 {
1780 start_layout_child_element (parser, context, element_name,
1781 attribute_names, attribute_values,
1782 error);
1783 }
1784 else
1785 {
1786 set_error (error, context,
1787 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_UNKNOWN_ELEMENT,
1788 "Element <%s> may not appear in this context\n",
1789 element_name);
1790 }
1791
1792 add_context_to_error (error, context);
1793}
1794
1795/* we want to make sure that the <Layout> or <DefaultLayout> is either empty,
1796 * or contain one <Merge> of type "all", or contain one <Merge> of type "menus"
1797 * and one <Merge> of type "files". If this is not the case, we try to clean up
1798 * things:
1799 * + if there is at least one <Merge> of type "all", then we only keep the
1800 * last <Merge> of type "all" and remove all others <Merge>
1801 * + if there is no <Merge> with type "all", we keep only the last <Merge> of
1802 * type "menus" and the last <Merge> of type "files". If there's no <Merge>
1803 * of type "menus" we append one, and then if there's no <Merge> of type
1804 * "files", we append one. (So menus are before files)
1805 */
1806static gboolean
1807fixup_layout_node (GMarkupParseContext *context G_GNUC_UNUSED__attribute__ ((__unused__)),
1808 MenuParser *parser G_GNUC_UNUSED__attribute__ ((__unused__)),
1809 MenuLayoutNode *node,
1810 GError **error G_GNUC_UNUSED__attribute__ ((__unused__)))
1811{
1812 MenuLayoutNode *child;
1813 MenuLayoutNode *last_all;
1814 MenuLayoutNode *last_menus;
1815 MenuLayoutNode *last_files;
1816 int n_all;
1817 int n_menus;
1818 int n_files;
1819
1820 if (!node->children)
1821 {
1822 return TRUE(!(0));
1823 }
1824
1825 last_all = NULL((void*)0);
1826 last_menus = NULL((void*)0);
1827 last_files = NULL((void*)0);
1828 n_all = 0;
1829 n_menus = 0;
1830 n_files = 0;
1831
1832 child = node->children;
1833 while (child != NULL((void*)0))
1834 {
1835 switch (child->type)
1836 {
1837 case MENU_LAYOUT_NODE_MERGE:
1838 switch (menu_layout_node_merge_get_type (child))
1839 {
1840 case MENU_LAYOUT_MERGE_NONE:
1841 break;
1842
1843 case MENU_LAYOUT_MERGE_MENUS:
1844 last_menus = child;
1845 n_menus++;
1846 break;
1847
1848 case MENU_LAYOUT_MERGE_FILES:
1849 last_files = child;
1850 n_files++;
1851 break;
1852
1853 case MENU_LAYOUT_MERGE_ALL:
1854 last_all = child;
1855 n_all++;
1856 break;
1857
1858 default:
1859 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "menu-layout.c",
1859, ((const char*) (__func__)), ((void*)0)); } while (0)
;
1860 break;
1861 }
1862 break;
1863
1864 default:
1865 break;
1866 }
1867
1868 child = node_next (child);
1869 }
1870
1871 if ((n_all == 1 && n_menus == 0 && n_files == 0) ||
1872 (n_all == 0 && n_menus == 1 && n_files == 1))
1873 {
1874 return TRUE(!(0));
1875 }
1876 else if (n_all > 1 || n_menus > 1 || n_files > 1 ||
1877 (n_all == 1 && (n_menus != 0 || n_files != 0)))
1878 {
1879 child = node->children;
1880 while (child != NULL((void*)0))
1881 {
1882 MenuLayoutNode *next;
1883
1884 next = node_next (child);
1885
1886 switch (child->type)
1887 {
1888 case MENU_LAYOUT_NODE_MERGE:
1889 switch (menu_layout_node_merge_get_type (child))
1890 {
1891 case MENU_LAYOUT_MERGE_NONE:
1892 break;
1893
1894 case MENU_LAYOUT_MERGE_MENUS:
1895 if (n_all || last_menus != child)
1896 {
1897 menu_verbose ("removing duplicated merge menus element\n");
1898 menu_layout_node_unlink (child);
1899 }
1900 break;
1901
1902 case MENU_LAYOUT_MERGE_FILES:
1903 if (n_all || last_files != child)
1904 {
1905 menu_verbose ("removing duplicated merge files element\n");
1906 menu_layout_node_unlink (child);
1907 }
1908 break;
1909
1910 case MENU_LAYOUT_MERGE_ALL:
1911 if (last_all != child)
1912 {
1913 menu_verbose ("removing duplicated merge all element\n");
1914 menu_layout_node_unlink (child);
1915 }
1916 break;
1917
1918 default:
1919 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "menu-layout.c",
1919, ((const char*) (__func__)), ((void*)0)); } while (0)
;
1920 break;
1921 }
1922 break;
1923
1924 default:
1925 break;
1926 }
1927
1928 child = next;
1929 }
1930 }
1931
1932 if (n_all == 0 && n_menus == 0)
1933 {
1934 last_menus = menu_layout_node_new (MENU_LAYOUT_NODE_MERGE);
1935 menu_layout_node_merge_set_type (last_menus, "menus");
1936 menu_verbose ("appending missing merge menus element\n");
1937 menu_layout_node_append_child (node, last_menus);
1938 }
1939
1940 if (n_all == 0 && n_files == 0)
1941 {
1942 last_files = menu_layout_node_new (MENU_LAYOUT_NODE_MERGE);
1943 menu_layout_node_merge_set_type (last_files, "files");
1944 menu_verbose ("appending missing merge files element\n");
1945 menu_layout_node_append_child (node, last_files);
1946 }
1947
1948 return TRUE(!(0));
1949}
1950
1951/* we want to a) check that we have old-new pairs and b) canonicalize
1952 * such that each <Move> has exactly one old-new pair
1953 */
1954static gboolean
1955fixup_move_node (GMarkupParseContext *context,
1956 MenuParser *parser G_GNUC_UNUSED__attribute__ ((__unused__)),
1957 MenuLayoutNode *node,
1958 GError **error)
1959{
1960 MenuLayoutNode *child;
1961 int n_old;
1962 int n_new;
1963
1964 n_old = 0;
1965 n_new = 0;
1966
1967 child = node->children;
1968 while (child != NULL((void*)0))
1969 {
1970 switch (child->type)
1971 {
1972 case MENU_LAYOUT_NODE_OLD:
1973 if (n_new != n_old)
1974 {
1975 set_error (error, context,
1976 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1977 "<Old>/<New> elements not paired properly\n");
1978 return FALSE(0);
1979 }
1980
1981 n_old += 1;
1982
1983 break;
1984
1985 case MENU_LAYOUT_NODE_NEW:
1986 n_new += 1;
1987
1988 if (n_new != n_old)
1989 {
1990 set_error (error, context,
1991 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
1992 "<Old>/<New> elements not paired properly\n");
1993 return FALSE(0);
1994 }
1995
1996 break;
1997
1998 default:
1999 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "menu-layout.c",
1999, ((const char*) (__func__)), ((void*)0)); } while (0)
;
2000 break;
2001 }
2002
2003 child = node_next (child);
2004 }
2005
2006 if (n_new == 0 || n_old == 0)
2007 {
2008 set_error (error, context,
2009 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
2010 "<Old>/<New> elements missing under <Move>\n");
2011 return FALSE(0);
2012 }
2013
2014 g_assert (n_new == n_old)do { if (n_new == n_old) ; else g_assertion_message_expr (((gchar
*) 0), "menu-layout.c", 2014, ((const char*) (__func__)), "n_new == n_old"
); } while (0)
;
2015 g_assert ((n_new + n_old) % 2 == 0)do { if ((n_new + n_old) % 2 == 0) ; else g_assertion_message_expr
(((gchar*) 0), "menu-layout.c", 2015, ((const char*) (__func__
)), "(n_new + n_old) % 2 == 0"); } while (0)
;
2016
2017 if (n_new > 1)
2018 {
2019 MenuLayoutNode *prev;
2020 MenuLayoutNode *append_after;
2021
2022 /* Need to split the <Move> into multiple <Move> */
2023
2024 n_old = 0;
2025 n_new = 0;
2026 prev = NULL((void*)0);
2027 append_after = node;
2028
2029 child = node->children;
2030 while (child != NULL((void*)0))
2031 {
2032 MenuLayoutNode *next;
2033
2034 next = node_next (child);
2035
2036 switch (child->type)
2037 {
2038 case MENU_LAYOUT_NODE_OLD:
2039 n_old += 1;
2040 break;
2041
2042 case MENU_LAYOUT_NODE_NEW:
2043 n_new += 1;
2044 break;
2045
2046 default:
2047 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "menu-layout.c",
2047, ((const char*) (__func__)), ((void*)0)); } while (0)
;
2048 break;
2049 }
2050
2051 if (n_old == n_new &&
2052 n_old > 1)
2053 {
2054 /* Move the just-completed pair */
2055 MenuLayoutNode *new_move;
2056
2057 g_assert (prev != NULL)do { if (prev != ((void*)0)) ; else g_assertion_message_expr (
((gchar*) 0), "menu-layout.c", 2057, ((const char*) (__func__
)), "prev != NULL"); } while (0)
;
2058
2059 new_move = menu_layout_node_new (MENU_LAYOUT_NODE_MOVE);
2060 menu_verbose ("inserting new_move after append_after\n");
2061 menu_layout_node_insert_after (append_after, new_move);
2062 append_after = new_move;
2063
2064 menu_layout_node_steal (prev);
2065 menu_layout_node_steal (child);
2066
2067 menu_verbose ("appending prev to new_move\n");
2068 menu_layout_node_append_child (new_move, prev);
2069 menu_verbose ("appending child to new_move\n");
2070 menu_layout_node_append_child (new_move, child);
2071
2072 menu_verbose ("Created new move element old = %s new = %s\n",
2073 menu_layout_node_move_get_old (new_move),
2074 menu_layout_node_move_get_new (new_move));
2075
2076 menu_layout_node_unref (new_move);
2077 menu_layout_node_unref (prev);
2078 menu_layout_node_unref (child);
2079
2080 prev = NULL((void*)0);
2081 }
2082 else
2083 {
2084 prev = child;
Value stored to 'prev' is never read
2085 }
2086
2087 prev = child;
2088 child = next;
2089 }
2090 }
2091
2092 return TRUE(!(0));
2093}
2094
2095static void
2096end_element_handler (GMarkupParseContext *context,
2097 const char *element_name,
2098 gpointer user_data,
2099 GError **error)
2100{
2101 MenuParser *parser = user_data;
2102
2103 g_assert (parser->stack_top != NULL)do { if (parser->stack_top != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "menu-layout.c", 2103, ((const char*) (__func__
)), "parser->stack_top != NULL"); } while (0)
;
2104
2105 switch (parser->stack_top->type)
2106 {
2107 case MENU_LAYOUT_NODE_APP_DIR:
2108 case MENU_LAYOUT_NODE_DIRECTORY_DIR:
2109 case MENU_LAYOUT_NODE_NAME:
2110 case MENU_LAYOUT_NODE_DIRECTORY:
2111 case MENU_LAYOUT_NODE_FILENAME:
2112 case MENU_LAYOUT_NODE_CATEGORY:
2113 case MENU_LAYOUT_NODE_MERGE_DIR:
2114 case MENU_LAYOUT_NODE_LEGACY_DIR:
2115 case MENU_LAYOUT_NODE_OLD:
2116 case MENU_LAYOUT_NODE_NEW:
2117 case MENU_LAYOUT_NODE_MENUNAME:
2118 if (menu_layout_node_get_content (parser->stack_top) == NULL((void*)0))
2119 {
2120 set_error (error, context,
2121 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_INVALID_CONTENT,
2122 "Element <%s> is required to contain text and was empty\n",
2123 element_name);
2124 goto out;
2125 }
2126 break;
2127
2128 case MENU_LAYOUT_NODE_MENU:
2129 if (!has_child_of_type (parser->stack_top, MENU_LAYOUT_NODE_NAME))
2130 {
2131 set_error (error, context,
2132 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
2133 "<Menu> elements are required to contain a <Name> element\n");
2134 goto out;
2135 }
2136 break;
2137
2138 case MENU_LAYOUT_NODE_ROOT:
2139 case MENU_LAYOUT_NODE_PASSTHROUGH:
2140 case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
2141 case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
2142 case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
2143 case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
2144 case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
2145 case MENU_LAYOUT_NODE_INCLUDE:
2146 case MENU_LAYOUT_NODE_EXCLUDE:
2147 case MENU_LAYOUT_NODE_ALL:
2148 case MENU_LAYOUT_NODE_AND:
2149 case MENU_LAYOUT_NODE_OR:
2150 case MENU_LAYOUT_NODE_NOT:
2151 case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
2152 case MENU_LAYOUT_NODE_DELETED:
2153 case MENU_LAYOUT_NODE_NOT_DELETED:
2154 case MENU_LAYOUT_NODE_SEPARATOR:
2155 case MENU_LAYOUT_NODE_MERGE:
2156 case MENU_LAYOUT_NODE_MERGE_FILE:
2157 break;
2158
2159 case MENU_LAYOUT_NODE_LAYOUT:
2160 case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
2161 if (!fixup_layout_node (context, parser, parser->stack_top, error))
2162 goto out;
2163 break;
2164
2165 case MENU_LAYOUT_NODE_MOVE:
2166 if (!fixup_move_node (context, parser, parser->stack_top, error))
2167 goto out;
2168 break;
2169 }
2170
2171 out:
2172 parser->stack_top = parser->stack_top->parent;
2173}
2174
2175static gboolean
2176all_whitespace (const char *text,
2177 int text_len)
2178{
2179 const char *p;
2180 const char *end;
2181
2182 p = text;
2183 end = text + text_len;
2184
2185 while (p != end)
2186 {
2187 if (!g_ascii_isspace (*p)((g_ascii_table[(guchar) (*p)] & G_ASCII_SPACE) != 0))
2188 return FALSE(0);
2189
2190 p = g_utf8_next_char (p)((p) + g_utf8_skip[*(const guchar *)(p)]);
2191 }
2192
2193 return TRUE(!(0));
2194}
2195
2196static void
2197text_handler (GMarkupParseContext *context,
2198 const char *text,
2199 gsize text_len,
2200 gpointer user_data,
2201 GError **error)
2202{
2203 MenuParser *parser = user_data;
2204
2205 switch (parser->stack_top->type)
2206 {
2207 case MENU_LAYOUT_NODE_APP_DIR:
2208 case MENU_LAYOUT_NODE_DIRECTORY_DIR:
2209 case MENU_LAYOUT_NODE_NAME:
2210 case MENU_LAYOUT_NODE_DIRECTORY:
2211 case MENU_LAYOUT_NODE_FILENAME:
2212 case MENU_LAYOUT_NODE_CATEGORY:
2213 case MENU_LAYOUT_NODE_MERGE_FILE:
2214 case MENU_LAYOUT_NODE_MERGE_DIR:
2215 case MENU_LAYOUT_NODE_LEGACY_DIR:
2216 case MENU_LAYOUT_NODE_OLD:
2217 case MENU_LAYOUT_NODE_NEW:
2218 case MENU_LAYOUT_NODE_MENUNAME:
2219 g_assert (menu_layout_node_get_content (parser->stack_top) == NULL)do { if (menu_layout_node_get_content (parser->stack_top) ==
((void*)0)) ; else g_assertion_message_expr (((gchar*) 0), "menu-layout.c"
, 2219, ((const char*) (__func__)), "menu_layout_node_get_content (parser->stack_top) == NULL"
); } while (0)
;
2220
2221 menu_layout_node_set_content (parser->stack_top, text);
2222 break;
2223
2224 case MENU_LAYOUT_NODE_ROOT:
2225 case MENU_LAYOUT_NODE_PASSTHROUGH:
2226 case MENU_LAYOUT_NODE_MENU:
2227 case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
2228 case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
2229 case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
2230 case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
2231 case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
2232 case MENU_LAYOUT_NODE_INCLUDE:
2233 case MENU_LAYOUT_NODE_EXCLUDE:
2234 case MENU_LAYOUT_NODE_ALL:
2235 case MENU_LAYOUT_NODE_AND:
2236 case MENU_LAYOUT_NODE_OR:
2237 case MENU_LAYOUT_NODE_NOT:
2238 case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
2239 case MENU_LAYOUT_NODE_MOVE:
2240 case MENU_LAYOUT_NODE_DELETED:
2241 case MENU_LAYOUT_NODE_NOT_DELETED:
2242 case MENU_LAYOUT_NODE_LAYOUT:
2243 case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
2244 case MENU_LAYOUT_NODE_SEPARATOR:
2245 case MENU_LAYOUT_NODE_MERGE:
2246 if (!all_whitespace (text, text_len))
2247 {
2248 set_error (error, context,
2249 G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
2250 "No text is allowed inside element <%s>",
2251 g_markup_parse_context_get_element (context));
2252 }
2253 break;
2254 }
2255
2256 add_context_to_error (error, context);
2257}
2258
2259static void
2260passthrough_handler (GMarkupParseContext *context,
2261 const char *passthrough_text,
2262 gsize text_len G_GNUC_UNUSED__attribute__ ((__unused__)),
2263 gpointer user_data,
2264 GError **error)
2265{
2266 MenuParser *parser = user_data;
2267 MenuLayoutNode *node;
2268
2269 /* don't push passthrough on the stack, it's not an element */
2270
2271 node = menu_layout_node_new (MENU_LAYOUT_NODE_PASSTHROUGH);
2272 menu_layout_node_set_content (node, passthrough_text);
2273
2274 menu_layout_node_append_child (parser->stack_top, node);
2275 menu_layout_node_unref (node);
2276
2277 add_context_to_error (error, context);
2278}
2279
2280static void
2281menu_parser_init (MenuParser *parser)
2282{
2283 parser->root = menu_layout_node_new (MENU_LAYOUT_NODE_ROOT);
2284 parser->stack_top = parser->root;
2285}
2286
2287static void
2288menu_parser_free (MenuParser *parser)
2289{
2290 if (parser->root)
2291 menu_layout_node_unref (parser->root);
2292}
2293
2294MenuLayoutNode *
2295menu_layout_load (const char *filename,
2296 const char *non_prefixed_basename,
2297 GError **err)
2298{
2299 GMainContext *main_context;
2300 GMarkupParseContext *context;
2301 MenuLayoutNodeRoot *root;
2302 MenuLayoutNode *retval;
2303 MenuParser parser;
2304 GError *error;
2305 GString *str;
2306 char *text;
2307 char *s;
2308 gsize length;
2309
2310 text = NULL((void*)0);
2311 length = 0;
2312 retval = NULL((void*)0);
2313 context = NULL((void*)0);
2314
2315 main_context = g_main_context_get_thread_default ();
2316
2317 menu_verbose ("Loading \"%s\" from disk\n", filename);
2318
2319 if (!g_file_get_contents (filename,
2320 &text,
2321 &length,
2322 err))
2323 {
2324 menu_verbose ("Failed to load \"%s\"\n",
2325 filename);
2326 return NULL((void*)0);
2327 }
2328
2329 g_assert (text != NULL)do { if (text != ((void*)0)) ; else g_assertion_message_expr (
((gchar*) 0), "menu-layout.c", 2329, ((const char*) (__func__
)), "text != NULL"); } while (0)
;
2330
2331 menu_parser_init (&parser);
2332
2333 root = (MenuLayoutNodeRoot *) parser.root;
2334
2335 root->basedir = g_path_get_dirname (filename);
2336 menu_verbose ("Set basedir \"%s\"\n", root->basedir);
2337
2338 if (non_prefixed_basename)
2339 s = g_strdup (non_prefixed_basename)g_strdup_inline (non_prefixed_basename);
2340 else
2341 s = g_path_get_basename (filename);
2342 str = g_string_new (s);
2343 if (g_str_has_suffix (str->str, ".menu")(__builtin_constant_p (".menu")? __extension__ ({ const char *
const __str = (str->str); const char * const __suffix = (
".menu"); 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
) (str->str, ".menu") )
)
2344 g_string_truncate (str, str->len - strlen (".menu"))g_string_truncate_inline (str, str->len - strlen (".menu")
)
;
2345
2346 root->name = str->str;
2347 menu_verbose ("Set menu name \"%s\"\n", root->name);
2348
2349 g_string_free (str, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((str)
, ((0))) : g_string_free_and_steal (str)) : (g_string_free) (
(str), ((0))))
;
2350 g_free (s);
2351
2352 context = g_markup_parse_context_new (&menu_funcs, 0, &parser, NULL((void*)0));
2353
2354 error = NULL((void*)0);
2355 if (!g_markup_parse_context_parse (context,
2356 text,
2357 length,
2358 &error))
2359 goto out;
2360
2361 error = NULL((void*)0);
2362 g_markup_parse_context_end_parse (context, &error);
2363
2364 root->main_context = main_context ? g_main_context_ref (main_context) : NULL((void*)0);
2365
2366 out:
2367 if (context)
2368 g_markup_parse_context_free (context);
2369 g_free (text);
2370
2371 if (error)
2372 {
2373 menu_verbose ("Error \"%s\" loading \"%s\"\n",
2374 error->message, filename);
2375 g_propagate_error (err, error);
2376 }
2377 else if (has_child_of_type (parser.root, MENU_LAYOUT_NODE_MENU))
2378 {
2379 menu_verbose ("File loaded OK\n");
2380 retval = parser.root;
2381 parser.root = NULL((void*)0);
2382 }
2383 else
2384 {
2385 menu_verbose ("Did not have a root element in file\n");
2386 g_set_error (err, G_MARKUP_ERRORg_markup_error_quark (), G_MARKUP_ERROR_PARSE,
2387 "Menu file %s did not contain a root <Menu> element",
2388 filename);
2389 }
2390
2391 menu_parser_free (&parser);
2392
2393 return retval;
2394}