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 }