ref: fb0e2e9c6043e8bba9efda507111bab946db50be
parent: e32330da41503781238c33e659e8e5e095662349
author: sirjofri <sirjofri@sirjofri.de>
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, "<");
+ break;
+ case '>':
+ Bprint(bout, ">");
+ break;
+ case '&':
+ Bprint(bout, "&");
+ 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");