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));
}