no tabs
[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     xmlFree(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     g_message("paths %s", paths);
271     while ((e = strchr(e + 1, ':'))) {
272         *e = '\0';
273         g_message("s %s", s);
274         if (s[0] != '\0')
275             list = g_slist_append(list, g_strdup(s));
276         s = e + 1;
277     }
278     if (s[0] != '\0')
279         list = g_slist_append(list, g_strdup(s));
280     g_free(c);
281     return list;
282 }
283
284 void parse_paths_startup()
285 {
286     gchar *path;
287
288     if (xdg_start)
289         return;
290     xdg_start = TRUE;
291
292     path = getenv("XDG_CONFIG_HOME");
293     if (path && path[0] != '\0') /* not unset or empty */
294         xdg_config_home_path = g_build_filename(path, NULL);
295     else
296         xdg_config_home_path = g_build_filename(g_get_home_dir(), ".config",
297                                                 NULL);
298
299     path = getenv("XDG_DATA_HOME");
300     if (path && path[0] != '\0') /* not unset or empty */
301         xdg_data_home_path = g_build_filename(path, NULL);
302     else
303         xdg_data_home_path = g_build_filename(g_get_home_dir(), ".local",
304                                               "share", NULL);
305
306     path = getenv("XDG_CONFIG_DIRS");
307     if (path && path[0] != '\0') /* not unset or empty */
308         xdg_config_dir_paths = split_paths(path);
309     else {
310         xdg_config_dir_paths = g_slist_append(xdg_config_dir_paths,
311                                               g_build_filename
312                                               (G_DIR_SEPARATOR_S,
313                                                "etc", "xdg", NULL));
314         xdg_config_dir_paths = g_slist_append(xdg_config_dir_paths,
315                                               g_strdup(CONFIGDIR));
316     }
317     xdg_config_dir_paths = g_slist_prepend(xdg_config_dir_paths,
318                                            xdg_config_home_path);
319     
320     path = getenv("XDG_DATA_DIRS");
321     if (path && path[0] != '\0') /* not unset or empty */
322         xdg_data_dir_paths = split_paths(path);
323     else {
324         xdg_data_dir_paths = g_slist_append(xdg_data_dir_paths,
325                                             g_build_filename
326                                             (G_DIR_SEPARATOR_S,
327                                              "usr", "local", "share", NULL));
328         xdg_data_dir_paths = g_slist_append(xdg_data_dir_paths,
329                                             g_build_filename
330                                             (G_DIR_SEPARATOR_S,
331                                              "usr", "share", NULL));
332         xdg_config_dir_paths = g_slist_append(xdg_config_dir_paths,
333                                               g_strdup(DATADIR));
334     }
335     xdg_data_dir_paths = g_slist_prepend(xdg_data_dir_paths,
336                                          xdg_data_home_path);
337 }
338
339 void parse_paths_shutdown()
340 {
341     GSList *it;
342
343     if (!xdg_start)
344         return;
345     xdg_start = FALSE;
346
347     for (it = xdg_config_dir_paths; it; it = g_slist_next(it))
348         g_free(it->data);
349     g_slist_free(xdg_config_dir_paths);
350     xdg_config_dir_paths = NULL;
351 }
352
353 gchar *parse_expand_tilde(const gchar *f)
354 {
355     gchar **spl;
356     gchar *ret;
357
358     if (!f)
359         return NULL;
360     spl = g_strsplit(f, "~", 0);
361     ret = g_strjoinv(g_get_home_dir(), spl);
362     g_strfreev(spl);
363     return ret;
364 }
365
366 void parse_mkdir_path(const gchar *path, gint mode)
367 {
368     gchar *c, *e;
369
370     g_assert(path[0] == '/');
371
372     c = g_strdup(path);
373     e = c;
374     while ((e = strchr(e + 1, '/'))) {
375         *e = '\0';
376         mkdir(c, mode);
377         *e = '/';
378     }
379     mkdir(c, mode);
380     g_free(c);
381 }
382
383 const gchar* parse_xdg_config_home_path()
384 {
385     return xdg_config_home_path;
386 }
387
388 const gchar* parse_xdg_data_home_path()
389 {
390     return xdg_data_home_path;
391 }
392
393 GSList* parse_xdg_config_dir_paths()
394 {
395     return xdg_config_dir_paths;
396 }
397
398 GSList* parse_xdg_data_dir_paths()
399 {
400     return xdg_data_dir_paths;
401 }