cmd.c 11 KB

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