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 <u.h>
2
#include <libc.h>
3
#include <draw.h>
4
#include <thread.h>
5
#include <cursor.h>
6
#include <mouse.h>
7
#include <keyboard.h>
8
#include <frame.h>
9
#include <fcall.h>
10
#include <plumb.h>
11
#include "dat.h"
12
#include "fns.h"
13
 
14
enum
15
{
16
	Ctlsize	= 5*12
17
};
18
 
19
char	Edel[]		= "deleted window";
20
char	Ebadctl[]		= "ill-formed control message";
21
char	Ebadaddr[]	= "bad address syntax";
22
char	Eaddr[]		= "address out of range";
23
char	Einuse[]		= "already in use";
24
char	Ebadevent[]	= "bad event syntax";
25
extern char Eperm[];
26
 
27
static
28
void
29
clampaddr(Window *w)
30
{
31
	if(w->addr.q0 < 0)
32
		w->addr.q0 = 0;
33
	if(w->addr.q1 < 0)
34
		w->addr.q1 = 0;
35
	if(w->addr.q0 > w->body.file->nc)
36
		w->addr.q0 = w->body.file->nc;
37
	if(w->addr.q1 > w->body.file->nc)
38
		w->addr.q1 = w->body.file->nc;
39
}
40
 
41
void
42
xfidctl(void *arg)
43
{
44
	Xfid *x;
45
	void (*f)(Xfid*);
46
 
47
	threadsetname("xfidctlthread");
48
	x = arg;
49
	for(;;){
50
		f = recvp(x->c);
51
		(*f)(x);
52
		flushimage(display, 1);
53
		sendp(cxfidfree, x);
54
	}
55
}
56
 
57
void
58
xfidflush(Xfid *x)
59
{
60
	Fcall fc;
61
	int i, j;
62
	Window *w;
63
	Column *c;
64
	Xfid *wx;
65
 
66
	/* search windows for matching tag */
67
	qlock(&row);
68
	for(j=0; j<row.ncol; j++){
69
		c = row.col[j];
70
		for(i=0; i<c->nw; i++){
71
			w = c->w[i];
72
			winlock(w, 'E');
73
			wx = w->eventx;
74
			if(wx!=nil && wx->tag==x->oldtag){
75
				w->eventx = nil;
76
				wx->flushed = TRUE;
77
				sendp(wx->c, nil);
78
				winunlock(w);
79
				goto out;
80
			}
81
			winunlock(w);
82
		}
83
	}
84
out:
85
	qunlock(&row);
86
	respond(x, &fc, nil);
87
}
88
 
89
void
90
xfidopen(Xfid *x)
91
{
92
	Fcall fc;
93
	Window *w;
94
	Text *t;
95
	char *s;
96
	Rune *r;
97
	int m, n, q, q0, q1;
98
 
99
	w = x->f->w;
100
	t = &w->body;
101
	if(w){
102
		winlock(w, 'E');
103
		q = FILE(x->f->qid);
104
		switch(q){
105
		case QWaddr:
106
			if(w->nopen[q]++ == 0){
107
				w->addr = (Range){0,0};
108
				w->limit = (Range){-1,-1};
109
			}
110
			break;
111
		case QWdata:
112
		case QWxdata:
113
			w->nopen[q]++;
114
			break;
115
		case QWevent:
116
			if(w->nopen[q]++ == 0){
117
				if(!w->isdir && w->col!=nil){
118
					w->filemenu = FALSE;
119
					winsettag(w);
120
				}
121
			}
122
			break;
123
		case QWrdsel:
124
			/*
125
			 * Use a temporary file.
126
			 * A pipe would be the obvious, but we can't afford the
127
			 * broken pipe notification.  Using the code to read QWbody
128
			 * is n², which should probably also be fixed.  Even then,
129
			 * though, we'd need to squirrel away the data in case it's
130
			 * modified during the operation, e.g. by |sort
131
			 */
132
			if(w->rdselfd > 0){
133
				winunlock(w);
134
				respond(x, &fc, Einuse);
135
				return;
136
			}
137
			w->rdselfd = tempfile();
138
			if(w->rdselfd < 0){
139
				winunlock(w);
140
				respond(x, &fc, "can't create temp file");
141
				return;
142
			}
143
			w->nopen[q]++;
144
			q0 = t->q0;
145
			q1 = t->q1;
146
			r = fbufalloc();
147
			s = fbufalloc();
148
			while(q0 < q1){
149
				n = q1 - q0;
150
				if(n > BUFSIZE/UTFmax)
151
					n = BUFSIZE/UTFmax;
152
				bufread(t->file, q0, r, n);
153
				m = snprint(s, BUFSIZE+1, "%.*S", n, r);
154
				if(write(w->rdselfd, s, m) != m){
155
					warning(nil, "can't write temp file for pipe command %r\n");
156
					break;
157
				}
158
				q0 += n;
159
			}
160
			fbuffree(s);
161
			fbuffree(r);
162
			break;
163
		case QWwrsel:
164
			w->nopen[q]++;
165
			seq++;
166
			filemark(t->file);
167
			cut(t, t, nil, FALSE, TRUE, nil, 0);
168
			w->wrselrange = (Range){t->q1, t->q1};
169
			w->nomark = TRUE;
170
			break;
171
		case QWeditout:
172
			if(editing == FALSE){
173
				winunlock(w);
174
				respond(x, &fc, Eperm);
175
				return;
176
			}
177
			w->wrselrange = (Range){t->q1, t->q1};
178
			break;
179
		}
180
		winunlock(w);
181
	}
182
	fc.qid = x->f->qid;
183
	fc.iounit = messagesize-IOHDRSZ;
184
	x->f->open = TRUE;
185
	respond(x, &fc, nil);
186
}
187
 
188
void
189
xfidclose(Xfid *x)
190
{
191
	Fcall fc;
192
	Window *w;
193
	int q;
194
	Text *t;
195
 
196
	w = x->f->w;
197
	x->f->busy = FALSE;
198
	if(x->f->open == FALSE){
199
		if(w != nil)
200
			winclose(w);
201
		respond(x, &fc, nil);
202
		return;
203
	}
204
 
205
	x->f->open = FALSE;
206
	if(w){
207
		winlock(w, 'E');
208
		q = FILE(x->f->qid);
209
		switch(q){
210
		case QWctl:
211
			if(w->ctlfid!=~0 && w->ctlfid==x->f->fid){
212
				w->ctlfid = ~0;
213
				qunlock(&w->ctllock);
214
			}
215
			break;
216
		case QWdata:
217
		case QWxdata:
218
			w->nomark = FALSE;
219
			/* fall through */
220
		case QWaddr:
221
		case QWevent:	/* BUG: do we need to shut down Xfid? */
222
			if(--w->nopen[q] == 0){
223
				if(q == QWdata || q == QWxdata)
224
					w->nomark = FALSE;
225
				if(q==QWevent && !w->isdir && w->col!=nil){
226
					w->filemenu = TRUE;
227
					winsettag(w);
228
				}
229
				if(q == QWevent){
230
					free(w->dumpstr);
231
					free(w->dumpdir);
232
					w->dumpstr = nil;
233
					w->dumpdir = nil;
234
				}
235
			}
236
			break;
237
		case QWrdsel:
238
			close(w->rdselfd);
239
			w->rdselfd = 0;
240
			break;
241
		case QWwrsel:
242
			w->nomark = FALSE;
243
			t = &w->body;
244
			/* before: only did this if !w->noscroll, but that didn't seem right in practice */
245
			textshow(t, min(w->wrselrange.q0, t->file->nc),
246
				min(w->wrselrange.q1, t->file->nc), 1);
247
			textscrdraw(t);
248
			break;
249
		}
250
		winunlock(w);
251
		winclose(w);
252
	}
253
	respond(x, &fc, nil);
254
}
255
 
256
void
257
xfidread(Xfid *x)
258
{
259
	Fcall fc;
260
	int n, q;
261
	uint off;
262
	char *b;
263
	char buf[256];
264
	Window *w;
265
 
266
	q = FILE(x->f->qid);
267
	w = x->f->w;
268
	if(w == nil){
269
		fc.count = 0;
270
		switch(q){
271
		case Qcons:
272
		case Qlabel:
273
			break;
274
		case Qindex:
275
			xfidindexread(x);
276
			return;
277
		default:
278
			warning(nil, "unknown qid %d\n", q);
279
			break;
280
		}
281
		respond(x, &fc, nil);
282
		return;
283
	}
284
	winlock(w, 'F');
285
	if(w->col == nil){
286
		winunlock(w);
287
		respond(x, &fc, Edel);
288
		return;
289
	}
290
	off = x->offset;
291
	switch(q){
292
	case QWaddr:
293
		textcommit(&w->body, TRUE);
294
		clampaddr(w);
295
		sprint(buf, "%11d %11d ", w->addr.q0, w->addr.q1);
296
		goto Readbuf;
297
 
298
	case QWbody:
299
		xfidutfread(x, &w->body, w->body.file->nc, QWbody);
300
		break;
301
 
302
	case QWctl:
303
		b = winctlprint(w, buf, 1);
304
		goto Readb;
305
 
306
	Readbuf:
307
		b = buf;
308
	Readb:
309
		n = strlen(b);
310
		if(off > n)
311
			off = n;
312
		if(off+x->count > n)
313
			x->count = n-off;
314
		fc.count = x->count;
315
		fc.data = b+off;
316
		respond(x, &fc, nil);
317
		if(b != buf)
318
			free(b);
319
		break;
320
 
321
	case QWevent:
322
		xfideventread(x, w);
323
		break;
324
 
325
	case QWdata:
326
		/* BUG: what should happen if q1 > q0? */
327
		if(w->addr.q0 > w->body.file->nc){
328
			respond(x, &fc, Eaddr);
329
			break;
330
		}
331
		w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->body.file->nc);
332
		w->addr.q1 = w->addr.q0;
333
		break;
334
 
335
	case QWxdata:
336
		/* BUG: what should happen if q1 > q0? */
337
		if(w->addr.q0 > w->body.file->nc){
338
			respond(x, &fc, Eaddr);
339
			break;
340
		}
341
		w->addr.q0 += xfidruneread(x, &w->body, w->addr.q0, w->addr.q1);
342
		break;
343
 
344
	case QWtag:
345
		xfidutfread(x, &w->tag, w->tag.file->nc, QWtag);
346
		break;
347
 
348
	case QWrdsel:
349
		seek(w->rdselfd, off, 0);
350
		n = x->count;
351
		if(n > BUFSIZE)
352
			n = BUFSIZE;
353
		b = fbufalloc();
354
		n = read(w->rdselfd, b, n);
355
		if(n < 0){
356
			respond(x, &fc, "I/O error in temp file");
357
			break;
358
		}
359
		fc.count = n;
360
		fc.data = b;
361
		respond(x, &fc, nil);
362
		fbuffree(b);
363
		break;
364
 
365
	default:
366
		sprint(buf, "unknown qid %d in read", q);
367
		respond(x, &fc, nil);
368
	}
369
	winunlock(w);
370
}
371
 
372
static Rune*
373
fullrunewrite(Xfid *x, int *inr)
374
{
375
	int q, cnt, c, nb, nr;
376
	Rune *r;
377
 
378
	q = x->f->nrpart;
379
	cnt = x->count;
380
	if(q > 0){
381
		memmove(x->data+q, x->data, cnt);	/* there's room; see fsysproc */
382
		memmove(x->data, x->f->rpart, q);
383
		cnt += q;
384
		x->f->nrpart = 0;
385
	}
386
	r = runemalloc(cnt);
387
	cvttorunes(x->data, cnt-UTFmax, r, &nb, &nr, nil);
388
	/* approach end of buffer */
389
	while(fullrune(x->data+nb, cnt-nb)){
390
		c = nb;
391
		nb += chartorune(&r[nr], x->data+c);
392
		if(r[nr])
393
			nr++;
394
	}
395
	if(nb < cnt){
396
		memmove(x->f->rpart, x->data+nb, cnt-nb);
397
		x->f->nrpart = cnt-nb;
398
	}
399
	*inr = nr;
400
	return r;
401
}
402
 
403
void
404
xfidwrite(Xfid *x)
405
{
406
	Fcall fc;
407
	int c, qid, nb, nr, eval;
408
	char buf[64], *err;
409
	Window *w;
410
	Rune *r;
411
	Range a;
412
	Text *t;
413
	uint q0, tq0, tq1;
414
 
415
	qid = FILE(x->f->qid);
416
	w = x->f->w;
417
	if(w){
418
		c = 'F';
419
		if(qid==QWtag || qid==QWbody)
420
			c = 'E';
421
		winlock(w, c);
422
		if(w->col == nil){
423
			winunlock(w);
424
			respond(x, &fc, Edel);
425
			return;
426
		}
427
	}
428
	x->data[x->count] = 0;
429
	switch(qid){
430
	case Qcons:
431
		w = errorwin(x->f->mntdir, 'X');
432
		t=&w->body;
433
		goto BodyTag;
434
 
435
	case Qlabel:
436
		fc.count = x->count;
437
		respond(x, &fc, nil);
438
		break;
439
 
440
	case QWaddr:
441
		x->data[x->count] = 0;
442
		r = bytetorune(x->data, &nr);
443
		t = &w->body;
444
		wincommit(w, t);
445
		eval = TRUE;
446
		a = address(x->f->mntdir, t, w->limit, w->addr, r, 0, nr, rgetc, &eval, (uint*)&nb);
447
		free(r);
448
		if(nb < nr){
449
			respond(x, &fc, Ebadaddr);
450
			break;
451
		}
452
		if(!eval){
453
			respond(x, &fc, Eaddr);
454
			break;
455
		}
456
		w->addr = a;
457
		fc.count = x->count;
458
		respond(x, &fc, nil);
459
		break;
460
 
461
	case Qeditout:
462
	case QWeditout:
463
		r = fullrunewrite(x, &nr);
464
		if(w)
465
			err = edittext(w, w->wrselrange.q1, r, nr);
466
		else
467
			err = edittext(nil, 0, r, nr);
468
		free(r);
469
		if(err != nil){
470
			respond(x, &fc, err);
471
			break;
472
		}
473
		fc.count = x->count;
474
		respond(x, &fc, nil);
475
		break;
476
 
477
	case QWerrors:
478
		w = errorwinforwin(w);
479
		t = &w->body;
480
		goto BodyTag;
481
 
482
	case QWbody:
483
	case QWwrsel:
484
		t = &w->body;
485
		goto BodyTag;
486
 
487
	case QWctl:
488
		xfidctlwrite(x, w);
489
		break;
490
 
491
	case QWdata:
492
		a = w->addr;
493
		t = &w->body;
494
		wincommit(w, t);
495
		if(a.q0>t->file->nc || a.q1>t->file->nc){
496
			respond(x, &fc, Eaddr);
497
			break;
498
		}
499
		r = runemalloc(x->count);
500
		cvttorunes(x->data, x->count, r, &nb, &nr, nil);
501
		if(w->nomark == FALSE){
502
			seq++;
503
			filemark(t->file);
504
		}
505
		q0 = a.q0;
506
		if(a.q1 > q0){
507
			textdelete(t, q0, a.q1, TRUE);
508
			w->addr.q1 = q0;
509
		}
510
		tq0 = t->q0;
511
		tq1 = t->q1;
512
		textinsert(t, q0, r, nr, TRUE);
513
		if(tq0 >= q0)
514
			tq0 += nr;
515
		if(tq1 >= q0)
516
			tq1 += nr;
517
		textsetselect(t, tq0, tq1);
518
		if(!t->w->noscroll)
519
			textshow(t, q0, q0+nr, 0);
520
		textscrdraw(t);
521
		winsettag(w);
522
		free(r);
523
		w->addr.q0 += nr;
524
		w->addr.q1 = w->addr.q0;
525
		fc.count = x->count;
526
		respond(x, &fc, nil);
527
		break;
528
 
529
	case QWevent:
530
		xfideventwrite(x, w);
531
		break;
532
 
533
	case QWtag:
534
		t = &w->tag;
535
		goto BodyTag;
536
 
537
	BodyTag:
538
		r = fullrunewrite(x, &nr);
539
		if(nr > 0){
540
			wincommit(w, t);
541
			if(qid == QWwrsel){
542
				q0 = w->wrselrange.q1;
543
				if(q0 > t->file->nc)
544
					q0 = t->file->nc;
545
			}else
546
				q0 = t->file->nc;
547
			if(qid == QWtag)
548
				textinsert(t, q0, r, nr, TRUE);
549
			else{
550
				if(w->nomark == FALSE){
551
					seq++;
552
					filemark(t->file);
553
				}
554
				q0 = textbsinsert(t, q0, r, nr, TRUE, &nr);
555
				textsetselect(t, t->q0, t->q1);	/* insert could leave it somewhere else */
556
				if(qid!=QWwrsel && !t->w->noscroll)
557
					textshow(t, q0+nr, q0+nr, 1);
558
				textscrdraw(t);
559
			}
560
			winsettag(w);
561
			if(qid == QWwrsel)
562
				w->wrselrange.q1 += nr;
563
			free(r);
564
		}
565
		fc.count = x->count;
566
		respond(x, &fc, nil);
567
		break;
568
 
569
	default:
570
		sprint(buf, "unknown qid %d in write", qid);
571
		respond(x, &fc, buf);
572
		break;
573
	}
574
	if(w)
575
		winunlock(w);
576
}
577
 
578
void
579
xfidctlwrite(Xfid *x, Window *w)
580
{
581
	Fcall fc;
582
	int i, m, n, nb, nr, nulls;
583
	Rune *r;
584
	char *err, *p, *pp, *q, *e;
585
	int isfbuf, scrdraw, settag;
586
	Text *t;
587
 
588
	err = nil;
589
	e = x->data+x->count;
590
	scrdraw = FALSE;
591
	settag = FALSE;
592
	isfbuf = TRUE;
593
	if(x->count < RBUFSIZE)
594
		r = fbufalloc();
595
	else{
596
		isfbuf = FALSE;
597
		r = emalloc(x->count*UTFmax+1);
598
	}
599
	x->data[x->count] = 0;
600
	textcommit(&w->tag, TRUE);
601
	for(n=0; n<x->count; n+=m){
602
		p = x->data+n;
603
		if(strncmp(p, "lock", 4) == 0){	/* make window exclusive use */
604
			qlock(&w->ctllock);
605
			w->ctlfid = x->f->fid;
606
			m = 4;
607
		}else
608
		if(strncmp(p, "unlock", 6) == 0){	/* release exclusive use */
609
			w->ctlfid = ~0;
610
			qunlock(&w->ctllock);
611
			m = 6;
612
		}else
613
		if(strncmp(p, "clean", 5) == 0){	/* mark window 'clean', seq=0 */
614
			t = &w->body;
615
			t->eq0 = ~0;
616
			filereset(t->file);
617
			t->file->mod = FALSE;
618
			w->dirty = FALSE;
619
			settag = TRUE;
620
			m = 5;
621
		}else
622
		if(strncmp(p, "dirty", 5) == 0){	/* mark window 'dirty' */
623
			t = &w->body;
624
			/* doesn't change sequence number, so "Put" won't appear.  it shouldn't. */
625
			t->file->mod = TRUE;
626
			w->dirty = TRUE;
627
			settag = TRUE;
628
			m = 5;
629
		}else
630
		if(strncmp(p, "show", 4) == 0){	/* show dot */
631
			t = &w->body;
632
			textshow(t, t->q0, t->q1, 1);
633
			m = 4;
634
		}else
635
		if(strncmp(p, "name ", 5) == 0){	/* set file name */
636
			pp = p+5;
637
			m = 5;
638
			q = memchr(pp, '\n', e-pp);
639
			if(q==nil || q==pp){
640
				err = Ebadctl;
641
				break;
642
			}
643
			*q = 0;
644
			nulls = FALSE;
645
			cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
646
			if(nulls){
647
				err = "nulls in file name";
648
				break;
649
			}
650
			for(i=0; i<nr; i++)
651
				if(r[i] <= ' '){
652
					err = "bad character in file name";
653
					goto out;
654
				}
655
out:
656
			seq++;
657
			filemark(w->body.file);
658
			winsetname(w, r, nr);
659
			m += (q+1) - pp;
660
		}else
661
		if(strncmp(p, "dump ", 5) == 0){	/* set dump string */
662
			pp = p+5;
663
			m = 5;
664
			q = memchr(pp, '\n', e-pp);
665
			if(q==nil || q==pp){
666
				err = Ebadctl;
667
				break;
668
			}
669
			*q = 0;
670
			nulls = FALSE;
671
			cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
672
			if(nulls){
673
				err = "nulls in dump string";
674
				break;
675
			}
676
			w->dumpstr = runetobyte(r, nr);
677
			m += (q+1) - pp;
678
		}else
679
		if(strncmp(p, "dumpdir ", 8) == 0){	/* set dump directory */
680
			pp = p+8;
681
			m = 8;
682
			q = memchr(pp, '\n', e-pp);
683
			if(q==nil || q==pp){
684
				err = Ebadctl;
685
				break;
686
			}
687
			*q = 0;
688
			nulls = FALSE;
689
			cvttorunes(pp, q-pp, r, &nb, &nr, &nulls);
690
			if(nulls){
691
				err = "nulls in dump directory string";
692
				break;
693
			}
694
			w->dumpdir = runetobyte(r, nr);
695
			m += (q+1) - pp;
696
		}else
697
		if(strncmp(p, "delete", 6) == 0){	/* delete for sure */
698
			colclose(w->col, w, TRUE);
699
			m = 6;
700
		}else
701
		if(strncmp(p, "del", 3) == 0){	/* delete, but check dirty */
702
			if(!winclean(w, TRUE)){
703
				err = "file dirty";
704
				break;
705
			}
706
			colclose(w->col, w, TRUE);
707
			m = 3;
708
		}else
709
		if(strncmp(p, "get", 3) == 0){	/* get file */
710
			get(&w->body, nil, nil, FALSE, XXX, nil, 0);
711
			m = 3;
712
		}else
713
		if(strncmp(p, "put", 3) == 0){	/* put file */
714
			put(&w->body, nil, nil, XXX, XXX, nil, 0);
715
			m = 3;
716
		}else
717
		if(strncmp(p, "dot=addr", 8) == 0){	/* set dot */
718
			textcommit(&w->body, TRUE);
719
			clampaddr(w);
720
			w->body.q0 = w->addr.q0;
721
			w->body.q1 = w->addr.q1;
722
			textsetselect(&w->body, w->body.q0, w->body.q1);
723
			settag = TRUE;
724
			m = 8;
725
		}else
726
		if(strncmp(p, "addr=dot", 8) == 0){	/* set addr */
727
			w->addr.q0 = w->body.q0;
728
			w->addr.q1 = w->body.q1;
729
			m = 8;
730
		}else
731
		if(strncmp(p, "limit=addr", 10) == 0){	/* set limit */
732
			textcommit(&w->body, TRUE);
733
			clampaddr(w);
734
			w->limit.q0 = w->addr.q0;
735
			w->limit.q1 = w->addr.q1;
736
			m = 10;
737
		}else
738
		if(strncmp(p, "nomark", 6) == 0){	/* turn off automatic marking */
739
			w->nomark = TRUE;
740
			m = 6;
741
		}else
742
		if(strncmp(p, "mark", 4) == 0){	/* mark file */
743
			seq++;
744
			filemark(w->body.file);
745
			settag = TRUE;
746
			m = 4;
747
		}else
748
		if(strncmp(p, "nomenu", 6) == 0){	/* turn off automatic menu */
749
			w->filemenu = FALSE;
750
			m = 6;
751
		}else
752
		if(strncmp(p, "menu", 4) == 0){	/* enable automatic menu */
753
			w->filemenu = TRUE;
754
			m = 4;
755
		}else
756
		if(strncmp(p, "noscroll", 8) == 0){	/* turn off automatic scrolling */
757
			w->noscroll = TRUE;
758
			m = 8;
759
		}else
760
		if(strncmp(p, "cleartag", 8) == 0){	/* wipe tag right of bar */
761
			wincleartag(w);
762
			settag = TRUE;
763
			m = 8;
764
		}else
765
		if(strncmp(p, "scroll", 6) == 0){	/* turn on automatic scrolling (writes to body only) */
766
			w->noscroll = FALSE;
767
			m = 6;
768
		}else{
769
			err = Ebadctl;
770
			break;
771
		}
772
		while(p[m] == '\n')
773
			m++;
774
	}
775
 
776
	if(isfbuf)
777
		fbuffree(r);
778
	else
779
		free(r);
780
	if(err)
781
		n = 0;
782
	fc.count = n;
783
	respond(x, &fc, err);
784
	if(settag)
785
		winsettag(w);
786
	if(scrdraw)
787
		textscrdraw(&w->body);
788
}
789
 
790
void
791
xfideventwrite(Xfid *x, Window *w)
792
{
793
	Fcall fc;
794
	int m, n;
795
	Rune *r;
796
	char *err, *p, *q;
797
	int isfbuf;
798
	Text *t;
799
	int c;
800
	uint q0, q1;
801
 
802
	err = nil;
803
	isfbuf = TRUE;
804
	if(x->count < RBUFSIZE)
805
		r = fbufalloc();
806
	else{
807
		isfbuf = FALSE;
808
		r = emalloc(x->count*UTFmax+1);
809
	}
810
	for(n=0; n<x->count; n+=m){
811
		p = x->data+n;
812
		w->owner = *p++;	/* disgusting */
813
		c = *p++;
814
		while(*p == ' ')
815
			p++;
816
		q0 = strtoul(p, &q, 10);
817
		if(q == p)
818
			goto Rescue;
819
		p = q;
820
		while(*p == ' ')
821
			p++;
822
		q1 = strtoul(p, &q, 10);
823
		if(q == p)
824
			goto Rescue;
825
		p = q;
826
		while(*p == ' ')
827
			p++;
828
		if(*p++ != '\n')
829
			goto Rescue;
830
		m = p-(x->data+n);
831
		if('a'<=c && c<='z')
832
			t = &w->tag;
833
		else if('A'<=c && c<='Z')
834
			t = &w->body;
835
		else
836
			goto Rescue;
837
		if(q0>t->file->nc || q1>t->file->nc || q0>q1)
838
			goto Rescue;
839
 
840
		qlock(&row);	/* just like mousethread */
841
		switch(c){
842
		case 'x':
843
		case 'X':
844
			execute(t, q0, q1, TRUE, nil);
845
			break;
846
		case 'l':
847
		case 'L':
848
			look3(t, q0, q1, TRUE);
849
			break;
850
		default:
851
			qunlock(&row);
852
			goto Rescue;
853
		}
854
		qunlock(&row);
855
 
856
	}
857
 
858
    Out:
859
	if(isfbuf)
860
		fbuffree(r);
861
	else
862
		free(r);
863
	if(err)
864
		n = 0;
865
	fc.count = n;
866
	respond(x, &fc, err);
867
	return;
868
 
869
    Rescue:
870
	err = Ebadevent;
871
	goto Out;
872
}
873
 
874
void
875
xfidutfread(Xfid *x, Text *t, uint q1, int qid)
876
{
877
	Fcall fc;
878
	Window *w;
879
	Rune *r;
880
	char *b, *b1;
881
	uint q, off, boff;
882
	int m, n, nr, nb;
883
 
884
	w = t->w;
885
	wincommit(w, t);
886
	off = x->offset;
887
	r = fbufalloc();
888
	b = fbufalloc();
889
	b1 = fbufalloc();
890
	n = 0;
891
	if(qid==w->utflastqid && off>=w->utflastboff && w->utflastq<=q1){
892
		boff = w->utflastboff;
893
		q = w->utflastq;
894
	}else{
895
		/* BUG: stupid code: scan from beginning */
896
		boff = 0;
897
		q = 0;
898
	}
899
	w->utflastqid = qid;
900
	while(q<q1 && n<x->count){
901
		/*
902
		 * Updating here avoids partial rune problem: we're always on a
903
		 * char boundary. The cost is we will usually do one more read
904
		 * than we really need, but that's better than being n^2.
905
		 */
906
		w->utflastboff = boff;
907
		w->utflastq = q;
908
		nr = q1-q;
909
		if(nr > BUFSIZE/UTFmax)
910
			nr = BUFSIZE/UTFmax;
911
		bufread(t->file, q, r, nr);
912
		nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
913
		if(boff >= off){
914
			m = nb;
915
			if(boff+m > off+x->count)
916
				m = off+x->count - boff;
917
			memmove(b1+n, b, m);
918
			n += m;
919
		}else if(boff+nb > off){
920
			if(n != 0)
921
				error("bad count in utfrune");
922
			m = nb - (off-boff);
923
			if(m > x->count)
924
				m = x->count;
925
			memmove(b1, b+(off-boff), m);
926
			n += m;
927
		}
928
		boff += nb;
929
		q += nr;
930
	}
931
	fbuffree(r);
932
	fbuffree(b);
933
	fc.count = n;
934
	fc.data = b1;
935
	respond(x, &fc, nil);
936
	fbuffree(b1);
937
}
938
 
939
int
940
xfidruneread(Xfid *x, Text *t, uint q0, uint q1)
941
{
942
	Fcall fc;
943
	Window *w;
944
	Rune *r, junk;
945
	char *b, *b1;
946
	uint q, boff;
947
	int i, rw, m, n, nr, nb;
948
 
949
	w = t->w;
950
	wincommit(w, t);
951
	r = fbufalloc();
952
	b = fbufalloc();
953
	b1 = fbufalloc();
954
	n = 0;
955
	q = q0;
956
	boff = 0;
957
	while(q<q1 && n<x->count){
958
		nr = q1-q;
959
		if(nr > BUFSIZE/UTFmax)
960
			nr = BUFSIZE/UTFmax;
961
		bufread(t->file, q, r, nr);
962
		nb = snprint(b, BUFSIZE+1, "%.*S", nr, r);
963
		m = nb;
964
		if(boff+m > x->count){
965
			i = x->count - boff;
966
			/* copy whole runes only */
967
			m = 0;
968
			nr = 0;
969
			while(m < i){
970
				rw = chartorune(&junk, b+m);
971
				if(m+rw > i)
972
					break;
973
				m += rw;
974
				nr++;
975
			}
976
			if(m == 0)
977
				break;
978
		}
979
		memmove(b1+n, b, m);
980
		n += m;
981
		boff += nb;
982
		q += nr;
983
	}
984
	fbuffree(r);
985
	fbuffree(b);
986
	fc.count = n;
987
	fc.data = b1;
988
	respond(x, &fc, nil);
989
	fbuffree(b1);
990
	return q-q0;
991
}
992
 
993
void
994
xfideventread(Xfid *x, Window *w)
995
{
996
	Fcall fc;
997
	char *b;
998
	int i, n;
999
 
1000
	i = 0;
1001
	x->flushed = FALSE;
1002
	while(w->nevents == 0){
1003
		if(i){
1004
			if(!x->flushed)
1005
				respond(x, &fc, "window shut down");
1006
			return;
1007
		}
1008
		w->eventx = x;
1009
		winunlock(w);
1010
		recvp(x->c);
1011
		winlock(w, 'F');
1012
		i++;
1013
	}
1014
 
1015
	n = w->nevents;
1016
	if(n > x->count)
1017
		n = x->count;
1018
	fc.count = n;
1019
	fc.data = w->events;
1020
	respond(x, &fc, nil);
1021
	b = w->events;
1022
	w->events = estrdup(w->events+n);
1023
	free(b);
1024
	w->nevents -= n;
1025
}
1026
 
1027
void
1028
xfidindexread(Xfid *x)
1029
{
1030
	Fcall fc;
1031
	int i, j, m, n, nmax, isbuf, cnt, off;
1032
	Window *w;
1033
	char *b;
1034
	Rune *r;
1035
	Column *c;
1036
 
1037
	qlock(&row);
1038
	nmax = 0;
1039
	for(j=0; j<row.ncol; j++){
1040
		c = row.col[j];
1041
		for(i=0; i<c->nw; i++){
1042
			w = c->w[i];
1043
			nmax += Ctlsize + w->tag.file->nc*UTFmax + 1;
1044
		}
1045
	}
1046
	nmax++;
1047
	isbuf = (nmax<=RBUFSIZE);
1048
	if(isbuf)
1049
		b = (char*)x->buf;
1050
	else
1051
		b = emalloc(nmax);
1052
	r = fbufalloc();
1053
	n = 0;
1054
	for(j=0; j<row.ncol; j++){
1055
		c = row.col[j];
1056
		for(i=0; i<c->nw; i++){
1057
			w = c->w[i];
1058
			/* only show the currently active window of a set */
1059
			if(w->body.file->curtext != &w->body)
1060
				continue;
1061
			winctlprint(w, b+n, 0);
1062
			n += Ctlsize;
1063
			m = min(RBUFSIZE, w->tag.file->nc);
1064
			bufread(w->tag.file, 0, r, m);
1065
			m = n + snprint(b+n, nmax-n-1, "%.*S", m, r);
1066
			while(n<m && b[n]!='\n')
1067
				n++;
1068
			b[n++] = '\n';
1069
		}
1070
	}
1071
	qunlock(&row);
1072
	off = x->offset;
1073
	cnt = x->count;
1074
	if(off > n)
1075
		off = n;
1076
	if(off+cnt > n)
1077
		cnt = n-off;
1078
	fc.count = cnt;
1079
	memmove(r, b+off, cnt);
1080
	fc.data = (char*)r;
1081
	if(!isbuf)
1082
		free(b);
1083
	respond(x, &fc, nil);
1084
	fbuffree(r);
1085
}