shithub: xml-9atom

ref: ce4a8027322c53b2832f221e2956c90dcd65fd1a
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, 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;
}