1 #!/bin/sh 2 # $NetBSD: ctwm_app_menu,v 1.8 2026/04/17 11:07:12 nia Exp $ 3 # 4 # Copyright (c) 2020-2026 The NetBSD Foundation, Inc. 5 # All rights reserved. 6 # 7 # This code is derived from software contributed to The NetBSD Foundation 8 # by Nia Alarie. 9 # 10 # Redistribution and use in source and binary forms, with or without 11 # modification, are permitted provided that the following conditions 12 # are met: 13 # 1. Redistributions of source code must retain the above copyright 14 # notice, this list of conditions and the following disclaimer. 15 # 2. Redistributions in binary form must reproduce the above copyright 16 # notice, this list of conditions and the following disclaimer in the 17 # documentation and/or other materials provided with the distribution. 18 # 19 # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 # POSSIBILITY OF SUCH DAMAGE. 30 # 31 set -eu -o pipefail 32 33 LOCALBASE=$(pkg_info -Q LOCALBASE pkg_install 2>/dev/null) || 34 LOCALBASE=/usr/pkg 35 36 find "$LOCALBASE"/share/applications -name '*.desktop' -exec awk ' 37 function resetentry() { 38 name = menu = exec = "" 39 terminal = nodisplay = 0 40 } 41 function printentry() { 42 if (nodisplay) 43 return 44 if (terminal) 45 exec = "xterm -class UXTerm -e " exec 46 if (!menu) 47 menu = "Misc" 48 printf "%02d\t%s\t%s\t%s\n", menuorder[menu], quote(menu), 49 quote(" " name), "!" quote(exec " &") 50 } 51 52 function quote(s) { 53 gsub(/\\/, "\\\\", s) 54 gsub(/\"/, "\\\"", s) 55 gsub(/\t/, "\\t", s) 56 return "\"" s "\"" 57 } 58 59 function iskey(k, loc1) { 60 loc1 = $0; 61 sub(/ *=.*$/, "", loc1); 62 return loc1 == k; 63 } 64 65 function value(v) { 66 v = $0; 67 sub(/^[^=]*= */, "", v); 68 return v; 69 } 70 71 BEGIN { 72 menuno = 0 73 menuorder["Accessories"] = menuno++ 74 menuorder["Games"] = menuno++ 75 menuorder["Graphics"] = menuno++ 76 menuorder["Internet"] = menuno++ 77 menuorder["Multimedia"] = menuno++ 78 menuorder["Office"] = menuno++ 79 menuorder["Programming"] = menuno++ 80 menuorder["System"] = menuno++ 81 menuorder["Misc"] = menuno++ 82 for (menu in menuorder) 83 printf "%02d\t%s\n", menuorder[menu], quote(menu) 84 85 # Map from matching category to menu. This is a 86 # partial substring match: if the right-hand side of 87 # a Categories= line matches a key here, we take the 88 # corresponding menu. So, e.g., any desktop file with 89 # a "X-GNOME-SystemSettings" category matches "System". 90 catmenu["Audio"] = "Multimedia" 91 catmenu["Development"] = "Programming" 92 catmenu["Game"] = "Games" 93 catmenu["Graphics"] = "Graphics" 94 catmenu["Network"] = "Internet" 95 catmenu["Office"] = "Office" 96 catmenu["System"] = "System" 97 catmenu["Utility"] = "Accessories" 98 99 # Categories to match, in precedence order: the first 100 # matching category in this list is the one which we 101 # use to decide the menu. 102 catno = 0 103 catorder[catno++] = "Audio" 104 catorder[catno++] = "Development" 105 catorder[catno++] = "Graphics" 106 catorder[catno++] = "Game" 107 catorder[catno++] = "Office" 108 catorder[catno++] = "Network" 109 catorder[catno++] = "System" 110 catorder[catno++] = "Utility" 111 112 resetentry() 113 } 114 115 { gsub(/\r/, "") } 116 FNR == 1 && NR > 1 { printentry() } 117 END { printentry() } 118 FNR == 1 { resetentry() } 119 120 iskey("Name") && !name { name = value() } 121 /^Terminal *= *true$/ { terminal = 1 } 122 iskey("OnlyShowIn") { nodisplay = 1 } 123 /^NoDisplay *= *true$/ { nodisplay = 1 } 124 iskey("Exec") && !exec { exec = value() 125 gsub(/ %.*/, "", exec) 126 if (exec ~ /\"/) # XXX 127 nodisplay = 1 } 128 iskey("Categories") && !menu { 129 categories = value() 130 for (i = 0; i < catno; i++) { 131 if (categories ~ catorder[i]) { 132 menu = catmenu[catorder[i]] 133 break 134 } 135 } 136 } 137 ' '{}' + \ 138 | sort -u \ 139 | awk -F '\t' ' 140 function startmenu(menu) { 141 printf "menu %s\n", menu 142 printf "{\n" 143 printf "\t%s\tf.title\n", menu 144 curmenu = menu 145 } 146 function endmenu() { 147 if (!curmenu) 148 return 149 printf "}\n" 150 } 151 152 # Inputs: 1=order 2=menu [3=name 4=exec], strings pre-quoted. 153 $2 != curmenu { endmenu(); startmenu($2) } 154 NF == 4 { printf "\t%s %s \n", $3, $4 } 155 END { endmenu(); } 156 ' 157