main.c - sam - An updated version of the sam text editor.
 (HTM) git clone git://vernunftzentrum.de/sam.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
       main.c (23368B)
       ---
            1 
            2 /* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
            3 #include <u.h>
            4 #include <libg.h>
            5 #include <frame.h>
            6 #include <unistd.h>
            7 #include "flayer.h"
            8 #include "samterm.h"
            9 
           10 extern uint64_t _bgpixel;
           11 extern void hmoveto(int, int64_t, Flayer *);
           12 
           13 Text    cmd;
           14 wchar_t    *scratch;
           15 int64_t    nscralloc;
           16 extern Bitmap   screen;
           17 unsigned int cursor;
           18 Mouse   mouse;
           19 Flayer  *which = NULL;
           20 Flayer  *flast = NULL;
           21 Flayer  *work = NULL;
           22 int64_t    snarflen;
           23 int64_t    typestart = -1;
           24 int64_t    typeend = -1;
           25 int64_t    typeesc = -1;
           26 bool    modified = false;       /* strange lookahead for menus */
           27 char    lock = 1;
           28 bool    hasunlocked = false;
           29 bool expandtabs = false;
           30 bool autoindent = false;
           31 char *machine = "localhost";
           32 int exfd = -1;
           33 const char *exname;
           34 bool followfocus = false;
           35 
           36 void
           37 removeext(void)
           38 {
           39     if (exname)
           40         unlink(exname);
           41 }
           42 
           43 int
           44 main(int argc, char *argv[])
           45 {
           46     int i, got, scr, opt;
           47     Text *t;
           48     Rectangle r;
           49     Flayer *nwhich;
           50     char rcpath[PATH_MAX + 1] = {0};
           51     FILE *rc = NULL;
           52 
           53     setlocale(LC_ALL, "");
           54     installdefaultbindings();
           55     installdefaultchords();
           56 
           57     if (getenv("SAMRC"))
           58         strncpy(rcpath, getenv("SAMRC"), PATH_MAX);
           59     else
           60         snprintf(rcpath, PATH_MAX, "%s/.samrc", getenv("HOME") ? getenv("HOME") : ".");
           61 
           62     while ((opt = getopt(argc, argv, "ef:n:r:")) != -1){
           63         switch (opt){
           64             case 'r':
           65                 machine = optarg;
           66                 break;
           67 
           68             case 'f':
           69                 exfd = atoi(optarg);
           70                 break;
           71 
           72             case 'n':
           73                 exname = optarg;
           74                 atexit(removeext);
           75                 break;
           76         }
           77     }
           78 
           79     rc = fopen(rcpath, "r");
           80     if (rc){
           81         loadrcfile(rc);
           82         fclose(rc);
           83     }
           84 
           85     getscreen(argc, argv);
           86     initio();
           87     scratch = alloc(100*RUNESIZE);
           88     nscralloc = 100;
           89     r = screen.r;
           90     r.max.y = r.min.y+Dy(r)/5;
           91     flstart(screen.clipr);
           92     rinit(&cmd.rasp);
           93     flnew(&cmd.l[0], stgettext, 1, &cmd);
           94     cmd.l[0].bg = getbg();
           95     flinit(&cmd.l[0], r, font, cmd.l[0].bg);
           96     cmd.nwin = 1;
           97     which = &cmd.l[0];
           98     cmd.tag = Untagged;
           99     outTs(Tversion, VERSION);
          100     startnewfile(Tstartcmdfile, &cmd);
          101 
          102     got = 0;
          103     for(;;got = waitforio()){
          104         if(hasunlocked && RESHAPED())
          105             reshape();
          106         if(got&RHost)
          107             rcv();
          108         if(got&RExtern){
          109             for(i=0; cmd.l[i].textfn==0; i++)
          110                 ;
          111             current(&cmd.l[i]);
          112             flsetselect(which, cmd.rasp.nrunes, cmd.rasp.nrunes);
          113             type(which);
          114         }
          115         if(got&RKeyboard){
          116             if(which)
          117                 type(which);
          118             else
          119                 kbdblock();
          120         }
          121         if(got&RMouse){
          122             if(lock==2 || !ptinrect(mouse.xy, screen.r)){
          123                 mouseunblock();
          124                 continue;
          125             }
          126             nwhich = flwhich(mouse.xy);
          127             scr = which && ptinrect(mouse.xy, which->scroll);
          128             if(mouse.buttons)
          129                 flushtyping(true);
          130             if(mouse.buttons&1){
          131                 if(nwhich){
          132                     if(nwhich!=which)
          133                         current(nwhich);
          134                     else if(scr)
          135                         scroll(which, 1, 1);
          136                     else{
          137                         t=(Text *)which->user1;
          138                         if(flselect(which)){
          139                             outTsl(Tdclick, t->tag, which->p0);
          140                             t->lock++;
          141                         }else if(t!=&cmd)
          142                             outcmd();
          143                     }
          144                 }
          145             }else if((mouse.buttons&2) && which){
          146                 if(scr)
          147                     scroll(which, 2, 2);
          148                 else
          149                     menu2hit();
          150             }else if((mouse.buttons&4)){
          151                 if(scr)
          152                     scroll(which, 3, 3);
          153                 else
          154                     menu3hit();
          155             }else if(followfocus && nwhich && nwhich!=which){
          156                 current(nwhich);
          157             }
          158             mouseunblock();
          159         }
          160     }
          161 
          162     return EXIT_SUCCESS;
          163 }
          164 
          165 
          166 void
          167 reshape(void)
          168 {
          169     int i;
          170 
          171     flreshape(screen.clipr);
          172     for(i = 0; i<nname; i++)
          173         if(text[i])
          174             hcheck(text[i]->tag);
          175 }
          176 
          177 void
          178 current(Flayer *nw)
          179 {
          180     Text *t;
          181 
          182     if(which)
          183         flborder(which, false);
          184     if(nw){
          185         flushtyping(true);
          186         if (!followfocus)
          187             flupfront(nw);
          188         flborder(nw, true);
          189         buttons(Up);
          190         t = (Text *)nw->user1;
          191         t->front = nw-&t->l[0];
          192         if(t != &cmd)
          193             work = nw;
          194     }
          195     which = nw;
          196 }
          197 
          198 void
          199 closeup(Flayer *l)
          200 {
          201     Text *t=(Text *)l->user1;
          202     int m;
          203 
          204     m = whichmenu(t->tag);
          205     if(m < 0)
          206         return;
          207     flclose(l);
          208     if(l == which){
          209         which = 0;
          210         current(flwhich(Pt(0, 0)));
          211     }
          212     if(l == flast)
          213         flast = 0;
          214     if(l == work)
          215         work = 0;
          216     if(--t->nwin == 0){
          217         rclear(&t->rasp);
          218         free(t);
          219         text[m] = 0;
          220     }else if(l == &t->l[t->front]){
          221         for(m=0; m<NL; m++) /* find one; any one will do */
          222             if(t->l[m].textfn){
          223                 t->front = m;
          224                 return;
          225             }
          226         panic("close");
          227     }
          228 }
          229 
          230 Flayer *
          231 findl(Text *t)
          232 {
          233     int i;
          234     for(i = 0; i<NL; i++)
          235         if(t->l[i].textfn==0)
          236             return &t->l[i];
          237     return 0;
          238 }
          239 
          240 void
          241 duplicate(Flayer *l, Rectangle r, XftFont *f, int close)
          242 {
          243     Text *t=(Text *)l->user1;
          244     Flayer *nl = findl(t);
          245     wchar_t *rp;
          246     uint64_t n;
          247 
          248     if(nl){
          249         flnew(nl, stgettext, l->user0, (char *)t);
          250         flinit(nl, r, f, l->bg);
          251         nl->origin = l->origin;
          252         rp = (*l->textfn)(l, l->f.nchars, &n);
          253         flinsert(nl, rp, rp+n, l->origin);
          254         flsetselect(nl, l->p0, l->p1);
          255         if(close){
          256             flclose(l);
          257             if(l==which)
          258                 which = 0;
          259         }else
          260             t->nwin++;
          261         current(nl);
          262         hcheck(t->tag);
          263     }
          264     cursorswitch(cursor);
          265 }
          266 
          267 void
          268 buttons(int updown)
          269 {
          270     while(((mouse.buttons&7)!=0) != updown)
          271         frgetmouse();
          272 }
          273 
          274 int
          275 getr(Rectangle *rp)
          276 {
          277     Point p;
          278     Rectangle r;
          279 
          280     *rp = getrect(3, &mouse);
          281     if(rp->max.x && rp->max.x-rp->min.x<=5 && rp->max.y-rp->min.y<=5){
          282         p = rp->min;
          283         r = cmd.l[cmd.front].entire;
          284         *rp = screen.r;
          285         if(cmd.nwin==1){
          286             if (p.y <= r.min.y)
          287                 rp->max.y = r.min.y;
          288             else if (p.y >= r.max.y)
          289                 rp->min.y = r.max.y;
          290             if (p.x <= r.min.x)
          291                 rp->max.x = r.min.x;
          292             else if (p.x >= r.max.x)
          293                 rp->min.x = r.max.x;
          294         }
          295     }
          296     return rectclip(rp, screen.r) &&
          297        rp->max.x-rp->min.x>100 && rp->max.y-rp->min.y>40;
          298 }
          299 
          300 void
          301 snarf(Text *t, int w)
          302 {
          303     Flayer *l = &t->l[w];
          304 
          305     if(l->p1>l->p0){
          306         snarflen = l->p1-l->p0;
          307         outTsll(Tsnarf, t->tag, l->p0, l->p1);
          308     }
          309 }
          310 
          311 void
          312 cut(Text *t, int w, bool save, bool check)
          313 {
          314     int64_t p0, p1;
          315     Flayer *l;
          316 
          317     l = &t->l[w];
          318     p0 = l->p0;
          319     p1 = l->p1;
          320     if(p0 == p1)
          321         return;
          322     if(p0 < 0)
          323         panic("cut");
          324     if(save)
          325         snarf(t, w);
          326     outTsll(Tcut, t->tag, p0, p1);
          327     flsetselect(l, p0, p0);
          328     t->lock++;
          329     hcut(t->tag, p0, p1-p0);
          330     if(check)
          331         hcheck(t->tag);
          332 }
          333 
          334 void
          335 paste(Text *t, int w)
          336 {
          337     if(snarflen){
          338         cut(t, w, false, false);
          339         t->lock++;
          340         outTsl(Tpaste, t->tag, t->l[w].p0);
          341     }
          342 }
          343 
          344 void
          345 scrorigin(Flayer *l, int but, int64_t p0)
          346 {
          347     Text *t=(Text *)l->user1;
          348 
          349     switch(but){
          350     case 1:
          351         outTslll(Torigin, t->tag, l->origin, p0, getlayer(l, t));
          352         break;
          353     case 2:
          354         outTslll(Torigin, t->tag, p0, 1L, getlayer(l, t));
          355         break;
          356     case 3:
          357         horigin(t->tag, p0, NULL);
          358     }
          359 }
          360 
          361 int
          362 raspc(Rasp *r, int64_t p)
          363 {
          364     uint64_t n;
          365     rload(r, p, p+1, &n);
          366     if(n)
          367         return scratch[0];
          368     return 0;
          369 }
          370 
          371 int64_t
          372 ctlw(Rasp *r, int64_t o, int64_t p)
          373 {
          374     int c;
          375 
          376     if(--p < o)
          377         return o;
          378     if(raspc(r, p)=='\n')
          379         return p;
          380     for(; p>=o && !iswalnum(c=raspc(r, p)); --p)
          381         if(c=='\n')
          382             return p+1;
          383     for(; p>o && iswalnum(raspc(r, p-1)); --p)
          384         ;
          385     return p>=o? p : o;
          386 }
          387 
          388 int64_t
          389 ctlu(Rasp *r, int64_t o, int64_t p)
          390 {
          391     for(; p-1>=o && raspc(r, p-1)!='\n'; --p)
          392         ;
          393     return p>=o? p : o;
          394 }
          395 
          396 int64_t
          397 indent(Flayer *l, long p)
          398 {
          399         Text *t = (Text *)l->user1;
          400         static wchar_t sbuf[7] = {' ',' ',' ',' ',' ',' ',' '};
          401         static wchar_t tbuf[7] = {'\t','\t','\t','\t','\t','\t','\t'};
          402         int i, is, it, q, c, space;
          403 
          404         q = p - 1; is = 0; it = 0; space = true;
          405         while(--q >= l->origin) {
          406                 c = raspc(&t->rasp, q);
          407                 if(c == '\n') {
          408             break;
          409                 } else if(c == '\t') {
          410                         ++it;
          411                 } else if(c == ' ') {
          412                         ++is;
          413                 } else {
          414                         it = is = 0; 
          415                         space = false;
          416                 }
          417         }
          418     if(space) 
          419         it = is = 0;
          420 
          421         while(it != 0) {
          422                 i = it>7?7:it;
          423                 hgrow(t->tag, p, i, 0);
          424                 t->lock++;
          425                 hdatarune(t->tag, p, tbuf, i);
          426                 it -= i; p += i;
          427         }
          428         while(is != 0) {
          429                 i = is > 7? 7 : is;
          430                 hgrow(t->tag, p, i, 0);
          431                 t->lock++;
          432                 hdatarune(t->tag, p, sbuf, i);
          433                 is -= i; p += i;
          434         }
          435 
          436         return typeend = l->p0 = l->p1 = p;
          437 }
          438 
          439 int
          440 center(Flayer *l, int64_t a)
          441 {
          442     Text *t = l->user1;
          443 
          444     if (!t->lock && (a < l->origin || l->origin + l->f.nchars < a)){
          445         a = (a > t->rasp.nrunes) ? t->rasp.nrunes : a;
          446         outTslll(Torigin, t->tag, a, 2L, getlayer(l, t));
          447         return 1;
          448     }
          449 
          450     return 0;
          451 }
          452 
          453 int
          454 onethird(Flayer *l, int64_t a)
          455 {
          456     Text *t;
          457     Rectangle s;
          458     int64_t lines;
          459 
          460     t = l->user1;
          461     if(!t->lock && (a<l->origin || l->origin+l->f.nchars<a)){
          462         if(a > t->rasp.nrunes)
          463             a = t->rasp.nrunes;
          464         s = inset(l->scroll, 1);
          465         lines = ((s.max.y-s.min.y)/l->f.fheight+1)/3;
          466         if (lines < 2)
          467             lines = 2;
          468         outTslll(Torigin, t->tag, a, lines, getlayer(l, t));
          469         return 1;
          470     }
          471     return 0;
          472 }
          473 
          474 
          475 int
          476 XDisplay(Display *);
          477 
          478 extern Display * _dpy;
          479 
          480 void
          481 flushtyping(bool clearesc)
          482 {
          483     Text *t;
          484     uint64_t n;
          485 
          486     if (clearesc)
          487         typeesc = -1;   
          488     if (typestart == typeend){
          489         modified = false;
          490         return;
          491     }
          492     t = which->user1;
          493     if(t != &cmd)
          494         modified = true;
          495     rload(&t->rasp, typestart, typeend, &n);
          496     scratch[n] = 0;
          497     if(t==&cmd && typeend==t->rasp.nrunes && scratch[typeend-typestart-1]=='\n'){
          498         setlock();
          499         outcmd();
          500     }
          501     outTslS(Ttype, t->tag, typestart, scratch);
          502     typestart = -1;
          503     typeend = -1;
          504     XFlush(_dpy);
          505 }
          506 
          507 static int64_t
          508 cmdscrolldown(Flayer *l, int64_t a, Text *t, const char *arg)
          509 {
          510     flushtyping(false);
          511     center(l, l->origin + l->f.nchars + 1);
          512     return a;
          513 }
          514 
          515 static int64_t
          516 cmdscrollup(Flayer *l, int64_t a, Text *t, const char *arg)
          517 {
          518     flushtyping(false);
          519     outTslll(Torigin, t->tag, l->origin, l->f.maxlines + 1, getlayer(l, t));
          520     return a;
          521 }
          522 
          523 static int64_t
          524 cmdcharleft(Flayer *l, int64_t a, Text *t, const char *arg)
          525 {
          526     flsetselect(l, a, a);
          527     flushtyping(false);
          528     if (a > 0)
          529         a--;
          530     flsetselect(l, a, a);
          531     center(l, a);
          532 
          533     return a;
          534 }
          535 
          536 static int64_t
          537 cmdcharright(Flayer *l, int64_t a, Text *t, const char *arg)
          538 {
          539     flsetselect(l, a, a);
          540     flushtyping(false);
          541     if (a < t->rasp.nrunes)
          542         a++;
          543     flsetselect(l, a, a);
          544     center(l, a);
          545 
          546     return a;
          547 }
          548 
          549 static int64_t
          550 cmdeol(Flayer *l, int64_t a, Text *t, const char *arg)
          551 {
          552     flsetselect(l, a, a);
          553     flushtyping(true);
          554     while(a < t->rasp.nrunes)
          555          if(raspc(&t->rasp, a++) == '\n') {
          556              a--;
          557              break;
          558          }
          559 
          560     flsetselect(l, a, a);
          561     center(l, a);
          562 
          563     return a;
          564 }
          565 
          566 static int64_t
          567 cmdbol(Flayer *l, int64_t a, Text *t, const char *arg)
          568 {
          569     flsetselect(l, a, a);
          570     flushtyping(true);
          571     while (a > 0){
          572         if (raspc(&t->rasp, --a) == '\n'){
          573             a++;
          574             break;
          575         }
          576     }
          577 
          578     flsetselect(l, a, a);
          579     center(l, a);
          580 
          581     return a;
          582 }
          583 
          584 static int64_t
          585 cmdscrollupline(Flayer *l, int64_t a, Text *t, const char *arg)
          586 {
          587     if (l->origin > 0)
          588         hmoveto(t->tag, l->origin - 1, l);
          589     return a;
          590 }
          591 
          592 static int64_t
          593 cmdscrolldownline(Flayer *l, int64_t a, Text *t, const char *arg)
          594 {
          595     int64_t e = t->rasp.nrunes;
          596 
          597     horigin(t->tag,
          598             l->origin + frcharofpt(&l->f,Pt(l->f.r.min.x, l->f.r.min.y + l->f.fheight)),
          599             l);
          600 
          601     return a;
          602 }
          603 
          604 static int64_t
          605 cmdlineup(Flayer *l, int64_t a, Text *t, const char *arg)
          606 {
          607     flsetselect(l, a, a);
          608     flushtyping(true);
          609     if (a > 0){
          610         int64_t n0, n1, count = 0;
          611         while (a > 0 && raspc(&t->rasp, a - 1) != '\n'){
          612             a--;
          613             count++;
          614         }
          615         if (a > 0){
          616             n1 = a;
          617             a--;
          618             while (a > 0 && raspc(&t->rasp, a - 1) != '\n')
          619                 a--;
          620     
          621             n0 = a;
          622             a = (n0 + count >= n1) ? n1 - 1 : n0 + count;
          623             flsetselect(l, a, a);
          624             center(l, a);
          625         }
          626     }
          627 
          628     return a;
          629 }
          630 
          631 static int64_t
          632 cmdlinedown(Flayer *l, int64_t a, Text *t, const char *arg)
          633 {
          634     flsetselect(l, a, a);
          635     flushtyping(true);
          636     if (a < t->rasp.nrunes){
          637         int64_t p0, count = 0;
          638 
          639         p0 = a;
          640         while (a > 0 && raspc(&t->rasp, a - 1) != '\n'){
          641             a--;
          642             count++;
          643         }
          644 
          645         a = p0;
          646         while (a < t->rasp.nrunes && raspc(&t->rasp, a) != '\n')
          647             a++;
          648 
          649         if (a < t->rasp.nrunes){
          650             a++;
          651             while (a < t->rasp.nrunes && count > 0 && raspc(&t->rasp, a) != '\n'){
          652                 a++;
          653                 count--;
          654             }
          655             if (a != p0){
          656                 flsetselect(l, a, a);
          657                 center(l, a);
          658             }
          659         }
          660     }
          661 
          662     return a;
          663 }
          664 
          665 static int64_t
          666 cmdjump(Flayer *l, int64_t a, Text *u, const char *arg)
          667 {
          668     Text *t = NULL;
          669 
          670     if (which == &cmd.l[cmd.front] && flast)
          671         current(flast);
          672     else{
          673         l = &cmd.l[cmd.front];
          674         t = (Text *)l->user1;
          675         flast = which;
          676         current(l);
          677         flushtyping(false);
          678         flsetselect(l, t->rasp.nrunes, t->rasp.nrunes);
          679         center(l, a);
          680     }
          681 
          682     return a;
          683 }
          684 
          685 static int64_t
          686 cmdlook(Flayer *l, int64_t a, Text *t, const char *arg)
          687 {
          688     outTsll(Tlook, t->tag, which->p0, which->p1);
          689     setlock();
          690     return a;
          691 }
          692 
          693 static int64_t
          694 cmdsearch(Flayer *l, int64_t a, Text *t, const char *arg)
          695 {
          696     if (t != &cmd && haspat()){
          697         outcmd();
          698         outT0(Tsearch);
          699         setlock();
          700     }
          701     return a;
          702 }
          703 
          704 static int64_t
          705 cmdwrite(Flayer *l, int64_t a, Text *t, const char *arg)
          706 {
          707     cursorswitch(BullseyeCursor);
          708     if (t != &cmd){
          709         outTs(Twrite, t->tag);
          710         setlock();
          711     }
          712     cursorswitch(cursor);
          713     return a;
          714 }
          715 
          716 static int64_t
          717 cmdescape(Flayer *l, int64_t a, Text *t, const char *arg)
          718 {
          719     if (typeesc >= 0){
          720         l->p0 = typeesc;
          721         l->p1 = a;
          722         flushtyping(true);
          723     }
          724 
          725     for (l = t->l; l < &t->l[NL]; l++)
          726         if (l->textfn)
          727             flsetselect(l, l->p0, l->p1);
          728 
          729     return a;
          730 }
          731 
          732 static int64_t
          733 cmddelword(Flayer *l, int64_t a, Text *t, const char *arg)
          734 {
          735     if (l->f.p0 > 0 && a > 0)
          736         l->p0 = ctlw(&t->rasp, l->origin, a);
          737 
          738     l->p1 = a;
          739     if (l->p1 != l->p0){
          740         if(typestart<=l->p0 && l->p1<=typeend){
          741             t->lock++;  /* to call hcut */
          742             hcut(t->tag, l->p0, l->p1-l->p0);
          743             /* hcheck is local because we know rasp is contiguous */
          744             hcheck(t->tag);
          745         }else{
          746             flushtyping(false);
          747             cut(t, t->front, false, true);
          748         }
          749     }
          750 
          751     return a;
          752 }
          753 
          754 static int64_t
          755 cmddelbol(Flayer *l, int64_t a, Text *t, const char *arg)
          756 {
          757     if (l->f.p0 > 0 && a > 0)
          758         l->p0 = ctlu(&t->rasp, l->origin, a);
          759 
          760     l->p1 = a;
          761     if (l->p1 != l->p0){
          762         if(typestart<=l->p0 && l->p1<=typeend){
          763             t->lock++;  /* to call hcut */
          764             hcut(t->tag, l->p0, l->p1-l->p0);
          765             /* hcheck is local because we know rasp is contiguous */
          766             hcheck(t->tag);
          767         }else{
          768             flushtyping(false);
          769             cut(t, t->front, false, true);
          770         }
          771     }
          772 
          773     return a;
          774 }
          775 
          776 static int64_t
          777 cmddelbs(Flayer *l, int64_t a, Text *t, const char *arg)
          778 {
          779     if (l->f.p0 > 0 && a > 0)
          780         l->p0 = a - 1;
          781 
          782     l->p1 = a;
          783     if (l->p1 != l->p0){
          784         if(typestart <= l->p0 && l->p1 <= typeend){
          785             t->lock++;  /* to call hcut */
          786             hcut(t->tag, l->p0, l->p1 - l->p0);
          787             /* hcheck is local because we know rasp is contiguous */
          788             hcheck(t->tag);
          789         }else{
          790             flushtyping(false);
          791             cut(t, t->front, false, true);
          792         }
          793     }
          794 
          795     return a;
          796 }
          797 
          798 static int64_t
          799 cmddel(Flayer *l, int64_t a, Text *t, const char *arg)
          800 {
          801     l->p0 = a;
          802     if (a < t->rasp.nrunes)
          803         l->p1 = a + 1;
          804     if (l->p1 != l->p0){
          805         if(typestart <= l->p0 && l->p1 <= typeend){
          806             t->lock++;  /* to call hcut */
          807             hcut(t->tag, l->p0, l->p1 - l->p0);
          808             /* hcheck is local because we know rasp is contiguous */
          809             hcheck(t->tag);
          810         }else{
          811             flushtyping(false);
          812             cut(t, t->front, false, true);
          813         }
          814     }
          815 
          816     return a;
          817 }
          818 
          819 int
          820 getlayer(const Flayer *l, const Text *t)
          821 {
          822     int i;
          823     for (i = 0; i < NL; i++){
          824         if (&t->l[i] == l)
          825             return i;
          826     }
          827 
          828     return -1;
          829 }
          830 
          831 static int64_t
          832 cmdexchange(Flayer *l, int64_t a, Text *t, const char *arg)
          833 {
          834     int w = getlayer(l, t);
          835     if (w >= 0){
          836         snarf(t, w);
          837         outT0(Tstartsnarf);
          838         setlock();
          839     }
          840 
          841     return a;
          842 }
          843 
          844 static int64_t
          845 cmdsnarf(Flayer *l, int64_t a, Text *t, const char *arg)
          846 {
          847     flushtyping(false);
          848 
          849     int w = getlayer(l, t);
          850     if (w >= 0)
          851         snarf(t, w);
          852 
          853     return a;
          854 }
          855 
          856 static int64_t
          857 cmdcut(Flayer *l, int64_t a, Text *t, const char *arg)
          858 {
          859     flushtyping(false);
          860 
          861     int w = getlayer(l, t);
          862     if (w >= 0)
          863         cut(t, w, true, true);
          864 
          865     return a;
          866 }
          867 
          868 static int64_t
          869 cmdpaste(Flayer *l, int64_t a, Text *t, const char *arg)
          870 {
          871     flushtyping(false);
          872 
          873     int w = getlayer(l, t);
          874     if (w >= 0)
          875         paste(t, w);
          876 
          877     return a;
          878 }
          879 
          880 static int64_t
          881 cmdtab(Flayer *l, int64_t a, Text *t, const char *arg)
          882 {
          883     flushtyping(false);
          884 
          885     if (!expandtabs)
          886         pushkbd('\t');
          887     else{
          888         int col = 0, nspaces = 8, off = a;
          889         int i;
          890         while (off > 0 && raspc(&t->rasp, off - 1) != '\n')
          891             off--, col++;
          892 
          893         nspaces = tabwidth - col % tabwidth;
          894         for (i = 0; i < nspaces; i++)
          895             pushkbd(' ');
          896     }
          897 
          898     return a;
          899 }
          900 
          901 static int64_t
          902 cmdsend(Flayer *l, int64_t a, Text *t, const char *arg)
          903 {
          904     bool dojump = (t != &cmd);
          905 
          906     flushtyping(false);
          907     if (dojump)
          908         cmdjump(l, a, t, NULL);
          909 
          910     for (const char *c = arg; *c; c++){
          911         pushkbd(*c);
          912         type(&cmd.l[cmd.front]);
          913         flushtyping(false);
          914     }
          915     pushkbd('\n');
          916     type(&cmd.l[cmd.front]);
          917     flushtyping(false);
          918 
          919     if (dojump)
          920         cmdjump(l, a, t, NULL);
          921 
          922     return a;
          923 }
          924 
          925 static int64_t
          926 cmdnone(Flayer *l, int64_t a, Text *t, const char *arg)
          927 {
          928     return a;
          929 }
          930 
          931 typedef int64_t (*Commandfunc)(Flayer *, int64_t, Text *, const char *);
          932 typedef struct CommandEntry CommandEntry;
          933 struct CommandEntry{
          934     Commandfunc f;
          935     bool unlocked;
          936     bool docut;
          937 };
          938 
          939 CommandEntry commands[Cmax] ={
          940     [Cnone]           = {cmdnone,           false, false},
          941     [Cscrolldown]     = {cmdscrolldown,     false, false},
          942     [Cscrollup]       = {cmdscrollup,       false, false},
          943     [Cscrolldownline] = {cmdscrolldownline, false, false},
          944     [Cscrollupline]   = {cmdscrollupline,   false, false},
          945     [Ccharleft]       = {cmdcharleft,       false, false},
          946     [Ccharright]      = {cmdcharright,      false, false},
          947     [Clineup]         = {cmdlineup,         false, false},
          948     [Clinedown]       = {cmdlinedown,       false, false},
          949     [Cjump]           = {cmdjump,           false, false},
          950     [Cescape]         = {cmdescape,         false, false},
          951     [Csnarf]          = {cmdsnarf,          false, false},
          952     [Ccut]            = {cmdcut,            false, false},
          953     [Cpaste]          = {cmdpaste,          false, false},
          954     [Cexchange]       = {cmdexchange,       false, false},
          955     [Cdelword]        = {cmddelword,        true,  false},
          956     [Cdelbol]         = {cmddelbol,         true,  false},
          957     [Cdelbs]          = {cmddelbs,          true,  true},
          958     [Cdel]            = {cmddel,            true,  true},
          959     [Ceol]            = {cmdeol,            false, false},
          960     [Cbol]            = {cmdbol,            false, false},
          961     [Ctab]            = {cmdtab,            false, false},
          962     [Csend]           = {cmdsend,           false, false},
          963     [Clook]           = {cmdlook,           false, false},
          964     [Csearch]         = {cmdsearch,         false, false},
          965     [Cwrite]          = {cmdwrite,          false, false}
          966 };
          967 
          968 
          969 void
          970 type(Flayer *l)    /* what a bloody mess this is -- but it's getting better! */
          971 {
          972     Text *t = (Text *)l->user1;
          973     wchar_t buf[100];
          974     Keystroke k = {0};
          975     wchar_t *p = buf;
          976     int64_t a;
          977 
          978     if(lock || t->lock){
          979         kbdblock();
          980         return;
          981     }
          982 
          983     k = qpeekc();
          984     a = l->p0;
          985     if (a != l->p1 && (k.k != Kcommand || commands[k.c].docut)){
          986         flushtyping(true);
          987         cut(t, t->front, true, true);
          988         return; /* it may now be locked */
          989     }
          990 
          991     while (((k = kbdchar()), k.c) > 0) {
          992         if (k.k == Kcommand)
          993             break;
          994 
          995         *p++ = k.c;
          996         if (k.c == '\n' || p >= buf + sizeof(buf) / sizeof(buf[0]))
          997             break;
          998     }
          999 
         1000     if (k.k == Kcommand){
         1001         flushtyping(false);
         1002         if (k.c < 0 || k.c >= Cmax || commands[k.c].f == NULL)
         1003             panic("command table miss");
         1004 
         1005         CommandEntry *e = &commands[k.c];
         1006         if (!e->unlocked || !lock){
         1007             if (k.t == Tcurrent)
         1008                 a = e->f(l, a, t, k.a);
         1009             else{
         1010                 Flayer *lt = flwhich(k.p);
         1011                 if (lt)
         1012                     lt->p0 = e->f(lt, lt->p0, (Text *)lt->user1, k.a);
         1013             }
         1014         }
         1015     }
         1016 
         1017     if (p > buf){
         1018         if (typestart < 0)
         1019             typestart = a;
         1020 
         1021         if (typeesc < 0)
         1022             typeesc = a;
         1023 
         1024         hgrow(t->tag, a, p-buf, 0);
         1025         t->lock++;  /* pretend we Trequest'ed for hdatarune*/
         1026         hdatarune(t->tag, a, buf, p-buf);
         1027         a += p-buf;
         1028         l->p0 = a;
         1029         l->p1 = a;
         1030         typeend = a;
         1031         if (autoindent && k.c == '\n' && t!=&cmd)
         1032             a = indent(l, a);
         1033         if (k.c == '\n' || typeend - typestart > 100)
         1034             flushtyping(false);
         1035         onethird(l, a);
         1036     }
         1037 
         1038     if (typeesc >= l->p0)
         1039         typeesc = l->p0;
         1040 
         1041     if (typestart >= 0){
         1042         if(typestart >= l->p0)
         1043             typestart = l->p0;
         1044         typeend = l->p0;
         1045         if (typestart == typeend){
         1046             typestart = -1;
         1047             typeend = -1;
         1048             modified = false;
         1049         }
         1050     }
         1051 }
         1052 
         1053 void
         1054 outcmd(void)
         1055 {
         1056     if(work)
         1057         outTsll(Tworkfile, ((Text *)work->user1)->tag, work->p0, work->p1);
         1058 }
         1059 
         1060 void
         1061 panic(char *s)
         1062 {
         1063     fprintf(stderr, "samterm:panic: ");
         1064     perror(s);
         1065     abort();
         1066 }
         1067 
         1068 wchar_t*
         1069 stgettext(Flayer *l, int64_t n, uint64_t *np)
         1070 {
         1071     Text *t;
         1072 
         1073     t = l->user1;
         1074     rload(&t->rasp, l->origin, l->origin+n, np);
         1075     return scratch;
         1076 }
         1077 
         1078 int64_t
         1079 scrtotal(Flayer *l)
         1080 {
         1081     return ((Text *)l->user1)->rasp.nrunes;
         1082 }
         1083 
         1084 void*
         1085 alloc(uint64_t n)
         1086 {
         1087     void *p;
         1088 
         1089     p = malloc(n);
         1090     if(p == 0)
         1091         panic("alloc");
         1092     memset(p, 0, n);
         1093     return p;
         1094 }