util: Added a few missing 'static' attributes for functions
[wmaker-crm.git] / util / wmmenugen_parse_wmconfig.c
blob988e69deb1601ebbd11f3010cf38e0b31dd9b2b9
1 /*
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>
24 #include <sys/stat.h>
26 #include <ctype.h>
27 #include <ftw.h>
28 #if DEBUG
29 #include <errno.h>
30 #endif
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
36 #include "wmmenugen.h"
38 typedef struct {
39 char *Name;
40 char *Exec;
41 char *Category;
42 char *Restart;
43 int Flags;
44 } WMConfigMenuEntry;
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);
50 void parse_wmconfig(const char *file, void (*addWMMenuEntryCallback)(WMMenuEntry *aEntry))
52 FILE *fp;
53 char buf[1024];
54 char *p, *lastlabel, *label, *key, *value;
55 WMConfigMenuEntry *wmc;
56 WMMenuEntry *wm;
58 lastlabel = label = key = value = NULL;
60 fp = fopen(file, "r");
61 if (!fp) {
62 #if DEBUG
63 fprintf(stderr, "Error opening file %s: %s\n", file, strerror(errno));
64 #endif
65 return;
68 wmc = (WMConfigMenuEntry *)wmalloc(sizeof(WMConfigMenuEntry));
69 wmc->Name = NULL;
70 wmc->Exec = NULL;
71 wmc->Category = NULL;
72 wmc->Restart = NULL;
73 wmc->Flags = 0;
75 wm = (WMMenuEntry *)wmalloc(sizeof(WMMenuEntry));
77 memset(buf, 0, sizeof(buf));
79 while (fgets(buf, sizeof(buf), fp)) {
81 p = buf;
83 /* skip whitespaces */
84 while (isspace(*p))
85 p++;
86 /* skip comments, empty lines */
87 if (*p == '\r' || *p == '\n' || *p == '#') {
88 memset(buf, 0, sizeof(buf));
89 continue;
91 /* trim crlf */
92 buf[strcspn(buf, "\r\n")] = '\0';
93 if (strlen(buf) == 0)
94 continue;
96 parse_wmconfig_line(&label, &key, &value, p);
98 if (label && strlen(label) == 0)
99 continue;
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);
109 wfree(lastlabel);
110 lastlabel = wstrdup(label);
113 if (key && value) {
114 if (strcmp(key, "name") == 0)
115 wmc->Name = value;
116 else if (strcmp(key, "exec") == 0)
117 wmc->Exec = value;
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;
128 fclose(fp);
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)
141 (void)filename;
142 (void)st;
143 (void)tflags;
144 (void)ftw;
146 return True;
147 #if 0 /* not dead code, example */
149 /* or we could have gone intro extremes */
150 char *base_name;
151 Bool ret;
153 (void)tflags;
155 base_name = wstrdup(filename + ftw->base);
156 ret = True;
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? */
164 ret = False;
166 wfree(base_name);
168 return ret;
169 #endif
172 /* get a line allocating label, key and value as necessary */
173 static void parse_wmconfig_line(char **label, char **key, char **value, const char *line)
175 const char *p;
176 int kstart, kend;
178 p = line;
179 *label = *key = *value = NULL;
180 kstart = kend = 0;
182 /* skip whitespace */
183 while (isspace(*(p + kstart)))
184 kstart++;
186 kend = kstart;
187 /* find end of label */
188 while (*(p + kend) && !isspace(*(p + kend)))
189 kend++;
191 /* label */
192 *label = wstrndup(p + kstart, kend - kstart);
193 kstart = kend + 1;
195 /* skip whitespace */
196 while (isspace(*(p + kstart)))
197 kstart++;
199 kend = kstart;
200 /* find end of key */
201 while (*(p + kend) && !isspace(*(p + kend)))
202 kend++;
204 /* key */
205 *key = wstrndup(p + kstart, kend - kstart);
206 kstart = kend + 1;
208 /* skip until after " */
209 while (*(p + kstart) && *(p + kstart) != '"')
210 kstart++;
211 kstart++;
213 kend = kstart;
214 /* skip until " */
215 while (*(p + kend) && *(p + kend) != '"')
216 kend++;
218 /* value */
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)
225 char *p;
226 size_t slen;
228 /* only Exec is mandatory, and it's better exist in a known place */
229 if (!((*wmc)->Exec &&
230 *(*wmc)->Exec &&
231 fileInPath((*wmc)->Exec)))
232 return False;
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;
237 p = (*wmc)->Exec;
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> */
246 if (!(*wmc)->Name) {
247 (*wmc)->Name = wstrdup((*wmc)->Exec);
248 p = strchr((*wmc)->Name, ' ');
249 if (p)
250 *p = '\0';
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;
266 else
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;
275 return True;
278 static void init_wmconfig_storage(WMConfigMenuEntry **wmc)
280 if ((*wmc)->Category)
281 wfree((*wmc)->Category);
282 (*wmc)->Category = NULL;
283 if ((*wmc)->Name)
284 wfree((*wmc)->Name);
285 (*wmc)->Name = NULL;
286 if ((*wmc)->Restart)
287 wfree((*wmc)->Restart);
288 (*wmc)->Restart = NULL;
289 (*wmc)->Flags = 0;