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