diff options
Diffstat (limited to 'acme/wiki')
-rw-r--r-- | acme/wiki/guide | 3 | ||||
-rw-r--r-- | acme/wiki/src/awiki.h | 114 | ||||
-rw-r--r-- | acme/wiki/src/main.c | 60 | ||||
-rw-r--r-- | acme/wiki/src/mkfile | 14 | ||||
-rw-r--r-- | acme/wiki/src/util.c | 89 | ||||
-rw-r--r-- | acme/wiki/src/wiki.c | 602 | ||||
-rw-r--r-- | acme/wiki/src/win.c | 341 | ||||
-rwxr-xr-x | acme/wiki/wiki.diff | 27 |
8 files changed, 1250 insertions, 0 deletions
diff --git a/acme/wiki/guide b/acme/wiki/guide new file mode 100644 index 000000000..a86332554 --- /dev/null +++ b/acme/wiki/guide @@ -0,0 +1,3 @@ +Local 9fs wiki +# Local wikifs /sys/lib/wiki +Wiki /mnt/wiki diff --git a/acme/wiki/src/awiki.h b/acme/wiki/src/awiki.h new file mode 100644 index 000000000..19fd61738 --- /dev/null +++ b/acme/wiki/src/awiki.h @@ -0,0 +1,114 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <thread.h> + +/* acme */ +typedef struct Event Event; +typedef struct Window Window; + +enum +{ + STACK = 8192, + EVENTSIZE = 256, + NEVENT = 5, +}; + +struct Event +{ + int c1; + int c2; + int q0; + int q1; + int flag; + int nb; + int nr; + char b[EVENTSIZE*UTFmax+1]; + Rune r[EVENTSIZE+1]; +}; + +struct Window +{ + /* file descriptors */ + int ctl; + int event; + int addr; + int data; + Biobuf *body; + + /* event input */ + char buf[512]; + char *bufp; + int nbuf; + Event e[NEVENT]; + + int warned; + int id; + int open; + Channel *cevent; /* chan(Event*) */ +}; + +extern Window* newwindow(void); +extern int winopenfile(Window*, char*); +extern void winopenbody(Window*, int); +extern void winclosebody(Window*); +extern void wintagwrite(Window*, char*, int); +extern void winname(Window*, char*); +extern void winwriteevent(Window*, Event*); +extern void winread(Window*, uint, uint, char*); +extern int windel(Window*, int); +extern void wingetevent(Window*, Event*); +extern void wineventproc(void*); +extern void winwritebody(Window*, char*, int); +extern void winclean(Window*); +extern int winisdirty(Window*); +extern int winselect(Window*, char*, int); +extern int winsetaddr(Window*, char*, int); +extern char* winreadbody(Window*, int*); +extern void windormant(Window*); +extern void winsetdump(Window*, char*, char*); + +extern char* readfile(char*, char*, int*); +extern void ctlprint(int, char*, ...); +extern void* emalloc(uint); +extern char* estrdup(char*); +extern char* estrstrdup(char*, char*); +extern char* egrow(char*, char*, char*); +extern char* eappend(char*, char*, char*); +extern void error(char*, ...); +extern int tokenizec(char*, char**, int, char*); + +typedef struct Treq Treq; +typedef struct Wiki Wiki; + +struct Treq { + char *title; + Channel *c; /* chan(int) */ +}; + +struct Wiki { + QLock; + int isnew; + int special; + char *arg; + char *addr; + int n; + int dead; + Window *win; + ulong time; + int linked; + Wiki *next; + Wiki *prev; +}; + +extern int debug; +extern int mapfd; +extern char *email; +extern char *dir; + +void wikinew(char*); +int wikiopen(char*, char*); +int wikiput(Wiki*); +void wikiget(Wiki*); +int wikidiff(Wiki*); + diff --git a/acme/wiki/src/main.c b/acme/wiki/src/main.c new file mode 100644 index 000000000..f76cafff9 --- /dev/null +++ b/acme/wiki/src/main.c @@ -0,0 +1,60 @@ +#include "awiki.h" + +int debug; +int mapfd; +char *email; +char *dir; + +void +usage(void) +{ + fprint(2, "usage: Wiki [-e email] [dir]\n"); + exits("usage"); +} + +void +threadmain(int argc, char **argv) +{ + char *s; + Dir *d; + + rfork(RFNAMEG); + ARGBEGIN{ + case 'D': + debug++; + break; + case 'e': + email = EARGF(usage()); + break; + default: + usage(); + break; + }ARGEND + + if(argc > 1) + usage(); + if(argc == 1) + dir = argv[0]; + else + dir = "/mnt/wiki"; + + if(chdir(dir) < 0){ + fprint(2, "chdir(%s) fails: %r\n", dir); + threadexitsall(nil); + } + + if((mapfd = open("map", ORDWR)) < 0){ + fprint(2, "open(map): %r\n"); + threadexitsall(nil); + } + + if((d = dirstat("1")) == nil){ + fprint(2, "dirstat(%s/1) fails: %r\n", dir); + threadexitsall(nil); + } + s = emalloc(strlen(d->name)+2); + strcpy(s, d->name); + strcat(s, "/"); + wikiopen(s, nil); + threadexits(nil); +} diff --git a/acme/wiki/src/mkfile b/acme/wiki/src/mkfile new file mode 100644 index 000000000..89431e702 --- /dev/null +++ b/acme/wiki/src/mkfile @@ -0,0 +1,14 @@ +</$objtype/mkfile + +TARG=Wiki + +OFILES=\ + main.$O\ + util.$O\ + wiki.$O\ + win.$O\ + +HFILES=awiki.h +BIN=../../bin/$objtype + +</sys/src/cmd/mkone diff --git a/acme/wiki/src/util.c b/acme/wiki/src/util.c new file mode 100644 index 000000000..832d97321 --- /dev/null +++ b/acme/wiki/src/util.c @@ -0,0 +1,89 @@ +#include "awiki.h" + +void* +emalloc(uint n) +{ + void *p; + + p = malloc(n); + if(p == nil) + error("can't malloc: %r"); + memset(p, 0, n); + return p; +} + +char* +estrdup(char *s) +{ + char *t; + + t = emalloc(strlen(s)+1); + strcpy(t, s); + return t; +} + +char* +estrstrdup(char *s, char *t) +{ + char *u; + + u = emalloc(strlen(s)+strlen(t)+1); + strcpy(u, s); + strcat(u, t); + return u; +} + +char* +eappend(char *s, char *sep, char *t) +{ + char *u; + + if(t == nil) + u = estrstrdup(s, sep); + else{ + u = emalloc(strlen(s)+strlen(sep)+strlen(t)+1); + strcpy(u, s); + strcat(u, sep); + strcat(u, t); + } + free(s); + return u; +} + +char* +egrow(char *s, char *sep, char *t) +{ + s = eappend(s, sep, t); + free(t); + return s; +} + +void +error(char *fmt, ...) +{ + int n; + va_list arg; + char buf[256]; + + fprint(2, "Wiki: "); + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof buf, fmt, arg) - buf; + va_end(arg); + write(2, buf, n); + write(2, "\n", 1); + threadexitsall(fmt); +} + +void +ctlprint(int fd, char *fmt, ...) +{ + int n; + va_list arg; + char buf[256]; + + va_start(arg, fmt); + n = vseprint(buf, buf+sizeof buf, fmt, arg) - buf; + va_end(arg); + if(write(fd, buf, n) != n) + error("control file write(%s) error: %r", buf); +} 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; +} + diff --git a/acme/wiki/src/win.c b/acme/wiki/src/win.c new file mode 100644 index 000000000..3eec1e9bd --- /dev/null +++ b/acme/wiki/src/win.c @@ -0,0 +1,341 @@ +#include "awiki.h" + +Window* +newwindow(void) +{ + char buf[12]; + Window *w; + + w = emalloc(sizeof(Window)); + w->ctl = open("/mnt/wsys/new/ctl", ORDWR|OCEXEC); + if(w->ctl<0 || read(w->ctl, buf, 12)!=12) + error("can't open window ctl file: %r"); + ctlprint(w->ctl, "noscroll\n"); + w->id = atoi(buf); + w->event = winopenfile(w, "event"); + w->addr = -1; /* will be opened when needed */ + w->body = nil; + w->data = -1; + w->cevent = chancreate(sizeof(Event*), 0); + if(w->cevent == nil) + error("cevent is nil: %r"); + return w; +} + +void +winsetdump(Window *w, char *dir, char *cmd) +{ + if(dir != nil) + ctlprint(w->ctl, "dumpdir %s\n", dir); + if(cmd != nil) + ctlprint(w->ctl, "dump %s\n", cmd); +} + +void +wineventproc(void *v) +{ + Window *w; + int i; + + threadsetname("wineventproc"); + w = v; + for(i=0; ; i++){ + if(i >= NEVENT) + i = 0; + wingetevent(w, &w->e[i]); + sendp(w->cevent, &w->e[i]); + } +} + +int +winopenfile(Window *w, char *f) +{ + char buf[64]; + int fd; + + sprint(buf, "/mnt/wsys/%d/%s", w->id, f); + fd = open(buf, ORDWR|OCEXEC); + if(fd < 0) + error("can't open window file %s: %r", f); + return fd; +} + +void +wintagwrite(Window *w, char *s, int n) +{ + int fd; + + fd = winopenfile(w, "tag"); + if(write(fd, s, n) != n) + error("tag write: %r"); + close(fd); +} + +void +winname(Window *w, char *s) +{ + ctlprint(w->ctl, "name %s\n", s); +} + +void +winopenbody(Window *w, int mode) +{ + char buf[256]; + + sprint(buf, "/mnt/wsys/%d/body", w->id); + w->body = Bopen(buf, mode|OCEXEC); + if(w->body == nil) + error("can't open window body file: %r"); +} + +void +winclosebody(Window *w) +{ + if(w->body != nil){ + Bterm(w->body); + w->body = nil; + } +} + +void +winwritebody(Window *w, char *s, int n) +{ + if(w->body == nil) + winopenbody(w, OWRITE); + if(Bwrite(w->body, s, n) != n) + error("write error to window: %r"); +} + +int +wingetec(Window *w) +{ + if(w->nbuf == 0){ + w->nbuf = read(w->event, w->buf, sizeof w->buf); + if(w->nbuf <= 0){ + /* probably because window has exited, and only called by wineventproc, so just shut down */ + threadexits(nil); + } + w->bufp = w->buf; + } + w->nbuf--; + return *w->bufp++; +} + +int +wingeten(Window *w) +{ + int n, c; + + n = 0; + while('0'<=(c=wingetec(w)) && c<='9') + n = n*10+(c-'0'); + if(c != ' ') + error("event number syntax"); + return n; +} + +int +wingeter(Window *w, char *buf, int *nb) +{ + Rune r; + int n; + + r = wingetec(w); + buf[0] = r; + n = 1; + if(r >= Runeself) { + while(!fullrune(buf, n)) + buf[n++] = wingetec(w); + chartorune(&r, buf); + } + *nb = n; + return r; +} + +void +wingetevent(Window *w, Event *e) +{ + int i, nb; + + e->c1 = wingetec(w); + e->c2 = wingetec(w); + e->q0 = wingeten(w); + e->q1 = wingeten(w); + e->flag = wingeten(w); + e->nr = wingeten(w); + if(e->nr > EVENTSIZE) + error("event string too long"); + e->nb = 0; + for(i=0; i<e->nr; i++){ + e->r[i] = wingeter(w, e->b+e->nb, &nb); + e->nb += nb; + } + e->r[e->nr] = 0; + e->b[e->nb] = 0; + if(wingetec(w) != '\n') + error("event syntax error"); +} + +void +winwriteevent(Window *w, Event *e) +{ + fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1); +} + +static int +nrunes(char *s, int nb) +{ + int i, n; + Rune r; + + n = 0; + for(i=0; i<nb; n++) + i += chartorune(&r, s+i); + return n; +} + +void +winread(Window *w, uint q0, uint q1, char *data) +{ + int m, n, nr; + char buf[256]; + + if(w->addr < 0) + w->addr = winopenfile(w, "addr"); + if(w->data < 0) + w->data = winopenfile(w, "data"); + m = q0; + while(m < q1){ + n = sprint(buf, "#%d", m); + if(write(w->addr, buf, n) != n) + error("error writing addr: %r"); + n = read(w->data, buf, sizeof buf); + if(n <= 0) + error("reading data: %r"); + nr = nrunes(buf, n); + while(m+nr >q1){ + do; while(n>0 && (buf[--n]&0xC0)==0x80); + --nr; + } + if(n == 0) + break; + memmove(data, buf, n); + data += n; + *data = 0; + m += nr; + } +} + +void +windormant(Window *w) +{ + if(w->addr >= 0){ + close(w->addr); + w->addr = -1; + } + if(w->body != nil){ + Bterm(w->body); + w->body = nil; + } + if(w->data >= 0){ + close(w->data); + w->data = -1; + } +} + + +int +windel(Window *w, int sure) +{ + if(sure) + write(w->ctl, "delete\n", 7); + else if(write(w->ctl, "del\n", 4) != 4) + return 0; + /* event proc will die due to read error from event file */ + windormant(w); + close(w->ctl); + w->ctl = -1; + close(w->event); + w->event = -1; + return 1; +} + +void +winclean(Window *w) +{ + if(w->body) + Bflush(w->body); + ctlprint(w->ctl, "clean\n"); +} + +int +winisdirty(Window *w) +{ + char m; + + if (seek(w->ctl, 4*(11+1) + 10, 0) < 0) + error("control file seek error: %r"); + + if(read(w->ctl, &m, 1) != 1) + error("control file read error: %r"); + + if (m == '0') + return 0; + else if (m == '1') + return 1; + else + error("can't parse ismodified field: %c", m); + return 1; // better safe than sorry + +} + +int +winsetaddr(Window *w, char *addr, int errok) +{ + if(w->addr < 0) + w->addr = winopenfile(w, "addr"); + if(write(w->addr, addr, strlen(addr)) < 0){ + if(!errok) + error("error writing addr(%s): %r", addr); + return 0; + } + return 1; +} + +int +winselect(Window *w, char *addr, int errok) +{ + if(winsetaddr(w, addr, errok)){ + ctlprint(w->ctl, "dot=addr\n"); + return 1; + } + return 0; +} + +char* +winreadbody(Window *w, int *np) /* can't use readfile because acme doesn't report the length */ +{ + char *s; + int m, na, n; + + if(w->body != nil) + winclosebody(w); + winopenbody(w, OREAD); + s = nil; + na = 0; + n = 0; + for(;;){ + if(na < n+512){ + na += 1024; + s = realloc(s, na+1); + } + m = Bread(w->body, s+n, na-n); + if(m <= 0) + break; + n += m; + } + s[n] = 0; + winclosebody(w); + *np = n; + return s; +} diff --git a/acme/wiki/wiki.diff b/acme/wiki/wiki.diff new file mode 100755 index 000000000..6214571b9 --- /dev/null +++ b/acme/wiki/wiki.diff @@ -0,0 +1,27 @@ +#!/bin/rc + +rfork n +cd $1 +*=(`{ls -drp [0-9]*}) + +while(! ~ $#* 0 1){ + diff -n $2/index.txt $1/index.txt | awk -F'[\/ :]' ' + $1 ~/^[0-9]+$/ { + getA = "cat "$5"/current | sed -n -e ''1d; /^A/s/^A//p; /^#/q''" + getA | getline A; close getA + $1 = t2d($1) + $5 = t2d($5) + print "\n" A ":\n" $1":"$3" "$4" "$5":"$7 + next + } + { print } + + function t2d(t) { + c = "date "t; c|getline l; close c + split(l, a, "[ :]+") + return a[1]" "a[2]" "a[3]" "a[4]":"a[5]" "a[8]"("t")" + }' + shift +} + +echo clean >/dev/ctl >[2]/dev/null |