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
;
51 gchar
*last_error_file
;
53 gchar
*last_error_message
;
56 static void obt_xml_save_last_error(ObtXmlInst
* inst
);
58 static void destfunc(struct Callback
*c
)
61 g_slice_free(struct Callback
, c
);
64 ObtXmlInst
* obt_xml_instance_new(void)
66 ObtXmlInst
*i
= g_slice_new(ObtXmlInst
);
68 i
->xdg_paths
= obt_paths_new();
69 i
->callbacks
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
70 (GDestroyNotify
)destfunc
);
74 i
->last_error_file
= NULL
;
75 i
->last_error_line
= -1;
76 i
->last_error_message
= NULL
;
80 void obt_xml_instance_ref(ObtXmlInst
*i
)
85 void obt_xml_instance_unref(ObtXmlInst
*i
)
87 if (i
&& --i
->ref
== 0) {
88 obt_paths_unref(i
->xdg_paths
);
89 g_hash_table_destroy(i
->callbacks
);
90 g_free(i
->last_error_file
);
91 g_free(i
->last_error_message
);
92 g_slice_free(ObtXmlInst
, i
);
96 xmlDocPtr
obt_xml_doc(ObtXmlInst
*i
)
98 g_assert(i
->doc
); /* a doc is open? */
102 xmlNodePtr
obt_xml_root(ObtXmlInst
*i
)
104 g_assert(i
->doc
); /* a doc is open? */
108 void obt_xml_register(ObtXmlInst
*i
, const gchar
*tag
,
109 ObtXmlCallback func
, gpointer data
)
113 if (g_hash_table_lookup(i
->callbacks
, tag
)) {
114 g_error("Tag '%s' already registered", tag
);
118 c
= g_slice_new(struct Callback
);
119 c
->tag
= g_strdup(tag
);
122 g_hash_table_insert(i
->callbacks
, c
->tag
, c
);
125 void obt_xml_unregister(ObtXmlInst
*i
, const gchar
*tag
)
127 g_hash_table_remove(i
->callbacks
, tag
);
130 static gboolean
load_file(ObtXmlInst
*i
,
132 const gchar
*filename
,
133 const gchar
*root_node
,
139 g_assert(i
->doc
== NULL
); /* another doc isn't open already? */
143 for (it
= paths
; !r
&& it
; it
= g_slist_next(it
)) {
147 if (!domain
&& !filename
) /* given a full path to the file */
148 path
= g_strdup(it
->data
);
150 path
= g_build_filename(it
->data
, domain
, filename
, NULL
);
152 if (stat(path
, &s
) >= 0) {
153 /* XML_PARSE_BLANKS is needed apparently, or the tree can end up
154 with extra nodes in it. */
155 i
->doc
= xmlReadFile(path
, NULL
, (XML_PARSE_NOBLANKS
|
157 xmlXIncludeProcessFlags(i
->doc
, (XML_PARSE_NOBLANKS
|
160 i
->root
= xmlDocGetRootElement(i
->doc
);
164 g_message("%s is an empty XML document", path
);
166 else if (xmlStrcmp(i
->root
->name
,
167 (const xmlChar
*)root_node
)) {
171 g_message("XML document %s is of wrong type. Root "
172 "node is not '%s'", path
, root_node
);
175 i
->path
= g_strdup(path
);
184 obt_xml_save_last_error(i
);
189 gboolean
obt_xml_load_file(ObtXmlInst
*i
,
191 const gchar
*root_node
)
196 paths
= g_slist_append(NULL
, g_strdup(path
));
198 r
= load_file(i
, NULL
, NULL
, root_node
, paths
);
202 paths
= g_slist_delete_link(paths
, paths
);
207 gboolean
obt_xml_load_config_file(ObtXmlInst
*i
,
209 const gchar
*filename
,
210 const gchar
*root_node
)
212 GSList
*it
, *paths
= NULL
;
215 for (it
= obt_paths_config_dirs(i
->xdg_paths
); it
; it
= g_slist_next(it
))
216 paths
= g_slist_append(paths
, g_strdup(it
->data
));
218 r
= load_file(i
, domain
, filename
, root_node
, paths
);
222 paths
= g_slist_delete_link(paths
, paths
);
227 gboolean
obt_xml_load_data_file(ObtXmlInst
*i
,
229 const gchar
*filename
,
230 const gchar
*root_node
)
232 GSList
*it
, *paths
= NULL
;
235 for (it
= obt_paths_data_dirs(i
->xdg_paths
); it
; it
= g_slist_next(it
))
236 paths
= g_slist_append(paths
, g_strdup(it
->data
));
238 r
= load_file(i
, domain
, filename
, root_node
, paths
);
242 paths
= g_slist_delete_link(paths
, paths
);
247 gboolean
obt_xml_load_theme_file(ObtXmlInst
*i
,
250 const gchar
*filename
,
251 const gchar
*root_node
)
253 GSList
*it
, *paths
= NULL
;
256 /* use ~/.themes for backwards compatibility */
257 paths
= g_slist_append
258 (paths
, g_build_filename(g_get_home_dir(), ".themes", theme
, NULL
));
260 for (it
= obt_paths_data_dirs(i
->xdg_paths
); it
; it
= g_slist_next(it
))
261 paths
= g_slist_append
262 (paths
, g_build_filename(it
->data
, "themes", theme
, NULL
));
264 r
= load_file(i
, domain
, filename
, root_node
, paths
);
268 paths
= g_slist_delete_link(paths
, paths
);
274 gboolean
obt_xml_load_mem(ObtXmlInst
*i
,
275 gpointer data
, guint len
, const gchar
*root_node
)
279 g_assert(i
->doc
== NULL
); /* another doc isn't open already? */
283 i
->doc
= xmlParseMemory(data
, len
);
285 i
->root
= xmlDocGetRootElement(i
->doc
);
289 g_message("Given memory is an empty document");
291 else if (xmlStrcmp(i
->root
->name
, (const xmlChar
*)root_node
)) {
295 g_message("XML Document in given memory is of wrong "
296 "type. Root node is not '%s'\n", root_node
);
302 obt_xml_save_last_error(i
);
307 static void obt_xml_save_last_error(ObtXmlInst
* inst
)
309 xmlErrorPtr error
= xmlGetLastError();
311 inst
->last_error_file
= g_strdup(error
->file
);
312 inst
->last_error_line
= error
->line
;
313 inst
->last_error_message
= g_strdup(error
->message
);
314 xmlResetError(error
);
318 gboolean
obt_xml_last_error(ObtXmlInst
*inst
)
320 return inst
->last_error_file
&&
321 inst
->last_error_line
>= 0 &&
322 inst
->last_error_message
;
325 gchar
* obt_xml_last_error_file(ObtXmlInst
*inst
)
327 if (!obt_xml_last_error(inst
))
329 return inst
->last_error_file
;
332 gint
obt_xml_last_error_line(ObtXmlInst
*inst
)
334 if (!obt_xml_last_error(inst
))
336 return inst
->last_error_line
;
339 gchar
* obt_xml_last_error_message(ObtXmlInst
*inst
)
341 if (!obt_xml_last_error(inst
))
343 return inst
->last_error_message
;
346 gboolean
obt_xml_save_file(ObtXmlInst
*inst
,
350 return xmlSaveFormatFile(path
, inst
->doc
, pretty
) != -1;
353 void obt_xml_close(ObtXmlInst
*i
)
364 void obt_xml_tree(ObtXmlInst
*i
, xmlNodePtr node
)
366 g_assert(i
->doc
); /* a doc is open? */
370 struct Callback
*c
= g_hash_table_lookup(i
->callbacks
, node
->name
);
371 if (c
) c
->func(node
, c
->data
);
377 void obt_xml_tree_from_root(ObtXmlInst
*i
)
379 obt_xml_tree(i
, i
->root
->children
);
382 gchar
*obt_xml_node_string_unstripped(xmlNodePtr node
)
384 xmlChar
*c
= xmlNodeGetContent(node
);
386 s
= g_strdup(c
? (gchar
*)c
: "");
391 gchar
*obt_xml_node_string(xmlNodePtr node
)
393 gchar
* result
= obt_xml_node_string_unstripped(node
);
394 g_strstrip(result
); /* strip leading/trailing whitespace */
398 gint
obt_xml_node_int(xmlNodePtr node
)
400 xmlChar
*c
= xmlNodeGetContent(node
);
402 if (c
) g_strstrip((char*)c
); /* strip leading/trailing whitespace */
403 i
= c
? atoi((gchar
*)c
) : 0;
408 gboolean
obt_xml_node_bool(xmlNodePtr node
)
410 xmlChar
*c
= xmlNodeGetContent(node
);
412 if (c
) g_strstrip((char*)c
); /* strip leading/trailing whitespace */
413 if (c
&& !xmlStrcasecmp(c
, (const xmlChar
*) "true"))
415 else if (c
&& !xmlStrcasecmp(c
, (const xmlChar
*) "yes"))
417 else if (c
&& !xmlStrcasecmp(c
, (const xmlChar
*) "on"))
423 gboolean
obt_xml_node_contains(xmlNodePtr node
, const gchar
*val
)
425 xmlChar
*c
= xmlNodeGetContent(node
);
427 if (c
) g_strstrip((char*)c
); /* strip leading/trailing whitespace */
428 r
= !xmlStrcasecmp(c
, (const xmlChar
*) val
);
433 xmlNodePtr
obt_xml_find_node(xmlNodePtr node
, const gchar
*tag
)
436 if (!xmlStrcmp(node
->name
, (const xmlChar
*) tag
))
443 gboolean
obt_xml_attr_bool(xmlNodePtr node
, const gchar
*name
,
446 xmlChar
*c
= xmlGetProp(node
, (const xmlChar
*) name
);
449 g_strstrip((char*)c
); /* strip leading/trailing whitespace */
450 if (!xmlStrcasecmp(c
, (const xmlChar
*) "true"))
451 *value
= TRUE
, r
= TRUE
;
452 else if (!xmlStrcasecmp(c
, (const xmlChar
*) "yes"))
453 *value
= TRUE
, r
= TRUE
;
454 else if (!xmlStrcasecmp(c
, (const xmlChar
*) "on"))
455 *value
= TRUE
, r
= TRUE
;
456 else if (!xmlStrcasecmp(c
, (const xmlChar
*) "false"))
457 *value
= FALSE
, r
= TRUE
;
458 else if (!xmlStrcasecmp(c
, (const xmlChar
*) "no"))
459 *value
= FALSE
, r
= TRUE
;
460 else if (!xmlStrcasecmp(c
, (const xmlChar
*) "off"))
461 *value
= FALSE
, r
= TRUE
;
467 gboolean
obt_xml_attr_int(xmlNodePtr node
, const gchar
*name
, gint
*value
)
469 xmlChar
*c
= xmlGetProp(node
, (const xmlChar
*) name
);
472 g_strstrip((char*)c
); /* strip leading/trailing whitespace */
473 *value
= atoi((gchar
*)c
);
480 gboolean
obt_xml_attr_string_unstripped(xmlNodePtr node
, const gchar
*name
,
483 xmlChar
*c
= xmlGetProp(node
, (const xmlChar
*) name
);
486 *value
= g_strdup((gchar
*)c
);
493 gboolean
obt_xml_attr_string(xmlNodePtr node
, const gchar
*name
,
496 gboolean result
= obt_xml_attr_string_unstripped(node
, name
, value
);
498 g_strstrip(*value
); /* strip leading/trailing whitespace */
502 gboolean
obt_xml_attr_contains(xmlNodePtr node
, const gchar
*name
,
505 xmlChar
*c
= xmlGetProp(node
, (const xmlChar
*) name
);
508 g_strstrip((char*)c
); /* strip leading/trailing whitespace */
509 r
= !xmlStrcasecmp(c
, (const xmlChar
*) val
);