ecmd.c 23 KB

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