ecmd.c 24 KB

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