shithub: xmltools

Download patch

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);
 }