ref: fb0e2e9c6043e8bba9efda507111bab946db50be
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); } static void printnum(int num) { print("%d\n", num); } 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 Xstring: for (i = 0; i < r.num; i++) printstr(r.strings[i]); break; case Xelem: for (i = 0; i < r.num; i++) printelem(r.elems[i]); break; case Xnum: for (i = 0; i < r.num; i++) printnum(r.numbers[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); } }