]> git.lizzy.rs Git - plan9front.git/blob - acme/bin/source/win/main.c
fill /acme
[plan9front.git] / acme / bin / source / win / main.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <thread.h>
5 #include <fcall.h>
6 #include <9p.h>
7 #include <ctype.h>
8 #include "dat.h"
9
10 void    mainctl(void*);
11 void    startcmd(char *[], int*);
12 void    stdout2body(void*);
13
14 int     debug;
15 int     notepg;
16 int     eraseinput;
17 int     dirty = 0;
18
19 Window *win;            /* the main window */
20
21 void
22 usage(void)
23 {
24         fprint(2, "usage: win [command]\n");
25         threadexitsall("usage");
26 }
27
28 void
29 threadmain(int argc, char *argv[])
30 {
31         int i, j;
32         char *dir, *tag, *name;
33         char buf[1024], **av;
34
35         quotefmtinstall();
36         rfork(RFNAMEG);
37         ARGBEGIN{
38         case 'd':
39                 debug = 1;
40                 chatty9p++;
41                 break;
42         case 'e':
43                 eraseinput = 1;
44                 break;
45         case 'D':
46 {extern int _threaddebuglevel;
47                 _threaddebuglevel = 1<<20;
48 }
49         }ARGEND
50
51         if(argc == 0){
52                 av = emalloc(3*sizeof(char*));
53                 av[0] = "rc";
54                 av[1] = "-i";
55                 name = getenv("sysname");
56         }else{
57                 av = argv;
58                 name = utfrrune(av[0], '/');
59                 if(name)
60                         name++;
61                 else
62                         name = av[0];
63         }
64
65         if(getwd(buf, sizeof buf) == 0)
66                 dir = "/";
67         else
68                 dir = buf;
69         dir = estrdup(dir);
70         tag = estrdup(dir);
71         tag = eappend(estrdup(tag), "/-", name);
72         win = newwindow();
73         snprint(buf, sizeof buf, "%d", win->id);
74         putenv("winid", buf);
75         winname(win, tag);
76         wintagwrite(win, "Send Noscroll", 5+8);
77         threadcreate(mainctl, win, STACK);
78         mountcons();
79         threadcreate(fsloop, nil, STACK);
80         startpipe();
81         startcmd(av, &notepg);
82
83         strcpy(buf, "win");
84         j = 3;
85         for(i=0; i<argc && j+1+strlen(argv[i])+1<sizeof buf; i++){
86                 strcpy(buf+j, " ");
87                 strcpy(buf+j+1, argv[i]);
88                 j += 1+strlen(argv[i]);
89         }
90
91         ctlprint(win->ctl, "scroll");
92         winsetdump(win, dir, buf);
93 }
94
95 int
96 EQUAL(char *s, char *t)
97 {
98         while(tolower(*s) == tolower(*t++))
99                 if(*s++ == '\0')
100                         return 1;
101         return 0;
102 }
103
104 int
105 command(Window *w, char *s)
106 {
107         while(*s==' ' || *s=='\t' || *s=='\n')
108                 s++;
109         if(strcmp(s, "Delete")==0 || strcmp(s, "Del")==0){
110                 windel(w, 1);
111                 threadexitsall(nil);
112                 return 1;
113         }
114         if(EQUAL(s, "scroll")){
115                 ctlprint(w->ctl, "scroll\nshow");
116                 return 1;
117         }
118         if(EQUAL(s, "noscroll")){
119                 ctlprint(w->ctl, "noscroll");
120                 return 1;
121         }
122         return 0;
123 }
124
125 static long
126 utfncpy(char *to, char *from, int n)
127 {
128         char *end, *e;
129
130         e = to+n;
131         if(to >= e)
132                 return 0;
133         end = memccpy(to, from, '\0', e - to);
134         if(end == nil){
135                 end = e;
136                 if(end[-1]&0x80){
137                         if(end-2>=to && (end[-2]&0xE0)==0xC0)
138                                 return end-to;
139                         if(end-3>=to && (end[-3]&0xF0)==0xE0)
140                                 return end-to;
141                         while(end>to && (*--end&0xC0)==0x80)
142                                 ;
143                 }
144         }else
145                 end--;
146         return end - to;
147 }
148
149 /* sendinput and fsloop run in the same proc (can't interrupt each other). */
150 static Req *q;
151 static Req **eq;
152 static int
153 __sendinput(Window *w, ulong q0, ulong q1)
154 {
155         char *s, *t;
156         int n, nb, eofchar;
157         static int partial;
158         static char tmp[UTFmax];
159         Req *r;
160         Rune rune;
161
162         if(!q)
163                 return 0;
164
165         r = q;
166         n = 0;
167         if(partial){
168         Partial:
169                 nb = partial;
170                 if(nb > r->ifcall.count)
171                         nb = r->ifcall.count;
172                 memmove(r->ofcall.data, tmp, nb);
173                 if(nb!=partial)
174                         memmove(tmp, tmp+nb, partial-nb);
175                 partial -= nb;
176                 q = r->aux;
177                 if(q == nil)
178                         eq = &q;
179                 r->aux = nil;
180                 r->ofcall.count = nb;
181                 if(debug)
182                         fprint(2, "satisfy read with partial\n");
183                 respond(r, nil);
184                 return n;
185         }
186         if(q0==q1)
187                 return 0;
188         s = emalloc((q1-q0)*UTFmax+1);
189         n = winread(w, q0, q1, s);
190         s[n] = '\0';
191         t = strpbrk(s, "\n\004");
192         if(t == nil){
193                 free(s);
194                 return 0;
195         }
196         r = q;
197         eofchar = 0;
198         if(*t == '\004'){
199                 eofchar = 1;
200                 *t = '\0';
201         }else
202                 *++t = '\0';
203         nb = utfncpy((char*)r->ofcall.data, s, r->ifcall.count);
204         if(nb==0 && s<t && r->ifcall.count > 0){
205                 partial = utfncpy(tmp, s, UTFmax);
206                 assert(partial > 0);
207                 chartorune(&rune, tmp);
208                 partial = runelen(rune);
209                 free(s);
210                 n = 1;
211                 goto Partial;
212         }
213         n = utfnlen(r->ofcall.data, nb);
214         if(nb==strlen(s) && eofchar)
215                 n++;
216         r->ofcall.count = nb;
217         q = r->aux;
218         if(q == nil)
219                 eq = &q;
220         r->aux = nil;
221         if(debug)
222                 fprint(2, "read returns %lud-%lud: %.*q\n", q0, q0+n, n, r->ofcall.data);
223         respond(r, nil);
224         return n;
225 }
226
227 static int
228 _sendinput(Window *w, ulong q0, ulong *q1)
229 {
230         char buf[32];
231         int n;
232
233         n = __sendinput(w, q0, *q1);
234         if(!n || !eraseinput)
235                 return n;
236         /* erase q0 to q0+n */
237         sprint(buf, "#%lud,#%lud", q0, q0+n);
238         winsetaddr(w, buf, 0);
239         write(w->data, buf, 0);
240         *q1 -= n;
241         return 0;
242 }
243
244 int
245 sendinput(Window *w, ulong q0, ulong *q1)
246 {
247         ulong n;
248         Req *oq;
249
250         n = 0;
251         do {
252                 oq = q;
253                 n += _sendinput(w, q0+n, q1);
254         } while(q != oq);
255         return n;
256 }
257
258 Event esendinput;
259 void
260 fsloop(void*)
261 {
262         Fsevent e;
263         Req **l, *r;
264
265         eq = &q;
266         memset(&esendinput, 0, sizeof esendinput);
267         esendinput.c1 = 'C';
268         for(;;){
269                 while(recv(fschan, &e) == -1)
270                         ;
271                 r = e.r;
272                 switch(e.type){
273                 case 'r':
274                         *eq = r;
275                         r->aux = nil;
276                         eq = &r->aux;
277                         /* call sendinput with hostpt and endpt */
278                         sendp(win->cevent, &esendinput);
279                         break;
280                 case 'f':
281                         for(l=&q; *l; l=&(*l)->aux){
282                                 if(*l == r->oldreq){
283                                         *l = (*l)->aux;
284                                         if(*l == nil)
285                                                 eq = l;
286                                         respond(r->oldreq, "interrupted");
287                                         break;
288                                 }
289                         }
290                         respond(r, nil);
291                         break;
292                 }
293         }
294 }       
295
296 void
297 sendit(char *s)
298 {
299 //      char tmp[32];
300
301         write(win->body, s, strlen(s));
302 /*
303  * RSC: The problem here is that other procs can call sendit,
304  * so we lose our single-threadedness if we call sendinput.
305  * In fact, we don't even have the right queue memory,
306  * I think that we'll get a write event from the body write above,
307  * and we can do the sendinput then, from our single thread.
308  *
309  * I still need to figure out how to test this assertion for
310  * programs that use /srv/win*
311  *
312         winselect(win, "$", 0);
313         seek(win->addr, 0UL, 0);
314         if(read(win->addr, tmp, 2*12) == 2*12)
315                 hostpt += sendinput(win, hostpt, atol(tmp), );
316  */
317 }
318
319 void
320 execevent(Window *w, Event *e, int (*command)(Window*, char*))
321 {
322         Event *ea, *e2;
323         int n, na, len, needfree;
324         char *s, *t;
325
326         ea = nil;
327         e2 = nil;
328         if(e->flag & 2)
329                 e2 = recvp(w->cevent);
330         if(e->flag & 8){
331                 ea = recvp(w->cevent);
332                 na = ea->nb;
333                 recvp(w->cevent);
334         }else
335                 na = 0;
336
337         needfree = 0;
338         s = e->b;
339         if(e->nb==0 && (e->flag&2)){
340                 s = e2->b;
341                 e->q0 = e2->q0;
342                 e->q1 = e2->q1;
343                 e->nb = e2->nb;
344         }
345         if(e->nb==0 && e->q0<e->q1){
346                 /* fetch data from window */
347                 s = emalloc((e->q1-e->q0)*UTFmax+2);
348                 n = winread(w, e->q0, e->q1, s);
349                 s[n] = '\0';
350                 needfree = 1;
351         }else 
352         if(na){
353                 t = emalloc(strlen(s)+1+na+2);
354                 sprint(t, "%s %s", s, ea->b);
355                 if(needfree)
356                         free(s);
357                 s = t;
358                 needfree = 1;
359         }
360
361         /* if it's a known command, do it */
362         /* if it's a long message, it can't be for us anyway */
363         if(!command(w, s) && s[0]!='\0'){       /* send it as typed text */
364                 /* if it's a built-in from the tag, send it back */
365                 if(e->flag & 1)
366                         fprint(w->event, "%c%c%d %d\n", e->c1, e->c2, e->q0, e->q1);
367                 else{   /* send text to main window */
368                         len = strlen(s);
369                         if(len>0 && s[len-1]!='\n' && s[len-1]!='\004'){
370                                 if(!needfree){
371                                         /* if(needfree), we left room for a newline before */
372                                         t = emalloc(len+2);
373                                         strcpy(t, s);
374                                         s = t;
375                                         needfree = 1;
376                                 }
377                                 s[len++] = '\n';
378                                 s[len] = '\0';
379                         }
380                         sendit(s);
381                 }
382         }
383         if(needfree)
384                 free(s);
385 }
386
387 int
388 hasboundary(Rune *r, int nr)
389 {
390         int i;
391
392         for(i=0; i<nr; i++)
393                 if(r[i]=='\n' || r[i]=='\004')
394                         return 1;
395         return 0;
396 }
397
398 void
399 mainctl(void *v)
400 {
401         Window *w;
402         Event *e;
403         int delta, pendingS, pendingK;
404         ulong hostpt, endpt;
405         char tmp[32];
406
407         w = v;
408         proccreate(wineventproc, w, STACK);
409
410         hostpt = 0;
411         endpt = 0;
412         winsetaddr(w, "0", 0);
413         pendingS = 0;
414         pendingK = 0;
415         for(;;){
416                 if(debug)
417                         fprint(2, "input range %lud-%lud\n", hostpt, endpt);
418                 e = recvp(w->cevent);
419                 if(debug)
420                         fprint(2, "msg: %C %C %d %d %d %d %q\n",
421                                 e->c1 ? e->c1 : ' ', e->c2 ? e->c2 : ' ', e->q0, e->q1, e->flag, e->nb, e->b);
422                 switch(e->c1){
423                 default:
424                 Unknown:
425                         fprint(2, "unknown message %c%c\n", e->c1, e->c2);
426                         break;
427
428                 case 'C':       /* input needed for /dev/cons */
429                         if(pendingS)
430                                 pendingK = 1;
431                         else
432                                 hostpt += sendinput(w, hostpt, &endpt);
433                         break;
434
435                 case 'S':       /* output to stdout */
436                         sprint(tmp, "#%lud", hostpt);
437                         winsetaddr(w, tmp, 0);
438                         write(w->data, e->b, e->nb);
439                         pendingS += e->nr;
440                         break;
441         
442                 case 'E':       /* write to tag or body; body happens due to sendit */
443                         delta = e->q1-e->q0;
444                         if(e->c2=='I'){
445                                 endpt += delta;
446                                 if(e->q0 < hostpt)
447                                         hostpt += delta;
448                                 else
449                                         hostpt += sendinput(w, hostpt, &endpt);
450                                 break;
451                         }
452                         if(!islower(e->c2))
453                                 fprint(2, "win msg: %C %C %d %d %d %d %q\n",
454                                         e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b);
455                         break;
456         
457                 case 'F':       /* generated by our actions (specifically case 'S' above) */
458                         delta = e->q1-e->q0;
459                         if(e->c2=='D'){
460                                 /* we know about the delete by _sendinput */
461                                 break;
462                         }
463                         if(e->c2=='I'){
464                                 pendingS -= e->q1 - e->q0;
465                                 if(pendingS < 0)
466                                         fprint(2, "win: pendingS = %d\n", pendingS);
467                                 if(e->q0 != hostpt)
468                                         fprint(2, "win: insert at %d expected %lud\n", e->q0, hostpt);
469                                 endpt += delta;
470                                 hostpt += delta;
471                                 sendp(writechan, nil);
472                                 if(pendingS == 0 && pendingK){
473                                         pendingK = 0;
474                                         hostpt += sendinput(w, hostpt, &endpt);
475                                 }
476                                 break;
477                         }
478                         if(!islower(e->c2))
479                                 fprint(2, "win msg: %C %C %d %d %d %d %q\n",
480                                         e->c1, e->c2, e->q0, e->q1, e->flag, e->nb, e->b);
481                         break;
482
483                 case 'K':
484                         delta = e->q1-e->q0;
485                         switch(e->c2){
486                         case 'D':
487                                 endpt -= delta;
488                                 if(e->q1 < hostpt)
489                                         hostpt -= delta;
490                                 else if(e->q0 < hostpt)
491                                         hostpt = e->q0;
492                                 break;
493                         case 'I':
494                                 delta = e->q1 - e->q0;
495                                 endpt += delta;
496                                 if(endpt < e->q1)       /* just in case */
497                                         endpt = e->q1;
498                                 if(e->q0 < hostpt)
499                                         hostpt += delta;
500                                 if(e->nr>0 && e->r[e->nr-1]==0x7F){
501                                         write(notepg, "interrupt", 9);
502                                         hostpt = endpt;
503                                         break;
504                                 }
505                                 if(e->q0 >= hostpt
506                                 && hasboundary(e->r, e->nr)){
507                                         /*
508                                          * If we are between the S message (which
509                                          * we processed by inserting text in the
510                                          * window) and the F message notifying us
511                                          * that the text has been inserted, then our
512                                          * impression of the hostpt and acme's
513                                          * may be different.  This could be seen if you
514                                          * hit enter a bunch of times in a con
515                                          * session.  To work around the unreliability,
516                                          * only send input if we don't have an S pending.
517                                          * The same race occurs between when a character
518                                          * is typed and when we get notice of it, but
519                                          * since characters tend to be typed at the end
520                                          * of the buffer, we don't run into it.  There's
521                                          * no workaround possible for this typing race,
522                                          * since we can't tell when the user has typed
523                                          * something but we just haven't been notified.
524                                          */
525                                         if(pendingS)
526                                                 pendingK = 1;
527                                         else
528                                                 hostpt += sendinput(w, hostpt, &endpt);
529                                 }
530                                 break;
531                         }
532                         break;
533         
534                 case 'M':       /* mouse */
535                         delta = e->q1-e->q0;
536                         switch(e->c2){
537                         case 'x':
538                         case 'X':
539                                 execevent(w, e, command);
540                                 break;
541         
542                         case 'l':       /* reflect all searches back to acme */
543                         case 'L':
544                                 if(e->flag & 2)
545                                         recvp(w->cevent);
546                                 winwriteevent(w, e);
547                                 break;
548         
549                         case 'I':
550                                 endpt += delta;
551                                 if(e->q0 < hostpt)
552                                         hostpt += delta;
553                                 else
554                                         hostpt += sendinput(w, hostpt, &endpt);
555                                 break;
556
557                         case 'D':
558                                 endpt -= delta;
559                                 if(e->q1 < hostpt)
560                                         hostpt -= delta;
561                                 else if(e->q0 < hostpt)
562                                         hostpt = e->q0;
563                                 break;
564                         case 'd':       /* modify away; we don't care */
565                         case 'i':
566                                 break;
567         
568                         default:
569                                 goto Unknown;
570                         }
571                 }
572         }
573 }
574
575 enum
576 {
577         NARGS           = 100,
578         NARGCHAR        = 8*1024,
579         EXECSTACK       = STACK+(NARGS+1)*sizeof(char*)+NARGCHAR
580 };
581
582 struct Exec
583 {
584         char            **argv;
585         Channel *cpid;
586 };
587
588 int
589 lookinbin(char *s)
590 {
591         if(s[0] == '/')
592                 return 0;
593         if(s[0]=='.' && s[1]=='/')
594                 return 0;
595         if(s[0]=='.' && s[1]=='.' && s[2]=='/')
596                 return 0;
597         return 1;
598 }
599
600 /* adapted from mail.  not entirely free of details from that environment */
601 void
602 execproc(void *v)
603 {
604         struct Exec *e;
605         char *cmd, **av;
606         Channel *cpid;
607
608         e = v;
609         rfork(RFCFDG|RFNOTEG);
610         av = e->argv;
611         close(0);
612         open("/dev/cons", OREAD);
613         close(1);
614         open("/dev/cons", OWRITE);
615         dup(1, 2);
616         cpid = e->cpid;
617         free(e);
618         procexec(cpid, av[0], av);
619         if(lookinbin(av[0])){
620                 cmd = estrstrdup("/bin/", av[0]);
621                 procexec(cpid, cmd, av);
622         }
623         error("can't exec %s: %r", av[0]);
624 }
625
626 void
627 startcmd(char *argv[], int *notepg)
628 {
629         struct Exec *e;
630         Channel *cpid;
631         char buf[64];
632         int pid;
633
634         e = emalloc(sizeof(struct Exec));
635         e->argv = argv;
636         cpid = chancreate(sizeof(ulong), 0);
637         e->cpid = cpid;
638         sprint(buf, "/mnt/wsys/%d", win->id);
639         bind(buf, "/dev/acme", MREPL);
640         proccreate(execproc, e, EXECSTACK);
641         do
642                 pid = recvul(cpid);
643         while(pid == -1);
644         sprint(buf, "/proc/%d/notepg", pid);
645         *notepg = open(buf, OWRITE);
646 }