1 #include "sam.h" 2 /* 3 * Files are splayed out a factor of NDISC to reduce indirect block access 4 */ 5 Discdesc *files[NDISC]; 6 Discdesc *transcripts[NDISC]; 7 Buffer *undobuf; 8 static String *ftempstr(Rune*, int); 9 int fcount; 10 File *lastfile; 11 12 void puthdr_csl(Buffer*, char, short, Posn); 13 void puthdr_cs(Buffer*, char, short); 14 void puthdr_M(Buffer*, Posn, Range, Range, Mod, short); 15 void puthdr_cll(Buffer*, char, Posn, Posn); 16 void Fflush(File*); 17 18 enum{ 19 SKIP=50, /* max dist between file changes folded together */ 20 MAXCACHE=STRSIZE, /* max length of cache. must be < 32K-BLOCKSIZE */ 21 }; 22 23 void 24 Fstart(void) 25 { 26 undobuf = Bopen(Dstart()); 27 snarfbuf = Bopen(Dstart()); 28 plan9buf = Bopen(Dstart()); 29 } 30 31 void 32 Fmark(File *f, Mod m) 33 { 34 Buffer *t = f->transcript; 35 Posn p; 36 37 if(f->state == Readerr) 38 return; 39 if(f->state == Unread) /* this is implicit 'e' of a file */ 40 return; 41 p = m==0? -1 : f->markp; 42 f->markp = t->nrunes; 43 puthdr_M(t, p, f->dot.r, f->mark, f->mod, f->state); 44 f->ndot = f->dot; 45 f->marked = TRUE; 46 f->mod = m; 47 f->hiposn = -1; 48 /* Safety first */ 49 f->cp1 = f->cp2 = 0; 50 } 51 52 File * 53 Fopen(void) 54 { 55 File *f; 56 57 f = emalloc(sizeof(File)); 58 if(files[fcount] == 0){ 59 files[fcount] = Dstart(); 60 transcripts[fcount] = Dstart(); 61 } 62 f->buf = Bopen(files[fcount]); 63 f->transcript = Bopen(transcripts[fcount]); 64 if(++fcount == NDISC) 65 fcount = 0; 66 f->nrunes = 0; 67 f->markp = 0; 68 f->mod = 0; 69 f->dot.f = f; 70 f->ndot.f = f; 71 f->dev = ~0; 72 f->qid = ~0; 73 Strinit0(&f->name); 74 Strinit(&f->cache); 75 f->state = Unread; 76 Fmark(f, (Mod)0); 77 return f; 78 } 79 80 void 81 Fclose(File *f) 82 { 83 if(f == lastfile) 84 lastfile = 0; 85 Bterm(f->buf); 86 Bterm(f->transcript); 87 Strclose(&f->name); 88 Strclose(&f->cache); 89 if(f->rasp) 90 listfree(f->rasp); 91 free(f); 92 } 93 94 void 95 Finsert(File *f, String *str, Posn p1) 96 { 97 Buffer *t = f->transcript; 98 99 if(f->state == Readerr) 100 return; 101 if(str->n == 0) 102 return; 103 if(str->n<0 || str->n>STRSIZE) 104 panic("Finsert"); 105 if(f->mod < modnum) 106 Fmark(f, modnum); 107 if(p1 < f->hiposn) 108 error(Esequence); 109 if(str->n >= BLOCKSIZE){ /* don't bother with the cache */ 110 Fflush(f); 111 puthdr_csl(t, 'i', str->n, p1); 112 Binsert(t, str, t->nrunes); 113 }else{ /* insert into the cache instead of the transcript */ 114 if(f->cp2==0 && f->cp1==0 && f->cache.n==0) /* empty cache */ 115 f->cp1 = f->cp2 = p1; 116 if(p1-f->cp2>SKIP || f->cache.n+str->n>MAXCACHE-SKIP){ 117 Fflush(f); 118 f->cp1 = f->cp2 = p1; 119 } 120 if(f->cp2 != p1){ /* grab the piece in between */ 121 Rune buf[SKIP]; 122 String s; 123 Fchars(f, buf, f->cp2, p1); 124 s.s = buf; 125 s.n = p1-f->cp2; 126 Strinsert(&f->cache, &s, f->cache.n); 127 f->cp2 = p1; 128 } 129 Strinsert(&f->cache, str, f->cache.n); 130 } 131 if(f != cmd) 132 quitok = FALSE; 133 f->closeok = FALSE; 134 if(f->state == Clean) 135 state(f, Dirty); 136 f->hiposn = p1; 137 } 138 139 void 140 Fdelete(File *f, Posn p1, Posn p2) 141 { 142 if(f->state == Readerr) 143 return; 144 if(p1==p2) 145 return; 146 if(f->mod<modnum) 147 Fmark(f, modnum); 148 if(p1<f->hiposn) 149 error(Esequence); 150 if(p1-f->cp2>SKIP) 151 Fflush(f); 152 if(f->cp2==0 && f->cp1==0 && f->cache.n==0) /* empty cache */ 153 f->cp1 = f->cp2 = p1; 154 if(f->cp2 != p1){ /* grab the piece in between */ 155 if(f->cache.n+(p1-f->cp2)>MAXCACHE){ 156 Fflush(f); 157 f->cp1 = f->cp2 = p1; 158 }else{ 159 Rune buf[SKIP]; 160 String s; 161 Fchars(f, buf, f->cp2, p1); 162 s.s = buf; 163 s.n = p1-f->cp2; 164 Strinsert(&f->cache, &s, f->cache.n); 165 } 166 } 167 f->cp2 = p2; 168 if(f!=cmd) 169 quitok = FALSE; 170 f->closeok = FALSE; 171 if(f->state==Clean) 172 state(f, Dirty); 173 f->hiposn = p2; 174 } 175 176 void 177 Fflush(File *f) 178 { 179 Buffer *t = f->transcript; 180 Posn p1 = f->cp1, p2 = f->cp2; 181 182 if(f->state == Readerr) 183 return; 184 if(p1 != p2) 185 puthdr_cll(t, 'd', p1, p2); 186 if(f->cache.n){ 187 puthdr_csl(t, 'i', f->cache.n, p2); 188 Binsert(t, &f->cache, t->nrunes); 189 Strzero(&f->cache); 190 } 191 f->cp1 = f->cp2 = 0; 192 } 193 194 void 195 Fsetname(File *f, String *s) 196 { 197 Buffer *t = f->transcript; 198 199 if(f->state == Readerr) 200 return; 201 if(f->state == Unread){ /* This is setting initial file name */ 202 Strduplstr(&f->name, s); 203 sortname(f); 204 }else{ 205 if(f->mod < modnum) 206 Fmark(f, modnum); 207 puthdr_cs(t, 'f', s->n); 208 Binsert(t, s, t->nrunes); 209 } 210 } 211 212 /* 213 * The heart of it all. Fupdate will run along the transcript list, executing 214 * the commands and converting them into their inverses for a later undo pass. 215 * The pass runs top to bottom, so addresses in the transcript are tracked 216 * (by the var. delta) so they stay valid during the operation. This causes 217 * all operations to appear to happen simultaneously, which is why the addresses 218 * passed to Fdelete and Finsert never take into account other changes occurring 219 * in this command (and is why things are done this way). 220 */ 221 int 222 Fupdate(File *f, int mktrans, int toterm) 223 { 224 Buffer *t = f->transcript; 225 Buffer *u = undobuf; 226 int n, ni; 227 Posn p0, p1, p2, p, deltadot = 0, deltamark = 0, delta = 0; 228 int changes = FALSE; 229 union Hdr buf; 230 Rune tmp[BLOCKSIZE+1]; /* +1 for NUL in 'f' case */ 231 232 if(f->state == Readerr) 233 return FALSE; 234 if(lastfile && f!=lastfile) 235 Bclean(lastfile->transcript); /* save memory when multifile */ 236 lastfile = f; 237 Fflush(f); 238 if(f->marked) 239 p0 = f->markp+sizeof(Mark)/RUNESIZE; 240 else 241 p0 = 0; 242 f->dot = f->ndot; 243 while((n=Bread(t, (Rune*)&buf, sizeof buf/RUNESIZE, p0)) > 0){ 244 switch(buf.cs.c){ 245 default: 246 panic("unknown in Fupdate"); 247 case 'd': 248 p1 = buf.cll.l; 249 p2 = buf.cll.l1; 250 p0 += sizeof(struct _cll)/RUNESIZE; 251 if(p2 <= f->dot.r.p1) 252 deltadot -= p2-p1; 253 if(p2 <= f->mark.p1) 254 deltamark -= p2-p1; 255 p1 += delta, p2+=delta; 256 delta -= p2-p1; 257 if(!mktrans) 258 for(p = p1; p<p2; p+=ni){ 259 if(p2-p>BLOCKSIZE) 260 ni = BLOCKSIZE; 261 else 262 ni = p2-p; 263 puthdr_csl(u, 'i', ni, p1); 264 Bread(f->buf, tmp, ni, p); 265 Binsert(u, ftempstr(tmp, ni), u->nrunes); 266 } 267 f->nrunes -= p2-p1; 268 Bdelete(f->buf, p1, p2); 269 changes = TRUE; 270 break; 271 272 case 'f': 273 n = buf.cs.s; 274 p0 += sizeof(struct _cs)/RUNESIZE; 275 Strinsure(&genstr, n+1); 276 Bread(t, tmp, n, p0); 277 tmp[n] = 0; 278 p0 += n; 279 Strdupl(&genstr, tmp); 280 if(!mktrans){ 281 puthdr_cs(u, 'f', f->name.n); 282 Binsert(u, &f->name, u->nrunes); 283 } 284 Strduplstr(&f->name, &genstr); 285 sortname(f); 286 changes = TRUE; 287 break; 288 289 case 'i': 290 n = buf.csl.s; 291 p1 = buf.csl.l; 292 p0 += sizeof(struct _csl)/RUNESIZE; 293 if(p1 < f->dot.r.p1) 294 deltadot += n; 295 if(p1 < f->mark.p1) 296 deltamark += n; 297 p1 += delta; 298 delta += n; 299 if(!mktrans) 300 puthdr_cll(u, 'd', p1, p1+n); 301 changes = TRUE; 302 f->nrunes += n; 303 while(n > 0){ 304 if(n > BLOCKSIZE) 305 ni = BLOCKSIZE; 306 else 307 ni = n; 308 Bread(t, tmp, ni, p0); 309 Binsert(f->buf, ftempstr(tmp, ni), p1); 310 n -= ni; 311 p1 += ni; 312 p0 += ni; 313 } 314 break; 315 } 316 } 317 toterminal(f, toterm); 318 f->dot.r.p1 += deltadot; 319 f->dot.r.p2 += deltadot; 320 if(f->dot.r.p1 > f->nrunes) 321 f->dot.r.p1 = f->nrunes; 322 if(f->dot.r.p2 > f->nrunes) 323 f->dot.r.p2 = f->nrunes; 324 f->mark.p1 += deltamark; 325 f->mark.p2 += deltamark; 326 if(f->mark.p1 > f->nrunes) 327 f->mark.p1 = f->nrunes; 328 if(f->mark.p2 > f->nrunes) 329 f->mark.p2 = f->nrunes; 330 if(n < 0) 331 panic("Fupdate read"); 332 if(f == cmd) 333 f->mod = 0; /* can't undo command file */ 334 if(p0 > f->markp+sizeof(Posn)/RUNESIZE){ /* for undo, this throws away the undo transcript */ 335 if(f->mod > 0){ /* can't undo the dawn of time */ 336 Bdelete(t, f->markp+sizeof(Mark)/RUNESIZE, t->nrunes); 337 /* copy the undo list back into the transcript */ 338 for(p = 0; p<u->nrunes; p+=ni){ 339 if(u->nrunes-p>BLOCKSIZE) 340 ni = BLOCKSIZE; 341 else 342 ni = u->nrunes-p; 343 Bread(u, tmp, ni, p); 344 Binsert(t, ftempstr(tmp, ni), t->nrunes); 345 } 346 } 347 Bdelete(u, (Posn)0, u->nrunes); 348 } 349 return f==cmd? FALSE : changes; 350 } 351 352 void 353 puthdr_csl(Buffer *b, char c, short s, Posn p) 354 { 355 struct _csl buf; 356 357 if(p < 0) 358 panic("puthdr_csP"); 359 buf.c = c; 360 buf.s = s; 361 buf.l = p; 362 Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes); 363 } 364 365 void 366 puthdr_cs(Buffer *b, char c, short s) 367 { 368 struct _cs buf; 369 370 buf.c = c; 371 buf.s = s; 372 Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes); 373 } 374 375 void 376 puthdr_M(Buffer *b, Posn p, Range dot, Range mk, Mod m, short s1) 377 { 378 Mark mark; 379 static first = 1; 380 381 if(!first && p<0) 382 panic("puthdr_M"); 383 mark.p = p; 384 mark.dot = dot; 385 mark.mark = mk; 386 mark.m = m; 387 mark.s1 = s1; 388 Binsert(b, ftempstr((Rune *)&mark, sizeof mark/RUNESIZE), b->nrunes); 389 } 390 391 void 392 puthdr_cll(Buffer *b, char c, Posn p1, Posn p2) 393 { 394 struct _cll buf; 395 396 if(p1<0 || p2<0) 397 panic("puthdr_cll"); 398 buf.c = c; 399 buf.l = p1; 400 buf.l1 = p2; 401 Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes); 402 } 403 404 long 405 Fchars(File *f, Rune *addr, Posn p1, Posn p2) 406 { 407 return Bread(f->buf, addr, p2-p1, p1); 408 } 409 410 int 411 Fgetcset(File *f, Posn p) 412 { 413 if(p<0 || p>f->nrunes) 414 panic("Fgetcset out of range"); 415 if((f->ngetc = Fchars(f, f->getcbuf, p, p+NGETC))<0) 416 panic("Fgetcset Bread fail"); 417 f->getcp = p; 418 f->getci = 0; 419 return f->ngetc; 420 } 421 422 int 423 Fbgetcset(File *f, Posn p) 424 { 425 if(p<0 || p>f->nrunes) 426 panic("Fbgetcset out of range"); 427 if((f->ngetc = Fchars(f, f->getcbuf, p<NGETC? (Posn)0 : p-NGETC, p))<0) 428 panic("Fbgetcset Bread fail"); 429 f->getcp = p; 430 f->getci = f->ngetc; 431 return f->ngetc; 432 } 433 434 int 435 Fgetcload(File *f, Posn p) 436 { 437 if(Fgetcset(f, p)){ 438 --f->ngetc; 439 f->getcp++; 440 return f->getcbuf[f->getci++]; 441 } 442 return -1; 443 } 444 445 int 446 Fbgetcload(File *f, Posn p) 447 { 448 if(Fbgetcset(f, p)){ 449 --f->getcp; 450 return f->getcbuf[--f->getci]; 451 } 452 return -1; 453 } 454 455 static String* 456 ftempstr(Rune *s, int n) 457 { 458 static String p; 459 460 p.s = s; 461 p.n = n; 462 p.size = n; 463 return &p; 464 } 465