ref: e32330da41503781238c33e659e8e5e095662349
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--; } } 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{ 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; } } target = xmlnew(8192); if (!target) sysfatal("%r"); 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); }