ref: fb0e2e9c6043e8bba9efda507111bab946db50be
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--; } } 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; 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; 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 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) { 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 if (hasdocchild(el)) Bprint(bout, "\n"); } static void printelemend(Elem *el, int level) { if (!el->child) return; if (hasdocchild(el)) 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 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 = 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.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) Bprint(bout, "%s", r.elems[0]->pcdata); break; case Xstring: 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 *el, int level) { 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 }; static Efunc* findfunc(char *s) { Efunc *e; for (e = efuncs; e->name; e++) if (strcmp(e->name, s) == 0) if (e->f) return e; else break; 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; } } 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; } // xmldebug = 1; if (!roottemplate) sysfatal("malformed data: no root template"); bout = Bfdopen(1, OWRITE); if (!bout) sysfatal("%r"); process(roottemplate->el, xml->root, 0); exits(nil); }