remove debug prints
[dana/openbox.git] / parser / parse.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    parse.c for the Openbox window manager
4    Copyright (c) 2003        Ben Jansens
5
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.
10
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.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "parse.h"
20 #include <glib.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24
25 static gboolean xdg_start;
26 static gchar   *xdg_config_home_path;
27 static gchar   *xdg_data_home_path;
28 static GSList  *xdg_config_dir_paths;
29 static GSList  *xdg_data_dir_paths;
30
31 struct Callback {
32     char *tag;
33     ParseCallback func;
34     void *data;
35 };
36
37 struct _ObParseInst {
38     GHashTable *callbacks;
39 };
40
41 static void destfunc(struct Callback *c)
42 {
43     g_free(c->tag);
44     g_free(c);
45 }
46
47 ObParseInst* parse_startup()
48 {
49     ObParseInst *i = g_new(ObParseInst, 1);
50     i->callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
51                                          (GDestroyNotify)destfunc);
52     return i;
53 }
54
55 void parse_shutdown(ObParseInst *i)
56 {
57     if (i) {
58         g_hash_table_destroy(i->callbacks);
59         g_free(i);
60     }
61 }
62
63 void parse_register(ObParseInst *i, const char *tag,
64                     ParseCallback func, void *data)
65 {
66     struct Callback *c;
67
68     if ((c = g_hash_table_lookup(i->callbacks, tag))) {
69         g_warning("tag '%s' already registered", tag);
70         return;
71     }
72
73     c = g_new(struct Callback, 1);
74     c->tag = g_strdup(tag);
75     c->func = func;
76     c->data = data;
77     g_hash_table_insert(i->callbacks, c->tag, c);
78 }
79
80 gboolean parse_load_rc(xmlDocPtr *doc, xmlNodePtr *root)
81 {
82     GSList *it;
83     gchar *path;
84     gboolean r = FALSE;
85
86     for (it = xdg_config_dir_paths; !r && it; it = g_slist_next(it)) {
87         path = g_build_filename(it->data, "openbox", "rc.xml", NULL);
88         r = parse_load(path, "openbox_config", doc, root);
89         g_free(path);
90     }
91     if (!r)
92         g_warning("unable to find a valid config file, using defaults");
93     return r;
94 }
95
96 gboolean parse_load_menu(const gchar *file, xmlDocPtr *doc, xmlNodePtr *root)
97 {
98     GSList *it;
99     gchar *path;
100     gboolean r = FALSE;
101
102     if (file[0] == '/') {
103         r = parse_load(file, "openbox_menu", doc, root);
104     } else {
105         for (it = xdg_config_dir_paths; !r && it; it = g_slist_next(it)) {
106             path = g_build_filename(it->data, "openbox", file, NULL);
107             r = parse_load(path, "openbox_menu", doc, root);
108             g_free(path);
109         }
110     }
111     if (!r)
112         g_warning("unable to find a valid menu file '%s'", file);
113     return r;
114 }
115
116 gboolean parse_load(const char *path, const char *rootname,
117                     xmlDocPtr *doc, xmlNodePtr *root)
118 {
119     if ((*doc = xmlParseFile(path))) {
120         *root = xmlDocGetRootElement(*doc);
121         if (!*root) {
122             xmlFreeDoc(*doc);
123             *doc = NULL;
124             g_warning("%s is an empty document", path);
125         } else {
126             if (xmlStrcasecmp((*root)->name, (const xmlChar*)rootname)) {
127                 xmlFreeDoc(*doc);
128                 *doc = NULL;
129                 g_warning("document %s is of wrong type. root node is "
130                           "not '%s'", path, rootname);
131             }
132         }
133     }
134     if (!*doc)
135         return FALSE;
136     return TRUE;
137 }
138
139 gboolean parse_load_mem(gpointer data, guint len, const char *rootname,
140                         xmlDocPtr *doc, xmlNodePtr *root)
141 {
142     if ((*doc = xmlParseMemory(data, len))) {
143         *root = xmlDocGetRootElement(*doc);
144         if (!*root) {
145             xmlFreeDoc(*doc);
146             *doc = NULL;
147             g_warning("Given memory is an empty document");
148         } else {
149             if (xmlStrcasecmp((*root)->name, (const xmlChar*)rootname)) {
150                 xmlFreeDoc(*doc);
151                 *doc = NULL;
152                 g_warning("document in given memory is of wrong type. root "
153                           "node is not '%s'", rootname);
154             }
155         }
156     }
157     if (!*doc)
158         return FALSE;
159     return TRUE;
160 }
161
162 void parse_close(xmlDocPtr doc)
163 {
164     xmlFreeDoc(doc);
165 }
166
167 void parse_tree(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node)
168 {
169     while (node) {
170         struct Callback *c = g_hash_table_lookup(i->callbacks, node->name);
171
172         if (c)
173             c->func(i, doc, node, c->data);
174
175         node = node->next;
176     }
177 }
178
179 char *parse_string(xmlDocPtr doc, xmlNodePtr node)
180 {
181     xmlChar *c = xmlNodeListGetString(doc, node->children, TRUE);
182     char *s = g_strdup(c ? (char*)c : "");
183     xmlFree(c);
184     return s;
185 }
186
187 int parse_int(xmlDocPtr doc, xmlNodePtr node)
188 {
189     xmlChar *c = xmlNodeListGetString(doc, node->children, TRUE);
190     int i = atoi((char*)c);
191     xmlFree(c);
192     return i;
193 }
194
195 gboolean parse_bool(xmlDocPtr doc, xmlNodePtr node)
196 {
197     xmlChar *c = xmlNodeListGetString(doc, node->children, TRUE);
198     gboolean b = FALSE;
199     if (!xmlStrcasecmp(c, (const xmlChar*) "true"))
200         b = TRUE;
201     else if (!xmlStrcasecmp(c, (const xmlChar*) "yes"))
202         b = TRUE;
203     else if (!xmlStrcasecmp(c, (const xmlChar*) "on"))
204         b = TRUE;
205     xmlFree(c);
206     return b;
207 }
208
209 gboolean parse_contains(const char *val, xmlDocPtr doc, xmlNodePtr node)
210 {
211     xmlChar *c = xmlNodeListGetString(doc, node->children, TRUE);
212     gboolean r;
213     r = !xmlStrcasecmp(c, (const xmlChar*) val);
214     xmlFree(c);
215     return r;
216 }
217
218 xmlNodePtr parse_find_node(const char *tag, xmlNodePtr node)
219 {
220     while (node) {
221         if (!xmlStrcasecmp(node->name, (const xmlChar*) tag))
222             return node;
223         node = node->next;
224     }
225     return NULL;
226 }
227
228 gboolean parse_attr_int(const char *name, xmlNodePtr node, int *value)
229 {
230     xmlChar *c = xmlGetProp(node, (const xmlChar*) name);
231     gboolean r = FALSE;
232     if (c) {
233         *value = atoi((char*)c);
234         r = TRUE;
235     }
236     xmlFree(c);
237     return r;
238 }
239
240 gboolean parse_attr_string(const char *name, xmlNodePtr node, char **value)
241 {
242     xmlChar *c = xmlGetProp(node, (const xmlChar*) name);
243     gboolean r = FALSE;
244     if (c) {
245         *value = g_strdup((char*)c);
246         r = TRUE;
247     }
248     xmlFree(c);
249     return r;
250 }
251
252 gboolean parse_attr_contains(const char *val, xmlNodePtr node,
253                              const char *name)
254 {
255     xmlChar *c = xmlGetProp(node, (const xmlChar*) name);
256     gboolean r;
257     r = !xmlStrcasecmp(c, (const xmlChar*) val);
258     xmlFree(c);
259     return r;
260 }
261
262 static GSList* split_paths(const gchar *paths)
263 {
264     GSList *list = NULL;
265     gchar *c, *e, *s;
266
267     c = g_strdup(paths);
268     s = c;
269     e = c - 1;
270     while ((e = strchr(e + 1, ':'))) {
271         *e = '\0';
272         if (s[0] != '\0')
273             list = g_slist_append(list, g_strdup(s));
274         s = e + 1;
275     }
276     if (s[0] != '\0')
277         list = g_slist_append(list, g_strdup(s));
278     g_free(c);
279     return list;
280 }
281
282 void parse_paths_startup()
283 {
284     gchar *path;
285
286     if (xdg_start)
287         return;
288     xdg_start = TRUE;
289
290     path = getenv("XDG_CONFIG_HOME");
291     if (path && path[0] != '\0') /* not unset or empty */
292         xdg_config_home_path = g_build_filename(path, NULL);
293     else
294         xdg_config_home_path = g_build_filename(g_get_home_dir(), ".config",
295                                                 NULL);
296
297     path = getenv("XDG_DATA_HOME");
298     if (path && path[0] != '\0') /* not unset or empty */
299         xdg_data_home_path = g_build_filename(path, NULL);
300     else
301         xdg_data_home_path = g_build_filename(g_get_home_dir(), ".local",
302                                               "share", NULL);
303
304     path = getenv("XDG_CONFIG_DIRS");
305     if (path && path[0] != '\0') /* not unset or empty */
306         xdg_config_dir_paths = split_paths(path);
307     else {
308         xdg_config_dir_paths = g_slist_append(xdg_config_dir_paths,
309                                               g_build_filename
310                                               (G_DIR_SEPARATOR_S,
311                                                "etc", "xdg", NULL));
312         xdg_config_dir_paths = g_slist_append(xdg_config_dir_paths,
313                                               g_strdup(CONFIGDIR));
314     }
315     xdg_config_dir_paths = g_slist_prepend(xdg_config_dir_paths,
316                                            xdg_config_home_path);
317     
318     path = getenv("XDG_DATA_DIRS");
319     if (path && path[0] != '\0') /* not unset or empty */
320         xdg_data_dir_paths = split_paths(path);
321     else {
322         xdg_data_dir_paths = g_slist_append(xdg_data_dir_paths,
323                                             g_build_filename
324                                             (G_DIR_SEPARATOR_S,
325                                              "usr", "local", "share", NULL));
326         xdg_data_dir_paths = g_slist_append(xdg_data_dir_paths,
327                                             g_build_filename
328                                             (G_DIR_SEPARATOR_S,
329                                              "usr", "share", NULL));
330         xdg_data_dir_paths = g_slist_append(xdg_data_dir_paths,
331                                             g_strdup(DATADIR));
332     }
333     xdg_data_dir_paths = g_slist_prepend(xdg_data_dir_paths,
334                                          xdg_data_home_path);
335 }
336
337 void parse_paths_shutdown()
338 {
339     GSList *it;
340
341     if (!xdg_start)
342         return;
343     xdg_start = FALSE;
344
345     for (it = xdg_config_dir_paths; it; it = g_slist_next(it))
346         g_free(it->data);
347     g_slist_free(xdg_config_dir_paths);
348     xdg_config_dir_paths = NULL;
349     for (it = xdg_data_dir_paths; it; it = g_slist_next(it))
350         g_free(it->data);
351     g_slist_free(xdg_data_dir_paths);
352     xdg_data_dir_paths = NULL;
353 }
354
355 gchar *parse_expand_tilde(const gchar *f)
356 {
357     gchar **spl;
358     gchar *ret;
359
360     if (!f)
361         return NULL;
362     spl = g_strsplit(f, "~", 0);
363     ret = g_strjoinv(g_get_home_dir(), spl);
364     g_strfreev(spl);
365     return ret;
366 }
367
368 void parse_mkdir_path(const gchar *path, gint mode)
369 {
370     gchar *c, *e;
371
372     g_assert(path[0] == '/');
373
374     c = g_strdup(path);
375     e = c;
376     while ((e = strchr(e + 1, '/'))) {
377         *e = '\0';
378         mkdir(c, mode);
379         *e = '/';
380     }
381     mkdir(c, mode);
382     g_free(c);
383 }
384
385 const gchar* parse_xdg_config_home_path()
386 {
387     return xdg_config_home_path;
388 }
389
390 const gchar* parse_xdg_data_home_path()
391 {
392     return xdg_data_home_path;
393 }
394
395 GSList* parse_xdg_config_dir_paths()
396 {
397     return xdg_config_dir_paths;
398 }
399
400 GSList* parse_xdg_data_dir_paths()
401 {
402     return xdg_data_dir_paths;
403 }