edit.c 12 KB

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