]> Dogcows Code - chaz/openbox/blob - parser/parse.c
avoid duplicates in the path lists
[chaz/openbox] / 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 gchar *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 gchar *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 gchar *path, const gchar *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 gchar *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 gchar *parse_string(xmlDocPtr doc, xmlNodePtr node)
180 {
181 xmlChar *c = xmlNodeListGetString(doc, node->children, TRUE);
182 gchar *s = g_strdup(c ? (gchar*)c : "");
183 xmlFree(c);
184 return s;
185 }
186
187 gint parse_int(xmlDocPtr doc, xmlNodePtr node)
188 {
189 xmlChar *c = xmlNodeListGetString(doc, node->children, TRUE);
190 gint i = atoi((gchar*)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 gchar *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 gchar *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 gchar *name, xmlNodePtr node, gint *value)
229 {
230 xmlChar *c = xmlGetProp(node, (const xmlChar*) name);
231 gboolean r = FALSE;
232 if (c) {
233 *value = atoi((gchar*)c);
234 r = TRUE;
235 }
236 xmlFree(c);
237 return r;
238 }
239
240 gboolean parse_attr_string(const gchar *name, xmlNodePtr node, gchar **value)
241 {
242 xmlChar *c = xmlGetProp(node, (const xmlChar*) name);
243 gboolean r = FALSE;
244 if (c) {
245 *value = g_strdup((gchar*)c);
246 r = TRUE;
247 }
248 xmlFree(c);
249 return r;
250 }
251
252 gboolean parse_attr_contains(const gchar *val, xmlNodePtr node,
253 const gchar *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 gint slist_path_cmp(const gchar *a, const gchar *b)
263 {
264 return strcmp(a, b);
265 }
266
267 typedef GSList* (*GSListFunc) (gpointer list, gconstpointer data);
268
269 static GSList* slist_path_add(GSList *list, gpointer data, GSListFunc func)
270 {
271 g_assert(func);
272
273 if (!data)
274 return list;
275
276 if (!g_slist_find_custom(list, data, (GCompareFunc) slist_path_cmp))
277 list = func(list, data);
278
279 return list;
280 }
281
282 static GSList* split_paths(const gchar *paths)
283 {
284 GSList *list = NULL;
285 gchar **spl, **it;
286
287 if (!paths)
288 return NULL;
289 spl = g_strsplit(paths, ":", -1);
290 for (it = spl; *it; ++it)
291 list = slist_path_add(list, *it, (GSListFunc) g_slist_append);
292 g_free(spl);
293 return list;
294 }
295
296 void parse_paths_startup()
297 {
298 gchar *path;
299
300 if (xdg_start)
301 return;
302 xdg_start = TRUE;
303
304 path = getenv("XDG_CONFIG_HOME");
305 if (path && path[0] != '\0') /* not unset or empty */
306 xdg_config_home_path = g_build_filename(path, NULL);
307 else
308 xdg_config_home_path = g_build_filename(g_get_home_dir(), ".config",
309 NULL);
310
311 path = getenv("XDG_DATA_HOME");
312 if (path && path[0] != '\0') /* not unset or empty */
313 xdg_data_home_path = g_build_filename(path, NULL);
314 else
315 xdg_data_home_path = g_build_filename(g_get_home_dir(), ".local",
316 "share", NULL);
317
318 path = getenv("XDG_CONFIG_DIRS");
319 if (path && path[0] != '\0') /* not unset or empty */
320 xdg_config_dir_paths = split_paths(path);
321 else {
322 xdg_config_dir_paths = slist_path_add(xdg_config_dir_paths,
323 g_build_filename
324 (G_DIR_SEPARATOR_S,
325 "etc", "xdg", NULL),
326 (GSListFunc) g_slist_append);
327 xdg_config_dir_paths = slist_path_add(xdg_config_dir_paths,
328 g_strdup(CONFIGDIR),
329 (GSListFunc) g_slist_append);
330 }
331 xdg_config_dir_paths = slist_path_add(xdg_config_dir_paths,
332 xdg_config_home_path,
333 (GSListFunc) g_slist_prepend);
334
335 path = getenv("XDG_DATA_DIRS");
336 if (path && path[0] != '\0') /* not unset or empty */
337 xdg_data_dir_paths = split_paths(path);
338 else {
339 xdg_data_dir_paths = slist_path_add(xdg_data_dir_paths,
340 g_build_filename
341 (G_DIR_SEPARATOR_S,
342 "usr", "local", "share", NULL),
343 (GSListFunc) g_slist_append);
344 xdg_data_dir_paths = slist_path_add(xdg_data_dir_paths,
345 g_build_filename
346 (G_DIR_SEPARATOR_S,
347 "usr", "share", NULL),
348 (GSListFunc) g_slist_append);
349 xdg_data_dir_paths = slist_path_add(xdg_data_dir_paths,
350 g_strdup(DATADIR),
351 (GSListFunc) g_slist_append);
352 }
353 xdg_data_dir_paths = slist_path_add(xdg_data_dir_paths,
354 xdg_data_home_path,
355 (GSListFunc) g_slist_prepend);
356 }
357
358 void parse_paths_shutdown()
359 {
360 GSList *it;
361
362 if (!xdg_start)
363 return;
364 xdg_start = FALSE;
365
366 for (it = xdg_config_dir_paths; it; it = g_slist_next(it))
367 g_free(it->data);
368 g_slist_free(xdg_config_dir_paths);
369 xdg_config_dir_paths = NULL;
370 for (it = xdg_data_dir_paths; it; it = g_slist_next(it))
371 g_free(it->data);
372 g_slist_free(xdg_data_dir_paths);
373 xdg_data_dir_paths = NULL;
374 }
375
376 gchar *parse_expand_tilde(const gchar *f)
377 {
378 gchar **spl;
379 gchar *ret;
380
381 if (!f)
382 return NULL;
383 spl = g_strsplit(f, "~", 0);
384 ret = g_strjoinv(g_get_home_dir(), spl);
385 g_strfreev(spl);
386 return ret;
387 }
388
389 void parse_mkdir_path(const gchar *path, gint mode)
390 {
391 gchar *c, *e;
392
393 g_assert(path[0] == '/');
394
395 c = g_strdup(path);
396 e = c;
397 while ((e = strchr(e + 1, '/'))) {
398 *e = '\0';
399 mkdir(c, mode);
400 *e = '/';
401 }
402 mkdir(c, mode);
403 g_free(c);
404 }
405
406 const gchar* parse_xdg_config_home_path()
407 {
408 return xdg_config_home_path;
409 }
410
411 const gchar* parse_xdg_data_home_path()
412 {
413 return xdg_data_home_path;
414 }
415
416 GSList* parse_xdg_config_dir_paths()
417 {
418 return xdg_config_dir_paths;
419 }
420
421 GSList* parse_xdg_data_dir_paths()
422 {
423 return xdg_data_dir_paths;
424 }
This page took 0.058519 seconds and 5 git commands to generate.