Bug Summary

File:menu-layout.c
Warning:line 2161, column 9
This statement is never executed

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-19-124449-11079-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)
;
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)
;
489
490 menu_layout_node_steal (node);
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)
;
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)
;
529
530 switch (node->type)
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;
554 }
555
556 if (node->parent && node->parent->children == node)
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;
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)
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 != NULL((void*)0))
1833 {
1834 switch (child->type)
1835 {
1836 case MENU_LAYOUT_NODE_MERGE:
1837 switch (menu_layout_node_merge_get_type (child))
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;
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;
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;
1862
1863 default:
1864 break;
1865 }
1866
1867 child = node_next (child);
1868 }
1869
1870 if ((n_all == 1 && n_menus == 0 && n_files == 0) ||
1871 (n_all == 0 && n_menus == 1 && n_files == 1))
1872 {
1873 return TRUE(!(0));
1874 }
1875 else if (n_all > 1 || n_menus > 1 || n_files > 1 ||
1876 (n_all == 1 && (n_menus != 0 || n_files != 0)))
1877 {
1878 child = node->children;
1879 while (child != NULL((void*)0))
1880 {
1881 MenuLayoutNode *next;
1882
1883 next = node_next (child);
1884
1885 switch (child->type)
1886 {
1887 case MENU_LAYOUT_NODE_MERGE:
1888 switch (menu_layout_node_merge_get_type (child))
1889 {
1890 case MENU_LAYOUT_MERGE_NONE:
1891 break;
1892
1893 case MENU_LAYOUT_MERGE_MENUS:
1894 if (n_all || last_menus != child)
1895 {
1896 menu_verbose ("removing duplicated merge menus element\n");
1897 menu_layout_node_unlink (child);
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 != child)
1911 {
1912 menu_verbose ("removing duplicated merge all element\n");
1913 menu_layout_node_unlink (child);
1914 }
1915 break;
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;
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)
;
2103
2104 switch (parser->stack_top->type)
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))
2161 goto out;
This statement is never executed
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}