From 4dc3d04c273428174aa949c4133939d02852baae Mon Sep 17 00:00:00 2001 From: "raveit65 (via Travis CI)" Date: Mon, 22 Feb 2021 22:03:02 +0000 Subject: Deploy mate-desktop/mate-menus to github.com/mate-desktop/mate-menus.git:gh-pages --- .../index.html | 111 + .../report-26579a.html | 2746 +++++++++++ .../report-2f48ff.html | 2803 +++++++++++ .../report-5c0da0.html | 2803 +++++++++++ .../report-5dda50.html | 783 +++ .../report-936604.html | 783 +++ .../report-980be6.html | 2746 +++++++++++ .../scanview.css | 62 + .../sorttable.js | 492 ++ .../0.html | 1956 ++++++++ .../1.html | 5148 ++++++++++++++++++++ .../index.html | 126 + .../stats.html | 109 + .../style.css | 137 + .../index.html | 111 + .../report-2e65d7.html | 2803 +++++++++++ .../report-4a49cc.html | 2746 +++++++++++ .../report-723168.html | 783 +++ .../report-991093.html | 2803 +++++++++++ .../report-be5089.html | 783 +++ .../report-cb70e8.html | 2746 +++++++++++ .../scanview.css | 62 + .../sorttable.js | 492 ++ .../0.html | 1956 ++++++++ .../1.html | 5148 ++++++++++++++++++++ .../index.html | 126 + .../stats.html | 109 + .../style.css | 137 + .../index.html | 111 + .../report-4662b9.html | 2803 +++++++++++ .../report-52fcde.html | 2803 +++++++++++ .../report-806dae.html | 2746 +++++++++++ .../report-a59008.html | 2746 +++++++++++ .../report-bab32b.html | 783 +++ .../report-e775a0.html | 783 +++ .../scanview.css | 62 + .../sorttable.js | 492 ++ .../0.html | 1956 ++++++++ .../1.html | 5148 ++++++++++++++++++++ .../index.html | 126 + .../stats.html | 109 + .../style.css | 137 + .../index.html | 111 + .../report-274f0b.html | 2803 +++++++++++ .../report-62c569.html | 2746 +++++++++++ .../report-818782.html | 783 +++ .../report-b90326.html | 2803 +++++++++++ .../report-bd7b91.html | 783 +++ .../report-f01eb2.html | 2746 +++++++++++ .../scanview.css | 62 + .../sorttable.js | 492 ++ .../0.html | 1956 ++++++++ .../1.html | 5148 ++++++++++++++++++++ .../index.html | 126 + .../stats.html | 109 + .../style.css | 137 + .../index.html | 111 + .../report-43a9f0.html | 783 +++ .../report-4c3c0e.html | 2746 +++++++++++ .../report-84fedf.html | 2746 +++++++++++ .../report-ac1752.html | 2803 +++++++++++ .../report-bbcae0.html | 783 +++ .../report-face03.html | 2803 +++++++++++ .../scanview.css | 62 + .../sorttable.js | 492 ++ .../0.html | 5148 ++++++++++++++++++++ .../index.html | 123 + .../stats.html | 108 + .../style.css | 137 + .../index.html | 111 + .../report-023dd5.html | 783 +++ .../report-0cdc50.html | 2746 +++++++++++ .../report-123bae.html | 783 +++ .../report-491601.html | 2746 +++++++++++ .../report-c41301.html | 2803 +++++++++++ .../report-e47f1b.html | 2803 +++++++++++ .../scanview.css | 62 + .../sorttable.js | 492 ++ .../0.html | 5148 ++++++++++++++++++++ .../index.html | 123 + .../stats.html | 108 + .../style.css | 137 + .../index.html | 111 + .../report-0099b5.html | 2803 +++++++++++ .../report-319d36.html | 2746 +++++++++++ .../report-a6b7a0.html | 2803 +++++++++++ .../report-afd61a.html | 783 +++ .../report-cce679.html | 2746 +++++++++++ .../report-f3407b.html | 783 +++ .../scanview.css | 62 + .../sorttable.js | 492 ++ .../0.html | 5148 ++++++++++++++++++++ .../index.html | 123 + .../stats.html | 108 + .../style.css | 137 + .../index.html | 111 + .../report-366b87.html | 2746 +++++++++++ .../report-36b3bf.html | 2803 +++++++++++ .../report-3b67fc.html | 2803 +++++++++++ .../report-49f7d6.html | 783 +++ .../report-71afd1.html | 2746 +++++++++++ .../report-a602eb.html | 783 +++ .../scanview.css | 62 + .../sorttable.js | 492 ++ .../0.html | 1956 ++++++++ .../1.html | 5148 ++++++++++++++++++++ .../index.html | 126 + .../stats.html | 109 + .../style.css | 137 + .../index.html | 111 + .../report-103542.html | 2746 +++++++++++ .../report-27e6bf.html | 2803 +++++++++++ .../report-957ecf.html | 783 +++ .../report-c8e528.html | 2803 +++++++++++ .../report-cf3e61.html | 2746 +++++++++++ .../report-ffe08d.html | 783 +++ .../scanview.css | 62 + .../sorttable.js | 492 ++ .../0.html | 5148 ++++++++++++++++++++ .../index.html | 123 + .../stats.html | 108 + .../style.css | 137 + .../index.html | 111 + .../report-17b76a.html | 2746 +++++++++++ .../report-65ae22.html | 2803 +++++++++++ .../report-69a70e.html | 783 +++ .../report-82b186.html | 2746 +++++++++++ .../report-d27deb.html | 783 +++ .../report-e12d4a.html | 2803 +++++++++++ .../scanview.css | 62 + .../sorttable.js | 492 ++ .../0.html | 5148 ++++++++++++++++++++ .../index.html | 123 + .../stats.html | 108 + .../style.css | 137 + CNAME | 1 + index.html | 46 + 137 files changed, 198297 insertions(+) create mode 100644 2021-01-26-091604-5205-1@811952a5706c_gettext-support/index.html create mode 100644 2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-26579a.html create mode 100644 2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-2f48ff.html create mode 100644 2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-5c0da0.html create mode 100644 2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-5dda50.html create mode 100644 2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-936604.html create mode 100644 2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-980be6.html create mode 100644 2021-01-26-091604-5205-1@811952a5706c_gettext-support/scanview.css create mode 100644 2021-01-26-091604-5205-1@811952a5706c_gettext-support/sorttable.js create mode 100644 2021-01-26-091711-0626-cppcheck@811952a5706c_gettext-support/0.html create mode 100644 2021-01-26-091711-0626-cppcheck@811952a5706c_gettext-support/1.html create mode 100644 2021-01-26-091711-0626-cppcheck@811952a5706c_gettext-support/index.html create mode 100644 2021-01-26-091711-0626-cppcheck@811952a5706c_gettext-support/stats.html create mode 100644 2021-01-26-091711-0626-cppcheck@811952a5706c_gettext-support/style.css create mode 100644 2021-01-29-131231-4701-1@1c989ea4e5ea_master/index.html create mode 100644 2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-2e65d7.html create mode 100644 2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-4a49cc.html create mode 100644 2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-723168.html create mode 100644 2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-991093.html create mode 100644 2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-be5089.html create mode 100644 2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-cb70e8.html create mode 100644 2021-01-29-131231-4701-1@1c989ea4e5ea_master/scanview.css create mode 100644 2021-01-29-131231-4701-1@1c989ea4e5ea_master/sorttable.js create mode 100644 2021-01-29-131335-9628-cppcheck@1c989ea4e5ea_master/0.html create mode 100644 2021-01-29-131335-9628-cppcheck@1c989ea4e5ea_master/1.html create mode 100644 2021-01-29-131335-9628-cppcheck@1c989ea4e5ea_master/index.html create mode 100644 2021-01-29-131335-9628-cppcheck@1c989ea4e5ea_master/stats.html create mode 100644 2021-01-29-131335-9628-cppcheck@1c989ea4e5ea_master/style.css create mode 100644 2021-01-30-012612-5206-1@e05b48afe576_gettext-support/index.html create mode 100644 2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-4662b9.html create mode 100644 2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-52fcde.html create mode 100644 2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-806dae.html create mode 100644 2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-a59008.html create mode 100644 2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-bab32b.html create mode 100644 2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-e775a0.html create mode 100644 2021-01-30-012612-5206-1@e05b48afe576_gettext-support/scanview.css create mode 100644 2021-01-30-012612-5206-1@e05b48afe576_gettext-support/sorttable.js create mode 100644 2021-01-30-012719-8694-cppcheck@e05b48afe576_gettext-support/0.html create mode 100644 2021-01-30-012719-8694-cppcheck@e05b48afe576_gettext-support/1.html create mode 100644 2021-01-30-012719-8694-cppcheck@e05b48afe576_gettext-support/index.html create mode 100644 2021-01-30-012719-8694-cppcheck@e05b48afe576_gettext-support/stats.html create mode 100644 2021-01-30-012719-8694-cppcheck@e05b48afe576_gettext-support/style.css create mode 100644 2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/index.html create mode 100644 2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-274f0b.html create mode 100644 2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-62c569.html create mode 100644 2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-818782.html create mode 100644 2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-b90326.html create mode 100644 2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-bd7b91.html create mode 100644 2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-f01eb2.html create mode 100644 2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/scanview.css create mode 100644 2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/sorttable.js create mode 100644 2021-01-30-053806-6255-cppcheck@d04f74e269c9_ax_enable_debug/0.html create mode 100644 2021-01-30-053806-6255-cppcheck@d04f74e269c9_ax_enable_debug/1.html create mode 100644 2021-01-30-053806-6255-cppcheck@d04f74e269c9_ax_enable_debug/index.html create mode 100644 2021-01-30-053806-6255-cppcheck@d04f74e269c9_ax_enable_debug/stats.html create mode 100644 2021-01-30-053806-6255-cppcheck@d04f74e269c9_ax_enable_debug/style.css create mode 100644 2021-01-31-142554-4695-1@a4e85eb563a5_master/index.html create mode 100644 2021-01-31-142554-4695-1@a4e85eb563a5_master/report-43a9f0.html create mode 100644 2021-01-31-142554-4695-1@a4e85eb563a5_master/report-4c3c0e.html create mode 100644 2021-01-31-142554-4695-1@a4e85eb563a5_master/report-84fedf.html create mode 100644 2021-01-31-142554-4695-1@a4e85eb563a5_master/report-ac1752.html create mode 100644 2021-01-31-142554-4695-1@a4e85eb563a5_master/report-bbcae0.html create mode 100644 2021-01-31-142554-4695-1@a4e85eb563a5_master/report-face03.html create mode 100644 2021-01-31-142554-4695-1@a4e85eb563a5_master/scanview.css create mode 100644 2021-01-31-142554-4695-1@a4e85eb563a5_master/sorttable.js create mode 100644 2021-01-31-142700-0966-cppcheck@a4e85eb563a5_master/0.html create mode 100644 2021-01-31-142700-0966-cppcheck@a4e85eb563a5_master/index.html create mode 100644 2021-01-31-142700-0966-cppcheck@a4e85eb563a5_master/stats.html create mode 100644 2021-01-31-142700-0966-cppcheck@a4e85eb563a5_master/style.css create mode 100644 2021-02-03-042556-4698-1@f831e5f9db05_master/index.html create mode 100644 2021-02-03-042556-4698-1@f831e5f9db05_master/report-023dd5.html create mode 100644 2021-02-03-042556-4698-1@f831e5f9db05_master/report-0cdc50.html create mode 100644 2021-02-03-042556-4698-1@f831e5f9db05_master/report-123bae.html create mode 100644 2021-02-03-042556-4698-1@f831e5f9db05_master/report-491601.html create mode 100644 2021-02-03-042556-4698-1@f831e5f9db05_master/report-c41301.html create mode 100644 2021-02-03-042556-4698-1@f831e5f9db05_master/report-e47f1b.html create mode 100644 2021-02-03-042556-4698-1@f831e5f9db05_master/scanview.css create mode 100644 2021-02-03-042556-4698-1@f831e5f9db05_master/sorttable.js create mode 100644 2021-02-03-042700-7163-cppcheck@f831e5f9db05_master/0.html create mode 100644 2021-02-03-042700-7163-cppcheck@f831e5f9db05_master/index.html create mode 100644 2021-02-03-042700-7163-cppcheck@f831e5f9db05_master/stats.html create mode 100644 2021-02-03-042700-7163-cppcheck@f831e5f9db05_master/style.css create mode 100644 2021-02-06-203249-4706-1@e1431577975f_master/index.html create mode 100644 2021-02-06-203249-4706-1@e1431577975f_master/report-0099b5.html create mode 100644 2021-02-06-203249-4706-1@e1431577975f_master/report-319d36.html create mode 100644 2021-02-06-203249-4706-1@e1431577975f_master/report-a6b7a0.html create mode 100644 2021-02-06-203249-4706-1@e1431577975f_master/report-afd61a.html create mode 100644 2021-02-06-203249-4706-1@e1431577975f_master/report-cce679.html create mode 100644 2021-02-06-203249-4706-1@e1431577975f_master/report-f3407b.html create mode 100644 2021-02-06-203249-4706-1@e1431577975f_master/scanview.css create mode 100644 2021-02-06-203249-4706-1@e1431577975f_master/sorttable.js create mode 100644 2021-02-06-203354-1052-cppcheck@e1431577975f_master/0.html create mode 100644 2021-02-06-203354-1052-cppcheck@e1431577975f_master/index.html create mode 100644 2021-02-06-203354-1052-cppcheck@e1431577975f_master/stats.html create mode 100644 2021-02-06-203354-1052-cppcheck@e1431577975f_master/style.css create mode 100644 2021-02-07-103917-5218-1@b068f27edb36_gettext-support/index.html create mode 100644 2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-366b87.html create mode 100644 2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-36b3bf.html create mode 100644 2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-3b67fc.html create mode 100644 2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-49f7d6.html create mode 100644 2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-71afd1.html create mode 100644 2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-a602eb.html create mode 100644 2021-02-07-103917-5218-1@b068f27edb36_gettext-support/scanview.css create mode 100644 2021-02-07-103917-5218-1@b068f27edb36_gettext-support/sorttable.js create mode 100644 2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/0.html create mode 100644 2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/1.html create mode 100644 2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/index.html create mode 100644 2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/stats.html create mode 100644 2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/style.css create mode 100644 2021-02-07-133540-5220-1@32ef0c02ef62_master/index.html create mode 100644 2021-02-07-133540-5220-1@32ef0c02ef62_master/report-103542.html create mode 100644 2021-02-07-133540-5220-1@32ef0c02ef62_master/report-27e6bf.html create mode 100644 2021-02-07-133540-5220-1@32ef0c02ef62_master/report-957ecf.html create mode 100644 2021-02-07-133540-5220-1@32ef0c02ef62_master/report-c8e528.html create mode 100644 2021-02-07-133540-5220-1@32ef0c02ef62_master/report-cf3e61.html create mode 100644 2021-02-07-133540-5220-1@32ef0c02ef62_master/report-ffe08d.html create mode 100644 2021-02-07-133540-5220-1@32ef0c02ef62_master/scanview.css create mode 100644 2021-02-07-133540-5220-1@32ef0c02ef62_master/sorttable.js create mode 100644 2021-02-07-133646-9107-cppcheck@32ef0c02ef62_master/0.html create mode 100644 2021-02-07-133646-9107-cppcheck@32ef0c02ef62_master/index.html create mode 100644 2021-02-07-133646-9107-cppcheck@32ef0c02ef62_master/stats.html create mode 100644 2021-02-07-133646-9107-cppcheck@32ef0c02ef62_master/style.css create mode 100644 2021-02-22-220133-5328-1@f50da0520f04_master/index.html create mode 100644 2021-02-22-220133-5328-1@f50da0520f04_master/report-17b76a.html create mode 100644 2021-02-22-220133-5328-1@f50da0520f04_master/report-65ae22.html create mode 100644 2021-02-22-220133-5328-1@f50da0520f04_master/report-69a70e.html create mode 100644 2021-02-22-220133-5328-1@f50da0520f04_master/report-82b186.html create mode 100644 2021-02-22-220133-5328-1@f50da0520f04_master/report-d27deb.html create mode 100644 2021-02-22-220133-5328-1@f50da0520f04_master/report-e12d4a.html create mode 100644 2021-02-22-220133-5328-1@f50da0520f04_master/scanview.css create mode 100644 2021-02-22-220133-5328-1@f50da0520f04_master/sorttable.js create mode 100644 2021-02-22-220237-5809-cppcheck@f50da0520f04_master/0.html create mode 100644 2021-02-22-220237-5809-cppcheck@f50da0520f04_master/index.html create mode 100644 2021-02-22-220237-5809-cppcheck@f50da0520f04_master/stats.html create mode 100644 2021-02-22-220237-5809-cppcheck@f50da0520f04_master/style.css create mode 100644 CNAME create mode 100644 index.html diff --git a/2021-01-26-091604-5205-1@811952a5706c_gettext-support/index.html b/2021-01-26-091604-5205-1@811952a5706c_gettext-support/index.html new file mode 100644 index 0000000..4448c57 --- /dev/null +++ b/2021-01-26-091604-5205-1@811952a5706c_gettext-support/index.html @@ -0,0 +1,111 @@ + + +rootdir - scan-build results + + + + + + +

rootdir - scan-build results

+ + + + + + + +
User:root@8091786ee5f7
Working Directory:/rootdir
Command Line:make -j 2
Clang Version:clang version 11.0.0 (Fedora 11.0.0-2.fc33) +
Date:Tue Jan 26 09:16:04 2021
+

Bug Summary

+ + + + + + +
Bug TypeQuantityDisplay?
All Bugs6
Dead store
Dead assignment4
Logic error
Dereference of null pointer2
+

Reports

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bug GroupBug Type ▾FileFunction/MethodLinePath Length
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
+ + diff --git a/2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-26579a.html b/2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-26579a.html new file mode 100644 index 0000000..49b7cb2 --- /dev/null +++ b/2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-26579a.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

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

Bug Summary

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-01-26-091604-5205-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-936604.html b/2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-936604.html new file mode 100644 index 0000000..971e349 --- /dev/null +++ b/2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-936604.html @@ -0,0 +1,783 @@ + + + +menu-monitor.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-01-26-091604-5205-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-980be6.html b/2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-980be6.html new file mode 100644 index 0000000..e20a938 --- /dev/null +++ b/2021-01-26-091604-5205-1@811952a5706c_gettext-support/report-980be6.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 2
+   1  libmenu/menu-layout.c
+   1  libmenu/desktop-entries.c
+

+ +
+
+ + + diff --git a/2021-01-26-091711-0626-cppcheck@811952a5706c_gettext-support/style.css b/2021-01-26-091711-0626-cppcheck@811952a5706c_gettext-support/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-01-26-091711-0626-cppcheck@811952a5706c_gettext-support/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} diff --git a/2021-01-29-131231-4701-1@1c989ea4e5ea_master/index.html b/2021-01-29-131231-4701-1@1c989ea4e5ea_master/index.html new file mode 100644 index 0000000..990ad7a --- /dev/null +++ b/2021-01-29-131231-4701-1@1c989ea4e5ea_master/index.html @@ -0,0 +1,111 @@ + + +rootdir - scan-build results + + + + + + +

rootdir - scan-build results

+ + + + + + + +
User:root@eb139e0ca2fd
Working Directory:/rootdir
Command Line:make -j 2
Clang Version:clang version 11.0.0 (Fedora 11.0.0-2.fc33) +
Date:Fri Jan 29 13:12:31 2021
+

Bug Summary

+ + + + + + +
Bug TypeQuantityDisplay?
All Bugs6
Dead store
Dead assignment4
Logic error
Dereference of null pointer2
+

Reports

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bug GroupBug Type ▾FileFunction/MethodLinePath Length
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
+ + diff --git a/2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-2e65d7.html b/2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-2e65d7.html new file mode 100644 index 0000000..c2a53f9 --- /dev/null +++ b/2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-2e65d7.html @@ -0,0 +1,2803 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-01-29-131231-4701-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-991093.html b/2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-991093.html new file mode 100644 index 0000000..b4edbb5 --- /dev/null +++ b/2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-991093.html @@ -0,0 +1,2803 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-01-29-131231-4701-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-cb70e8.html b/2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-cb70e8.html new file mode 100644 index 0000000..46a8c59 --- /dev/null +++ b/2021-01-29-131231-4701-1@1c989ea4e5ea_master/report-cb70e8.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 2
+   1  libmenu/menu-layout.c
+   1  libmenu/desktop-entries.c
+

+ +
+
+ + + diff --git a/2021-01-29-131335-9628-cppcheck@1c989ea4e5ea_master/style.css b/2021-01-29-131335-9628-cppcheck@1c989ea4e5ea_master/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-01-29-131335-9628-cppcheck@1c989ea4e5ea_master/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} diff --git a/2021-01-30-012612-5206-1@e05b48afe576_gettext-support/index.html b/2021-01-30-012612-5206-1@e05b48afe576_gettext-support/index.html new file mode 100644 index 0000000..7b3bca6 --- /dev/null +++ b/2021-01-30-012612-5206-1@e05b48afe576_gettext-support/index.html @@ -0,0 +1,111 @@ + + +rootdir - scan-build results + + + + + + +

rootdir - scan-build results

+ + + + + + + +
User:root@2efd1a53a223
Working Directory:/rootdir
Command Line:make -j 2
Clang Version:clang version 11.0.0 (Fedora 11.0.0-2.fc33) +
Date:Sat Jan 30 01:26:12 2021
+

Bug Summary

+ + + + + + +
Bug TypeQuantityDisplay?
All Bugs6
Dead store
Dead assignment4
Logic error
Dereference of null pointer2
+

Reports

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bug GroupBug Type ▾FileFunction/MethodLinePath Length
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
+ + diff --git a/2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-4662b9.html b/2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-4662b9.html new file mode 100644 index 0000000..eb819fb --- /dev/null +++ b/2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-4662b9.html @@ -0,0 +1,2803 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Bug Summary

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

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-01-30-012612-5206-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-e775a0.html b/2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-e775a0.html new file mode 100644 index 0000000..612168e --- /dev/null +++ b/2021-01-30-012612-5206-1@e05b48afe576_gettext-support/report-e775a0.html @@ -0,0 +1,783 @@ + + + +menu-monitor.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 2
+   1  libmenu/menu-layout.c
+   1  libmenu/desktop-entries.c
+

+ +
+
+ + + diff --git a/2021-01-30-012719-8694-cppcheck@e05b48afe576_gettext-support/style.css b/2021-01-30-012719-8694-cppcheck@e05b48afe576_gettext-support/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-01-30-012719-8694-cppcheck@e05b48afe576_gettext-support/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} diff --git a/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/index.html b/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/index.html new file mode 100644 index 0000000..b354f11 --- /dev/null +++ b/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/index.html @@ -0,0 +1,111 @@ + + +rootdir - scan-build results + + + + + + +

rootdir - scan-build results

+ + + + + + + +
User:root@1c428638c210
Working Directory:/rootdir
Command Line:make -j 2
Clang Version:clang version 11.0.0 (Fedora 11.0.0-2.fc33) +
Date:Sat Jan 30 05:37:02 2021
+

Bug Summary

+ + + + + + +
Bug TypeQuantityDisplay?
All Bugs6
Dead store
Dead assignment4
Logic error
Dereference of null pointer2
+

Reports

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bug GroupBug Type ▾FileFunction/MethodLinePath Length
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
+ + diff --git a/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-274f0b.html b/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-274f0b.html new file mode 100644 index 0000000..916959c --- /dev/null +++ b/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-274f0b.html @@ -0,0 +1,2803 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-01-30-053702-4700-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-b90326.html b/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-b90326.html new file mode 100644 index 0000000..cbeba49 --- /dev/null +++ b/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-b90326.html @@ -0,0 +1,2803 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-01-30-053702-4700-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-f01eb2.html b/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-f01eb2.html new file mode 100644 index 0000000..0f8633a --- /dev/null +++ b/2021-01-30-053702-4700-1@d04f74e269c9_ax_enable_debug/report-f01eb2.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 2
+   1  libmenu/menu-layout.c
+   1  libmenu/desktop-entries.c
+

+ +
+
+ + + diff --git a/2021-01-30-053806-6255-cppcheck@d04f74e269c9_ax_enable_debug/style.css b/2021-01-30-053806-6255-cppcheck@d04f74e269c9_ax_enable_debug/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-01-30-053806-6255-cppcheck@d04f74e269c9_ax_enable_debug/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} diff --git a/2021-01-31-142554-4695-1@a4e85eb563a5_master/index.html b/2021-01-31-142554-4695-1@a4e85eb563a5_master/index.html new file mode 100644 index 0000000..b52b3e7 --- /dev/null +++ b/2021-01-31-142554-4695-1@a4e85eb563a5_master/index.html @@ -0,0 +1,111 @@ + + +rootdir - scan-build results + + + + + + +

rootdir - scan-build results

+ + + + + + + +
User:root@b3ddaf81318d
Working Directory:/rootdir
Command Line:make -j 2
Clang Version:clang version 11.0.0 (Fedora 11.0.0-2.fc33) +
Date:Sun Jan 31 14:25:54 2021
+

Bug Summary

+ + + + + + +
Bug TypeQuantityDisplay?
All Bugs6
Dead store
Dead assignment4
Logic error
Dereference of null pointer2
+

Reports

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bug GroupBug Type ▾FileFunction/MethodLinePath Length
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
+ + diff --git a/2021-01-31-142554-4695-1@a4e85eb563a5_master/report-43a9f0.html b/2021-01-31-142554-4695-1@a4e85eb563a5_master/report-43a9f0.html new file mode 100644 index 0000000..8e23781 --- /dev/null +++ b/2021-01-31-142554-4695-1@a4e85eb563a5_master/report-43a9f0.html @@ -0,0 +1,783 @@ + + + +menu-monitor.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-01-31-142554-4695-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-01-31-142554-4695-1@a4e85eb563a5_master/report-4c3c0e.html b/2021-01-31-142554-4695-1@a4e85eb563a5_master/report-4c3c0e.html new file mode 100644 index 0000000..8ab850a --- /dev/null +++ b/2021-01-31-142554-4695-1@a4e85eb563a5_master/report-4c3c0e.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-01-31-142554-4695-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-01-31-142554-4695-1@a4e85eb563a5_master/report-face03.html b/2021-01-31-142554-4695-1@a4e85eb563a5_master/report-face03.html new file mode 100644 index 0000000..916ada3 --- /dev/null +++ b/2021-01-31-142554-4695-1@a4e85eb563a5_master/report-face03.html @@ -0,0 +1,2803 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 1
+   1  libmenu/menu-layout.c
+

+ +
+
+ + + diff --git a/2021-01-31-142700-0966-cppcheck@a4e85eb563a5_master/style.css b/2021-01-31-142700-0966-cppcheck@a4e85eb563a5_master/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-01-31-142700-0966-cppcheck@a4e85eb563a5_master/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} diff --git a/2021-02-03-042556-4698-1@f831e5f9db05_master/index.html b/2021-02-03-042556-4698-1@f831e5f9db05_master/index.html new file mode 100644 index 0000000..4b3ea2e --- /dev/null +++ b/2021-02-03-042556-4698-1@f831e5f9db05_master/index.html @@ -0,0 +1,111 @@ + + +rootdir - scan-build results + + + + + + +

rootdir - scan-build results

+ + + + + + + +
User:root@35a686f3f2de
Working Directory:/rootdir
Command Line:make -j 2
Clang Version:clang version 11.0.0 (Fedora 11.0.0-2.fc33) +
Date:Wed Feb 3 04:25:56 2021
+

Bug Summary

+ + + + + + +
Bug TypeQuantityDisplay?
All Bugs6
Dead store
Dead assignment4
Logic error
Dereference of null pointer2
+

Reports

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bug GroupBug Type ▾FileFunction/MethodLinePath Length
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
+ + diff --git a/2021-02-03-042556-4698-1@f831e5f9db05_master/report-023dd5.html b/2021-02-03-042556-4698-1@f831e5f9db05_master/report-023dd5.html new file mode 100644 index 0000000..192370d --- /dev/null +++ b/2021-02-03-042556-4698-1@f831e5f9db05_master/report-023dd5.html @@ -0,0 +1,783 @@ + + + +menu-monitor.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-02-03-042556-4698-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-02-03-042556-4698-1@f831e5f9db05_master/report-0cdc50.html b/2021-02-03-042556-4698-1@f831e5f9db05_master/report-0cdc50.html new file mode 100644 index 0000000..c60ad44 --- /dev/null +++ b/2021-02-03-042556-4698-1@f831e5f9db05_master/report-0cdc50.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-02-03-042556-4698-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-02-03-042556-4698-1@f831e5f9db05_master/report-491601.html b/2021-02-03-042556-4698-1@f831e5f9db05_master/report-491601.html new file mode 100644 index 0000000..1c16ba8 --- /dev/null +++ b/2021-02-03-042556-4698-1@f831e5f9db05_master/report-491601.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

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

Bug Summary

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

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 1
+   1  libmenu/menu-layout.c
+

+ +
+
+ + + diff --git a/2021-02-03-042700-7163-cppcheck@f831e5f9db05_master/style.css b/2021-02-03-042700-7163-cppcheck@f831e5f9db05_master/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-02-03-042700-7163-cppcheck@f831e5f9db05_master/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} diff --git a/2021-02-06-203249-4706-1@e1431577975f_master/index.html b/2021-02-06-203249-4706-1@e1431577975f_master/index.html new file mode 100644 index 0000000..8e734e9 --- /dev/null +++ b/2021-02-06-203249-4706-1@e1431577975f_master/index.html @@ -0,0 +1,111 @@ + + +rootdir - scan-build results + + + + + + +

rootdir - scan-build results

+ + + + + + + +
User:root@5e01fc77112b
Working Directory:/rootdir
Command Line:make -j 2
Clang Version:clang version 11.0.0 (Fedora 11.0.0-2.fc33) +
Date:Sat Feb 6 20:32:49 2021
+

Bug Summary

+ + + + + + +
Bug TypeQuantityDisplay?
All Bugs6
Dead store
Dead assignment4
Logic error
Dereference of null pointer2
+

Reports

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bug GroupBug Type ▾FileFunction/MethodLinePath Length
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
+ + diff --git a/2021-02-06-203249-4706-1@e1431577975f_master/report-0099b5.html b/2021-02-06-203249-4706-1@e1431577975f_master/report-0099b5.html new file mode 100644 index 0000000..548d685 --- /dev/null +++ b/2021-02-06-203249-4706-1@e1431577975f_master/report-0099b5.html @@ -0,0 +1,2803 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-02-06-203249-4706-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-02-06-203249-4706-1@e1431577975f_master/report-cce679.html b/2021-02-06-203249-4706-1@e1431577975f_master/report-cce679.html new file mode 100644 index 0000000..39b1968 --- /dev/null +++ b/2021-02-06-203249-4706-1@e1431577975f_master/report-cce679.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 1
+   1  libmenu/menu-layout.c
+

+ +
+
+ + + diff --git a/2021-02-06-203354-1052-cppcheck@e1431577975f_master/style.css b/2021-02-06-203354-1052-cppcheck@e1431577975f_master/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-02-06-203354-1052-cppcheck@e1431577975f_master/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} diff --git a/2021-02-07-103917-5218-1@b068f27edb36_gettext-support/index.html b/2021-02-07-103917-5218-1@b068f27edb36_gettext-support/index.html new file mode 100644 index 0000000..8bc0f48 --- /dev/null +++ b/2021-02-07-103917-5218-1@b068f27edb36_gettext-support/index.html @@ -0,0 +1,111 @@ + + +rootdir - scan-build results + + + + + + +

rootdir - scan-build results

+ + + + + + + +
User:root@4a6cf75c3293
Working Directory:/rootdir
Command Line:make -j 2
Clang Version:clang version 11.0.0 (Fedora 11.0.0-2.fc33) +
Date:Sun Feb 7 10:39:17 2021
+

Bug Summary

+ + + + + + +
Bug TypeQuantityDisplay?
All Bugs6
Dead store
Dead assignment4
Logic error
Dereference of null pointer2
+

Reports

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bug GroupBug Type ▾FileFunction/MethodLinePath Length
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
+ + diff --git a/2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-366b87.html b/2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-366b87.html new file mode 100644 index 0000000..208f736 --- /dev/null +++ b/2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-366b87.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

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

Bug Summary

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-02-07-103917-5218-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-71afd1.html b/2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-71afd1.html new file mode 100644 index 0000000..3b22a53 --- /dev/null +++ b/2021-02-07-103917-5218-1@b068f27edb36_gettext-support/report-71afd1.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 2
+   1  libmenu/menu-layout.c
+   1  libmenu/desktop-entries.c
+

+ +
+
+ + + diff --git a/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/style.css b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-02-07-104018-8190-cppcheck@b068f27edb36_gettext-support/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} diff --git a/2021-02-07-133540-5220-1@32ef0c02ef62_master/index.html b/2021-02-07-133540-5220-1@32ef0c02ef62_master/index.html new file mode 100644 index 0000000..0aa1b5f --- /dev/null +++ b/2021-02-07-133540-5220-1@32ef0c02ef62_master/index.html @@ -0,0 +1,111 @@ + + +rootdir - scan-build results + + + + + + +

rootdir - scan-build results

+ + + + + + + +
User:root@03e6b9086dd7
Working Directory:/rootdir
Command Line:make -j 2
Clang Version:clang version 11.0.0 (Fedora 11.0.0-2.fc33) +
Date:Sun Feb 7 13:35:40 2021
+

Bug Summary

+ + + + + + +
Bug TypeQuantityDisplay?
All Bugs6
Dead store
Dead assignment4
Logic error
Dereference of null pointer2
+

Reports

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bug GroupBug Type ▾FileFunction/MethodLinePath Length
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
+ + diff --git a/2021-02-07-133540-5220-1@32ef0c02ef62_master/report-103542.html b/2021-02-07-133540-5220-1@32ef0c02ef62_master/report-103542.html new file mode 100644 index 0000000..f95701c --- /dev/null +++ b/2021-02-07-133540-5220-1@32ef0c02ef62_master/report-103542.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-02-07-133540-5220-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-02-07-133540-5220-1@32ef0c02ef62_master/report-c8e528.html b/2021-02-07-133540-5220-1@32ef0c02ef62_master/report-c8e528.html new file mode 100644 index 0000000..c331c44 --- /dev/null +++ b/2021-02-07-133540-5220-1@32ef0c02ef62_master/report-c8e528.html @@ -0,0 +1,2803 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 1
+   1  libmenu/menu-layout.c
+

+ +
+
+ + + diff --git a/2021-02-07-133646-9107-cppcheck@32ef0c02ef62_master/style.css b/2021-02-07-133646-9107-cppcheck@32ef0c02ef62_master/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-02-07-133646-9107-cppcheck@32ef0c02ef62_master/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} diff --git a/2021-02-22-220133-5328-1@f50da0520f04_master/index.html b/2021-02-22-220133-5328-1@f50da0520f04_master/index.html new file mode 100644 index 0000000..6e34865 --- /dev/null +++ b/2021-02-22-220133-5328-1@f50da0520f04_master/index.html @@ -0,0 +1,111 @@ + + +rootdir - scan-build results + + + + + + +

rootdir - scan-build results

+ + + + + + + +
User:root@e0a96213d203
Working Directory:/rootdir
Command Line:make -j 2
Clang Version:clang version 11.0.0 (Fedora 11.0.0-2.fc33) +
Date:Mon Feb 22 22:01:33 2021
+

Bug Summary

+ + + + + + +
Bug TypeQuantityDisplay?
All Bugs6
Dead store
Dead assignment4
Logic error
Dereference of null pointer2
+

Reports

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Bug GroupBug Type ▾FileFunction/MethodLinePath Length
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Dead storeDead assignmentmenu-layout.cfixup_move_node20841View Report
Dead storeDead assignmentmenu-monitor.cmonitor_callback1601View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
Logic errorDereference of null pointermenu-layout.cmenu_layout_node_steal56774View Report
+ + diff --git a/2021-02-22-220133-5328-1@f50da0520f04_master/report-17b76a.html b/2021-02-22-220133-5328-1@f50da0520f04_master/report-17b76a.html new file mode 100644 index 0000000..9332347 --- /dev/null +++ b/2021-02-22-220133-5328-1@f50da0520f04_master/report-17b76a.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-02-22-220133-5328-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-02-22-220133-5328-1@f50da0520f04_master/report-82b186.html b/2021-02-22-220133-5328-1@f50da0520f04_master/report-82b186.html new file mode 100644 index 0000000..edb7b83 --- /dev/null +++ b/2021-02-22-220133-5328-1@f50da0520f04_master/report-82b186.html @@ -0,0 +1,2746 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

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

Bug Summary

+ + + + +
File:menu-monitor.c
Warning:line 160, column 3
Value stored to 'event' is never read
+ +

Annotated Source Code

+

Press '?' + to see keyboard shortcuts

+ + +
clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name menu-monitor.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 static -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/11.0.0 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib64/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -D MATEMENU_I_KNOW_THIS_IS_UNSTABLE -D G_DISABLE_ASSERT -D G_DISABLE_CHECKS -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/11.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-unused-parameter -Wno-sign-compare -fdebug-compilation-dir /rootdir/libmenu -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-checker security.insecureAPI.strcpy -analyzer-output=html -faddrsig -o /rootdir/html-report/2021-02-22-220133-5328-1 -x c menu-monitor.c +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
1/*
2 * Copyright (C) 2005 Red Hat, Inc.
3 * Copyright (C) 2006 Mark McLoughlin
4 * Copyright (C) 2007 Sebastian Dröge
5 * Copyright (C) 2008 Vincent Untz
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include <config.h>
24
25#include "menu-monitor.h"
26
27#include <gio/gio.h>
28
29#include "menu-util.h"
30
31struct MenuMonitor {
32 char* path;
33 guint refcount;
34
35 GSList* notifies;
36
37 GFileMonitor* monitor;
38
39 guint is_directory: 1;
40};
41
42typedef struct {
43 MenuMonitor* monitor;
44 MenuMonitorEvent event;
45 char* path;
46} MenuMonitorEventInfo;
47
48typedef struct {
49 MenuMonitorNotifyFunc notify_func;
50 gpointer user_data;
51 guint refcount;
52} MenuMonitorNotify;
53
54static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify);
55static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify);
56
57static GHashTable* monitors_registry = NULL((void*)0);
58static guint events_idle_handler = 0;
59static GSList* pending_events = NULL((void*)0);
60
61static void invoke_notifies(MenuMonitor* monitor, MenuMonitorEvent event, const char* path)
62{
63 GSList *copy;
64 GSList *tmp;
65
66 copy = g_slist_copy (monitor->notifies);
67 g_slist_foreach (copy,
68 (GFunc) mate_menu_monitor_notify_refmenu_monitor_notify_ref,
69 NULL((void*)0));
70
71 tmp = copy;
72 while (tmp != NULL((void*)0))
73 {
74 MenuMonitorNotify *notify = tmp->data;
75 GSList *next = tmp->next;
76
77 if (notify->notify_func)
78 {
79 notify->notify_func (monitor, event, path, notify->user_data);
80 }
81
82 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
83
84 tmp = next;
85 }
86
87 g_slist_free (copy);
88}
89
90static gboolean emit_events_in_idle(void)
91{
92 GSList *events_to_emit;
93 GSList *tmp;
94
95 events_to_emit = pending_events;
96
97 pending_events = NULL((void*)0);
98 events_idle_handler = 0;
99
100 tmp = events_to_emit;
101 while (tmp != NULL((void*)0))
102 {
103 MenuMonitorEventInfo *event_info = tmp->data;
104
105 mate_menu_monitor_refmenu_monitor_ref(event_info->monitor);
106
107 tmp = tmp->next;
108 }
109
110 tmp = events_to_emit;
111 while (tmp != NULL((void*)0))
112 {
113 MenuMonitorEventInfo *event_info = tmp->data;
114
115 invoke_notifies (event_info->monitor,
116 event_info->event,
117 event_info->path);
118
119 menu_monitor_unref (event_info->monitor);
120 event_info->monitor = NULL((void*)0);
121
122 g_free (event_info->path);
123 event_info->path = NULL((void*)0);
124
125 event_info->event = MENU_MONITOR_EVENT_INVALID;
126
127 g_free (event_info);
128
129 tmp = tmp->next;
130 }
131
132 g_slist_free (events_to_emit);
133
134 return FALSE(0);
135}
136
137static void menu_monitor_queue_event(MenuMonitorEventInfo* event_info)
138{
139 pending_events = g_slist_append (pending_events, event_info);
140
141 if (events_idle_handler == 0)
142 {
143 events_idle_handler = g_idle_add ((GSourceFunc) emit_events_in_idle, NULL((void*)0));
144 }
145}
146
147static inline char* get_registry_key(const char* path, gboolean is_directory)
148{
149 return g_strdup_printf ("%s:%s",
150 path,
151 is_directory ? "<dir>" : "<file>");
152}
153
154static gboolean monitor_callback (GFileMonitor* monitor, GFile* child, GFile* other_file, GFileMonitorEvent eflags, gpointer user_data)
155{
156 MenuMonitorEventInfo *event_info;
157 MenuMonitorEvent event;
158 MenuMonitor *menu_monitor = (MenuMonitor *) user_data;
159
160 event = MENU_MONITOR_EVENT_INVALID;
Value stored to 'event' is never read
161 switch (eflags)
162 {
163 case G_FILE_MONITOR_EVENT_CHANGED:
164 event = MENU_MONITOR_EVENT_CHANGED;
165 break;
166 case G_FILE_MONITOR_EVENT_CREATED:
167 event = MENU_MONITOR_EVENT_CREATED;
168 break;
169 case G_FILE_MONITOR_EVENT_DELETED:
170 event = MENU_MONITOR_EVENT_DELETED;
171 break;
172 default:
173 return TRUE(!(0));
174 }
175
176 event_info = g_new0 (MenuMonitorEventInfo, 1)((MenuMonitorEventInfo *) g_malloc0_n ((1), sizeof (MenuMonitorEventInfo
)))
;
177
178 event_info->path = g_file_get_path (child);
179 event_info->event = event;
180 event_info->monitor = menu_monitor;
181
182 menu_monitor_queue_event (event_info);
183
184 return TRUE(!(0));
185}
186
187static MenuMonitor* register_monitor(const char* path, gboolean is_directory)
188{
189 MenuMonitor *retval;
190 GFile *file;
191
192 retval = g_new0 (MenuMonitor, 1)((MenuMonitor *) g_malloc0_n ((1), sizeof (MenuMonitor)));
193
194 retval->path = g_strdup (path);
195 retval->refcount = 1;
196 retval->is_directory = is_directory != FALSE(0);
197
198 file = g_file_new_for_path (retval->path);
199
200 if (file == NULL((void*)0))
201 {
202 menu_verbose ("Not adding monitor on '%s', failed to create GFile\n",
203 retval->path);
204 return retval;
205 }
206
207 if (retval->is_directory)
208 retval->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE,
209 NULL((void*)0), NULL((void*)0));
210 else
211 retval->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
212 NULL((void*)0), NULL((void*)0));
213
214 g_object_unref (G_OBJECT (file)((((GObject*) g_type_check_instance_cast ((GTypeInstance*) ((
file)), (((GType) ((20) << (2))))))))
);
215
216 if (retval->monitor == NULL((void*)0))
217 {
218 menu_verbose ("Not adding monitor on '%s', failed to create monitor\n",
219 retval->path);
220 return retval;
221 }
222
223 g_signal_connect (retval->monitor, "changed",g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
224 G_CALLBACK (monitor_callback), retval)g_signal_connect_data ((retval->monitor), ("changed"), (((
GCallback) (monitor_callback))), (retval), ((void*)0), (GConnectFlags
) 0)
;
225
226 return retval;
227}
228
229static MenuMonitor* lookup_monitor(const char* path, gboolean is_directory)
230{
231 MenuMonitor *retval;
232 char *registry_key;
233
234 retval = NULL((void*)0);
235
236 registry_key = get_registry_key (path, is_directory);
237
238 if (monitors_registry == NULL((void*)0))
239 {
240 monitors_registry = g_hash_table_new_full (g_str_hash,
241 g_str_equal,
242 g_free,
243 NULL((void*)0));
244 }
245 else
246 {
247 retval = g_hash_table_lookup (monitors_registry, registry_key);
248 }
249
250 if (retval == NULL((void*)0))
251 {
252 retval = register_monitor (path, is_directory);
253 g_hash_table_insert (monitors_registry, registry_key, retval);
254
255 return retval;
256 }
257 else
258 {
259 g_free (registry_key);
260
261 return mate_menu_monitor_refmenu_monitor_ref(retval);
262 }
263}
264
265MenuMonitor* mate_menu_monitor_file_getmenu_get_file_monitor(const char* path)
266{
267 g_return_val_if_fail(path != NULL, NULL)do{ (void)0; }while (0);
268
269 return lookup_monitor(path, FALSE(0));
270}
271
272MenuMonitor* menu_get_directory_monitor(const char* path)
273{
274 g_return_val_if_fail (path != NULL, NULL)do{ (void)0; }while (0);
275
276 return lookup_monitor (path, TRUE(!(0)));
277}
278
279MenuMonitor* mate_menu_monitor_refmenu_monitor_ref(MenuMonitor* monitor)
280{
281 g_return_val_if_fail(monitor != NULL, NULL)do{ (void)0; }while (0);
282 g_return_val_if_fail(monitor->refcount > 0, NULL)do{ (void)0; }while (0);
283
284 monitor->refcount++;
285
286 return monitor;
287}
288
289static void menu_monitor_clear_pending_events(MenuMonitor* monitor)
290{
291 GSList *tmp;
292
293 tmp = pending_events;
294 while (tmp != NULL((void*)0))
295 {
296 MenuMonitorEventInfo *event_info = tmp->data;
297 GSList *next = tmp->next;
298
299 if (event_info->monitor == monitor)
300 {
301 pending_events = g_slist_delete_link (pending_events, tmp);
302
303 g_free (event_info->path);
304 event_info->path = NULL((void*)0);
305
306 event_info->monitor = NULL((void*)0);
307 event_info->event = MENU_MONITOR_EVENT_INVALID;
308
309 g_free (event_info);
310 }
311
312 tmp = next;
313 }
314}
315
316void menu_monitor_unref(MenuMonitor* monitor)
317{
318 char *registry_key;
319
320 g_return_if_fail (monitor != NULL)do{ (void)0; }while (0);
321 g_return_if_fail (monitor->refcount > 0)do{ (void)0; }while (0);
322
323 if (--monitor->refcount > 0)
324 return;
325
326 registry_key = get_registry_key (monitor->path, monitor->is_directory);
327 g_hash_table_remove (monitors_registry, registry_key);
328 g_free (registry_key);
329
330 if (g_hash_table_size (monitors_registry) == 0)
331 {
332 g_hash_table_destroy (monitors_registry);
333 monitors_registry = NULL((void*)0);
334 }
335
336 if (monitor->monitor)
337 {
338 g_file_monitor_cancel (monitor->monitor);
339 g_object_unref (monitor->monitor);
340 monitor->monitor = NULL((void*)0);
341 }
342
343 g_slist_foreach (monitor->notifies, (GFunc) mate_menu_monitor_notify_unrefmenu_monitor_notify_unref, NULL((void*)0));
344 g_slist_free (monitor->notifies);
345 monitor->notifies = NULL((void*)0);
346
347 menu_monitor_clear_pending_events (monitor);
348
349 g_free (monitor->path);
350 monitor->path = NULL((void*)0);
351
352 g_free (monitor);
353}
354
355static MenuMonitorNotify* mate_menu_monitor_notify_refmenu_monitor_notify_ref(MenuMonitorNotify* notify)
356{
357 g_return_val_if_fail(notify != NULL, NULL)do{ (void)0; }while (0);
358 g_return_val_if_fail(notify->refcount > 0, NULL)do{ (void)0; }while (0);
359
360 notify->refcount++;
361
362 return notify;
363}
364
365static void mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(MenuMonitorNotify* notify)
366{
367 g_return_if_fail(notify != NULL)do{ (void)0; }while (0);
368 g_return_if_fail(notify->refcount > 0)do{ (void)0; }while (0);
369
370 if (--notify->refcount > 0)
371 {
372 return;
373 }
374
375 g_free(notify);
376}
377
378void menu_monitor_add_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
379{
380 MenuMonitorNotify* notify;
381
382 g_return_if_fail(monitor != NULL)do{ (void)0; }while (0);
383 g_return_if_fail(notify_func != NULL)do{ (void)0; }while (0);
384
385 GSList* tmp = monitor->notifies;
386
387 while (tmp != NULL((void*)0))
388 {
389 notify = tmp->data;
390
391 if (notify->notify_func == notify_func && notify->user_data == user_data)
392 {
393 break;
394 }
395
396 tmp = tmp->next;
397 }
398
399 if (tmp == NULL((void*)0))
400 {
401 notify = g_new0(MenuMonitorNotify, 1)((MenuMonitorNotify *) g_malloc0_n ((1), sizeof (MenuMonitorNotify
)))
;
402 notify->notify_func = notify_func;
403 notify->user_data = user_data;
404 notify->refcount = 1;
405
406 monitor->notifies = g_slist_append(monitor->notifies, notify);
407 }
408}
409
410void mate_menu_monitor_notify_removemenu_monitor_remove_notify(MenuMonitor* monitor, MenuMonitorNotifyFunc notify_func, gpointer user_data)
411{
412 GSList* tmp = monitor->notifies;
413
414 while (tmp != NULL((void*)0))
415 {
416 MenuMonitorNotify* notify = tmp->data;
417 GSList* next = tmp->next;
418
419 if (notify->notify_func == notify_func && notify->user_data == user_data)
420 {
421 notify->notify_func = NULL((void*)0);
422 notify->user_data = NULL((void*)0);
423
424 mate_menu_monitor_notify_unrefmenu_monitor_notify_unref(notify);
425
426 monitor->notifies = g_slist_delete_link(monitor->notifies, tmp);
427 }
428
429 tmp = next;
430 }
431}
diff --git a/2021-02-22-220133-5328-1@f50da0520f04_master/report-e12d4a.html b/2021-02-22-220133-5328-1@f50da0520f04_master/report-e12d4a.html new file mode 100644 index 0000000..88c2647 --- /dev/null +++ b/2021-02-22-220133-5328-1@f50da0520f04_master/report-e12d4a.html @@ -0,0 +1,2803 @@ + + + +menu-layout.c + + + + + + + + + + + + + + + + + + + + + + + + + + +

Bug Summary

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

Top 10 files for portability severity, total findings: 5
+   5  libmenu/menu-layout.c
+

+

Top 10 files for style severity, total findings: 1
+   1  libmenu/menu-layout.c
+

+ +
+
+ + + diff --git a/2021-02-22-220237-5809-cppcheck@f50da0520f04_master/style.css b/2021-02-22-220237-5809-cppcheck@f50da0520f04_master/style.css new file mode 100644 index 0000000..07125f4 --- /dev/null +++ b/2021-02-22-220237-5809-cppcheck@f50da0520f04_master/style.css @@ -0,0 +1,137 @@ + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + margin: 0; + width: auto; +} + +h1 { + margin: 10px; +} + +.header { + border-bottom: thin solid #aaa; +} + +.footer { + border-top: thin solid #aaa; + font-size: 90%; + margin-top: 5px; +} + +.footer ul { + list-style-type: none; + padding-left: 0; +} + +.footer > p { + margin: 4px; +} + +.wrapper { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: justify; + -ms-flex-pack: justify; + justify-content: space-between; +} + +#menu, +#menu_index { + text-align: left; + width: 350px; + height: 90vh; + min-height: 200px; + overflow: auto; + position: -webkit-sticky; + position: sticky; + top: 0; + padding: 0 15px 15px 15px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +#content, +#content_index { + background-color: #fff; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + padding: 0 15px 15px 15px; + width: calc(100% - 350px); + height: 100%; + overflow-x: auto; +} + +#filename { + margin-left: 10px; + font-size: 12px; + z-index: 1; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + z-index: 10; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.d-none { + display: none; +} diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..4d825a5 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +mate-menus.mate-desktop.dev diff --git a/index.html b/index.html new file mode 100644 index 0000000..eb1bdb8 --- /dev/null +++ b/index.html @@ -0,0 +1,46 @@ + + + + + mate-menus Code Analyzer results + + +

+ mate-desktop/mate-menus Static analyzer results +

+ GitHub + Build Status +
+Commit: f50da0520f0493303ae7565e478bb31f561a114a
+Compare: 32ef0c02ef62...f50da0520f04
+Branch: master
+Time: 2021-02-22 22:02:37+00:00
+Messages:
+
+tx: temporarily disable build for ArchLinux
+
+
+ + + -- cgit v1.2.1