shithub: xmltools

ref: e32330da41503781238c33e659e8e5e095662349
dir: /xslt.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <xml.h>
#include <xpath.h>

void
usage(void)
{
	fprint(2, "usage: %s\n", argv0);
	exits(nil);
}

#define TAB '	'

Biobuf *bout = nil;

static void
indent(int level)
{
	while (level > 0) {
		Bprint(bout, "	");
		level--;
	}
}

char Amatch[] = "match";
char Aselect[] = "select";

Xml *xml = nil;
Xml *style = nil;
Xml *target = nil;

Ns *xslns = nil;

typedef struct Template Template;
struct Template {
	Elem *el;
	Template *next;
};

Template *templates = nil;
Template *lasttemplate = nil;
Template *roottemplate = nil;

static void process(Elem *tel, Elem *el, int level);

static void
addtemplate(Elem *el)
{
	if (!templates) {
		templates = mallocz(sizeof(Template), 1);
		templates->el = el;
		lasttemplate = templates;
		return;
	}
	lasttemplate->next = mallocz(sizeof(Template), 1);
	lasttemplate = lasttemplate->next;
	lasttemplate->el = el;
}

static void
findtemplates(Elem *el)
{
	Elem *e;
	
	for (e = el; e; e = e->next) {
		if (e->ns == xslns && strcmp(e->name, "template") == 0)
			addtemplate(e);
		if (e->child)
			findtemplates(e->child);
	}
}

static char*
templatematch(Elem *el, char *s)
{
	Attr *a;
	for (a = el->attrs; a; a = a->next)
		if (a->name && strcmp(a->name, s) == 0)
			return a->value;
	return nil;
}

static Template*
findtemp(Elem *e)
{
	Template *t;
	char *s, *u;
	s = templatematch(e, Aselect);
	if (!s)
		sysfatal("bad syntax: template without match");
	for (t = templates; t; t = t->next) {
		u = templatematch(t->el, Amatch);
		if (strcmp(s, u) == 0)
			return t;
	}
	werrstr("template '%s'", s);
	return nil;
}

static void
printelemstart(Elem *el, int level)
{
	Attr *a;
	indent(level);
	Bprint(bout, "<%s", el->name);
	for (a = el->attrs; a; a = a->next) {
		Bprint(bout, " %s", a->name);
		if (a->value)
			Bprint(bout, "='%s'", a->value);
	}
	if (!el->child)
		Bprint(bout, " />\n");
	else
		Bprint(bout, ">");

	if (el->pcdata)
		Bprint(bout, "%s", el->pcdata);
	else
		Bprint(bout, "\n");
}

static void
printelemend(Elem *el, int level)
{
	if (!el->child)
		return;
	if (!el->pcdata)
		indent(level);
	Bprint(bout, "</%s>\n", el->name);
}

typedef struct Efunc Efunc;
struct Efunc {
	char *name;
	void (*f)(Elem*,Elem*,int);
};

static void
fapplytemplates(Elem *tel, Elem *el, int level)
{
	Template *t;
	XpResult r;
	int i;
	
	t = findtemp(tel);
	if (!t) {
		fprint(2, "unable to find template: %r");
		return;
	}
	r = xmllookpath(el, templatematch(t->el, Amatch));
	if (!r.type)
		return;
	if (r.type != Xelem)
		return;
	for (i = 0; i < r.num; i++)
		process(t->el, r.elems[i], level);
}

static void
fvalueof(Elem *tel, Elem *el, int)
{
	XpResult r;
	char *s;
	
	s = templatematch(tel, Aselect);
	if (!s) {
		fprint(2, "value-of without select attr\n");
		return;
	}
	r = xmllookpath(el, s);
	if (r.num != 1)
		return;
	switch (r.type) {
	case Xelem:
		if (r.elems[0]->pcdata)
			Bprint(bout, "%s", r.elems[0]->pcdata);
		break;
	case Xstring:
		Bprint(bout, "%s", r.strings[0]);
		break;
	}
}

Efunc efuncs[] = {
	{ "apply-templates", fapplytemplates },
	{ "value-of", fvalueof },
	{ nil, nil },
};

static void
fbackupfunc(Elem *tel, Elem*, int)
{
	fprint(2, "unknown xslt function: %s\n", tel->name);
}
Efunc fbackup = { nil, fbackupfunc };

static Efunc*
findfunc(char *s)
{
	Efunc *e;
	for (e = efuncs; e->name; e++)
		if (strcmp(e->name, s) == 0)
			return e;
	return &fbackup;
}

static void
process(Elem *tel, Elem *el, int level)
{
	Elem *e;
	Efunc *f;
	
	for (e = tel->child; e; e = e->next) {
		if (e->ns != xslns) {
			printelemstart(e, level);
			process(e, el, level + 1);
			printelemend(e, level);
			continue;
		}
		f = findfunc(e->name);
		f->f(e, el, level);
	}
}

void
main(int argc, char **argv)
{
	int fd;
	Ns *ns;
	XpResult r;
	char *s = nil;

	ARGBEGIN{
	case 'h':
		usage();
		break;
	case 's':
		s = EARGF(usage());
		break;
	}ARGEND;

	if (argc && *argv[0]) {
		fd = open(argv[0], OREAD);
		if (fd < 0)
			sysfatal("unable to open file: %r");
		xml = xmlparse(fd, 8192, Fcrushwhite);
		close(fd);
	} else {
		xml = xmlparse(0, 8192, Fcrushwhite);
	}
	if (!xml)
		sysfatal("error parsing xml: %r");

	if (s) {
		fd = open(s, OREAD);
		if (fd < 0)
			sysfatal("unable to open file: %r");
		style = xmlparse(fd, 8192, Fcrushwhite);
		close(fd);
	}
	
	if (!style)
		sysfatal("error parsing xslt: %r");
	
	for (ns = style->ns; ns; ns = ns->next) {
		if (ns->decl && strstr(ns->decl, "/XSL/Transform")) {
			xslns = ns;
			break;
		}
	}
	
	target = xmlnew(8192);
	if (!target)
		sysfatal("%r");
	
	findtemplates(style->root);
	
	for (Template *t = templates; t; t = t->next) {
		s = templatematch(t->el, Amatch);
		r = xmllookpath(xml->root, s);
		if (r.num != 1)
			continue;
		if (r.type != Xelem)
			continue;
		if (r.elems[0] != xml->root)
			continue;
		roottemplate = t;
		break;
	}
	
	if (!roottemplate)
		sysfatal("malformed data: no root template");
	
	bout = Bfdopen(1, OWRITE);
	if (!bout)
		sysfatal("%r");
	process(roottemplate->el, xml->root, 0);
	exits(nil);
}