text.c 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399
  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 <complete.h>
  12. #include "dat.h"
  13. #include "fns.h"
  14. Image *tagcols[NCOL];
  15. Image *textcols[NCOL];
  16. enum{
  17. TABDIR = 3 /* width of tabs in directory windows */
  18. };
  19. void
  20. textinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL])
  21. {
  22. t->file = f;
  23. t->all = r;
  24. t->scrollr = r;
  25. t->scrollr.max.x = r.min.x+Scrollwid;
  26. t->lastsr = nullrect;
  27. r.min.x += Scrollwid+Scrollgap;
  28. t->eq0 = ~0;
  29. t->ncache = 0;
  30. t->reffont = rf;
  31. t->tabstop = maxtab;
  32. memmove(t->Frame.cols, cols, sizeof t->Frame.cols);
  33. textredraw(t, r, rf->f, screen, -1);
  34. }
  35. void
  36. textredraw(Text *t, Rectangle r, Font *f, Image *b, int odx)
  37. {
  38. int maxt;
  39. Rectangle rr;
  40. frinit(t, r, f, b, t->Frame.cols);
  41. rr = t->r;
  42. rr.min.x -= Scrollwid+Scrollgap; /* back fill to scroll bar */
  43. draw(t->b, rr, t->cols[BACK], nil, ZP);
  44. /* use no wider than 3-space tabs in a directory */
  45. maxt = maxtab;
  46. if(t->what == Body){
  47. if(t->w->isdir)
  48. maxt = min(TABDIR, maxtab);
  49. else
  50. maxt = t->tabstop;
  51. }
  52. t->maxtab = maxt*stringwidth(f, "0");
  53. if(t->what==Body && t->w->isdir && odx!=Dx(t->all)){
  54. if(t->maxlines > 0){
  55. textreset(t);
  56. textcolumnate(t, t->w->dlp, t->w->ndl);
  57. textshow(t, 0, 0, 1);
  58. }
  59. }else{
  60. textfill(t);
  61. textsetselect(t, t->q0, t->q1);
  62. }
  63. }
  64. int
  65. textresize(Text *t, Rectangle r)
  66. {
  67. int odx;
  68. if(Dy(r) > 0)
  69. r.max.y -= Dy(r)%t->font->height;
  70. else
  71. r.max.y = r.min.y;
  72. odx = Dx(t->all);
  73. t->all = r;
  74. t->scrollr = r;
  75. t->scrollr.max.x = r.min.x+Scrollwid;
  76. t->lastsr = nullrect;
  77. r.min.x += Scrollwid+Scrollgap;
  78. frclear(t, 0);
  79. textredraw(t, r, t->font, t->b, odx);
  80. return r.max.y;
  81. }
  82. void
  83. textclose(Text *t)
  84. {
  85. free(t->cache);
  86. frclear(t, 1);
  87. filedeltext(t->file, t);
  88. t->file = nil;
  89. rfclose(t->reffont);
  90. if(argtext == t)
  91. argtext = nil;
  92. if(typetext == t)
  93. typetext = nil;
  94. if(seltext == t)
  95. seltext = nil;
  96. if(mousetext == t)
  97. mousetext = nil;
  98. if(barttext == t)
  99. barttext = nil;
  100. }
  101. int
  102. dircmp(void *a, void *b)
  103. {
  104. Dirlist *da, *db;
  105. int i, n;
  106. da = *(Dirlist**)a;
  107. db = *(Dirlist**)b;
  108. n = min(da->nr, db->nr);
  109. i = memcmp(da->r, db->r, n*sizeof(Rune));
  110. if(i)
  111. return i;
  112. return da->nr - db->nr;
  113. }
  114. void
  115. textcolumnate(Text *t, Dirlist **dlp, int ndl)
  116. {
  117. int i, j, w, colw, mint, maxt, ncol, nrow;
  118. Dirlist *dl;
  119. uint q1;
  120. if(t->file->ntext > 1)
  121. return;
  122. mint = stringwidth(t->font, "0");
  123. /* go for narrower tabs if set more than 3 wide */
  124. t->maxtab = min(maxtab, TABDIR)*mint;
  125. maxt = t->maxtab;
  126. colw = 0;
  127. for(i=0; i<ndl; i++){
  128. dl = dlp[i];
  129. w = dl->wid;
  130. if(maxt-w%maxt < mint || w%maxt==0)
  131. w += mint;
  132. if(w % maxt)
  133. w += maxt-(w%maxt);
  134. if(w > colw)
  135. colw = w;
  136. }
  137. if(colw == 0)
  138. ncol = 1;
  139. else
  140. ncol = max(1, Dx(t->r)/colw);
  141. nrow = (ndl+ncol-1)/ncol;
  142. q1 = 0;
  143. for(i=0; i<nrow; i++){
  144. for(j=i; j<ndl; j+=nrow){
  145. dl = dlp[j];
  146. fileinsert(t->file, q1, dl->r, dl->nr);
  147. q1 += dl->nr;
  148. if(j+nrow >= ndl)
  149. break;
  150. w = dl->wid;
  151. if(maxt-w%maxt < mint){
  152. fileinsert(t->file, q1, L"\t", 1);
  153. q1++;
  154. w += mint;
  155. }
  156. do{
  157. fileinsert(t->file, q1, L"\t", 1);
  158. q1++;
  159. w += maxt-(w%maxt);
  160. }while(w < colw);
  161. }
  162. fileinsert(t->file, q1, L"\n", 1);
  163. q1++;
  164. }
  165. }
  166. uint
  167. textload(Text *t, uint q0, char *file, int setqid)
  168. {
  169. Rune *rp;
  170. Dirlist *dl, **dlp;
  171. int fd, i, j, n, ndl, nulls;
  172. uint q, q1;
  173. Dir *d, *dbuf;
  174. char *tmp;
  175. Text *u;
  176. if(t->ncache!=0 || t->file->nc || t->w==nil || t!=&t->w->body)
  177. error("text.load");
  178. if(t->w->isdir && t->file->nname==0){
  179. warning(nil, "empty directory name\n");
  180. return 0;
  181. }
  182. fd = open(file, OREAD);
  183. if(fd < 0){
  184. warning(nil, "can't open %s: %r\n", file);
  185. return 0;
  186. }
  187. d = dirfstat(fd);
  188. if(d == nil){
  189. warning(nil, "can't fstat %s: %r\n", file);
  190. goto Rescue;
  191. }
  192. nulls = FALSE;
  193. if(d->qid.type & QTDIR){
  194. /* this is checked in get() but it's possible the file changed underfoot */
  195. if(t->file->ntext > 1){
  196. warning(nil, "%s is a directory; can't read with multiple windows on it\n", file);
  197. goto Rescue;
  198. }
  199. t->w->isdir = TRUE;
  200. t->w->filemenu = FALSE;
  201. if(t->file->name[t->file->nname-1] != '/'){
  202. rp = runemalloc(t->file->nname+1);
  203. runemove(rp, t->file->name, t->file->nname);
  204. rp[t->file->nname] = '/';
  205. winsetname(t->w, rp, t->file->nname+1);
  206. free(rp);
  207. }
  208. dlp = nil;
  209. ndl = 0;
  210. dbuf = nil;
  211. while((n=dirread(fd, &dbuf)) > 0){
  212. for(i=0; i<n; i++){
  213. dl = emalloc(sizeof(Dirlist));
  214. j = strlen(dbuf[i].name);
  215. tmp = emalloc(j+1+1);
  216. memmove(tmp, dbuf[i].name, j);
  217. if(dbuf[i].qid.type & QTDIR)
  218. tmp[j++] = '/';
  219. tmp[j] = '\0';
  220. dl->r = bytetorune(tmp, &dl->nr);
  221. dl->wid = stringwidth(t->font, tmp);
  222. free(tmp);
  223. ndl++;
  224. dlp = realloc(dlp, ndl*sizeof(Dirlist*));
  225. dlp[ndl-1] = dl;
  226. }
  227. free(dbuf);
  228. }
  229. qsort(dlp, ndl, sizeof(Dirlist*), dircmp);
  230. t->w->dlp = dlp;
  231. t->w->ndl = ndl;
  232. textcolumnate(t, dlp, ndl);
  233. q1 = t->file->nc;
  234. }else{
  235. t->w->isdir = FALSE;
  236. t->w->filemenu = TRUE;
  237. q1 = q0 + fileload(t->file, q0, fd, &nulls);
  238. }
  239. if(setqid){
  240. t->file->dev = d->dev;
  241. t->file->mtime = d->mtime;
  242. t->file->qidpath = d->qid.path;
  243. }
  244. close(fd);
  245. rp = fbufalloc();
  246. for(q=q0; q<q1; q+=n){
  247. n = q1-q;
  248. if(n > RBUFSIZE)
  249. n = RBUFSIZE;
  250. bufread(t->file, q, rp, n);
  251. if(q < t->org)
  252. t->org += n;
  253. else if(q <= t->org+t->nchars)
  254. frinsert(t, rp, rp+n, q-t->org);
  255. if(t->lastlinefull)
  256. break;
  257. }
  258. fbuffree(rp);
  259. for(i=0; i<t->file->ntext; i++){
  260. u = t->file->text[i];
  261. if(u != t){
  262. if(u->org > u->file->nc) /* will be 0 because of reset(), but safety first */
  263. u->org = 0;
  264. textresize(u, u->all);
  265. textbacknl(u, u->org, 0); /* go to beginning of line */
  266. }
  267. textsetselect(u, q0, q0);
  268. }
  269. if(nulls)
  270. warning(nil, "%s: NUL bytes elided\n", file);
  271. free(d);
  272. return q1-q0;
  273. Rescue:
  274. close(fd);
  275. return 0;
  276. }
  277. uint
  278. textbsinsert(Text *t, uint q0, Rune *r, uint n, int tofile, int *nrp)
  279. {
  280. Rune *bp, *tp, *up;
  281. int i, initial;
  282. if(t->what == Tag){ /* can't happen but safety first: mustn't backspace over file name */
  283. Err:
  284. textinsert(t, q0, r, n, tofile);
  285. *nrp = n;
  286. return q0;
  287. }
  288. bp = r;
  289. for(i=0; i<n; i++)
  290. if(*bp++ == '\b'){
  291. --bp;
  292. initial = 0;
  293. tp = runemalloc(n);
  294. runemove(tp, r, i);
  295. up = tp+i;
  296. for(; i<n; i++){
  297. *up = *bp++;
  298. if(*up == '\b')
  299. if(up == tp)
  300. initial++;
  301. else
  302. --up;
  303. else
  304. up++;
  305. }
  306. if(initial){
  307. if(initial > q0)
  308. initial = q0;
  309. q0 -= initial;
  310. textdelete(t, q0, q0+initial, tofile);
  311. }
  312. n = up-tp;
  313. textinsert(t, q0, tp, n, tofile);
  314. free(tp);
  315. *nrp = n;
  316. return q0;
  317. }
  318. goto Err;
  319. }
  320. void
  321. textinsert(Text *t, uint q0, Rune *r, uint n, int tofile)
  322. {
  323. int c, i;
  324. Text *u;
  325. if(tofile && t->ncache != 0)
  326. error("text.insert");
  327. if(n == 0)
  328. return;
  329. if(tofile){
  330. fileinsert(t->file, q0, r, n);
  331. if(t->what == Body){
  332. t->w->dirty = TRUE;
  333. t->w->utflastqid = -1;
  334. }
  335. if(t->file->ntext > 1)
  336. for(i=0; i<t->file->ntext; i++){
  337. u = t->file->text[i];
  338. if(u != t){
  339. u->w->dirty = TRUE; /* always a body */
  340. textinsert(u, q0, r, n, FALSE);
  341. textsetselect(u, u->q0, u->q1);
  342. textscrdraw(u);
  343. }
  344. }
  345. }
  346. if(q0 < t->q1)
  347. t->q1 += n;
  348. if(q0 < t->q0)
  349. t->q0 += n;
  350. if(q0 < t->org)
  351. t->org += n;
  352. else if(q0 <= t->org+t->nchars)
  353. frinsert(t, r, r+n, q0-t->org);
  354. if(t->w){
  355. c = 'i';
  356. if(t->what == Body)
  357. c = 'I';
  358. if(n <= EVENTSIZE)
  359. winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q0+n, n, n, r);
  360. else
  361. winevent(t->w, "%c%d %d 0 0 \n", c, q0, q0+n, n);
  362. }
  363. }
  364. void
  365. textfill(Text *t)
  366. {
  367. Rune *rp;
  368. int i, n, m, nl;
  369. if(t->lastlinefull || t->nofill)
  370. return;
  371. if(t->ncache > 0){
  372. if(t->w != nil)
  373. wincommit(t->w, t);
  374. else
  375. textcommit(t, TRUE);
  376. }
  377. rp = fbufalloc();
  378. do{
  379. n = t->file->nc-(t->org+t->nchars);
  380. if(n == 0)
  381. break;
  382. if(n > 2000) /* educated guess at reasonable amount */
  383. n = 2000;
  384. bufread(t->file, t->org+t->nchars, rp, n);
  385. /*
  386. * it's expensive to frinsert more than we need, so
  387. * count newlines.
  388. */
  389. nl = t->maxlines-t->nlines;
  390. m = 0;
  391. for(i=0; i<n; ){
  392. if(rp[i++] == '\n'){
  393. m++;
  394. if(m >= nl)
  395. break;
  396. }
  397. }
  398. frinsert(t, rp, rp+i, t->nchars);
  399. }while(t->lastlinefull == FALSE);
  400. fbuffree(rp);
  401. }
  402. void
  403. textdelete(Text *t, uint q0, uint q1, int tofile)
  404. {
  405. uint n, p0, p1;
  406. int i, c;
  407. Text *u;
  408. if(tofile && t->ncache != 0)
  409. error("text.delete");
  410. n = q1-q0;
  411. if(n == 0)
  412. return;
  413. if(tofile){
  414. filedelete(t->file, q0, q1);
  415. if(t->what == Body){
  416. t->w->dirty = TRUE;
  417. t->w->utflastqid = -1;
  418. }
  419. if(t->file->ntext > 1)
  420. for(i=0; i<t->file->ntext; i++){
  421. u = t->file->text[i];
  422. if(u != t){
  423. u->w->dirty = TRUE; /* always a body */
  424. textdelete(u, q0, q1, FALSE);
  425. textsetselect(u, u->q0, u->q1);
  426. textscrdraw(u);
  427. }
  428. }
  429. }
  430. if(q0 < t->q0)
  431. t->q0 -= min(n, t->q0-q0);
  432. if(q0 < t->q1)
  433. t->q1 -= min(n, t->q1-q0);
  434. if(q1 <= t->org)
  435. t->org -= n;
  436. else if(q0 < t->org+t->nchars){
  437. p1 = q1 - t->org;
  438. if(p1 > t->nchars)
  439. p1 = t->nchars;
  440. if(q0 < t->org){
  441. t->org = q0;
  442. p0 = 0;
  443. }else
  444. p0 = q0 - t->org;
  445. frdelete(t, p0, p1);
  446. textfill(t);
  447. }
  448. if(t->w){
  449. c = 'd';
  450. if(t->what == Body)
  451. c = 'D';
  452. winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1);
  453. }
  454. }
  455. void
  456. textconstrain(Text *t, uint q0, uint q1, uint *p0, uint *p1)
  457. {
  458. *p0 = min(q0, t->file->nc);
  459. *p1 = min(q1, t->file->nc);
  460. }
  461. Rune
  462. textreadc(Text *t, uint q)
  463. {
  464. Rune r;
  465. if(t->cq0<=q && q<t->cq0+t->ncache)
  466. r = t->cache[q-t->cq0];
  467. else
  468. bufread(t->file, q, &r, 1);
  469. return r;
  470. }
  471. int
  472. textbswidth(Text *t, Rune c)
  473. {
  474. uint q, eq;
  475. Rune r;
  476. int skipping;
  477. /* there is known to be at least one character to erase */
  478. if(c == 0x08) /* ^H: erase character */
  479. return 1;
  480. q = t->q0;
  481. skipping = TRUE;
  482. while(q > 0){
  483. r = textreadc(t, q-1);
  484. if(r == '\n'){ /* eat at most one more character */
  485. if(q == t->q0) /* eat the newline */
  486. --q;
  487. break;
  488. }
  489. if(c == 0x17){
  490. eq = isalnum(r);
  491. if(eq && skipping) /* found one; stop skipping */
  492. skipping = FALSE;
  493. else if(!eq && !skipping)
  494. break;
  495. }
  496. --q;
  497. }
  498. return t->q0-q;
  499. }
  500. int
  501. textfilewidth(Text *t, uint q0, int oneelement)
  502. {
  503. uint q;
  504. Rune r;
  505. q = q0;
  506. while(q > 0){
  507. r = textreadc(t, q-1);
  508. if(r <= ' ')
  509. break;
  510. if(oneelement && r=='/')
  511. break;
  512. --q;
  513. }
  514. return q0-q;
  515. }
  516. Rune*
  517. textcomplete(Text *t)
  518. {
  519. int i, nstr, npath;
  520. uint q;
  521. Rune tmp[200];
  522. Rune *str, *path;
  523. Rune *rp;
  524. Completion *c;
  525. char *s, *dirs;
  526. Runestr dir;
  527. /* control-f: filename completion; works back to white space or / */
  528. if(t->q0<t->file->nc && textreadc(t, t->q0)>' ') /* must be at end of word */
  529. return nil;
  530. nstr = textfilewidth(t, t->q0, TRUE);
  531. str = runemalloc(nstr);
  532. npath = textfilewidth(t, t->q0-nstr, FALSE);
  533. path = runemalloc(npath);
  534. c = nil;
  535. rp = nil;
  536. dirs = nil;
  537. q = t->q0-nstr;
  538. for(i=0; i<nstr; i++)
  539. str[i] = textreadc(t, q++);
  540. q = t->q0-nstr-npath;
  541. for(i=0; i<npath; i++)
  542. path[i] = textreadc(t, q++);
  543. /* is path rooted? if not, we need to make it relative to window path */
  544. if(npath>0 && path[0]=='/')
  545. dir = (Runestr){path, npath};
  546. else{
  547. dir = dirname(t, nil, 0);
  548. if(dir.nr + 1 + npath > nelem(tmp)){
  549. free(dir.r);
  550. goto Return;
  551. }
  552. if(dir.nr == 0){
  553. dir.nr = 1;
  554. dir.r = runestrdup(L".");
  555. }
  556. runemove(tmp, dir.r, dir.nr);
  557. tmp[dir.nr] = '/';
  558. runemove(tmp+dir.nr+1, path, npath);
  559. free(dir.r);
  560. dir.r = tmp;
  561. dir.nr += 1+npath;
  562. dir = cleanrname(dir);
  563. }
  564. s = smprint("%.*S", nstr, str);
  565. dirs = smprint("%.*S", dir.nr, dir.r);
  566. c = complete(dirs, s);
  567. free(s);
  568. if(c == nil){
  569. warning(nil, "error attempting completion: %r\n");
  570. goto Return;
  571. }
  572. if(!c->advance){
  573. warning(nil, "%.*S%s%.*S*%s\n",
  574. dir.nr, dir.r,
  575. dir.nr>0 && dir.r[dir.nr-1]!='/' ? "/" : "",
  576. nstr, str,
  577. c->nmatch? "" : ": no matches in:");
  578. for(i=0; i<c->nfile; i++)
  579. warning(nil, " %s\n", c->filename[i]);
  580. }
  581. if(c->advance)
  582. rp = runesmprint("%s", c->string);
  583. else
  584. rp = nil;
  585. Return:
  586. freecompletion(c);
  587. free(dirs);
  588. free(str);
  589. free(path);
  590. return rp;
  591. }
  592. void
  593. texttype(Text *t, Rune r)
  594. {
  595. uint q0, q1;
  596. int nnb, nb, n, i;
  597. int nr;
  598. Rune *rp;
  599. Text *u;
  600. if(t->what!=Body && r=='\n')
  601. return;
  602. nr = 1;
  603. rp = &r;
  604. switch(r){
  605. case Kleft:
  606. if(t->q0 > 0){
  607. if(t->w)
  608. wincommit(t->w, t);
  609. else
  610. textcommit(t, TRUE);
  611. textshow(t, t->q0-1, t->q0-1, TRUE);
  612. }
  613. return;
  614. case Kright:
  615. if(t->q1 < t->file->nc){
  616. if(t->w)
  617. wincommit(t->w, t);
  618. else
  619. textcommit(t, TRUE);
  620. textshow(t, t->q1+1, t->q1+1, TRUE);
  621. }
  622. return;
  623. case Kdown:
  624. n = t->maxlines/3;
  625. goto case_Down;
  626. case Kscrollonedown:
  627. n = mousescrollsize(t->maxlines);
  628. if(n <= 0)
  629. n = 1;
  630. goto case_Down;
  631. case Kpgdown:
  632. n = 2*t->maxlines/3;
  633. case_Down:
  634. q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height));
  635. textsetorigin(t, q0, TRUE);
  636. return;
  637. case Kup:
  638. n = t->maxlines/3;
  639. goto case_Up;
  640. case Kscrolloneup:
  641. n = mousescrollsize(t->maxlines);
  642. goto case_Up;
  643. case Kpgup:
  644. n = 2*t->maxlines/3;
  645. case_Up:
  646. q0 = textbacknl(t, t->org, n);
  647. textsetorigin(t, q0, TRUE);
  648. return;
  649. case Khome:
  650. textshow(t, 0, 0, FALSE);
  651. return;
  652. case Kend:
  653. if(t->w)
  654. wincommit(t->w, t);
  655. else
  656. textcommit(t, TRUE);
  657. textshow(t, t->file->nc, t->file->nc, FALSE);
  658. return;
  659. }
  660. if(t->what == Body){
  661. seq++;
  662. filemark(t->file);
  663. }
  664. if(t->q1 > t->q0){
  665. if(t->ncache != 0)
  666. error("text.type");
  667. cut(t, t, nil, TRUE, TRUE, nil, 0);
  668. t->eq0 = ~0;
  669. }
  670. textshow(t, t->q0, t->q0, 1);
  671. switch(r){
  672. case 0x06:
  673. case Kins:
  674. rp = textcomplete(t);
  675. if(rp == nil)
  676. return;
  677. nr = runestrlen(rp);
  678. break; /* fall through to normal insertion case */
  679. case 0x1B:
  680. if(t->eq0 != ~0)
  681. textsetselect(t, t->eq0, t->q0);
  682. if(t->ncache > 0){
  683. if(t->w != nil)
  684. wincommit(t->w, t);
  685. else
  686. textcommit(t, TRUE);
  687. }
  688. return;
  689. case 0x08: /* ^H: erase character */
  690. case 0x15: /* ^U: erase line */
  691. case 0x17: /* ^W: erase word */
  692. if(t->q0 == 0) /* nothing to erase */
  693. return;
  694. nnb = textbswidth(t, r);
  695. q1 = t->q0;
  696. q0 = q1-nnb;
  697. /* if selection is at beginning of window, avoid deleting invisible text */
  698. if(q0 < t->org){
  699. q0 = t->org;
  700. nnb = q1-q0;
  701. }
  702. if(nnb <= 0)
  703. return;
  704. for(i=0; i<t->file->ntext; i++){
  705. u = t->file->text[i];
  706. u->nofill = TRUE;
  707. nb = nnb;
  708. n = u->ncache;
  709. if(n > 0){
  710. if(q1 != u->cq0+n)
  711. error("text.type backspace");
  712. if(n > nb)
  713. n = nb;
  714. u->ncache -= n;
  715. textdelete(u, q1-n, q1, FALSE);
  716. nb -= n;
  717. }
  718. if(u->eq0==q1 || u->eq0==~0)
  719. u->eq0 = q0;
  720. if(nb && u==t)
  721. textdelete(u, q0, q0+nb, TRUE);
  722. if(u != t)
  723. textsetselect(u, u->q0, u->q1);
  724. else
  725. textsetselect(t, q0, q0);
  726. u->nofill = FALSE;
  727. }
  728. for(i=0; i<t->file->ntext; i++)
  729. textfill(t->file->text[i]);
  730. return;
  731. case '\n':
  732. if(t->w->autoindent){
  733. /* find beginning of previous line using backspace code */
  734. nnb = textbswidth(t, 0x15); /* ^U case */
  735. rp = runemalloc(nnb + 1);
  736. nr = 0;
  737. rp[nr++] = r;
  738. for(i=0; i<nnb; i++){
  739. r = textreadc(t, t->q0-nnb+i);
  740. if(r != ' ' && r != '\t')
  741. break;
  742. rp[nr++] = r;
  743. }
  744. }
  745. break; /* fall through to normal code */
  746. }
  747. /* otherwise ordinary character; just insert, typically in caches of all texts */
  748. for(i=0; i<t->file->ntext; i++){
  749. u = t->file->text[i];
  750. if(u->eq0 == ~0)
  751. u->eq0 = t->q0;
  752. if(u->ncache == 0)
  753. u->cq0 = t->q0;
  754. else if(t->q0 != u->cq0+u->ncache)
  755. error("text.type cq1");
  756. textinsert(u, t->q0, rp, nr, FALSE);
  757. if(u != t)
  758. textsetselect(u, u->q0, u->q1);
  759. if(u->ncache+nr > u->ncachealloc){
  760. u->ncachealloc += 10 + nr;
  761. u->cache = runerealloc(u->cache, u->ncachealloc);
  762. }
  763. runemove(u->cache+u->ncache, rp, nr);
  764. u->ncache += nr;
  765. }
  766. if(rp != &r)
  767. free(rp);
  768. textsetselect(t, t->q0+nr, t->q0+nr);
  769. if(r=='\n' && t->w!=nil)
  770. wincommit(t->w, t);
  771. }
  772. void
  773. textcommit(Text *t, int tofile)
  774. {
  775. if(t->ncache == 0)
  776. return;
  777. if(tofile)
  778. fileinsert(t->file, t->cq0, t->cache, t->ncache);
  779. if(t->what == Body){
  780. t->w->dirty = TRUE;
  781. t->w->utflastqid = -1;
  782. }
  783. t->ncache = 0;
  784. }
  785. static Text *clicktext;
  786. static uint clickmsec;
  787. static Text *selecttext;
  788. static uint selectq;
  789. /*
  790. * called from frame library
  791. */
  792. void
  793. framescroll(Frame *f, int dl)
  794. {
  795. if(f != &selecttext->Frame)
  796. error("frameselect not right frame");
  797. textframescroll(selecttext, dl);
  798. }
  799. void
  800. textframescroll(Text *t, int dl)
  801. {
  802. uint q0;
  803. if(dl == 0){
  804. scrsleep(100);
  805. return;
  806. }
  807. if(dl < 0){
  808. q0 = textbacknl(t, t->org, -dl);
  809. if(selectq > t->org+t->p0)
  810. textsetselect(t, t->org+t->p0, selectq);
  811. else
  812. textsetselect(t, selectq, t->org+t->p0);
  813. }else{
  814. if(t->org+t->nchars == t->file->nc)
  815. return;
  816. q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+dl*t->font->height));
  817. if(selectq > t->org+t->p1)
  818. textsetselect(t, t->org+t->p1, selectq);
  819. else
  820. textsetselect(t, selectq, t->org+t->p1);
  821. }
  822. textsetorigin(t, q0, TRUE);
  823. }
  824. void
  825. textselect(Text *t)
  826. {
  827. uint q0, q1;
  828. int b, x, y;
  829. int state, op;
  830. selecttext = t;
  831. /*
  832. * To have double-clicking and chording, we double-click
  833. * immediately if it might make sense.
  834. */
  835. b = mouse->buttons;
  836. q0 = t->q0;
  837. q1 = t->q1;
  838. selectq = t->org+frcharofpt(t, mouse->xy);
  839. if(clicktext==t && mouse->msec-clickmsec<500)
  840. if(q0==q1 && selectq==q0){
  841. textdoubleclick(t, &q0, &q1);
  842. textsetselect(t, q0, q1);
  843. flushimage(display, 1);
  844. x = mouse->xy.x;
  845. y = mouse->xy.y;
  846. /* stay here until something interesting happens */
  847. do
  848. readmouse(mousectl);
  849. while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3);
  850. mouse->xy.x = x; /* in case we're calling frselect */
  851. mouse->xy.y = y;
  852. q0 = t->q0; /* may have changed */
  853. q1 = t->q1;
  854. selectq = q0;
  855. }
  856. if(mouse->buttons == b){
  857. t->Frame.scroll = framescroll;
  858. frselect(t, mousectl);
  859. /* horrible botch: while asleep, may have lost selection altogether */
  860. if(selectq > t->file->nc)
  861. selectq = t->org + t->p0;
  862. t->Frame.scroll = nil;
  863. if(selectq < t->org)
  864. q0 = selectq;
  865. else
  866. q0 = t->org + t->p0;
  867. if(selectq > t->org+t->nchars)
  868. q1 = selectq;
  869. else
  870. q1 = t->org+t->p1;
  871. }
  872. if(q0 == q1){
  873. if(q0==t->q0 && clicktext==t && mouse->msec-clickmsec<500){
  874. textdoubleclick(t, &q0, &q1);
  875. clicktext = nil;
  876. }else{
  877. clicktext = t;
  878. clickmsec = mouse->msec;
  879. }
  880. }else
  881. clicktext = nil;
  882. textsetselect(t, q0, q1);
  883. flushimage(display, 1);
  884. state = op = 0; /* undo when possible; +1 for cut, -1 for paste */
  885. while(mouse->buttons){
  886. mouse->msec = 0;
  887. b = mouse->buttons;
  888. if(b & 6){
  889. if(state==0 && op==0 && t->what==Body){
  890. seq++;
  891. filemark(t->w->body.file);
  892. }
  893. if(b & 2){
  894. if(state==-1 && t->what==Body){
  895. winundo(t->w, TRUE);
  896. textsetselect(t, q0, t->q0);
  897. state = 0;
  898. }else if(state != 1 && op != -1){
  899. cut(t, t, nil, TRUE, TRUE, nil, 0);
  900. op = state = 1;
  901. }
  902. }else{
  903. if(state==1 && t->what==Body){
  904. winundo(t->w, TRUE);
  905. textsetselect(t, q0, t->q1);
  906. state = 0;
  907. }else if(state != -1 && op != 1){
  908. paste(t, t, nil, TRUE, FALSE, nil, 0);
  909. op = state = -1;
  910. }
  911. }
  912. textscrdraw(t);
  913. clearmouse();
  914. }
  915. flushimage(display, 1);
  916. while(mouse->buttons == b)
  917. readmouse(mousectl);
  918. clicktext = nil;
  919. }
  920. }
  921. void
  922. textshow(Text *t, uint q0, uint q1, int doselect)
  923. {
  924. int qe;
  925. int nl;
  926. uint q;
  927. if(t->what != Body){
  928. if(doselect)
  929. textsetselect(t, q0, q1);
  930. return;
  931. }
  932. if(t->w!=nil && t->maxlines==0)
  933. colgrow(t->col, t->w, 1);
  934. if(doselect)
  935. textsetselect(t, q0, q1);
  936. qe = t->org+t->nchars;
  937. if(t->org<=q0 && (q0<qe || (q0==qe && qe==t->file->nc+t->ncache)))
  938. textscrdraw(t);
  939. else{
  940. if(t->w->nopen[QWevent] > 0)
  941. nl = 3*t->maxlines/4;
  942. else
  943. nl = t->maxlines/4;
  944. q = textbacknl(t, q0, nl);
  945. /* avoid going backwards if trying to go forwards - long lines! */
  946. if(!(q0>t->org && q<t->org))
  947. textsetorigin(t, q, TRUE);
  948. while(q0 > t->org+t->nchars)
  949. textsetorigin(t, t->org+1, FALSE);
  950. }
  951. }
  952. static
  953. int
  954. region(int a, int b)
  955. {
  956. if(a < b)
  957. return -1;
  958. if(a == b)
  959. return 0;
  960. return 1;
  961. }
  962. void
  963. selrestore(Frame *f, Point pt0, uint p0, uint p1)
  964. {
  965. if(p1<=f->p0 || p0>=f->p1){
  966. /* no overlap */
  967. frdrawsel0(f, pt0, p0, p1, f->cols[BACK], f->cols[TEXT]);
  968. return;
  969. }
  970. if(p0>=f->p0 && p1<=f->p1){
  971. /* entirely inside */
  972. frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
  973. return;
  974. }
  975. /* they now are known to overlap */
  976. /* before selection */
  977. if(p0 < f->p0){
  978. frdrawsel0(f, pt0, p0, f->p0, f->cols[BACK], f->cols[TEXT]);
  979. p0 = f->p0;
  980. pt0 = frptofchar(f, p0);
  981. }
  982. /* after selection */
  983. if(p1 > f->p1){
  984. frdrawsel0(f, frptofchar(f, f->p1), f->p1, p1, f->cols[BACK], f->cols[TEXT]);
  985. p1 = f->p1;
  986. }
  987. /* inside selection */
  988. frdrawsel0(f, pt0, p0, p1, f->cols[HIGH], f->cols[HTEXT]);
  989. }
  990. void
  991. textsetselect(Text *t, uint q0, uint q1)
  992. {
  993. int p0, p1;
  994. /* t->p0 and t->p1 are always right; t->q0 and t->q1 may be off */
  995. t->q0 = q0;
  996. t->q1 = q1;
  997. /* compute desired p0,p1 from q0,q1 */
  998. p0 = q0-t->org;
  999. p1 = q1-t->org;
  1000. if(p0 < 0)
  1001. p0 = 0;
  1002. if(p1 < 0)
  1003. p1 = 0;
  1004. if(p0 > t->nchars)
  1005. p0 = t->nchars;
  1006. if(p1 > t->nchars)
  1007. p1 = t->nchars;
  1008. if(p0==t->p0 && p1==t->p1)
  1009. return;
  1010. /* screen disagrees with desired selection */
  1011. if(t->p1<=p0 || p1<=t->p0 || p0==p1 || t->p1==t->p0){
  1012. /* no overlap or too easy to bother trying */
  1013. frdrawsel(t, frptofchar(t, t->p0), t->p0, t->p1, 0);
  1014. frdrawsel(t, frptofchar(t, p0), p0, p1, 1);
  1015. goto Return;
  1016. }
  1017. /* overlap; avoid unnecessary painting */
  1018. if(p0 < t->p0){
  1019. /* extend selection backwards */
  1020. frdrawsel(t, frptofchar(t, p0), p0, t->p0, 1);
  1021. }else if(p0 > t->p0){
  1022. /* trim first part of selection */
  1023. frdrawsel(t, frptofchar(t, t->p0), t->p0, p0, 0);
  1024. }
  1025. if(p1 > t->p1){
  1026. /* extend selection forwards */
  1027. frdrawsel(t, frptofchar(t, t->p1), t->p1, p1, 1);
  1028. }else if(p1 < t->p1){
  1029. /* trim last part of selection */
  1030. frdrawsel(t, frptofchar(t, p1), p1, t->p1, 0);
  1031. }
  1032. Return:
  1033. t->p0 = p0;
  1034. t->p1 = p1;
  1035. }
  1036. /*
  1037. * Release the button in less than DELAY ms and it's considered a null selection
  1038. * if the mouse hardly moved, regardless of whether it crossed a char boundary.
  1039. */
  1040. enum {
  1041. DELAY = 2,
  1042. MINMOVE = 4,
  1043. };
  1044. uint
  1045. xselect(Frame *f, Mousectl *mc, Image *col, uint *p1p) /* when called, button is down */
  1046. {
  1047. uint p0, p1, q, tmp;
  1048. ulong msec;
  1049. Point mp, pt0, pt1, qt;
  1050. int reg, b;
  1051. mp = mc->xy;
  1052. b = mc->buttons;
  1053. msec = mc->msec;
  1054. /* remove tick */
  1055. if(f->p0 == f->p1)
  1056. frtick(f, frptofchar(f, f->p0), 0);
  1057. p0 = p1 = frcharofpt(f, mp);
  1058. pt0 = frptofchar(f, p0);
  1059. pt1 = frptofchar(f, p1);
  1060. reg = 0;
  1061. frtick(f, pt0, 1);
  1062. do{
  1063. q = frcharofpt(f, mc->xy);
  1064. if(p1 != q){
  1065. if(p0 == p1)
  1066. frtick(f, pt0, 0);
  1067. if(reg != region(q, p0)){ /* crossed starting point; reset */
  1068. if(reg > 0)
  1069. selrestore(f, pt0, p0, p1);
  1070. else if(reg < 0)
  1071. selrestore(f, pt1, p1, p0);
  1072. p1 = p0;
  1073. pt1 = pt0;
  1074. reg = region(q, p0);
  1075. if(reg == 0)
  1076. frdrawsel0(f, pt0, p0, p1, col, display->white);
  1077. }
  1078. qt = frptofchar(f, q);
  1079. if(reg > 0){
  1080. if(q > p1)
  1081. frdrawsel0(f, pt1, p1, q, col, display->white);
  1082. else if(q < p1)
  1083. selrestore(f, qt, q, p1);
  1084. }else if(reg < 0){
  1085. if(q > p1)
  1086. selrestore(f, pt1, p1, q);
  1087. else
  1088. frdrawsel0(f, qt, q, p1, col, display->white);
  1089. }
  1090. p1 = q;
  1091. pt1 = qt;
  1092. }
  1093. if(p0 == p1)
  1094. frtick(f, pt0, 1);
  1095. flushimage(f->display, 1);
  1096. readmouse(mc);
  1097. }while(mc->buttons == b);
  1098. if(mc->msec-msec < DELAY && p0!=p1
  1099. && abs(mp.x-mc->xy.x)<MINMOVE
  1100. && abs(mp.y-mc->xy.y)<MINMOVE) {
  1101. if(reg > 0)
  1102. selrestore(f, pt0, p0, p1);
  1103. else if(reg < 0)
  1104. selrestore(f, pt1, p1, p0);
  1105. p1 = p0;
  1106. }
  1107. if(p1 < p0){
  1108. tmp = p0;
  1109. p0 = p1;
  1110. p1 = tmp;
  1111. }
  1112. pt0 = frptofchar(f, p0);
  1113. if(p0 == p1)
  1114. frtick(f, pt0, 0);
  1115. selrestore(f, pt0, p0, p1);
  1116. /* restore tick */
  1117. if(f->p0 == f->p1)
  1118. frtick(f, frptofchar(f, f->p0), 1);
  1119. flushimage(f->display, 1);
  1120. *p1p = p1;
  1121. return p0;
  1122. }
  1123. int
  1124. textselect23(Text *t, uint *q0, uint *q1, Image *high, int mask)
  1125. {
  1126. uint p0, p1;
  1127. int buts;
  1128. p0 = xselect(t, mousectl, high, &p1);
  1129. buts = mousectl->buttons;
  1130. if((buts & mask) == 0){
  1131. *q0 = p0+t->org;
  1132. *q1 = p1+t->org;
  1133. }
  1134. while(mousectl->buttons)
  1135. readmouse(mousectl);
  1136. return buts;
  1137. }
  1138. int
  1139. textselect2(Text *t, uint *q0, uint *q1, Text **tp)
  1140. {
  1141. int buts;
  1142. *tp = nil;
  1143. buts = textselect23(t, q0, q1, but2col, 4);
  1144. if(buts & 4)
  1145. return 0;
  1146. if(buts & 1){ /* pick up argument */
  1147. *tp = argtext;
  1148. return 1;
  1149. }
  1150. return 1;
  1151. }
  1152. int
  1153. textselect3(Text *t, uint *q0, uint *q1)
  1154. {
  1155. int h;
  1156. h = (textselect23(t, q0, q1, but3col, 1|2) == 0);
  1157. return h;
  1158. }
  1159. static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 };
  1160. static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 };
  1161. static Rune left2[] = { L'\n', 0 };
  1162. static Rune left3[] = { L'\'', L'"', L'`', 0 };
  1163. static
  1164. Rune *left[] = {
  1165. left1,
  1166. left2,
  1167. left3,
  1168. nil
  1169. };
  1170. static
  1171. Rune *right[] = {
  1172. right1,
  1173. left2,
  1174. left3,
  1175. nil
  1176. };
  1177. void
  1178. textdoubleclick(Text *t, uint *q0, uint *q1)
  1179. {
  1180. int c, i;
  1181. Rune *r, *l, *p;
  1182. uint q;
  1183. for(i=0; left[i]!=nil; i++){
  1184. q = *q0;
  1185. l = left[i];
  1186. r = right[i];
  1187. /* try matching character to left, looking right */
  1188. if(q == 0)
  1189. c = '\n';
  1190. else
  1191. c = textreadc(t, q-1);
  1192. p = runestrchr(l, c);
  1193. if(p != nil){
  1194. if(textclickmatch(t, c, r[p-l], 1, &q))
  1195. *q1 = q-(c!='\n');
  1196. return;
  1197. }
  1198. /* try matching character to right, looking left */
  1199. if(q == t->file->nc)
  1200. c = '\n';
  1201. else
  1202. c = textreadc(t, q);
  1203. p = runestrchr(r, c);
  1204. if(p != nil){
  1205. if(textclickmatch(t, c, l[p-r], -1, &q)){
  1206. *q1 = *q0+(*q0<t->file->nc && c=='\n');
  1207. *q0 = q;
  1208. if(c!='\n' || q!=0 || textreadc(t, 0)=='\n')
  1209. (*q0)++;
  1210. }
  1211. return;
  1212. }
  1213. }
  1214. /* try filling out word to right */
  1215. while(*q1<t->file->nc && isalnum(textreadc(t, *q1)))
  1216. (*q1)++;
  1217. /* try filling out word to left */
  1218. while(*q0>0 && isalnum(textreadc(t, *q0-1)))
  1219. (*q0)--;
  1220. }
  1221. int
  1222. textclickmatch(Text *t, int cl, int cr, int dir, uint *q)
  1223. {
  1224. Rune c;
  1225. int nest;
  1226. nest = 1;
  1227. for(;;){
  1228. if(dir > 0){
  1229. if(*q == t->file->nc)
  1230. break;
  1231. c = textreadc(t, *q);
  1232. (*q)++;
  1233. }else{
  1234. if(*q == 0)
  1235. break;
  1236. (*q)--;
  1237. c = textreadc(t, *q);
  1238. }
  1239. if(c == cr){
  1240. if(--nest==0)
  1241. return 1;
  1242. }else if(c == cl)
  1243. nest++;
  1244. }
  1245. return cl=='\n' && nest==1;
  1246. }
  1247. uint
  1248. textbacknl(Text *t, uint p, uint n)
  1249. {
  1250. int i, j;
  1251. /* look for start of this line if n==0 */
  1252. if(n==0 && p>0 && textreadc(t, p-1)!='\n')
  1253. n = 1;
  1254. i = n;
  1255. while(i-->0 && p>0){
  1256. --p; /* it's at a newline now; back over it */
  1257. if(p == 0)
  1258. break;
  1259. /* at 128 chars, call it a line anyway */
  1260. for(j=128; --j>0 && p>0; p--)
  1261. if(textreadc(t, p-1)=='\n')
  1262. break;
  1263. }
  1264. return p;
  1265. }
  1266. void
  1267. textsetorigin(Text *t, uint org, int exact)
  1268. {
  1269. int i, a, fixup;
  1270. Rune *r;
  1271. uint n;
  1272. if(org>0 && !exact){
  1273. /* org is an estimate of the char posn; find a newline */
  1274. /* don't try harder than 256 chars */
  1275. for(i=0; i<256 && org<t->file->nc; i++){
  1276. if(textreadc(t, org) == '\n'){
  1277. org++;
  1278. break;
  1279. }
  1280. org++;
  1281. }
  1282. }
  1283. a = org-t->org;
  1284. fixup = 0;
  1285. if(a>=0 && a<t->nchars){
  1286. frdelete(t, 0, a);
  1287. fixup = 1; /* frdelete can leave end of last line in wrong selection mode; it doesn't know what follows */
  1288. }
  1289. else if(a<0 && -a<t->nchars){
  1290. n = t->org - org;
  1291. r = runemalloc(n);
  1292. bufread(t->file, org, r, n);
  1293. frinsert(t, r, r+n, 0);
  1294. free(r);
  1295. }else
  1296. frdelete(t, 0, t->nchars);
  1297. t->org = org;
  1298. textfill(t);
  1299. textscrdraw(t);
  1300. textsetselect(t, t->q0, t->q1);
  1301. if(fixup && t->p1 > t->p0)
  1302. frdrawsel(t, frptofchar(t, t->p1-1), t->p1-1, t->p1, 1);
  1303. }
  1304. void
  1305. textreset(Text *t)
  1306. {
  1307. t->file->seq = 0;
  1308. t->eq0 = ~0;
  1309. /* do t->delete(0, t->nc, TRUE) without building backup stuff */
  1310. textsetselect(t, t->org, t->org);
  1311. frdelete(t, 0, t->nchars);
  1312. t->org = 0;
  1313. t->q0 = 0;
  1314. t->q1 = 0;
  1315. filereset(t->file);
  1316. bufreset(t->file);
  1317. }