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