1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 obt/xml.c for the Openbox window manager
4 Copyright (c) 2003-2007 Dana Jansens
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.
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.
16 See the COPYING file for a copy of the GNU General Public License.
20 #include "obt/paths.h"
22 #include <libxml/xinclude.h>
28 #ifdef HAVE_SYS_STAT_H
29 # include <sys/stat.h>
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
47 GHashTable
*callbacks
;
53 static void destfunc(struct Callback
*c
)
56 g_slice_free(struct Callback
, c
);
59 ObtXmlInst
* obt_xml_instance_new(void)
61 ObtXmlInst
*i
= g_slice_new(ObtXmlInst
);
63 i
->xdg_paths
= obt_paths_new();
64 i
->callbacks
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
65 (GDestroyNotify
)destfunc
);
72 void obt_xml_instance_ref(ObtXmlInst
*i
)
77 void obt_xml_instance_unref(ObtXmlInst
*i
)
79 if (i
&& --i
->ref
== 0) {
80 obt_paths_unref(i
->xdg_paths
);
81 g_hash_table_destroy(i
->callbacks
);
82 g_slice_free(ObtXmlInst
, i
);
86 xmlDocPtr
obt_xml_doc(ObtXmlInst
*i
)
88 g_assert(i
->doc
); /* a doc is open? */
92 xmlNodePtr
obt_xml_root(ObtXmlInst
*i
)
94 g_assert(i
->doc
); /* a doc is open? */
98 void obt_xml_register(ObtXmlInst
*i
, const gchar
*tag
,
99 ObtXmlCallback func
, gpointer data
)
103 if (g_hash_table_lookup(i
->callbacks
, tag
)) {
104 g_error("Tag '%s' already registered", tag
);
108 c
= g_slice_new(struct Callback
);
109 c
->tag
= g_strdup(tag
);
112 g_hash_table_insert(i
->callbacks
, c
->tag
, c
);
115 static gboolean
load_file(ObtXmlInst
*i
,
117 const gchar
*filename
,
118 const gchar
*root_node
,
124 g_assert(i
->doc
== NULL
); /* another doc isn't open already? */
126 for (it
= paths
; !r
&& it
; it
= g_slist_next(it
)) {
130 if (!domain
&& !filename
) /* given a full path to the file */
131 path
= g_strdup(it
->data
);
133 path
= g_build_filename(it
->data
, domain
, filename
, NULL
);
135 if (stat(path
, &s
) >= 0) {
136 /* XML_PARSE_BLANKS is needed apparently, or the tree can end up
137 with extra nodes in it. */
138 i
->doc
= xmlReadFile(path
, NULL
, (XML_PARSE_NOBLANKS
|
140 xmlXIncludeProcessFlags(i
->doc
, (XML_PARSE_NOBLANKS
|
143 i
->root
= xmlDocGetRootElement(i
->doc
);
147 g_message("%s is an empty XML document", path
);
149 else if (xmlStrcmp(i
->root
->name
,
150 (const xmlChar
*)root_node
)) {
154 g_message("XML document %s is of wrong type. Root "
155 "node is not '%s'", path
, root_node
);
158 i
->path
= g_strdup(path
);
170 gboolean
obt_xml_load_file(ObtXmlInst
*i
,
172 const gchar
*root_node
)
177 paths
= g_slist_append(NULL
, g_strdup(path
));
179 r
= load_file(i
, NULL
, NULL
, root_node
, paths
);
183 paths
= g_slist_delete_link(paths
, paths
);
188 gboolean
obt_xml_load_config_file(ObtXmlInst
*i
,
190 const gchar
*filename
,
191 const gchar
*root_node
)
193 GSList
*it
, *paths
= NULL
;
196 for (it
= obt_paths_config_dirs(i
->xdg_paths
); it
; it
= g_slist_next(it
))
197 paths
= g_slist_append(paths
, g_strdup(it
->data
));
199 r
= load_file(i
, domain
, filename
, root_node
, paths
);
203 paths
= g_slist_delete_link(paths
, paths
);
208 gboolean
obt_xml_load_data_file(ObtXmlInst
*i
,
210 const gchar
*filename
,
211 const gchar
*root_node
)
213 GSList
*it
, *paths
= NULL
;
216 for (it
= obt_paths_data_dirs(i
->xdg_paths
); it
; it
= g_slist_next(it
))
217 paths
= g_slist_append(paths
, g_strdup(it
->data
));
219 r
= load_file(i
, domain
, filename
, root_node
, paths
);
223 paths
= g_slist_delete_link(paths
, paths
);
228 gboolean
obt_xml_load_theme_file(ObtXmlInst
*i
,
231 const gchar
*filename
,
232 const gchar
*root_node
)
234 GSList
*it
, *paths
= NULL
;
237 /* use ~/.themes for backwards compatibility */
238 paths
= g_slist_append
239 (paths
, g_build_filename(g_get_home_dir(), ".themes", theme
, NULL
));
241 for (it
= obt_paths_data_dirs(i
->xdg_paths
); it
; it
= g_slist_next(it
))
242 paths
= g_slist_append
243 (paths
, g_build_filename(it
->data
, "themes", theme
, NULL
));
245 r
= load_file(i
, domain
, filename
, root_node
, paths
);
249 paths
= g_slist_delete_link(paths
, paths
);
255 gboolean
obt_xml_load_mem(ObtXmlInst
*i
,
256 gpointer data
, guint len
, const gchar
*root_node
)
260 g_assert(i
->doc
== NULL
); /* another doc isn't open already? */
262 i
->doc
= xmlParseMemory(data
, len
);
264 i
->root
= xmlDocGetRootElement(i
->doc
);
268 g_message("Given memory is an empty document");
270 else if (xmlStrcmp(i
->root
->name
, (const xmlChar
*)root_node
)) {
274 g_message("XML Document in given memory is of wrong "
275 "type. Root node is not '%s'\n", root_node
);
283 gboolean
obt_xml_save_file(ObtXmlInst
*inst
,
287 return xmlSaveFormatFile(path
, inst
->doc
, pretty
) != -1;
290 void obt_xml_close(ObtXmlInst
*i
)
301 void obt_xml_tree(ObtXmlInst
*i
, xmlNodePtr node
)
303 g_assert(i
->doc
); /* a doc is open? */
307 struct Callback
*c
= g_hash_table_lookup(i
->callbacks
, node
->name
);
308 if (c
) c
->func(node
, c
->data
);
314 void obt_xml_tree_from_root(ObtXmlInst
*i
)
316 obt_xml_tree(i
, i
->root
->children
);
319 gchar
*obt_xml_node_string(xmlNodePtr node
)
321 xmlChar
*c
= xmlNodeGetContent(node
);
323 if (c
) g_strstrip((char*)c
); /* strip leading/trailing whitespace */
324 s
= g_strdup(c
? (gchar
*)c
: "");
329 gint
obt_xml_node_int(xmlNodePtr node
)
331 xmlChar
*c
= xmlNodeGetContent(node
);
333 if (c
) g_strstrip((char*)c
); /* strip leading/trailing whitespace */
334 i
= c
? atoi((gchar
*)c
) : 0;
339 gboolean
obt_xml_node_bool(xmlNodePtr node
)
341 xmlChar
*c
= xmlNodeGetContent(node
);
343 if (c
) g_strstrip((char*)c
); /* strip leading/trailing whitespace */
344 if (c
&& !xmlStrcasecmp(c
, (const xmlChar
*) "true"))
346 else if (c
&& !xmlStrcasecmp(c
, (const xmlChar
*) "yes"))
348 else if (c
&& !xmlStrcasecmp(c
, (const xmlChar
*) "on"))
354 gboolean
obt_xml_node_contains(xmlNodePtr node
, const gchar
*val
)
356 xmlChar
*c
= xmlNodeGetContent(node
);
358 if (c
) g_strstrip((char*)c
); /* strip leading/trailing whitespace */
359 r
= !xmlStrcasecmp(c
, (const xmlChar
*) val
);
364 xmlNodePtr
obt_xml_find_node(xmlNodePtr node
, const gchar
*tag
)
367 if (!xmlStrcmp(node
->name
, (const xmlChar
*) tag
))
374 gboolean
obt_xml_attr_bool(xmlNodePtr node
, const gchar
*name
,
377 xmlChar
*c
= xmlGetProp(node
, (const xmlChar
*) name
);
380 g_strstrip((char*)c
); /* strip leading/trailing whitespace */
381 if (!xmlStrcasecmp(c
, (const xmlChar
*) "true"))
382 *value
= TRUE
, r
= TRUE
;
383 else if (!xmlStrcasecmp(c
, (const xmlChar
*) "yes"))
384 *value
= TRUE
, r
= TRUE
;
385 else if (!xmlStrcasecmp(c
, (const xmlChar
*) "on"))
386 *value
= TRUE
, r
= TRUE
;
387 else if (!xmlStrcasecmp(c
, (const xmlChar
*) "false"))
388 *value
= FALSE
, r
= TRUE
;
389 else if (!xmlStrcasecmp(c
, (const xmlChar
*) "no"))
390 *value
= FALSE
, r
= TRUE
;
391 else if (!xmlStrcasecmp(c
, (const xmlChar
*) "off"))
392 *value
= FALSE
, r
= TRUE
;
398 gboolean
obt_xml_attr_int(xmlNodePtr node
, const gchar
*name
, gint
*value
)
400 xmlChar
*c
= xmlGetProp(node
, (const xmlChar
*) name
);
403 g_strstrip((char*)c
); /* strip leading/trailing whitespace */
404 *value
= atoi((gchar
*)c
);
411 gboolean
obt_xml_attr_string(xmlNodePtr node
, const gchar
*name
,
414 xmlChar
*c
= xmlGetProp(node
, (const xmlChar
*) name
);
417 g_strstrip((char*)c
); /* strip leading/trailing whitespace */
418 *value
= g_strdup((gchar
*)c
);
425 gboolean
obt_xml_attr_contains(xmlNodePtr node
, const gchar
*name
,
428 xmlChar
*c
= xmlGetProp(node
, (const xmlChar
*) name
);
431 g_strstrip((char*)c
); /* strip leading/trailing whitespace */
432 r
= !xmlStrcasecmp(c
, (const xmlChar
*) val
);