plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

rows.c (17123B)


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