tacme.c - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tacme.c (24330B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <draw.h>
            4 #include <thread.h>
            5 #include <cursor.h>
            6 #include <mouse.h>
            7 #include <keyboard.h>
            8 #include <frame.h>
            9 #include <fcall.h>
           10 #include <plumb.h>
           11 #include <libsec.h>
           12 #include "dat.h"
           13 #include "fns.h"
           14         /* for generating syms in mkfile only: */
           15         #include <bio.h>
           16         #include "edit.h"
           17 
           18 void        mousethread(void*);
           19 void        keyboardthread(void*);
           20 void        waitthread(void*);
           21 void        xfidallocthread(void*);
           22 void        newwindowthread(void*);
           23 void        plumbproc(void*);
           24 int        timefmt(Fmt*);
           25 
           26 Reffont        **fontcache;
           27 int                nfontcache;
           28 char                wdir[512] = ".";
           29 Reffont        *reffonts[2];
           30 int                snarffd = -1;
           31 int                mainpid;
           32 int                swapscrollbuttons = FALSE;
           33 char                *mtpt;
           34 
           35 enum{
           36         NSnarf = 1000        /* less than 1024, I/O buffer size */
           37 };
           38 Rune        snarfrune[NSnarf+1];
           39 
           40 char                *fontnames[2] =
           41 {
           42         "/lib/font/bit/lucsans/euro.8.font",
           43         "/lib/font/bit/lucm/unicode.9.font"
           44 };
           45 
           46 Command *command;
           47 
           48 void        shutdownthread(void*);
           49 void        acmeerrorinit(void);
           50 void        readfile(Column*, char*);
           51 static int        shutdown(void*, char*);
           52 
           53 void
           54 derror(Display *d, char *errorstr)
           55 {
           56         USED(d);
           57         error(errorstr);
           58 }
           59 
           60 void
           61 threadmain(int argc, char *argv[])
           62 {
           63         int i;
           64         char *p, *loadfile;
           65         Column *c;
           66         int ncol;
           67         Display *d;
           68 
           69         rfork(RFENVG|RFNAMEG);
           70 
           71         ncol = -1;
           72 
           73         loadfile = nil;
           74         ARGBEGIN{
           75         case 'D':
           76                 {extern int _threaddebuglevel;
           77                 _threaddebuglevel = ~0;
           78                 }
           79                 break;
           80         case 'a':
           81                 globalautoindent = TRUE;
           82                 break;
           83         case 'b':
           84                 bartflag = TRUE;
           85                 break;
           86         case 'c':
           87                 p = ARGF();
           88                 if(p == nil)
           89                         goto Usage;
           90                 ncol = atoi(p);
           91                 if(ncol <= 0)
           92                         goto Usage;
           93                 break;
           94         case 'f':
           95                 fontnames[0] = ARGF();
           96                 if(fontnames[0] == nil)
           97                         goto Usage;
           98                 break;
           99         case 'F':
          100                 fontnames[1] = ARGF();
          101                 if(fontnames[1] == nil)
          102                         goto Usage;
          103                 break;
          104         case 'l':
          105                 loadfile = ARGF();
          106                 if(loadfile == nil)
          107                         goto Usage;
          108                 break;
          109         case 'm':
          110                 mtpt = ARGF();
          111                 if(mtpt == nil)
          112                         goto Usage;
          113                 break;
          114         case 'r':
          115                 swapscrollbuttons = TRUE;
          116                 break;
          117         case 'W':
          118                 winsize = ARGF();
          119                 if(winsize == nil)
          120                         goto Usage;
          121                 break;
          122         default:
          123         Usage:
          124                 fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile -W winsize\n");
          125                 threadexitsall("usage");
          126         }ARGEND
          127 
          128         fontnames[0] = estrdup(fontnames[0]);
          129         fontnames[1] = estrdup(fontnames[1]);
          130 
          131         quotefmtinstall();
          132         fmtinstall('t', timefmt);
          133 
          134         cputype = getenv("cputype");
          135         objtype = getenv("objtype");
          136         home = getenv("HOME");
          137         acmeshell = getenv("acmeshell");
          138         if(acmeshell && *acmeshell == '\0')
          139                 acmeshell = nil;
          140         p = getenv("tabstop");
          141         if(p != nil){
          142                 maxtab = strtoul(p, nil, 0);
          143                 free(p);
          144         }
          145         if(maxtab == 0)
          146                 maxtab = 4;
          147         if(loadfile)
          148                 rowloadfonts(loadfile);
          149         putenv("font", fontnames[0]);
          150         snarffd = open("/dev/snarf", OREAD|OCEXEC);
          151 /*
          152         if(cputype){
          153                 sprint(buf, "/acme/bin/%s", cputype);
          154                 bind(buf, "/bin", MBEFORE);
          155         }
          156         bind("/acme/bin", "/bin", MBEFORE);
          157 */
          158         getwd(wdir, sizeof wdir);
          159 
          160 /*
          161         if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
          162                 fprint(2, "acme: can't open display: %r\n");
          163                 threadexitsall("geninitdraw");
          164         }
          165 */
          166         if(initdraw(derror, fontnames[0], "acme") < 0){
          167                 fprint(2, "acme: can't open display: %r\n");
          168                 threadexitsall("initdraw");
          169         }
          170 
          171         d = display;
          172         font = d->defaultfont;
          173 /*assert(font); */
          174 
          175         reffont.f = font;
          176         reffonts[0] = &reffont;
          177         incref(&reffont.ref);        /* one to hold up 'font' variable */
          178         incref(&reffont.ref);        /* one to hold up reffonts[0] */
          179         fontcache = emalloc(sizeof(Reffont*));
          180         nfontcache = 1;
          181         fontcache[0] = &reffont;
          182 
          183         iconinit();
          184         timerinit();
          185         rxinit();
          186 
          187         cwait = threadwaitchan();
          188         ccommand = chancreate(sizeof(Command**), 0);
          189         ckill = chancreate(sizeof(Rune*), 0);
          190         cxfidalloc = chancreate(sizeof(Xfid*), 0);
          191         cxfidfree = chancreate(sizeof(Xfid*), 0);
          192         cnewwindow = chancreate(sizeof(Channel*), 0);
          193         cerr = chancreate(sizeof(char*), 0);
          194         cedit = chancreate(sizeof(int), 0);
          195         cexit = chancreate(sizeof(int), 0);
          196         cwarn = chancreate(sizeof(void*), 1);
          197         if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
          198                 fprint(2, "acme: can't create initial channels: %r\n");
          199                 threadexitsall("channels");
          200         }
          201         chansetname(ccommand, "ccommand");
          202         chansetname(ckill, "ckill");
          203         chansetname(cxfidalloc, "cxfidalloc");
          204         chansetname(cxfidfree, "cxfidfree");
          205         chansetname(cnewwindow, "cnewwindow");
          206         chansetname(cerr, "cerr");
          207         chansetname(cedit, "cedit");
          208         chansetname(cexit, "cexit");
          209         chansetname(cwarn, "cwarn");
          210 
          211         mousectl = initmouse(nil, screen);
          212         if(mousectl == nil){
          213                 fprint(2, "acme: can't initialize mouse: %r\n");
          214                 threadexitsall("mouse");
          215         }
          216         mouse = &mousectl->m;
          217         keyboardctl = initkeyboard(nil);
          218         if(keyboardctl == nil){
          219                 fprint(2, "acme: can't initialize keyboard: %r\n");
          220                 threadexitsall("keyboard");
          221         }
          222         mainpid = getpid();
          223         startplumbing();
          224 /*
          225         plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
          226         if(plumbeditfd < 0)
          227                 fprint(2, "acme: can't initialize plumber: %r\n");
          228         else{
          229                 cplumb = chancreate(sizeof(Plumbmsg*), 0);
          230                 threadcreate(plumbproc, nil, STACK);
          231         }
          232         plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
          233 */
          234 
          235         fsysinit();
          236 
          237         #define        WPERCOL        8
          238         disk = diskinit();
          239         if(!loadfile || !rowload(&row, loadfile, TRUE)){
          240                 rowinit(&row, screen->clipr);
          241                 if(ncol < 0){
          242                         if(argc == 0)
          243                                 ncol = 2;
          244                         else{
          245                                 ncol = (argc+(WPERCOL-1))/WPERCOL;
          246                                 if(ncol < 2)
          247                                         ncol = 2;
          248                         }
          249                 }
          250                 if(ncol == 0)
          251                         ncol = 2;
          252                 for(i=0; i<ncol; i++){
          253                         c = rowadd(&row, nil, -1);
          254                         if(c==nil && i==0)
          255                                 error("initializing columns");
          256                 }
          257                 c = row.col[row.ncol-1];
          258                 if(argc == 0)
          259                         readfile(c, wdir);
          260                 else
          261                         for(i=0; i<argc; i++){
          262                                 p = utfrrune(argv[i], '/');
          263                                 if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
          264                                         readfile(c, argv[i]);
          265                                 else
          266                                         readfile(row.col[i/WPERCOL], argv[i]);
          267                         }
          268         }
          269         flushimage(display, 1);
          270 
          271         acmeerrorinit();
          272         threadcreate(keyboardthread, nil, STACK);
          273         threadcreate(mousethread, nil, STACK);
          274         threadcreate(waitthread, nil, STACK);
          275         threadcreate(xfidallocthread, nil, STACK);
          276         threadcreate(newwindowthread, nil, STACK);
          277 /*        threadcreate(shutdownthread, nil, STACK); */
          278         threadnotify(shutdown, 1);
          279         recvul(cexit);
          280         killprocs();
          281         threadexitsall(nil);
          282 }
          283 
          284 void
          285 readfile(Column *c, char *s)
          286 {
          287         Window *w;
          288         Rune rb[256];
          289         int nr;
          290         Runestr rs;
          291 
          292         w = coladd(c, nil, nil, -1);
          293         if(s[0] != '/')
          294                 runesnprint(rb, sizeof rb, "%s/%s", wdir, s);
          295         else
          296                 runesnprint(rb, sizeof rb, "%s", s);
          297         nr = runestrlen(rb);
          298         rs = cleanrname(runestr(rb, nr));
          299         winsetname(w, rs.r, rs.nr);
          300         textload(&w->body, 0, s, 1);
          301         w->body.file->mod = FALSE;
          302         w->dirty = FALSE;
          303         winsettag(w);
          304         winresize(w, w->r, FALSE, TRUE);
          305         textscrdraw(&w->body);
          306         textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
          307         xfidlog(w, "new");
          308 }
          309 
          310 char *ignotes[] = {
          311         "sys: write on closed pipe",
          312         "sys: ttin",
          313         "sys: ttou",
          314         "sys: tstp",
          315         nil
          316 };
          317 
          318 char *oknotes[] ={
          319         "delete",
          320         "hangup",
          321         "kill",
          322         "exit",
          323         nil
          324 };
          325 
          326 int        dumping;
          327 
          328 static int
          329 shutdown(void *v, char *msg)
          330 {
          331         int i;
          332 
          333         USED(v);
          334 
          335         for(i=0; ignotes[i]; i++)
          336                 if(strncmp(ignotes[i], msg, strlen(ignotes[i])) == 0)
          337                         return 1;
          338 
          339         killprocs();
          340         if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
          341                 dumping = TRUE;
          342                 rowdump(&row, nil);
          343         }
          344         for(i=0; oknotes[i]; i++)
          345                 if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
          346                         threadexitsall(msg);
          347         print("acme: %s\n", msg);
          348         return 0;
          349 }
          350 
          351 /*
          352 void
          353 shutdownthread(void *v)
          354 {
          355         char *msg;
          356         Channel *c;
          357 
          358         USED(v);
          359 
          360         threadsetname("shutdown");
          361         c = threadnotechan();
          362         while((msg = recvp(c)) != nil)
          363                 shutdown(nil, msg);
          364 }
          365 */
          366 
          367 void
          368 killprocs(void)
          369 {
          370         Command *c;
          371 
          372         fsysclose();
          373 /*        if(display) */
          374 /*                flushimage(display, 1); */
          375 
          376         for(c=command; c; c=c->next)
          377                 postnote(PNGROUP, c->pid, "hangup");
          378 }
          379 
          380 static int errorfd;
          381 int erroutfd;
          382 
          383 void
          384 acmeerrorproc(void *v)
          385 {
          386         char *buf;
          387         int n;
          388 
          389         USED(v);
          390         threadsetname("acmeerrorproc");
          391         buf = emalloc(8192+1);
          392         while((n=read(errorfd, buf, 8192)) >= 0){
          393                 buf[n] = '\0';
          394                 sendp(cerr, estrdup(buf));
          395         }
          396         free(buf);
          397 }
          398 
          399 void
          400 acmeerrorinit(void)
          401 {
          402         int pfd[2];
          403 
          404         if(pipe(pfd) < 0)
          405                 error("can't create pipe");
          406 #if 0
          407         sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
          408         fd = create(acmeerrorfile, OWRITE, 0666);
          409         if(fd < 0){
          410                 remove(acmeerrorfile);
          411                   fd = create(acmeerrorfile, OWRITE, 0666);
          412                 if(fd < 0)
          413                         error("can't create acmeerror file");
          414         }
          415         sprint(buf, "%d", pfd[0]);
          416         write(fd, buf, strlen(buf));
          417         close(fd);
          418         /* reopen pfd[1] close on exec */
          419         sprint(buf, "/fd/%d", pfd[1]);
          420         errorfd = open(buf, OREAD|OCEXEC);
          421 #endif
          422         fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
          423         fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
          424         erroutfd = pfd[0];
          425         errorfd = pfd[1];
          426         if(errorfd < 0)
          427                 error("can't re-open acmeerror file");
          428         proccreate(acmeerrorproc, nil, STACK);
          429 }
          430 
          431 /*
          432 void
          433 plumbproc(void *v)
          434 {
          435         Plumbmsg *m;
          436 
          437         USED(v);
          438         threadsetname("plumbproc");
          439         for(;;){
          440                 m = threadplumbrecv(plumbeditfd);
          441                 if(m == nil)
          442                         threadexits(nil);
          443                 sendp(cplumb, m);
          444         }
          445 }
          446 */
          447 
          448 void
          449 keyboardthread(void *v)
          450 {
          451         Rune r;
          452         Timer *timer;
          453         Text *t;
          454         enum { KTimer, KKey, NKALT };
          455         static Alt alts[NKALT+1];
          456 
          457         USED(v);
          458         alts[KTimer].c = nil;
          459         alts[KTimer].v = nil;
          460         alts[KTimer].op = CHANNOP;
          461         alts[KKey].c = keyboardctl->c;
          462         alts[KKey].v = &r;
          463         alts[KKey].op = CHANRCV;
          464         alts[NKALT].op = CHANEND;
          465 
          466         timer = nil;
          467         typetext = nil;
          468         threadsetname("keyboardthread");
          469         for(;;){
          470                 switch(alt(alts)){
          471                 case KTimer:
          472                         timerstop(timer);
          473                         t = typetext;
          474                         if(t!=nil && t->what==Tag){
          475                                 winlock(t->w, 'K');
          476                                 wincommit(t->w, t);
          477                                 winunlock(t->w);
          478                                 flushimage(display, 1);
          479                         }
          480                         alts[KTimer].c = nil;
          481                         alts[KTimer].op = CHANNOP;
          482                         break;
          483                 case KKey:
          484                 casekeyboard:
          485                         typetext = rowtype(&row, r, mouse->xy);
          486                         t = typetext;
          487                         if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright))        /* scrolling doesn't change activecol */
          488                                 activecol = t->col;
          489                         if(t!=nil && t->w!=nil)
          490                                 t->w->body.file->curtext = &t->w->body;
          491                         if(timer != nil)
          492                                 timercancel(timer);
          493                         if(t!=nil && t->what==Tag) {
          494                                 timer = timerstart(500);
          495                                 alts[KTimer].c = timer->c;
          496                                 alts[KTimer].op = CHANRCV;
          497                         }else{
          498                                 timer = nil;
          499                                 alts[KTimer].c = nil;
          500                                 alts[KTimer].op = CHANNOP;
          501                         }
          502                         if(nbrecv(keyboardctl->c, &r) > 0)
          503                                 goto casekeyboard;
          504                         flushimage(display, 1);
          505                         break;
          506                 }
          507         }
          508 }
          509 
          510 void
          511 mousethread(void *v)
          512 {
          513         Text *t, *argt;
          514         int but;
          515         uint q0, q1;
          516         Window *w;
          517         Plumbmsg *pm;
          518         Mouse m;
          519         char *act;
          520         enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
          521         static Alt alts[NMALT+1];
          522 
          523         USED(v);
          524         threadsetname("mousethread");
          525         alts[MResize].c = mousectl->resizec;
          526         alts[MResize].v = nil;
          527         alts[MResize].op = CHANRCV;
          528         alts[MMouse].c = mousectl->c;
          529         alts[MMouse].v = &mousectl->m;
          530         alts[MMouse].op = CHANRCV;
          531         alts[MPlumb].c = cplumb;
          532         alts[MPlumb].v = &pm;
          533         alts[MPlumb].op = CHANRCV;
          534         alts[MWarnings].c = cwarn;
          535         alts[MWarnings].v = nil;
          536         alts[MWarnings].op = CHANRCV;
          537         if(cplumb == nil)
          538                 alts[MPlumb].op = CHANNOP;
          539         alts[NMALT].op = CHANEND;
          540 
          541         for(;;){
          542                 qlock(&row.lk);
          543                 flushwarnings();
          544                 qunlock(&row.lk);
          545                 flushimage(display, 1);
          546                 switch(alt(alts)){
          547                 case MResize:
          548                         if(getwindow(display, Refnone) < 0)
          549                                 error("attach to window");
          550                         draw(screen, screen->r, display->white, nil, ZP);
          551                         iconinit();
          552                         scrlresize();
          553                         rowresize(&row, screen->clipr);
          554                         break;
          555                 case MPlumb:
          556                         if(strcmp(pm->type, "text") == 0){
          557                                 act = plumblookup(pm->attr, "action");
          558                                 if(act==nil || strcmp(act, "showfile")==0)
          559                                         plumblook(pm);
          560                                 else if(strcmp(act, "showdata")==0)
          561                                         plumbshow(pm);
          562                         }
          563                         plumbfree(pm);
          564                         break;
          565                 case MWarnings:
          566                         break;
          567                 case MMouse:
          568                         /*
          569                          * Make a copy so decisions are consistent; mousectl changes
          570                          * underfoot.  Can't just receive into m because this introduces
          571                          * another race; see /sys/src/libdraw/mouse.c.
          572                          */
          573                         m = mousectl->m;
          574                         qlock(&row.lk);
          575                         t = rowwhich(&row, m.xy);
          576 
          577                         if((t!=mousetext && t!=nil && t->w!=nil) &&
          578                                 (mousetext==nil || mousetext->w==nil || t->w->id!=mousetext->w->id)) {
          579                                 xfidlog(t->w, "focus");
          580                         }
          581 
          582                         if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
          583                                 winlock(mousetext->w, 'M');
          584                                 mousetext->eq0 = ~0;
          585                                 wincommit(mousetext->w, mousetext);
          586                                 winunlock(mousetext->w);
          587                         }
          588                         mousetext = t;
          589                         if(t == nil)
          590                                 goto Continue;
          591                         w = t->w;
          592                         if(t==nil || m.buttons==0)
          593                                 goto Continue;
          594                         but = 0;
          595                         if(m.buttons == 1)
          596                                 but = 1;
          597                         else if(m.buttons == 2)
          598                                 but = 2;
          599                         else if(m.buttons == 4)
          600                                 but = 3;
          601                         barttext = t;
          602                         if(t->what==Body && ptinrect(m.xy, t->scrollr)){
          603                                 if(but){
          604                                         if(swapscrollbuttons){
          605                                                 if(but == 1)
          606                                                         but = 3;
          607                                                 else if(but == 3)
          608                                                         but = 1;
          609                                         }
          610                                         winlock(w, 'M');
          611                                         t->eq0 = ~0;
          612                                         textscroll(t, but);
          613                                         winunlock(w);
          614                                 }
          615                                 goto Continue;
          616                         }
          617                         /* scroll buttons, wheels, etc. */
          618                         if(w != nil && (m.buttons & (8|16))){
          619                                 if(m.buttons & 8)
          620                                         but = Kscrolloneup;
          621                                 else
          622                                         but = Kscrollonedown;
          623                                 winlock(w, 'M');
          624                                 t->eq0 = ~0;
          625                                 texttype(t, but);
          626                                 winunlock(w);
          627                                 goto Continue;
          628                         }
          629                         if(ptinrect(m.xy, t->scrollr)){
          630                                 if(but){
          631                                         if(t->what == Columntag)
          632                                                 rowdragcol(&row, t->col, but);
          633                                         else if(t->what == Tag){
          634                                                 coldragwin(t->col, t->w, but);
          635                                                 if(t->w)
          636                                                         barttext = &t->w->body;
          637                                         }
          638                                         if(t->col)
          639                                                 activecol = t->col;
          640                                 }
          641                                 goto Continue;
          642                         }
          643                         if(m.buttons){
          644                                 if(w)
          645                                         winlock(w, 'M');
          646                                 t->eq0 = ~0;
          647                                 if(w)
          648                                         wincommit(w, t);
          649                                 else
          650                                         textcommit(t, TRUE);
          651                                 if(m.buttons & 1){
          652                                         textselect(t);
          653                                         if(w)
          654                                                 winsettag(w);
          655                                         argtext = t;
          656                                         seltext = t;
          657                                         if(t->col)
          658                                                 activecol = t->col;        /* button 1 only */
          659                                         if(t->w!=nil && t==&t->w->body)
          660                                                 activewin = t->w;
          661                                 }else if(m.buttons & 2){
          662                                         if(textselect2(t, &q0, &q1, &argt))
          663                                                 execute(t, q0, q1, FALSE, argt);
          664                                 }else if(m.buttons & 4){
          665                                         if(textselect3(t, &q0, &q1))
          666                                                 look3(t, q0, q1, FALSE);
          667                                 }
          668                                 if(w)
          669                                         winunlock(w);
          670                                 goto Continue;
          671                         }
          672     Continue:
          673                         qunlock(&row.lk);
          674                         break;
          675                 }
          676         }
          677 }
          678 
          679 /*
          680  * There is a race between process exiting and our finding out it was ever created.
          681  * This structure keeps a list of processes that have exited we haven't heard of.
          682  */
          683 typedef struct Pid Pid;
          684 struct Pid
          685 {
          686         int        pid;
          687         char        msg[ERRMAX];
          688         Pid        *next;
          689 };
          690 
          691 void
          692 waitthread(void *v)
          693 {
          694         Waitmsg *w;
          695         Command *c, *lc;
          696         uint pid;
          697         int found, ncmd;
          698         Rune *cmd;
          699         char *err;
          700         Text *t;
          701         Pid *pids, *p, *lastp;
          702         enum { WErr, WKill, WWait, WCmd, NWALT };
          703         Alt alts[NWALT+1];
          704 
          705         USED(v);
          706         threadsetname("waitthread");
          707         pids = nil;
          708         alts[WErr].c = cerr;
          709         alts[WErr].v = &err;
          710         alts[WErr].op = CHANRCV;
          711         alts[WKill].c = ckill;
          712         alts[WKill].v = &cmd;
          713         alts[WKill].op = CHANRCV;
          714         alts[WWait].c = cwait;
          715         alts[WWait].v = &w;
          716         alts[WWait].op = CHANRCV;
          717         alts[WCmd].c = ccommand;
          718         alts[WCmd].v = &c;
          719         alts[WCmd].op = CHANRCV;
          720         alts[NWALT].op = CHANEND;
          721 
          722         command = nil;
          723         for(;;){
          724                 switch(alt(alts)){
          725                 case WErr:
          726                         qlock(&row.lk);
          727                         warning(nil, "%s", err);
          728                         free(err);
          729                         flushimage(display, 1);
          730                         qunlock(&row.lk);
          731                         break;
          732                 case WKill:
          733                         found = FALSE;
          734                         ncmd = runestrlen(cmd);
          735                         for(c=command; c; c=c->next){
          736                                 /* -1 for blank */
          737                                 if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
          738                                         if(postnote(PNGROUP, c->pid, "kill") < 0)
          739                                                 warning(nil, "kill %S: %r\n", cmd);
          740                                         found = TRUE;
          741                                 }
          742                         }
          743                         if(!found)
          744                                 warning(nil, "Kill: no process %S\n", cmd);
          745                         free(cmd);
          746                         break;
          747                 case WWait:
          748                         pid = w->pid;
          749                         lc = nil;
          750                         for(c=command; c; c=c->next){
          751                                 if(c->pid == pid){
          752                                         if(lc)
          753                                                 lc->next = c->next;
          754                                         else
          755                                                 command = c->next;
          756                                         break;
          757                                 }
          758                                 lc = c;
          759                         }
          760                         qlock(&row.lk);
          761                         t = &row.tag;
          762                         textcommit(t, TRUE);
          763                         if(c == nil){
          764                                 /* helper processes use this exit status */
          765                                 if(strncmp(w->msg, "libthread", 9) != 0){
          766                                         p = emalloc(sizeof(Pid));
          767                                         p->pid = pid;
          768                                         strncpy(p->msg, w->msg, sizeof(p->msg));
          769                                         p->next = pids;
          770                                         pids = p;
          771                                 }
          772                         }else{
          773                                 if(search(t, c->name, c->nname)){
          774                                         textdelete(t, t->q0, t->q1, TRUE);
          775                                         textsetselect(t, 0, 0);
          776                                 }
          777                                 if(w->msg[0])
          778                                         warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg);
          779                                 flushimage(display, 1);
          780                         }
          781                         qunlock(&row.lk);
          782                         free(w);
          783     Freecmd:
          784                         if(c){
          785                                 if(c->iseditcmd)
          786                                         sendul(cedit, 0);
          787                                 free(c->text);
          788                                 free(c->name);
          789                                 fsysdelid(c->md);
          790                                 free(c);
          791                         }
          792                         break;
          793                 case WCmd:
          794                         /* has this command already exited? */
          795                         lastp = nil;
          796                         for(p=pids; p!=nil; p=p->next){
          797                                 if(p->pid == c->pid){
          798                                         if(p->msg[0])
          799                                                 warning(c->md, "%s\n", p->msg);
          800                                         if(lastp == nil)
          801                                                 pids = p->next;
          802                                         else
          803                                                 lastp->next = p->next;
          804                                         free(p);
          805                                         goto Freecmd;
          806                                 }
          807                                 lastp = p;
          808                         }
          809                         c->next = command;
          810                         command = c;
          811                         qlock(&row.lk);
          812                         t = &row.tag;
          813                         textcommit(t, TRUE);
          814                         textinsert(t, 0, c->name, c->nname, TRUE);
          815                         textsetselect(t, 0, 0);
          816                         flushimage(display, 1);
          817                         qunlock(&row.lk);
          818                         break;
          819                 }
          820         }
          821 }
          822 
          823 void
          824 xfidallocthread(void *v)
          825 {
          826         Xfid *xfree, *x;
          827         enum { Alloc, Free, N };
          828         static Alt alts[N+1];
          829 
          830         USED(v);
          831         threadsetname("xfidallocthread");
          832         alts[Alloc].c = cxfidalloc;
          833         alts[Alloc].v = nil;
          834         alts[Alloc].op = CHANRCV;
          835         alts[Free].c = cxfidfree;
          836         alts[Free].v = &x;
          837         alts[Free].op = CHANRCV;
          838         alts[N].op = CHANEND;
          839 
          840         xfree = nil;
          841         for(;;){
          842                 switch(alt(alts)){
          843                 case Alloc:
          844                         x = xfree;
          845                         if(x)
          846                                 xfree = x->next;
          847                         else{
          848                                 x = emalloc(sizeof(Xfid));
          849                                 x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
          850                                 chansetname(x->c, "xc%p", x->c);
          851                                 x->arg = x;
          852                                 threadcreate(xfidctl, x->arg, STACK);
          853                         }
          854                         sendp(cxfidalloc, x);
          855                         break;
          856                 case Free:
          857                         x->next = xfree;
          858                         xfree = x;
          859                         break;
          860                 }
          861         }
          862 }
          863 
          864 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
          865 void
          866 newwindowthread(void *v)
          867 {
          868         Window *w;
          869 
          870         USED(v);
          871         threadsetname("newwindowthread");
          872 
          873         for(;;){
          874                 /* only fsysproc is talking to us, so synchronization is trivial */
          875                 recvp(cnewwindow);
          876                 w = makenewwindow(nil);
          877                 winsettag(w);
          878                 xfidlog(w, "new");
          879                 sendp(cnewwindow, w);
          880         }
          881 }
          882 
          883 Reffont*
          884 rfget(int fix, int save, int setfont, char *name)
          885 {
          886         Reffont *r;
          887         Font *f;
          888         int i;
          889 
          890         r = nil;
          891         if(name == nil){
          892                 name = fontnames[fix];
          893                 r = reffonts[fix];
          894         }
          895         if(r == nil){
          896                 for(i=0; i<nfontcache; i++)
          897                         if(strcmp(name, fontcache[i]->f->name) == 0){
          898                                 r = fontcache[i];
          899                                 goto Found;
          900                         }
          901                 f = openfont(display, name);
          902                 if(f == nil){
          903                         warning(nil, "can't open font file %s: %r\n", name);
          904                         return nil;
          905                 }
          906                 r = emalloc(sizeof(Reffont));
          907                 r->f = f;
          908                 fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
          909                 fontcache[nfontcache++] = r;
          910         }
          911     Found:
          912         if(save){
          913                 incref(&r->ref);
          914                 if(reffonts[fix])
          915                         rfclose(reffonts[fix]);
          916                 reffonts[fix] = r;
          917                 if(name != fontnames[fix]){
          918                         free(fontnames[fix]);
          919                         fontnames[fix] = estrdup(name);
          920                 }
          921         }
          922         if(setfont){
          923                 reffont.f = r->f;
          924                 incref(&r->ref);
          925                 rfclose(reffonts[0]);
          926                 font = r->f;
          927                 reffonts[0] = r;
          928                 incref(&r->ref);
          929                 iconinit();
          930         }
          931         incref(&r->ref);
          932         return r;
          933 }
          934 
          935 void
          936 rfclose(Reffont *r)
          937 {
          938         int i;
          939 
          940         if(decref(&r->ref) == 0){
          941                 for(i=0; i<nfontcache; i++)
          942                         if(r == fontcache[i])
          943                                 break;
          944                 if(i >= nfontcache)
          945                         warning(nil, "internal error: can't find font in cache\n");
          946                 else{
          947                         nfontcache--;
          948                         memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
          949                 }
          950                 freefont(r->f);
          951                 free(r);
          952         }
          953 }
          954 
          955 Cursor boxcursor = {
          956         {-7, -7},
          957         {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
          958          0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
          959          0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
          960          0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
          961         {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
          962          0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
          963          0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
          964          0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
          965 };
          966 
          967 Cursor2 boxcursor2 = {
          968         {-15, -15},
          969         {0xFF, 0xFF, 0xFF, 0xFF,
          970          0xFF, 0xFF, 0xFF, 0xFF,
          971          0xFF, 0xFF, 0xFF, 0xFF,
          972          0xFF, 0xFF, 0xFF, 0xFF,
          973          0xFF, 0xFF, 0xFF, 0xFF,
          974          0xFF, 0xFF, 0xFF, 0xFF,
          975          0xFF, 0xFF, 0xFF, 0xFF,
          976          0xFF, 0xFF, 0xFF, 0xFF,
          977          0xFF, 0xFF, 0xFF, 0xFF,
          978          0xFF, 0xFF, 0xFF, 0xFF,
          979          0xFF, 0xC0, 0x03, 0xFF,
          980          0xFF, 0xC0, 0x03, 0xFF,
          981          0xFF, 0xC0, 0x03, 0xFF,
          982          0xFF, 0xC0, 0x03, 0xFF,
          983          0xFF, 0xC0, 0x03, 0xFF,
          984          0xFF, 0xC0, 0x03, 0xFF,
          985          0xFF, 0xC0, 0x03, 0xFF,
          986          0xFF, 0xC0, 0x03, 0xFF,
          987          0xFF, 0xC0, 0x03, 0xFF,
          988          0xFF, 0xC0, 0x03, 0xFF,
          989          0xFF, 0xC0, 0x03, 0xFF,
          990          0xFF, 0xC0, 0x03, 0xFF,
          991          0xFF, 0xFF, 0xFF, 0xFF,
          992          0xFF, 0xFF, 0xFF, 0xFF,
          993          0xFF, 0xFF, 0xFF, 0xFF,
          994          0xFF, 0xFF, 0xFF, 0xFF,
          995          0xFF, 0xFF, 0xFF, 0xFF,
          996          0xFF, 0xFF, 0xFF, 0xFF,
          997          0xFF, 0xFF, 0xFF, 0xFF,
          998          0xFF, 0xFF, 0xFF, 0xFF,
          999          0xFF, 0xFF, 0xFF, 0xFF,
         1000          0xFF, 0xFF, 0xFF, 0xFF},
         1001         {0x00, 0x00, 0x00, 0x00,
         1002          0x00, 0x00, 0x00, 0x00,
         1003          0x3F, 0xFF, 0xFF, 0xFC,
         1004          0x3F, 0xFF, 0xFF, 0xFC,
         1005          0x3F, 0xFF, 0xFF, 0xFC,
         1006          0x3F, 0xFF, 0xFF, 0xFC,
         1007          0x3F, 0xFF, 0xFF, 0xFC,
         1008          0x3F, 0xFF, 0xFF, 0xFC,
         1009          0x3F, 0x00, 0x00, 0xFC,
         1010          0x3F, 0x00, 0x00, 0xFC,
         1011          0x3F, 0x00, 0x00, 0xFC,
         1012          0x3F, 0x00, 0x00, 0xFC,
         1013          0x3F, 0x00, 0x00, 0xFC,
         1014          0x3F, 0x00, 0x00, 0xFC,
         1015          0x3F, 0x00, 0x00, 0xFC,
         1016          0x3F, 0x00, 0x00, 0xFC,
         1017          0x3F, 0x00, 0x00, 0xFC,
         1018          0x3F, 0x00, 0x00, 0xFC,
         1019          0x3F, 0x00, 0x00, 0xFC,
         1020          0x3F, 0x00, 0x00, 0xFC,
         1021          0x3F, 0x00, 0x00, 0xFC,
         1022          0x3F, 0x00, 0x00, 0xFC,
         1023          0x3F, 0x00, 0x00, 0xFC,
         1024          0x3F, 0x00, 0x00, 0xFC,
         1025          0x3F, 0xFF, 0xFF, 0xFC,
         1026          0x3F, 0xFF, 0xFF, 0xFC,
         1027          0x3F, 0xFF, 0xFF, 0xFC,
         1028          0x3F, 0xFF, 0xFF, 0xFC,
         1029          0x3F, 0xFF, 0xFF, 0xFC,
         1030          0x3F, 0xFF, 0xFF, 0xFC,
         1031          0x00, 0x00, 0x00, 0x00,
         1032          0x00, 0x00, 0x00, 0x00}
         1033 };
         1034 
         1035 void
         1036 iconinit(void)
         1037 {
         1038         Rectangle r;
         1039         Image *tmp;
         1040 
         1041         if(tagcols[BACK] == nil) {
         1042                 /* Blue */
         1043                 tagcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
         1044                 tagcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
         1045                 tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
         1046                 tagcols[TEXT] = display->black;
         1047                 tagcols[HTEXT] = display->black;
         1048 
         1049                 /* Yellow */
         1050                 textcols[BACK] = allocimagemix(display, DPaleyellow, DWhite);
         1051                 textcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
         1052                 textcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
         1053                 textcols[TEXT] = display->black;
         1054                 textcols[HTEXT] = display->black;
         1055         }
         1056 
         1057         r = Rect(0, 0, Scrollwid+ButtonBorder, font->height+1);
         1058         if(button && eqrect(r, button->r))
         1059                 return;
         1060 
         1061         if(button){
         1062                 freeimage(button);
         1063                 freeimage(modbutton);
         1064                 freeimage(colbutton);
         1065         }
         1066 
         1067         button = allocimage(display, r, screen->chan, 0, DNofill);
         1068         draw(button, r, tagcols[BACK], nil, r.min);
         1069         r.max.x -= ButtonBorder;
         1070         border(button, r, ButtonBorder, tagcols[BORD], ZP);
         1071 
         1072         r = button->r;
         1073         modbutton = allocimage(display, r, screen->chan, 0, DNofill);
         1074         draw(modbutton, r, tagcols[BACK], nil, r.min);
         1075         r.max.x -= ButtonBorder;
         1076         border(modbutton, r, ButtonBorder, tagcols[BORD], ZP);
         1077         r = insetrect(r, ButtonBorder);
         1078         tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
         1079         draw(modbutton, r, tmp, nil, ZP);
         1080         freeimage(tmp);
         1081 
         1082         r = button->r;
         1083         colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
         1084 
         1085         but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
         1086         but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
         1087 }
         1088 
         1089 /*
         1090  * /dev/snarf updates when the file is closed, so we must open our own
         1091  * fd here rather than use snarffd
         1092  */
         1093 
         1094 /* rio truncates larges snarf buffers, so this avoids using the
         1095  * service if the string is huge */
         1096 
         1097 #define MAXSNARF 100*1024
         1098 
         1099 void
         1100 acmeputsnarf(void)
         1101 {
         1102         int i, n;
         1103         Fmt f;
         1104         char *s;
         1105 
         1106         if(snarfbuf.nc==0)
         1107                 return;
         1108         if(snarfbuf.nc > MAXSNARF)
         1109                 return;
         1110 
         1111         fmtstrinit(&f);
         1112         for(i=0; i<snarfbuf.nc; i+=n){
         1113                 n = snarfbuf.nc-i;
         1114                 if(n >= NSnarf)
         1115                         n = NSnarf;
         1116                 bufread(&snarfbuf, i, snarfrune, n);
         1117                 if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
         1118                         break;
         1119         }
         1120         s = fmtstrflush(&f);
         1121         if(s && s[0])
         1122                 putsnarf(s);
         1123         free(s);
         1124 }
         1125 
         1126 void
         1127 acmegetsnarf(void)
         1128 {
         1129         char *s;
         1130         int nb, nr, nulls, len;
         1131         Rune *r;
         1132 
         1133         s = getsnarf();
         1134         if(s == nil || s[0]==0){
         1135                 free(s);
         1136                 return;
         1137         }
         1138 
         1139         len = strlen(s);
         1140         r = runemalloc(len+1);
         1141         cvttorunes(s, len, r, &nb, &nr, &nulls);
         1142         bufreset(&snarfbuf);
         1143         bufinsert(&snarfbuf, 0, r, nr);
         1144         free(r);
         1145         free(s);
         1146 }
         1147 
         1148 int
         1149 ismtpt(char *file)
         1150 {
         1151         int n;
         1152 
         1153         if(mtpt == nil)
         1154                 return 0;
         1155 
         1156         /* This is not foolproof, but it will stop a lot of them. */
         1157         n = strlen(mtpt);
         1158         return strncmp(file, mtpt, n) == 0 && ((n > 0 && mtpt[n-1] == '/') || file[n] == '/' || file[n] == 0);
         1159 }
         1160 
         1161 int
         1162 timefmt(Fmt *f)
         1163 {
         1164         Tm *tm;
         1165 
         1166         tm = localtime(va_arg(f->args, ulong));
         1167         return fmtprint(f, "%04d/%02d/%02d %02d:%02d:%02d",
         1168                 tm->year+1900, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec);
         1169 }