ref: 4e09200f7e4bf8791c9c720922734e0759206b72
dir: /libxpath/xmllookpath.c/
#include <u.h> #include <libc.h> #include <xml.h> #include <xpath.h> #include "dat.h" static XpResult recurse(Elem*, Node*); 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 char* resulttypestring(int type) { switch (type) { case Xelem: return "elem"; case Xstring: return "string"; case Xnum: return "num"; } return "invalid"; } 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 Xelem: 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 Xstring: 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; case Xnum: if (n >= a->size) { a->numbers = realloc(a->numbers, bufsize(n) * sizeof(int)); } memcpy(&a->numbers[a->num], b.numbers, b.num * sizeof(int)); a->num = n; free(b.numbers); break; } Out: if (xmldebug) { fprint(2, "appendresult:\n"); fprint(2, " type: %s\n", resulttypestring(a->type)); switch (a->type) { case Xelem: for (n = 0; n < a->num; n++) { fprint(2, " e: "); dbgprintnode(a->elems[n]); fprint(2, "\n"); } break; case Xstring: for (n = 0; n < a->num; n++) { fprint(2, " s: %s\n", a->strings[n]); } break; case Xnum: for (n = 0; n < a->num; n++) { fprint(2, " n: %d\n", a->numbers[n]); } break; } } } static XpResult getattrvalue(Elem *ep, char *attr) { XpResult r; Attr *a; r.type = 0; for (a = ep->attrs; a; a = a->next) if (strcmp(a->name, attr) == 0) { buildsinglestring(&r, a->value); return r; } return r; } static int equals(Elem *e, Node *c) { XpResult ra, rb; int n; n = 0; if (c->anode && c->bnode) { ra = recurse(e, c->anode); rb = recurse(e, c->bnode); if (ra.num != 1) { goto Out; } if (rb.num != 1) { goto Out; } if (ra.type != rb.type) { werrstr("equals: A.type != B.type (%s != %s)\n", resulttypestring(ra.type), resulttypestring(rb.type)); goto Out; } if (ra.type == Xstring) { n = strcmp(ra.strings[0], rb.strings[0]) == 0; goto Out; } if (ra.type == Xelem) { n = ra.elems[0] == rb.elems[0]; goto Out; } if (ra.type == Xnum) { n = ra.numbers[0] == rb.numbers[0]; goto Out; } sysfatal("code error"); } Out: xmlfreeresult(&ra); xmlfreeresult(&rb); return n; } static int evalnum(Elem *e, Node *n, int *result) { XpResult r; r = recurse(e, n); if (!r.type) return 0; if (r.num != 1) goto Out; if (r.type != Xnum) goto Out; *result = r.numbers[0]; xmlfreeresult(&r); return 1; Out: xmlfreeresult(&r); return 0; } static int cmpthan(Elem *e, Node *c) { int a, b; if (!(c->anode && c->bnode)) { werrstr("Cond: anode or bnode not valid"); return 0; } if (!evalnum(e, c->anode, &a)) { werrstr("Cond: anode no value"); return 0; } if (!evalnum(e, c->bnode, &b)) { werrstr("Cond: bnode no value"); return 0; } fprint(2, "comparing: %d <%d> %d\n", a, c->type, b); switch (c->type) { case Nlt: return a < b; case Ngt: return a > b; case Nle: return a <= b; case Nge: return a >= b; } werrstr("Cond: invalid cmpthan type"); return 0; } static int evalcond(Elem *e, Node *c) { Attr *a; if (!c) return 1; switch (c->type) { case Nand: return evalcond(e, c->anode) && evalcond(e, c->bnode); case Nor: return evalcond(e, c->anode) || evalcond(e, c->bnode); case Nindex: if (!(c->anode && c->anode->type == Nnumber)) { werrstr("invalid check: position() with no number"); return 0; } return position(e) == c->anode->number; break; case Nattribute: for (a = e->attrs; a; a = a->next) if (strcmp(a->name, c->anode->name->name) == 0) { if (!c->bnode->name->name) return 1; if (strcmp(a->value, c->bnode->name->name) == 0) return 1; } return 0; case Nnumber: return position(e) == c->number; case Neq: return equals(e, c); case Nneq: return !equals(e, c); case Nlt: case Ngt: case Nle: case Nge: return cmpthan(e, c); } werrstr("unhandled predicate condition: %s\n", type2str(c->type)); return 1; } static XpResult recurse(Elem *ep, Node *n) { XpResult r; memset(&r, 0, sizeof(XpResult)); if (!n) { buildsingleelem(&r, ep); return r; } if (n->type == Nroot) { while (ep->parent) ep = ep->parent; r = recurse(ep, n->chain); return r; } if (n->type == Nattribute) { return getattrvalue(ep, n->name->name); } if (n->type == Nfunction) { if (!(n->func && n->func->f)) sysfatal("error: no valid func"); n->func->f(&r, ep); return r; } if (n->type == Ndescself) { /* descendant or self */ for (Elem *e = ep->child; e; e = e->next) { if (e->name && strcmp(e->name, n->name->name) == 0 && evalcond(e, n->cond)) { /* if found, proceed with next rule */ appendresult(&r, recurse(e, n->chain)); } /* search for more occuring children */ appendresult(&r, recurse(e, n)); } return r; } if (n->type == Nchild) { for (Elem *e = ep->child; e; e = e->next) if (e->name && strcmp(e->name, n->name->name) == 0 && evalcond(e, n->cond)) { appendresult(&r, recurse(e, n->chain)); } return r; } if (n->type == Nstring) { buildsinglestring(&r, n->name->name); return r; } if (n->type == Nnumber) { buildsinglenum(&r, n->number); return r; } if (n->type >= Nand) { /* boolean nodes */ buildsinglenum(&r, evalcond(ep, n)); return r; } return r; } /* * search for element using XPath, starting at ep. */ XpResult xmllookpath(Elem *ep, char *path) { Node *nodes; Elem *p, root; XpResult r; char err[2]; memset(&r, 0, sizeof(XpResult)); nodes = parsexpath(path); if (!nodes) { r.error = 1; return r; } if (xmldebug) debugprintnodes(nil, 0); p = ep; while (p->parent) p = p->parent; memset(&root, 0, sizeof(Elem)); root.child = p; p->parent = &root; r = recurse(ep, nodes); rerrstr(err, sizeof(err)); if (*err) r.error = 1; root.child = nil; p->parent = nil; return r; } void xmlfreeresult(XpResult *r) { switch (r->type) { case Xelem: if (r->elems) free(r->elems); break; case Xstring: if (r->strings) free(r->strings); break; case Xnum: if (r->numbers) free(r->numbers); break; default: break; } memset(r, 0, sizeof(XpResult)); }