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);
}