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