ref: f896677cdcd52890b3bc215d655c7bea323a4755
dir: /xq.c/
#include <u.h> #include <libc.h> #include <xml.h> #include <bio.h> #include <regexp.h> void usage(void) { fprint(2, "usage: %s file\n", argv0); exits("usage"); } char Enotfound[] = "not found\n"; char Einvalidsyntax[] = "invalid syntax\n"; 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); } char prompt[] = "X: "; void main(int argc, char **argv) { Xml *x; int fd; char *file = nil; char *q; Biobuf *bin; ARGBEGIN{ case 'f': file = EARGF(usage()); break; default: break; }ARGEND; fd = 0; if (file) { fd = open(file, OREAD); if (fd < 0) sysfatal("error opening file: %r"); } x = xmlparse(fd, 8192, Fcrushwhite); if (!x) sysfatal("error parsing file"); if (argc) { q = argv[0]; query(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); } }