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
, char *line
);
48 static void init_wmconfig_storage(WMConfigMenuEntry
**wmc
);
50 void parse_wmconfig(const char *file
, void (*addWMMenuEntryCallback
)(WMMenuEntry
*aEntry
))
54 char *p
, *lastlabel
, *label
, *key
, *value
;
55 WMConfigMenuEntry
*wmc
;
58 lastlabel
= label
= key
= value
= NULL
;
60 fp
= fopen(file
, "r");
63 fprintf(stderr
, "Error opening file %s: %s\n", file
, strerror(errno
));
68 wmc
= (WMConfigMenuEntry
*)wmalloc(sizeof(WMConfigMenuEntry
));
75 wm
= (WMMenuEntry
*)wmalloc(sizeof(WMMenuEntry
));
77 memset(buf
, 0, sizeof(buf
));
79 while (fgets(buf
, sizeof(buf
), fp
)) {
83 /* skip whitespaces */
86 /* skip comments, empty lines */
87 if (*p
== '\r' || *p
== '\n' || *p
== '#') {
88 memset(buf
, 0, sizeof(buf
));
92 buf
[strcspn(buf
, "\r\n")] = '\0';
96 parse_wmconfig_line(&label
, &key
, &value
, p
);
98 if (label
&& strlen(label
) == 0)
100 if (!lastlabel
&& label
)
101 lastlabel
= wstrdup(label
);
103 if (strcmp(lastlabel
, label
) != 0) {
104 if (wmc_to_wm(&wmc
, &wm
)) {
105 (*addWMMenuEntryCallback
)(wm
);
106 init_wmconfig_storage(&wmc
);
110 lastlabel
= wstrdup(label
);
114 if (strcmp(key
, "name") == 0)
116 else if (strcmp(key
, "exec") == 0)
118 else if (strcmp(key
, "group") == 0)
119 wmc
->Category
= value
;
120 else if (strcmp(key
, "restart") == 0)
121 wmc
->Restart
= value
;
122 else if (strcmp(key
, "terminal") == 0)
123 wmc
->Flags
|= F_TERMINAL
;
130 if (wmc_to_wm(&wmc
, &wm
)) {
131 (*addWMMenuEntryCallback
)(wm
);
132 init_wmconfig_storage(&wmc
);
136 /* an example to illustrate validateFilename.
137 * with wmconfig, no special handling is needed
139 Bool
wmconfig_validate_file(const char *filename
, const struct stat
*st
, int tflags
, struct FTW
*ftw
)
147 #if 0 /* not dead code, example */
149 /* or we could have gone intro extremes */
155 base_name
= wstrdup(filename
+ ftw
->base
);
158 if (!S_ISREG(st
->st_mode
) || /* not a regular file */
159 (st
->st_uid
!= 0 && st
->st_uid
!= getuid()) || /* bad guy injected this file */
160 strpbrk(base_name
, ".") || /* wmconfig typically has no extension */
161 st
->st_size
>= 128 * 131072 || /* noone writes wmconfig files > 128K */
162 st
->st_size
== 0 || /* nor empty ones */
163 ftw
->level
> 16) /* how did we get this deep? */
172 /* get a line allocating label, key and value as necessary */
173 static void parse_wmconfig_line(char **label
, char **key
, char **value
, char *line
)
179 *label
= *key
= *value
= NULL
;
182 /* skip whitespace */
183 while (isspace(*(p
+ kstart
)))
187 /* find end of label */
188 while (*(p
+ kend
) && !isspace(*(p
+ kend
)))
192 *label
= wstrndup(p
+ kstart
, kend
- kstart
);
195 /* skip whitespace */
196 while (isspace(*(p
+ kstart
)))
200 /* find end of key */
201 while (*(p
+ kend
) && !isspace(*(p
+ kend
)))
205 *key
= wstrndup(p
+ kstart
, kend
- kstart
);
208 /* skip until after " */
209 while (*(p
+ kstart
) && *(p
+ kstart
) != '"')
215 while (*(p
+ kend
) && *(p
+ kend
) != '"')
219 *value
= wstrndup(p
+ kstart
, kend
- kstart
);
222 /* normalize and convert one wmconfig-format entry to wm format */
223 static Bool
wmc_to_wm(WMConfigMenuEntry
**wmc
, WMMenuEntry
**wm
)
228 /* only Exec is mandatory, and it's better exist in a known place */
229 if (!((*wmc
)->Exec
&&
231 fileInPath((*wmc
)->Exec
)))
234 /* normalize Exec: wmconfig tends to stick an ampersand
235 * at the end of everything, which we don't need */
236 slen
= strlen((*wmc
)->Exec
) - 1;
238 while (slen
> 0 && (isspace(*(p
+ slen
)) || *(p
+ slen
) == '&'))
239 *(p
+ slen
--) = '\0';
241 /* if there's no Name, use the first word of Exec; still better
242 * than nothing. i realize it's highly arguable whether `xterm' from
243 * `xterm -e "ssh dev push-to-prod"' is helpful or not, but since
244 * the alternative is to completely lose the entry, i opt for this.
245 * you could just fix the descriptor file to have a label <G> */
247 (*wmc
)->Name
= wstrdup((*wmc
)->Exec
);
248 p
= strchr((*wmc
)->Name
, ' ');
253 /* if there's no Category, use "Applications"; apparently "no category"
254 * can manifest both as no `group' descriptor at all, or a group
255 * descriptor of "" */
256 if (!(*wmc
)->Category
|| !*(*wmc
)->Category
)
257 (*wmc
)->Category
= wstrdup("Applications");
259 /* the `restart' type is used for restart, restart other
260 * wm and quit current wm too. separate these cases. */
261 if ((*wmc
)->Restart
) {
262 if (strcmp((*wmc
)->Restart
, "restart") == 0)
263 (*wmc
)->Flags
|= F_RESTART_SELF
;
264 else if (strcmp((*wmc
)->Restart
, "quit") == 0)
265 (*wmc
)->Flags
|= F_QUIT
;
267 (*wmc
)->Flags
|= F_RESTART_OTHER
;
270 (*wm
)->Name
= (*wmc
)->Name
;
271 (*wm
)->CmdLine
= (*wmc
)->Exec
;
272 (*wm
)->SubMenu
= (*wmc
)->Category
;
273 (*wm
)->Flags
= (*wmc
)->Flags
;
278 static void init_wmconfig_storage(WMConfigMenuEntry
**wmc
)
280 if ((*wmc
)->Category
)
281 wfree((*wmc
)->Category
);
282 (*wmc
)->Category
= NULL
;
287 wfree((*wmc
)->Restart
);
288 (*wmc
)->Restart
= NULL
;