/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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 voidcacheinit(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) { s_free(ns); 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;} int okayinlist[Nwtxt] ={ [Wbullet] 1, [Wlink] 1, [Wman] 1, [Wplain] 1,}; int okayinpre[Nwtxt] ={ [Wlink] 1, [Wman] 1, [Wpre] 1,}; int okayinpara[Nwtxt] ={ [Wpara] 1, [Wlink] 1, [Wman] 1, [Wplain] 1,}; char*nospaces(char *s){ char *q; s = strdup(s); if(s == nil) return nil; for(q=s; *q; q++) if(*q == ' ') *q = '_'; return s;} String*pagehtml(String *s, Wpage *wtxt, int ty){ char *p, tmp[40]; int inlist, inpara, inpre, t, tnext; Wpage *w; inlist = 0; inpre = 0; inpara = 0; for(w=wtxt; w; w=w->next){ t = w->type; tnext = Whr; if(w->next) tnext = w->next->type; if(inlist && !okayinlist[t]){ inlist = 0; s = s_append(s, "\n</li>\n</ul>\n"); } if(inpre && !okayinpre[t]){ inpre = 0; s = s_append(s, "</pre>\n"); } switch(t){ case Wheading: p = nospaces(w->text); s = s_appendlist(s, "\n<a name=\"", p, "\" /><h3>", w->text, "</h3>\n", nil); free(p); break; case Wpara: if(inpara){ s = s_append(s, "\n</p>\n"); inpara = 0; } if(okayinpara[tnext]){ s = s_append(s, "\n<p class='para'>\n"); inpara = 1; } break; case Wbullet: if(!inlist){ inlist = 1; s = s_append(s, "\n<ul>\n"); }else s = s_append(s, "\n</li>\n"); s = s_append(s, "\n<li>\n"); break; case Wlink: 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: 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(!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: s = s_escappend(s, w->text, 0); break; } } if(inlist) s = s_append(s, "\n</li>\n</ul>\n"); if(inpre) s = s_append(s, "</pre>\n"); if(inpara) s = s_append(s, "\n</p>\n"); return s;} 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 intdodiff(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((p = strpbrk(p, "acd")) == nil) continue; n1 = atoi(p+1); if(q = strchr(p, ',')) n2 = atoi(q+1); else n2 = n1; switch(*p){ case 'a': case 'c': s = s_append(s, "<span class='old_text'>"); s = copythru(s, &pnew, &nline, n1-1); s = s_append(s, "</span><span class='new_text'>"); s = copythru(s, &pnew, &nline, n2); s = s_append(s, "</span>"); break; } } close(fdiff); s = s_append(s, "<span class='old_text'>"); s = s_append(s, pnew); s = s_append(s, "</span>"); } 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 /><div class='diff_head'>\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, "</div><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");Lines 1–500 of 834. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #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) { s_free(ns); 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; } int okayinlist[Nwtxt] = { [Wbullet] 1, [Wlink] 1, [Wman] 1, [Wplain] 1, }; int okayinpre[Nwtxt] = { [Wlink] 1, [Wman] 1, [Wpre] 1, }; int okayinpara[Nwtxt] = { [Wpara] 1, [Wlink] 1, [Wman] 1, [Wplain] 1, }; char* nospaces(char *s) { char *q; s = strdup(s); if(s == nil) return nil; for(q=s; *q; q++) if(*q == ' ') *q = '_'; return s; } String* pagehtml(String *s, Wpage *wtxt, int ty) { char *p, tmp[40]; int inlist, inpara, inpre, t, tnext; Wpage *w; inlist = 0; inpre = 0; inpara = 0; for(w=wtxt; w; w=w->next){ t = w->type; tnext = Whr; if(w->next) tnext = w->next->type; if(inlist && !okayinlist[t]){ inlist = 0; s = s_append(s, "\n</li>\n</ul>\n"); } if(inpre && !okayinpre[t]){ inpre = 0; s = s_append(s, "</pre>\n"); } switch(t){ case Wheading: p = nospaces(w->text); s = s_appendlist(s, "\n<a name=\"", p, "\" /><h3>", w->text, "</h3>\n", nil); free(p); break; case Wpara: if(inpara){ s = s_append(s, "\n</p>\n"); inpara = 0; } if(okayinpara[tnext]){ s = s_append(s, "\n<p class='para'>\n"); inpara = 1; } break; case Wbullet: if(!inlist){ inlist = 1; s = s_append(s, "\n<ul>\n"); }else s = s_append(s, "\n</li>\n"); s = s_append(s, "\n<li>\n"); break; case Wlink: 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: 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(!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: s = s_escappend(s, w->text, 0); break; } } if(inlist) s = s_append(s, "\n</li>\n</ul>\n"); if(inpre) s = s_append(s, "</pre>\n"); if(inpara) s = s_append(s, "\n</p>\n"); return s; } 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((p = strpbrk(p, "acd")) == nil) continue; n1 = atoi(p+1); if(q = strchr(p, ',')) n2 = atoi(q+1); else n2 = n1; switch(*p){ case 'a': case 'c': s = s_append(s, "<span class='old_text'>"); s = copythru(s, &pnew, &nline, n1-1); s = s_append(s, "</span><span class='new_text'>"); s = copythru(s, &pnew, &nline, n2); s = s_append(s, "</span>"); break; } } close(fdiff); s = s_append(s, "<span class='old_text'>"); s = s_append(s, pnew); s = s_append(s, "</span>"); } 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 /><div class='diff_head'>\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, "</div><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");