Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include "awiki.h"
2
 
3
Wiki *wlist;
4
 
5
void
6
link(Wiki *w)
7
{
8
	if(w->linked)
9
		return;
10
	w->linked = 1;
11
	w->prev = nil;
12
	w->next = wlist;
13
	if(wlist)
14
		wlist->prev = w;
15
	wlist = w;
16
}
17
 
18
void
19
unlink(Wiki *w)
20
{
21
	if(!w->linked)
22
		return;
23
	w->linked = 0;
24
 
25
	if(w->next)
26
		w->next->prev = w->prev;
27
	if(w->prev)
28
		w->prev->next = w->next;
29
	else
30
		wlist = w->next;
31
 
32
	w->next = nil;
33
	w->prev = nil;
34
}
35
 
36
void
37
wikiname(Window *w, char *name)
38
{
39
	char *p, *q;
40
 
41
	p = emalloc(strlen(dir)+1+strlen(name)+1+1);
42
	strcpy(p, dir);
43
	strcat(p, "/");
44
	strcat(p, name);
45
	for(q=p; *q; q++)
46
		if(*q==' ')
47
			*q = '_';
48
	winname(w, p);
49
	free(p);
50
}
51
 
52
int
53
wikiput(Wiki *w)
54
{
55
	int fd, n;
56
	char buf[1024], *p;
57
	Biobuf *b;
58
 
59
	if((fd = open("new", ORDWR)) < 0){
60
		fprint(2, "Wiki: cannot open raw: %r\n");
61
		return -1;
62
	}
63
 
64
	winopenbody(w->win, OREAD);
65
	b = w->win->body;
66
	if((p = Brdline(b, '\n'))==nil){
67
	Short:
68
		winclosebody(w->win);
69
		fprint(2, "Wiki: no data\n");
70
		close(fd);
71
		return -1;
72
	}
73
	write(fd, p, Blinelen(b));
74
 
75
	snprint(buf, sizeof buf, "D%lud\n", w->time);
76
	if(email)
77
		snprint(buf+strlen(buf), sizeof(buf)-strlen(buf), "A%s\n", email);
78
 
79
	if(Bgetc(b) == '#'){
80
		p = Brdline(b, '\n');
81
		if(p == nil)
82
			goto Short;
83
		snprint(buf+strlen(buf), sizeof(buf)-strlen(buf), "C%s\n", p);
84
	}
85
	write(fd, buf, strlen(buf));
86
	write(fd, "\n\n", 2);
87
 
88
	while((n = Bread(b, buf, sizeof buf)) > 0)
89
		write(fd, buf, n);
90
	winclosebody(w->win);
91
 
92
	werrstr("");
93
	if((n=write(fd, "", 0)) != 0){
94
		fprint(2, "Wiki commit %lud %d %d: %r\n", w->time, fd, n);
95
		close(fd);
96
		return -1;
97
	}
98
	seek(fd, 0, 0);
99
	if((n = read(fd, buf, 300)) < 0){
100
		fprint(2, "Wiki readback: %r\n");
101
		close(fd);
102
		return -1;
103
	}
104
	close(fd);
105
	buf[n] = '\0';
106
	sprint(buf, "%s/", buf);
107
	free(w->arg);
108
	w->arg = estrdup(buf);
109
	w->isnew = 0;
110
	wikiget(w);
111
	wikiname(w->win, w->arg);
112
	return n;
113
}
114
 
115
void
116
wikiget(Wiki *w)
117
{
118
	char *p;
119
	int fd, normal;
120
	Biobuf *bin;
121
 
122
	fprint(w->win->ctl, "dirty\n");
123
 
124
	p = emalloc(strlen(w->arg)+8+1);
125
	strcpy(p, w->arg);
126
	normal = 1;
127
	if(p[strlen(p)-1] == '/'){
128
		normal = 0;
129
		strcat(p, "current");
130
	}else if(strlen(p)>8 && strcmp(p+strlen(p)-8, "/current")==0){
131
		normal = 0;
132
		w->arg[strlen(w->arg)-7] = '\0';
133
	}
134
 
135
	if((fd = open(p, OREAD)) < 0){
136
		fprint(2, "Wiki: cannot read %s: %r\n", p);
137
		winclean(w->win);
138
		return;
139
	}
140
	free(p);
141
 
142
	winopenbody(w->win, OWRITE);
143
	bin = emalloc(sizeof(*bin));
144
	Binit(bin, fd, OREAD);
145
 
146
	p = nil;
147
	if(!normal){
148
		if((p = Brdline(bin, '\n')) == nil){
149
			fprint(2, "Wiki: cannot read title: %r\n");
150
			winclean(w->win);
151
			close(fd);
152
			free(bin);
153
			return;
154
		}
155
		p[Blinelen(bin)-1] = '\0';
156
	}
157
	/* clear window */
158
	if(w->win->data < 0)
159
		w->win->data = winopenfile(w->win, "data");
160
	if(winsetaddr(w->win, ",", 0))
161
		write(w->win->data, "", 0);
162
 
163
	if(!normal)
164
		Bprint(w->win->body, "%s\n\n", p);
165
 
166
	while(p = Brdline(bin, '\n')){
167
		p[Blinelen(bin)-1] = '\0';
168
		if(normal)
169
			Bprint(w->win->body, "%s\n", p);
170
		else{
171
			if(p[0]=='D')
172
				w->time = strtoul(p+1, 0, 10);
173
			else if(p[0]=='#')
174
				Bprint(w->win->body, "%s\n", p+1);
175
		}
176
	}
177
	winclean(w->win);
178
	free(bin);
179
	close(fd);
180
}
181
 
182
static int
183
iscmd(char *s, char *cmd)
184
{
185
	int len;
186
 
187
	len = strlen(cmd);
188
	return strncmp(s, cmd, len)==0 && (s[len]=='\0' || s[len]==' ' || s[len]=='\t' || s[len]=='\n');
189
}
190
 
191
static char*
192
skip(char *s, char *cmd)
193
{
194
	s += strlen(cmd);
195
	while(*s==' ' || *s=='\t' || *s=='\n')
196
		s++;
197
	return s;
198
}
199
 
200
int
201
wikiload(Wiki *w, char *arg)
202
{
203
	char *p, *q, *path, *addr;
204
	int rv;
205
 
206
	p = nil;
207
	if(arg[0] == '/')
208
		path = arg;
209
	else{
210
		p = emalloc(strlen(w->arg)+1+strlen(arg)+1);
211
		strcpy(p, w->arg);
212
		if(q = strrchr(p, '/')){
213
			++q;
214
			*q = '\0';
215
		}else
216
			*p = '\0';
217
		strcat(p, arg);
218
		cleanname(p);
219
		path = p;
220
	}
221
	if(addr=strchr(path, ':'))
222
		*addr++ = '\0';
223
 
224
	rv = wikiopen(path, addr)==0;
225
	free(p);
226
	if(rv)
227
		return 1;
228
	return wikiopen(arg, 0)==0;
229
}
230
 
231
/* return 1 if handled, 0 otherwise */
232
int
233
wikicmd(Wiki *w, char *s)
234
{
235
	char *p;
236
	s = skip(s, "");
237
 
238
	if(iscmd(s, "Del")){
239
		if(windel(w->win, 0))
240
			w->dead = 1;
241
		return 1;
242
	}
243
	if(iscmd(s, "New")){
244
		wikinew(skip(s, "New"));
245
		return 1;
246
	}
247
	if(iscmd(s, "History"))
248
		return wikiload(w, "history.txt");
249
	if(iscmd(s, "Diff"))
250
		return wikidiff(w);
251
	if(iscmd(s, "Get")){
252
		if(winisdirty(w->win) && !w->win->warned){
253
			w->win->warned = 1;
254
			fprint(2, "%s/%s modified\n", dir, w->arg);
255
		}else{
256
			w->win->warned = 0;
257
			wikiget(w);
258
		}
259
		return 1;
260
	}
261
	if(iscmd(s, "Put")){
262
		if((p=strchr(w->arg, '/')) && p[1]!='\0')
263
			fprint(2, "%s/%s is read-only\n", dir, w->arg);
264
		else
265
			wikiput(w);
266
		return 1;
267
	}
268
	return 0;
269
}
270
 
271
/* need to expand selection more than default word */
272
static long
273
eval(Window *w, char *s, ...)
274
{
275
	char buf[64];
276
	va_list arg;
277
 
278
	va_start(arg, s);
279
	vsnprint(buf, sizeof buf, s, arg);
280
	va_end(arg);
281
 
282
	if(winsetaddr(w, buf, 1)==0)
283
		return -1;
284
 
285
	if(pread(w->addr, buf, 24, 0) != 24)
286
		return -1;
287
	return strtol(buf, 0, 10);
288
}
289
 
290
static int
291
getdot(Window *w, long *q0, long *q1)
292
{
293
	char buf[24];
294
 
295
	ctlprint(w->ctl, "addr=dot\n");
296
	if(pread(w->addr, buf, 24, 0) != 24)
297
		return -1;
298
	*q0 = atoi(buf);
299
	*q1 = atoi(buf+12);
300
	return 0;
301
}
302
 
303
static Event*
304
expand(Window *w, Event *e, Event *eacme)
305
{
306
	long q0, q1, x;
307
 
308
	if(getdot(w, &q0, &q1)==0 && q0 <= e->q0 && e->q0 <= q1){
309
		e->q0 = q0;
310
		e->q1 = q1;
311
		return e;
312
	}
313
 
314
	q0 = eval(w, "#%lud-/\\[/", e->q0);
315
	if(q0 < 0)
316
		return eacme;
317
	if(eval(w, "#%lud+/\\]/", q0) < e->q0)	/* [ closes before us */
318
		return eacme;
319
	q1 = eval(w, "#%lud+/\\]/", e->q1);
320
	if(q1 < 0)
321
		return eacme;
322
	if((x=eval(w, "#%lud-/\\[/", q1))==-1 || x > e->q1)	/* ] opens after us */
323
		return eacme;
324
	e->q0 = q0+1;
325
	e->q1 = q1;
326
	return e;
327
}
328
 
329
void
330
acmeevent(Wiki *wiki, Event *e)
331
{
332
	Event *ea, *e2, *eq;
333
	Window *w;
334
	char *s, *t, *buf;
335
	int na;
336
 
337
	w = wiki->win;
338
	switch(e->c1){	/* origin of action */
339
	default:
340
	Unknown:
341
		fprint(2, "unknown message %c%c\n", e->c1, e->c2);
342
		break;
343
 
344
	case 'F':	/* generated by our actions; ignore */
345
		break;
346
 
347
	case 'E':	/* write to body or tag; can't affect us */
348
		break;
349
 
350
	case 'K':	/* type away; we don't care */
351
		if(e->c2 == 'I' || e->c2 == 'D')
352
			w->warned = 0;
353
		break;
354
 
355
	case 'M':	/* mouse event */
356
		switch(e->c2){		/* type of action */
357
		case 'x':	/* mouse: button 2 in tag */
358
		case 'X':	/* mouse: button 2 in body */
359
			ea = nil;
360
			//e2 = nil;
361
			s = e->b;
362
			if(e->flag & 2){	/* null string with non-null expansion */
363
				e2 = recvp(w->cevent);
364
				if(e->nb==0)
365
					s = e2->b;
366
			}
367
			if(e->flag & 8){	/* chorded argument */
368
				ea = recvp(w->cevent);	/* argument */
369
				na = ea->nb;
370
				recvp(w->cevent);		/* ignore origin */
371
			}else
372
				na = 0;
373
 
374
			/* append chorded arguments */
375
			if(na){
376
				t = emalloc(strlen(s)+1+na+1);
377
				sprint(t, "%s %s", s, ea->b);
378
				s = t;
379
			}
380
			/* if it's a known command, do it */
381
			/* if it's a long message, it can't be for us anyway */
382
		//	DPRINT(2, "exec: %s\n", s);
383
			if(!wikicmd(wiki, s))	/* send it back */
384
				winwriteevent(w, e);
385
			if(na)
386
				free(s);
387
			break;
388
 
389
		case 'l':	/* mouse: button 3 in tag */
390
		case 'L':	/* mouse: button 3 in body */
391
			//buf = nil;
392
			eq = e;
393
			if(e->flag & 2){	/* we do our own expansion for loads */
394
				e2 = recvp(w->cevent);
395
				eq = expand(w, eq, e2);
396
			}
397
			s = eq->b;
398
			if(eq->q1>eq->q0 && eq->nb==0){
399
				buf = emalloc((eq->q1-eq->q0)*UTFmax+1);
400
				winread(w, eq->q0, eq->q1, buf);
401
				s = buf;
402
			}
403
			if(!wikiload(wiki, s))
404
				winwriteevent(w, e);
405
			break;
406
 
407
		case 'i':	/* mouse: text inserted in tag */
408
		case 'd':	/* mouse: text deleted from tag */
409
			break;
410
 
411
		case 'I':	/* mouse: text inserted in body */
412
		case 'D':	/* mouse: text deleted from body */
413
			w->warned = 0;
414
			break;
415
 
416
		default:
417
			goto Unknown;
418
		}
419
	}
420
}
421
 
422
void
423
wikithread(void *v)
424
{
425
	char tmp[40];
426
	Event *e;
427
	Wiki *w;
428
 
429
	w = v;
430
 
431
	if(w->isnew){
432
		sprint(tmp, "+new+%d", w->isnew);
433
		wikiname(w->win, tmp);
434
		if(w->arg){
435
			winopenbody(w->win, OWRITE);
436
			Bprint(w->win->body, "%s\n\n", w->arg);
437
		}
438
		winclean(w->win);
439
	}else if(!w->special){
440
		wikiget(w);
441
		wikiname(w->win, w->arg);
442
		if(w->addr)
443
			winselect(w->win, w->addr, 1);
444
	}
445
	fprint(w->win->ctl, "menu\n");
446
	wintagwrite(w->win, "Get History Diff New", 4+8+4+4);
447
	winclean(w->win);
448
 
449
	while(!w->dead && (e = recvp(w->win->cevent)))
450
		acmeevent(w, e);
451
 
452
	windormant(w->win);
453
	unlink(w);
454
	free(w->win);
455
	free(w->arg);
456
	free(w);
457
	threadexits(nil);
458
}
459
 
460
int
461
wikiopen(char *arg, char *addr)
462
{
463
	Dir *d;
464
	char *p;
465
	Wiki *w;
466
 
467
/*
468
	if(arg==nil){
469
		if(write(mapfd, title, strlen(title)) < 0
470
		|| seek(mapfd, 0, 0) < 0 || (n=read(mapfd, tmp, sizeof(tmp)-2)) < 0){
471
			fprint(2, "Wiki: no page '%s' found: %r\n", title);
472
			return -1;
473
		}
474
		if(tmp[n-1] == '\n')
475
			tmp[--n] = '\0';
476
		tmp[n++] = '/';
477
		tmp[n] = '\0';
478
		arg = tmp;
479
	}
480
*/
481
 
482
	/* replace embedded '\n' in links by ' ' */
483
	for(p=arg; *p; p++)
484
		if(*p=='\n')
485
			*p = ' ';
486
 
487
	if(strncmp(arg, dir, strlen(dir))==0 && arg[strlen(dir)]=='/' && arg[strlen(dir)+1])
488
		arg += strlen(dir)+1;
489
	else if(arg[0] == '/')
490
		return -1;
491
 
492
	if((d = dirstat(arg)) == nil)
493
		return -1;
494
 
495
	if((d->mode&DMDIR) && arg[strlen(arg)-1] != '/'){
496
		p = emalloc(strlen(arg)+2);
497
		strcpy(p, arg);
498
		strcat(p, "/");
499
		arg = p;
500
	}else if(!(d->mode&DMDIR) && arg[strlen(arg)-1]=='/'){
501
		arg = estrdup(arg);
502
		arg[strlen(arg)-1] = '\0';
503
	}else
504
		arg = estrdup(arg);
505
	free(d);
506
 
507
	/* rewrite /current into / */
508
	if(strlen(arg) > 8 && strcmp(arg+strlen(arg)-8, "/current")==0)
509
		arg[strlen(arg)-8+1] = '\0';
510
 
511
	/* look for window already open */
512
	for(w=wlist; w; w=w->next){
513
		if(strcmp(w->arg, arg)==0){
514
			ctlprint(w->win->ctl, "show\n");
515
			return 0;
516
		}
517
	}
518
 
519
	w = emalloc(sizeof *w);
520
	w->arg = arg;
521
	w->addr = addr;
522
	w->win = newwindow();
523
	link(w);
524
 
525
	proccreate(wineventproc, w->win, STACK);
526
	threadcreate(wikithread, w, STACK);
527
	return 0;
528
}
529
 
530
void
531
wikinew(char *arg)
532
{
533
	static int n;
534
	Wiki *w;
535
 
536
	w = emalloc(sizeof *w);
537
	if(arg)
538
		arg = estrdup(arg);
539
	w->arg = arg;
540
	w->win = newwindow();
541
	w->isnew = ++n;
542
	proccreate(wineventproc, w->win, STACK);
543
	threadcreate(wikithread, w, STACK);
544
}
545
 
546
typedef struct Diffarg Diffarg;
547
struct Diffarg {
548
	Wiki *w;
549
	char *dir;
550
};
551
 
552
void
553
execdiff(void *v)
554
{
555
	char buf[64];
556
	Diffarg *a;
557
 
558
	a = v;
559
 
560
	rfork(RFFDG);
561
	close(0);
562
	open("/dev/null", OREAD);
563
	sprint(buf, "/mnt/wsys/%d/body", a->w->win->id);
564
	close(1);
565
	open(buf, OWRITE);
566
	close(2);
567
	open(buf, OWRITE);
568
	sprint(buf, "/mnt/wsys/%d", a->w->win->id);
569
	bind(buf, "/dev", MBEFORE);
570
 
571
	procexecl(nil, "/acme/wiki/wiki.diff", "wiki.diff", a->dir, nil);
572
}
573
 
574
int
575
wikidiff(Wiki *w)
576
{
577
	Diffarg *d;
578
	char *p, *q, *r;
579
	Wiki *nw;
580
 
581
	p = emalloc(strlen(w->arg)+10);
582
	strcpy(p, w->arg);
583
	if(q = strchr(p, '/'))
584
		*q = '\0';
585
	r = estrdup(p);
586
	strcat(p, "/+Diff");
587
 
588
	nw = emalloc(sizeof *w);
589
	nw->arg = p;
590
	nw->win = newwindow();
591
	nw->special = 1;
592
 
593
	d = emalloc(sizeof(*d));
594
	d->w = nw;
595
	d->dir = r;
596
	wikiname(nw->win, p);
597
	proccreate(wineventproc, nw->win, STACK);
598
	proccreate(execdiff, d, STACK);
599
	threadcreate(wikithread, nw, STACK);
600
	return 1;
601
}
602