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