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>
33 #include "wmmenugen.h"
35 static void addWMMenuEntryCallback(WMMenuEntry
*aEntry
);
36 static void assemblePLMenuFunc(WMTreeNode
*aNode
, void *data
);
37 static int dirParseFunc(const char *filename
, const struct stat
*st
, int tflags
, struct FTW
*ftw
);
38 static int menuSortFunc(const void *left
, const void *right
);
39 static int nodeFindSubMenuByNameFunc(const void *item
, const void *cdata
);
40 static WMTreeNode
*findPositionInMenu(const char *submenu
);
43 typedef void fct_parse_menufile(const char *file
, cb_add_menu_entry
*addWMMenuEntryCallback
);
44 typedef Bool
fct_validate_filename(const char *filename
, const struct stat
*st
, int tflags
, struct FTW
*ftw
);
47 static WMArray
*plMenuNodes
;
48 static const char *terminal
;
49 static fct_parse_menufile
*parse
;
50 static fct_validate_filename
*validateFilename
;
52 static const char *prog_name
;
54 /* Global Variables from wmmenugen.h */
56 char *env_lang
, *env_ctry
, *env_enc
, *env_mod
;
58 static void print_help(void)
60 printf("Usage: %s -parser:<parser> fspec [fspec...]\n", prog_name
);
61 puts("Dynamically generate a menu in Property List format for Window Maker");
63 puts(" -h, --help\t\tdisplay this help and exit");
64 puts(" -parser=<name>\tspecify the format of the input, see below");
65 puts(" --version\t\toutput version information and exit");
67 puts("fspec: the file to be converted or the directory containing all the menu files");
69 puts("Known parsers:");
70 puts(" xdg\t\tDesktop Entry from FreeDesktop standard");
71 puts(" wmconfig\tfrom the menu generation tool by the same name");
75 static const char *get_parser_name(void)
77 if (parse
== &parse_xdg
)
79 if (parse
== &parse_wmconfig
)
82 /* This case is not supposed to happen, but if it does it means that someone to update this list */
87 int main(int argc
, char **argv
)
94 plMenuNodes
= WMCreateArray(8); /* grows on demand */
95 menu
= (WMTreeNode
*)NULL
;
97 validateFilename
= NULL
;
99 /* assemblePLMenuFunc passes this around */
100 previousDepth
= (int *)wmalloc(sizeof(int));
103 /* currently this is used only by the xdg parser, but it might be useful
104 * in the future localizing other menus, so it won't hurt to have it here.
106 parse_locale(NULL
, &env_lang
, &env_ctry
, &env_enc
, &env_mod
);
107 terminal
= find_terminal_emulator();
109 for (i
= 1; i
< argc
; i
++)
111 if (strncmp(argv
[i
], "-parser", 7) == 0 &&
112 (argv
[i
][7] == '=' ||
113 argv
[i
][7] == ':' || /* for legacy compatibility */
114 argv
[i
][7] == '\0')) {
117 if (argv
[i
][7] == '\0') {
119 fprintf(stderr
, "%s: Missing parser name after \"-parser\"\n", prog_name
);
127 if (strcmp(name
, "xdg") == 0) {
129 } else if (strcmp(name
, "wmconfig") == 0) {
130 parse
= &parse_wmconfig
;
131 validateFilename
= &wmconfig_validate_file
;
133 fprintf(stderr
, "%s: Unknown parser \"%s\"\n", prog_name
, name
);
139 if (strcmp(argv
[i
], "--version") == 0) {
140 printf("%s (Window Maker %s)\n", prog_name
, VERSION
);
144 if (strcmp(argv
[i
], "-h") == 0 ||
145 strcmp(argv
[i
], "-help") == 0 ||
146 strcmp(argv
[i
], "--help") == 0) {
152 fprintf(stderr
, "%s: argument \"%s\" with no valid parser\n", prog_name
, argv
[i
]);
157 fprintf(stderr
, "%s: Using parser \"%s\" to process \"%s\"\n",
158 prog_name
, get_parser_name(), argv
[i
]);
161 if (stat(argv
[i
], &st
) == -1) {
162 fprintf(stderr
, "%s: unable to stat \"%s\", %s\n",
163 prog_name
, argv
[i
], strerror(errno
));
165 } else if (S_ISREG(st
.st_mode
)) {
166 parse(argv
[i
], addWMMenuEntryCallback
);
167 } else if (S_ISDIR(st
.st_mode
)) {
168 nftw(argv
[i
], dirParseFunc
, 16, FTW_PHYS
);
170 fprintf(stderr
, "%s: \"%s\" is not a file or directory\n", prog_name
, argv
[i
]);
176 fprintf(stderr
, "%s: parsers failed to create a valid menu\n", prog_name
);
180 WMSortTree(menu
, menuSortFunc
);
181 WMTreeWalk(menu
, assemblePLMenuFunc
, previousDepth
, True
);
183 i
= WMGetArrayItemCount(plMenuNodes
);
184 if (i
> 2) { /* more than one submenu unprocessed is almost certainly an error */
185 fprintf(stderr
, "%s: unprocessed levels on the stack. fishy.\n", prog_name
);
187 } else if (i
> 1 ) { /* possibly the top-level attachment is not yet done */
188 WMPropList
*first
, *next
;
190 next
= WMPopFromArray(plMenuNodes
);
191 first
= WMPopFromArray(plMenuNodes
);
192 WMAddToPLArray(first
, next
);
193 WMAddToArray(plMenuNodes
, first
);
196 puts(WMGetPropListDescription((WMPropList
*)WMGetFromArray(plMenuNodes
, 0), True
));
201 static int dirParseFunc(const char *filename
, const struct stat
*st
, int tflags
, struct FTW
*ftw
)
207 if (validateFilename
&&
208 !validateFilename(filename
, st
, tflags
, ftw
))
211 parse(filename
, addWMMenuEntryCallback
);
215 /* upon fully deducing one particular menu entry, parsers call back to this
216 * function to have said menu entry added to the wm menu. initializes wm menu
217 * with a root element if needed.
219 static void addWMMenuEntryCallback(WMMenuEntry
*aEntry
)
224 wm
= (WMMenuEntry
*)wmalloc(sizeof(WMMenuEntry
)); /* this entry */
225 at
= (WMTreeNode
*)NULL
; /* will be a child of this entry */
230 root
= (WMMenuEntry
*)wmalloc(sizeof(WMMenuEntry
));
231 root
->Name
= "Applications";
232 root
->CmdLine
= NULL
;
233 root
->SubMenu
= NULL
;
235 menu
= WMCreateTreeNode(root
);
239 at
= findPositionInMenu(aEntry
->SubMenu
);
244 wm
->Flags
= aEntry
->Flags
;
245 wm
->Name
= wstrdup(aEntry
->Name
);
246 wm
->CmdLine
= wstrdup(aEntry
->CmdLine
);
248 WMAddItemToTree(at
, wm
);
252 /* creates the proplist menu out of the abstract menu representation in `menu'.
254 static void assemblePLMenuFunc(WMTreeNode
*aNode
, void *data
)
260 wm
= (WMMenuEntry
*)WMGetDataForTreeNode(aNode
);
261 cDepth
= WMGetTreeNodeDepth(aNode
);
262 pDepth
= *(int *)data
;
264 if (pDepth
> cDepth
) { /* just ascended out of a/several submenu(s) */
265 WMPropList
*last
, *but
; /* merge the tail up to the current position */
267 for (i
= pDepth
- cDepth
; i
> 0; i
--) {
268 last
= WMPopFromArray(plMenuNodes
);
269 but
= WMPopFromArray(plMenuNodes
);
270 WMAddToPLArray(but
, last
);
271 WMAddToArray(plMenuNodes
, but
);
275 if (!wm
->CmdLine
) { /* new submenu */
276 WMAddToArray(plMenuNodes
, WMCreatePLArray(WMCreatePLString(wm
->Name
), NULL
));
277 } else { /* new menu item */
278 WMPropList
*tmp1
, *tmp2
;
279 pl
= WMPopFromArray(plMenuNodes
);
280 if (wm
->Flags
& F_RESTART_OTHER
) { /* RESTART, somewm */
284 memset(buf
, 0, sizeof(buf
));
285 snprintf(buf
, sizeof(buf
), "%s %s", _("Restart"), wm
->Name
);
287 tmp1
= WMCreatePLString(buf
);
288 tmp2
= WMCreatePLString("RESTART");
289 tmp3
= WMCreatePLString(wm
->CmdLine
);
290 WMAddToPLArray(pl
, WMCreatePLArray(tmp1
, tmp2
, tmp3
, NULL
));
291 WMReleasePropList(tmp3
);
292 } else if (wm
->Flags
& F_RESTART_SELF
) {/* RESTART */
293 tmp1
= WMCreatePLString(_("Restart Window Maker"));
294 tmp2
= WMCreatePLString("RESTART");
295 WMAddToPLArray(pl
, WMCreatePLArray(tmp1
, tmp2
, NULL
));
296 } else if (wm
->Flags
& F_QUIT
) { /* EXIT */
297 tmp1
= WMCreatePLString(_("Exit Window Maker"));
298 tmp2
= WMCreatePLString("EXIT");
299 WMAddToPLArray(pl
, WMCreatePLArray(tmp1
, tmp2
, NULL
));
300 } else { /* plain simple command */
304 memset(buf
, 0, sizeof(buf
));
305 if (wm
->Flags
& F_TERMINAL
) /* XXX: quoting! */
306 snprintf(buf
, sizeof(buf
), "%s -e \"%s\"", terminal
, wm
->CmdLine
);
308 snprintf(buf
, sizeof(buf
), "%s", wm
->CmdLine
);
309 tmp1
= WMCreatePLString(wm
->Name
);
310 tmp2
= WMCreatePLString("SHEXEC");
311 tmp3
= WMCreatePLString(buf
);
312 WMAddToPLArray(pl
, WMCreatePLArray(tmp1
, tmp2
, tmp3
, NULL
));
313 WMReleasePropList(tmp3
);
315 WMReleasePropList(tmp1
);
316 WMReleasePropList(tmp2
);
318 WMAddToArray(plMenuNodes
, pl
);
321 *(int *)data
= cDepth
;
325 /* sort the menu tree; callback for WMSortTree()
327 static int menuSortFunc(const void *left
, const void *right
)
330 WMMenuEntry
*rightwm
;
332 leftwm
= (WMMenuEntry
*)WMGetDataForTreeNode(*(WMTreeNode
**)left
);
333 rightwm
= (WMMenuEntry
*)WMGetDataForTreeNode(*(WMTreeNode
**)right
);
336 if (!leftwm
->CmdLine
&& rightwm
->CmdLine
)
338 if (leftwm
->CmdLine
&& !rightwm
->CmdLine
)
341 /* the rest lexicographically */
342 return strcasecmp(leftwm
->Name
, rightwm
->Name
);
346 /* returns the leaf an entry with the submenu spec `submenu' attaches to.
347 * creates `submenu' path (anchored to the root) along the way.
349 static WMTreeNode
*findPositionInMenu(const char *submenu
)
353 WMTreeNode
*node
, *pnode
;
356 /* qualify submenu with "Applications/" (the root node) */
357 memset(buf
, 0, sizeof(buf
));
358 snprintf(buf
, sizeof(buf
), "Applications/%s", submenu
);
360 /* start at the root */
363 q
= strtok(buf
, "/");
366 node
= WMFindInTreeWithDepthLimit(pnode
, nodeFindSubMenuByNameFunc
, q
, 1);
368 wm
= (WMMenuEntry
*)wmalloc(sizeof(WMMenuEntry
));
370 wm
->Name
= wstrdup(q
);
373 node
= WMAddNodeToTree(pnode
, WMCreateTreeNode(wm
));
375 q
= strtok(NULL
, "/");
381 /* find node where Name = cdata and node is a submenu
383 static int nodeFindSubMenuByNameFunc(const void *item
, const void *cdata
)
387 wm
= (WMMenuEntry
*)item
;
389 if (wm
->CmdLine
) /* if it has a cmdline, it can't be a submenu */
392 return strcmp(wm
->Name
, (const char *)cdata
) == 0;