Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <fcall.h>
5 #include <9pclient.h>
6 #include "term.h"
8 const char *termprog = "win";
10 #define EVENTSIZE 256
11 #define STACK 32768
13 typedef struct Event Event;
14 typedef struct Q Q;
16 struct Event
17 {
18 int c1;
19 int c2;
20 int q0;
21 int q1;
22 int flag;
23 int nb;
24 int nr;
25 char b[EVENTSIZE*UTFmax+1];
26 Rune r[EVENTSIZE+1];
27 };
29 Event blank = {
30 'M',
31 'X',
32 0, 0, 0, 1, 1,
33 { ' ', 0 },
34 { ' ', 0 }
35 };
37 struct Q
38 {
39 QLock lk;
40 int p;
41 int k;
42 };
44 Q q;
46 CFid *eventfd;
47 CFid *addrfd;
48 CFid *datafd;
49 CFid *ctlfd;
50 /* int bodyfd; */
52 char *typing;
53 int ntypeb;
54 int ntyper;
55 int ntypebreak;
56 int debug;
57 int rcfd;
58 int cook = 1;
59 int password;
60 int israw(int);
62 char *name;
64 char **prog;
65 Channel *cwait;
66 int pid = -1;
68 int label(char*, int);
69 void error(char*, ...);
70 void stdinproc(void*);
71 void stdoutproc(void*);
72 void type(Event*, int, CFid*, CFid*);
73 void sende(Event*, int, CFid*, CFid*, CFid*, int);
74 char *onestring(int, char**);
75 int delete(Event*);
76 void deltype(uint, uint);
77 void sendbs(int, int);
78 void runproc(void*);
80 int
81 fsfidprint(CFid *fid, char *fmt, ...)
82 {
83 char buf[256];
84 va_list arg;
85 int n;
87 va_start(arg, fmt);
88 n = vsnprint(buf, sizeof buf, fmt, arg);
89 va_end(arg);
90 return fswrite(fid, buf, n);
91 }
93 void
94 usage(void)
95 {
96 fprint(2, "usage: win cmd args...\n");
97 threadexitsall("usage");
98 }
100 void
101 waitthread(void *v)
103 recvp(cwait);
104 threadexitsall(nil);
107 void
108 hangupnote(void *a, char *msg)
110 if(strcmp(msg, "hangup") == 0 && pid != 0){
111 postnote(PNGROUP, pid, "hangup");
112 noted(NDFLT);
114 if(strstr(msg, "child")){
115 char buf[128];
116 int n;
118 n = awaitnohang(buf, sizeof buf-1);
119 if(n > 0){
120 buf[n] = 0;
121 if(atoi(buf) == pid)
122 threadexitsall(0);
124 noted(NCONT);
126 noted(NDFLT);
129 void
130 threadmain(int argc, char **argv)
132 int fd, id;
133 char buf[256];
134 char buf1[128];
135 CFsys *fs;
136 char *dump;
138 dump = onestring(argc, argv);
140 ARGBEGIN{
141 case 'd':
142 debug = 1;
143 break;
144 case 'n':
145 name = EARGF(usage());
146 break;
147 default:
148 usage();
149 }ARGEND
151 prog = argv;
153 if(name == nil){
154 if(argc > 0)
155 name = argv[0];
156 else{
157 name = sysname();
158 if(name == nil)
159 name = "gnot";
163 /*
164 * notedisable("sys: write on closed pipe");
165 * not okay to disable the note, because that
166 * gets inherited by the subshell, so that something
167 * as simple as "yes | sed 10q" never exits.
168 * call notifyoff instead. (is notedisable ever safe?)
169 */
170 notifyoff("sys: write on closed pipe");
172 noteenable("sys: child");
173 notify(hangupnote);
175 if((fs = nsmount("acme", "")) == 0)
176 sysfatal("nsmount acme: %r");
177 ctlfd = fsopen(fs, "new/ctl", ORDWR|OCEXEC);
178 if(ctlfd == 0 || fsread(ctlfd, buf, 12) != 12)
179 sysfatal("ctl: %r");
180 id = atoi(buf);
181 snprint(buf, sizeof buf, "%d", id);
182 putenv("winid", buf);
183 sprint(buf, "%d/tag", id);
184 fd = fsopenfd(fs, buf, OWRITE|OCEXEC);
185 write(fd, " Send", 1+4);
186 close(fd);
187 sprint(buf, "%d/event", id);
188 eventfd = fsopen(fs, buf, ORDWR|OCEXEC);
189 sprint(buf, "%d/addr", id);
190 addrfd = fsopen(fs, buf, ORDWR|OCEXEC);
191 sprint(buf, "%d/data", id);
192 datafd = fsopen(fs, buf, ORDWR|OCEXEC);
193 sprint(buf, "%d/body", id);
194 /* bodyfd = fsopenfd(fs, buf, ORDWR|OCEXEC); */
195 if(eventfd==nil || addrfd==nil || datafd==nil)
196 sysfatal("data files: %r");
197 /*
198 if(eventfd<0 || addrfd<0 || datafd<0 || bodyfd<0)
199 sysfatal("data files: %r");
200 */
201 fsunmount(fs);
203 cwait = threadwaitchan();
204 threadcreate(waitthread, nil, STACK);
205 pid = rcstart(argc, argv, &rcfd, nil);
206 if(pid == -1)
207 sysfatal("exec failed");
209 getwd(buf1, sizeof buf1);
210 sprint(buf, "name %s/-%s\n0\n", buf1, name);
211 fswrite(ctlfd, buf, strlen(buf));
212 sprint(buf, "dumpdir %s/\n", buf1);
213 fswrite(ctlfd, buf, strlen(buf));
214 sprint(buf, "dump %s\n", dump);
215 fswrite(ctlfd, buf, strlen(buf));
216 sprint(buf, "scroll");
217 fswrite(ctlfd, buf, strlen(buf));
219 updatewinsize(25, 80, 0, 0);
220 proccreate(stdoutproc, nil, STACK);
221 stdinproc(nil);
224 void
225 error(char *s, ...)
227 va_list arg;
229 if(s){
230 va_start(arg, s);
231 s = vsmprint(s, arg);
232 va_end(arg);
233 fprint(2, "win: %s: %r\n", s);
235 if(pid != -1)
236 postnote(PNGROUP, pid, "hangup");
237 threadexitsall(s);
240 char*
241 onestring(int argc, char **argv)
243 char *p;
244 int i, n;
245 static char buf[1024];
247 if(argc == 0)
248 return "";
249 p = buf;
250 for(i=0; i<argc; i++){
251 n = strlen(argv[i]);
252 if(p+n+1 >= buf+sizeof buf)
253 break;
254 memmove(p, argv[i], n);
255 p += n;
256 *p++ = ' ';
258 p[-1] = 0;
259 return buf;
262 int
263 getec(CFid *efd)
265 static char buf[8192];
266 static char *bufp;
267 static int nbuf;
269 if(nbuf == 0){
270 nbuf = fsread(efd, buf, sizeof buf);
271 if(nbuf <= 0)
272 error(nil);
273 bufp = buf;
275 --nbuf;
276 return *bufp++;
279 int
280 geten(CFid *efd)
282 int n, c;
284 n = 0;
285 while('0'<=(c=getec(efd)) && c<='9')
286 n = n*10+(c-'0');
287 if(c != ' ')
288 error("event number syntax");
289 return n;
292 int
293 geter(CFid *efd, char *buf, int *nb)
295 Rune r;
296 int n;
298 r = getec(efd);
299 buf[0] = r;
300 n = 1;
301 if(r < Runeself)
302 goto Return;
303 while(!fullrune(buf, n))
304 buf[n++] = getec(efd);
305 chartorune(&r, buf);
306 Return:
307 *nb = n;
308 return r;
311 void
312 gete(CFid *efd, Event *e)
314 int i, nb;
316 e->c1 = getec(efd);
317 e->c2 = getec(efd);
318 e->q0 = geten(efd);
319 e->q1 = geten(efd);
320 e->flag = geten(efd);
321 e->nr = geten(efd);
322 if(e->nr > EVENTSIZE)
323 error("event string too long");
324 e->nb = 0;
325 for(i=0; i<e->nr; i++){
326 e->r[i] = geter(efd, e->b+e->nb, &nb);
327 e->nb += nb;
329 e->r[e->nr] = 0;
330 e->b[e->nb] = 0;
331 if(getec(efd) != '\n')
332 error("event syntax 2");
335 int
336 nrunes(char *s, int nb)
338 int i, n;
339 Rune r;
341 n = 0;
342 for(i=0; i<nb; n++)
343 i += chartorune(&r, s+i);
344 return n;
347 void
348 stdinproc(void *v)
350 CFid *cfd = ctlfd;
351 CFid *efd = eventfd;
352 CFid *dfd = datafd;
353 CFid *afd = addrfd;
354 int fd0 = rcfd;
355 Event e, e2, e3, e4;
356 int n;
358 USED(v);
360 for(;;){
361 if(debug)
362 fprint(2, "typing[%d,%d)\n", q.p, q.p+ntyper);
363 gete(efd, &e);
364 if(debug)
365 fprint(2, "msg %c%c q[%d,%d)... ", e.c1, e.c2, e.q0, e.q1);
366 qlock(&q.lk);
367 switch(e.c1){
368 default:
369 Unknown:
370 print("unknown message %c%c\n", e.c1, e.c2);
371 break;
373 case 'E': /* write to body or tag; can't affect us */
374 switch(e.c2){
375 case 'I':
376 case 'D': /* body */
377 if(debug)
378 fprint(2, "shift typing %d... ", e.q1-e.q0);
379 q.p += e.q1-e.q0;
380 break;
382 case 'i':
383 case 'd': /* tag */
384 break;
386 default:
387 goto Unknown;
389 break;
391 case 'F': /* generated by our actions; ignore */
392 break;
394 case 'K':
395 case 'M':
396 switch(e.c2){
397 case 'I':
398 if(e.nr == 1 && e.r[0] == 0x7F) {
399 char buf[1];
400 fsprint(addrfd, "#%ud,#%ud", e.q0, e.q1);
401 fswrite(datafd, "", 0);
402 buf[0] = 0x7F;
403 write(fd0, buf, 1);
404 break;
406 if(e.q0 < q.p){
407 if(debug)
408 fprint(2, "shift typing %d... ", e.q1-e.q0);
409 q.p += e.q1-e.q0;
411 else if(e.q0 <= q.p+ntyper){
412 if(debug)
413 fprint(2, "type... ");
414 type(&e, fd0, afd, dfd);
416 break;
418 case 'D':
419 n = delete(&e);
420 q.p -= n;
421 if(israw(fd0) && e.q1 >= q.p+n)
422 sendbs(fd0, n);
423 break;
425 case 'x':
426 case 'X':
427 if(e.flag & 2)
428 gete(efd, &e2);
429 if(e.flag & 8){
430 gete(efd, &e3);
431 gete(efd, &e4);
433 if(e.flag&1 || (e.c2=='x' && e.nr==0 && e2.nr==0)){
434 /* send it straight back */
435 fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
436 break;
438 if(e.q0==e.q1 && (e.flag&2)){
439 e2.flag = e.flag;
440 e = e2;
442 char buf[100];
443 snprint(buf, sizeof buf, "%.*S", e.nr, e.r);
444 if(cistrcmp(buf, "cook") == 0) {
445 cook = 1;
446 break;
448 if(cistrcmp(buf, "nocook") == 0) {
449 cook = 0;
450 break;
452 if(e.flag & 8){
453 if(e.q1 != e.q0){
454 sende(&e, fd0, cfd, afd, dfd, 0);
455 sende(&blank, fd0, cfd, afd, dfd, 0);
457 sende(&e3, fd0, cfd, afd, dfd, 1);
458 }else if(e.q1 != e.q0)
459 sende(&e, fd0, cfd, afd, dfd, 1);
460 break;
462 case 'l':
463 case 'L':
464 /* just send it back */
465 if(e.flag & 2)
466 gete(efd, &e2);
467 fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
468 break;
470 case 'd':
471 case 'i':
472 break;
474 default:
475 goto Unknown;
478 qunlock(&q.lk);
482 int
483 dropcr(char *p, int n)
485 int i;
486 char *w, *r, *q;
488 r = p;
489 w = p;
490 for(i=0; i<n; i++) {
491 switch(*r) {
492 case '\b':
493 if(w > p)
494 w--;
495 break;
496 case '\r':
497 while(i<n-1 && *(r+1) == '\r') {
498 r++;
499 i++;
501 if(i<n && *(r+1) != '\n') {
502 q = r;
503 while(q>p && *(q-1) != '\n')
504 q--;
505 if(q > p) {
506 w = q;
507 break;
510 *w++ = '\n';
511 break;
512 default:
513 *w++ = *r;
514 break;
516 r++;
518 return w-p;
521 void
522 stdoutproc(void *v)
524 int fd1 = rcfd;
525 CFid *afd = addrfd;
526 CFid *dfd = datafd;
527 int n, m, w, npart;
528 char *buf, *s, *t;
529 Rune r;
530 char x[16], hold[UTFmax];
532 USED(v);
533 buf = malloc(8192+UTFmax+1);
534 npart = 0;
535 for(;;){
536 /* Let typing have a go -- maybe there's a rubout waiting. */
537 yield();
538 n = read(fd1, buf+npart, 8192);
539 if(n <= 0)
540 error(nil);
542 n = echocancel(buf+npart, n);
543 if(n == 0)
544 continue;
546 n = dropcrnl(buf+npart, n);
547 if(n == 0)
548 continue;
550 n = dropcr(buf+npart, n);
551 if(n == 0)
552 continue;
554 /* squash NULs */
555 s = memchr(buf+npart, 0, n);
556 if(s){
557 for(t=s; s<buf+npart+n; s++)
558 if(*t = *s) /* assign = */
559 t++;
560 n = t-(buf+npart);
563 n += npart;
565 /* hold on to final partial rune */
566 npart = 0;
567 while(n>0 && (buf[n-1]&0xC0)){
568 --n;
569 npart++;
570 if((buf[n]&0xC0)!=0x80){
571 if(fullrune(buf+n, npart)){
572 w = chartorune(&r, buf+n);
573 n += w;
574 npart -= w;
576 break;
579 if(n > 0){
580 memmove(hold, buf+n, npart);
581 buf[n] = 0;
582 n = label(buf, n);
583 buf[n] = 0;
585 // clumsy but effective: notice password
586 // prompts so we can disable echo.
587 password = 0;
588 if(cistrstr(buf, "password") || cistrstr(buf, "passphrase")) {
589 int i;
591 i = n;
592 while(i > 0 && buf[i-1] == ' ')
593 i--;
594 password = i > 0 && buf[i-1] == ':';
597 qlock(&q.lk);
598 m = sprint(x, "#%d", q.p);
599 if(fswrite(afd, x, m) != m){
600 fprint(2, "stdout writing address %s: %r; resetting\n", x);
601 if(fswrite(afd, "$", 1) < 0)
602 fprint(2, "reset: %r\n");
603 fsseek(afd, 0, 0);
604 m = fsread(afd, x, sizeof x-1);
605 if(m >= 0){
606 x[m] = 0;
607 q.p = atoi(x);
610 if(fswrite(dfd, buf, n) != n)
611 error("stdout writing body");
612 /* Make sure acme scrolls to the end of the above write. */
613 if(fswrite(dfd, nil, 0) != 0)
614 error("stdout flushing body");
615 q.p += nrunes(buf, n);
616 qunlock(&q.lk);
617 memmove(buf, hold, npart);
622 char wdir[512];
623 int
624 label(char *sr, int n)
626 char *sl, *el, *er, *r, *p;
628 er = sr+n;
629 for(r=er-1; r>=sr; r--)
630 if(*r == '\007')
631 break;
632 if(r < sr)
633 return n;
635 el = r+1;
636 if(el-sr > sizeof wdir - strlen(name) - 20)
637 sr = el - (sizeof wdir - strlen(name) - 20);
638 for(sl=el-3; sl>=sr; sl--)
639 if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
640 break;
641 if(sl < sr)
642 return n;
644 *r = 0;
645 if(strcmp(sl+3, "*9term-hold+") != 0) {
646 /*
647 * add /-sysname if not present
648 */
649 snprint(wdir, sizeof wdir, "name %s", sl+3);
650 p = strrchr(wdir, '/');
651 if(p==nil || *(p+1) != '-'){
652 p = wdir+strlen(wdir);
653 if(*(p-1) != '/')
654 *p++ = '/';
655 *p++ = '-';
656 strcpy(p, name);
658 strcat(wdir, "\n0\n");
659 fswrite(ctlfd, wdir, strlen(wdir));
662 memmove(sl, el, er-el);
663 n -= (el-sl);
664 return n;
667 int
668 delete(Event *e)
670 uint q0, q1;
671 int deltap;
673 q0 = e->q0;
674 q1 = e->q1;
675 if(q1 <= q.p)
676 return e->q1-e->q0;
677 if(q0 >= q.p+ntyper)
678 return 0;
679 deltap = 0;
680 if(q0 < q.p){
681 deltap = q.p-q0;
682 q0 = 0;
683 }else
684 q0 -= q.p;
685 if(q1 > q.p+ntyper)
686 q1 = ntyper;
687 else
688 q1 -= q.p;
689 deltype(q0, q1);
690 return deltap;
693 void
694 addtype(int c, uint p0, char *b, int nb, int nr)
696 int i, w;
697 Rune r;
698 uint p;
699 char *b0;
701 for(i=0; i<nb; i+=w){
702 w = chartorune(&r, b+i);
703 if((r==0x7F||r==3) && c=='K'){
704 write(rcfd, "\x7F", 1);
705 /* toss all typing */
706 q.p += ntyper+nr;
707 ntypebreak = 0;
708 ntypeb = 0;
709 ntyper = 0;
710 /* buglet: more than one delete ignored */
711 return;
713 if(r=='\n' || r==0x04)
714 ntypebreak++;
716 typing = realloc(typing, ntypeb+nb);
717 if(typing == nil)
718 error("realloc");
719 if(p0 == ntyper)
720 memmove(typing+ntypeb, b, nb);
721 else{
722 b0 = typing;
723 for(p=0; p<p0 && b0<typing+ntypeb; p++){
724 w = chartorune(&r, b0+i);
725 b0 += w;
727 if(p != p0)
728 error("typing: findrune");
729 memmove(b0+nb, b0, (typing+ntypeb)-b0);
730 memmove(b0, b, nb);
732 ntypeb += nb;
733 ntyper += nr;
736 int
737 israw(int fd0)
739 return (!cook || password) && !isecho(fd0);
742 void
743 sendtype(int fd0)
745 int i, n, nr, raw;
747 raw = israw(fd0);
748 while(ntypebreak || (raw && ntypeb > 0)){
749 for(i=0; i<ntypeb; i++)
750 if(typing[i]=='\n' || typing[i]==0x04 || (i==ntypeb-1 && raw)){
751 if((typing[i] == '\n' || typing[i] == 0x04) && ntypebreak > 0)
752 ntypebreak--;
753 n = i+1;
754 i++;
755 if(!raw)
756 echoed(typing, n);
757 if(write(fd0, typing, n) != n)
758 error("sending to program");
759 nr = nrunes(typing, i);
760 q.p += nr;
761 ntyper -= nr;
762 ntypeb -= i;
763 memmove(typing, typing+i, ntypeb);
764 goto cont2;
766 print("no breakchar\n");
767 ntypebreak = 0;
768 cont2:;
772 void
773 sendbs(int fd0, int n)
775 char buf[128];
776 int m;
778 memset(buf, 0x08, sizeof buf);
779 while(n > 0) {
780 m = sizeof buf;
781 if(m > n)
782 m = n;
783 n -= m;
784 write(fd0, buf, m);
788 void
789 deltype(uint p0, uint p1)
791 int w;
792 uint p, b0, b1;
793 Rune r;
795 /* advance to p0 */
796 b0 = 0;
797 for(p=0; p<p0 && b0<ntypeb; p++){
798 w = chartorune(&r, typing+b0);
799 b0 += w;
801 if(p != p0)
802 error("deltype 1");
803 /* advance to p1 */
804 b1 = b0;
805 for(; p<p1 && b1<ntypeb; p++){
806 w = chartorune(&r, typing+b1);
807 b1 += w;
808 if(r=='\n' || r==0x04)
809 ntypebreak--;
811 if(p != p1)
812 error("deltype 2");
813 memmove(typing+b0, typing+b1, ntypeb-b1);
814 ntypeb -= b1-b0;
815 ntyper -= p1-p0;
818 void
819 type(Event *e, int fd0, CFid *afd, CFid *dfd)
821 int m, n, nr;
822 char buf[128];
824 if(e->nr > 0)
825 addtype(e->c1, e->q0-q.p, e->b, e->nb, e->nr);
826 else{
827 m = e->q0;
828 while(m < e->q1){
829 n = sprint(buf, "#%d", m);
830 fswrite(afd, buf, n);
831 n = fsread(dfd, buf, sizeof buf);
832 nr = nrunes(buf, n);
833 while(m+nr > e->q1){
834 do; while(n>0 && (buf[--n]&0xC0)==0x80);
835 --nr;
837 if(n == 0)
838 break;
839 addtype(e->c1, m-q.p, buf, n, nr);
840 m += nr;
843 if(israw(fd0)) {
844 n = sprint(buf, "#%d,#%d", e->q0, e->q1);
845 fswrite(afd, buf, n);
846 fswrite(dfd, "", 0);
847 q.p -= e->q1 - e->q0;
849 sendtype(fd0);
850 if(e->nb > 0 && e->b[e->nb-1] == '\n')
851 cook = 1;
854 void
855 sende(Event *e, int fd0, CFid *cfd, CFid *afd, CFid *dfd, int donl)
857 int l, m, n, nr, lastc, end;
858 char abuf[16], buf[128];
860 end = q.p+ntyper;
861 l = sprint(abuf, "#%d", end);
862 fswrite(afd, abuf, l);
863 if(e->nr > 0){
864 fswrite(dfd, e->b, e->nb);
865 addtype(e->c1, ntyper, e->b, e->nb, e->nr);
866 lastc = e->r[e->nr-1];
867 }else{
868 m = e->q0;
869 lastc = 0;
870 while(m < e->q1){
871 n = sprint(buf, "#%d", m);
872 fswrite(afd, buf, n);
873 n = fsread(dfd, buf, sizeof buf);
874 nr = nrunes(buf, n);
875 while(m+nr > e->q1){
876 do; while(n>0 && (buf[--n]&0xC0)==0x80);
877 --nr;
879 if(n == 0)
880 break;
881 l = sprint(abuf, "#%d", end);
882 fswrite(afd, abuf, l);
883 fswrite(dfd, buf, n);
884 addtype(e->c1, ntyper, buf, n, nr);
885 lastc = buf[n-1];
886 m += nr;
887 end += nr;
890 if(donl && lastc!='\n'){
891 fswrite(dfd, "\n", 1);
892 addtype(e->c1, ntyper, "\n", 1, 1);
894 fswrite(cfd, "dot=addr", 8);
895 sendtype(fd0);