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"
9 #define EVENTSIZE 256
10 #define STACK 32768
12 typedef struct Event Event;
13 typedef struct Q Q;
15 struct Event
16 {
17 int c1;
18 int c2;
19 int q0;
20 int q1;
21 int flag;
22 int nb;
23 int nr;
24 char b[EVENTSIZE*UTFmax+1];
25 Rune r[EVENTSIZE+1];
26 };
28 Event blank = {
29 'M',
30 'X',
31 0, 0, 0, 1, 1,
32 { ' ', 0 },
33 { ' ', 0 }
34 };
36 struct Q
37 {
38 QLock lk;
39 int p;
40 int k;
41 };
43 Q q;
45 CFid *eventfd;
46 CFid *addrfd;
47 CFid *datafd;
48 CFid *ctlfd;
49 /* int bodyfd; */
51 char *typing;
52 int ntypeb;
53 int ntyper;
54 int ntypebreak;
55 int debug;
56 int rcfd;
57 int cook = 1;
58 int password;
59 int israw(int);
61 char *name;
63 char **prog;
64 Channel *cwait;
65 int pid = -1;
67 int label(char*, int);
68 void error(char*, ...);
69 void stdinproc(void*);
70 void stdoutproc(void*);
71 void type(Event*, int, CFid*, CFid*);
72 void sende(Event*, int, CFid*, CFid*, CFid*, int);
73 char *onestring(int, char**);
74 int delete(Event*);
75 void deltype(uint, uint);
76 void sendbs(int, int);
77 void runproc(void*);
79 int
80 fsfidprint(CFid *fid, char *fmt, ...)
81 {
82 char buf[256];
83 va_list arg;
84 int n;
86 va_start(arg, fmt);
87 n = vsnprint(buf, sizeof buf, fmt, arg);
88 va_end(arg);
89 return fswrite(fid, buf, n);
90 }
92 void
93 usage(void)
94 {
95 fprint(2, "usage: win cmd args...\n");
96 threadexitsall("usage");
97 }
99 void
100 waitthread(void *v)
102 recvp(cwait);
103 threadexitsall(nil);
106 void
107 hangupnote(void *a, char *msg)
109 if(strcmp(msg, "hangup") == 0 && pid != 0){
110 postnote(PNGROUP, pid, "hangup");
111 noted(NDFLT);
113 if(strstr(msg, "child")){
114 char buf[128];
115 int n;
117 n = awaitnohang(buf, sizeof buf-1);
118 if(n > 0){
119 buf[n] = 0;
120 if(atoi(buf) == pid)
121 threadexitsall(0);
123 noted(NCONT);
125 noted(NDFLT);
128 void
129 threadmain(int argc, char **argv)
131 int fd, id;
132 char buf[256];
133 char buf1[128];
134 CFsys *fs;
135 char *dump;
137 dump = onestring(argc, argv);
139 ARGBEGIN{
140 case 'd':
141 debug = 1;
142 break;
143 case 'n':
144 name = EARGF(usage());
145 break;
146 default:
147 usage();
148 }ARGEND
150 prog = argv;
152 if(name == nil){
153 if(argc > 0)
154 name = argv[0];
155 else{
156 name = sysname();
157 if(name == nil)
158 name = "gnot";
162 /*
163 * notedisable("sys: write on closed pipe");
164 * not okay to disable the note, because that
165 * gets inherited by the subshell, so that something
166 * as simple as "yes | sed 10q" never exits.
167 * call notifyoff instead. (is notedisable ever safe?)
168 */
169 notifyoff("sys: write on closed pipe");
171 noteenable("sys: child");
172 notify(hangupnote);
174 if((fs = nsmount("acme", "")) == 0)
175 sysfatal("nsmount acme: %r");
176 ctlfd = fsopen(fs, "new/ctl", ORDWR|OCEXEC);
177 if(ctlfd == 0 || fsread(ctlfd, buf, 12) != 12)
178 sysfatal("ctl: %r");
179 id = atoi(buf);
180 snprint(buf, sizeof buf, "%d", id);
181 putenv("winid", buf);
182 sprint(buf, "%d/tag", id);
183 fd = fsopenfd(fs, buf, OWRITE|OCEXEC);
184 write(fd, " Send", 1+4);
185 close(fd);
186 sprint(buf, "%d/event", id);
187 eventfd = fsopen(fs, buf, ORDWR|OCEXEC);
188 sprint(buf, "%d/addr", id);
189 addrfd = fsopen(fs, buf, ORDWR|OCEXEC);
190 sprint(buf, "%d/data", id);
191 datafd = fsopen(fs, buf, ORDWR|OCEXEC);
192 sprint(buf, "%d/body", id);
193 /* bodyfd = fsopenfd(fs, buf, ORDWR|OCEXEC); */
194 if(eventfd==nil || addrfd==nil || datafd==nil)
195 sysfatal("data files: %r");
196 /*
197 if(eventfd<0 || addrfd<0 || datafd<0 || bodyfd<0)
198 sysfatal("data files: %r");
199 */
200 fsunmount(fs);
202 cwait = threadwaitchan();
203 threadcreate(waitthread, nil, STACK);
204 pid = rcstart(argc, argv, &rcfd, nil);
205 if(pid == -1)
206 sysfatal("exec failed");
208 getwd(buf1, sizeof buf1);
209 sprint(buf, "name %s/-%s\n0\n", buf1, name);
210 fswrite(ctlfd, buf, strlen(buf));
211 sprint(buf, "dumpdir %s/\n", buf1);
212 fswrite(ctlfd, buf, strlen(buf));
213 sprint(buf, "dump %s\n", dump);
214 fswrite(ctlfd, buf, strlen(buf));
215 sprint(buf, "scroll");
216 fswrite(ctlfd, buf, strlen(buf));
218 updatewinsize(25, 80, 0, 0);
219 proccreate(stdoutproc, nil, STACK);
220 stdinproc(nil);
223 void
224 error(char *s, ...)
226 va_list arg;
228 if(s){
229 va_start(arg, s);
230 s = vsmprint(s, arg);
231 va_end(arg);
232 fprint(2, "win: %s: %r\n", s);
234 if(pid != -1)
235 postnote(PNGROUP, pid, "hangup");
236 threadexitsall(s);
239 char*
240 onestring(int argc, char **argv)
242 char *p;
243 int i, n;
244 static char buf[1024];
246 if(argc == 0)
247 return "";
248 p = buf;
249 for(i=0; i<argc; i++){
250 n = strlen(argv[i]);
251 if(p+n+1 >= buf+sizeof buf)
252 break;
253 memmove(p, argv[i], n);
254 p += n;
255 *p++ = ' ';
257 p[-1] = 0;
258 return buf;
261 int
262 getec(CFid *efd)
264 static char buf[8192];
265 static char *bufp;
266 static int nbuf;
268 if(nbuf == 0){
269 nbuf = fsread(efd, buf, sizeof buf);
270 if(nbuf <= 0)
271 error(nil);
272 bufp = buf;
274 --nbuf;
275 return *bufp++;
278 int
279 geten(CFid *efd)
281 int n, c;
283 n = 0;
284 while('0'<=(c=getec(efd)) && c<='9')
285 n = n*10+(c-'0');
286 if(c != ' ')
287 error("event number syntax");
288 return n;
291 int
292 geter(CFid *efd, char *buf, int *nb)
294 Rune r;
295 int n;
297 r = getec(efd);
298 buf[0] = r;
299 n = 1;
300 if(r < Runeself)
301 goto Return;
302 while(!fullrune(buf, n))
303 buf[n++] = getec(efd);
304 chartorune(&r, buf);
305 Return:
306 *nb = n;
307 return r;
310 void
311 gete(CFid *efd, Event *e)
313 int i, nb;
315 e->c1 = getec(efd);
316 e->c2 = getec(efd);
317 e->q0 = geten(efd);
318 e->q1 = geten(efd);
319 e->flag = geten(efd);
320 e->nr = geten(efd);
321 if(e->nr > EVENTSIZE)
322 error("event string too long");
323 e->nb = 0;
324 for(i=0; i<e->nr; i++){
325 e->r[i] = geter(efd, e->b+e->nb, &nb);
326 e->nb += nb;
328 e->r[e->nr] = 0;
329 e->b[e->nb] = 0;
330 if(getec(efd) != '\n')
331 error("event syntax 2");
334 int
335 nrunes(char *s, int nb)
337 int i, n;
338 Rune r;
340 n = 0;
341 for(i=0; i<nb; n++)
342 i += chartorune(&r, s+i);
343 return n;
346 void
347 stdinproc(void *v)
349 CFid *cfd = ctlfd;
350 CFid *efd = eventfd;
351 CFid *dfd = datafd;
352 CFid *afd = addrfd;
353 int fd0 = rcfd;
354 Event e, e2, e3, e4;
355 int n;
357 USED(v);
359 for(;;){
360 if(debug)
361 fprint(2, "typing[%d,%d)\n", q.p, q.p+ntyper);
362 gete(efd, &e);
363 if(debug)
364 fprint(2, "msg %c%c q[%d,%d)... ", e.c1, e.c2, e.q0, e.q1);
365 qlock(&q.lk);
366 switch(e.c1){
367 default:
368 Unknown:
369 print("unknown message %c%c\n", e.c1, e.c2);
370 break;
372 case 'E': /* write to body or tag; can't affect us */
373 switch(e.c2){
374 case 'I':
375 case 'D': /* body */
376 if(debug)
377 fprint(2, "shift typing %d... ", e.q1-e.q0);
378 q.p += e.q1-e.q0;
379 break;
381 case 'i':
382 case 'd': /* tag */
383 break;
385 default:
386 goto Unknown;
388 break;
390 case 'F': /* generated by our actions; ignore */
391 break;
393 case 'K':
394 case 'M':
395 switch(e.c2){
396 case 'I':
397 if(e.nr == 1 && e.r[0] == 0x7F) {
398 char buf[1];
399 fsprint(addrfd, "#%ud,#%ud", e.q0, e.q1);
400 fswrite(datafd, "", 0);
401 buf[0] = 0x7F;
402 write(fd0, buf, 1);
403 break;
405 if(e.q0 < q.p){
406 if(debug)
407 fprint(2, "shift typing %d... ", e.q1-e.q0);
408 q.p += e.q1-e.q0;
410 else if(e.q0 <= q.p+ntyper){
411 if(debug)
412 fprint(2, "type... ");
413 type(&e, fd0, afd, dfd);
415 break;
417 case 'D':
418 n = delete(&e);
419 q.p -= n;
420 if(israw(fd0) && e.q1 >= q.p+n)
421 sendbs(fd0, n);
422 break;
424 case 'x':
425 case 'X':
426 if(e.flag & 2)
427 gete(efd, &e2);
428 if(e.flag & 8){
429 gete(efd, &e3);
430 gete(efd, &e4);
432 if(e.flag&1 || (e.c2=='x' && e.nr==0 && e2.nr==0)){
433 /* send it straight back */
434 fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
435 break;
437 if(e.q0==e.q1 && (e.flag&2)){
438 e2.flag = e.flag;
439 e = e2;
441 char buf[100];
442 snprint(buf, sizeof buf, "%.*S", e.nr, e.r);
443 if(cistrcmp(buf, "cook") == 0) {
444 cook = 1;
445 break;
447 if(cistrcmp(buf, "nocook") == 0) {
448 cook = 0;
449 break;
451 if(e.flag & 8){
452 if(e.q1 != e.q0){
453 sende(&e, fd0, cfd, afd, dfd, 0);
454 sende(&blank, fd0, cfd, afd, dfd, 0);
456 sende(&e3, fd0, cfd, afd, dfd, 1);
457 }else if(e.q1 != e.q0)
458 sende(&e, fd0, cfd, afd, dfd, 1);
459 break;
461 case 'l':
462 case 'L':
463 /* just send it back */
464 if(e.flag & 2)
465 gete(efd, &e2);
466 fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
467 break;
469 case 'd':
470 case 'i':
471 break;
473 default:
474 goto Unknown;
477 qunlock(&q.lk);
481 void
482 stdoutproc(void *v)
484 int fd1 = rcfd;
485 CFid *afd = addrfd;
486 CFid *dfd = datafd;
487 int n, m, w, npart;
488 char *buf, *s, *t;
489 Rune r;
490 char x[16], hold[UTFmax];
492 USED(v);
493 buf = malloc(8192+UTFmax+1);
494 npart = 0;
495 for(;;){
496 /* Let typing have a go -- maybe there's a rubout waiting. */
497 yield();
498 n = read(fd1, buf+npart, 8192);
499 if(n <= 0)
500 error(nil);
502 n = echocancel(buf+npart, n);
503 if(n == 0)
504 continue;
506 n = dropcrnl(buf+npart, n);
507 if(n == 0)
508 continue;
510 /* squash NULs */
511 s = memchr(buf+npart, 0, n);
512 if(s){
513 for(t=s; s<buf+npart+n; s++)
514 if(*t = *s) /* assign = */
515 t++;
516 n = t-(buf+npart);
519 n += npart;
521 /* hold on to final partial rune */
522 npart = 0;
523 while(n>0 && (buf[n-1]&0xC0)){
524 --n;
525 npart++;
526 if((buf[n]&0xC0)!=0x80){
527 if(fullrune(buf+n, npart)){
528 w = chartorune(&r, buf+n);
529 n += w;
530 npart -= w;
532 break;
535 if(n > 0){
536 memmove(hold, buf+n, npart);
537 buf[n] = 0;
538 n = label(buf, n);
539 buf[n] = 0;
541 // clumsy but effective: notice password
542 // prompts so we can disable echo.
543 password = 0;
544 if(cistrstr(buf, "password")) {
545 int i;
547 i = n;
548 while(i > 0 && buf[i-1] == ' ')
549 i--;
550 password = i > 0 && buf[i-1] == ':';
553 qlock(&q.lk);
554 m = sprint(x, "#%d", q.p);
555 if(fswrite(afd, x, m) != m){
556 fprint(2, "stdout writing address %s: %r; resetting\n", x);
557 if(fswrite(afd, "$", 1) < 0)
558 fprint(2, "reset: %r\n");
559 fsseek(afd, 0, 0);
560 m = fsread(afd, x, sizeof x-1);
561 if(m >= 0){
562 x[m] = 0;
563 q.p = atoi(x);
566 if(fswrite(dfd, buf, n) != n)
567 error("stdout writing body");
568 /* Make sure acme scrolls to the end of the above write. */
569 if(fswrite(dfd, nil, 0) != 0)
570 error("stdout flushing body");
571 q.p += nrunes(buf, n);
572 qunlock(&q.lk);
573 memmove(buf, hold, npart);
578 char wdir[512];
579 int
580 label(char *sr, int n)
582 char *sl, *el, *er, *r, *p;
584 er = sr+n;
585 for(r=er-1; r>=sr; r--)
586 if(*r == '\007')
587 break;
588 if(r < sr)
589 return n;
591 el = r+1;
592 if(el-sr > sizeof wdir - strlen(name) - 20)
593 sr = el - sizeof wdir - strlen(name) - 20;
594 for(sl=el-3; sl>=sr; sl--)
595 if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
596 break;
597 if(sl < sr)
598 return n;
600 *r = 0;
601 /*
602 * add /-sysname if not present
603 */
604 snprint(wdir, sizeof wdir, "name %s", sl+3);
605 p = strrchr(wdir, '/');
606 if(p==nil || *(p+1) != '-'){
607 p = wdir+strlen(wdir);
608 if(*(p-1) != '/')
609 *p++ = '/';
610 *p++ = '-';
611 strcpy(p, name);
613 strcat(wdir, "\n0\n");
614 fswrite(ctlfd, wdir, strlen(wdir));
616 memmove(sl, el, er-el);
617 n -= (el-sl);
618 return n;
621 int
622 delete(Event *e)
624 uint q0, q1;
625 int deltap;
627 q0 = e->q0;
628 q1 = e->q1;
629 if(q1 <= q.p)
630 return e->q1-e->q0;
631 if(q0 >= q.p+ntyper)
632 return 0;
633 deltap = 0;
634 if(q0 < q.p){
635 deltap = q.p-q0;
636 q0 = 0;
637 }else
638 q0 -= q.p;
639 if(q1 > q.p+ntyper)
640 q1 = ntyper;
641 else
642 q1 -= q.p;
643 deltype(q0, q1);
644 return deltap;
647 void
648 addtype(int c, uint p0, char *b, int nb, int nr)
650 int i, w;
651 Rune r;
652 uint p;
653 char *b0;
655 for(i=0; i<nb; i+=w){
656 w = chartorune(&r, b+i);
657 if((r==0x7F||r==3) && c=='K'){
658 write(rcfd, "\x7F", 1);
659 /* toss all typing */
660 q.p += ntyper+nr;
661 ntypebreak = 0;
662 ntypeb = 0;
663 ntyper = 0;
664 /* buglet: more than one delete ignored */
665 return;
667 if(r=='\n' || r==0x04)
668 ntypebreak++;
670 typing = realloc(typing, ntypeb+nb);
671 if(typing == nil)
672 error("realloc");
673 if(p0 == ntyper)
674 memmove(typing+ntypeb, b, nb);
675 else{
676 b0 = typing;
677 for(p=0; p<p0 && b0<typing+ntypeb; p++){
678 w = chartorune(&r, b0+i);
679 b0 += w;
681 if(p != p0)
682 error("typing: findrune");
683 memmove(b0+nb, b0, (typing+ntypeb)-b0);
684 memmove(b0, b, nb);
686 ntypeb += nb;
687 ntyper += nr;
690 int
691 israw(int fd0)
693 return (!cook || password) && !isecho(fd0);
696 void
697 sendtype(int fd0)
699 int i, n, nr, raw;
701 raw = israw(fd0);
702 while(ntypebreak || (raw && ntypeb > 0)){
703 for(i=0; i<ntypeb; i++)
704 if(typing[i]=='\n' || typing[i]==0x04 || (i==ntypeb-1 && raw)){
705 if((typing[i] == '\n' || typing[i] == 0x04) && ntypebreak > 0)
706 ntypebreak--;
707 n = i+1;
708 i++;
709 if(!raw)
710 echoed(typing, n);
711 if(write(fd0, typing, n) != n)
712 error("sending to program");
713 nr = nrunes(typing, i);
714 q.p += nr;
715 ntyper -= nr;
716 ntypeb -= i;
717 memmove(typing, typing+i, ntypeb);
718 goto cont2;
720 print("no breakchar\n");
721 ntypebreak = 0;
722 cont2:;
726 void
727 sendbs(int fd0, int n)
729 char buf[128];
730 int m;
732 memset(buf, 0x08, sizeof buf);
733 while(n > 0) {
734 m = sizeof buf;
735 if(m > n)
736 m = n;
737 n -= m;
738 write(fd0, buf, m);
742 void
743 deltype(uint p0, uint p1)
745 int w;
746 uint p, b0, b1;
747 Rune r;
749 /* advance to p0 */
750 b0 = 0;
751 for(p=0; p<p0 && b0<ntypeb; p++){
752 w = chartorune(&r, typing+b0);
753 b0 += w;
755 if(p != p0)
756 error("deltype 1");
757 /* advance to p1 */
758 b1 = b0;
759 for(; p<p1 && b1<ntypeb; p++){
760 w = chartorune(&r, typing+b1);
761 b1 += w;
762 if(r=='\n' || r==0x04)
763 ntypebreak--;
765 if(p != p1)
766 error("deltype 2");
767 memmove(typing+b0, typing+b1, ntypeb-b1);
768 ntypeb -= b1-b0;
769 ntyper -= p1-p0;
772 void
773 type(Event *e, int fd0, CFid *afd, CFid *dfd)
775 int m, n, nr;
776 char buf[128];
778 if(e->nr > 0)
779 addtype(e->c1, e->q0-q.p, e->b, e->nb, e->nr);
780 else{
781 m = e->q0;
782 while(m < e->q1){
783 n = sprint(buf, "#%d", m);
784 fswrite(afd, buf, n);
785 n = fsread(dfd, buf, sizeof buf);
786 nr = nrunes(buf, n);
787 while(m+nr > e->q1){
788 do; while(n>0 && (buf[--n]&0xC0)==0x80);
789 --nr;
791 if(n == 0)
792 break;
793 addtype(e->c1, m-q.p, buf, n, nr);
794 m += nr;
797 if(israw(fd0)) {
798 n = sprint(buf, "#%d,#%d", e->q0, e->q1);
799 fswrite(afd, buf, n);
800 fswrite(dfd, "", 0);
801 q.p -= e->q1 - e->q0;
803 sendtype(fd0);
804 if(e->nb > 0 && e->b[e->nb-1] == '\n')
805 cook = 1;
808 void
809 sende(Event *e, int fd0, CFid *cfd, CFid *afd, CFid *dfd, int donl)
811 int l, m, n, nr, lastc, end;
812 char abuf[16], buf[128];
814 end = q.p+ntyper;
815 l = sprint(abuf, "#%d", end);
816 fswrite(afd, abuf, l);
817 if(e->nr > 0){
818 fswrite(dfd, e->b, e->nb);
819 addtype(e->c1, ntyper, e->b, e->nb, e->nr);
820 lastc = e->r[e->nr-1];
821 }else{
822 m = e->q0;
823 lastc = 0;
824 while(m < e->q1){
825 n = sprint(buf, "#%d", m);
826 fswrite(afd, buf, n);
827 n = fsread(dfd, buf, sizeof buf);
828 nr = nrunes(buf, n);
829 while(m+nr > e->q1){
830 do; while(n>0 && (buf[--n]&0xC0)==0x80);
831 --nr;
833 if(n == 0)
834 break;
835 l = sprint(abuf, "#%d", end);
836 fswrite(afd, abuf, l);
837 fswrite(dfd, buf, n);
838 addtype(e->c1, ntyper, buf, n, nr);
839 lastc = buf[n-1];
840 m += nr;
841 end += nr;
844 if(donl && lastc!='\n'){
845 fswrite(dfd, "\n", 1);
846 addtype(e->c1, ntyper, "\n", 1, 1);
848 fswrite(cfd, "dot=addr", 8);
849 sendtype(fd0);