ref: e32330da41503781238c33e659e8e5e095662349
dir: /xslt.c/
#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);
}