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