ref: dd05e41b9a71be3441f36d9b944274da3cd64f35
dir: /xq.c/
#include <u.h>
#include <libc.h>
#include <xml.h>
#include <xpath.h>
#include <bio.h>
#include <regexp.h>
void
usage(void)
{
fprint(2, "usage: %s [-t] [-f file]\n", argv0);
exits("usage");
}
char Enotfound[] = "not found\n";
char Einvalidsyntax[] = "invalid syntax\n";
int flags;
void
printattr(Elem *e, char *attr)
{
Attr *a;
for (a = e->attrs; a; a = a->next) {
if (strcmp(a->name, attr) == 0) {
print("%s\n", a->value);
return;
}
}
}
void
printtext(Elem *e)
{
print("%s\n", e->pcdata);
}
void
printelem(Elem *e)
{
Attr *a;
print("<%s", e->name);
for (a = e->attrs; a; a = a->next) {
print(" %s='%s'", a->name, a->value);
}
print(" />\n");
}
Reprog *fattr = nil;
Reprog *fnum = nil;
Elem*
getfiltered(Elem *e, char *s, char **q)
{
Resub match[3];
Elem *el;
char *attr, *val;
char *new;
int id, i;
if (!fattr)
fattr = regcomp("\\[@(.+)=\\'(.+)\\'\\]");
if (!fnum)
fnum = regcomp("\\[([0-9]+)\\]");
// fprint(2, "e: %s\nq: %s\n", e->name, s);
memset(match, 0, 3*sizeof(Resub));
if (regexec(fattr, s, match, 3)) {
*match[0].sp = 0;
new = match[0].ep;
attr = match[1].sp;
*match[1].ep = 0;
val = match[2].sp;
*match[2].ep = 0;
el = xmllook(e, s, attr, val);
if (!el) {
fprint(2, Enotfound);
return nil;
}
/* new path has to start with the self element */
attr = strrchr(s, '/');
if (!attr) {
fprint(2, Einvalidsyntax);
return nil;
}
attr++;
i = strlen(attr);
new -= i;
memmove(new, attr, i);
return getfiltered(el, new, q);
}
memset(match, 0, 3*sizeof(Resub));
if (regexec(fnum, s, match, 3)) {
*match[0].sp = 0;
new = match[0].ep;
*match[1].ep = 0;
id = atoi(match[1].sp);
attr = strrchr(s, '/');
if (!attr) {
fprint(2, Einvalidsyntax);
return nil;
}
*attr = 0;
attr++;
el = xmllook(e, s, nil, nil);
if (!el) {
fprint(2, Enotfound);
return nil;
}
i = 0;
for (el = el->child; el; el = el->next) {
if (strcmp(el->name, attr) == 0) {
i++;
if (i == id) {
/* new path has to start with the self element */
i = strlen(attr);
new -= i;
memmove(new, attr, i);
return getfiltered(el, new, q);
}
}
}
fprint(2, Enotfound);
return nil;
}
/* simple checks for obvious syntax errors, if nothing matches */
if (strpbrk(s, "[]=\n")) {
fprint(2, Einvalidsyntax);
return nil;
}
*q = s;
return e;
}
void
query(char *q, Xml *x)
{
Elem *e;
char *at;
char *text;
e = getfiltered(x->root, q, &q);
if (!e) {
return;
}
at = strstr(q, "/@");
if (at) {
*at = 0;
at += 2;
}
text = strstr(q, "/text()");
if (text) {
*text = 0;
}
e = xmllook(e, q, at, nil);
if (!e) {
fprint(2, Enotfound);
return;
}
if (text) {
printtext(e);
return;
}
if (at) {
printattr(e, at);
return;
}
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
main(int argc, char **argv)
{
Xml *x;
int fd;
char *file = nil;
char *q;
Biobuf *bin;
flags = 0;
ARGBEGIN{
case 'f':
file = EARGF(usage());
break;
case 't':
flags |= Fcrushwhite;
break;
default:
break;
}ARGEND;
fd = 0;
if (file) {
fd = open(file, OREAD);
if (fd < 0)
sysfatal("error opening file: %r");
}
x = xmlparse(fd, 8192, flags);
if (!x)
sysfatal("error parsing file");
if (argc) {
q = argv[0];
query2(q, x);
exits(nil);
}
bin = Bfdopen(0, OREAD);
if (!bin)
sysfatal("error: %r");
print(prompt);
while (q = Brdstr(bin, '\n', 1)) {
if (!q)
exits(nil);
if (*q == 0) {
free(q);
continue;
}
query(q, x);
free(q);
print(prompt);
}
}