ref: 47a288683dbdc55c5df9b9f65db9909de6021af0
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";
}
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 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, Cond *c)
{
XpResult ra, rb;
int n;
if (c->anode && c->bnode) {
ra = recurse(e, c->anode);
rb = recurse(e, c->bnode);
if (ra.num != 1) {
return 0;
}
if (rb.num != 1) {
return 0;
}
if (ra.type != rb.type) {
werrstr("equals: A.type != B.type (%s != %s)\n",
resulttypestring(ra.type), resulttypestring(rb.type));
return 0;
}
if (ra.type == Xstring)
return strcmp(ra.strings[0], rb.strings[0]) == 0;
if (ra.type == Xelem)
return ra.elems[0] == rb.elems[0];
if (ra.type == Xnum)
return ra.numbers[0] == rb.numbers[0];
sysfatal("code error");
}
return 0;
}
static int
evalcond(Elem *e, Cond *c)
{
Attr *a;
if (!c)
return 1;
switch (c->type) {
case CTand:
return evalcond(e, c->a) && evalcond(e, c->b);
case CTor:
return evalcond(e, c->a) || evalcond(e, c->b);
case CTindex:
return position(e) == c->index;
break;
case CTattr:
for (a = e->attrs; a; a = a->next)
if (strcmp(a->name, c->attr->name) == 0
&& strcmp(a->value, c->value->name) == 0)
return 1;
return 0;
case CThasattr:
for (a = e->attrs; a; a = a->next)
if (strcmp(a->name, c->attr->name) == 0)
return 1;
return 0;
case CTeq:
return equals(e, c);
}
werrstr("unhandled predicate condition: %d\n", c->type);
return 1;
}
static XpResult
recurse(Elem *ep, Node *n)
{
XpResult r;
char *s;
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 (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 (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;
}
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);
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;
}