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, 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 fileinsert(File *f, uint p0, Rune *s, uint ns) 270 { 271 if(p0 > f->nc) 272 panic("internal error: fileinsert"); 273 if(f->seq > 0) 274 fileuninsert(f, &f->delta, p0, ns); 275 bufinsert(f, p0, s, ns); 276 if(ns) 277 f->mod = TRUE; 278 } 279 280 void 281 fileuninsert(File *f, Buffer *delta, uint p0, uint ns) 282 { 283 Undo u; 284 285 /* undo an insertion by deleting */ 286 u.type = Delete; 287 u.mod = f->mod; 288 u.seq = f->seq; 289 u.p0 = p0; 290 u.n = ns; 291 bufinsert(delta, delta->nc, (Rune*)&u, Undosize); 292 } 293 294 void 295 filedelete(File *f, uint p0, uint p1) 296 { 297 if(!(p0<=p1 && p0<=f->nc && p1<=f->nc)) 298 panic("internal error: filedelete"); 299 if(f->seq > 0) 300 fileundelete(f, &f->delta, p0, p1); 301 bufdelete(f, p0, p1); 302 if(p1 > p0) 303 f->mod = TRUE; 304 } 305 306 void 307 fileundelete(File *f, Buffer *delta, uint p0, uint p1) 308 { 309 Undo u; 310 Rune *buf; 311 uint i, n; 312 313 /* undo a deletion by inserting */ 314 u.type = Insert; 315 u.mod = f->mod; 316 u.seq = f->seq; 317 u.p0 = p0; 318 u.n = p1-p0; 319 buf = fbufalloc(); 320 for(i=p0; i<p1; i+=n){ 321 n = p1 - i; 322 if(n > RBUFSIZE) 323 n = RBUFSIZE; 324 bufread(f, i, buf, n); 325 bufinsert(delta, delta->nc, buf, n); 326 } 327 fbuffree(buf); 328 bufinsert(delta, delta->nc, (Rune*)&u, Undosize); 329 330 } 331 332 int 333 filereadc(File *f, uint q) 334 { 335 Rune r; 336 337 if(q >= f->nc) 338 return -1; 339 bufread(f, q, &r, 1); 340 return r; 341 } 342 343 void 344 filesetname(File *f, String *s) 345 { 346 if(!f->unread) /* This is setting initial file name */ 347 fileunsetname(f, &f->delta); 348 Strduplstr(&f->name, s); 349 sortname(f); 350 f->unread = TRUE; 351 } 352 353 void 354 fileunsetname(File *f, Buffer *delta) 355 { 356 String s; 357 Undo u; 358 359 /* undo a file name change by restoring old name */ 360 u.type = Filename; 361 u.mod = f->mod; 362 u.seq = f->seq; 363 u.p0 = 0; /* unused */ 364 Strinit(&s); 365 Strduplstr(&s, &f->name); 366 fullname(&s); 367 u.n = s.n; 368 if(s.n) 369 bufinsert(delta, delta->nc, s.s, s.n); 370 bufinsert(delta, delta->nc, (Rune*)&u, Undosize); 371 Strclose(&s); 372 } 373 374 void 375 fileunsetdot(File *f, Buffer *delta, Range dot) 376 { 377 Undo u; 378 379 u.type = Dot; 380 u.mod = f->mod; 381 u.seq = f->seq; 382 u.p0 = dot.p1; 383 u.n = dot.p2 - dot.p1; 384 bufinsert(delta, delta->nc, (Rune*)&u, Undosize); 385 } 386 387 void 388 fileunsetmark(File *f, Buffer *delta, Range mark) 389 { 390 Undo u; 391 392 u.type = Mark; 393 u.mod = f->mod; 394 u.seq = f->seq; 395 u.p0 = mark.p1; 396 u.n = mark.p2 - mark.p1; 397 bufinsert(delta, delta->nc, (Rune*)&u, Undosize); 398 } 399 400 uint 401 fileload(File *f, uint p0, int fd, int *nulls) 402 { 403 if(f->seq > 0) 404 panic("undo in file.load unimplemented"); 405 return bufload(f, p0, fd, nulls); 406 } 407 408 int 409 fileupdate(File *f, int notrans, int toterm) 410 { 411 uint p1, p2; 412 int mod; 413 414 if(f->rescuing) 415 return FALSE; 416 417 flushmerge(); 418 419 /* 420 * fix the modification bit 421 * subtle point: don't save it away in the log. 422 * 423 * if another change is made, the correct f->mod 424 * state is saved in the undo log by filemark 425 * when setting the dot and mark. 426 * 427 * if the change is undone, the correct state is 428 * saved from f in the fileun... routines. 429 */ 430 mod = f->mod; 431 f->mod = f->prevmod; 432 if(f == cmd) 433 notrans = TRUE; 434 else{ 435 fileunsetdot(f, &f->delta, f->prevdot); 436 fileunsetmark(f, &f->delta, f->prevmark); 437 } 438 f->dot = f->ndot; 439 fileundo(f, FALSE, !notrans, &p1, &p2, toterm); 440 f->mod = mod; 441 442 if(f->delta.nc == 0) 443 f->seq = 0; 444 445 if(f == cmd) 446 return FALSE; 447 448 if(f->mod){ 449 f->closeok = 0; 450 quitok = 0; 451 }else 452 f->closeok = 1; 453 return TRUE; 454 } 455 456 long 457 prevseq(Buffer *b) 458 { 459 Undo u; 460 uint up; 461 462 up = b->nc; 463 if(up == 0) 464 return 0; 465 up -= Undosize; 466 bufread(b, up, (Rune*)&u, Undosize); 467 return u.seq; 468 } 469 470 long 471 undoseq(File *f, int isundo) 472 { 473 if(isundo) 474 return f->seq; 475 476 return prevseq(&f->epsilon); 477 } 478 479 void 480 fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag) 481 { 482 Undo u; 483 Rune *buf; 484 uint i, n, up; 485 uint stop; 486 Buffer *delta, *epsilon; 487 488 if(isundo){ 489 /* undo; reverse delta onto epsilon, seq decreases */ 490 delta = &f->delta; 491 epsilon = &f->epsilon; 492 stop = f->seq; 493 }else{ 494 /* redo; reverse epsilon onto delta, seq increases */ 495 delta = &f->epsilon; 496 epsilon = &f->delta; 497 stop = 0; /* don't know yet */ 498 } 499 500 raspstart(f); 501 while(delta->nc > 0){ 502 up = delta->nc-Undosize; 503 bufread(delta, up, (Rune*)&u, Undosize); 504 if(isundo){ 505 if(u.seq < stop){ 506 f->seq = u.seq; 507 raspdone(f, flag); 508 return; 509 } 510 }else{ 511 if(stop == 0) 512 stop = u.seq; 513 if(u.seq > stop){ 514 raspdone(f, flag); 515 return; 516 } 517 } 518 switch(u.type){ 519 default: 520 panic("undo unknown u.type"); 521 break; 522 523 case Delete: 524 f->seq = u.seq; 525 if(canredo) 526 fileundelete(f, epsilon, u.p0, u.p0+u.n); 527 f->mod = u.mod; 528 bufdelete(f, u.p0, u.p0+u.n); 529 raspdelete(f, u.p0, u.p0+u.n, flag); 530 *q0p = u.p0; 531 *q1p = u.p0; 532 break; 533 534 case Insert: 535 f->seq = u.seq; 536 if(canredo) 537 fileuninsert(f, epsilon, u.p0, u.n); 538 f->mod = u.mod; 539 up -= u.n; 540 buf = fbufalloc(); 541 for(i=0; i<u.n; i+=n){ 542 n = u.n - i; 543 if(n > RBUFSIZE) 544 n = RBUFSIZE; 545 bufread(delta, up+i, buf, n); 546 bufinsert(f, u.p0+i, buf, n); 547 raspinsert(f, u.p0+i, buf, n, flag); 548 } 549 fbuffree(buf); 550 *q0p = u.p0; 551 *q1p = u.p0+u.n; 552 break; 553 554 case Filename: 555 f->seq = u.seq; 556 if(canredo) 557 fileunsetname(f, epsilon); 558 f->mod = u.mod; 559 up -= u.n; 560 561 Strinsure(&f->name, u.n+1); 562 bufread(delta, up, f->name.s, u.n); 563 f->name.s[u.n] = 0; 564 f->name.n = u.n; 565 fixname(&f->name); 566 sortname(f); 567 break; 568 case Dot: 569 f->seq = u.seq; 570 if(canredo) 571 fileunsetdot(f, epsilon, f->dot.r); 572 f->mod = u.mod; 573 f->dot.r.p1 = u.p0; 574 f->dot.r.p2 = u.p0 + u.n; 575 break; 576 case Mark: 577 f->seq = u.seq; 578 if(canredo) 579 fileunsetmark(f, epsilon, f->mark); 580 f->mod = u.mod; 581 f->mark.p1 = u.p0; 582 f->mark.p2 = u.p0 + u.n; 583 break; 584 } 585 bufdelete(delta, up, delta->nc); 586 } 587 if(isundo) 588 f->seq = 0; 589 raspdone(f, flag); 590 } 591 592 void 593 filereset(File *f) 594 { 595 bufreset(&f->delta); 596 bufreset(&f->epsilon); 597 f->seq = 0; 598 } 599 600 void 601 fileclose(File *f) 602 { 603 Strclose(&f->name); 604 bufclose(f); 605 bufclose(&f->delta); 606 bufclose(&f->epsilon); 607 if(f->rasp) 608 listfree(f->rasp); 609 free(f); 610 } 611 612 void 613 filemark(File *f) 614 { 615 616 if(f->unread) 617 return; 618 if(f->epsilon.nc) 619 bufdelete(&f->epsilon, 0, f->epsilon.nc); 620 621 if(f != cmd){ 622 f->prevdot = f->dot.r; 623 f->prevmark = f->mark; 624 f->prevseq = f->seq; 625 f->prevmod = f->mod; 626 } 627 628 f->ndot = f->dot; 629 f->seq = seq; 630 f->hiposn = 0; 631 } 632