shithub: xmltools

Download patch

ref: dd05e41b9a71be3441f36d9b944274da3cd64f35
parent: f896677cdcd52890b3bc215d655c7bea323a4755
author: sirjofri <sirjofri@sirjofri.de>
date: Thu Jul 11 11:33:36 EDT 2024

uses libxpath

--- a/README
+++ b/README
@@ -1,6 +1,9 @@
 XML tools
 
-This package requires libxml from 9atom
+This package requires libxml from 9atom and libxpath.
+See also:
+- https://shithub.us/sirjofri/xml-9atom/HEAD/info.html
+- https://git.sr.ht/~sirjofri/xml-9atom
 
 
 XQ: xml query
@@ -10,7 +13,9 @@
 
 path is an XPath (but not everything is supported).
 
-Supported XPath features:
+Xq currently can handle some queries itself (without libxpath). To make use of these, you have to adjust the xq.c file to call 'query' instead of 'query2'. This is a leftover that will eventually be removed.
+
+Limited XPath features (without libxpath):
 
 - @attr: /hello/world/@attr
 - text(): /hello/world/text()
--- a/test/xq.rc
+++ b/test/xq.rc
@@ -13,20 +13,6 @@
 nl='
 '
 
-cat <<EOF >/tmp/test.xml
-<?xml?>
-<hello hattr="hval">
-	<world wattr="wval" wattr2="bla">
-		Free text
-		<stuff sattr="sval"/>
-	</world>
-	<world wattr="wval2">
-		Another free text
-		<stuff sattr="sval2"/>
-	</world>
-</hello>
-EOF
-
 fn testxq{
 	# hack to print test cases more correct
 	p=`{echo $"1 | sed 's/''''/''/g'}
@@ -34,14 +20,19 @@
 	n=`{echo $"1 | sed 's/''/''''/g'}
 	c=`{echo $"cmd ''''^$"n^''' >/tmp/out >[2]/tmp/err'}
 	eval $"c
+	if (~ $2 '')
+		echo -n '' >/tmp/want
+	if not
+		echo $"2 >/tmp/want
 	if (~ $#extended 1) {
-		echo $nl^'expect:' $"p $nl^$"2
-		cat /tmp/out /tmp/err
+		echo $nl^'expect:' $"p
+		cat /tmp/want
+		echo got:
+		cat /tmp/out
+		echo err:
+		cat /tmp/err
 	}
-	r=`{cat /tmp/out}
-	if (~ $#r 0)
-		r=`{cat /tmp/err}
-	if (~ $"2 $"r) {
+	if (cmp -s /tmp/want /tmp/out) {
 		if (~ $#extended 1)
 			echo '→ success' $"p
 	}
@@ -50,22 +41,58 @@
 	}
 }
 
+cat <<EOF >/tmp/test.xml
+<?xml?>
+<hello hattr="hval">
+	<world wattr="wval" wattr2="bla">
+		Free text
+		<stuff sattr="sval"/>
+	</world>
+	<world wattr="wval2">
+		Another free text
+		<stuff sattr="sval2"/>
+	</world>
+</hello>
+EOF
+
 # first test expects data from pipe
-cmd='cat /tmp/test.xml | 6.xq '
-testxq '/hello/world' '<world wattr=''wval'' wattr2=''bla'' />'
+cmd='cat /tmp/test.xml | 6.xq -t '
+testxq '/hello/world' \
+'<world wattr=''wval'' wattr2=''bla'' />
+<world wattr=''wval2'' />'
 
 # remaining tests read from file directly
-cmd='6.xq -f /tmp/test.xml '
+cmd='6.xq -t -f /tmp/test.xml '
 
-testxq '/hello/world' '<world wattr=''wval'' wattr2=''bla'' />'
-testxq '/hello/world/@wattr' 'wval'
-testxq '/hello/world/text()' 'Free text'
-testxq '/hello/world[@wattr=''wval2'']/text()' 'Another free text'
-testxq '/hello/world[@wattr=''wval2'']/stuff' '<stuff sattr=''sval2'' />'
-testxq '/hello/world[@wattr=''none'']' 'not found'
-testxq '/hello//stuff/@sattr' 'sval'
-testxq '/hello/world[2]' '<world wattr=''wval2'' />'
-testxq '/hello/world[2]/stuff' '<stuff sattr=''sval2'' />'
+testxq '/hello/world' \
+'<world wattr=''wval'' wattr2=''bla'' />
+<world wattr=''wval2'' />'
+
+testxq '/hello/world/@wattr' \
+'wval
+wval2'
+
+testxq '/hello/world/text()' \
+'Free text
+Another free text'
+
+testxq '/hello/world[@wattr=''wval2'']/text()' \
+'Another free text'
+
+testxq '/hello/world[@wattr=''wval2'']/stuff' \
+'<stuff sattr=''sval2'' />'
+
+testxq '/hello/world[@wattr=''none'']' ''
+
+testxq '/hello//stuff/@sattr' \
+'sval
+sval2'
+
+testxq '/hello/world[2]' \
+'<world wattr=''wval2'' />'
+
+testxq '/hello/world[2]/stuff' \
+'<stuff sattr=''sval2'' />'
 
 if (~ $#console 0)
 	exit
--- a/xq.c
+++ b/xq.c
@@ -1,6 +1,7 @@
 #include <u.h>
 #include <libc.h>
 #include <xml.h>
+#include <xpath.h>
 #include <bio.h>
 #include <regexp.h>
 
@@ -7,7 +8,7 @@
 void
 usage(void)
 {
-	fprint(2, "usage: %s file\n", argv0);
+	fprint(2, "usage: %s [-t] [-f file]\n", argv0);
 	exits("usage");
 }
 
@@ -14,6 +15,8 @@
 char Enotfound[] = "not found\n";
 char Einvalidsyntax[] = "invalid syntax\n";
 
+int flags;
+
 void
 printattr(Elem *e, char *attr)
 {
@@ -183,6 +186,49 @@
 	printelem(e);
 }
 
+static void
+printstr(char *s)
+{
+	char *p;
+	char *t;
+	
+	if (!(flags & Fcrushwhite)) {
+		print("%s\n", s);
+		return;
+	}
+	
+	t = strdup(s);
+	for (p = &t[strlen(t)-1]; *p == ' '; p--)
+		*p = 0;
+	print("%s\n", t);
+	free(t);
+}
+
+void
+query2(char *q, Xml *x)
+{
+	XpResult r;
+	int i;
+	
+//	xmldebug = 1;
+	
+	r = xmllookpath(x->root, q);
+	
+	switch (r.type) {
+	default:
+		fprint(2, "not found\n");
+		break;
+	case XTstring:
+		for (i = 0; i < r.num; i++)
+			printstr(r.strings[i]);
+		break;
+	case XTelem:
+		for (i = 0; i < r.num; i++)
+			printelem(r.elems[i]);
+		break;
+	}
+}
+
 char prompt[] = "X: ";
 
 void
@@ -194,10 +240,14 @@
 	char *q;
 	Biobuf *bin;
 	
+	flags = 0;
 	ARGBEGIN{
 	case 'f':
 		file = EARGF(usage());
 		break;
+	case 't':
+		flags |= Fcrushwhite;
+		break;
 	default:
 		break;
 	}ARGEND;
@@ -209,13 +259,13 @@
 			sysfatal("error opening file: %r");
 	}
 	
-	x = xmlparse(fd, 8192, Fcrushwhite);
+	x = xmlparse(fd, 8192, flags);
 	if (!x)
 		sysfatal("error parsing file");
 	
 	if (argc) {
 		q = argv[0];
-		query(q, x);
+		query2(q, x);
 		exits(nil);
 	}