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 #define MAXSNARF 100*1024
15
16 char Einuse[] = "file in use";
17 char Edeleted[] = "window deleted";
18 char Ebadreq[] = "bad graphics request";
19 char Etooshort[] = "buffer too small";
20 char Ebadtile[] = "unknown tile";
21 char Eshort[] = "short i/o request";
22 char Elong[] = "snarf buffer too long";
23 char Eunkid[] = "unknown id in attach";
24 char Ebadrect[] = "bad rectangle in attach";
25 char Ewindow[] = "cannot make window";
26 char Enowindow[] = "window has no image";
27 char Ebadmouse[] = "bad format on /dev/mouse";
28 char Ebadwrect[] = "rectangle outside screen";
29 char Ebadoffset[] = "window read not on scan line boundary";
30 extern char Eperm[];
31
32 static Xfid *xfidfree;
33 static Xfid *xfid;
34 static Channel *cxfidalloc; /* chan(Xfid*) */
35 static Channel *cxfidfree; /* chan(Xfid*) */
36
37 static char *tsnarf;
38 static int ntsnarf;
39
40 void
xfidallocthread(void *)41 xfidallocthread(void*)
42 {
43 Xfid *x;
44 enum { Alloc, Free, N };
45 static Alt alts[N+1];
46
47 alts[Alloc].c = cxfidalloc;
48 alts[Alloc].v = nil;
49 alts[Alloc].op = CHANRCV;
50 alts[Free].c = cxfidfree;
51 alts[Free].v = &x;
52 alts[Free].op = CHANRCV;
53 alts[N].op = CHANEND;
54 for(;;){
55 switch(alt(alts)){
56 case Alloc:
57 x = xfidfree;
58 if(x)
59 xfidfree = x->free;
60 else{
61 x = emalloc(sizeof(Xfid));
62 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
63 x->flushc = chancreate(sizeof(int), 0); /* notification only; no data */
64 x->flushtag = -1;
65 x->next = xfid;
66 xfid = x;
67 threadcreate(xfidctl, x, 16384);
68 }
69 if(x->ref != 0){
70 fprint(2, "%p incref %ld\n", x, x->ref);
71 error("incref");
72 }
73 if(x->flushtag != -1)
74 error("flushtag in allocate");
75 incref(x);
76 sendp(cxfidalloc, x);
77 break;
78 case Free:
79 if(x->ref != 0){
80 fprint(2, "%p decref %ld\n", x, x->ref);
81 error("decref");
82 }
83 if(x->flushtag != -1)
84 error("flushtag in free");
85 x->free = xfidfree;
86 xfidfree = x;
87 break;
88 }
89 }
90 }
91
92 Channel*
xfidinit(void)93 xfidinit(void)
94 {
95 cxfidalloc = chancreate(sizeof(Xfid*), 0);
96 cxfidfree = chancreate(sizeof(Xfid*), 0);
97 threadcreate(xfidallocthread, nil, STACK);
98 return cxfidalloc;
99 }
100
101 void
xfidctl(void * arg)102 xfidctl(void *arg)
103 {
104 Xfid *x;
105 void (*f)(Xfid*);
106 char buf[64];
107
108 x = arg;
109 snprint(buf, sizeof buf, "xfid.%p", x);
110 threadsetname(buf);
111 for(;;){
112 f = recvp(x->c);
113 (*f)(x);
114 if(decref(x) == 0)
115 sendp(cxfidfree, x);
116 }
117 }
118
119 void
xfidflush(Xfid * x)120 xfidflush(Xfid *x)
121 {
122 Fcall t;
123 Xfid *xf;
124
125 for(xf=xfid; xf; xf=xf->next)
126 if(xf->flushtag == x->oldtag){
127 xf->flushtag = -1;
128 xf->flushing = TRUE;
129 incref(xf); /* to hold data structures up at tail of synchronization */
130 if(xf->ref == 1)
131 error("ref 1 in flush");
132 if(canqlock(&xf->active)){
133 qunlock(&xf->active);
134 sendul(xf->flushc, 0);
135 }else{
136 qlock(&xf->active); /* wait for him to finish */
137 qunlock(&xf->active);
138 }
139 xf->flushing = FALSE;
140 if(decref(xf) == 0)
141 sendp(cxfidfree, xf);
142 break;
143 }
144 filsysrespond(x->fs, x, &t, nil);
145 }
146
147 void
xfidattach(Xfid * x)148 xfidattach(Xfid *x)
149 {
150 Fcall t;
151 int id, hideit, scrollit;
152 Window *w;
153 char *err, *n, *dir, errbuf[ERRMAX];
154 int pid, newlymade;
155 Rectangle r;
156 Image *i;
157
158 t.qid = x->f->qid;
159 qlock(&all);
160 w = nil;
161 err = Eunkid;
162 newlymade = FALSE;
163 hideit = 0;
164
165 if(x->aname[0] == 'N'){ /* N 100,100, 200, 200 - old syntax */
166 n = x->aname+1;
167 pid = strtoul(n, &n, 0);
168 if(*n == ',')
169 n++;
170 r.min.x = strtoul(n, &n, 0);
171 if(*n == ',')
172 n++;
173 r.min.y = strtoul(n, &n, 0);
174 if(*n == ',')
175 n++;
176 r.max.x = strtoul(n, &n, 0);
177 if(*n == ',')
178 n++;
179 r.max.y = strtoul(n, &n, 0);
180 Allocate:
181 if(!goodrect(r))
182 err = Ebadrect;
183 else{
184 if(hideit)
185 i = allocimage(display, r, screen->chan, 0, DWhite);
186 else
187 i = allocwindow(wscreen, r, Refbackup, DWhite);
188 if(i){
189 border(i, r, Selborder, display->black, ZP);
190 if(pid == 0)
191 pid = -1; /* make sure we don't pop a shell! - UGH */
192 w = new(i, hideit, scrolling, pid, nil, nil, nil);
193 flushimage(display, 1);
194 newlymade = TRUE;
195 }else
196 err = Ewindow;
197 }
198 }else if(strncmp(x->aname, "new", 3) == 0){ /* new -dx -dy - new syntax, as in wctl */
199 pid = 0;
200 if(parsewctl(nil, ZR, &r, &pid, nil, &hideit, &scrollit, &dir, x->aname, errbuf) < 0)
201 err = errbuf;
202 else
203 goto Allocate;
204 }else{
205 id = atoi(x->aname);
206 w = wlookid(id);
207 }
208 x->f->w = w;
209 if(w == nil){
210 qunlock(&all);
211 x->f->busy = FALSE;
212 filsysrespond(x->fs, x, &t, err);
213 return;
214 }
215 if(!newlymade) /* counteract dec() in winshell() */
216 incref(w);
217 qunlock(&all);
218 filsysrespond(x->fs, x, &t, nil);
219 }
220
221 void
xfidopen(Xfid * x)222 xfidopen(Xfid *x)
223 {
224 Fcall t;
225 Window *w;
226
227 w = x->f->w;
228 if(w->deleted){
229 filsysrespond(x->fs, x, &t, Edeleted);
230 return;
231 }
232 switch(FILE(x->f->qid)){
233 case Qconsctl:
234 if(w->ctlopen){
235 filsysrespond(x->fs, x, &t, Einuse);
236 return;
237 }
238 w->ctlopen = TRUE;
239 break;
240 case Qkbdin:
241 if(w != wkeyboard){
242 filsysrespond(x->fs, x, &t, Eperm);
243 return;
244 }
245 break;
246 case Qmouse:
247 if(w->mouseopen){
248 filsysrespond(x->fs, x, &t, Einuse);
249 return;
250 }
251 /*
252 * Reshaped: there's a race if the appl. opens the
253 * window, is resized, and then opens the mouse,
254 * but that's rare. The alternative is to generate
255 * a resized event every time a new program starts
256 * up in a window that has been resized since the
257 * dawn of time. We choose the lesser evil.
258 */
259 w->resized = FALSE;
260 w->mouseopen = TRUE;
261 break;
262 case Qsnarf:
263 if(x->mode==ORDWR || x->mode==OWRITE){
264 if(tsnarf)
265 free(tsnarf); /* collision, but OK */
266 ntsnarf = 0;
267 tsnarf = malloc(1);
268 }
269 break;
270 case Qwctl:
271 if(x->mode==OREAD || x->mode==ORDWR){
272 /*
273 * It would be much nicer to implement fan-out for wctl reads,
274 * so multiple people can see the resizings, but rio just isn't
275 * structured for that. It's structured for /dev/cons, which gives
276 * alternate data to alternate readers. So to keep things sane for
277 * wctl, we compromise and give an error if two people try to
278 * open it. Apologies.
279 */
280 if(w->wctlopen){
281 filsysrespond(x->fs, x, &t, Einuse);
282 return;
283 }
284 w->wctlopen = TRUE;
285 w->wctlready = 1;
286 wsendctlmesg(w, Wakeup, ZR, nil);
287 }
288 break;
289 }
290 t.qid = x->f->qid;
291 t.iounit = messagesize-IOHDRSZ;
292 x->f->open = TRUE;
293 x->f->mode = x->mode;
294 filsysrespond(x->fs, x, &t, nil);
295 }
296
297 void
xfidclose(Xfid * x)298 xfidclose(Xfid *x)
299 {
300 Fcall t;
301 Window *w;
302 int nb, nulls;
303
304 w = x->f->w;
305 switch(FILE(x->f->qid)){
306 case Qconsctl:
307 if(w->rawing){
308 w->rawing = FALSE;
309 wsendctlmesg(w, Rawoff, ZR, nil);
310 }
311 if(w->holding){
312 w->holding = FALSE;
313 wsendctlmesg(w, Holdoff, ZR, nil);
314 }
315 w->ctlopen = FALSE;
316 break;
317 case Qcursor:
318 w->cursorp = nil;
319 wsetcursor(w, FALSE);
320 break;
321 case Qmouse:
322 w->resized = FALSE;
323 w->mouseopen = FALSE;
324 if(w->i != nil)
325 wsendctlmesg(w, Refresh, w->i->r, nil);
326 break;
327 /* odd behavior but really ok: replace snarf buffer when /dev/snarf is closed */
328 case Qsnarf:
329 if(x->f->mode==ORDWR || x->f->mode==OWRITE){
330 snarf = runerealloc(snarf, ntsnarf+1);
331 cvttorunes(tsnarf, ntsnarf, snarf, &nb, &nsnarf, &nulls);
332 free(tsnarf);
333 tsnarf = nil;
334 ntsnarf = 0;
335 }
336 break;
337 case Qwctl:
338 if(x->f->mode==OREAD || x->f->mode==ORDWR)
339 w->wctlopen = FALSE;
340 break;
341 }
342 wclose(w);
343 filsysrespond(x->fs, x, &t, nil);
344 }
345
346 void
xfidwrite(Xfid * x)347 xfidwrite(Xfid *x)
348 {
349 Fcall fc;
350 int c, cnt, qid, nb, off, nr;
351 char buf[256], *p;
352 Point pt;
353 Window *w;
354 Rune *r;
355 Conswritemesg cwm;
356 Stringpair pair;
357 enum { CWdata, CWflush, NCW };
358 Alt alts[NCW+1];
359
360 w = x->f->w;
361 if(w->deleted){
362 filsysrespond(x->fs, x, &fc, Edeleted);
363 return;
364 }
365 qid = FILE(x->f->qid);
366 cnt = x->count;
367 off = x->offset;
368 x->data[cnt] = 0;
369 switch(qid){
370 case Qcons:
371 nr = x->f->nrpart;
372 if(nr > 0){
373 memmove(x->data+nr, x->data, cnt); /* there's room: see malloc in filsysproc */
374 memmove(x->data, x->f->rpart, nr);
375 cnt += nr;
376 x->f->nrpart = 0;
377 }
378 r = runemalloc(cnt);
379 cvttorunes(x->data, cnt-UTFmax, r, &nb, &nr, nil);
380 /* approach end of buffer */
381 while(fullrune(x->data+nb, cnt-nb)){
382 c = nb;
383 nb += chartorune(&r[nr], x->data+c);
384 if(r[nr])
385 nr++;
386 }
387 if(nb < cnt){
388 memmove(x->f->rpart, x->data+nb, cnt-nb);
389 x->f->nrpart = cnt-nb;
390 }
391 x->flushtag = x->tag;
392
393 alts[CWdata].c = w->conswrite;
394 alts[CWdata].v = &cwm;
395 alts[CWdata].op = CHANRCV;
396 alts[CWflush].c = x->flushc;
397 alts[CWflush].v = nil;
398 alts[CWflush].op = CHANRCV;
399 alts[NCW].op = CHANEND;
400
401 switch(alt(alts)){
402 case CWdata:
403 break;
404 case CWflush:
405 filsyscancel(x);
406 return;
407 }
408
409 /* received data */
410 x->flushtag = -1;
411 if(x->flushing){
412 recv(x->flushc, nil); /* wake up flushing xfid */
413 pair.s = runemalloc(1);
414 pair.ns = 0;
415 send(cwm.cw, &pair); /* wake up window with empty data */
416 filsyscancel(x);
417 return;
418 }
419 qlock(&x->active);
420 pair.s = r;
421 pair.ns = nr;
422 send(cwm.cw, &pair);
423 fc.count = x->count;
424 filsysrespond(x->fs, x, &fc, nil);
425 qunlock(&x->active);
426 return;
427
428 case Qconsctl:
429 if(strncmp(x->data, "holdon", 6)==0){
430 if(w->holding++ == 0)
431 wsendctlmesg(w, Holdon, ZR, nil);
432 break;
433 }
434 if(strncmp(x->data, "holdoff", 7)==0 && w->holding){
435 if(--w->holding == FALSE)
436 wsendctlmesg(w, Holdoff, ZR, nil);
437 break;
438 }
439 if(strncmp(x->data, "rawon", 5)==0){
440 if(w->holding){
441 w->holding = FALSE;
442 wsendctlmesg(w, Holdoff, ZR, nil);
443 }
444 if(w->rawing++ == 0)
445 wsendctlmesg(w, Rawon, ZR, nil);
446 break;
447 }
448 if(strncmp(x->data, "rawoff", 6)==0 && w->rawing){
449 if(--w->rawing == 0)
450 wsendctlmesg(w, Rawoff, ZR, nil);
451 break;
452 }
453 filsysrespond(x->fs, x, &fc, "unknown control message");
454 return;
455
456 case Qcursor:
457 if(cnt < 2*4+2*2*16)
458 w->cursorp = nil;
459 else{
460 w->cursor.offset.x = BGLONG(x->data+0*4);
461 w->cursor.offset.y = BGLONG(x->data+1*4);
462 memmove(w->cursor.clr, x->data+2*4, 2*2*16);
463 w->cursorp = &w->cursor;
464 }
465 wsetcursor(w, !sweeping);
466 break;
467
468 case Qlabel:
469 if(off != 0){
470 filsysrespond(x->fs, x, &fc, "non-zero offset writing label");
471 return;
472 }
473 free(w->label);
474 w->label = emalloc(cnt+1);
475 memmove(w->label, x->data, cnt);
476 w->label[cnt] = 0;
477 break;
478
479 case Qmouse:
480 if(w!=input || Dx(w->screenr)<=0)
481 break;
482 if(x->data[0] != 'm'){
483 filsysrespond(x->fs, x, &fc, Ebadmouse);
484 return;
485 }
486 p = nil;
487 pt.x = strtoul(x->data+1, &p, 0);
488 if(p == nil){
489 filsysrespond(x->fs, x, &fc, Eshort);
490 return;
491 }
492 pt.y = strtoul(p, nil, 0);
493 if(w==input && wpointto(mouse->xy)==w)
494 wsendctlmesg(w, Movemouse, Rpt(pt, pt), nil);
495 break;
496
497 case Qsnarf:
498 /* always append only */
499 if(ntsnarf > MAXSNARF){ /* avoid thrashing when people cut huge text */
500 filsysrespond(x->fs, x, &fc, Elong);
501 return;
502 }
503 tsnarf = erealloc(tsnarf, ntsnarf+cnt+1); /* room for NUL */
504 memmove(tsnarf+ntsnarf, x->data, cnt);
505 ntsnarf += cnt;
506 snarfversion++;
507 break;
508
509 case Qwdir:
510 if(cnt == 0)
511 break;
512 if(x->data[cnt-1] == '\n'){
513 if(cnt == 1)
514 break;
515 x->data[cnt-1] = '\0';
516 }
517 /* assume data comes in a single write */
518 /*
519 * Problem: programs like dossrv, ftp produce illegal UTF;
520 * we must cope by converting it first.
521 */
522 snprint(buf, sizeof buf, "%.*s", cnt, x->data);
523 if(buf[0] == '/'){
524 free(w->dir);
525 w->dir = estrdup(buf);
526 }else{
527 p = emalloc(strlen(w->dir) + 1 + strlen(buf) + 1);
528 sprint(p, "%s/%s", w->dir, buf);
529 free(w->dir);
530 w->dir = cleanname(p);
531 }
532 break;
533
534 case Qkbdin:
535 keyboardsend(x->data, cnt);
536 break;
537
538 case Qwctl:
539 if(writewctl(x, buf) < 0){
540 filsysrespond(x->fs, x, &fc, buf);
541 return;
542 }
543 flushimage(display, 1);
544 break;
545
546 default:
547 fprint(2, buf, "unknown qid %d in write\n", qid);
548 sprint(buf, "unknown qid in write");
549 filsysrespond(x->fs, x, &fc, buf);
550 return;
551 }
552 fc.count = cnt;
553 filsysrespond(x->fs, x, &fc, nil);
554 }
555
556 int
readwindow(Image * i,char * t,Rectangle r,int offset,int n)557 readwindow(Image *i, char *t, Rectangle r, int offset, int n)
558 {
559 int ww, y;
560
561 offset -= 5*12;
562 ww = bytesperline(r, screen->depth);
563 r.min.y += offset/ww;
564 if(r.min.y >= r.max.y)
565 return 0;
566 y = r.min.y + n/ww;
567 if(y < r.max.y)
568 r.max.y = y;
569 if(r.max.y <= r.min.y)
570 return 0;
571 return unloadimage(i, r, (uchar*)t, n);
572 }
573
574 void
xfidread(Xfid * x)575 xfidread(Xfid *x)
576 {
577 Fcall fc;
578 int n, off, cnt, c;
579 uint qid;
580 char buf[128], *t;
581 char cbuf[30];
582 Window *w;
583 Mouse ms;
584 Rectangle r;
585 Image *i;
586 Channel *c1, *c2; /* chan (tuple(char*, int)) */
587 Consreadmesg crm;
588 Mousereadmesg mrm;
589 Consreadmesg cwrm;
590 Stringpair pair;
591 enum { CRdata, CRflush, NCR };
592 enum { MRdata, MRflush, NMR };
593 enum { WCRdata, WCRflush, NWCR };
594 Alt alts[NCR+1];
595
596 w = x->f->w;
597 if(w->deleted){
598 filsysrespond(x->fs, x, &fc, Edeleted);
599 return;
600 }
601 qid = FILE(x->f->qid);
602 off = x->offset;
603 cnt = x->count;
604 switch(qid){
605 case Qcons:
606 x->flushtag = x->tag;
607
608 alts[CRdata].c = w->consread;
609 alts[CRdata].v = &crm;
610 alts[CRdata].op = CHANRCV;
611 alts[CRflush].c = x->flushc;
612 alts[CRflush].v = nil;
613 alts[CRflush].op = CHANRCV;
614 alts[NMR].op = CHANEND;
615
616 switch(alt(alts)){
617 case CRdata:
618 break;
619 case CRflush:
620 filsyscancel(x);
621 return;
622 }
623
624 /* received data */
625 x->flushtag = -1;
626 c1 = crm.c1;
627 c2 = crm.c2;
628 t = malloc(cnt+UTFmax+1); /* room to unpack partial rune plus */
629 pair.s = t;
630 pair.ns = cnt;
631 send(c1, &pair);
632 if(x->flushing){
633 recv(x->flushc, nil); /* wake up flushing xfid */
634 recv(c2, nil); /* wake up window and toss data */
635 free(t);
636 filsyscancel(x);
637 return;
638 }
639 qlock(&x->active);
640 recv(c2, &pair);
641 fc.data = pair.s;
642 fc.count = pair.ns;
643 filsysrespond(x->fs, x, &fc, nil);
644 free(t);
645 qunlock(&x->active);
646 break;
647
648 case Qlabel:
649 n = strlen(w->label);
650 if(off > n)
651 off = n;
652 if(off+cnt > n)
653 cnt = n-off;
654 fc.data = w->label+off;
655 fc.count = cnt;
656 filsysrespond(x->fs, x, &fc, nil);
657 break;
658
659 case Qmouse:
660 x->flushtag = x->tag;
661
662 alts[MRdata].c = w->mouseread;
663 alts[MRdata].v = &mrm;
664 alts[MRdata].op = CHANRCV;
665 alts[MRflush].c = x->flushc;
666 alts[MRflush].v = nil;
667 alts[MRflush].op = CHANRCV;
668 alts[NMR].op = CHANEND;
669
670 switch(alt(alts)){
671 case MRdata:
672 break;
673 case MRflush:
674 filsyscancel(x);
675 return;
676 }
677
678 /* received data */
679 x->flushtag = -1;
680 if(x->flushing){
681 recv(x->flushc, nil); /* wake up flushing xfid */
682 recv(mrm.cm, nil); /* wake up window and toss data */
683 filsyscancel(x);
684 return;
685 }
686 qlock(&x->active);
687 recv(mrm.cm, &ms);
688 c = 'm';
689 if(w->resized)
690 c = 'r';
691 n = sprint(buf, "%c%11d %11d %11d %11ld ", c, ms.xy.x, ms.xy.y, ms.buttons, ms.msec);
692 w->resized = 0;
693 fc.data = buf;
694 fc.count = min(n, cnt);
695 filsysrespond(x->fs, x, &fc, nil);
696 qunlock(&x->active);
697 break;
698
699 case Qcursor:
700 filsysrespond(x->fs, x, &fc, "cursor read not implemented");
701 break;
702
703 /* The algorithm for snarf and text is expensive but easy and rarely used */
704 case Qsnarf:
705 getsnarf();
706 if(nsnarf)
707 t = runetobyte(snarf, nsnarf, &n);
708 else {
709 t = nil;
710 n = 0;
711 }
712 goto Text;
713
714 case Qtext:
715 t = wcontents(w, &n);
716 goto Text;
717
718 Text:
719 if(off > n){
720 off = n;
721 cnt = 0;
722 }
723 if(off+cnt > n)
724 cnt = n-off;
725 fc.data = t+off;
726 fc.count = cnt;
727 filsysrespond(x->fs, x, &fc, nil);
728 free(t);
729 break;
730
731 case Qwdir:
732 t = estrdup(w->dir);
733 n = strlen(t);
734 goto Text;
735
736 case Qwinid:
737 n = sprint(buf, "%11d ", w->id);
738 t = estrdup(buf);
739 goto Text;
740
741
742 case Qwinname:
743 n = strlen(w->name);
744 if(n == 0){
745 filsysrespond(x->fs, x, &fc, "window has no name");
746 break;
747 }
748 t = estrdup(w->name);
749 goto Text;
750
751 case Qwindow:
752 i = w->i;
753 if(i == nil || Dx(w->screenr)<=0){
754 filsysrespond(x->fs, x, &fc, Enowindow);
755 return;
756 }
757 r = w->screenr;
758 goto caseImage;
759
760 case Qscreen:
761 i = display->image;
762 if(i == nil){
763 filsysrespond(x->fs, x, &fc, "no top-level screen");
764 break;
765 }
766 r = i->r;
767 /* fall through */
768
769 caseImage:
770 if(off < 5*12){
771 n = sprint(buf, "%11s %11d %11d %11d %11d ",
772 chantostr(cbuf, screen->chan),
773 i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y);
774 t = estrdup(buf);
775 goto Text;
776 }
777 t = malloc(cnt);
778 fc.data = t;
779 n = readwindow(i, t, r, off, cnt); /* careful; fc.count is unsigned */
780 if(n < 0){
781 buf[0] = 0;
782 errstr(buf, sizeof buf);
783 filsysrespond(x->fs, x, &fc, buf);
784 }else{
785 fc.count = n;
786 filsysrespond(x->fs, x, &fc, nil);
787 }
788 free(t);
789 return;
790
791 case Qwctl: /* read returns rectangle, hangs if not resized */
792 if(cnt < 4*12){
793 filsysrespond(x->fs, x, &fc, Etooshort);
794 break;
795 }
796 x->flushtag = x->tag;
797
798 alts[WCRdata].c = w->wctlread;
799 alts[WCRdata].v = &cwrm;
800 alts[WCRdata].op = CHANRCV;
801 alts[WCRflush].c = x->flushc;
802 alts[WCRflush].v = nil;
803 alts[WCRflush].op = CHANRCV;
804 alts[NMR].op = CHANEND;
805
806 switch(alt(alts)){
807 case WCRdata:
808 break;
809 case WCRflush:
810 filsyscancel(x);
811 return;
812 }
813
814 /* received data */
815 x->flushtag = -1;
816 c1 = cwrm.c1;
817 c2 = cwrm.c2;
818 t = malloc(cnt+1); /* be sure to have room for NUL */
819 pair.s = t;
820 pair.ns = cnt+1;
821 send(c1, &pair);
822 if(x->flushing){
823 recv(x->flushc, nil); /* wake up flushing xfid */
824 recv(c2, nil); /* wake up window and toss data */
825 free(t);
826 filsyscancel(x);
827 return;
828 }
829 qlock(&x->active);
830 recv(c2, &pair);
831 fc.data = pair.s;
832 if(pair.ns > cnt)
833 pair.ns = cnt;
834 fc.count = pair.ns;
835 filsysrespond(x->fs, x, &fc, nil);
836 free(t);
837 qunlock(&x->active);
838 break;
839
840 default:
841 fprint(2, "unknown qid %d in read\n", qid);
842 sprint(buf, "unknown qid in read");
843 filsysrespond(x->fs, x, &fc, buf);
844 break;
845 }
846 }
847