cmd.c 11 KB

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