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