URI:
       tedit.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tedit.c (12156B)
       ---
            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 "edit.h"
           14 #include "fns.h"
           15 
           16 static char        linex[]="\n";
           17 static char        wordx[]=" \t\n";
           18 struct cmdtab cmdtab[]={
           19 /*        cmdc        text        regexp        addr        defcmd        defaddr        count        token         fn        */
           20         '\n',        0,        0,        0,        0,        aDot,        0,        0,        nl_cmd,
           21         'a',        1,        0,        0,        0,        aDot,        0,        0,        a_cmd,
           22         'b',        0,        0,        0,        0,        aNo,        0,        linex,        b_cmd,
           23         'c',        1,        0,        0,        0,        aDot,        0,        0,        c_cmd,
           24         'd',        0,        0,        0,        0,        aDot,        0,        0,        d_cmd,
           25         'e',        0,        0,        0,        0,        aNo,        0,        wordx,        e_cmd,
           26         'f',        0,        0,        0,        0,        aNo,        0,        wordx,        f_cmd,
           27         'g',        0,        1,        0,        'p',        aDot,        0,        0,        g_cmd,
           28         'i',        1,        0,        0,        0,        aDot,        0,        0,        i_cmd,
           29         'm',        0,        0,        1,        0,        aDot,        0,        0,        m_cmd,
           30         'p',        0,        0,        0,        0,        aDot,        0,        0,        p_cmd,
           31         'r',        0,        0,        0,        0,        aDot,        0,        wordx,        e_cmd,
           32         's',        0,        1,        0,        0,        aDot,        1,        0,        s_cmd,
           33         't',        0,        0,        1,        0,        aDot,        0,        0,        m_cmd,
           34         'u',        0,        0,        0,        0,        aNo,        2,        0,        u_cmd,
           35         'v',        0,        1,        0,        'p',        aDot,        0,        0,        g_cmd,
           36         'w',        0,        0,        0,        0,        aAll,        0,        wordx,        w_cmd,
           37         'x',        0,        1,        0,        'p',        aDot,        0,        0,        x_cmd,
           38         'y',        0,        1,        0,        'p',        aDot,        0,        0,        x_cmd,
           39         '=',        0,        0,        0,        0,        aDot,        0,        linex,        eq_cmd,
           40         'B',        0,        0,        0,        0,        aNo,        0,        linex,        B_cmd,
           41         'D',        0,        0,        0,        0,        aNo,        0,        linex,        D_cmd,
           42         'X',        0,        1,        0,        'f',        aNo,        0,        0,        X_cmd,
           43         'Y',        0,        1,        0,        'f',        aNo,        0,        0,        X_cmd,
           44         '<',        0,        0,        0,        0,        aDot,        0,        linex,        pipe_cmd,
           45         '|',        0,        0,        0,        0,        aDot,        0,        linex,        pipe_cmd,
           46         '>',        0,        0,        0,        0,        aDot,        0,        linex,        pipe_cmd,
           47 /* deliberately unimplemented:
           48         'k',        0,        0,        0,        0,        aDot,        0,        0,        k_cmd,
           49         'n',        0,        0,        0,        0,        aNo,        0,        0,        n_cmd,
           50         'q',        0,        0,        0,        0,        aNo,        0,        0,        q_cmd,
           51         '!',        0,        0,        0,        0,        aNo,        0,        linex,        plan9_cmd,
           52  */
           53         0,        0,        0,        0,        0,        0,        0,        0
           54 };
           55 
           56 Cmd        *parsecmd(int);
           57 Addr        *compoundaddr(void);
           58 Addr        *simpleaddr(void);
           59 void        freecmd(void);
           60 void        okdelim(int);
           61 
           62 Rune        *cmdstartp;
           63 Rune *cmdendp;
           64 Rune        *cmdp;
           65 Channel        *editerrc;
           66 
           67 String        *lastpat;
           68 int        patset;
           69 
           70 List        cmdlist;
           71 List        addrlist;
           72 List        stringlist;
           73 Text        *curtext;
           74 int        editing = Inactive;
           75 
           76 String*        newstring(int);
           77 
           78 void
           79 editthread(void *v)
           80 {
           81         Cmd *cmdp;
           82 
           83         USED(v);
           84         threadsetname("editthread");
           85         while((cmdp=parsecmd(0)) != 0){
           86                 if(cmdexec(curtext, cmdp) == 0)
           87                         break;
           88                 freecmd();
           89         }
           90         sendp(editerrc, nil);
           91 }
           92 
           93 void
           94 allelogterm(Window *w, void *x)
           95 {
           96         USED(x);
           97         elogterm(w->body.file);
           98 }
           99 
          100 void
          101 alleditinit(Window *w, void *x)
          102 {
          103         USED(x);
          104         textcommit(&w->tag, TRUE);
          105         textcommit(&w->body, TRUE);
          106         w->body.file->editclean = FALSE;
          107 }
          108 
          109 void
          110 allupdate(Window *w, void *x)
          111 {
          112         Text *t;
          113         int i;
          114         File *f;
          115 
          116         USED(x);
          117         t = &w->body;
          118         f = t->file;
          119         if(f->curtext != t)        /* do curtext only */
          120                 return;
          121         if(f->elog.type == Null)
          122                 elogterm(f);
          123         else if(f->elog.type != Empty){
          124                 elogapply(f);
          125                 if(f->editclean){
          126                         f->mod = FALSE;
          127                         for(i=0; i<f->ntext; i++)
          128                                 f->text[i]->w->dirty = FALSE;
          129                 }
          130         }
          131         textsetselect(t, t->q0, t->q1);
          132         textscrdraw(t);
          133         winsettag(w);
          134 }
          135 
          136 void
          137 editerror(char *fmt, ...)
          138 {
          139         va_list arg;
          140         char *s;
          141 
          142         va_start(arg, fmt);
          143         s = vsmprint(fmt, arg);
          144         va_end(arg);
          145         freecmd();
          146         allwindows(allelogterm, nil);        /* truncate the edit logs */
          147         sendp(editerrc, s);
          148         threadexits(nil);
          149 }
          150 
          151 void
          152 editcmd(Text *ct, Rune *r, uint n)
          153 {
          154         char *err;
          155 
          156         if(n == 0)
          157                 return;
          158         if(2*n > RBUFSIZE){
          159                 warning(nil, "string too long\n");
          160                 return;
          161         }
          162 
          163         allwindows(alleditinit, nil);
          164         if(cmdstartp)
          165                 free(cmdstartp);
          166         cmdstartp = runemalloc(n+2);
          167         runemove(cmdstartp, r, n);
          168         if(r[n-1] != '\n')
          169                 cmdstartp[n++] = '\n';
          170         cmdstartp[n] = '\0';
          171         cmdendp = cmdstartp+n;
          172         cmdp = cmdstartp;
          173         if(ct->w == nil)
          174                 curtext = nil;
          175         else
          176                 curtext = &ct->w->body;
          177         resetxec();
          178         if(editerrc == nil){
          179                 editerrc = chancreate(sizeof(char*), 0);
          180                 chansetname(editerrc, "editerrc");
          181                 lastpat = allocstring(0);
          182         }
          183         threadcreate(editthread, nil, STACK);
          184         err = recvp(editerrc);
          185         editing = Inactive;
          186         if(err != nil){
          187                 if(err[0] != '\0')
          188                         warning(nil, "Edit: %s\n", err);
          189                 free(err);
          190         }
          191 
          192         /* update everyone whose edit log has data */
          193         allwindows(allupdate, nil);
          194 }
          195 
          196 int
          197 getch(void)
          198 {
          199         if(cmdp == cmdendp)
          200                 return -1;
          201         return *cmdp++;
          202 }
          203 
          204 int
          205 nextc(void)
          206 {
          207         if(cmdp == cmdendp)
          208                 return -1;
          209         return *cmdp;
          210 }
          211 
          212 void
          213 ungetch(void)
          214 {
          215         if(--cmdp < cmdstartp)
          216                 error("ungetch");
          217 }
          218 
          219 long
          220 getnum(int signok)
          221 {
          222         long n;
          223         int c, sign;
          224 
          225         n = 0;
          226         sign = 1;
          227         if(signok>1 && nextc()=='-'){
          228                 sign = -1;
          229                 getch();
          230         }
          231         if((c=nextc())<'0' || '9'<c)        /* no number defaults to 1 */
          232                 return sign;
          233         while('0'<=(c=getch()) && c<='9')
          234                 n = n*10 + (c-'0');
          235         ungetch();
          236         return sign*n;
          237 }
          238 
          239 int
          240 cmdskipbl(void)
          241 {
          242         int c;
          243         do
          244                 c = getch();
          245         while(c==' ' || c=='\t');
          246         if(c >= 0)
          247                 ungetch();
          248         return c;
          249 }
          250 
          251 /*
          252  * Check that list has room for one more element.
          253  */
          254 void
          255 growlist(List *l)
          256 {
          257         if(l->u.listptr==0 || l->nalloc==0){
          258                 l->nalloc = INCR;
          259                 l->u.listptr = emalloc(INCR*sizeof(void*));
          260                 l->nused = 0;
          261         }else if(l->nused == l->nalloc){
          262                 l->u.listptr = erealloc(l->u.listptr, (l->nalloc+INCR)*sizeof(void*));
          263                 memset(l->u.ptr+l->nalloc, 0, INCR*sizeof(void*));
          264                 l->nalloc += INCR;
          265         }
          266 }
          267 
          268 /*
          269  * Remove the ith element from the list
          270  */
          271 void
          272 dellist(List *l, int i)
          273 {
          274         memmove(&l->u.ptr[i], &l->u.ptr[i+1], (l->nused-(i+1))*sizeof(void*));
          275         l->nused--;
          276 }
          277 
          278 /*
          279  * Add a new element, whose position is i, to the list
          280  */
          281 void
          282 inslist(List *l, int i, void *v)
          283 {
          284         growlist(l);
          285         memmove(&l->u.ptr[i+1], &l->u.ptr[i], (l->nused-i)*sizeof(void*));
          286         l->u.ptr[i] = v;
          287         l->nused++;
          288 }
          289 
          290 void
          291 listfree(List *l)
          292 {
          293         free(l->u.listptr);
          294         free(l);
          295 }
          296 
          297 String*
          298 allocstring(int n)
          299 {
          300         String *s;
          301 
          302         s = emalloc(sizeof(String));
          303         s->n = n;
          304         s->nalloc = n+10;
          305         s->r = emalloc(s->nalloc*sizeof(Rune));
          306         s->r[n] = '\0';
          307         return s;
          308 }
          309 
          310 void
          311 freestring(String *s)
          312 {
          313         free(s->r);
          314         free(s);
          315 }
          316 
          317 Cmd*
          318 newcmd(void){
          319         Cmd *p;
          320 
          321         p = emalloc(sizeof(Cmd));
          322         inslist(&cmdlist, cmdlist.nused, p);
          323         return p;
          324 }
          325 
          326 String*
          327 newstring(int n)
          328 {
          329         String *p;
          330 
          331         p = allocstring(n);
          332         inslist(&stringlist, stringlist.nused, p);
          333         return p;
          334 }
          335 
          336 Addr*
          337 newaddr(void)
          338 {
          339         Addr *p;
          340 
          341         p = emalloc(sizeof(Addr));
          342         inslist(&addrlist, addrlist.nused, p);
          343         return p;
          344 }
          345 
          346 void
          347 freecmd(void)
          348 {
          349         int i;
          350 
          351         while(cmdlist.nused > 0)
          352                 free(cmdlist.u.ucharptr[--cmdlist.nused]);
          353         while(addrlist.nused > 0)
          354                 free(addrlist.u.ucharptr[--addrlist.nused]);
          355         while(stringlist.nused>0){
          356                 i = --stringlist.nused;
          357                 freestring(stringlist.u.stringptr[i]);
          358         }
          359 }
          360 
          361 void
          362 okdelim(int c)
          363 {
          364         if(c=='\\' || ('a'<=c && c<='z')
          365         || ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
          366                 editerror("bad delimiter %c\n", c);
          367 }
          368 
          369 void
          370 atnl(void)
          371 {
          372         int c;
          373 
          374         cmdskipbl();
          375         c = getch();
          376         if(c != '\n')
          377                 editerror("newline expected (saw %C)", c);
          378 }
          379 
          380 void
          381 Straddc(String *s, int c)
          382 {
          383         if(s->n+1 >= s->nalloc){
          384                 s->nalloc += 10;
          385                 s->r = erealloc(s->r, s->nalloc*sizeof(Rune));
          386         }
          387         s->r[s->n++] = c;
          388         s->r[s->n] = '\0';
          389 }
          390 
          391 void
          392 getrhs(String *s, int delim, int cmd)
          393 {
          394         int c;
          395 
          396         while((c = getch())>0 && c!=delim && c!='\n'){
          397                 if(c == '\\'){
          398                         if((c=getch()) <= 0)
          399                                 error("bad right hand side");
          400                         if(c == '\n'){
          401                                 ungetch();
          402                                 c='\\';
          403                         }else if(c == 'n')
          404                                 c='\n';
          405                         else if(c!=delim && (cmd=='s' || c!='\\'))        /* s does its own */
          406                                 Straddc(s, '\\');
          407                 }
          408                 Straddc(s, c);
          409         }
          410         ungetch();        /* let client read whether delimiter, '\n' or whatever */
          411 }
          412 
          413 String *
          414 collecttoken(char *end)
          415 {
          416         String *s = newstring(0);
          417         int c;
          418 
          419         while((c=nextc())==' ' || c=='\t')
          420                 Straddc(s, getch()); /* blanks significant for getname() */
          421         while((c=getch())>0 && utfrune(end, c)==0)
          422                 Straddc(s, c);
          423         if(c != '\n')
          424                 atnl();
          425         return s;
          426 }
          427 
          428 String *
          429 collecttext(void)
          430 {
          431         String *s;
          432         int begline, i, c, delim;
          433 
          434         s = newstring(0);
          435         if(cmdskipbl()=='\n'){
          436                 getch();
          437                 i = 0;
          438                 do{
          439                         begline = i;
          440                         while((c = getch())>0 && c!='\n')
          441                                 i++, Straddc(s, c);
          442                         i++, Straddc(s, '\n');
          443                         if(c < 0)
          444                                 goto Return;
          445                 }while(s->r[begline]!='.' || s->r[begline+1]!='\n');
          446                 s->r[s->n-2] = '\0';
          447                 s->n -= 2;
          448         }else{
          449                 okdelim(delim = getch());
          450                 getrhs(s, delim, 'a');
          451                 if(nextc()==delim)
          452                         getch();
          453                 atnl();
          454         }
          455     Return:
          456         return s;
          457 }
          458 
          459 int
          460 cmdlookup(int c)
          461 {
          462         int i;
          463 
          464         for(i=0; cmdtab[i].cmdc; i++)
          465                 if(cmdtab[i].cmdc == c)
          466                         return i;
          467         return -1;
          468 }
          469 
          470 Cmd*
          471 parsecmd(int nest)
          472 {
          473         int i, c;
          474         struct cmdtab *ct;
          475         Cmd *cp, *ncp;
          476         Cmd cmd;
          477 
          478         cmd.next = cmd.u.cmd = 0;
          479         cmd.re = 0;
          480         cmd.flag = cmd.num = 0;
          481         cmd.addr = compoundaddr();
          482         if(cmdskipbl() == -1)
          483                 return 0;
          484         if((c=getch())==-1)
          485                 return 0;
          486         cmd.cmdc = c;
          487         if(cmd.cmdc=='c' && nextc()=='d'){        /* sleazy two-character case */
          488                 getch();                /* the 'd' */
          489                 cmd.cmdc='c'|0x100;
          490         }
          491         i = cmdlookup(cmd.cmdc);
          492         if(i >= 0){
          493                 if(cmd.cmdc == '\n')
          494                         goto Return;        /* let nl_cmd work it all out */
          495                 ct = &cmdtab[i];
          496                 if(ct->defaddr==aNo && cmd.addr)
          497                         editerror("command takes no address");
          498                 if(ct->count)
          499                         cmd.num = getnum(ct->count);
          500                 if(ct->regexp){
          501                         /* x without pattern -> .*\n, indicated by cmd.re==0 */
          502                         /* X without pattern is all files */
          503                         if((ct->cmdc!='x' && ct->cmdc!='X') ||
          504                            ((c = nextc())!=' ' && c!='\t' && c!='\n')){
          505                                 cmdskipbl();
          506                                 if((c = getch())=='\n' || c<0)
          507                                         editerror("no address");
          508                                 okdelim(c);
          509                                 cmd.re = getregexp(c);
          510                                 if(ct->cmdc == 's'){
          511                                         cmd.u.text = newstring(0);
          512                                         getrhs(cmd.u.text, c, 's');
          513                                         if(nextc() == c){
          514                                                 getch();
          515                                                 if(nextc() == 'g')
          516                                                         cmd.flag = getch();
          517                                         }
          518 
          519                                 }
          520                         }
          521                 }
          522                 if(ct->addr && (cmd.u.mtaddr=simpleaddr())==0)
          523                         editerror("bad address");
          524                 if(ct->defcmd){
          525                         if(cmdskipbl() == '\n'){
          526                                 getch();
          527                                 cmd.u.cmd = newcmd();
          528                                 cmd.u.cmd->cmdc = ct->defcmd;
          529                         }else if((cmd.u.cmd = parsecmd(nest))==0)
          530                                 error("defcmd");
          531                 }else if(ct->text)
          532                         cmd.u.text = collecttext();
          533                 else if(ct->token)
          534                         cmd.u.text = collecttoken(ct->token);
          535                 else
          536                         atnl();
          537         }else
          538                 switch(cmd.cmdc){
          539                 case '{':
          540                         cp = 0;
          541                         do{
          542                                 if(cmdskipbl()=='\n')
          543                                         getch();
          544                                 ncp = parsecmd(nest+1);
          545                                 if(cp)
          546                                         cp->next = ncp;
          547                                 else
          548                                         cmd.u.cmd = ncp;
          549                         }while(cp = ncp);
          550                         break;
          551                 case '}':
          552                         atnl();
          553                         if(nest==0)
          554                                 editerror("right brace with no left brace");
          555                         return 0;
          556                 default:
          557                         editerror("unknown command %c", cmd.cmdc);
          558                 }
          559     Return:
          560         cp = newcmd();
          561         *cp = cmd;
          562         return cp;
          563 }
          564 
          565 String*
          566 getregexp(int delim)
          567 {
          568         String *buf, *r;
          569         int i, c;
          570 
          571         buf = allocstring(0);
          572         for(i=0; ; i++){
          573                 if((c = getch())=='\\'){
          574                         if(nextc()==delim)
          575                                 c = getch();
          576                         else if(nextc()=='\\'){
          577                                 Straddc(buf, c);
          578                                 c = getch();
          579                         }
          580                 }else if(c==delim || c=='\n')
          581                         break;
          582                 if(i >= RBUFSIZE)
          583                         editerror("regular expression too long");
          584                 Straddc(buf, c);
          585         }
          586         if(c!=delim && c)
          587                 ungetch();
          588         if(buf->n > 0){
          589                 patset = TRUE;
          590                 freestring(lastpat);
          591                 lastpat = buf;
          592         }else
          593                 freestring(buf);
          594         if(lastpat->n == 0)
          595                 editerror("no regular expression defined");
          596         r = newstring(lastpat->n);
          597         runemove(r->r, lastpat->r, lastpat->n);        /* newstring put \0 at end */
          598         return r;
          599 }
          600 
          601 Addr *
          602 simpleaddr(void)
          603 {
          604         Addr addr;
          605         Addr *ap, *nap;
          606 
          607         addr.num = 0;
          608         addr.next = 0;
          609         addr.u.left = 0;
          610         switch(cmdskipbl()){
          611         case '#':
          612                 addr.type = getch();
          613                 addr.num = getnum(1);
          614                 break;
          615         case '0': case '1': case '2': case '3': case '4':
          616         case '5': case '6': case '7': case '8': case '9':
          617                 addr.num = getnum(1);
          618                 addr.type='l';
          619                 break;
          620         case '/': case '?': case '"':
          621                 addr.u.re = getregexp(addr.type = getch());
          622                 break;
          623         case '.':
          624         case '$':
          625         case '+':
          626         case '-':
          627         case '\'':
          628                 addr.type = getch();
          629                 break;
          630         default:
          631                 return 0;
          632         }
          633         if(addr.next = simpleaddr())
          634                 switch(addr.next->type){
          635                 case '.':
          636                 case '$':
          637                 case '\'':
          638                         if(addr.type=='"')
          639                                 break;
          640                         /* fall through */
          641                 case '"':
          642                         editerror("bad address syntax");
          643                         break;
          644                 case 'l':
          645                 case '#':
          646                         if(addr.type=='"')
          647                                 break;
          648                         /* fall through */
          649                 case '/':
          650                 case '?':
          651                         if(addr.type!='+' && addr.type!='-'){
          652                                 /* insert the missing '+' */
          653                                 nap = newaddr();
          654                                 nap->type='+';
          655                                 nap->next = addr.next;
          656                                 addr.next = nap;
          657                         }
          658                         break;
          659                 case '+':
          660                 case '-':
          661                         break;
          662                 default:
          663                         error("simpleaddr");
          664                 }
          665         ap = newaddr();
          666         *ap = addr;
          667         return ap;
          668 }
          669 
          670 Addr *
          671 compoundaddr(void)
          672 {
          673         Addr addr;
          674         Addr *ap, *next;
          675 
          676         addr.u.left = simpleaddr();
          677         if((addr.type = cmdskipbl())!=',' && addr.type!=';')
          678                 return addr.u.left;
          679         getch();
          680         next = addr.next = compoundaddr();
          681         if(next && (next->type==',' || next->type==';') && next->u.left==0)
          682                 editerror("bad address syntax");
          683         ap = newaddr();
          684         *ap = addr;
          685         return ap;
          686 }