master rows.c
ohmu  ·  2026-09-13
  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 <bio.h>
 11#include <plumb.h>
 12#include <libsec.h>
 13#include "parser.h"
 14#include "dat.h"
 15#include "fns.h"
 16
 17static Rune Lcolhdr[] = {
 18	'N', 'e', 'w', 'c', 'o', 'l', ' ',
 19	'K', 'i', 'l', 'l', ' ',
 20	'P', 'u', 't', 'a', 'l', 'l', ' ',
 21	'D', 'u', 'm', 'p', ' ',
 22	'E', 'x', 'i', 't', ' ',
 23	0
 24};
 25
 26Image *rowsbg;
 27Image *bordercol;
 28
 29void
 30rowinit(Row *row, Rectangle r)
 31{
 32	Rectangle r1;
 33	Text *t;
 34	
 35    if(ispastel("root"))
 36    	rowsbg = allocimagemix(display, C_ROOTBG, C_MIX);
 37    else
 38        rowsbg = allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_ROOTBG);
 39     
 40    if(ispastel("border"))
 41    	bordercol = allocimagemix(display, C_BORDER, C_MIX);
 42    else
 43        bordercol = allocimage(display, Rect(0,0,1,1), RGBA32, 1, C_BORDER);
 44 
 45	draw(screen, r, rowsbg, nil, ZP);
 46	row->r = r;
 47	row->col = nil;
 48	row->ncol = 0;
 49	r1 = r;
 50	r1.max.y = r1.min.y + font->height;
 51	t = &row->tag;
 52	textinit(t, fileaddtext(nil, t), r1, rfget(FALSE, FALSE, FALSE, nil), tagcols);
 53	t->what = Rowtag;
 54	t->row = row;
 55	t->w = nil;
 56	t->col = nil;
 57	r1.min.y = r1.max.y;
 58	r1.max.y += Border;
 59    draw(screen, r1, bordercol, nil, ZP);
 60	textinsert(t, 0, Lcolhdr, 29, TRUE);
 61	textsetselect(t, t->file->b.nc, t->file->b.nc);
 62}
 63
 64Column*
 65rowadd(Row *row, Column *c, int x)
 66{
 67	Rectangle r, r1;
 68	Column *d;
 69	int i;
 70
 71	d = nil;
 72	r = row->r;
 73	r.min.y = row->tag.fr.r.max.y+Border;
 74	if(x<r.min.x && row->ncol>0){	/*steal 40% of last column by default */
 75		d = row->col[row->ncol-1];
 76		x = d->r.min.x + 3*Dx(d->r)/5;
 77	}
 78	/* look for column we'll land on */
 79	for(i=0; i<row->ncol; i++){
 80		d = row->col[i];
 81		if(x < d->r.max.x)
 82			break;
 83	}
 84	if(row->ncol > 0){
 85		if(i < row->ncol)
 86			i++;	/* new column will go after d */
 87		r = d->r;
 88		if(Dx(r) < 100)
 89			return nil;
 90		draw(screen, r, rowsbg, nil, ZP);
 91		r1 = r;
 92		r1.max.x = min(x-Border, r.max.x-50);
 93		if(Dx(r1) < 50)
 94			r1.max.x = r1.min.x+50;
 95		colresize(d, r1);
 96		r1.min.x = r1.max.x;
 97		r1.max.x = r1.min.x+Border;
 98		draw(screen, r1, bordercol, nil, ZP);
 99		r.min.x = r1.max.x;
100	}
101	if(c == nil){
102		c = emalloc(sizeof(Column));
103		colinit(c, r);
104		incref(&reffont.ref);
105	}else
106		colresize(c, r);
107	c->row = row;
108	c->tag.row = row;
109	row->col = realloc(row->col, (row->ncol+1)*sizeof(Column*));
110	memmove(row->col+i+1, row->col+i, (row->ncol-i)*sizeof(Column*));
111	row->col[i] = c;
112	row->ncol++;
113	clearmouse();
114	return c;
115}
116
117void
118rowresize(Row *row, Rectangle r)
119{
120	int i, deltax;
121	Rectangle or, r1, r2;
122	Column *c;
123
124	or = row->r;
125	deltax = r.min.x - or.min.x;
126	row->r = r;
127	r1 = r;
128	r1.max.y = r1.min.y + font->height;
129	textresize(&row->tag, r1, TRUE);
130	r1.min.y = r1.max.y;
131	r1.max.y += Border;
132	draw(screen, r1, bordercol, nil, ZP);
133	r.min.y = r1.max.y;
134	r1 = r;
135	r1.max.x = r1.min.x;
136	for(i=0; i<row->ncol; i++){
137		c = row->col[i];
138		r1.min.x = r1.max.x;
139		/* the test should not be necessary, but guarantee we don't lose a pixel */
140		if(i == row->ncol-1)
141			r1.max.x = r.max.x;
142		else
143			r1.max.x = (c->r.max.x-or.min.x)*Dx(r)/Dx(or) + deltax;
144		if(i > 0){
145			r2 = r1;
146			r2.max.x = r2.min.x+Border;
147			draw(screen, r2, bordercol, nil, ZP);
148			r1.min.x = r2.max.x;
149		}
150		colresize(c, r1);
151	}
152}
153
154void
155rowdragcol(Row *row, Column *c, int _0)
156{
157	Rectangle r;
158	int i, b, x;
159	Point p, op;
160	Column *d;
161
162	USED(_0);
163
164	clearmouse();
165	setcursor(mousectl, &boxcursor);
166	b = mouse->buttons;
167	op = mouse->xy;
168	while(mouse->buttons == b)
169		readmouse(mousectl);
170	setcursor(mousectl, nil);
171	if(mouse->buttons){
172		while(mouse->buttons)
173			readmouse(mousectl);
174		return;
175	}
176
177	for(i=0; i<row->ncol; i++)
178		if(row->col[i] == c)
179			goto Found;
180	error("can't find column");
181
182  Found:
183	p = mouse->xy;
184	if((abs(p.x-op.x)<5 && abs(p.y-op.y)<5))
185		return;
186	if((i>0 && p.x<row->col[i-1]->r.min.x) || (i<row->ncol-1 && p.x>c->r.max.x)){
187		/* shuffle */
188		x = c->r.min.x;
189		rowclose(row, c, FALSE);
190		if(rowadd(row, c, p.x) == nil)	/* whoops! */
191		if(rowadd(row, c, x) == nil)		/* WHOOPS! */
192		if(rowadd(row, c, -1)==nil){		/* shit! */
193			rowclose(row, c, TRUE);
194			return;
195		}
196		colmousebut(c);
197		return;
198	}
199	if(i == 0)
200		return;
201	d = row->col[i-1];
202	if(p.x < d->r.min.x+80+Scrollwid)
203		p.x = d->r.min.x+80+Scrollwid;
204	if(p.x > c->r.max.x-80-Scrollwid)
205		p.x = c->r.max.x-80-Scrollwid;
206	r = d->r;
207	r.max.x = c->r.max.x;
208	draw(screen, r, bordercol, nil, ZP);
209	r.max.x = p.x;
210	colresize(d, r);
211	r = c->r;
212	r.min.x = p.x;
213	r.max.x = r.min.x;
214	r.max.x += Border;
215	draw(screen, r, bordercol, nil, ZP);
216	r.min.x = r.max.x;
217	r.max.x = c->r.max.x;
218	colresize(c, r);
219	colmousebut(c);
220}
221
222void
223rowclose(Row *row, Column *c, int dofree)
224{
225	Rectangle r;
226	int i;
227
228	for(i=0; i<row->ncol; i++)
229		if(row->col[i] == c)
230			goto Found;
231	error("can't find column");
232  Found:
233	r = c->r;
234	if(dofree)
235		colcloseall(c);
236	row->ncol--;
237	memmove(row->col+i, row->col+i+1, (row->ncol-i)*sizeof(Column*));
238	row->col = realloc(row->col, row->ncol*sizeof(Column*));
239	if(row->ncol == 0){
240		draw(screen, r, rowsbg, nil, ZP);
241		return;
242	}
243	if(i == row->ncol){		/* extend last column right */
244		c = row->col[i-1];
245		r.min.x = c->r.min.x;
246		r.max.x = row->r.max.x;
247	}else{			/* extend next window left */
248		c = row->col[i];
249		r.max.x = c->r.max.x;
250	}
251	draw(screen, r, rowsbg, nil, ZP);
252	colresize(c, r);
253}
254
255Column*
256rowwhichcol(Row *row, Point p)
257{
258	int i;
259	Column *c;
260
261	for(i=0; i<row->ncol; i++){
262		c = row->col[i];
263		if(ptinrect(p, c->r))
264			return c;
265	}
266	return nil;
267}
268
269Text*
270rowwhich(Row *row, Point p)
271{
272	Column *c;
273
274	if(ptinrect(p, row->tag.all))
275		return &row->tag;
276	c = rowwhichcol(row, p);
277	if(c)
278		return colwhich(c, p);
279	return nil;
280}
281
282Text*
283rowtype(Row *row, Rune r, Point p)
284{
285	Window *w;
286	Text *t;
287
288	if(r == 0)
289		r = Runeerror;
290
291	clearmouse();
292	qlock(&row->lk);
293	if(bartflag)
294		t = barttext;
295	else
296		t = rowwhich(row, p);
297	if(t!=nil && !(t->what==Tag && ptinrect(p, t->scrollr))){
298		w = t->w;
299		if(w == nil)
300			texttype(t, r);
301		else{
302			winlock(w, 'K');
303			wintype(w, t, r);
304			/* Expand tag if necessary */
305			if(t->what == Tag){
306				t->w->tagsafe = FALSE;
307				if(r == '\n')
308					t->w->tagexpand = TRUE;
309				winresize(w, w->r, TRUE, TRUE);
310			}
311			winunlock(w);
312		}
313	}
314	qunlock(&row->lk);
315	return t;
316}
317
318int
319rowclean(Row *row)
320{
321	int clean;
322	int i;
323
324	clean = TRUE;
325	for(i=0; i<row->ncol; i++)
326		clean &= colclean(row->col[i]);
327	return clean;
328}
329
330void
331rowdump(Row *row, char *file)
332{
333	int i, j, fd, m, n, dumped;
334	uint q0, q1;
335	Biobuf *b;
336	char *buf, *a, *fontname;
337	Rune *r;
338	Column *c;
339	Window *w, *w1;
340	Text *t;
341
342	if(row->ncol == 0)
343		return;
344	buf = fbufalloc();
345	if(file == nil){
346		if(home == nil){
347			warning(nil, "can't find file for dump: $home not defined\n");
348			goto Rescue;
349		}
350		sprint(buf, "%s", dumppath);
351		file = buf;
352	}
353	fd = create(file, OWRITE, 0600);
354	if(fd < 0){
355		warning(nil, "can't open %s: %r\n", file);
356		goto Rescue;
357	}
358	b = emalloc(sizeof(Biobuf));
359	Binit(b, fd, OWRITE);
360	r = fbufalloc();
361	Bprint(b, "%s\n", wdir);
362	Bprint(b, "%s\n", fontnames[0]);
363	Bprint(b, "%s\n", fontnames[1]);
364	for(i=0; i<row->ncol; i++){
365		c = row->col[i];
366		Bprint(b, "%11.7f", 100.0*(c->r.min.x-row->r.min.x)/Dx(row->r));
367		if(i == row->ncol-1)
368			Bputc(b, '\n');
369		else
370			Bputc(b, ' ');
371	}
372	for(i=0; i<row->ncol; i++){
373		c = row->col[i];
374		for(j=0; j<c->nw; j++)
375			c->w[j]->body.file->dumpid = 0;
376	}
377	m = min(RBUFSIZE, row->tag.file->b.nc);
378	bufread(&row->tag.file->b, 0, r, m);
379	n = 0;
380	while(n<m && r[n]!='\n')
381		n++;
382	Bprint(b, "w %.*S\n", n, r);
383	for(i=0; i<row->ncol; i++){
384		c = row->col[i];
385		m = min(RBUFSIZE, c->tag.file->b.nc);
386		bufread(&c->tag.file->b, 0, r, m);
387		n = 0;
388		while(n<m && r[n]!='\n')
389			n++;
390		Bprint(b, "c%11d %.*S\n", i, n, r);
391	}
392	for(i=0; i<row->ncol; i++){
393		c = row->col[i];
394		for(j=0; j<c->nw; j++){
395			w = c->w[j];
396			wincommit(w, &w->tag);
397			t = &w->body;
398			/* windows owned by others get special treatment */
399			if(w->nopen[QWevent] > 0)
400				if(w->dumpstr == nil)
401					continue;
402			/* zeroxes of external windows are tossed */
403			if(t->file->ntext > 1)
404				for(n=0; n<t->file->ntext; n++){
405					w1 = t->file->text[n]->w;
406					if(w == w1)
407						continue;
408					if(w1->nopen[QWevent])
409						goto Continue2;
410				}
411			fontname = "";
412			if(t->reffont->f != font)
413				fontname = t->reffont->f->name;
414			if(t->file->nname)
415				a = runetobyte(t->file->name, t->file->nname);
416			else
417				a = emalloc(1);
418			if(t->file->dumpid){
419				dumped = FALSE;
420				Bprint(b, "x%11d %11d %11d %11d %11.7f %s\n", i, t->file->dumpid,
421					w->body.q0, w->body.q1,
422					100.0*(w->r.min.y-c->r.min.y)/Dy(c->r),
423					fontname);
424			}else if(w->dumpstr){
425				dumped = FALSE;
426				Bprint(b, "e%11d %11d %11d %11d %11.7f %s\n", i, t->file->dumpid,
427					0, 0,
428					100.0*(w->r.min.y-c->r.min.y)/Dy(c->r),
429					fontname);
430			}else if((w->dirty==FALSE && access(a, 0)==0) || w->isdir){
431				dumped = FALSE;
432				t->file->dumpid = w->id;
433				Bprint(b, "f%11d %11d %11d %11d %11.7f %s\n", i, w->id,
434					w->body.q0, w->body.q1,
435					100.0*(w->r.min.y-c->r.min.y)/Dy(c->r),
436					fontname);
437			}else{
438				dumped = TRUE;
439				t->file->dumpid = w->id;
440				Bprint(b, "F%11d %11d %11d %11d %11.7f %11d %s\n", i, j,
441					w->body.q0, w->body.q1,
442					100.0*(w->r.min.y-c->r.min.y)/Dy(c->r),
443					w->body.file->b.nc, fontname);
444			}
445			free(a);
446			winctlprint(w, buf, 0);
447			Bwrite(b, buf, strlen(buf));
448			m = min(RBUFSIZE, w->tag.file->b.nc);
449			bufread(&w->tag.file->b, 0, r, m);
450			n = 0;
451			while(n<m && r[n]!='\n')
452				n++;
453			Bprint(b, "%.*S\n", n, r);
454			if(dumped){
455				q0 = 0;
456				q1 = t->file->b.nc;
457				while(q0 < q1){
458					n = q1 - q0;
459					if(n > BUFSIZE/UTFmax)
460						n = BUFSIZE/UTFmax;
461					bufread(&t->file->b, q0, r, n);
462					Bprint(b, "%.*S", n, r);
463					q0 += n;
464				}
465			}
466			if(w->dumpstr){
467				if(w->dumpdir)
468					Bprint(b, "%s\n%s\n", w->dumpdir, w->dumpstr);
469				else
470					Bprint(b, "\n%s\n", w->dumpstr);
471			}
472    Continue2:;
473		}
474	}
475	Bterm(b);
476	close(fd);
477	free(b);
478	fbuffree(r);
479
480   Rescue:
481	fbuffree(buf);
482}
483
484static
485char*
486rdline(Biobuf *b, int *linep)
487{
488	char *l;
489
490	l = Brdline(b, '\n');
491	if(l)
492		(*linep)++;
493	return l;
494}
495
496/*
497 * Get font names from load file so we don't load fonts we won't use
498 */
499void
500rowloadfonts(char *file)
501{
502	int i;
503	Biobuf *b;
504	char *l;
505
506	b = Bopen(file, OREAD);
507	if(b == nil)
508		return;
509	/* current directory */
510	l = Brdline(b, '\n');
511	if(l == nil)
512		goto Return;
513	/* global fonts */
514	for(i=0; i<2; i++){
515		l = Brdline(b, '\n');
516		if(l == nil)
517			goto Return;
518		l[Blinelen(b)-1] = 0;
519		if(*l && strcmp(l, fontnames[i])!=0){
520			free(fontnames[i]);
521			fontnames[i] = estrdup(l);
522		}
523	}
524    Return:
525	Bterm(b);
526}
527
528int
529rowload(Row *row, char *file, int initing)
530{
531	int i, j, line, y, nr, nfontr, n, ns, ndumped, dumpid, x, fd, done;
532	double percent;
533	Biobuf *b, *bout;
534	char *buf, *l, *t, *fontname;
535	Rune *r, *fontr;
536	int rune;
537	Column *c, *c1, *c2;
538	uint q0, q1;
539	Rectangle r1, r2;
540	Window *w;
541
542	buf = fbufalloc();
543	if(file == nil){
544		if(home == nil){
545			warning(nil, "can't find file for load: $home not defined\n");
546			goto Rescue1;
547		}
548		sprint(buf, "%s", dumppath);
549		file = buf;
550	}
551	b = Bopen(file, OREAD);
552	if(b == nil){
553		warning(nil, "can't open load file %s: %r\n", file);
554		goto Rescue1;
555	}
556	/* current directory */
557	line = 0;
558	l = rdline(b, &line);
559	if(l == nil)
560		goto Rescue2;
561	l[Blinelen(b)-1] = 0;
562	if(chdir(l) < 0){
563		warning(nil, "can't chdir %s\n", l);
564		goto Rescue2;
565	}
566	/* global fonts */
567	for(i=0; i<2; i++){
568		l = rdline(b, &line);
569		if(l == nil)
570			goto Rescue2;
571		l[Blinelen(b)-1] = 0;
572		if(*l && strcmp(l, fontnames[i])!=0)
573			rfget(i, TRUE, i==0 && initing, l);
574	}
575	if(initing && row->ncol==0)
576		rowinit(row, screen->clipr);
577	l = rdline(b, &line);
578	if(l == nil)
579		goto Rescue2;
580	j = Blinelen(b)/12;
581	if(j<=0 || j>10)
582		goto Rescue2;
583	for(i=0; i<j; i++){
584		percent = atof(l+i*12);
585		if(percent<0 || percent>=100)
586			goto Rescue2;
587		x = row->r.min.x+percent*Dx(row->r)/100+0.5;
588		if(i < row->ncol){
589			if(i == 0)
590				continue;
591			c1 = row->col[i-1];
592			c2 = row->col[i];
593			r1 = c1->r;
594			r2 = c2->r;
595			if(x<Border)
596				x = Border;
597			r1.max.x = x-Border;
598			r2.min.x = x;
599			if(Dx(r1) < 50 || Dx(r2) < 50)
600				continue;
601			draw(screen, Rpt(r1.min, r2.max), bordercol, nil, ZP);
602			colresize(c1, r1);
603			colresize(c2, r2);
604			r2.min.x = x-Border;
605			r2.max.x = x;
606			draw(screen, r2, bordercol, nil, ZP);
607		}
608		if(i >= row->ncol)
609			rowadd(row, nil, x);
610	}
611	done = 0;
612	while(!done){
613		l = rdline(b, &line);
614		if(l == nil)
615			break;
616		switch(l[0]){
617		case 'c':
618			l[Blinelen(b)-1] = 0;
619			i = atoi(l+1+0*12);
620			r = bytetorune(l+1*12, &nr);
621			ns = -1;
622			for(n=0; n<nr; n++){
623				if(r[n] == '/')
624					ns = n;
625				if(r[n] == ' ')
626					break;
627			}
628			textdelete(&row->col[i]->tag, 0, row->col[i]->tag.file->b.nc, TRUE);
629			textinsert(&row->col[i]->tag, 0, r+n+1, nr-(n+1), TRUE);
630			break;
631		case 'w':
632			l[Blinelen(b)-1] = 0;
633			r = bytetorune(l+2, &nr);
634			ns = -1;
635			for(n=0; n<nr; n++){
636				if(r[n] == '/')
637					ns = n;
638				if(r[n] == ' ')
639					break;
640			}
641			textdelete(&row->tag, 0, row->tag.file->b.nc, TRUE);
642			textinsert(&row->tag, 0, r, nr, TRUE);
643			break;
644		default:
645			done = 1;
646			break;
647		}
648	}
649	for(;;){
650		if(l == nil)
651			break;
652		dumpid = 0;
653		switch(l[0]){
654		case 'e':
655			if(Blinelen(b) < 1+5*12+1)
656				goto Rescue2;
657			l = rdline(b, &line);	/* ctl line; ignored */
658			if(l == nil)
659				goto Rescue2;
660			l = rdline(b, &line);	/* directory */
661			if(l == nil)
662				goto Rescue2;
663			l[Blinelen(b)-1] = 0;
664			if(*l == '\0'){
665				if(home == nil)
666					r = bytetorune("./", &nr);
667				else{
668					t = emalloc(strlen(home)+1+1);
669					sprint(t, "%s/", home);
670					r = bytetorune(t, &nr);
671					free(t);
672				}
673			}else
674				r = bytetorune(l, &nr);
675			l = rdline(b, &line);	/* command */
676			if(l == nil)
677				goto Rescue2;
678			t = emalloc(Blinelen(b)+1);
679			memmove(t, l, Blinelen(b));
680			run(nil, t, r, nr, TRUE, nil, nil, FALSE);
681			/* r is freed in run() */
682			goto Nextline;
683		case 'f':
684			if(Blinelen(b) < 1+5*12+1)
685				goto Rescue2;
686			fontname = l+1+5*12;
687			ndumped = -1;
688			break;
689		case 'F':
690			if(Blinelen(b) < 1+6*12+1)
691				goto Rescue2;
692			fontname = l+1+6*12;
693			ndumped = atoi(l+1+5*12+1);
694			break;
695		case 'x':
696			if(Blinelen(b) < 1+5*12+1)
697				goto Rescue2;
698			fontname = l+1+5*12;
699			ndumped = -1;
700			dumpid = atoi(l+1+1*12);
701			break;
702		default:
703			goto Rescue2;
704		}
705		l[Blinelen(b)-1] = 0;
706		fontr = nil;
707		nfontr = 0;
708		if(*fontname)
709			fontr = bytetorune(fontname, &nfontr);
710		i = atoi(l+1+0*12);
711		j = atoi(l+1+1*12);
712		q0 = atoi(l+1+2*12);
713		q1 = atoi(l+1+3*12);
714		percent = atof(l+1+4*12);
715		if(i<0 || i>10)
716			goto Rescue2;
717		if(i > row->ncol)
718			i = row->ncol;
719		c = row->col[i];
720		y = c->r.min.y+(percent*Dy(c->r))/100+0.5;
721		if(y<c->r.min.y || y>=c->r.max.y)
722			y = -1;
723		if(dumpid == 0)
724			w = coladd(c, nil, nil, y);
725		else
726			w = coladd(c, nil, lookid(dumpid, TRUE), y);
727		if(w == nil)
728			goto Nextline;
729		w->dumpid = j;
730		l = rdline(b, &line);
731		if(l == nil)
732			goto Rescue2;
733		l[Blinelen(b)-1] = 0;
734		r = bytetorune(l+5*12, &nr);
735		ns = -1;
736		for(n=0; n<nr; n++){
737			if(r[n] == '/')
738				ns = n;
739			if(r[n] == ' ')
740				break;
741		}
742		if(dumpid == 0)
743			winsetname(w, r, n);
744		for(; n<nr; n++)
745			if(r[n] == '|')
746				break;
747		wincleartag(w);
748		textinsert(&w->tag, w->tag.file->b.nc, r+n+1, nr-(n+1), TRUE);
749		if(ndumped >= 0){
750			/* simplest thing is to put it in a file and load that */
751			sprint(buf, "/tmp/d%d.%.4sacme", PID, getuser());
752			fd = create(buf, OWRITE, 0600);
753			if(fd < 0){
754				free(r);
755				warning(nil, "can't create temp file: %r\n");
756				goto Rescue2;
757			}
758			bout = emalloc(sizeof(Biobuf));
759			Binit(bout, fd, OWRITE);
760			for(n=0; n<ndumped; n++){
761				rune = Bgetrune(b);
762				if(rune == '\n')
763					line++;
764				if(rune == Beof){
765					free(r);
766					Bterm(bout);
767					free(bout);
768					close(fd);
769					remove(buf);
770					goto Rescue2;
771				}
772				Bputrune(bout, rune);
773			}
774			Bterm(bout);
775			free(bout);
776			textload(&w->body, 0, buf, 1);
777			remove(buf);
778			close(fd);
779			w->body.file->mod = TRUE;
780			for(n=0; n<w->body.file->ntext; n++)
781				w->body.file->text[n]->w->dirty = TRUE;
782			winsettag(w);
783		}else if(dumpid==0 && r[ns+1]!='+' && r[ns+1]!='-')
784			get(&w->body, nil, nil, FALSE, XXX, nil, 0);
785		if(fontr){
786			fontx(&w->body, nil, nil, 0, 0, fontr, nfontr);
787			free(fontr);
788		}
789		free(r);
790		if(q0>w->body.file->b.nc || q1>w->body.file->b.nc || q0>q1)
791			q0 = q1 = 0;
792		textshow(&w->body, q0, q1, 1);
793		w->maxlines = min(w->body.fr.nlines, max(w->maxlines, w->body.fr.maxlines));
794		xfidlog(w, "new");
795Nextline:
796		l = rdline(b, &line);
797	}
798	Bterm(b);
799	fbuffree(buf);
800	return TRUE;
801
802Rescue2:
803	warning(nil, "bad load file %s:%d\n", file, line);
804	Bterm(b);
805Rescue1:
806	fbuffree(buf);
807	return FALSE;
808}
809
810void
811allwindows(void (*f)(Window*, void*), void *arg)
812{
813	int i, j;
814	Column *c;
815
816	for(i=0; i<row.ncol; i++){
817		c = row.col[i];
818		for(j=0; j<c->nw; j++)
819			(*f)(c->w[j], arg);
820	}
821}