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 int
308 isexpand(Rune r)
310 return r=='_' || ('0' <= r && r <= '9') || isalpharune(r);
313 void
314 hangupnote(void *a, char *msg)
316 if(getpid() != mainpid)
317 noted(NDFLT);
318 if(strcmp(msg, "hangup") == 0 && rcpid != 0){
319 postnote(PNGROUP, rcpid, "hangup");
320 noted(NDFLT);
322 if(strstr(msg, "child")){
323 char buf[128];
324 int n;
326 n = awaitnohang(buf, sizeof buf-1);
327 if(n > 0){
328 buf[n] = 0;
329 if(atoi(buf) == rcpid)
330 threadexitsall(0);
332 noted(NCONT);
334 noted(NDFLT);
337 void
338 hostproc(void *arg)
340 Channel *c;
341 int i, n, which;
343 c = arg;
345 i = 0;
346 for(;;){
347 /* Let typing have a go -- maybe there's a rubout waiting. */
348 yield();
350 i = 1-i; /* toggle */
351 n = read(rcfd, rcbuf[i].data, sizeof rcbuf[i].data);
352 if(n <= 0){
353 if(n < 0)
354 fprint(2, "9term: host read error: %r\n");
355 threadexitsall("host");
357 rcbuf[i].n = n;
358 which = i;
359 send(c, &which);
363 void
364 hoststart(void)
366 hostc = chancreate(sizeof(int), 0);
367 proccreate(hostproc, hostc, 32*1024);
370 void
371 loop(void)
373 Rune r;
374 int i;
375 Alt a[5];
377 a[0].c = mc->c;
378 a[0].v = &mc->m;
379 a[0].op = CHANRCV;
381 a[1].c = kc->c;
382 a[1].v = &r;
383 a[1].op = CHANRCV;
385 a[2].c = hostc;
386 a[2].v = &i;
387 a[2].op = CHANRCV;
389 a[3].c = mc->resizec;
390 a[3].v = nil;
391 a[3].op = CHANRCV;
393 a[4].c = nil;
394 a[4].v = nil;
395 a[4].op = CHANEND;
397 for(;;) {
398 tcheck();
400 scrdraw();
401 flushimage(display, 1);
402 a[2].op = CHANRCV;
403 if(!scrolling && t.qh > t.org+t.f->nchars)
404 a[2].op = CHANNOP;;
405 switch(alt(a)) {
406 default:
407 sysfatal("impossible");
408 case 0:
409 t.m = mc->m;
410 mouse();
411 break;
412 case 1:
413 key(r);
414 break;
415 case 2:
416 conswrite((char*)rcbuf[i].data, rcbuf[i].n);
417 break;
418 case 3:
419 doreshape();
420 break;
425 void
426 doreshape(void)
428 if(getwindow(display, Refnone) < 0)
429 sysfatal("can't reattach to window");
430 draw(screen, screen->r, cols[BACK], nil, ZP);
431 geom();
432 scrdraw();
435 void
436 geom(void)
438 Point p;
439 Rectangle r;
441 if(!acmecolors){
442 if(_windowhasfocus){
443 cols[TEXT] = cols[HTEXT] = display->black;
444 hcols[TEXT] = hcols[HTEXT] = blue;
445 }else{
446 cols[TEXT] = cols[HTEXT] = palegrey;
447 hcols[TEXT] = hcols[HTEXT] = paleblue;
451 r = screen->r;
452 r.min.y++;
453 r.max.y--;
455 scrollr = r;
456 scrollr.max.x = r.min.x+Scrollwid;
457 lastsr = Rect(0,0,0,0);
459 r.min.x += Scrollwid+Scrollgap;
461 frclear(t.f, 0);
462 frinit(t.f, r, font, screen, holdon ? hcols : cols);
463 t.f->maxtab = maxtab*stringwidth(font, "0");
464 fill();
465 updatesel();
467 p = stringsize(font, "0");
468 if(p.x == 0 || p.y == 0)
469 return;
471 updatewinsize(Dy(r)/p.y, Dx(r)/p.x, Dx(r), Dy(r));
474 void
475 drawhold(int holdon)
477 if(holdon)
478 setcursor(mc, &whitearrow);
479 else
480 setcursor(mc, nil);
482 draw(screen, screen->r, cols[BACK], nil, ZP);
483 geom();
484 scrdraw();
487 void
488 wordclick(uint *q0, uint *q1)
490 while(*q1<t.nr && !isspace(t.r[*q1]))
491 (*q1)++;
492 while(*q0>0 && !isspace(t.r[*q0-1]))
493 (*q0)--;
496 int
497 aselect(uint *q0, uint *q1, Image *color)
499 int cancel;
500 uint oldq0, oldq1, newq0, newq1;
502 /* save old selection */
503 oldq0 = t.q0;
504 oldq1 = t.q1;
506 /* sweep out area and record it */
507 t.f->cols[HIGH] = color;
508 t.f->cols[HTEXT] = display->white;
509 mselect();
510 newq0 = t.q0;
511 newq1 = t.q1;
513 cancel = 0;
514 if(t.m.buttons != 0){
515 while(t.m.buttons){
516 readmouse(mc);
517 t.m = mc->m;
519 cancel = 1;
522 /* restore old selection */
523 t.f->cols[HIGH] = cols[HIGH];
524 t.f->cols[HTEXT] = cols[HTEXT];
525 t.q0 = oldq0;
526 t.q1 = oldq1;
527 updatesel();
529 if(cancel)
530 return -1;
532 /* selected a region */
533 if(newq0 < newq1){
534 *q0 = newq0;
535 *q1 = newq1;
536 return 0;
539 /* clicked inside previous selection */
540 /* the "<=" in newq0 <= oldq1 allows us to click the right edge */
541 if(oldq0 <= newq0 && newq0 <= oldq1){
542 *q0 = oldq0;
543 *q1 = oldq1;
544 return 0;
547 /* just a click */
548 *q0 = newq0;
549 *q1 = newq1;
550 return 0;
553 static Rune Lnl[1] = { '\n' };
555 void
556 mouse(void)
558 int but;
559 uint q0, q1;
561 but = t.m.buttons;
563 if(but != 1 && but != 2 && but != 4 && but != 8 && but != 16)
564 return;
566 if (ptinrect(t.m.xy, scrollr)) {
567 scroll(but);
568 if(t.qh<=t.org+t.f->nchars)
569 consread();
570 return;
573 switch(but) {
574 case 1:
575 mselect();
576 break;
577 case 2:
578 if(button2exec){
579 if(aselect(&q0, &q1, execcolor) >= 0){
580 if(q0 == q1)
581 wordclick(&q0, &q1);
582 if(q0 == q1)
583 break;
584 t.q0 = t.q1 = t.nr;
585 updatesel();
586 paste(t.r+q0, q1-q0, 1);
587 if(t.r[q1-1] != '\n')
588 paste(Lnl, 1, 1);
590 break;
592 domenu2(2);
593 break;
594 case 4:
595 bouncemouse(&t.m);
596 break;
597 /*
598 if(aselect(&q0, &q1, plumbcolor) >= 0)
599 plumb(q0, q1);
600 break;
601 */
602 case 8:
603 scrollup(mousescrollsize(t.f->maxlines));
604 break;
605 case 16:
606 scrolldown(mousescrollsize(t.f->maxlines));
607 break;
611 void
612 mselect(void)
614 int b, x, y;
615 uint q0;
617 b = t.m.buttons;
618 q0 = frcharofpt(t.f, t.m.xy) + t.org;
619 if(t.m.msec-clickmsec<500 && clickq0==q0 && t.q0==t.q1 && b==1){
620 doubleclick(&t.q0, &t.q1);
621 updatesel();
622 /* t.t.i->flush(); */
623 x = t.m.xy.x;
624 y = t.m.xy.y;
625 /* stay here until something interesting happens */
626 do {
627 readmouse(mc);
628 t.m = mc->m;
629 } while(t.m.buttons==b && abs(t.m.xy.x-x)<4 && abs(t.m.xy.y-y)<4);
630 t.m.xy.x = x; /* in case we're calling frselect */
631 t.m.xy.y = y;
632 clickmsec = 0;
635 if(t.m.buttons == b) {
636 frselect(t.f, mc);
637 t.m = mc->m;
638 t.q0 = t.f->p0 + t.org;
639 t.q1 = t.f->p1 + t.org;
640 clickmsec = t.m.msec;
641 clickq0 = t.q0;
643 if((t.m.buttons != b) &&(b&1)){
644 enum{Cancut = 1, Canpaste = 2} state = Cancut | Canpaste;
645 while(t.m.buttons){
646 if(t.m.buttons&2){
647 if(state&Cancut){
648 snarf();
649 cut();
650 state = Canpaste;
652 }else if(t.m.buttons&4){
653 if(state&Canpaste){
654 snarfupdate();
655 if(t.nsnarf){
656 paste(t.snarf, t.nsnarf, 0);
658 state = Cancut;
661 readmouse(mc);
662 t.m = mc->m;
667 Rune newline[] = { '\n', 0 };
669 void
670 domenu2(int but)
672 if(scrolling)
673 menu2str[Scroll] = "+ scroll";
674 else
675 menu2str[Scroll] = "- scroll";
676 if(cooked)
677 menu2str[Cooked] = "+ mustecho";
678 else
679 menu2str[Cooked] = "- mustecho";
681 switch(menuhit(but, mc, &menu2, nil)){
682 case -1:
683 break;
684 case Cut:
685 snarf();
686 cut();
687 if(scrolling)
688 show(t.q0);
689 break;
690 case Paste:
691 snarfupdate();
692 paste(t.snarf, t.nsnarf, 0);
693 if(scrolling)
694 show(t.q0);
695 break;
696 case Snarf:
697 snarf();
698 if(scrolling)
699 show(t.q0);
700 break;
701 case Send:
702 if(t.q0 != t.q1)
703 snarf();
704 else
705 snarfupdate();
706 t.q0 = t.q1 = t.nr;
707 updatesel();
708 paste(t.snarf, t.nsnarf, 1);
709 if(t.nsnarf == 0 || t.snarf[t.nsnarf-1] != '\n')
710 paste(newline, 1, 1);
711 show(t.nr);
712 consread();
713 break;
714 case Scroll:
715 scrolling = !scrolling;
716 if (scrolling) {
717 show(t.nr);
718 consread();
720 break;
721 case Plumb:
722 plumb(t.q0, t.q1);
723 break;
724 case Cooked:
725 cooked = !cooked;
726 break;
727 default:
728 sysfatal("bad menu item");
732 int
733 windfilewidth(uint q0, int oneelement)
735 uint q;
736 Rune r;
738 q = q0;
739 while(q > 0){
740 r = t.r[q-1];
741 if(r<=' ')
742 break;
743 if(oneelement && r=='/')
744 break;
745 --q;
747 return q0-q;
750 void
751 showcandidates(Completion *c)
753 int i;
754 Fmt f;
755 Rune *rp;
756 uint nr, qline, q0;
757 char *s;
759 runefmtstrinit(&f);
760 if (c->nmatch == 0)
761 s = "[no matches in ";
762 else
763 s = "[";
764 if(c->nfile > 32)
765 fmtprint(&f, "%s%d files]\n", s, c->nfile);
766 else{
767 fmtprint(&f, "%s", s);
768 for(i=0; i<c->nfile; i++){
769 if(i > 0)
770 fmtprint(&f, " ");
771 fmtprint(&f, "%s", c->filename[i]);
773 fmtprint(&f, "]\n");
775 /* place text at beginning of line before host point */
776 qline = t.qh;
777 while(qline>0 && t.r[qline-1] != '\n')
778 qline--;
780 rp = runefmtstrflush(&f);
781 nr = runestrlen(rp);
783 q0 = t.q0;
784 q0 += insert(rp, nr, qline, 0) - qline;
785 free(rp);
786 t.q0 = q0+nr;
787 t.q1 = q0+nr;
788 updatesel();
791 Rune*
792 namecomplete(void)
794 int nstr, npath;
795 Rune *rp, *path, *str;
796 Completion *c;
797 char *s, *dir, *root;
799 /* control-f: filename completion; works back to white space or / */
800 if(t.q0<t.nr && t.r[t.q0]>' ') /* must be at end of word */
801 return nil;
802 nstr = windfilewidth(t.q0, TRUE);
803 str = runemalloc(nstr);
804 runemove(str, t.r+(t.q0-nstr), nstr);
805 npath = windfilewidth(t.q0-nstr, FALSE);
806 path = runemalloc(npath);
807 runemove(path, t.r+(t.q0-nstr-npath), npath);
808 rp = nil;
810 /* is path rooted? if not, we need to make it relative to window path */
811 if(npath>0 && path[0]=='/'){
812 dir = malloc(UTFmax*npath+1);
813 sprint(dir, "%.*S", npath, path);
814 }else{
815 if(strcmp(wdir, "") == 0)
816 root = ".";
817 else
818 root = wdir;
819 dir = malloc(strlen(root)+1+UTFmax*npath+1);
820 sprint(dir, "%s/%.*S", root, npath, path);
822 dir = cleanname(dir);
824 s = smprint("%.*S", nstr, str);
825 c = complete(dir, s);
826 free(s);
827 if(c == nil)
828 goto Return;
830 if(!c->advance)
831 showcandidates(c);
833 if(c->advance)
834 rp = runesmprint("%s", c->string);
836 Return:
837 freecompletion(c);
838 free(dir);
839 free(path);
840 free(str);
841 return rp;
844 void
845 scrollup(int n)
847 setorigin(backnl(t.org, n), 1);
850 void
851 scrolldown(int n)
853 setorigin(line2q(n), 1);
854 if(t.qh<=t.org+t.f->nchars)
855 consread();
858 void
859 key(Rune r)
861 Rune *rp;
862 int nr;
864 if(r == 0)
865 return;
866 switch(r){
867 case Kpgup:
868 scrollup(t.f->maxlines*2/3);
869 return;
870 case Kpgdown:
871 scrolldown(t.f->maxlines*2/3);
872 return;
873 case Kup:
874 scrollup(t.f->maxlines/3);
875 return;
876 case Kdown:
877 scrolldown(t.f->maxlines/3);
878 return;
879 case Kleft:
880 if(t.q0 > 0){
881 t.q0--;
882 t.q1 = t.q0;
883 updatesel();
884 show(t.q0);
886 return;
887 case Kright:
888 if(t.q1 < t.nr){
889 t.q1++;
890 t.q0 = t.q1;
891 updatesel();
892 show(t.q1);
894 return;
895 case Khome:
896 show(0);
897 return;
898 case Kend:
899 case 0x05:
900 show(t.nr);
901 return;
903 /*
904 * Non-standard extensions.
905 */
906 case CUT:
907 snarf();
908 cut();
909 if(scrolling)
910 show(t.q0);
911 return;
912 case COPY:
913 snarf();
914 if(scrolling)
915 show(t.q0);
916 return;
917 case PASTE:
918 snarfupdate();
919 paste(t.snarf, t.nsnarf, 0);
920 if(scrolling)
921 show(t.q0);
922 return;
925 /*
926 * This if used to be below the if(rawon() && t.q0==t.nr),
927 * but let's try putting it here. This will allow ESC-processing
928 * to toggle hold mode even in remote SSH connections.
929 * The drawback is that vi-style processing gets harder.
930 * If you find yourself in some weird readline mode, good
931 * luck getting out without ESC. Let's see who complains.
932 */
933 if(r==ESC){ /* toggle hold */
934 holdon = !holdon;
935 drawhold(holdon);
936 /* replaceintegerproperty("_9WM_HOLD_MODE", 1, 32, holdon); */
937 if(!holdon)
938 consread();
939 return;
942 if(!holdon && rawon() && t.q0 == t.nr){
943 addraw(&r, 1);
944 consread();
945 return;
948 if(r == 0x7F){ /* DEL: send interrupt; what a mess */
949 if(holdon){
950 holdon = 0;
951 drawhold(holdon);
953 t.qh = t.q0 = t.q1 = t.nr;
954 show(t.q0);
955 write(rcfd, "\x7F", 1);
956 return;
959 snarf();
961 switch(r) {
962 case 0x06: /* ^F: file name completion */
963 case Kins: /* Insert: file name completion */
964 rp = namecomplete();
965 if(rp == nil)
966 return;
967 nr = runestrlen(rp);
968 paste(rp, nr, 1);
969 free(rp);
970 return;
971 case 0x08: /* ^H: erase character */
972 case 0x15: /* ^U: erase line */
973 case 0x17: /* ^W: erase word */
974 if (t.q0 != 0 && t.q0 != t.qh)
975 t.q0 -= bswidth(r, t.q0, 1);
976 cut();
977 break;
978 default:
979 paste(&r, 1, 1);
980 break;
982 if(scrolling)
983 show(t.q0);
986 int
987 bswidth(Rune c, uint start, int eatnl)
989 uint q, eq, stop;
990 Rune r;
991 int skipping;
993 /* there is known to be at least one character to erase */
994 if(c == 0x08) /* ^H: erase character */
995 return 1;
996 q = start;
997 stop = 0;
998 if(q > t.qh)
999 stop = t.qh;
1000 skipping = 1;
1001 while(q > stop){
1002 r = t.r[q-1];
1003 if(r == '\n'){ /* eat at most one more character */
1004 if(q == start && eatnl) /* eat the newline */
1005 --q;
1006 break;
1008 if(c == 0x17){
1009 eq = isexpand(r);
1010 if(eq && skipping) /* found one; stop skipping */
1011 skipping = 0;
1012 else if(!eq && !skipping)
1013 break;
1015 --q;
1017 return start-q;
1020 int
1021 consready(void)
1023 int i, c;
1025 if(holdon)
1026 return 0;
1028 if(rawon())
1029 return t.nraw != 0 || t.qh < t.nr;
1031 /* look to see if there is a complete line */
1032 for(i=t.qh; i<t.nr; i++){
1033 c = t.r[i];
1034 if(c=='\n' || c=='\004' || c=='\x7F')
1035 return 1;
1037 return 0;
1041 void
1042 consread(void)
1044 char buf[8000], *p;
1045 int c, width, n;
1046 int s, raw;
1048 raw = rawon();
1049 for(;;) {
1050 if(!consready())
1051 return;
1052 n = sizeof(buf);
1053 p = buf;
1054 c = 0;
1055 while(n >= UTFmax && (t.qh<t.nr || t.nraw > 0)) {
1056 if(t.qh == t.nr){
1057 width = runetochar(p, &t.raw[0]);
1058 t.nraw--;
1059 runemove(t.raw, t.raw+1, t.nraw);
1060 }else
1061 width = runetochar(p, &t.r[t.qh++]);
1062 c = *p;
1063 p += width;
1064 n -= width;
1065 if(!raw && (c == '\n' || c == '\004' || c == '\x7F'))
1066 break;
1068 n = p-buf;
1071 * If we've been echoing, make sure the terminal isn't
1072 * while we do the write. This screws up if someone
1073 * else tries to turn off echo at the same time we do
1074 * (we'll turn it on again after the write), but that's not
1075 * too likely.
1077 s = setecho(sfd, 0);
1078 if(write(rcfd, buf, n) < 0)
1079 threadexitsall(0);
1080 if(s)
1081 setecho(sfd, s);
1085 void
1086 conswrite(char *p, int n)
1088 int n2, i;
1089 Rune buf2[1000], *q;
1091 /* convert to runes */
1092 i = t.npart;
1093 if(i > 0){
1094 /* handle partial runes */
1095 while(i < UTFmax && n>0) {
1096 t.part[i] = *p;
1097 i++;
1098 p++;
1099 n--;
1100 if(fullrune(t.part, i)) {
1101 t.npart = 0;
1102 chartorune(buf2, t.part);
1103 runewrite(buf2, 1);
1104 break;
1107 /* there is a little extra room in a message buf */
1110 while(n >= UTFmax || fullrune(p, n)) {
1111 n2 = nelem(buf2);
1112 q = buf2;
1114 while(n2) {
1115 if(n < UTFmax && !fullrune(p, n))
1116 break;
1117 i = chartorune(q, p);
1118 p += i;
1119 n -= i;
1120 n2--;
1121 q++;
1123 runewrite(buf2, q-buf2);
1126 if(n != 0) {
1127 assert(n+t.npart < UTFmax);
1128 memcpy(t.part+t.npart, p, n);
1129 t.npart += n;
1132 if(scrolling)
1133 show(t.qh);
1136 void
1137 runewrite(Rune *r, int n)
1139 static int havecr;
1140 int i;
1141 uint initial;
1142 uint q0, q1;
1143 uint p0, p1;
1144 Rune *p, *q;
1146 n = label(r, n);
1147 if(n == 0)
1148 return;
1150 /* process trailing \r from previous write */
1151 initial = 0;
1152 if(havecr && *r != '\r' && *r != '\n')
1153 initial = bswidth(0x15, t.qh, 0);
1154 havecr = 0;
1156 /* get rid of backspaces */
1157 p = q = r;
1158 for(i=0; i<n; i++) {
1159 if(*p == '\b') {
1160 if(q == r)
1161 initial++;
1162 else
1163 --q;
1164 } else if(*p == '\r') { /* treat like ^U */
1165 /* convert CR without NL into erased line */
1166 /* i feel really sleazy about this but it helps */
1167 while(i<n-1 && *(p+1) == '\r'){
1168 i++;
1169 p++;
1171 if(i<n-1 && *(p+1) != '\n'){
1172 while(q > r && *(q-1) != '\n')
1173 q--;
1174 if(q==r)
1175 initial = bswidth(0x15, t.qh, 0);
1176 }else if(i == n-1)
1177 havecr = 1;
1178 } else if(*p)
1179 *q++ = *p;
1180 p++;
1182 n = q-r;
1184 if(initial){
1185 /* write turned into a delete */
1187 if(initial > t.qh)
1188 initial = t.qh;
1189 q0 = t.qh-initial;
1190 q1 = t.qh;
1192 runemove(t.r+q0, t.r+q1, t.nr-q1);
1193 t.nr -= initial;
1194 t.qh -= initial;
1195 if(t.q0 > q1)
1196 t.q0 -= initial;
1197 else if(t.q0 > q0)
1198 t.q0 = q0;
1199 if(t.q1 > q1)
1200 t.q1 -= initial;
1201 else if(t.q1 > q0)
1202 t.q1 = q0;
1203 if(t.org > q1)
1204 t.org -= initial;
1205 else if(q0 < t.org+t.f->nchars){
1206 if(t.org < q0)
1207 p0 = q0 - t.org;
1208 else {
1209 t.org = q0;
1210 p0 = 0;
1212 p1 = q1 - t.org;
1213 if(p1 > t.f->nchars)
1214 p1 = t.f->nchars;
1215 frdelete(t.f, p0, p1);
1216 fill();
1218 updatesel();
1221 insert(r, n, t.qh, 1);
1225 void
1226 cut(void)
1228 uint n, p0, p1;
1229 uint q0, q1;
1231 q0 = t.q0;
1232 q1 = t.q1;
1234 if (q0 < t.org && q1 >= t.org)
1235 show(q0);
1237 n = q1-q0;
1238 if(n == 0)
1239 return;
1240 runemove(t.r+q0, t.r+q1, t.nr-q1);
1241 t.nr -= n;
1242 t.q0 = t.q1 = q0;
1243 if(q1 < t.qh)
1244 t.qh -= n;
1245 else if(q0 < t.qh)
1246 t.qh = q0;
1247 if(q1 < t.org)
1248 t.org -= n;
1249 else if(q0 < t.org+t.f->nchars){
1250 assert(q0 >= t.org);
1251 p0 = q0 - t.org;
1252 p1 = q1 - t.org;
1253 if(p1 > t.f->nchars)
1254 p1 = t.f->nchars;
1255 frdelete(t.f, p0, p1);
1256 fill();
1258 updatesel();
1261 void
1262 snarfupdate(void)
1264 char *pp;
1265 int n, i;
1266 Rune *p;
1268 pp = getsnarf();
1269 if(pp == nil)
1270 return;
1271 n = strlen(pp);
1272 if(n <= 0) {
1273 /*t.nsnarf = 0;*/
1274 return;
1276 t.snarf = runerealloc(t.snarf, n);
1277 for(i=0,p=t.snarf; i<n; p++)
1278 i += chartorune(p, pp+i);
1279 t.nsnarf = p-t.snarf;
1283 char sbuf[SnarfSize];
1284 void
1285 snarf(void)
1287 char *p;
1288 int i, n;
1289 Rune *rp;
1291 if(t.q1 == t.q0)
1292 return;
1293 n = t.q1-t.q0;
1294 t.snarf = runerealloc(t.snarf, n);
1295 for(i=0,p=sbuf,rp=t.snarf; i<n && p < sbuf+SnarfSize-UTFmax; i++){
1296 *rp++ = *(t.r+t.q0+i);
1297 p += runetochar(p, t.r+t.q0+i);
1299 t.nsnarf = rp-t.snarf;
1300 *p = '\0';
1301 putsnarf(sbuf);
1304 uint
1305 min(uint x, uint y)
1307 if(x < y)
1308 return x;
1309 return y;
1312 uint
1313 max(uint x, uint y)
1315 if(x > y)
1316 return x;
1317 return y;
1320 uint
1321 insert(Rune *r, int n, uint q0, int hostwrite)
1323 uint m;
1325 if(n == 0)
1326 return q0;
1327 if(t.nr+n>HiWater && q0>=t.org && q0>=t.qh){
1328 m = min(HiWater-LoWater, min(t.org, t.qh));
1329 t.org -= m;
1330 t.qh -= m;
1331 if(t.q0 > m)
1332 t.q0 -= m;
1333 else
1334 t.q0 = 0;
1335 if(t.q1 > m)
1336 t.q1 -= m;
1337 else
1338 t.q1 = 0;
1339 t.nr -= m;
1340 runemove(t.r, t.r+m, t.nr);
1341 q0 -= m;
1343 if(t.nr+n > t.maxr){
1345 * Minimize realloc breakage:
1346 * Allocate at least MinWater
1347 * Double allocation size each time
1348 * But don't go much above HiWater
1350 m = max(min(2*(t.nr+n), HiWater), t.nr+n)+MinWater;
1351 if(m > HiWater)
1352 m = max(HiWater+MinWater, t.nr+n);
1353 if(m > t.maxr){
1354 t.r = runerealloc(t.r, m);
1355 t.maxr = m;
1358 runemove(t.r+q0+n, t.r+q0, t.nr-q0);
1359 runemove(t.r+q0, r, n);
1360 t.nr += n;
1361 /* if output touches, advance selection, not qh; works best for keyboard and output */
1362 if(q0 <= t.q1)
1363 t.q1 += n;
1364 if(q0 <= t.q0)
1365 t.q0 += n;
1366 if(q0 < t.qh || (q0==t.qh && hostwrite))
1367 t.qh += n;
1368 else
1369 consread();
1370 if(q0 < t.org)
1371 t.org += n;
1372 else if(q0 <= t.org+t.f->nchars)
1373 frinsert(t.f, r, r+n, q0-t.org);
1374 return q0;
1377 void
1378 paste(Rune *r, int n, int advance)
1380 Rune *rbuf;
1382 if(!holdon && rawon() && t.q0==t.nr){
1383 addraw(r, n);
1384 consread();
1385 return;
1388 cut();
1389 if(n == 0)
1390 return;
1393 * if this is a button2 execute then we might have been passed
1394 * runes inside the buffer. must save them before realloc.
1396 rbuf = nil;
1397 if(t.r <= r && r < t.r+n){
1398 rbuf = runemalloc(n);
1399 runemove(rbuf, r, n);
1400 r = rbuf;
1403 insert(r, n, t.q0, 0);
1404 updatesel();
1405 free(rbuf);
1408 void
1409 fill(void)
1411 if (t.f->nlines >= t.f->maxlines)
1412 return;
1413 frinsert(t.f, t.r + t.org + t.f->nchars, t.r + t.nr, t.f->nchars);
1416 void
1417 updatesel(void)
1419 Frame *f;
1420 uint n;
1422 f = t.f;
1423 if(t.org+f->p0 == t.q0 && t.org+f->p1 == t.q1)
1424 return;
1426 n = t.f->nchars;
1428 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 0);
1429 if (t.q0 >= t.org)
1430 f->p0 = t.q0-t.org;
1431 else
1432 f->p0 = 0;
1433 if(f->p0 > n)
1434 f->p0 = n;
1435 if (t.q1 >= t.org)
1436 f->p1 = t.q1-t.org;
1437 else
1438 f->p1 = 0;
1439 if(f->p1 > n)
1440 f->p1 = n;
1441 frdrawsel(f, frptofchar(f, f->p0), f->p0, f->p1, 1);
1444 if(t.qh<=t.org+t.f.nchars && t.cwqueue != 0)
1445 t.cwqueue->wakeup <-= 0;
1448 tcheck();
1451 void
1452 show(uint q0)
1454 int nl;
1455 uint q, oq;
1457 if(cansee(q0))
1458 return;
1460 if (q0<t.org)
1461 nl = t.f->maxlines/5;
1462 else
1463 nl = 4*t.f->maxlines/5;
1464 q = backnl(q0, nl);
1465 /* avoid going in the wrong direction */
1466 if (q0>t.org && q<t.org)
1467 q = t.org;
1468 setorigin(q, 0);
1469 /* keep trying until q0 is on the screen */
1470 while(!cansee(q0)) {
1471 assert(q0 >= t.org);
1472 oq = q;
1473 q = line2q(t.f->maxlines-nl);
1474 assert(q > oq);
1475 setorigin(q, 1);
1479 int
1480 cansee(uint q0)
1482 uint qe;
1484 qe = t.org+t.f->nchars;
1486 if(q0>=t.org && q0 < qe)
1487 return 1;
1488 if (q0 != qe)
1489 return 0;
1490 if (t.f->nlines < t.f->maxlines)
1491 return 1;
1492 if (q0 > 0 && t.r[t.nr-1] == '\n')
1493 return 0;
1494 return 1;
1498 void
1499 setorigin(uint org, int exact)
1501 int i, a;
1502 uint n;
1504 if(org>0 && !exact){
1505 /* try and start after a newline */
1506 /* don't try harder than 256 chars */
1507 for(i=0; i<256 && org<t.nr; i++){
1508 if(t.r[org-1] == '\n')
1509 break;
1510 org++;
1513 a = org-t.org;
1515 if(a>=0 && a<t.f->nchars)
1516 frdelete(t.f, 0, a);
1517 else if(a<0 && -a<100*t.f->maxlines){
1518 n = t.org - org;
1519 frinsert(t.f, t.r+org, t.r+org+n, 0);
1520 }else
1521 frdelete(t.f, 0, t.f->nchars);
1522 t.org = org;
1523 fill();
1524 updatesel();
1528 uint
1529 line2q(uint n)
1531 Frame *f;
1533 f = t.f;
1534 return frcharofpt(f, Pt(f->r.min.x, f->r.min.y + n*font->height))+t.org;
1537 uint
1538 backnl(uint p, uint n)
1540 int i, j;
1542 for (i = n;; i--) {
1543 /* at 256 chars, call it a line anyway */
1544 for(j=256; --j>0 && p>0; p--)
1545 if(t.r[p-1]=='\n')
1546 break;
1547 if (p == 0 || i == 0)
1548 return p;
1549 p--;
1553 void
1554 addraw(Rune *r, int nr)
1556 t.raw = runerealloc(t.raw, t.nraw+nr);
1557 runemove(t.raw+t.nraw, r, nr);
1558 t.nraw += nr;
1560 if(t.crqueue != nil)
1561 t.crqueue->wakeup <-= 0;
1566 Rune left1[] = { '{', '[', '(', '<', 0xab, 0 };
1567 Rune right1[] = { '}', ']', ')', '>', 0xbb, 0 };
1568 Rune left2[] = { '\n', 0 };
1569 Rune left3[] = { '\'', '"', '`', 0 };
1571 Rune *left[] = {
1572 left1,
1573 left2,
1574 left3,
1578 Rune *right[] = {
1579 right1,
1580 left2,
1581 left3,
1585 void
1586 doubleclick(uint *q0, uint *q1)
1588 int c, i;
1589 Rune *r, *l, *p;
1590 uint q;
1592 for(i=0; left[i]!=0; i++){
1593 q = *q0;
1594 l = left[i];
1595 r = right[i];
1596 /* try matching character to left, looking right */
1597 if(q == 0)
1598 c = '\n';
1599 else
1600 c = t.r[q-1];
1601 p = strrune(l, c);
1602 if(p != 0){
1603 if(clickmatch(c, r[p-l], 1, &q))
1604 *q1 = q-(c!='\n');
1605 return;
1607 /* try matching character to right, looking left */
1608 if(q == t.nr)
1609 c = '\n';
1610 else
1611 c = t.r[q];
1612 p = strrune(r, c);
1613 if(p != 0){
1614 if(clickmatch(c, l[p-r], -1, &q)){
1615 *q1 = *q0+(*q0<t.nr && c=='\n');
1616 *q0 = q;
1617 if(c!='\n' || q!=0 || t.r[0]=='\n')
1618 (*q0)++;
1620 return;
1623 /* try filling out word to right */
1624 while(*q1<t.nr && isexpand(t.r[*q1]))
1625 (*q1)++;
1626 /* try filling out word to left */
1627 while(*q0>0 && isexpand(t.r[*q0-1]))
1628 (*q0)--;
1631 int
1632 clickmatch(int cl, int cr, int dir, uint *q)
1634 Rune c;
1635 int nest;
1637 nest = 1;
1638 for(;;){
1639 if(dir > 0){
1640 if(*q == t.nr)
1641 break;
1642 c = t.r[*q];
1643 (*q)++;
1644 }else{
1645 if(*q == 0)
1646 break;
1647 (*q)--;
1648 c = t.r[*q];
1650 if(c == cr){
1651 if(--nest==0)
1652 return 1;
1653 }else if(c == cl)
1654 nest++;
1656 return cl=='\n' && nest==1;
1659 void
1660 tcheck(void)
1662 Frame *f;
1664 f = t.f;
1666 assert(t.q0 <= t.q1 && t.q1 <= t.nr);
1667 assert(t.org <= t.nr && t.qh <= t.nr);
1668 assert(f->p0 <= f->p1 && f->p1 <= f->nchars);
1669 assert(t.org + f->nchars <= t.nr);
1670 assert(t.org+f->nchars==t.nr || (f->nlines >= f->maxlines));
1673 Rune*
1674 strrune(Rune *s, Rune c)
1676 Rune c1;
1678 if(c == 0) {
1679 while(*s++)
1681 return s-1;
1684 while(c1 = *s++)
1685 if(c1 == c)
1686 return s-1;
1687 return 0;
1690 void
1691 scrdraw(void)
1693 Rectangle r, r1, r2;
1694 static Image *scrx;
1696 r = scrollr;
1697 r.min.x += 1; /* border between margin and bar */
1698 r1 = r;
1699 if(scrx==0 || scrx->r.max.y < r.max.y){
1700 if(scrx)
1701 freeimage(scrx);
1702 scrx = allocimage(display, Rect(0, 0, 32, r.max.y), screen->chan, 1, DPaleyellow);
1703 if(scrx == 0)
1704 sysfatal("scroll balloc");
1706 r1.min.x = 0;
1707 r1.max.x = Dx(r);
1708 r2 = scrpos(r1, t.org, t.org+t.f->nchars, t.nr);
1709 if(!eqrect(r2, lastsr)){
1710 lastsr = r2;
1711 draw(scrx, r1, cols[BORD], nil, ZP);
1712 draw(scrx, r2, cols[BACK], nil, r2.min);
1713 // r2 = r1;
1714 // r2.min.x = r2.max.x-1;
1715 // draw(scrx, r2, cols[BORD], nil, ZP);
1716 draw(screen, r, scrx, nil, r1.min);
1720 Rectangle
1721 scrpos(Rectangle r, ulong p0, ulong p1, ulong tot)
1723 long h;
1724 Rectangle q;
1726 q = insetrect(r, 1);
1727 h = q.max.y-q.min.y;
1728 if(tot == 0)
1729 return q;
1730 if(tot > 1024L*1024L)
1731 tot >>= 10, p0 >>= 10, p1 >>= 10;
1732 if(p0 > 0)
1733 q.min.y += h*p0/tot;
1734 if(p1 < tot)
1735 q.max.y -= h*(tot-p1)/tot;
1736 if(q.max.y < q.min.y+2){
1737 if(q.min.y+2 <= r.max.y)
1738 q.max.y = q.min.y+2;
1739 else
1740 q.min.y = q.max.y-2;
1742 return q;
1745 void
1746 scroll(int but)
1748 uint p0, oldp0;
1749 Rectangle s;
1750 int x, y, my, h, first, exact;
1752 s = insetrect(scrollr, 1);
1753 h = s.max.y-s.min.y;
1754 x = (s.min.x+s.max.x)/2;
1755 oldp0 = ~0;
1756 first = 1;
1757 do{
1758 if(t.m.xy.x<s.min.x || s.max.x<=t.m.xy.x){
1759 readmouse(mc);
1760 t.m = mc->m;
1761 }else{
1762 my = t.m.xy.y;
1763 if(my < s.min.y)
1764 my = s.min.y;
1765 if(my >= s.max.y)
1766 my = s.max.y;
1767 // if(!eqpt(t.m.xy, Pt(x, my)))
1768 // cursorset(Pt(x, my));
1769 exact = 1;
1770 if(but == 2){
1771 y = my;
1772 if(y > s.max.y-2)
1773 y = s.max.y-2;
1774 if(t.nr > 1024*1024)
1775 p0 = ((t.nr>>10)*(y-s.min.y)/h)<<10;
1776 else
1777 p0 = t.nr*(y-s.min.y)/h;
1778 exact = 0;
1779 } else if(but == 1)
1780 p0 = backnl(t.org, (my-s.min.y)/font->height);
1781 else
1782 p0 = t.org+frcharofpt(t.f, Pt(s.max.x, my));
1784 if(oldp0 != p0)
1785 setorigin(p0, exact);
1786 oldp0 = p0;
1787 scrdraw();
1788 readmouse(mc);
1789 t.m = mc->m;
1791 }while(t.m.buttons & (1<<(but-1)));
1794 void
1795 plumbstart(void)
1797 if((plumbfd = plumbopen("send", OWRITE)) < 0)
1798 fprint(2, "9term: plumbopen: %r\n");
1801 void
1802 plumb(uint q0, uint q1)
1804 Plumbmsg *pm;
1805 char *p;
1806 int i, p0, n;
1807 char cbuf[100];
1809 pm = malloc(sizeof(Plumbmsg));
1810 pm->src = strdup("9term");
1811 pm->dst = 0;
1812 pm->wdir = strdup(wdir);
1813 pm->type = strdup("text");
1814 pm->data = nil;
1815 if(q1 > q0)
1816 pm->attr = nil;
1817 else{
1818 p0 = q0;
1819 wordclick(&q0, &q1);
1820 sprint(cbuf, "click=%d", p0-q0);
1821 pm->attr = plumbunpackattr(cbuf);
1823 if(q0==q1){
1824 plumbfree(pm);
1825 return;
1827 pm->data = malloc(SnarfSize);
1828 n = q1 - q0;
1829 for(i=0,p=pm->data; i<n && p < pm->data + SnarfSize-UTFmax; i++)
1830 p += runetochar(p, t.r+q0+i);
1831 *p = '\0';
1832 pm->ndata = strlen(pm->data);
1833 if(plumbsend(plumbfd, pm) < 0){
1834 setcursor(mc, &query);
1835 sleep(500);
1836 if(holdon)
1837 setcursor(mc, &whitearrow);
1838 else
1839 setcursor(mc, nil);
1841 plumbfree(pm);
1845 * Process in-band messages about window title changes.
1846 * The messages are of the form:
1848 * \033];xxx\007
1850 * where xxx is the new directory. This format was chosen
1851 * because it changes the label on xterm windows.
1853 int
1854 label(Rune *sr, int n)
1856 Rune *sl, *el, *er, *r;
1857 char *p;
1859 er = sr+n;
1860 for(r=er-1; r>=sr; r--)
1861 if(*r == '\007')
1862 break;
1863 if(r < sr)
1864 return n;
1866 el = r+1;
1867 if(el-sr > sizeof wdir)
1868 sr = el - sizeof wdir;
1869 for(sl=el-3; sl>=sr; sl--)
1870 if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
1871 break;
1872 if(sl < sr)
1873 return n;
1875 snprint(wdir, sizeof wdir, "%.*S", (el-1)-(sl+3), sl+3);
1876 drawsetlabel(wdir);
1878 /* remove trailing /-sysname if present */
1879 p = strrchr(wdir, '/');
1880 if(p && *(p+1) == '-'){
1881 if(p == wdir)
1882 p++;
1883 *p = 0;
1886 runemove(sl, el, er-el);
1887 n -= (el-sl);
1888 return n;
1891 int
1892 rawon(void)
1894 return !cooked && !isecho(sfd);
1898 * Clumsy hack to make " and "" work.
1899 * Then again, what's not a clumsy hack here in Unix land?
1902 char adir[100];
1903 int afd;
1905 void
1906 removethesocket(void)
1908 if(thesocket[0])
1909 if(remove(thesocket) < 0)
1910 fprint(2, "remove %s: %r\n", thesocket);
1913 void
1914 servedevtext(void)
1916 char buf[100];
1918 snprint(buf, sizeof buf, "unix!/tmp/9term-text.%d", getpid());
1920 if((afd = announce(buf, adir)) < 0){
1921 putenv("text9term", "");
1922 return;
1925 putenv("text9term", buf);
1926 proccreate(listenproc, nil, STACK);
1927 strcpy(thesocket, buf+5);
1928 atexit(removethesocket);
1931 void
1932 listenproc(void *arg)
1934 int fd;
1935 char dir[100];
1937 USED(arg);
1938 for(;;){
1939 fd = listen(adir, dir);
1940 if(fd < 0){
1941 close(afd);
1942 return;
1944 proccreate(textthread, (void*)fd, STACK);
1948 void
1949 textthread(void *arg)
1951 int fd, i, x, n, end;
1952 Rune r;
1953 char buf[4096], *p, *ep;
1955 fd = (int)arg;
1956 p = buf;
1957 ep = buf+sizeof buf;
1958 end = t.org+t.nr; /* avoid possible output loop */
1959 for(i=t.org;; i++){
1960 if(i >= end || ep-p < UTFmax){
1961 for(x=0; x<p-buf; x+=n)
1962 if((n = write(fd, buf+x, (p-x)-buf)) <= 0)
1963 goto break2;
1965 if(i >= end)
1966 break;
1967 p = buf;
1969 if(i < t.org)
1970 i = t.org;
1971 r = t.r[i-t.org];
1972 if(r < Runeself)
1973 *p++ = r;
1974 else
1975 p += runetochar(p, &r);
1977 break2:
1978 close(fd);