xref: /plan9/acme/wiki/src/wiki.c (revision 90630c3ae7e1186c94d00c68ec472672552283ad)
19a747e4fSDavid du Colombier #include "awiki.h"
29a747e4fSDavid du Colombier 
39a747e4fSDavid du Colombier Wiki *wlist;
49a747e4fSDavid du Colombier 
59a747e4fSDavid du Colombier void
link(Wiki * w)69a747e4fSDavid du Colombier link(Wiki *w)
79a747e4fSDavid du Colombier {
89a747e4fSDavid du Colombier 	if(w->linked)
99a747e4fSDavid du Colombier 		return;
109a747e4fSDavid du Colombier 	w->linked = 1;
119a747e4fSDavid du Colombier 	w->prev = nil;
129a747e4fSDavid du Colombier 	w->next = wlist;
139a747e4fSDavid du Colombier 	if(wlist)
149a747e4fSDavid du Colombier 		wlist->prev = w;
159a747e4fSDavid du Colombier 	wlist = w;
169a747e4fSDavid du Colombier }
179a747e4fSDavid du Colombier 
189a747e4fSDavid du Colombier void
unlink(Wiki * w)199a747e4fSDavid du Colombier unlink(Wiki *w)
209a747e4fSDavid du Colombier {
219a747e4fSDavid du Colombier 	if(!w->linked)
229a747e4fSDavid du Colombier 		return;
239a747e4fSDavid du Colombier 	w->linked = 0;
249a747e4fSDavid du Colombier 
259a747e4fSDavid du Colombier 	if(w->next)
269a747e4fSDavid du Colombier 		w->next->prev = w->prev;
279a747e4fSDavid du Colombier 	if(w->prev)
289a747e4fSDavid du Colombier 		w->prev->next = w->next;
299a747e4fSDavid du Colombier 	else
309a747e4fSDavid du Colombier 		wlist = w->next;
319a747e4fSDavid du Colombier 
329a747e4fSDavid du Colombier 	w->next = nil;
339a747e4fSDavid du Colombier 	w->prev = nil;
349a747e4fSDavid du Colombier }
359a747e4fSDavid du Colombier 
369a747e4fSDavid du Colombier void
wikiname(Window * w,char * name)379a747e4fSDavid du Colombier wikiname(Window *w, char *name)
389a747e4fSDavid du Colombier {
399a747e4fSDavid du Colombier 	char *p, *q;
409a747e4fSDavid du Colombier 
419a747e4fSDavid du Colombier 	p = emalloc(strlen(dir)+1+strlen(name)+1+1);
429a747e4fSDavid du Colombier 	strcpy(p, dir);
439a747e4fSDavid du Colombier 	strcat(p, "/");
449a747e4fSDavid du Colombier 	strcat(p, name);
459a747e4fSDavid du Colombier 	for(q=p; *q; q++)
469a747e4fSDavid du Colombier 		if(*q==' ')
479a747e4fSDavid du Colombier 			*q = '_';
489a747e4fSDavid du Colombier 	winname(w, p);
499a747e4fSDavid du Colombier 	free(p);
509a747e4fSDavid du Colombier }
519a747e4fSDavid du Colombier 
529a747e4fSDavid du Colombier int
wikiput(Wiki * w)539a747e4fSDavid du Colombier wikiput(Wiki *w)
549a747e4fSDavid du Colombier {
559a747e4fSDavid du Colombier 	int fd, n;
569a747e4fSDavid du Colombier 	char buf[1024], *p;
579a747e4fSDavid du Colombier 	Biobuf *b;
589a747e4fSDavid du Colombier 
599a747e4fSDavid du Colombier 	if((fd = open("new", ORDWR)) < 0){
609a747e4fSDavid du Colombier 		fprint(2, "Wiki: cannot open raw: %r\n");
619a747e4fSDavid du Colombier 		return -1;
629a747e4fSDavid du Colombier 	}
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier 	winopenbody(w->win, OREAD);
659a747e4fSDavid du Colombier 	b = w->win->body;
669a747e4fSDavid du Colombier 	if((p = Brdline(b, '\n'))==nil){
679a747e4fSDavid du Colombier 	Short:
689a747e4fSDavid du Colombier 		winclosebody(w->win);
699a747e4fSDavid du Colombier 		fprint(2, "Wiki: no data\n");
709a747e4fSDavid du Colombier 		close(fd);
719a747e4fSDavid du Colombier 		return -1;
729a747e4fSDavid du Colombier 	}
739a747e4fSDavid du Colombier 	write(fd, p, Blinelen(b));
749a747e4fSDavid du Colombier 
759a747e4fSDavid du Colombier 	snprint(buf, sizeof buf, "D%lud\n", w->time);
769a747e4fSDavid du Colombier 	if(email)
779a747e4fSDavid du Colombier 		snprint(buf+strlen(buf), sizeof(buf)-strlen(buf), "A%s\n", email);
789a747e4fSDavid du Colombier 
799a747e4fSDavid du Colombier 	if(Bgetc(b) == '#'){
809a747e4fSDavid du Colombier 		p = Brdline(b, '\n');
819a747e4fSDavid du Colombier 		if(p == nil)
829a747e4fSDavid du Colombier 			goto Short;
839a747e4fSDavid du Colombier 		snprint(buf+strlen(buf), sizeof(buf)-strlen(buf), "C%s\n", p);
849a747e4fSDavid du Colombier 	}
859a747e4fSDavid du Colombier 	write(fd, buf, strlen(buf));
869a747e4fSDavid du Colombier 	write(fd, "\n\n", 2);
879a747e4fSDavid du Colombier 
889a747e4fSDavid du Colombier 	while((n = Bread(b, buf, sizeof buf)) > 0)
899a747e4fSDavid du Colombier 		write(fd, buf, n);
909a747e4fSDavid du Colombier 	winclosebody(w->win);
919a747e4fSDavid du Colombier 
929a747e4fSDavid du Colombier 	werrstr("");
939a747e4fSDavid du Colombier 	if((n=write(fd, "", 0)) != 0){
949a747e4fSDavid du Colombier 		fprint(2, "Wiki commit %lud %d %d: %r\n", w->time, fd, n);
959a747e4fSDavid du Colombier 		close(fd);
969a747e4fSDavid du Colombier 		return -1;
979a747e4fSDavid du Colombier 	}
989a747e4fSDavid du Colombier 	seek(fd, 0, 0);
9922a0222bSDavid du Colombier 	if((n = read(fd, buf, 300)) < 0){
1009a747e4fSDavid du Colombier 		fprint(2, "Wiki readback: %r\n");
1019a747e4fSDavid du Colombier 		close(fd);
1029a747e4fSDavid du Colombier 		return -1;
1039a747e4fSDavid du Colombier 	}
1049a747e4fSDavid du Colombier 	close(fd);
1059a747e4fSDavid du Colombier 	buf[n] = '\0';
10622a0222bSDavid du Colombier 	sprint(buf, "%s/", buf);
1079a747e4fSDavid du Colombier 	free(w->arg);
1089a747e4fSDavid du Colombier 	w->arg = estrdup(buf);
1099a747e4fSDavid du Colombier 	w->isnew = 0;
1109a747e4fSDavid du Colombier 	wikiget(w);
1119a747e4fSDavid du Colombier 	wikiname(w->win, w->arg);
1129a747e4fSDavid du Colombier 	return n;
1139a747e4fSDavid du Colombier }
1149a747e4fSDavid du Colombier 
1159a747e4fSDavid du Colombier void
wikiget(Wiki * w)1169a747e4fSDavid du Colombier wikiget(Wiki *w)
1179a747e4fSDavid du Colombier {
1189a747e4fSDavid du Colombier 	char *p;
1199a747e4fSDavid du Colombier 	int fd, normal;
1209a747e4fSDavid du Colombier 	Biobuf *bin;
1219a747e4fSDavid du Colombier 
1229a747e4fSDavid du Colombier 	fprint(w->win->ctl, "dirty\n");
1239a747e4fSDavid du Colombier 
1249a747e4fSDavid du Colombier 	p = emalloc(strlen(w->arg)+8+1);
1259a747e4fSDavid du Colombier 	strcpy(p, w->arg);
1269a747e4fSDavid du Colombier 	normal = 1;
1279a747e4fSDavid du Colombier 	if(p[strlen(p)-1] == '/'){
1289a747e4fSDavid du Colombier 		normal = 0;
1299a747e4fSDavid du Colombier 		strcat(p, "current");
1309a747e4fSDavid du Colombier 	}else if(strlen(p)>8 && strcmp(p+strlen(p)-8, "/current")==0){
1319a747e4fSDavid du Colombier 		normal = 0;
1329a747e4fSDavid du Colombier 		w->arg[strlen(w->arg)-7] = '\0';
1339a747e4fSDavid du Colombier 	}
1349a747e4fSDavid du Colombier 
1359a747e4fSDavid du Colombier 	if((fd = open(p, OREAD)) < 0){
1369a747e4fSDavid du Colombier 		fprint(2, "Wiki: cannot read %s: %r\n", p);
1379a747e4fSDavid du Colombier 		winclean(w->win);
1389a747e4fSDavid du Colombier 		return;
1399a747e4fSDavid du Colombier 	}
1409a747e4fSDavid du Colombier 	free(p);
1419a747e4fSDavid du Colombier 
1429a747e4fSDavid du Colombier 	winopenbody(w->win, OWRITE);
1439a747e4fSDavid du Colombier 	bin = emalloc(sizeof(*bin));
1449a747e4fSDavid du Colombier 	Binit(bin, fd, OREAD);
1459a747e4fSDavid du Colombier 
1469a747e4fSDavid du Colombier 	p = nil;
1479a747e4fSDavid du Colombier 	if(!normal){
1489a747e4fSDavid du Colombier 		if((p = Brdline(bin, '\n')) == nil){
1499a747e4fSDavid du Colombier 			fprint(2, "Wiki: cannot read title: %r\n");
1509a747e4fSDavid du Colombier 			winclean(w->win);
1519a747e4fSDavid du Colombier 			close(fd);
1529a747e4fSDavid du Colombier 			free(bin);
1539a747e4fSDavid du Colombier 			return;
1549a747e4fSDavid du Colombier 		}
1559a747e4fSDavid du Colombier 		p[Blinelen(bin)-1] = '\0';
1569a747e4fSDavid du Colombier 	}
1579a747e4fSDavid du Colombier 	/* clear window */
1589a747e4fSDavid du Colombier 	if(w->win->data < 0)
1599a747e4fSDavid du Colombier 		w->win->data = winopenfile(w->win, "data");
1609a747e4fSDavid du Colombier 	if(winsetaddr(w->win, ",", 0))
1619a747e4fSDavid du Colombier 		write(w->win->data, "", 0);
1629a747e4fSDavid du Colombier 
1639a747e4fSDavid du Colombier 	if(!normal)
1649a747e4fSDavid du Colombier 		Bprint(w->win->body, "%s\n\n", p);
1659a747e4fSDavid du Colombier 
1669a747e4fSDavid du Colombier 	while(p = Brdline(bin, '\n')){
1679a747e4fSDavid du Colombier 		p[Blinelen(bin)-1] = '\0';
1689a747e4fSDavid du Colombier 		if(normal)
1699a747e4fSDavid du Colombier 			Bprint(w->win->body, "%s\n", p);
1709a747e4fSDavid du Colombier 		else{
1719a747e4fSDavid du Colombier 			if(p[0]=='D')
1729a747e4fSDavid du Colombier 				w->time = strtoul(p+1, 0, 10);
1739a747e4fSDavid du Colombier 			else if(p[0]=='#')
1749a747e4fSDavid du Colombier 				Bprint(w->win->body, "%s\n", p+1);
1759a747e4fSDavid du Colombier 		}
1769a747e4fSDavid du Colombier 	}
1779a747e4fSDavid du Colombier 	winclean(w->win);
1789a747e4fSDavid du Colombier 	free(bin);
1799a747e4fSDavid du Colombier 	close(fd);
1809a747e4fSDavid du Colombier }
1819a747e4fSDavid du Colombier 
1829a747e4fSDavid du Colombier static int
iscmd(char * s,char * cmd)1839a747e4fSDavid du Colombier iscmd(char *s, char *cmd)
1849a747e4fSDavid du Colombier {
1859a747e4fSDavid du Colombier 	int len;
1869a747e4fSDavid du Colombier 
1879a747e4fSDavid du Colombier 	len = strlen(cmd);
1889a747e4fSDavid du Colombier 	return strncmp(s, cmd, len)==0 && (s[len]=='\0' || s[len]==' ' || s[len]=='\t' || s[len]=='\n');
1899a747e4fSDavid du Colombier }
1909a747e4fSDavid du Colombier 
1919a747e4fSDavid du Colombier static char*
skip(char * s,char * cmd)1929a747e4fSDavid du Colombier skip(char *s, char *cmd)
1939a747e4fSDavid du Colombier {
1949a747e4fSDavid du Colombier 	s += strlen(cmd);
1959a747e4fSDavid du Colombier 	while(*s==' ' || *s=='\t' || *s=='\n')
1969a747e4fSDavid du Colombier 		s++;
1979a747e4fSDavid du Colombier 	return s;
1989a747e4fSDavid du Colombier }
1999a747e4fSDavid du Colombier 
2009a747e4fSDavid du Colombier int
wikiload(Wiki * w,char * arg)2019a747e4fSDavid du Colombier wikiload(Wiki *w, char *arg)
2029a747e4fSDavid du Colombier {
2039a747e4fSDavid du Colombier 	char *p, *q, *path, *addr;
2049a747e4fSDavid du Colombier 	int rv;
2059a747e4fSDavid du Colombier 
2069a747e4fSDavid du Colombier 	p = nil;
2079a747e4fSDavid du Colombier 	if(arg[0] == '/')
2089a747e4fSDavid du Colombier 		path = arg;
2099a747e4fSDavid du Colombier 	else{
2109a747e4fSDavid du Colombier 		p = emalloc(strlen(w->arg)+1+strlen(arg)+1);
2119a747e4fSDavid du Colombier 		strcpy(p, w->arg);
2129a747e4fSDavid du Colombier 		if(q = strrchr(p, '/')){
2139a747e4fSDavid du Colombier 			++q;
2149a747e4fSDavid du Colombier 			*q = '\0';
2159a747e4fSDavid du Colombier 		}else
2169a747e4fSDavid du Colombier 			*p = '\0';
2179a747e4fSDavid du Colombier 		strcat(p, arg);
2189a747e4fSDavid du Colombier 		cleanname(p);
2199a747e4fSDavid du Colombier 		path = p;
2209a747e4fSDavid du Colombier 	}
2219a747e4fSDavid du Colombier 	if(addr=strchr(path, ':'))
2229a747e4fSDavid du Colombier 		*addr++ = '\0';
2239a747e4fSDavid du Colombier 
2249a747e4fSDavid du Colombier 	rv = wikiopen(path, addr)==0;
2259a747e4fSDavid du Colombier 	free(p);
2269a747e4fSDavid du Colombier 	if(rv)
2279a747e4fSDavid du Colombier 		return 1;
2289a747e4fSDavid du Colombier 	return wikiopen(arg, 0)==0;
2299a747e4fSDavid du Colombier }
2309a747e4fSDavid du Colombier 
2319a747e4fSDavid du Colombier /* return 1 if handled, 0 otherwise */
2329a747e4fSDavid du Colombier int
wikicmd(Wiki * w,char * s)2339a747e4fSDavid du Colombier wikicmd(Wiki *w, char *s)
2349a747e4fSDavid du Colombier {
2359a747e4fSDavid du Colombier 	char *p;
2369a747e4fSDavid du Colombier 	s = skip(s, "");
2379a747e4fSDavid du Colombier 
2389a747e4fSDavid du Colombier 	if(iscmd(s, "Del")){
2399a747e4fSDavid du Colombier 		if(windel(w->win, 0))
2409a747e4fSDavid du Colombier 			w->dead = 1;
2419a747e4fSDavid du Colombier 		return 1;
2429a747e4fSDavid du Colombier 	}
2439a747e4fSDavid du Colombier 	if(iscmd(s, "New")){
2449a747e4fSDavid du Colombier 		wikinew(skip(s, "New"));
2459a747e4fSDavid du Colombier 		return 1;
2469a747e4fSDavid du Colombier 	}
2479a747e4fSDavid du Colombier 	if(iscmd(s, "History"))
2489a747e4fSDavid du Colombier 		return wikiload(w, "history.txt");
2499a747e4fSDavid du Colombier 	if(iscmd(s, "Diff"))
2509a747e4fSDavid du Colombier 		return wikidiff(w);
2519a747e4fSDavid du Colombier 	if(iscmd(s, "Get")){
252*90630c3aSDavid du Colombier 		if(winisdirty(w->win) && !w->win->warned){
253*90630c3aSDavid du Colombier 			w->win->warned = 1;
2549a747e4fSDavid du Colombier 			fprint(2, "%s/%s modified\n", dir, w->arg);
255*90630c3aSDavid du Colombier 		}else{
256*90630c3aSDavid du Colombier 			w->win->warned = 0;
2579a747e4fSDavid du Colombier 			wikiget(w);
258*90630c3aSDavid du Colombier 		}
2599a747e4fSDavid du Colombier 		return 1;
2609a747e4fSDavid du Colombier 	}
2619a747e4fSDavid du Colombier 	if(iscmd(s, "Put")){
2629a747e4fSDavid du Colombier 		if((p=strchr(w->arg, '/')) && p[1]!='\0')
2639a747e4fSDavid du Colombier 			fprint(2, "%s/%s is read-only\n", dir, w->arg);
2649a747e4fSDavid du Colombier 		else
2659a747e4fSDavid du Colombier 			wikiput(w);
2669a747e4fSDavid du Colombier 		return 1;
2679a747e4fSDavid du Colombier 	}
2689a747e4fSDavid du Colombier 	return 0;
2699a747e4fSDavid du Colombier }
2709a747e4fSDavid du Colombier 
2719a747e4fSDavid du Colombier /* need to expand selection more than default word */
2729a747e4fSDavid du Colombier static long
eval(Window * w,char * s,...)2739a747e4fSDavid du Colombier eval(Window *w, char *s, ...)
2749a747e4fSDavid du Colombier {
2759a747e4fSDavid du Colombier 	char buf[64];
2769a747e4fSDavid du Colombier 	va_list arg;
2779a747e4fSDavid du Colombier 
2789a747e4fSDavid du Colombier 	va_start(arg, s);
2799a747e4fSDavid du Colombier 	vsnprint(buf, sizeof buf, s, arg);
2809a747e4fSDavid du Colombier 	va_end(arg);
2819a747e4fSDavid du Colombier 
2829a747e4fSDavid du Colombier 	if(winsetaddr(w, buf, 1)==0)
2839a747e4fSDavid du Colombier 		return -1;
2849a747e4fSDavid du Colombier 
2859a747e4fSDavid du Colombier 	if(pread(w->addr, buf, 24, 0) != 24)
2869a747e4fSDavid du Colombier 		return -1;
2879a747e4fSDavid du Colombier 	return strtol(buf, 0, 10);
2889a747e4fSDavid du Colombier }
2899a747e4fSDavid du Colombier 
2909a747e4fSDavid du Colombier static int
getdot(Window * w,long * q0,long * q1)2919a747e4fSDavid du Colombier getdot(Window *w, long *q0, long *q1)
2929a747e4fSDavid du Colombier {
2939a747e4fSDavid du Colombier 	char buf[24];
2949a747e4fSDavid du Colombier 
2959a747e4fSDavid du Colombier 	ctlprint(w->ctl, "addr=dot\n");
2969a747e4fSDavid du Colombier 	if(pread(w->addr, buf, 24, 0) != 24)
2979a747e4fSDavid du Colombier 		return -1;
2989a747e4fSDavid du Colombier 	*q0 = atoi(buf);
2999a747e4fSDavid du Colombier 	*q1 = atoi(buf+12);
3009a747e4fSDavid du Colombier 	return 0;
3019a747e4fSDavid du Colombier }
3029a747e4fSDavid du Colombier 
3039a747e4fSDavid du Colombier static Event*
expand(Window * w,Event * e,Event * eacme)3049a747e4fSDavid du Colombier expand(Window *w, Event *e, Event *eacme)
3059a747e4fSDavid du Colombier {
3069a747e4fSDavid du Colombier 	long q0, q1, x;
3079a747e4fSDavid du Colombier 
3089a747e4fSDavid du Colombier 	if(getdot(w, &q0, &q1)==0 && q0 <= e->q0 && e->q0 <= q1){
3099a747e4fSDavid du Colombier 		e->q0 = q0;
3109a747e4fSDavid du Colombier 		e->q1 = q1;
3119a747e4fSDavid du Colombier 		return e;
3129a747e4fSDavid du Colombier 	}
3139a747e4fSDavid du Colombier 
3149a747e4fSDavid du Colombier 	q0 = eval(w, "#%lud-/\\[/", e->q0);
3159a747e4fSDavid du Colombier 	if(q0 < 0)
3169a747e4fSDavid du Colombier 		return eacme;
3179a747e4fSDavid du Colombier 	if(eval(w, "#%lud+/\\]/", q0) < e->q0)	/* [ closes before us */
3189a747e4fSDavid du Colombier 		return eacme;
3199a747e4fSDavid du Colombier 	q1 = eval(w, "#%lud+/\\]/", e->q1);
3209a747e4fSDavid du Colombier 	if(q1 < 0)
3219a747e4fSDavid du Colombier 		return eacme;
3229a747e4fSDavid du Colombier 	if((x=eval(w, "#%lud-/\\[/", q1))==-1 || x > e->q1)	/* ] opens after us */
3239a747e4fSDavid du Colombier 		return eacme;
3249a747e4fSDavid du Colombier 	e->q0 = q0+1;
3259a747e4fSDavid du Colombier 	e->q1 = q1;
3269a747e4fSDavid du Colombier 	return e;
3279a747e4fSDavid du Colombier }
3289a747e4fSDavid du Colombier 
3299a747e4fSDavid du Colombier void
acmeevent(Wiki * wiki,Event * e)3309a747e4fSDavid du Colombier acmeevent(Wiki *wiki, Event *e)
3319a747e4fSDavid du Colombier {
3329a747e4fSDavid du Colombier 	Event *ea, *e2, *eq;
3339a747e4fSDavid du Colombier 	Window *w;
3349a747e4fSDavid du Colombier 	char *s, *t, *buf;
3359a747e4fSDavid du Colombier 	int na;
3369a747e4fSDavid du Colombier 
3379a747e4fSDavid du Colombier 	w = wiki->win;
3389a747e4fSDavid du Colombier 	switch(e->c1){	/* origin of action */
3399a747e4fSDavid du Colombier 	default:
3409a747e4fSDavid du Colombier 	Unknown:
3419a747e4fSDavid du Colombier 		fprint(2, "unknown message %c%c\n", e->c1, e->c2);
3429a747e4fSDavid du Colombier 		break;
3439a747e4fSDavid du Colombier 
3449a747e4fSDavid du Colombier 	case 'F':	/* generated by our actions; ignore */
3459a747e4fSDavid du Colombier 		break;
3469a747e4fSDavid du Colombier 
3479a747e4fSDavid du Colombier 	case 'E':	/* write to body or tag; can't affect us */
348*90630c3aSDavid du Colombier 		break;
349*90630c3aSDavid du Colombier 
3509a747e4fSDavid du Colombier 	case 'K':	/* type away; we don't care */
3519a747e4fSDavid du Colombier 		if(e->c2 == 'I' || e->c2 == 'D')
352*90630c3aSDavid du Colombier 			w->warned = 0;
3539a747e4fSDavid du Colombier 		break;
3549a747e4fSDavid du Colombier 
3559a747e4fSDavid du Colombier 	case 'M':	/* mouse event */
3569a747e4fSDavid du Colombier 		switch(e->c2){		/* type of action */
3579a747e4fSDavid du Colombier 		case 'x':	/* mouse: button 2 in tag */
3589a747e4fSDavid du Colombier 		case 'X':	/* mouse: button 2 in body */
3599a747e4fSDavid du Colombier 			ea = nil;
3609a747e4fSDavid du Colombier 			//e2 = nil;
3619a747e4fSDavid du Colombier 			s = e->b;
3629a747e4fSDavid du Colombier 			if(e->flag & 2){	/* null string with non-null expansion */
3639a747e4fSDavid du Colombier 				e2 = recvp(w->cevent);
3649a747e4fSDavid du Colombier 				if(e->nb==0)
3659a747e4fSDavid du Colombier 					s = e2->b;
3669a747e4fSDavid du Colombier 			}
3679a747e4fSDavid du Colombier 			if(e->flag & 8){	/* chorded argument */
3689a747e4fSDavid du Colombier 				ea = recvp(w->cevent);	/* argument */
3699a747e4fSDavid du Colombier 				na = ea->nb;
3709a747e4fSDavid du Colombier 				recvp(w->cevent);		/* ignore origin */
3719a747e4fSDavid du Colombier 			}else
3729a747e4fSDavid du Colombier 				na = 0;
3739a747e4fSDavid du Colombier 
3749a747e4fSDavid du Colombier 			/* append chorded arguments */
3759a747e4fSDavid du Colombier 			if(na){
3769a747e4fSDavid du Colombier 				t = emalloc(strlen(s)+1+na+1);
3779a747e4fSDavid du Colombier 				sprint(t, "%s %s", s, ea->b);
3789a747e4fSDavid du Colombier 				s = t;
3799a747e4fSDavid du Colombier 			}
3809a747e4fSDavid du Colombier 			/* if it's a known command, do it */
3819a747e4fSDavid du Colombier 			/* if it's a long message, it can't be for us anyway */
3829a747e4fSDavid du Colombier 		//	DPRINT(2, "exec: %s\n", s);
3839a747e4fSDavid du Colombier 			if(!wikicmd(wiki, s))	/* send it back */
3849a747e4fSDavid du Colombier 				winwriteevent(w, e);
3859a747e4fSDavid du Colombier 			if(na)
3869a747e4fSDavid du Colombier 				free(s);
3879a747e4fSDavid du Colombier 			break;
3889a747e4fSDavid du Colombier 
3899a747e4fSDavid du Colombier 		case 'l':	/* mouse: button 3 in tag */
3909a747e4fSDavid du Colombier 		case 'L':	/* mouse: button 3 in body */
3919a747e4fSDavid du Colombier 			//buf = nil;
3929a747e4fSDavid du Colombier 			eq = e;
3939a747e4fSDavid du Colombier 			if(e->flag & 2){	/* we do our own expansion for loads */
3949a747e4fSDavid du Colombier 				e2 = recvp(w->cevent);
3959a747e4fSDavid du Colombier 				eq = expand(w, eq, e2);
3969a747e4fSDavid du Colombier 			}
3979a747e4fSDavid du Colombier 			s = eq->b;
3989a747e4fSDavid du Colombier 			if(eq->q1>eq->q0 && eq->nb==0){
3999a747e4fSDavid du Colombier 				buf = emalloc((eq->q1-eq->q0)*UTFmax+1);
4009a747e4fSDavid du Colombier 				winread(w, eq->q0, eq->q1, buf);
4019a747e4fSDavid du Colombier 				s = buf;
4029a747e4fSDavid du Colombier 			}
4039a747e4fSDavid du Colombier 			if(!wikiload(wiki, s))
4049a747e4fSDavid du Colombier 				winwriteevent(w, e);
4059a747e4fSDavid du Colombier 			break;
4069a747e4fSDavid du Colombier 
4079a747e4fSDavid du Colombier 		case 'i':	/* mouse: text inserted in tag */
4089a747e4fSDavid du Colombier 		case 'd':	/* mouse: text deleted from tag */
4099a747e4fSDavid du Colombier 			break;
4109a747e4fSDavid du Colombier 
4119a747e4fSDavid du Colombier 		case 'I':	/* mouse: text inserted in body */
4129a747e4fSDavid du Colombier 		case 'D':	/* mouse: text deleted from body */
413*90630c3aSDavid du Colombier 			w->warned = 0;
4149a747e4fSDavid du Colombier 			break;
4159a747e4fSDavid du Colombier 
4169a747e4fSDavid du Colombier 		default:
4179a747e4fSDavid du Colombier 			goto Unknown;
4189a747e4fSDavid du Colombier 		}
4199a747e4fSDavid du Colombier 	}
4209a747e4fSDavid du Colombier }
4219a747e4fSDavid du Colombier 
4229a747e4fSDavid du Colombier void
wikithread(void * v)4239a747e4fSDavid du Colombier wikithread(void *v)
4249a747e4fSDavid du Colombier {
4259a747e4fSDavid du Colombier 	char tmp[40];
4269a747e4fSDavid du Colombier 	Event *e;
4279a747e4fSDavid du Colombier 	Wiki *w;
4289a747e4fSDavid du Colombier 
4299a747e4fSDavid du Colombier 	w = v;
4309a747e4fSDavid du Colombier 
4319a747e4fSDavid du Colombier 	if(w->isnew){
4329a747e4fSDavid du Colombier 		sprint(tmp, "+new+%d", w->isnew);
4339a747e4fSDavid du Colombier 		wikiname(w->win, tmp);
4349a747e4fSDavid du Colombier 		if(w->arg){
4359a747e4fSDavid du Colombier 			winopenbody(w->win, OWRITE);
4369a747e4fSDavid du Colombier 			Bprint(w->win->body, "%s\n\n", w->arg);
4379a747e4fSDavid du Colombier 		}
4389a747e4fSDavid du Colombier 		winclean(w->win);
4399a747e4fSDavid du Colombier 	}else if(!w->special){
4409a747e4fSDavid du Colombier 		wikiget(w);
4419a747e4fSDavid du Colombier 		wikiname(w->win, w->arg);
4429a747e4fSDavid du Colombier 		if(w->addr)
4439a747e4fSDavid du Colombier 			winselect(w->win, w->addr, 1);
4449a747e4fSDavid du Colombier 	}
445*90630c3aSDavid du Colombier 	fprint(w->win->ctl, "menu\n");
446*90630c3aSDavid du Colombier 	wintagwrite(w->win, "Get History Diff New", 4+8+4+4);
447*90630c3aSDavid du Colombier 	winclean(w->win);
4489a747e4fSDavid du Colombier 
4499a747e4fSDavid du Colombier 	while(!w->dead && (e = recvp(w->win->cevent)))
4509a747e4fSDavid du Colombier 		acmeevent(w, e);
4519a747e4fSDavid du Colombier 
4529a747e4fSDavid du Colombier 	windormant(w->win);
4539a747e4fSDavid du Colombier 	unlink(w);
4549a747e4fSDavid du Colombier 	free(w->win);
4559a747e4fSDavid du Colombier 	free(w->arg);
4569a747e4fSDavid du Colombier 	free(w);
4579a747e4fSDavid du Colombier 	threadexits(nil);
4589a747e4fSDavid du Colombier }
4599a747e4fSDavid du Colombier 
4609a747e4fSDavid du Colombier int
wikiopen(char * arg,char * addr)4619a747e4fSDavid du Colombier wikiopen(char *arg, char *addr)
4629a747e4fSDavid du Colombier {
4639a747e4fSDavid du Colombier 	Dir *d;
4649a747e4fSDavid du Colombier 	char *p;
4659a747e4fSDavid du Colombier 	Wiki *w;
4669a747e4fSDavid du Colombier 
4679a747e4fSDavid du Colombier /*
4689a747e4fSDavid du Colombier 	if(arg==nil){
4699a747e4fSDavid du Colombier 		if(write(mapfd, title, strlen(title)) < 0
4709a747e4fSDavid du Colombier 		|| seek(mapfd, 0, 0) < 0 || (n=read(mapfd, tmp, sizeof(tmp)-2)) < 0){
4719a747e4fSDavid du Colombier 			fprint(2, "Wiki: no page '%s' found: %r\n", title);
4729a747e4fSDavid du Colombier 			return -1;
4739a747e4fSDavid du Colombier 		}
4749a747e4fSDavid du Colombier 		if(tmp[n-1] == '\n')
4759a747e4fSDavid du Colombier 			tmp[--n] = '\0';
4769a747e4fSDavid du Colombier 		tmp[n++] = '/';
4779a747e4fSDavid du Colombier 		tmp[n] = '\0';
4789a747e4fSDavid du Colombier 		arg = tmp;
4799a747e4fSDavid du Colombier 	}
4809a747e4fSDavid du Colombier */
481e40528acSDavid du Colombier 
482e40528acSDavid du Colombier 	/* replace embedded '\n' in links by ' ' */
483e40528acSDavid du Colombier 	for(p=arg; *p; p++)
484e40528acSDavid du Colombier 		if(*p=='\n')
485e40528acSDavid du Colombier 			*p = ' ';
486e40528acSDavid du Colombier 
4879a747e4fSDavid du Colombier 	if(strncmp(arg, dir, strlen(dir))==0 && arg[strlen(dir)]=='/' && arg[strlen(dir)+1])
4889a747e4fSDavid du Colombier 		arg += strlen(dir)+1;
4899a747e4fSDavid du Colombier 	else if(arg[0] == '/')
4909a747e4fSDavid du Colombier 		return -1;
4919a747e4fSDavid du Colombier 
4929a747e4fSDavid du Colombier 	if((d = dirstat(arg)) == nil)
4939a747e4fSDavid du Colombier 		return -1;
4949a747e4fSDavid du Colombier 
4959a747e4fSDavid du Colombier 	if((d->mode&DMDIR) && arg[strlen(arg)-1] != '/'){
4969a747e4fSDavid du Colombier 		p = emalloc(strlen(arg)+2);
4979a747e4fSDavid du Colombier 		strcpy(p, arg);
4989a747e4fSDavid du Colombier 		strcat(p, "/");
4999a747e4fSDavid du Colombier 		arg = p;
5009a747e4fSDavid du Colombier 	}else if(!(d->mode&DMDIR) && arg[strlen(arg)-1]=='/'){
5019a747e4fSDavid du Colombier 		arg = estrdup(arg);
5029a747e4fSDavid du Colombier 		arg[strlen(arg)-1] = '\0';
5039a747e4fSDavid du Colombier 	}else
5049a747e4fSDavid du Colombier 		arg = estrdup(arg);
5059a747e4fSDavid du Colombier 	free(d);
5069a747e4fSDavid du Colombier 
5079a747e4fSDavid du Colombier 	/* rewrite /current into / */
5089a747e4fSDavid du Colombier 	if(strlen(arg) > 8 && strcmp(arg+strlen(arg)-8, "/current")==0)
5099a747e4fSDavid du Colombier 		arg[strlen(arg)-8+1] = '\0';
5109a747e4fSDavid du Colombier 
5119a747e4fSDavid du Colombier 	/* look for window already open */
5129a747e4fSDavid du Colombier 	for(w=wlist; w; w=w->next){
5139a747e4fSDavid du Colombier 		if(strcmp(w->arg, arg)==0){
5149a747e4fSDavid du Colombier 			ctlprint(w->win->ctl, "show\n");
5159a747e4fSDavid du Colombier 			return 0;
5169a747e4fSDavid du Colombier 		}
5179a747e4fSDavid du Colombier 	}
5189a747e4fSDavid du Colombier 
5199a747e4fSDavid du Colombier 	w = emalloc(sizeof *w);
5209a747e4fSDavid du Colombier 	w->arg = arg;
5219a747e4fSDavid du Colombier 	w->addr = addr;
5229a747e4fSDavid du Colombier 	w->win = newwindow();
5239a747e4fSDavid du Colombier 	link(w);
5249a747e4fSDavid du Colombier 
5259a747e4fSDavid du Colombier 	proccreate(wineventproc, w->win, STACK);
5269a747e4fSDavid du Colombier 	threadcreate(wikithread, w, STACK);
5279a747e4fSDavid du Colombier 	return 0;
5289a747e4fSDavid du Colombier }
5299a747e4fSDavid du Colombier 
5309a747e4fSDavid du Colombier void
wikinew(char * arg)5319a747e4fSDavid du Colombier wikinew(char *arg)
5329a747e4fSDavid du Colombier {
5339a747e4fSDavid du Colombier 	static int n;
5349a747e4fSDavid du Colombier 	Wiki *w;
5359a747e4fSDavid du Colombier 
5369a747e4fSDavid du Colombier 	w = emalloc(sizeof *w);
5379a747e4fSDavid du Colombier 	if(arg)
5389a747e4fSDavid du Colombier 		arg = estrdup(arg);
5399a747e4fSDavid du Colombier 	w->arg = arg;
5409a747e4fSDavid du Colombier 	w->win = newwindow();
5419a747e4fSDavid du Colombier 	w->isnew = ++n;
5429a747e4fSDavid du Colombier 	proccreate(wineventproc, w->win, STACK);
5439a747e4fSDavid du Colombier 	threadcreate(wikithread, w, STACK);
5449a747e4fSDavid du Colombier }
5459a747e4fSDavid du Colombier 
5469a747e4fSDavid du Colombier typedef struct Diffarg Diffarg;
5479a747e4fSDavid du Colombier struct Diffarg {
5489a747e4fSDavid du Colombier 	Wiki *w;
5499a747e4fSDavid du Colombier 	char *dir;
5509a747e4fSDavid du Colombier };
5519a747e4fSDavid du Colombier 
5529a747e4fSDavid du Colombier void
execdiff(void * v)5539a747e4fSDavid du Colombier execdiff(void *v)
5549a747e4fSDavid du Colombier {
5559a747e4fSDavid du Colombier 	char buf[64];
5569a747e4fSDavid du Colombier 	Diffarg *a;
5579a747e4fSDavid du Colombier 
5589a747e4fSDavid du Colombier 	a = v;
5599a747e4fSDavid du Colombier 
5609a747e4fSDavid du Colombier 	rfork(RFFDG);
5619a747e4fSDavid du Colombier 	close(0);
5629a747e4fSDavid du Colombier 	open("/dev/null", OREAD);
5639a747e4fSDavid du Colombier 	sprint(buf, "/mnt/wsys/%d/body", a->w->win->id);
5649a747e4fSDavid du Colombier 	close(1);
5659a747e4fSDavid du Colombier 	open(buf, OWRITE);
5669a747e4fSDavid du Colombier 	close(2);
5679a747e4fSDavid du Colombier 	open(buf, OWRITE);
5689a747e4fSDavid du Colombier 	sprint(buf, "/mnt/wsys/%d", a->w->win->id);
5699a747e4fSDavid du Colombier 	bind(buf, "/dev", MBEFORE);
5709a747e4fSDavid du Colombier 
5719a747e4fSDavid du Colombier 	procexecl(nil, "/acme/wiki/wiki.diff", "wiki.diff", a->dir, nil);
5729a747e4fSDavid du Colombier }
5739a747e4fSDavid du Colombier 
5749a747e4fSDavid du Colombier int
wikidiff(Wiki * w)5759a747e4fSDavid du Colombier wikidiff(Wiki *w)
5769a747e4fSDavid du Colombier {
5779a747e4fSDavid du Colombier 	Diffarg *d;
5789a747e4fSDavid du Colombier 	char *p, *q, *r;
5799a747e4fSDavid du Colombier 	Wiki *nw;
5809a747e4fSDavid du Colombier 
5819a747e4fSDavid du Colombier 	p = emalloc(strlen(w->arg)+10);
5829a747e4fSDavid du Colombier 	strcpy(p, w->arg);
5839a747e4fSDavid du Colombier 	if(q = strchr(p, '/'))
5849a747e4fSDavid du Colombier 		*q = '\0';
5859a747e4fSDavid du Colombier 	r = estrdup(p);
5869a747e4fSDavid du Colombier 	strcat(p, "/+Diff");
5879a747e4fSDavid du Colombier 
5889a747e4fSDavid du Colombier 	nw = emalloc(sizeof *w);
5899a747e4fSDavid du Colombier 	nw->arg = p;
5909a747e4fSDavid du Colombier 	nw->win = newwindow();
5919a747e4fSDavid du Colombier 	nw->special = 1;
5929a747e4fSDavid du Colombier 
5939a747e4fSDavid du Colombier 	d = emalloc(sizeof(*d));
5949a747e4fSDavid du Colombier 	d->w = nw;
5959a747e4fSDavid du Colombier 	d->dir = r;
5969a747e4fSDavid du Colombier 	wikiname(nw->win, p);
5979a747e4fSDavid du Colombier 	proccreate(wineventproc, nw->win, STACK);
5989a747e4fSDavid du Colombier 	proccreate(execdiff, d, STACK);
5999a747e4fSDavid du Colombier 	threadcreate(wikithread, nw, STACK);
6009a747e4fSDavid du Colombier 	return 1;
6019a747e4fSDavid du Colombier }
6029a747e4fSDavid du Colombier 
603