shithub: xml-9atom

ref: f1e559d47916b74f473153a0a04df6956ca4052b
dir: /libxpath/xmllookpath.c/

View raw version
#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, 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 (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;
	}
	
	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));
}