Bug Summary

File:menu-layout.c
Warning:line 566, column 20
Access to field 'prev' results in a dereference of a null pointer (loaded from field 'next')

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