plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

xfid.c (21283B)


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