ref: e864dc493153ca5083018c94a3737165613ef0d5
dir: /libxpath/xmllookpath.c/
#include <u.h> #include <libc.h> #include <xml.h> #include <xpath.h> #include <regexp.h> Reprog *fattr = nil; Reprog *fnum = nil; Reprog *fattrend = nil; static int attrmatches(Elem *e, char *attr, char *value) { Attr *a; for (a = e->attrs; a; a = a->next) { if (strcmp(a->name, attr) == 0 && strcmp(a->value, value) == 0) return 1; } return 0; } static int bufsize(int m) { int b = 32; return (m/b + 1) * b; } static void dbgprintnode(Elem *e) { Attr *a; fprint(2, "<%s", e->name); for (a = e->attrs; a; a = a->next) fprint(2, " %s='%s'", a->name, a->value); fprint(2, " />"); } static void appendresult(XpResult *a, XpResult b) { int n; if (b.num < 1) return; if (!a->type) { *a = b; goto Out; } if (a->type != b.type) sysfatal("error: incompatible type"); n = a->num + b.num; switch (a->type) { case XTelem: if (n >= a->size) { a->elems = realloc(a->elems, bufsize(n) * sizeof(Elem*)); } memcpy(&a->elems[a->num], b.elems, b.num * sizeof(Elem*)); a->num = n; free(b.elems); break; case XTstring: if (n >= a->size) { a->strings = realloc(a->strings, bufsize(n) * sizeof(char*)); } memcpy(&a->strings[a->num], b.strings, b.num * sizeof(char*)); a->num = n; free(b.strings); break; } Out: if (xmldebug) { fprint(2, "appendresult:\n"); fprint(2, " type: %s\n", a->type == XTelem ? "elems" : "string"); switch (a->type) { case XTelem: for (n = 0; n < a->num; n++) { fprint(2, " e: "); dbgprintnode(a->elems[n]); fprint(2, "\n"); } break; case XTstring: for (n = 0; n < a->num; n++) { fprint(2, " s: %s\n", a->strings[n]); } } } } static void buildsinglestring(XpResult *a, char *s) { a->type = XTstring; a->num = a->size = 1; a->strings = malloc(sizeof(char*)); a->strings[0] = s; } static void buildsingleelem(XpResult *a, Elem *e) { a->type = XTelem; a->num = a->size = 1; a->elems = malloc(sizeof(Elem*)); a->elems[0] = e; } static char* catchallpath(char *path, char *new, int catchall) { if (!catchall) return path; path--; *path = '/'; path--; *path = '/'; if (new) { new--; *new = '/'; } return path; } /* * search for element using XPath, starting at ep. */ XpResult xmllookpath(Elem *ep, char *path) { Resub match[3]; Elem *el, *rel; Attr *a; char *attr, *val; char *new; int id, i; int isroot; char *s; XpResult r, nr, mr; int catchall; int newcatchall; if (!fattr) fattr = regcomp("\\[@(.+)=\\'(.+)\\'\\]"); if (!fnum) fnum = regcomp("\\[([0-9]+)\\]"); if (!fattrend) fattrend = regcomp("@(.+)$"); if (xmldebug) { fprint(2, "xmllookpath: %s %s\n", ep->name, path); } memset(&r, 0, sizeof(XpResult)); if (!path || !*path) { if (xmldebug) fprint(2, " final, return %s\n", ep->name); buildsingleelem(&r, ep); return r; } /* handle starting '/' as document root and '//' as catchall */ isroot = 0; catchall = 0; if (path[0] == '/') { if (path[1] == '/') { /* catchall */ catchall = 1; path += 2; } else { /* root */ isroot = 1; path++; } } if (isroot) { while (ep->parent) ep = ep->parent; } newcatchall = 0; new = strchr(catchall ? path + 2 : path, '/'); if (new) { *new = 0; new++; if (new[0] == '/') { newcatchall = 1; new++; } } if (xmldebug) { fprint(2, " query is root: %d\n", isroot); fprint(2, " query is catchall: %d\n", catchall); fprint(2, " query is newcatchall: %d\n", newcatchall); fprint(2, " testing path part: %s\n", path); fprint(2, " new path part: %s\n", new); } if (catchall) { if (xmldebug) fprint(2, " rule catchall matches: %s\n", path); for (el = ep->child; el; el = el->next) { nr = xmllookpath(el, path); if (nr.type) { if (xmldebug) fprint(2, " found element\n"); for (i = 0; i < nr.num; i++) { appendresult(&r, xmllookpath(nr.elems[i], new)); } free(nr.elems); continue; } if (xmldebug) fprint(2, " found child element\n"); appendresult(&r, xmllookpath(el, catchallpath(path, new, catchall))); } return r; } memset(match, 0, 3*sizeof(Resub)); if (regexec(fattr, path, match, 3)) { if (xmldebug) fprint(2, " rule [a=b] matches: %s\n", path); *match[0].sp = 0; attr = match[1].sp; *match[1].ep = 0; val = match[2].sp; *match[2].ep = 0; for (el = ep->child; el; el = el->next) { if (!attrmatches(el, attr, val)) continue; appendresult(&r, xmllookpath(el, new)); } return r; } memset(match, 0, 3*sizeof(Resub)); if (regexec(fnum, path, match, 3)) { if (xmldebug) fprint(2, " rule [n] matches: %s\n", path); *match[0].sp = 0; *match[1].ep = 0; id = atoi(match[1].sp); i = 0; for (el = ep->child; el; el = el->next) { if (strcmp(el->name, path) != 0) continue; i++; if (i == id) { return xmllookpath(el, new); } } return r; } memset(match, 0, 3*sizeof(Resub)); if (regexec(fattrend, path, match, 3)) { if (xmldebug) fprint(2, " rule @attr matches: %s - %s\n", ep->name, path); *match[1].ep = 0; attr = match[1].sp; for (a = ep->attrs; a; a = a->next) { if (strcmp(a->name, attr) != 0) continue; buildsinglestring(&r, a->value); if (xmldebug) fprint(2, " value: %s\n", a->value); return r; } if (xmldebug) fprint(2, " no value\n"); return r; } if (strcmp(path, "text()") == 0) { if (xmldebug) fprint(2, " rule text() matches: %s\n", path); buildsinglestring(&r, ep->pcdata); return r; } new = catchallpath(new, nil, newcatchall); if (xmldebug) fprint(2, " no match, run for all childrennnn: %s\n", new); rel = isroot ? ep : ep->child; for (el = rel; el; el = el->next) { if (xmldebug) { fprint(2, " runchildren: "); dbgprintnode(el); fprint(2, "\n"); } if (newcatchall || strcmp(el->name, path) == 0) { appendresult(&r, xmllookpath(el, new)); } } return r; }