shithub: xmltools

Download patch

ref: fb0e2e9c6043e8bba9efda507111bab946db50be
parent: e32330da41503781238c33e659e8e5e095662349
author: sirjofri <[email protected]>
date: Tue Jul 30 15:03:17 EDT 2024

adds more functionality to xslt

--- a/test/e.xml
+++ b/test/e.xml
@@ -1,6 +1,7 @@
 <html>
 	<body>
 		<div>
+			<!-- User 1 -->
 			<h1>User: User 1</h1>
 			<ul>
 				<li>Login: u1</li>
@@ -8,6 +9,7 @@
 			</ul>
 		</div>
 		<div>
+			<!-- User 2 -->
 			<h1>User: User 2</h1>
 			<ul>
 				<li>Login: u2</li>
@@ -14,5 +16,6 @@
 				<li>Email: user2@server</li>
 			</ul>
 		</div>
+		<div>User 1, User 2</div>
 	</body>
 </html>
--- a/test/s.xml
+++ b/test/s.xml
@@ -6,6 +6,18 @@
 		<html>
 		<body>
 			<xsl:apply-templates select="user" />
+			<xsl:loop select="user">
+				<xsl:fallback>
+					<div>
+					<xsl:for-each select="user">
+						<xsl:value-of select="name" />
+						<xsl:if test="position()!=last()">
+							<xsl:text>, </xsl:text>
+						</xsl:if>
+					</xsl:for-each>
+					</div>
+				</xsl:fallback>
+			</xsl:loop>
 		</body>
 		</html>
 	</xsl:template>
@@ -12,6 +24,7 @@
 	
 	<xsl:template match="user">
 		<div>
+			<xsl:comment><xsl:value-of select="name" /></xsl:comment>
 			<h1>User: <xsl:value-of select="name" /></h1>
 			<ul>
 			<li>Login: <xsl:value-of select="@id" /></li>
--- a/xslt.c
+++ b/xslt.c
@@ -24,15 +24,61 @@
 	}
 }
 
+static void
+printesc(char *s, int noesc)
+{
+	if (noesc) {
+		Bprint(bout, "%s", s);
+		return;
+	}
+	for (; *s; s++) {
+		switch (*s) {
+		case '<':
+			Bprint(bout, "&lt;");
+			break;
+		case '>':
+			Bprint(bout, "&gt;");
+			break;
+		case '&':
+			Bprint(bout, "&amp;");
+			break;
+		default:
+			Bputc(bout, *s);
+			break;
+		}
+	}
+}
+
 char Amatch[] = "match";
 char Aselect[] = "select";
 
 Xml *xml = nil;
 Xml *style = nil;
-Xml *target = nil;
 
 Ns *xslns = nil;
 
+static Elem*
+docparent(Elem *el)
+{
+	el = el->parent;
+	while (el->ns == xslns)
+		el = el->parent;
+	return el;
+}
+
+static int
+hasdocchild(Elem *el)
+{
+	Elem *e;
+	for (e = el->child; e; e = e->next) {
+		if (e->ns != xslns)
+			return 1;
+		if (hasdocchild(e))
+			return 1;
+	}
+	return 0;
+}
+
 typedef struct Template Template;
 struct Template {
 	Elem *el;
@@ -82,6 +128,16 @@
 	return nil;
 }
 
+static char*
+etemplatematch(Elem *el, char *a)
+{
+	char *s;
+	s = templatematch(el, a);
+	if (!s)
+		fprint(2, "%s with missing attribute %s\n", el->name, a);
+	return s;
+}
+
 static Template*
 findtemp(Elem *e)
 {
@@ -117,7 +173,7 @@
 
 	if (el->pcdata)
 		Bprint(bout, "%s", el->pcdata);
-	else
+	else if (hasdocchild(el))
 		Bprint(bout, "\n");
 }
 
@@ -126,7 +182,7 @@
 {
 	if (!el->child)
 		return;
-	if (!el->pcdata)
+	if (hasdocchild(el))
 		indent(level);
 	Bprint(bout, "</%s>\n", el->name);
 }
@@ -159,19 +215,122 @@
 }
 
 static void
-fvalueof(Elem *tel, Elem *el, int)
+fattribute(Elem *tel, Elem *el, int)
 {
+	/* this should add the attributes to the parent node, which is
+	not trivial with the current architecture. Think:
+		addattribute(parent-elem, "name", pcdata)
+	*/
+	USED(tel, el);
+	fprint(2, "attribute: not implemented yet!\n");
+}
+
+static void
+fcomment(Elem *tel, Elem *el, int level)
+{
+	indent(level);
+	Bprint(bout, "<!-- ");
+	if (tel->pcdata)
+		Bprint(bout, "%s", tel->pcdata);
+	else
+		process(tel, el, level);
+	Bprint(bout, " -->\n");
+}
+
+static void
+ffallback(Elem *tel, Elem *el, int level)
+{
+	process(tel, el, level);
+}
+
+static void
+fforeach(Elem *tel, Elem *el, int level)
+{
 	XpResult r;
+	int i;
 	char *s;
 	
-	s = templatematch(tel, Aselect);
-	if (!s) {
-		fprint(2, "value-of without select attr\n");
+	s = etemplatematch(tel, Aselect);
+	if (!s)
 		return;
+	r = xmllookpath(el, s);
+	if (!r.num)
+		goto Out;
+	if (r.type != Xelem) {
+		fprint(2, "bad result type: %d != %d\n", r.type, Xelem);
+		goto Out;
 	}
+	for (i = 0; i < r.num; i++)
+		process(tel, r.elems[i], level+1);
+Out:
+	xmlfreeresult(&r);
+}
+
+static void
+fif(Elem *tel, Elem *el, int level)
+{
+	char *s;
+	XpResult r;
+	
+	s = etemplatematch(tel, "test");
+	if (!s)
+		return;
+	
 	r = xmllookpath(el, s);
-	if (r.num != 1)
+	if (r.error) {
+		fprint(2, "error: %r\n");
 		return;
+	}
+	if (r.type != Xnum) {
+		fprint(2, "if: result is not type Xnum (%d != %d)\n", r.type, Xnum);
+		return;
+	}
+	if (r.num != 1) {
+		fprint(2, "if: expected only 1 result, got %d\n", r.num);
+		return;
+	}
+	if (!r.numbers[0])
+		goto Out;
+	process(tel, el, level);
+Out:
+	xmlfreeresult(&r);
+}
+
+static void
+ftext(Elem *tel, Elem*, int)
+{
+	int noesc = 0;
+	char *s;
+	
+	if (tel->child)
+		fprint(2, "xslt text: Ignoring all children\n");
+	
+	s = templatematch(tel, "disable-output-escaping");
+	if (s) {
+		if (strcmp(s, "yes") == 0)
+			noesc = 1;
+		else if (strcmp(s, "no") == 0)
+			noesc = 0;
+		else {
+			fprint(2, "xslt text: disable-output-escaping: invalid value %s\n", s);
+		}
+	}
+	if (tel->pcdata)
+		printesc(tel->pcdata, noesc);
+}
+
+static void
+fvalueof(Elem *tel, Elem *el, int)
+{
+	XpResult r;
+	char *s;
+	
+	s = etemplatematch(tel, Aselect);
+	if (!s)
+		return;
+	r = xmllookpath(el, s);
+	if (r.num != 1)
+		goto Out;
 	switch (r.type) {
 	case Xelem:
 		if (r.elems[0]->pcdata)
@@ -181,18 +340,66 @@
 		Bprint(bout, "%s", r.strings[0]);
 		break;
 	}
+Out:
+	xmlfreeresult(&r);
 }
 
 Efunc efuncs[] = {
+	{ "apply-imports", nil },
 	{ "apply-templates", fapplytemplates },
+	{ "attribute", fattribute },
+	{ "attribute-set", nil },
+	{ "call-template", nil },
+	{ "choose", nil },
+	{ "comment", fcomment },
+	{ "copy", nil },
+	{ "copy-of", nil },
+	{ "decimal-format", nil },
+	{ "element", nil },
+	{ "fallback", ffallback },
+	{ "for-each", fforeach },
+	{ "if", fif },
+	{ "import", nil },
+	{ "include", nil },
+	{ "key", nil },
+	{ "message", nil },
+	{ "namespace-alias", nil },
+	{ "number", nil },
+	{ "otherwise", nil },
+	{ "output", nil },
+	{ "param", nil },
+	{ "preserve-space", nil },
+	{ "processing-instruction", nil },
+	{ "sort", nil },
+	{ "strip-space", nil },
+	{ "stylesheet", nil },
+	{ "template", nil },
+	{ "text", ftext },
+	{ "transform", nil },
 	{ "value-of", fvalueof },
+	{ "variable", nil },
+	{ "when", nil },
+	{ "with-param", nil },
 	{ nil, nil },
 };
 
 static void
-fbackupfunc(Elem *tel, Elem*, int)
+fbackupfunc(Elem *tel, Elem *el, int level)
 {
-	fprint(2, "unknown xslt function: %s\n", tel->name);
+	Elem *fel;
+	
+	for (fel = tel->child; fel; fel = fel->next) {
+		if (fel->ns != xslns)
+			continue;
+		if (strcmp(fel->name, "fallback") != 0)
+			continue;
+		break;
+	}
+	if (!fel) {
+		fprint(2, "xslt function not implemented: %s\n", tel->name);
+		return;
+	}
+	process(fel, el, level);
 }
 Efunc fbackup = { nil, fbackupfunc };
 
@@ -202,7 +409,10 @@
 	Efunc *e;
 	for (e = efuncs; e->name; e++)
 		if (strcmp(e->name, s) == 0)
-			return e;
+			if (e->f)
+				return e;
+			else
+				break;
 	return &fbackup;
 }
 
@@ -271,10 +481,6 @@
 		}
 	}
 	
-	target = xmlnew(8192);
-	if (!target)
-		sysfatal("%r");
-	
 	findtemplates(style->root);
 	
 	for (Template *t = templates; t; t = t->next) {
@@ -289,6 +495,8 @@
 		roottemplate = t;
 		break;
 	}
+	
+//	xmldebug = 1;
 	
 	if (!roottemplate)
 		sysfatal("malformed data: no root template");