2 * wmmenugen - Window Maker PropList menu generator
4 * Copyright (c) 2010. Tamas Tevesz <ice@extreme.hu>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include <sys/types.h>
32 #include "wmmenugen.h"
34 static void addWMMenuEntryCallback(WMMenuEntry
*aEntry
);
35 static void assemblePLMenuFunc(WMTreeNode
*aNode
, void *data
);
36 static int dirParseFunc(const char *filename
, const struct stat
*st
, int tflags
, struct FTW
*ftw
);
37 static int menuSortFunc(const void *left
, const void *right
);
38 static int nodeFindSubMenuByNameFunc(const void *item
, const void *cdata
);
39 static WMTreeNode
*findPositionInMenu(const char *submenu
);
40 static void (*parse
)(const char *file
, void (*addWMMenuEntryCallback
)(WMMenuEntry
*aEntry
));
41 static Bool (*validateFilename
)(const char *filename
, const struct stat
*st
, int tflags
, struct FTW
*ftw
);
43 static WMArray
*plMenuNodes
;
46 extern char *__progname
;
48 /* Global Variables from wmmenugen.h */
50 char *env_lang
, *env_ctry
, *env_enc
, *env_mod
;
52 int main(int argc
, char **argv
)
58 plMenuNodes
= WMCreateArray(8); /* grows on demand */
59 menu
= (WMTreeNode
*)NULL
;
61 validateFilename
= NULL
;
63 /* assemblePLMenuFunc passes this around */
64 previousDepth
= (int *)wmalloc(sizeof(int));
67 /* currently this is used only by the xdg parser, but it might be useful
68 * in the future localizing other menus, so it won't hurt to have it here.
70 parse_locale(NULL
, &env_lang
, &env_ctry
, &env_enc
, &env_mod
);
71 terminal
= find_terminal_emulator();
74 fprintf(stderr
, "Usage: %s -parser:<parser> fspec [fpsec...] "
75 "[-parser:<parser> fspec [fpsec...]...]\n", __progname
);
76 fputs( "Known parsers: xdg wmconfig\n", stderr
);
80 for (i
= 1; i
< argc
; i
++)
82 if (strncmp(argv
[i
], "-parser:", 8) == 0) {
83 if (strcmp(argv
[i
] + 8, "xdg") == 0) {
85 fputs("Using parser \"xdg\"\n", stderr
);
88 } else if (strcmp(argv
[i
] + 8, "wmconfig") == 0) {
90 fputs("Using parser \"wmconfig\"\n", stderr
);
92 parse
= &parse_wmconfig
;
93 validateFilename
= &wmconfig_validate_file
;
95 fprintf(stderr
, "%s: Unknown parser \"%s\"\n", __progname
, argv
[i
] + 8);
101 if (stat(argv
[i
], &st
) == -1) {
102 fprintf(stderr
, "%s: unable to stat \"%s\"\n", __progname
, argv
[i
]);
103 } else if (S_ISREG(st
.st_mode
)) {
104 parse(argv
[i
], addWMMenuEntryCallback
);
105 } else if (S_ISDIR(st
.st_mode
)) {
106 nftw(argv
[i
], dirParseFunc
, 16, FTW_PHYS
);
108 fprintf(stderr
, "%s: \"%s\" is not a file or directory\n", __progname
, argv
[i
]);
111 fprintf(stderr
, "%s: argument \"%s\" with no valid parser\n", __progname
, argv
[i
]);
116 fprintf(stderr
, "%s: parsers failed to create a valid menu\n", __progname
);
120 WMSortTree(menu
, menuSortFunc
);
121 WMTreeWalk(menu
, assemblePLMenuFunc
, previousDepth
, True
);
123 i
= WMGetArrayItemCount(plMenuNodes
);
124 if (i
> 2) { /* more than one submenu unprocessed is almost certainly an error */
125 fprintf(stderr
, "%s: unprocessed levels on the stack. fishy.\n", __progname
);
127 } else if (i
> 1 ) { /* possibly the top-level attachment is not yet done */
128 WMPropList
*first
, *next
;
129 next
= WMPopFromArray(plMenuNodes
);
130 first
= WMPopFromArray(plMenuNodes
);
131 WMAddToPLArray(first
, next
);
132 WMAddToArray(plMenuNodes
, first
);
135 printf("%s\n", WMGetPropListDescription((WMPropList
*)WMGetFromArray(plMenuNodes
, 0), True
));
140 static int dirParseFunc(const char *filename
, const struct stat
*st
, int tflags
, struct FTW
*ftw
)
146 if (validateFilename
&&
147 !validateFilename(filename
, st
, tflags
, ftw
))
150 parse(filename
, addWMMenuEntryCallback
);
154 /* upon fully deducing one particular menu entry, parsers call back to this
155 * function to have said menu entry added to the wm menu. initializes wm menu
156 * with a root element if needed.
158 static void addWMMenuEntryCallback(WMMenuEntry
*aEntry
)
163 wm
= (WMMenuEntry
*)wmalloc(sizeof(WMMenuEntry
)); /* this entry */
164 at
= (WMTreeNode
*)NULL
; /* will be a child of this entry */
169 root
= (WMMenuEntry
*)wmalloc(sizeof(WMMenuEntry
));
170 root
->Name
= "Applications";
171 root
->CmdLine
= NULL
;
172 root
->SubMenu
= NULL
;
174 menu
= WMCreateTreeNode(root
);
178 at
= findPositionInMenu(wstrdup(aEntry
->SubMenu
));
183 wm
->Flags
= aEntry
->Flags
;
184 wm
->Name
= wstrdup(aEntry
->Name
);
185 wm
->CmdLine
= wstrdup(aEntry
->CmdLine
);
187 WMAddItemToTree(at
, wm
);
191 /* creates the proplist menu out of the abstract menu representation in `menu'.
193 static void assemblePLMenuFunc(WMTreeNode
*aNode
, void *data
)
199 wm
= (WMMenuEntry
*)WMGetDataForTreeNode(aNode
);
200 cDepth
= WMGetTreeNodeDepth(aNode
);
201 pDepth
= *(int *)data
;
203 if (pDepth
> cDepth
) { /* just ascended out of a/several submenu(s) */
204 WMPropList
*last
, *but
; /* merge the tail up to the current position */
206 for (i
= pDepth
- cDepth
; i
> 0; i
--) {
207 last
= WMPopFromArray(plMenuNodes
);
208 but
= WMPopFromArray(plMenuNodes
);
209 WMAddToPLArray(but
, last
);
210 WMAddToArray(plMenuNodes
, but
);
214 if (!wm
->CmdLine
) { /* new submenu */
215 WMAddToArray(plMenuNodes
, WMCreatePLArray(WMCreatePLString(wm
->Name
), NULL
));
216 } else { /* new menu item */
217 pl
= WMPopFromArray(plMenuNodes
);
218 if (wm
->Flags
& F_RESTART_OTHER
) { /* RESTART, somewm */
220 memset(buf
, 0, sizeof(buf
));
221 snprintf(buf
, sizeof(buf
), "%s %s", _("Restart"), wm
->Name
);
222 WMAddToPLArray(pl
, WMCreatePLArray(
223 WMCreatePLString(buf
),
224 WMCreatePLString("RESTART"),
225 WMCreatePLString(wm
->CmdLine
),
228 } else if (wm
->Flags
& F_RESTART_SELF
) {/* RESTART */
229 WMAddToPLArray(pl
, WMCreatePLArray(
230 WMCreatePLString(_("Restart Window Maker")),
231 WMCreatePLString("RESTART"),
234 } else if (wm
->Flags
& F_QUIT
) { /* EXIT */
235 WMAddToPLArray(pl
, WMCreatePLArray(
236 WMCreatePLString(_("Exit Window Maker")),
237 WMCreatePLString("EXIT"),
240 } else { /* plain simple command */
242 memset(buf
, 0, sizeof(buf
));
243 if (wm
->Flags
& F_TERMINAL
) /* XXX: quoting! */
244 snprintf(buf
, sizeof(buf
), "%s -e \"%s\"", terminal
, wm
->CmdLine
);
246 snprintf(buf
, sizeof(buf
), "%s", wm
->CmdLine
);
247 WMAddToPLArray(pl
, WMCreatePLArray(
248 WMCreatePLString(wm
->Name
),
249 WMCreatePLString("SHEXEC"),
250 WMCreatePLString(buf
),
254 WMAddToArray(plMenuNodes
, pl
);
257 *(int *)data
= cDepth
;
261 /* sort the menu tree; callback for WMSortTree()
263 static int menuSortFunc(const void *left
, const void *right
)
266 WMMenuEntry
*rightwm
;
268 leftwm
= (WMMenuEntry
*)WMGetDataForTreeNode(*(WMTreeNode
**)left
);
269 rightwm
= (WMMenuEntry
*)WMGetDataForTreeNode(*(WMTreeNode
**)right
);
272 if (!leftwm
->CmdLine
&& rightwm
->CmdLine
)
274 if (leftwm
->CmdLine
&& !rightwm
->CmdLine
)
277 /* the rest lexicographically */
278 return strcasecmp(leftwm
->Name
, rightwm
->Name
);
282 /* returns the leaf an entry with the submenu spec `submenu' attaches to.
283 * creates `submenu' path (anchored to the root) along the way.
285 static WMTreeNode
*findPositionInMenu(const char *submenu
)
289 WMTreeNode
*node
, *pnode
;
292 /* qualify submenu with "Applications/" (the root node) */
293 memset(buf
, 0, sizeof(buf
));
294 snprintf(buf
, sizeof(buf
), "Applications/%s", submenu
);
296 /* start at the root */
299 q
= strtok(buf
, "/");
302 node
= WMFindInTreeWithDepthLimit(pnode
, nodeFindSubMenuByNameFunc
, q
, 1);
304 wm
= (WMMenuEntry
*)wmalloc(sizeof(WMMenuEntry
));
306 wm
->Name
= wstrdup(q
);
309 node
= WMAddNodeToTree(pnode
, WMCreateTreeNode(wm
));
311 q
= strtok(NULL
, "/");
317 /* find node where Name = cdata and node is a submenu
319 static int nodeFindSubMenuByNameFunc(const void *item
, const void *cdata
)
323 wm
= (WMMenuEntry
*)item
;
325 if (wm
->CmdLine
) /* if it has a cmdline, it can't be a submenu */
328 return strcmp(wm
->Name
, (const char *)cdata
) == 0;