ecmd.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329
  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. int Glooping;
  23. int nest;
  24. char Enoname[] = "no file name given";
  25. Address addr;
  26. File *menu;
  27. Rangeset sel;
  28. extern Text* curtext;
  29. Rune *collection;
  30. int ncollection;
  31. int append(File*, Cmd*, int32_t);
  32. int pdisplay(File*);
  33. void pfilename(File*);
  34. void looper(File*, Cmd*, int);
  35. void filelooper(Cmd*, int);
  36. void linelooper(File*, Cmd*);
  37. Address lineaddr(int32_t, Address, int);
  38. int filematch(File*, String*);
  39. File *tofile(String*);
  40. Rune* cmdname(File *f, String *s, int);
  41. void runpipe(Text*, int, Rune*, int, int);
  42. void
  43. clearcollection(void)
  44. {
  45. free(collection);
  46. collection = nil;
  47. ncollection = 0;
  48. }
  49. void
  50. resetxec(void)
  51. {
  52. Glooping = nest = 0;
  53. clearcollection();
  54. }
  55. void
  56. mkaddr(Address *a, File *f)
  57. {
  58. a->r.q0 = f->curtext->q0;
  59. a->r.q1 = f->curtext->q1;
  60. a->f = f;
  61. }
  62. int
  63. cmdexec(Text *t, Cmd *cp)
  64. {
  65. int i;
  66. Addr *ap;
  67. File *f;
  68. Window *w;
  69. Address dot;
  70. if(t == nil)
  71. w = nil;
  72. else
  73. w = t->w;
  74. if(w==nil && (cp->addr==0 || cp->addr->type!='"') &&
  75. !utfrune("bBnqUXY!", cp->cmdc) &&
  76. !(cp->cmdc=='D' && cp->text))
  77. editerror("no current window");
  78. i = cmdlookup(cp->cmdc); /* will be -1 for '{' */
  79. f = nil;
  80. if(t && t->w){
  81. t = &t->w->body;
  82. f = t->file;
  83. f->curtext = t;
  84. }
  85. if(i>=0 && cmdtab[i].defaddr != aNo){
  86. if((ap=cp->addr)==0 && cp->cmdc!='\n'){
  87. cp->addr = ap = newaddr();
  88. ap->type = '.';
  89. if(cmdtab[i].defaddr == aAll)
  90. ap->type = '*';
  91. }else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){
  92. ap->next = newaddr();
  93. ap->next->type = '.';
  94. if(cmdtab[i].defaddr == aAll)
  95. ap->next->type = '*';
  96. }
  97. if(cp->addr){ /* may be false for '\n' (only) */
  98. static Address none = {0,0,nil};
  99. if(f){
  100. mkaddr(&dot, f);
  101. addr = cmdaddress(ap, dot, 0);
  102. }else /* a " */
  103. addr = cmdaddress(ap, none, 0);
  104. f = addr.f;
  105. t = f->curtext;
  106. }
  107. }
  108. switch(cp->cmdc){
  109. case '{':
  110. mkaddr(&dot, f);
  111. if(cp->addr != nil)
  112. dot = cmdaddress(cp->addr, dot, 0);
  113. for(cp = cp->cmd; cp; cp = cp->next){
  114. if(dot.r.q1 > t->file->Buffer.nc)
  115. editerror("dot extends past end of buffer during { command");
  116. t->q0 = dot.r.q0;
  117. t->q1 = dot.r.q1;
  118. cmdexec(t, cp);
  119. }
  120. break;
  121. default:
  122. if(i < 0)
  123. editerror("unknown command %c in cmdexec", cp->cmdc);
  124. i = (*cmdtab[i].fn)(t, cp);
  125. return i;
  126. }
  127. return 1;
  128. }
  129. char*
  130. edittext(Window *w, int q, Rune *r, int nr)
  131. {
  132. File *f;
  133. f = w->body.file;
  134. switch(editing){
  135. case Inactive:
  136. return "permission denied";
  137. case Inserting:
  138. eloginsert(f, q, r, nr);
  139. return nil;
  140. case Collecting:
  141. collection = runerealloc(collection, ncollection+nr+1);
  142. runemove(collection+ncollection, r, nr);
  143. ncollection += nr;
  144. collection[ncollection] = '\0';
  145. return nil;
  146. default:
  147. return "unknown state in edittext";
  148. }
  149. }
  150. /* string is known to be NUL-terminated */
  151. Rune*
  152. filelist(Text *t, Rune *r, int nr)
  153. {
  154. if(nr == 0)
  155. return nil;
  156. r = skipbl(r, nr, &nr);
  157. if(r[0] != '<')
  158. return runestrdup(r);
  159. /* use < command to collect text */
  160. clearcollection();
  161. runpipe(t, '<', r+1, nr-1, Collecting);
  162. return collection;
  163. }
  164. int
  165. a_cmd(Text *t, Cmd *cp)
  166. {
  167. return append(t->file, cp, addr.r.q1);
  168. }
  169. int
  170. b_cmd(Text*unused, Cmd *cp)
  171. {
  172. File *f;
  173. f = tofile(cp->text);
  174. if(nest == 0)
  175. pfilename(f);
  176. curtext = f->curtext;
  177. return TRUE;
  178. }
  179. int
  180. B_cmd(Text *t, Cmd *cp)
  181. {
  182. Rune *list, *r, *s;
  183. int nr;
  184. list = filelist(t, cp->text->r, cp->text->n);
  185. if(list == nil)
  186. editerror(Enoname);
  187. r = list;
  188. nr = runestrlen(r);
  189. r = skipbl(r, nr, &nr);
  190. if(nr == 0)
  191. new(t, t, nil, 0, 0, r, 0);
  192. else while(nr > 0){
  193. s = findbl(r, nr, &nr);
  194. *s = '\0';
  195. new(t, t, nil, 0, 0, r, runestrlen(r));
  196. if(nr > 0)
  197. r = skipbl(s+1, nr-1, &nr);
  198. }
  199. clearcollection();
  200. return TRUE;
  201. }
  202. int
  203. c_cmd(Text *t, Cmd *cp)
  204. {
  205. elogreplace(t->file, addr.r.q0, addr.r.q1, cp->text->r, cp->text->n);
  206. t->q0 = addr.r.q0;
  207. t->q1 = addr.r.q0;
  208. return TRUE;
  209. }
  210. int
  211. d_cmd(Text *t, Cmd*c)
  212. {
  213. if(addr.r.q1 > addr.r.q0)
  214. elogdelete(t->file, addr.r.q0, addr.r.q1);
  215. t->q0 = addr.r.q0;
  216. t->q1 = addr.r.q0;
  217. return TRUE;
  218. }
  219. void
  220. D1(Text *t)
  221. {
  222. if(t->w->body.file->ntext>1 || winclean(t->w, FALSE))
  223. colclose(t->col, t->w, TRUE);
  224. }
  225. int
  226. D_cmd(Text *t, Cmd *cp)
  227. {
  228. Rune *list, *r, *s, *n;
  229. int nr, nn;
  230. Window *w;
  231. Runestr dir, rs;
  232. char buf[128];
  233. list = filelist(t, cp->text->r, cp->text->n);
  234. if(list == nil){
  235. D1(t);
  236. return TRUE;
  237. }
  238. dir = dirname(t, nil, 0);
  239. r = list;
  240. nr = runestrlen(r);
  241. r = skipbl(r, nr, &nr);
  242. do{
  243. s = findbl(r, nr, &nr);
  244. *s = '\0';
  245. /* first time through, could be empty string, meaning delete file empty name */
  246. nn = runestrlen(r);
  247. if(r[0]=='/' || nn==0 || dir.nr==0){
  248. rs.r = runestrdup(r);
  249. rs.nr = nn;
  250. }else{
  251. n = runemalloc(dir.nr+1+nn);
  252. runemove(n, dir.r, dir.nr);
  253. n[dir.nr] = '/';
  254. runemove(n+dir.nr+1, r, nn);
  255. rs = cleanrname((Runestr){n, dir.nr+1+nn});
  256. }
  257. w = lookfile(rs.r, rs.nr);
  258. if(w == nil){
  259. snprint(buf, sizeof buf, "no such file %.*S", rs.nr, rs.r);
  260. free(rs.r);
  261. editerror(buf);
  262. }
  263. free(rs.r);
  264. D1(&w->body);
  265. if(nr > 0)
  266. r = skipbl(s+1, nr-1, &nr);
  267. }while(nr > 0);
  268. clearcollection();
  269. free(dir.r);
  270. return TRUE;
  271. }
  272. static int
  273. readloader(void *v, uint q0, Rune *r, int nr)
  274. {
  275. if(nr > 0)
  276. eloginsert(v, q0, r, nr);
  277. return 0;
  278. }
  279. int
  280. e_cmd(Text *t, Cmd *cp)
  281. {
  282. Rune *name;
  283. File *f;
  284. int i, isdir, q0, q1, fd, nulls, samename, allreplaced;
  285. char *s, tmp[128];
  286. Dir *d;
  287. f = t->file;
  288. q0 = addr.r.q0;
  289. q1 = addr.r.q1;
  290. if(cp->cmdc == 'e'){
  291. if(winclean(t->w, TRUE)==FALSE)
  292. editerror(""); /* winclean generated message already */
  293. q0 = 0;
  294. q1 = f->Buffer.nc;
  295. }
  296. allreplaced = (q0==0 && q1==f->Buffer.nc);
  297. name = cmdname(f, cp->text, cp->cmdc=='e');
  298. if(name == nil)
  299. editerror(Enoname);
  300. i = runestrlen(name);
  301. samename = runeeq(name, i, t->file->name, t->file->nname);
  302. s = runetobyte(name, i);
  303. free(name);
  304. fd = open(s, OREAD);
  305. if(fd < 0){
  306. snprint(tmp, sizeof tmp, "can't open %s: %r", s);
  307. free(s);
  308. editerror(tmp);
  309. }
  310. d = dirfstat(fd);
  311. isdir = (d!=nil && (d->qid.type&QTDIR));
  312. free(d);
  313. if(isdir){
  314. close(fd);
  315. snprint(tmp, sizeof tmp, "%s is a directory", s);
  316. free(s);
  317. editerror(tmp);
  318. }
  319. elogdelete(f, q0, q1);
  320. nulls = 0;
  321. loadfile(fd, q1, &nulls, readloader, f);
  322. free(s);
  323. close(fd);
  324. if(nulls)
  325. warning(nil, "%s: NUL bytes elided\n", s);
  326. else if(allreplaced && samename)
  327. f->editclean = TRUE;
  328. return TRUE;
  329. }
  330. int
  331. f_cmd(Text *t, Cmd *cp)
  332. {
  333. Rune *name;
  334. String *str;
  335. String empty;
  336. if(cp->text == nil){
  337. empty.n = 0;
  338. empty.r = (Rune*)L"";
  339. str = &empty;
  340. }else
  341. str = cp->text;
  342. name = cmdname(t->file, str, TRUE);
  343. free(name);
  344. pfilename(t->file);
  345. return TRUE;
  346. }
  347. int
  348. g_cmd(Text *t, Cmd *cp)
  349. {
  350. if(t->file != addr.f){
  351. warning(nil, "internal error: g_cmd f!=addr.f\n");
  352. return FALSE;
  353. }
  354. if(rxcompile(cp->re->r) == FALSE)
  355. editerror("bad regexp in g command");
  356. if(rxexecute(t, nil, addr.r.q0, addr.r.q1, &sel) ^ cp->cmdc=='v'){
  357. t->q0 = addr.r.q0;
  358. t->q1 = addr.r.q1;
  359. return cmdexec(t, cp->cmd);
  360. }
  361. return TRUE;
  362. }
  363. int
  364. i_cmd(Text *t, Cmd *cp)
  365. {
  366. return append(t->file, cp, addr.r.q0);
  367. }
  368. void
  369. copy(File *f, Address addr2)
  370. {
  371. int32_t p;
  372. int ni;
  373. Rune *buf;
  374. buf = fbufalloc();
  375. for(p=addr.r.q0; p<addr.r.q1; p+=ni){
  376. ni = addr.r.q1-p;
  377. if(ni > RBUFSIZE)
  378. ni = RBUFSIZE;
  379. bufread(&f->Buffer, p, buf, ni);
  380. eloginsert(addr2.f, addr2.r.q1, buf, ni);
  381. }
  382. fbuffree(buf);
  383. }
  384. void
  385. move(File *f, Address addr2)
  386. {
  387. if(addr.f!=addr2.f || addr.r.q1<=addr2.r.q0){
  388. elogdelete(f, addr.r.q0, addr.r.q1);
  389. copy(f, addr2);
  390. }else if(addr.r.q0 >= addr2.r.q1){
  391. copy(f, addr2);
  392. elogdelete(f, addr.r.q0, addr.r.q1);
  393. }else if(addr.r.q0==addr2.r.q0 && addr.r.q1==addr2.r.q1){
  394. ; /* move to self; no-op */
  395. }else
  396. editerror("move overlaps itself");
  397. }
  398. int
  399. m_cmd(Text *t, Cmd *cp)
  400. {
  401. Address dot, addr2;
  402. mkaddr(&dot, t->file);
  403. addr2 = cmdaddress(cp->mtaddr, dot, 0);
  404. if(cp->cmdc == 'm')
  405. move(t->file, addr2);
  406. else
  407. copy(t->file, addr2);
  408. return TRUE;
  409. }
  410. int
  411. p_cmd(Text *t, Cmd*unusedCmd)
  412. {
  413. return pdisplay(t->file);
  414. }
  415. int
  416. s_cmd(Text *t, Cmd *cp)
  417. {
  418. int i, j, k, c, m, n, nrp, didsub;
  419. int32_t p1, op, delta;
  420. String *buf;
  421. Rangeset *rp;
  422. char *err;
  423. Rune *rbuf;
  424. n = cp->num;
  425. op= -1;
  426. if(rxcompile(cp->re->r) == FALSE)
  427. editerror("bad regexp in s command");
  428. nrp = 0;
  429. rp = nil;
  430. delta = 0;
  431. didsub = FALSE;
  432. for(p1 = addr.r.q0; p1<=addr.r.q1 && rxexecute(t, nil, p1, addr.r.q1, &sel); ){
  433. if(sel.r[0].q0 == sel.r[0].q1){ /* empty match? */
  434. if(sel.r[0].q0 == op){
  435. p1++;
  436. continue;
  437. }
  438. p1 = sel.r[0].q1+1;
  439. }else
  440. p1 = sel.r[0].q1;
  441. op = sel.r[0].q1;
  442. if(--n>0)
  443. continue;
  444. nrp++;
  445. rp = erealloc(rp, nrp*sizeof(Rangeset));
  446. rp[nrp-1] = sel;
  447. }
  448. rbuf = fbufalloc();
  449. buf = allocstring(0);
  450. for(m=0; m<nrp; m++){
  451. buf->n = 0;
  452. buf->r[0] = L'\0';
  453. sel = rp[m];
  454. for(i = 0; i<cp->text->n; i++)
  455. if((c = cp->text->r[i])=='\\' && i<cp->text->n-1){
  456. c = cp->text->r[++i];
  457. if('1'<=c && c<='9') {
  458. j = c-'0';
  459. if(sel.r[j].q1-sel.r[j].q0>RBUFSIZE){
  460. err = "replacement string too long";
  461. goto Err;
  462. }
  463. bufread(&t->file->Buffer, sel.r[j].q0, rbuf, sel.r[j].q1-sel.r[j].q0);
  464. for(k=0; k<sel.r[j].q1-sel.r[j].q0; k++)
  465. Straddc(buf, rbuf[k]);
  466. }else
  467. Straddc(buf, c);
  468. }else if(c!='&')
  469. Straddc(buf, c);
  470. else{
  471. if(sel.r[0].q1-sel.r[0].q0>RBUFSIZE){
  472. err = "right hand side too long in substitution";
  473. goto Err;
  474. }
  475. bufread(&t->file->Buffer, sel.r[0].q0, rbuf, sel.r[0].q1-sel.r[0].q0);
  476. for(k=0; k<sel.r[0].q1-sel.r[0].q0; k++)
  477. Straddc(buf, rbuf[k]);
  478. }
  479. elogreplace(t->file, sel.r[0].q0, sel.r[0].q1, buf->r, buf->n);
  480. delta -= sel.r[0].q1-sel.r[0].q0;
  481. delta += buf->n;
  482. didsub = 1;
  483. if(!cp->flag)
  484. break;
  485. }
  486. free(rp);
  487. freestring(buf);
  488. fbuffree(rbuf);
  489. if(!didsub && nest==0)
  490. editerror("no substitution");
  491. t->q0 = addr.r.q0;
  492. t->q1 = addr.r.q1;
  493. return TRUE;
  494. Err:
  495. free(rp);
  496. freestring(buf);
  497. fbuffree(rbuf);
  498. editerror(err);
  499. return FALSE;
  500. }
  501. int
  502. u_cmd(Text *t, Cmd *cp)
  503. {
  504. int n, oseq, flag;
  505. n = cp->num;
  506. flag = TRUE;
  507. if(n < 0){
  508. n = -n;
  509. flag = FALSE;
  510. }
  511. oseq = -1;
  512. while(n-->0 && t->file->seq!=0 && t->file->seq!=oseq){
  513. oseq = t->file->seq;
  514. undo(t, nil, nil, flag, 0, nil, 0);
  515. }
  516. return TRUE;
  517. }
  518. int
  519. w_cmd(Text *t, Cmd *cp)
  520. {
  521. Rune *r;
  522. File *f;
  523. f = t->file;
  524. if(f->seq == seq)
  525. editerror("can't write file with pending modifications");
  526. r = cmdname(f, cp->text, FALSE);
  527. if(r == nil)
  528. editerror("no name specified for 'w' command");
  529. putfile(f, addr.r.q0, addr.r.q1, r, runestrlen(r));
  530. /* r is freed by putfile */
  531. return TRUE;
  532. }
  533. int
  534. x_cmd(Text *t, Cmd *cp)
  535. {
  536. if(cp->re)
  537. looper(t->file, cp, cp->cmdc=='x');
  538. else
  539. linelooper(t->file, cp);
  540. return TRUE;
  541. }
  542. int
  543. X_cmd(Text*unused, Cmd *cp)
  544. {
  545. filelooper(cp, cp->cmdc=='X');
  546. return TRUE;
  547. }
  548. void
  549. runpipe(Text *t, int cmd, Rune *cr, int ncr, int state)
  550. {
  551. Rune *r, *s;
  552. int n;
  553. Runestr dir;
  554. Window *w;
  555. r = skipbl(cr, ncr, &n);
  556. if(n == 0)
  557. editerror("no command specified for %c", cmd);
  558. w = nil;
  559. if(state == Inserting){
  560. w = t->w;
  561. t->q0 = addr.r.q0;
  562. t->q1 = addr.r.q1;
  563. if(cmd == '<' || cmd=='|')
  564. elogdelete(t->file, t->q0, t->q1);
  565. }
  566. s = runemalloc(n+2);
  567. s[0] = cmd;
  568. runemove(s+1, r, n);
  569. n++;
  570. dir.r = nil;
  571. dir.nr = 0;
  572. if(t != nil)
  573. dir = dirname(t, nil, 0);
  574. if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
  575. free(dir.r);
  576. dir.r = nil;
  577. dir.nr = 0;
  578. }
  579. editing = state;
  580. if(t!=nil && t->w!=nil)
  581. incref(&t->w->Ref); /* run will decref */
  582. run(w, runetobyte(s, n), dir.r, dir.nr, TRUE, nil, nil, TRUE);
  583. free(s);
  584. if(t!=nil && t->w!=nil)
  585. winunlock(t->w);
  586. qunlock(&row.QLock);
  587. recvul(cedit);
  588. qlock(&row.QLock);
  589. editing = Inactive;
  590. if(t!=nil && t->w!=nil)
  591. winlock(t->w, 'M');
  592. }
  593. int
  594. pipe_cmd(Text *t, Cmd *cp)
  595. {
  596. runpipe(t, cp->cmdc, cp->text->r, cp->text->n, Inserting);
  597. return TRUE;
  598. }
  599. int32_t
  600. nlcount(Text *t, int32_t q0, int32_t q1)
  601. {
  602. int32_t nl;
  603. Rune *buf;
  604. int i, nbuf;
  605. buf = fbufalloc();
  606. nbuf = 0;
  607. i = nl = 0;
  608. while(q0 < q1){
  609. if(i == nbuf){
  610. nbuf = q1-q0;
  611. if(nbuf > RBUFSIZE)
  612. nbuf = RBUFSIZE;
  613. bufread(&t->file->Buffer, q0, buf, nbuf);
  614. i = 0;
  615. }
  616. if(buf[i++] == '\n')
  617. nl++;
  618. q0++;
  619. }
  620. fbuffree(buf);
  621. return nl;
  622. }
  623. void
  624. printposn(Text *t, int charsonly)
  625. {
  626. int32_t l1, l2;
  627. if (t != nil && t->file != nil && t->file->name != nil)
  628. warning(nil, "%.*S:", t->file->nname, t->file->name);
  629. if(!charsonly){
  630. l1 = 1+nlcount(t, 0, addr.r.q0);
  631. l2 = l1+nlcount(t, addr.r.q0, addr.r.q1);
  632. /* check if addr ends with '\n' */
  633. if(addr.r.q1>0 && addr.r.q1>addr.r.q0 && textreadc(t, addr.r.q1-1)=='\n')
  634. --l2;
  635. warning(nil, "%lud", l1);
  636. if(l2 != l1)
  637. warning(nil, ",%lud", l2);
  638. warning(nil, "\n");
  639. return;
  640. }
  641. warning(nil, "#%d", addr.r.q0);
  642. if(addr.r.q1 != addr.r.q0)
  643. warning(nil, ",#%d", addr.r.q1);
  644. warning(nil, "\n");
  645. }
  646. int
  647. eq_cmd(Text *t, Cmd *cp)
  648. {
  649. int charsonly;
  650. switch(cp->text->n){
  651. case 0:
  652. charsonly = FALSE;
  653. break;
  654. case 1:
  655. if(cp->text->r[0] == '#'){
  656. charsonly = TRUE;
  657. break;
  658. }
  659. default:
  660. SET(charsonly);
  661. editerror("newline expected");
  662. }
  663. printposn(t, charsonly);
  664. return TRUE;
  665. }
  666. int
  667. nl_cmd(Text *t, Cmd *cp)
  668. {
  669. Address a;
  670. File *f;
  671. f = t->file;
  672. if(cp->addr == 0){
  673. /* First put it on newline boundaries */
  674. mkaddr(&a, f);
  675. addr = lineaddr(0, a, -1);
  676. a = lineaddr(0, a, 1);
  677. addr.r.q1 = a.r.q1;
  678. if(addr.r.q0==t->q0 && addr.r.q1==t->q1){
  679. mkaddr(&a, f);
  680. addr = lineaddr(1, a, 1);
  681. }
  682. }
  683. textshow(t, addr.r.q0, addr.r.q1, 1);
  684. return TRUE;
  685. }
  686. int
  687. append(File *f, Cmd *cp, int32_t p)
  688. {
  689. if(cp->text->n > 0)
  690. eloginsert(f, p, cp->text->r, cp->text->n);
  691. f->curtext->q0 = p;
  692. f->curtext->q1 = p;
  693. return TRUE;
  694. }
  695. int
  696. pdisplay(File *f)
  697. {
  698. int32_t p1, p2;
  699. int np;
  700. Rune *buf;
  701. p1 = addr.r.q0;
  702. p2 = addr.r.q1;
  703. if(p2 > f->Buffer.nc)
  704. p2 = f->Buffer.nc;
  705. buf = fbufalloc();
  706. while(p1 < p2){
  707. np = p2-p1;
  708. if(np>RBUFSIZE-1)
  709. np = RBUFSIZE-1;
  710. bufread(&f->Buffer, p1, buf, np);
  711. buf[np] = L'\0';
  712. warning(nil, "%S", buf);
  713. p1 += np;
  714. }
  715. fbuffree(buf);
  716. f->curtext->q0 = addr.r.q0;
  717. f->curtext->q1 = addr.r.q1;
  718. return TRUE;
  719. }
  720. void
  721. pfilename(File *f)
  722. {
  723. int dirty;
  724. Window *w;
  725. w = f->curtext->w;
  726. /* same check for dirty as in settag, but we know ncache==0 */
  727. dirty = !w->isdir && !w->isscratch && f->mod;
  728. warning(nil, "%c%c%c %.*S\n", " '"[dirty],
  729. '+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name);
  730. }
  731. void
  732. loopcmd(File *f, Cmd *cp, Range *rp, int32_t nrp)
  733. {
  734. int32_t i;
  735. for(i=0; i<nrp; i++){
  736. f->curtext->q0 = rp[i].q0;
  737. f->curtext->q1 = rp[i].q1;
  738. cmdexec(f->curtext, cp);
  739. }
  740. }
  741. void
  742. looper(File *f, Cmd *cp, int xy)
  743. {
  744. int32_t p, op, nrp;
  745. Range r, tr;
  746. Range *rp;
  747. r = addr.r;
  748. op= xy? -1 : r.q0;
  749. nest++;
  750. if(rxcompile(cp->re->r) == FALSE)
  751. editerror("bad regexp in %c command", cp->cmdc);
  752. nrp = 0;
  753. rp = nil;
  754. for(p = r.q0; p<=r.q1; ){
  755. if(!rxexecute(f->curtext, nil, p, r.q1, &sel)){ /* no match, but y should still run */
  756. if(xy || op>r.q1)
  757. break;
  758. tr.q0 = op, tr.q1 = r.q1;
  759. p = r.q1+1; /* exit next loop */
  760. }else{
  761. if(sel.r[0].q0==sel.r[0].q1){ /* empty match? */
  762. if(sel.r[0].q0==op){
  763. p++;
  764. continue;
  765. }
  766. p = sel.r[0].q1+1;
  767. }else
  768. p = sel.r[0].q1;
  769. if(xy)
  770. tr = sel.r[0];
  771. else
  772. tr.q0 = op, tr.q1 = sel.r[0].q0;
  773. }
  774. op = sel.r[0].q1;
  775. nrp++;
  776. rp = erealloc(rp, nrp*sizeof(Range));
  777. rp[nrp-1] = tr;
  778. }
  779. loopcmd(f, cp->cmd, rp, nrp);
  780. free(rp);
  781. --nest;
  782. }
  783. void
  784. linelooper(File *f, Cmd *cp)
  785. {
  786. int32_t nrp, p;
  787. Range r, linesel;
  788. Address a, a3;
  789. Range *rp;
  790. nest++;
  791. nrp = 0;
  792. rp = nil;
  793. r = addr.r;
  794. a3.f = f;
  795. a3.r.q0 = a3.r.q1 = r.q0;
  796. a = lineaddr(0, a3, 1);
  797. linesel = a.r;
  798. for(p = r.q0; p<r.q1; p = a3.r.q1){
  799. a3.r.q0 = a3.r.q1;
  800. if(p!=r.q0 || linesel.q1==p){
  801. a = lineaddr(1, a3, 1);
  802. linesel = a.r;
  803. }
  804. if(linesel.q0 >= r.q1)
  805. break;
  806. if(linesel.q1 >= r.q1)
  807. linesel.q1 = r.q1;
  808. if(linesel.q1 > linesel.q0)
  809. if(linesel.q0>=a3.r.q1 && linesel.q1>a3.r.q1){
  810. a3.r = linesel;
  811. nrp++;
  812. rp = erealloc(rp, nrp*sizeof(Range));
  813. rp[nrp-1] = linesel;
  814. continue;
  815. }
  816. break;
  817. }
  818. loopcmd(f, cp->cmd, rp, nrp);
  819. free(rp);
  820. --nest;
  821. }
  822. struct Looper
  823. {
  824. Cmd *cp;
  825. int XY;
  826. Window **w;
  827. int nw;
  828. } loopstruct; /* only one; X and Y can't nest */
  829. void
  830. alllooper(Window *w, void *v)
  831. {
  832. Text *t;
  833. struct Looper *lp;
  834. Cmd *cp;
  835. lp = v;
  836. cp = lp->cp;
  837. // if(w->isscratch || w->isdir)
  838. // return;
  839. t = &w->body;
  840. /* only use this window if it's the current window for the file */
  841. if(t->file->curtext != t)
  842. return;
  843. // if(w->nopen[QWevent] > 0)
  844. // return;
  845. /* no auto-execute on files without names */
  846. if(cp->re==nil && t->file->nname==0)
  847. return;
  848. if(cp->re==nil || filematch(t->file, cp->re)==lp->XY){
  849. lp->w = erealloc(lp->w, (lp->nw+1)*sizeof(Window*));
  850. lp->w[lp->nw++] = w;
  851. }
  852. }
  853. void
  854. alllocker(Window *w, void *v)
  855. {
  856. if(v)
  857. incref(&w->Ref);
  858. else
  859. winclose(w);
  860. }
  861. void
  862. filelooper(Cmd *cp, int XY)
  863. {
  864. int i;
  865. if(Glooping++)
  866. editerror("can't nest %c command", "YX"[XY]);
  867. nest++;
  868. loopstruct.cp = cp;
  869. loopstruct.XY = XY;
  870. if(loopstruct.w) /* error'ed out last time */
  871. free(loopstruct.w);
  872. loopstruct.w = nil;
  873. loopstruct.nw = 0;
  874. allwindows(alllooper, &loopstruct);
  875. /*
  876. * add a ref to all windows to keep safe windows accessed by X
  877. * that would not otherwise have a ref to hold them up during
  878. * the shenanigans. note this with globalincref so that any
  879. * newly created windows start with an extra reference.
  880. */
  881. allwindows(alllocker, (void*)1);
  882. globalincref = 1;
  883. for(i=0; i<loopstruct.nw; i++)
  884. cmdexec(&loopstruct.w[i]->body, cp->cmd);
  885. allwindows(alllocker, (void*)0);
  886. globalincref = 0;
  887. free(loopstruct.w);
  888. loopstruct.w = nil;
  889. --Glooping;
  890. --nest;
  891. }
  892. void
  893. nextmatch(File *f, String *r, int32_t p, int sign)
  894. {
  895. if(rxcompile(r->r) == FALSE)
  896. editerror("bad regexp in command address");
  897. if(sign >= 0){
  898. if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel))
  899. editerror("no match for regexp");
  900. if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q0==p){
  901. if(++p>f->Buffer.nc)
  902. p = 0;
  903. if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel))
  904. editerror("address");
  905. }
  906. }else{
  907. if(!rxbexecute(f->curtext, p, &sel))
  908. editerror("no match for regexp");
  909. if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q1==p){
  910. if(--p<0)
  911. p = f->Buffer.nc;
  912. if(!rxbexecute(f->curtext, p, &sel))
  913. editerror("address");
  914. }
  915. }
  916. }
  917. File *matchfile(String*);
  918. Address charaddr(int32_t, Address, int);
  919. Address lineaddr(int32_t, Address, int);
  920. Address
  921. cmdaddress(Addr *ap, Address a, int sign)
  922. {
  923. File *f = a.f;
  924. Address a1, a2;
  925. do{
  926. switch(ap->type){
  927. case 'l':
  928. case '#':
  929. a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, sign);
  930. break;
  931. case '.':
  932. mkaddr(&a, f);
  933. break;
  934. case '$':
  935. a.r.q0 = a.r.q1 = f->Buffer.nc;
  936. break;
  937. case '\'':
  938. editerror("can't handle '");
  939. // a.r = f->mark;
  940. break;
  941. case '?':
  942. sign = -sign;
  943. if(sign == 0)
  944. sign = -1;
  945. /* fall through */
  946. case '/':
  947. nextmatch(f, ap->re, sign>=0? a.r.q1 : a.r.q0, sign);
  948. a.r = sel.r[0];
  949. break;
  950. case '"':
  951. f = matchfile(ap->re);
  952. mkaddr(&a, f);
  953. break;
  954. case '*':
  955. a.r.q0 = 0, a.r.q1 = f->Buffer.nc;
  956. return a;
  957. case ',':
  958. case ';':
  959. if(ap->left)
  960. a1 = cmdaddress(ap->left, a, 0);
  961. else
  962. a1.f = a.f, a1.r.q0 = a1.r.q1 = 0;
  963. if(ap->type == ';'){
  964. f = a1.f;
  965. a = a1;
  966. f->curtext->q0 = a1.r.q0;
  967. f->curtext->q1 = a1.r.q1;
  968. }
  969. if(ap->next)
  970. a2 = cmdaddress(ap->next, a, 0);
  971. else
  972. a2.f = a.f, a2.r.q0 = a2.r.q1 = f->Buffer.nc;
  973. if(a1.f != a2.f)
  974. editerror("addresses in different files");
  975. a.f = a1.f, a.r.q0 = a1.r.q0, a.r.q1 = a2.r.q1;
  976. if(a.r.q1 < a.r.q0)
  977. editerror("addresses out of order");
  978. return a;
  979. case '+':
  980. case '-':
  981. sign = 1;
  982. if(ap->type == '-')
  983. sign = -1;
  984. if(ap->next==0 || ap->next->type=='+' || ap->next->type=='-')
  985. a = lineaddr(1L, a, sign);
  986. break;
  987. default:
  988. error("cmdaddress");
  989. return a;
  990. }
  991. }while(ap = ap->next); /* assign = */
  992. return a;
  993. }
  994. struct Tofile{
  995. File *f;
  996. String *r;
  997. };
  998. void
  999. alltofile(Window *w, void *v)
  1000. {
  1001. Text *t;
  1002. struct Tofile *tp;
  1003. tp = v;
  1004. if(tp->f != nil)
  1005. return;
  1006. if(w->isscratch || w->isdir)
  1007. return;
  1008. t = &w->body;
  1009. /* only use this window if it's the current window for the file */
  1010. if(t->file->curtext != t)
  1011. return;
  1012. // if(w->nopen[QWevent] > 0)
  1013. // return;
  1014. if(runeeq(tp->r->r, tp->r->n, t->file->name, t->file->nname))
  1015. tp->f = t->file;
  1016. }
  1017. File*
  1018. tofile(String *r)
  1019. {
  1020. struct Tofile t;
  1021. String rr;
  1022. rr.r = skipbl(r->r, r->n, &rr.n);
  1023. t.f = nil;
  1024. t.r = &rr;
  1025. allwindows(alltofile, &t);
  1026. if(t.f == nil)
  1027. editerror("no such file\"%S\"", rr.r);
  1028. return t.f;
  1029. }
  1030. void
  1031. allmatchfile(Window *w, void *v)
  1032. {
  1033. struct Tofile *tp;
  1034. Text *t;
  1035. tp = v;
  1036. if(w->isscratch || w->isdir)
  1037. return;
  1038. t = &w->body;
  1039. /* only use this window if it's the current window for the file */
  1040. if(t->file->curtext != t)
  1041. return;
  1042. // if(w->nopen[QWevent] > 0)
  1043. // return;
  1044. if(filematch(w->body.file, tp->r)){
  1045. if(tp->f != nil)
  1046. editerror("too many files match \"%S\"", tp->r->r);
  1047. tp->f = w->body.file;
  1048. }
  1049. }
  1050. File*
  1051. matchfile(String *r)
  1052. {
  1053. struct Tofile tf;
  1054. tf.f = nil;
  1055. tf.r = r;
  1056. allwindows(allmatchfile, &tf);
  1057. if(tf.f == nil)
  1058. editerror("no file matches \"%S\"", r->r);
  1059. return tf.f;
  1060. }
  1061. int
  1062. filematch(File *f, String *r)
  1063. {
  1064. char *buf;
  1065. Rune *rbuf;
  1066. Window *w;
  1067. int match, i, dirty;
  1068. Rangeset s;
  1069. /* compile expr first so if we get an error, we haven't allocated anything */
  1070. if(rxcompile(r->r) == FALSE)
  1071. editerror("bad regexp in file match");
  1072. buf = fbufalloc();
  1073. w = f->curtext->w;
  1074. /* same check for dirty as in settag, but we know ncache==0 */
  1075. dirty = !w->isdir && !w->isscratch && f->mod;
  1076. snprint(buf, BUFSIZE, "%c%c%c %.*S\n", " '"[dirty],
  1077. '+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name);
  1078. rbuf = bytetorune(buf, &i);
  1079. fbuffree(buf);
  1080. match = rxexecute(nil, rbuf, 0, i, &s);
  1081. free(rbuf);
  1082. return match;
  1083. }
  1084. Address
  1085. charaddr(int32_t l, Address addr, int sign)
  1086. {
  1087. if(sign == 0)
  1088. addr.r.q0 = addr.r.q1 = l;
  1089. else if(sign < 0)
  1090. addr.r.q1 = addr.r.q0 -= l;
  1091. else if(sign > 0)
  1092. addr.r.q0 = addr.r.q1 += l;
  1093. if(addr.r.q0<0 || addr.r.q1>addr.f->Buffer.nc)
  1094. editerror("address out of range");
  1095. return addr;
  1096. }
  1097. Address
  1098. lineaddr(int32_t l, Address addr, int sign)
  1099. {
  1100. int n;
  1101. int c;
  1102. File *f = addr.f;
  1103. Address a;
  1104. int32_t p;
  1105. a.f = f;
  1106. if(sign >= 0){
  1107. if(l == 0){
  1108. if(sign==0 || addr.r.q1==0){
  1109. a.r.q0 = a.r.q1 = 0;
  1110. return a;
  1111. }
  1112. a.r.q0 = addr.r.q1;
  1113. p = addr.r.q1-1;
  1114. }else{
  1115. if(sign==0 || addr.r.q1==0){
  1116. p = 0;
  1117. n = 1;
  1118. }else{
  1119. p = addr.r.q1-1;
  1120. n = textreadc(f->curtext, p++)=='\n';
  1121. }
  1122. while(n < l){
  1123. if(p >= f->Buffer.nc)
  1124. editerror("address out of range");
  1125. if(textreadc(f->curtext, p++) == '\n')
  1126. n++;
  1127. }
  1128. a.r.q0 = p;
  1129. }
  1130. while(p < f->Buffer.nc && textreadc(f->curtext, p++)!='\n')
  1131. ;
  1132. a.r.q1 = p;
  1133. }else{
  1134. p = addr.r.q0;
  1135. if(l == 0)
  1136. a.r.q1 = addr.r.q0;
  1137. else{
  1138. for(n = 0; n<l; ){ /* always runs once */
  1139. if(p == 0){
  1140. if(++n != l)
  1141. editerror("address out of range");
  1142. }else{
  1143. c = textreadc(f->curtext, p-1);
  1144. if(c != '\n' || ++n != l)
  1145. p--;
  1146. }
  1147. }
  1148. a.r.q1 = p;
  1149. if(p > 0)
  1150. p--;
  1151. }
  1152. while(p > 0 && textreadc(f->curtext, p-1)!='\n') /* lines start after a newline */
  1153. p--;
  1154. a.r.q0 = p;
  1155. }
  1156. return a;
  1157. }
  1158. struct Filecheck
  1159. {
  1160. File *f;
  1161. Rune *r;
  1162. int nr;
  1163. };
  1164. void
  1165. allfilecheck(Window *w, void *v)
  1166. {
  1167. struct Filecheck *fp;
  1168. File *f;
  1169. fp = v;
  1170. f = w->body.file;
  1171. if(w->body.file == fp->f)
  1172. return;
  1173. if(runeeq(fp->r, fp->nr, f->name, f->nname))
  1174. warning(nil, "warning: duplicate file name \"%.*S\"\n", fp->nr, fp->r);
  1175. }
  1176. Rune*
  1177. cmdname(File *f, String *str, int set)
  1178. {
  1179. Rune *r, *s;
  1180. int n;
  1181. struct Filecheck fc;
  1182. Runestr newname;
  1183. r = nil;
  1184. n = str->n;
  1185. s = str->r;
  1186. if(n == 0){
  1187. /* no name; use existing */
  1188. if(f->nname == 0)
  1189. return nil;
  1190. r = runemalloc(f->nname+1);
  1191. runemove(r, f->name, f->nname);
  1192. return r;
  1193. }
  1194. s = skipbl(s, n, &n);
  1195. if(n == 0)
  1196. goto Return;
  1197. if(s[0] == '/'){
  1198. r = runemalloc(n+1);
  1199. runemove(r, s, n);
  1200. }else{
  1201. newname = dirname(f->curtext, runestrdup(s), n);
  1202. n = newname.nr;
  1203. r = runemalloc(n+1); /* NUL terminate */
  1204. runemove(r, newname.r, n);
  1205. free(newname.r);
  1206. }
  1207. fc.f = f;
  1208. fc.r = r;
  1209. fc.nr = n;
  1210. allwindows(allfilecheck, &fc);
  1211. if(f->nname == 0)
  1212. set = TRUE;
  1213. Return:
  1214. if(set && !runeeq(r, n, f->name, f->nname)){
  1215. filemark(f);
  1216. f->mod = TRUE;
  1217. f->curtext->w->dirty = TRUE;
  1218. winsetname(f->curtext->w, r, n);
  1219. }
  1220. return r;
  1221. }