edit.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <draw.h>
  4. #include <thread.h>
  5. #include <cursor.h>
  6. #include <mouse.h>
  7. #include <keyboard.h>
  8. #include <frame.h>
  9. #include <fcall.h>
  10. #include <plumb.h>
  11. #include "dat.h"
  12. #include "edit.h"
  13. #include "fns.h"
  14. static char linex[]="\n";
  15. static char wordx[]=" \t\n";
  16. struct cmdtab cmdtab[]={
  17. /* cmdc text regexp addr defcmd defaddr count token fn */
  18. '\n', 0, 0, 0, 0, aDot, 0, 0, nl_cmd,
  19. 'a', 1, 0, 0, 0, aDot, 0, 0, a_cmd,
  20. 'b', 0, 0, 0, 0, aNo, 0, linex, b_cmd,
  21. 'c', 1, 0, 0, 0, aDot, 0, 0, c_cmd,
  22. 'd', 0, 0, 0, 0, aDot, 0, 0, d_cmd,
  23. 'e', 0, 0, 0, 0, aNo, 0, wordx, e_cmd,
  24. 'f', 0, 0, 0, 0, aNo, 0, wordx, f_cmd,
  25. 'g', 0, 1, 0, 'p', aDot, 0, 0, g_cmd,
  26. 'i', 1, 0, 0, 0, aDot, 0, 0, i_cmd,
  27. 'm', 0, 0, 1, 0, aDot, 0, 0, m_cmd,
  28. 'p', 0, 0, 0, 0, aDot, 0, 0, p_cmd,
  29. 'r', 0, 0, 0, 0, aDot, 0, wordx, e_cmd,
  30. 's', 0, 1, 0, 0, aDot, 1, 0, s_cmd,
  31. 't', 0, 0, 1, 0, aDot, 0, 0, m_cmd,
  32. 'u', 0, 0, 0, 0, aNo, 2, 0, u_cmd,
  33. 'v', 0, 1, 0, 'p', aDot, 0, 0, g_cmd,
  34. 'w', 0, 0, 0, 0, aAll, 0, wordx, w_cmd,
  35. 'x', 0, 1, 0, 'p', aDot, 0, 0, x_cmd,
  36. 'y', 0, 1, 0, 'p', aDot, 0, 0, x_cmd,
  37. '=', 0, 0, 0, 0, aDot, 0, linex, eq_cmd,
  38. 'B', 0, 0, 0, 0, aNo, 0, linex, B_cmd,
  39. 'D', 0, 0, 0, 0, aNo, 0, linex, D_cmd,
  40. 'X', 0, 1, 0, 'f', aNo, 0, 0, X_cmd,
  41. 'Y', 0, 1, 0, 'f', aNo, 0, 0, X_cmd,
  42. '<', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd,
  43. '|', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd,
  44. '>', 0, 0, 0, 0, aDot, 0, linex, pipe_cmd,
  45. /* deliberately unimplemented:
  46. 'k', 0, 0, 0, 0, aDot, 0, 0, k_cmd,
  47. 'n', 0, 0, 0, 0, aNo, 0, 0, n_cmd,
  48. 'q', 0, 0, 0, 0, aNo, 0, 0, q_cmd,
  49. '!', 0, 0, 0, 0, aNo, 0, linex, plan9_cmd,
  50. */
  51. 0, 0, 0, 0, 0, 0, 0, 0,
  52. };
  53. Cmd *parsecmd(int);
  54. Addr *compoundaddr(void);
  55. Addr *simpleaddr(void);
  56. void freecmd(void);
  57. void okdelim(int);
  58. Rune *cmdstartp;
  59. Rune *cmdendp;
  60. Rune *cmdp;
  61. Channel *editerrc;
  62. String *lastpat;
  63. int patset;
  64. List cmdlist;
  65. List addrlist;
  66. List stringlist;
  67. Text *curtext;
  68. int editing = Inactive;
  69. String* newstring(int);
  70. void
  71. editthread(void*)
  72. {
  73. Cmd *cmdp;
  74. threadsetname("editthread");
  75. while((cmdp=parsecmd(0)) != 0){
  76. // ocurfile = curfile;
  77. // loaded = curfile && !curfile->unread;
  78. if(cmdexec(curtext, cmdp) == 0)
  79. break;
  80. freecmd();
  81. }
  82. sendp(editerrc, nil);
  83. }
  84. void
  85. allelogterm(Window *w, void*)
  86. {
  87. elogterm(w->body.file);
  88. }
  89. void
  90. alleditinit(Window *w, void*)
  91. {
  92. textcommit(&w->tag, TRUE);
  93. textcommit(&w->body, TRUE);
  94. w->body.file->editclean = FALSE;
  95. }
  96. void
  97. allupdate(Window *w, void*)
  98. {
  99. Text *t;
  100. int i;
  101. File *f;
  102. t = &w->body;
  103. f = t->file;
  104. if(f->curtext != t) /* do curtext only */
  105. return;
  106. if(f->elog.type == Null)
  107. elogterm(f);
  108. else if(f->elog.type != Empty){
  109. elogapply(f);
  110. if(f->editclean){
  111. f->mod = FALSE;
  112. for(i=0; i<f->ntext; i++)
  113. f->text[i]->w->dirty = FALSE;
  114. }
  115. }
  116. textsetselect(t, t->q0, t->q1);
  117. textscrdraw(t);
  118. winsettag(w);
  119. }
  120. void
  121. editerror(char *fmt, ...)
  122. {
  123. va_list arg;
  124. char *s;
  125. va_start(arg, fmt);
  126. s = vsmprint(fmt, arg);
  127. va_end(arg);
  128. freecmd();
  129. allwindows(allelogterm, nil); /* truncate the edit logs */
  130. sendp(editerrc, s);
  131. threadexits(nil);
  132. }
  133. void
  134. editcmd(Text *ct, Rune *r, uint n)
  135. {
  136. char *err;
  137. if(n == 0)
  138. return;
  139. if(2*n > RBUFSIZE){
  140. warning(nil, "string too long\n");
  141. return;
  142. }
  143. allwindows(alleditinit, nil);
  144. if(cmdstartp)
  145. free(cmdstartp);
  146. cmdstartp = runemalloc(n+2);
  147. runemove(cmdstartp, r, n);
  148. if(r[n] != '\n')
  149. cmdstartp[n++] = '\n';
  150. cmdstartp[n] = '\0';
  151. cmdendp = cmdstartp+n;
  152. cmdp = cmdstartp;
  153. if(ct->w == nil)
  154. curtext = nil;
  155. else
  156. curtext = &ct->w->body;
  157. resetxec();
  158. if(editerrc == nil){
  159. editerrc = chancreate(sizeof(char*), 0);
  160. lastpat = allocstring(0);
  161. }
  162. threadcreate(editthread, nil, STACK);
  163. err = recvp(editerrc);
  164. editing = Inactive;
  165. if(err != nil){
  166. if(err[0] != '\0')
  167. warning(nil, "Edit: %s\n", err);
  168. free(err);
  169. }
  170. /* update everyone whose edit log has data */
  171. allwindows(allupdate, nil);
  172. }
  173. int
  174. getch(void)
  175. {
  176. if(*cmdp == *cmdendp)
  177. return -1;
  178. return *cmdp++;
  179. }
  180. int
  181. nextc(void)
  182. {
  183. if(*cmdp == *cmdendp)
  184. return -1;
  185. return *cmdp;
  186. }
  187. void
  188. ungetch(void)
  189. {
  190. if(--cmdp < cmdstartp)
  191. error("ungetch");
  192. }
  193. long
  194. getnum(int signok)
  195. {
  196. long n;
  197. int c, sign;
  198. n = 0;
  199. sign = 1;
  200. if(signok>1 && nextc()=='-'){
  201. sign = -1;
  202. getch();
  203. }
  204. if((c=nextc())<'0' || '9'<c) /* no number defaults to 1 */
  205. return sign;
  206. while('0'<=(c=getch()) && c<='9')
  207. n = n*10 + (c-'0');
  208. ungetch();
  209. return sign*n;
  210. }
  211. int
  212. cmdskipbl(void)
  213. {
  214. int c;
  215. do
  216. c = getch();
  217. while(c==' ' || c=='\t');
  218. if(c >= 0)
  219. ungetch();
  220. return c;
  221. }
  222. /*
  223. * Check that list has room for one more element.
  224. */
  225. void
  226. growlist(List *l)
  227. {
  228. if(l->listptr==0 || l->nalloc==0){
  229. l->nalloc = INCR;
  230. l->listptr = emalloc(INCR*sizeof(long));
  231. l->nused = 0;
  232. }else if(l->nused == l->nalloc){
  233. l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(long));
  234. memset((void*)(l->longptr+l->nalloc), 0, INCR*sizeof(long));
  235. l->nalloc += INCR;
  236. }
  237. }
  238. /*
  239. * Remove the ith element from the list
  240. */
  241. void
  242. dellist(List *l, int i)
  243. {
  244. memmove(&l->longptr[i], &l->longptr[i+1], (l->nused-(i+1))*sizeof(long));
  245. l->nused--;
  246. }
  247. /*
  248. * Add a new element, whose position is i, to the list
  249. */
  250. void
  251. inslist(List *l, int i, long val)
  252. {
  253. growlist(l);
  254. memmove(&l->longptr[i+1], &l->longptr[i], (l->nused-i)*sizeof(long));
  255. l->longptr[i] = val;
  256. l->nused++;
  257. }
  258. void
  259. listfree(List *l)
  260. {
  261. free(l->listptr);
  262. free(l);
  263. }
  264. String*
  265. allocstring(int n)
  266. {
  267. String *s;
  268. s = emalloc(sizeof(String));
  269. s->n = n;
  270. s->nalloc = n+10;
  271. s->r = emalloc(s->nalloc*sizeof(Rune));
  272. s->r[n] = '\0';
  273. return s;
  274. }
  275. void
  276. freestring(String *s)
  277. {
  278. free(s->r);
  279. free(s);
  280. }
  281. Cmd*
  282. newcmd(void){
  283. Cmd *p;
  284. p = emalloc(sizeof(Cmd));
  285. inslist(&cmdlist, cmdlist.nused, (long)p);
  286. return p;
  287. }
  288. String*
  289. newstring(int n)
  290. {
  291. String *p;
  292. p = allocstring(n);
  293. inslist(&stringlist, stringlist.nused, (long)p);
  294. return p;
  295. }
  296. Addr*
  297. newaddr(void)
  298. {
  299. Addr *p;
  300. p = emalloc(sizeof(Addr));
  301. inslist(&addrlist, addrlist.nused, (long)p);
  302. return p;
  303. }
  304. void
  305. freecmd(void)
  306. {
  307. int i;
  308. while(cmdlist.nused > 0)
  309. free(cmdlist.ucharptr[--cmdlist.nused]);
  310. while(addrlist.nused > 0)
  311. free(addrlist.ucharptr[--addrlist.nused]);
  312. while(stringlist.nused>0){
  313. i = --stringlist.nused;
  314. freestring(stringlist.stringptr[i]);
  315. }
  316. }
  317. void
  318. okdelim(int c)
  319. {
  320. if(c=='\\' || ('a'<=c && c<='z')
  321. || ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
  322. editerror("bad delimiter %c\n", c);
  323. }
  324. void
  325. atnl(void)
  326. {
  327. int c;
  328. cmdskipbl();
  329. c = getch();
  330. if(c != '\n')
  331. editerror("newline expected (saw %C)", c);
  332. }
  333. void
  334. Straddc(String *s, int c)
  335. {
  336. if(s->n+1 >= s->nalloc){
  337. s->nalloc += 10;
  338. s->r = erealloc(s->r, s->nalloc*sizeof(Rune));
  339. }
  340. s->r[s->n++] = c;
  341. s->r[s->n] = '\0';
  342. }
  343. void
  344. getrhs(String *s, int delim, int cmd)
  345. {
  346. int c;
  347. while((c = getch())>0 && c!=delim && c!='\n'){
  348. if(c == '\\'){
  349. if((c=getch()) <= 0)
  350. error("bad right hand side");
  351. if(c == '\n'){
  352. ungetch();
  353. c='\\';
  354. }else if(c == 'n')
  355. c='\n';
  356. else if(c!=delim && (cmd=='s' || c!='\\')) /* s does its own */
  357. Straddc(s, '\\');
  358. }
  359. Straddc(s, c);
  360. }
  361. ungetch(); /* let client read whether delimiter, '\n' or whatever */
  362. }
  363. String *
  364. collecttoken(char *end)
  365. {
  366. String *s = newstring(0);
  367. int c;
  368. while((c=nextc())==' ' || c=='\t')
  369. Straddc(s, getch()); /* blanks significant for getname() */
  370. while((c=getch())>0 && utfrune(end, c)==0)
  371. Straddc(s, c);
  372. if(c != '\n')
  373. atnl();
  374. return s;
  375. }
  376. String *
  377. collecttext(void)
  378. {
  379. String *s;
  380. int begline, i, c, delim;
  381. s = newstring(0);
  382. if(cmdskipbl()=='\n'){
  383. getch();
  384. i = 0;
  385. do{
  386. begline = i;
  387. while((c = getch())>0 && c!='\n')
  388. i++, Straddc(s, c);
  389. i++, Straddc(s, '\n');
  390. if(c < 0)
  391. goto Return;
  392. }while(s->r[begline]!='.' || s->r[begline+1]!='\n');
  393. s->r[s->n-2] = '\0';
  394. s->n -= 2;
  395. }else{
  396. okdelim(delim = getch());
  397. getrhs(s, delim, 'a');
  398. if(nextc()==delim)
  399. getch();
  400. atnl();
  401. }
  402. Return:
  403. return s;
  404. }
  405. int
  406. cmdlookup(int c)
  407. {
  408. int i;
  409. for(i=0; cmdtab[i].cmdc; i++)
  410. if(cmdtab[i].cmdc == c)
  411. return i;
  412. return -1;
  413. }
  414. Cmd*
  415. parsecmd(int nest)
  416. {
  417. int i, c;
  418. struct cmdtab *ct;
  419. Cmd *cp, *ncp;
  420. Cmd cmd;
  421. cmd.next = cmd.cmd = 0;
  422. cmd.re = 0;
  423. cmd.flag = cmd.num = 0;
  424. cmd.addr = compoundaddr();
  425. if(cmdskipbl() == -1)
  426. return 0;
  427. if((c=getch())==-1)
  428. return 0;
  429. cmd.cmdc = c;
  430. if(cmd.cmdc=='c' && nextc()=='d'){ /* sleazy two-character case */
  431. getch(); /* the 'd' */
  432. cmd.cmdc='c'|0x100;
  433. }
  434. i = cmdlookup(cmd.cmdc);
  435. if(i >= 0){
  436. if(cmd.cmdc == '\n')
  437. goto Return; /* let nl_cmd work it all out */
  438. ct = &cmdtab[i];
  439. if(ct->defaddr==aNo && cmd.addr)
  440. editerror("command takes no address");
  441. if(ct->count)
  442. cmd.num = getnum(ct->count);
  443. if(ct->regexp){
  444. /* x without pattern -> .*\n, indicated by cmd.re==0 */
  445. /* X without pattern is all files */
  446. if((ct->cmdc!='x' && ct->cmdc!='X') ||
  447. ((c = nextc())!=' ' && c!='\t' && c!='\n')){
  448. cmdskipbl();
  449. if((c = getch())=='\n' || c<0)
  450. editerror("no address");
  451. okdelim(c);
  452. cmd.re = getregexp(c);
  453. if(ct->cmdc == 's'){
  454. cmd.text = newstring(0);
  455. getrhs(cmd.text, c, 's');
  456. if(nextc() == c){
  457. getch();
  458. if(nextc() == 'g')
  459. cmd.flag = getch();
  460. }
  461. }
  462. }
  463. }
  464. if(ct->addr && (cmd.mtaddr=simpleaddr())==0)
  465. editerror("bad address");
  466. if(ct->defcmd){
  467. if(cmdskipbl() == '\n'){
  468. getch();
  469. cmd.cmd = newcmd();
  470. cmd.cmd->cmdc = ct->defcmd;
  471. }else if((cmd.cmd = parsecmd(nest))==0)
  472. error("defcmd");
  473. }else if(ct->text)
  474. cmd.text = collecttext();
  475. else if(ct->token)
  476. cmd.text = collecttoken(ct->token);
  477. else
  478. atnl();
  479. }else
  480. switch(cmd.cmdc){
  481. case '{':
  482. cp = 0;
  483. do{
  484. if(cmdskipbl()=='\n')
  485. getch();
  486. ncp = parsecmd(nest+1);
  487. if(cp)
  488. cp->next = ncp;
  489. else
  490. cmd.cmd = ncp;
  491. }while(cp = ncp);
  492. break;
  493. case '}':
  494. atnl();
  495. if(nest==0)
  496. editerror("right brace with no left brace");
  497. return 0;
  498. default:
  499. editerror("unknown command %c", cmd.cmdc);
  500. }
  501. Return:
  502. cp = newcmd();
  503. *cp = cmd;
  504. return cp;
  505. }
  506. String*
  507. getregexp(int delim)
  508. {
  509. String *buf, *r;
  510. int i, c;
  511. buf = allocstring(0);
  512. for(i=0; ; i++){
  513. if((c = getch())=='\\'){
  514. if(nextc()==delim)
  515. c = getch();
  516. else if(nextc()=='\\'){
  517. Straddc(buf, c);
  518. c = getch();
  519. }
  520. }else if(c==delim || c=='\n')
  521. break;
  522. if(i >= RBUFSIZE)
  523. editerror("regular expression too long");
  524. Straddc(buf, c);
  525. }
  526. if(c!=delim && c)
  527. ungetch();
  528. if(buf->n > 0){
  529. patset = TRUE;
  530. freestring(lastpat);
  531. lastpat = buf;
  532. }else
  533. freestring(buf);
  534. if(lastpat->n == 0)
  535. editerror("no regular expression defined");
  536. r = newstring(lastpat->n);
  537. runemove(r->r, lastpat->r, lastpat->n); /* newstring put \0 at end */
  538. return r;
  539. }
  540. Addr *
  541. simpleaddr(void)
  542. {
  543. Addr addr;
  544. Addr *ap, *nap;
  545. addr.next = 0;
  546. addr.left = 0;
  547. switch(cmdskipbl()){
  548. case '#':
  549. addr.type = getch();
  550. addr.num = getnum(1);
  551. break;
  552. case '0': case '1': case '2': case '3': case '4':
  553. case '5': case '6': case '7': case '8': case '9':
  554. addr.num = getnum(1);
  555. addr.type='l';
  556. break;
  557. case '/': case '?': case '"':
  558. addr.re = getregexp(addr.type = getch());
  559. break;
  560. case '.':
  561. case '$':
  562. case '+':
  563. case '-':
  564. case '\'':
  565. addr.type = getch();
  566. break;
  567. default:
  568. return 0;
  569. }
  570. if(addr.next = simpleaddr())
  571. switch(addr.next->type){
  572. case '.':
  573. case '$':
  574. case '\'':
  575. if(addr.type!='"')
  576. case '"':
  577. editerror("bad address syntax");
  578. break;
  579. case 'l':
  580. case '#':
  581. if(addr.type=='"')
  582. break;
  583. /* fall through */
  584. case '/':
  585. case '?':
  586. if(addr.type!='+' && addr.type!='-'){
  587. /* insert the missing '+' */
  588. nap = newaddr();
  589. nap->type='+';
  590. nap->next = addr.next;
  591. addr.next = nap;
  592. }
  593. break;
  594. case '+':
  595. case '-':
  596. break;
  597. default:
  598. error("simpleaddr");
  599. }
  600. ap = newaddr();
  601. *ap = addr;
  602. return ap;
  603. }
  604. Addr *
  605. compoundaddr(void)
  606. {
  607. Addr addr;
  608. Addr *ap, *next;
  609. addr.left = simpleaddr();
  610. if((addr.type = cmdskipbl())!=',' && addr.type!=';')
  611. return addr.left;
  612. getch();
  613. next = addr.next = compoundaddr();
  614. if(next && (next->type==',' || next->type==';') && next->left==0)
  615. editerror("bad address syntax");
  616. ap = newaddr();
  617. *ap = addr;
  618. return ap;
  619. }