Blob


1 #include <u.h>
2 #include <signal.h>
3 #include <libc.h>
4 #include <ctype.h>
5 #include <draw.h>
6 #include <thread.h>
7 #include <mouse.h>
8 #include <cursor.h>
9 #include <keyboard.h>
10 #include <frame.h>
11 #include <plumb.h>
12 #include <complete.h>
13 #include "term.h"
15 enum
16 {
17 STACK = 32768
18 };
20 int noecho = 0;
22 void servedevtext(void);
23 void listenproc(void*);
24 void textthread(void*);
26 typedef struct Text Text;
27 typedef struct Readbuf Readbuf;
29 enum
30 {
31 HiWater = 640000, /* max size of history */
32 LoWater = 400000, /* min size of history after max'ed */
33 MinWater = 20000,
34 };
36 /* various geometric paramters */
37 enum
38 {
39 Scrollwid = 12, /* width of scroll bar */
40 Scrollgap = 4, /* gap right of scroll bar */
41 Maxtab = 4,
42 };
44 enum
45 {
46 Cut,
47 Paste,
48 Snarf,
49 Send,
50 Plumb,
51 Scroll,
52 Cooked,
53 };
55 #define ESC 0x1B
56 #define CUT 0x18 /* ctrl-x */
57 #define COPY 0x03 /* crtl-c */
58 #define PASTE 0x16 /* crtl-v */
60 #define READBUFSIZE 8192
61 #define TRUE 1
62 #define FALSE 0
65 struct Text
66 {
67 Frame *f; /* frame ofr terminal */
68 Mouse m;
69 uint nr; /* num of runes in term */
70 uint maxr; /* max num of runes in r */
71 Rune *r; /* runes for term */
72 uint nraw; /* num of runes in raw buffer */
73 Rune *raw; /* raw buffer */
74 uint org; /* first rune on the screen */
75 uint q0; /* start of selection region */
76 uint q1; /* end of selection region */
77 uint qh; /* unix point */
78 int npart; /* partial runes read from console */
79 char part[UTFmax];
80 int nsnarf; /* snarf buffer */
81 Rune *snarf;
82 };
84 struct Readbuf
85 {
86 short n; /* # bytes in buf */
87 uchar data[READBUFSIZE]; /* data bytes */
88 };
90 void mouse(void);
91 void domenu2(int);
92 void loop(void);
93 void geom(void);
94 void fill(void);
95 void tcheck(void);
96 void updatesel(void);
97 void doreshape(void);
98 void runewrite(Rune*, int);
99 void consread(void);
100 void conswrite(char*, int);
101 int bswidth(Rune c, uint start, int eatnl);
102 void cut(void);
103 void paste(Rune*, int, int);
104 void snarfupdate(void);
105 void snarf(void);
106 void show(uint);
107 void key(Rune);
108 void setorigin(uint org, int exact);
109 uint line2q(uint);
110 uint backnl(uint, uint);
111 int cansee(uint);
112 uint backnl(uint, uint);
113 void addraw(Rune*, int);
114 void mselect(void);
115 void doubleclick(uint *q0, uint *q1);
116 int clickmatch(int cl, int cr, int dir, uint *q);
117 Rune *strrune(Rune *s, Rune c);
118 int consready(void);
119 Rectangle scrpos(Rectangle r, ulong p0, ulong p1, ulong tot);
120 void scrdraw(void);
121 void scroll(int);
122 void hostproc(void *arg);
123 void hoststart(void);
124 void plumbstart(void);
125 void plumb(uint, uint);
126 void plumbclick(uint*, uint*);
127 uint insert(Rune*, int, uint, int);
128 void scrolldown(int);
129 void scrollup(int);
131 #define runemalloc(n) malloc((n)*sizeof(Rune))
132 #define runerealloc(a, n) realloc(a, (n)*sizeof(Rune))
133 #define runemove(a, b, n) memmove(a, b, (n)*sizeof(Rune))
134 Rectangle scrollr; /* scroll bar rectangle */
135 Rectangle lastsr; /* used for scroll bar */
136 int holdon; /* hold mode */
137 int rawon(void); /* raw mode */
138 int cooked; /* force cooked */
139 int scrolling; /* window scrolls */
140 int clickmsec; /* time of last click */
141 uint clickq0; /* point of last click */
142 int rcfd;
143 int sfd; /* slave fd, to get/set terminal mode */
144 int rcpid;
145 int maxtab;
146 int use9wm;
147 Mousectl* mc;
148 Keyboardctl* kc;
149 Channel* hostc;
150 Readbuf rcbuf[2];
151 int mainpid;
152 int acmecolors;
153 int plumbfd;
154 int button2exec;
155 int label(Rune*, int);
156 char wdir[1024];
157 char childwdir[1024];
158 void hangupnote(void*, char*);
159 char thesocket[100];
161 char *menu2str[] = {
162 "cut",
163 "paste",
164 "snarf",
165 "send",
166 "plumb",
167 "scroll",
168 "cooked",
170 };
172 Image* cols[NCOL];
173 Image* hcols[NCOL];
174 Image* palegrey;
175 Image* paleblue;
176 Image* blue;
177 Image *plumbcolor;
178 Image *execcolor;
180 Menu menu2 =
182 menu2str
183 };
185 Text t;
187 Cursor whitearrow = {
188 {0, 0},
189 {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
190 0xFF, 0xF0, 0xFF, 0xF0, 0xFF, 0xF8, 0xFF, 0xFC,
191 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC,
192 0xF3, 0xF8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, },
193 {0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x06, 0xC0, 0x1C,
194 0xC0, 0x30, 0xC0, 0x30, 0xC0, 0x38, 0xC0, 0x1C,
195 0xC0, 0x0E, 0xC0, 0x07, 0xCE, 0x0E, 0xDF, 0x1C,
196 0xD3, 0xB8, 0xF1, 0xF0, 0xE0, 0xE0, 0xC0, 0x40, }
197 };
199 void
200 usage(void)
202 fprint(2, "usage: 9term [-ars] [-W winsize] [cmd ...]\n");
203 threadexitsall("usage");
206 void
207 threadmain(int argc, char *argv[])
209 char *p, *font;
210 char buf[32];
212 rfork(RFNOTEG);
213 font = nil;
214 _wantfocuschanges = 1;
215 mainpid = getpid();
216 ARGBEGIN{
217 default:
218 usage();
219 case 'a': /* acme mode */
220 button2exec++;
221 break;
222 case 'f':
223 font = EARGF(usage());
224 break;
225 case 's':
226 scrolling++;
227 break;
228 case 'w': /* started from "rio" window manager */
229 use9wm = 1;
230 break;
231 case 'W':
232 winsize = EARGF(usage());
233 break;
234 }ARGEND
236 if(font)
237 putenv("font", font);
239 p = getenv("tabstop");
240 if(p == 0)
241 p = getenv("TABSTOP");
242 if(p != 0 && maxtab <= 0)
243 maxtab = strtoul(p, 0, 0);
244 if(maxtab <= 0)
245 maxtab = 4; /* be like rio */
247 snprint(buf, sizeof buf, "%d", maxtab);
248 putenv("tabstop", buf);
250 initdraw(0, nil, "9term");
251 notify(hangupnote);
252 noteenable("sys: child");
253 servedevtext();
255 mc = initmouse(nil, screen);
256 kc = initkeyboard(nil);
257 rcpid = rcstart(argc, argv, &rcfd, &sfd);
258 hoststart();
259 plumbstart();
261 t.f = mallocz(sizeof(Frame), 1);
263 if(acmecolors){
264 cols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
265 cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DDarkyellow);
266 cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DYellowgreen);
267 }else{
268 cols[BACK] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DWhite);
269 cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
270 cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x999999FF);
272 cols[TEXT] = display->black;
273 cols[HTEXT] = display->black;
274 palegrey = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x666666FF);
276 hcols[BACK] = cols[BACK];
277 hcols[HIGH] = cols[HIGH];
278 blue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DMedblue);
279 paleblue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DGreyblue);
281 hcols[BORD] = blue;
282 hcols[TEXT] = hcols[BORD];
283 hcols[HTEXT] = hcols[TEXT];
285 plumbcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x006600FF);
286 execcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAA0000FF);
288 if(!blue || !palegrey || !paleblue || !plumbcolor || !execcolor)
289 sysfatal("alloc colors: %r");
290 draw(screen, screen->r, cols[BACK], nil, ZP);
291 geom();
292 loop();
295 void
296 hangupnote(void *a, char *msg)
298 if(getpid() != mainpid)
299 noted(NDFLT);
300 if(strcmp(msg, "hangup") == 0 && rcpid != 0){
301 postnote(PNGROUP, rcpid, "hangup");
302 noted(NDFLT);
304 if(strstr(msg, "child")){
305 /* bug: do better */
306 threadexitsall(0);
308 noted(NDFLT);
311 void
312 hostproc(void *arg)
314 Channel *c;
315 int i, n, which;
317 c = arg;
319 i = 0;
320 for(;;){
321 /* Let typing have a go -- maybe there's a rubout waiting. */
322 yield();
324 i = 1-i; /* toggle */
325 n = read(rcfd, rcbuf[i].data, sizeof rcbuf[i].data);
326 if(n <= 0){
327 if(n < 0)
328 fprint(2, "9term: host read error: %r\n");
329 threadexitsall("host");
331 rcbuf[i].n = n;
332 which = i;
333 send(c, &which);
337 void
338 hoststart(void)
340 hostc = chancreate(sizeof(int), 0);
341 proccreate(hostproc, hostc, 32*1024);
344 void
345 loop(void)
347 Rune r;
348 int i;
349 Alt a[5];
351 a[0].c = mc->c;
352 a[0].v = &mc->m;
353 a[0].op = CHANRCV;
355 a[1].c = kc->c;
356 a[1].v = &r;
357 a[1].op = CHANRCV;
359 a[2].c = hostc;
360 a[2].v = &i;
361 a[2].op = CHANRCV;
363 a[3].c = mc->resizec;
364 a[3].v = nil;
365 a[3].op = CHANRCV;
367 a[4].c = nil;
368 a[4].v = nil;
369 a[4].op = CHANEND;
371 for(;;) {
372 tcheck();
374 scrdraw();
375 flushimage(display, 1);
376 a[2].op = CHANRCV;
377 if(!scrolling && t.qh > t.org+t.f->nchars)
378 a[2].op = CHANNOP;;
379 switch(alt(a)) {
380 default:
381 sysfatal("impossible");
382 case 0:
383 t.m = mc->m;
384 mouse();
385 break;
386 case 1:
387 key(r);
388 break;
389 case 2:
390 conswrite((char*)rcbuf[i].data, rcbuf[i].n);
391 break;
392 case 3:
393 doreshape();
394 break;
399 void
400 doreshape(void)
402 if(getwindow(display, Refnone) < 0)
403 sysfatal("can't reattach to window");
404 draw(screen, screen->r, cols[BACK], nil, ZP);
405 geom();
406 scrdraw();
409 void
410 geom(void)
412 Point p;
413 Rectangle r;
415 if(!acmecolors){
416 if(_windowhasfocus){
417 cols[TEXT] = cols[HTEXT] = display->black;
418 hcols[TEXT] = hcols[HTEXT] = blue;
419 }else{
420 cols[TEXT] = cols[HTEXT] = palegrey;
421 hcols[TEXT] = hcols[HTEXT] = paleblue;
425 r = screen->r;
426 r.min.y++;
427 r.max.y--;
429 scrollr = r;
430 scrollr.max.x = r.min.x+Scrollwid;
431 lastsr = Rect(0,0,0,0);
433 r.min.x += Scrollwid+Scrollgap;
435 frclear(t.f, 0);
436 frinit(t.f, r, font, screen, holdon ? hcols : cols);
437 t.f->maxtab = maxtab*stringwidth(font, "0");
438 fill();
439 updatesel();
441 p = stringsize(font, "0");
442 if(p.x == 0 || p.y == 0)
443 return;
445 updatewinsize(Dy(r)/p.y, Dx(r)/p.x, Dx(r), Dy(r));
448 void
449 drawhold(int holdon)
451 if(holdon)
452 setcursor(mc, &whitearrow);
453 else
454 setcursor(mc, nil);
456 draw(screen, screen->r, cols[BACK], nil, ZP);
457 geom();
458 scrdraw();
461 void
462 wordclick(uint *q0, uint *q1)
464 while(*q1<t.nr && !isspace(t.r[*q1]))
465 (*q1)++;
466 while(*q0>0 && !isspace(t.r[*q0-1]))
467 (*q0)--;
470 int
471 aselect(uint *q0, uint *q1, Image *color)
473 int cancel;
474 uint oldq0, oldq1, newq0, newq1;
476 /* save old selection */
477 oldq0 = t.q0;
478 oldq1 = t.q1;
480 /* sweep out area and record it */
481 t.f->cols[HIGH] = color;
482 t.f->cols[HTEXT] = display->white;
483 mselect();
484 newq0 = t.q0;
485 newq1 = t.q1;
487 cancel = 0;
488 if(t.m.buttons != 0){
489 while(t.m.buttons){
490 readmouse(mc);
491 t.m = mc->m;
493 cancel = 1;
496 /* restore old selection */
497 t.f->cols[HIGH] = cols[HIGH];
498 t.f->cols[HTEXT] = cols[HTEXT];
499 t.q0 = oldq0;
500 t.q1 = oldq1;
501 updatesel();
503 if(cancel)
504 return -1;
506 /* selected a region */
507 if(newq0 < newq1){
508 *q0 = newq0;
509 *q1 = newq1;
510 return 0;
513 /* clicked inside previous selection */
514 /* the "<=" in newq0 <= oldq1 allows us to click the right edge */
515 if(oldq0 <= newq0 && newq0 <= oldq1){
516 *q0 = oldq0;
517 *q1 = oldq1;
518 return 0;
521 /* just a click */
522 *q0 = newq0;
523 *q1 = newq1;
524 return 0;
527 static Rune Lnl[1] = { '\n' };
529 void
530 mouse(void)
532 int but;
533 uint q0, q1;
535 but = t.m.buttons;
537 if(but != 1 && but != 2 && but != 4 && but != 8 && but != 16)
538 return;
540 if (ptinrect(t.m.xy, scrollr)) {
541 scroll(but);
542 if(t.qh<=t.org+t.f->nchars)
543 consread();
544 return;
547 switch(but) {
548 case 1:
549 mselect();
550 break;
551 case 2:
552 if(button2exec){
553 if(aselect(&q0, &q1, execcolor) >= 0){
554 if(q0 == q1)
555 wordclick(&q0, &q1);
556 if(q0 == q1)
557 break;
558 t.q0 = t.q1 = t.nr;
559 updatesel();
560 paste(t.r+q0, q1-q0, 1);
561 if(t.r[q1-1] != '\n')
562 paste(Lnl, 1, 1);
564 break;
566 domenu2(2);
567 break;
568 case 4:
569 bouncemouse(&t.m);
570 break;
571 /*
572 if(aselect(&q0, &q1, plumbcolor) >= 0)
573 plumb(q0, q1);
574 break;
575 */
576 case 8:
577 scrollup(mousescrollsize(t.f->maxlines));
578 break;
579 case 16:
580 scrolldown(mousescrollsize(t.f->maxlines));
581 break;
585 void
586 mselect(void)
588 int b, x, y;
589 uint q0;
591 b = t.m.buttons;
592 q0 = frcharofpt(t.f, t.m.xy) + t.org;
593 if(t.m.msec-clickmsec<500 && clickq0==q0 && t.q0==t.q1 && b==1){
594 doubleclick(&t.q0, &t.q1);
595 updatesel();
596 /* t.t.i->flush(); */
597 x = t.m.xy.x;
598 y = t.m.xy.y;
599 /* stay here until something interesting happens */
600 do {
601 readmouse(mc);
602 t.m = mc->m;
603 } while(t.m.buttons==b && abs(t.m.xy.x-x)<4 && abs(t.m.xy.y-y)<4);
604 t.m.xy.x = x; /* in case we're calling frselect */
605 t.m.xy.y = y;
606 clickmsec = 0;
609 if(t.m.buttons == b) {
610 frselect(t.f, mc);
611 t.m = mc->m;
612 t.q0 = t.f->p0 + t.org;
613 t.q1 = t.f->p1 + t.org;
614 clickmsec = t.m.msec;
615 clickq0 = t.q0;
617 if((t.m.buttons != b) &&(b&1)){
618 enum{Cancut = 1, Canpaste = 2} state = Cancut | Canpaste;
619 while(t.m.buttons){
620 if(t.m.buttons&2){
621 if(state&Cancut){
622 snarf();
623 cut();
624 state = Canpaste;
626 }else if(t.m.buttons&4){
627 if(state&Canpaste){
628 snarfupdate();
629 if(t.nsnarf){
630 paste(t.snarf, t.nsnarf, 0);
632 state = Cancut;
635 readmouse(mc);
636 t.m = mc->m;
641 Rune newline[] = { '\n', 0 };
643 void
644 domenu2(int but)
646 if(scrolling)
647 menu2str[Scroll] = "+ scroll";
648 else
649 menu2str[Scroll] = "- scroll";
650 if(cooked)
651 menu2str[Cooked] = "+ mustecho";
652 else
653 menu2str[Cooked] = "- mustecho";
655 switch(menuhit(but, mc, &menu2, nil)){
656 case -1:
657 break;
658 case Cut:
659 snarf();
660 cut();
661 if(scrolling)
662 show(t.q0);
663 break;
664 case Paste:
665 snarfupdate();
666 paste(t.snarf, t.nsnarf, 0);
667 if(scrolling)
668 show(t.q0);
669 break;
670 case Snarf:
671 snarf();
672 if(scrolling)
673 show(t.q0);
674 break;
675 case Send:
676 if(t.q0 != t.q1)
677 snarf();
678 else
679 snarfupdate();
680 t.q0 = t.q1 = t.nr;
681 updatesel();
682 paste(t.snarf, t.nsnarf, 1);
683 if(t.nsnarf == 0 || t.snarf[t.nsnarf-1] != '\n')
684 paste(newline, 1, 1);
685 show(t.nr);
686 consread();
687 break;
688 case Scroll:
689 scrolling = !scrolling;
690 if (scrolling) {
691 show(t.nr);
692 consread();
694 break;
695 case Plumb:
696 plumb(t.q0, t.q1);
697 break;
698 case Cooked:
699 cooked = !cooked;
700 break;
701 default:
702 sysfatal("bad menu item");
706 int
707 windfilewidth(uint q0, int oneelement)
709 uint q;
710 Rune r;
712 q = q0;
713 while(q > 0){
714 r = t.r[q-1];
715 if(r<=' ')
716 break;
717 if(oneelement && r=='/')
718 break;
719 --q;
721 return q0-q;
724 void
725 showcandidates(Completion *c)
727 int i;
728 Fmt f;
729 Rune *rp;
730 uint nr, qline, q0;
731 char *s;
733 runefmtstrinit(&f);
734 if (c->nmatch == 0)
735 s = "[no matches in ";
736 else
737 s = "[";
738 if(c->nfile > 32)
739 fmtprint(&f, "%s%d files]\n", s, c->nfile);
740 else{
741 fmtprint(&f, "%s", s);
742 for(i=0; i<c->nfile; i++){
743 if(i > 0)
744 fmtprint(&f, " ");
745 fmtprint(&f, "%s", c->filename[i]);
747 fmtprint(&f, "]\n");
749 /* place text at beginning of line before host point */
750 qline = t.qh;
751 while(qline>0 && t.r[qline-1] != '\n')
752 qline--;
754 rp = runefmtstrflush(&f);
755 nr = runestrlen(rp);
757 q0 = t.q0;
758 q0 += insert(rp, nr, qline, 0) - qline;
759 free(rp);
760 t.q0 = q0+nr;
761 t.q1 = q0+nr;
762 updatesel();
765 Rune*
766 namecomplete(void)
768 int nstr, npath;
769 Rune *rp, *path, *str;
770 Completion *c;
771 char *s, *dir, *root;
773 /* control-f: filename completion; works back to white space or / */
774 if(t.q0<t.nr && t.r[t.q0]>' ') /* must be at end of word */
775 return nil;
776 nstr = windfilewidth(t.q0, TRUE);
777 str = runemalloc(nstr);
778 runemove(str, t.r+(t.q0-nstr), nstr);
779 npath = windfilewidth(t.q0-nstr, FALSE);
780 path = runemalloc(npath);
781 runemove(path, t.r+(t.q0-nstr-npath), npath);
782 rp = nil;
784 /* is path rooted? if not, we need to make it relative to window path */
785 if(npath>0 && path[0]=='/'){
786 dir = malloc(UTFmax*npath+1);
787 sprint(dir, "%.*S", npath, path);
788 }else{
789 if(strcmp(wdir, "") == 0)
790 root = ".";
791 else
792 root = wdir;
793 dir = malloc(strlen(root)+1+UTFmax*npath+1);
794 sprint(dir, "%s/%.*S", root, npath, path);
796 dir = cleanname(dir);
798 s = smprint("%.*S", nstr, str);
799 c = complete(dir, s);
800 free(s);
801 if(c == nil)
802 goto Return;
804 if(!c->advance)
805 showcandidates(c);
807 if(c->advance)
808 rp = runesmprint("%s", c->string);
810 Return:
811 freecompletion(c);
812 free(dir);
813 free(path);
814 free(str);
815 return rp;
818 void
819 scrollup(int n)
821 setorigin(backnl(t.org, n), 1);
824 void
825 scrolldown(int n)
827 setorigin(line2q(n), 1);
828 if(t.qh<=t.org+t.f->nchars)
829 consread();
832 void
833 key(Rune r)
835 Rune *rp;
836 int nr;
838 if(r == 0)
839 return;
840 switch(r){
841 case Kpgup:
842 scrollup(t.f->maxlines*2/3);
843 return;
844 case Kpgdown:
845 scrolldown(t.f->maxlines*2/3);
846 return;
847 case Kup:
848 scrollup(t.f->maxlines/3);
849 return;
850 case Kdown:
851 scrolldown(t.f->maxlines/3);
852 return;
853 case Kleft:
854 if(t.q0 > 0){
855 t.q0--;
856 t.q1 = t.q0;
857 updatesel();
858 show(t.q0);
860 return;
861 case Kright:
862 if(t.q1 < t.nr){
863 t.q1++;
864 t.q0 = t.q1;
865 updatesel();
866 show(t.q1);
868 return;
869 case Khome:
870 show(0);
871 return;
872 case Kend:
873 case 0x05:
874 show(t.nr);
875 return;
877 /*
878 * Non-standard extensions.
879 */
880 case CUT:
881 snarf();
882 cut();
883 if(scrolling)
884 show(t.q0);
885 return;
886 case COPY:
887 snarf();
888 if(scrolling)
889 show(t.q0);
890 return;
891 case PASTE:
892 snarfupdate();
893 paste(t.snarf, t.nsnarf, 0);
894 if(scrolling)
895 show(t.q0);
896 return;
899 switch(r) {
900 /* case 0x03: can't do this because ^C is COPY */
901 case 0x7F: /* DEL: send interrupt */
902 paste(&r, 1, 1);
903 t.qh = t.q0 = t.q1 = t.nr;
904 show(t.q0);
905 /* must write the interrupt character in case app is in raw mode (e.g., ssh) */
906 write(rcfd, "\x7F", 1);
907 // postnote(PNGROUP, rcpid, "interrupt");
908 return;
911 if(rawon() && t.q0==t.nr){
912 addraw(&r, 1);
913 consread();
914 return;
917 if(r==ESC || (holdon && r==0x7F)){ /* toggle hold */
918 holdon = !holdon;
919 drawhold(holdon);
920 // replaceintegerproperty("_9WM_HOLD_MODE", 1, 32, holdon);
921 if(!holdon)
922 consread();
923 if(r==ESC)
924 return;
927 snarf();
929 switch(r) {
930 case 0x06: /* ^F: file name completion */
931 case Kins: /* Insert: file name completion */
932 rp = namecomplete();
933 if(rp == nil)
934 return;
935 nr = runestrlen(rp);
936 paste(rp, nr, 1);
937 free(rp);
938 return;
939 case 0x08: /* ^H: erase character */
940 case 0x15: /* ^U: erase line */
941 case 0x17: /* ^W: erase word */
942 if (t.q0 != 0 && t.q0 != t.qh)
943 t.q0 -= bswidth(r, t.q0, 1);
944 cut();
945 break;
946 default:
947 paste(&r, 1, 1);
948 break;
950 if(scrolling)
951 show(t.q0);
954 int
955 bswidth(Rune c, uint start, int eatnl)
957 uint q, eq, stop;
958 Rune r;
959 int skipping;
961 /* there is known to be at least one character to erase */
962 if(c == 0x08) /* ^H: erase character */
963 return 1;
964 q = start;
965 stop = 0;
966 if(q > t.qh)
967 stop = t.qh;
968 skipping = 1;
969 while(q > stop){
970 r = t.r[q-1];
971 if(r == '\n'){ /* eat at most one more character */
972 if(q == start && eatnl) /* eat the newline */
973 --q;
974 break;
976 if(c == 0x17){
977 eq = isalnum(r);
978 if(eq && skipping) /* found one; stop skipping */
979 skipping = 0;
980 else if(!eq && !skipping)
981 break;
983 --q;
985 return start-q;
988 int
989 consready(void)
991 int i, c;
993 if(holdon)
994 return 0;
996 if(rawon())
997 return t.nraw != 0;
999 /* look to see if there is a complete line */
1000 for(i=t.qh; i<t.nr; i++){
1001 c = t.r[i];
1002 if(c=='\n' || c=='\004' || c=='\x7F')
1003 return 1;
1005 return 0;
1009 void
1010 consread(void)
1012 char buf[8000], *p;
1013 int c, width, n;
1014 int s, raw;
1016 raw = rawon();
1017 for(;;) {
1018 if(!consready())
1019 return;
1020 n = sizeof(buf);
1021 p = buf;
1022 c = 0;
1023 while(n >= UTFmax && (t.qh<t.nr || t.nraw > 0)) {
1024 if(t.qh == t.nr){
1025 width = runetochar(p, &t.raw[0]);
1026 t.nraw--;
1027 runemove(t.raw, t.raw+1, t.nraw);
1028 }else
1029 width = runetochar(p, &t.r[t.qh++]);
1030 c = *p;
1031 p += width;
1032 n -= width;
1033 if(!raw && (c == '\n' || c == '\004' || c == '\x7F'))
1034 break;
1036 n = p-buf;
1039 * If we've been echoing, make sure the terminal isn't
1040 * while we do the write. This screws up if someone
1041 * else tries to turn off echo at the same time we do
1042 * (we'll turn it on again after the write), but that's not
1043 * too likely.
1045 s = setecho(sfd, 0);
1046 if(write(rcfd, buf, n) < 0)
1047 exits(0);
1048 if(s)
1049 setecho(sfd, s);
1053 void
1054 conswrite(char *p, int n)
1056 int n2, i;
1057 Rune buf2[1000], *q;
1059 /* convert to runes */
1060 i = t.npart;
1061 if(i > 0){
1062 /* handle partial runes */
1063 while(i < UTFmax && n>0) {
1064 t.part[i] = *p;
1065 i++;
1066 p++;
1067 n--;
1068 if(fullrune(t.part, i)) {
1069 t.npart = 0;
1070 chartorune(buf2, t.part);
1071 runewrite(buf2, 1);
1072 break;
1075 /* there is a little extra room in a message buf */
1078 while(n >= UTFmax || fullrune(p, n)) {
1079 n2 = nelem(buf2);
1080 q = buf2;
1082 while(n2) {
1083 if(n < UTFmax && !fullrune(p, n))
1084 break;
1085 i = chartorune(q, p);
1086 p += i;
1087 n -= i;
1088 n2--;
1089 q++;
1091 runewrite(buf2, q-buf2);
1094 if(n != 0) {
1095 assert(n+t.npart < UTFmax);
1096 memcpy(t.part+t.npart, p, n);
1097 t.npart += n;
1100 if(scrolling)
1101 show(t.qh);
1104 void
1105 runewrite(Rune *r, int n)
1107 int i;
1108 uint initial;
1109 uint q0, q1;
1110 uint p0, p1;
1111 Rune *p, *q;
1113 n = label(r, n);
1114 if(n == 0)
1115 return;
1117 /* get rid of backspaces */
1118 initial = 0;
1119 p = q = r;
1120 for(i=0; i<n; i++) {
1121 if(*p == '\b') {
1122 if(q == r)
1123 initial++;
1124 else
1125 --q;
1126 } else if(*p == '\r') { /* treat like ^U */
1127 /* convert CR without NL into erased line */
1128 /* i feel really sleazy about this but it helps */
1129 while(i<n-1 && *(p+1) == '\r'){
1130 i++;
1131 p++;
1133 if(i<n-1 && *(p+1) != '\n'){
1134 while(q > r && *(q-1) != '\n')
1135 q--;
1136 if(q==r)
1137 initial = bswidth(0x15, t.qh, 0);
1139 } else if(*p)
1140 *q++ = *p;
1141 p++;
1143 n = q-r;
1145 if(initial){
1146 /* write turned into a delete */
1148 if(initial > t.qh)
1149 initial = t.qh;
1150 q0 = t.qh-initial;
1151 q1 = t.qh;
1153 runemove(t.r+q0, t.r+q1, t.nr-q1);
1154 t.nr -= initial;
1155 t.qh -= initial;
1156 if(t.q0 > q1)
1157 t.q0 -= initial;
1158 else if(t.q0 > q0)
1159 t.q0 = q0;
1160 if(t.q1 > q1)
1161 t.q1 -= initial;
1162 else if(t.q1 > q0)
1163 t.q1 = q0;
1164 if(t.org > q1)
1165 t.org -= initial;
1166 else if(q0 < t.org+t.f->nchars){
1167 if(t.org < q0)
1168 p0 = q0 - t.org;
1169 else {
1170 t.org = q0;
1171 p0 = 0;
1173 p1 = q1 - t.org;
1174 if(p1 > t.f->nchars)
1175 p1 = t.f->nchars;
1176 frdelete(t.f, p0, p1);
1177 fill();
1179 updatesel();
1182 insert(r, n, t.qh, 1);
1186 void
1187 cut(void)
1189 uint n, p0, p1;
1190 uint q0, q1;
1192 q0 = t.q0;
1193 q1 = t.q1;
1195 if (q0 < t.org && q1 >= t.org)
1196 show(q0);
1198 n = q1-q0;
1199 if(n == 0)
1200 return;
1201 runemove(t.r+q0, t.r+q1, t.nr-q1);
1202 t.nr -= n;
1203 t.q0 = t.q1 = q0;
1204 if(q1 < t.qh)
1205 t.qh -= n;
1206 else if(q0 < t.qh)
1207 t.qh = q0;
1208 if(q1 < t.org)
1209 t.org -= n;
1210 else if(q0 < t.org+t.f->nchars){
1211 assert(q0 >= t.org);
1212 p0 = q0 - t.org;
1213 p1 = q1 - t.org;
1214 if(p1 > t.f->nchars)
1215 p1 = t.f->nchars;
1216 frdelete(t.f, p0, p1);
1217 fill();
1219 updatesel();
1222 void
1223 snarfupdate(void)
1225 char *pp;
1226 int n, i;
1227 Rune *p;
1229 pp = getsnarf();
1230 if(pp == nil)
1231 return;
1232 n = strlen(pp);
1233 if(n <= 0) {
1234 /*t.nsnarf = 0;*/
1235 return;
1237 t.snarf = runerealloc(t.snarf, n);
1238 for(i=0,p=t.snarf; i<n; p++)
1239 i += chartorune(p, pp+i);
1240 t.nsnarf = p-t.snarf;
1244 char sbuf[SnarfSize];
1245 void
1246 snarf(void)
1248 char *p;
1249 int i, n;
1250 Rune *rp;
1252 if(t.q1 == t.q0)
1253 return;
1254 n = t.q1-t.q0;
1255 t.snarf = runerealloc(t.snarf, n);
1256 for(i=0,p=sbuf,rp=t.snarf; i<n && p < sbuf+SnarfSize-UTFmax; i++){
1257 *rp++ = *(t.r+t.q0+i);
1258 p += runetochar(p, t.r+t.q0+i);
1260 t.nsnarf = rp-t.snarf;
1261 *p = '\0';
1262 putsnarf(sbuf);
1265 uint
1266 min(uint x, uint y)
1268 if(x < y)
1269 return x;
1270 return y;
1273 uint
1274 max(uint x, uint y)
1276 if(x > y)
1277 return x;
1278 return y;
1281 uint
1282 insert(Rune *r, int n, uint q0, int hostwrite)
1284 uint m;
1286 if(n == 0)
1287 return q0;
1288 if(t.nr+n>HiWater && q0>=t.org && q0>=t.qh){
1289 m = min(HiWater-LoWater, min(t.org, t.qh));
1290 t.org -= m;
1291 t.qh -= m;
1292 if(t.q0 > m)
1293 t.q0 -= m;
1294 else
1295 t.q0 = 0;
1296 if(t.q1 > m)
1297 t.q1 -= m;
1298 else
1299 t.q1 = 0;
1300 t.nr -= m;
1301 runemove(t.r, t.r+m, t.nr);
1302 q0 -= m;
1304 if(t.nr+n > t.maxr){
1306 * Minimize realloc breakage:
1307 * Allocate at least MinWater
1308 * Double allocation size each time
1309 * But don't go much above HiWater
1311 m = max(min(2*(t.nr+n), HiWater), t.nr+n)+MinWater;
1312 if(m > HiWater)
1313 m = max(HiWater+MinWater, t.nr+n);
1314 if(m > t.maxr){
1315 t.r = runerealloc(t.r, m);
1316 t.maxr = m;
1319 runemove(t.r+q0+n, t.r+q0, t.nr-q0);
1320 runemove(t.r+q0, r, n);
1321 t.nr += n;
1322 /* if output touches, advance selection, not qh; works best for keyboard and output */
1323 if(q0 <= t.q1)
1324 t.q1 += n;
1325 if(q0 <= t.q0)
1326 t.q0 += n;
1327 if(q0 < t.qh || (q0==t.qh && hostwrite))
1328 t.qh += n;
1329 else
1330 consread();
1331 if(q0 < t.org)
1332 t.org += n;
1333 else if(q0 <= t.org+t.f->nchars)
1334 frinsert(t.f, r, r+n, q0-t.org);
1335 return q0;
1338 void
1339 paste(Rune *r, int n, int advance)
1341 Rune *rbuf;
1343 if(rawon() && t.q0==t.nr){
1344 addraw(r, n);
1345 return;
1348 cut();
1349 if(n == 0)
1350 return;
1353 * if this is a button2 execute then we might have been passed
1354 * runes inside the buffer. must save them before realloc.
1356 rbuf = nil;
1357 if(t.r <= r && r < t.r+n){
1358 rbuf = runemalloc(n);
1359 runemove(rbuf, r, n);
1360 r = rbuf;
1363 insert(r, n, t.q0, 0);
1364 updatesel();
1365 free(rbuf);
1368 void
1369 fill(void)
1371 if (t.f->nlines >= t.f->maxlines)
1372 return;
1373 frinsert(t.f, t.r + t.org + t.f->nchars, t.r + t.nr, t.f->nchars);
1376 void
1377 updatesel(void)
1379 Frame *f;
1380 uint n;
1382 f = t.f;
1383 if(t.org+f->p0 == t.q0 && t.org+f->p1 == t.q1)
1384 return;
1386 n = t.f->nchars;
1388 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
1389 if (t.q0 >= t.org)
1390 f->p0 = t.q0-t.org;
1391 else
1392 f->p0 = 0;
1393 if(f->p0 > n)
1394 f->p0 = n;
1395 if (t.q1 >= t.org)
1396 f->p1 = t.q1-t.org;
1397 else
1398 f->p1 = 0;
1399 if(f->p1 > n)
1400 f->p1 = n;
1401 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
1404 if(t.qh<=t.org+t.f.nchars && t.cwqueue != 0)
1405 t.cwqueue->wakeup <-= 0;
1408 tcheck();
1411 void
1412 show(uint q0)
1414 int nl;
1415 uint q, oq;
1417 if(cansee(q0))
1418 return;
1420 if (q0<t.org)
1421 nl = t.f->maxlines/5;
1422 else
1423 nl = 4*t.f->maxlines/5;
1424 q = backnl(q0, nl);
1425 /* avoid going in the wrong direction */
1426 if (q0>t.org && q<t.org)
1427 q = t.org;
1428 setorigin(q, 0);
1429 /* keep trying until q0 is on the screen */
1430 while(!cansee(q0)) {
1431 assert(q0 >= t.org);
1432 oq = q;
1433 q = line2q(t.f->maxlines-nl);
1434 assert(q > oq);
1435 setorigin(q, 1);
1439 int
1440 cansee(uint q0)
1442 uint qe;
1444 qe = t.org+t.f->nchars;
1446 if(q0>=t.org && q0 < qe)
1447 return 1;
1448 if (q0 != qe)
1449 return 0;
1450 if (t.f->nlines < t.f->maxlines)
1451 return 1;
1452 if (q0 > 0 && t.r[t.nr-1] == '\n')
1453 return 0;
1454 return 1;
1458 void
1459 setorigin(uint org, int exact)
1461 int i, a;
1462 uint n;
1464 if(org>0 && !exact){
1465 /* try and start after a newline */
1466 /* don't try harder than 256 chars */
1467 for(i=0; i<256 && org<t.nr; i++){
1468 if(t.r[org-1] == '\n')
1469 break;
1470 org++;
1473 a = org-t.org;
1475 if(a>=0 && a<t.f->nchars)
1476 frdelete(t.f, 0, a);
1477 else if(a<0 && -a<100*t.f->maxlines){
1478 n = t.org - org;
1479 frinsert(t.f, t.r+org, t.r+org+n, 0);
1480 }else
1481 frdelete(t.f, 0, t.f->nchars);
1482 t.org = org;
1483 fill();
1484 updatesel();
1488 uint
1489 line2q(uint n)
1491 Frame *f;
1493 f = t.f;
1494 return frcharofpt(f, Pt(f->r.min.x, f->r.min.y + n*font->height))+t.org;
1497 uint
1498 backnl(uint p, uint n)
1500 int i, j;
1502 for (i = n;; i--) {
1503 /* at 256 chars, call it a line anyway */
1504 for(j=256; --j>0 && p>0; p--)
1505 if(t.r[p-1]=='\n')
1506 break;
1507 if (p == 0 || i == 0)
1508 return p;
1509 p--;
1511 return 0; /* alef bug */
1514 void
1515 addraw(Rune *r, int nr)
1517 t.raw = runerealloc(t.raw, t.nraw+nr);
1518 runemove(t.raw+t.nraw, r, nr);
1519 t.nraw += nr;
1521 if(t.crqueue != nil)
1522 t.crqueue->wakeup <-= 0;
1527 Rune left1[] = { '{', '[', '(', '<', 0xab, 0 };
1528 Rune right1[] = { '}', ']', ')', '>', 0xbb, 0 };
1529 Rune left2[] = { '\n', 0 };
1530 Rune left3[] = { '\'', '"', '`', 0 };
1532 Rune *left[] = {
1533 left1,
1534 left2,
1535 left3,
1539 Rune *right[] = {
1540 right1,
1541 left2,
1542 left3,
1546 void
1547 doubleclick(uint *q0, uint *q1)
1549 int c, i;
1550 Rune *r, *l, *p;
1551 uint q;
1553 for(i=0; left[i]!=0; i++){
1554 q = *q0;
1555 l = left[i];
1556 r = right[i];
1557 /* try matching character to left, looking right */
1558 if(q == 0)
1559 c = '\n';
1560 else
1561 c = t.r[q-1];
1562 p = strrune(l, c);
1563 if(p != 0){
1564 if(clickmatch(c, r[p-l], 1, &q))
1565 *q1 = q-(c!='\n');
1566 return;
1568 /* try matching character to right, looking left */
1569 if(q == t.nr)
1570 c = '\n';
1571 else
1572 c = t.r[q];
1573 p = strrune(r, c);
1574 if(p != 0){
1575 if(clickmatch(c, l[p-r], -1, &q)){
1576 *q1 = *q0+(*q0<t.nr && c=='\n');
1577 *q0 = q;
1578 if(c!='\n' || q!=0 || t.r[0]=='\n')
1579 (*q0)++;
1581 return;
1584 /* try filling out word to right */
1585 while(*q1<t.nr && isalnum(t.r[*q1]))
1586 (*q1)++;
1587 /* try filling out word to left */
1588 while(*q0>0 && isalnum(t.r[*q0-1]))
1589 (*q0)--;
1592 int
1593 clickmatch(int cl, int cr, int dir, uint *q)
1595 Rune c;
1596 int nest;
1598 nest = 1;
1599 for(;;){
1600 if(dir > 0){
1601 if(*q == t.nr)
1602 break;
1603 c = t.r[*q];
1604 (*q)++;
1605 }else{
1606 if(*q == 0)
1607 break;
1608 (*q)--;
1609 c = t.r[*q];
1611 if(c == cr){
1612 if(--nest==0)
1613 return 1;
1614 }else if(c == cl)
1615 nest++;
1617 return cl=='\n' && nest==1;
1620 void
1621 tcheck(void)
1623 Frame *f;
1625 f = t.f;
1627 assert(t.q0 <= t.q1 && t.q1 <= t.nr);
1628 assert(t.org <= t.nr && t.qh <= t.nr);
1629 assert(f->p0 <= f->p1 && f->p1 <= f->nchars);
1630 assert(t.org + f->nchars <= t.nr);
1631 assert(t.org+f->nchars==t.nr || (f->nlines >= f->maxlines));
1634 Rune*
1635 strrune(Rune *s, Rune c)
1637 Rune c1;
1639 if(c == 0) {
1640 while(*s++)
1642 return s-1;
1645 while(c1 = *s++)
1646 if(c1 == c)
1647 return s-1;
1648 return 0;
1651 void
1652 scrdraw(void)
1654 Rectangle r, r1, r2;
1655 static Image *scrx;
1657 r = scrollr;
1658 r.min.x += 1; /* border between margin and bar */
1659 r1 = r;
1660 if(scrx==0 || scrx->r.max.y < r.max.y){
1661 if(scrx)
1662 freeimage(scrx);
1663 scrx = allocimage(display, Rect(0, 0, 32, r.max.y), screen->chan, 1, DPaleyellow);
1664 if(scrx == 0)
1665 sysfatal("scroll balloc");
1667 r1.min.x = 0;
1668 r1.max.x = Dx(r);
1669 r2 = scrpos(r1, t.org, t.org+t.f->nchars, t.nr);
1670 if(!eqrect(r2, lastsr)){
1671 lastsr = r2;
1672 draw(scrx, r1, cols[BORD], nil, ZP);
1673 draw(scrx, r2, cols[BACK], nil, r2.min);
1674 // r2 = r1;
1675 // r2.min.x = r2.max.x-1;
1676 // draw(scrx, r2, cols[BORD], nil, ZP);
1677 draw(screen, r, scrx, nil, r1.min);
1681 Rectangle
1682 scrpos(Rectangle r, ulong p0, ulong p1, ulong tot)
1684 long h;
1685 Rectangle q;
1687 q = insetrect(r, 1);
1688 h = q.max.y-q.min.y;
1689 if(tot == 0)
1690 return q;
1691 if(tot > 1024L*1024L)
1692 tot >>= 10, p0 >>= 10, p1 >>= 10;
1693 if(p0 > 0)
1694 q.min.y += h*p0/tot;
1695 if(p1 < tot)
1696 q.max.y -= h*(tot-p1)/tot;
1697 if(q.max.y < q.min.y+2){
1698 if(q.min.y+2 <= r.max.y)
1699 q.max.y = q.min.y+2;
1700 else
1701 q.min.y = q.max.y-2;
1703 return q;
1706 void
1707 scroll(int but)
1709 uint p0, oldp0;
1710 Rectangle s;
1711 int x, y, my, h, first, exact;
1713 s = insetrect(scrollr, 1);
1714 h = s.max.y-s.min.y;
1715 x = (s.min.x+s.max.x)/2;
1716 oldp0 = ~0;
1717 first = 1;
1718 do{
1719 if(t.m.xy.x<s.min.x || s.max.x<=t.m.xy.x){
1720 readmouse(mc);
1721 t.m = mc->m;
1722 }else{
1723 my = t.m.xy.y;
1724 if(my < s.min.y)
1725 my = s.min.y;
1726 if(my >= s.max.y)
1727 my = s.max.y;
1728 // if(!eqpt(t.m.xy, Pt(x, my)))
1729 // cursorset(Pt(x, my));
1730 exact = 1;
1731 if(but == 2){
1732 y = my;
1733 if(y > s.max.y-2)
1734 y = s.max.y-2;
1735 if(t.nr > 1024*1024)
1736 p0 = ((t.nr>>10)*(y-s.min.y)/h)<<10;
1737 else
1738 p0 = t.nr*(y-s.min.y)/h;
1739 exact = 0;
1740 } else if(but == 1)
1741 p0 = backnl(t.org, (my-s.min.y)/font->height);
1742 else
1743 p0 = t.org+frcharofpt(t.f, Pt(s.max.x, my));
1745 if(oldp0 != p0)
1746 setorigin(p0, exact);
1747 oldp0 = p0;
1748 scrdraw();
1749 readmouse(mc);
1750 t.m = mc->m;
1752 }while(t.m.buttons & (1<<(but-1)));
1755 void
1756 plumbstart(void)
1758 if((plumbfd = plumbopen("send", OWRITE)) < 0)
1759 fprint(2, "9term: plumbopen: %r\n");
1762 void
1763 plumb(uint q0, uint q1)
1765 Plumbmsg *pm;
1766 char *p;
1767 int i, p0, n;
1768 char cbuf[100];
1770 pm = malloc(sizeof(Plumbmsg));
1771 pm->src = strdup("9term");
1772 pm->dst = 0;
1773 pm->wdir = strdup(wdir);
1774 pm->type = strdup("text");
1775 pm->data = nil;
1776 if(q1 > q0)
1777 pm->attr = nil;
1778 else{
1779 p0 = q0;
1780 wordclick(&q0, &q1);
1781 sprint(cbuf, "click=%d", p0-q0);
1782 pm->attr = plumbunpackattr(cbuf);
1784 if(q0==q1){
1785 plumbfree(pm);
1786 return;
1788 pm->data = malloc(SnarfSize);
1789 n = q1 - q0;
1790 for(i=0,p=pm->data; i<n && p < pm->data + SnarfSize-UTFmax; i++)
1791 p += runetochar(p, t.r+q0+i);
1792 *p = '\0';
1793 pm->ndata = strlen(pm->data);
1794 plumbsend(plumbfd, pm);
1795 plumbfree(pm);
1799 * Process in-band messages about window title changes.
1800 * The messages are of the form:
1802 * \033];xxx\007
1804 * where xxx is the new directory. This format was chosen
1805 * because it changes the label on xterm windows.
1807 int
1808 label(Rune *sr, int n)
1810 Rune *sl, *el, *er, *r;
1812 er = sr+n;
1813 for(r=er-1; r>=sr; r--)
1814 if(*r == '\007')
1815 break;
1816 if(r < sr)
1817 return n;
1819 el = r+1;
1820 if(el-sr > sizeof wdir)
1821 sr = el - sizeof wdir;
1822 for(sl=el-3; sl>=sr; sl--)
1823 if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
1824 break;
1825 if(sl < sr)
1826 return n;
1828 snprint(wdir, sizeof wdir, "%.*S", (el-1)-(sl+3), sl+3);
1829 drawsetlabel(display, wdir);
1831 runemove(sl, el, er-el);
1832 n -= (el-sl);
1833 return n;
1836 int
1837 rawon(void)
1839 return !cooked && !isecho(sfd);
1843 * Clumsy hack to make " and "" work.
1844 * Then again, what's not a clumsy hack here in Unix land?
1847 char adir[100];
1848 int afd;
1850 void
1851 removethesocket(void)
1853 if(thesocket[0])
1854 if(remove(thesocket) < 0)
1855 fprint(2, "remove %s: %r\n", thesocket);
1858 void
1859 servedevtext(void)
1861 char buf[100];
1863 snprint(buf, sizeof buf, "unix!/tmp/9term-text.%d", getpid());
1865 if((afd = announce(buf, adir)) < 0){
1866 putenv("text9term", "");
1867 return;
1870 putenv("text9term", buf);
1871 proccreate(listenproc, nil, STACK);
1872 strcpy(thesocket, buf+5);
1873 atexit(removethesocket);
1876 void
1877 listenproc(void *arg)
1879 int fd;
1880 char dir[100];
1882 USED(arg);
1883 for(;;){
1884 fd = listen(adir, dir);
1885 if(fd < 0){
1886 close(afd);
1887 return;
1889 proccreate(textthread, (void*)fd, STACK);
1893 void
1894 textthread(void *arg)
1896 int fd, i, x, n, end;
1897 Rune r;
1898 char buf[4096], *p, *ep;
1900 fd = (int)arg;
1901 p = buf;
1902 ep = buf+sizeof buf;
1903 end = t.org+t.nr; /* avoid possible output loop */
1904 for(i=t.org;; i++){
1905 if(i >= end || ep-p < UTFmax){
1906 for(x=0; x<p-buf; x+=n)
1907 if((n = write(fd, buf+x, (p-x)-buf)) <= 0)
1908 goto break2;
1910 if(i >= end)
1911 break;
1912 p = buf;
1914 if(i < t.org)
1915 i = t.org;
1916 r = t.r[i-t.org];
1917 if(r < Runeself)
1918 *p++ = r;
1919 else
1920 p += runetochar(p, &r);
1922 break2:
1923 close(fd);