2 * wmmenugen - Window Maker PropList menu generator
4 * Wmconfig <http://www.arrishq.net/> parser functions
6 * Copyright (c) 2010. Tamas Tevesz <ice@extreme.hu>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <sys/types.h>
36 #include "wmmenugen.h"
46 static Bool
wmc_to_wm(WMConfigMenuEntry
**wmc
, WMMenuEntry
**wm
);
47 static void parse_wmconfig_line(char **label
, char **key
, char **value
, const char *line
);
48 static void init_wmconfig_storage(WMConfigMenuEntry
**wmc
);
51 void parse_wmconfig(const char *file
, cb_add_menu_entry
*addWMMenuEntryCallback
)
55 char *p
, *lastlabel
, *label
, *key
, *value
;
56 WMConfigMenuEntry
*wmc
;
59 lastlabel
= label
= key
= value
= NULL
;
61 fp
= fopen(file
, "r");
64 fprintf(stderr
, "Error opening file %s: %s\n", file
, strerror(errno
));
69 wmc
= (WMConfigMenuEntry
*)wmalloc(sizeof(WMConfigMenuEntry
));
76 wm
= (WMMenuEntry
*)wmalloc(sizeof(WMMenuEntry
));
78 memset(buf
, 0, sizeof(buf
));
80 while (fgets(buf
, sizeof(buf
), fp
)) {
84 /* skip whitespaces */
87 /* skip comments, empty lines */
88 if (*p
== '\r' || *p
== '\n' || *p
== '#') {
89 memset(buf
, 0, sizeof(buf
));
93 buf
[strcspn(buf
, "\r\n")] = '\0';
97 parse_wmconfig_line(&label
, &key
, &value
, p
);
99 if (label
&& strlen(label
) == 0)
101 if (!lastlabel
&& label
)
102 lastlabel
= wstrdup(label
);
104 if (strcmp(lastlabel
, label
) != 0) {
105 if (wmc_to_wm(&wmc
, &wm
)) {
106 (*addWMMenuEntryCallback
)(wm
);
107 init_wmconfig_storage(&wmc
);
111 lastlabel
= wstrdup(label
);
115 if (strcmp(key
, "name") == 0)
117 else if (strcmp(key
, "exec") == 0)
119 else if (strcmp(key
, "group") == 0)
120 wmc
->Category
= value
;
121 else if (strcmp(key
, "restart") == 0)
122 wmc
->Restart
= value
;
123 else if (strcmp(key
, "terminal") == 0)
124 wmc
->Flags
|= F_TERMINAL
;
131 if (wmc_to_wm(&wmc
, &wm
)) {
132 (*addWMMenuEntryCallback
)(wm
);
133 init_wmconfig_storage(&wmc
);
137 /* an example to illustrate validateFilename.
138 * with wmconfig, no special handling is needed
140 Bool
wmconfig_validate_file(const char *filename
, const struct stat
*st
, int tflags
, struct FTW
*ftw
)
148 #if 0 /* not dead code, example */
150 /* or we could have gone intro extremes */
156 base_name
= wstrdup(filename
+ ftw
->base
);
159 if (!S_ISREG(st
->st_mode
) || /* not a regular file */
160 (st
->st_uid
!= 0 && st
->st_uid
!= getuid()) || /* bad guy injected this file */
161 strpbrk(base_name
, ".") || /* wmconfig typically has no extension */
162 st
->st_size
>= 128 * 131072 || /* noone writes wmconfig files > 128K */
163 st
->st_size
== 0 || /* nor empty ones */
164 ftw
->level
> 16) /* how did we get this deep? */
173 /* get a line allocating label, key and value as necessary */
174 static void parse_wmconfig_line(char **label
, char **key
, char **value
, const char *line
)
180 *label
= *key
= *value
= NULL
;
183 /* skip whitespace */
184 while (isspace(*(p
+ kstart
)))
188 /* find end of label */
189 while (*(p
+ kend
) && !isspace(*(p
+ kend
)))
193 *label
= wstrndup(p
+ kstart
, kend
- kstart
);
196 /* skip whitespace */
197 while (isspace(*(p
+ kstart
)))
201 /* find end of key */
202 while (*(p
+ kend
) && !isspace(*(p
+ kend
)))
206 *key
= wstrndup(p
+ kstart
, kend
- kstart
);
209 /* skip until after " */
210 while (*(p
+ kstart
) && *(p
+ kstart
) != '"')
216 while (*(p
+ kend
) && *(p
+ kend
) != '"')
220 *value
= wstrndup(p
+ kstart
, kend
- kstart
);
223 /* normalize and convert one wmconfig-format entry to wm format */
224 static Bool
wmc_to_wm(WMConfigMenuEntry
**wmc
, WMMenuEntry
**wm
)
229 /* only Exec is mandatory, and it's better exist in a known place */
230 if (!((*wmc
)->Exec
&&
232 fileInPath((*wmc
)->Exec
)))
235 /* normalize Exec: wmconfig tends to stick an ampersand
236 * at the end of everything, which we don't need */
237 slen
= strlen((*wmc
)->Exec
) - 1;
239 while (slen
> 0 && (isspace(*(p
+ slen
)) || *(p
+ slen
) == '&'))
240 *(p
+ slen
--) = '\0';
242 /* if there's no Name, use the first word of Exec; still better
243 * than nothing. i realize it's highly arguable whether `xterm' from
244 * `xterm -e "ssh dev push-to-prod"' is helpful or not, but since
245 * the alternative is to completely lose the entry, i opt for this.
246 * you could just fix the descriptor file to have a label <G> */
248 (*wmc
)->Name
= wstrdup((*wmc
)->Exec
);
249 p
= strchr((*wmc
)->Name
, ' ');
254 /* if there's no Category, use "Applications"; apparently "no category"
255 * can manifest both as no `group' descriptor at all, or a group
256 * descriptor of "" */
257 if (!(*wmc
)->Category
|| !*(*wmc
)->Category
)
258 (*wmc
)->Category
= wstrdup("Applications");
260 /* the `restart' type is used for restart, restart other
261 * wm and quit current wm too. separate these cases. */
262 if ((*wmc
)->Restart
) {
263 if (strcmp((*wmc
)->Restart
, "restart") == 0)
264 (*wmc
)->Flags
|= F_RESTART_SELF
;
265 else if (strcmp((*wmc
)->Restart
, "quit") == 0)
266 (*wmc
)->Flags
|= F_QUIT
;
268 (*wmc
)->Flags
|= F_RESTART_OTHER
;
271 (*wm
)->Name
= (*wmc
)->Name
;
272 (*wm
)->CmdLine
= (*wmc
)->Exec
;
273 (*wm
)->SubMenu
= (*wmc
)->Category
;
274 (*wm
)->Flags
= (*wmc
)->Flags
;
279 static void init_wmconfig_storage(WMConfigMenuEntry
**wmc
)
281 if ((*wmc
)->Category
)
282 wfree((*wmc
)->Category
);
283 (*wmc
)->Category
= NULL
;
288 wfree((*wmc
)->Restart
);
289 (*wmc
)->Restart
= NULL
;