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 int noecho = 1;
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;
59 char *name;
61 char **prog;
62 Channel *cwait;
63 int pid = -1;
65 int label(char*, int);
66 void error(char*, ...);
67 void stdinproc(void*);
68 void stdoutproc(void*);
69 void type(Event*, int, CFid*, CFid*);
70 void sende(Event*, int, CFid*, CFid*, CFid*, int);
71 char *onestring(int, char**);
72 int delete(Event*);
73 void deltype(uint, uint);
74 void runproc(void*);
76 int
77 fsfidprint(CFid *fid, char *fmt, ...)
78 {
79 char buf[256];
80 va_list arg;
81 int n;
83 va_start(arg, fmt);
84 n = vsnprint(buf, sizeof buf, fmt, arg);
85 va_end(arg);
86 return fswrite(fid, buf, n);
87 }
89 void
90 usage(void)
91 {
92 fprint(2, "usage: win cmd args...\n");
93 threadexitsall("usage");
94 }
96 void
97 waitthread(void *v)
98 {
99 recvp(cwait);
100 threadexitsall(nil);
103 void
104 hangupnote(void *a, char *msg)
106 if(strcmp(msg, "hangup") == 0 && pid != 0){
107 postnote(PNGROUP, pid, "hangup");
108 noted(NDFLT);
110 if(strstr(msg, "child")){
111 /* bug: do better */
112 threadexitsall(0);
114 noted(NDFLT);
117 void
118 threadmain(int argc, char **argv)
120 int fd, id;
121 char buf[256];
122 char buf1[128];
123 CFsys *fs;
124 char *dump;
126 dump = onestring(argc, argv);
128 ARGBEGIN{
129 case 'd':
130 debug = 1;
131 break;
132 case 'n':
133 name = EARGF(usage());
134 break;
135 default:
136 usage();
137 }ARGEND
139 prog = argv;
141 if(name == nil){
142 if(argc > 0)
143 name = argv[0];
144 else{
145 name = sysname();
146 if(name == nil)
147 name = "gnot";
151 /*
152 * notedisable("sys: write on closed pipe");
153 * not okay to disable the note, because that
154 * gets inherited by the subshell, so that something
155 * as simple as "yes | sed 10q" never exits.
156 * call notifyoff instead. (is notedisable ever safe?)
157 */
158 notifyoff("sys: write on closed pipe");
160 noteenable("sys: child");
161 notify(hangupnote);
163 if((fs = nsmount("acme", "")) == 0)
164 sysfatal("nsmount acme: %r");
165 ctlfd = fsopen(fs, "new/ctl", ORDWR|OCEXEC);
166 if(ctlfd == 0 || fsread(ctlfd, buf, 12) != 12)
167 sysfatal("ctl: %r");
168 id = atoi(buf);
169 snprint(buf, sizeof buf, "%d", id);
170 putenv("winid", buf);
171 sprint(buf, "%d/tag", id);
172 fd = fsopenfd(fs, buf, OWRITE|OCEXEC);
173 write(fd, " Send Noscroll", 1+4+1+8);
174 close(fd);
175 sprint(buf, "%d/event", id);
176 eventfd = fsopen(fs, buf, ORDWR|OCEXEC);
177 sprint(buf, "%d/addr", id);
178 addrfd = fsopen(fs, buf, ORDWR|OCEXEC);
179 sprint(buf, "%d/data", id);
180 datafd = fsopen(fs, buf, ORDWR|OCEXEC);
181 sprint(buf, "%d/body", id);
182 /* bodyfd = fsopenfd(fs, buf, ORDWR|OCEXEC); */
183 if(eventfd==nil || addrfd==nil || datafd==nil)
184 sysfatal("data files: %r");
185 /*
186 if(eventfd<0 || addrfd<0 || datafd<0 || bodyfd<0)
187 sysfatal("data files: %r");
188 */
189 fsunmount(fs);
191 cwait = threadwaitchan();
192 threadcreate(waitthread, nil, STACK);
193 pid = rcstart(argc, argv, &rcfd, nil);
194 if(pid == -1)
195 sysfatal("exec failed");
197 getwd(buf1, sizeof buf1);
198 sprint(buf, "name %s/-%s\n0\n", buf1, name);
199 fswrite(ctlfd, buf, strlen(buf));
200 sprint(buf, "dumpdir %s/\n", buf1);
201 fswrite(ctlfd, buf, strlen(buf));
202 sprint(buf, "dump %s\n", dump);
203 fswrite(ctlfd, buf, strlen(buf));
204 sprint(buf, "scroll");
205 fswrite(ctlfd, buf, strlen(buf));
207 updatewinsize(25, 80, 0, 0);
208 proccreate(stdoutproc, nil, STACK);
209 stdinproc(nil);
212 void
213 error(char *s, ...)
215 va_list arg;
217 if(s){
218 va_start(arg, s);
219 s = vsmprint(s, arg);
220 va_end(arg);
221 fprint(2, "win: %s: %r\n", s);
223 if(pid != -1)
224 postnote(PNGROUP, pid, "hangup");
225 threadexitsall(s);
228 char*
229 onestring(int argc, char **argv)
231 char *p;
232 int i, n;
233 static char buf[1024];
235 if(argc == 0)
236 return "";
237 p = buf;
238 for(i=0; i<argc; i++){
239 n = strlen(argv[i]);
240 if(p+n+1 >= buf+sizeof buf)
241 break;
242 memmove(p, argv[i], n);
243 p += n;
244 *p++ = ' ';
246 p[-1] = 0;
247 return buf;
250 int
251 getec(CFid *efd)
253 static char buf[8192];
254 static char *bufp;
255 static int nbuf;
257 if(nbuf == 0){
258 nbuf = fsread(efd, buf, sizeof buf);
259 if(nbuf <= 0)
260 error(nil);
261 bufp = buf;
263 --nbuf;
264 return *bufp++;
267 int
268 geten(CFid *efd)
270 int n, c;
272 n = 0;
273 while('0'<=(c=getec(efd)) && c<='9')
274 n = n*10+(c-'0');
275 if(c != ' ')
276 error("event number syntax");
277 return n;
280 int
281 geter(CFid *efd, char *buf, int *nb)
283 Rune r;
284 int n;
286 r = getec(efd);
287 buf[0] = r;
288 n = 1;
289 if(r < Runeself)
290 goto Return;
291 while(!fullrune(buf, n))
292 buf[n++] = getec(efd);
293 chartorune(&r, buf);
294 Return:
295 *nb = n;
296 return r;
299 void
300 gete(CFid *efd, Event *e)
302 int i, nb;
304 e->c1 = getec(efd);
305 e->c2 = getec(efd);
306 e->q0 = geten(efd);
307 e->q1 = geten(efd);
308 e->flag = geten(efd);
309 e->nr = geten(efd);
310 if(e->nr > EVENTSIZE)
311 error("event string too long");
312 e->nb = 0;
313 for(i=0; i<e->nr; i++){
314 e->r[i] = geter(efd, e->b+e->nb, &nb);
315 e->nb += nb;
317 e->r[e->nr] = 0;
318 e->b[e->nb] = 0;
319 if(getec(efd) != '\n')
320 error("event syntax 2");
323 int
324 nrunes(char *s, int nb)
326 int i, n;
327 Rune r;
329 n = 0;
330 for(i=0; i<nb; n++)
331 i += chartorune(&r, s+i);
332 return n;
335 void
336 stdinproc(void *v)
338 CFid *cfd = ctlfd;
339 CFid *efd = eventfd;
340 CFid *dfd = datafd;
341 CFid *afd = addrfd;
342 int fd0 = rcfd;
343 Event e, e2, e3, e4;
345 USED(v);
347 for(;;){
348 if(debug)
349 fprint(2, "typing[%d,%d)\n", q.p, q.p+ntyper);
350 gete(efd, &e);
351 if(debug)
352 fprint(2, "msg %c%c q[%d,%d)... ", e.c1, e.c2, e.q0, e.q1);
353 qlock(&q.lk);
354 switch(e.c1){
355 default:
356 Unknown:
357 print("unknown message %c%c\n", e.c1, e.c2);
358 break;
360 case 'E': /* write to body; can't affect us */
361 if(debug)
362 fprint(2, "shift typing %d... ", e.q1-e.q0);
363 q.p += e.q1-e.q0;
364 break;
366 case 'F': /* generated by our actions; ignore */
367 break;
369 case 'K':
370 case 'M':
371 switch(e.c2){
372 case 'I':
373 if(e.nr == 1 && e.r[0] == 0x7F) {
374 char buf[1];
375 fsprint(addrfd, "#%ud,#%ud", e.q0, e.q1);
376 fswrite(datafd, "", 0);
377 buf[0] = 0x7F;
378 write(fd0, buf, 1);
379 break;
381 if(e.q0 < q.p){
382 if(debug)
383 fprint(2, "shift typing %d... ", e.q1-e.q0);
384 q.p += e.q1-e.q0;
386 else if(e.q0 <= q.p+ntyper){
387 if(debug)
388 fprint(2, "type... ");
389 type(&e, fd0, afd, dfd);
391 break;
393 case 'D':
394 q.p -= delete(&e);
395 break;
397 case 'x':
398 case 'X':
399 if(e.flag & 2)
400 gete(efd, &e2);
401 if(e.flag & 8){
402 gete(efd, &e3);
403 gete(efd, &e4);
405 if(e.flag&1 || (e.c2=='x' && e.nr==0 && e2.nr==0)){
406 /* send it straight back */
407 fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
408 break;
410 if(e.q0==e.q1 && (e.flag&2)){
411 e2.flag = e.flag;
412 e = e2;
414 char buf[100];
415 snprint(buf, sizeof buf, "%.*S", e.nr, e.r);
416 if(cistrcmp(buf, "scroll") == 0) {
417 fsprint(ctlfd, "scroll\nshow");
418 break;
420 if(cistrcmp(buf, "noscroll") == 0) {
421 fsprint(ctlfd, "noscroll");
422 break;
424 if(e.flag & 8){
425 if(e.q1 != e.q0){
426 sende(&e, fd0, cfd, afd, dfd, 0);
427 sende(&blank, fd0, cfd, afd, dfd, 0);
429 sende(&e3, fd0, cfd, afd, dfd, 1);
430 }else if(e.q1 != e.q0)
431 sende(&e, fd0, cfd, afd, dfd, 1);
432 break;
434 case 'l':
435 case 'L':
436 /* just send it back */
437 if(e.flag & 2)
438 gete(efd, &e2);
439 fsfidprint(efd, "%c%c%d %d\n", e.c1, e.c2, e.q0, e.q1);
440 break;
442 case 'd':
443 case 'i':
444 break;
446 default:
447 goto Unknown;
450 qunlock(&q.lk);
454 void
455 stdoutproc(void *v)
457 int fd1 = rcfd;
458 CFid *afd = addrfd;
459 CFid *dfd = datafd;
460 int n, m, w, npart;
461 char *buf, *s, *t;
462 Rune r;
463 char x[16], hold[UTFmax];
465 USED(v);
466 buf = malloc(8192+UTFmax+1);
467 npart = 0;
468 for(;;){
469 /* Let typing have a go -- maybe there's a rubout waiting. */
470 yield();
471 n = read(fd1, buf+npart, 8192);
472 if(n <= 0)
473 error(nil);
475 /* squash NULs */
476 s = memchr(buf+npart, 0, n);
477 if(s){
478 for(t=s; s<buf+npart+n; s++)
479 if(*t = *s) /* assign = */
480 t++;
481 n = t-(buf+npart);
484 n += npart;
486 /* hold on to final partial rune */
487 npart = 0;
488 while(n>0 && (buf[n-1]&0xC0)){
489 --n;
490 npart++;
491 if((buf[n]&0xC0)!=0x80){
492 if(fullrune(buf+n, npart)){
493 w = chartorune(&r, buf+n);
494 n += w;
495 npart -= w;
497 break;
500 if(n > 0){
501 memmove(hold, buf+n, npart);
502 buf[n] = 0;
503 n = label(buf, n);
504 buf[n] = 0;
505 qlock(&q.lk);
506 m = sprint(x, "#%d", q.p);
507 if(fswrite(afd, x, m) != m){
508 fprint(2, "stdout writing address: %r; resetting\n");
509 fswrite(afd, "$", 1);
510 m = fsread(afd, x, sizeof x-1);
511 if(m >= 0){
512 x[m] = 0;
513 q.p = atoi(x);
516 if(fswrite(dfd, buf, n) != n)
517 error("stdout writing body");
518 q.p += nrunes(buf, n);
519 qunlock(&q.lk);
520 memmove(buf, hold, npart);
525 char wdir[512];
526 int
527 label(char *sr, int n)
529 char *sl, *el, *er, *r, *p;
531 er = sr+n;
532 for(r=er-1; r>=sr; r--)
533 if(*r == '\007')
534 break;
535 if(r < sr)
536 return n;
538 el = r+1;
539 if(el-sr > sizeof wdir - strlen(name) - 20)
540 sr = el - sizeof wdir - strlen(name) - 20;
541 for(sl=el-3; sl>=sr; sl--)
542 if(sl[0]=='\033' && sl[1]==']' && sl[2]==';')
543 break;
544 if(sl < sr)
545 return n;
547 *r = 0;
548 /*
549 * add /-sysname if not present
550 */
551 snprint(wdir, sizeof wdir, "name %s", sl+3);
552 p = strrchr(wdir, '/');
553 if(p==nil || *(p+1) != '-'){
554 p = wdir+strlen(wdir);
555 if(*(p-1) != '/')
556 *p++ = '/';
557 *p++ = '-';
558 strcpy(p, name);
560 strcat(wdir, "\n0\n");
561 fswrite(ctlfd, wdir, strlen(wdir));
563 memmove(sl, el, er-el);
564 n -= (el-sl);
565 return n;
568 int
569 delete(Event *e)
571 uint q0, q1;
572 int deltap;
574 q0 = e->q0;
575 q1 = e->q1;
576 if(q1 <= q.p)
577 return e->q1-e->q0;
578 if(q0 >= q.p+ntyper)
579 return 0;
580 deltap = 0;
581 if(q0 < q.p){
582 deltap = q.p-q0;
583 q0 = 0;
584 }else
585 q0 -= q.p;
586 if(q1 > q.p+ntyper)
587 q1 = ntyper;
588 else
589 q1 -= q.p;
590 deltype(q0, q1);
591 return deltap;
594 void
595 addtype(int c, uint p0, char *b, int nb, int nr)
597 int i, w;
598 Rune r;
599 uint p;
600 char *b0;
602 for(i=0; i<nb; i+=w){
603 w = chartorune(&r, b+i);
604 if((r==0x7F||r==3) && c=='K'){
605 write(rcfd, "\x7F", 1);
606 /* toss all typing */
607 q.p += ntyper+nr;
608 ntypebreak = 0;
609 ntypeb = 0;
610 ntyper = 0;
611 /* buglet: more than one delete ignored */
612 return;
614 if(r=='\n' || r==0x04)
615 ntypebreak++;
617 typing = realloc(typing, ntypeb+nb);
618 if(typing == nil)
619 error("realloc");
620 if(p0 == ntyper)
621 memmove(typing+ntypeb, b, nb);
622 else{
623 b0 = typing;
624 for(p=0; p<p0 && b0<typing+ntypeb; p++){
625 w = chartorune(&r, b0+i);
626 b0 += w;
628 if(p != p0)
629 error("typing: findrune");
630 memmove(b0+nb, b0, (typing+ntypeb)-b0);
631 memmove(b0, b, nb);
633 ntypeb += nb;
634 ntyper += nr;
637 void
638 sendtype(int fd0)
640 int i, n, nr;
642 while(ntypebreak){
643 for(i=0; i<ntypeb; i++)
644 if(typing[i]=='\n' || typing[i]==0x04){
645 n = i+1;
646 i++;
647 if(write(fd0, typing, n) != n)
648 error("sending to program");
649 nr = nrunes(typing, i);
650 q.p += nr;
651 ntyper -= nr;
652 ntypeb -= i;
653 memmove(typing, typing+i, ntypeb);
654 ntypebreak--;
655 goto cont2;
657 print("no breakchar\n");
658 ntypebreak = 0;
659 cont2:;
663 void
664 deltype(uint p0, uint p1)
666 int w;
667 uint p, b0, b1;
668 Rune r;
670 /* advance to p0 */
671 b0 = 0;
672 for(p=0; p<p0 && b0<ntypeb; p++){
673 w = chartorune(&r, typing+b0);
674 b0 += w;
676 if(p != p0)
677 error("deltype 1");
678 /* advance to p1 */
679 b1 = b0;
680 for(; p<p1 && b1<ntypeb; p++){
681 w = chartorune(&r, typing+b1);
682 b1 += w;
683 if(r=='\n' || r==0x04)
684 ntypebreak--;
686 if(p != p1)
687 error("deltype 2");
688 memmove(typing+b0, typing+b1, ntypeb-b1);
689 ntypeb -= b1-b0;
690 ntyper -= p1-p0;
693 void
694 type(Event *e, int fd0, CFid *afd, CFid *dfd)
696 int m, n, nr;
697 char buf[128];
699 if(e->nr > 0)
700 addtype(e->c1, e->q0-q.p, e->b, e->nb, e->nr);
701 else{
702 m = e->q0;
703 while(m < e->q1){
704 n = sprint(buf, "#%d", m);
705 fswrite(afd, buf, n);
706 n = fsread(dfd, buf, sizeof buf);
707 nr = nrunes(buf, n);
708 while(m+nr > e->q1){
709 do; while(n>0 && (buf[--n]&0xC0)==0x80);
710 --nr;
712 if(n == 0)
713 break;
714 addtype(e->c1, m-q.p, buf, n, nr);
715 m += nr;
718 sendtype(fd0);
721 void
722 sende(Event *e, int fd0, CFid *cfd, CFid *afd, CFid *dfd, int donl)
724 int l, m, n, nr, lastc, end;
725 char abuf[16], buf[128];
727 end = q.p+ntyper;
728 l = sprint(abuf, "#%d", end);
729 fswrite(afd, abuf, l);
730 if(e->nr > 0){
731 fswrite(dfd, e->b, e->nb);
732 addtype(e->c1, ntyper, e->b, e->nb, e->nr);
733 lastc = e->r[e->nr-1];
734 }else{
735 m = e->q0;
736 lastc = 0;
737 while(m < e->q1){
738 n = sprint(buf, "#%d", m);
739 fswrite(afd, buf, n);
740 n = fsread(dfd, buf, sizeof buf);
741 nr = nrunes(buf, n);
742 while(m+nr > e->q1){
743 do; while(n>0 && (buf[--n]&0xC0)==0x80);
744 --nr;
746 if(n == 0)
747 break;
748 l = sprint(abuf, "#%d", end);
749 fswrite(afd, abuf, l);
750 fswrite(dfd, buf, n);
751 addtype(e->c1, ntyper, buf, n, nr);
752 lastc = buf[n-1];
753 m += nr;
754 end += nr;
757 if(donl && lastc!='\n'){
758 fswrite(dfd, "\n", 1);
759 addtype(e->c1, ntyper, "\n", 1, 1);
761 fswrite(cfd, "dot=addr", 8);
762 sendtype(fd0);