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