Skip to content

Commit 32e27ba

Browse files
committed
Add xml_add_node() and ensure nodes exist before getting/setting
1 parent ee53abf commit 32e27ba

File tree

3 files changed

+129
-1
lines changed

3 files changed

+129
-1
lines changed

main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,12 @@ main(int argc, char **argv)
281281
}
282282
xml_init(filename);
283283

284+
/* ensure all relevant nodes exist before we start getting/setting */
285+
xml_add_node("/labwc_config/theme/cornerradius");
286+
xml_add_node("/labwc_config/theme/name");
287+
xml_add_node("/labwc_config/libinput/device/naturalscroll");
288+
xml_save();
289+
284290
/* load themes */
285291
find_themes(&openbox_themes, "themes", "openbox-3/themerc");
286292
find_themes(&gtk_themes, "themes", "gtk-3.0/gtk.css");

xml.c

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
#define _POSIX_C_SOURCE 200809L
2+
#include <assert.h>
23
#include <ctype.h>
4+
#include <glib.h>
35
#include <libxml/parser.h>
46
#include <libxml/tree.h>
7+
#include <libxml/xpath.h>
8+
#include <libxml/xpathInternals.h>
59
#include <stdbool.h>
610
#include <stdio.h>
11+
#include <stdlib.h>
712
#include <string.h>
813
#include <strings.h>
14+
#include <unistd.h>
915

1016
static struct ctx {
1117
char *filename;
1218
xmlDoc *doc;
19+
xmlXPathContextPtr xpath_ctx_ptr;
1320
char *nodename;
1421
char *value;
1522
enum {
@@ -140,7 +147,12 @@ xml_init(const char *filename)
140147
ctx.filename = strdup(filename);
141148
ctx.doc = xmlReadFile(filename, NULL, 0);
142149
if (!ctx.doc) {
143-
fprintf(stderr, "Failed to parse %s\n", filename);
150+
fprintf(stderr, "warn: xmlReadFile('%s')\n", filename);
151+
}
152+
ctx.xpath_ctx_ptr = xmlXPathNewContext(ctx.doc);
153+
if (!ctx.xpath_ctx_ptr) {
154+
fprintf(stderr, "warn: xmlXPathNewContext()\n");
155+
xmlFreeDoc(ctx.doc);
144156
}
145157
}
146158

@@ -153,6 +165,7 @@ xml_save(void)
153165
void
154166
xml_finish(void)
155167
{
168+
xmlXPathFreeContext(ctx.xpath_ctx_ptr);
156169
xmlFreeDoc(ctx.doc);
157170
xmlCleanupParser();
158171
}
@@ -212,3 +225,98 @@ xml_get_bool_text(char *nodename)
212225
return -1;
213226
}
214227
}
228+
229+
char *
230+
xml_get_content(char *xpath_expr)
231+
{
232+
xmlChar *ret = NULL;
233+
xmlXPathObjectPtr object = xmlXPathEvalExpression((xmlChar *)xpath_expr, ctx.xpath_ctx_ptr);
234+
if (!object) {
235+
fprintf(stderr, "warn: xmlXPathEvalExpression()\n");
236+
return NULL;
237+
}
238+
if (!object->nodesetval) {
239+
fprintf(stderr, "warn: no nodesetval\n");
240+
goto out;
241+
}
242+
for (int i = 0; i < object->nodesetval->nodeNr; i++) {
243+
if (!object->nodesetval->nodeTab[i]) {
244+
continue;
245+
}
246+
247+
/* Just grab the first node and go */
248+
ret = xmlNodeGetContent(object->nodesetval->nodeTab[i]);
249+
goto out;
250+
251+
/*
252+
* We could process the node here and do things like:
253+
* xmlNode *children = object->nodesetval->nodeTab[i]->children;
254+
* for (xmlNode *cur = children; cur; cur = cur->next) { }
255+
*/
256+
}
257+
258+
out:
259+
xmlXPathFreeObject(object);
260+
return (char *)ret;
261+
}
262+
263+
static xmlNode *
264+
get_node(xmlChar *expr)
265+
{
266+
xmlNode *ret = NULL;
267+
xmlXPathObjectPtr object = xmlXPathEvalExpression(expr, ctx.xpath_ctx_ptr);
268+
if (!object) {
269+
fprintf(stderr, "warn: xmlXPathEvalExpression()\n");
270+
return NULL;
271+
}
272+
if (!object->nodesetval) {
273+
fprintf(stderr, "warn: no nodesetval\n");
274+
goto out2;
275+
}
276+
277+
for (int i = 0; i < object->nodesetval->nodeNr; i++) {
278+
if (!object->nodesetval->nodeTab[i]) {
279+
continue;
280+
}
281+
ret = object->nodesetval->nodeTab[i];
282+
break;
283+
}
284+
out2:
285+
xmlXPathFreeObject(object);
286+
return ret;
287+
}
288+
289+
void
290+
xml_add_node(char *xpath_expr)
291+
{
292+
/* find existing parent */
293+
char *parent_expr = strdup(xpath_expr);
294+
xmlNode *parent_node = NULL;
295+
while (parent_expr && *parent_expr) {
296+
parent_node = get_node((xmlChar *)parent_expr);
297+
if (parent_node) {
298+
break;
299+
}
300+
char *p = strrchr(parent_expr, '/');
301+
if (p && *p) {
302+
*p = '\0';
303+
} else {
304+
break;
305+
}
306+
}
307+
assert(parent_expr);
308+
if (!*parent_expr) {
309+
/* the whole xpath expression is new, so add to root */
310+
parent_node = xmlDocGetRootElement(ctx.doc);
311+
}
312+
313+
/* add new nodes */
314+
gchar **nodes = g_strsplit(xpath_expr + strlen(parent_expr), "/", -1);
315+
for (gchar **s = nodes; *s; s++) {
316+
if (*s && **s) {
317+
parent_node = xmlNewChild(parent_node, NULL, (xmlChar *)*s, NULL);
318+
}
319+
}
320+
g_free(parent_expr);
321+
g_strfreev(nodes);
322+
}

xml.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,18 @@ char *xml_get(char *nodename);
1010
int xml_get_int(char *nodename);
1111
int xml_get_bool_text(char *nodename);
1212

13+
/**
14+
* xml_get_content - get content of node specified by xpath
15+
* @xpath_expr: xpath expression for node
16+
*/
17+
char *xml_get_content(char *xpath_expr);
18+
19+
/**
20+
* xml_add_node - add xml nodes from xpath
21+
* @xpath_expr: xpath expression for new node
22+
* For example xpath_expr="/labwc_config/a/b/c" creates
23+
* <labwc_config><a><b><c /></b></a></labwc_config>
24+
*/
25+
void xml_add_node(char *xpath_expr);
26+
1327
#endif /* __XML_H */

0 commit comments

Comments
 (0)