ecmd.c 24 KB

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