cmd.c - 9base - revived minimalist port of Plan 9 userland to Unix
 (HTM) git clone git://git.suckless.org/9base
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       cmd.c (10833B)
       ---
            1 #include "sam.h"
            2 #include "parse.h"
            3 
            4 static char        linex[]="\n";
            5 static char        wordx[]=" \t\n";
            6 struct cmdtab cmdtab[]={
            7 /*        cmdc        text        regexp        addr        defcmd        defaddr        count        token         fn        */
            8         '\n',        0,        0,        0,        0,        aDot,        0,        0,        nl_cmd,
            9         'a',        1,        0,        0,        0,        aDot,        0,        0,        a_cmd,
           10         'b',        0,        0,        0,        0,        aNo,        0,        linex,        b_cmd,
           11         'B',        0,        0,        0,        0,        aNo,        0,        linex,        b_cmd,
           12         'c',        1,        0,        0,        0,        aDot,        0,        0,        c_cmd,
           13         'd',        0,        0,        0,        0,        aDot,        0,        0,        d_cmd,
           14         'D',        0,        0,        0,        0,        aNo,        0,        linex,        D_cmd,
           15         'e',        0,        0,        0,        0,        aNo,        0,        wordx,        e_cmd,
           16         'f',        0,        0,        0,        0,        aNo,        0,        wordx,        f_cmd,
           17         'g',        0,        1,        0,        'p',        aDot,        0,        0,        g_cmd,
           18         'i',        1,        0,        0,        0,        aDot,        0,        0,        i_cmd,
           19         'k',        0,        0,        0,        0,        aDot,        0,        0,        k_cmd,
           20         'm',        0,        0,        1,        0,        aDot,        0,        0,        m_cmd,
           21         'n',        0,        0,        0,        0,        aNo,        0,        0,        n_cmd,
           22         'p',        0,        0,        0,        0,        aDot,        0,        0,        p_cmd,
           23         'q',        0,        0,        0,        0,        aNo,        0,        0,        q_cmd,
           24         'r',        0,        0,        0,        0,        aDot,        0,        wordx,        e_cmd,
           25         's',        0,        1,        0,        0,        aDot,        1,        0,        s_cmd,
           26         't',        0,        0,        1,        0,        aDot,        0,        0,        m_cmd,
           27         'u',        0,        0,        0,        0,        aNo,        2,        0,        u_cmd,
           28         'v',        0,        1,        0,        'p',        aDot,        0,        0,        g_cmd,
           29         'w',        0,        0,        0,        0,        aAll,        0,        wordx,        w_cmd,
           30         'x',        0,        1,        0,        'p',        aDot,        0,        0,        x_cmd,
           31         'y',        0,        1,        0,        'p',        aDot,        0,        0,        x_cmd,
           32         'X',        0,        1,        0,        'f',        aNo,        0,        0,        X_cmd,
           33         'Y',        0,        1,        0,        'f',        aNo,        0,        0,        X_cmd,
           34         '!',        0,        0,        0,        0,        aNo,        0,        linex,        plan9_cmd,
           35         '>',        0,        0,        0,        0,        aDot,        0,        linex,        plan9_cmd,
           36         '<',        0,        0,        0,        0,        aDot,        0,        linex,        plan9_cmd,
           37         '|',        0,        0,        0,        0,        aDot,        0,        linex,        plan9_cmd,
           38         '=',        0,        0,        0,        0,        aDot,        0,        linex,        eq_cmd,
           39         'c'|0x100,0,        0,        0,        0,        aNo,        0,        wordx,        cd_cmd,
           40         0,        0,        0,        0,        0,        0,        0,        0
           41 };
           42 Cmd        *parsecmd(int);
           43 Addr        *compoundaddr(void);
           44 Addr        *simpleaddr(void);
           45 void        freecmd(void);
           46 void        okdelim(int);
           47 
           48 Rune        line[BLOCKSIZE];
           49 Rune        termline[BLOCKSIZE];
           50 Rune        *linep = line;
           51 Rune        *terminp = termline;
           52 Rune        *termoutp = termline;
           53 
           54 List        cmdlist = { 'p' };
           55 List        addrlist = { 'p' };
           56 List        relist = { 'p' };
           57 List        stringlist = { 'p' };
           58 
           59 int        eof;
           60 
           61 void
           62 resetcmd(void)
           63 {
           64         linep = line;
           65         *linep = 0;
           66         terminp = termoutp = termline;
           67         freecmd();
           68 }
           69 
           70 int
           71 inputc(void)
           72 {
           73         int n, nbuf;
           74         char buf[UTFmax];
           75         Rune r;
           76 
           77     Again:
           78         nbuf = 0;
           79         if(downloaded){
           80                 while(termoutp == terminp){
           81                         cmdupdate();
           82                         if(patset)
           83                                 tellpat();
           84                         while(termlocked > 0){
           85                                 outT0(Hunlock);
           86                                 termlocked--;
           87                         }
           88                         if(rcv() == 0)
           89                                 return -1;
           90                 }
           91                 r = *termoutp++;
           92                 if(termoutp == terminp)
           93                         terminp = termoutp = termline;
           94         }else{
           95                    do{
           96                         n = read(0, buf+nbuf, 1);
           97                         if(n <= 0)
           98                                 return -1;
           99                         nbuf += n;
          100                 }while(!fullrune(buf, nbuf));
          101                 chartorune(&r, buf);
          102         }
          103         if(r == 0){
          104                 warn(Wnulls);
          105                 goto Again;
          106         }
          107         return r;
          108 }
          109 
          110 int
          111 inputline(void)
          112 {
          113         int i, c, start;
          114 
          115         /*
          116          * Could set linep = line and i = 0 here and just
          117          * error(Etoolong) below, but this way we keep
          118          * old input buffer history around for a while.
          119          * This is useful only for debugging.
          120          */
          121         i = linep - line;
          122         do{
          123                 if((c = inputc())<=0)
          124                         return -1;
          125                 if(i == nelem(line)-1){
          126                         if(linep == line)
          127                                 error(Etoolong);
          128                         start = linep - line;
          129                         runemove(line, linep, i-start);
          130                         i -= start;
          131                         linep = line;
          132                 }
          133         }while((line[i++]=c) != '\n');
          134         line[i] = 0;
          135         return 1;
          136 }
          137 
          138 int
          139 getch(void)
          140 {
          141         if(eof)
          142                 return -1;
          143         if(*linep==0 && inputline()<0){
          144                 eof = TRUE;
          145                 return -1;
          146         }
          147         return *linep++;
          148 }
          149 
          150 int
          151 nextc(void)
          152 {
          153         if(*linep == 0)
          154                 return -1;
          155         return *linep;
          156 }
          157 
          158 void
          159 ungetch(void)
          160 {
          161         if(--linep < line)
          162                 panic("ungetch");
          163 }
          164 
          165 Posn
          166 getnum(int signok)
          167 {
          168         Posn n=0;
          169         int c, sign;
          170 
          171         sign = 1;
          172         if(signok>1 && nextc()=='-'){
          173                 sign = -1;
          174                 getch();
          175         }
          176         if((c=nextc())<'0' || '9'<c)        /* no number defaults to 1 */
          177                 return sign;
          178         while('0'<=(c=getch()) && c<='9')
          179                 n = n*10 + (c-'0');
          180         ungetch();
          181         return sign*n;
          182 }
          183 
          184 int
          185 skipbl(void)
          186 {
          187         int c;
          188         do
          189                 c = getch();
          190         while(c==' ' || c=='\t');
          191         if(c >= 0)
          192                 ungetch();
          193         return c;
          194 }
          195 
          196 void
          197 termcommand(void)
          198 {
          199         Posn p;
          200 
          201         for(p=cmdpt; p<cmd->b.nc; p++){
          202                 if(terminp >= &termline[BLOCKSIZE]){
          203                         cmdpt = cmd->b.nc;
          204                         error(Etoolong);
          205                 }
          206                 *terminp++ = filereadc(cmd, p);
          207         }
          208         cmdpt = cmd->b.nc;
          209 }
          210 
          211 void
          212 cmdloop(void)
          213 {
          214         Cmd *cmdp;
          215         File *ocurfile;
          216         int loaded;
          217 
          218         for(;;){
          219                 if(!downloaded && curfile && curfile->unread)
          220                         load(curfile);
          221                 if((cmdp = parsecmd(0))==0){
          222                         if(downloaded){
          223                                 rescue();
          224                                 exits("eof");
          225                         }
          226                         break;
          227                 }
          228                 ocurfile = curfile;
          229                 loaded = curfile && !curfile->unread;
          230                 if(cmdexec(curfile, cmdp) == 0)
          231                         break;
          232                 freecmd();
          233                 cmdupdate();
          234                 update();
          235                 if(downloaded && curfile &&
          236                     (ocurfile!=curfile || (!loaded && !curfile->unread)))
          237                         outTs(Hcurrent, curfile->tag);
          238                         /* don't allow type ahead on files that aren't bound */
          239                 if(downloaded && curfile && curfile->rasp == 0)
          240                         terminp = termoutp;
          241         }
          242 }
          243 
          244 Cmd *
          245 newcmd(void){
          246         Cmd *p;
          247 
          248         p = emalloc(sizeof(Cmd));
          249         inslist(&cmdlist, cmdlist.nused, (long)p);
          250         return p;
          251 }
          252 
          253 Addr*
          254 newaddr(void)
          255 {
          256         Addr *p;
          257 
          258         p = emalloc(sizeof(Addr));
          259         inslist(&addrlist, addrlist.nused, (long)p);
          260         return p;
          261 }
          262 
          263 String*
          264 newre(void)
          265 {
          266         String *p;
          267 
          268         p = emalloc(sizeof(String));
          269         inslist(&relist, relist.nused, (long)p);
          270         Strinit(p);
          271         return p;
          272 }
          273 
          274 String*
          275 newstring(void)
          276 {
          277         String *p;
          278 
          279         p = emalloc(sizeof(String));
          280         inslist(&stringlist, stringlist.nused, (long)p);
          281         Strinit(p);
          282         return p;
          283 }
          284 
          285 void
          286 freecmd(void)
          287 {
          288         int i;
          289 
          290         while(cmdlist.nused > 0)
          291                 free(cmdlist.voidpptr[--cmdlist.nused]);
          292         while(addrlist.nused > 0)
          293                 free(addrlist.voidpptr[--addrlist.nused]);
          294         while(relist.nused > 0){
          295                 i = --relist.nused;
          296                 Strclose(relist.stringpptr[i]);
          297                 free(relist.stringpptr[i]);
          298         }
          299         while(stringlist.nused>0){
          300                 i = --stringlist.nused;
          301                 Strclose(stringlist.stringpptr[i]);
          302                 free(stringlist.stringpptr[i]);
          303         }
          304 }
          305 
          306 int
          307 lookup(int c)
          308 {
          309         int i;
          310 
          311         for(i=0; cmdtab[i].cmdc; i++)
          312                 if(cmdtab[i].cmdc == c)
          313                         return i;
          314         return -1;
          315 }
          316 
          317 void
          318 okdelim(int c)
          319 {
          320         if(c=='\\' || ('a'<=c && c<='z')
          321         || ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
          322                 error_c(Edelim, c);
          323 }
          324 
          325 void
          326 atnl(void)
          327 {
          328         skipbl();
          329         if(getch() != '\n')
          330                 error(Enewline);
          331 }
          332 
          333 void
          334 getrhs(String *s, int delim, int cmd)
          335 {
          336         int c;
          337 
          338         while((c = getch())>0 && c!=delim && c!='\n'){
          339                 if(c == '\\'){
          340                         if((c=getch()) <= 0)
          341                                 error(Ebadrhs);
          342                         if(c == '\n'){
          343                                 ungetch();
          344                                 c='\\';
          345                         }else if(c == 'n')
          346                                 c='\n';
          347                         else if(c!=delim && (cmd=='s' || c!='\\'))        /* s does its own */
          348                                 Straddc(s, '\\');
          349                 }
          350                 Straddc(s, c);
          351         }
          352         ungetch();        /* let client read whether delimeter, '\n' or whatever */
          353 }
          354 
          355 String *
          356 collecttoken(char *end)
          357 {
          358         String *s = newstring();
          359         int c;
          360 
          361         while((c=nextc())==' ' || c=='\t')
          362                 Straddc(s, getch()); /* blanks significant for getname() */
          363         while((c=getch())>0 && utfrune(end, c)==0)
          364                 Straddc(s, c);
          365         Straddc(s, 0);
          366         if(c != '\n')
          367                 atnl();
          368         return s;
          369 }
          370 
          371 String *
          372 collecttext(void)
          373 {
          374         String *s = newstring();
          375         int begline, i, c, delim;
          376 
          377         if(skipbl()=='\n'){
          378                 getch();
          379                 i = 0;
          380                 do{
          381                         begline = i;
          382                         while((c = getch())>0 && c!='\n')
          383                                 i++, Straddc(s, c);
          384                         i++, Straddc(s, '\n');
          385                         if(c < 0)
          386                                 goto Return;
          387                 }while(s->s[begline]!='.' || s->s[begline+1]!='\n');
          388                 Strdelete(s, s->n-2, s->n);
          389         }else{
          390                 okdelim(delim = getch());
          391                 getrhs(s, delim, 'a');
          392                 if(nextc()==delim)
          393                         getch();
          394                 atnl();
          395         }
          396     Return:
          397         Straddc(s, 0);                /* JUST FOR CMDPRINT() */
          398         return s;
          399 }
          400 
          401 Cmd *
          402 parsecmd(int nest)
          403 {
          404         int i, c;
          405         struct cmdtab *ct;
          406         Cmd *cp, *ncp;
          407         Cmd cmd;
          408 
          409         cmd.next = cmd.ccmd = 0;
          410         cmd.re = 0;
          411         cmd.flag = cmd.num = 0;
          412         cmd.addr = compoundaddr();
          413         if(skipbl() == -1)
          414                 return 0;
          415         if((c=getch())==-1)
          416                 return 0;
          417         cmd.cmdc = c;
          418         if(cmd.cmdc=='c' && nextc()=='d'){        /* sleazy two-character case */
          419                 getch();                /* the 'd' */
          420                 cmd.cmdc='c'|0x100;
          421         }
          422         i = lookup(cmd.cmdc);
          423         if(i >= 0){
          424                 if(cmd.cmdc == '\n')
          425                         goto Return;        /* let nl_cmd work it all out */
          426                 ct = &cmdtab[i];
          427                 if(ct->defaddr==aNo && cmd.addr)
          428                         error(Enoaddr);
          429                 if(ct->count)
          430                         cmd.num = getnum(ct->count);
          431                 if(ct->regexp){
          432                         /* x without pattern -> .*\n, indicated by cmd.re==0 */
          433                         /* X without pattern is all files */
          434                         if((ct->cmdc!='x' && ct->cmdc!='X') ||
          435                            ((c = nextc())!=' ' && c!='\t' && c!='\n')){
          436                                 skipbl();
          437                                 if((c = getch())=='\n' || c<0)
          438                                         error(Enopattern);
          439                                 okdelim(c);
          440                                 cmd.re = getregexp(c);
          441                                 if(ct->cmdc == 's'){
          442                                         cmd.ctext = newstring();
          443                                         getrhs(cmd.ctext, c, 's');
          444                                         if(nextc() == c){
          445                                                 getch();
          446                                                 if(nextc() == 'g')
          447                                                         cmd.flag = getch();
          448                                         }
          449                         
          450                                 }
          451                         }
          452                 }
          453                 if(ct->addr && (cmd.caddr=simpleaddr())==0)
          454                         error(Eaddress);
          455                 if(ct->defcmd){
          456                         if(skipbl() == '\n'){
          457                                 getch();
          458                                 cmd.ccmd = newcmd();
          459                                 cmd.ccmd->cmdc = ct->defcmd;
          460                         }else if((cmd.ccmd = parsecmd(nest))==0)
          461                                 panic("defcmd");
          462                 }else if(ct->text)
          463                         cmd.ctext = collecttext();
          464                 else if(ct->token)
          465                         cmd.ctext = collecttoken(ct->token);
          466                 else
          467                         atnl();
          468         }else
          469                 switch(cmd.cmdc){
          470                 case '{':
          471                         cp = 0;
          472                         do{
          473                                 if(skipbl()=='\n')
          474                                         getch();
          475                                 ncp = parsecmd(nest+1);
          476                                 if(cp)
          477                                         cp->next = ncp;
          478                                 else
          479                                         cmd.ccmd = ncp;
          480                         }while(cp = ncp);
          481                         break;
          482                 case '}':
          483                         atnl();
          484                         if(nest==0)
          485                                 error(Enolbrace);
          486                         return 0;
          487                 default:
          488                         error_c(Eunk, cmd.cmdc);
          489                 }
          490     Return:
          491         cp = newcmd();
          492         *cp = cmd;
          493         return cp;
          494 }
          495 
          496 String*                                /* BUGGERED */
          497 getregexp(int delim)
          498 {
          499         String *r = newre();
          500         int c;
          501 
          502         for(Strzero(&genstr); ; Straddc(&genstr, c))
          503                 if((c = getch())=='\\'){
          504                         if(nextc()==delim)
          505                                 c = getch();
          506                         else if(nextc()=='\\'){
          507                                 Straddc(&genstr, c);
          508                                 c = getch();
          509                         }
          510                 }else if(c==delim || c=='\n')
          511                         break;
          512         if(c!=delim && c)
          513                 ungetch();
          514         if(genstr.n > 0){
          515                 patset = TRUE;
          516                 Strduplstr(&lastpat, &genstr);
          517                 Straddc(&lastpat, '\0');
          518         }
          519         if(lastpat.n <= 1)
          520                 error(Epattern);
          521         Strduplstr(r, &lastpat);
          522         return r;
          523 }
          524 
          525 Addr *
          526 simpleaddr(void)
          527 {
          528         Addr addr;
          529         Addr *ap, *nap;
          530 
          531         addr.next = 0;
          532         addr.left = 0;
          533         addr.num = 0;
          534         switch(skipbl()){
          535         case '#':
          536                 addr.type = getch();
          537                 addr.num = getnum(1);
          538                 break;
          539         case '0': case '1': case '2': case '3': case '4':
          540         case '5': case '6': case '7': case '8': case '9': 
          541                 addr.num = getnum(1);
          542                 addr.type='l';
          543                 break;
          544         case '/': case '?': case '"':
          545                 addr.are = getregexp(addr.type = getch());
          546                 break;
          547         case '.':
          548         case '$':
          549         case '+':
          550         case '-':
          551         case '\'':
          552                 addr.type = getch();
          553                 break;
          554         default:
          555                 return 0;
          556         }
          557         if(addr.next = simpleaddr())
          558                 switch(addr.next->type){
          559                 case '.':
          560                 case '$':
          561                 case '\'':
          562                         if(addr.type!='"')
          563                 case '"':
          564                                 error(Eaddress);
          565                         break;
          566                 case 'l':
          567                 case '#':
          568                         if(addr.type=='"')
          569                                 break;
          570                         /* fall through */
          571                 case '/':
          572                 case '?':
          573                         if(addr.type!='+' && addr.type!='-'){
          574                                 /* insert the missing '+' */
          575                                 nap = newaddr();
          576                                 nap->type='+';
          577                                 nap->next = addr.next;
          578                                 addr.next = nap;
          579                         }
          580                         break;
          581                 case '+':
          582                 case '-':
          583                         break;
          584                 default:
          585                         panic("simpleaddr");
          586                 }
          587         ap = newaddr();
          588         *ap = addr;
          589         return ap;
          590 }
          591 
          592 Addr *
          593 compoundaddr(void)
          594 {
          595         Addr addr;
          596         Addr *ap, *next;
          597 
          598         addr.left = simpleaddr();
          599         if((addr.type = skipbl())!=',' && addr.type!=';')
          600                 return addr.left;
          601         getch();
          602         next = addr.next = compoundaddr();
          603         if(next && (next->type==',' || next->type==';') && next->left==0)
          604                 error(Eaddress);
          605         ap = newaddr();
          606         *ap = addr;
          607         return ap;
          608 }