123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812 |
- #include <u.h>
- #include <libc.h>
- #include <bio.h>
- #include <String.h>
- #include <thread.h>
- #include "wiki.h"
- /*
- * Get HTML and text templates from underlying file system.
- * Caches them, which means changes don't take effect for
- * up to Tcache seconds after they are made.
- *
- * If the files are deleted, we keep returning the last
- * known copy.
- */
- enum {
- WAIT = 60
- };
- static char *name[2*Ntemplate] = {
- [Tpage] "page.html",
- [Tedit] "edit.html",
- [Tdiff] "diff.html",
- [Thistory] "history.html",
- [Tnew] "new.html",
- [Toldpage] "oldpage.html",
- [Twerror] "werror.html",
- [Ntemplate+Tpage] "page.txt",
- [Ntemplate+Tdiff] "diff.txt",
- [Ntemplate+Thistory] "history.txt",
- [Ntemplate+Toldpage] "oldpage.txt",
- [Ntemplate+Twerror] "werror.txt",
- };
- static struct {
- RWLock;
- String *s;
- ulong t;
- Qid qid;
- } cache[2*Ntemplate];
- static void
- cacheinit(void)
- {
- int i;
- static int x;
- static Lock l;
- if(x)
- return;
- lock(&l);
- if(x){
- unlock(&l);
- return;
- }
- for(i=0; i<2*Ntemplate; i++)
- if(name[i])
- cache[i].s = s_copy("");
- x = 1;
- unlock(&l);
- }
- static String*
- gettemplate(int type)
- {
- int n;
- Biobuf *b;
- Dir *d;
- String *s, *ns;
- if(name[type]==nil)
- return nil;
- cacheinit();
- rlock(&cache[type]);
- if(0 && cache[type].t+Tcache >= time(0)){
- s = s_incref(cache[type].s);
- runlock(&cache[type]);
- return s;
- }
- runlock(&cache[type]);
- // d = nil;
- wlock(&cache[type]);
- if(0 && cache[type].t+Tcache >= time(0) || (d = wdirstat(name[type])) == nil)
- goto Return;
- if(0 && d->qid.vers == cache[type].qid.vers && d->qid.path == cache[type].qid.path){
- cache[type].t = time(0);
- goto Return;
- }
- if((b = wBopen(name[type], OREAD)) == nil)
- goto Return;
- ns = s_reset(nil);
- do
- n = s_read(b, ns, Bsize);
- while(n > 0);
- Bterm(b);
- if(n < 0)
- goto Return;
- s_free(cache[type].s);
- cache[type].s = ns;
- cache[type].qid = d->qid;
- cache[type].t = time(0);
- Return:
- free(d);
- s = s_incref(cache[type].s);
- wunlock(&cache[type]);
- return s;
- }
-
- /*
- * Write wiki document in HTML.
- */
- static String*
- s_escappend(String *s, char *p, int pre)
- {
- char *q;
- while(q = strpbrk(p, pre ? "<>&" : " <>&")){
- s = s_nappend(s, p, q-p);
- switch(*q){
- case '<':
- s = s_append(s, "<");
- break;
- case '>':
- s = s_append(s, ">");
- break;
- case '&':
- s = s_append(s, "&");
- break;
- case ' ':
- s = s_append(s, "\n");
- }
- p = q+1;
- }
- s = s_append(s, p);
- return s;
- }
- static char*
- mkurl(char *s, int ty)
- {
- char *p, *q;
- if(strncmp(s, "http:", 5)==0
- || strncmp(s, "https:", 6)==0
- || strncmp(s, "#", 1)==0
- || strncmp(s, "ftp:", 4)==0
- || strncmp(s, "mailto:", 7)==0
- || strncmp(s, "telnet:", 7)==0
- || strncmp(s, "file:", 5)==0)
- return estrdup(s);
- if(strchr(s, ' ')==nil && strchr(s, '@')!=nil){
- p = emalloc(strlen(s)+8);
- strcpy(p, "mailto:");
- strcat(p, s);
- return p;
- }
- if(ty == Toldpage)
- p = smprint("../../%s", s);
- else
- p = smprint("../%s", s);
- for(q=p; *q; q++)
- if(*q==' ')
- *q = '_';
- return p;
- }
- String*
- pagehtml(String *s, Wpage *wtxt, int ty)
- {
- int inlist, inpre, inpara;
- char *p, tmp[40];
- Wpage *w;
- inlist = 0;
- inpre = 0;
- inpara = 0;
- for(w=wtxt; w; w=w->next){
- switch(w->type){
- case Wheading:
- /*
- if(!inpara){
- inpara = 1;
- s = s_append(s, "\n<p>\n");
- }
- */
- s = s_appendlist(s, "<br />\n<a name=\"",w->text,"\" /><h3>", w->text, "</h3>\n", nil);
- break;
- case Wpara:
- if(inlist){
- inlist = 0;
- s = s_append(s, "\n</ul>\n");
- }
- if(inpre){
- inpre = 0;
- s = s_append(s, "</pre>\n");
- }
- if(!inpara){
- inpara = 1;
- s = s_append(s, "\n<p>\n");
- }
- break;
- case Wbullet:
- if(inpre){
- inpre = 0;
- s = s_append(s, "</pre>\n");
- }
- if(!inlist){
- inlist = 1;
- s = s_append(s, "\n<ul>\n");
- }
- if(inpara)
- inpara = 0;
- s = s_append(s, "\n<li>\n");
- break;
- case Wlink:
- if(inpara)
- inpara = 0;
- if(w->url == nil)
- p = mkurl(w->text, ty);
- else
- p = w->url;
- s = s_appendlist(s, "<a href=\"", p, "\">", nil);
- s = s_escappend(s, w->text, 0);
- s = s_append(s, "</a>");
- if(w->url == nil)
- free(p);
- break;
- case Wman:
- if(inpara)
- inpara = 0;
-
- sprint(tmp, "%d", w->section);
- s = s_appendlist(s,
- "<a href=\"http://plan9.bell-labs.com/magic/man2html/",
- tmp, "/", w->text, "\"><i>", w->text, "</i>(",
- tmp, ")</a>", nil);
- break;
-
- case Wpre:
- if(inpara)
- inpara = 0;
- if(inlist){
- inlist = 0;
- s = s_append(s, "\n</ul>\n");
- }
- if(!inpre){
- inpre = 1;
- s = s_append(s, "\n<pre>\n");
- }
- s = s_escappend(s, w->text, 1);
- s = s_append(s, "\n");
- break;
-
- case Whr:
- s = s_append(s, "<hr />");
- break;
- case Wplain:
- if(inpre){
- inpre = 0;
- s = s_append(s, "</pre>\n");
- }
- if(inpara)
- inpara = 0;
- s = s_escappend(s, w->text, 0);
- break;
- }
- }
- if(inlist)
- s = s_append(s, "\n</ul>\n");
- if(inpre)
- s = s_append(s, "</pre>\n");
- if(!inpara)
- s = s_append(s, "\n<p>\n");
- return s;
- }
- static String*
- grey(String *s)
- {
- return s_append(s, "<font color=#777777>");
- }
- static String*
- ungrey(String *s)
- {
- return s_append(s, "</font>");
- }
- static String*
- copythru(String *s, char **newp, int *nlinep, int l)
- {
- char *oq, *q, *r;
- int ol;
- q = *newp;
- oq = q;
- ol = *nlinep;
- while(ol < l){
- if(r = strchr(q, '\n'))
- q = r+1;
- else{
- q += strlen(q);
- break;
- }
- ol++;
- }
- if(*nlinep < l)
- *nlinep = l;
- *newp = q;
- return s_nappend(s, oq, q-oq);
- }
- static int
- dodiff(char *f1, char *f2)
- {
- int p[2];
- if(pipe(p) < 0){
- return -1;
- }
- switch(fork()){
- case -1:
- return -1;
- case 0:
- close(p[0]);
- dup(p[1], 1);
- execl("/bin/diff", "diff", f1, f2, nil);
- _exits(nil);
- }
- close(p[1]);
- return p[0];
- }
- /* print document i grayed out, with only diffs relative to j in black */
- static String*
- s_diff(String *s, Whist *h, int i, int j)
- {
- char *p, *q, *pnew;
- int fdiff, fd1, fd2, n1, n2;
- Biobuf b;
- char fn1[40], fn2[40];
- String *new, *old;
- int nline;
- if(j < 0)
- return pagehtml(s, h->doc[i].wtxt, Tpage);
- strcpy(fn1, "/tmp/wiki.XXXXXX");
- strcpy(fn2, "/tmp/wiki.XXXXXX");
- if((fd1 = opentemp(fn1)) < 0 || (fd2 = opentemp(fn2)) < 0){
- close(fd1);
- s = s_append(s, "\nopentemp failed; sorry\n");
- return s;
- }
- new = pagehtml(s_reset(nil), h->doc[i].wtxt, Tpage);
- old = pagehtml(s_reset(nil), h->doc[j].wtxt, Tpage);
- write(fd1, s_to_c(new), s_len(new));
- write(fd2, s_to_c(old), s_len(old));
- fdiff = dodiff(fn2, fn1);
- if(fdiff < 0)
- s = s_append(s, "\ndiff failed; sorry\n");
- else{
- nline = 0;
- pnew = s_to_c(new);
- Binit(&b, fdiff, OREAD);
- while(p = Brdline(&b, '\n')){
- if(p[0]=='<' || p[0]=='>' || p[0]=='-')
- continue;
- p[Blinelen(&b)-1] = '\0';
- if((q = strpbrk(p, "acd")) == nil)
- continue;
- switch(*q){
- case 'a':
- case 'c':
- n1 = atoi(q+1);
- if(q = strchr(q, ','))
- n2 = atoi(q+1);
- else
- n2 = n1;
- s = grey(s);
- s = copythru(s, &pnew, &nline, n1-1);
- s = ungrey(s);
- s = copythru(s, &pnew, &nline, n2);
- break;
- }
- }
- close(fdiff);
- s = grey(s);
- s = s_append(s, pnew);
- s = ungrey(s);
- }
- s_free(new);
- s_free(old);
- close(fd1);
- close(fd2);
- return s;
- }
- static String*
- diffhtml(String *s, Whist *h)
- {
- int i;
- char tmp[50];
- char *atime;
- for(i=h->ndoc-1; i>=0; i--){
- s = s_append(s, "<hr>\n");
- if(i==h->current)
- sprint(tmp, "index.html");
- else
- sprint(tmp, "%lud", h->doc[i].time);
- atime = ctime(h->doc[i].time);
- atime[strlen(atime)-1] = '\0';
- s = s_appendlist(s,
- "<a href=\"", tmp, "\">",
- atime, "</a>", nil);
- if(h->doc[i].author)
- s = s_appendlist(s, ", ", h->doc[i].author, nil);
- if(h->doc[i].conflict)
- s = s_append(s, ", conflicting write");
- s = s_append(s, "\n");
- if(h->doc[i].comment)
- s = s_appendlist(s, "<br><i>", h->doc[i].comment, "</i>\n", nil);
- s = s_append(s, "<br><hr>");
- s = s_diff(s, h, i, i-1);
- }
- s = s_append(s, "<hr>");
- return s;
- }
- static String*
- historyhtml(String *s, Whist *h)
- {
- int i;
- char tmp[40];
- char *atime;
- s = s_append(s, "<ul>\n");
- for(i=h->ndoc-1; i>=0; i--){
- if(i==h->current)
- sprint(tmp, "index.html");
- else
- sprint(tmp, "%lud", h->doc[i].time);
- atime = ctime(h->doc[i].time);
- atime[strlen(atime)-1] = '\0';
- s = s_appendlist(s,
- "<li><a href=\"", tmp, "\">",
- atime, "</a>", nil);
- if(h->doc[i].author)
- s = s_appendlist(s, ", ", h->doc[i].author, nil);
- if(h->doc[i].conflict)
- s = s_append(s, ", conflicting write");
- s = s_append(s, "\n");
- if(h->doc[i].comment)
- s = s_appendlist(s, "<br><i>", h->doc[i].comment, "</i>\n", nil);
- }
- s = s_append(s, "</ul>");
- return s;
- }
- String*
- tohtml(Whist *h, Wdoc *d, int ty)
- {
- char *atime;
- char *p, *q, ver[40];
- int nsub;
- Sub sub[3];
- String *s, *t;
- t = gettemplate(ty);
- if(p = strstr(s_to_c(t), "PAGE"))
- q = p+4;
- else{
- p = s_to_c(t)+s_len(t);
- q = nil;
- }
- nsub = 0;
- if(h){
- sub[nsub] = (Sub){ "TITLE", h->title };
- nsub++;
- }
- if(d){
- sprint(ver, "%lud", d->time);
- sub[nsub] = (Sub){ "VERSION", ver };
- nsub++;
- atime = ctime(d->time);
- atime[strlen(atime)-1] = '\0';
- sub[nsub] = (Sub){ "DATE", atime };
- nsub++;
- }
- s = s_reset(nil);
- s = s_appendsub(s, s_to_c(t), p-s_to_c(t), sub, nsub);
- switch(ty){
- case Tpage:
- case Toldpage:
- s = pagehtml(s, d->wtxt, ty);
- break;
- case Tedit:
- s = pagetext(s, d->wtxt, 0);
- break;
- case Tdiff:
- s = diffhtml(s, h);
- break;
- case Thistory:
- s = historyhtml(s, h);
- break;
- case Tnew:
- case Twerror:
- break;
- }
- if(q)
- s = s_appendsub(s, q, strlen(q), sub, nsub);
- s_free(t);
- return s;
- }
- enum {
- LINELEN = 70,
- };
- static String*
- s_appendbrk(String *s, char *p, char *prefix, int dosharp)
- {
- char *e, *w, *x;
- int first, l;
- Rune r;
- first = 1;
- while(*p){
- s = s_append(s, p);
- e = strrchr(s_to_c(s), '\n');
- if(e == nil)
- e = s_to_c(s);
- else
- e++;
- if(utflen(e) <= LINELEN)
- break;
- x = e; l=LINELEN;
- while(l--)
- x+=chartorune(&r,x);
- x = strchr(x, ' ');
- if(x){
- *x = '\0';
- w = strrchr(e, ' ');
- *x = ' ';
- }else
- w = strrchr(e, ' ');
-
- if(w-s_to_c(s) < strlen(prefix))
- break;
-
- x = estrdup(w+1);
- *w = '\0';
- s->ptr = w;
- s_append(s, "\n");
- if(dosharp)
- s_append(s, "#");
- s_append(s, prefix);
- if(!first)
- free(p);
- first = 0;
- p = x;
- }
- if(!first)
- free(p);
- return s;
- }
- static void
- s_endline(String *s, int dosharp)
- {
- if(dosharp){
- if(s->ptr == s->base+1 && s->ptr[-1] == '#')
- return;
- if(s->ptr > s->base+1 && s->ptr[-1] == '#' && s->ptr[-2] == '\n')
- return;
- s_append(s, "\n#");
- }else{
- if(s->ptr > s->base+1 && s->ptr[-1] == '\n')
- return;
- s_append(s, "\n");
- }
- }
- String*
- pagetext(String *s, Wpage *page, int dosharp)
- {
- int inlist, inpara;
- char *prefix, *sharp, tmp[40];
- String *t;
- Wpage *w;
- inlist = 0;
- inpara = 0;
- prefix = "";
- sharp = dosharp ? "#" : "";
- s = s_append(s, sharp);
- for(w=page; w; w=w->next){
- switch(w->type){
- case Wheading:
- if(inlist){
- prefix = "";
- inlist = 0;
- }
- s_endline(s, dosharp);
- if(!inpara){
- inpara = 1;
- s = s_appendlist(s, "\n", sharp, nil);
- }
- s = s_appendlist(s, w->text, "\n", sharp, "\n", sharp, nil);
- break;
- case Wpara:
- s_endline(s, dosharp);
- if(inlist){
- prefix = "";
- inlist = 0;
- }
- if(!inpara){
- inpara = 1;
- s = s_appendlist(s, "\n", sharp, nil);
- }
- break;
- case Wbullet:
- s_endline(s, dosharp);
- if(!inlist)
- inlist = 1;
- if(inpara)
- inpara = 0;
- s = s_append(s, " *\t");
- prefix = "\t";
- break;
- case Wlink:
- if(inpara)
- inpara = 0;
- t = s_append(s_copy("["), w->text);
- if(w->url == nil)
- t = s_append(t, "]");
- else{
- t = s_append(t, " | ");
- t = s_append(t, w->url);
- t = s_append(t, "]");
- }
- s = s_appendbrk(s, s_to_c(t), prefix, dosharp);
- s_free(t);
- break;
- case Wman:
- if(inpara)
- inpara = 0;
- s = s_appendbrk(s, w->text, prefix, dosharp);
- sprint(tmp, "(%d)", w->section);
- s = s_appendbrk(s, tmp, prefix, dosharp);
- break;
-
- case Wpre:
- if(inlist){
- prefix = "";
- inlist = 0;
- }
- if(inpara)
- inpara = 0;
- s_endline(s, dosharp);
- s = s_appendlist(s, "! ", w->text, "\n", sharp, nil);
- break;
- case Whr:
- s_endline(s, dosharp);
- s = s_appendlist(s, "------------------------------------------------------ \n", sharp, nil);
- break;
- case Wplain:
- if(inpara)
- inpara = 0;
- s = s_appendbrk(s, w->text, prefix, dosharp);
- break;
- }
- }
- s_endline(s, dosharp);
- s->ptr--;
- *s->ptr = '\0';
- return s;
- }
- static String*
- historytext(String *s, Whist *h)
- {
- int i;
- char tmp[40];
- char *atime;
- for(i=h->ndoc-1; i>=0; i--){
- if(i==h->current)
- sprint(tmp, "[current]");
- else
- sprint(tmp, "[%lud/]", h->doc[i].time);
- atime = ctime(h->doc[i].time);
- atime[strlen(atime)-1] = '\0';
- s = s_appendlist(s, " * ", tmp, " ", atime, nil);
- if(h->doc[i].author)
- s = s_appendlist(s, ", ", h->doc[i].author, nil);
- if(h->doc[i].conflict)
- s = s_append(s, ", conflicting write");
- s = s_append(s, "\n");
- if(h->doc[i].comment)
- s = s_appendlist(s, "<i>", h->doc[i].comment, "</i>\n", nil);
- }
- return s;
- }
- String*
- totext(Whist *h, Wdoc *d, int ty)
- {
- char *atime;
- char *p, *q, ver[40];
- int nsub;
- Sub sub[3];
- String *s, *t;
- t = gettemplate(Ntemplate+ty);
- if(p = strstr(s_to_c(t), "PAGE"))
- q = p+4;
- else{
- p = s_to_c(t)+s_len(t);
- q = nil;
- }
- nsub = 0;
- if(h){
- sub[nsub] = (Sub){ "TITLE", h->title };
- nsub++;
- }
- if(d){
- sprint(ver, "%lud", d->time);
- sub[nsub] = (Sub){ "VERSION", ver };
- nsub++;
- atime = ctime(d->time);
- atime[strlen(atime)-1] = '\0';
- sub[nsub] = (Sub){ "DATE", atime };
- nsub++;
- }
-
- s = s_reset(nil);
- s = s_appendsub(s, s_to_c(t), p-s_to_c(t), sub, nsub);
- switch(ty){
- case Tpage:
- case Toldpage:
- s = pagetext(s, d->wtxt, 0);
- break;
- case Thistory:
- s = historytext(s, h);
- break;
- case Tnew:
- case Twerror:
- break;
- }
- if(q)
- s = s_appendsub(s, q, strlen(q), sub, nsub);
- s_free(t);
- return s;
- }
- String*
- doctext(String *s, Wdoc *d)
- {
- char tmp[40];
- sprint(tmp, "D%lud", d->time);
- s = s_append(s, tmp);
- if(d->comment){
- s = s_append(s, "\nC");
- s = s_append(s, d->comment);
- }
- if(d->author){
- s = s_append(s, "\nA");
- s = s_append(s, d->author);
- }
- if(d->conflict)
- s = s_append(s, "\nX");
- s = s_append(s, "\n");
- s = pagetext(s, d->wtxt, 1);
- return s;
- }
|