9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

file.c (10709B)


      1 #include "sam.h"
      2 
      3 /*
      4  * Structure of Undo list:
      5  * 	The Undo structure follows any associated data, so the list
      6  *	can be read backwards: read the structure, then read whatever
      7  *	data is associated (insert string, file name) and precedes it.
      8  *	The structure includes the previous value of the modify bit
      9  *	and a sequence number; successive Undo structures with the
     10  *	same sequence number represent simultaneous changes.
     11  */
     12 
     13 typedef struct Undo Undo;
     14 typedef struct Merge Merge;
     15 
     16 struct Undo
     17 {
     18 	short	type;		/* Delete, Insert, Filename, Dot, Mark */
     19 	short	mod;		/* modify bit */
     20 	uint	seq;		/* sequence number */
     21 	uint	p0;		/* location of change (unused in f) */
     22 	uint	n;		/* # runes in string or file name */
     23 };
     24 
     25 struct Merge
     26 {
     27 	File	*f;
     28 	uint	seq;		/* of logged change */
     29 	uint	p0;		/* location of change (unused in f) */
     30 	uint	n;		/* # runes to delete */
     31 	uint	nbuf;		/* # runes to insert */
     32 	Rune	buf[RBUFSIZE];
     33 };
     34 
     35 enum
     36 {
     37 	Maxmerge = 50,
     38 	Undosize = sizeof(Undo)/sizeof(Rune)
     39 };
     40 
     41 static Merge	merge;
     42 
     43 File*
     44 fileopen(void)
     45 {
     46 	File *f;
     47 
     48 	f = emalloc(sizeof(File));
     49 	f->dot.f = f;
     50 	f->ndot.f = f;
     51 	f->seq = 0;
     52 	f->mod = FALSE;
     53 	f->unread = TRUE;
     54 	Strinit0(&f->name);
     55 	return f;
     56 }
     57 
     58 int
     59 fileisdirty(File *f)
     60 {
     61 	return f->seq != f->cleanseq;
     62 }
     63 
     64 static void
     65 wrinsert(Buffer *delta, int seq, int mod, uint p0, Rune *s, uint ns)
     66 {
     67 	Undo u;
     68 
     69 	u.type = Insert;
     70 	u.mod = mod;
     71 	u.seq = seq;
     72 	u.p0 = p0;
     73 	u.n = ns;
     74 	bufinsert(delta, delta->nc, s, ns);
     75 	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
     76 }
     77 
     78 static void
     79 wrdelete(Buffer *delta, int seq, int mod, uint p0, uint p1)
     80 {
     81 	Undo u;
     82 
     83 	u.type = Delete;
     84 	u.mod = mod;
     85 	u.seq = seq;
     86 	u.p0 = p0;
     87 	u.n = p1 - p0;
     88 	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
     89 }
     90 
     91 void
     92 flushmerge(void)
     93 {
     94 	File *f;
     95 
     96 	f = merge.f;
     97 	if(f == nil)
     98 		return;
     99 	if(merge.seq != f->seq)
    100 		panic("flushmerge seq mismatch");
    101 	if(merge.n != 0)
    102 		wrdelete(&f->epsilon, f->seq, TRUE, merge.p0, merge.p0+merge.n);
    103 	if(merge.nbuf != 0)
    104 		wrinsert(&f->epsilon, f->seq, TRUE, merge.p0+merge.n, merge.buf, merge.nbuf);
    105 	merge.f = nil;
    106 	merge.n = 0;
    107 	merge.nbuf = 0;
    108 }
    109 
    110 void
    111 mergeextend(File *f, uint p0)
    112 {
    113 	uint mp0n;
    114 
    115 	mp0n = merge.p0+merge.n;
    116 	if(mp0n != p0){
    117 		bufread(&f->b, mp0n, merge.buf+merge.nbuf, p0-mp0n);
    118 		merge.nbuf += p0-mp0n;
    119 		merge.n = p0-merge.p0;
    120 	}
    121 }
    122 
    123 /*
    124  * like fileundelete, but get the data from arguments
    125  */
    126 void
    127 loginsert(File *f, uint p0, Rune *s, uint ns)
    128 {
    129 	if(f->rescuing)
    130 		return;
    131 	if(ns == 0)
    132 		return;
    133 	if(ns<0 || ns>STRSIZE)
    134 		panic("loginsert");
    135 	if(f->seq < seq)
    136 		filemark(f);
    137 	if(p0 < f->hiposn)
    138 		error(Esequence);
    139 
    140 	if(merge.f != f
    141 	|| p0-(merge.p0+merge.n)>Maxmerge			/* too far */
    142 	|| merge.nbuf+((p0+ns)-(merge.p0+merge.n))>=RBUFSIZE)	/* too long */
    143 		flushmerge();
    144 
    145 	if(ns>=RBUFSIZE){
    146 		if(!(merge.n == 0 && merge.nbuf == 0 && merge.f == nil))
    147 			panic("loginsert bad merge state");
    148 		wrinsert(&f->epsilon, f->seq, TRUE, p0, s, ns);
    149 	}else{
    150 		if(merge.f != f){
    151 			merge.f = f;
    152 			merge.p0 = p0;
    153 			merge.seq = f->seq;
    154 		}
    155 		mergeextend(f, p0);
    156 
    157 		/* append string to merge */
    158 		runemove(merge.buf+merge.nbuf, s, ns);
    159 		merge.nbuf += ns;
    160 	}
    161 
    162 	f->hiposn = p0;
    163 	if(!f->unread && !f->mod)
    164 		state(f, Dirty);
    165 }
    166 
    167 void
    168 logdelete(File *f, uint p0, uint p1)
    169 {
    170 	if(f->rescuing)
    171 		return;
    172 	if(p0 == p1)
    173 		return;
    174 	if(f->seq < seq)
    175 		filemark(f);
    176 	if(p0 < f->hiposn)
    177 		error(Esequence);
    178 
    179 	if(merge.f != f
    180 	|| p0-(merge.p0+merge.n)>Maxmerge			/* too far */
    181 	|| merge.nbuf+(p0-(merge.p0+merge.n))>=RBUFSIZE){	/* too long */
    182 		flushmerge();
    183 		merge.f = f;
    184 		merge.p0 = p0;
    185 		merge.seq = f->seq;
    186 	}
    187 
    188 	mergeextend(f, p0);
    189 
    190 	/* add to deletion */
    191 	merge.n = p1-merge.p0;
    192 
    193 	f->hiposn = p1;
    194 	if(!f->unread && !f->mod)
    195 		state(f, Dirty);
    196 }
    197 
    198 /*
    199  * like fileunsetname, but get the data from arguments
    200  */
    201 void
    202 logsetname(File *f, String *s)
    203 {
    204 	Undo u;
    205 	Buffer *delta;
    206 
    207 	if(f->rescuing)
    208 		return;
    209 
    210 	if(f->unread){	/* This is setting initial file name */
    211 		filesetname(f, s);
    212 		return;
    213 	}
    214 
    215 	if(f->seq < seq)
    216 		filemark(f);
    217 
    218 	/* undo a file name change by restoring old name */
    219 	delta = &f->epsilon;
    220 	u.type = Filename;
    221 	u.mod = TRUE;
    222 	u.seq = f->seq;
    223 	u.p0 = 0;	/* unused */
    224 	u.n = s->n;
    225 	if(s->n)
    226 		bufinsert(delta, delta->nc, s->s, s->n);
    227 	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
    228 	if(!f->unread && !f->mod)
    229 		state(f, Dirty);
    230 }
    231 
    232 #ifdef NOTEXT
    233 File*
    234 fileaddtext(File *f, Text *t)
    235 {
    236 	if(f == nil){
    237 		f = emalloc(sizeof(File));
    238 		f->unread = TRUE;
    239 	}
    240 	f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*));
    241 	f->text[f->ntext++] = t;
    242 	f->curtext = t;
    243 	return f;
    244 }
    245 
    246 void
    247 filedeltext(File *f, Text *t)
    248 {
    249 	int i;
    250 
    251 	for(i=0; i<f->ntext; i++)
    252 		if(f->text[i] == t)
    253 			goto Found;
    254 	panic("can't find text in filedeltext");
    255 
    256     Found:
    257 	f->ntext--;
    258 	if(f->ntext == 0){
    259 		fileclose(f);
    260 		return;
    261 	}
    262 	memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*));
    263 	if(f->curtext == t)
    264 		f->curtext = f->text[0];
    265 }
    266 #endif
    267 
    268 void
    269 fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
    270 {
    271 	Undo u;
    272 
    273 	/* undo an insertion by deleting */
    274 	u.type = Delete;
    275 	u.mod = f->mod;
    276 	u.seq = f->seq;
    277 	u.p0 = p0;
    278 	u.n = ns;
    279 	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
    280 }
    281 
    282 void
    283 fileundelete(File *f, Buffer *delta, uint p0, uint p1)
    284 {
    285 	Undo u;
    286 	Rune *buf;
    287 	uint i, n;
    288 
    289 	/* undo a deletion by inserting */
    290 	u.type = Insert;
    291 	u.mod = f->mod;
    292 	u.seq = f->seq;
    293 	u.p0 = p0;
    294 	u.n = p1-p0;
    295 	buf = fbufalloc();
    296 	for(i=p0; i<p1; i+=n){
    297 		n = p1 - i;
    298 		if(n > RBUFSIZE)
    299 			n = RBUFSIZE;
    300 		bufread(&f->b, i, buf, n);
    301 		bufinsert(delta, delta->nc, buf, n);
    302 	}
    303 	fbuffree(buf);
    304 	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
    305 
    306 }
    307 
    308 int
    309 filereadc(File *f, uint q)
    310 {
    311 	Rune r;
    312 
    313 	if(q >= f->b.nc)
    314 		return -1;
    315 	bufread(&f->b, q, &r, 1);
    316 	return r;
    317 }
    318 
    319 void
    320 filesetname(File *f, String *s)
    321 {
    322 	if(!f->unread)	/* This is setting initial file name */
    323 		fileunsetname(f, &f->delta);
    324 	Strduplstr(&f->name, s);
    325 	sortname(f);
    326 	f->unread = TRUE;
    327 }
    328 
    329 void
    330 fileunsetname(File *f, Buffer *delta)
    331 {
    332 	String s;
    333 	Undo u;
    334 
    335 	/* undo a file name change by restoring old name */
    336 	u.type = Filename;
    337 	u.mod = f->mod;
    338 	u.seq = f->seq;
    339 	u.p0 = 0;	/* unused */
    340 	Strinit(&s);
    341 	Strduplstr(&s, &f->name);
    342 	fullname(&s);
    343 	u.n = s.n;
    344 	if(s.n)
    345 		bufinsert(delta, delta->nc, s.s, s.n);
    346 	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
    347 	Strclose(&s);
    348 }
    349 
    350 void
    351 fileunsetdot(File *f, Buffer *delta, Range dot)
    352 {
    353 	Undo u;
    354 
    355 	u.type = Dot;
    356 	u.mod = f->mod;
    357 	u.seq = f->seq;
    358 	u.p0 = dot.p1;
    359 	u.n = dot.p2 - dot.p1;
    360 	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
    361 }
    362 
    363 void
    364 fileunsetmark(File *f, Buffer *delta, Range mark)
    365 {
    366 	Undo u;
    367 
    368 	u.type = Mark;
    369 	u.mod = f->mod;
    370 	u.seq = f->seq;
    371 	u.p0 = mark.p1;
    372 	u.n = mark.p2 - mark.p1;
    373 	bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
    374 }
    375 
    376 uint
    377 fileload(File *f, uint p0, int fd, int *nulls)
    378 {
    379 	if(f->seq > 0)
    380 		panic("undo in file.load unimplemented");
    381 	return bufload(&f->b, p0, fd, nulls);
    382 }
    383 
    384 int
    385 fileupdate(File *f, int notrans, int toterm)
    386 {
    387 	uint p1, p2;
    388 	int mod;
    389 
    390 	if(f->rescuing)
    391 		return FALSE;
    392 
    393 	flushmerge();
    394 
    395 	/*
    396 	 * fix the modification bit
    397 	 * subtle point: don't save it away in the log.
    398 	 *
    399 	 * if another change is made, the correct f->mod
    400 	 * state is saved  in the undo log by filemark
    401 	 * when setting the dot and mark.
    402 	 *
    403 	 * if the change is undone, the correct state is
    404 	 * saved from f in the fileun... routines.
    405 	 */
    406 	mod = f->mod;
    407 	f->mod = f->prevmod;
    408 	if(f == cmd)
    409 		notrans = TRUE;
    410 	else{
    411 		fileunsetdot(f, &f->delta, f->prevdot);
    412 		fileunsetmark(f, &f->delta, f->prevmark);
    413 	}
    414 	f->dot = f->ndot;
    415 	fileundo(f, FALSE, !notrans, &p1, &p2, toterm);
    416 	f->mod = mod;
    417 
    418 	if(f->delta.nc == 0)
    419 		f->seq = 0;
    420 
    421 	if(f == cmd)
    422 		return FALSE;
    423 
    424 	if(f->mod){
    425 		f->closeok = 0;
    426 		quitok = 0;
    427 	}else
    428 		f->closeok = 1;
    429 	return TRUE;
    430 }
    431 
    432 long
    433 prevseq(Buffer *b)
    434 {
    435 	Undo u;
    436 	uint up;
    437 
    438 	up = b->nc;
    439 	if(up == 0)
    440 		return 0;
    441 	up -= Undosize;
    442 	bufread(b, up, (Rune*)&u, Undosize);
    443 	return u.seq;
    444 }
    445 
    446 long
    447 undoseq(File *f, int isundo)
    448 {
    449 	if(isundo)
    450 		return f->seq;
    451 
    452 	return prevseq(&f->epsilon);
    453 }
    454 
    455 void
    456 fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag)
    457 {
    458 	Undo u;
    459 	Rune *buf;
    460 	uint i, n, up;
    461 	uint stop;
    462 	Buffer *delta, *epsilon;
    463 
    464 	if(isundo){
    465 		/* undo; reverse delta onto epsilon, seq decreases */
    466 		delta = &f->delta;
    467 		epsilon = &f->epsilon;
    468 		stop = f->seq;
    469 	}else{
    470 		/* redo; reverse epsilon onto delta, seq increases */
    471 		delta = &f->epsilon;
    472 		epsilon = &f->delta;
    473 		stop = 0;	/* don't know yet */
    474 	}
    475 
    476 	raspstart(f);
    477 	while(delta->nc > 0){
    478 		/* rasp and buffer are in sync; sync with wire if needed */
    479 		if(needoutflush())
    480 			raspflush(f);
    481 		up = delta->nc-Undosize;
    482 		bufread(delta, up, (Rune*)&u, Undosize);
    483 		if(isundo){
    484 			if(u.seq < stop){
    485 				f->seq = u.seq;
    486 				raspdone(f, flag);
    487 				return;
    488 			}
    489 		}else{
    490 			if(stop == 0)
    491 				stop = u.seq;
    492 			if(u.seq > stop){
    493 				raspdone(f, flag);
    494 				return;
    495 			}
    496 		}
    497 		switch(u.type){
    498 		default:
    499 			panic("undo unknown u.type");
    500 			break;
    501 
    502 		case Delete:
    503 			f->seq = u.seq;
    504 			if(canredo)
    505 				fileundelete(f, epsilon, u.p0, u.p0+u.n);
    506 			f->mod = u.mod;
    507 			bufdelete(&f->b, u.p0, u.p0+u.n);
    508 			raspdelete(f, u.p0, u.p0+u.n, flag);
    509 			*q0p = u.p0;
    510 			*q1p = u.p0;
    511 			break;
    512 
    513 		case Insert:
    514 			f->seq = u.seq;
    515 			if(canredo)
    516 				fileuninsert(f, epsilon, u.p0, u.n);
    517 			f->mod = u.mod;
    518 			up -= u.n;
    519 			buf = fbufalloc();
    520 			for(i=0; i<u.n; i+=n){
    521 				n = u.n - i;
    522 				if(n > RBUFSIZE)
    523 					n = RBUFSIZE;
    524 				bufread(delta, up+i, buf, n);
    525 				bufinsert(&f->b, u.p0+i, buf, n);
    526 				raspinsert(f, u.p0+i, buf, n, flag);
    527 			}
    528 			fbuffree(buf);
    529 			*q0p = u.p0;
    530 			*q1p = u.p0+u.n;
    531 			break;
    532 
    533 		case Filename:
    534 			f->seq = u.seq;
    535 			if(canredo)
    536 				fileunsetname(f, epsilon);
    537 			f->mod = u.mod;
    538 			up -= u.n;
    539 
    540 			Strinsure(&f->name, u.n+1);
    541 			bufread(delta, up, f->name.s, u.n);
    542 			f->name.s[u.n] = 0;
    543 			f->name.n = u.n;
    544 			fixname(&f->name);
    545 			sortname(f);
    546 			break;
    547 		case Dot:
    548 			f->seq = u.seq;
    549 			if(canredo)
    550 				fileunsetdot(f, epsilon, f->dot.r);
    551 			f->mod = u.mod;
    552 			f->dot.r.p1 = u.p0;
    553 			f->dot.r.p2 = u.p0 + u.n;
    554 			break;
    555 		case Mark:
    556 			f->seq = u.seq;
    557 			if(canredo)
    558 				fileunsetmark(f, epsilon, f->mark);
    559 			f->mod = u.mod;
    560 			f->mark.p1 = u.p0;
    561 			f->mark.p2 = u.p0 + u.n;
    562 			break;
    563 		}
    564 		bufdelete(delta, up, delta->nc);
    565 	}
    566 	if(isundo)
    567 		f->seq = 0;
    568 	raspdone(f, flag);
    569 }
    570 
    571 void
    572 filereset(File *f)
    573 {
    574 	bufreset(&f->delta);
    575 	bufreset(&f->epsilon);
    576 	f->seq = 0;
    577 }
    578 
    579 void
    580 fileclose(File *f)
    581 {
    582 	Strclose(&f->name);
    583 	bufclose(&f->b);
    584 	bufclose(&f->delta);
    585 	bufclose(&f->epsilon);
    586 	if(f->rasp)
    587 		listfree(f->rasp);
    588 	free(f);
    589 }
    590 
    591 void
    592 filemark(File *f)
    593 {
    594 
    595 	if(f->unread)
    596 		return;
    597 	if(f->epsilon.nc)
    598 		bufdelete(&f->epsilon, 0, f->epsilon.nc);
    599 
    600 	if(f != cmd){
    601 		f->prevdot = f->dot.r;
    602 		f->prevmark = f->mark;
    603 		f->prevseq = f->seq;
    604 		f->prevmod = f->mod;
    605 	}
    606 
    607 	f->ndot = f->dot;
    608 	f->seq = seq;
    609 	f->hiposn = 0;
    610 }