ref: e32330da41503781238c33e659e8e5e095662349
parent: fc012e4dcc216d33f07262e64e3417043a7aa564
author: sirjofri <[email protected]>
date: Thu Jul 25 09:15:21 EDT 2024
adds first working xslt stuff, see test for what works
--- /dev/null
+++ b/test/e.xml
@@ -1,0 +1,18 @@
+<html>
+ <body>
+ <div>
+ <h1>User: User 1</h1>
+ <ul>
+ <li>Login: u1</li>
+ <li>Email: user1@server</li>
+ </ul>
+ </div>
+ <div>
+ <h1>User: User 2</h1>
+ <ul>
+ <li>Login: u2</li>
+ <li>Email: user2@server</li>
+ </ul>
+ </div>
+ </body>
+</html>
--- a/test/mkfile
+++ b/test/mkfile
@@ -2,5 +2,6 @@
TEST=\
xq\
+ xslt\
</sys/src/cmd/mktest
--- /dev/null
+++ b/test/s.xml
@@ -1,0 +1,22 @@
+<xsl:stylesheet
+ version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+>
+ <xsl:template match="/users">
+ <html>
+ <body>
+ <xsl:apply-templates select="user" />
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template match="user">
+ <div>
+ <h1>User: <xsl:value-of select="name" /></h1>
+ <ul>
+ <li>Login: <xsl:value-of select="@id" /></li>
+ <li>Email: <xsl:value-of select="email" /></li>
+ </ul>
+ </div>
+ </xsl:template>
+</xsl:stylesheet>
--- /dev/null
+++ b/test/x.xml
@@ -1,0 +1,10 @@
+<users>
+ <user id="u1">
+ <name>User 1</name>
+ <email>user1@server</email>
+ </user>
+ <user id="u2">
+ <name>User 2</name>
+ <email>user2@server</email>
+ </user>
+</users>
--- /dev/null
+++ b/test/xslt.rc
@@ -1,0 +1,7 @@
+#!/bin/rc
+
+rfork en
+ramfs
+
+../6.xslt -s s.xml x.xml > /tmp/ex.xml
+diff -u /tmp/ex.xml e.xml
--- a/xslt.c
+++ b/xslt.c
@@ -1,11 +1,9 @@
#include <u.h>
#include <libc.h>
+#include <bio.h>
#include <xml.h>
#include <xpath.h>
-Xml *xml = nil;
-Xml *style = nil;
-
void
usage(void)
{
@@ -13,10 +11,225 @@
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{
@@ -32,29 +245,57 @@
fd = open(argv[0], OREAD);
if (fd < 0)
sysfatal("unable to open file: %r");
- xml = xmlparse(fd, 8192, 0);
+ xml = xmlparse(fd, 8192, Fcrushwhite);
close(fd);
} else {
- xml = xmlparse(0, 8192, 0);
+ xml = xmlparse(0, 8192, Fcrushwhite);
}
+ if (!xml)
+ sysfatal("error parsing xml: %r");
- xmldebug = 1;
if (s) {
fd = open(s, OREAD);
if (fd < 0)
sysfatal("unable to open file: %r");
- style = xmlparse(fd, 8192, 0);
+ style = xmlparse(fd, 8192, Fcrushwhite);
close(fd);
}
if (!style)
- sysfatal("err: %r");
+ sysfatal("error parsing xslt: %r");
- xmlprint(style, 2);
+ for (ns = style->ns; ns; ns = ns->next) {
+ if (ns->decl && strstr(ns->decl, "/XSL/Transform")) {
+ xslns = ns;
+ break;
+ }
+ }
- Ns *ns;
+ target = xmlnew(8192);
+ if (!target)
+ sysfatal("%r");
- for (ns = style->ns; ns; ns = ns->next) {
- fprint(2, "ns: %s → %s\n", ns->name, ns->decl);
+ 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);
}