ecmd.c 23 KB

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