cmd.c 10 KB

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