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 Cursor query = {
200 {-7,-7},
201 {0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe,
202 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8,
203 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0,
204 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
205 {0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c,
206 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0,
207 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80,
208 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
209 };
211 void
212 usage(void)
214 fprint(2, "usage: 9term [-ars] [-W winsize] [cmd ...]\n");
215 threadexitsall("usage");
218 void
219 threadmain(int argc, char *argv[])
221 char *p, *font;
222 char buf[32];
224 rfork(RFNOTEG);
225 font = nil;
226 _wantfocuschanges = 1;
227 mainpid = getpid();
228 ARGBEGIN{
229 default:
230 usage();
231 case 'a': /* acme mode */
232 button2exec++;
233 break;
234 case 'f':
235 font = EARGF(usage());
236 break;
237 case 's':
238 scrolling++;
239 break;
240 case 'w': /* started from "rio" window manager */
241 use9wm = 1;
242 break;
243 case 'W':
244 winsize = EARGF(usage());
245 break;
246 }ARGEND
248 if(font)
249 putenv("font", font);
251 p = getenv("tabstop");
252 if(p == 0)
253 p = getenv("TABSTOP");
254 if(p != 0 && maxtab <= 0)
255 maxtab = strtoul(p, 0, 0);
256 if(maxtab <= 0)
257 maxtab = 4; /* be like rio */
259 snprint(buf, sizeof buf, "%d", maxtab);
260 putenv("tabstop", buf);
262 initdraw(0, nil, "9term");
263 notify(hangupnote);
264 noteenable("sys: child");
265 servedevtext();
267 mc = initmouse(nil, screen);
268 kc = initkeyboard(nil);
269 rcpid = rcstart(argc, argv, &rcfd, &sfd);
270 hoststart();
271 plumbstart();
273 t.f = mallocz(sizeof(Frame), 1);
275 if(acmecolors){
276 cols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
277 cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DDarkyellow);
278 cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DYellowgreen);
279 }else{
280 cols[BACK] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DWhite);
281 cols[HIGH] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
282 cols[BORD] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x999999FF);
284 cols[TEXT] = display->black;
285 cols[HTEXT] = display->black;
286 palegrey = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0x666666FF);
288 hcols[BACK] = cols[BACK];
289 hcols[HIGH] = cols[HIGH];
290 blue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DMedblue);
291 paleblue = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DGreyblue);
293 hcols[BORD] = blue;
294 hcols[TEXT] = hcols[BORD];
295 hcols[HTEXT] = hcols[TEXT];
297 plumbcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x006600FF);
298 execcolor = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAA0000FF);
300 if(!blue || !palegrey || !paleblue || !plumbcolor || !execcolor)
301 sysfatal("alloc colors: %r");
302 draw(screen, screen->r, cols[BACK], nil, ZP);
303 geom();
304 loop();
307 void
308 hangupnote(void *a, char *msg)
310 if(getpid() != mainpid)
311 noted(NDFLT);
312 if(strcmp(msg, "hangup") == 0 && rcpid != 0){
313 postnote(PNGROUP, rcpid, "hangup");
314 noted(NDFLT);
316 if(strstr(msg, "child")){
317 /* bug: do better */
318 threadexitsall(0);
320 noted(NDFLT);
323 void
324 hostproc(void *arg)
326 Channel *c;
327 int i, n, which;
329 c = arg;
331 i = 0;
332 for(;;){
333 /* Let typing have a go -- maybe there's a rubout waiting. */
334 yield();
336 i = 1-i; /* toggle */
337 n = read(rcfd, rcbuf[i].data, sizeof rcbuf[i].data);
338 if(n <= 0){
339 if(n < 0)
340 fprint(2, "9term: host read error: %r\n");
341 threadexitsall("host");
343 rcbuf[i].n = n;
344 which = i;
345 send(c, &which);
349 void
350 hoststart(void)
352 hostc = chancreate(sizeof(int), 0);
353 proccreate(hostproc, hostc, 32*1024);
356 void
357 loop(void)
359 Rune r;
360 int i;
361 Alt a[5];
363 a[0].c = mc->c;
364 a[0].v = &mc->m;
365 a[0].op = CHANRCV;
367 a[1].c = kc->c;
368 a[1].v = &r;
369 a[1].op = CHANRCV;
371 a[2].c = hostc;
372 a[2].v = &i;
373 a[2].op = CHANRCV;
375 a[3].c = mc->resizec;
376 a[3].v = nil;
377 a[3].op = CHANRCV;
379 a[4].c = nil;
380 a[4].v = nil;
381 a[4].op = CHANEND;
383 for(;;) {
384 tcheck();
386 scrdraw();
387 flushimage(display, 1);
388 a[2].op = CHANRCV;
389 if(!scrolling && t.qh > t.org+t.f->nchars)
390 a[2].op = CHANNOP;;
391 switch(alt(a)) {
392 default:
393 sysfatal("impossible");
394 case 0:
395 t.m = mc->m;
396 mouse();
397 break;
398 case 1:
399 key(r);
400 break;
401 case 2:
402 conswrite((char*)rcbuf[i].data, rcbuf[i].n);
403 break;
404 case 3:
405 doreshape();
406 break;
411 void
412 doreshape(void)
414 if(getwindow(display, Refnone) < 0)
415 sysfatal("can't reattach to window");
416 draw(screen, screen->r, cols[BACK], nil, ZP);
417 geom();
418 scrdraw();
421 void
422 geom(void)
424 Point p;
425 Rectangle r;
427 if(!acmecolors){
428 if(_windowhasfocus){
429 cols[TEXT] = cols[HTEXT] = display->black;
430 hcols[TEXT] = hcols[HTEXT] = blue;
431 }else{
432 cols[TEXT] = cols[HTEXT] = palegrey;
433 hcols[TEXT] = hcols[HTEXT] = paleblue;
437 r = screen->r;
438 r.min.y++;
439 r.max.y--;
441 scrollr = r;
442 scrollr.max.x = r.min.x+Scrollwid;
443 lastsr = Rect(0,0,0,0);
445 r.min.x += Scrollwid+Scrollgap;
447 frclear(t.f, 0);
448 frinit(t.f, r, font, screen, holdon ? hcols : cols);
449 t.f->maxtab = maxtab*stringwidth(font, "0");
450 fill();
451 updatesel();
453 p = stringsize(font, "0");
454 if(p.x == 0 || p.y == 0)
455 return;
457 updatewinsize(Dy(r)/p.y, Dx(r)/p.x, Dx(r), Dy(r));
460 void
461 drawhold(int holdon)
463 if(holdon)
464 setcursor(mc, &whitearrow);
465 else
466 setcursor(mc, nil);
468 draw(screen, screen->r, cols[BACK], nil, ZP);
469 geom();
470 scrdraw();
473 void
474 wordclick(uint *q0, uint *q1)
476 while(*q1<t.nr && !isspace(t.r[*q1]))
477 (*q1)++;
478 while(*q0>0 && !isspace(t.r[*q0-1]))
479 (*q0)--;
482 int
483 aselect(uint *q0, uint *q1, Image *color)
485 int cancel;
486 uint oldq0, oldq1, newq0, newq1;
488 /* save old selection */
489 oldq0 = t.q0;
490 oldq1 = t.q1;
492 /* sweep out area and record it */
493 t.f->cols[HIGH] = color;
494 t.f->cols[HTEXT] = display->white;
495 mselect();
496 newq0 = t.q0;
497 newq1 = t.q1;
499 cancel = 0;
500 if(t.m.buttons != 0){
501 while(t.m.buttons){
502 readmouse(mc);
503 t.m = mc->m;
505 cancel = 1;
508 /* restore old selection */
509 t.f->cols[HIGH] = cols[HIGH];
510 t.f->cols[HTEXT] = cols[HTEXT];
511 t.q0 = oldq0;
512 t.q1 = oldq1;
513 updatesel();
515 if(cancel)
516 return -1;
518 /* selected a region */
519 if(newq0 < newq1){
520 *q0 = newq0;
521 *q1 = newq1;
522 return 0;
525 /* clicked inside previous selection */
526 /* the "<=" in newq0 <= oldq1 allows us to click the right edge */
527 if(oldq0 <= newq0 && newq0 <= oldq1){
528 *q0 = oldq0;
529 *q1 = oldq1;
530 return 0;
533 /* just a click */
534 *q0 = newq0;
535 *q1 = newq1;
536 return 0;
539 static Rune Lnl[1] = { '\n' };
541 void
542 mouse(void)
544 int but;
545 uint q0, q1;
547 but = t.m.buttons;
549 if(but != 1 && but != 2 && but != 4 && but != 8 && but != 16)
550 return;
552 if (ptinrect(t.m.xy, scrollr)) {
553 scroll(but);
554 if(t.qh<=t.org+t.f->nchars)
555 consread();
556 return;
559 switch(but) {
560 case 1:
561 mselect();
562 break;
563 case 2:
564 if(button2exec){
565 if(aselect(&q0, &q1, execcolor) >= 0){
566 if(q0 == q1)
567 wordclick(&q0, &q1);
568 if(q0 == q1)
569 break;
570 t.q0 = t.q1 = t.nr;
571 updatesel();
572 paste(t.r+q0, q1-q0, 1);
573 if(t.r[q1-1] != '\n')
574 paste(Lnl, 1, 1);
576 break;
578 domenu2(2);
579 break;
580 case 4:
581 bouncemouse(&t.m);
582 break;
583 /*
584 if(aselect(&q0, &q1, plumbcolor) >= 0)
585 plumb(q0, q1);
586 break;
587 */
588 case 8:
589 scrollup(mousescrollsize(t.f->maxlines));
590 break;
591 case 16:
592 scrolldown(mousescrollsize(t.f->maxlines));
593 break;
597 void
598 mselect(void)
600 int b, x, y;
601 uint q0;
603 b = t.m.buttons;
604 q0 = frcharofpt(t.f, t.m.xy) + t.org;
605 if(t.m.msec-clickmsec<500 && clickq0==q0 && t.q0==t.q1 && b==1){
606 doubleclick(&t.q0, &t.q1);
607 updatesel();
608 /* t.t.i->flush(); */
609 x = t.m.xy.x;
610 y = t.m.xy.y;
611 /* stay here until something interesting happens */
612 do {
613 readmouse(mc);
614 t.m = mc->m;
615 } while(t.m.buttons==b && abs(t.m.xy.x-x)<4 && abs(t.m.xy.y-y)<4);
616 t.m.xy.x = x; /* in case we're calling frselect */
617 t.m.xy.y = y;
618 clickmsec = 0;
621 if(t.m.buttons == b) {
622 frselect(t.f, mc);
623 t.m = mc->m;
624 t.q0 = t.f->p0 + t.org;
625 t.q1 = t.f->p1 + t.org;
626 clickmsec = t.m.msec;
627 clickq0 = t.q0;
629 if((t.m.buttons != b) &&(b&1)){
630 enum{Cancut = 1, Canpaste = 2} state = Cancut | Canpaste;
631 while(t.m.buttons){
632 if(t.m.buttons&2){
633 if(state&Cancut){
634 snarf();
635 cut();
636 state = Canpaste;
638 }else if(t.m.buttons&4){
639 if(state&Canpaste){
640 snarfupdate();
641 if(t.nsnarf){
642 paste(t.snarf, t.nsnarf, 0);
644 state = Cancut;
647 readmouse(mc);
648 t.m = mc->m;
653 Rune newline[] = { '\n', 0 };
655 void
656 domenu2(int but)
658 if(scrolling)
659 menu2str[Scroll] = "+ scroll";
660 else
661 menu2str[Scroll] = "- scroll";
662 if(cooked)
663 menu2str[Cooked] = "+ mustecho";
664 else
665 menu2str[Cooked] = "- mustecho";
667 switch(menuhit(but, mc, &menu2, nil)){
668 case -1:
669 break;
670 case Cut:
671 snarf();
672 cut();
673 if(scrolling)
674 show(t.q0);
675 break;
676 case Paste:
677 snarfupdate();
678 paste(t.snarf, t.nsnarf, 0);
679 if(scrolling)
680 show(t.q0);
681 break;
682 case Snarf:
683 snarf();
684 if(scrolling)
685 show(t.q0);
686 break;
687 case Send:
688 if(t.q0 != t.q1)
689 snarf();
690 else
691 snarfupdate();
692 t.q0 = t.q1 = t.nr;
693 updatesel();
694 paste(t.snarf, t.nsnarf, 1);
695 if(t.nsnarf == 0 || t.snarf[t.nsnarf-1] != '\n')
696 paste(newline, 1, 1);
697 show(t.nr);
698 consread();
699 break;
700 case Scroll:
701 scrolling = !scrolling;
702 if (scrolling) {
703 show(t.nr);
704 consread();
706 break;
707 case Plumb:
708 plumb(t.q0, t.q1);
709 break;
710 case Cooked:
711 cooked = !cooked;
712 break;
713 default:
714 sysfatal("bad menu item");
718 int
719 windfilewidth(uint q0, int oneelement)
721 uint q;
722 Rune r;
724 q = q0;
725 while(q > 0){
726 r = t.r[q-1];
727 if(r<=' ')
728 break;
729 if(oneelement && r=='/')
730 break;
731 --q;
733 return q0-q;
736 void
737 showcandidates(Completion *c)
739 int i;
740 Fmt f;
741 Rune *rp;
742 uint nr, qline, q0;
743 char *s;
745 runefmtstrinit(&f);
746 if (c->nmatch == 0)
747 s = "[no matches in ";
748 else
749 s = "[";
750 if(c->nfile > 32)
751 fmtprint(&f, "%s%d files]\n", s, c->nfile);
752 else{
753 fmtprint(&f, "%s", s);
754 for(i=0; i<c->nfile; i++){
755 if(i > 0)
756 fmtprint(&f, " ");
757 fmtprint(&f, "%s", c->filename[i]);
759 fmtprint(&f, "]\n");
761 /* place text at beginning of line before host point */
762 qline = t.qh;
763 while(qline>0 && t.r[qline-1] != '\n')
764 qline--;
766 rp = runefmtstrflush(&f);
767 nr = runestrlen(rp);
769 q0 = t.q0;
770 q0 += insert(rp, nr, qline, 0) - qline;
771 free(rp);
772 t.q0 = q0+nr;
773 t.q1 = q0+nr;
774 updatesel();
777 Rune*
778 namecomplete(void)
780 int nstr, npath;
781 Rune *rp, *path, *str;
782 Completion *c;
783 char *s, *dir, *root;
785 /* control-f: filename completion; works back to white space or / */
786 if(t.q0<t.nr && t.r[t.q0]>' ') /* must be at end of word */
787 return nil;
788 nstr = windfilewidth(t.q0, TRUE);
789 str = runemalloc(nstr);
790 runemove(str, t.r+(t.q0-nstr), nstr);
791 npath = windfilewidth(t.q0-nstr, FALSE);
792 path = runemalloc(npath);
793 runemove(path, t.r+(t.q0-nstr-npath), npath);
794 rp = nil;
796 /* is path rooted? if not, we need to make it relative to window path */
797 if(npath>0 && path[0]=='/'){
798 dir = malloc(UTFmax*npath+1);
799 sprint(dir, "%.*S", npath, path);
800 }else{
801 if(strcmp(wdir, "") == 0)
802 root = ".";
803 else
804 root = wdir;
805 dir = malloc(strlen(root)+1+UTFmax*npath+1);
806 sprint(dir, "%s/%.*S", root, npath, path);
808 dir = cleanname(dir);
810 s = smprint("%.*S", nstr, str);
811 c = complete(dir, s);
812 free(s);
813 if(c == nil)
814 goto Return;
816 if(!c->advance)
817 showcandidates(c);
819 if(c->advance)
820 rp = runesmprint("%s", c->string);
822 Return:
823 freecompletion(c);
824 free(dir);
825 free(path);
826 free(str);
827 return rp;
830 void
831 scrollup(int n)
833 setorigin(backnl(t.org, n), 1);
836 void
837 scrolldown(int n)
839 setorigin(line2q(n), 1);
840 if(t.qh<=t.org+t.f->nchars)
841 consread();
844 void
845 key(Rune r)
847 Rune *rp;
848 int nr;
850 if(r == 0)
851 return;
852 switch(r){
853 case Kpgup:
854 scrollup(t.f->maxlines*2/3);
855 return;
856 case Kpgdown:
857 scrolldown(t.f->maxlines*2/3);
858 return;
859 case Kup:
860 scrollup(t.f->maxlines/3);
861 return;
862 case Kdown:
863 scrolldown(t.f->maxlines/3);
864 return;
865 case Kleft:
866 if(t.q0 > 0){
867 t.q0--;
868 t.q1 = t.q0;
869 updatesel();
870 show(t.q0);
872 return;
873 case Kright:
874 if(t.q1 < t.nr){
875 t.q1++;
876 t.q0 = t.q1;
877 updatesel();
878 show(t.q1);
880 return;
881 case Khome:
882 show(0);
883 return;
884 case Kend:
885 case 0x05:
886 show(t.nr);
887 return;
889 /*
890 * Non-standard extensions.
891 */
892 case CUT:
893 snarf();
894 cut();
895 if(scrolling)
896 show(t.q0);
897 return;
898 case COPY:
899 snarf();
900 if(scrolling)
901 show(t.q0);
902 return;
903 case PASTE:
904 snarfupdate();
905 paste(t.snarf, t.nsnarf, 0);
906 if(scrolling)
907 show(t.q0);
908 return;
911 switch(r) {
912 /* case 0x03: can't do this because ^C is COPY */
913 case 0x7F: /* DEL: send interrupt */
914 paste(&r, 1, 1);
915 t.qh = t.q0 = t.q1 = t.nr;
916 show(t.q0);
917 /* must write the interrupt character in case app is in raw mode (e.g., ssh) */
918 write(rcfd, "\x7F", 1);
919 // postnote(PNGROUP, rcpid, "interrupt");
920 return;
923 if(rawon() && t.q0==t.nr){
924 addraw(&r, 1);
925 consread();
926 return;
929 if(r==ESC || (holdon && r==0x7F)){ /* toggle hold */
930 holdon = !holdon;
931 drawhold(holdon);
932 // replaceintegerproperty("_9WM_HOLD_MODE", 1, 32, holdon);
933 if(!holdon)
934 consread();
935 if(r==ESC)
936 return;
939 snarf();
941 switch(r) {
942 case 0x06: /* ^F: file name completion */
943 case Kins: /* Insert: file name completion */
944 rp = namecomplete();
945 if(rp == nil)
946 return;
947 nr = runestrlen(rp);
948 paste(rp, nr, 1);
949 free(rp);
950 return;
951 case 0x08: /* ^H: erase character */
952 case 0x15: /* ^U: erase line */
953 case 0x17: /* ^W: erase word */
954 if (t.q0 != 0 && t.q0 != t.qh)
955 t.q0 -= bswidth(r, t.q0, 1);
956 cut();
957 break;
958 default:
959 paste(&r, 1, 1);
960 break;
962 if(scrolling)
963 show(t.q0);
966 int
967 bswidth(Rune c, uint start, int eatnl)
969 uint q, eq, stop;
970 Rune r;
971 int skipping;
973 /* there is known to be at least one character to erase */
974 if(c == 0x08) /* ^H: erase character */
975 return 1;
976 q = start;
977 stop = 0;
978 if(q > t.qh)
979 stop = t.qh;
980 skipping = 1;
981 while(q > stop){
982 r = t.r[q-1];
983 if(r == '\n'){ /* eat at most one more character */
984 if(q == start && eatnl) /* eat the newline */
985 --q;
986 break;
988 if(c == 0x17){
989 eq = isalnum(r);
990 if(eq && skipping) /* found one; stop skipping */
991 skipping = 0;
992 else if(!eq && !skipping)
993 break;
995 --q;
997 return start-q;
1000 int
1001 consready(void)
1003 int i, c;
1005 if(holdon)
1006 return 0;
1008 if(rawon())
1009 return t.nraw != 0;
1011 /* look to see if there is a complete line */
1012 for(i=t.qh; i<t.nr; i++){
1013 c = t.r[i];
1014 if(c=='\n' || c=='\004' || c=='\x7F')
1015 return 1;
1017 return 0;
1021 void
1022 consread(void)
1024 char buf[8000], *p;
1025 int c, width, n;
1026 int s, raw;
1028 raw = rawon();
1029 for(;;) {
1030 if(!consready())
1031 return;
1032 n = sizeof(buf);
1033 p = buf;
1034 c = 0;
1035 while(n >= UTFmax && (t.qh<t.nr || t.nraw > 0)) {
1036 if(t.qh == t.nr){
1037 width = runetochar(p, &t.raw[0]);
1038 t.nraw--;
1039 runemove(t.raw, t.raw+1, t.nraw);
1040 }else
1041 width = runetochar(p, &t.r[t.qh++]);
1042 c = *p;
1043 p += width;
1044 n -= width;
1045 if(!raw && (c == '\n' || c == '\004' || c == '\x7F'))
1046 break;
1048 n = p-buf;
1051 * If we've been echoing, make sure the terminal isn't
1052 * while we do the write. This screws up if someone
1053 * else tries to turn off echo at the same time we do
1054 * (we'll turn it on again after the write), but that's not
1055 * too likely.
1057 s = setecho(sfd, 0);
1058 if(write(rcfd, buf, n) < 0)
1059 exits(0);
1060 if(s)
1061 setecho(sfd, s);
1065 void
1066 conswrite(char *p, int n)
1068 int n2, i;
1069 Rune buf2[1000], *q;
1071 /* convert to runes */
1072 i = t.npart;
1073 if(i > 0){
1074 /* handle partial runes */
1075 while(i < UTFmax && n>0) {
1076 t.part[i] = *p;
1077 i++;
1078 p++;
1079 n--;
1080 if(fullrune(t.part, i)) {
1081 t.npart = 0;
1082 chartorune(buf2, t.part);
1083 runewrite(buf2, 1);
1084 break;
1087 /* there is a little extra room in a message buf */
1090 while(n >= UTFmax || fullrune(p, n)) {
1091 n2 = nelem(buf2);
1092 q = buf2;
1094 while(n2) {
1095 if(n < UTFmax && !fullrune(p, n))
1096 break;
1097 i = chartorune(q, p);
1098 p += i;
1099 n -= i;
1100 n2--;
1101 q++;
1103 runewrite(buf2, q-buf2);
1106 if(n != 0) {
1107 assert(n+t.npart < UTFmax);
1108 memcpy(t.part+t.npart, p, n);
1109 t.npart += n;
1112 if(scrolling)
1113 show(t.qh);
1116 void
1117 runewrite(Rune *r, int n)
1119 int i;
1120 uint initial;
1121 uint q0, q1;
1122 uint p0, p1;
1123 Rune *p, *q;
1125 n = label(r, n);
1126 if(n == 0)
1127 return;
1129 /* get rid of backspaces */
1130 initial = 0;
1131 p = q = r;
1132 for(i=0; i<n; i++) {
1133 if(*p == '\b') {
1134 if(q == r)
1135 initial++;
1136 else
1137 --q;
1138 } else if(*p == '\r') { /* treat like ^U */
1139 /* convert CR without NL into erased line */
1140 /* i feel really sleazy about this but it helps */
1141 while(i<n-1 && *(p+1) == '\r'){
1142 i++;
1143 p++;
1145 if(i<n-1 && *(p+1) != '\n'){
1146 while(q > r && *(q-1) != '\n')
1147 q--;
1148 if(q==r)
1149 initial = bswidth(0x15, t.qh, 0);
1151 } else if(*p)
1152 *q++ = *p;
1153 p++;
1155 n = q-r;
1157 if(initial){
1158 /* write turned into a delete */
1160 if(initial > t.qh)
1161 initial = t.qh;
1162 q0 = t.qh-initial;
1163 q1 = t.qh;
1165 runemove(t.r+q0, t.r+q1, t.nr-q1);
1166 t.nr -= initial;
1167 t.qh -= initial;
1168 if(t.q0 > q1)
1169 t.q0 -= initial;
1170 else if(t.q0 > q0)
1171 t.q0 = q0;
1172 if(t.q1 > q1)
1173 t.q1 -= initial;
1174 else if(t.q1 > q0)
1175 t.q1 = q0;
1176 if(t.org > q1)
1177 t.org -= initial;
1178 else if(q0 < t.org+t.f->nchars){
1179 if(t.org < q0)
1180 p0 = q0 - t.org;
1181 else {
1182 t.org = q0;
1183 p0 = 0;
1185 p1 = q1 - t.org;
1186 if(p1 > t.f->nchars)
1187 p1 = t.f->nchars;
1188 frdelete(t.f, p0, p1);
1189 fill();
1191 updatesel();
1194 insert(r, n, t.qh, 1);
1198 void
1199 cut(void)
1201 uint n, p0, p1;
1202 uint q0, q1;
1204 q0 = t.q0;
1205 q1 = t.q1;
1207 if (q0 < t.org && q1 >= t.org)
1208 show(q0);
1210 n = q1-q0;
1211 if(n == 0)
1212 return;
1213 runemove(t.r+q0, t.r+q1, t.nr-q1);
1214 t.nr -= n;
1215 t.q0 = t.q1 = q0;
1216 if(q1 < t.qh)
1217 t.qh -= n;
1218 else if(q0 < t.qh)
1219 t.qh = q0;
1220 if(q1 < t.org)
1221 t.org -= n;
1222 else if(q0 < t.org+t.f->nchars){
1223 assert(q0 >= t.org);
1224 p0 = q0 - t.org;
1225 p1 = q1 - t.org;
1226 if(p1 > t.f->nchars)
1227 p1 = t.f->nchars;
1228 frdelete(t.f, p0, p1);
1229 fill();
1231 updatesel();
1234 void
1235 snarfupdate(void)
1237 char *pp;
1238 int n, i;
1239 Rune *p;
1241 pp = getsnarf();
1242 if(pp == nil)
1243 return;
1244 n = strlen(pp);
1245 if(n <= 0) {
1246 /*t.nsnarf = 0;*/
1247 return;
1249 t.snarf = runerealloc(t.snarf, n);
1250 for(i=0,p=t.snarf; i<n; p++)
1251 i += chartorune(p, pp+i);
1252 t.nsnarf = p-t.snarf;
1256 char sbuf[SnarfSize];
1257 void
1258 snarf(void)
1260 char *p;
1261 int i, n;
1262 Rune *rp;
1264 if(t.q1 == t.q0)
1265 return;
1266 n = t.q1-t.q0;
1267 t.snarf = runerealloc(t.snarf, n);
1268 for(i=0,p=sbuf,rp=t.snarf; i<n && p < sbuf+SnarfSize-UTFmax; i++){
1269 *rp++ = *(t.r+t.q0+i);
1270 p += runetochar(p, t.r+t.q0+i);
1272 t.nsnarf = rp-t.snarf;
1273 *p = '\0';
1274 putsnarf(sbuf);
1277 uint
1278 min(uint x, uint y)
1280 if(x < y)
1281 return x;
1282 return y;
1285 uint
1286 max(uint x, uint y)
1288 if(x > y)
1289 return x;
1290 return y;
1293 uint
1294 insert(Rune *r, int n, uint q0, int hostwrite)
1296 uint m;
1298 if(n == 0)
1299 return q0;
1300 if(t.nr+n>HiWater && q0>=t.org && q0>=t.qh){
1301 m = min(HiWater-LoWater, min(t.org, t.qh));
1302 t.org -= m;
1303 t.qh -= m;
1304 if(t.q0 > m)
1305 t.q0 -= m;
1306 else
1307 t.q0 = 0;
1308 if(t.q1 > m)
1309 t.q1 -= m;
1310 else
1311 t.q1 = 0;
1312 t.nr -= m;
1313 runemove(t.r, t.r+m, t.nr);
1314 q0 -= m;
1316 if(t.nr+n > t.maxr){
1318 * Minimize realloc breakage:
1319 * Allocate at least MinWater
1320 * Double allocation size each time
1321 * But don't go much above HiWater
1323 m = max(min(2*(t.nr+n), HiWater), t.nr+n)+MinWater;
1324 if(m > HiWater)
1325 m = max(HiWater+MinWater, t.nr+n);
1326 if(m > t.maxr){
1327 t.r = runerealloc(t.r, m);
1328 t.maxr = m;
1331 runemove(t.r+q0+n, t.r+q0, t.nr-q0);
1332 runemove(t.r+q0, r, n);
1333 t.nr += n;
1334 /* if output touches, advance selection, not qh; works best for keyboard and output */
1335 if(q0 <= t.q1)
1336 t.q1 += n;
1337 if(q0 <= t.q0)
1338 t.q0 += n;
1339 if(q0 < t.qh || (q0==t.qh && hostwrite))
1340 t.qh += n;
1341 else
1342 consread();
1343 if(q0 < t.org)
1344 t.org += n;
1345 else if(q0 <= t.org+t.f->nchars)
1346 frinsert(t.f, r, r+n, q0-t.org);
1347 return q0;
1350 void
1351 paste(Rune *r, int n, int advance)
1353 Rune *rbuf;
1355 if(rawon() && t.q0==t.nr){
1356 addraw(r, n);
1357 return;
1360 cut();
1361 if(n == 0)
1362 return;
1365 * if this is a button2 execute then we might have been passed
1366 * runes inside the buffer. must save them before realloc.
1368 rbuf = nil;
1369 if(t.r <= r && r < t.r+n){
1370 rbuf = runemalloc(n);
1371 runemove(rbuf, r, n);
1372 r = rbuf;
1375 insert(r, n, t.q0, 0);
1376 updatesel();
1377 free(rbuf);
1380 void
1381 fill(void)
1383 if (t.f->nlines >= t.f->maxlines)
1384 return;
1385 frinsert(t.f, t.r + t.org + t.f->nchars, t.r + t.nr, t.f->nchars);
1388 void
1389 updatesel(void)
1391 Frame *f;
1392 uint n;
1394 f = t.f;
1395 if(t.org+f->p0 == t.q0 && t.org+f->p1 == t.q1)
1396 return;
1398 n = t.f->nchars;
1400 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
1401 if (t.q0 >= t.org)
1402 f->p0 = t.q0-t.org;
1403 else
1404 f->p0 = 0;
1405 if(f->p0 > n)
1406 f->p0 = n;
1407 if (t.q1 >= t.org)
1408 f->p1 = t.q1-t.org;
1409 else
1410 f->p1 = 0;
1411 if(f->p1 > n)
1412 f->p1 = n;
1413 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
1416 if(t.qh<=t.org+t.f.nchars && t.cwqueue != 0)
1417 t.cwqueue->wakeup <-= 0;
1420 tcheck();
1423 void
1424 show(uint q0)
1426 int nl;
1427 uint q, oq;
1429 if(cansee(q0))
1430 return;
1432 if (q0<t.org)
1433 nl = t.f->maxlines/5;
1434 else
1435 nl = 4*t.f->maxlines/5;
1436 q = backnl(q0, nl);
1437 /* avoid going in the wrong direction */
1438 if (q0>t.org && q<t.org)
1439 q = t.org;
1440 setorigin(q, 0);
1441 /* keep trying until q0 is on the screen */
1442 while(!cansee(q0)) {
1443 assert(q0 >= t.org);
1444 oq = q;
1445 q = line2q(t.f->maxlines-nl);
1446 assert(q > oq);
1447 setorigin(q, 1);
1451 int
1452 cansee(uint q0)
1454 uint qe;
1456 qe = t.org+t.f->nchars;
1458 if(q0>=t.org && q0 < qe)
1459 return 1;
1460 if (q0 != qe)
1461 return 0;
1462 if (t.f->nlines < t.f->maxlines)
1463 return 1;
1464 if (q0 > 0 && t.r[t.nr-1] == '\n')
1465 return 0;
1466 return 1;
1470 void
1471 setorigin(uint org, int exact)
1473 int i, a;
1474 uint n;
1476 if(org>0 && !exact){
1477 /* try and start after a newline */
1478 /* don't try harder than 256 chars */
1479 for(i=0; i<256 && org<t.nr; i++){
1480 if(t.r[org-1] == '\n')
1481 break;
1482 org++;
1485 a = org-t.org;
1487 if(a>=0 && a<t.f->nchars)
1488 frdelete(t.f, 0, a);
1489 else if(a<0 && -a<100*t.f->maxlines){
1490 n = t.org - org;
1491 frinsert(t.f, t.r+org, t.r+org+n, 0);
1492 }else
1493 frdelete(t.f, 0, t.f->nchars);
1494 t.org = org;
1495 fill();
1496 updatesel();
1500 uint
1501 line2q(uint n)
1503 Frame *f;
1505 f = t.f;
1506 return frcharofpt(f, Pt(f->r.min.x, f->r.min.y + n*font->height))+t.org;
1509 uint
1510 backnl(uint p, uint n)
1512 int i, j;
1514 for (i = n;; i--) {
1515 /* at 256 chars, call it a line anyway */
1516 for(j=256; --j>0 && p>0; p--)
1517 if(t.r[p-1]=='\n')
1518 break;
1519 if (p == 0 || i == 0)
1520 return p;
1521 p--;
1523 return 0; /* alef bug */
1526 void
1527 addraw(Rune *r, int nr)
1529 t.raw = runerealloc(t.raw, t.nraw+nr);
1530 runemove(t.raw+t.nraw, r, nr);
1531 t.nraw += nr;
1533 if(t.crqueue != nil)
1534 t.crqueue->wakeup <-= 0;
1539 Rune left1[] = { '{', '[', '(', '<', 0xab, 0 };
1540 Rune right1[] = { '}', ']', ')', '>', 0xbb, 0 };
1541 Rune left2[] = { '\n', 0 };
1542 Rune left3[] = { '\'', '"', '`', 0 };
1544 Rune *left[] = {
1545 left1,
1546 left2,
1547 left3,
1551 Rune *right[] = {
1552 right1,
1553 left2,
1554 left3,
1558 void
1559 doubleclick(uint *q0, uint *q1)
1561 int c, i;
1562 Rune *r, *l, *p;
1563 uint q;
1565 for(i=0; left[i]!=0; i++){
1566 q = *q0;
1567 l = left[i];
1568 r = right[i];
1569 /* try matching character to left, looking right */
1570 if(q == 0)
1571 c = '\n';
1572 else
1573 c = t.r[q-1];
1574 p = strrune(l, c);
1575 if(p != 0){
1576 if(clickmatch(c, r[p-l], 1, &q))
1577 *q1 = q-(c!='\n');
1578 return;
1580 /* try matching character to right, looking left */
1581 if(q == t.nr)
1582 c = '\n';
1583 else
1584 c = t.r[q];
1585 p = strrune(r, c);
1586 if(p != 0){
1587 if(clickmatch(c, l[p-r], -1, &q)){
1588 *q1 = *q0+(*q0<t.nr && c=='\n');
1589 *q0 = q;
1590 if(c!='\n' || q!=0 || t.r[0]=='\n')
1591 (*q0)++;
1593 return;
1596 /* try filling out word to right */
1597 while(*q1<t.nr && isalnum(t.r[*q1]))
1598 (*q1)++;
1599 /* try filling out word to left */
1600 while(*q0>0 && isalnum(t.r[*q0-1]))
1601 (*q0)--;
1604 int
1605 clickmatch(int cl, int cr, int dir, uint *q)
1607 Rune c;
1608 int nest;
1610 nest = 1;
1611 for(;;){
1612 if(dir > 0){
1613 if(*q == t.nr)
1614 break;
1615 c = t.r[*q];
1616 (*q)++;
1617 }else{
1618 if(*q == 0)
1619 break;
1620 (*q)--;
1621 c = t.r[*q];
1623 if(c == cr){
1624 if(--nest==0)
1625 return 1;
1626 }else if(c == cl)
1627 nest++;
1629 return cl=='\n' && nest==1;
1632 void
1633 tcheck(void)
1635 Frame *f;
1637 f = t.f;
1639 assert(t.q0 <= t.q1 && t.q1 <= t.nr);
1640 assert(t.org <= t.nr && t.qh <= t.nr);
1641 assert(f->p0 <= f->p1 && f->p1 <= f->nchars);
1642 assert(t.org + f->nchars <= t.nr);
1643 assert(t.org+f->nchars==t.nr || (f->nlines >= f->maxlines));
1646 Rune*
1647 strrune(Rune *s, Rune c)
1649 Rune c1;
1651 if(c == 0) {
1652 while(*s++)
1654 return s-1;
1657 while(c1 = *s++)
1658 if(c1 == c)
1659 return s-1;
1660 return 0;
1663 void
1664 scrdraw(void)
1666 Rectangle r, r1, r2;
1667 static Image *scrx;
1669 r = scrollr;
1670 r.min.x += 1; /* border between margin and bar */
1671 r1 = r;
1672 if(scrx==0 || scrx->r.max.y < r.max.y){
1673 if(scrx)
1674 freeimage(scrx);
1675 scrx = allocimage(display, Rect(0, 0, 32, r.max.y), screen->chan, 1, DPaleyellow);
1676 if(scrx == 0)
1677 sysfatal("scroll balloc");
1679 r1.min.x = 0;
1680 r1.max.x = Dx(r);
1681 r2 = scrpos(r1, t.org, t.org+t.f->nchars, t.nr);
1682 if(!eqrect(r2, lastsr)){
1683 lastsr = r2;
1684 draw(scrx, r1, cols[BORD], nil, ZP);
1685 draw(scrx, r2, cols[BACK], nil, r2.min);
1686 // r2 = r1;
1687 // r2.min.x = r2.max.x-1;
1688 // draw(scrx, r2, cols[BORD], nil, ZP);
1689 draw(screen, r, scrx, nil, r1.min);
1693 Rectangle
1694 scrpos(Rectangle r, ulong p0, ulong p1, ulong tot)
1696 long h;
1697 Rectangle q;
1699 q = insetrect(r, 1);
1700 h = q.max.y-q.min.y;
1701 if(tot == 0)
1702 return q;
1703 if(tot > 1024L*1024L)
1704 tot >>= 10, p0 >>= 10, p1 >>= 10;
1705 if(p0 > 0)
1706 q.min.y += h*p0/tot;
1707 if(p1 < tot)
1708 q.max.y -= h*(tot-p1)/tot;
1709 if(q.max.y < q.min.y+2){
1710 if(q.min.y+2 <= r.max.y)
1711 q.max.y = q.min.y+2;
1712 else
1713 q.min.y = q.max.y-2;
1715 return q;
1718 void
1719 scroll(int but)
1721 uint p0, oldp0;
1722 Rectangle s;
1723 int x, y, my, h, first, exact;
1725 s = insetrect(scrollr, 1);
1726 h = s.max.y-s.min.y;
1727 x = (s.min.x+s.max.x)/2;
1728 oldp0 = ~0;
1729 first = 1;
1730 do{
1731 if(t.m.xy.x<s.min.x || s.max.x<=t.m.xy.x){
1732 readmouse(mc);
1733 t.m = mc->m;
1734 }else{
1735 my = t.m.xy.y;
1736 if(my < s.min.y)
1737 my = s.min.y;
1738 if(my >= s.max.y)
1739 my = s.max.y;
1740 // if(!eqpt(t.m.xy, Pt(x, my)))
1741 // cursorset(Pt(x, my));
1742 exact = 1;
1743 if(but == 2){
1744 y = my;
1745 if(y > s.max.y-2)
1746 y = s.max.y-2;
1747 if(t.nr > 1024*1024)
1748 p0 = ((t.nr>>10)*(y-s.min.y)/h)<<10;
1749 else
1750 p0 = t.nr*(y-s.min.y)/h;
1751 exact = 0;
1752 } else if(but == 1)
1753 p0 = backnl(t.org, (my-s.min.y)/font->height);
1754 else
1755 p0 = t.org+frcharofpt(t.f, Pt(s.max.x, my));
1757 if(oldp0 != p0)
1758 setorigin(p0, exact);
1759 oldp0 = p0;
1760 scrdraw();
1761 readmouse(mc);
1762 t.m = mc->m;
1764 }while(t.m.buttons & (1<<(but-1)));
1767 void
1768 plumbstart(void)
1770 if((plumbfd = plumbopen("send", OWRITE)) < 0)
1771 fprint(2, "9term: plumbopen: %r\n");
1774 void
1775 plumb(uint q0, uint q1)
1777 Plumbmsg *pm;
1778 char *p;
1779 int i, p0, n;
1780 char cbuf[100];
1782 pm = malloc(sizeof(Plumbmsg));
1783 pm->src = strdup("9term");
1784 pm->dst = 0;
1785 pm->wdir = strdup(wdir);
1786 pm->type = strdup("text");
1787 pm->data = nil;
1788 if(q1 > q0)
1789 pm->attr = nil;
1790 else{
1791 p0 = q0;
1792 wordclick(&q0, &q1);
1793 sprint(cbuf, "click=%d", p0-q0);
1794 pm->attr = plumbunpackattr(cbuf);
1796 if(q0==q1){
1797 plumbfree(pm);
1798 return;
1800 pm->data = malloc(SnarfSize);
1801 n = q1 - q0;
1802 for(i=0,p=pm->data; i<n && p < pm->data + SnarfSize-UTFmax; i++)
1803 p += runetochar(p, t.r+q0+i);
1804 *p = '\0';
1805 pm->ndata = strlen(pm->data);
1806 if(plumbsend(plumbfd, pm) < 0){
1807 setcursor(mc, &query);
1808 sleep(500);
1809 if(holdon)
1810 setcursor(mc, &whitearrow);
1811 else
1812 setcursor(mc, nil);
1814 plumbfree(pm);
1818 * Process in-band messages about window title changes.
1819 * The messages are of the form:
1821 * \033];xxx\007
1823 * where xxx is the new directory. This format was chosen
1824 * because it changes the label on xterm windows.
1826 int
1827 label(Rune *sr, int n)
1829 Rune *sl, *el, *er, *r;
1831 er = sr+n;
1832 for(r=er-1; r>=sr; r--)
1833 if(*r == '\007')
1834 break;
1835 if(r < sr)
1836 return n;
1838 el = r+1;
1839 if(el-sr > sizeof wdir)
1840 sr = el - sizeof wdir;
1841 for(sl=el-3; sl>=sr; sl--)
1842 if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
1843 break;
1844 if(sl < sr)
1845 return n;
1847 snprint(wdir, sizeof wdir, "%.*S", (el-1)-(sl+3), sl+3);
1848 drawsetlabel(wdir);
1850 runemove(sl, el, er-el);
1851 n -= (el-sl);
1852 return n;
1855 int
1856 rawon(void)
1858 return !cooked && !isecho(sfd);
1862 * Clumsy hack to make " and "" work.
1863 * Then again, what's not a clumsy hack here in Unix land?
1866 char adir[100];
1867 int afd;
1869 void
1870 removethesocket(void)
1872 if(thesocket[0])
1873 if(remove(thesocket) < 0)
1874 fprint(2, "remove %s: %r\n", thesocket);
1877 void
1878 servedevtext(void)
1880 char buf[100];
1882 snprint(buf, sizeof buf, "unix!/tmp/9term-text.%d", getpid());
1884 if((afd = announce(buf, adir)) < 0){
1885 putenv("text9term", "");
1886 return;
1889 putenv("text9term", buf);
1890 proccreate(listenproc, nil, STACK);
1891 strcpy(thesocket, buf+5);
1892 atexit(removethesocket);
1895 void
1896 listenproc(void *arg)
1898 int fd;
1899 char dir[100];
1901 USED(arg);
1902 for(;;){
1903 fd = listen(adir, dir);
1904 if(fd < 0){
1905 close(afd);
1906 return;
1908 proccreate(textthread, (void*)fd, STACK);
1912 void
1913 textthread(void *arg)
1915 int fd, i, x, n, end;
1916 Rune r;
1917 char buf[4096], *p, *ep;
1919 fd = (int)arg;
1920 p = buf;
1921 ep = buf+sizeof buf;
1922 end = t.org+t.nr; /* avoid possible output loop */
1923 for(i=t.org;; i++){
1924 if(i >= end || ep-p < UTFmax){
1925 for(x=0; x<p-buf; x+=n)
1926 if((n = write(fd, buf+x, (p-x)-buf)) <= 0)
1927 goto break2;
1929 if(i >= end)
1930 break;
1931 p = buf;
1933 if(i < t.org)
1934 i = t.org;
1935 r = t.r[i-t.org];
1936 if(r < Runeself)
1937 *p++ = r;
1938 else
1939 p += runetochar(p, &r);
1941 break2:
1942 close(fd);