diff options
author | cinap_lenrek <cinap_lenrek@localhost> | 2011-04-14 17:27:24 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@localhost> | 2011-04-14 17:27:24 +0000 |
commit | a150899221a5badff9740703b754f901b4f52762 (patch) | |
tree | 3d6911874b0ee763e40490d04f56fc09d89ccfa6 /acme/wiki/src/wiki.c | |
parent | 71cfa9c637386ebe00fc6d1bf6215db6657559f4 (diff) | |
download | plan9front-a150899221a5badff9740703b754f901b4f52762.tar.xz |
fill /acme
Diffstat (limited to 'acme/wiki/src/wiki.c')
-rw-r--r-- | acme/wiki/src/wiki.c | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/acme/wiki/src/wiki.c b/acme/wiki/src/wiki.c new file mode 100644 index 000000000..ca53c3a09 --- /dev/null +++ b/acme/wiki/src/wiki.c @@ -0,0 +1,602 @@ +#include "awiki.h" + +Wiki *wlist; + +void +link(Wiki *w) +{ + if(w->linked) + return; + w->linked = 1; + w->prev = nil; + w->next = wlist; + if(wlist) + wlist->prev = w; + wlist = w; +} + +void +unlink(Wiki *w) +{ + if(!w->linked) + return; + w->linked = 0; + + if(w->next) + w->next->prev = w->prev; + if(w->prev) + w->prev->next = w->next; + else + wlist = w->next; + + w->next = nil; + w->prev = nil; +} + +void +wikiname(Window *w, char *name) +{ + char *p, *q; + + p = emalloc(strlen(dir)+1+strlen(name)+1+1); + strcpy(p, dir); + strcat(p, "/"); + strcat(p, name); + for(q=p; *q; q++) + if(*q==' ') + *q = '_'; + winname(w, p); + free(p); +} + +int +wikiput(Wiki *w) +{ + int fd, n; + char buf[1024], *p; + Biobuf *b; + + if((fd = open("new", ORDWR)) < 0){ + fprint(2, "Wiki: cannot open raw: %r\n"); + return -1; + } + + winopenbody(w->win, OREAD); + b = w->win->body; + if((p = Brdline(b, '\n'))==nil){ + Short: + winclosebody(w->win); + fprint(2, "Wiki: no data\n"); + close(fd); + return -1; + } + write(fd, p, Blinelen(b)); + + snprint(buf, sizeof buf, "D%lud\n", w->time); + if(email) + snprint(buf+strlen(buf), sizeof(buf)-strlen(buf), "A%s\n", email); + + if(Bgetc(b) == '#'){ + p = Brdline(b, '\n'); + if(p == nil) + goto Short; + snprint(buf+strlen(buf), sizeof(buf)-strlen(buf), "C%s\n", p); + } + write(fd, buf, strlen(buf)); + write(fd, "\n\n", 2); + + while((n = Bread(b, buf, sizeof buf)) > 0) + write(fd, buf, n); + winclosebody(w->win); + + werrstr(""); + if((n=write(fd, "", 0)) != 0){ + fprint(2, "Wiki commit %lud %d %d: %r\n", w->time, fd, n); + close(fd); + return -1; + } + seek(fd, 0, 0); + if((n = read(fd, buf, 300)) < 0){ + fprint(2, "Wiki readback: %r\n"); + close(fd); + return -1; + } + close(fd); + buf[n] = '\0'; + sprint(buf, "%s/", buf); + free(w->arg); + w->arg = estrdup(buf); + w->isnew = 0; + wikiget(w); + wikiname(w->win, w->arg); + return n; +} + +void +wikiget(Wiki *w) +{ + char *p; + int fd, normal; + Biobuf *bin; + + fprint(w->win->ctl, "dirty\n"); + + p = emalloc(strlen(w->arg)+8+1); + strcpy(p, w->arg); + normal = 1; + if(p[strlen(p)-1] == '/'){ + normal = 0; + strcat(p, "current"); + }else if(strlen(p)>8 && strcmp(p+strlen(p)-8, "/current")==0){ + normal = 0; + w->arg[strlen(w->arg)-7] = '\0'; + } + + if((fd = open(p, OREAD)) < 0){ + fprint(2, "Wiki: cannot read %s: %r\n", p); + winclean(w->win); + return; + } + free(p); + + winopenbody(w->win, OWRITE); + bin = emalloc(sizeof(*bin)); + Binit(bin, fd, OREAD); + + p = nil; + if(!normal){ + if((p = Brdline(bin, '\n')) == nil){ + fprint(2, "Wiki: cannot read title: %r\n"); + winclean(w->win); + close(fd); + free(bin); + return; + } + p[Blinelen(bin)-1] = '\0'; + } + /* clear window */ + if(w->win->data < 0) + w->win->data = winopenfile(w->win, "data"); + if(winsetaddr(w->win, ",", 0)) + write(w->win->data, "", 0); + + if(!normal) + Bprint(w->win->body, "%s\n\n", p); + + while(p = Brdline(bin, '\n')){ + p[Blinelen(bin)-1] = '\0'; + if(normal) + Bprint(w->win->body, "%s\n", p); + else{ + if(p[0]=='D') + w->time = strtoul(p+1, 0, 10); + else if(p[0]=='#') + Bprint(w->win->body, "%s\n", p+1); + } + } + winclean(w->win); + free(bin); + close(fd); +} + +static int +iscmd(char *s, char *cmd) +{ + int len; + + len = strlen(cmd); + return strncmp(s, cmd, len)==0 && (s[len]=='\0' || s[len]==' ' || s[len]=='\t' || s[len]=='\n'); +} + +static char* +skip(char *s, char *cmd) +{ + s += strlen(cmd); + while(*s==' ' || *s=='\t' || *s=='\n') + s++; + return s; +} + +int +wikiload(Wiki *w, char *arg) +{ + char *p, *q, *path, *addr; + int rv; + + p = nil; + if(arg[0] == '/') + path = arg; + else{ + p = emalloc(strlen(w->arg)+1+strlen(arg)+1); + strcpy(p, w->arg); + if(q = strrchr(p, '/')){ + ++q; + *q = '\0'; + }else + *p = '\0'; + strcat(p, arg); + cleanname(p); + path = p; + } + if(addr=strchr(path, ':')) + *addr++ = '\0'; + + rv = wikiopen(path, addr)==0; + free(p); + if(rv) + return 1; + return wikiopen(arg, 0)==0; +} + +/* return 1 if handled, 0 otherwise */ +int +wikicmd(Wiki *w, char *s) +{ + char *p; + s = skip(s, ""); + + if(iscmd(s, "Del")){ + if(windel(w->win, 0)) + w->dead = 1; + return 1; + } + if(iscmd(s, "New")){ + wikinew(skip(s, "New")); + return 1; + } + if(iscmd(s, "History")) + return wikiload(w, "history.txt"); + if(iscmd(s, "Diff")) + return wikidiff(w); + if(iscmd(s, "Get")){ + if(winisdirty(w->win) && !w->win->warned){ + w->win->warned = 1; + fprint(2, "%s/%s modified\n", dir, w->arg); + }else{ + w->win->warned = 0; + wikiget(w); + } + return 1; + } + if(iscmd(s, "Put")){ + if((p=strchr(w->arg, '/')) && p[1]!='\0') + fprint(2, "%s/%s is read-only\n", dir, w->arg); + else + wikiput(w); + return 1; + } + return 0; +} + +/* need to expand selection more than default word */ +static long +eval(Window *w, char *s, ...) +{ + char buf[64]; + va_list arg; + + va_start(arg, s); + vsnprint(buf, sizeof buf, s, arg); + va_end(arg); + + if(winsetaddr(w, buf, 1)==0) + return -1; + + if(pread(w->addr, buf, 24, 0) != 24) + return -1; + return strtol(buf, 0, 10); +} + +static int +getdot(Window *w, long *q0, long *q1) +{ + char buf[24]; + + ctlprint(w->ctl, "addr=dot\n"); + if(pread(w->addr, buf, 24, 0) != 24) + return -1; + *q0 = atoi(buf); + *q1 = atoi(buf+12); + return 0; +} + +static Event* +expand(Window *w, Event *e, Event *eacme) +{ + long q0, q1, x; + + if(getdot(w, &q0, &q1)==0 && q0 <= e->q0 && e->q0 <= q1){ + e->q0 = q0; + e->q1 = q1; + return e; + } + + q0 = eval(w, "#%lud-/\\[/", e->q0); + if(q0 < 0) + return eacme; + if(eval(w, "#%lud+/\\]/", q0) < e->q0) /* [ closes before us */ + return eacme; + q1 = eval(w, "#%lud+/\\]/", e->q1); + if(q1 < 0) + return eacme; + if((x=eval(w, "#%lud-/\\[/", q1))==-1 || x > e->q1) /* ] opens after us */ + return eacme; + e->q0 = q0+1; + e->q1 = q1; + return e; +} + +void +acmeevent(Wiki *wiki, Event *e) +{ + Event *ea, *e2, *eq; + Window *w; + char *s, *t, *buf; + int na; + + w = wiki->win; + switch(e->c1){ /* origin of action */ + default: + Unknown: + fprint(2, "unknown message %c%c\n", e->c1, e->c2); + break; + + case 'F': /* generated by our actions; ignore */ + break; + + case 'E': /* write to body or tag; can't affect us */ + break; + + case 'K': /* type away; we don't care */ + if(e->c2 == 'I' || e->c2 == 'D') + w->warned = 0; + break; + + case 'M': /* mouse event */ + switch(e->c2){ /* type of action */ + case 'x': /* mouse: button 2 in tag */ + case 'X': /* mouse: button 2 in body */ + ea = nil; + //e2 = nil; + s = e->b; + if(e->flag & 2){ /* null string with non-null expansion */ + e2 = recvp(w->cevent); + if(e->nb==0) + s = e2->b; + } + if(e->flag & 8){ /* chorded argument */ + ea = recvp(w->cevent); /* argument */ + na = ea->nb; + recvp(w->cevent); /* ignore origin */ + }else + na = 0; + + /* append chorded arguments */ + if(na){ + t = emalloc(strlen(s)+1+na+1); + sprint(t, "%s %s", s, ea->b); + s = t; + } + /* if it's a known command, do it */ + /* if it's a long message, it can't be for us anyway */ + // DPRINT(2, "exec: %s\n", s); + if(!wikicmd(wiki, s)) /* send it back */ + winwriteevent(w, e); + if(na) + free(s); + break; + + case 'l': /* mouse: button 3 in tag */ + case 'L': /* mouse: button 3 in body */ + //buf = nil; + eq = e; + if(e->flag & 2){ /* we do our own expansion for loads */ + e2 = recvp(w->cevent); + eq = expand(w, eq, e2); + } + s = eq->b; + if(eq->q1>eq->q0 && eq->nb==0){ + buf = emalloc((eq->q1-eq->q0)*UTFmax+1); + winread(w, eq->q0, eq->q1, buf); + s = buf; + } + if(!wikiload(wiki, s)) + winwriteevent(w, e); + break; + + case 'i': /* mouse: text inserted in tag */ + case 'd': /* mouse: text deleted from tag */ + break; + + case 'I': /* mouse: text inserted in body */ + case 'D': /* mouse: text deleted from body */ + w->warned = 0; + break; + + default: + goto Unknown; + } + } +} + +void +wikithread(void *v) +{ + char tmp[40]; + Event *e; + Wiki *w; + + w = v; + + if(w->isnew){ + sprint(tmp, "+new+%d", w->isnew); + wikiname(w->win, tmp); + if(w->arg){ + winopenbody(w->win, OWRITE); + Bprint(w->win->body, "%s\n\n", w->arg); + } + winclean(w->win); + }else if(!w->special){ + wikiget(w); + wikiname(w->win, w->arg); + if(w->addr) + winselect(w->win, w->addr, 1); + } + fprint(w->win->ctl, "menu\n"); + wintagwrite(w->win, "Get History Diff New", 4+8+4+4); + winclean(w->win); + + while(!w->dead && (e = recvp(w->win->cevent))) + acmeevent(w, e); + + windormant(w->win); + unlink(w); + free(w->win); + free(w->arg); + free(w); + threadexits(nil); +} + +int +wikiopen(char *arg, char *addr) +{ + Dir *d; + char *p; + Wiki *w; + +/* + if(arg==nil){ + if(write(mapfd, title, strlen(title)) < 0 + || seek(mapfd, 0, 0) < 0 || (n=read(mapfd, tmp, sizeof(tmp)-2)) < 0){ + fprint(2, "Wiki: no page '%s' found: %r\n", title); + return -1; + } + if(tmp[n-1] == '\n') + tmp[--n] = '\0'; + tmp[n++] = '/'; + tmp[n] = '\0'; + arg = tmp; + } +*/ + + /* replace embedded '\n' in links by ' ' */ + for(p=arg; *p; p++) + if(*p=='\n') + *p = ' '; + + if(strncmp(arg, dir, strlen(dir))==0 && arg[strlen(dir)]=='/' && arg[strlen(dir)+1]) + arg += strlen(dir)+1; + else if(arg[0] == '/') + return -1; + + if((d = dirstat(arg)) == nil) + return -1; + + if((d->mode&DMDIR) && arg[strlen(arg)-1] != '/'){ + p = emalloc(strlen(arg)+2); + strcpy(p, arg); + strcat(p, "/"); + arg = p; + }else if(!(d->mode&DMDIR) && arg[strlen(arg)-1]=='/'){ + arg = estrdup(arg); + arg[strlen(arg)-1] = '\0'; + }else + arg = estrdup(arg); + free(d); + + /* rewrite /current into / */ + if(strlen(arg) > 8 && strcmp(arg+strlen(arg)-8, "/current")==0) + arg[strlen(arg)-8+1] = '\0'; + + /* look for window already open */ + for(w=wlist; w; w=w->next){ + if(strcmp(w->arg, arg)==0){ + ctlprint(w->win->ctl, "show\n"); + return 0; + } + } + + w = emalloc(sizeof *w); + w->arg = arg; + w->addr = addr; + w->win = newwindow(); + link(w); + + proccreate(wineventproc, w->win, STACK); + threadcreate(wikithread, w, STACK); + return 0; +} + +void +wikinew(char *arg) +{ + static int n; + Wiki *w; + + w = emalloc(sizeof *w); + if(arg) + arg = estrdup(arg); + w->arg = arg; + w->win = newwindow(); + w->isnew = ++n; + proccreate(wineventproc, w->win, STACK); + threadcreate(wikithread, w, STACK); +} + +typedef struct Diffarg Diffarg; +struct Diffarg { + Wiki *w; + char *dir; +}; + +void +execdiff(void *v) +{ + char buf[64]; + Diffarg *a; + + a = v; + + rfork(RFFDG); + close(0); + open("/dev/null", OREAD); + sprint(buf, "/mnt/wsys/%d/body", a->w->win->id); + close(1); + open(buf, OWRITE); + close(2); + open(buf, OWRITE); + sprint(buf, "/mnt/wsys/%d", a->w->win->id); + bind(buf, "/dev", MBEFORE); + + procexecl(nil, "/acme/wiki/wiki.diff", "wiki.diff", a->dir, nil); +} + +int +wikidiff(Wiki *w) +{ + Diffarg *d; + char *p, *q, *r; + Wiki *nw; + + p = emalloc(strlen(w->arg)+10); + strcpy(p, w->arg); + if(q = strchr(p, '/')) + *q = '\0'; + r = estrdup(p); + strcat(p, "/+Diff"); + + nw = emalloc(sizeof *w); + nw->arg = p; + nw->win = newwindow(); + nw->special = 1; + + d = emalloc(sizeof(*d)); + d->w = nw; + d->dir = r; + wikiname(nw->win, p); + proccreate(wineventproc, nw->win, STACK); + proccreate(execdiff, d, STACK); + threadcreate(wikithread, nw, STACK); + return 1; +} + |